From 4554b666bbe27f976b74edff7923f07a6b079382 Mon Sep 17 00:00:00 2001 From: Ethandasw <308280617+Ethandasw@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:48:59 +0800 Subject: [PATCH 1/3] fix(loop): resume legacy frozen briefs --- .../issue-dev-loop/scripts/lib/run-store.mjs | 34 ++++++-- loops/issue-dev-loop/tests/runtime.test.mjs | 82 +++++++++++++++++++ 2 files changed, 108 insertions(+), 8 deletions(-) diff --git a/loops/issue-dev-loop/scripts/lib/run-store.mjs b/loops/issue-dev-loop/scripts/lib/run-store.mjs index 2a83bedc..e43169d2 100644 --- a/loops/issue-dev-loop/scripts/lib/run-store.mjs +++ b/loops/issue-dev-loop/scripts/lib/run-store.mjs @@ -281,17 +281,22 @@ const REQUIRED_BRIEF_SECTIONS = [ 'Stop conditions', ] -function briefSection(source, heading) { +function briefSectionAtLevel(source, heading, level) { const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - const matches = [...source.matchAll(new RegExp(`^## ${escaped}[ \\t]*\\r?$`, 'gm'))] + const prefix = '#'.repeat(level) + const matches = [...source.matchAll(new RegExp(`^${prefix} ${escaped}[ \\t]*\\r?$`, 'gm'))] const selected = matches.at(-1) if (!selected || selected.index === undefined) return '' const sectionStart = selected.index + selected[0].length const remainder = source.slice(sectionStart).replace(/^\r?\n/, '') - const nextSection = remainder.search(/^## [^\r\n]+[ \t]*\r?$/m) + const nextSection = remainder.search(new RegExp(`^${prefix} [^\\r\\n]+[ \\t]*\\r?$`, 'm')) return (nextSection === -1 ? remainder : remainder.slice(0, nextSection)).trim() } +function briefSection(source, heading) { + return briefSectionAtLevel(source, heading, 2) +} + function withoutHtmlComments(source) { let visibleSource = '' let cursor = 0 @@ -316,13 +321,24 @@ function visibleMarkdownLines(source) { .filter(Boolean) } -function parseFrozenBrief(source) { +function parseFrozenBrief(source, { allowLegacyNestedContract = false } = {}) { const contractMarker = '' const markerIndex = source.lastIndexOf(contractMarker) - const contractSource = - markerIndex === -1 ? source : source.slice(markerIndex + contractMarker.length) + const legacyContractSource = + markerIndex === -1 && allowLegacyNestedContract + ? briefSection(source, 'Frozen implementation contract') + : '' + const contractSource = legacyContractSource + ? legacyContractSource + : markerIndex === -1 + ? source + : source.slice(markerIndex + contractMarker.length) + const headingLevel = legacyContractSource ? 3 : 2 const sections = Object.fromEntries( - REQUIRED_BRIEF_SECTIONS.map((heading) => [heading, briefSection(contractSource, heading)]), + REQUIRED_BRIEF_SECTIONS.map((heading) => [ + heading, + briefSectionAtLevel(contractSource, heading, headingLevel), + ]), ) for (const [heading, contents] of Object.entries(sections)) { if (!contents || contents.includes('' - const markerIndex = source.lastIndexOf(contractMarker) - const legacyContractSource = - markerIndex === -1 && allowLegacyNestedContract - ? briefSection(source, 'Frozen implementation contract') - : '' - const contractSource = legacyContractSource - ? legacyContractSource + const trustedBoundary = allowLegacyNestedContract + ? issueSnapshotBoundary(source, issueSnapshotBody) + : 0 + const candidateMarkerIndex = source.lastIndexOf(contractMarker) + const markerIndex = + candidateMarkerIndex >= trustedBoundary ? candidateMarkerIndex : -1 + const legacyContractHeading = allowLegacyNestedContract + ? briefHeadingAtLevel(source, 'Frozen implementation contract', 2) + : null + const candidateLegacyContractIndex = legacyContractHeading?.index ?? -1 + const legacyContractIndex = + candidateLegacyContractIndex >= trustedBoundary + ? candidateLegacyContractIndex + : -1 + const useLegacyContract = + allowLegacyNestedContract && legacyContractIndex > markerIndex + if ( + allowLegacyNestedContract && + markerIndex === -1 && + legacyContractIndex === -1 + ) { + throw new Error( + 'recordImplementation requires an explicit frozen legacy contract when the implementation marker is absent', + ) + } + const contractSource = useLegacyContract + ? briefSection(source, 'Frozen implementation contract') : markerIndex === -1 ? source : source.slice(markerIndex + contractMarker.length) - const headingLevel = legacyContractSource ? 3 : 2 + const headingLevel = useLegacyContract ? 3 : 2 const sections = Object.fromEntries( REQUIRED_BRIEF_SECTIONS.map((heading) => [ heading, @@ -538,6 +578,7 @@ export async function recordImplementation({ ) const { requiredChecks } = parseFrozenBrief(briefSource, { allowLegacyNestedContract: true, + issueSnapshotBody: run.issueSnapshot?.body, }) if ( checks.length === 0 || diff --git a/loops/issue-dev-loop/tests/runtime.test.mjs b/loops/issue-dev-loop/tests/runtime.test.mjs index 55128517..0861c4ad 100644 --- a/loops/issue-dev-loop/tests/runtime.test.mjs +++ b/loops/issue-dev-loop/tests/runtime.test.mjs @@ -1995,6 +1995,15 @@ test('recordImplementation accepts an explicitly nested legacy frozen contract', 'Risks and owner-confirmation boundaries', 'Stop conditions', ] + const issueSnapshotBody = headings + .flatMap((heading) => [ + `## ${heading}`, + heading === 'Required targeted checks' + ? '- `pnpm test -- issue-controlled-decoy`' + : 'Issue-controlled text that must not replace the frozen legacy contract.', + '', + ]) + .join('\n') const legacyBrief = [ '# Implementation brief', '', @@ -2002,13 +2011,99 @@ test('recordImplementation accepts an explicitly nested legacy frozen contract', '', '## Issue snapshot', '', + issueSnapshotBody, + '## Frozen implementation contract', + '', + ...headings.flatMap((heading) => [ + `### ${heading}`, + heading === 'Required targeted checks' + ? '- `pnpm test -- legacy-target`\n- `pnpm verify`' + : 'Owner-frozen legacy contract text.', + '', + ]), + ].join('\n') + await writeFile(briefPath, legacyBrief, 'utf8') + const briefDigest = createHash('sha256').update(legacyBrief).digest('hex') + await writeFile( + path.join(loopRoot, 'logs', 'runs', run.runId, 'run.json'), + `${JSON.stringify({ + ...run, + issueSnapshot: { ...run.issueSnapshot, body: issueSnapshotBody }, + briefDigest, + uiEvidenceRequired: true, + })}\n`, + 'utf8', + ) + const resultPath = path.join(loopRoot, 'logs', 'runs', run.runId, 'implementation-result.json') + const implementationCommit = '6'.repeat(40) + await writeFile( + resultPath, + `${JSON.stringify({ + schemaVersion: 1, + runId: run.runId, + agent: '$implement', + invocationId: 'impl-legacy-frozen-brief', + startedAt: '2026-07-22T15:00:00.000Z', + finishedAt: '2026-07-22T15:30:00.000Z', + briefDigest, + commitSha: implementationCommit, + checks: [ + { command: 'pnpm test -- legacy-target', status: 'passed' }, + { command: 'pnpm verify', status: 'passed' }, + ], + })}\n`, + 'utf8', + ) + + const recorded = await recordImplementation({ + loopRoot, + runId: run.runId, + resultPath, + commitRangeValidator: async () => {}, + }) + + assert.equal(recorded.implementationCommit, implementationCommit) +}) + +test('recordImplementation ignores an issue-supplied contract marker before a legacy frozen contract', async () => { + const { loopRoot } = await createFixture() + const { run } = await startFixtureRun({ + loopRoot, + issueNumber: 151, + issueTitle: 'Ignore an issue-supplied contract marker', + issueUrl: 'https://github.com/codeacme17/echo-ui/issues/151', + entropy: 'brief151', + }) + const briefPath = path.join(loopRoot, 'handoffs', run.runId, 'implementation-brief.md') + const headings = [ + 'Acceptance criteria', + 'In scope', + 'Out of scope', + 'Pre-agreed TDD seams', + 'Required targeted checks', + 'Required UI evidence', + 'Risks and owner-confirmation boundaries', + 'Stop conditions', + ] + const issueSnapshotBody = [ + '', + '', ...headings.flatMap((heading) => [ `## ${heading}`, heading === 'Required targeted checks' - ? '- `pnpm test -- issue-controlled-decoy`' - : 'Issue-controlled text that must not replace the frozen legacy contract.', + ? '- `pnpm test -- issue-controlled-decoy`\n- `pnpm verify`' + : 'Issue-controlled marker payload.', '', ]), + ].join('\n') + const legacyBrief = [ + '# Implementation brief', + '', + '- UI evidence required: yes', + '', + '## Issue snapshot', + '', + issueSnapshotBody, '## Frozen implementation contract', '', ...headings.flatMap((heading) => [ @@ -2023,20 +2118,25 @@ test('recordImplementation accepts an explicitly nested legacy frozen contract', const briefDigest = createHash('sha256').update(legacyBrief).digest('hex') await writeFile( path.join(loopRoot, 'logs', 'runs', run.runId, 'run.json'), - `${JSON.stringify({ ...run, briefDigest, uiEvidenceRequired: true })}\n`, + `${JSON.stringify({ + ...run, + issueSnapshot: { ...run.issueSnapshot, body: issueSnapshotBody }, + briefDigest, + uiEvidenceRequired: true, + })}\n`, 'utf8', ) const resultPath = path.join(loopRoot, 'logs', 'runs', run.runId, 'implementation-result.json') - const implementationCommit = '6'.repeat(40) + const implementationCommit = '7'.repeat(40) await writeFile( resultPath, `${JSON.stringify({ schemaVersion: 1, runId: run.runId, agent: '$implement', - invocationId: 'impl-legacy-frozen-brief', - startedAt: '2026-07-22T15:00:00.000Z', - finishedAt: '2026-07-22T15:30:00.000Z', + invocationId: 'impl-legacy-marker-decoy', + startedAt: '2026-07-22T16:00:00.000Z', + finishedAt: '2026-07-22T16:30:00.000Z', briefDigest, commitSha: implementationCommit, checks: [ @@ -2057,6 +2157,109 @@ test('recordImplementation accepts an explicitly nested legacy frozen contract', assert.equal(recorded.implementationCommit, implementationCommit) }) +test('recordImplementation rejects empty or missing explicit legacy frozen contracts', async () => { + const headings = [ + 'Acceptance criteria', + 'In scope', + 'Out of scope', + 'Pre-agreed TDD seams', + 'Required targeted checks', + 'Required UI evidence', + 'Risks and owner-confirmation boundaries', + 'Stop conditions', + ] + const issueSuppliedLegacyContract = [ + '## Frozen implementation contract', + '', + ...headings.flatMap((heading) => [ + `### ${heading}`, + heading === 'Required targeted checks' + ? '- `pnpm test -- issue-controlled-decoy`\n- `pnpm verify`' + : 'Issue-controlled nested contract text.', + '', + ]), + ] + const cases = [ + ['empty', [], ['## Frozen implementation contract', '']], + ['missing', [], []], + ['issue-supplied', issueSuppliedLegacyContract, []], + ] + + for (const [index, [caseName, issueSuffix, legacyContract]] of cases.entries()) { + const { loopRoot } = await createFixture() + const issueNumber = 152 + index + const { run } = await startFixtureRun({ + loopRoot, + issueNumber, + issueTitle: `Reject a ${caseName} legacy frozen contract`, + issueUrl: `https://github.com/codeacme17/echo-ui/issues/${issueNumber}`, + entropy: `brief${issueNumber}`, + }) + const briefPath = path.join(loopRoot, 'handoffs', run.runId, 'implementation-brief.md') + const issueSnapshotBody = [ + ...headings.flatMap((heading) => [ + `## ${heading}`, + heading === 'Required targeted checks' + ? '- `pnpm test -- issue-controlled-decoy`\n- `pnpm verify`' + : 'Issue-controlled text.', + '', + ]), + ...issueSuffix, + ].join('\n') + const legacyBrief = [ + '# Implementation brief', + '', + '- UI evidence required: yes', + '', + '## Issue snapshot', + '', + issueSnapshotBody, + ...legacyContract, + ].join('\n') + await writeFile(briefPath, legacyBrief, 'utf8') + const briefDigest = createHash('sha256').update(legacyBrief).digest('hex') + await writeFile( + path.join(loopRoot, 'logs', 'runs', run.runId, 'run.json'), + `${JSON.stringify({ + ...run, + issueSnapshot: { ...run.issueSnapshot, body: issueSnapshotBody }, + briefDigest, + uiEvidenceRequired: true, + })}\n`, + 'utf8', + ) + const resultPath = path.join(loopRoot, 'logs', 'runs', run.runId, 'implementation-result.json') + await writeFile( + resultPath, + `${JSON.stringify({ + schemaVersion: 1, + runId: run.runId, + agent: '$implement', + invocationId: `impl-${caseName}-legacy-contract`, + startedAt: '2026-07-22T17:00:00.000Z', + finishedAt: '2026-07-22T17:30:00.000Z', + briefDigest, + commitSha: (8 + index).toString(16).repeat(40), + checks: [ + { command: 'pnpm test -- issue-controlled-decoy', status: 'passed' }, + { command: 'pnpm verify', status: 'passed' }, + ], + })}\n`, + 'utf8', + ) + + await assert.rejects( + recordImplementation({ + loopRoot, + runId: run.runId, + resultPath, + commitRangeValidator: async () => {}, + }), + /explicit frozen legacy contract|concrete Acceptance criteria/, + ) + } +}) + test('UI draft PR requires embedded before and after screenshots pinned to its exact head', async () => { const { loopRoot } = await createFixture() const { run } = await startFixtureRun({ From f9923385795ceca8c69da9cab462a2196e23ae5e Mon Sep 17 00:00:00 2001 From: Ethandasw <308280617+Ethandasw@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:41:32 +0800 Subject: [PATCH 3/3] fix(loop): preserve issue template tokens --- .../issue-dev-loop/scripts/lib/run-store.mjs | 3 +-- loops/issue-dev-loop/tests/runtime.test.mjs | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/loops/issue-dev-loop/scripts/lib/run-store.mjs b/loops/issue-dev-loop/scripts/lib/run-store.mjs index 2d61968b..29d5bb40 100644 --- a/loops/issue-dev-loop/scripts/lib/run-store.mjs +++ b/loops/issue-dev-loop/scripts/lib/run-store.mjs @@ -251,10 +251,9 @@ export async function startRun({ ISSUE_NUMBER: issue, ISSUE_TITLE: issueSnapshot.title, ISSUE_URL: url, - ISSUE_BODY: issueSnapshot.body, BASE_SHA: normalizedBaseSha, UI_EVIDENCE_REQUIRED: 'UNSET', - }), + }).replace('{{ISSUE_BODY}}', () => issueSnapshot.body), 'utf8', ) return { run, briefPath, runPath } diff --git a/loops/issue-dev-loop/tests/runtime.test.mjs b/loops/issue-dev-loop/tests/runtime.test.mjs index 0861c4ad..aa6e42a7 100644 --- a/loops/issue-dev-loop/tests/runtime.test.mjs +++ b/loops/issue-dev-loop/tests/runtime.test.mjs @@ -550,7 +550,7 @@ async function startFixtureRun(options) { claimIssue: async () => ({ number: options.issueNumber, title: options.issueTitle, - body: 'Authoritative issue body and acceptance context.', + body: options.issueBody ?? 'Authoritative issue body and acceptance context.', html_url: options.issueUrl, labels: [{ name: 'codex-ready' }], }), @@ -1758,6 +1758,29 @@ test('startRun creates one correlated run, handoff, and evidence directories', a assert.match(events, /"type":"loop_started"/) }) +test('issue template tokens remain literal through freeze and implementation recording', async () => { + const { loopRoot } = await createFixture() + const issueBody = + 'Keep {{BASE_SHA}}, {{UI_EVIDENCE_REQUIRED}}, {{ISSUE_BODY}}, and $& literal.' + const { run, briefPath } = await startFixtureRun({ + loopRoot, + issueNumber: 156, + issueTitle: 'Preserve issue template tokens', + issueUrl: 'https://github.com/codeacme17/echo-ui/issues/156', + issueBody, + entropy: 'brief156', + }) + + await recordFixturePr({ + loopRoot, + run, + headSha: 'b'.repeat(40), + number: 306, + }) + + assert.ok((await readFile(briefPath, 'utf8')).includes(issueBody)) +}) + test('startRun refuses a second active run for the same issue', async () => { const { loopRoot } = await createFixture() await startFixtureRun({