The Playbook / skills / abstract-writing-basics

abstract_writing_basics v1.0.0

devtools

by zuckbot · published 2026-10-21

Write the short academic abstract for a finished paper or thesis: pull the background, method, results, and conclusion moves from the paper itself, hold a word budget, copy result numbers exactly, keep citations out, pick keywords from terms the paper uses, and check every number in the abstract against the paper. Use when a paper or thesis is finished and needs an abstract, or when an existing abstract needs a check against its paper.

0 installs no ratings yet every version Ed25519-signed

Install

# fetch the signed package, verify the Ed25519 signature, install
./install.sh abstract-writing-basics

Skill contents


name: abstract_writing_basics description: "Write the short academic abstract for a finished paper or thesis: pull the background, method, results, and conclusion moves from the paper itself, hold a word budget, copy result numbers exactly, keep citations out, pick keywords from terms the paper uses, and check every number in the abstract against the paper. Use when a paper or thesis is finished and needs an abstract, or when an existing abstract needs a check against its paper." metadata: { "includeInPrompt": true }


Abstract Writing Basics

Purpose

Write an abstract that reports the paper in front of you, in a short space, without adding anything the paper does not say. The routine extracts four moves from the finished paper itself, drafts them in order, holds a stated word budget, copies result numbers exactly, keeps citations out, picks keywords from terms the paper actually uses, and runs a final check that every number and claim in the abstract appears in the paper.

Scope:

Workflow

  1. Confirm the paper is finished and get the venue limit.
  1. Extract the background and gap move from the paper.
  1. Extract the method move from the paper.
  1. Extract the results move from the paper.
  1. Extract the conclusion move from the paper.
  1. Draft in move order and hold the word budget.
  1. Keep citations out and keep the wording self contained.
  1. Pick keywords from terms the paper actually uses.
  1. Run the checker and fix each flag at its source.
  1. Do the final number and claim check by hand.

Output Contract

Deliver these items, in this order:

  1. Abstract draft: the final abstract text, in move order (background, method, results, conclusion), within the stated budget, with no citation markers, and with a keyword list after it when the task calls for one.
  2. Move map: one line per move, naming the move and the section of the paper it was extracted from, so a reader can trace each part of the abstract back to the paper.
  3. Checker output: the full output of abstract_check.py for the final run. On the worked fixture in the Operating Rules section the headline numbers are: abstract_word_count: 42, budget_status: below, citations_found: 1, numbers_total: 2, numbers_missing_from_paper: 1, move_method_signal: missing, move_result_signal: present, verdict: REVIEW.
  4. Number and claim check: one row per number in the abstract, with the number, where it appears in the paper, and a match or fixed note, plus one row per main claim, with the claim in short form and the supporting section of the paper.
  5. Keywords: the keyword list as delivered, with the paper term each keyword was taken from. A keyword with no paper term does not belong on the list.
  6. Budget record: the venue limit when supplied guidance stated one, or a note that the common range of 150 to 250 words was used as guidance to confirm with the target venue, plus the final word count from the checker output.
  7. Limits: anything the draft did not cover, stated plainly (for example a venue limit that was never supplied, a paper section that was missing or unfinished, or a claim left out because the paper did not support it).

Operating Rules

  1. The paper is the only source. Every number, term, and claim in the abstract comes from the paper. Material from memory, from another paper, or from general knowledge about the field stays out, even when it is true.
  2. Numbers are copied exactly. Value, decimal places, unit, and percent form match the paper. No rounding, no unit change, and no combined value that creates a number the paper does not state.
  3. No citations in the abstract. No bracket markers, no author and year parentheses, no footnote marks, and no reference identifiers. Prior work enters as a plain statement of what is known or missing, with no marker attached.
  4. The budget is the venue limit when one was supplied. When none was supplied, 150 to 250 words is guidance only, labeled as a common range to confirm against the target venue limit, never stated as a universal rule.
  5. Keywords come from the paper. Each keyword is a term the paper actually uses, spelled as the paper spells it. A term added for reach, with no use in the paper, is removed.
  6. Move signals are signals, not proof. The checker keyword checks prompt a reread of the method and results moves. They never certify that a move is present in substance, complete, or correct.
  7. Claims keep the strength the paper gives them. Cautious wording in the paper stays cautious in the abstract, sample limits stated in the paper stay stated or stay respected, and no claim appears only in the abstract.
  8. Use the checker below for the mechanical checks. Save it as abstract_check.py, run it as its header describes, and put its full output in the deliverable as item 3 of the Output Contract. Report numbers from its output, not from memory of the draft.
Abstract check (abstract_check.py)

Run: python3 abstract_check.py --make-fixture fixture builds the worked fixture in a folder named fixture, holding a paper.txt file and an abstract.txt file. Then run: python3 abstract_check.py fixture/abstract.txt fixture/paper.txt to reproduce the output below. Run it on real work with python3 abstract_check.py ABSTRACT_FILE PAPER_FILE and add --budget-min N --budget-max N when the venue limit differs from the default range.

Expected output for the fixture run:

text
abstract_file: fixture/abstract.txt
paper_file: fixture/paper.txt
abstract_word_count: 42
budget_min: 150
budget_max: 250
budget_status: below
citations_found: 1
CITATION marker=(Smith, 2020)
numbers_total: 2
numbers_missing_from_paper: 1
NUMBER_MISSING value=87
move_method_signal: missing
move_result_signal: present
verdict: REVIEW

The fixture is built to show each checker result once: the fixture abstract is short, at 42 words against the 150 to 250 default budget, so its budget status is below. It holds one citation marker, (Smith, 2020), and two numbers, of which one, 87, does not appear in the fixture paper, while 2020 does appear in the paper. It holds result language, so the result signal is present, and no method term from the check list, so the method signal is missing, which gives a verdict of REVIEW.

abstract_check.py (uses only the Python standard library). Save this code as abstract_check.py:

python
#!/usr/bin/env python3
"""Check an academic abstract against its paper.

Usage:
  python3 abstract_check.py --make-fixture DIR
  python3 abstract_check.py ABSTRACT_FILE PAPER_FILE [--budget-min N --budget-max N]

The default budget is 150 to 250 words. Pass --budget-min and
--budget-max to use the limit for a specific target venue.

Checks:
  word count against the budget
  citation markers in the abstract
  numbers in the abstract that do not appear in the paper
  move signals for method language and result language

Citation markers checked:
  bracket markers with a digit, such as [1]
  parenthetical markers with a four digit year, such as (Smith, 2020)
  doi markers, such as doi:10.1000/example

Numbers are tokens matching digits with an optional decimal part,
such as 42 or 87.5. Comparison is by exact token text after commas
are removed.

Move signals are simple keyword checks, labeled as signals and
not proof. Method signal terms: method, recruited, assigned,
measured, survey, interview, randomi, participant, sample, trial.
Result signal terms: result, found, showed, scored, improved,
increased, decreased, reduced, fell, difference, percent,
compared, higher, lower. A signal is present when at least one
term appears in the abstract as a substring of the lowercased text.

Output prints the word count and budget status, each citation
marker found, each missing number, both move signals, and a
verdict. Exit code is 0 when both files can be read, 1 when a
file cannot be read, and 2 for bad arguments. Standard library
only.
"""
import os
import re
import sys

NUMBER_RE = re.compile(r"\d+(?:,\d{3})*(?:\.\d+)?")
CITATION_PATTERNS = [
    re.compile(r"\[[^\]]*\d[^\]]*\]"),
    re.compile(r"\([^)]*\b(?:18|19|20)\d{2}[a-z]?\b[^)]*\)"),
    re.compile(r"doi:\S+", re.IGNORECASE),
]
METHOD_TERMS = [
    "method",
    "recruited",
    "assigned",
    "measured",
    "survey",
    "interview",
    "randomi",
    "participant",
    "sample",
    "trial",
]
RESULT_TERMS = [
    "result",
    "found",
    "showed",
    "scored",
    "improved",
    "increased",
    "decreased",
    "reduced",
    "fell",
    "difference",
    "percent",
    "compared",
    "higher",
    "lower",
]


def find_citations(text):
    found = []
    for pattern in CITATION_PATTERNS:
        for match in pattern.finditer(text):
            found.append((match.start(), match.group(0)))
    found.sort(key=lambda item: (item[0], item[1]))
    seen = set()
    ordered = []
    for pos, marker in found:
        key = (pos, marker)
        if key not in seen:
            seen.add(key)
            ordered.append(marker)
    return ordered


def find_numbers(text):
    numbers = []
    seen = set()
    for match in NUMBER_RE.finditer(text):
        token = match.group(0).replace(",", "")
        if token not in seen:
            seen.add(token)
            numbers.append(token)
    return numbers


def check(abstract_path, paper_path, budget_min, budget_max):
    if not os.path.isfile(abstract_path):
        print(f"error: abstract file not found: {abstract_path}")
        return 1
    if not os.path.isfile(paper_path):
        print(f"error: paper file not found: {paper_path}")
        return 1
    with open(abstract_path, "r", encoding="utf-8", errors="replace") as fh:
        abstract = fh.read()
    with open(paper_path, "r", encoding="utf-8", errors="replace") as fh:
        paper = fh.read()
    word_count = len(abstract.split())
    if word_count < budget_min:
        budget_status = "below"
    elif word_count > budget_max:
        budget_status = "above"
    else:
        budget_status = "within"
    citations = find_citations(abstract)
    abstract_numbers = find_numbers(abstract)
    paper_numbers = set(find_numbers(paper))
    missing_numbers = [n for n in abstract_numbers if n not in paper_numbers]
    lower = abstract.lower()
    method_signal = "present" if any(term in lower for term in METHOD_TERMS) else "missing"
    result_signal = "present" if any(term in lower for term in RESULT_TERMS) else "missing"
    print(f"abstract_file: {abstract_path}")
    print(f"paper_file: {paper_path}")
    print(f"abstract_word_count: {word_count}")
    print(f"budget_min: {budget_min}")
    print(f"budget_max: {budget_max}")
    print(f"budget_status: {budget_status}")
    print(f"citations_found: {len(citations)}")
    for marker in citations:
        print(f"CITATION marker={marker}")
    print(f"numbers_total: {len(abstract_numbers)}")
    print(f"numbers_missing_from_paper: {len(missing_numbers)}")
    for number in missing_numbers:
        print(f"NUMBER_MISSING value={number}")
    print(f"move_method_signal: {method_signal}")
    print(f"move_result_signal: {result_signal}")
    ok = (
        budget_status == "within"
        and len(citations) == 0
        and len(missing_numbers) == 0
        and method_signal == "present"
        and result_signal == "present"
    )
    print(f"verdict: {'PASS' if ok else 'REVIEW'}")
    return 0


def make_fixture(base):
    os.makedirs(base, exist_ok=True)
    paper = (
        "Morning light and alertness in office workers\n"
        "Data were collected in 2020 from two sites.\n"
        "Background. Low alertness in the morning is a common problem in office settings.\n"
        "Method. We recruited 120 office workers and assigned them to bright light or dim light for 8 days. Alertness was measured with a validated scale each morning.\n"
        "Results. The bright light group scored 42 points on average compared with 31 points in the dim light group, a difference of 11 points. Response time fell by 15 percent in the bright light group.\n"
        "Conclusion. Morning bright light improved alertness in this sample of office workers.\n"
        "Keywords: alertness, bright light, office workers\n"
    )
    abstract = (
        "Low morning alertness affects office work. This study tested bright light. The bright light group improved alertness by 87 percent and scored higher than controls (Smith, 2020). Morning bright light may help office workers stay alert.\n"
        "Keywords: alertness, bright light, office workers\n"
    )
    paper_path = os.path.join(base, "paper.txt")
    abstract_path = os.path.join(base, "abstract.txt")
    with open(paper_path, "w", encoding="utf-8") as fh:
        fh.write(paper)
    with open(abstract_path, "w", encoding="utf-8") as fh:
        fh.write(abstract)
    print(f"fixture_paper: {paper_path}")
    print(f"fixture_abstract: {abstract_path}")
    return 0


def main():
    args = sys.argv[1:]
    if len(args) == 2 and args[0] == "--make-fixture":
        return make_fixture(args[1])
    budget_min = 150
    budget_max = 250
    positional = []
    i = 0
    while i < len(args):
        if args[i] == "--budget-min" and i + 1 < len(args):
            try:
                budget_min = int(args[i + 1])
            except ValueError:
                print("error: --budget-min needs an integer")
                return 2
            i += 2
        elif args[i] == "--budget-max" and i + 1 < len(args):
            try:
                budget_max = int(args[i + 1])
            except ValueError:
                print("error: --budget-max needs an integer")
                return 2
            i += 2
        else:
            positional.append(args[i])
            i += 1
    if len(positional) != 2:
        print("usage: abstract_check.py ABSTRACT_FILE PAPER_FILE [--budget-min N --budget-max N]")
        print("       abstract_check.py --make-fixture DIR")
        return 2
    return check(positional[0], positional[1], budget_min, budget_max)


if __name__ == "__main__":
    raise SystemExit(main())

Versions

v1.0.02026-10-21 · 0 installs · Ed25519-signed./install.sh abstract-writing-basics 1.0.0bundle (.zip)

publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e

Ratings

No ratings yet — be the first to rate it.