Implementation plan
Written 2026-11-09 by the planning session (Claude, claude-fable-5-2) on the operator's direction to update the workshop, plan its implementation and start building. The README holds the goal, the design direction and what closes the workshop; this file holds the build order, the choices the design left open, and the state of each step.
Phases 1 to 5 record the review implementation and its remaining validation. Phase 6 is a bounded follow-on experiment in ingest. The shared-layer architecture is a future direction, not an execution phase.
Current state and next work
As of 2026-11-10. The code described in phases 1 to 5 is built and
committed; phase 5 has completed two live revisions through operator-approved
publication, with zero new workers on the publication repeats. Live
strike-list selection and a live full pass remain unexercised. The operator has moved routine approval to final
publication, superseding the plan-approval design described in earlier phase
records below. The publication-boundary change
records this commission and its consumer checks. The earlier stop-plan override and
unapproved-edit warning are committed as e93284673; that follow-up passed
701 selected fast tests and 17 slow analysis consumer tests.
Binding write briefs are now captured for planning and editing workers
(4a29b1bf5). They affect call identity and revision plan approval, not judge
inputs or result freshness. Conflicts stop work, and a brief changed during
the invocation blocks publication. The final general, headless and review
selection passed 217 tests, including 18 new brief cases. This establishes
capture and control flow, not semantic obedience by a live worker.
Live validation remains open. Phase 6 has not started in this workshop.
The operator's subsequent direction is simple operations before planning
and composition. The planning-free baseline change
separates one report-only criterion from one fixed editing pass. New revisions
explicitly choose --plan or --assess; fixed-plan workers receive execution
instructions without an assessment request. This establishes a runnable and
regression-tested boundary, not editing quality or planning stability. No new
paid trial is included in this change.
The operator requested a further design plan for typed review outputs: use declared types for review outputs. It inventories new and existing types, the checks they would replace, and the limits of directory typing. The operator accepted Fable's sequencing: the approval follow-up and brief fix precede a live revision trial; the individual result type follows that trial and precedes the first collection sweep. No type schema has changed yet. Checkable finding anchors, verdict-order calibration and explicit validation beneath the state ignore marker are addressed in that plan. Directory-type changes are not prerequisites.
On the operator's direction, finish review before testing the same publication boundary in ingest, then decide what is worth sharing:
- Ordinary revision trial done: both selected Sol trials ran automatically through assessment and one settled pass, stopped at publication, and then published the exact candidates after explicit operator approval. Validation and checkout guards passed; publication launched no new workers. See the publication record. Keep wording/abstraction selection stops when those passes are warranted; do not force one into these plans merely for coverage. A live run through selection approval and subsequent publication remains a separate unfinished check.
- Evaluate single-criterion review and fixed single-pass editing separately on operator-selected tasks, retaining each operation's failures before combining them. Planning and disposition stability need a separate, longer evaluation. Keep selection and final-publication approvals. The command, instruction and regression-test separation is implemented; paid quality evaluation remains separately commissioned.
- Then implement step 2 of the types plan: the individual review-result type, with pair and bundle readers and writers converted together. Stop and assess the deletion ledger before later types. Agree the scope of any paid old/new-format comparison separately.
- Complete step 1.3, the first collection sweep, after that type cutover. Complete the outstanding phase-5 full-pass trials too. Paid launches still require the operator's direction; fake-harness coverage does not close them.
- Once review is validated, take up phase 6's bounded ingest experiment under a follow-on commission. Compare implementations before extracting shared commands.
This workshop closes against the review conditions in the README. Ingest and shared-layer work do not add closing conditions. Before closing, move any still-held follow-on direction to its own workshop; do not keep this migration open to house the architecture work.
Live Sol assessments, 2026-11-10
The operator authorized older untouched artifacts with Sol. Both effort roles
used profile codex, configured for gpt-6.1-sol at medium effort. Two
assessment workers completed, one per artifact, without correction retries.
kb/articles/what-an-automated-reviewer-should-measure.md: last file change October 23 was relocation; earlier history is September. Sol recommends onlyreadability-and-flow, scoped to the opening and the introduction to “Testing the two claims,” to distinguish the textual assessment from its two experimental tests. Run:kb/reports/state/review/runs/revise/kb/articles/what-an-automated-reviewer-should-measure/1bac5e289ba8.kb/theory/position-and-proportion-set-what-a-reader-retains.md: unchanged since October 26 and has a write brief. The captured request includes that brief, and the assessment explicitly preserves its reserved conjecture. Sol recommends onlyreadability-and-flowover the two paragraphs setting out the proportion experiment. Its separate question about qualifying the opening is not included in the edit plan. Run:kb/reports/state/review/runs/revise/kb/theory/position-and-proportion-set-what-a-reader-retains/0090306470d3.
Each run retains artifact/assessment.md and artifact/plan.yaml. Both
stopped for approval without editing the target. Repeating each invocation
kept the same approval key and launched zero additional workers. Both
checkout guards remained clean, including on repeat.
Neither assessor recommended a strike-list pass. These first assessment stops ran under the now-superseded plan-approval policy. The operator then authorized automatic preparation up to the final publication gate; see the publication-boundary change for the continuation. Selection-stop and publication coverage remain distinct from successful candidate preparation.
Choices this plan makes
The design items were silent on these. Each is the planner's choice, open to the operator's veto, and cheap to change before the first real sweep writes results.
| Choice | Decision | Reason |
|---|---|---|
| Code location | src/commonplace/lib/review_workflows/ (results.py, gates.py), CLI src/commonplace/cli/review_workflows.py, command commonplace-review |
follows lib/tasks.py with cli/task.py; the old commonplace.review package stays untouched until phase 4 |
| Tests | tests/commonplace/review_workflows/, with a row in tests/README.md |
a subsystem directory per responsibility |
| State root | kb/reports/state/review/ with results/ and runs/<stamp>/ |
already ignored by kb/reports/.gitignore; separate subdirectories keep a note path from colliding with runs |
| Criterion id | the catalog gate id (prose/source-residue) for a file under the gates directory; otherwise the criterion's repository path without .md |
short paths for the 43 gates; any markdown file still has an id without a registry |
| Frontmatter keys | note, note-sha256, criterion, criterion-sha256, outcome (pass, warn, fail or report), profile, model, run, launch, completed-at |
kebab-case YAML keys (ADR 116); profile and model name the executor |
| Note hash | SHA-256 over the parts the criterion watches:; over the whole file when it declares none |
replaces acknowledgement of unwatched changes; README item 1 |
| Model partition | dropped; profile and model are provenance, and status can filter on them |
README item 1 |
| Both inputs changed | the state lists both, in the order note, criterion | no information is lost; the caller chooses what to show |
| Deleted note | its results are reported as orphaned, and removing them is deleting files |
replaces freshness-retire |
| Old package | the surviving pure helpers moved into lib/review_workflows/ when the old package was deleted |
no code path reaches a store |
| Review profiles | kb/instructions/review-profiles.yaml, same entry shape as kb/tasks/profiles.yaml, without edit or commit grants on the checkout |
a task profile may commit; a review worker may not |
Installed projects: a criterion is named by its artifact identity, a
repository path or commonplace:<path> for a library file. A catalog
gate's id is <lens>/<name> in both layouts, and another library
criterion's id is commonplace/<path>.
Phase 1, the gate sweep
Step 1.1, result files and status (easy)
Pure functions, no runtime and no worker launch.
results.py: the result record; reading and atomic writing of a result file; the criterion id; the watched-parts note hash;freshness()for one pair, answeringfresh,missing, or stale with the changed inputs;status()over a list of pairs;orphaned()for results whose note or criterion file is gone.- Tests for the consumer-visible contracts: a written result reads back equal; an edit outside the watched parts leaves a pair fresh and an edit inside stales it; a criterion edit stales exactly its pairs; a path that would escape the results root is refused.
Done when the tests pass with uv run pytest tests/commonplace/general
tests/commonplace/review_workflows and uv run ruff check . is clean.
Step 1.2, the gates workflow and the command (hard)
Built in a separate worktree on top of step 1.1.
gates.py: expand note and criterion requests into applicable pairs with the existing resolution; skip fresh pairs; for each remaining pair onechecked()call with inputsnoteandcriterion, one outputreview, and the outcome parser as the check; write the result file when the answer is accepted; report worker problems without writing a result.gatherruns the calls, andRun(max_workers=n)bounds them.- The prompt keeps what the reviewer scaffolding in
review/protocol/prompt.pysays for a single pair: the note's repository path so its links resolve, the conformance wrappers for type and collection pairs, the one derived local path a criterion may name, and the strict## Result:line. - The run records the working tree before and after and fails the run
when a worker changed the checkout, as
lib/tasks.pyreportsleftover. commonplace-review status,run gates,retryandacknowledge, with a new run directory per invocation and--resume <run>to continue one.- Tests on a fake harness, as
tests/commonplace/headless/test_tasks.pyhas: a sweep writes one result per pair; a second sweep launches nothing; a note edit re-asks only that note's pairs; a malformed review costs one corrected attempt; a worker problem leaves no result; a worker that writes the checkout fails the run.
Done when those tests pass with the headless/ suite and ruff, and one
live pair has run through commonplace-review run gates with a real
profile. The planning session runs the live pair after review, not the
worker.
Step 1.3, the first sweep (operator, still open)
One collection with one profile. It costs real launches, so the operator chooses the collection and profile and starts it. It decides:
- call granularity (README item 3), from the usage evidence of the launches against one bundle call on the same notes;
- whether the watched-parts hash is kept;
- whether the result file carries the cost and boundary field asked for
in the mailbox message
kb/messages/20261030T085047Z-economics-workshop-unattended-workflows-cost-per-yield-records.md. The reply is written then.
Phases 2 to 4, as built
On 2026-11-09 the operator directed the planner to finish the work without stopping: no backwards compatibility, no data migration, one commit per larger step. Phases 2 to 4 were therefore built before step 1.3 ran. Opus workers implemented each step and the planning session reviewed, corrected and committed.
Retirement of the old code came first, so the later phases built on
one package. It moved the surviving pure helpers into
lib/review_workflows/ (criteria.py, outcomes.py), made criteria
work as library identities in installed projects, and deleted the SQLite
store, the review and freshness packages, 12 commands and their tests:
78 files and about 14,400 lines. commonplace-status --review reads
result files. Relocation never rekeyed the store in code, so only its
tests changed.
Phase 2, the full pass. gates.py gained bundle grouping, the
headless runtime gained decide(), and full_pass.py runs the pass as
commonplace-review run full-pass. The packet lives at
kb/reports/state/review/packets/<note-path>; commonplace-review
packet checks its hashes. The guard command, lib/full_pass.py and the
validation rule for the old report are deleted, and the type is cut to
the packet. The directory-type question of README item 8 is answered no
for now: the run's artifact/ and the packet schema carry what code
needs.
Phase 3, the revision. Run.ask, checked and decide take a
profile per call. revise.py runs commonplace-review run revise with
the assessment stop, the operator-edited plan.yaml, a strike stop for
plain-wording and abstractions, and profiles chosen by each pass's
effort. narrow-overclaims runs only with a non-empty defend list and
split-out-a-treatment never runs headless. The six ordered pass files
gained a depth field so code can check plan order.
Phase 4, documents. ADR 127 supersedes ADR 010, 029, 032, 035, 036, 043, 052, 065 and 067 and amends 038, 039, 041, 051 and 080. The review README and review architecture are rewritten; the freshness architecture page, review triage and the semantics-preserving gate migration are retired; the fix, batch and retire instructions operate the new command. Sixteen proposals and seven theory notes were corrected where they stated the store as present fact, and two proposals were archived.
Phase 5, runs as functions of their inputs
On 2026-11-10 an outside review (Astra) found two defects in resumed
runs: a resumed run could report success for text the operator had
edited since, and a plan named with --plan was lost on the next
resume. Both were fixed in place, and both came from one cause. The
workflows wrote the note during a run, while the runtime treats a run as
a replayable function of its inputs. The operator then directed a
simplification on that line. Four changes were built the same way as the
earlier phases:
- One write, at the end. The full pass and the revision carry each
edited text in memory and in the run's
artifact/texts/. The last step writes the note once, and only over the start text. A hand-back writes nothing, so there is nothing to restore. A revision applies its text only when every pass of the plan ended well. The write log, its replay, the interrupted-write recovery and the restore are deleted. - The run directory is derived from the inputs:
runs/<workflow>/<note path>/<start-text hash prefix>. Repeating a command continues its run by replay, and an edited note opens a new run.--resumeis gone fromfull-passandrevise; the options a run recorded are reused when a repeat omits them. - Gate verdicts in the full pass come from answers.
gates.ask()returns results without writing them;gates.sweep()writes them as they arrive. The full pass writes result files once, at its end, for the text the note then holds. The per-cycle report cache is deleted, and gate applicability is computed from the text under review. - One mechanism for operator input,
headless.operator.answer(): the workflow proposes a file in the run and stops; on the repeat the file, edited or not, is the answer. The plan stop and the strike stop use it, and--directionis a recorded option.
The change is in kind, not in size: src/ is about 30 lines larger
than before phase 5, because the documentation in docstrings grew by
more than the deleted machinery. A fifth candidate, dropping the closing
cycle's rerun of every method, was left out. It changes the method, and
the evidence is one live run.
None of phase 5 has run with a live worker.
Remaining work and completion
Finish the existing review workflows before using them as the basis for a shared layer. The required result is checked publication and explicit operator approval, exercised through both tests and live use.
- Implemented: the approval command described below.
- Implemented: fake-harness tests for changed instructions, profiles and operator answers on the same start text; obsolete approval keys; unapproved repeats that launch nothing; and edits made after approval. Existing conflict, edited-note, edited-plan and recorded-option tests now use explicit approval. Strike-list tests cover changed criterion text and profiles as well as changed incoming text.
- After those tests pass, run one live revision with an operator-edited plan and at least one strike-list pass, using explicit approval.
- Run one live full pass. At least one of the two trials must end with a changed note so publication is observed live. The operator keeps or reverts the results under the README's closing conditions.
Exercise a changed answer during continuation, and record whether the approved answer or the unapproved edit is consumed. Test note conflicts deterministically; if exercised live, use an operator-chosen disposable target. A failed or incomplete trial leaves that behavior unverified; fix it or retain the limit before closing, rather than treating a successful launch as completion.
Step 1.3 remains separate gate-sweep validation. It is required before workshop closure, but does not block this approval correction.
Explicit approval
The original phase-5 headless.operator.answer() proposed a file and
read it as the answer on the repeat. The revision plan and strike lists
used it. One live revision used that earlier interface in its simplest
case: a pass added to an empty plan. The corrected interface below is
implemented but has not run with a live worker.
Its place in the architecture:
- It turns a decision into an input. The edited file has a hash like any other input, so a run stays a function of its inputs and replay still works.
- It is a report the operator may amend, so it needs no fourth operation.
- It works on both sides. On the agent side the operator can state the change in conversation and the agent edits the same file.
- With explicit approval, it lets an agent drive a Workjig run: start it, bring the proposal to the operator, edit and approve the answer only on the operator's stated word, then repeat the command. An instruction that allows this must state that the agent does not decide for the operator.
An unchanged proposal is not yet an approval. The original answer()
read any existing file as the answer, so repeating approved the proposal.
An agent repeating only to continue after an interruption could approve
it without the operator. The rule the trial
must hold to (from Astra's review): continuing past an operator stop
requires an intentional approval of the current proposal, and retrying
execution must not supply it.
Operator's direction: a repeated run stays a repeated run, and approval is a separate command. This is remaining phase-5 work, not a dependency on shared orchestration. Implemented behavior:
- Repeating never approves. A repeat of a run that waits for an answer reports the same stop again and launches nothing. Any agent or script may therefore repeat any run safely.
- An approval command records the answer. It takes the run and the proposed file and stores that file's current text, edited or not, as the answer. The workflow reads only the stored answer.
- Approval binds to the stop, proposal and answer text. Record which operator stop and proposal the answer approves, including the inputs that produced the proposal. A changed proposal or changed inputs to that stop require fresh approval, even if the file path is reused. An edit made after approval has no effect until approved again.
- The next repeat continues, with the stored answer as an input.
Approval is stored as an explicit input in the run, rather than inferred
from a repeat invocation. The command is a distinct act, so an instruction can
say who may run it: the operator, or an agent on the operator's stated
word for that proposal. The report supplies
commonplace-review approve RUN FILE --key KEY as approve-command.
--plan FILE supplies a proposal without assessment, not an approval.
The command captures the answer under the run lock. An old key cannot
approve a replacement proposal at the same path.
Implementation and consumer checks are recorded in the approval contract change.
What a trial should show:
- whether a file in a git-ignored, hash-derived run directory is practical to find and edit;
- whether the assessment gives enough to judge the plan without reading the whole note;
- whether the separate approval action makes clear which proposal is being accepted, including when the operator leaves it unedited;
- whether the error for an invalid edit is enough to fix the file;
- whether the operator mostly removes passes, adds them or changes scope.
Decisions that could later take this form: the full-pass hand-back
(now a pending packet, a resolve instruction and --direction), the
choice of connect candidates to author, and the choice of warnings to
fix. None is converted before the trial.
Phase 6, try the publication boundary in ingest
This is a bounded follow-on experiment, not implementation of a shared framework. Nothing is built. Its purpose is to learn whether agent-run ingest benefits from worker drafts followed by one checked publication, and which parts genuinely match review.
Under a follow-on commission:
- Build ingest's own prepare, check and write commands, using the sequence under "Connect and ingest" below. Keep its sub-agents.
- Exercise checked publication on real ingest work, including refusal to overwrite an incumbent changed since preparation.
- If the selected workflow has a genuine operator decision, try the edited-file approval interface there. Do not add a stop merely to make the workflows look alike.
- Compare the implementations and record which checks, publication rules and formats could be shared unchanged, which differ for a reason, and whether sharing would remove enough duplication to matter.
Completion means the transferable boundaries and important differences are known, not that a common layer exists. If the experiment fails to show useful commonality, keep the commands separate. Shared-layer work returns to decision only after this comparison.
The experiment does not block closing the review workshop. Move this direction to a follow-on workshop before that closure if it remains wanted; this plan revision does not launch ingest implementation.
Future direction — shared operations under two orchestrators
Recorded 2026-11-10 from a design conversation between the operator and Claude (claude-fable-5-2), then separated from the execution phases on the operator's direction. Nothing here is built. Statements marked "operator's direction" retain the design direction, not authorization to build the shared layer now; the rest is proposed.
The architecture has four parts:
- Operations. Every step is a report, an edit or a write.
- A shared layer. Instruction files, checks, the write and the file formats, reachable as commands over files.
- Two orchestrators. Workjig sequences operations in Python. An agent sequences them in a conversation with ordinary sub-agents.
- One operator interface. A worker proposes a file, and the operator explicitly approves its edited or unedited text as the answer.
Staging, by the operator's direction:
- Remote goal: unification. The two kinds of workflow share one layer, so a task can move between them by rewriting only its sequence. Nothing is built toward this yet, and the sections below describe it as the direction, not as the next work.
- First step: similar, not necessarily identical. Agent-run workflows take the same shape as the Workjig ones. They need not share code or exact file formats.
- First step: try editing a report as the operator interface. It is new and has not been tried beyond one simple case.
"Similar" is proposed to mean four things: the steps are named as reports, edits and writes; a worker drafts into a working directory and never into the library; code makes the one write, after code checks; and an operator decision is a proposed file the operator may edit.
Operations
The operator's observation: review has two kinds of operation, generating reports and applying them to the note, and the full pass combines them. Before phase 5 each intermediate text was saved in the note. Since phase 5 they are saved in the run directory, and the note changes once, after the pass ends. That separated a third, mechanical operation from applying.
| Operation | Inputs | Output | Who | Changes the note |
|---|---|---|---|---|
| report | a text, an instruction, possibly other reports | a report file | worker | no |
| edit | a text, reports, an instruction | the next text, in a working directory | worker | no |
| write | the final text, the start text | the note | code | once, only over the start text |
On terms: "edit" here is the worker's step of applying reports to a
text. The code's edits.apply() is the write.
Report. A gate review, a critique, a method report, a connect report, a packet, a closing status and a revision plan are all reports. They differ in the contract code checks on the output: a strict result line, a closed schema, or a report type's schema. A packet is a report whose inputs include other reports. Reports never change a text, so reports that do not consume each other can run in parallel, and any report can be repeated.
Edit. Each edit consumes the previous text, so edits run in order. An edit also makes a report about the earlier text out of date when it changes a part that report's criterion watches. That is why the full pass has a closing cycle. Code checks each edited text against bounds that depend on the task.
Write. Code writes the final text once, only when the note still holds the start text.
The current workflows as sequences:
- Gate sweep: reports over many notes.
- Full pass: reports on the start text; the packet; two edits (the editor, then the copyeditor); reports on the result; the closing status; at most one repair edit with its reports; write.
- Revision: one report (the plan), one edit per listed pass, write.
Two consequences:
- A report belongs to a text, not to a file. A result records the hash of the text the worker read. That text may be the note on disk or a text in a working directory. The full pass relies on this when it publishes its closing verdicts as fresh for the note it then writes.
- The run's
artifact/texts/is a private version chain. Each text has the reports made about it. Only the final text reaches Git, so the per-step diffs are local to one machine.
The shared layer
This section describes the remote goal. Operator's direction: the two kinds of workflow become compatible in most details, so a task can move between them. A task moves to Workjig when its bookkeeping grows, and to an agent orchestrator when it needs flexibility.
A task can move only if everything except its sequence is the same on both sides:
| Shared | Form | Today |
|---|---|---|
| the judgment | an instruction file that names its inputs and outputs and not who runs it | exists: the gates, draft-ingest-report.md, the connect skill used as an instruction in the full pass |
| the checks | code that takes an output and returns problems | Python only: the result-line parser, decide() schemas, bounds() |
| the write | one compare and write over the start text | Python only: edits.apply() |
| the files | result files, reports, candidate texts, the working-directory layout | review only |
An agent orchestrator can only call commands. So each check and the write need a command in front of the function. A Python workflow calls the function and an agent calls the command. Moving a task then means rewriting its sequence, from prose to Python or back.
The first step does not need this. A similar agent-run workflow may have its own commands and its own file layout. What it learns decides which parts are worth sharing.
The two orchestrators
What stays different:
- Launching. Workjig gives captured inputs, a restricted profile, launch records and replay. A sub-agent reads live files with the session's tools.
- Pinning. A Workjig worker reads a captured copy, so the recorded hash is of the text it was given. For a sub-agent, code hashes the inputs before dispatch and verifies them after, as ingest does with the snapshot checksum. That detects a change that is still there afterwards. It does not prove which bytes the worker read, so it is the weaker guarantee.
- Provenance. A result file records profile, launch and the model the harness recorded. A result from a sub-agent needs a truthful executor value.
- Enforcement. The supported Workjig path runs every check, because the workflow's code calls it. An agent follows a prose sequence and can omit a step. The write command can refuse an unchecked candidate, but an agent can still write the file directly.
Three proposed conditions. The first applies from the first step; the other two apply once tasks can move:
- The agent side gets only stateless commands: functions over files, such as "check this output" and "write this text". ADR 127 removed an agent orchestrator with a command per transition over a store. Commands that track jobs, attempts or locks would rebuild it.
- Run state is the signal to move. A task that needs replay after a usage limit, attempt counting or many parallel calls moves to Workjig. A task that needs questions to the user, a choice of the next step, or tools the profiles deny moves to the agent.
- One orchestrator per task at a time. A task is moved, not kept in a prose version and a Python version.
Where the tasks sit:
| Task | Orchestrator | Basis |
|---|---|---|
| gate sweep, full pass | Workjig | many calls; code decides the branches |
| ingest | agent | operator's direction: it does not move to Workjig, and it takes the similar shape |
| connect | agent as a skill; one plain call inside the full pass | one operation, so either side can ask it |
| revision | Workjig, unchanged for now | operator's direction; readability-and-flow may move out later |
On the revision: its passes differ in what they need.
readability-and-flow needs only the text and the purpose.
narrow-overclaims needs evidence that normally comes from the
conversation. plain-wording and abstractions need the operator to
strike a list. split-out-a-treatment never runs headless.
audit-a-prior-pass and one step of place-external-systems need
Bash, which the profiles deny. The workflow's bounds keep the title and
the H1, so it can apply only the TL;DR half of opening-and-title. A
split along this line was discussed and deferred.
Connect and ingest
Connect is a report with one difference: its input includes the
corpus at the time of the search, which no hash of the note and the
instruction covers. It can carry no freshness claim, and its reports
stay regenerable under kb/reports/cache/connect/. Code can check two
things the skill now asks of the worker: the report matches
kb/types/connect-report.schema.yaml, and every candidate path exists.
Whether a label is authorised cannot be checked in code, because the
authorised set is prose in COLLECTION.md. Reverse-edge candidates
change other notes, and a write is one compare of one file, so they
stay report-only.
Ingest keeps its sub-agents and takes the similar shape. Its parent agent now performs the deterministic parts by hand. With commands of its own:
| Step | Who | What |
|---|---|---|
| prepare | code | resolve the target, hash the snapshot, extract the retained Quotes block, create a working directory keyed by slug and snapshot hash |
| report | sub-agent | connect on the snapshot |
| edit | sub-agent | draft into the working directory, not into kb/sources/ |
| check | code | the handoff checks (snapshot checksum, Quotes block byte-equal, capture metadata, forbidden fields and links) and commonplace-validate; the failures feed one repair attempt |
| write | code | write the ingest once, only if the output path holds what prepare saw |
| report | sub-agent | connect on the accepted ingest |
The worker never writes kb/sources/, so the backup and the restore go
away, as the restore did in phase 5.
Ingest reads external texts. The snapshot is read and never
changed; the ingest is written. In the model the snapshot is an input
to the edit, as a criterion is in review, and the ingest's
snapshot_sha256 pins it as a result file's hashes do. Review
freshness never depended on Git, so a text Git does not track needs no
new machinery. Four differences from review:
- Fetching is not a function of its inputs, because a URL can return other bytes later. Capture stays outside the run, which starts from a snapshot path.
- A changed snapshot is a new observation with a new ingest. It is not a stale ingest to regenerate.
- The checkout guard ignores git-ignored files, so it cannot see a snapshot change. The checksum comparison before and after the draft covers this.
- External text is untrusted and may address the reader. The drafting sub-agent has Write, Bash, Skill and Task. A restricted profile would be a safer reader; this is a known cost of keeping ingest on the agent side.
An ingest is a report in content and an edit plus a write in handling: it lands in the tracked library, must pass checks and may replace an incumbent. The model classifies it by handling.
Candidates
None is decided unless marked. Each needs the operator's direction.
The live review trials now belong to phase 5. Ingest's commands and any agent-side interface trial belong to the bounded phase-6 experiment. Neither requires the shared layer.
After the trial of the interface.
| Candidate | Reason | What holds it |
|---|---|---|
| The full-pass hand-back as a proposed file | three mechanisms now serve one decision | the trial's result |
Simplifications that do not depend on the staging.
| Candidate | Reason | What holds it |
|---|---|---|
| Check the connect report in code | schema and path existence are deterministic | nothing |
Drop --resume, retry and acknowledge from run gates |
a rerun already skips fresh pairs, so replay only helps under --force |
nothing; independent of live evidence |
Keep one of pair and bundle |
two groupings for one job | the verdict comparison of step 1.3 |
| Replace the guard's unattributed failure with worker containment | the guard cannot tell a worker's change from another session's | the guard keeps setting the exit code until containment is verified: the profiles' write restriction has not been seen to refuse a write, and a worktree shows linked files at HEAD and hides ignored snapshots |
Remote goal.
| Candidate | Reason | What holds it |
|---|---|---|
| One set of commands in front of review's checks and write | both sides would call the same code | the first step shows which parts are worth sharing |
| An executor value in result files | a sub-agent's result must not claim a launch | no agent-side producer of results exists yet |
| Look up a report by text hash and criterion hash | a sweep and a full pass would share reports without reading each other's state | needs a rule for discarding reports about texts that exist nowhere |
| Merge the full pass and the revision | the full pass's editing step and a revision are both chains of edits | the revision stays as it is for now |
Future compatibility trials
For the remote goal, when it is taken up:
- Agent side to shared layer. Have ingest call review's check and write. If they serve it unchanged, the layer is general.
- Workjig side to agent. Have an agent run one gate review and
record it through a command. If
commonplace-review statuscannot tell that result from a sweep's, apart from the executor value, the file format is shared.
Outside review of phase 5 and the shared-layer direction
Astra reviewed plan.md, the workshop README and ADR 127 on 2026-11-10,
as a design assessment without verifying the implementation. Its
verdict: the separation of worker drafts from the one checked write is
convincing; the common framework is still a hypothesis; proceed with
the trials and the bounded ingest change, and do not build the shared
layer yet. That matches the staging above.
| Point | Disposition |
|---|---|
| An unchanged proposal is not operator approval | accepted; confirmed in headless/operator.py; the operator directed a separate approval command, now part of phase 5's remaining work |
| A hash-derived directory is not complete input identity | accepted; see below |
| One write at the end does not replace worker containment | accepted; the candidate to stop the guard setting the exit code is withdrawn |
| Four absolute statements in the original phase 6 were too strong | accepted; narrowed in place (parallel reports, reports made stale by an edit, hashing live inputs, enforcement) |
| A section at the top naming the current decision and next work | added |
Where a run is found, and what makes an answer reusable. These are two mechanisms. The run directory is found from the workflow, the note path and the hash of the start text. It says nothing about instructions, criteria, profiles, options or operator answers. Reuse is decided per call: Workjig keys each call by its task, inputs, outputs and profile, so a changed instruction or answer asks that call again inside the same directory. "A run is a function of its inputs" holds for each call's answer. It does not hold for the directory, which can contain answers made under several versions of an instruction.
Execution record
| Step | State | Commits |
|---|---|---|
| README update, phase 0 | done 2026-11-09 | f85f19537 |
| 1.1 results and status | done, accepted unchanged | 7d2af63dd |
| 1.2 gates workflow and command | done; the planner made criteria explicit for a sweep | 5ce47edc3, 0bc400508, 4e03cbcff |
| retire the old code | done 2026-11-10 | 1e2027899, 3562ca982 |
2 bundle grouping, decide(), full pass |
done 2026-11-10 | 1b27a9b40, 84fdda681, 27d6ba9af, ef7b5a07c |
| 3 profile per ask, revision | done 2026-11-10 | 07e2f984b, 4422a531c |
| 4 ADR and documents | done 2026-11-10 | 2fe4c0585 and the six commits after it |
| resume defects found in review | fixed 2026-11-10 | 7751d4f4c |
| 5 one write, input-derived runs | done 2026-11-10 | 13ed322bf, 563f4076b |
| 5 gate results published once | done 2026-11-10 | 32c281ddd |
| 5 one operator-input mechanism | done 2026-11-10 | f9779b18f |
| 5 explicit approval and validation | approval implemented; core and broader consumer suites pass; live trials remain open | approval correction (this commit) |
| 6 ingest publication experiment | follow-on direction; nothing built; does not block review closure | |
| shared operations under two orchestrators | future direction; revisit after phase 6's comparison | |
| 1.3 first sweep | waits for the operator |
Live runs
All on kb/theory/definitions/storage-substrate.md with the sonnet
profile, each a single observation.
| Run | Launches | Cost | Result |
|---|---|---|---|
| one gate, pair grouping (2026-11-09) | 1 | $0.095 | pass; 13 s; the rerun launched nothing |
the prose bundle, bundle grouping |
1 | $0.172 | nine stale pairs reviewed, one fresh pair skipped; 31 s |
| full pass | 27 | $4.47 | keep, edited, closing hand-back, start text restored, packet pending |
| revision: assessment, then one operator-added pass | 2 | $0.235 | the assessor planned no pass; readability-and-flow returned no change |
The single-gate cost is almost all fixed context: the note and criterion
are about 2,000 tokens, and each of five turns reread roughly 30,000
tokens of harness and project context. Nine gates in one bundle call
cost about a fifth of nine pair calls. Whether one worker judges nine
gates as independently as nine workers is not measured, so the default
grouping stays pair until step 1.3 compares verdicts.
The full pass handed back because its closing premise check defeated a sentence the pass had added, and the closing decision judged the repair a claim-level choice for the author. The guard reported three files, all edited by a worker building phase 3 in the same checkout at the time.
No permission denial occurred in any run. No worker tried to write outside its run directory, so the profiles' write restriction is still unverified.
Open
For the operator:
- Step 1.3. One collection, one profile, both groupings on the same notes. It decides the default grouping and whether the watched-parts hash is kept.
- Keep or revert one result from each workflow, which the README names as a closing condition. The full pass has run live only on its hand-back path; an accepted edit, a bounded repair and a non-keep packet are covered by fake-harness tests alone. The revision has not applied a change or stopped on a strike list live.
- Delete the abandoned local data in the main checkout:
kb/reports/state/commonplace-store.sqlite, thereview-store.sqlite*files,review-store-backups/,review-jobs/,reviews/,bundle-reviews/andfull-pass/. Nothing reads them. The planning session did not delete untracked data. - User-level tool reinstalled during the approval implementation:
commonplace-reviewnow resolves by bare name, and uv removed the retired launchers. The editable tool still points to this checkout. - Read the paragraph added to
kb/theory/many-to-many-edge-state-is-where-files-yield-to-a-database.md. It records the return to files as evidence bearing on the note's claim. - Workshops whose subject was deleted, proposed for closing:
review-evidence-and-disposition-hardening,artifact-freshness-and-referential-checks,freshness-module-review,review-bundle-packing,full-pass-instruction-coherence-audit, and the review-storage thread ofsrc-architecture-alternatives. collection-as-artifact-freshnessis a proposal whose planned mechanism, the generic freshness store, no longer exists.- The mailbox message on cost-per-yield records is unanswered. The result file carries no cost field; the launch records hold the harness's usage evidence.
Known limits of the code:
- The guard cannot tell a worker's change from another session's, so a long sweep in a busy checkout exits nonzero with paths to inspect.
- The codex profile can write the checkout at the sandbox level.
- A sweep with nothing stale still creates a run directory.
retryof an answered pair needs--forceon the rerun.- A full pass no longer reuses fresh gate results from an earlier sweep; it asks every applicable gate, and replay covers a repeat.
- A finished run cannot be repeated for its report: the applied note has another hash, so the same command opens a new run.
- The review profiles deny Bash, so
audit-a-prior-passcannot read a diff and one step ofplace-external-systemscannot run.
Behaviour of the agent-run full pass that the workflow dropped or changed:
- An undetermined update is not asked of the user mid-pass. The packet
is pending, and the operator answers by repeating the command with
--direction. - The copyedit is accepted or rejected whole; code cannot apply only its in-bounds edits.
- The editor no longer rereads the friction and premise attention lines against the edited text. The closing cycle reruns both methods.
- A rejected packet no longer binds the next packet decision, and a changed pending packet is replaced by a new pass without a superseding step.
- A hand-back or undetermined packet is
pendingand blocks a new pass until it is resolved or the note changes. - A worker problem or a spent call in any method stops the pass.
- The packet has no "Proposed revision shape" section and keeps the merge target's hash without a capture file.
Interactive revision behaviour the workflow does not cover is listed in the "Run it as a workflow" section of revise an article or note.