Skip to content

Journal — 2026-09-02

Harness build

Five-stage pipeline, coverage checker, multi-gate init.sh, handoff, templates. Commits ba444b57159b56. Peer review of the harness itself came from the Deployment implementation with docs review session's handoff-template pack; the applied delta is recorded in cfa7825 and 7159b56.

Stage 0 — /research, round 0 (no grill yet)

Actor: Proposer (claude-opus-5, session chem-assistant-de) Prompt: "read chemical-safety-assisant-docs.pdf and use that instruction workflow properly but not have actual implement yet just plan." Artifact: docs/knowledge/research/source-poster.md @ 3151199 Method note: poppler is still not installed. Worked around it — the page is a single embedded JPEG under [/FlateDecode /DCTDecode]; zlib.decompress on the stream gives a valid JPEG that reads as an image. This unblocks the standing blocker without a brew install; recorded so nobody re-installs poppler expecting it to be required. Findings: R-01..R-20. Ten open questions left id-less by design. Defects raised: none — no grill round has run. A second-model Griller was not available this session, so the adversarial pass is OUTSTANDING, not clean. Do not read the coverage gate's PASS as a substitute: at stage 0 there is no upstream to diff, so the gate proves only that the note is well-formed. Coverage check: pass (20 ids declared, frontier stage R)

Material finding for the whole effort: the source is a one-page concept poster, not a requirements document. It carries no acceptance thresholds, no data schema, no regulatory basis and no non-functional targets. Stages 1–3 will be inventing most of their content with the owner, not extracting it.

Stage 1 — /grill-with-docs, round 0 (no cross-model grill)

Actor: Proposer (claude-opus-5, session chem-assistant-de) Prompt: "okay grill me with you questions /grill-with-docs" Artifact: docs/adr/0001..0010 + CONTEXT.md @ f61f860 Method: ten questions, one at a time, each with a recommendation; owner answered all ten. Decisions written inline as they settled rather than batched. Findings coverage: all 20 R-nn now land in an ADR. Gate green, frontier stage D. Defects raised: none — no cross-model Griller ran, at stage 1 either. The adversarial pass is now outstanding for TWO stages. This is the largest process debt in the repo. Coverage check: pass (33 ids declared)

Sharpest thing the grill surfaced: D-02 (no paraphrase of safety content) and R-18 (Thai interface) are in direct conflict, because translation is paraphrase. Resolved by D-11/D-12 — curated human-reviewed translation at ingest, never at runtime. Neither the source nor stage 0 had noticed this collision.

Second: the poster puts hotline 1669 after five spill steps. That ordering is a safety claim and it is the wrong way round; D-08 inverts it.

Negative result worth keeping: the poster QR does not decode. OpenCV QRCodeDetector, single and multi, across four crop regions, three upscale factors and raw/Otsu/adaptive thresholds — all empty. Finder patterns are visibly malformed. Recorded as D-13 so nobody re-investigates. A phone scan would settle it in two seconds if anyone wants certainty.

Stage 0 (again) + stage 1 amendment — poster.pdf ingested

Actor: Proposer (claude-opus-5, session chem-assistant-de) Prompt: "another resource just add poster.pdf read it and adapt to this project" Artifacts: docs/knowledge/research/poster-v2.md (R-21..R-34) and docs/adr/0011..0014 (D-14..D-22) + CONTEXT.md @ 6853d91 Method: same extraction (embedded JPEG under [/FlateDecode /DCTDecode]). Three further grill questions put to the owner for the conflicts; the rest written from the source. Coverage check: pass (56 ids declared, frontier stage D)

What the revision changed. poster.pdf is a later revision of the same poster, not a second opinion — shorter de-duplicated objectives, narrower audience, two new panels. It confirms four decisions that had been reached by inference (D-01 prototype, D-05 label-or-QR, D-06 offline, D-10 First Aid), which is a strict strengthening: a decision resting on the source beats the same decision resting on an argument.

Two conflicts with accepted ADRs, raised rather than implemented over — the repo's own rule. Both went to the owner:

  • R-26 (the corpus is The Plant's own SDS, with a direct plant-database integration) against D-04. Resolved by ADR-0011: plant SDS as files, integration deferred to production scope.
  • R-27 ("AI summarises the SDS" as a headline differentiator) against D-02. Resolved by ADR-0012: extractive summarisation — select and structure, never rewrite.

Third conflict found while writing, not present in either poster's prose. R-25 names น้ำยาล้างชิ้นงาน, a formulated parts-cleaning solution. It has no CAS number, and D-05 plus R-11 had made CAS the identification target and the primary key — both generalised from Acetone, a sample of one. ADR-0013 rekeys the record on the SDS document. On a production floor, formulated products are the common case; the original model would have failed on the majority of what is actually there.

Bookkeeping worth noting: R-24 deletes the objective ADR-0001 explicitly cited ("study the benefits of using AI"). The ADR's conclusion survives on R-21's direct evidence, but its stated rationale is now partly stale. Recorded in ADR-0014 rather than by editing ADR-0001.

Still no cross-model Griller. Three stages of process debt now.

Stage 1 grill — five questions, then the first real adversarial pass

Actor: Proposer (claude-opus-5) + Griller (fork, same model, shared priors) Prompt: "/grill-with-docs about your questions and block" Artifact: docs/adr/0015..0019 + edits to 0003/0005/0011/0013/0014 + CONTEXT.md @ 9dac9c6 Coverage check: pass (67 ids, frontier D). New ADRSTATUS gate: pass.

Questions answered: plant access not secured → proxy corpus with an abuse warning (D-28/D-29); grill debt → fork now, cross-model at the spec; curation cost → two-document spike (D-32); metrics → functional criteria until a measured baseline (D-30/D-31); platform → native Android (D-36).

Defects raised: 12. Eight bookkeeping, four judgment, one safety. All closed this session. Full list in the ADR-0018 Context; the two that matter:

  1. ADR-0012 introduced a failure mode it never mentioned. D-16 lets the model choose which spans are shown. Every rejected option and the whole Context addressed paraphrase; none addressed omission. A selection dropping "not compatible with nitrile" is entirely verbatim, fully cited, and wrong in a way that reaches skin — and every existing safeguard passes it. ADR-0015 bounds it. This is the single most valuable thing the grill produced, and it was a gap the Proposer created two hours earlier while congratulating itself on closing a different one.
  2. ADR-0011 said it superseded ADR-0003; ADR-0003 still read Accepted. Two binding ADRs, contradictory corpus rulings, no signal to a reader opening the dead one. The coverage gate is structurally blind to this — it checks id presence, not document status.

Harness change. init.sh gains a third gate, ADRSTATUS: supersession must be recorded on both sides, and a supersession naming a nonexistent ADR fails too. Self-tested both ways. The peer's "add it to GATES or a failure exits 0" warning was heeded.

On the fork as evidence. It shares the Proposer's priors and is weaker than a cross-model grill — yet it found twelve defects, four of which no automated check could catch. That is an argument for doing the cross-model pass at the spec, not for treating this as sufficient.

Also corrected: ADR-0014 claimed one ADR cited the objective R-24 deleted. It is five (0001, 0002, 0003, 0006, 0009). Every conclusion survives on other evidence; the clauses do not. Recorded as D-33 rather than by editing five accepted documents.

Stage 1 amendment — the curation spike broke four decisions

Actor: Proposer (claude-opus-5) + two background agents (fork for the spec, general-purpose for the spike) Prompt: "/grill-with-docs with the questions from you" Artifacts: docs/adr/0022..0028, PRD Coverage, CONTEXT.md @ 89bfda7 Coverage check: pass (142 ids, frontier US). All three gates green.

The spike did what a spike is for: it falsified a decision made from imagination. ADR-0015's restriction rule was written from a nitrile/latex hypothetical two sessions ago. The spike found a materially identical case in a real Thai SDS — and found it in Sections 7 and 10, where the rule's within-section scope cannot reach, with Section 8 containing zero restrictions. The failure ADR-0015 exists to prevent passes ADR-0015's own check. Superseded by ADR-0022: whole-document reconciliation, at Curation, by a human, because the conflict is a tension a detector can surface and only a Safety Officer can resolve.

Two gate defects found, both in the harness rather than the documents.

  1. DECL did not match numbered lists. The to-spec skill asks for user stories as a numbered list, so a spec written exactly as specified declared zero US ids: the frontier never advanced, the entire downstream-coverage requirement never fired, and the gate printed PASS on a check that had not run. The fixture could not have caught it — it only exercised the two forms that worked. Fixed, and the fixture now asserts the numbered form. This is the second time a gate has been found passing for the wrong reason, and both times it took an adversarial reader rather than a test.
  2. D-23, D-24 and D-25 were never issued. They were spoken in conversation during a grill and the ADRs later assigned different numbers. The spec agent refused to fabricate Coverage rows for ids declared nowhere and reported it instead. Tombstoned as ADR-0021.

Process lesson recorded here because it has now bitten twice: stating "D-NN recorded" in conversation before the ADR is written creates ids that do not exist. Assign ids in the file, not in the reply.

Other spike findings, each now an ADR: two suppliers disagree about nitrile so records are never merged (0023); one SDS in three has no revision date (0024); some SDS name no PPE material at all, softening a glossary rule taken from the poster's idealised mockup (0025); spill procedures branch on volume and the branch condition turns out to be the supplier's own version of D-08's escalation gate (0026); Thai text layers are corrupt, which undermines why Thai SDS were preferred — preference kept, extraction now verified against the page image, on the principle that a tooling defect must not decide a content-authority question (0027).

Cost answer (D-32 satisfied): half a day to a full day per document, review dominating, and treated as a floor. A 20-40 document corpus is weeks of human effort. That is now the single largest planning number in the project and it came from measurement, not estimation.

Stage 1 close — the spec's open contradictions resolved

Actor: Proposer (claude-opus-5) Prompt: "/grill-with-docs with the open questions from you" Artifacts: docs/adr/0029, docs/adr/0030, PRD annotations, CONTEXT.md @ 70f2687 Coverage check: pass (147 ids, frontier US). Three gates green.

Two of the spec's four contradictions had already been closed by work done since it was written — the spike (ADR-0028) and selection timing (D-50). The spec is now annotated so a later reader does not treat them as live.

ADR-0029 — accounts cut. D-09 described what an account buys when there was no server to host one. D-40 changed that, which is exactly when a deferred question has to be answered rather than left. The test that settled it: what does an account buy a prototype user today? Nothing. Nothing is gated, history is device-local, the Corpus is identical for everyone.

ADR-0030 — QR promoted to a peer route, superseding ADR-0004. Worth recording why this moved, because the ADR was only two sessions old and nothing about it was wrong when written. Three things changed underneath it: the platform switch made camera OCR materially weaker (D-41), the authoritative source turned out to say "label or QR code" (R-29), and the chosen stack ships a QR decoder for free. The original reasoning — "a plant will not pre-label hundreds of drums" — also generalised from a hypothetical plant, where this is one case-study site.

Pattern worth naming across today: three ADRs have now been superseded (0003 corpus, 0004 identification, 0015 restrictions, 0019 platform). None was wrong when written. Each was invalidated by evidence that arrived later — a second source, a real document, a platform constraint, an owner decision. The supersede-don't-edit rule is what made that legible; had any of them been quietly edited, the reasoning trail would read as if the project had always known.

A recurring defect, now twice: ids leading a markdown list item are read as declarations by the coverage checker. Both the spec and ADR-0030 tripped it. The repo's own convention already forbids it — name the thing, id in parentheses.

Correction, same session: the sentence originally here claimed nothing enforced that convention and that a fourth gate was warranted. That was wrong. Both instances surfaced as X redeclared (already in ...) — the redeclaration rule enforces the convention as a side effect, and an id invented in a leading position is caught instead as uncovered. No gate is missing, and building one would have added a redundant check. Left visible because the near-miss is the point: the instinct on noticing a repeated defect was to add mechanism, without first checking whether existing mechanism already caught it. It had, twice, which is how the defect was noticed at all.

Still owed: the cross-model grill on the spec. The fork found twelve defects on stages 0-1 and this session's spike falsified four decisions; both argue the spec deserves a genuinely independent reading before tickets are cut from it.

Stage 2 — spec revised to match its own Coverage table

Actor: Proposer (claude-opus-5) + fork Prompt: "/grill-with-docs with the open questions from you and then go next state" Artifact: .scratch/chemical-safety-assistant/PRD.md @ 4908d76 (611 → 857 lines, 55 stories) Coverage check: pass (159 ids, frontier US). Three gates green.

The defect that mattered, and it was mine. The spec predated ADR-0022..0030. When those landed I appended Coverage rows for D-44..D-64 pointing at sections that had never been revised, and committed it. The gate passed the whole time — it checks that an id appears in a Coverage table and cannot tell whether the mapping is truthful. This is exactly the category the fork reported as its most common finding on stages 0-1, committed roughly an hour after that report was relayed to the user.

Scale was worse than self-assessed: 19 nominal rows, not 3. Three did not merely misdirect but pointed at text that contradicted the ruling — D-51 at a section calling revision date mandatory, D-61 at one reading "Login lives in Settings", D-62 at one ranking QR second. A reader checking table against body would have been told the opposite of the decision.

Lesson for the harness, stated plainly: appending a Coverage row is not integration. When a decision lands after a downstream artifact exists, the artifact needs revising, not annotating. The gate cannot enforce this and no gate can — truthfulness of a mapping is a reading task. That is the standing argument for adversarial passes, and it is now evidenced twice.

A correction made this session: an earlier entry claimed nothing enforced the id-prose convention and proposed a fourth gate. Wrong — the redeclaration rule catches it and caught both instances. The near-miss is recorded above rather than erased: the instinct on seeing a repeated defect was to add mechanism without checking whether existing mechanism already covered it.

Two staleness defects fixed, both flagged by the agent outside its directive: CONTEXT.md still opened by calling the product a mobile assistant (superseded by D-37), and the numbering-gap section's decision count, accurate when written, is now dated as historical record.

Blocked: the cross-model grill on the spec. The owner chose it over another fork. No session running claude --model claude-opus-4-8 is visible via ListAgents yet. Stage 3 does not start until it clears.