Development record
The scribe's context is assembled and its output is ingested by a verb, and the record can show that no session held both a reading and the ledger
Typed links: builds_on itd-188 (the scribe definition), itd-183 (the assembler and manifest idiom), itd-180 (the disposition validator), itd-185 (the ingest idiom); refines itd-188 (the protocol becomes a verb).
Press Release
The scribe is a verb, not a protocol.
abcd scribe assemblebuilds the scribe's context from the ledger's allow list and nothing else, and writes a manifest of what it passed.abcd scribe ingest --scribe-json <path>validates what the scribe returned, transcribes dispositions, admissions and surprises through the same validators the capture verbs use, and refuses a payload that authors anything. Every assembly, reading or scribe, carries a per-run stamp naming its kind and its run, and the session store gains a check that reports a retained transcript carrying both stamps of one run. Until now the scribe's inverse access rule was held by its definition alone; now it is held by construction, and a reader can check it.
"The reading session and the ledger session were always meant to be two sessions," said an AI/agent researcher who maintains the ledger with machine help. "I want the tool to build the scribe's context so I cannot accidentally hand it the tree, and I want the transcript store to be able to say whether any session held both."
Why This Matters
itd-188 specifies the scribe as a definition with an inverse access rule, hand-run until an ingest verb lands, with two acceptance criteria: the scribe's assembled context contains ledger content and no shipped-tree material, and each reading run and each scribe run is a distinct retained session with no session holding both. Iteration 1 shipped the definition with the access rule stated in its allow list, and its own text says "There is no ingest verb". The fidelity verdict found both criteria met by declaration rather than by mechanism: no assembler runs for the scribe, and session retention cannot show that no session held both.
The scribe exists so that machine assistance in maintaining the ledger remains available without any context holding both ledger content and a reading. That property is the mirror of the read block, and the read block is held by an assembler, a manifest and an eval. The scribe deserves the same three things, or its half of the wall is an assertion.
Decisions flagged for the maintainer
Both were adopted by the maintainer on 2026-09-02 as adr-2609021016275803, which states the mechanism behind brief invariant 15 and amends the invariant to name the stamp and the check.
- The invariant exists, and the ADR states its mechanism. Brief invariant 15 states that no session holds both a reading and the ledger; the ADR states the per-run context stamp and the session check as the mechanism, and the invariant's amendment names them. The per-run stamp is a change to the reading bundle's shape and moves the assembler version, as itd-199 did for the bundle's selector block.
- A new top-level verb.
scribeis a verb of its own rather than a sub-verb ofreading, because the two contexts must never share a front door. The surface coverage and index gates require its plugin page, its brief surface chapter row and its entry in the released surface snapshot, all in scope below.
What's In Scope
abcd scribe assemble --run <rdg-N>, which requires an ingested run and builds the scribe's context from the ledger's directories as the record declares them (the readings, dispositions, admissions, surprises and reframes stores, and the issue ledger's three status directories), together with the researcher's supplied dispositions text, and writes a manifest of what it passed by path. The run's reading records come from the store, not from a raw reading output supplied twice. It refuses any path outside the allow list by construction, including the shipped tree, the brief, the intents, the specs, the decisions and the session-transcript store.abcd scribe ingest --scribe-json <path>, which validates the scribe's four outputs (records to file, fidelity flags, outstanding items, refusals), writes dispositions, admissions and surprises through the existing validators and stores, and refuses a payload carrying a field the scribe may not author: a ground, a resolution, a disposition the researcher did not supply.- Per-run context stamps. A reading bundle and a scribe context each carry a stamp naming the kind of session, the run and a digest of the context, matched exactly, so a session that merely reads the documentation carries none.
abcd historygains a check, run byhistory listand by the smoke lane, that reports a retained transcript carrying both stamps of one run by name, reports which runs it saw when none does, and says the property is unobserved when the store holds no transcript that carries any stamp. - The scribe's manifest is parked in the local tier at assembly and promoted beside the run at ingest, inside the read block, so the next reading cannot see it.
- Surfaces: the plugin page
commands/scribe.md, a row in the brief's surface chapter index, the released surface snapshot, and the definition's Delivery section rewritten to name the verb.
What's Out of Scope
- Any judgement by the verb about the content transcribed. The scribe authors nothing and the verb authors nothing.
- A change to the scribe's allow list. The rule stays the definition's; the verb enforces it.
- Redaction policy. Records written through the capture validators inherit the redaction those verbs already apply.
Mechanism
We expect an assembler with an allow list to hold the inverse access rule for the same reason the reading assembler holds the read block: positive inclusion excludes by default, and a manifest makes the exclusion checkable. We expect the session-kind stamp to reach a retained transcript because a session reads its bundle or its context through a tool whose result the transcript retains, so the stamp text is in the transcript when the content was. It fails where a host hands a session content the transcript does not retain, which the scope conditions disclose and the check reports as unobservable rather than as clean.
Scope Conditions
- The scribe's context is assembled from committed ledger content and from supplied text. A scribe that needs uncommitted ledger content is outside this scope.
- The session check reads the native session-transcript store and can only see what a host retained. Where the host assembles context before anything is retained, the check reports that it cannot observe the property, and the definition's protocol remains the gate.
- The verb transcribes into the stores that exist. A record family the scribe is asked to write that has no store is refused, never invented.
Acceptance Criteria
- Given an ingested run and supplied dispositions, when
scribe assembleruns, then its context contains the run's reading records from the store, the other ledger content, and the supplied text, its manifest names every path passed, and no path from the shipped tree, the durable record outside the ledger, or the transcript store appears. - Given a scribe payload carrying a disposition the researcher supplied, when
scribe ingestruns, then a disposition record exists through the disposition validator and nothing else was written. - Given a scribe payload carrying a ground or a resolution the researcher did not supply, when
scribe ingestruns, then it refuses and names the field. - Given a scribe payload naming an item it was given no disposition for, when
scribe ingestruns, then the item appears in the outstanding report and no record was written for it. - Given a retained transcript carrying a reading stamp and a scribe stamp of one run, when the session check runs, then it reports the transcript by name.
- Given two retained transcripts, one carrying each stamp of one run, when the session check runs, then it reports that no retained transcript carries two stamps of one run.
- Given an empty store, when the session check runs, then it reports that the property is unobserved rather than clean.
- Given the read-block eval over a repository holding a scribe manifest, when it runs, then the manifest does not reach any reading.
Prior Art
- itd-188 and its spec (the definition), itd-183 (the assembler and manifest idiom), itd-180 (the disposition validator), the
abcd historystore. - The cold-reading rulings of 2026-08-28 in the decision log.
Open Questions
None. The flagged decisions are adopted as adr-2609021016275803.
Audit Notes
Fidelity review — receipt rcp-6654dbf923b3 (verifier intent-auditor claude-fable-5-1).
Provenance: intent-auditor@claude-fable-5-1 · rubric_hash sha256:effa65b3e9e88ff29433b443ec2be159522a8b0b71cf1434526514aa61edb13e · prompt_hash sha256:34a24bad1b9e20b4164136080330b158cd3074b20815d120269663ffd6d5dd36 Input attestations: diff:internal/core/scribe/, internal/core/history/separation.go, internal/core/sessionkind/, internal/surface/cli/history.go, commands/scribe.md, evals/{coldreading_fixture,smoke}_test.go at 4001d807 (git ls-tree -r)@sha256:965e74af30dcb1a630023cf07598e711ecfe826ea8d086e36692a106176ea973;
Acceptance rollup: MET 8 · MET_WITH_CONCERNS 0 · NOT_MET 0 · INCONCLUSIVE 0
Per-criterion verdicts:
- ac-1 — MET: Assemble requires an ingested run, builds the context from the ledger directories plus the supplied text and writes a manifest of every path passed; the test plants sentinels in the shipped tree, the durable record and the transcript store and asserts none reaches the context, that the run's records come from the store, and that the manifest names every path evidence: internal/core/scribe/assemble.go:61 — "func Assemble(req AssembleRequest) (AssembleResult, error) {" evidence: internal/core/scribe/scribe.go:81 — "func AllowList() []string {" evidence: internal/core/scribe/assemble_test.go:41 — "func TestScribeContextIsLedgerAndSuppliedTextOnly" evidence: internal/core/scribe/assemble_test.go:82 — "func TestScribeContextCarriesTheRunsRecordsFromTheStore" evidence: internal/core/scribe/assemble_test.go:107 — "func TestScribeManifestNamesEveryPathPassed" evidence: internal/core/scribe/assemble_test.go:186 — "func TestScribeAssembleRefusesAnUncommittedRun"
- ac-2 — MET: Ingest writes a supplied disposition through capture.Disposition, the existing validator, and the test asserts exactly one disposition file carrying the supplied ground with the admissions and surprises stores untouched evidence: internal/core/scribe/ingest.go:223 — "r, err := capture.Disposition(capture.DispositionRequest{" evidence: internal/core/scribe/ingest_test.go:99 — "func TestScribeIngestWritesASuppliedDisposition"
- ac-3 — MET: a key outside the closed output shapes (a resolution on a disposition) is refused by name with the item it was on, a ground the supplied text does not carry is refused naming grounds and the item, on dispositions and admissions alike, and a disposition for an item the text never names is refused; every case proves the ledger unchanged evidence: internal/core/scribe/ingest.go:335 — "func refuseKeys(where string, obj map[string]json.RawMessage, allowed map[string]bool) error {" evidence: internal/core/scribe/ingest_test.go:139 — "func TestScribeIngestRefusesAnAuthoredField" evidence: internal/core/scribe/ingest_test.go:172 — "func TestScribeIngestRefusesAnUnsuppliedGround" evidence: internal/core/scribe/ingest_test.go:196 — "func TestScribeIngestRefusesAnUnsuppliedAdmissionGround" evidence: internal/core/scribe/ingest_test.go:212 — "func TestScribeIngestRefusesAnUnsuppliedDisposition"
- ac-4 — MET: an all-outstanding payload returns both items in the result's outstanding list with the ledger digest unchanged, an item both dispositioned and outstanding refuses, and an outstanding item of another run refuses by name; a silent item is refused too evidence: internal/core/scribe/ingest_test.go:230 — "func TestScribeIngestReportsOutstandingAndWritesNothing" evidence: internal/core/scribe/ingest_test.go:264 — "func TestScribeIngestRefusesASilentItem"
- ac-5 — MET: SessionSeparation reads the store's records and reports a transcript carrying a reading stamp and a scribe stamp of one run as a Violation naming the session and file; the test asserts the exact violation, and history list prints the summary while the CLI test exits one on a breach evidence: internal/core/history/separation.go:62 — "func SessionSeparation(repoRoot, rootSHA string) (SeparationReport, error) {" evidence: internal/core/history/separation_test.go:96 — "func TestSeparationNamesATranscriptCarryingBothStampsOfOneRun" evidence: internal/surface/cli/history.go:172 — "if rep, err := history.SessionSeparation(repoRoot, rootSHA); err == nil {" evidence: internal/surface/cli/history_separation_surface_test.go:47 — "func TestHistorySeparationNamesABreachAndExitsOne"
- ac-6 — MET: two transcripts each carrying one stamp of one run yield no violation, two transcripts and two stamped counted, the run named, and a summary saying no retained transcript carries two stamps of one run; a reading stamp and a scribe stamp of two different runs are likewise no violation evidence: internal/core/history/separation_test.go:139 — "func TestSeparationReportsNoTranscriptCarryingTwoStamps" evidence: internal/core/history/separation_test.go:119 — "func TestSeparationIgnoresTwoStampsOfTwoRuns"
- ac-7 — MET: an empty store, and a store whose transcripts carry no stamp, set Unobserved with a reason rather than reporting clean; the CLI renders it and the smoke lane runs history separation over an empty store expecting exit 0 evidence: internal/core/history/separation.go:115 — "rep.Unobserved, rep.Reason = true, reasonNoTranscript" evidence: internal/core/history/separation_test.go:168 — "func TestSeparationReportsAnEmptyStoreAsUnobserved" evidence: internal/surface/cli/history_separation_surface_test.go:19 — "func TestHistorySeparationRendersUnobservedOnAnEmptyStore" evidence: evals/smoke_test.go:90 — "{[]string{"history", "separation"}, true, true},"
- ac-8 — MET: the manifest is promoted last through reading.WriteRunArtefact into the run's directory, which the exclusion floor denies to every assembly, and the read-block eval fixture plants a promoted scribe-manifest.json beside a run as EXHAUST that no reading may carry; the tagged eval passes at BASE evidence: internal/core/scribe/ingest.go:259 — "rel, err := reading.WriteRunArtefact(req.RepoRoot, out.Run, ManifestFileName, m)" evidence: evals/coldreading_fixture_test.go:230 — ""repo:.abcd/development/readings/rdg-2608300900000001/scribe-manifest.json"," evidence: internal/core/scribe/ingest_test.go:395 — "func TestScribeIngestPromotesTheManifestLast"
Gap audit:
- honoured:
- the allow list is derived from the ledger's declared directories and fails closed evidence: internal/core/scribe/assemble_test.go:171 — "func TestAllowListIsDerivedFromLedgerDirs" evidence: internal/core/scribe/assemble_test.go:147 — "func TestAssertAllowListFailsClosed"
- a reading bundle and a scribe context each carry a per-run stamp matched exactly, and a session that merely reads the documentation carries none evidence: internal/core/scribe/assemble_test.go:208 — "func TestScribeContextCarriesThePerRunStamp" evidence: internal/core/reading/manifest_test.go:39 — "func TestBundleCarriesTheReadingStampOfItsRun" evidence: internal/core/sessionkind/sessionkind_test.go:18 — "func TestStampsArePerRunAndMatchedExactly" evidence: internal/core/history/separation_test.go:79 — "func TestARecordWithoutContextStampsStillParses"
- ingest proves the context hash and lands admissions and surprises through the capture stores, meeting the ordering gate evidence: internal/core/scribe/ingest_test.go:285 — "func TestScribeIngestProvesTheContextHash" evidence: internal/core/scribe/ingest_test.go:443 — "func TestScribeIngestWritesAdmissionsAndSurprises" evidence: internal/core/scribe/ingest_test.go:379 — "func TestScribeIngestRefusesBeforeTheComparativeRun"
- the manifest is parked at assembly and promoted only after every record landed, write-once beside the run evidence: internal/core/scribe/ingest.go:256 — "if len(res.Landed()) == 0 {" evidence: internal/core/reading/ingest_comparative_test.go:241 — "func TestWriteRunArtefactIsWriteOnce"
- surfaces: the plugin page, the brief's surface index row and the definition's Delivery section name the verb
evidence: commands/scribe.md:1 — "scribe"
evidence: .abcd/development/brief/04-surfaces/README.md:47 — "| 32 |
/abcd:scribe| shipped |" evidence: internal/core/lint/scribecontract_test.go:621 — "func TestScribeDeliveryNamesTheVerb"
- diverged: (none)
- missing: (none)
Scope-condition dispositions:
- cond-2609020626046719 — survived: assemble refuses a run without its commit marker and reads the ledger's committed directories plus the supplied text; nothing reads uncommitted ledger content evidence: internal/core/scribe/assemble_test.go:186 — "func TestScribeAssembleRefusesAnUncommittedRun" evidence: internal/core/scribe/assemble_test.go:41 — "func TestScribeContextIsLedgerAndSuppliedTextOnly"
- cond-2609020626048270 — survived: the check reads the native store's metadata only and reports unobserved with a reason when no transcript or no stamped transcript is retained, rather than clean evidence: internal/core/history/separation.go:117 — "rep.Unobserved, rep.Reason = true, reasonNoStamp" evidence: internal/core/history/separation_test.go:195 — "func TestSeparationReadsMetadataOnly"
- cond-2609020626049512 — survived: the payload is a closed shape: dispositions, admissions and surprises land through the existing stores and any other key is refused by name, so a family with no store cannot be written evidence: internal/core/scribe/ingest.go:335 — "func refuseKeys(" evidence: internal/core/scribe/ingest_test.go:139 — "func TestScribeIngestRefusesAnAuthoredField"
Grounds
- pursued: we expect an allow-list assembler with a manifest to hold the scribe's inverse access rule by construction as the reading assembler holds the read block, and a session-kind stamp to make the two-sessions property observable in the transcript store; a scribe context reaching shipped-tree material, or a stamp that never lands in a retained transcript, would show it wrong
Frontmatter
| id | itd-2609020625402599 |
|---|---|
| type | intent |
| lifecycle | shipped |
| kind | standalone |
| severity | minor |
.abcd/development/intents/shipped/itd-2609020625402599-the-scribe-s-context-is-assembled-and.md
open on GitHub ↗ · commit history ↗
References4
- itd-180A cold reading's findings land as reading records, and the researcher's response is a separate disposition record — two acts, two writes, never collapsedbuilds on
- itd-183The cold reading sees exactly what the assembler passes — positive inclusion, field projection, and a per-run manifestbuilds on
- itd-185One ingest verb validates every cold-reading output — including what the reading was licensed to produce, not only what it sawbuilds on
- itd-188Machine assistance in maintaining the ledger, without any context that holds both ledger content and a readingbuilds on
Referenced by3
- adr-2609021016275803No session holds both a reading and the ledger, and a per-run context stamp is how the record shows itrelated
- iss-2610262045054936itd-187 holds two assemblies byte-identical with only the manifest excluded; itd-2609020625402599 puts a per-run stamp naming the run in every bundlerelated
- spc-2609020626045177The scribe verb: an allow-list assembler, an authoring-refusing ingest, and a per-run context stamp the transcript store can checkimplemented by
mentions4
- dcn-26090200000000202026-09-02 — The entailment reading is handed the OBJECT SET's drafts and planned intents, and none by default (maintainer, at the Phase A review). The framework's section 13 fixes the object set as "Iteration 1's shipped state and claim record" — the fifteen workstream intents, which the record extends by the ten Iteration 2 intents — and the readings companion's section 6.2 makes drafts and planned intents ADMISSIBLE at that position: admissible is a permission, the object set is the scope, and the assembly was handing every draft and planned intent in the repository (147 projected intents on the Phase A tree) because a record row is admitted whole when the object set names none of its records. The drafts and planned rows therefore narrow by the entry's record list exactly as the shipped row does, so a draft or planned intent travels only when the committed entry names it; the companion's admissibility becomes a switch on the entailment entry — `admit_drafts_and_planned`, yes or no for drafts and planned intents beyond the object set, default no, refused at load on any other position because those two rows are admitted at entailment alone. The committed entailment entry names the ten Iteration 2 intents (itd-194, itd-2609021003095168, itd-2609020625400169, itd-2609020625400194, itd-2609020625400445, itd-2609020625402518, itd-2609020625402599, itd-2609020625405170, itd-2609020625405251, itd-2609020625407419) beside the fifteen, and declares no switch; several of the ten are shipped by the time Phase A completes and travel through the shipped row, which is the same object set either way. Issues, ADRs and every other warm family stay excluded by construction and gain no switch. Divergence register entry 1 is corrected accordingly: it recorded the ten as reaching the position through the companion's admission, which is the reading this ruling replaces.
- dcn-26092600000000032026-09-26 — Three departures the scribe lane (itd-2609020625402599, spc-2609020626045177) made from its closed spec, which its review found recorded only in code, the chapter or the lane report, recorded here (implementer of lane fix2-scribe, autonomous run A). First, the surface chapter is `04-surfaces/31-scribe.md`, not the `24-scribe.md` the spec names: row 24 is `decide`'s, taken before the lane landed, so the chapter took the next free row. The spec is closed and keeps its text; four open lanes claim row 31 (build, lab, source ledger and this one), so the integration step renumbers three of them. Second, the transcript store's check is `SessionSeparation(repoRoot, rootSHA)`, not the `SessionSeparation(rootSHA)` the spec names, because it reads through `history.List`, which takes the repository root to find a checkout's opt-in per-repo transcript store; the report is unchanged. Third, the scribe's context is assembled from the ledger as it stands in the working tree, uncommitted records included, while the intent's scope condition (cond-2609020626046719) says committed ledger content. The working-tree read is what the code does today and the chapter says so. Whether the condition or the code should move is not decided here: it is captured as iss-2609261056373310, a ruling owed.
- iss-2609261056373310scribe assemble reads the ledger as it stands in the working tree, uncommitted records included, while itd-2609020625402599's scope condition (cond-2609020626046719) says the context is assembled from committed ledger content; which one moves is a ruling owed: amend the condition to the working tree, or have the assembler read committed content (and refuse or report uncommitted records)
- itd-199A reading is about something narrower than everything, and its scope is a committed, adjustable choice