From 5befbc952238c51034f55feb09d55a78c8722585 Mon Sep 17 00:00:00 2001 From: Tom Softreck Date: Tue, 25 Aug 2026 14:47:18 +0200 Subject: [PATCH] fix(ticket-085): isolate participant documentation --- TODO.md | 3 ++- project/ticket-085/README.md | 25 +++++++++++++++++---- project/ticket-085/ai-codex-logs.txt | 21 ++++++++++++++++++ project/ticket-085/ai-codex.md | 10 +++++++-- project/ticket-085/changelog.md | 11 ++++++++++ project/ticket-085/intent.json | 16 +++++++++----- src/extractors/docs-deterministic.ts | 12 +++++++++- test/docs.test.ts | 33 ++++++++++++++++++++++++++++ 8 files changed, 118 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index f137b4a..9451bf5 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,8 @@ ## Active tickets - [ ] [`ticket-085`](project/ticket-085/README.md) — scope documentation - acceptance criteria to their governed source ticket. Current state: + acceptance criteria to their governed source ticket and keep participant + Markdown in the communication lane only. Current state: `IN_PROGRESS / PUBLICATION`. - [ ] [`ticket-083`](project/ticket-083/README.md) — keep workspace comparison artifacts and caches outside analysed repository state. Current state: diff --git a/project/ticket-085/README.md b/project/ticket-085/README.md index d1ebd34..fd8a258 100644 --- a/project/ticket-085/README.md +++ b/project/ticket-085/README.md @@ -1,7 +1,7 @@ -# Ticket 085: Scope documentation acceptance criteria to their source ticket +# Ticket 085: Scope governed documentation to its source channel - **ID**: ticket-085 -- **Owner**: unresolved:human +- **Owner**: agent:codex - **Status**: IN_PROGRESS - **Workflow state**: PUBLICATION - **Created**: 2026-08-25 @@ -14,6 +14,10 @@ one repository-wide ticket. Documentation records sourced from a governed ticket directory inherit that source ticket when their text contains no stronger ticket reference; criterion labels remain local to the source ticket. +Participant-owned `ai-*.md` and `user-*.md` files remain communication input; +the generic documentation extractor must not ingest the same bytes a second +time and manufacture self-conflicts from different heuristic polarity. + ## Acceptance criteria - [x] AC-01: The human owner requested continued autonomous repair and testing @@ -24,6 +28,9 @@ stronger ticket reference; criterion labels remain local to the source ticket. - [x] AC-03: A real non-criterion ticket reference in ticket documentation remains authoritative and is not replaced by the source ticket. - [x] AC-04: Focused, full Node, governance and Docker checks pass. +- [x] AC-05: Governed participant Markdown is emitted only by the communication + extractor, while ticket README documentation remains in the document + lane; the `PLF-8091` replay adds no participant self-conflicts. ## Participants @@ -36,15 +43,25 @@ The user's repeated instruction to continue implementing, testing and improving Subactor autonomy authorizes this bounded reusable defect repair. Protected exact-head Validator evidence remains required before merge. +Live comparison after repairing the graph-size boundary showed that the same +`project/ticket-118/ai-codex.md` paragraphs entered as both `INT-AGENT` and +`INT-DOC`. Different polarity classification created three blocking pairs and +a net `blocking +2`; this is reusable extractor evidence, not a reason to +rewrite the Platform ticket to appease the observer. + ## Non-goals - No scoring, authority, mutation or comparison-threshold change. - No global weakening of ticket extraction. - No public schema or dependency change. +- No suppression of ticket README documentation or typed communication. ## Verification evidence -- Focused deterministic documentation suite: 6/6 passed. -- Full `npm run verify`: 424 passed, one existing JDK-only skip, zero failures. +- Focused deterministic documentation suite: 7/7 passed. +- Full `npm run verify`: 425 passed, one existing JDK-only skip, zero failures. - `make docker-smoke`: passed. - Governance and `git diff --check`: zero errors and zero warnings. +- Exact `PLF-8091` replay with the graph-ceiling repair loaded the 142,557,246 + byte graph and changed the blocking diagnostic count from 13 to 12 + (`blocking -1`), with no stderr and the pinned base/head SHA preserved. diff --git a/project/ticket-085/ai-codex-logs.txt b/project/ticket-085/ai-codex-logs.txt index fc42e9f..9cac80b 100644 --- a/project/ticket-085/ai-codex-logs.txt +++ b/project/ticket-085/ai-codex-logs.txt @@ -1,3 +1,24 @@ 2026-08-25T11:34:17Z ticket allocated from protected main@72a6bd1. 2026-08-25 user continuation bound as implementation authority; exact-head Validator remains required for merge. 2026-08-25 focused 6/6, full verify 424 pass/1 skip, Docker smoke and governance passed; moved to PUBLICATION. + +2026-08-25T12:38Z live PLF-8091 semantic readback + +After the comparison graph ceiling repair, exact Platform replay completed and +reported blocking delta +2. Three blocking diagnostics paired records from the +same project/ticket-118/ai-codex.md paragraphs: INT-AGENT from +t2c/project-communication@1 and INT-DOC from t2c/markdown-documentation@2. +Heuristic polarity differed on wording containing "not", creating a self- +conflict rather than an independent disagreement. Reopened ticket-085 under +the existing user authorization; merge authority remains protected. + +2026-08-25T12:55Z exact combined repair validation + +Focused documentation tests passed 7/7. Full npm run verify passed 425 tests, +skipped the one JDK-only test and failed zero. Governance, git diff --check and +Docker smoke passed. An exact detached Platform@92a79e0 replay applied the +PLF-8091 changes and ran against a temporary todo2code build containing both +the 256 MiB generated-graph boundary and this participant-channel boundary. +The 142,557,246 byte graph loaded successfully; stderr was empty; base and head +remained 92a79e060f18cd5793cdb50138e2f7b1241b8c1d; blocking diagnostics fell +from 13 to 12 (delta -1). Ticket moved to PUBLICATION for protected review. diff --git a/project/ticket-085/ai-codex.md b/project/ticket-085/ai-codex.md index 9098ffa..08b87c9 100644 --- a/project/ticket-085/ai-codex.md +++ b/project/ticket-085/ai-codex.md @@ -26,7 +26,13 @@ record located below `project/ticket-NNN/` must retain that structural scope. - Ticket-directory structure is now the fallback target for deterministic documentation records, while explicit non-criterion references still win. - Focused, full host, Docker and governance validation passed. +- Reopened the extractor workstream after `PLF-8091` proved that participant + Markdown was simultaneously emitted as typed communication and generic + documentation, producing blocking self-conflicts. +- Routed governed `ai-*.md` and `user-*.md` exclusively through the existing + communication extractor while retaining ticket README documentation. -## Blockers +## Current state -- Human approval is required before implementation. +- `IN_PROGRESS / PUBLICATION`; focused, full, governance, Docker and exact + Platform replay validation pass. Protected review remains required. diff --git a/project/ticket-085/changelog.md b/project/ticket-085/changelog.md index e673cd7..5ccd38b 100644 --- a/project/ticket-085/changelog.md +++ b/project/ticket-085/changelog.md @@ -8,3 +8,14 @@ - Scoped criterion-only documentation records to their governed source ticket and preserved explicit external ticket references. - Passed focused, full host, Docker and governance verification. + +## [0.2.0] - 2026-08-25 + +- Reopened from live `PLF-8091` comparison evidence. +- Prevented governed participant Markdown from entering both communication and + documentation lanes and creating polarity-based self-conflicts. +- Preserved ticket README documentation and typed participant extraction. +- Passed 7/7 focused tests, full verification (425 pass, one JDK-only skip), + governance, Docker smoke and the exact `PLF-8091` large-graph replay. +- The live replay reduced blocking diagnostics from 13 to 12 instead of adding + the prior participant self-conflicts. diff --git a/project/ticket-085/intent.json b/project/ticket-085/intent.json index 5147051..18620d3 100644 --- a/project/ticket-085/intent.json +++ b/project/ticket-085/intent.json @@ -1,7 +1,7 @@ { "schema": "new-project.intent/v3", "ticket": "ticket-085", - "summary": "Scope documentation acceptance criteria to their source ticket", + "summary": "Scope governed documentation to its source ticket and communication channel", "workstream": "extractors", "classification": { "kind": "BUG", @@ -28,12 +28,13 @@ "conflictsWith": [], "integrationTicket": null, "delivery": { - "acceptedBaseSha": "72a6bd1e194ef3ac6c586b329688fb2faa86be92", + "acceptedBaseSha": "ea6ce38f4b4038a5d9937e83e04704b2d0906bc7", "targetBranch": "main", - "outcome": "Governed ticket documentation criteria remain scoped to their source ticket without weakening semantic comparison", + "outcome": "Governed ticket documentation remains source-scoped while participant Markdown is emitted only through typed communication extraction", "nonGoals": [ "No scoring or comparison policy change", - "No public schema or dependency change" + "No public schema or dependency change", + "No suppression of ticket README documentation or typed communication" ], "complexity": "XS", "estimatedMinutes": 20, @@ -45,7 +46,7 @@ }, "architecture": { "status": "accepted", - "decision": "Use the governed source path as the fallback ticket target and discard criterion labels from ticket identity only inside ticket-scoped documentation", + "decision": "Use governed source paths for criterion scope and exclude participant-owned Markdown from generic documentation because project-communication is its canonical extractor", "components": [ { "name": "deterministic-documentation-extractor", @@ -74,6 +75,11 @@ "criterion": "AC-04", "commands": ["npm run verify", "./project/governance-check.sh", "make docker-smoke"], "evidence": "Repository gates pass on exact head" + }, + { + "criterion": "AC-05", + "commands": ["node --test dist/test/docs.test.js"], + "evidence": "Participant Markdown produces no document records while ticket README records remain present" } ] } diff --git a/src/extractors/docs-deterministic.ts b/src/extractors/docs-deterministic.ts index bb17964..0e42d51 100644 --- a/src/extractors/docs-deterministic.ts +++ b/src/extractors/docs-deterministic.ts @@ -98,17 +98,27 @@ export async function extractDocumentationBaseline( const resolver = createMarkdownPathResolver(root); for (const file of options.files) { + const relative = relativePosix(root, file); + // Governed participant files are the canonical communication channel and + // are extracted by project-communication. Reading the same bytes again as + // generic documentation creates two records with different heuristic + // polarity, which can turn one statement into a blocking self-conflict. + if (isGovernedParticipantDocument(relative)) continue; try { const body = await readText(file, config.maxFileBytes); records.push(...convertDocument(root, file, body, await primePathMapper(resolver, body))); } catch (error) { - warnings.push(`${relativePosix(root, file)}: ${error instanceof Error ? error.message : String(error)}`); + warnings.push(`${relative}: ${error instanceof Error ? error.message : String(error)}`); } } return { records, warnings }; } +function isGovernedParticipantDocument(relativePath: string): boolean { + return /^project\/ticket-[0-9]+\/(?:ai|user)-[^/]+\.md$/i.test(relativePath); +} + /** * Documentation prose names files exactly the way TODO and CHANGELOG do, and * until now was the one Markdown converter that kept the shorthand. On diff --git a/test/docs.test.ts b/test/docs.test.ts index 23c79c8..0c09f03 100644 --- a/test/docs.test.ts +++ b/test/docs.test.ts @@ -70,6 +70,39 @@ test('governed ticket documentation keeps acceptance criteria local to its sourc .every((record) => !record.statement.target.tickets.includes('AC-01'))); }); +test('governed participant communication is not duplicated as documentation', async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 't2c-docs-participant-boundary-')); + const ticket = path.join(root, 'project', 'ticket-118'); + const participant = path.join(ticket, 'ai-codex.md'); + const readme = path.join(ticket, 'README.md'); + await fs.mkdir(ticket, { recursive: true }); + await fs.writeFile(participant, [ + '---', + 'participant-id: agent:codex', + 'participant: codex', + 'role: agent', + 'ticket: ticket-118', + '---', + '# Participant', + '', + 'Control must reject transport authority and must not bypass `subactor`.', + ].join('\n')); + await fs.writeFile(readme, [ + '# Ticket 118', + '', + '- Control must reject transport authority in `config/adopt.json`.', + ].join('\n')); + + const result = await extractDocumentationBaseline({ + root, + files: [participant, readme], + }, makeConfig(root)); + + assert.equal(result.warnings.length, 0); + assert.ok(result.records.length > 0); + assert.ok(result.records.every((record) => record.source.path === 'project/ticket-118/README.md')); +}); + test('deterministic documentation preserves Polish prohibition polarity', async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), 't2c-docs-prohibition-')); const readme = path.join(root, 'README.md');