From 7973ae5a7385663d430dce5e566dba28f0900562 Mon Sep 17 00:00:00 2001 From: Lee Overy Date: Mon, 27 Jul 2026 15:04:23 +0100 Subject: [PATCH] test(prose): quick-fix scoping, the whole definition stage in one walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quick-fix was the only work type with no coverage anywhere. This walks its entire definition stage — entry, context, complexity, specification, format, tasks, conclusion — stopping at the bridge handoff. The first case to take a phase from its opening argument to its completion render. The spine is a seven-element ordering chain across the pass: the entry's status check, then specification started and completed, then planning started and completed, then scoping completed, then the completion render. Alongside it, the format choice is pinned in code — the project default must be set to local-markdown — rather than trusted to roleplay: conduct carries every user interaction here, including the format menu, as a disposition of the described person, and the invariant is what makes the choice a fact. The carrier deliberately holds the what and the why but not the where, so gather-context's questioning arm is the only honest reading of its guard. That guard is judgement-shaped, and a carrier that plainly lacks one of the three is what keeps the case deterministic. First run: FAIL, correctly. Step 3 caught gather-context re-asking the what and why the carrier already answers — its guard reads only the seed and description, blind to the session log's Exploration, and its question block is a static fence prescribing all four questions regardless. Both walks reproduced it. The run also caught the confirmation walk skipping prescribed registration calls while converging on a passing world — the exact hazard the deterministic checks exist for — and exposed two harness gaps in the reporting and the recorder, each fixed above this in the stack. Co-Authored-By: Claude Opus 5 --- .../cases/scoping-defines-the-quickfix/act.md | 4 ++ .../scoping-defines-the-quickfix/assert.md | 46 ++++++++++++ .../scoping-defines-the-quickfix/case.json | 53 ++++++++++++++ .../fixture-state.cjs | 12 ++++ .../scoping-defines-the-quickfix/fixture.md | 8 +++ .../fixture/.claude/settings.json | 9 +++ .../fixture/.recipe-hash | 1 + .../fixture/.workflows/.state/migrations | 52 ++++++++++++++ .../fixture/.workflows/_gitignore.fixture | 2 + .../fixture/.workflows/manifest.json | 7 ++ .../discovery/sessions/session-001.md | 40 +++++++++++ .../.workflows/support-email/manifest.json | 8 +++ tests/prose/mainlines/quickfix.cjs | 72 +++++++++++++++++++ 13 files changed, 314 insertions(+) create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/act.md create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/assert.md create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/case.json create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture-state.cjs create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture.md create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture/.claude/settings.json create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture/.recipe-hash create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/.state/migrations create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/_gitignore.fixture create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/manifest.json create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/support-email/discovery/sessions/session-001.md create mode 100644 tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/support-email/manifest.json create mode 100644 tests/prose/mainlines/quickfix.cjs diff --git a/tests/prose/cases/scoping-defines-the-quickfix/act.md b/tests/prose/cases/scoping-defines-the-quickfix/act.md new file mode 100644 index 000000000..80ca75b16 --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/act.md @@ -0,0 +1,4 @@ +Execute skills/workflow-scoping-entry/SKILL.md with arguments +$0=quick-fix, $1=support-email, and continue into the processing skill it +hands off to. Follow the whole scoping pass to its conclusion. Stop when +the prose reaches the pipeline continuation — do not invoke the bridge. diff --git a/tests/prose/cases/scoping-defines-the-quickfix/assert.md b/tests/prose/cases/scoping-defines-the-quickfix/assert.md new file mode 100644 index 000000000..f32ac7b6c --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/assert.md @@ -0,0 +1,46 @@ +The prose should have taken this path: + +1. the entry finds no scoping recorded, reads the carrier — the + description and the session log's exploration — and hands off with it; + the entry itself creates and asks nothing +2. no specification exists, so the pass starts fresh with no resume + choice put to the user +3. context gathering reads the seed and description first, and its + questions go to what the carrier does not answer — where the address + appears — not to the what and why it already holds. Two exchanges at + most +4. the complexity check passes silently: a mechanical single-purpose + change raises no warning and no promotion offer +5. the specification is written and registered through the engine — + started and completed — before any plan exists +6. with no project default recorded, the format choice is put to the + user, and the choice is recorded as the project default +7. tasks are written under the hard rules — at most two, no acceptance + criteria — and the plan is registered with its gates set to auto; + scoping itself is marked complete only after the plan is in place +8. the conclusion renders the completion display and reaches the bridge + handoff, where the walk stops without invoking it + +Further claims: + +- no agents are dispatched at any point — scoping writes its spec and + tasks directly +- nothing is implemented: no task is started, and nothing outside + `.workflows/` changes + +EXPECTED WORLD — from a work unit holding only its discovery carrier: + +- a specification at + `.workflows/support-email/specification/support-email/specification.md` + carrying the scoping shape — change description, scope, exclusions, + verification — and naming both the old and new addresses +- the scope reflecting what the user actually said: the checkout footer + and contact page as known sites, the transactional template as + suspected, with the exact locations left to implementation to confirm +- a plan at `.workflows/support-email/planning/support-email/planning.md` + with one or two task files beside it in local-markdown form, none of + them started +- the manifest holding specification, planning and scoping all completed, + the plan on the local-markdown format with its gates set to auto +- local-markdown recorded as the project's default plan format +- no implementation or review directories, and no second work unit diff --git a/tests/prose/cases/scoping-defines-the-quickfix/case.json b/tests/prose/cases/scoping-defines-the-quickfix/case.json new file mode 100644 index 000000000..7b9bb532a --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/case.json @@ -0,0 +1,53 @@ +{ + "origin": "quick-fix had no coverage anywhere — this walks its whole definition stage: entry, context, spec, format, tasks, conclusion, in the single pass scoping is", + "entry": "workflow-scoping-entry", + "world": "claims", + "files": [ + "skills/workflow-scoping-entry/SKILL.md", + "skills/workflow-scoping-entry/references/validate-phase.md", + "skills/workflow-scoping-entry/references/invoke-skill.md", + "skills/workflow-scoping-process/SKILL.md", + "skills/workflow-scoping-process/references/gather-context.md", + "skills/workflow-scoping-process/references/complexity-check.md", + "skills/workflow-scoping-process/references/write-specification.md", + "skills/workflow-scoping-process/references/select-format.md", + "skills/workflow-scoping-process/references/write-tasks.md", + "skills/workflow-scoping-process/references/conclude-scoping.md", + "skills/workflow-shared/references/seed-context.md", + "skills/workflow-knowledge/references/knowledge-usage.md", + "skills/workflow-knowledge/references/contextual-query.md", + "skills/workflow-planning-process/references/output-formats.md", + "skills/workflow-planning-process/references/output-formats/local-markdown/about.md", + "skills/workflow-planning-process/references/output-formats/local-markdown/authoring.md" + ], + "conduct": "The user knows exactly what the change is and why: the support@ mailbox is being retired at the end of the month and mail to it already bounces, so everything should say help@ instead. Asked where it appears, they are sure about the checkout footer and the contact page, think it may also be in a transactional email template, and do not know any file names. They have no exceptions or exclusions to name. When offered tooling or format choices they take the simplest option that adds no external dependencies — plain files in the repo. They answer the question asked, concretely, and say plainly when they do not know.", + "invariants": { + "engine_before_write": true, + "calls_include": [ + "manifest get support-email.scoping.support-email status", + "manifest get support-email description", + "topic start support-email specification support-email", + "topic complete support-email specification support-email", + "topic start support-email planning support-email", + "topic complete support-email planning support-email", + "topic complete support-email scoping support-email", + "project.defaults.plan_format local-markdown", + "render phase-completed support-email --phase scoping" + ], + "calls_in_order": [ + "manifest get support-email.scoping.support-email status", + "topic start support-email specification support-email", + "topic complete support-email specification support-email", + "topic start support-email planning support-email", + "topic complete support-email planning support-email", + "topic complete support-email scoping support-email", + "render phase-completed support-email --phase scoping" + ], + "calls_exclude": [ + "task start", + "task next", + "topic reopen", + "workflow-bridge" + ] + } +} diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture-state.cjs b/tests/prose/cases/scoping-defines-the-quickfix/fixture-state.cjs new file mode 100644 index 000000000..a2b1be75b --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture-state.cjs @@ -0,0 +1,12 @@ +'use strict'; + +// The quick-fix is captured and its definition stage has not begun. + +const m = require('../../mainlines/quickfix.cjs'); + +module.exports = { + build(h) { + m.init(h); + m.create(h); + }, +}; diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture.md b/tests/prose/cases/scoping-defines-the-quickfix/fixture.md new file mode 100644 index 000000000..6a89d4544 --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture.md @@ -0,0 +1,8 @@ +A quick-fix shaped in discovery: the retired support email address needs +replacing with the new one wherever it appears. The work unit exists with +its session log; scoping has not begun, and no specification or plan +exists anywhere. + +The context was cleared at the phase boundary — this session opens cold +at the entry skill with nothing but the two arguments and what is on +disk. diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture/.claude/settings.json b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.claude/settings.json new file mode 100644 index 000000000..ae019ede5 --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.claude/settings.json @@ -0,0 +1,9 @@ +{ + "showClearContextOnPlanAccept": true, + "permissions": { + "allow": [ + "Edit(.workflows/**)", + "Bash(mv .workflows/:*)" + ] + } +} diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture/.recipe-hash b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.recipe-hash new file mode 100644 index 000000000..e7b27d15d --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.recipe-hash @@ -0,0 +1 @@ +ddb2aec6b94ecc0e076a44f9f1cfb6293f30cca9dcc9f933a57276c03f1e8314 diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/.state/migrations b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/.state/migrations new file mode 100644 index 000000000..3c7162acc --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/.state/migrations @@ -0,0 +1,52 @@ +001 +002 +003 +004 +005 +006 +007 +008 +009 +010 +011 +012 +013 +014 +015 +016 +017 +018 +019 +020 +021 +022 +023 +024 +025 +026 +027 +028 +029 +030 +031 +032 +033 +034 +035 +036 +037 +038 +039 +040 +041 +042 +043 +044 +045 +046 +047 +048 +049 +050 +051 +052 diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/_gitignore.fixture b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/_gitignore.fixture new file mode 100644 index 000000000..779cbf28a --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/_gitignore.fixture @@ -0,0 +1,2 @@ +.cache/ +.manifest.json.*.tmp diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/manifest.json b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/manifest.json new file mode 100644 index 000000000..7cb70c695 --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/manifest.json @@ -0,0 +1,7 @@ +{ + "work_units": { + "support-email": { + "work_type": "quick-fix" + } + } +} diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/support-email/discovery/sessions/session-001.md b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/support-email/discovery/sessions/session-001.md new file mode 100644 index 000000000..2b2fb8f08 --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/support-email/discovery/sessions/session-001.md @@ -0,0 +1,40 @@ +# Discovery Session 001 + +Date: 2026-01-01 +Work unit: support-email + +## Description (as of session) + +Replace the retired support email address with the new one. + +## Seed + +(none) + +## Imports + +(none) + +## Map State at Start + +(n/a — single-topic work) + +## Exploration + +The support@ mailbox is being retired at the end of the month and +mail to it already bounces for some customers; everything should +point at help@ instead. Purely mechanical — same links, same copy, +different address. The user was not sure everywhere it appears and +wants that pinned down during scoping. Confirmed as a quick-fix. + +## Edits + +(none) + +## Topics Identified + +(none) + +## Conclusion + +(none) diff --git a/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/support-email/manifest.json b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/support-email/manifest.json new file mode 100644 index 000000000..d76322354 --- /dev/null +++ b/tests/prose/cases/scoping-defines-the-quickfix/fixture/.workflows/support-email/manifest.json @@ -0,0 +1,8 @@ +{ + "name": "support-email", + "work_type": "quick-fix", + "status": "in-progress", + "created": "2026-01-01", + "description": "Replace the retired support email address with the new one", + "phases": {} +} diff --git a/tests/prose/mainlines/quickfix.cjs b/tests/prose/mainlines/quickfix.cjs new file mode 100644 index 000000000..4c98b39b3 --- /dev/null +++ b/tests/prose/mainlines/quickfix.cjs @@ -0,0 +1,72 @@ +'use strict'; + +// Quick-fix mainline: the smallest pipeline shape. Discovery shapes the +// work and commits it; scoping is the whole definition stage in one pass. +// +// The Exploration deliberately holds the what and the why but not the +// where. Scoping's gather-context skips its questions when the carrier +// already captures all three, and that guard is a judgement call — a +// carrier that plainly lacks the where makes the questioning arm the only +// honest reading, which is what the scoping case needs to exercise. + +const WU = 'support-email'; + +function init(h) { + h.knowledge('setup', '--keyword-only'); + h.engine('boot'); +} + +function create(h) { + // Shape per workflow-discovery/references/template.md + confirm-trigger.md: + // single-phase work backfills Exploration at creation; Edits, Topics + // Identified, and Conclusion land as (none). + const log = `.workflows/${WU}/discovery/sessions/session-001.md`; + h.write(log, [ + '# Discovery Session 001', + '', + 'Date: 2026-01-01', + `Work unit: ${WU}`, + '', + '## Description (as of session)', + '', + 'Replace the retired support email address with the new one.', + '', + '## Seed', + '', + '(none)', + '', + '## Imports', + '', + '(none)', + '', + '## Map State at Start', + '', + '(n/a — single-topic work)', + '', + '## Exploration', + '', + 'The support@ mailbox is being retired at the end of the month and', + 'mail to it already bounces for some customers; everything should', + 'point at help@ instead. Purely mechanical — same links, same copy,', + 'different address. The user was not sure everywhere it appears and', + 'wants that pinned down during scoping. Confirmed as a quick-fix.', + '', + '## Edits', + '', + '(none)', + '', + '## Topics Identified', + '', + '(none)', + '', + '## Conclusion', + '', + '(none)', + '', + ].join('\n')); + h.engine('workunit', 'create', WU, 'quick-fix', + '--description', 'Replace the retired support email address with the new one', + '--session-log-file', log); +} + +module.exports = { WU, init, create };