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.
- package/CHANGELOG.md +78 -0
- package/README.md +24 -0
- package/install/lib/claude-native-setup.js +37 -0
- package/install/package.json +1 -1
- package/install/packages/platform-config/lib/validate.js +0 -14
- package/install/src/webui/api.js +6 -0
- package/install/src/webui/server.js +8 -1
- package/install/templates/.claude/CLAUDE.md +18 -0
- package/install/templates/.claude/hooks/lib/strict-config.json +46 -0
- package/install/templates/.claude/hooks/lib/strict-runtime.js +82 -0
- package/install/templates/.claude/hooks/strict-context-inject.js +86 -0
- package/install/templates/.claude/hooks/strict-scope-guard.js +101 -0
- package/install/templates/.claude/hooks/strict-stop-guard.js +100 -0
- package/install/templates/.claude/rules/strict-mode.md +166 -0
- package/install/templates/.claude/settings.json +12 -0
- package/install/templates/.claude/skills/byan-strict/SKILL.md +54 -0
- package/install/templates/.githooks/pre-commit +15 -0
- package/install/templates/_byan/_config/strict-mode.yaml +258 -0
- package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-sync-rules.js +24 -0
- package/install/templates/_byan/mcp/byan-mcp-server/bin/strict-precommit-gate.js +21 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/fd-state.js +2 -1
- package/install/templates/_byan/mcp/byan-mcp-server/lib/precommit-gate.js +120 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/strict-activation.js +76 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/strict-mode.js +391 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/strict-sync.js +140 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/sync-rules.js +261 -0
- package/install/templates/_byan/mcp/byan-mcp-server/server.js +207 -1
- package/package.json +6 -2
- package/src/byan-v2/data/strict-mantras.json +188 -0
- 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);
|