docs(corpus): launchpad/docs/corpus/AGENTS.md -- the corpus authoring node (#636) - #1462
Conversation
tucktuck101
left a comment
There was a problem hiding this comment.
Request changes
High — the provenance-update shortcut can certify stale evidence
launchpad/docs/corpus/AGENTS.md:338-344, 400-404 says an empty git diff --name-only <recorded-sha> -- <the ledger's paths> result means every cited source is unchanged and every claim still stands. That is false for valid citation forms this document permits. A path:line citation is not a filesystem pathspec without normalization, while commit, graph-edge, tool-result, external URL, and GitHub-link citations cannot be checked by that command at all. The command can return empty while a cited source changed or was never checked.
Limit the shortcut to normalized repository-file paths; explicitly exclude non-file citations; and state only that the selected files are unchanged. Re-verification, not an empty path-only diff, establishes that a FACT still holds.
Medium — the citation table misattributes seven validator forms to a six-form contract
launchpad/docs/corpus/AGENTS.md:190-205 says CONTRACT.md defines six citation shapes, then presents seven rows after combining file line/range and adding GitHub links plus external URLs. launchpad/project-intelligence/CONTRACT.md:80-92 defines the six contract forms as file range, file line, bare path, graph edge, tool result, and commit. GitHub/external URLs are validator-recognized inputs, not forms defined by that contract.
Separate the contractual six shapes from the validator's additional URL handling so the new governing document has one unambiguous taxonomy.
Medium — a core instruction lacks a supporting ledger entry
launchpad/docs/corpus/AGENTS.md:127-130 directs authors to keep one independently maintainable idea per node and file newly discovered second concepts as separate tasks. The document requires a front-matter evidence entry for every substantive claim (:156-158), but its only #636 TEAM_KNOWLEDGE entry (:82-84) supports the recorded-revision requirement, not this one-node procedure. The linked issue makes independent maintainability a definition-of-done requirement.
Add a correctly classified evidence entry for the one-node rule, attributed to #636 or a more authoritative accepted source.
What looks good
- Front matter uses schema-defined fields and valid conditional evidence fields; omitting relationships is correct for the first node.
- The document correctly distinguishes structural citation validation from semantic support and warns that non-fatal
UNVERIFIEDnotices are not proof. - The create/update/retire structure, stable-ID guidance, and retirement relationship handling are clear.
- The replacement discovery test removes the obsolete empty-corpus assumption, compares against an independent walk, and retains the fixture test as the controlled exclusion proof.
Panel: fable + codex.
#636) Three sibling agents authored corpus nodes following this document literally, and reported what broke. These four are the defects that produced real false claims in their work, not stylistic complaints. Filed as #1471; fixed here because #1462 is still open and 45 further nodes would inherit them. 1. THE ONLY-NODE JUSTIFICATION WENT FALSE, AND TWO AGENTS COPIED IT. "A node with no sibling to point at correctly declares none" read as a general rule. It was a fact about one moment -- when this was the corpus's only node -- and stopped holding as soon as a second existed. Both the #1309 and #1307 agents reproduced the reasoning and wrote a justification that was false at the time they wrote it; their adjudicators caught it, not their own reviews. Now says to enumerate what exists and give the real reason, and records why. 2. AN ISSUE-ONLY SOURCE HAD NO HONEST CLASS. The citation table lists tool results as legitimate while "Three things a passing run does not mean" says a FACT resting only on UNVERIFIED citations "has not been checked by anything", with one named exception. A claim whose only source is a GitHub issue has no openable form and cannot be SHA-pinned, so both passages applied and disagreed. The #1307 agent hit this with nothing honest to choose. Now states it: TEAM_KNOWLEDGE with provided_by naming the issue, which is what ADR-0029 requires of GitHub history anyway. 3. A RANGE WAS READ AS A MAPPING. The not-covered table paired eight subjects with "#1307-#1351", which is 45 issues and no subject-to-issue pairing at all. The #1307 agent cited this table for nine invented mappings -- exactly the "FACT cited to a file that does not discuss the claim" failure the document warns about two sections earlier. Now says outright that it is a range, not a mapping, and gives the search command. 4. CREATE STEP 3 ROUTED SOMETHING STEP 8 DID NOT ACCEPT. Step 3 sends expected-but-unverified items to the scope section; step 8 said only "state what the node does not cover". A boundary and a confidence disclosure are different things, so working the steps in order left that category homeless. Both the #1307 and #1309 agents reported it independently. Step 8 now names both. Verified: schema errors 0, 21 ledger entries, validator exit 0, 79 tests OK. Refs #636 #1471 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
Held in draft deliberately — the cross-model gate has not runDo not mark this ready for review yet. This is not an oversight. The review pipeline for corpus work under #605 requires a cross-vendor final pass. That No alternative external provider is installed ( Same-model reviews did run and their findings were addressed — see this PR's own Decision (Serina, 2026-08-26): hold every corpus PR in draft until credits are When credits return, the sequence is: run the cross-model final on this branch's current |
Correction: the draft hold above is liftedMy earlier comment on this PR said "do not mark this ready for review". That no longer Serina marked this PR ready for review deliberately, superseding the earlier hold. I What is still true, and is the reason the earlier comment existed: The cross-vendor final review has not run on this branch. So this PR is review-ready with a known, recorded gap in review coverage, rather than When credits return, the cross-model pass on the current head is still worth running. |
…1311) A same-vendor review-final pass, run because the cross-model gate's confirmation died on credits, found that all three earlier fixes corrected the instance they were raised against and not the class. That is the failure mode a same-vendor reviewer is worst at catching, so it is worth naming: the sweep, not the sentence, was the deliverable. 1. "no check reads the field" survived in two more places after the previous commit corrected the section it was raised in -- including under "Expected but not verified", the sentence that tells a reader what WAS established. Both now say no check branches on WHICH value the field holds. Swept: no occurrence of the false phrasing remains in either file. 2. The authority table still credited the whole SHOULD list to "this node, and nothing else" while SHOULD 2 restates AGENTS.md's step 3. #1481, filed by the commit that wrote the table, already recorded that SHOULD 2 is a restatement -- so the document and its own issue disagreed. The table now splits SHOULD 2 out. 3. The duplication measurement -- the most falsifiable claim in the document -- had no ledger entry, which is exactly the omission finding 3 asked to sweep for. It now has one, plus entries for the validator never reading body prose and for the #1481/#1473 cross-references. Also: the phrase count was still low (five shared runs, not four; MUST 6 matches the AGENTS.md *Updating a node* section rather than the retirement one, so entry 18 now says "retiring or updating"); adding the duplication entry made the AGENTS.md citation count two, so the prose saying "One evidence entry cites" is corrected again; the excluded `developer` audience is now disclosed rather than silently decided; and the plan's ALREADY TRUE block, a committed artefact a future agent reads, no longer asserts the claim the gate refuted. MERGE-ORDER PROOF, prompted by #1488 defect B. AGENTS.md step 9 covers relationships to unmerged nodes but says nothing about bare-path CITATIONS to them, which carry the identical hazard. Three-way check with the negative control that makes it worth anything: 1. this node alone on a clean origin/launchpad -> exit 1, failing on exactly the two AGENTS.md citations (entries 13 and 18) 2. control, same node with those two citations swapped for a file that IS on launchpad -> exit 0, proving the check discriminates 3. the ACTUAL PR merge result, origin/launchpad + this whole branch -> exit 0 Test 3 is the one that decides it: this branch carries AGENTS.md, so the merge result resolves both citations whichever of #1462 or this PR lands first. The citations are kept -- they source the retirement-procedure claims and the duplication measurement -- and the dependency stays disclosed in the body. Ledger reconciled against the finished body per #1488 defect C. 23 entries, 22 bare-path citations all resolving, one commit-only FACT, no relationships key. Validator exit 0; 79 + 27 tests OK. Refs #605, #1481, #1488 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
…dger (#1314) Two defects from #1488, filed by the #1312 sibling, both of which applied here. DEFECT B -- MERGE-ORDER HAZARD IN BARE-PATH CITATIONS. AGENTS.md step 9 forbids a RELATIONSHIP target absent from the merge target and gives a git ls-tree check for it. It says nothing about CITATIONS, and the hazard is identical: a citation resolves against whatever tree the validator runs in, so a path present only on the authoring branch passes locally and is a hard error after merge. This node had it. Nine ledger entries cite launchpad/docs/corpus/AGENTS.md, which is absent from origin/launchpad. Measured, with the negative control that makes the proof mean anything: TEST 1 node on a clean origin/launchpad tree -> FAIL, 9 errors, exit 1 TEST 2 same node, AGENTS.md citations swapped out -> PASS, exit 0 Test 2 is what proves those citations are the cause rather than something else in the node. Without it test 1 shows only that something is wrong. The citations are NOT removed -- nine of this node's claims are about what AGENTS.md says, and it is based on #1462 precisely because it cannot exist without that file. The fix is to state the constraint with its measurement and gate the merge: this node must never reach launchpad ahead of #1462. The general rule is now written down, because step 9 does not carry it, citing #1488. DEFECT C -- LEDGER/BODY RECONCILIATION. A full both-directions pass found 40 body claims with no ledger entry, concentrated in the merge-order section I had just written -- which is exactly the failure mode #1488 describes, claims shifting as the document is written. Added seven entries covering AGENTS.md step 9, AGENTS.md as the lifecycle and provided_by source, memory.py's narrower field contract, COMPATIBILITY.md's additive case, #605's outcome text, and the #1488/#1462 gating. Extended three entries whose body text had outgrown them: ADR-0029's two claim-type rankings, memory.py's stale-flagging, and the schema README. Trimmed rather than sourced where the body had quietly extended a sourced entry: five cells in the scope-and-omissions table carried clauses AGENTS.md does not record. The table now says outright that its mappings are AGENTS.md's and not this node's inferences. The reverse direction was clean -- no entry lacked a body home -- except that the authority entry was pointed at rather than claimed. The body now states where the MUSTs and SHOULDs get their authority. Caught while re-verifying: adding entries moved the AGENTS.md citation count from seven to nine, making the sentence stating it false. Fixed, and the number re-derived from the ledger rather than edited by hand. Verified: TEST 1 and TEST 2 both re-run against the final node; validator exit 0 with 2 expected UNVERIFIED notices; 79 corpus tests OK; 27 schema tests OK. Refs #1314. Cites #1488. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
…ed at (#636) Closes the review panel's High finding and one of its two Mediums on #1462. HIGH -- the provenance-update shortcut could certify stale evidence. Two passages said an empty `git diff --name-only <recorded-sha> -- <ledger paths>` established that every cited source was unchanged and every claim still stood, and the second went further: "the command is the check". Both are false for citation forms this document itself permits. Measured, not argued. A file that DOES differ reports as changed with a bare path and reports empty output with exit status 0 once `:127` or `:219-221` is appended -- no error, no warning. So an unnormalized `path:line` citation makes a changed file indistinguishable from an unchanged one, which is the worst available failure shape for a provenance check. Recorded as a new FACT entry with the tool-result citation that demonstrates it. The rule is now stated with its reach: normalize positions off the path first; only bare path, file line and file range are in scope; graph edge, tool result, commit and the two URL forms are excluded explicitly, so the branch is simply unavailable when a ledger contains one. And the conclusion is narrowed to what a diff can support -- those files are unchanged -- with re-verification, not an empty path-only diff, left as the only thing that establishes a FACT still holds. MEDIUM -- the one-node rule had no supporting ledger entry. "One node is one independently maintainable idea" is a substantive instruction and the ledger's only #636 entry backed the recorded-revision requirement instead. Added as TEAM_KNOWLEDGE attributed to #636's definition of done, which states it near-verbatim, matching the existing entry's classification for the same source. Also corrected a wrong-direction cross-reference: "*Updating a node* below" in Scope and omissions, where that section is above it. The recorded revision stays at 0052f5a. Checked by the corrected procedure rather than asserted: the one non-file citation was excluded, the nine normalized file paths were passed to `git diff` against 0052f5a, and the result was empty -- so the cited sources are unchanged and moving the revision would assert a re-check that added nothing. The second Medium is not addressed because it is stale, and the evidence is in the PR thread rather than in this commit: the citation table was already corrected by ebe2daf and now says outright that it is seven rows and is not a summary of CONTRACT.md section 3. The remaining cross-document disagreement between CONTRACT.md, validate.py and this file is tracked as #1478. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
Review addressed — two findings fixed, one is stalePushed as High — the provenance-update shortcut can certify stale evidence · fixedConfirmed, and the failure mode is worse than the finding describes. I measured it rather than reasoning about it: A file that does differ reports as changed with a bare path and reports empty output with exit 0 once a position suffix is appended. No error. No warning. So an unnormalized I hit it live while addressing this, which is the best argument for the fix: a throwaway script I wrote to apply the corrected procedure mis-parsed my own tool-result citation as a file path, and Both passages are rewritten:
The old line "the command is the check" is gone. The honest reading is now stated as one sentence in the document: the file-naming citations I normalized and passed are unchanged; every other claim in this ledger is unverified by this command. New ledger entry recording the measurement, as a
It reports as a non-fatal Medium — the citation table misattributes seven validator forms to a six-form contract · stale, not fixedThis was already corrected before the review landed, so there is nothing to change. At branch head,
The fix is commit Flagging rather than silently skipping, since "the reviewer was looking at an older commit" is a claim that deserves the evidence attached. The remaining genuine disagreement — Medium — a core instruction lacks a supporting ledger entry · fixedConfirmed. "One node is one independently maintainable idea" is a substantive instruction, and the ledger's only #636 entry backed the recorded-revision requirement instead. Added as
Also fixed, not from the reviewA wrong-direction cross-reference: Scope and omissions said " The recorded revision stays at 0052f5aChecked by the corrected procedure rather than asserted. The one non-file citation ( So the cited sources are unchanged and moving the revision would assert a re-check that added nothing — branch 4 of the rule, applied as the corrected text now permits it to be. The new Verification(The Not verified
Merge order — unchanged and worth restatingThis PR's diff is the shared So #1468, #1469 and #1477 being APPROVED + CLEAN is misleading: each of their diffs contains the |
|
@tucktuck101 re-requesting review — three commits have landed since your
The last one is the largest: it rewrites the "checking whether cited files moved" Worth knowing while you look: ten other open corpus PRs carry this file, and all ten No claim here that your findings are resolved — that is your call. This comment only |
* docs(corpus): plan for issue #636 -- launchpad/docs/corpus/AGENTS.md Reviewed by an independent cross-model review-plan pass (Codex, gpt-5.6-sol, high effort): eleven findings, all applied. Three more were found afterwards, the load-bearing one being that the plan blocked its own STEP 2 on an OPEN item -- an OPEN item a step must consume is a missing input, not an open question. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * feat(corpus): AGENTS.md frontmatter and skeleton (#636 STEPs 2-3) The corpus's first hand-authored node. Frontmatter validates against node.schema.json with zero errors; 11 evidence entries; no relationships, because no sibling node exists to target and an unresolved target is a hard validator error. The repository revision is recorded in the evidence ledger -- the schema calls that array the provenance ledger and permits no other field, so it is the only schema-legal place for it. STEP 3 (RUNS HERE) proved the validator actually scans the node rather than skipping it: mutating type: to a value outside the enum produced 'FAIL corpus-agents: schema violation at type' and exit 1, naming the node's id; restoring it returned exit 0. Also replaces test_real_corpus_root_currently_has_no_content_outside_schema, which asserted the corpus root is EMPTY and therefore could not survive the first authored node. Authorised by Serina as a deliberate widening of the plan's LEFT OUT scope, recorded in the plan. The replacement asserts what the original's own comment said it was for -- content present AND nothing from schema/ -- and both halves were negative-controlled before being trusted. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md node-contract section (#636 STEP 4) Adds the lookup table of authoritative sources and the 'what a corpus node is' section: one file per node, one idea per node, where nodes go, id permanence, relationship resolution, and the authored-vs-generated boundary. Reproduces no enum member list and no field-combination matrix -- those stay canonical in node.schema.json and schema/README.md and are linked instead. The checker never reads body prose, so a copy here would stay green while drifting. Verified: no type-enum member and none of entry_class/provided_by/ confidence appear in the body. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md evidence and citation section (#636 STEP 5) The part that lives nowhere else: what the checker actually does with each of CONTRACT.md's six citation shapes, and three things a passing run does not mean -- that a citation supports its claim (checking is structural, the file is never opened and compared), that UNVERIFIED is a pass, or that a line number was verified (#1459). Names the three evidence classes and what each is FOR, but not which fields the schema requires per class -- that matrix stays in node.schema.json. Verified absent from the body: entry_class, provided_by, confidence. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md create/update/retire procedures (#636 STEP 6) This is #605's acceptance criterion for the file -- a cold-start agent creating, updating or retiring one node without oral guidance. Three numbered procedures, each ending in the same check command. Adds a 'Running the check' section giving the interpreter form (works without Hermit; the just recipe needs activation first) and documenting --root, since without it the command always validates the real corpus whatever directory you are standing in. Retire covers the three things most likely to be got wrong: inbound relationships resolved before retirement, status set rather than the file deleted, and the id never reused or renamed. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md scope and omissions (#636 STEP 7) Names each deferred area with its owning issue rather than leaving a reader unable to tell a gap from an omission: the standards and templates (#1307-#1351, none merged), generated-artifact provenance (#1316), claim-type and flagged state (#1410), the human entry point (#639), and unverified line numbers in citations (#1459). Records why this node carries no relationships -- at its recorded revision it was the only authored node, and a target no node carries is a hard error -- so the absence reads as deliberate rather than forgotten. Every repo path cited in the body verified to resolve to a real file. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): audit AGENTS.md against its own ledger (#636 STEP 8) The audit found two things the earlier steps had missed. The document's own rule -- 'a FACT resting only on UNVERIFIED citations has not been checked by anything' -- condemned the node's own ledger entry 1, the provenance record, whose only citation is a commit reference. Resolved by stating the one carve-out narrowly and giving it a real check (git cat-file -e <sha>, run, exit 0) rather than by quietly exempting it. A commit citation on a claim about repository CONTENT is explicitly still not covered. Ledger entry 11 overreached: 'every change under launchpad/docs/corpus is gated in CI' where the workflow triggers on pull requests and pushes to launchpad. Narrowed to what the workflow actually does. Also records a scope judgement the DoD asks for: the citation-shape table is reference material rather than instruction and belongs in the evidence standard (#1314) when that lands. Noted in place rather than folded in silently. Verified: all 11 ledger entries resolve, every body path is a real file, merge-base equals the recorded revision, and exactly one hand-authored .md was added under the corpus root. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * test(corpus): strengthen real-root discovery test (#636, review-tests finding) An independent review-tests pass defeated the first replacement: patching discover_markdown_files to a hardcoded 'return [root / "AGENTS.md"]' -- a constant that never touches the filesystem -- satisfied both 'non-empty' and 'nothing from schema/' while proving neither discovery nor exclusion. My own negative control had broken the exclusion set and emptied the corpus, but never tried a constant. Showing that assertions CAN fail is not the same as showing they can only pass for the right reason. The expectation is now derived from an independent filesystem walk and compared for equality, which catches partial walks and both exclusion directions. It does NOT catch the constant today, and the docstring says so rather than claiming otherwise: the corpus holds exactly one node, so [root/'AGENTS.md'] IS the correct answer and no assertion against the real tree can tell a constant from a real walk. That resolves itself at the second node. test_sibling_discovered_schema_dir_excluded catches it now -- verified FAIL under that mutant -- and is the real proof of discovery behaviour. Measured, after fixing a mutation harness that had patched a different module instance than the test uses and so reported every mutant as caught: returns nothing -> FAIL (caught) exclusion disabled -> FAIL (caught) constant return -> PASS (not caught; sibling catches it) Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address review-code findings on AGENTS.md (#636) FINDING A -- the carve-out claimed enforcement it does not have. 'There is exactly one carve-out, and widening it defeats the rule' read as though some check held the line. Nothing does: a second, third or tenth commit-only FACT produces extra non-fatal UNVERIFIED notices and still exits 0, which the reviewer proved by adding one. Now says plainly that this is a convention a REVIEWER holds, names the signal to look for (more than one commit-only FACT in a ledger), and states that no check will raise it. FINDING B -- create-node step 3 asked the author to record four categories of evidence and gave a destination to exactly one of them. Adds the mapping: revision to the ledger, sources to the citations of the claims they support, and expected-but-unverified to the body's scope section. Plus the rule that anything reaching none of the three was either a missing claim or a stale note. Applying B to this node exposed that it did not follow its own new rule -- the same self-contradiction class STEP 8 caught. Its scope section now names the two things unverified when it was written: no agent harness was tested reading it as a resolved AGENTS.md, and relationships.schema.json's enum was never read directly. Re-audited after the change: exactly one commit-only FACT remains, the enforcement claim is gone, validator exits 0, 79 tests pass. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address the cross-model final review (#636) Codex review-final returned REQUEST_CHANGES with seven conditions, then a second pass confirmed five fixed and refused two. Both refusals were correct. This commit is the whole of that work. 1. LEDGER COMPLETENESS. Seven entries added (11 -> 18) for claims the body taught with no classified entry behind them: the evidence classes, the "supersedes" vocabulary, the syntax-only GitHub check, the external-URL verdict, and the delete-vs-retire distinction. The recheck caught two of these still wrong. An entry said the schema "permits no field outside that set" after listing the six required fields -- it also permits "relationships", so the entry was wrong about the file it cited. And the "supersedes" entry cited node.schema.json, which carries only the enum; the directionality it asserts ("source replaces target; target becomes historical") lives in relationships.schema.json:30, a file this node had listed as read-but-unverified and cited anyway. Read it, cited it. 2. RETIREMENT WAS BUILT ON A FALSE PREMISE. It claimed inbound relationships "no longer resolve" after retirement. Measured: with the file retained -- which the same procedure requires -- they resolve fine; only deletion breaks them. Rewritten so the reason matches the mechanism, the danger is stated correctly (edges keep pointing at a node that stopped being current, and nothing warns you), and a replacement declares "supersedes", which the procedure never mentioned. 3. CITATION TABLE OVERSTATED VERIFICATION. A GitHub link pinned to a real SHA naming a file that never existed returns ok -- the checker never contacts GitHub. The table now carries a "does it prove the target exists?" column, gains the external-URL row it was missing, and names the trap. All seven rows independently confirmed against the classifier. 4. PROVENANCE SEMANTICS -- and the defect the recheck was right to refuse. The update procedure said to bump the revision on every edit; this node kept its original across seven. The first attempt defined the field as an evidence snapshot and classified that as INFERENCE citing node.schema.json. That schema says nothing whatever about revisions, so the citation could not support the statement: a policy choice had been given a class that made it look derived. It is now TEAM_KNOWLEDGE attributed to #636's own definition of done -- "the draft is checked against the repository revision recorded in provenance" -- which is where the meaning comes from. ADR-0029 requires exactly that: history and team knowledge stay attributed rather than being promoted to fact. The node also claimed it was "the only authored node in the corpus" at the recorded revision. It did not exist at that revision and neither did any other node: git cat-file -e on that path at that sha exits 128, and the corpus held zero .md files outside schema/. Corrected. The revision still does not move, and the reason is checkable rather than asserted: every cited source is byte-identical between that revision and now, so verifying a claim at HEAD and at the recorded revision were the same act. git diff --name-only <sha> -- <cited paths> returns nothing. 5. AUDIENCE. Added "reviewer": the document assigns reviewers an enforcement duty no tool performs, so they are addressed by it. The adjudicator had declined this finding (nothing consumes the field today); the final pass argued audience describes who is addressed, not who parses it. Went with the latter. Whether "developer" also belongs is left undecided. 6. The test module docstring claimed absolute fixture isolation that was false before this branch and more visibly false after it. States the rule with its one named exception instead. Flagged independently by the adjudicator and the final pass. Pre-existing, fixed while in the file: the same docstring advertised "python3 -m unittest launchpad.project_intelligence.corpus.tests.test_validate", which has never worked -- the directory is "project-intelligence" with a hyphen, not a legal package name. Confirmed ModuleNotFoundError, removed. 7. Full suites re-run in a writable environment: 79 validator tests OK, 27 schema tests OK, real corpus exit 0. The plan's two-node create/update/ retire charter ran with its negative control -- a deliberately invalid node failed and named itself, proving the scratch root was the one scanned; retirement kept beta's edge resolving; deleting alpha instead broke it exactly as the corrected text now says. Final state: 18 ledger entries, schema errors 0, every citation resolves, no cited source changed since the recorded revision, exactly one commit-only FACT, 79 + 27 tests green, real corpus exit 0. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): standard for the confidence field (#1309) Adds launchpad/docs/corpus/standards/confidence.md, the canonical policy node for the confidence key on an evidence entry. The field is a number in a closed interval, required for INFERENCE only and forbidden on FACT and TEAM_KNOWLEDGE. The schema holds that much. What it cannot hold is what the number means, and an uninterpreted number between 0 and 1 is decoration -- an author picks 0.8 because it feels about right and a reader treats it as calibrated. The standard states that no calibration exists or is collected, that values are not comparable between authors, and that no confidence however high promotes an INFERENCE to a FACT. Its load-bearing section separates reasoning from evidence from dressing up a decision: an entry whose citation supports the subject of a claim but not the choice the claim makes is a decision in disguise and belongs in TEAM_KNOWLEDGE. That move was caught by cross-model review on #636 and no check can catch it, so it is documented with both worked examples. Records a measured divergence: a NaN confidence satisfies node.schema.json and passes corpus validation while memory.py rejects the identical value, because every comparison against NaN is false. Filed as #1463; the standard carries a MUST for finite values to cover it until that lands. Declares no relationships -- every sibling standard is unmerged, and a target naming an id no loaded node carries is a hard validation error. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): stop asserting provenance policy this node cannot source (#636) Third cross-model pass, third refusal on the same condition. It was right three times, and the third refusal finally identified why the first two fixes failed: the document was writing corpus-wide provenance policy it had no authority to write. Reclassifying it (INFERENCE, then TEAM_KNOWLEDGE) only changed the label on an unsourced decision. WHAT ACTUALLY CHANGED: the document stops making the claim. #1321 ("document corpus standard for provenance", unlanded) owns the question of whether a recorded revision may stay put across edits. This node now says so, states its own approach as working practice pending that standard rather than as a rule others must follow, and defers. The ledger entry no longer asserts the policy -- it records only what #636's definition of done actually requires, which is a fact about the issue and is attributed to it. Also fixes a self-contradiction the last fix opened, exactly the failure shape this repository keeps producing. The previous commit added a FACT citing relationships.schema.json for the supersedes directionality. The same node still said, in its unverified list, that relationships.schema.json had never been read. Both could not stand. The file WAS read; the stale disclaimer is now narrowed to what remains genuinely unchecked -- that the two enum lists agree, which rests on a test that was not run. Ledger completeness, third sweep: entries added for the generated-artifact rejection, the non-enforcement of the one-commit-only-FACT convention, and positional citations not being bounds-checked. Prose that asserted this node's own history -- node counts at the recorded revision, byte-identity of cited sources -- is removed rather than given entries: it told the reader to trust the document about a fact they can check themselves in one command, so the document now gives them the command instead. TEAM_KNOWLEDGE's description corrected. It said "a person told you", which the schema does not require (it asks who or what told the corpus) and which made the class look unavailable for an attributed issue or decision record. It now also says outright what it is not: a place to park a decision you made yourself. Attributing an extrapolation to the thing it started from does not make it something you were told. Updating step 4 restated as four explicit branches including the byte-identical case, which the earlier "bump on every edit" wording omitted while this very node relied on it -- the inconsistency the pass named. Verified: 21 ledger entries, schema errors 0, every citation resolves, exactly one commit-only FACT, no cited source changed since the recorded revision, 79 validator tests OK, real corpus exit 0. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): make the confidence standard obey itself (#1309) Two review findings, both adjudicated Medium, both defects in this document rather than in anything it describes. A standard that contradicts itself teaches the wrong lesson to every sibling that copies it, so both are fixed here rather than deferred. review-code, confirmed by review-adjudicate: the Guidance section said to pick one number per band and reuse it, but never said which number belonged to which band -- and then shipped two INFERENCE entries at two different values with no band label and no stated reason. The bands now carry explicit pegs of 0.8, 0.6 and 0.4, the criterial column is named so the illustrations stop reading as tests, and this node's own two entries are labelled to their bands as the nearest worked example. The scope-and-omissions entry now disclaims the specific values, not just the idea of banding: they are agreed pegs so two authors mean the same thing by the same numeral, and they measure nothing. review-plan, surviving into the document and caught by review-adjudicate: the header claimed the field-combination rules and enum members were deliberately not copied, while Requirement 1 restates the confidence row and names all three classes. A standard about this field cannot omit the field's own rule, so the claim was the wrong half to keep. The header now names that one restatement as a deliberate drift surface and points at the two places that must change if the schema's rule ever does. One neighbouring hole opened by the first fix and closed here: Requirement 6 forbids moving a number, and the second entry moved from 0.7 to 0.6. Snapping an unchanged assessment onto a band peg is not the move that rule prohibits, but from a diff the two are indistinguishable, so Requirement 6 now says so and requires the reason be recorded. This message is that record. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): fix the standard's claims about itself (#1309) review-final found nine findings, and the cluster that mattered was not about confidence at all -- it was the document's claims about its own contents. For a node whose whole subject is not asserting more than the evidence supports, and which roughly 45 siblings are meant to copy, that is the report. All nine are defects in this document, all small, all inside the issue's scope, so they are fixed here rather than deferred. High. The stated reason for declaring no relationships was false. It said there was nothing this node could legally point at, and the next sentence named AGENTS.md. AGENTS.md carries id corpus-agents, is a loaded node, and a references edge to it validates today. The convention of declaring none is settled and unchanged; the justification is now the true one -- add the edge set in one pass once the siblings land -- and names corpus-agents as the first edge to make. The same false premise was in the plan's LEFT OUT and is corrected there too. Medium, four of them. The Requirements preamble claimed the first three MUSTs were machine-enforced while Requirement 3 said the schema does not reach it; it now says two. The header named two places tracking the schema when three do, and is now a table. The peg rationale asserted cross-author shared meaning that the reader rules and this node's own 0.8 INFERENCE both deny; adopting pegs is now stated as a hope about future practice, never as something a reader may sort on. The only-INFERENCE-in-the-corpus FACT cited AGENTS.md alone, which cannot establish a quantifier -- it now also cites validate.py for the schema/ exclusion and the fixture that carries the other one, which is exactly the merely-on-topic citation this document calls the tell. Medium, and the sharpest. Requirement 6's carve-out claimed re-encoding means the judgement is identical and only its expression changed. Measured against the document's own rules that is false: 0.7 to 0.6 moved the entry a full band from its neighbour, and ranking within a class is the one thing the number legitimately does. The carve-out now says re-encoding preserves the assessment but can change the rank, requires the record to name the Means row it matches, and admits there is no tie-break for a midpoint. Low, two. A number between 0 and 1 restated bounds the header said were left to the schema. And MUST be finite was overbroad -- .inf and -.inf are both caught on range, so NaN is the single value that escapes, because every comparison against it is false and the assertions never fire. Requirement 3 and the enforcement table both say so now. Per Requirement 6 as amended: the 0.6 entry matches the Medium band's Means row, an absence-of-evidence argument over a scope that was checked, because its scope is three files rather than the repository. Not fixed here: the NaN divergence itself stays filed as #1463, since the schema sits outside this issue's scope. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): fix four AGENTS.md defects found by authoring against it (#636) Three sibling agents authored corpus nodes following this document literally, and reported what broke. These four are the defects that produced real false claims in their work, not stylistic complaints. Filed as #1471; fixed here because #1462 is still open and 45 further nodes would inherit them. 1. THE ONLY-NODE JUSTIFICATION WENT FALSE, AND TWO AGENTS COPIED IT. "A node with no sibling to point at correctly declares none" read as a general rule. It was a fact about one moment -- when this was the corpus's only node -- and stopped holding as soon as a second existed. Both the #1309 and #1307 agents reproduced the reasoning and wrote a justification that was false at the time they wrote it; their adjudicators caught it, not their own reviews. Now says to enumerate what exists and give the real reason, and records why. 2. AN ISSUE-ONLY SOURCE HAD NO HONEST CLASS. The citation table lists tool results as legitimate while "Three things a passing run does not mean" says a FACT resting only on UNVERIFIED citations "has not been checked by anything", with one named exception. A claim whose only source is a GitHub issue has no openable form and cannot be SHA-pinned, so both passages applied and disagreed. The #1307 agent hit this with nothing honest to choose. Now states it: TEAM_KNOWLEDGE with provided_by naming the issue, which is what ADR-0029 requires of GitHub history anyway. 3. A RANGE WAS READ AS A MAPPING. The not-covered table paired eight subjects with "#1307-#1351", which is 45 issues and no subject-to-issue pairing at all. The #1307 agent cited this table for nine invented mappings -- exactly the "FACT cited to a file that does not discuss the claim" failure the document warns about two sections earlier. Now says outright that it is a range, not a mapping, and gives the search command. 4. CREATE STEP 3 ROUTED SOMETHING STEP 8 DID NOT ACCEPT. Step 3 sends expected-but-unverified items to the scope section; step 8 said only "state what the node does not cover". A boundary and a confidence disclosure are different things, so working the steps in order left that category homeless. Both the #1307 and #1309 agents reported it independently. Step 8 now names both. Verified: schema errors 0, 21 ledger entries, validator exit 0, 79 tests OK. Refs #636 #1471 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): fix a false attribution and the relationship-target trap (#636) Two more defects reported by the #1308 agent after authoring against this document. Both are mine, and the first is embarrassing in context. 1. THE CITATION TABLE MISATTRIBUTED ITSELF, IN THE SECTION ABOUT NOT DOING THAT. It said "CONTRACT.md §3 defines the six shapes" above a SEVEN-row table whose GitHub-link and external-URL rows appear nowhere in §3. Measured: grepping that section for http/url/github returns zero hits. So the sentence cited a file that does not discuss two of the rows beneath it -- the exact failure this document warns about, committed by this document. The #1308 agent took the table as an accurate summary and built a scope argument on "four of six shapes name code" before their plan review caught it. Now states what §3 actually enumerates, that the two URL forms are the validator's and not §3's, and that the table is seven rows and not a summary. 2. "ADD RELATIONSHIPS ONLY TO NODES THAT EXIST" WAS UNFOLLOWABLE, AND DANGEROUSLY SO. "Exist" was ambiguous between the branch you are working on and the branch you merge into, and the checker loads whatever is present where it runs. An agent branched off this unmerged node could therefore target `corpus-agents`, validate clean locally, and break CI on `launchpad`, where the node is absent. The #1308 agent hit exactly that and their review-code raised it as a Blocker. Step 9 now says "exists on the branch you are merging INTO", explains why the distinction is the trap, and gives the command to check the merge base rather than the local tree. This is the second round of AGENTS.md fixes sourced from agents authoring against it, and the pattern is worth naming: every defect found this way was invisible to three review passes over the document itself, and obvious within minutes to someone trying to follow it. Reviewing a procedure is not the same as executing one. Verified: validator exit 0, 79 tests OK, schema clean. Refs #636 #1471 #1478 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> --------- Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
* docs(corpus): plan for issue #636 -- launchpad/docs/corpus/AGENTS.md Reviewed by an independent cross-model review-plan pass (Codex, gpt-5.6-sol, high effort): eleven findings, all applied. Three more were found afterwards, the load-bearing one being that the plan blocked its own STEP 2 on an OPEN item -- an OPEN item a step must consume is a missing input, not an open question. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * feat(corpus): AGENTS.md frontmatter and skeleton (#636 STEPs 2-3) The corpus's first hand-authored node. Frontmatter validates against node.schema.json with zero errors; 11 evidence entries; no relationships, because no sibling node exists to target and an unresolved target is a hard validator error. The repository revision is recorded in the evidence ledger -- the schema calls that array the provenance ledger and permits no other field, so it is the only schema-legal place for it. STEP 3 (RUNS HERE) proved the validator actually scans the node rather than skipping it: mutating type: to a value outside the enum produced 'FAIL corpus-agents: schema violation at type' and exit 1, naming the node's id; restoring it returned exit 0. Also replaces test_real_corpus_root_currently_has_no_content_outside_schema, which asserted the corpus root is EMPTY and therefore could not survive the first authored node. Authorised by Serina as a deliberate widening of the plan's LEFT OUT scope, recorded in the plan. The replacement asserts what the original's own comment said it was for -- content present AND nothing from schema/ -- and both halves were negative-controlled before being trusted. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md node-contract section (#636 STEP 4) Adds the lookup table of authoritative sources and the 'what a corpus node is' section: one file per node, one idea per node, where nodes go, id permanence, relationship resolution, and the authored-vs-generated boundary. Reproduces no enum member list and no field-combination matrix -- those stay canonical in node.schema.json and schema/README.md and are linked instead. The checker never reads body prose, so a copy here would stay green while drifting. Verified: no type-enum member and none of entry_class/provided_by/ confidence appear in the body. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md evidence and citation section (#636 STEP 5) The part that lives nowhere else: what the checker actually does with each of CONTRACT.md's six citation shapes, and three things a passing run does not mean -- that a citation supports its claim (checking is structural, the file is never opened and compared), that UNVERIFIED is a pass, or that a line number was verified (#1459). Names the three evidence classes and what each is FOR, but not which fields the schema requires per class -- that matrix stays in node.schema.json. Verified absent from the body: entry_class, provided_by, confidence. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md create/update/retire procedures (#636 STEP 6) This is #605's acceptance criterion for the file -- a cold-start agent creating, updating or retiring one node without oral guidance. Three numbered procedures, each ending in the same check command. Adds a 'Running the check' section giving the interpreter form (works without Hermit; the just recipe needs activation first) and documenting --root, since without it the command always validates the real corpus whatever directory you are standing in. Retire covers the three things most likely to be got wrong: inbound relationships resolved before retirement, status set rather than the file deleted, and the id never reused or renamed. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md scope and omissions (#636 STEP 7) Names each deferred area with its owning issue rather than leaving a reader unable to tell a gap from an omission: the standards and templates (#1307-#1351, none merged), generated-artifact provenance (#1316), claim-type and flagged state (#1410), the human entry point (#639), and unverified line numbers in citations (#1459). Records why this node carries no relationships -- at its recorded revision it was the only authored node, and a target no node carries is a hard error -- so the absence reads as deliberate rather than forgotten. Every repo path cited in the body verified to resolve to a real file. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): audit AGENTS.md against its own ledger (#636 STEP 8) The audit found two things the earlier steps had missed. The document's own rule -- 'a FACT resting only on UNVERIFIED citations has not been checked by anything' -- condemned the node's own ledger entry 1, the provenance record, whose only citation is a commit reference. Resolved by stating the one carve-out narrowly and giving it a real check (git cat-file -e <sha>, run, exit 0) rather than by quietly exempting it. A commit citation on a claim about repository CONTENT is explicitly still not covered. Ledger entry 11 overreached: 'every change under launchpad/docs/corpus is gated in CI' where the workflow triggers on pull requests and pushes to launchpad. Narrowed to what the workflow actually does. Also records a scope judgement the DoD asks for: the citation-shape table is reference material rather than instruction and belongs in the evidence standard (#1314) when that lands. Noted in place rather than folded in silently. Verified: all 11 ledger entries resolve, every body path is a real file, merge-base equals the recorded revision, and exactly one hand-authored .md was added under the corpus root. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * test(corpus): strengthen real-root discovery test (#636, review-tests finding) An independent review-tests pass defeated the first replacement: patching discover_markdown_files to a hardcoded 'return [root / "AGENTS.md"]' -- a constant that never touches the filesystem -- satisfied both 'non-empty' and 'nothing from schema/' while proving neither discovery nor exclusion. My own negative control had broken the exclusion set and emptied the corpus, but never tried a constant. Showing that assertions CAN fail is not the same as showing they can only pass for the right reason. The expectation is now derived from an independent filesystem walk and compared for equality, which catches partial walks and both exclusion directions. It does NOT catch the constant today, and the docstring says so rather than claiming otherwise: the corpus holds exactly one node, so [root/'AGENTS.md'] IS the correct answer and no assertion against the real tree can tell a constant from a real walk. That resolves itself at the second node. test_sibling_discovered_schema_dir_excluded catches it now -- verified FAIL under that mutant -- and is the real proof of discovery behaviour. Measured, after fixing a mutation harness that had patched a different module instance than the test uses and so reported every mutant as caught: returns nothing -> FAIL (caught) exclusion disabled -> FAIL (caught) constant return -> PASS (not caught; sibling catches it) Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address review-code findings on AGENTS.md (#636) FINDING A -- the carve-out claimed enforcement it does not have. 'There is exactly one carve-out, and widening it defeats the rule' read as though some check held the line. Nothing does: a second, third or tenth commit-only FACT produces extra non-fatal UNVERIFIED notices and still exits 0, which the reviewer proved by adding one. Now says plainly that this is a convention a REVIEWER holds, names the signal to look for (more than one commit-only FACT in a ledger), and states that no check will raise it. FINDING B -- create-node step 3 asked the author to record four categories of evidence and gave a destination to exactly one of them. Adds the mapping: revision to the ledger, sources to the citations of the claims they support, and expected-but-unverified to the body's scope section. Plus the rule that anything reaching none of the three was either a missing claim or a stale note. Applying B to this node exposed that it did not follow its own new rule -- the same self-contradiction class STEP 8 caught. Its scope section now names the two things unverified when it was written: no agent harness was tested reading it as a resolved AGENTS.md, and relationships.schema.json's enum was never read directly. Re-audited after the change: exactly one commit-only FACT remains, the enforcement claim is gone, validator exits 0, 79 tests pass. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address the cross-model final review (#636) Codex review-final returned REQUEST_CHANGES with seven conditions, then a second pass confirmed five fixed and refused two. Both refusals were correct. This commit is the whole of that work. 1. LEDGER COMPLETENESS. Seven entries added (11 -> 18) for claims the body taught with no classified entry behind them: the evidence classes, the "supersedes" vocabulary, the syntax-only GitHub check, the external-URL verdict, and the delete-vs-retire distinction. The recheck caught two of these still wrong. An entry said the schema "permits no field outside that set" after listing the six required fields -- it also permits "relationships", so the entry was wrong about the file it cited. And the "supersedes" entry cited node.schema.json, which carries only the enum; the directionality it asserts ("source replaces target; target becomes historical") lives in relationships.schema.json:30, a file this node had listed as read-but-unverified and cited anyway. Read it, cited it. 2. RETIREMENT WAS BUILT ON A FALSE PREMISE. It claimed inbound relationships "no longer resolve" after retirement. Measured: with the file retained -- which the same procedure requires -- they resolve fine; only deletion breaks them. Rewritten so the reason matches the mechanism, the danger is stated correctly (edges keep pointing at a node that stopped being current, and nothing warns you), and a replacement declares "supersedes", which the procedure never mentioned. 3. CITATION TABLE OVERSTATED VERIFICATION. A GitHub link pinned to a real SHA naming a file that never existed returns ok -- the checker never contacts GitHub. The table now carries a "does it prove the target exists?" column, gains the external-URL row it was missing, and names the trap. All seven rows independently confirmed against the classifier. 4. PROVENANCE SEMANTICS -- and the defect the recheck was right to refuse. The update procedure said to bump the revision on every edit; this node kept its original across seven. The first attempt defined the field as an evidence snapshot and classified that as INFERENCE citing node.schema.json. That schema says nothing whatever about revisions, so the citation could not support the statement: a policy choice had been given a class that made it look derived. It is now TEAM_KNOWLEDGE attributed to #636's own definition of done -- "the draft is checked against the repository revision recorded in provenance" -- which is where the meaning comes from. ADR-0029 requires exactly that: history and team knowledge stay attributed rather than being promoted to fact. The node also claimed it was "the only authored node in the corpus" at the recorded revision. It did not exist at that revision and neither did any other node: git cat-file -e on that path at that sha exits 128, and the corpus held zero .md files outside schema/. Corrected. The revision still does not move, and the reason is checkable rather than asserted: every cited source is byte-identical between that revision and now, so verifying a claim at HEAD and at the recorded revision were the same act. git diff --name-only <sha> -- <cited paths> returns nothing. 5. AUDIENCE. Added "reviewer": the document assigns reviewers an enforcement duty no tool performs, so they are addressed by it. The adjudicator had declined this finding (nothing consumes the field today); the final pass argued audience describes who is addressed, not who parses it. Went with the latter. Whether "developer" also belongs is left undecided. 6. The test module docstring claimed absolute fixture isolation that was false before this branch and more visibly false after it. States the rule with its one named exception instead. Flagged independently by the adjudicator and the final pass. Pre-existing, fixed while in the file: the same docstring advertised "python3 -m unittest launchpad.project_intelligence.corpus.tests.test_validate", which has never worked -- the directory is "project-intelligence" with a hyphen, not a legal package name. Confirmed ModuleNotFoundError, removed. 7. Full suites re-run in a writable environment: 79 validator tests OK, 27 schema tests OK, real corpus exit 0. The plan's two-node create/update/ retire charter ran with its negative control -- a deliberately invalid node failed and named itself, proving the scratch root was the one scanned; retirement kept beta's edge resolving; deleting alpha instead broke it exactly as the corrected text now says. Final state: 18 ledger entries, schema errors 0, every citation resolves, no cited source changed since the recorded revision, exactly one commit-only FACT, 79 + 27 tests green, real corpus exit 0. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): stop asserting provenance policy this node cannot source (#636) Third cross-model pass, third refusal on the same condition. It was right three times, and the third refusal finally identified why the first two fixes failed: the document was writing corpus-wide provenance policy it had no authority to write. Reclassifying it (INFERENCE, then TEAM_KNOWLEDGE) only changed the label on an unsourced decision. WHAT ACTUALLY CHANGED: the document stops making the claim. #1321 ("document corpus standard for provenance", unlanded) owns the question of whether a recorded revision may stay put across edits. This node now says so, states its own approach as working practice pending that standard rather than as a rule others must follow, and defers. The ledger entry no longer asserts the policy -- it records only what #636's definition of done actually requires, which is a fact about the issue and is attributed to it. Also fixes a self-contradiction the last fix opened, exactly the failure shape this repository keeps producing. The previous commit added a FACT citing relationships.schema.json for the supersedes directionality. The same node still said, in its unverified list, that relationships.schema.json had never been read. Both could not stand. The file WAS read; the stale disclaimer is now narrowed to what remains genuinely unchecked -- that the two enum lists agree, which rests on a test that was not run. Ledger completeness, third sweep: entries added for the generated-artifact rejection, the non-enforcement of the one-commit-only-FACT convention, and positional citations not being bounds-checked. Prose that asserted this node's own history -- node counts at the recorded revision, byte-identity of cited sources -- is removed rather than given entries: it told the reader to trust the document about a fact they can check themselves in one command, so the document now gives them the command instead. TEAM_KNOWLEDGE's description corrected. It said "a person told you", which the schema does not require (it asks who or what told the corpus) and which made the class look unavailable for an attributed issue or decision record. It now also says outright what it is not: a place to park a decision you made yourself. Attributing an extrapolation to the thing it started from does not make it something you were told. Updating step 4 restated as four explicit branches including the byte-identical case, which the earlier "bump on every edit" wording omitted while this very node relied on it -- the inconsistency the pass named. Verified: 21 ledger entries, schema errors 0, every citation resolves, exactly one commit-only FACT, no cited source changed since the recorded revision, 79 validator tests OK, real corpus exit 0. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): README.md front matter and what-is-here-today (#639 STEP 1) Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): README.md routing table to the authoritative sources (#639 STEP 2) Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): README.md how the corpus is checked, and what a pass does not establish (#639 STEP 3) Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): README.md scope, omissions, and the audiences and type decisions (#639 STEP 4) Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): audit README.md against its own ledger (#639 STEP 5) Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address review-code and adjudication findings (#639) Rebased onto a1e8bbc first. The branch was cut from 60d4947, which was the tip of task/636-corpus-agents-md at the time; #636 advanced one commit 38 minutes later and that commit is the precedent for the Blocker below. The recorded revision moves to a1e8bbc because every claim was re-verified against it, not because the file was edited. BLOCKER (confirmed by adjudication). The TEAM_KNOWLEDGE entry attributed "declare no relationships" to a shared task brief. No such convention is recorded anywhere -- the corpus-plan issue template says the opposite, asking for "typed relationships appropriate to the node". Following a1e8bbc's fix for the identical defect on #636: the document stops making the claim. The entry is removed and the body now states the absence as this node's own decision, explicitly not a rule for the corpus, and defers the question to the per-type standards that own it. HIGH (confirmed). A FACT claimed #1307-#1351 were #605's forty-five children with none merged, citing schema/README.md and AGENTS.md. Both were checked: schema/README.md names no issue in that range and says nothing about merge status; AGENTS.md never mentions #605 at all. The claim is true but was sourced to files that do not establish it -- the #636 defect exactly. Split into two entries that say only what each cited file says, and the count is dropped from both ledger and body. MEDIUM (confirmed, relocated by adjudication). The page promised "Nothing here restates a rule" and then restated two, and said three limits were "not repeated here" three lines after summarising them. The restatements are legitimate orientation for a door; the absolute promises were false. The stated policy now matches what the document does: no rule is owned here, every summary names its owner, and the owner wins. Also, following a1e8bbc's model: prose asserting how many nodes the corpus holds is replaced by the command that lists them. That removes a count which goes stale silently, and removes the one tool-result citation with it -- the ledger's only remaining UNVERIFIED entry is now the provenance commit. A fix opened its neighbour, as this repository keeps doing. Adding the enumeration command gave the body a second fenced block, which desynchronised the STEP 2 gate's inline-backtick pairing and collapsed its path count from 13 to 1. The >=6 floor added after review-plan is the only reason that surfaced instead of passing quietly. The gate now strips fenced blocks before scanning; commands belong in fences, citations belong in prose. Verified: 21 ledger entries, exactly one commit-only FACT, no FACT without evidence, 15 backticked paths and 18 link targets all resolve, no cited source has changed since a1e8bbc, validator exit 0, 79 validator tests OK, 27 schema tests OK. Refs #639 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address the final-review findings on README.md (#639) Six findings from the final pass, all fixed. The Blocker was process, not content; the rest were the correspondence invariant breaking again in the commit that fixed the previous round. BLOCKER -- no step ledger existed. Diagnosed rather than patched: the installed serina-skills plugin cache is pinned at 0.11.0 (dated Aug 21) whose build-change ships SKILL.md alone and never mentions a ledger, while the marketplace copy (Aug 25) carries record-step.sh and names it nine times. The skill as served has no ledger step, which is why sibling #636 has none either. The ledger is now recorded from this session's own knowledge of each step's verified done-when, not inferred from commit messages -- the distinction record-step.sh's own header insists on. It lands in .superpowers/, which is git-excluded, so it is a local artefact and not part of this diff. MEDIUM -- evidence entry 11 was orphaned by the previous commit. Splitting the #605 FACT in two while deleting the body's only #605 sentence left the #622/#605/#602 lineage entry with no body claim. Exactly the neighbour-hole shape this repository keeps producing, opened by the fix for the last round. The lineage is now stated in the body, where a human arriving at a half-built corpus needs it. MEDIUM -- "first developer audience, first deliberate exclusion" was a substantive claim with no ledger entry, and merge-order-fragile: any sibling landing first with developer falsifies it and nothing checks. Now pinned to the recorded revision, ledgered as a FACT against AGENTS.md's own audiences, and it says outright that a later sibling may get there first. MEDIUM -- the rewritten policy promised "every one of them names the file that owns it" and then the very next section named none. The same absolute-promise defect the rewrite existed to fix, reintroduced one paragraph lower. "What a node is" now names ADR-0028 and AGENTS.md as its owners. LOW -- entry 12 said AGENTS.md names three further gaps; it names four. The fourth is #639, this node -- so the undercount hid the fact that AGENTS.md declares this page as one of its own gaps. Corrected, and the body now says the two nodes are a declared pair rather than overlapping documents. LOW -- the published enumeration command is cwd-sensitive; its section now says to run it from the repository root, as the validator's section already did. LOW -- "parent feature #605" did not match the cited source's "parent PRD #605". Reworded to what schema/README.md actually says. Verified after the fixes: 22 ledger entries, all 22 corresponding to a body claim and no body claim unledgered (checked entry by entry), exactly one commit-only FACT, no FACT without evidence, 17 backticked paths and 20 link targets resolve, no cited source changed since a1e8bbc, the published command runs and prints both nodes, validator exit 0, 79 validator tests OK, 27 schema tests OK. Refs #639 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): fix four AGENTS.md defects found by authoring against it (#636) Three sibling agents authored corpus nodes following this document literally, and reported what broke. These four are the defects that produced real false claims in their work, not stylistic complaints. Filed as #1471; fixed here because #1462 is still open and 45 further nodes would inherit them. 1. THE ONLY-NODE JUSTIFICATION WENT FALSE, AND TWO AGENTS COPIED IT. "A node with no sibling to point at correctly declares none" read as a general rule. It was a fact about one moment -- when this was the corpus's only node -- and stopped holding as soon as a second existed. Both the #1309 and #1307 agents reproduced the reasoning and wrote a justification that was false at the time they wrote it; their adjudicators caught it, not their own reviews. Now says to enumerate what exists and give the real reason, and records why. 2. AN ISSUE-ONLY SOURCE HAD NO HONEST CLASS. The citation table lists tool results as legitimate while "Three things a passing run does not mean" says a FACT resting only on UNVERIFIED citations "has not been checked by anything", with one named exception. A claim whose only source is a GitHub issue has no openable form and cannot be SHA-pinned, so both passages applied and disagreed. The #1307 agent hit this with nothing honest to choose. Now states it: TEAM_KNOWLEDGE with provided_by naming the issue, which is what ADR-0029 requires of GitHub history anyway. 3. A RANGE WAS READ AS A MAPPING. The not-covered table paired eight subjects with "#1307-#1351", which is 45 issues and no subject-to-issue pairing at all. The #1307 agent cited this table for nine invented mappings -- exactly the "FACT cited to a file that does not discuss the claim" failure the document warns about two sections earlier. Now says outright that it is a range, not a mapping, and gives the search command. 4. CREATE STEP 3 ROUTED SOMETHING STEP 8 DID NOT ACCEPT. Step 3 sends expected-but-unverified items to the scope section; step 8 said only "state what the node does not cover". A boundary and a confidence disclosure are different things, so working the steps in order left that category homeless. Both the #1307 and #1309 agents reported it independently. Step 8 now names both. Verified: schema errors 0, 21 ledger entries, validator exit 0, 79 tests OK. Refs #636 #1471 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): fix a false attribution and the relationship-target trap (#636) Two more defects reported by the #1308 agent after authoring against this document. Both are mine, and the first is embarrassing in context. 1. THE CITATION TABLE MISATTRIBUTED ITSELF, IN THE SECTION ABOUT NOT DOING THAT. It said "CONTRACT.md §3 defines the six shapes" above a SEVEN-row table whose GitHub-link and external-URL rows appear nowhere in §3. Measured: grepping that section for http/url/github returns zero hits. So the sentence cited a file that does not discuss two of the rows beneath it -- the exact failure this document warns about, committed by this document. The #1308 agent took the table as an accurate summary and built a scope argument on "four of six shapes name code" before their plan review caught it. Now states what §3 actually enumerates, that the two URL forms are the validator's and not §3's, and that the table is seven rows and not a summary. 2. "ADD RELATIONSHIPS ONLY TO NODES THAT EXIST" WAS UNFOLLOWABLE, AND DANGEROUSLY SO. "Exist" was ambiguous between the branch you are working on and the branch you merge into, and the checker loads whatever is present where it runs. An agent branched off this unmerged node could therefore target `corpus-agents`, validate clean locally, and break CI on `launchpad`, where the node is absent. The #1308 agent hit exactly that and their review-code raised it as a Blocker. Step 9 now says "exists on the branch you are merging INTO", explains why the distinction is the trap, and gives the command to check the merge base rather than the local tree. This is the second round of AGENTS.md fixes sourced from agents authoring against it, and the pattern is worth naming: every defect found this way was invisible to three review passes over the document itself, and obvious within minutes to someone trying to follow it. Reviewing a procedure is not the same as executing one. Verified: validator exit 0, 79 tests OK, schema clean. Refs #636 #1471 #1478 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> --------- Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
* docs(corpus): plan for issue #636 -- launchpad/docs/corpus/AGENTS.md Reviewed by an independent cross-model review-plan pass (Codex, gpt-5.6-sol, high effort): eleven findings, all applied. Three more were found afterwards, the load-bearing one being that the plan blocked its own STEP 2 on an OPEN item -- an OPEN item a step must consume is a missing input, not an open question. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * feat(corpus): AGENTS.md frontmatter and skeleton (#636 STEPs 2-3) The corpus's first hand-authored node. Frontmatter validates against node.schema.json with zero errors; 11 evidence entries; no relationships, because no sibling node exists to target and an unresolved target is a hard validator error. The repository revision is recorded in the evidence ledger -- the schema calls that array the provenance ledger and permits no other field, so it is the only schema-legal place for it. STEP 3 (RUNS HERE) proved the validator actually scans the node rather than skipping it: mutating type: to a value outside the enum produced 'FAIL corpus-agents: schema violation at type' and exit 1, naming the node's id; restoring it returned exit 0. Also replaces test_real_corpus_root_currently_has_no_content_outside_schema, which asserted the corpus root is EMPTY and therefore could not survive the first authored node. Authorised by Serina as a deliberate widening of the plan's LEFT OUT scope, recorded in the plan. The replacement asserts what the original's own comment said it was for -- content present AND nothing from schema/ -- and both halves were negative-controlled before being trusted. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md node-contract section (#636 STEP 4) Adds the lookup table of authoritative sources and the 'what a corpus node is' section: one file per node, one idea per node, where nodes go, id permanence, relationship resolution, and the authored-vs-generated boundary. Reproduces no enum member list and no field-combination matrix -- those stay canonical in node.schema.json and schema/README.md and are linked instead. The checker never reads body prose, so a copy here would stay green while drifting. Verified: no type-enum member and none of entry_class/provided_by/ confidence appear in the body. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md evidence and citation section (#636 STEP 5) The part that lives nowhere else: what the checker actually does with each of CONTRACT.md's six citation shapes, and three things a passing run does not mean -- that a citation supports its claim (checking is structural, the file is never opened and compared), that UNVERIFIED is a pass, or that a line number was verified (#1459). Names the three evidence classes and what each is FOR, but not which fields the schema requires per class -- that matrix stays in node.schema.json. Verified absent from the body: entry_class, provided_by, confidence. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md create/update/retire procedures (#636 STEP 6) This is #605's acceptance criterion for the file -- a cold-start agent creating, updating or retiring one node without oral guidance. Three numbered procedures, each ending in the same check command. Adds a 'Running the check' section giving the interpreter form (works without Hermit; the just recipe needs activation first) and documenting --root, since without it the command always validates the real corpus whatever directory you are standing in. Retire covers the three things most likely to be got wrong: inbound relationships resolved before retirement, status set rather than the file deleted, and the id never reused or renamed. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): AGENTS.md scope and omissions (#636 STEP 7) Names each deferred area with its owning issue rather than leaving a reader unable to tell a gap from an omission: the standards and templates (#1307-#1351, none merged), generated-artifact provenance (#1316), claim-type and flagged state (#1410), the human entry point (#639), and unverified line numbers in citations (#1459). Records why this node carries no relationships -- at its recorded revision it was the only authored node, and a target no node carries is a hard error -- so the absence reads as deliberate rather than forgotten. Every repo path cited in the body verified to resolve to a real file. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): audit AGENTS.md against its own ledger (#636 STEP 8) The audit found two things the earlier steps had missed. The document's own rule -- 'a FACT resting only on UNVERIFIED citations has not been checked by anything' -- condemned the node's own ledger entry 1, the provenance record, whose only citation is a commit reference. Resolved by stating the one carve-out narrowly and giving it a real check (git cat-file -e <sha>, run, exit 0) rather than by quietly exempting it. A commit citation on a claim about repository CONTENT is explicitly still not covered. Ledger entry 11 overreached: 'every change under launchpad/docs/corpus is gated in CI' where the workflow triggers on pull requests and pushes to launchpad. Narrowed to what the workflow actually does. Also records a scope judgement the DoD asks for: the citation-shape table is reference material rather than instruction and belongs in the evidence standard (#1314) when that lands. Noted in place rather than folded in silently. Verified: all 11 ledger entries resolve, every body path is a real file, merge-base equals the recorded revision, and exactly one hand-authored .md was added under the corpus root. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * test(corpus): strengthen real-root discovery test (#636, review-tests finding) An independent review-tests pass defeated the first replacement: patching discover_markdown_files to a hardcoded 'return [root / "AGENTS.md"]' -- a constant that never touches the filesystem -- satisfied both 'non-empty' and 'nothing from schema/' while proving neither discovery nor exclusion. My own negative control had broken the exclusion set and emptied the corpus, but never tried a constant. Showing that assertions CAN fail is not the same as showing they can only pass for the right reason. The expectation is now derived from an independent filesystem walk and compared for equality, which catches partial walks and both exclusion directions. It does NOT catch the constant today, and the docstring says so rather than claiming otherwise: the corpus holds exactly one node, so [root/'AGENTS.md'] IS the correct answer and no assertion against the real tree can tell a constant from a real walk. That resolves itself at the second node. test_sibling_discovered_schema_dir_excluded catches it now -- verified FAIL under that mutant -- and is the real proof of discovery behaviour. Measured, after fixing a mutation harness that had patched a different module instance than the test uses and so reported every mutant as caught: returns nothing -> FAIL (caught) exclusion disabled -> FAIL (caught) constant return -> PASS (not caught; sibling catches it) Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address review-code findings on AGENTS.md (#636) FINDING A -- the carve-out claimed enforcement it does not have. 'There is exactly one carve-out, and widening it defeats the rule' read as though some check held the line. Nothing does: a second, third or tenth commit-only FACT produces extra non-fatal UNVERIFIED notices and still exits 0, which the reviewer proved by adding one. Now says plainly that this is a convention a REVIEWER holds, names the signal to look for (more than one commit-only FACT in a ledger), and states that no check will raise it. FINDING B -- create-node step 3 asked the author to record four categories of evidence and gave a destination to exactly one of them. Adds the mapping: revision to the ledger, sources to the citations of the claims they support, and expected-but-unverified to the body's scope section. Plus the rule that anything reaching none of the three was either a missing claim or a stale note. Applying B to this node exposed that it did not follow its own new rule -- the same self-contradiction class STEP 8 caught. Its scope section now names the two things unverified when it was written: no agent harness was tested reading it as a resolved AGENTS.md, and relationships.schema.json's enum was never read directly. Re-audited after the change: exactly one commit-only FACT remains, the enforcement claim is gone, validator exits 0, 79 tests pass. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address the cross-model final review (#636) Codex review-final returned REQUEST_CHANGES with seven conditions, then a second pass confirmed five fixed and refused two. Both refusals were correct. This commit is the whole of that work. 1. LEDGER COMPLETENESS. Seven entries added (11 -> 18) for claims the body taught with no classified entry behind them: the evidence classes, the "supersedes" vocabulary, the syntax-only GitHub check, the external-URL verdict, and the delete-vs-retire distinction. The recheck caught two of these still wrong. An entry said the schema "permits no field outside that set" after listing the six required fields -- it also permits "relationships", so the entry was wrong about the file it cited. And the "supersedes" entry cited node.schema.json, which carries only the enum; the directionality it asserts ("source replaces target; target becomes historical") lives in relationships.schema.json:30, a file this node had listed as read-but-unverified and cited anyway. Read it, cited it. 2. RETIREMENT WAS BUILT ON A FALSE PREMISE. It claimed inbound relationships "no longer resolve" after retirement. Measured: with the file retained -- which the same procedure requires -- they resolve fine; only deletion breaks them. Rewritten so the reason matches the mechanism, the danger is stated correctly (edges keep pointing at a node that stopped being current, and nothing warns you), and a replacement declares "supersedes", which the procedure never mentioned. 3. CITATION TABLE OVERSTATED VERIFICATION. A GitHub link pinned to a real SHA naming a file that never existed returns ok -- the checker never contacts GitHub. The table now carries a "does it prove the target exists?" column, gains the external-URL row it was missing, and names the trap. All seven rows independently confirmed against the classifier. 4. PROVENANCE SEMANTICS -- and the defect the recheck was right to refuse. The update procedure said to bump the revision on every edit; this node kept its original across seven. The first attempt defined the field as an evidence snapshot and classified that as INFERENCE citing node.schema.json. That schema says nothing whatever about revisions, so the citation could not support the statement: a policy choice had been given a class that made it look derived. It is now TEAM_KNOWLEDGE attributed to #636's own definition of done -- "the draft is checked against the repository revision recorded in provenance" -- which is where the meaning comes from. ADR-0029 requires exactly that: history and team knowledge stay attributed rather than being promoted to fact. The node also claimed it was "the only authored node in the corpus" at the recorded revision. It did not exist at that revision and neither did any other node: git cat-file -e on that path at that sha exits 128, and the corpus held zero .md files outside schema/. Corrected. The revision still does not move, and the reason is checkable rather than asserted: every cited source is byte-identical between that revision and now, so verifying a claim at HEAD and at the recorded revision were the same act. git diff --name-only <sha> -- <cited paths> returns nothing. 5. AUDIENCE. Added "reviewer": the document assigns reviewers an enforcement duty no tool performs, so they are addressed by it. The adjudicator had declined this finding (nothing consumes the field today); the final pass argued audience describes who is addressed, not who parses it. Went with the latter. Whether "developer" also belongs is left undecided. 6. The test module docstring claimed absolute fixture isolation that was false before this branch and more visibly false after it. States the rule with its one named exception instead. Flagged independently by the adjudicator and the final pass. Pre-existing, fixed while in the file: the same docstring advertised "python3 -m unittest launchpad.project_intelligence.corpus.tests.test_validate", which has never worked -- the directory is "project-intelligence" with a hyphen, not a legal package name. Confirmed ModuleNotFoundError, removed. 7. Full suites re-run in a writable environment: 79 validator tests OK, 27 schema tests OK, real corpus exit 0. The plan's two-node create/update/ retire charter ran with its negative control -- a deliberately invalid node failed and named itself, proving the scratch root was the one scanned; retirement kept beta's edge resolving; deleting alpha instead broke it exactly as the corrected text now says. Final state: 18 ledger entries, schema errors 0, every citation resolves, no cited source changed since the recorded revision, exactly one commit-only FACT, 79 + 27 tests green, real corpus exit 0. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): stop asserting provenance policy this node cannot source (#636) Third cross-model pass, third refusal on the same condition. It was right three times, and the third refusal finally identified why the first two fixes failed: the document was writing corpus-wide provenance policy it had no authority to write. Reclassifying it (INFERENCE, then TEAM_KNOWLEDGE) only changed the label on an unsourced decision. WHAT ACTUALLY CHANGED: the document stops making the claim. #1321 ("document corpus standard for provenance", unlanded) owns the question of whether a recorded revision may stay put across edits. This node now says so, states its own approach as working practice pending that standard rather than as a rule others must follow, and defers. The ledger entry no longer asserts the policy -- it records only what #636's definition of done actually requires, which is a fact about the issue and is attributed to it. Also fixes a self-contradiction the last fix opened, exactly the failure shape this repository keeps producing. The previous commit added a FACT citing relationships.schema.json for the supersedes directionality. The same node still said, in its unverified list, that relationships.schema.json had never been read. Both could not stand. The file WAS read; the stale disclaimer is now narrowed to what remains genuinely unchecked -- that the two enum lists agree, which rests on a test that was not run. Ledger completeness, third sweep: entries added for the generated-artifact rejection, the non-enforcement of the one-commit-only-FACT convention, and positional citations not being bounds-checked. Prose that asserted this node's own history -- node counts at the recorded revision, byte-identity of cited sources -- is removed rather than given entries: it told the reader to trust the document about a fact they can check themselves in one command, so the document now gives them the command instead. TEAM_KNOWLEDGE's description corrected. It said "a person told you", which the schema does not require (it asks who or what told the corpus) and which made the class look unavailable for an attributed issue or decision record. It now also says outright what it is not: a place to park a decision you made yourself. Attributing an extrapolation to the thing it started from does not make it something you were told. Updating step 4 restated as four explicit branches including the byte-identical case, which the earlier "bump on every edit" wording omitted while this very node relied on it -- the inconsistency the pass named. Verified: 21 ledger entries, schema errors 0, every citation resolves, exactly one commit-only FACT, no cited source changed since the recorded revision, 79 validator tests OK, real corpus exit 0. Refs #636 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): add the decision-references standard (#1310) Adds launchpad/docs/corpus/standards/decision-references.md, the canonical policy node for how a corpus node cites an accepted decision -- an ADR or a ratified specification -- and when a decision is the wrong citation entirely. The pivot the standard has to make decidable is ADR-0029's: the same record is the right citation for an intent/authorization claim and the wrong one for a behaviour claim. The node gives an author a single question that decides it, three worked examples from this repository, and separated MUST/SHOULD sections. Also covers what an author does when two accepted decisions of the same claim type conflict -- record both, flag, escalate as a type:adr issue, never pick a side -- and what happens to a citation whose record is later superseded: the path still resolves, so validation stays green and the citation goes quietly wrong. Enforcement of the flagged state in the schema and checker is #1410's work and is named as deferred, not implemented here. No relationships are declared: no sibling standard node exists yet and an unresolvable target is a hard error. Refs: ADR-0029, ADR-0028, launchpad/decisions/README.md Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): fix MUST 3's false claim about the checker (#1310) review-code found, and review-adjudicate confirmed and promoted to Blocker, that MUST 3 asserted a bare repository path is "the one citation shape checked against the filesystem". That is false. _classify_citation routes a path:line citation through _FILE_POSITION_RE and then into _classify_repo_path -- the same filesystem check a bare path gets. Only the line number goes unchecked, which is what AGENTS.md's own citation table has said all along. The false rationale also produced a second verdict on one input: MUST 3 forbade a line position outright while the SHOULD section only "preferred" a bare path, so a reviewer enforcing the MUST and an author following the SHOULD were both correctly following this file. MUST 3 now states the real reason -- the path is checked either way, and a line number is an unverified precision that drifts silently -- and keeps the prohibition. The overlapping SHOULD bullet is replaced with one that does not restate the rule at a weaker strength. A ledger entry records the path:line behaviour, and the reviewer checklist now says MUST 3 is unenforced too. Also narrows the "cite the record, not the issue" SHOULD so it cannot read as permitting an authorization claim to cite an issue, which MUST 1 forbids. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): address the final review; rebase onto the moved base (#1310) The base branch advanced while this was being built. review-final caught it: origin/task/636-corpus-agents-md moved to a1e8bbc, which rewrote AGENTS.md -- the only cited source that moved -- and broke this node two ways. REBASE AND REVISION. Rebased onto a1e8bbc and moved the recorded revision there after re-verifying every claim at that revision. Verified: git diff --name-only a1e8bbc HEAD -- <every ledger path> is empty, so no cited source has moved since. TWO ENTRIES THE BASE COMMIT INVALIDATED. a1e8bbc deleted the AGENTS.md sentence "it is still the only one" that a FACT quoted -- a FACT citing a file that no longer says it, which the validator cannot see. That entry is gone, replaced by a FACT the checker's source does support, and the body now gives the reader a command instead of asking them to take the node's word for repository state. The same commit narrowed TEAM_KNOWLEDGE: it is now explicitly "not a place to park a decision you made yourself". One entry did exactly that. #636 spent a whole commit learning that reclassifying such a claim only relabels it, so this takes #636's remedy -- the entry now records a fact ABOUT the issue, attributed to it, and the body carries the policy in its own voice. Both TEAM_KNOWLEDGE entries now match the sibling node's provided_by shape. SELF-CONTRADICTIONS. The node broke three of its own rules. - MUST 7 was unconditional, so the ledger's two ADR-0001 citations violated it. Scoped to intent claims, which is the rule it always meant. - MUST 5 named three ADR sections; README lists five and this node's own security-implications FACT cites a sixth. Now covers every section. - Three behaviour claims about the checker had no ledger entry, including the load-bearing "a flagged node validates green" -- which MUST 2 requires executable evidence for. Added, and verified: validate.py never reads status. NEIGHBOUR HOLE FROM THE LAST FIX. The previous commit corrected MUST 3 and the reviewer checklist but left "What the checks establish" describing bare paths only and counting three consequences, so that section still implied a path:line citation would fail. Now states both shapes, four consequences, and the fourth is the conflict blindness the enforcement discussion depends on. ALSO. "the whole of ADR-0029" understated the ADR by half and would let a section-at-a-time reader miss the escalation duty. The reviewer checklist claimed to hold every MUST while omitting MUST 6, the one nothing else catches. The conflict procedure withheld the status value the same body names three times. The omissions table now discloses that this node settles a slice of #1459's and #1314's territory rather than implying it defers both wholly. Cross-model note: the mandatory Codex pass could not run -- workspace out of credits, no other external CLI installed -- so review-final stood in and is same-model. Recorded in the PR as a gap, not as a pass. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): fix four AGENTS.md defects found by authoring against it (#636) Three sibling agents authored corpus nodes following this document literally, and reported what broke. These four are the defects that produced real false claims in their work, not stylistic complaints. Filed as #1471; fixed here because #1462 is still open and 45 further nodes would inherit them. 1. THE ONLY-NODE JUSTIFICATION WENT FALSE, AND TWO AGENTS COPIED IT. "A node with no sibling to point at correctly declares none" read as a general rule. It was a fact about one moment -- when this was the corpus's only node -- and stopped holding as soon as a second existed. Both the #1309 and #1307 agents reproduced the reasoning and wrote a justification that was false at the time they wrote it; their adjudicators caught it, not their own reviews. Now says to enumerate what exists and give the real reason, and records why. 2. AN ISSUE-ONLY SOURCE HAD NO HONEST CLASS. The citation table lists tool results as legitimate while "Three things a passing run does not mean" says a FACT resting only on UNVERIFIED citations "has not been checked by anything", with one named exception. A claim whose only source is a GitHub issue has no openable form and cannot be SHA-pinned, so both passages applied and disagreed. The #1307 agent hit this with nothing honest to choose. Now states it: TEAM_KNOWLEDGE with provided_by naming the issue, which is what ADR-0029 requires of GitHub history anyway. 3. A RANGE WAS READ AS A MAPPING. The not-covered table paired eight subjects with "#1307-#1351", which is 45 issues and no subject-to-issue pairing at all. The #1307 agent cited this table for nine invented mappings -- exactly the "FACT cited to a file that does not discuss the claim" failure the document warns about two sections earlier. Now says outright that it is a range, not a mapping, and gives the search command. 4. CREATE STEP 3 ROUTED SOMETHING STEP 8 DID NOT ACCEPT. Step 3 sends expected-but-unverified items to the scope section; step 8 said only "state what the node does not cover". A boundary and a confidence disclosure are different things, so working the steps in order left that category homeless. Both the #1307 and #1309 agents reported it independently. Step 8 now names both. Verified: schema errors 0, 21 ledger entries, validator exit 0, 79 tests OK. Refs #636 #1471 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> * docs(corpus): fix a false attribution and the relationship-target trap (#636) Two more defects reported by the #1308 agent after authoring against this document. Both are mine, and the first is embarrassing in context. 1. THE CITATION TABLE MISATTRIBUTED ITSELF, IN THE SECTION ABOUT NOT DOING THAT. It said "CONTRACT.md §3 defines the six shapes" above a SEVEN-row table whose GitHub-link and external-URL rows appear nowhere in §3. Measured: grepping that section for http/url/github returns zero hits. So the sentence cited a file that does not discuss two of the rows beneath it -- the exact failure this document warns about, committed by this document. The #1308 agent took the table as an accurate summary and built a scope argument on "four of six shapes name code" before their plan review caught it. Now states what §3 actually enumerates, that the two URL forms are the validator's and not §3's, and that the table is seven rows and not a summary. 2. "ADD RELATIONSHIPS ONLY TO NODES THAT EXIST" WAS UNFOLLOWABLE, AND DANGEROUSLY SO. "Exist" was ambiguous between the branch you are working on and the branch you merge into, and the checker loads whatever is present where it runs. An agent branched off this unmerged node could therefore target `corpus-agents`, validate clean locally, and break CI on `launchpad`, where the node is absent. The #1308 agent hit exactly that and their review-code raised it as a Blocker. Step 9 now says "exists on the branch you are merging INTO", explains why the distinction is the trap, and gives the command to check the merge base rather than the local tree. This is the second round of AGENTS.md fixes sourced from agents authoring against it, and the pattern is worth naming: every defect found this way was invisible to three review passes over the document itself, and obvious within minutes to someone trying to follow it. Reviewing a procedure is not the same as executing one. Verified: validator exit 0, 79 tests OK, schema clean. Refs #636 #1471 #1478 Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com> --------- Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
…ed at (#636) Rebased onto `launchpad` and reduced to one file. #1468, #1469 and #1477 merged while this PR sat on CHANGES_REQUESTED, and each carried the shared #636 base, so two of this branch's three files -- the #636 plan and test_validate.py -- are now byte-identical on `launchpad` and no longer belong in this diff. What is left is the fix itself. The consequence worth stating plainly: those merges landed the UNFIXED corpus AGENTS.md on `launchpad`, including the line this commit removes -- "Do not take that on this document's word -- the command is the check". The High finding is on the default branch now, which is why this is a defect fix rather than a pre-merge correction. HIGH -- the provenance-update shortcut could certify stale evidence. Two passages said an empty `git diff --name-only <recorded-sha> -- <ledger paths>` established that every cited source was unchanged and every claim still stood. Both are false for citation forms this document itself permits. Measured, not argued. A file that DOES differ reports as changed with a bare path and reports empty output with exit status 0 once `:127` or `:219-221` is appended -- no error, no warning. So an unnormalized `path:line` citation makes a changed file indistinguishable from an unchanged one, which is the worst available failure shape for a provenance check. Recorded as a new FACT entry with the tool-result citation that demonstrates it. The rule now states its reach: normalize positions off the path first; only bare path, file line and file range are in scope; graph edge, tool result, commit and the two URL forms are excluded explicitly, so the branch is simply unavailable when a ledger contains one. The conclusion is narrowed to what a diff can support -- those files are unchanged -- with re-verification left as the only thing that establishes a FACT still holds. MEDIUM -- the one-node rule had no supporting ledger entry. "One node is one independently maintainable idea" is a substantive instruction and the ledger's only #636 entry backed the recorded-revision requirement instead. Added as TEAM_KNOWLEDGE attributed to #636's definition of done, which states it near-verbatim, matching the existing entry's classification for the same source. Also corrected a wrong-direction cross-reference: "*Updating a node* below" in Scope and omissions, where that section is above it. The recorded revision stays at 0052f5a, re-checked against the NEW base after the rebase rather than assumed to still hold: the two non-file citations were excluded, the nine normalized file paths were passed to `git diff` against 0052f5a from `launchpad` tip 0ffc1c9, and the result was empty. The second Medium is not addressed because it is stale -- the citation table was already corrected by ebe2daf and says outright that it is seven rows and is not a summary of CONTRACT.md section 3. The remaining cross-document disagreement is tracked as #1478. Evidence is in the PR thread. Validated against the full four-node corpus now on `launchpad`, not just this node: PASS, 5 non-fatal UNVERIFIED notices, all commit or tool-result citations that name no openable file. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
…d FACT (#1307) Addresses the Medium finding from the review panel on PR #1470. The ledger recorded "One node carries one recorded revision covering its whole ledger, and moving that revision makes a statement about every claim the node holds" as a FACT cited to AGENTS.md, and section 3's maintenance-clock test rested on it as though it were settled. AGENTS.md says the opposite about its own status: whether a recorded revision may stay put while a node is edited is #1321's to decide, is unlanded, and what AGENTS.md states is that document's working practice rather than a corpus-wide rule -- three review passes having already rejected attempts to present it as one. The entry now states what AGENTS.md actually says, including that it is working practice pending #1321. That is a FACT about a document this branch carries, which is checkable; the previous entry asserted the underlying rule, which is not settled. Section 3 now names the premise as working practice, defers to #1321 explicitly, and says what survives if #1321 rules the other way: two halves on different maintenance clocks still force a choice about what the recorded revision covers, so the test is worth running either way and only its sharpness depends on the ruling. The section 6 checklist item is scoped the same way. Also merges the current head of task/636-corpus-agents-md. The branch was carrying AGENTS.md at e422283 while the base had advanced to 8c6c21a, which rewrote the revision-shortcut procedure this finding is about -- fixing the claim against the stale copy would have described text that had already changed. Verification, after the merge and the edits: python3 launchpad/project-intelligence/corpus/validate.py PASS corpus validation found no errors; 4 item(s) reported unverified python3 -m unittest discover -s launchpad/docs/corpus/schema/tests -p "test_*.py" Ran 27 tests -- OK python3 -m unittest discover -s launchpad/project-intelligence/corpus/tests -p "test_*.py" Ran 79 tests -- OK The review's second finding, on merge order, is not addressed here: it asks that #1462 merge first so this PR carries only the atomicity standard. That is a sequencing decision, not a change to this branch. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
launchpad gained launchpad/docs/corpus/AGENTS.md while this branch was open, via PRs #1468, #1469 and #1477 merging -- each carried the shared #636 base. That made an add/add conflict against the copy this branch had merged from task/636-corpus-agents-md. Resolved in favour of launchpad's copy (e422283), NOT this branch's newer one (8c6c21a). The newer copy carries #1462's fixes for its outstanding review findings. Those fixes belong to #1462 and are still under review; landing them through this PR would merge unreviewed changes to another PR's file under cover of an atomicity-standard change. This branch's own fix is unaffected either way: the "What the corpus has NOT settled about revisions" passage that the recast ledger entry cites is present in both copies, so the claim is true against whichever version ends up on launchpad. Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
eb8de57 to
919886b
Compare
Rebased onto
|
| File | On launchpad |
This branch |
|---|---|---|
launchpad/plans/2026-08-26-issue-636-corpus-agents-md.md |
65d3570b82 |
same → dropped |
launchpad/project-intelligence/corpus/tests/test_validate.py |
ffdff6ffc0 |
same → dropped |
launchpad/docs/corpus/AGENTS.md |
e42228305d |
the fix → kept |
launchpad/docs/corpus/AGENTS.md | 59 +++++++++-----
1 file changed, 48 insertions(+), 11 deletions(-)
The consequence worth stating plainly. Those merges landed the unfixed AGENTS.md on launchpad, including the line this PR removes:
Do not take that on this document's word — the command is the check.
So the High finding is on the default branch now, and this is a defect fix rather than a pre-merge correction. Nothing about the fix changed in the rebase — the same two findings are addressed, the same way, with the same evidence as in my earlier comment.
Re-verified after the rebase, not carried over
The base moved from 0052f5a7 to 0ffc1c9e4, so everything base-dependent was re-run rather than assumed.
$ python3 launchpad/project-intelligence/corpus/validate.py
UNVERIFIED corpus-agents: evidence entry 1, citation 1: is a commit reference, which names no openable file
UNVERIFIED corpus-agents: evidence entry 20, citation 1: is a graph-edge or tool-result citation, which names no openable file
UNVERIFIED corpus-readme: evidence entry 1, citation 1: is a commit reference, which names no openable file
UNVERIFIED corpus-standard-confidence: evidence entry 1, citation 1: is a commit reference, which names no openable file
UNVERIFIED corpus-standard-decision-references: evidence entry 1, citation 1: is a commit reference, which names no openable file
PASS corpus validation found no errors; 5 item(s) reported unverified
$ python3 -m unittest discover -s launchpad/project-intelligence/corpus/tests -p "test_*.py"
Ran 79 tests in 0.318s
OK
That is the full four-node corpus now on launchpad, not just this node — corpus-readme, corpus-standard-confidence and corpus-standard-decision-references are the three that landed. All five notices are commit or tool-result citations that name no openable file, which is the honest outcome for those forms.
Recorded revision stays at 0052f5a7, re-checked against the new base using the corrected procedure this PR introduces: the two non-file citations excluded, the nine normalized file paths passed to git diff against 0052f5a7 from tip 0ffc1c9e4, result empty.
Still needs
A re-review to clear CHANGES_REQUESTED. mergeable: MERGEABLE, all seven checks SUCCESS including the corpus validate job.
Not verified
- I did not re-check whether the three merged nodes' own claims survived their merge. This PR touches only
AGENTS.md; the validator passing says their citations resolve structurally, not that their statements are still true against the current tree. - The two dropped files were compared by blob hash, not by reading them. Identical hashes are conclusive for "no content difference", but I did not review what those files say now that they are on
launchpad.
Addresses all three findings from the review panel on PR #1480. Each verdict below was measured by calling validate._classify_citation directly rather than read off the document. High -- entry 15 and section 8 asserted as FACT that AGENTS.md "introduces its citation table as CONTRACT.md section 3's six shapes and then lists seven rows". The AGENTS.md this branch carries says the opposite in as many words: the table "is not a summary of section 3", because its two URL rows are forms validate.py recognises and section 3 does not enumerate. That correction landed in ebe2daf, after this node recorded its claim against 60d4947. The entry and section 8 now describe the AGENTS.md that actually ships beside them. Section 8 also now records what happened, because the node is a standard about citation discipline and this was a live instance of the failure it teaches against: a claim recorded against one revision of a moving document is not a claim about the document that finally ships. AGENTS.md records the same incident from its own side. The open CONTRACT.md-versus-validator divergence is unchanged and still points at #1478. Medium -- the ledger said a bare repository path "is opened on disk" while a later entry said a citation resolving to a real file "is never opened". Both cannot hold. Measured: a bare path returns ok on resolution alone and the file's contents are never read. The first entry now says resolved, and says explicitly that resolution establishes only that the file is there. Medium -- the ledger scoped the UNVERIFIED channel to "non-GitHub URLs", while the node's own table records GitHub issue and pull-request URLs as unverified. Measured: #1459 -> unverified #1462 -> unverified https://example.com/spec -> unverified .../blob/<full-40-sha>/path -> ok .../blob/launchpad/path -> error (mutable ref, ADR-0003) The entry now scopes the channel to external URLs that are not pinned repository links, and names GitHub issue and pull-request URLs as included -- which matches the table and the measured behaviour. Also merges origin/launchpad, which gained the corpus tree when #1468, #1469 and #1477 merged. No conflict: this branch already carried the same AGENTS.md blob. Verification: python3 launchpad/project-intelligence/corpus/validate.py PASS corpus validation found no errors; 5 item(s) reported unverified Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
tucktuck101
left a comment
There was a problem hiding this comment.
am approving after verbal discussions.
Summary
Creates
launchpad/docs/corpus/AGENTS.md, the first hand-authored node in the documentation corpus, giving a cold-start agent the instructions to create, update and retire one node. Its YAML front matter is validated by the checker #623 merged, so this is machine-checked input rather than prose. Also replaces one test in that checker's suite which asserted the corpus was empty and therefore could not survive the first authored node.Related issue
Closes #636
Issue type
Task
Agent provenance
Objective
Create
launchpad/docs/corpus/AGENTS.mdas the canonical instruction node for authoring corpus nodes, satisfying #605's criterion that it enable create/update/retire without oral guidance.Impacted components
launchpad/docs/corpus/AGENTS.md
launchpad/plans/2026-08-26-issue-636-corpus-agents-md.md
launchpad/project-intelligence/corpus/tests/test_validate.py
Approach and rejected alternatives
The document links the schema, the ADRs and
CONTRACT.mdrather than restating them. A second copy of an enum or a field matrix drifts silently, and the checker never reads body prose — so a stale copy would stay green forever. What the document does add is the part living nowhere else: what the checker actually establishes, and what a passing run does not mean.Rejected: restating the front-matter contract inline. More convenient for a reader, but it creates a second source of truth for values the schema already owns, with no mechanism to detect divergence.
Rejected: putting the repository revision in a
provenancefront-matter field. The DoD asks for provenance, butnode.schema.jsonsetsadditionalProperties: falseand defines no such field — adding one fails validation. The schema calls theevidencearray the provenance ledger, so the revision is recorded there as a commit citation.Scope note. The plan barred touching #623's suite. One test had to change anyway:
test_real_corpus_root_currently_has_no_content_outside_schemaasserted the corpus root was empty, which this issue necessarily falsifies, and it runs in CI. The widening was authorised and is recorded in the plan'sLEFT OUTsection, withreview-testsadded to the review roster as a consequence.Verification
Command run:
Raw output:
The single
UNVERIFIEDnotice is the provenance entry's commit citation, which names no openable file by nature. TheFAIL corpus root does not existline is a test's captured stdout for the missing-root case, not a failing test — the suite reportsOK.The create/update/retire procedures were also exercised literally against a scratch corpus root, with a negative control proving that root was the one scanned:
Not verified
AGENTS.md.preflight_core.pyresolves it as the nearest rules file for the subtree, but it resolves the path without parsing content, so whether YAML front matter degrades the file for a harness that actually reads it as instructions is unknown.relationships.schema.jsonwas not read directly.node.schema.jsonstates a test guards the two relationship enums against drift; that test was not run.developerbelongs inaudiencesalongsideagentandreviewerwas not decided.Security implications
No change to exposure or blast radius. The change adds one Markdown document and edits one test; no runtime code, no credentials, no network surface, no new CI permissions. The corpus validate workflow triggers on
pull_requestrather thanpull_request_target, so this branch's content executes with fork permissions and no repository secrets.One documentation-level note with a security flavour: the citation table now states explicitly that a GitHub link pinned to a full SHA is checked syntactically only — the validator never contacts GitHub, so a link naming a file that has never existed passes. That was previously implied to be verified, and an author trusting the old wording could have cited a nonexistent source.
Escalations
Raised rather than decided:
audiences. Two independent reviewers disagreed. The adjudicator declined it — nothing in the tree consumes the field. The cross-model final pass called it merge-blocking, since the document instructs reviewers directly. Resolved in favour of the latter (revieweradded);developerleft open. This is taxonomy precedent for the remaining 45 corpus nodes.review-docsdoes not exist. The review gate's roster selects it for.mdpaths and no such agent exists, so the gate fails closed on every docs-heavy change. Not specific to this PR — it will block the whole corpus rollout.type: agentvsgovernance. Decided by the initiating human as precedent for the remaining nodes;governancewas the rejected alternative on the grounds it would eventually absorb every process document and stop discriminating.path:linecitations whose line does not exist. This PR routes around it by preferring bare paths rather than changing the validator.