@dzhechkov/harness-core 0.3.115 → 0.3.117

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.
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Adversarial plan-gate — the challenge panel (feature challenge-panel-plan-gate, ADR-001).
3
+ *
4
+ * feature-adr's cross-model Codex QE (Step 8) bites AFTER the code is written. The most expensive mistakes
5
+ * (overengineering, silent decisions, cemented degradations, test-theater, unrealistic scope) cement at the
6
+ * PLAN stage. This module is the deterministic "cartridge": it assembles a WIDE context pack and emits a
7
+ * fixed C1-C8 "break it, don't confirm it" brief + a verdict schema. The LLM "shot" — a FRESH adversary that
8
+ * did NOT write the plan, plus a mandatory cross-validator — is fired by the `challenge-panel` SKILL.
9
+ *
10
+ * The set/brief/select/render functions are PURE + deterministic (sorted, no clock/random) so the same
11
+ * context yields a byte-identical brief; the assemble helper does disk I/O with TOP-LEVEL node:fs imports
12
+ * (harness-core is ESM — a lazy require() is undefined at runtime; the R1 footgun).
13
+ *
14
+ * SAFETY PROPERTIES (load-bearing, both named by the ADR and pinned by a test):
15
+ * 1. (ADR §1) `pickAdversaryModel` returns a family DIFFERENT from the plan author — the panel is never
16
+ * the plan's own author (author bias). The врезка/skill honor this; the helper makes it testable.
17
+ * 2. (ADR §2) `buildChallengeBrief` inlines the WIDE context (vision + testing + map + degradations +
18
+ * code hints when supplied) — narrow context = shallow findings (the archive's core lesson).
19
+ * 3. (ADR §3) C1 is degradation-registry-aware: deviating from a REGISTERED accepted degradation is NOT a
20
+ * finding; and the gate is ADVISE — `confirmedVerdict` drops non-cross-validated P0/P1 (FP/theory),
21
+ * nothing here auto-aborts.
22
+ */
23
+ export type CId = 'C1' | 'C2' | 'C3' | 'C4' | 'C5' | 'C6' | 'C7' | 'C8';
24
+ export type ChallengeSeverity = 'P0' | 'P1' | 'P2';
25
+ export interface ChallengeQuestion {
26
+ readonly id: CId;
27
+ readonly title: string;
28
+ readonly prompt: string;
29
+ }
30
+ /**
31
+ * The fixed, DEPERSONALIZED owner-question set (FR-2). Frozen + generic — no product/vendor/section-number
32
+ * hardcode. Each is phrased in "break it, don't confirm it" mode. C1 is degradation-registry-aware.
33
+ */
34
+ export declare const CHALLENGE_QUESTIONS: readonly ChallengeQuestion[];
35
+ export interface ChallengeContext {
36
+ readonly plan: string;
37
+ readonly planPath: string;
38
+ readonly vision?: string;
39
+ readonly testing?: string;
40
+ readonly map?: string;
41
+ readonly degradations?: string;
42
+ readonly codeHints?: string;
43
+ }
44
+ /** JSON Schema for the panel's verdict — the skill forces the adversary to emit exactly this shape. */
45
+ export declare const CHALLENGE_VERDICT_SCHEMA: object;
46
+ export interface ChallengeFinding {
47
+ readonly c: CId;
48
+ readonly severity: ChallengeSeverity;
49
+ readonly title: string;
50
+ readonly why: string;
51
+ readonly where?: string;
52
+ readonly crossValidated?: boolean;
53
+ }
54
+ export interface Verdict {
55
+ readonly findings: readonly ChallengeFinding[];
56
+ readonly summary: string;
57
+ }
58
+ /**
59
+ * Assemble the WIDE context pack (FR-1). Impure I/O; NEVER throws — a missing plan yields an empty `plan`
60
+ * (the caller/CLI reports it), a missing calibration doc simply drops that field.
61
+ */
62
+ export declare function assembleChallengeContext(repoRoot: string, planPath: string): ChallengeContext;
63
+ /**
64
+ * PURE (FR-1/NFR-1): same context → byte-identical brief. Inlines the WIDE context (vision + testing + map +
65
+ * degradations + code hints when present) so the adversary reasons over the whole product, not a slice
66
+ * (ADR §2), and states the hard invariant + the "break it" mandate + the degradation-registry rule.
67
+ */
68
+ export declare function buildChallengeBrief(ctx: ChallengeContext): string;
69
+ /**
70
+ * Sanitize ONE raw finding (QE #10): reject anything whose `c`/`severity` is not a known enum value or
71
+ * whose required strings are missing — an out-of-enum finding NEVER survives to render/gate. Returns null
72
+ * for a bad finding so the caller drops it.
73
+ */
74
+ export declare function sanitizeFinding(raw: unknown): ChallengeFinding | null;
75
+ /**
76
+ * Validate a raw adversary payload into a Verdict (QE #7): a value without a `findings` ARRAY is NOT a
77
+ * verdict (returns null → the caller falls back LOUDLY, never a fake-clean empty verdict). Bad individual
78
+ * findings are dropped via sanitizeFinding; `summary` defaults to '' but the shape must be right.
79
+ */
80
+ export declare function sanitizeVerdict(raw: unknown): Verdict | null;
81
+ /** The P0/P1 findings that MUST be cross-validated before reaching the owner (FR-5). Sorted, deterministic. */
82
+ export declare function findingsNeedingCrossValidation(v: Verdict): ChallengeFinding[];
83
+ /**
84
+ * Drop non-cross-validated P0/P1 (FP/theory) — the anti-noise / advise-not-block property (ADR §3, FR-5).
85
+ * POSITIONAL, collision-free (QE #5/#6): `realFlags[i]` aligns to `findingsNeedingCrossValidation(v)[i]` —
86
+ * matching by INDEX, never by title, so two findings sharing a title can never cross-contaminate. A P0/P1
87
+ * survives ONLY when its flag is explicitly `true` (a missing/`false`/`undefined` flag ⇒ dropped — the
88
+ * anti-noise default: not-validated is treated as refuted). P2 pass through untouched. Deterministic sort.
89
+ */
90
+ export declare function confirmedVerdict(v: Verdict, realFlags: readonly boolean[]): Verdict;
91
+ /** Human surface, grouped by severity (FR-3). ADVISE — never an abort. PURE. */
92
+ export declare function renderVerdict(v: Verdict): string;
93
+ /**
94
+ * HARD INVARIANT (ADR §1, FR-4): pick an adversary model family DIFFERENT from the plan author's, so the
95
+ * panel is never the plan's own author (author bias). Claude author → Codex adversary; Codex author →
96
+ * a fresh Claude adversary. PURE — the врезка/skill call this and dispatch accordingly.
97
+ */
98
+ export declare function pickAdversaryModel(plannerModel: string): {
99
+ model: string;
100
+ note: string;
101
+ };
102
+ /** Normalize a model id to a coarse family for the panel-≠-author invariant. */
103
+ export declare function classifyModelFamily(model: string): 'claude' | 'openai' | 'unknown';
104
+ //# sourceMappingURL=challenge-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge-panel.d.ts","sourceRoot":"","sources":["../src/challenge-panel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,MAAM,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACxE,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAKnD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAmE1D,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,uGAAuG;AACvG,eAAO,MAAM,wBAAwB,EAAE,MAoBrC,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;IAChB,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAmBD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAO7F;AA+CD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAsCjE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CASrE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAM5D;AAED,+GAA+G;AAC/G,wBAAgB,8BAA8B,CAAC,CAAC,EAAE,OAAO,GAAG,gBAAgB,EAAE,CAI7E;AASD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAQnF;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAoBhD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMxF;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAKlF"}
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Adversarial plan-gate — the challenge panel (feature challenge-panel-plan-gate, ADR-001).
3
+ *
4
+ * feature-adr's cross-model Codex QE (Step 8) bites AFTER the code is written. The most expensive mistakes
5
+ * (overengineering, silent decisions, cemented degradations, test-theater, unrealistic scope) cement at the
6
+ * PLAN stage. This module is the deterministic "cartridge": it assembles a WIDE context pack and emits a
7
+ * fixed C1-C8 "break it, don't confirm it" brief + a verdict schema. The LLM "shot" — a FRESH adversary that
8
+ * did NOT write the plan, plus a mandatory cross-validator — is fired by the `challenge-panel` SKILL.
9
+ *
10
+ * The set/brief/select/render functions are PURE + deterministic (sorted, no clock/random) so the same
11
+ * context yields a byte-identical brief; the assemble helper does disk I/O with TOP-LEVEL node:fs imports
12
+ * (harness-core is ESM — a lazy require() is undefined at runtime; the R1 footgun).
13
+ *
14
+ * SAFETY PROPERTIES (load-bearing, both named by the ADR and pinned by a test):
15
+ * 1. (ADR §1) `pickAdversaryModel` returns a family DIFFERENT from the plan author — the panel is never
16
+ * the plan's own author (author bias). The врезка/skill honor this; the helper makes it testable.
17
+ * 2. (ADR §2) `buildChallengeBrief` inlines the WIDE context (vision + testing + map + degradations +
18
+ * code hints when supplied) — narrow context = shallow findings (the archive's core lesson).
19
+ * 3. (ADR §3) C1 is degradation-registry-aware: deviating from a REGISTERED accepted degradation is NOT a
20
+ * finding; and the gate is ADVISE — `confirmedVerdict` drops non-cross-validated P0/P1 (FP/theory),
21
+ * nothing here auto-aborts.
22
+ */
23
+ import { existsSync, readFileSync, realpathSync } from 'node:fs';
24
+ import { join, resolve, sep } from 'node:path';
25
+ const SEV_RANK = { P0: 3, P1: 2, P2: 1 };
26
+ const VALID_CID = new Set(['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8']);
27
+ const isValidSeverity = (s) => s === 'P0' || s === 'P1' || s === 'P2';
28
+ /**
29
+ * The fixed, DEPERSONALIZED owner-question set (FR-2). Frozen + generic — no product/vendor/section-number
30
+ * hardcode. Each is phrased in "break it, don't confirm it" mode. C1 is degradation-registry-aware.
31
+ */
32
+ export const CHALLENGE_QUESTIONS = Object.freeze([
33
+ Object.freeze({
34
+ id: 'C1',
35
+ title: 'Architecture anti-cement',
36
+ prompt: 'Does this plan cement a NEW bad pattern, a wrong boundary, or a shortcut that later work will be ' +
37
+ 'forced to copy? Check it against the product map + vision. IMPORTANT: deviating from a pattern that ' +
38
+ 'is REGISTERED in the accepted-degradations registry is NOT a finding — that debt is already owned. ' +
39
+ 'A genuinely new degradation the plan introduces → name it and propose it for the registry.',
40
+ }),
41
+ Object.freeze({
42
+ id: 'C2',
43
+ title: 'Production-ready',
44
+ prompt: 'Where would this fall over in production? Missing error handling, unhandled failure modes, ' +
45
+ 'resource leaks, missing observability, config/secret handling, migration/rollback. Name the ' +
46
+ 'concrete input or condition that breaks it, not a general worry.',
47
+ }),
48
+ Object.freeze({
49
+ id: 'C3',
50
+ title: 'Test sufficiency + honesty (both ways)',
51
+ prompt: 'Attack the test plan from BOTH sides. Under-testing: which claim — especially a safety property the ' +
52
+ 'ADR NAMES ("never X") — has no falsifying test? A test that cannot fail (a wrapper, a tautology, ' +
53
+ 'asserting the mock) is not coverage. Over-testing: which tests are theater — restating the ' +
54
+ 'implementation, testing the framework, brittle snapshots that verify nothing a user cares about?',
55
+ }),
56
+ Object.freeze({
57
+ id: 'C4',
58
+ title: 'Overengineering sweep',
59
+ prompt: 'What in this plan is built for a requirement nobody stated? Speculative generality, an abstraction ' +
60
+ 'with one caller, a config knob no one asked for, a plugin seam for a single case. For each: what is ' +
61
+ 'the simpler thing that meets the ACTUAL requirement?',
62
+ }),
63
+ Object.freeze({
64
+ id: 'C5',
65
+ title: 'Silent decisions',
66
+ prompt: 'Which load-bearing decisions did the plan make WITHOUT surfacing them as a decision? A default that ' +
67
+ 'is really a policy, a chosen tradeoff presented as the only option, a dependency added in passing. ' +
68
+ 'Each silent decision the owner did not get to refuse is a finding.',
69
+ }),
70
+ Object.freeze({
71
+ id: 'C6',
72
+ title: 'Runtime consistency',
73
+ prompt: 'Will this behave consistently with how the rest of the system already works — same error shape, ' +
74
+ 'same config source, same module/ESM conventions, same logging, same naming? Point to the specific ' +
75
+ 'existing convention the plan contradicts.',
76
+ }),
77
+ Object.freeze({
78
+ id: 'C7',
79
+ title: 'Scope',
80
+ prompt: 'Is the plan more than ~1.5× the size the request actually needs? If so, what is the concrete cut ' +
81
+ 'list — which files/steps/abstractions to drop to hit the real requirement — and what is genuinely ' +
82
+ 'load-bearing and must stay?',
83
+ }),
84
+ Object.freeze({
85
+ id: 'C8',
86
+ title: 'Executability',
87
+ prompt: 'Could an executor who is NOT the plan author complete every step without coming back to ask what was ' +
88
+ 'meant? Find the steps that are under-specified, assume unstated context, or hide a research task ' +
89
+ 'behind an imperative verb ("integrate X", "wire up Y") with no concrete how.',
90
+ }),
91
+ ]);
92
+ /** JSON Schema for the panel's verdict — the skill forces the adversary to emit exactly this shape. */
93
+ export const CHALLENGE_VERDICT_SCHEMA = Object.freeze({
94
+ type: 'object',
95
+ required: ['findings', 'summary'],
96
+ properties: {
97
+ findings: {
98
+ type: 'array',
99
+ items: {
100
+ type: 'object',
101
+ required: ['c', 'severity', 'title', 'why'],
102
+ properties: {
103
+ c: { type: 'string', enum: ['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8'] },
104
+ severity: { type: 'string', enum: ['P0', 'P1', 'P2'] },
105
+ title: { type: 'string' },
106
+ why: { type: 'string', description: 'concrete failing input/condition, not a general worry' },
107
+ where: { type: 'string', description: 'plan section / file:line if locatable' },
108
+ },
109
+ },
110
+ },
111
+ summary: { type: 'string' },
112
+ },
113
+ });
114
+ // Where architecture-layer calibration docs live (committed intent layer; inherits R1/R5 paths).
115
+ // Module-local — the canonical exported constants live in feature-adr-setup.ts / architecture.ts.
116
+ const CP_VISION = 'architecture/vision.md';
117
+ const CP_TESTING = 'architecture/testing.md';
118
+ const CP_MAP = 'architecture/map.json';
119
+ const CP_DEGRADATIONS = 'architecture/degradations.md';
120
+ const readIf = (p) => {
121
+ try {
122
+ if (!existsSync(p))
123
+ return undefined;
124
+ const t = readFileSync(p, 'utf8');
125
+ return t.trim() === '' ? undefined : t;
126
+ }
127
+ catch {
128
+ return undefined; // unreadable ⇒ run with less calibration, never error (FR-6)
129
+ }
130
+ };
131
+ /**
132
+ * Assemble the WIDE context pack (FR-1). Impure I/O; NEVER throws — a missing plan yields an empty `plan`
133
+ * (the caller/CLI reports it), a missing calibration doc simply drops that field.
134
+ */
135
+ export function assembleChallengeContext(repoRoot, planPath) {
136
+ // Path containment (QE #12): `--plan ../../etc/passwd` must NOT read outside the repo. Resolve and require
137
+ // the plan to stay under repoRoot; an escaping path yields an empty plan (the caller reports "not found").
138
+ const rootAbs = resolve(repoRoot);
139
+ const absPlan = resolve(rootAbs, planPath);
140
+ const plan = containedRead(rootAbs, absPlan) ?? '';
141
+ return withCalibration({ plan, planPath }, repoRoot);
142
+ }
143
+ /**
144
+ * Read a file ONLY if it stays under repoRoot both lexically AND after resolving symlinks (QE #12): a `..`
145
+ * escape is blocked lexically, and a symlink inside the repo pointing OUT (e.g. `plan.md -> /etc/passwd`) is
146
+ * blocked by re-checking the realpath of both the file and the root. Returns undefined on any escape/error.
147
+ */
148
+ function containedRead(rootAbs, absPath) {
149
+ if (absPath !== rootAbs && !absPath.startsWith(rootAbs + sep))
150
+ return undefined; // lexical `..` escape
151
+ if (!existsSync(absPath))
152
+ return undefined;
153
+ let realFile;
154
+ let realRoot;
155
+ try {
156
+ realFile = realpathSync(absPath);
157
+ realRoot = realpathSync(rootAbs);
158
+ }
159
+ catch {
160
+ return undefined; // broken/dangling symlink
161
+ }
162
+ if (realFile !== realRoot && !realFile.startsWith(realRoot + sep))
163
+ return undefined; // symlink escape
164
+ return readIf(realFile);
165
+ }
166
+ // exactOptionalPropertyTypes: build the optional fields by conditional spread (never assign `undefined`).
167
+ // The calibration docs are containment-checked too (a symlinked `architecture/vision.md -> /etc/passwd` must
168
+ // not leak either) — same realpath guard as the plan.
169
+ function withCalibration(base, repoRoot) {
170
+ const rootAbs = resolve(repoRoot);
171
+ const readDoc = (rel) => containedRead(rootAbs, resolve(rootAbs, rel));
172
+ const vision = readDoc(CP_VISION);
173
+ const testing = readDoc(CP_TESTING);
174
+ const map = readDoc(CP_MAP);
175
+ const degradations = readDoc(CP_DEGRADATIONS);
176
+ return {
177
+ plan: base.plan,
178
+ planPath: base.planPath,
179
+ ...(vision === undefined ? {} : { vision }),
180
+ ...(testing === undefined ? {} : { testing }),
181
+ ...(map === undefined ? {} : { map }),
182
+ ...(degradations === undefined ? {} : { degradations }),
183
+ ...(base.codeHints === undefined ? {} : { codeHints: base.codeHints }),
184
+ };
185
+ }
186
+ const HR = '─'.repeat(72);
187
+ const section = (title, body) => body === undefined ? `## ${title}\n(not provided — panel runs with less calibration)\n` : `## ${title}\n${body}\n`;
188
+ /**
189
+ * PURE (FR-1/NFR-1): same context → byte-identical brief. Inlines the WIDE context (vision + testing + map +
190
+ * degradations + code hints when present) so the adversary reasons over the whole product, not a slice
191
+ * (ADR §2), and states the hard invariant + the "break it" mandate + the degradation-registry rule.
192
+ */
193
+ export function buildChallengeBrief(ctx) {
194
+ const lines = [];
195
+ lines.push('# CHALLENGE PANEL — adversarial plan-gate');
196
+ lines.push('');
197
+ lines.push('You are a FRESH adversarial reviewer. You did NOT write this plan. Your job is to BREAK it, not to ' +
198
+ 'confirm it — find the concrete way each answer fails, or state plainly that you could not. A finding ' +
199
+ 'is a specific failing input/condition/omission, never a general worry. Every P0/P1 you raise will be ' +
200
+ 'independently cross-validated, so do not pad — theory that cannot be reproduced will be dropped.');
201
+ lines.push('');
202
+ lines.push(HR);
203
+ lines.push(section('PLAN UNDER REVIEW (' + ctx.planPath + ')', ctx.plan === '' ? undefined : ctx.plan));
204
+ lines.push(section('PRODUCT VISION (boundaries + principles)', ctx.vision));
205
+ lines.push(section('TESTING POLICY (what "done" + honest tests mean here)', ctx.testing));
206
+ lines.push(section('PRODUCT MAP (subsystems + existing conventions)', ctx.map));
207
+ lines.push(section('ACCEPTED-DEGRADATIONS REGISTRY (deviating from THESE is NOT a finding)', ctx.degradations));
208
+ lines.push(section('CODE HINTS', ctx.codeHints));
209
+ lines.push(HR);
210
+ lines.push('');
211
+ lines.push('## Ask each question in "break it, don\'t confirm it" mode');
212
+ for (const q of CHALLENGE_QUESTIONS) {
213
+ lines.push('');
214
+ lines.push(`### ${q.id} — ${q.title}`);
215
+ lines.push(q.prompt);
216
+ }
217
+ lines.push('');
218
+ lines.push(HR);
219
+ lines.push('## Output');
220
+ lines.push('Return findings tagged by C-number with severity P0 (would ship a serious defect / cements bad ' +
221
+ 'architecture), P1 (real gap, fix before code), or P2 (worth noting). For each: a concrete `why` ' +
222
+ '(the failing input/condition) and `where` if locatable. Then a one-paragraph `summary`. This gate ' +
223
+ 'ADVISES — it does not block; the owner decides.');
224
+ lines.push('');
225
+ lines.push('Verdict JSON schema: ' + JSON.stringify(CHALLENGE_VERDICT_SCHEMA));
226
+ return lines.join('\n');
227
+ }
228
+ /**
229
+ * Sanitize ONE raw finding (QE #10): reject anything whose `c`/`severity` is not a known enum value or
230
+ * whose required strings are missing — an out-of-enum finding NEVER survives to render/gate. Returns null
231
+ * for a bad finding so the caller drops it.
232
+ */
233
+ export function sanitizeFinding(raw) {
234
+ if (raw === null || typeof raw !== 'object')
235
+ return null;
236
+ const r = raw;
237
+ if (typeof r.c !== 'string' || !VALID_CID.has(r.c))
238
+ return null; // typeof-guard: a hostile {toString:null} must NOT throw
239
+ if (!isValidSeverity(r.severity))
240
+ return null;
241
+ if (typeof r.title !== 'string' || r.title === '')
242
+ return null;
243
+ if (typeof r.why !== 'string' || r.why === '')
244
+ return null;
245
+ const out = { c: r.c, severity: r.severity, title: r.title, why: r.why };
246
+ return typeof r.where === 'string' && r.where !== '' ? { ...out, where: r.where } : out;
247
+ }
248
+ /**
249
+ * Validate a raw adversary payload into a Verdict (QE #7): a value without a `findings` ARRAY is NOT a
250
+ * verdict (returns null → the caller falls back LOUDLY, never a fake-clean empty verdict). Bad individual
251
+ * findings are dropped via sanitizeFinding; `summary` defaults to '' but the shape must be right.
252
+ */
253
+ export function sanitizeVerdict(raw) {
254
+ if (raw === null || typeof raw !== 'object')
255
+ return null;
256
+ const r = raw;
257
+ if (!Array.isArray(r.findings))
258
+ return null;
259
+ const findings = r.findings.map(sanitizeFinding).filter((f) => f !== null);
260
+ return { findings, summary: typeof r.summary === 'string' ? r.summary : '' };
261
+ }
262
+ /** The P0/P1 findings that MUST be cross-validated before reaching the owner (FR-5). Sorted, deterministic. */
263
+ export function findingsNeedingCrossValidation(v) {
264
+ return [...v.findings]
265
+ .filter((f) => f.severity === 'P0' || f.severity === 'P1')
266
+ .sort(cmpFinding);
267
+ }
268
+ function cmpFinding(a, b) {
269
+ const s = SEV_RANK[b.severity] - SEV_RANK[a.severity];
270
+ if (s !== 0)
271
+ return s;
272
+ if (a.c !== b.c)
273
+ return a.c < b.c ? -1 : 1;
274
+ return a.title < b.title ? -1 : a.title > b.title ? 1 : 0;
275
+ }
276
+ /**
277
+ * Drop non-cross-validated P0/P1 (FP/theory) — the anti-noise / advise-not-block property (ADR §3, FR-5).
278
+ * POSITIONAL, collision-free (QE #5/#6): `realFlags[i]` aligns to `findingsNeedingCrossValidation(v)[i]` —
279
+ * matching by INDEX, never by title, so two findings sharing a title can never cross-contaminate. A P0/P1
280
+ * survives ONLY when its flag is explicitly `true` (a missing/`false`/`undefined` flag ⇒ dropped — the
281
+ * anti-noise default: not-validated is treated as refuted). P2 pass through untouched. Deterministic sort.
282
+ */
283
+ export function confirmedVerdict(v, realFlags) {
284
+ const need = findingsNeedingCrossValidation(v);
285
+ const kept = v.findings.filter((f) => f.severity === 'P2');
286
+ need.forEach((f, i) => {
287
+ if (realFlags[i] === true)
288
+ kept.push({ ...f, crossValidated: true });
289
+ });
290
+ kept.sort(cmpFinding);
291
+ return { findings: kept, summary: v.summary };
292
+ }
293
+ /** Human surface, grouped by severity (FR-3). ADVISE — never an abort. PURE. */
294
+ export function renderVerdict(v) {
295
+ if (v.findings.length === 0) {
296
+ return `Challenge panel: no cross-validated findings. ${v.summary}`.trim();
297
+ }
298
+ const byId = (s) => v.findings.filter((f) => f.severity === s).sort(cmpFinding);
299
+ const out = ['Challenge panel verdict (advisory — you decide):', ''];
300
+ for (const sev of ['P0', 'P1', 'P2']) {
301
+ const group = byId(sev);
302
+ if (group.length === 0)
303
+ continue;
304
+ out.push(`### ${sev} (${group.length})`);
305
+ for (const f of group) {
306
+ const cv = f.crossValidated ? ' ✓cross-validated' : '';
307
+ const where = f.where ? ` [${f.where}]` : '';
308
+ out.push(`- ${f.c} ${f.title}${where}${cv}`);
309
+ out.push(` why: ${f.why}`);
310
+ }
311
+ out.push('');
312
+ }
313
+ out.push(v.summary);
314
+ return out.join('\n').trim();
315
+ }
316
+ /**
317
+ * HARD INVARIANT (ADR §1, FR-4): pick an adversary model family DIFFERENT from the plan author's, so the
318
+ * panel is never the plan's own author (author bias). Claude author → Codex adversary; Codex author →
319
+ * a fresh Claude adversary. PURE — the врезка/skill call this and dispatch accordingly.
320
+ */
321
+ export function pickAdversaryModel(plannerModel) {
322
+ const fam = classifyModelFamily(plannerModel);
323
+ if (fam === 'claude')
324
+ return { model: 'codex', note: `plan authored on ${plannerModel} (Claude) → Codex adversary (cross-family)` };
325
+ if (fam === 'openai')
326
+ return { model: 'claude', note: `plan authored on ${plannerModel} (OpenAI/Codex) → fresh Claude adversary (cross-family)` };
327
+ // Unknown author family: default to a Claude adversary but say so — verify it is genuinely NOT the author's family.
328
+ return { model: 'claude', note: `plan author family UNKNOWN (${plannerModel}) → Claude adversary by default; verify it is cross-family before trusting the verdict` };
329
+ }
330
+ /** Normalize a model id to a coarse family for the panel-≠-author invariant. */
331
+ export function classifyModelFamily(model) {
332
+ const m = String(model).toLowerCase();
333
+ if (/claude|opus|sonnet|haiku|fable/.test(m))
334
+ return 'claude';
335
+ if (/codex|gpt|openai|\bo[1-9]\b/.test(m))
336
+ return 'openai';
337
+ return 'unknown';
338
+ }
339
+ //# sourceMappingURL=challenge-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge-panel.js","sourceRoot":"","sources":["../src/challenge-panel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Guided feature-adr onboarding (feature guided-feature-adr-setup, ADR-001).
3
+ *
4
+ * The `configure-feature-adr` SKILL runs a short interview and fills a `SetupSpec`; `dz feature-adr-setup`
5
+ * scaffolds the project-awareness files from that spec — so a user configures feature-adr WITHOUT knowing
6
+ * any manifest schema ("complex inside, simple outside"). The plan/render/scaffold builders are PURE +
7
+ * deterministic (sorted, no clock/random); the scan helper does disk I/O with TOP-LEVEL node:fs (ESM).
8
+ *
9
+ * SAFETY PROPERTY (ADR-001 §2, load-bearing): `scaffoldFromSpec` NEVER emits a full overwrite of an existing
10
+ * file. A structured file (manifest / project-skills) is UNION-merged (existing content preserved); a prose
11
+ * doc (vision.md / testing.md) that already exists is left untouched (`unchanged`), never clobbered.
12
+ */
13
+ import type { Subsystem, SubsystemManifest } from './architecture.js';
14
+ import type { ProjectSkillManifest, ExtraSkill } from './project-skills.js';
15
+ export interface VisionSpec {
16
+ readonly core: string;
17
+ readonly direction?: string;
18
+ readonly boundaries?: readonly string[];
19
+ readonly principles?: readonly string[];
20
+ }
21
+ export interface TestingSpec {
22
+ readonly commands?: readonly string[];
23
+ readonly doneDefinition?: string;
24
+ readonly gates?: readonly string[];
25
+ }
26
+ export type SubsystemSpec = Subsystem;
27
+ export interface SetupSpec {
28
+ readonly vision?: VisionSpec;
29
+ readonly subsystems?: readonly SubsystemSpec[];
30
+ readonly testing?: TestingSpec;
31
+ /** critic: 'auto' ⇒ a generated project-critic skill; a path ⇒ that file. brand/impl-bar: a path. */
32
+ readonly roles?: Partial<Record<'critic' | 'brand' | 'impl-bar', string>>;
33
+ readonly extra?: readonly ExtraSkill[];
34
+ /** scaffold a starter `architecture/degradations.md` — the accepted-degradations registry the R6
35
+ * challenge panel (C1) reads so it does not re-flag debt you already own. Create-if-absent. */
36
+ readonly degradations?: boolean;
37
+ }
38
+ export interface SetupScan {
39
+ readonly visionExists: boolean;
40
+ readonly manifestExists: boolean;
41
+ readonly projectSkillsExists: boolean;
42
+ readonly testingExists: boolean;
43
+ readonly packages: readonly string[];
44
+ readonly reviewCorpus: boolean;
45
+ }
46
+ export interface SetupPlan {
47
+ readonly exists: {
48
+ readonly vision: boolean;
49
+ readonly manifest: boolean;
50
+ readonly projectSkills: boolean;
51
+ readonly testing: boolean;
52
+ };
53
+ readonly discoveredPackages: readonly string[];
54
+ readonly hasReviewCorpus: boolean;
55
+ readonly missing: readonly string[];
56
+ readonly suggestions: readonly string[];
57
+ }
58
+ export interface ScaffoldFile {
59
+ readonly path: string;
60
+ readonly action: 'create' | 'augment' | 'unchanged';
61
+ readonly content: string;
62
+ readonly note?: string;
63
+ }
64
+ export interface ScaffoldResult {
65
+ readonly files: readonly ScaffoldFile[];
66
+ }
67
+ /** One existing on-disk file: `exists` distinguishes ABSENT from EXISTS-BUT-UNREADABLE (never clobber either). */
68
+ export interface ExistingFile {
69
+ readonly exists: boolean;
70
+ readonly content?: string;
71
+ }
72
+ /** The existing files the scaffold compares against. */
73
+ export interface ExistingScaffoldFiles {
74
+ readonly vision: ExistingFile;
75
+ readonly manifest: ExistingFile;
76
+ readonly projectSkills: ExistingFile;
77
+ readonly testing: ExistingFile;
78
+ readonly degradations?: ExistingFile;
79
+ }
80
+ export declare const P_VISION = "architecture/vision.md";
81
+ export declare const P_TESTING = "architecture/testing.md";
82
+ export declare const P_MANIFEST = "architecture/subsystems.manifest.json";
83
+ export declare const P_PROJECT_SKILLS = "architecture/project-skills.json";
84
+ export declare const P_CRITIC = "architecture/project-critic/SKILL.md";
85
+ export declare const P_DEGRADATIONS = "architecture/degradations.md";
86
+ /** What exists, what is discoverable, what is still missing — the read-only "which docs, and where?" answer (FR-2). PURE. */
87
+ export declare function buildSetupPlan(scan: SetupScan): SetupPlan;
88
+ /** Suggest a starter subsystem grouping from discovered packages (deterministic heuristic — user refines). PURE. */
89
+ export declare function suggestSubsystems(packages: readonly string[]): SubsystemSpec[];
90
+ /** architecture/vision.md from the spec. Deterministic. */
91
+ export declare function renderVisionDoc(v: VisionSpec): string;
92
+ /** architecture/testing.md from the spec — the project's verification procedure (feeds Step 8 QE). Deterministic. */
93
+ export declare function renderTestingDoc(t: TestingSpec): string;
94
+ /** Starter accepted-degradations registry (R6 challenge panel C1 reads this). PURE. */
95
+ export declare function renderDegradationsDoc(): string;
96
+ /** Build a subsystem manifest from the spec's subsystems. PURE. */
97
+ export declare function buildManifestFromSpec(subs: readonly SubsystemSpec[]): SubsystemManifest;
98
+ /** Wire the project-skills manifest from the spec: product-vision + testing point at the scaffolded docs;
99
+ * critic/brand/impl-bar are added when the spec supplies them; extra is carried through. PURE. */
100
+ export declare function buildProjectSkillsFromSpec(spec: SetupSpec): ProjectSkillManifest;
101
+ /**
102
+ * Build the scaffold plan from the spec + what already exists. PURE + deterministic. AUGMENT-NEVER-CLOBBER
103
+ * (ADR-001 §2, load-bearing): a file that EXISTS is never replaced — prose is left as-is; structured files
104
+ * are append-only merged (existing content, order, and unknown keys preserved). A file only gets `create`
105
+ * when it is genuinely ABSENT. Malformed existing content never crashes and never clobbers.
106
+ */
107
+ export declare function scaffoldFromSpec(spec: SetupSpec, existing: ExistingScaffoldFiles): ScaffoldResult;
108
+ /** Human preview of the scaffold plan. Deterministic. */
109
+ export declare function renderScaffoldPreview(result: ScaffoldResult): string;
110
+ /** Scan the repo for the setup plan: what exists + discovered packages + a review corpus. Impure; never throws. */
111
+ export declare function scanForSetup(repoRoot: string): SetupScan;
112
+ /** Read the existing files the scaffold needs to compare against. Impure; never throws. */
113
+ export declare function readExistingForScaffold(repoRoot: string): ExistingScaffoldFiles;
114
+ //# sourceMappingURL=feature-adr-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-adr-setup.d.ts","sourceRoot":"","sources":["../src/feature-adr-setup.ts"],"names":[],"mappings":"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"}