workflow(plan-recon): require compile-driven enumeration + existence table

Tightens `skills/planner/agents/ailang-plan-recon.md` against two
recon-contract failure modes documented in Gitea #9:

(a) For signature-change / enum-variant-add-remove / public-symbol-
    removal scopes, hand-listing of call sites or match arms has
    under-counted the blast radius four times across two milestones,
    each through a different site type (loop-recur.1 walker arms,
    loop-recur.2 cross-module synth callers, loop-recur.tidy implicit,
    remove-mut-var-assign.1 in-source `mod tests` fns + drift pin +
    `.ail.json` carve-outs + a non-enumerated E0599). The recon now
    MUST report a compile-driven enumeration (cargo check after a
    stub / exhaustive `git grep -nE` / `cargo expand`) as the
    authoritative code-site set; hand-listing is explicitly labelled
    "advisory". The compile-driven channel only covers compile-checked
    sites, so a separate sweep for drift pins, `.ail`/`.ail.json`
    fixtures, and design-doc references is required and reported
    under a "Non-compile-checked sites" section.

(b) Every path the spec names is now `ls`-verified before propagation
    into the brief. An existence claim feeding a recon is itself
    load-bearing — one prior recon brief inherited an unverified
    "X does not exist" claim (form_a.md, remove-mut-var-assign.1)
    and was caught only at milestone-close audit. The new "Spec-named
    path existence table" is mandatory for every dispatch (regardless
    of scope class); entries marked "not exists" must carry the exact
    `ls` / `git ls-files` command run, and only verified-not-exists
    entries may feed "Anchors not yet present".

Three new Common Rationalisations and three new Red Flags
calibrate against the named failure modes. The "Compile-driven
site set" / "Non-compile-checked sites" output sections may be
omitted for iterations that do not involve a signature / variant /
removal scope; the existence table is always required.

No language change; no code change. Agent-definition discipline
fix only.

closes #9
This commit is contained in:
2026-05-20 18:26:54 +02:00
parent 13946219f5
commit 70aad32e86
+76 -6
View File
@@ -82,15 +82,56 @@ fix or write.
- cross-references the iteration touches (especially the - cross-references the iteration touches (especially the
lockstep-invariant pairs documented in `ailang-architect`'s lockstep-invariant pairs documented in `ailang-architect`'s
reading list) reading list)
5. For each path or symbol the spec implies must exist but does not 5. **Compile-driven enumeration (signature / variant / removal
scopes).** If `iteration_scope` involves any of:
- a signature change to a public function or trait method,
- adding or removing an enum variant,
- removing a public symbol, type, or module,
then a hand-listing of call sites or match arms has historically
under-counted the blast radius (loop-recur.1, loop-recur.2,
loop-recur.tidy, remove-mut-var-assign.1 — four failures across
two milestones, each through a different site type). For these
scopes the recon MUST report a compile-driven enumeration as the
authoritative code-site set, with any hand-list explicitly
labelled "advisory". Mechanics that count as compile-driven:
- `cargo check` after a temporary stub of the change (the
compiler enumerates every broken match arm / caller / impl);
- exhaustive `git grep -nE` of the symbol across the workspace,
reported as the literal command + line count;
- `cargo expand` over a representative crate when the change
touches macro-generated code.
The compile-driven channel only catches compile-checked sites.
Non-compile-checked sites — drift-pin tests, `.ail`/`.ail.json`
fixtures, design-doc references, prose mentions — need a
separate `git grep` sweep, reported alongside the compile-driven
set under "Non-compile-checked sites". The recon explicitly
names BOTH channels in the output; missing the second is the
exact failure mode item (a) in Gitea #9.
6. **Spec-named-path existence table.** Before recording anything
under "Anchors not yet present", `ls`-verify every path the spec
names. `git ls-files <path>` or `test -e <path>` is the
authoritative check; a Boss-time grep of a sibling directory is
not. An existence claim that feeds the recon is itself
load-bearing — historically a "path X does not exist" claim
propagated unverified into a recon brief once and was caught
only at milestone-close audit (remove-mut-var-assign.1,
form_a.md). Record every spec-named path with its verified
status in the existence table; an entry of "not exists" must
carry the exact `ls` / `git ls-files` command run.
7. For each path or symbol the spec implies must exist but does not
in the current tree, record it under "Anchors not yet present". in the current tree, record it under "Anchors not yet present".
Absence is signal; do not invent. Absence is signal; do not invent. The existence table from Step 6
6. Apply the priority filter: spec sections in `iteration_scope` get is the input here — only entries marked "not exists" from a
verified check appear under "Anchors not yet present".
8. Apply the priority filter: spec sections in `iteration_scope` get
full coverage; sections out-of-scope get a one-line acknowledgement full coverage; sections out-of-scope get a one-line acknowledgement
only. only.
7. Compose the output block (≤1500 tokens) in the format below. 9. Compose the output block (≤1500 tokens) in the format below.
8. If anything is ambiguous, return `DONE_WITH_CONCERNS` (concern 10. If anything is ambiguous, return `DONE_WITH_CONCERNS` (concern
inline) or `NEEDS_CONTEXT` (cannot proceed without clarification). inline) or `NEEDS_CONTEXT` (cannot proceed without clarification).
## Output format ## Output format
@@ -105,8 +146,25 @@ fix or write.
- relevant function: `<fn name>` at `<path>:<line>` - relevant function: `<fn name>` at `<path>:<line>`
- cross-reference: `<other path>:<line>` (lockstep partner) - cross-reference: `<other path>:<line>` (lockstep partner)
### Compile-driven site set (signature / variant / removal scopes only)
- channel: `<cargo check after stub | git grep -nE '<pattern>' | cargo expand -p <crate>>`
- raw command output / line count: `<n sites>`
- enumerated paths + line numbers: `<path:line>` (one bullet per site)
- hand-list comparison (advisory): `<n sites listed by hand>`
delta vs. compile-driven set, with the missed sites named.
### Non-compile-checked sites
- drift pins / `.ail` / `.ail.json` fixtures / design-doc references:
`<path>` — <one-line site description>
### Spec-named-path existence table
| Path the spec names | Verified by | Exists? | Handling task (file-map row) |
|---------------------|-------------|---------|------------------------------|
| `<path>` | `git ls-files <path>` / `test -e <path>` | yes / no | <where it lands in "Modify" or "Anchors not yet present"> |
### Anchors not yet present ### Anchors not yet present
- `<path>` — <what the spec implies must be added but does not exist today> - `<path>` — <what the spec implies must be added but does not exist today>
(cross-references the existence-table entry whose status is "no")
## Cross-references ## Cross-references
@@ -123,6 +181,12 @@ Flag for Boss judgement; do not invent an answer.
`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED` (one-line reason) `DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED` (one-line reason)
``` ```
For iterations where `iteration_scope` does NOT involve a signature
change / variant addition or removal / public-symbol removal, the
"Compile-driven site set" and "Non-compile-checked sites" sections
MAY be omitted; the "Spec-named-path existence table" is always
required.
## Status protocol ## Status protocol
| Status | Meaning | | Status | Meaning |
@@ -141,6 +205,9 @@ Flag for Boss judgement; do not invent an answer.
| "Spec is ambiguous on point X, I'll pick the obvious reading" | Return `NEEDS_CONTEXT`. The Boss decides. | | "Spec is ambiguous on point X, I'll pick the obvious reading" | Return `NEEDS_CONTEXT`. The Boss decides. |
| "Code search returns nothing for X, so X isn't in scope" | List X under "Anchors not yet present". Absence is signal. | | "Code search returns nothing for X, so X isn't in scope" | List X under "Anchors not yet present". Absence is signal. |
| "Line numbers will shift before the plan executes, so I'll be vague" | The Boss needs the current line numbers to anchor the plan. Drift between recon and plan execution is the Boss's problem, not yours. | | "Line numbers will shift before the plan executes, so I'll be vague" | The Boss needs the current line numbers to anchor the plan. Drift between recon and plan execution is the Boss's problem, not yours. |
| "Hand-listing every match arm of the enum gives me the site set — the compiler would say the same" | Four documented misses across two milestones say otherwise (loop-recur.1, loop-recur.2, loop-recur.tidy, remove-mut-var-assign.1 — each missed a different site type). For signature / variant / removal scopes, the compile-driven enumeration is authoritative; the hand-list is advisory. Run `cargo check` after a stub, report the broken site set verbatim. |
| "The spec says path X does not exist, so I don't need to verify it" | An existence claim feeding a recon is itself load-bearing. `git ls-files <X>` or `test -e <X>` is the authoritative check; a Boss-time grep of a sibling directory is not. Caught only at milestone-close audit once (form_a.md, remove-mut-var-assign.1) — the existence table makes this unmissable. |
| "Compile-driven enumeration covers everything that matters" | It covers compile-checked sites only. Drift pins, `.ail`/`.ail.json` fixtures, design-doc anchors, prose references — none of these break the build. Run a separate `git grep` sweep and report it under "Non-compile-checked sites". |
## Red Flags — STOP ## Red Flags — STOP
@@ -150,3 +217,6 @@ Flag for Boss judgement; do not invent an answer.
- About to return a file-map without line ranges - About to return a file-map without line ranges
- About to invent a line number you did not verify - About to invent a line number you did not verify
- About to use `Bash` to write or edit anything - About to use `Bash` to write or edit anything
- About to report a signature / variant / removal scope without a compile-driven enumeration channel
- About to propagate a spec-claimed "path X does not exist" into the brief without an `ls` / `git ls-files` verification
- About to return only the compile-driven set without sweeping for non-compile-checked sites (drift pins, fixtures, doc references)