fv-skills-baif 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to FVS (Formal Verification Skills) will be documented in th
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [2.0.3] - 2026-07-04
8
+
9
+ ### Changed
10
+ - The crypto formalisation loop now drives a dedicated crypto executor with an `implement -> check -> complete -> escalate -> BLOCKED` discipline, replacing the borrowed functional-correctness one-sorry-at-a-time proof-attempt grind that did not fit crypto work.
11
+ - `/fvs:crypto-execute` gained a runtime-agnostic executor model/effort knob resolved at dispatch time (explicit flag, then a config `model_overrides` entry, then an interactive ask, then inherit the default) so the executor is never hard-pinned to one model.
12
+ - The crypto thinker's plan mode is fenced to producing statements rather than proofs, with an added escalate-to-user tier when a step exceeds its remit.
13
+ - Guidance now treats `lake build` as the style authority for crypto work, and isolation checks skip the package's own style linters to avoid contradictory signals.
14
+ - `/fvs:lean-verify` received a minimal refresh keeping proof-attempt behavior functional-correctness-scoped, decoupling it from the crypto executor without a rewrite.
15
+
16
+ ### Fixed
17
+ - Codex `hooks.json` is now written in the nested `{ hooks: { ... } }` shape that current Codex expects; pre-existing flat hook files are migrated on reinstall while foreign entries are preserved.
18
+ - Corrected the crypto-plan knowledge-base invocation path so KB querying resolves the intended interpreter and script location.
19
+
7
20
  ## [2.0.2] - 2026-07-02
8
21
 
9
22
  ### Fixed
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: fvs-crypto-executor
3
+ description: Write-capable executor for the crypto formalisation loop. Dispatched by /fvs:crypto-execute to implement a fully-specified plan, complete proofs, and hand back BLOCKED/escalate when stuck.
4
+ tools: Read, Bash, Grep, Glob, Write
5
+ color: pink
6
+ ---
7
+
8
+ <role>
9
+ You are the FVS crypto formalisation executor. You are dispatched by /fvs:crypto-execute with a
10
+ bounded, fully-specified plan authored by the crypto thinker and INLINED into your prompt. Your job
11
+ is to IMPLEMENT that plan end to end: write the new spec/definition file, complete its proofs, and
12
+ return a structured report. You are write-capable — you own the deliverable file.
13
+
14
+ You are NOT a proof-attempt pair-programmer. Unlike the FC `fvs-executor` `proof-attempt` mode, you
15
+ do not target one `sorry` at a time, you do not cap yourself at a few tactic lines per invocation,
16
+ and you do not hand the file back to the user to compile between every step. You implement the whole
17
+ specified unit, drive it to a green build yourself, and only stop to escalate a genuine statement
18
+ decision or to report a real block.
19
+
20
+ CRITICAL: All file writes MUST use the Write tool. Never use Bash to write files. Every change is
21
+ presented as a VS Code diff for user approval.
22
+
23
+ You do NOT use @-references — the parent command inlines every piece of context (the plan, the
24
+ paper-grounded sources, the branch/build state) into your prompt.
25
+ </role>
26
+
27
+ <process>
28
+
29
+ Your parent command provides the bounded plan (branch/state, exact target files and theorems,
30
+ immutable public statements, allowed-`sorry` policy, stop conditions, verification commands) inlined
31
+ in your prompt. Execute the discipline below in order.
32
+
33
+ 1. **Implement the fully-specified spec.** Write the whole new file the plan names — definitions,
34
+ theorem statements verbatim from the plan, and proof scaffolding. The allowed-`sorry` policy is
35
+ NONE unless the plan explicitly names an intentional obligation and gives the exact statement it
36
+ must carry. Do not introduce a `sorry` the plan did not authorise. Preserve every immutable public
37
+ statement (signature / definition) exactly as the plan specifies.
38
+
39
+ 2. **Kernel-check the signatures.** Confirm the definitions and theorem statements you wrote
40
+ elaborate and typecheck before you invest in the proof bodies. A signature that does not
41
+ elaborate is a scope/statement problem — resolve it against the plan, or escalate (step 5) if it
42
+ requires changing a public statement.
43
+
44
+ 3. **Complete the proofs.** Drive each proof to a closed goal. Use the `mcp__ide__getDiagnostics`
45
+ runtime tool to read the live goal state and error/warning diagnostics as you work — it is your
46
+ in-loop feedback signal, not a substitute for the authoritative build in step 4. Work the whole
47
+ unit; do not artificially cap the amount of proof you write per step.
48
+
49
+ 4. **Self-fix mechanical issues, and run the build as the style authority.** After the proofs close
50
+ under diagnostics, run the build and fix mechanical fallout yourself (unresolved identifiers,
51
+ import order, missing lemmas that exist under another name, arithmetic side-goals). EXPECT style
52
+ warnings that surface ONLY at `lake build` and never at `lake env lean` / `--stdin` isolation
53
+ checks or at `mcp__ide__getDiagnostics`: the package style linters (for example
54
+ `linter.style.show` and `linter.style.longLine`) run at build time. `lake build` is the style
55
+ authority; isolation checks are advisory and cannot certify style. Reproduce the style pass
56
+ cheaply with `lake env lean -Dlinter.style.show=true -Dlinter.style.longLine=true <file>`, but the
57
+ authoritative gate is the build. House style: prefer `change` over a goal-altering `show`
58
+ (semantics-preserving by defeq) and wrap lines at 100 columns.
59
+
60
+ 5. **Escalate to the user for any statement adjustment.** If closing the work requires changing a
61
+ public signature or theorem statement (anything the plan marked immutable), HALT and escalate. Do
62
+ NOT silently rewrite a statement to make a proof go through. State the exact statement, the
63
+ before/after you propose, and why it is needed; record the approved edit before/after once the
64
+ user rules.
65
+
66
+ 6. **Hand back BLOCKED when genuinely stuck.** If a prerequisite is absent, the build cannot be made
67
+ green after honest effort, or a modeling decision is required that you must not make, stop and
68
+ report BLOCKED with the concrete blocker — do not grind indefinitely.
69
+
70
+ Write your run report to `IMPLEMENTATION_nN.md` (where `nN` is the iteration the command passes you),
71
+ capturing what you implemented, the final build state, any authorised `sorry` obligations with their
72
+ statements, and any escalation/block.
73
+
74
+ **Anti-pattern this agent rejects (the FC lean-verify sorry-grind — stays FC-only):** no
75
+ one-`sorry`-at-a-time targeting; no ≤3-line-per-invocation tactic cap; no
76
+ user-compiles-between-steps pair-programming. That discipline belongs to the FC `fvs-executor`
77
+ `proof-attempt` mode and must not leak into the crypto loop.
78
+
79
+ </process>
80
+
81
+ <fvs_hard_rules>
82
+ - NEVER run a bare `lake build` -- always `nice -n 19 lake build` with the `set -o pipefail` / `${PIPESTATUS` guard so a piped build failure is never masked.
83
+ - NEVER edit generated Lean (`Types.lean` / `Funs.lean`).
84
+ - All writes MUST use the Write tool -- never echo, cat, or Bash redirection. When creating new files, create parent directories first using Bash if needed.
85
+ - Escalate, do not overrule: never change an immutable public statement to force a proof through -- HALT and ask, then record the approved before/after.
86
+ - NEVER call `gh` to open or create any upstream artifact.
87
+ - This is a Lean-via-Aeneas pipeline only -- no other-framework verification paths.
88
+ </fvs_hard_rules>
89
+
90
+ <return_format>
91
+
92
+ On successful completion, end your output with:
93
+
94
+ ```
95
+ ## IMPLEMENTATION COMPLETE
96
+
97
+ **Iteration:** nN
98
+ **Files written:** {list of file paths, including IMPLEMENTATION_nN.md}
99
+ **Build:** green via `nice -n 19 lake build`
100
+ **Obligations:** {named allowed-sorry obligations with their statements, or "none"}
101
+ **Summary:** {1-2 sentences on what was implemented and proven}
102
+ ```
103
+
104
+ When a public-statement decision is required:
105
+
106
+ ```
107
+ ## ESCALATE
108
+
109
+ **Iteration:** nN
110
+ **Statement at stake:** {the immutable signature/theorem the work needs changed}
111
+ **Proposed change:** {before -> after}
112
+ **Why:** {what fails without it}
113
+ ```
114
+
115
+ When genuinely stuck:
116
+
117
+ ```
118
+ ## BLOCKED
119
+
120
+ **Iteration:** nN
121
+ **Blocker:** {the concrete missing prerequisite, red build, or modeling decision}
122
+ **Build state:** {last known state from `nice -n 19 lake build`}
123
+ **What would unblock:** {the specific input needed}
124
+ ```
125
+
126
+ </return_format>
127
+
128
+ <success_criteria>
129
+ - [ ] Implemented the fully-specified plan as a whole unit (no unauthorised `sorry`; immutable public statements preserved verbatim)
130
+ - [ ] Kernel-checked signatures, then completed proofs using `mcp__ide__getDiagnostics` for in-loop goal/diagnostic feedback
131
+ - [ ] Ran `nice -n 19 lake build` as the style authority and self-fixed mechanical + style fallout (expecting style warnings that surface only at build time, not in isolation checks)
132
+ - [ ] Escalated (never overruled) any immutable-public-statement change; handed back BLOCKED when genuinely stuck
133
+ - [ ] Did NOT use the one-`sorry` / ≤3-line / user-compiles-between-steps proof-attempt grind
134
+ - [ ] Wrote the run report to `IMPLEMENTATION_nN.md` and returned with a ## IMPLEMENTATION COMPLETE / ## ESCALATE / ## BLOCKED header
135
+ - [ ] All writes via the Write tool; no bare `lake build`; no generated-Lean edits; no `gh` auto-open; Lean-via-Aeneas pipeline only; no @-references
136
+ </success_criteria>
137
+ </content>
138
+ </invoke>
@@ -34,6 +34,16 @@ Your parent command provides the stage via a `<thinker_mode>` tag and the inline
34
34
  prior plan/review in `fv-plans/<topic>/`.
35
35
  **Output (returned as text):** ONE bounded executor plan.
36
36
 
37
+ **Depth fence (statements, not proofs):** the plan carries the SPEC VERBATIM (APIs, def bodies,
38
+ theorem/definition statements) but contains NO proof bodies and NO tactic scripts -- authoring the
39
+ proof is the executor's job, not yours. You do NOT compile or type-check while planning: at most ONE
40
+ coarse go/no-go compile check, and only when viability genuinely hinges on an architectural unknown.
41
+ That single go/no-go probe is a viability check, NOT a style certification -- `lake build` in the
42
+ executor's loop remains the style authority (a planner that does not fully compile cannot certify
43
+ style). `Bash` stays in your tool list SOLELY for that one permitted go/no-go probe, and so the plan
44
+ can author the `nice -n 19 lake build` command as text the executor runs; it is not a license to
45
+ iterate a proof while planning.
46
+
37
47
  The plan is bounded and runtime-neutral -- it must be executable by a Claude, Codex, or other
38
48
  runtime's executor with no thinker in the loop. State EVERY field explicitly:
39
49
 
@@ -48,7 +58,8 @@ runtime's executor with no thinker in the loop. State EVERY field explicitly:
48
58
  the exact statement each must carry. A `sorry` is never judged by count; only a named obligation
49
59
  with the correct statement is acceptable.
50
60
  6. **Stop conditions** -- the explicit conditions under which the executor halts (target reached,
51
- build red after N attempts, a modeling decision needed).
61
+ build red after N attempts, a modeling decision needed). A modeling decision or any change to a
62
+ public statement is ESCALATED to the user -- never decided by the thinker.
52
63
  7. **Verification commands** -- ALWAYS `nice -n 19 lake build` (never a bare `lake build`), with the
53
64
  `set -o pipefail` / `${PIPESTATUS` guard so a piped build failure is never masked.
54
65
  8. **Expected artifact updates** -- which `fv-plans/<topic>/{plans,reviews,sources,merge}` files the
@@ -79,6 +79,7 @@ Your parent command provides `<execution_mode>` and `<research_findings>` tags.
79
79
 
80
80
  <mode name="proof-attempt">
81
81
  **Dispatched by:** /fvs:lean-verify
82
+ **Scope:** Functional-correctness track only. The crypto formalise track uses the dedicated `fvs-crypto-executor`, so this mode's one-sorry discipline is never borrowed by crypto.
82
83
  **Input:** Current proof state, available lemmas, recommended strategy from research
83
84
  **Output:** Modified spec file with tactic steps replacing sorry
84
85
 
package/bin/install.js CHANGED
@@ -60,6 +60,10 @@ const CODEX_AGENT_SANDBOX = {
60
60
  // return (the command body persists the artifacts) and the auditor only
61
61
  // introspects and returns a table -- both are read-only.
62
62
  'fvs-crypto-thinker': 'read-only',
63
+ // The crypto executor owns the deliverable file -- it writes the new spec and
64
+ // completes proofs, so it needs workspace-write; the unmapped default is
65
+ // read-only, which would silently fail every write on Codex.
66
+ 'fvs-crypto-executor': 'workspace-write',
63
67
  'fvs-axiom-auditor': 'read-only',
64
68
  };
65
69
 
@@ -91,6 +95,9 @@ const FVS_CODEX_AGENT_EFFORT = {
91
95
  // so it runs at xhigh (the dual-runtime Codex thinker must think at least this
92
96
  // hard); the read-only auditor introspects axioms at high.
93
97
  'fvs-crypto-thinker': 'xhigh',
98
+ // The crypto executor is the dial-down implementation stage (vs the thinker's
99
+ // xhigh authoring) -- it executes a fully-specified plan, so it runs at high.
100
+ 'fvs-crypto-executor': 'high',
94
101
  'fvs-axiom-auditor': 'high',
95
102
  };
96
103
 
@@ -1369,9 +1376,15 @@ function isFvsManagedCodexHookCommand(commandText, configDir) {
1369
1376
 
1370
1377
  // Read hooks.json, drop any prior FVS-managed entries for `eventName`, then
1371
1378
  // append exactly one fresh managed entry (unless managedCommand is null, which
1372
- // means remove-only). Foreign entries are preserved and the file is written back
1373
- // in the SAME shape it used: nested `{ hooks: { <Event>: [...] } }` or flat
1374
- // `{ <Event>: [...] }`. Returns { changed, wrote, path }.
1379
+ // means remove-only). Foreign entries are preserved and the file is ALWAYS
1380
+ // written back in the nested `{ "hooks": { "<Event>": [...] } }` shape that
1381
+ // current Codex (0.142.x) requires — the older flat `{ "<Event>": [...] }` shape
1382
+ // is silently ignored by Codex. A flat top-level table is treated as a legacy
1383
+ // read source and migrated to nested: the sanitized event table plus any foreign
1384
+ // top-level event keys (e.g. Stop, PostToolUse, SubagentStart) are rehomed under
1385
+ // `hooks`, so the written object is exactly `{ hooks: { ...all events... } }`
1386
+ // (foreign non-event top-level content is preserved at the top level).
1387
+ // Returns { changed, wrote, path }.
1375
1388
  function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
1376
1389
  const hooksJsonPath = path.join(targetDir, 'hooks.json');
1377
1390
  const managedCommand = typeof opts.managedCommand === 'string' ? opts.managedCommand : null;
@@ -1394,8 +1407,21 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
1394
1407
 
1395
1408
  const usesNestedHooksObject =
1396
1409
  parsed.hooks && typeof parsed.hooks === 'object' && !Array.isArray(parsed.hooks);
1397
- const hookTable = usesNestedHooksObject ? parsed.hooks : parsed;
1398
- const eventEntries = Array.isArray(hookTable[eventName]) ? hookTable[eventName] : [];
1410
+
1411
+ // Build the normalized nested event table. Seed it from any existing nested
1412
+ // `parsed.hooks` events, then rehome any foreign top-level event keys — a
1413
+ // legacy flat table stores events directly at the top level, so migrate them
1414
+ // (an already-nested key wins over a same-named flat one).
1415
+ const nestedTable = {};
1416
+ if (usesNestedHooksObject) {
1417
+ for (const [key, value] of Object.entries(parsed.hooks)) nestedTable[key] = value;
1418
+ }
1419
+ for (const [key, value] of Object.entries(parsed)) {
1420
+ if (key === 'hooks') continue;
1421
+ if (Array.isArray(value) && !(key in nestedTable)) nestedTable[key] = value;
1422
+ }
1423
+
1424
+ const eventEntries = Array.isArray(nestedTable[eventName]) ? nestedTable[eventName] : [];
1399
1425
 
1400
1426
  let removedManaged = false;
1401
1427
  const sanitizedEntries = [];
@@ -1423,28 +1449,41 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
1423
1449
  }
1424
1450
 
1425
1451
  if (sanitizedEntries.length > 0) {
1426
- hookTable[eventName] = sanitizedEntries;
1452
+ nestedTable[eventName] = sanitizedEntries;
1427
1453
  } else {
1428
- delete hookTable[eventName];
1454
+ delete nestedTable[eventName];
1455
+ }
1456
+
1457
+ // Assemble the always-nested output. Preserve foreign non-event top-level keys
1458
+ // (anything other than `hooks` whose value is not an array) so unrelated
1459
+ // user-authored content survives; the event keys themselves live under `hooks`.
1460
+ const output = {};
1461
+ for (const [key, value] of Object.entries(parsed)) {
1462
+ if (key === 'hooks') continue;
1463
+ if (Array.isArray(value)) continue;
1464
+ output[key] = value;
1465
+ }
1466
+ if (Object.keys(nestedTable).length > 0) {
1467
+ output.hooks = nestedTable;
1429
1468
  }
1430
- if (usesNestedHooksObject) parsed.hooks = hookTable;
1431
1469
 
1432
- // When the reconcile empties the object (no foreign keys remain) and a file
1433
- // existed on disk, delete it rather than leaving an orphaned `{}` the user
1434
- // never authored. Foreign content keeps at least one key, so this only fires
1435
- // for an FVS-only file. A non-existent file with nothing to write is a no-op.
1436
- if (Object.keys(parsed).length === 0) {
1470
+ // When the reconcile empties the whole normalized object (no events, no foreign
1471
+ // top-level content) and a file existed on disk, delete it rather than leaving
1472
+ // an orphaned `{}` the user never authored. Foreign content keeps at least one
1473
+ // key, so this only fires for an FVS-only file. A non-existent file with
1474
+ // nothing to write is a no-op.
1475
+ if (Object.keys(output).length === 0) {
1437
1476
  if (currentContent !== null) {
1438
1477
  const removed = removedManaged || currentContent.trim() !== '{}';
1439
1478
  fs.unlinkSync(hooksJsonPath);
1440
1479
  return { changed: true, wrote: removed, path: hooksJsonPath, deleted: true };
1441
1480
  }
1442
- return { changed: changed || removedManaged, wrote: false, path: hooksJsonPath };
1481
+ return { changed: removedManaged, wrote: false, path: hooksJsonPath };
1443
1482
  }
1444
1483
 
1445
- const nextContent = `${JSON.stringify(parsed, null, 2)}\n`;
1484
+ const nextContent = `${JSON.stringify(output, null, 2)}\n`;
1446
1485
  const changed = currentContent !== nextContent;
1447
- const shouldWrite = changed && (currentContent !== null || Object.keys(parsed).length > 0);
1486
+ const shouldWrite = changed && (currentContent !== null || Object.keys(output).length > 0);
1448
1487
  if (shouldWrite) {
1449
1488
  atomicWriteFileSync(hooksJsonPath, nextContent, 'utf8');
1450
1489
  }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: fvs:crypto-execute
3
3
  description: Run the current iteration's bounded executor plan under a green-build guard and a bounded loop
4
- argument-hint: "<topic> nN"
4
+ argument-hint: "<topic> nN [--model <value>] [--effort <value>]"
5
5
  allowed-tools:
6
6
  - Read
7
7
  - Write
@@ -15,13 +15,15 @@ allowed-tools:
15
15
 
16
16
  <objective>
17
17
  Run the current iteration's bounded executor plan (`EXEC_PLAN_nN.md` or `FOLLOWUP_PLAN_nN.md`). The
18
- existing `fvs-executor` runs the plan in proof-attempt mode under a bounded loop with the green-build
19
- guard; this command body owns loop termination and the build status check.
18
+ dedicated `fvs-crypto-executor` implements the fully-specified plan under the green-build guard; this
19
+ command body owns dispatch and the build status check, and routes the executor's ESCALATE/BLOCKED
20
+ return to the user.
20
21
 
21
22
  This command is the EXECUTE stage of the single-runtime loop (plan -> execute -> eval -> followup).
22
23
  The plan it runs is RUNTIME-NEUTRAL: the loop runs as a `(R1; R1)` same-runtime pair by default, with
23
- an optional secondary runtime available for the planning/eval stages in a later wave. A failed proof
24
- triggers a SHORT interactive redirect early -- never a long unattended grind.
24
+ an optional secondary runtime available for the planning/eval stages in a later wave. When the
25
+ executor hits a public-statement change or cannot proceed, it hands back ESCALATE/BLOCKED and the
26
+ command routes that to the user -- never a long unattended grind.
25
27
 
26
28
  Output: the executed proof changes on the working branch, with `build.log` captured for the eval.
27
29
  </objective>
@@ -42,11 +44,20 @@ iteration from `plans/` and runs it. The loop is restartable from its on-disk re
42
44
  ## Step 1: Resolve the topic slug + paths (path safety)
43
45
 
44
46
  Resolve the topic into a slug (whitespace -> `-`, capitalization preserved, e.g.
45
- `CKA from KEM` -> `CKA-from-KEM`). Treat the topic + iteration arg as UNTRUSTED: REJECT a slug with
46
- shell metacharacters, QUOTE every path expansion, NEVER `eval` a path.
47
+ `CKA from KEM` -> `CKA-from-KEM`). Treat the topic + iteration arg AND the new `--model` / `--effort`
48
+ flag values as UNTRUSTED: REJECT a slug with shell metacharacters, QUOTE every path expansion, NEVER
49
+ `eval` a path. The `--model` / `--effort` values are opaque, runtime-valid strings -- do NOT validate
50
+ them against any model taxonomy; reject only shell metacharacters (the same path-safety check).
47
51
 
48
52
  ```bash
49
- TOPIC_RAW="$1"; ITER="$2"
53
+ TOPIC_RAW=""; ITER=""; EXEC_MODEL=""; EXEC_EFFORT=""
54
+ while [ $# -gt 0 ]; do
55
+ case "$1" in
56
+ --model ) EXEC_MODEL="$2"; shift 2 ;;
57
+ --effort ) EXEC_EFFORT="$2"; shift 2 ;;
58
+ * ) if [ -z "$TOPIC_RAW" ]; then TOPIC_RAW="$1"; elif [ -z "$ITER" ]; then ITER="$1"; fi; shift ;;
59
+ esac
60
+ done
50
61
  case "$TOPIC_RAW" in
51
62
  *[';|&$`()<>'*]* ) echo "FVS >> ERROR: topic contains shell metacharacters" >&2; exit 1 ;;
52
63
  *..*|*/* ) echo "FVS >> ERROR: topic contains '..' or '/' (path traversal); refusing" >&2; exit 1 ;;
@@ -55,6 +66,11 @@ case "$ITER" in
55
66
  n[0-9]* ) : ;;
56
67
  * ) echo "FVS >> ERROR: iteration must be of the form nN" >&2; exit 1 ;;
57
68
  esac
69
+ for FLAGVAL in "$EXEC_MODEL" "$EXEC_EFFORT"; do
70
+ case "$FLAGVAL" in
71
+ *[';|&$`()<>'*]* ) echo "FVS >> ERROR: --model/--effort contains shell metacharacters" >&2; exit 1 ;;
72
+ esac
73
+ done
58
74
  SLUG=$(printf '%s' "$TOPIC_RAW" | tr -s '[:space:]' '-')
59
75
  ROOT=".formalising/fv-plans/$SLUG"
60
76
  ```
@@ -68,22 +84,48 @@ Read the bounded executor plan for this iteration -- `plans/EXEC_PLAN_nN.md`, or
68
84
  the executor needs (it is runtime-neutral and bounded -- branch/state, exact targets, immutable public
69
85
  statements, allowed-`sorry` policy, stop conditions, verification command).
70
86
 
71
- ## Step 3: Resolve the executor model + dispatch
87
+ ## Step 3: Resolve the executor model + effort + dispatch
72
88
 
73
- Resolve `$EXECUTOR_MODEL` for `fvs-executor` via the model-profiles dispatch sequence. `cat` the
74
- bounded plan and INLINE it into the prompt (references do not cross the Task boundary):
89
+ Resolve `$EXECUTOR_MODEL` and `$EXECUTOR_EFFORT` for `fvs-crypto-executor` AT DISPATCH TIME -- never
90
+ pinned in the agent frontmatter. The resolved values are opaque, runtime-valid strings passed
91
+ STRAIGHT THROUGH to `Task(model=...)`; FVS keeps NO cross-provider model/effort taxonomy (an invalid
92
+ value is rejected by the runtime itself). Resolve with this ladder:
93
+
94
+ 1. The per-run `--model` / `--effort` flags from Step 1, if present.
95
+ 2. Else a top-level override in `.formalising/fvs-config.json`:
96
+ `model_overrides["fvs-crypto-executor"]`. Read it at the TOP-LEVEL `model_overrides` key the
97
+ model-profiles resolver actually consults -- NOT the template's nested `model.model_profile` (a
98
+ pre-existing shape mismatch, out of scope here; do not depend on the nested key).
99
+ 3. Else, in an interactive run, ASK via `AskUserQuestion` which model + effort to use for the
100
+ execution subagent, offering "inherit / default" as a choice.
101
+ 4. Else default `inherit` (works zero-config on every runtime).
102
+
103
+ ```bash
104
+ CONFIG=$(cat .formalising/fvs-config.json 2>/dev/null || echo '{}')
105
+ # 1. flag -> 2. top-level model_overrides["fvs-crypto-executor"] -> 4. inherit
106
+ # (3. AskUserQuestion runs between 2 and 4 in an interactive run)
107
+ EXECUTOR_MODEL="${EXEC_MODEL:-$(printf '%s' "$CONFIG" | OVERRIDE_KEY='model_overrides["fvs-crypto-executor"]' read_top_level_override)}"
108
+ EXECUTOR_MODEL="${EXECUTOR_MODEL:-inherit}"
109
+ EXECUTOR_EFFORT="${EXEC_EFFORT:-inherit}"
110
+ ```
111
+
112
+ On Codex the `model=` parameter is silently ignored and per-agent effort is FIXED at install time
113
+ (from `FVS_CODEX_AGENT_EFFORT`), so the per-run `--effort` flag is a Claude / OpenCode / Gemini
114
+ nicety; Codex users tune the crypto executor's effort via the agent `.toml` / reinstall.
115
+
116
+ `cat` the bounded plan and INLINE it into the prompt (references do not cross the Task boundary):
75
117
 
76
118
  ```
77
119
  Task(
78
- subagent_type="fvs-executor",
120
+ subagent_type="fvs-crypto-executor",
79
121
  model="$EXECUTOR_MODEL",
80
122
  description="Run bounded plan",
81
- prompt="Execute mode: proof-attempt
123
+ prompt="Execute the bounded crypto plan.
82
124
 
83
125
  <bounded_plan>...the inlined EXEC_PLAN_nN.md / FOLLOWUP_PLAN_nN.md...</bounded_plan>
84
126
 
85
- Target one sorry at a time with small tactic blocks; after 3 failed attempts on a goal, return
86
- NEEDS INPUT. Return with ## EXECUTION COMPLETE"
127
+ Implement the fully-specified plan; self-fix at the green build; ESCALATE for any public-statement
128
+ change and hand back BLOCKED if you cannot proceed. Return with ## IMPLEMENTATION COMPLETE"
87
129
  )
88
130
  ```
89
131
 
@@ -98,23 +140,19 @@ nice -n 19 lake build 2>&1 | tee "$ROOT/build.log"
98
140
  test ${PIPESTATUS[0]} -eq 0 || echo "FVS >> build red -- a proof did not close"
99
141
  ```
100
142
 
101
- ## Step 5: Enforce the bounded loop
102
-
103
- Bound the loop so a failed proof triggers a SHORT interactive redirect early, not a long grind:
104
-
105
- | Bound | Default | Rationale |
106
- |-------|---------|-----------|
107
- | Per-step attempt-cap | `3` | Matches the FVS `lean-verify` per-sorry limit. |
108
- | Cycle hard-cap | `~25` | On exhaustion, HALT with the transcript. |
109
- | No-progress key | `sha256(target || goal-state)` | A same-key recurrence after a fix is known-stuck. |
143
+ `lake build` is the STYLE authority: `lake env lean` / `--stdin` isolation does NOT run the package
144
+ style linters (`linter.style.show`, `linter.style.longLine` from `weak.linter.mathlibStandardSet`),
145
+ so a style warning surfaces only here. Reproduce it cheaply and early with `lake env lean
146
+ -Dlinter.style.show=true -Dlinter.style.longLine=true <file>`. House style uses `change` (not a
147
+ goal-altering `show`) and wraps lines to <=100 columns.
110
148
 
111
- ```bash
112
- PROGRESS_KEY=$(printf '%s' "$TARGET$GOAL" | sha256sum | cut -c1-16)
113
- ```
149
+ ## Step 5: Route the executor's ESCALATE/BLOCKED return
114
150
 
115
- On the attempt-cap, the cycle hard-cap, or a no-progress recurrence: HALT and redirect via
151
+ The `fvs-crypto-executor` owns its own implement -> check -> complete -> escalate -> BLOCKED
152
+ discipline; this command does not re-drive a per-goal grind. When the executor returns ESCALATE (a
153
+ public-statement change is needed) or BLOCKED (it cannot proceed), HALT and redirect to the user via
116
154
  `AskUserQuestion` (degrade to plain-text + WAIT on a secondary runtime that lacks it) -- a short
117
- interactive redirect early beats a long unattended grind.
155
+ interactive redirect beats a long unattended grind.
118
156
 
119
157
  ## Step 6: Run-end banner + next command
120
158
 
@@ -133,15 +171,17 @@ Plan: plans/{EXEC_PLAN | FOLLOWUP_PLAN}_{ITER}.md
133
171
  </process>
134
172
 
135
173
  <codex_skill_adapter>
136
- On a secondary runtime, the bounded-loop redirect (Step 5) degrades to a plain-text question and
137
- WAITS for the user; it is fail-closed (never auto-picks a default, never writes an upstream artifact).
138
- The `Task(...)` dispatch survives intact (the `model=` parameter is silently ignored on Codex).
174
+ On a secondary runtime, both the Step 3 model/effort ask and the Step 5 ESCALATE/BLOCKED redirect
175
+ degrade to a plain-text question and WAIT for the user; each is fail-closed (never auto-picks a
176
+ default beyond the ladder's `inherit`, never writes an upstream artifact). The `Task(...)` dispatch
177
+ survives intact (the `model=` parameter is silently ignored on Codex, where per-agent effort is fixed
178
+ at install time, so the per-run `--effort` flag is a no-op there).
139
179
  </codex_skill_adapter>
140
180
 
141
181
  <success_criteria>
142
182
  - [ ] Topic + iteration resolved; shell metacharacters rejected; every path quoted; no `eval`.
143
- - [ ] The bounded plan (`EXEC_PLAN_nN.md` / `FOLLOWUP_PLAN_nN.md`) read and inlined; `fvs-executor` dispatched (`subagent_type="fvs-executor"`).
183
+ - [ ] The bounded plan (`EXEC_PLAN_nN.md` / `FOLLOWUP_PLAN_nN.md`) read and inlined; `fvs-crypto-executor` dispatched (`subagent_type="fvs-crypto-executor"`).
144
184
  - [ ] The build runs under `set -o pipefail` + `${PIPESTATUS` reading the tool's real status; always `nice -n 19 lake build` (never a bare `lake build`).
145
- - [ ] Bounded loop enforced (per-step attempt-cap, cycle hard-cap, no-progress key); a failed proof triggers a SHORT interactive redirect early.
185
+ - [ ] The executor's ESCALATE/BLOCKED return is routed to the user (short interactive redirect early, never a long unattended grind).
146
186
  - [ ] No `gh` open/create; no generated-Lean write.
147
187
  </success_criteria>
@@ -99,7 +99,7 @@ QHASH=$(printf '%s' "$QUESTION" | shasum -a 256 | cut -c1-16)
99
99
  if [ -f "$ROOT/sources/$QHASH.json" ]; then
100
100
  cat "$ROOT/sources/$QHASH.json" # cache hit -- re-read, do NOT re-query
101
101
  else
102
- python3 scripts/fvs-kb-query.py ask "$QUESTION" --notebook "$NOTEBOOK_ID" --json \
102
+ .formalising/.kb-venv/bin/python ~/.claude/scripts/fvs-kb-query.py ask "$QUESTION" --notebook "$NOTEBOOK_ID" --json \
103
103
  | tee "$ROOT/sources/$QHASH.json" # cache the answer for the next iteration
104
104
  fi
105
105
  ```
@@ -14,6 +14,8 @@ allowed-tools:
14
14
  <objective>
15
15
  Orchestrate interactive proof development for a Lean specification using two-phase subagent dispatch (research -> iterative execute). Dispatches fvs-researcher to analyze sorry locations and recommend proof strategies, then iteratively dispatches fvs-executor to replace each sorry ONE AT A TIME with small tactic blocks.
16
16
 
17
+ This is a functional-correctness (FC) track command. Its `fvs-executor` `proof-attempt` mode is FC-only: the crypto formalise track drives its own dedicated executor, so the one-sorry pair-programming loop below is exclusive to this command and is not shared with or borrowed by any other track.
18
+
17
19
  This is the most interactive command -- it feels like pair programming. The executor proposes a small tactic step, the user checks Lean compiles, and the cycle repeats. This is a locked user decision and must not be overridden.
18
20
 
19
21
  Output: Spec file with sorry replaced by complete proof (VERIFIED) or clear report of where proof got stuck (STUCK).
package/fv-skills/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.2
1
+ 2.0.3
@@ -132,6 +132,24 @@ Task(
132
132
  - **Invalid profile name:** Fall back to `quality` with a warning.
133
133
  - **Empty overrides:** Ignored. Equivalent to no overrides.
134
134
 
135
+ ## crypto-execute Model + Effort Knob
136
+
137
+ `/fvs:crypto-execute` resolves the model + effort for `fvs-crypto-executor` ABOVE the profile table,
138
+ at its own dispatch site (command Step 3) -- not from a `PROFILE_TABLE` row. The crypto executor is
139
+ intentionally NOT pinned, so there is deliberately **no `fvs-crypto-executor` row and no `opus`
140
+ value** in the table above. The resolution ladder is:
141
+
142
+ 1. A per-run `--model` / `--effort` flag on the command.
143
+ 2. Else a top-level override `model_overrides["fvs-crypto-executor"]` in `.formalising/fvs-config.json`
144
+ (read at the top-level `model_overrides` key this resolver consults, not the template's nested
145
+ `model.model_profile`).
146
+ 3. Else an interactive `AskUserQuestion` offering "inherit / default".
147
+ 4. Else the default `inherit`.
148
+
149
+ The resolved value is an opaque, runtime-valid string passed straight to `Task(model=...)`; FVS keeps
150
+ no cross-provider taxonomy. On Codex `model=` is ignored and per-agent effort is fixed at install
151
+ time, so the per-run `--effort` flag is a Claude / OpenCode / Gemini nicety.
152
+
135
153
  ## Two-Phase Dispatch
136
154
 
137
155
  Each main command dispatches two subagents in sequence:
@@ -4,10 +4,10 @@ plan under a green-build guard and a bounded loop, so a failed proof triggers a
4
4
  redirect early rather than a long unattended grind.
5
5
 
6
6
  This workflow is the state machine for `/fvs:crypto-execute`. The command body resolves the topic +
7
- iteration, reads the bounded plan from `plans/`, dispatches the existing `fvs-executor` in
8
- proof-attempt mode, and owns the build-status check + loop termination. The plan is RUNTIME-NEUTRAL:
9
- the loop runs as a `(R1; R1)` same-runtime pair by default, with an optional secondary runtime for
10
- the planning/eval stages in a later wave.
7
+ iteration, reads the bounded plan from `plans/`, dispatches the dedicated `fvs-crypto-executor`, and
8
+ owns the build-status check + routing the executor's ESCALATE/BLOCKED return to the user. The plan is
9
+ RUNTIME-NEUTRAL: the loop runs as a `(R1; R1)` same-runtime pair by default, with an optional
10
+ secondary runtime for the planning/eval stages in a later wave.
11
11
 
12
12
  Hard invariants this workflow preserves:
13
13
  - The build's exit status is read from the TOOL (`set -o pipefail` / `${PIPESTATUS`), never from the
@@ -38,19 +38,19 @@ allowed-`sorry` policy, stop conditions, verification command).
38
38
  </step>
39
39
 
40
40
  <step name="dispatch_executor">
41
- ## Step 3: Dispatch the executor (proof-attempt mode)
41
+ ## Step 3: Dispatch the crypto executor
42
42
 
43
- Resolve `$EXECUTOR_MODEL` via the model-profiles sequence, then dispatch the existing executor,
44
- INLINING the bounded plan:
43
+ Resolve `$EXECUTOR_MODEL` via the model-profiles sequence, then dispatch the dedicated crypto
44
+ executor, INLINING the bounded plan:
45
45
 
46
46
  ```
47
- Task(subagent_type="fvs-executor", model="$EXECUTOR_MODEL",
47
+ Task(subagent_type="fvs-crypto-executor", model="$EXECUTOR_MODEL",
48
48
  description="Run bounded plan",
49
- prompt="Execute mode: proof-attempt ...inlined bounded plan... Return with ## EXECUTION COMPLETE")
49
+ prompt="Execute the bounded crypto plan. ...inlined bounded plan... Return with ## IMPLEMENTATION COMPLETE")
50
50
  ```
51
51
 
52
- Proof-attempt discipline (reused from the existing executor): target one sorry at a time with small
53
- tactic blocks; after 3 failed attempts on a goal, return NEEDS INPUT.
52
+ The `fvs-crypto-executor` owns its own implement -> check -> complete -> escalate -> BLOCKED
53
+ discipline; this workflow does not re-drive a per-goal grind.
54
54
  </step>
55
55
 
56
56
  <step name="green_build_guard">
@@ -64,23 +64,21 @@ set -o pipefail
64
64
  nice -n 19 lake build 2>&1 | tee "$ROOT/build.log"
65
65
  test ${PIPESTATUS[0]} -eq 0 || echo "build red -- a proof did not close"
66
66
  ```
67
- </step>
68
-
69
- <step name="loop_bounds">
70
- ## Step 5: Bounded loop -- caps + the no-progress rule
71
67
 
72
- | Bound | Default | Rationale |
73
- |-------|---------|-----------|
74
- | Per-step attempt-cap | `3` | Matches the FVS `lean-verify` per-sorry limit. |
75
- | Cycle hard-cap | `~25` | On exhaustion, HALT with the full transcript. |
76
- | No-progress key | `sha256(target || goal-state)` | A same-key recurrence after a fix is known-stuck. |
68
+ `lake build` is the STYLE authority: `lake env lean` / `--stdin` isolation does NOT run the package
69
+ style linters (`linter.style.show`, `linter.style.longLine` from `weak.linter.mathlibStandardSet`),
70
+ so a style warning surfaces only here. Reproduce it cheaply and early with `lake env lean
71
+ -Dlinter.style.show=true -Dlinter.style.longLine=true <file>`. House style uses `change` (not a
72
+ goal-altering `show`) and wraps lines to <=100 columns.
73
+ </step>
77
74
 
78
- ```bash
79
- PROGRESS_KEY=$(printf '%s' "$TARGET$GOAL" | sha256sum | cut -c1-16)
80
- ```
75
+ <step name="route_escalation">
76
+ ## Step 5: Route the executor's ESCALATE/BLOCKED return
81
77
 
82
- On the attempt-cap, the cycle hard-cap, or a no-progress recurrence: HALT and redirect (a short
83
- interactive redirect early beats a long unattended grind). On a secondary runtime that lacks an
78
+ The `fvs-crypto-executor` owns escalation (implement -> check -> complete -> escalate -> BLOCKED);
79
+ this workflow does not enforce a per-goal attempt grind. When the executor returns ESCALATE (a
80
+ public-statement change is needed) or BLOCKED (it cannot proceed), HALT and redirect to the user (a
81
+ short interactive redirect beats a long unattended grind). On a secondary runtime that lacks an
84
82
  interactive prompt, degrade to plain-text and WAIT (fail-closed).
85
83
  </step>
86
84
 
@@ -88,8 +86,8 @@ interactive prompt, degrade to plain-text and WAIT (fail-closed).
88
86
 
89
87
  <success_criteria>
90
88
  - [ ] Topic + iteration resolved; shell metacharacters rejected; paths quoted; no `eval`.
91
- - [ ] The bounded plan read and inlined; `fvs-executor` dispatched (`subagent_type="fvs-executor"`) in proof-attempt mode.
89
+ - [ ] The bounded plan read and inlined; `fvs-crypto-executor` dispatched (`subagent_type="fvs-crypto-executor"`).
92
90
  - [ ] The build runs under `set -o pipefail` + `${PIPESTATUS` reading the tool's real status; always `nice -n 19 lake build` (never a bare `lake build`).
93
- - [ ] Bounded loop enforced (attempt-cap, cycle hard-cap, `sha256` no-progress key); a failed proof triggers a SHORT interactive redirect early.
91
+ - [ ] The executor's ESCALATE/BLOCKED return is routed to the user (short interactive redirect early, never a long unattended grind).
94
92
  - [ ] No generated-Lean write; no `gh` open/create.
95
93
  </success_criteria>
@@ -58,7 +58,7 @@ QHASH=$(printf '%s' "$QUESTION" | shasum -a 256 | cut -c1-16)
58
58
  if [ -f "$ROOT/sources/$QHASH.json" ]; then
59
59
  cat "$ROOT/sources/$QHASH.json" # cache hit -- re-read, do NOT re-query
60
60
  else
61
- python3 scripts/fvs-kb-query.py ask "$QUESTION" --notebook "$NOTEBOOK_ID" --json \
61
+ .formalising/.kb-venv/bin/python ~/.claude/scripts/fvs-kb-query.py ask "$QUESTION" --notebook "$NOTEBOOK_ID" --json \
62
62
  | tee "$ROOT/sources/$QHASH.json" # cache the answer under sources/
63
63
  fi
64
64
  ```
@@ -2,6 +2,10 @@
2
2
  Orchestrate interactive proof development for a Lean specification using two-phase
3
3
  subagent dispatch (research -> iterative execute).
4
4
 
5
+ This is a functional-correctness (FC) track workflow. The fvs-executor proof-attempt
6
+ mode it drives is FC-only -- the crypto formalise track uses its own dedicated executor,
7
+ so this one-sorry loop is exclusive to lean-verify and is not shared with any other track.
8
+
5
9
  Dispatches fvs-researcher to analyze sorry locations and recommend proof strategies,
6
10
  then iteratively dispatches fvs-executor to replace each sorry ONE AT A TIME with
7
11
  small tactic blocks. The user checks Lean compiles between each step.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fv-skills-baif",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "Formal verification skills for Claude Code, Codex, OpenCode, and Gemini. Rust -> Lean 4 via Aeneas.",
5
5
  "bin": {
6
6
  "fv-skills-baif": "bin/install.js"