create-byan-agent 2.16.1 → 2.17.1

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 (30) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +24 -0
  3. package/install/lib/claude-native-setup.js +37 -0
  4. package/install/package.json +1 -1
  5. package/install/packages/platform-config/lib/validate.js +0 -14
  6. package/install/src/webui/api.js +6 -0
  7. package/install/src/webui/server.js +8 -1
  8. package/install/templates/.claude/CLAUDE.md +18 -0
  9. package/install/templates/.claude/hooks/lib/strict-config.json +46 -0
  10. package/install/templates/.claude/hooks/lib/strict-runtime.js +82 -0
  11. package/install/templates/.claude/hooks/strict-context-inject.js +86 -0
  12. package/install/templates/.claude/hooks/strict-scope-guard.js +101 -0
  13. package/install/templates/.claude/hooks/strict-stop-guard.js +100 -0
  14. package/install/templates/.claude/rules/strict-mode.md +166 -0
  15. package/install/templates/.claude/settings.json +12 -0
  16. package/install/templates/.claude/skills/byan-strict/SKILL.md +54 -0
  17. package/install/templates/.githooks/pre-commit +15 -0
  18. package/install/templates/_byan/_config/strict-mode.yaml +258 -0
  19. package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-sync-rules.js +24 -0
  20. package/install/templates/_byan/mcp/byan-mcp-server/bin/strict-precommit-gate.js +21 -0
  21. package/install/templates/_byan/mcp/byan-mcp-server/lib/fd-state.js +2 -1
  22. package/install/templates/_byan/mcp/byan-mcp-server/lib/precommit-gate.js +120 -0
  23. package/install/templates/_byan/mcp/byan-mcp-server/lib/strict-activation.js +76 -0
  24. package/install/templates/_byan/mcp/byan-mcp-server/lib/strict-mode.js +391 -0
  25. package/install/templates/_byan/mcp/byan-mcp-server/lib/strict-sync.js +140 -0
  26. package/install/templates/_byan/mcp/byan-mcp-server/lib/sync-rules.js +261 -0
  27. package/install/templates/_byan/mcp/byan-mcp-server/server.js +207 -1
  28. package/package.json +6 -2
  29. package/src/byan-v2/data/strict-mantras.json +188 -0
  30. package/src/byan-v2/generation/mantra-validator.js +39 -4
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Stop hook — BYAN Strict Mode end-of-turn guard.
4
+ *
5
+ * When a strict session is engaged (active + scope locked + not completed),
6
+ * block the turn from ending IF the assistant's last message claims the work
7
+ * is done. Completion must be earned through byan_strict_complete (3 passes,
8
+ * last verdict "ok"), which flips state.completed and disengages this guard.
9
+ *
10
+ * A mid-task yield (asking the user a question, reporting progress without a
11
+ * completion claim) is allowed — the guard only fires on a premature "done".
12
+ *
13
+ * Non-blocking on any IO/parse error : the hook never traps a turn when it
14
+ * cannot read the state.
15
+ */
16
+
17
+ const { loadConfig, loadState, isEngaged, passCount, lastVerdict, readStdin, parseJson } =
18
+ require('./lib/strict-runtime');
19
+
20
+ const DEFAULT_MARKERS = ['done', 'finished', 'complete', 'delivered', 'ready'];
21
+
22
+ function claimsCompletion(text, markers) {
23
+ if (!text) return false;
24
+ const lower = text.toLowerCase();
25
+ return (markers || DEFAULT_MARKERS).some((m) => {
26
+ const marker = String(m).toLowerCase();
27
+ if (/^[a-z]+$/.test(marker)) {
28
+ return new RegExp(`\\b${marker}\\b`).test(lower);
29
+ }
30
+ return lower.includes(marker);
31
+ });
32
+ }
33
+
34
+ function extractLastAssistantText(payload) {
35
+ if (!payload || typeof payload !== 'object') return '';
36
+ const tx = payload.transcript || payload.messages || [];
37
+ if (!Array.isArray(tx)) return '';
38
+ for (let i = tx.length - 1; i >= 0; i--) {
39
+ const m = tx[i];
40
+ if (m && m.role === 'assistant') {
41
+ if (typeof m.content === 'string') return m.content;
42
+ if (Array.isArray(m.content)) {
43
+ return m.content.map((c) => (c && c.text ? c.text : '')).join(' ');
44
+ }
45
+ }
46
+ }
47
+ return '';
48
+ }
49
+
50
+ // Pure decision : returns { block, reason }.
51
+ function decideStop({ state, config, lastAssistantText }) {
52
+ if (!isEngaged(state)) return { block: false };
53
+
54
+ const markers = config && config.completion_claim_markers;
55
+ if (!claimsCompletion(lastAssistantText, markers)) return { block: false };
56
+
57
+ const minPasses = (config && config.min_passes) || 3;
58
+ const done = passCount(state);
59
+ const verdict = lastVerdict(state);
60
+
61
+ // Defensive : if somehow 3 ok passes are recorded but complete() was not
62
+ // called, still block and tell the agent to call complete.
63
+ const base =
64
+ (config && config.banners && config.banners.stop_block) ||
65
+ 'Strict mode: the turn cannot end. The locked scope has not been completed.';
66
+
67
+ const reason =
68
+ `${base}\n` +
69
+ `Progress: ${done}/${minPasses} self-verify passes, last verdict=${verdict || 'none'}.\n` +
70
+ `You claimed completion but byan_strict_complete has not produced an audit token. ` +
71
+ `Run byan_strict_self_verify until the scope is satisfied (last pass verdict "ok"), ` +
72
+ `then call byan_strict_complete. If the scope changed, re-lock it.`;
73
+
74
+ return { block: true, reason };
75
+ }
76
+
77
+ if (require.main === module) {
78
+ (async () => {
79
+ const state = loadState();
80
+ if (!isEngaged(state)) {
81
+ process.stdout.write(JSON.stringify({ continue: true }));
82
+ process.exit(0);
83
+ }
84
+ const config = loadConfig();
85
+ const payload = parseJson(await readStdin());
86
+ const lastAssistantText = extractLastAssistantText(payload);
87
+
88
+ const decision = decideStop({ state, config, lastAssistantText });
89
+ if (!decision.block) {
90
+ process.stdout.write(JSON.stringify({ continue: true }));
91
+ process.exit(0);
92
+ }
93
+ process.stdout.write(
94
+ JSON.stringify({ decision: 'block', reason: decision.reason, systemMessage: decision.reason })
95
+ );
96
+ process.exit(2);
97
+ })();
98
+ }
99
+
100
+ module.exports = { decideStop, claimsCompletion, extractLastAssistantText };
@@ -0,0 +1,166 @@
1
+ # BYAN Strict Mode — Anti-Downgrade Enforcement
2
+
3
+ > The user asked for something complete. Strict mode exists to stop the agent
4
+ > from quietly delivering less: an MVP instead of the prod app, a stub instead
5
+ > of the feature, a template filled without care.
6
+
7
+ ## Principe
8
+
9
+ Strict mode locks a contract (the scope) at the start of a task, forces the
10
+ agent to self-verify its work against that contract at least three times, and
11
+ blocks the delivery (the commit) until verification is earned. It works on the
12
+ three platforms BYAN targets: Claude Code, Codex, GitHub Copilot.
13
+
14
+ | Layer | Mechanism | Platforms |
15
+ |-------|-----------|-----------|
16
+ | Scope lock + self-verify + complete | MCP tools (`byan_strict_*`) | all 3 (MCP) |
17
+ | In-session blocking | Claude Code hooks (Stop / PreToolUse / UserPromptSubmit) | Claude Code |
18
+ | Context injection | `AGENTS.md` block / `copilot-instructions.md` block | Codex / Copilot |
19
+ | Final net | `.githooks/pre-commit` audit gate | all 3 (commit time) |
20
+
21
+ Codex and Copilot have no in-session blocking hook. The pre-commit gate is the
22
+ net that catches them: a commit cannot land if a strict session was engaged but
23
+ not completed correctly.
24
+
25
+ ## Source de verite
26
+
27
+ One file drives everything: `_byan/_config/strict-mode.yaml`. It holds the
28
+ confidence floor, the self-verify config, the activation keywords, the scope
29
+ lock template, the 12 hard mantras, and the per-platform injection blocks.
30
+
31
+ Edit the YAML, then run the generator:
32
+
33
+ ```bash
34
+ node _byan/mcp/byan-mcp-server/bin/byan-sync-rules.js
35
+ ```
36
+
37
+ The generator emits (idempotent, between `BYAN-STRICT:BEGIN/END` markers):
38
+
39
+ - `.claude/skills/byan-strict/SKILL.md` — the Claude Code skill
40
+ - `.claude/hooks/lib/strict-config.json` — runtime config for the hooks
41
+ - `AGENTS.md` block — Codex
42
+ - `.github/copilot-instructions.md` block — Copilot
43
+ - `src/byan-v2/data/strict-mantras.json` — the MantraValidator ruleset
44
+
45
+ Do not hand-edit the generated blocks; edit the YAML and regenerate.
46
+
47
+ ## Le protocole (4 etapes)
48
+
49
+ 1. **Lock the scope** — `byan_strict_lock_scope` with a verbatim restatement of
50
+ the request and a non-empty list of testable `acceptanceCriteria`. Optional
51
+ `allowedPaths` restrict where writes may land. The locked scope is the
52
+ contract.
53
+ 2. **Build the full scope** — do not substitute an MVP or a stub. If a part
54
+ cannot be done, surface it as a gap in self-verify; do not cut it silently.
55
+ 3. **Self-verify >= 3 times** — `byan_strict_self_verify` with `verdict` `ok`
56
+ or `gap` (a `gap` needs a non-empty `findings` array). Re-read the original
57
+ request each pass. The last pass must be `ok`.
58
+ 4. **Complete** — `byan_strict_complete` returns an audit token. Below three
59
+ passes, or with a `gap` on the last pass, completion is rejected.
60
+
61
+ ## Activation
62
+
63
+ - **Manual** — `byan_fd_start` with `strict: true`, or load the `byan-strict`
64
+ skill, or call `byan_strict_lock_scope` directly.
65
+ - **Auto-detect** — when the request mentions an activation keyword (`prod`,
66
+ `production`, `client`, `contrat`, `template officiel`, `livrable`,
67
+ `deliverable`, `mise en production`, `release`), the agent is prompted to
68
+ suggest strict mode. It suggests and confirms; it does not lock silently.
69
+ - **Cross-platform check** — `byan_strict_suggest` (MCP) scans any text against
70
+ the keyword list, for platforms without an in-session hook.
71
+
72
+ ## Les outils MCP
73
+
74
+ | Tool | Role |
75
+ |------|------|
76
+ | `byan_strict_lock_scope` | Lock the contract (scope + criteria + paths) |
77
+ | `byan_strict_self_verify` | Record a verification pass (`ok` / `gap`) |
78
+ | `byan_strict_complete` | Earn the audit token (needs >= 3 passes, last `ok`) |
79
+ | `byan_strict_status` | Read the current session state |
80
+ | `byan_strict_abort` | Deliberate, audited exit from strict mode |
81
+ | `byan_strict_suggest` | Detect production-grade keywords in a text |
82
+
83
+ State lives in `.byan-strict/` (gitignored): `state.json` (current) and
84
+ `audit.log` (append-only JSONL). The pre-commit gate reads this trail.
85
+
86
+ ## Persistance cote byan_web (autorite)
87
+
88
+ The byan_web API is the **authority** for strict sessions; the local
89
+ `.byan-strict/` state is a mirror. BYAN is an online tool (no network, no
90
+ Claude), so the server is reachable whenever strict mode runs, and its record
91
+ is final.
92
+
93
+ - **Push** — every mutation pushes best-effort to the API after the local write:
94
+ `lock_scope` -> `POST /api/strict-sessions` (idempotent on the local session
95
+ id), `self_verify` -> `PATCH` (append a pass), `complete` -> `PATCH`
96
+ (audit token), `abort` -> `PATCH`. The session is scoped to the API key's
97
+ user and attached to a `projectId` (arg or `BYAN_PROJECT_ID`).
98
+ - **Read** — `byan_strict_status` and the pre-commit gate consult the API first;
99
+ the local mirror is the fallback only when the API is genuinely unreachable
100
+ (so an offline machine is not hard-blocked, but online the server wins).
101
+ - **Best-effort** — a missing `BYAN_API_TOKEN`, a timeout, or a non-2xx
102
+ degrades to `synced: false` rather than throwing. The local protocol keeps
103
+ working regardless; the sync layer (`lib/strict-sync.js`) isolates all
104
+ network I/O so `strict-mode.js` stays pure-local.
105
+ - **Config** — `.mcp.json` passes `BYAN_API_TOKEN` via `${BYAN_API_TOKEN}`
106
+ (env expansion); the secret stays out of any tracked file. API side:
107
+ migration `033-strict-sessions.sql` + `routes/strict-sessions.js`.
108
+
109
+ ## Les hooks Claude Code
110
+
111
+ Registered globally in `.claude/settings.json`; each one is a no-op unless a
112
+ strict session is engaged (active + scope locked + not completed):
113
+
114
+ - **`strict-context-inject.js`** (UserPromptSubmit) — injects the strict banner
115
+ and live pass status; suggests strict mode on activation keywords.
116
+ - **`strict-scope-guard.js`** (PreToolUse) — denies `Write`/`Edit` outside the
117
+ locked `allowedPaths` (exempt: `.byan-strict/`, `_byan-output/`, `.git/`).
118
+ - **`strict-stop-guard.js`** (Stop) — blocks end-of-turn when the assistant
119
+ claims the work is done but `byan_strict_complete` has not produced an audit
120
+ token. A mid-task yield without a completion claim is allowed.
121
+
122
+ ## Le filet pre-commit
123
+
124
+ `.githooks/pre-commit` runs `strict-precommit-gate.js` on every commit:
125
+
126
+ - No strict session on disk -> pass (strict was not engaged).
127
+ - Aborted session -> pass (deliberate, audited).
128
+ - Engaged but not completed -> **block**.
129
+ - Completed with < 3 passes or last verdict not `ok` -> **block**.
130
+ - Completed correctly -> pass.
131
+
132
+ Emergency bypass: `git commit --no-verify`.
133
+
134
+ ## Confiance sur les claims durs
135
+
136
+ Claims in `security`, `performance`, or `compliance` need LEVEL-1 sourcing
137
+ (95%) under strict mode, or they are BLOCKED. This raises the default
138
+ fact-check floors. See @.claude/rules/fact-check.md for the proof levels.
139
+
140
+ ## Les 12 mantras durs
141
+
142
+ `STRICT-1` Scope Lock First · `STRICT-2` No Downgrade · `STRICT-3` No Silent Cut ·
143
+ `STRICT-4` Self-Verify Three Times · `STRICT-5` Re-Read The Original ·
144
+ `STRICT-6` Evidence Over Assertion · `STRICT-7` Hard Claim Floor ·
145
+ `STRICT-8` Gap Is Not Failure · `STRICT-9` Audit Trail ·
146
+ `STRICT-10` No Premature Complete · `STRICT-11` Honest Status ·
147
+ `STRICT-12` Full Over Fast.
148
+
149
+ Full text in `_byan/_config/strict-mode.yaml` and the generated SKILL.md.
150
+
151
+ ## FAQ
152
+
153
+ **Strict mode trapped my turn — I cannot finish.** You claimed completion while
154
+ a session was engaged. Either run `byan_strict_self_verify` until the scope is
155
+ satisfied then `byan_strict_complete`, or `byan_strict_abort` to exit.
156
+
157
+ **A write was denied.** The target is outside the locked `allowedPaths`. If it
158
+ belongs to the scope, re-lock with the corrected paths; otherwise do not write
159
+ it.
160
+
161
+ **The commit is blocked.** A strict session is engaged but not completed.
162
+ Complete or abort it. `--no-verify` bypasses (emergency only).
163
+
164
+ **Can I force strict mode on every commit?** No. Strict mode is opt-in /
165
+ auto-suggested. The gate enforces completion once engaged; it does not force
166
+ engagement.
@@ -30,6 +30,10 @@
30
30
  {
31
31
  "type": "command",
32
32
  "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fd-phase-guard.js"
33
+ },
34
+ {
35
+ "type": "command",
36
+ "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/strict-context-inject.js"
33
37
  }
34
38
  ]
35
39
  }
@@ -49,6 +53,10 @@
49
53
  {
50
54
  "type": "command",
51
55
  "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/stage-to-byan.js"
56
+ },
57
+ {
58
+ "type": "command",
59
+ "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/strict-stop-guard.js"
52
60
  }
53
61
  ]
54
62
  }
@@ -64,6 +72,10 @@
64
72
  {
65
73
  "type": "command",
66
74
  "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fact-check-absolutes.js"
75
+ },
76
+ {
77
+ "type": "command",
78
+ "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/strict-scope-guard.js"
67
79
  }
68
80
  ]
69
81
  }
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: byan-strict
3
+ description: >-
4
+ Enforcement mode that prevents scope downgrade and forces the agent to self-verify its work against the originally locked request before delivering. Invoke when the user asks for a production
5
+ deliverable, a complete app, a filled contract from a template, or uses
6
+ any activation keyword (`prod`, `production`, `client`, `contrat`, `template officiel`, `livrable`, `deliverable`, `mise en production`, `release`). Enforces scope-lock, N>=3
7
+ self-verify passes, and a 95% confidence floor on hard claims.
8
+ allowed-tools:
9
+ - mcp__byan__byan_strict_lock_scope
10
+ - mcp__byan__byan_strict_self_verify
11
+ - mcp__byan__byan_strict_complete
12
+ - mcp__byan__byan_strict_status
13
+ - mcp__byan__byan_strict_abort
14
+ ---
15
+
16
+ <!-- Generated by byan-sync-rules from _byan/_config/strict-mode.yaml. Do not hand-edit. -->
17
+
18
+ # BYAN Strict Mode
19
+
20
+ You are operating under BYAN Strict Mode. The user asked for something
21
+ complete. Downgrading the scope is the failure this mode exists to prevent.
22
+
23
+ ## Protocol
24
+
25
+ 1. **Lock the scope** with `byan_strict_lock_scope` before building. Provide a
26
+ verbatim restatement of the request and testable `acceptanceCriteria`. The
27
+ locked scope is the contract.
28
+ 2. **Build the full scope.** Do not substitute an MVP, a stub, or a simplified
29
+ version. If a part cannot be done, surface it as a gap — do not cut silently.
30
+ 3. **Self-verify at least 3 times** with
31
+ `byan_strict_self_verify`, re-reading the original request each pass. The
32
+ last pass must report verdict `ok`.
33
+ 4. **Complete** with `byan_strict_complete` to earn the audit token. Without it,
34
+ the pre-commit gate blocks the commit.
35
+
36
+ ## Hard claims
37
+
38
+ Claims in security, performance, or compliance need LEVEL-1 sourcing
39
+ (95%) or they are BLOCKED.
40
+
41
+ ## Mantras
42
+
43
+ - **STRICT-1 Scope Lock First** — Reformulate the request and lock the scope before any build. The locked scope is the contract for the rest of the task.
44
+ - **STRICT-2 No Downgrade** — Deliver the scope that was asked. Do not substitute an MVP, a stub, or a "simplified version" unless the user approves it and it is recorded in the audit trail.
45
+ - **STRICT-3 No Silent Cut** — If a part of the scope cannot be done, surface it as a gap in the self_verify findings. Do not drop it quietly.
46
+ - **STRICT-4 Self-Verify Three Times** — Run at least three self-verification passes against the locked acceptance criteria before calling complete.
47
+ - **STRICT-5 Re-Read The Original** — Each self-verify pass re-reads the original request, not your own summary of it.
48
+ - **STRICT-6 Evidence Over Assertion** — A "done" claim needs an artifact (test output, file diff, command result), not a statement.
49
+ - **STRICT-7 Hard Claim Floor** — Claims in security, performance, or compliance need LEVEL-1 sourcing (95%) or they are BLOCKED.
50
+ - **STRICT-8 Gap Is Not Failure** — Reporting a gap is the correct behavior. Hiding a gap is the violation.
51
+ - **STRICT-9 Audit Trail** — Every scope lock, verify pass, and completion is appended to the audit log. The commit gate reads this trail.
52
+ - **STRICT-10 No Premature Complete** — complete is rejected below three passes or when the last pass found a gap.
53
+ - **STRICT-11 Honest Status** — Report failing tests with their output. Report skipped steps as skipped. Do not round a partial result up to "done".
54
+ - **STRICT-12 Full Over Fast** — When the user states "complete, no cost compromise", speed is out of scope. Do not re-question the budget under the guise of an estimate.
@@ -25,6 +25,21 @@ if ! command -v node >/dev/null 2>&1; then
25
25
  exit 0
26
26
  fi
27
27
 
28
+ # BYAN Strict Mode gate — the platform-agnostic final net. If a strict session
29
+ # was engaged (scope locked) but not completed correctly, block the commit.
30
+ # No-op when strict mode was never engaged. Runs on every platform (Claude
31
+ # Code, Codex, Copilot) since it triggers at commit time, not in-session.
32
+ STRICT_GATE="_byan/mcp/byan-mcp-server/bin/strict-precommit-gate.js"
33
+ if [ -f "$STRICT_GATE" ]; then
34
+ if ! node "$STRICT_GATE" --root "$(git rev-parse --show-toplevel)"; then
35
+ echo ""
36
+ echo "Commit blocked by BYAN Strict Mode gate."
37
+ echo "Complete the strict session (byan_strict_complete) or abort it (byan_strict_abort)."
38
+ echo "Bypass with 'git commit --no-verify' (emergency only)."
39
+ exit 1
40
+ fi
41
+ fi
42
+
28
43
  if [ ! -f "$VALIDATOR" ]; then
29
44
  exit 0
30
45
  fi
@@ -0,0 +1,258 @@
1
+ # BYAN Strict Mode — Source of truth
2
+ #
3
+ # This file is the single canonical definition of BYAN Strict Mode.
4
+ # It is consumed by the `byan-sync-rules` generator (F3), which emits the
5
+ # per-platform artifacts (Claude Code SKILL.md + hooks, Codex AGENTS.md block,
6
+ # GitHub Copilot instructions block) from the values below.
7
+ #
8
+ # Edit this file, then run the generator. Do not hand-edit the generated blocks
9
+ # (they live between BYAN-STRICT:BEGIN / BYAN-STRICT:END markers).
10
+
11
+ version: 1
12
+ name: BYAN Strict Mode
13
+ slug: byan-strict
14
+ description: >-
15
+ Enforcement mode that prevents scope downgrade and forces the agent to
16
+ self-verify its work against the originally locked request before delivering.
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Confidence — hard claims must reach LEVEL-1 (95%).
20
+ # ---------------------------------------------------------------------------
21
+ confidence:
22
+ min_score: 95 # percentage; hard claims below this are BLOCKED
23
+ hard_claim_level: 1 # LEVEL-1 sourcing required on hard claims
24
+ applies_to:
25
+ - security
26
+ - performance
27
+ - compliance
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Self-verification — the iterate-and-check loop.
31
+ # Mirrors lib/strict-mode.js (MIN_SELF_VERIFY_PASSES = 3).
32
+ # ---------------------------------------------------------------------------
33
+ self_verify:
34
+ min_passes: 3
35
+ last_verdict_must_be: ok # the final pass must report no gap
36
+ reread_original_each_pass: true
37
+ verdicts:
38
+ - ok # no gap against the locked acceptance criteria
39
+ - gap # a gap was found; findings array must be non-empty
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Source floors per domain when strict mode is active.
43
+ # Stricter than the default fact-check floors. Level number = LEVEL-n.
44
+ # ---------------------------------------------------------------------------
45
+ source_floors:
46
+ security: 1 # raised from 2 -> 1 under strict mode
47
+ performance: 1 # raised from 2 -> 1 under strict mode
48
+ compliance: 1
49
+ general: 2 # raised from default under strict mode
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Activation — manual + auto-detection.
53
+ # byan_fd_start mode:"strict" sets it explicitly. Auto keywords in the user
54
+ # request raise a strict-mode suggestion that the agent confirms before locking.
55
+ # ---------------------------------------------------------------------------
56
+ activation:
57
+ manual_mode: strict
58
+ auto_keywords:
59
+ - prod
60
+ - production
61
+ - client
62
+ - contrat
63
+ - "template officiel"
64
+ - livrable
65
+ - deliverable
66
+ - "mise en production"
67
+ - release
68
+ auto_behavior: suggest # suggest -> confirm -> lock (do not lock silently)
69
+
70
+ # ---------------------------------------------------------------------------
71
+ # Scope lock — the contract template the agent fills before building.
72
+ # Feeds byan_strict_lock_scope (scope + acceptance_criteria + paths).
73
+ # ---------------------------------------------------------------------------
74
+ scope_lock:
75
+ min_scope_chars: 10
76
+ require_acceptance_criteria: true
77
+ template: |
78
+ SCOPE (verbatim restatement of what was asked):
79
+ {scope}
80
+
81
+ ACCEPTANCE CRITERIA (each one testable):
82
+ - {criterion}
83
+
84
+ PATHS in play:
85
+ - {path}
86
+
87
+ OUT OF SCOPE (explicitly excluded, agreed with user):
88
+ - {exclusion}
89
+
90
+ # ---------------------------------------------------------------------------
91
+ # Hard mantras — the rules the enforcement layer makes non-negotiable.
92
+ # These are imperatives, phrased to pass the fact-check absolute-word filter.
93
+ # ---------------------------------------------------------------------------
94
+ mantras:
95
+ - id: STRICT-1
96
+ name: Scope Lock First
97
+ rule: >-
98
+ Reformulate the request and lock the scope before any build. The locked
99
+ scope is the contract for the rest of the task.
100
+ rationale: A blurry scope is the entry point for silent downgrade.
101
+ priority: critical
102
+ validation_keywords: ["lock the scope", "byan_strict_lock_scope", "scope lock"]
103
+
104
+ - id: STRICT-2
105
+ name: No Downgrade
106
+ rule: >-
107
+ Deliver the scope that was asked. Do not substitute an MVP, a stub, or a
108
+ "simplified version" unless the user approves it and it is recorded in
109
+ the audit trail.
110
+ rationale: The user asked for a prod app; an MVP is a different deliverable.
111
+ priority: critical
112
+ validation_keywords: ["no downgrade", "downgrade", "mvp"]
113
+
114
+ - id: STRICT-3
115
+ name: No Silent Cut
116
+ rule: >-
117
+ If a part of the scope cannot be done, surface it as a gap in the
118
+ self_verify findings. Do not drop it quietly.
119
+ rationale: A dropped requirement the user did not see is a broken contract.
120
+ priority: high
121
+ validation_keywords: ["silent cut", "surface it as a gap", "do not cut silently"]
122
+
123
+ - id: STRICT-4
124
+ name: Self-Verify Three Times
125
+ rule: >-
126
+ Run at least three self-verification passes against the locked acceptance
127
+ criteria before calling complete.
128
+ rationale: One pass catches typos; three passes catch missing scope.
129
+ priority: critical
130
+ validation_keywords: ["self-verify", "byan_strict_self_verify", "three times"]
131
+
132
+ - id: STRICT-5
133
+ name: Re-Read The Original
134
+ rule: >-
135
+ Each self-verify pass re-reads the original request, not your own summary
136
+ of it.
137
+ rationale: Summaries drift; the source request does not.
138
+ priority: high
139
+ validation_keywords: ["re-read", "original request"]
140
+
141
+ - id: STRICT-6
142
+ name: Evidence Over Assertion
143
+ rule: >-
144
+ A "done" claim needs an artifact (test output, file diff, command
145
+ result), not a statement.
146
+ rationale: Stating success is cheap; showing it is the bar.
147
+ priority: high
148
+ validation_keywords: ["evidence", "artifact", "test output"]
149
+
150
+ - id: STRICT-7
151
+ name: Hard Claim Floor
152
+ rule: >-
153
+ Claims in security, performance, or compliance need LEVEL-1 sourcing
154
+ (95%) or they are BLOCKED.
155
+ rationale: These domains carry consequences that opinions cannot cover.
156
+ priority: critical
157
+ validation_keywords: ["level-1", "95%", "blocked"]
158
+
159
+ - id: STRICT-8
160
+ name: Gap Is Not Failure
161
+ rule: >-
162
+ Reporting a gap is the correct behavior. Hiding a gap is the violation.
163
+ rationale: Honesty about an unfinished part beats a false completion.
164
+ priority: high
165
+ validation_keywords: ["gap is", "hiding a gap", "the violation"]
166
+
167
+ - id: STRICT-9
168
+ name: Audit Trail
169
+ rule: >-
170
+ Every scope lock, verify pass, and completion is appended to the audit
171
+ log. The commit gate reads this trail.
172
+ rationale: Without a trail, enforcement is a suggestion, not a gate.
173
+ priority: high
174
+ validation_keywords: ["audit trail", "audit log", "audit token"]
175
+
176
+ - id: STRICT-10
177
+ name: No Premature Complete
178
+ rule: >-
179
+ complete is rejected below three passes or when the last pass found a gap.
180
+ rationale: Completion is earned by verification, not declared.
181
+ priority: critical
182
+ validation_keywords: ["byan_strict_complete", "rejected below", "earn"]
183
+
184
+ - id: STRICT-11
185
+ name: Honest Status
186
+ rule: >-
187
+ Report failing tests with their output. Report skipped steps as skipped.
188
+ Do not round a partial result up to "done".
189
+ rationale: A rounded-up status is the laziness the mode exists to stop.
190
+ priority: high
191
+ validation_keywords: ["honest status", "failing tests", "skipped"]
192
+
193
+ - id: STRICT-12
194
+ name: Full Over Fast
195
+ rule: >-
196
+ When the user states "complete, no cost compromise", speed is out of
197
+ scope. Do not re-question the budget under the guise of an estimate.
198
+ rationale: Re-asking "do you validate?" is downgrade wearing a tie.
199
+ priority: high
200
+ validation_keywords: ["full over fast", "no cost compromise", "out of scope"]
201
+
202
+ # ---------------------------------------------------------------------------
203
+ # Injection blocks — canonical text the generator embeds into each platform.
204
+ # Kept here so all three platforms stay in sync from one source.
205
+ # ---------------------------------------------------------------------------
206
+ injection:
207
+ context_banner: |
208
+ [STRICT MODE ACTIVE]
209
+ You are under BYAN Strict Mode. Before building:
210
+ 1. Lock the scope (byan_strict_lock_scope) with testable acceptance criteria.
211
+ While building:
212
+ 2. Do not downgrade the scope. Surface any gap, do not cut silently.
213
+ Before delivering:
214
+ 3. Run >= 3 self-verify passes (byan_strict_self_verify), re-reading the
215
+ original request each time. The last pass must report verdict "ok".
216
+ 4. Call byan_strict_complete to earn the audit token.
217
+ Hard claims (security/performance/compliance) require LEVEL-1 sourcing (95%).
218
+ A commit without a fresh, matching audit token is blocked by the pre-commit gate.
219
+
220
+ stop_block_reason: >-
221
+ Strict mode: the turn cannot end. The locked scope has not passed three
222
+ self-verify passes with a final "ok" verdict. Run byan_strict_self_verify
223
+ until the scope is satisfied, then byan_strict_complete.
224
+
225
+ pretooluse_deny_reason: >-
226
+ Strict mode: this write targets a path outside the locked scope. Either it
227
+ belongs to the scope (re-lock with the corrected paths) or it does not
228
+ (do not write it).
229
+
230
+ # ---------------------------------------------------------------------------
231
+ # Hooks config — consumed by the generated .claude/hooks/lib/strict-config.json,
232
+ # which the three Claude Code hooks read at runtime. Single source of truth so
233
+ # the enforcement behavior stays aligned with this file.
234
+ # ---------------------------------------------------------------------------
235
+ hooks:
236
+ # Words that signal the agent is claiming the work is finished. The Stop
237
+ # hook blocks end-of-turn when such a claim appears but the strict session
238
+ # was not completed (3 passes + audit token).
239
+ completion_claim_markers:
240
+ - done
241
+ - finished
242
+ - complete
243
+ - delivered
244
+ - ready
245
+ - shipped
246
+ - "terminé"
247
+ - fini
248
+ - "livré"
249
+ - "prêt"
250
+ - "c'est bon"
251
+ - "voilà"
252
+ scope_guard:
253
+ enforce_paths: true # deny Write/Edit outside locked allowed_paths
254
+ exempt_globs:
255
+ - ".byan-strict/"
256
+ - "_byan-output/"
257
+ - ".git/"
258
+ freshness_window_seconds: 600 # mirrors checkAuditTrail default
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import { syncRules } from '../lib/sync-rules.js';
3
+
4
+ // CLI wrapper for the byan-sync-rules generator.
5
+ // Usage: node bin/byan-sync-rules.js [--root <dir>] [--config <file>]
6
+
7
+ function parseArgs(argv) {
8
+ const args = {};
9
+ for (let i = 2; i < argv.length; i++) {
10
+ if (argv[i] === '--root') args.projectRoot = argv[++i];
11
+ else if (argv[i] === '--config') args.configPath = argv[++i];
12
+ }
13
+ return args;
14
+ }
15
+
16
+ try {
17
+ const report = syncRules(parseArgs(process.argv));
18
+ const lines = Object.entries(report).map(([file, action]) => ` ${action.padEnd(9)} ${file}`);
19
+ process.stdout.write('byan-sync-rules — strict mode artifacts\n' + lines.join('\n') + '\n');
20
+ process.exit(0);
21
+ } catch (err) {
22
+ process.stderr.write(`byan-sync-rules failed: ${err.message}\n`);
23
+ process.exit(1);
24
+ }
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { evaluateGate } from '../lib/precommit-gate.js';
3
+
4
+ // Pre-commit entry for the BYAN Strict Mode gate.
5
+ // Usage: node bin/strict-precommit-gate.js [--root <dir>]
6
+ // Exit 0 = allow commit, exit 1 = block commit.
7
+
8
+ function parseArgs(argv) {
9
+ const args = {};
10
+ for (let i = 2; i < argv.length; i++) {
11
+ if (argv[i] === '--root') args.projectRoot = argv[++i];
12
+ }
13
+ return args;
14
+ }
15
+
16
+ const result = await evaluateGate(parseArgs(process.argv));
17
+ if (result.pass) {
18
+ process.exit(0);
19
+ }
20
+ process.stderr.write(`[byan strict gate] BLOCK: ${result.reason}\n`);
21
+ process.exit(1);