synthesis-decision-packet · diff
v1.8.0 to v2.0.0
32 added, 313 removed. Audit A to A.
---
name: synthesis-decision-packet
- description: Collect many parallel decisions from a principal in one sitting instead of one per turn. Generates a self-contained HTML packet — one row per decision carrying the item, the agent's recommendation, the reasoning, and a link — with buttons labeled by what pressing them does, the consequence under each button, a per-row note box, local persistence, and a paste-able summary the principal returns in a single message; files the spec, the page, and the returned rulings in the owning project's resources/artifacts/ so every agent on the project can read them. Use when you owe five or more decisions of the same shape; when a review, migration, upgrade, triage, or backlog pass has produced a list someone must rule on; or when a per-item conversation is burning round-trips.
+ description: "Collect five or more parallel decisions from a principal in one sitting: a self-contained HTML packet with a recommendation per row, consequence-labeled buttons, notes and a paste-back summary, filed in the project. Use when a review, migration, upgrade, triage or backlog pass needs rulings."
license: "Apache-2.0"
depends_on: []
metadata:
author: "Rajiv Pant"
- version: "1.8.0"
+ version: "2.0.0"
+ format: v5
source_repo: "github.com/synthesisengineering/synthesis-skills"
source_type: "public"
---
# Decision Packet
- **Version 1.7.0** (2026-09-26)
-
When N unresolved decisions belong to the principal, collecting them one at a time
scales poorly. The measurement that produced this skill is blunt: **26 rounds of per-item
conversation produced 0 of 30 decisions. One packet produced 30 of 30, in one pass, in one
paste.**
- | Shape | Why it fails |
- |---|---|
- | One question per turn | N round-trips. This is what cost 26 rounds. |
- | One long prose report | The principal holds thirty judgments in their head, keeps their place, and composes a reply that re-identifies each item. |
- | A table in chat | Readable, not operable. Nowhere to record a decision, no state if they stop halfway. |
- | A form that submits somewhere | Needs a backend, and the reply still has to get back to the agent. |
-
- The failure is not that the principal lacks information. **The medium collects no structure**, so
- the burden of structuring the response falls on the person, every time, for every item.
-
**The property to preserve above all others: the principal's cost scales with the number of
*sittings*, not the number of *items*.**
+ ## Binding rules
+
+ Rules 1 to 6 are the six load-bearing properties in their original order, so "the sixth load-bearing property" still resolves. Later rules come from the other sections; new rules are appended.
+
+ 1. **Self-contained rows:** item, recommendation, reasoning, link. The principal never leaves the packet to decide.
+ 2. **Mark the recommendation on the control; never pre-select it.** A packet that opens decided reports decisions nobody made.
+ 3. **A free-text note box on every row,** beside the buttons. Never force the principal into your option set.
+ 4. **Local persistence keyed by exact spec and item.** A change anywhere in the spec starts fresh choices.
+ 5. **The packet generates the paste-able summary,** so the person never composes the structure.
+ 6. **File in the owning project's `resources/artifacts/`, never only as a chat artifact:** `build_packet.py --file-into` files spec and page; `record_rulings.py` files the rulings.
+ 7. **Only decisions that belong to the principal.** Execute constraint-determined choices and resolve delegated technical choices yourself.
+ 8. **The reader contract: a packet is a stranger-read document.** Name the `audience`, use plain labels, state `impact` both ways, label options by what pressing them does, gloss terms of art, and build every packet for a principal with `--strict-reader`.
+ 9. **Generate from a data array; never hand-author rows or hand-edit generator output.** The context doctor fails unverifiable packet pages.
+ 10. **Surface disagreement and prior positions with exact sources; never converge first,** and recommend against your own prior work where that is true.
+ 11. **Open every generated packet before handing it over.** Both permanent defects were found by loading the page, not reading the source.
+ 12. **A recorded ruling authorizes nothing** (`authorization.granted: false`). The action owner checks principal, scope and exact target before acting.
+ 13. **Show the complete material** for correspondence, code, images, media or documents in `review_assets`; external references are never fetched.
+
+ ## Contents
+
+ - [references/authoring.md](references/authoring.md): the six properties in full, review material, prior positions, content requirements, the reader contract. Read it before writing any spec.
+ - [references/filing-and-authority.md](references/filing-and-authority.md): the commands, filing, recording rulings, schema-2 records, provenance and action authority, retiring a packet. Read it when generating, filing or recording.
+ - [references/enforcement.md](references/enforcement.md): integrity markers, what the generator refuses, the two permanent fixtures. Read it when a build is refused, the doctor flags a packet, or before changing the generator.
+ - [references/review-assets.md](references/review-assets.md): the review-asset contract. Read it before constructing `review_assets`.
+ - [references/worked-example.md](references/worked-example.md): a complete spec, the filed copies, the paste and its rulings file. Read it before your first packet.
+ - [references/background.md](references/background.md): the origin measurement, relationship to autopilot, adversarial review and the handoff queue (`synthesis-project-management/scripts/handoff.py`), changelog, related skills. Read it when choosing between this skill and a neighbor.
+ - [references/coverage-map.md](references/coverage-map.md): where each part of the 1.8.0 text now lives.
+ - When to use it, When NOT to use it, Use: below.
+
## When to use it
First apply [decision ownership](../synthesis-thinking-framework/references/decision-ownership.md).
Packets collect decisions that actually belong to the principal. Execute choices
already determined by constraints and resolve delegated technical choices without
putting them back into a questionnaire. Honor an explicit supervised review request.
Existing user grants remain usable within their scope; the new record format does
not require the user to repeat them.
Load when you owe your principal **five or more parallel decisions of the same shape**, each
needing supporting context, where you have a defensible recommendation per item and the decisions
matter enough to deserve attention but are too numerous for per-item conversation.
Natural fits: review findings to fix or waive · dependency bumps to take or hold · drafts to
publish, edit, or kill · files to migrate or leave · flaky tests to quarantine or fix · features
to build now, later, or never · candidates to advance on a defined rubric.
## When NOT to use it
- **Fewer than about five decisions.** Just ask in chat. The generator refuses below five without
`--allow-small`.
- **The decisions are not parallel in shape.** A packet of unlike questions is a form, and a form
is worse than a conversation.
- **You have no recommendation per item.** Then the packet is a questionnaire and *your analysis
is not finished*. Do the analysis. The generator refuses a packet where no row carries a
recommendation.
The failure mode of a good pattern is over-application. These three limits are the skill.
- ## The six load-bearing properties
-
- Requirements, not suggestions. Each is why it worked.
-
- 1. **Self-contained rows.** Item, recommendation, reasoning, and a link to the underlying
- artifact. The principal never leaves the packet to decide.
- 2. **The recommendation is marked on the control, not merely stated in prose.** Agreeing costs one
- click. The agent's judgment does work instead of being described.
- **It is deliberately not pre-*selected*** — a packet that opens fully decided cannot distinguish
- "I agreed" from "I never looked", and would report decisions nobody made.
- 3. **A free-text box on every row, beside the buttons.** Never force a principal into your option
- set. In the origin run one such note, on one row, carried information no button could have.
- 4. **Local persistence keyed by exact spec and item.** Thirty decisions is more than one sitting for anyone doing
- it properly. Storage access is guarded: where a browser blocks it the packet still works and
- says so in the summary. A change anywhere in the canonical spec starts fresh choices;
- an old click cannot silently acquire a new meaning.
- 5. **A paste-able summary the tool generates.** *This is the property that closes the loop.* The
- structure you need is produced by the packet, not composed by the person.
- 6. **Filed in the owning project, never only as a chat artifact.** The spec, the generated page,
- and the rulings the principal pastes back live in the owning synthesis project's
- `resources/artifacts/` as `<date>-<slug>-spec.json`, `<date>-<slug>.html`, and
- `<date>-<slug>-rulings.json`. Later versions receive a digest suffix; earlier
- bytes are preserved. A packet that exists only as a published Claude artifact is
- readable by one client in one conversation; the project directory is readable by every agent
- working the project, ChatGPT Codex included, and by the next session after this one is gone.
- `build_packet.py --file-into` files the first two; `record_rulings.py` files the third.
-
- ## Inspect the actual material
-
- For decisions about correspondence, code, images, media or documents, include
- structured `review_assets` plus the exact row `revision` and `delivery` envelope.
- Read [the review-asset contract](references/review-assets.md) before constructing
- these fields. It defines the closed data types, integrity checks, safe Markdown,
- byte limits, copy/download behavior and unavailable-material refusal. Put the
- complete artifact there; keep context and recommendations in their own fields.
-
- The page renders material before the recommendation. The existing canonical
- spec binds every content and delivery field to saved choices and returned
- rulings. External references are never fetched automatically. Unavailable
- material stays visible but cannot acquire a selected ruling. A byte-perfect
- record still grants no authority and does not supersede an existing valid grant.
-
- ## Prior positions and contrary evidence
-
- Before recommending a change, read the principal's exact prior statements in the
- current task and selected durable record. A selected button never replaces an
- accompanying note. If an earlier position applies, each row must include
- `prior_position` with `statement` (exact words), `source_ref` (the actual evidence
- location), and `evidence_for` / `evidence_against` lists. Each evidence entry has
- `statement` and `source_ref`. Preserve original whitespace and wording; label an
- inference in the evidence statement instead of attributing it to the principal.
- An empty list means no evidence was recorded in this packet, not that none exists.
-
- The packet displays this block before context and recommendations and includes it
- in the spec-bound recorded ruling. A recommendation may disagree with the prior
- position when the evidence supports doing so; show the conflict. No prior
- position preselects a button or grants fresh execution authority. The new choice,
- free-text note and exact prior evidence remain separately recoverable. If the
- source is unavailable, report that missing input instead of inventing a position.
-
- ## Content requirements, which matter as much as the mechanics
-
- - **Name filters from the content, not from generic severity.** "Needs a fix", "We disagreed",
- "Ready as written", "Not yet decided" — so the principal picks their own path through the set
- rather than going 1 to N.
- - **Surface disagreement; never converge before the principal sees it.** Eight rows in the origin
- run showed two reviewers' conflicting verdicts and the principal broke all eight ties.
- Converging first would have shown a false consensus. Use the `disagreement` block.
- - **A summary band before the detail**, so the shape of the work reads in three seconds.
- - **Severity in form as well as words** — the coloured rail per row is how the eye finds exceptions
- while scrolling.
- - **Recommend against your own prior work where that is true**, including failed hypotheses. A
- packet that only argues one way is a sales document, and the buttons stop being trusted.
-
- ## The reader contract (v1.1.0) — comprehension is a load-bearing property
-
- The origin measurement has a dark twin, measured on the same principal on
- 2026-08-29: **a 15-row packet written in project-internal language collected
- 0 of 15 decisions.** Every mechanical property above worked — filters,
- persistence, marked recommendations — and none of it mattered, because the
- rows named things only the authoring session knew ("C1", "holdout",
- "quarantine", "protected strata"). The principal's verdict: "written in some
- alien or machine language." Structure without comprehension collects
- nothing. The packets that ran 30/30 were about things the principal already
- knew — articles, titles, links.
-
- So a packet is a **stranger-read document**, and authoring one starts where
- `synthesis-reader-briefing` starts: who reads this, what do they bring, what
- does it ask, what do they leave with. Then, per row:
-
- - **The label is plain language.** Internal IDs may appear as chips; they
- are never the name.
- - **Context says what the thing IS** in words the reader already has,
- before any result about it.
- - **An `impact` block states consequences, both ways** — what actually
- happens if they take the recommendation and if they don't, in outcomes
- the principal cares about (what ships, what it costs, what dies), never
- in internal treatment vocabulary. The generator renders it distinctly.
- - **Options are labeled by what pressing them does,** never by a bare
- acknowledgement. Measured on 2026-09-14: on a 9-row packet, three rows
- offering "Yes, do that" / "No" collected notes instead of decisions — the
- principal could not tell what each button would do to the thing in
- question, so the note boxes carried what the buttons should have. "Keep
- them on my phone" / "Take them off my phone" is the accepted form. The generator raises a
- READER finding for a bare label (yes, no, ok, okay, cancel, accept,
- decline, approve, reject, do it, go, stop, and their "Yes, do that" /
- "No thanks" forms, compared after trimming punctuation and case, and any
- of these padded with a stopword: "Accept it", "Approve this", "Do that")
- and for two labels in one option set that do not differ in a content word;
- `--strict-reader` refuses to build, naming the row, the labels, and the
- accepted form.
- - **The consequence sits on the button.** Each option button carries, under
- its label, what pressing it does: the option's own `consequence` when the
- spec gives one, otherwise `impact.accept` under the recommended option and
- `impact.decline` under every other one. The row's impact block stays as
- the row's summary; the text on the button is what the principal reads at
- the moment of choosing.
- - **Every surviving term of art gets a one-clause gloss** — inline on first
- use, or in the packet-level `glossary` band.
- - **`audience` names the reader.** One sentence. If you cannot write it,
- you do not know who the packet is for, and neither will they.
-
- `--strict-reader` makes the generator refuse a packet missing `audience` or
- per-row `impact`, or carrying option labels that name no consequence. **Use it
- for every packet handed to a principal.** The warnings print either way;
- strictness is the difference between a warning you read and a packet they
- cannot.
-
## Use
```bash
python3 scripts/build_packet.py --schema # the spec format
python3 scripts/build_packet.py spec.json -o packet.html --strict-reader
python3 scripts/build_packet.py spec.json --stdout # to a pipe
python3 scripts/build_packet.py spec.json --strict-reader \
--file-into PROJECT/resources/artifacts/ # + <date>-<slug>-spec.json, <date>-<slug>.html
python3 scripts/record_rulings.py paste.txt \
--spec PROJECT/resources/artifacts/<date>-<slug>-spec.json \
--file-into PROJECT/resources/artifacts/ # -> <date>-<slug>-rulings.json
```
-
- Write a JSON spec, generate, file, hand over the file. It is self-contained: no build step, no
- dependencies, no server. It opens from disk, over a local HTTP server, or published as an
- artifact, in light or dark, on a phone or a laptop.
-
- **File it, then hand it over.** `--file-into DIR` writes a dated copy of the spec and of the page
- into DIR after a successful build. DIR is the owning project's `resources/artifacts/` and must
- already exist: the generator refuses a missing directory rather than creating one where you did
- not mean. `--date YYYY-MM-DD` sets the date in the names (default: today). Publish the page as
- an artifact too if that is how the principal will open it; the filed copy is the one other
- agents read.
-
- **Record what came back.** Save the complete returned summary, including its final
- binding line. Run `record_rulings.py paste.txt --spec CURRENT-SPEC --file-into DIR`.
- The current spec is selected explicitly; omitting `--spec` works only when the
- directory has one matching candidate. Multiple versions require an explicit choice.
- The recorder refuses a changed spec, duplicate/missing/reordered rows, unknown
- option values, mismatched displayed text and malformed bindings before writing.
-
- Notes use LF line endings and omit trailing whitespace on each line, matching
- common clipboard and editor behavior. Leading indentation inside the note, word
- spacing and paragraph breaks remain significant. This tolerance applies only to
- notes; decisions, row labels and the current-spec binding remain exact. A refused
- paste names the affected row and field without repeating its note contents.
-
- Schema-2 records retain `packet`, `ruled_on`, `decided`, `total`, and per-row
- `id`, `label`, `choice_value`, `choice_label`, recommendation/bulk status and
- `note`. They add the complete canonical spec SHA-256, actual filed-spec byte
- digest, summary digest, claimed provenance and an explicit non-authorizing status.
- Canonical bytes are UTF-8 JSON with sorted object keys, compact separators and one
- final newline. Every field participates, including context, consequences and scope;
- duplicate JSON keys and nonfinite numbers are refused. Whitespace in an input JSON
- file does not change meaning; any change in its canonical content changes the binding.
-
- Identical imports are idempotent. A changed response receives a distinct filename;
- there is no destructive `--replace`. Preserve and commit the spec, page, original
- response and recorded versions in the owning project. An explicit `--out` path also
- preserves existing bytes; give a revised page a new path or use versioned filing.
-
- ## Provenance and action authority
-
- The parser records a response; it does not authenticate a principal or authorize
- an action. Every output has `authorization.granted: false` and
- `authentication: unverified`, including a byte-perfect synthetic or forged paste.
- This describes the tool's authority, not the validity of an existing user grant.
-
- When the trusted conversation supplies attribution, pass `--provenance FILE` with
- exactly five nonempty strings: `principal`, `source_ref`, `received_at` (an ISO
- timestamp with timezone), `scope`, and `authority_ref`. The recorder preserves
- these claims and the provenance-file digest as `claimed-unverified`; it cannot
- verify an identity merely because a local file names one. Missing provenance stays
- `unattributed-paste`, with unknown fields explicit. `ruled_on` is the supplied
- record date, not proof of when the principal acted.
-
- The existing action owner reads the authentic source and checks the principal,
- current scope, exact target/payload, validity and restrictions before acting.
- Put the concrete target, payload revision and decision boundary in the displayed
- `scope` or row context. A technical selection or preference cannot authorize a
- new deployment, disclosure, spend or other consequential effect.
-
- Historical text remains readable with `--legacy-unbound --stdout`; historical
- records remain untouched. This read does not produce a new bound filing. An old
- record's lack of a schema-2 binding neither authenticates it nor erases authority
- already supplied in a trusted user instruction. Resolve that authority through
- its actual owner and source.
-
- ## Enforcement (v1.5.0) — generator output is verified, not trusted
-
- Every page `build_packet.py` emits carries an integrity marker pinning its
- embedded payload (`synthesis-decision-packet spec-sha256`) and a separate
- canonical input-spec digest used by the summary and browser persistence.
- These are integrity checks, not proof of generator authorship or user identity.
- `record_rulings.py` validates the current spec and every selected value before
- filing. The context doctor's existing `skill-outputs` check fails
- any packet page under a project's `resources/artifacts/` that is not
- verifiable generator output: unmarked with no filed rulings is a defect
- (rebuild with the generator or remove it); a marker that disagrees with
- the embedded spec is a defect (never hand-edit generator output); a
- closed record (unmarked but ruled) warns. The rule ships on every
- machine with install, upgrade, and doctor — it is not a local note.
-
- **Generate from a data array; never hand-author rows.** Thirty hand-written blocks drift. One
- array with a render loop cannot. That is the whole reason this is a generator rather than a
- template.
-
- The generator validates before it emits and refuses to build a broken packet: duplicate ids
- (they key persistence), ids with leading, trailing or doubled whitespace or a line break (the
- summary line is `id label`, split on its first double space), a line break in the title, a row
- label or an option label (the paste is one line per field, and a packet that builds must file),
- an option set with fewer than two options (a one-button set records no decision), two options
- in one set sharing a value or a value that is not a non-empty string (the value keys the pressed
- state and the summary label through a DOM dataset, which stores strings), a recommendation
- outside its own option set, a packet with no recommendations at all, malformed option sets at
- the packet level or on a row, malformed disagreement, impact, or glossary blocks.
-
- ## Two defects that are permanent fixtures
-
- Both shipped in the reference implementation; one reached the principal in real use. They are
- regression-tested in `scripts/test_build_packet.py`.
-
- - **Charset in the first bytes.** Without `<meta charset="utf-8">` ahead of everything, typographic
- punctuation renders as mojibake when served over a plain local HTTP server. *Found by loading
- the page, not by reading the source* — which is why the fixture asserts on bytes and why you
- should always open a generated packet before handing it over.
- - **The copy control must never fail silently.** `navigator.clipboard.writeText` is blocked inside
- a sandboxed artifact iframe with no `clipboard-write` permission; in the origin run the button
- did nothing and *said* nothing, and the principal worked around it by hand. The required order:
- **select the textarea first** so a manual ⌘C/Ctrl+C always works, say something *synchronously*,
- then `document.execCommand("copy")` (which does work in sandboxed iframes), then the async API,
- and report honestly which path succeeded. A third fixture was added on 2026-08-28 after driving
- the real button surfaced an empty status line while the async promise was unsettled — an
- unbounded silent interval is the same defect as a permanent one.
-
- ## Relationship to other skills
-
- - **`synthesis-autopilot`** should *call* this for actual principal-owned gates, not
- reimplement it or return delegated decisions to the user. A
- round-trip budget only means something if one round-trip can carry many decisions.
- - **The adversarial review family** — this is where an engagement surfaces its unresolved
- disagreements. Pair it with a status for findings that are not open, not conceded, and not the
- agents' to close.
- - **The handoff queue** (`synthesis-project-management/scripts/handoff.py`) moves work *between
- agents*. The decision packet moves decisions *between agent and principal*. Together they are
- the two directions that stop routing everything through a person as the transport layer.
-
- ## Changelog
-
- - **1.5.0 (2026-09-20)** — Enforcement: the generator emits a provenance marker pinning the embedded spec; `record_rulings.py` refuses pastes with no filed `-spec.json`; the context doctor's `skill-outputs` check fails unmarked live packets as defects. After a session hand-authored two packets.
- - **1.4.0 (2026-09-14)** — Option labels must name consequences: READER findings for bare
- acknowledgements and for two labels that do not differ in a content word, fatal under
- `--strict-reader`, with the row, the labels, and the accepted form in the message. Each option
- button carries its consequence under the label (`consequence` per option, else the row's
- `impact` mapped onto the buttons). Sixth load-bearing property: packets and rulings are filed
- in the owning project's `resources/artifacts/` — `--file-into`, `--date`, and the new
- `record_rulings.py`, whose parser is pinned to the summary format the page emits. Per-row
- option sets are validated like the packet-level set; ids with edge or doubled whitespace are
- refused. The worked example's options now name outcomes. Repair round the same day: a line
- break in the title, a row label, an option label or an id is refused, so a packet that builds
- can always be filed; a one-button option set, two options sharing a value, and a non-string
- value are refused; an acknowledgement padded with a stopword ("Accept it", "Do that") is
- caught; `record_rulings.py` counts the title underline in UTF-16 units as the page does,
- reads whitespace-only lines as blank, and every refusal quotes the text it received; the
- worked example's paste and rulings file match its two-row spec, and a fixture holds them to
- the parser.
- - **1.3.0 (2026-08-29)** — Ids must be non-empty strings: JSON `1` and `"1"` become the same
- localStorage key, so JSON-distinct ids could share saved state.
- - **1.2.0 (2026-08-29)** — The relationship section names the handoff queue as the
- agent-to-agent transport.
- - **1.1.0 (2026-08-29)** — The reader contract: `audience`, `glossary`, per-row `impact`, and
- `--strict-reader`, after a 15-row packet in project-internal language collected 0 of 15.
- - **1.0.0 (2026-08-28)** — First release: the packet, the five properties, and the two
- permanent fixtures.
-
- ## Related
-
- - `references/worked-example.md` — a complete spec, the filed copies, the paste that comes
- back, and the rulings file it becomes.
- - `synthesis-reader-briefing` — the four questions every packet is authored
- against; the reader contract above is that skill applied to this medium.
- - `synthesis-thinking-framework` — for deciding *what* to recommend before you build the packet.
- - `synthesis-anti-shortcuts` — a packet whose rows hedge instead of recommending is the
- asking-as-shortcut costume in a new medium.
-
- ## Retiring historical interfaces
-
- Read [artifact succession](../synthesis-context-lifecycle/references/artifact-succession.md)
- before retiring a packet or reconciling transferred decisions. `context_edit.py`
- owns exact-byte custody and the existing multi-file transaction; the packet owner
- refuses filing a retired exact spec or equivalent payload as a new live interface.
- A rulings filename does not establish closure. Preserve actual answers and every
- unanswered or unverified obligation; archive and transfer grant no new authority.