From 659b2fe566bba42c1e09974e4d9642c94afbcc8c Mon Sep 17 00:00:00 2001 From: Tom Softreck Date: Fri, 21 Aug 2026 23:00:58 +0200 Subject: [PATCH 1/2] plan(ticket-083): bind comparison output scope --- TODO.md | 3 ++ project/TICKETS.md | 1 + project/ticket-083/README.md | 43 ++++++++++++++++++ project/ticket-083/ai-codex-logs.txt | 0 project/ticket-083/ai-codex.md | 32 +++++++++++++ project/ticket-083/changelog.md | 7 +++ project/ticket-083/intent.json | 67 ++++++++++++++++++++++++++++ project/ticket-083/preprompt.md | 8 ++++ 8 files changed, 161 insertions(+) create mode 100644 project/ticket-083/README.md create mode 100644 project/ticket-083/ai-codex-logs.txt create mode 100644 project/ticket-083/ai-codex.md create mode 100644 project/ticket-083/changelog.md create mode 100644 project/ticket-083/intent.json create mode 100644 project/ticket-083/preprompt.md diff --git a/TODO.md b/TODO.md index c4a92cd9..d789bdae 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,9 @@ ## Active tickets +- [ ] [`ticket-083`](project/ticket-083/README.md) — keep workspace comparison + artifacts and caches outside analysed repository state. Current state: + `IN_PROGRESS / EDIT`. - [ ] [`ticket-054`](project/ticket-054/README.md) — restore skills-agent discovery, prove a todo2code → Repair PR → independent Validator hand-off, then add three bounded todo2code-grounded skills. Current state: diff --git a/project/TICKETS.md b/project/TICKETS.md index f0f11d14..d5ca4411 100644 --- a/project/TICKETS.md +++ b/project/TICKETS.md @@ -57,4 +57,5 @@ of `project/README.md`, which remains a generated technical-analysis artifact. | **ticket-074** | [`README.md`](./ticket-074/README.md) | [`preprompt.md`](./ticket-074/preprompt.md) | - | [`ai-codex.md`](./ticket-074/ai-codex.md) | [`ai-codex-logs.txt`](./ticket-074/ai-codex-logs.txt) | [`changelog.md`](./ticket-074/changelog.md) | | **ticket-075** | [`README.md`](./ticket-075/README.md) | [`preprompt.md`](./ticket-075/preprompt.md) | - | [`ai-codex.md`](./ticket-075/ai-codex.md) | [`ai-codex-logs.txt`](./ticket-075/ai-codex-logs.txt) | [`changelog.md`](./ticket-075/changelog.md) | | **ticket-076** | [`README.md`](./ticket-076/README.md) | [`preprompt.md`](./ticket-076/preprompt.md) | - | [`ai-codex.md`](./ticket-076/ai-codex.md) | [`ai-codex-logs.txt`](./ticket-076/ai-codex-logs.txt) | [`changelog.md`](./ticket-076/changelog.md) | +| **ticket-077** | [`README.md`](./ticket-077/README.md) | [`preprompt.md`](./ticket-077/preprompt.md) | - | [`ai-devin.md`](./ticket-077/ai-devin.md) | [`ai-devin-logs.txt`](./ticket-077/ai-devin-logs.txt) | [`changelog.md`](./ticket-077/changelog.md) | diff --git a/project/ticket-083/README.md b/project/ticket-083/README.md new file mode 100644 index 00000000..faae98c9 --- /dev/null +++ b/project/ticket-083/README.md @@ -0,0 +1,43 @@ +# Ticket 083: Keep workspace comparison evidence outside analysed state + +- **ID**: ticket-083 +- **Owner**: unresolved:human +- **Status**: IN_PROGRESS +- **Workflow state**: EDIT +- **Created**: 2026-08-21 + +## Goal and scope + +Make `compare-workspace` observationally pure with respect to the repository it +analyses. Its requested output directory must control both pipeline artifacts +and extractor caches, including when that directory is outside the repository. +Generated evidence must not appear in the captured Git status or in either +intent graph. + +## Acceptance criteria + +- [x] AC-01: The human owner explicitly requested implementation and continued + autonomous testing on 2026-08-21. +- [ ] AC-02: An external `outputDir` receives current-workspace artifacts and + caches without creating `.intent` in the analysed repository. +- [ ] AC-03: Comparison status is captured before output creation and excludes + the selected in-repository output directory when it already exists. +- [ ] AC-04: Focused, full Node, governance and Docker checks pass. + +## Participants + +- Human participant: unresolved; no user-* file was created by this script. +- Agent participant: [ai-codex.md](ai-codex.md) + +## Approval gate + +The user's request to implement the missing ticket-to-code verification loop, +followed by explicit continuation, authorizes this bounded defect repair. This +conversation note is not merge authorization; protected exact-head Validator +evidence remains required. + +## Non-goals + +- No change to comparison scoring or authority semantics. +- No automatic application, approval, merge or ticket closure. +- No new dependency or public schema. diff --git a/project/ticket-083/ai-codex-logs.txt b/project/ticket-083/ai-codex-logs.txt new file mode 100644 index 00000000..e69de29b diff --git a/project/ticket-083/ai-codex.md b/project/ticket-083/ai-codex.md new file mode 100644 index 00000000..0a9604e8 --- /dev/null +++ b/project/ticket-083/ai-codex.md @@ -0,0 +1,32 @@ +--- +participant-id: agent:codex +participant: codex +role: agent +ticket: ticket-083 +--- +# Participant: codex (AI agent) + +## Understanding + +`compareWorkspaceIntent` correctly scopes its result directory, but passes the +unchanged ambient `T2CConfig` to both pipelines. AST and documentation caches +read `config.outputDir`, so an external `--out` can still create `.intent/cache` +inside the analysed checkout. Existing output can also be reported as dirty +input. The comparison then observes artifacts produced by the observer. + +## Execution plan + +1. Add a regression with an external output directory and enabled cache. +2. Bind each pipeline config's output directory to its actual pipeline output. +3. Exclude only the selected generated directory from the pre-run Git snapshot; + retain every unrelated dirty file. +4. Run focused, full, governance and Docker verification. + +## Actual changes + +- Read governance and current implementation, reproduced the defect on clean + Registry and Core worktrees, and recorded explicit approval before edits. + +## Blockers + +- None for implementation; protected publication remains independent. diff --git a/project/ticket-083/changelog.md b/project/ticket-083/changelog.md new file mode 100644 index 00000000..5472f73c --- /dev/null +++ b/project/ticket-083/changelog.md @@ -0,0 +1,7 @@ +# Ticket Changelog (ticket-083) + +## [0.1.0] - 2026-08-21 + +- Initial governance scaffold created. +- No human participant identity or content was generated. +- Bound the accepted plan for observationally pure workspace evidence. diff --git a/project/ticket-083/intent.json b/project/ticket-083/intent.json new file mode 100644 index 00000000..93002b50 --- /dev/null +++ b/project/ticket-083/intent.json @@ -0,0 +1,67 @@ +{ + "schema": "new-project.intent/v3", + "ticket": "ticket-083", + "summary": "Keep workspace comparison artifacts and caches outside the analysed repository state", + "workstream": "core-dsl", + "classification": { + "kind": "BUG", + "priority": "P1", + "origin": "regression" + }, + "allowedPaths": [ + "project/ticket-083/**", + "TODO.md", + "project/TICKETS.md", + "src/comparison/workspace.ts", + "test/workspace.test.ts" + ], + "forbiddenPaths": [ + "project/ticket-*/user-*.md", + "src/cli.ts", + "src/interfaces/**", + "src/pipeline/**", + "src/extractors/**", + "package.json", + "package-lock.json" + ], + "stacks": ["node", "docker"], + "dependsOn": [], + "conflictsWith": [], + "integrationTicket": null, + "delivery": { + "acceptedBaseSha": "d14bb260581e9c3f0fbe04f9f475a185476dd358", + "targetBranch": "main", + "outcome": "compare-workspace writes every generated artifact and cache only below its selected output directory without affecting observed repository state", + "nonGoals": [ + "No scoring or DSL schema change", + "No mutation authority", + "No CLI parsing change" + ], + "complexity": "XS", + "estimatedMinutes": 20, + "budgets": { + "maxImplementationFiles": 2, + "maxAffectedComponents": 1, + "maxPublicInterfaceChanges": 0, + "maxRuntimeDependencies": 0 + }, + "architecture": { + "status": "accepted", + "decision": "Derive a comparison-local config whose outputDir follows each pipeline root and selected evidence directory, and filter only the selected generated path from the pre-run status snapshot", + "components": [ + {"name": "workspace-comparison", "paths": ["src/comparison/workspace.ts", "test/workspace.test.ts"]} + ], + "responsibilityChanges": false, + "interfaceChanges": [], + "dataChanges": [], + "ui": {"impact": "none", "states": [], "evidence": []}, + "rollback": "Revert the implementation commit; comparison returns to using ambient config.outputDir" + }, + "runtimeDependencies": [], + "validation": [ + {"criterion": "AC-02", "commands": ["node --test dist/test/workspace.test.js"], "evidence": "External output regression leaves no generated directory in the analysed repository"}, + {"criterion": "AC-03", "commands": ["node --test dist/test/workspace.test.js"], "evidence": "Selected in-tree evidence is absent from changedFiles and graph input"}, + {"criterion": "AC-04", "commands": ["npm run verify", "./project/governance-check.sh", "make docker-smoke"], "evidence": "Repository gates pass on exact head"} + ] + } +} diff --git a/project/ticket-083/preprompt.md b/project/ticket-083/preprompt.md new file mode 100644 index 00000000..71083366 --- /dev/null +++ b/project/ticket-083/preprompt.md @@ -0,0 +1,8 @@ +# Ticket preprompt + +- **Task ID**: ticket-083 +- **Task title**: Keep workspace comparison evidence outside analysed state +- **Created**: 2026-08-21T21:00:08Z + +Keep executable implementation outside this governance/evidence directory. +Read a human-owned user-*.md file only when one exists. From 087ed6813facda063f3b514ad3c026edceadd346 Mon Sep 17 00:00:00 2001 From: Tom Softreck Date: Fri, 21 Aug 2026 23:07:16 +0200 Subject: [PATCH 2/2] fix(ticket-083): isolate comparison evidence --- TODO.md | 2 +- project/ticket-083/README.md | 18 ++++++++++++++---- project/ticket-083/ai-codex-logs.txt | 8 ++++++++ project/ticket-083/ai-codex.md | 4 ++++ project/ticket-083/changelog.md | 2 ++ src/comparison/workspace.ts | 22 +++++++++++++++++++--- test/workspace.test.ts | 14 ++++++++++++++ 7 files changed, 62 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index d789bdae..ab73d69e 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,7 @@ - [ ] [`ticket-083`](project/ticket-083/README.md) — keep workspace comparison artifacts and caches outside analysed repository state. Current state: - `IN_PROGRESS / EDIT`. + `IN_PROGRESS / PUBLICATION`. - [ ] [`ticket-054`](project/ticket-054/README.md) — restore skills-agent discovery, prove a todo2code → Repair PR → independent Validator hand-off, then add three bounded todo2code-grounded skills. Current state: diff --git a/project/ticket-083/README.md b/project/ticket-083/README.md index faae98c9..7c167e32 100644 --- a/project/ticket-083/README.md +++ b/project/ticket-083/README.md @@ -3,7 +3,7 @@ - **ID**: ticket-083 - **Owner**: unresolved:human - **Status**: IN_PROGRESS -- **Workflow state**: EDIT +- **Workflow state**: PUBLICATION - **Created**: 2026-08-21 ## Goal and scope @@ -18,11 +18,11 @@ intent graph. - [x] AC-01: The human owner explicitly requested implementation and continued autonomous testing on 2026-08-21. -- [ ] AC-02: An external `outputDir` receives current-workspace artifacts and +- [x] AC-02: An external `outputDir` receives current-workspace artifacts and caches without creating `.intent` in the analysed repository. -- [ ] AC-03: Comparison status is captured before output creation and excludes +- [x] AC-03: Comparison status is captured before output creation and excludes the selected in-repository output directory when it already exists. -- [ ] AC-04: Focused, full Node, governance and Docker checks pass. +- [x] AC-04: Focused, full Node, governance and Docker checks pass. ## Participants @@ -41,3 +41,13 @@ evidence remains required. - No change to comparison scoring or authority semantics. - No automatic application, approval, merge or ticket closure. - No new dependency or public schema. + +## Verification evidence + +- The focused workspace suite passes 3/3, including repeated in-tree output and + external output with cache ownership assertions. +- `npm run verify` passes 421 tests with one existing JDK-only skip; type, + module, environment, workflow, generated-analysis, structured-response and + schema gates pass. +- `make docker-smoke`, governance and diff checks pass locally. Protected + exact-head review remains pending. diff --git a/project/ticket-083/ai-codex-logs.txt b/project/ticket-083/ai-codex-logs.txt index e69de29b..2d49ff3d 100644 --- a/project/ticket-083/ai-codex-logs.txt +++ b/project/ticket-083/ai-codex-logs.txt @@ -0,0 +1,8 @@ +$ npm run build && node --test dist/test/workspace.test.js +PASS: 3 tests + +$ npm run verify +PASS: 421 tests, 1 JDK-only skip + +$ make docker-smoke +docker smoke: PASS diff --git a/project/ticket-083/ai-codex.md b/project/ticket-083/ai-codex.md index 0a9604e8..5e242b13 100644 --- a/project/ticket-083/ai-codex.md +++ b/project/ticket-083/ai-codex.md @@ -26,6 +26,10 @@ input. The comparison then observes artifacts produced by the observer. - Read governance and current implementation, reproduced the defect on clean Registry and Core worktrees, and recorded explicit approval before edits. +- Bound both pipeline configurations to their actual output directories and + excluded the selected in-repository evidence path from the status snapshot. +- Added regression coverage and passed focused, full Node and Docker checks. +- Transitioned to `IN_PROGRESS / PUBLICATION`; no merge is claimed locally. ## Blockers diff --git a/project/ticket-083/changelog.md b/project/ticket-083/changelog.md index 5472f73c..fdb73085 100644 --- a/project/ticket-083/changelog.md +++ b/project/ticket-083/changelog.md @@ -5,3 +5,5 @@ - Initial governance scaffold created. - No human participant identity or content was generated. - Bound the accepted plan for observationally pure workspace evidence. +- Kept extractor caches under the selected comparison output and excluded + generated evidence from observed Git state. diff --git a/src/comparison/workspace.ts b/src/comparison/workspace.ts index f3ad0612..94578386 100644 --- a/src/comparison/workspace.ts +++ b/src/comparison/workspace.ts @@ -144,7 +144,13 @@ export async function compareWorkspaceIntent( const baseRef = options.baseRef?.trim() || defaultBaseRef(); const baseCommit = (await git(repositoryRoot, ['rev-parse', '--verify', `${baseRef}^{commit}`])).trim(); const headCommit = (await git(repositoryRoot, ['rev-parse', '--verify', 'HEAD^{commit}'])).trim(); - const status = await git(repositoryRoot, ['status', '--porcelain=v1', '--untracked-files=all']); + const statusArguments = ['status', '--porcelain=v1', '--untracked-files=all']; + const outputRelativeToRepository = path.relative(repositoryRoot, path.resolve(root, outputDir)); + if (outputRelativeToRepository && !outputRelativeToRepository.startsWith('..') && !path.isAbsolute(outputRelativeToRepository)) { + const normalizedOutput = outputRelativeToRepository.replace(/\\/g, '/'); + statusArguments.push('--', '.', `:(exclude,top)${normalizedOutput}`, `:(exclude,top)${normalizedOutput}/**`); + } + const status = await git(repositoryRoot, statusArguments); const changedFiles = status.split(/\r?\n/).filter(Boolean).map((line) => line.slice(3)).sort(); const [behind, ahead] = parseAheadBehind(await git(repositoryRoot, ['rev-list', '--left-right', '--count', `${baseCommit}...HEAD`])); const deadlineDecision = calculateWorkspaceComparisonDeadline( @@ -169,8 +175,18 @@ export async function compareWorkspaceIntent( const baseOptions = await optionsForRoot(baseRoot, { ...pipelineOptions, root: baseRoot, outputDir: '.intent-compare-base' }); const currentOptions = await optionsForRoot(root, { ...pipelineOptions, root, outputDir }); const boundedOpenRouter = { ...config.openRouter, signal: deadlineController.signal }; - const baseConfig = { ...config, root: baseRoot, openRouter: boundedOpenRouter }; - const currentConfig = { ...config, root, openRouter: boundedOpenRouter }; + const baseConfig = { + ...config, + root: baseRoot, + outputDir: baseOptions.outputDir, + openRouter: boundedOpenRouter, + }; + const currentConfig = { + ...config, + root, + outputDir: currentOptions.outputDir, + openRouter: boundedOpenRouter, + }; let baseRun: Awaited>; let currentRun: Awaited>; diff --git a/test/workspace.test.ts b/test/workspace.test.ts index 7edfd8d2..c4acfc25 100644 --- a/test/workspace.test.ts +++ b/test/workspace.test.ts @@ -112,6 +112,18 @@ test('workspace comparison measures origin/main against uncommitted filesystem i assert.equal(baseManifest.stages.summary.status, 'skipped'); assert.equal(workspaceManifest.stages.summary.status, 'skipped'); assert.equal(baseManifest.configuration.summaryLlm, false); + assert.equal(await pathExists(path.join(root, '.intent')), false, 'ambient cache directory is not used'); + + const repeated = await compareWorkspaceIntent({ + root, + outputDir: '.intent-workspace', + includeDocumentationLlm: false, + }, config); + assert.deepEqual( + repeated.workspace.changedFiles, + ['runtime.ts', 'unplanned.ts'], + 'existing comparison evidence is excluded from the observed Git state', + ); const outside = path.join(parent, 'outside-comparison'); await assert.rejects( @@ -131,4 +143,6 @@ test('workspace comparison measures origin/main against uncommitted filesystem i assert.ok(await pathExists(comparisonFile)); assert.equal(path.relative(outside, comparisonFile).startsWith('..'), false, 'artifacts land under the requested directory'); assert.equal(await pathExists(path.join(root, 'outside-comparison')), false); + assert.equal(await pathExists(path.join(root, '.intent')), false, 'external output also owns extractor caches'); + assert.ok(await pathExists(path.join(outside, 'cache')), 'extractor cache follows the external output directory'); });