instar 1.3.625 → 1.3.627

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.
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/scaffold/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAgG/D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CA2E/F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiBvD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA2B1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,EACpB,WAAW,GAAE,OAAe,EAC5B,WAAW,GAAE,OAAe,GAC3B,MAAM,CA2hDR;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,EACpB,WAAW,GAAE,OAAe,GAC3B,MAAM,CA2TR"}
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/scaffold/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAgG/D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CA2E/F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiBvD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA2B1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,EACpB,WAAW,GAAE,OAAe,EAC5B,WAAW,GAAE,OAAe,GAC3B,MAAM,CA4hDR;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,EACpB,WAAW,GAAE,OAAe,GAC3B,MAAM,CA2TR"}
@@ -13,7 +13,7 @@
13
13
  // existing-agent migration there) so the two can never drift. Imported as a runtime
14
14
  // function call inside generateClaudeMd — no module-init cycle (PostUpdateMigrator
15
15
  // never imports templates).
16
- import { PLAYWRIGHT_PROFILE_REGISTRY_CLAUDEMD_SECTION } from '../core/PostUpdateMigrator.js';
16
+ import { PLAYWRIGHT_PROFILE_REGISTRY_CLAUDEMD_SECTION, MACHINE_LOAD_ASSESSMENT_CLAUDEMD_SECTION } from '../core/PostUpdateMigrator.js';
17
17
  /**
18
18
  * Generate AGENT.md — the agent's identity file.
19
19
  */
@@ -807,6 +807,7 @@ I declare owner/blockedOn at commitment creation; a later state change goes thro
807
807
  - **The autonomous balancer surface** — \`GET /credentials/rebalancer\` (the use-it-or-lose-it drainer is Increment B; this surfaces the env-token applicability gate's verdict + WHY re-pointing would refuse, when enabled).
808
808
  - **When to use** (PROACTIVE — these are the triggers): "flip my default account to X" / "make X my default" → \`POST /credentials/set-default\`; "which account is this session/slot on?" / "where does ~/.claude point?" → \`GET /credentials/locations\` (read it, don't infer from \`claude auth status\` — that reads a metadata file, not the live credential). Single-account agents are a no-op. (Spec: \`docs/specs/live-credential-repointing-rebalancer.md\`.)
809
809
  ${PLAYWRIGHT_PROFILE_REGISTRY_CLAUDEMD_SECTION(port)}
810
+ ${MACHINE_LOAD_ASSESSMENT_CLAUDEMD_SECTION()}
810
811
  **Per-Feature LLM Metrics & LLM Activity (Observable Intelligence)** — Audit what each of your LLM-driven gates/sentinels actually does: WHICH provider + model ran it, how often it ACTED (fired) vs found nothing (noop), how often it was skipped to save rate limits (shed), cost, and latency. This is the *Observable Intelligence* standard — no autonomous AI action the system takes is allowed to be invisible. Read-only observability — it never gates anything.
811
812
  - Check: \`curl -H "Authorization: Bearer $AUTH" "http://localhost:${port}/metrics/features?sinceHours=24"\`
812
813
  - Returns \`{ totals, features: [{ feature, frameworks, models, byModel, calls, realCalls, tokensIn, tokensOut, tokensCached, fired, noop, shed, fireRate, p50LatencyMs, p95LatencyMs, ... }] }\` — one row per system (e.g. MessagingToneGate, MessageSentinel). \`frameworks\`/\`models\` = which provider(s) actually served the call; \`fireRate\` = how often it acts; \`shed\` = skipped by the rate-limit guard. Filter with \`?feature=<name>\`.
@@ -1 +1 @@
1
- {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/scaffold/templates.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/scaffold/templates.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.625",
3
+ "version": "1.3.627",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,13 +28,15 @@
28
28
  "test:contract": "node scripts/run-contract-tests.js",
29
29
  "test:contract:raw": "vitest run --config vitest.contract.config.ts",
30
30
  "test:all": "vitest run && vitest run --config vitest.integration.config.ts && vitest run --config vitest.e2e.config.ts",
31
- "lint": "tsc --noEmit && node scripts/lint-no-direct-destructive.js && node scripts/lint-no-direct-llm-http.js && node scripts/lint-no-direct-url-log.js && node scripts/lint-no-unfunneled-topic-creation.js && node scripts/lint-no-unfunneled-headless-launch.js && node scripts/lint-no-unfunneled-credential-write.js && node scripts/lint-state-registry.js && node scripts/lint-cas-emit-placement.js && node scripts/lint-journal-actuation-ban.js && node scripts/lint-no-blocking-process-scans.js && node scripts/lint-dev-agent-dark-gate.js && node scripts/lint-guard-manifest.js && node scripts/lint-llm-attribution.js && node scripts/lint-no-mainthread-cartographer-walk.js && node scripts/check-codex-rule1-drift.js",
31
+ "lint": "tsc --noEmit && node scripts/lint-no-direct-destructive.js && node scripts/lint-no-direct-llm-http.js && node scripts/lint-no-direct-url-log.js && node scripts/lint-no-unfunneled-topic-creation.js && node scripts/lint-no-unfunneled-headless-launch.js && node scripts/lint-no-unfunneled-credential-write.js && node scripts/lint-state-registry.js && node scripts/lint-cas-emit-placement.js && node scripts/lint-journal-actuation-ban.js && node scripts/lint-no-blocking-process-scans.js && node scripts/lint-dev-agent-dark-gate.js && node scripts/lint-guard-manifest.js && node scripts/lint-llm-attribution.js && node scripts/lint-no-mainthread-cartographer-walk.js && node scripts/lint-scrape-fixture-realness.js && node scripts/check-codex-rule1-drift.js",
32
32
  "lint:destructive": "node scripts/lint-no-direct-destructive.js",
33
33
  "lint:dev-agent-dark-gate": "node scripts/lint-dev-agent-dark-gate.js",
34
34
  "lint:guard-manifest": "node scripts/lint-guard-manifest.js",
35
35
  "lint:dev-agent-dark-gate:staged": "node scripts/lint-dev-agent-dark-gate.js --staged",
36
36
  "lint:destructive:staged": "node scripts/lint-no-direct-destructive.js --staged",
37
37
  "lint:llm-http": "node scripts/lint-no-direct-llm-http.js",
38
+ "lint:scrape-realness": "node scripts/lint-scrape-fixture-realness.js",
39
+ "lint:scrape-realness:staged": "node scripts/lint-scrape-fixture-realness.js --staged",
38
40
  "lint:llm-attribution": "node scripts/lint-llm-attribution.js",
39
41
  "lint:llm-attribution:staged": "node scripts/lint-llm-attribution.js --staged",
40
42
  "lint:llm-http:staged": "node scripts/lint-no-direct-llm-http.js --staged",
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint-scrape-fixture-realness.js — enforces that every REGISTERED scrape/parser
4
+ * has a test that FEEDS it a structurally-real captured fixture and asserts on
5
+ * the result (not a hand-authored clean string).
6
+ *
7
+ * Implements piece 3 of docs/specs/scrape-fixture-realness.md (the "code=t"
8
+ * lesson, structurally enforced). A parser of untrusted real-world text is only
9
+ * as good as the realness of its test input. The check is precise by
10
+ * construction — a CURATED registry (SCRAPE_PARSERS), not a heuristic over all
11
+ * tests — so false-positives are near-zero (same posture as
12
+ * lint-no-direct-llm-http / lint-dev-agent-dark-gate). Adding/removing a registry
13
+ * entry requires a spec change.
14
+ *
15
+ * For each registered parser the lint verifies (spec §3):
16
+ * (a) tests/fixtures/captured/<slug>/ has >=1 `.txt`, each with a valid matching
17
+ * `.meta.json` (required fields present + capturedAt parses as ISO date).
18
+ * (b) testFile contains a test whose name === testName, and within that test's
19
+ * body ALL of: a loadCapturedFixture('<slug>', ...) call, the parserSymbol
20
+ * called with the loaded var as an argument (member-expression accepted),
21
+ * and an expect( call.
22
+ *
23
+ * It ALSO scans src/ for exported parse-prefixed / scrape-prefixed symbols not covered by the
24
+ * registry and prints a non-blocking WARNING ("register-or-justify") — a
25
+ * Close-the-Loop signal, never a block (Signal vs. Authority).
26
+ *
27
+ * Exit codes:
28
+ * 0 — every registered entry conforms (warnings do NOT affect exit code).
29
+ * 1 — at least one registered entry fails a sub-check.
30
+ *
31
+ * Usage:
32
+ * node scripts/lint-scrape-fixture-realness.js # full check
33
+ * node scripts/lint-scrape-fixture-realness.js --staged # staged files (still runs full registry check)
34
+ * node scripts/lint-scrape-fixture-realness.js path1 path2 # explicit paths (still runs full registry check)
35
+ */
36
+
37
+ import fs from 'node:fs';
38
+ import path from 'node:path';
39
+ import { fileURLToPath } from 'node:url';
40
+
41
+ const __filename = fileURLToPath(import.meta.url);
42
+ const __dirname = path.dirname(__filename);
43
+ const ROOT = path.resolve(__dirname, '..');
44
+
45
+ /**
46
+ * The curated registry of parsers whose realness is enforced. Each entry:
47
+ * { parserSymbol, fixtureSlug, testFile, testName }
48
+ *
49
+ * Adding/removing an entry requires a spec change (FD1/FD3). The seed is the one
50
+ * parser we KNOW consumes untrusted terminal text and bit us (the code=t bug).
51
+ */
52
+ export const SCRAPE_PARSERS = [
53
+ {
54
+ parserSymbol: 'FrameworkLoginDriver.parseArtifact',
55
+ fixtureSlug: 'claude-url-code-paste',
56
+ testFile: 'tests/unit/framework-login-driver.test.ts',
57
+ testName: 'parses the REAL wrapped Mac Mini login pane',
58
+ },
59
+ ];
60
+
61
+ const REQUIRED_META_FIELDS = ['source', 'command', 'capturedAt', 'machine', 'redactions', 'note'];
62
+
63
+ /**
64
+ * (a) — verify the captured-fixture directory + sidecars for a slug.
65
+ * @returns {string[]} list of failure messages (empty = ok)
66
+ */
67
+ export function checkFixtures(root, fixtureSlug) {
68
+ const failures = [];
69
+ const dir = path.join(root, 'tests', 'fixtures', 'captured', fixtureSlug);
70
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
71
+ failures.push(`fixture dir missing: tests/fixtures/captured/${fixtureSlug}/`);
72
+ return failures;
73
+ }
74
+ const entries = fs.readdirSync(dir);
75
+ const txts = entries.filter((f) => f.endsWith('.txt'));
76
+ if (txts.length === 0) {
77
+ failures.push(`no .txt captures in tests/fixtures/captured/${fixtureSlug}/`);
78
+ return failures;
79
+ }
80
+ for (const txt of txts) {
81
+ const base = txt.slice(0, -'.txt'.length);
82
+ const sidecar = `${base}.meta.json`;
83
+ const sidecarPath = path.join(dir, sidecar);
84
+ if (!fs.existsSync(sidecarPath)) {
85
+ failures.push(`missing sidecar ${fixtureSlug}/${sidecar} for capture ${fixtureSlug}/${txt}`);
86
+ continue;
87
+ }
88
+ let meta;
89
+ try {
90
+ meta = JSON.parse(fs.readFileSync(sidecarPath, 'utf-8'));
91
+ } catch (err) {
92
+ failures.push(`sidecar ${fixtureSlug}/${sidecar} is not valid JSON: ${err.message}`);
93
+ continue;
94
+ }
95
+ for (const field of REQUIRED_META_FIELDS) {
96
+ if (!(field in meta)) {
97
+ failures.push(`sidecar ${fixtureSlug}/${sidecar} missing required field "${field}"`);
98
+ }
99
+ }
100
+ if (meta.capturedAt !== undefined) {
101
+ const t = Date.parse(meta.capturedAt);
102
+ if (Number.isNaN(t)) {
103
+ failures.push(`sidecar ${fixtureSlug}/${sidecar} capturedAt "${meta.capturedAt}" is not a parseable ISO-8601 date`);
104
+ }
105
+ }
106
+ }
107
+ return failures;
108
+ }
109
+
110
+ /**
111
+ * Extract the body text of an `it('<name>', ...)` / `test('<name>', ...)` block
112
+ * by matching the opening and walking braces to the matching close. Returns null
113
+ * if no test with that exact name is found.
114
+ */
115
+ export function extractTestBody(source, testName) {
116
+ // Find an it/test call whose first string-literal arg === testName.
117
+ // Match the literal (single, double, or backtick quotes) followed by a comma.
118
+ const escaped = testName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
119
+ const opener = new RegExp(`\\b(?:it|test)\\s*\\(\\s*(['"\`])${escaped}\\1\\s*,`, 'g');
120
+ const m = opener.exec(source);
121
+ if (!m) return null;
122
+ // Walk forward from the end of the matched opener to find the body's braces.
123
+ // The test body is the arrow/function passed as the 2nd arg; find its first
124
+ // `{` after the opener and brace-match to its close.
125
+ let i = opener.lastIndex;
126
+ // Skip ahead to the first `{` that starts the callback body.
127
+ const braceStart = source.indexOf('{', i);
128
+ if (braceStart < 0) return null;
129
+ let depth = 0;
130
+ for (let j = braceStart; j < source.length; j++) {
131
+ const ch = source[j];
132
+ if (ch === '{') depth++;
133
+ else if (ch === '}') {
134
+ depth--;
135
+ if (depth === 0) {
136
+ return source.slice(braceStart + 1, j);
137
+ }
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+
143
+ /**
144
+ * (b) — verify the registered test exists and feeds-and-asserts.
145
+ * @returns {string[]} list of failure messages (empty = ok)
146
+ */
147
+ export function checkTest(root, entry) {
148
+ const failures = [];
149
+ const testPath = path.join(root, entry.testFile);
150
+ if (!fs.existsSync(testPath)) {
151
+ failures.push(`testFile missing: ${entry.testFile}`);
152
+ return failures;
153
+ }
154
+ const source = fs.readFileSync(testPath, 'utf-8');
155
+ const body = extractTestBody(source, entry.testName);
156
+ if (body === null) {
157
+ failures.push(`no test named "${entry.testName}" found in ${entry.testFile}`);
158
+ return failures;
159
+ }
160
+
161
+ // The loader call, capturing the variable it assigns to.
162
+ // const <v> = loadCapturedFixture('<slug>', ...)
163
+ const slugEsc = entry.fixtureSlug.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
164
+ const loaderRe = new RegExp(
165
+ `(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*loadCapturedFixture\\s*\\(\\s*['"\`]${slugEsc}['"\`]`,
166
+ );
167
+ const loaderMatch = body.match(loaderRe);
168
+ if (!loaderMatch) {
169
+ failures.push(
170
+ `test "${entry.testName}" must call loadCapturedFixture('${entry.fixtureSlug}', …) and assign the result to a variable`,
171
+ );
172
+ }
173
+ const loadedVar = loaderMatch ? loaderMatch[1] : null;
174
+
175
+ // The parser call, fed the loaded variable as an argument.
176
+ // Accept member-expression (Cls.method) or bare/aliased call.
177
+ // parserSymbol may be "Cls.method" — escape dots literally.
178
+ const parserEsc = entry.parserSymbol.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
179
+ let parserFed = false;
180
+ if (loadedVar) {
181
+ const varEsc = loadedVar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
182
+ // <parserSymbol>( ... <loadedVar> ... ) — loadedVar appears as an arg.
183
+ const parserRe = new RegExp(`${parserEsc}\\s*\\(\\s*${varEsc}\\b`);
184
+ parserFed = parserRe.test(body);
185
+ }
186
+ if (!parserFed) {
187
+ failures.push(
188
+ `test "${entry.testName}" must pass the loaded fixture variable as the first argument to ${entry.parserSymbol}(…)`,
189
+ );
190
+ }
191
+
192
+ // At least one expect( on the result.
193
+ if (!/\bexpect\s*\(/.test(body)) {
194
+ failures.push(`test "${entry.testName}" must assert on the parse result with expect(…)`);
195
+ }
196
+
197
+ return failures;
198
+ }
199
+
200
+ /** Recursively collect `.ts` files under a dir. */
201
+ function walkTs(dir, out, skip) {
202
+ let entries;
203
+ try {
204
+ entries = fs.readdirSync(dir, { withFileTypes: true });
205
+ } catch {
206
+ return;
207
+ }
208
+ for (const e of entries) {
209
+ if (skip.has(e.name)) continue;
210
+ const full = path.join(dir, e.name);
211
+ if (e.isDirectory()) walkTs(full, out, skip);
212
+ else if (e.name.endsWith('.ts') && !e.name.endsWith('.d.ts')) out.push(full);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Close-the-Loop signal: scan src/ for exported parse-prefixed / scrape-prefixed symbols not in
218
+ * the registry. Non-blocking — returns a warning list only.
219
+ * @returns {Array<{symbol:string,file:string}>}
220
+ */
221
+ export function findUnregisteredParsers(root) {
222
+ const skip = new Set(['node_modules', 'dist', 'build', '.instar', '.git', '.next', 'coverage']);
223
+ const files = [];
224
+ const srcDir = path.join(root, 'src');
225
+ if (fs.existsSync(srcDir)) walkTs(srcDir, files, skip);
226
+
227
+ // The bare method names already covered by the registry (e.g. "parseArtifact").
228
+ const registeredMethodNames = new Set(
229
+ SCRAPE_PARSERS.map((e) => {
230
+ const parts = e.parserSymbol.split('.');
231
+ return parts[parts.length - 1];
232
+ }),
233
+ );
234
+
235
+ const re = /export\s+(?:async\s+)?function\s+((?:parse|scrape)[A-Z]\w*)|export\s+const\s+((?:parse|scrape)[A-Z]\w*)/g;
236
+ const found = [];
237
+ const seen = new Set();
238
+ for (const file of files) {
239
+ let text;
240
+ try {
241
+ text = fs.readFileSync(file, 'utf-8');
242
+ } catch {
243
+ continue;
244
+ }
245
+ let m;
246
+ re.lastIndex = 0;
247
+ while ((m = re.exec(text)) !== null) {
248
+ const symbol = m[1] || m[2];
249
+ if (!symbol) continue;
250
+ if (registeredMethodNames.has(symbol)) continue;
251
+ const rel = path.relative(root, file).split(path.sep).join('/');
252
+ const key = `${rel}:${symbol}`;
253
+ if (seen.has(key)) continue;
254
+ seen.add(key);
255
+ found.push({ symbol, file: rel });
256
+ }
257
+ }
258
+ return found;
259
+ }
260
+
261
+ /**
262
+ * Run the full lint over the registry. Pure-ish (reads fs, returns a result).
263
+ * @returns {{ exitCode:number, errors:string[], warnings:string[], passed:Array }}
264
+ */
265
+ export function runLint(root = ROOT) {
266
+ const errors = [];
267
+ const passed = [];
268
+ for (const entry of SCRAPE_PARSERS) {
269
+ const failures = [...checkFixtures(root, entry.fixtureSlug), ...checkTest(root, entry)];
270
+ if (failures.length > 0) {
271
+ errors.push(
272
+ `Registered parser "${entry.parserSymbol}" (${entry.testFile}) failed realness check:\n` +
273
+ failures.map((f) => ` - ${f}`).join('\n'),
274
+ );
275
+ } else {
276
+ passed.push(entry);
277
+ }
278
+ }
279
+
280
+ const unregistered = findUnregisteredParsers(root);
281
+ const warnings = [];
282
+ if (unregistered.length > 0) {
283
+ warnings.push(
284
+ `register-or-justify: ${unregistered.length} exported parse*/scrape* symbol(s) in src/ are not in SCRAPE_PARSERS.\n` +
285
+ ` If they consume untrusted real-world text, register them (spec change) or justify out-of-scope in the PR:\n` +
286
+ unregistered.slice(0, 20).map((u) => ` • ${u.symbol} (${u.file})`).join('\n') +
287
+ (unregistered.length > 20 ? `\n • ...and ${unregistered.length - 20} more` : ''),
288
+ );
289
+ }
290
+
291
+ return { exitCode: errors.length > 0 ? 1 : 0, errors, warnings, passed };
292
+ }
293
+
294
+ function main() {
295
+ // --staged / explicit paths are accepted for parity with sibling lints, but
296
+ // the realness check is over the curated registry, so it always runs the full
297
+ // registry check regardless of which files changed.
298
+ const { exitCode, errors, warnings, passed } = runLint(ROOT);
299
+
300
+ if (errors.length > 0) {
301
+ console.error('lint-scrape-fixture-realness: registered parser(s) failed realness enforcement.\n');
302
+ console.error('A registered scrape/parser must have a test that FEEDS it a structurally-real captured');
303
+ console.error('fixture and asserts on the result. See docs/specs/scrape-fixture-realness.md.\n');
304
+ for (const e of errors) console.error(` ❌ ${e}`);
305
+ console.error('');
306
+ } else {
307
+ for (const e of passed) {
308
+ console.log(` ✅ ${e.parserSymbol} — realness fixture + feeds-and-asserts test OK (${e.testName})`);
309
+ }
310
+ }
311
+
312
+ if (warnings.length > 0) {
313
+ for (const w of warnings) console.warn(` ⚠️ ${w}`);
314
+ console.warn('');
315
+ }
316
+
317
+ process.exit(exitCode);
318
+ }
319
+
320
+ const isDirectRun = (() => {
321
+ try {
322
+ return process.argv[1] && path.resolve(process.argv[1]) === __filename;
323
+ } catch {
324
+ return false;
325
+ }
326
+ })();
327
+ if (isDirectRun) main();
@@ -428,6 +428,12 @@ try {
428
428
  warnings.push(`lint-no-direct-llm-http failed to run: ${err.message}`);
429
429
  }
430
430
 
431
+ // (The scrape-fixture-realness lint is enforced via the `npm run lint` chain in
432
+ // package.json, which CI runs — not duplicated here. A direct gate invocation
433
+ // would run against this gate's resolved ROOT, which is a scratch dir under the
434
+ // gate's own unit tests, where the registered fixtures don't exist; the chain in
435
+ // `npm run lint` runs in the real repo and is the authoritative enforcement.)
436
+
431
437
  // ── 6. URL.pathname filesystem guard ──────────────────────────────────
432
438
  // new URL(..., import.meta.url).pathname preserves %20-encoded spaces,
433
439
  // breaking filesystem operations. Use __dirname (via fileURLToPath) instead.
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * redact-captured-fixture.mjs — same-SHAPE, grammar-valid secret redaction for
4
+ * captured parser fixtures.
5
+ *
6
+ * Captured fixtures (tests/fixtures/captured/<slug>/) preserve every STRUCTURAL
7
+ * byte that matters to parsing (wrapping/ANSI/spacing/line-breaks) — those are
8
+ * the realness and are NEVER tidied. But a real capture carries secrets (OAuth
9
+ * client_id/state, tokens, usernames, paths). Committing them raw is a leak, so
10
+ * each secret VALUE is replaced with a same-SHAPE placeholder:
11
+ *
12
+ * - SAME LENGTH (so line positions + wrapping stay byte-identical),
13
+ * - SAME CHARACTER CLASS (hex stays hex, base64url stays base64url, …),
14
+ * - delimiters preserved AT THEIR POSITIONS (a UUID keeps its `-` offsets),
15
+ * - GRAMMAR-VALID (a redacted URL still parses; encoding form intact).
16
+ *
17
+ * A redaction that would change length is REJECTED — a length change shifts the
18
+ * wrapping and silently produces a passing-but-fake fixture.
19
+ *
20
+ * See docs/specs/scrape-fixture-realness.md (FD2) and
21
+ * tests/fixtures/captured/README.md.
22
+ *
23
+ * Usage (CLI):
24
+ * node scripts/redact-captured-fixture.mjs <raw.txt> \
25
+ * --redact '<find>:<class>' [--redact '<find>:<class>' ...] [-o out.txt]
26
+ *
27
+ * <class> ∈ hex | uuid | base64url | alnum | token
28
+ *
29
+ * Programmatic:
30
+ * import { redactCapture } from './redact-captured-fixture.mjs';
31
+ * const { redacted, redactions } = redactCapture(raw, [{ find, class }]);
32
+ */
33
+
34
+ import fs from 'node:fs';
35
+ import path from 'node:path';
36
+ import { fileURLToPath } from 'node:url';
37
+
38
+ // The supported redaction classes. Adding a class requires a spec change
39
+ // (it widens the same-shape contract).
40
+ export const REDACTION_CLASSES = new Set(['hex', 'uuid', 'base64url', 'alnum', 'token']);
41
+
42
+ // Per-class placeholder fill characters. The fill is a single canonical char of
43
+ // the class so the result is obviously inert (not a plausible real secret),
44
+ // while staying in the class so the parser's grammar is preserved.
45
+ const CLASS_FILL = {
46
+ hex: 'a', // [0-9a-f]
47
+ uuid: 'a', // hex within UUID segments; hyphens are preserved positionally
48
+ base64url: 'z', // [A-Za-z0-9_-]
49
+ alnum: 'x', // [A-Za-z0-9]
50
+ token: 'x', // generic opaque token — treated like alnum for the fill
51
+ };
52
+
53
+ // Characters that are DELIMITERS within a value of a given class — preserved at
54
+ // their exact positions so the value's internal structure (and thus length +
55
+ // grammar) is unchanged. Everything else is replaced with the class fill char.
56
+ const CLASS_DELIMITERS = {
57
+ hex: new Set([]),
58
+ uuid: new Set(['-']),
59
+ base64url: new Set(['-', '_']),
60
+ alnum: new Set([]),
61
+ token: new Set(['-', '_', '.']),
62
+ };
63
+
64
+ /**
65
+ * Build a same-length, same-class, grammar-valid placeholder for a secret value.
66
+ * Delimiter characters for the class are preserved at their positions.
67
+ */
68
+ function placeholderFor(value, cls) {
69
+ const fill = CLASS_FILL[cls];
70
+ const delims = CLASS_DELIMITERS[cls];
71
+ let out = '';
72
+ for (const ch of value) {
73
+ out += delims.has(ch) ? ch : fill;
74
+ }
75
+ return out;
76
+ }
77
+
78
+ /**
79
+ * Redact secrets from a raw capture, producing a same-shape result.
80
+ *
81
+ * @param {string} raw the raw captured text (structural bytes preserved)
82
+ * @param {Array<{find:string, class:string}>} specs secrets to redact
83
+ * @returns {{ redacted: string, redactions: Array<{what:string,strategy:string,length:number,class:string}> }}
84
+ * @throws if a class is unknown, a `find` is missing from `raw`, or a
85
+ * placeholder would change length (it never should — this is a guard).
86
+ */
87
+ export function redactCapture(raw, specs) {
88
+ if (typeof raw !== 'string') throw new Error('redactCapture: raw must be a string');
89
+ if (!Array.isArray(specs)) throw new Error('redactCapture: specs must be an array');
90
+
91
+ let redacted = raw;
92
+ const redactions = [];
93
+
94
+ for (const spec of specs) {
95
+ const { find } = spec;
96
+ const cls = spec.class;
97
+ if (typeof find !== 'string' || find.length === 0) {
98
+ throw new Error('redactCapture: each spec needs a non-empty `find` string');
99
+ }
100
+ if (!REDACTION_CLASSES.has(cls)) {
101
+ throw new Error(
102
+ `redactCapture: unknown class "${cls}" for find "${find}" — must be one of ${[...REDACTION_CLASSES].join(', ')}`,
103
+ );
104
+ }
105
+ if (!redacted.includes(find)) {
106
+ throw new Error(`redactCapture: secret to redact not found in capture: "${find}"`);
107
+ }
108
+
109
+ const placeholder = placeholderFor(find, cls);
110
+ // Length-preservation guard: the whole point is byte-identical wrapping.
111
+ if (placeholder.length !== find.length) {
112
+ throw new Error(
113
+ `redactCapture: placeholder length ${placeholder.length} != original length ${find.length} ` +
114
+ `for class "${cls}" — a length-changing redaction shifts wrapping and is rejected.`,
115
+ );
116
+ }
117
+
118
+ redacted = redacted.split(find).join(placeholder);
119
+ redactions.push({
120
+ what: find,
121
+ strategy: `same-length ${find.length} ${cls}-class placeholder, delimiters preserved at positions`,
122
+ length: find.length,
123
+ class: cls,
124
+ });
125
+ }
126
+
127
+ return { redacted, redactions };
128
+ }
129
+
130
+ // ── thin CLI wrapper ──────────────────────────────────────────────────
131
+ function parseArgs(argv) {
132
+ const specs = [];
133
+ let input = null;
134
+ let out = null;
135
+ for (let i = 0; i < argv.length; i++) {
136
+ const a = argv[i];
137
+ if (a === '--redact') {
138
+ const v = argv[++i];
139
+ const idx = v.lastIndexOf(':');
140
+ if (idx < 0) throw new Error(`--redact expects '<find>:<class>', got "${v}"`);
141
+ specs.push({ find: v.slice(0, idx), class: v.slice(idx + 1) });
142
+ } else if (a === '-o' || a === '--out') {
143
+ out = argv[++i];
144
+ } else if (!input) {
145
+ input = a;
146
+ }
147
+ }
148
+ return { input, out, specs };
149
+ }
150
+
151
+ function main() {
152
+ const argv = process.argv.slice(2);
153
+ if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {
154
+ console.log(
155
+ 'Usage: node scripts/redact-captured-fixture.mjs <raw.txt> --redact \'<find>:<class>\' [...] [-o out.txt]\n' +
156
+ ` <class> ∈ ${[...REDACTION_CLASSES].join(' | ')}`,
157
+ );
158
+ process.exit(argv.length === 0 ? 1 : 0);
159
+ }
160
+ const { input, out, specs } = parseArgs(argv);
161
+ const raw = fs.readFileSync(input, 'utf-8');
162
+ const { redacted, redactions } = redactCapture(raw, specs);
163
+ if (out) {
164
+ fs.writeFileSync(out, redacted);
165
+ console.error(`Wrote redacted capture to ${out}`);
166
+ } else {
167
+ process.stdout.write(redacted);
168
+ }
169
+ console.error(`Redactions (${redactions.length}):`);
170
+ for (const r of redactions) {
171
+ console.error(` - ${r.class} (len ${r.length}): ${r.strategy}`);
172
+ }
173
+ }
174
+
175
+ const isDirectRun = (() => {
176
+ try {
177
+ return process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
178
+ } catch {
179
+ return false;
180
+ }
181
+ })();
182
+ if (isDirectRun) main();