Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
25 changes: 21 additions & 4 deletions project/ticket-085/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions project/ticket-085/ai-codex-logs.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions project/ticket-085/ai-codex.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions project/ticket-085/changelog.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 11 additions & 5 deletions project/ticket-085/intent.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion src/extractors/docs-deterministic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions test/docs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Loading