// compiler-driven-edit — the observe-then-bounce loop for a // behaviour-preserving type/signature edit, as a Workflow script. // // This is the executor for the `compiler-driven` selector arm (see // boss/SKILL.md "Entry-path reflection"). The arm is entered on an // OBSERVABLE/syntactic trigger — "a type or signature edit at a definition // site that propagates mechanically across the sites the type checker // enumerates" — NOT on a prediction that the change is behaviour-preserving. // A real build + suite run then settles the verdict: // // clean build AND suite green unchanged -> DONE (edits unstaged; orchestrator commits) // a hole the edit cannot resolve -> BOUNCE up, per the straddle rule: // forces a design choice -> specify // is test-specifiable new behaviour -> tdd (RED-first) // the suite is not green / not unchanged -> BOUNCE to debug (RED-first) // // The fallible ex-ante guess ("this is behaviour-preserving") is replaced by // a cheap ex-post detection: a mis-routed behaviour change fails the // post-condition and bounces instead of shipping green-but-wrong. This // generalises the plugin's RED-first philosophy — observe, do not predict. // // "Suite green UNCHANGED" is load-bearing: the suite must pass with NO test // added, removed, or weakened to make it pass. A new test would mean the // edit encoded new behaviour — which is the tdd/spec arm, not this one. // // The `verify` step below (build + suite-green-unchanged) is the single // phase the implement loop's pure-refactor case could never invoke on its // own; here it is a standalone, independently-invokable agent call. // // Discipline: the script never commits and never moves main HEAD; all edits // stay unstaged for the orchestrator. The script has no shell of its own — // the edit and the verify both run inside agent() calls. // // Carrier — Workflow `args`: // edit_description: what type/signature change to make // def_site: the definition site (file + symbol) it originates from export const meta = { name: 'compiler-driven-edit', description: 'Execute a behaviour-preserving type/signature edit at a definition site, propagate it mechanically across the sites the build enumerates, then let a real build + suite run settle the verdict: clean build AND suite green unchanged -> done (edits unstaged for the orchestrator to commit); a hole the edit cannot resolve -> bounce up per the straddle rule (specify for a design choice, tdd for test-specifiable new behaviour); suite not green-unchanged -> bounce to debug (RED-first). Never commits, never moves main HEAD.', phases: [{ title: 'Edit + propagate' }, { title: 'Verify' }, { title: 'Verdict' }], } const { edit_description, def_site } = args || {} const STANDING = "Standing reading first (the plugin convention): read the project's CLAUDE.md " + '(its "## Skills plugin: project facts" name the build and test commands) and ' + '`git log -10 --format=full`. Resolve the build/test commands from those facts.' const EDIT_SCHEMA = { type: 'object', additionalProperties: false, required: ['build_clean', 'hole_needs_decision'], properties: { build_clean: { type: 'boolean', description: 'the project build is clean after the edit + mechanical propagation' }, hole_needs_decision: { type: 'boolean', description: 'a site could not be resolved mechanically: it either forces a design choice, or resolving it would encode new behaviour — so the change was not purely behaviour-preserving after all', }, bounce_to: { type: 'string', enum: ['specify', 'tdd'], description: 'on hole_needs_decision, per the straddle rule: `specify` if the hole forces a genuine design choice; `tdd` if the discovered work is new behaviour pinnable as one falsifiable assertion. When unsure, `specify` (the conservative heavy path, which itself bounces onward).', }, hole_detail: { type: 'string', description: 'on hole_needs_decision: what the hole forces (the decision, or the new behaviour discovered)' }, sites: { type: 'integer', description: 'number of sites the build flagged and the edit touched' }, }, } const VERIFY_SCHEMA = { type: 'object', additionalProperties: false, required: ['build_clean', 'suite_green_unchanged'], properties: { build_clean: { type: 'boolean' }, suite_green_unchanged: { type: 'boolean', description: 'the full suite passes with NO test added, removed, or weakened to make it pass', }, detail: { type: 'string', description: 'on failure: which tests went red, or which test was changed' }, }, } // ---- Phase 1 — make the edit and let the compiler enumerate the sites ---- phase('Edit + propagate') const edit = await agent( `${STANDING}\n\nMake this behaviour-preserving type/signature edit at its definition site, then propagate it ` + 'MECHANICALLY across every site the build flags — let the type checker enumerate the edit sites. Introduce NO ' + 'new behaviour, add NO new test, change NO existing test. If a flagged site cannot be resolved without making a ' + 'design decision, OR resolving it would encode new behaviour, STOP and report it as a hole rather than guessing. ' + 'When you report a hole, CLASSIFY it (straddle rule): set bounce_to = "specify" if it forces a genuine design ' + 'choice, or "tdd" if the discovered work is new behaviour pinnable as one falsifiable assertion. When unsure, ' + '"specify". Leave all edits UNSTAGED; never commit.\n\n' + `EDIT: ${edit_description}\nDEFINITION SITE: ${def_site}\n\n` + 'Then run the project build and report: is the build clean, does any hole need a decision (with detail + bounce_to), and how many sites were touched.', { agentType: 'implementer', label: 'edit+propagate', phase: 'Edit + propagate', schema: EDIT_SCHEMA }, ) if (!edit) return { status: 'BLOCKED', reason: 'edit agent did not return' } if (edit.hole_needs_decision) { // The change was not purely mechanical after all. Per the straddle rule, // route UP: a genuine design choice -> specify; test-specifiable new // behaviour -> tdd (RED-first). Default to specify when unclassified — // the conservative heavy path, which itself bounces onward. const to = edit.bounce_to === 'tdd' ? 'tdd' : 'specify' return { status: 'BOUNCE', to, reason: to === 'tdd' ? 'discovered test-specifiable new behaviour' : 'a hole forces a design decision', detail: edit.hole_detail || '', } } // ---- Phase 2 — verify (the independently-invokable phase) --------------- phase('Verify') const verify = await agent( `${STANDING}\n\nVerify the edit is behaviour-preserving WITHOUT changing any code or test. Run the project build ` + 'and the full test suite. Report whether the build is clean and whether the suite is green AND UNCHANGED — i.e. ' + 'every test passes and NO test was added, removed, or weakened versus the pre-edit baseline (compare `git diff ' + 'HEAD` for test-file changes). Do NOT edit anything; do NOT commit.', { label: 'verify(build+suite)', phase: 'Verify', schema: VERIFY_SCHEMA }, ) // ---- Phase 3 — verdict -------------------------------------------------- phase('Verdict') if (verify && verify.build_clean && verify.suite_green_unchanged) { return { status: 'DONE', sites: edit.sites || null, note: 'behaviour-preserving edit verified; edits unstaged in the working tree for the orchestrator to commit', } } // observe-then-bounce: the post-condition failed, so the change was NOT // behaviour-preserving after all. Route RED-first via debug rather than // shipping a green-but-wrong commit. return { status: 'BOUNCE', to: 'debug', reason: !verify ? 'verify agent did not return' : !verify.build_clean ? 'build not clean after the edit' : 'suite not green-unchanged — a regression surfaced, so the edit was not behaviour-preserving', detail: verify ? verify.detail || '' : '', }