create-byan-agent 2.39.0 → 2.42.0

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +153 -1
  2. package/install/bin/byan-handoff.js +139 -0
  3. package/install/bin/create-byan-agent-v2.js +70 -9
  4. package/install/lib/codex-autodelegate-setup.js +100 -0
  5. package/install/lib/codex-native-setup.js +94 -2
  6. package/install/lib/project-handoff.js +300 -0
  7. package/install/lib/rtk-integration.js +81 -14
  8. package/install/templates/.claude/CLAUDE.md +16 -0
  9. package/install/templates/.claude/hooks/codex-autodelegate.js +74 -0
  10. package/install/templates/.claude/hooks/lib/autodelegate-decision.js +152 -0
  11. package/install/templates/.claude/hooks/lib/perf-routing.js +47 -0
  12. package/install/templates/.claude/hooks/lib/usage-estimator.js +262 -0
  13. package/install/templates/.claude/hooks/tier-script-guard.js +185 -0
  14. package/install/templates/.claude/rules/native-workflows.md +33 -7
  15. package/install/templates/.claude/settings.json +35 -22
  16. package/install/templates/.claude/skills/byan-byan/SKILL.md +21 -2
  17. package/install/templates/.claude/skills/byan-codex/SKILL.md +7 -0
  18. package/install/templates/.claude/skills/byan-hermes-dispatch/SKILL.md +3 -1
  19. package/install/templates/.claude/workflows/sprint-planning.js +2 -2
  20. package/install/templates/.claude/workflows/testarch-trace.js +3 -2
  21. package/install/templates/.codex/skills/byan/SKILL.md +77 -0
  22. package/install/templates/_byan/INDEX.md +6 -2
  23. package/install/templates/_byan/_config/workflow-manifest.csv +1 -1
  24. package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-tier-script.js +52 -0
  25. package/install/templates/_byan/mcp/byan-mcp-server/lib/dispatch.js +22 -0
  26. package/install/templates/_byan/mcp/byan-mcp-server/lib/native-tiers.js +26 -9
  27. package/install/templates/_byan/mcp/byan-mcp-server/lib/tier-script.js +104 -0
  28. package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-lint.js +92 -6
  29. package/install/templates/_byan/mcp/byan-mcp-server/server.js +22 -7
  30. package/install/templates/_byan/mcp/byan-mcp-server/skill-bundles-manifest.json +3 -3
  31. package/install/templates/_byan/workflow/simple/byan/project-handoff-workflow.md +90 -0
  32. package/install/templates/dist/skill-bundles/byan-byan.zip +0 -0
  33. package/install/templates/dist/skill-bundles/byan-codex.zip +0 -0
  34. package/install/templates/dist/skill-bundles/byan-hermes-dispatch.zip +0 -0
  35. package/install/templates/docs/codex-auto-delegation.md +140 -0
  36. package/install/templates/docs/native-workflows-contract.md +55 -12
  37. package/package.json +2 -1
  38. package/src/loadbalancer/capability-matrix.js +14 -0
  39. package/src/loadbalancer/degradation-ladder.js +121 -0
  40. package/src/loadbalancer/loadbalancer.default.yaml +23 -1
  41. package/src/loadbalancer/mcp-server.js +200 -18
  42. package/src/loadbalancer/providers/codex-provider.js +260 -0
  43. package/src/loadbalancer/providers/factory.js +36 -0
  44. package/src/loadbalancer/subscription-window.js +142 -0
  45. package/src/loadbalancer/switch-tolerance.js +63 -0
  46. package/src/loadbalancer/tools/index.js +17 -2
@@ -26,16 +26,24 @@ A native workflow script mutates FD/strict state only through the `byan_fd_*` /
26
26
 
27
27
  Each `agent()` leaf runs on the session model unless the call sets `opts.model`.
28
28
  The tiering decision lives in one place — `_byan/mcp/byan-mcp-server/lib/native-tiers.js`
29
- (tier vocabulary, leaf classifier, model map). The doctrine is binary: a pure
30
- EXPLORATION leaf (read/load/parse/detect) may downgrade to `model: 'haiku'`;
31
- implementation / verification / analysis leaves OMIT `opts.model` and inherit the
32
- session model (no pin-up to opus).
29
+ (tier vocabulary, leaf classifier, model map). Three tiers:
30
+
31
+ - **cheap (`model: 'haiku'`)** a pure EXPLORATION leaf (read/load/parse/detect).
32
+ - **balanced (`model: 'sonnet'`)** — MECHANICAL verification, opt-in ONLY through
33
+ the `mech-` label prefix (`mech-validate-json`): a binary, judgment-free check
34
+ (JSON parses, schema matches, lint passes). Semantic/adversarial verification
35
+ is NOT mechanical and stays deep. The prefix is an authoring declaration the
36
+ linter holds the script to: a `mech-` leaf without `model: 'sonnet'` is a hard
37
+ violation (`mechanical-without-model` / `mechanical-below-tier`).
38
+ - **deep (OMIT `opts.model`)** — implementation / verification / analysis leaves
39
+ inherit the session model (no pin-up to opus).
33
40
 
34
41
  The linter splits the two directions:
35
42
 
36
- - **Floor (HARD, blocks the commit)** — a downgrade model on a PROTECTED leaf, or
37
- a pin-up, is a contract violation (`modelRoutingViolations`). This is the
38
- STRICT-2 No Downgrade net.
43
+ - **Floor (HARD, blocks the commit)** — a downgrade model on a PROTECTED leaf, a
44
+ pin-up, or a half-applied `mech-` opt-in is a contract violation
45
+ (`modelRoutingViolations` + `mechanicalLabelViolations`). This is the STRICT-2
46
+ No Downgrade net.
39
47
  - **Ceiling (ADVISORY, non-blocking)** — an exploration-labelled leaf that runs
40
48
  deep is *reported* (`byan-lint-workflows.js --advise`), not forced. `classifyLeaf`
41
49
  is permissive: many exploration-labelled leaves legitimately stay deep because
@@ -43,6 +51,24 @@ The linter splits the two directions:
43
51
  consumed verbatim downstream. The human owns that per-leaf call. Forcing haiku
44
52
  on them would be the very downgrade the floor forbids.
45
53
 
54
+ ## Ad-hoc scripts — the tier gate hook
55
+
56
+ The repo linter only sees committed files. An AD-HOC script (written inline for
57
+ one run) crosses exactly one chokepoint before it executes: the Workflow tool
58
+ invocation. `.claude/hooks/tier-script-guard.js` (PreToolUse, matcher `Workflow`)
59
+ runs the same analysis there via `lib/tier-script.js` and DENIES ONCE when
60
+ exploration/`mech-` leaves carry no tier — with the exact leaf list to fix. The
61
+ `// BYAN-TIER: reviewed` comment marker acknowledges deliberate deep choices; an
62
+ identical resubmission passes (deny-once by design); registry (name-only)
63
+ invocations pass untouched. Every decision lands in
64
+ `_byan-output/tier-ledger.jsonl` with a per-model histogram — the measurement
65
+ basis for token gains. Escape hatch: `touch .byan-tier/off`.
66
+
67
+ Authoring flow: BEFORE writing a script, call `byan_dispatch` with
68
+ `{ leaves: [{ label, nature? }] }` (batch mode) to get the `opts.model` per leaf;
69
+ write `model:` only where non-null. Standalone report:
70
+ `node _byan/mcp/byan-mcp-server/bin/byan-tier-script.js <file> [--json]`.
71
+
46
72
  A per-leaf "effort" knob is not available: the native `agent()` / Agent API exposes
47
73
  only `model`, so model tier is the sole token lever. Effort-by-complexity reduces
48
74
  to model-by-complexity.
@@ -6,15 +6,15 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/inject-soul.js"
9
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/inject-soul.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
10
10
  },
11
11
  {
12
12
  "type": "command",
13
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/inject-tao.js"
13
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/inject-tao.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
14
14
  },
15
15
  {
16
16
  "type": "command",
17
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/soul-memory-check.js"
17
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/soul-memory-check.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
18
18
  }
19
19
  ]
20
20
  }
@@ -25,23 +25,27 @@
25
25
  "hooks": [
26
26
  {
27
27
  "type": "command",
28
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/inject-voice-anchor.js"
28
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/inject-voice-anchor.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
29
29
  },
30
30
  {
31
31
  "type": "command",
32
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/inject-delivery-default.js"
32
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/inject-delivery-default.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
33
33
  },
34
34
  {
35
35
  "type": "command",
36
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/soul-memory-triggers.js"
36
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/soul-memory-triggers.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
37
37
  },
38
38
  {
39
39
  "type": "command",
40
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fd-phase-guard.js"
40
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/fd-phase-guard.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
41
41
  },
42
42
  {
43
43
  "type": "command",
44
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/strict-context-inject.js"
44
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/strict-context-inject.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
45
+ },
46
+ {
47
+ "type": "command",
48
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/codex-autodelegate.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
45
49
  }
46
50
  ]
47
51
  }
@@ -52,35 +56,35 @@
52
56
  "hooks": [
53
57
  {
54
58
  "type": "command",
55
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/mantra-validate.js"
59
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/mantra-validate.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
56
60
  },
57
61
  {
58
62
  "type": "command",
59
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fact-check-claims.js"
63
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/fact-check-claims.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
60
64
  },
61
65
  {
62
66
  "type": "command",
63
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fd-response-check.js"
67
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/fd-response-check.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
64
68
  },
65
69
  {
66
70
  "type": "command",
67
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/stage-to-byan.js"
71
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/stage-to-byan.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
68
72
  },
69
73
  {
70
74
  "type": "command",
71
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/strict-stop-guard.js"
75
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/strict-stop-guard.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
72
76
  },
73
77
  {
74
78
  "type": "command",
75
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/autobench-stop-guard.js"
79
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/autobench-stop-guard.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
76
80
  },
77
81
  {
78
82
  "type": "command",
79
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/punt-guard.js"
83
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/punt-guard.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
80
84
  },
81
85
  {
82
86
  "type": "command",
83
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/drain-advisory.js"
87
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/drain-advisory.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
84
88
  }
85
89
  ]
86
90
  }
@@ -91,15 +95,24 @@
91
95
  "hooks": [
92
96
  {
93
97
  "type": "command",
94
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tool-transparency.js"
98
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/tool-transparency.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
95
99
  },
96
100
  {
97
101
  "type": "command",
98
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fact-check-absolutes.js"
102
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/fact-check-absolutes.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
99
103
  },
100
104
  {
101
105
  "type": "command",
102
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/strict-scope-guard.js"
106
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/strict-scope-guard.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
107
+ }
108
+ ]
109
+ },
110
+ {
111
+ "matcher": "Workflow",
112
+ "hooks": [
113
+ {
114
+ "type": "command",
115
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/tier-script-guard.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
103
116
  }
104
117
  ]
105
118
  }
@@ -110,11 +123,11 @@
110
123
  "hooks": [
111
124
  {
112
125
  "type": "command",
113
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tool-failure-guard.js"
126
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/tool-failure-guard.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
114
127
  },
115
128
  {
116
129
  "type": "command",
117
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/leantime-fd-sync.js"
130
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/leantime-fd-sync.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
118
131
  }
119
132
  ]
120
133
  }
@@ -125,7 +138,7 @@
125
138
  "hooks": [
126
139
  {
127
140
  "type": "command",
128
- "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-compact-save.js"
141
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/pre-compact-save.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
129
142
  }
130
143
  ]
131
144
  }
@@ -18,6 +18,20 @@ Invoke this protocol when the user :
18
18
 
19
19
  If the user request is a simple question or chat, stay out of FD — respond normally.
20
20
 
21
+ ## 1.25. Claude/Codex handoff import trigger
22
+
23
+ When the user says `importe depuis claude` or `importe depuis codex`, handle it
24
+ before starting a new FD cycle:
25
+
26
+ 1. Run `byan-handoff latest --from <requested source> --prompt`.
27
+ 2. Use the generated prompt as resume context, inspect the listed files, then
28
+ continue the current work.
29
+
30
+ If no matching handoff exists, say that no handoff from that source was found and
31
+ offer `byan-handoff latest --prompt` as the fallback only if the user accepts
32
+ resuming from the newest handoff across all sources. Do not rely on native
33
+ Claude/Codex memory as the source of truth.
34
+
21
35
  ## 1.5. Freshness check (silent, once per session)
22
36
 
23
37
  Before responding to the user's first activation message in a session, call the MCP tool `byan_update_check` once. It is read-only and cheap (single npm registry lookup, 5s timeout, no side effects).
@@ -64,9 +78,14 @@ Never call `byan_update_apply` without explicit user consent. That tool returns
64
78
  - **Score ≥ 40** → main-thread (heavy) or delegate to `byan-hermes-dispatch`
65
79
  - **Model tier** (WHICH model), from the task NATURE — not its size (`byan_dispatch` returns it as `model`, via native-tiers, the single source of truth) :
66
80
  - nature `exploration` (load/read/scan/list/parse/fetch...) → `haiku`
81
+ - nature `mechanical` (binary judgment-free checks : JSON parses, schema matches, lint passes — label prefix `mech-`, explicit opt-in only) → `sonnet`
67
82
  - nature `implementation` / `verification` / `analysis` / unknown → deep = **inherit the session model**
68
- - Keep protected work (verify/analysis/implement) off haiku regardless of size ; no pin-up to opus. Pass an explicit `nature` to `byan_dispatch` when you know it.
69
- - **Inside native workflow scripts** (`.claude/workflows/*.js`) the SAME tiering applies per `agent()` leaf via `opts.model`, enforced by the workflow linter as a FLOOR not a ceiling : `modelRoutingViolations` HARD-blocks a downgrade on a protected leaf (or a pin-up) ; an exploration-labelled leaf left deep is a NON-blocking ADVISORY (`byan-lint-workflows.js --advise`), since many such leaves bear a gate/classification/exact-conversion and must stay deep — the human owns that call. No per-leaf effort knob exists (the API exposes only `model`), so effort-by-complexity reduces to model-by-complexity.
83
+ - Keep protected work (verify/analysis/implement) off haiku/sonnet regardless of size ; no pin-up to opus. Pass an explicit `nature` to `byan_dispatch` when you know it.
84
+ - **Inside native workflow scripts** (`.claude/workflows/*.js` OR ad-hoc) the SAME tiering applies per `agent()` leaf via `opts.model`, enforced as a FLOOR not a ceiling on TWO nets :
85
+ - **Repo linter** (committed scripts, pre-commit) : `modelRoutingViolations` HARD-blocks a downgrade on a protected leaf, a pin-up, or a half-applied `mech-` opt-in (`mechanical-without-model` / `mechanical-below-tier`) ; an exploration-labelled leaf left deep is a NON-blocking ADVISORY (`byan-lint-workflows.js --advise`), since many such leaves bear a gate/classification/exact-conversion and must stay deep — the human owns that call.
86
+ - **Tier gate hook** (EVERY Workflow invocation, inline or scriptPath) : `tier-script-guard.js` (PreToolUse) runs the same analysis (`lib/tier-script.js`) and DENIES ONCE when exploration/`mech-` leaves have no tier, with the exact leaf list to fix. Acknowledge deliberate deep choices with the `// BYAN-TIER: reviewed` comment marker ; an identical resubmission passes (deny-once by design, no trap). Every decision lands in `_byan-output/tier-ledger.jsonl` (the measurement basis for token gains). Escape hatch : `.byan-tier/off`.
87
+ - **Authoring aid** : BEFORE writing a script, call `byan_dispatch` with `{ leaves: [{ label, nature? }] }` (batch mode) to get the `opts.model` per leaf ; write `model:` only where non-null. Report with `node _byan/mcp/byan-mcp-server/bin/byan-tier-script.js <file> [--json]`.
88
+ - No per-leaf effort knob exists (the API exposes only `model`), so effort-by-complexity reduces to model-by-complexity.
70
89
  - **Output** : a table `{ feature → specialist → model → strategy → estimated_tokens }`.
71
90
  - **If no specialist matches** : halt. Ask user whether to run INT (agent recruitment) first. Do NOT fallback silently to general-purpose.
72
91
  - **Exit gate** : user validates the mapping.
@@ -19,3 +19,10 @@ description: "OpenCode/Codex integration specialist for BYAN skills Role: OpenCo
19
19
  - Validate .codex/prompts/ structure
20
20
  - Test skill detection before deployment
21
21
  - Handle Codex-specific terminology (skills not agents)
22
+ - When the user says `importe depuis claude` or `importe depuis codex`, run
23
+ `byan-handoff latest --from <requested source> --prompt`, then resume from
24
+ the generated prompt after inspecting the listed files. If none matches,
25
+ report that no handoff from that source exists and offer
26
+ `byan-handoff latest --prompt` as fallback only if the user accepts resuming
27
+ from the newest handoff across all sources. Native Claude/Codex memory is not
28
+ the source of truth.
@@ -55,7 +55,9 @@ Call the `byan_dispatch` MCP tool with `{ task: <goal>, parallelizable: <bool>,
55
55
  - `main-thread` — do it inline, no delegation
56
56
  - `agent-subagent-worktree` — spawn Agent tool with isolation worktree
57
57
  - `mcp-worker` — spawn Agent tool, no worktree
58
- - **model** (WHICH model), from the task NATURE via native-tiers, not its size : `haiku` (exploration only) or `null` = deep (inherit the session model). Pass an explicit `nature` (`exploration`/`implementation`/`verification`/`analysis`) when you know it; protected natures stay off haiku.
58
+ - **model** (WHICH model), from the task NATURE via native-tiers, not its size : `haiku` (exploration), `sonnet` (mechanical — explicit binary judgment-free checks only), or `null` = deep (inherit the session model). Pass an explicit `nature` (`exploration`/`mechanical`/`implementation`/`verification`/`analysis`) when you know it; protected natures stay off haiku/sonnet.
59
+
60
+ **Batch mode (workflow authoring)** : before WRITING a workflow script, call `byan_dispatch` with `{ leaves: [{ label, nature? }, ...] }` — it returns the `opts.model` value per leaf from the same source of truth. Write `model:` only where it is non-null. The `tier-script-guard` PreToolUse hook gates every Workflow invocation against this contract (deny-once with the exact leaf list; acknowledge deliberate deep choices with the `// BYAN-TIER: reviewed` comment marker).
59
61
 
60
62
  ### 4. Spawn the work
61
63
 
@@ -6,7 +6,7 @@ export const meta = {
6
6
  { title: 'STRUCTURE', detail: 'build the ordered development_status map: epic, its stories, its retrospective' },
7
7
  { title: 'DETECT', detail: 'intelligent per-story status detection, never downgrade an existing status' },
8
8
  { title: 'GENERATE', detail: 'write sprint-status.yaml with metadata as comments AND as parseable fields' },
9
- { title: 'VALIDATE', detail: 'coverage + legal-status + YAML checks, then count totals and report' },
9
+ { title: 'VALIDATE', detail: 'coverage + legal-status + YAML checks, then count totals and report', model: 'sonnet' },
10
10
  ],
11
11
  }
12
12
 
@@ -236,7 +236,7 @@ const validation = await agent(
236
236
  `- validYaml: the file parses as YAML.\n` +
237
237
  `Count totals: epicCount, storyCount, epicsInProgress, storiesDone. ` +
238
238
  `passed = true only if ALL six checks hold; otherwise list specific failures.`,
239
- { label: 'validate-and-report', phase: 'VALIDATE', schema: VALIDATE_SCHEMA }
239
+ { label: 'mech-validate-status', phase: 'VALIDATE', schema: VALIDATE_SCHEMA, model: 'sonnet' }
240
240
  )
241
241
 
242
242
  // Single top-level return — DATA only. The orchestrating skill presents this at
@@ -6,7 +6,7 @@ export const meta = {
6
6
  { title: 'DISCOVER_TESTS' },
7
7
  { title: 'MAP_CRITERIA' },
8
8
  { title: 'ANALYZE_GAPS' },
9
- { title: 'GATE_DECISION' }
9
+ { title: 'GATE_DECISION', model: 'sonnet' }
10
10
  ]
11
11
  }
12
12
 
@@ -260,8 +260,9 @@ const gate = await agent(
260
260
  'Return the deterministic decision, the rationale string, and the gate criteria (p0 met?, overall status MET/PARTIAL/NOT MET).'
261
261
  ].join(' '),
262
262
  {
263
- label: 'gate-decision',
263
+ label: 'mech-gate-decision',
264
264
  phase: 'GATE_DECISION',
265
+ model: 'sonnet',
265
266
  schema: {
266
267
  type: 'object',
267
268
  required: ['decision', 'rationale', 'gateCriteria'],
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: byan
3
+ description: BYAN local project skill. Use when working in the BYAN repo, invoking BYAN agents/workflows/commands, enforcing BYAN Strict Mode, or needing the portable BYAN filesystem map.
4
+ ---
5
+
6
+ # BYAN Local Skill
7
+
8
+ Use this skill when the user asks for BYAN behavior, mentions BYAN/BMAD, asks to
9
+ run an agent or workflow from this repo, or works on the BYAN platform itself.
10
+
11
+ ## Source Of Truth
12
+
13
+ BYAN's portable core lives in this repository. Do not treat native assistant
14
+ memory, generated caches, or platform-specific projections as authoritative.
15
+
16
+ Read `_byan/INDEX.md` first to find agents, workflows, commands, and project
17
+ artefacts. Avoid broad filesystem scans unless the index is missing, stale, or
18
+ insufficient for the requested task.
19
+
20
+ Core references:
21
+
22
+ - `_byan/INDEX.md` — filesystem map for BYAN agents, workflows, commands, and projects.
23
+ - `AGENTS.md` — Codex adapter for BYAN Strict Mode and delivery defaults.
24
+ - `.claude/rules/portable-core.md` — portable-core doctrine: `_byan/` and
25
+ `_byan-output/` are the source of truth; native platform features are
26
+ write-through accelerators.
27
+ - `_byan/agent/byan/byan.md` — BYAN core meta-agent when the task requires the
28
+ full BYAN persona and FD workflow.
29
+
30
+ ## Strict Mode
31
+
32
+ When BYAN Strict Mode is active, follow the audit protocol before delivery:
33
+
34
+ 1. Lock scope with `byan_strict_lock_scope` before building.
35
+ 2. Build the locked scope without silent downgrade.
36
+ 3. Run at least three `byan_strict_self_verify` passes against the original
37
+ request and acceptance criteria.
38
+ 4. Call `byan_strict_complete` only after the final pass is `ok`.
39
+
40
+ Surface gaps explicitly. Do not round skipped tests, blocked tools, or partial
41
+ work up to done.
42
+
43
+ ## Working Rules
44
+
45
+ - Prefer BYAN's local manifests and `_byan/INDEX.md` over filesystem wandering.
46
+ - Keep Codex-specific material under `.codex/` or installed Codex skills under
47
+ `$CODEX_HOME/skills` / `~/.codex/skills`.
48
+ - Use existing BYAN agents and workflows instead of inventing parallel
49
+ conventions.
50
+ - If a public surface changes, update the matching doc, manifest, template, or
51
+ generated projection when applicable.
52
+ - Preserve BYAN's no-emoji convention in code, commits, docs, and prompts.
53
+
54
+ ## Claude/Codex Handoff Import
55
+
56
+ When the user says `importe depuis claude` or `importe depuis codex`, handle it
57
+ without asking for more context if a matching handoff exists:
58
+
59
+ 1. Run `byan-handoff latest --from <requested source> --prompt`.
60
+ 2. Treat the generated prompt as the resume context, inspect the listed files,
61
+ then continue the work.
62
+
63
+ If no matching handoff exists, say that no handoff from that source was found and
64
+ offer `byan-handoff latest --prompt` as the fallback only if the user accepts
65
+ resuming from the newest handoff across all sources. Do not rely on native
66
+ Claude/Codex memory as the source of truth.
67
+
68
+ ## Installed Bundles In This Repo
69
+
70
+ Prebuilt Codex skill bundles are available under `dist/skill-bundles/`, including:
71
+
72
+ - `byan-byan.zip` — full BYAN FD/meta-agent skill.
73
+ - `byan-strict.zip` — strict scope-lock and audit protocol.
74
+ - `byan-codex.zip` — Codex/OpenCode integration specialist.
75
+
76
+ Use those bundles when the user asks for a specific packaged BYAN skill. Use this
77
+ local `byan` skill as the general repo-aware entrypoint.
@@ -3,7 +3,7 @@
3
3
  > Carte du systeme de fichiers BYAN. Genere automatiquement — ne pas editer a la main.
4
4
  > Source : `_byan/_config/*-manifest.csv` + scan `_byan/projet/`. Regenerer : `byan-build-index`.
5
5
 
6
- ## Agents (26)
6
+ ## Agents (28)
7
7
 
8
8
  ### bmb
9
9
  - `agent-builder` — Agent Building Expert — `_byan/agent/agent-builder/agent-builder.md`
@@ -18,6 +18,8 @@
18
18
  - `architect` — Architect — `_byan/agent/architect/architect.md`
19
19
  - `dev` — Developer Agent — `_byan/agent/dev/dev.md`
20
20
  - `expert-merise-agile` — Expert Merise Agile - Assistant de Conception & Rédaction — `_byan/agent/expert-merise-agile/expert-merise-agile.md`
21
+ - `jimmy` — Spécialiste Documentation Technique & Processus Internes — `_byan/agent/jimmy/jimmy.md`
22
+ - `mike` — Gestionnaire de Projet — Spécialiste Leantime — `_byan/agent/mike/mike.md`
21
23
  - `pm` — Product Manager — `_byan/agent/pm/pm.md`
22
24
  - `quick-flow-solo-dev` — Quick Flow Solo Dev — `_byan/agent/quick-flow-solo-dev/quick-flow-solo-dev.md`
23
25
  - `quinn` — QA Engineer — `_byan/agent/quinn/quinn.md`
@@ -41,10 +43,11 @@
41
43
  ### tea
42
44
  - `tea` — Master Test Architect and Quality Advisor — `_byan/agent/tea/tea.md`
43
45
 
44
- ## Workflows (45)
46
+ ## Workflows (47)
45
47
 
46
48
  ### bmb
47
49
  - `agent` — Tri-modal workflow for creating, editing, and validating BMAD Core compliant agents — `_byan/workflow/simple/agent/workflow.md`
50
+ - `byan-benchmark` — DATA-only benchmark engine for any decision fork: options x weighted-criteria matrix + best-first reco + dissent — `_byan/workflow/simple/bmb/byan-benchmark/workflow.md`
48
51
  - `module` — Quad-modal workflow for creating BMAD modules (Brief + Create + Edit + Validate) — `_byan/workflow/simple/module/workflow.md`
49
52
  - `turbo-whisper-configure` — Configure Turbo Whisper API, hotkeys, and preferences — `_byan/workflow/simple/turbo-whisper/configure-workflow.md`
50
53
  - `turbo-whisper-docker-setup` — Setup self-hosted faster-whisper-server with Docker for privacy and cost-free transcription — `_byan/workflow/simple/turbo-whisper/docker-setup-workflow.md`
@@ -86,6 +89,7 @@
86
89
  ### core
87
90
  - `brainstorming` — Facilitate interactive brainstorming sessions using diverse creative techniques and ideation methods — `_byan/workflow/simple/brainstorming/workflow.md`
88
91
  - `party-mode` — Orchestrates group discussions between all installed BMAD agents, enabling natural multi-agent conversations — `_byan/workflow/simple/party-mode/workflow.md`
92
+ - `project-handoff` — Export/import portable Markdown project state between Claude Code and Codex — `_byan/workflow/simple/byan/project-handoff-workflow.md`
89
93
 
90
94
  ### tea
91
95
  - `teach-me-testing` — Multi-session learning companion that teaches testing progressively through 7 structured sessions with state persistence — `_byan/workflow/simple/testarch/teach-me-testing/workflow.md`
@@ -45,4 +45,4 @@ name,description,module,path
45
45
  "turbo-whisper-docker-setup","Setup self-hosted faster-whisper-server with Docker for privacy and cost-free transcription","bmb","_byan/workflow/simple/turbo-whisper/docker-setup-workflow.md"
46
46
  "turbo-whisper-integrate","Integrate Turbo Whisper with GitHub Copilot CLI, Claude Code, and Codex platforms","bmb","_byan/workflow/simple/turbo-whisper/integrate-workflow.md"
47
47
  "byan-benchmark","DATA-only benchmark engine for any decision fork: options x weighted-criteria matrix + best-first reco + dissent","bmb","_byan/workflow/simple/bmb/byan-benchmark/workflow.md"
48
-
48
+ "project-handoff","Export/import portable Markdown project state between Claude Code and Codex","core","_byan/workflow/simple/byan/project-handoff-workflow.md"
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import { analyzeScript, formatGateReason } from '../lib/tier-script.js';
4
+
5
+ // Per-script tiering report — works on ANY workflow script text: a committed
6
+ // .claude/workflows/*.js, an ad-hoc script persisted under the session dir, or
7
+ // a draft. One verdict per statically-labelled leaf against native-tiers.
8
+ //
9
+ // Exit codes (hook/CI friendly): 0 clean or acknowledged, 1 tier gaps,
10
+ // 2 violations (violations dominate gaps).
11
+ // Usage: node bin/byan-tier-script.js <script-file> [--json]
12
+
13
+ const args = process.argv.slice(2);
14
+ const jsonMode = args.includes('--json');
15
+ const file = args.find((a) => !a.startsWith('--'));
16
+
17
+ if (!file) {
18
+ process.stderr.write('usage: byan-tier-script <script-file> [--json]\n');
19
+ process.exit(2);
20
+ }
21
+
22
+ let src;
23
+ try {
24
+ src = fs.readFileSync(file, 'utf8');
25
+ } catch (e) {
26
+ process.stderr.write(`[byan-tier-script] cannot read ${file}: ${e.message}\n`);
27
+ process.exit(2);
28
+ }
29
+
30
+ const analysis = analyzeScript(src);
31
+
32
+ if (jsonMode) {
33
+ process.stdout.write(JSON.stringify(analysis, null, 2) + '\n');
34
+ } else {
35
+ process.stdout.write(
36
+ `[byan-tier-script] ${file}: ${analysis.agentCalls} agent() call(s), ` +
37
+ `${analysis.leaves.length} labelled leaf/leaves, ` +
38
+ `${analysis.gaps.length} gap(s), ${analysis.violations.length} violation(s)` +
39
+ `${analysis.acknowledged ? ', acknowledged (BYAN-TIER: reviewed)' : ''}\n`
40
+ );
41
+ for (const l of analysis.leaves) {
42
+ const model = l.model === null ? '(inherit)' : l.model;
43
+ process.stdout.write(` ${l.verdict.padEnd(12)} ${l.label} [${l.class}] model=${model} expected=${l.expectedModel ?? '(inherit)'}\n`);
44
+ }
45
+ if (analysis.gaps.length || analysis.violations.length) {
46
+ process.stdout.write(formatGateReason(analysis) + '\n');
47
+ }
48
+ }
49
+
50
+ if (analysis.acknowledged) process.exit(0);
51
+ if (analysis.violations.length) process.exit(2);
52
+ process.exit(analysis.gaps.length ? 1 : 0);
@@ -73,3 +73,25 @@ export function dispatch({ task, complexity, parallelizable, nature } = {}) {
73
73
  reasoning: `${strategyReason}. ${tierReason}.`,
74
74
  };
75
75
  }
76
+
77
+ // Batch tiering — the authoring aid for workflow scripts. The author passes the
78
+ // planned leaves BEFORE writing the script and gets the opts.model value per
79
+ // leaf from the same source of truth (native-tiers). No strategy axis here:
80
+ // the leaves all run inside one Workflow invocation, so WHERE they run is the
81
+ // script's concern, only WHICH model each deserves is answered. An explicit
82
+ // valid nature wins; otherwise the label classifies; a miss stays protected
83
+ // (implementation -> deep -> null), same conservative path as dispatch().
84
+ export function dispatchBatch(leaves) {
85
+ if (!Array.isArray(leaves)) return [];
86
+ return leaves.map((leaf) => {
87
+ const { label, nature } = leaf && typeof leaf === 'object' ? leaf : {};
88
+ const leafType = VALID_NATURES.has(nature) ? nature : classifyLeaf({ label: label || '' });
89
+ const tier = tierFor(leafType);
90
+ return {
91
+ label: label || '',
92
+ nature: leafType,
93
+ tier,
94
+ model: TIER_MODEL[tier],
95
+ };
96
+ });
97
+ }
@@ -28,7 +28,8 @@ export const TIERS = Object.freeze({ CHEAP: 'cheap', BALANCED: 'balanced', DEEP:
28
28
  // session runs (Opus by default, but Sonnet if the user chose Sonnet). We never
29
29
  // PIN UP — pinning a leaf to a fixed high tier would override the user's session
30
30
  // choice and could silently DOWNGRADE a Sonnet/Opus session's heavy leaf. Only
31
- // cheap/balanced carry a value, and only exploration leaves ever get one.
31
+ // cheap/balanced carry a value: exploration leaves get cheap, explicit mech-
32
+ // leaves get balanced, nothing else ever gets one.
32
33
  //
33
34
  // Values are the harness model-selection aliases (same set as the Agent tool:
34
35
  // 'haiku' | 'sonnet' | 'opus'). They are version-independent. If a future
@@ -36,15 +37,26 @@ export const TIERS = Object.freeze({ CHEAP: 'cheap', BALANCED: 'balanced', DEEP:
36
37
  // flags every script literal that drifts from it, so the fan-out stays bounded.
37
38
  export const TIER_MODEL = Object.freeze({ cheap: 'haiku', balanced: 'sonnet', deep: null });
38
39
 
39
- // Leaf task-type taxonomy. EXPLORATION is the only downgrade-safe class; the
40
- // other three are protected (never downgraded).
40
+ // Leaf task-type taxonomy. EXPLORATION and MECHANICAL are the two
41
+ // downgrade-safe classes; the other three are protected (never downgraded).
42
+ // MECHANICAL is verification whose outcome is binary and judgment-free (JSON
43
+ // parses, schema matches, lint passes, a test suite exits 0) — it earns the
44
+ // balanced tier. Semantic/adversarial verification stays VERIFICATION (deep).
41
45
  export const LEAF_TYPES = Object.freeze({
42
46
  EXPLORATION: 'exploration',
47
+ MECHANICAL: 'mechanical',
43
48
  IMPLEMENTATION: 'implementation',
44
49
  VERIFICATION: 'verification',
45
50
  ANALYSIS: 'analysis',
46
51
  });
47
52
 
53
+ // MECHANICAL is opt-in ONLY, through this label prefix ('mech-validate-json').
54
+ // No keyword fuzziness: 'validate-json' without the prefix stays VERIFICATION
55
+ // (protected). The prefix is an explicit authoring act — the author asserts
56
+ // "this check is binary and judgment-free", and the linter can then hold the
57
+ // script to it (a mech- leaf must carry model: 'sonnet', nothing else).
58
+ export const MECHANICAL_PREFIX = 'mech-';
59
+
48
60
  // Label keyword sets, matched as substrings on the leaf LABEL (not the prompt —
49
61
  // see classifyLeaf). Protected sets are checked first so any protected signal
50
62
  // beats an exploration signal (conservative: when in doubt, do not downgrade).
@@ -73,6 +85,9 @@ function matchesAny(text, keywords) {
73
85
  export function classifyLeaf(leaf) {
74
86
  const label = String((leaf && leaf.label) || '').toLowerCase();
75
87
  if (!label) return LEAF_TYPES.IMPLEMENTATION;
88
+ // Explicit opt-in beats every keyword class: 'mech-validate-json' contains a
89
+ // verification keyword, but the author's declared intent is the signal.
90
+ if (label.startsWith(MECHANICAL_PREFIX)) return LEAF_TYPES.MECHANICAL;
76
91
  if (matchesAny(label, VERIFICATION_KEYWORDS)) return LEAF_TYPES.VERIFICATION;
77
92
  if (matchesAny(label, ANALYSIS_KEYWORDS)) return LEAF_TYPES.ANALYSIS;
78
93
  if (matchesAny(label, IMPLEMENTATION_KEYWORDS)) return LEAF_TYPES.IMPLEMENTATION;
@@ -80,13 +95,15 @@ export function classifyLeaf(leaf) {
80
95
  return LEAF_TYPES.IMPLEMENTATION;
81
96
  }
82
97
 
83
- // tierFor(taskType) -> a TIERS value. Conservative auto-routing: only EXPLORATION
84
- // is downgraded (cheap); every other type stays deep. BALANCED is part of the
85
- // vocabulary but is never auto-assigned it exists for an explicit, manual
86
- // opt-in on a leaf an author judges mid-weight. Automation only ever picks
87
- // cheap or deep.
98
+ // tierFor(taskType) -> a TIERS value. Conservative auto-routing: EXPLORATION
99
+ // downgrades to cheap, MECHANICAL to balanced, every other type stays deep.
100
+ // BALANCED is reachable ONLY through the explicit mech- label opt-in (see
101
+ // MECHANICAL_PREFIX) never through keyword classification so automation
102
+ // still cannot land it on judgment-bearing work.
88
103
  export function tierFor(taskType) {
89
- return taskType === LEAF_TYPES.EXPLORATION ? TIERS.CHEAP : TIERS.DEEP;
104
+ if (taskType === LEAF_TYPES.EXPLORATION) return TIERS.CHEAP;
105
+ if (taskType === LEAF_TYPES.MECHANICAL) return TIERS.BALANCED;
106
+ return TIERS.DEEP;
90
107
  }
91
108
 
92
109
  // modelForLeaf({ label }) -> the opts.model value to write (a string) or null