@volter/twin-standard 1.0.149 → 1.0.151

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +12 -3
  2. package/client-sdks/bun.lock +0 -32
  3. package/client-sdks/package.json +0 -2
  4. package/dist/client-sdks/bun.lock +0 -32
  5. package/dist/client-sdks/package.json +0 -2
  6. package/dist/src/assessment.d.ts +5 -4
  7. package/dist/src/assessment.js +2 -1
  8. package/dist/src/cli.js +21 -6
  9. package/dist/src/clients/harness.d.ts +3 -1
  10. package/dist/src/clients/harness.js +4 -2
  11. package/dist/src/clients/jira.d.ts +2 -0
  12. package/dist/src/clients/jira.js +71 -0
  13. package/dist/src/clients/polar.d.ts +2 -0
  14. package/dist/src/clients/polar.js +73 -0
  15. package/dist/src/clients/resend.js +38 -0
  16. package/dist/src/clients/run.js +1 -1
  17. package/dist/src/clients/sentry.js +1 -26
  18. package/dist/src/clients/smtp.d.ts +2 -0
  19. package/dist/src/clients/smtp.js +54 -0
  20. package/dist/src/clients/stripe.js +1 -1
  21. package/dist/src/clients/volteridentity.js +4 -3
  22. package/dist/src/clients/x.d.ts +2 -0
  23. package/dist/src/clients/x.js +64 -0
  24. package/dist/src/conformance.d.ts +4 -1
  25. package/dist/src/conformance.js +256 -6
  26. package/dist/src/consumer.js +5 -1
  27. package/dist/src/create-pack.d.ts +5 -0
  28. package/dist/src/create-pack.js +31 -3
  29. package/dist/src/protocol-3.js +30 -2
  30. package/dist/src/spec-ir.d.ts +2 -0
  31. package/dist/src/spec-ir.js +43 -0
  32. package/dist/src/tools/judge-brief.d.ts +1 -0
  33. package/dist/src/tools/judge-brief.js +67 -0
  34. package/dist/src/tools/life-coverage-walk.d.ts +2 -0
  35. package/dist/src/tools/life-coverage-walk.js +220 -0
  36. package/dist/src/tools/life-coverage.d.ts +2 -0
  37. package/dist/src/tools/life-coverage.js +271 -0
  38. package/dist/src/tools/packs-root.d.ts +9 -0
  39. package/dist/src/tools/packs-root.js +17 -0
  40. package/dist/src/tools/run-tool.d.ts +1 -0
  41. package/dist/src/tools/run-tool.js +18 -0
  42. package/dist/src/tools/score-pack.d.ts +2 -0
  43. package/dist/src/tools/score-pack.js +264 -0
  44. package/dist/src/tools/vendor-examples.d.ts +2 -0
  45. package/dist/src/tools/vendor-examples.js +1493 -0
  46. package/dist/src/verdict.js +1 -1
  47. package/dist/src/walk/behavior-journey.d.ts +6 -0
  48. package/dist/src/walk/behavior-journey.js +8 -6
  49. package/dist/src/walk/journey-kit.d.ts +2 -0
  50. package/dist/src/walk/journey-kit.js +6 -2
  51. package/dist/src/walk/rendered-scalar.d.ts +3 -0
  52. package/dist/src/walk/rendered-scalar.js +23 -0
  53. package/dist/src/walk/sse-example.js +45 -0
  54. package/package.json +2 -2
  55. package/src/assessment.ts +2 -1
  56. package/src/cli.ts +15 -2
  57. package/src/clients/harness.ts +4 -2
  58. package/src/clients/jira.ts +73 -0
  59. package/src/clients/polar.ts +73 -0
  60. package/src/clients/resend.ts +32 -0
  61. package/src/clients/run.ts +2 -2
  62. package/src/clients/sentry.ts +1 -25
  63. package/src/clients/smtp.ts +49 -0
  64. package/src/clients/stripe.ts +1 -1
  65. package/src/clients/volteridentity.ts +4 -3
  66. package/src/clients/x.ts +66 -0
  67. package/src/conformance.ts +192 -8
  68. package/src/consumer.ts +5 -1
  69. package/src/create-pack.ts +27 -3
  70. package/src/protocol-3.ts +24 -2
  71. package/src/spec-ir.ts +31 -0
  72. package/src/tools/judge-brief.ts +63 -0
  73. package/src/tools/life-coverage-walk.ts +173 -0
  74. package/src/tools/life-coverage.ts +237 -0
  75. package/src/tools/packs-root.ts +21 -0
  76. package/src/tools/run-tool.ts +10 -0
  77. package/src/tools/score-pack.ts +244 -0
  78. package/src/tools/vendor-examples.ts +1250 -0
  79. package/src/verdict.ts +1 -1
  80. package/src/walk/behavior-journey.ts +6 -2
  81. package/src/walk/journey-kit.ts +7 -2
  82. package/src/walk/rendered-scalar.ts +17 -0
  83. package/src/walk/sse-example.ts +37 -0
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env bun
2
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
3
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
4
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
5
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
6
+ });
7
+ }
8
+ return path;
9
+ };
10
+ // life-coverage — how much of a pack's hand-written code and declared state logic its customer life and its vendor's
11
+ // published examples (vendor-examples.ts) reach
12
+ // (docs/contributing/architecture.md, "Protocol 3", Coverage). Hand-written is every file under the pack's src/ on disk
13
+ // (its sharing lanes' too), but for the generated core (src/generated/) and the command line (src/cli.ts), which no
14
+ // World's fetch runs; a file of data (seed.ts, budget.ts) is in scope and has nothing to reach. State logic is every declared transition and every refusal its guard gives, read from the World's
15
+ // log and the answers. The standard is all of it: each item is reached, or recorded in journeys/unreachable.json with
16
+ // the reason no World reaches it; what is neither is missing, and a pack is done when nothing is missing.
17
+ //
18
+ // The instrument's limits, measured on Bun 1.3.10 (`bun test --coverage --coverage-reporter=lcov`): a line is counted
19
+ // by its first statement, so both sides of `a ? b : c` count once either runs; the last line of a block that never ran
20
+ // can read as run once (a function `if (x > 0) { return x; } const y = x * 7; return y - 1;` called with 5 marked
21
+ // `return y - 1` hit while `const y` read 0), so a lone unrun statement at a block's end can be counted reached; and a
22
+ // function declared below module-scope constants it follows was seen to mark every line above it unrun (aws's
23
+ // schedule.ts, until the function moved above them). A function that never runs still reads 0 on its first line, so
24
+ // an unreached function is never counted reached.
25
+ //
26
+ // Whether a recorded declaration ran in part is printed as advice, never counted: a `function`'s first line ran, or a
27
+ // body line before its last did, but measured on planetscale (life, 2026-09-26) an arrow constant read 0 on its first
28
+ // line and 140 on a property line inside it (SHOW_TABLES_FIELD, which nothing in that life calls), while arrow
29
+ // functions every UPDATE and GROUP BY runs (targetRows, computeAggregate) read 0 on their first line. The judge reads
30
+ // the advice; a record whose declaration did run in part is split, its unrun branch recorded as its own function.
31
+ //
32
+ // A walk that fails, and a file in scope (the pack's src/ on disk, its sharing lanes' too) that declares code yet
33
+ // measures no line, stop the measurement: neither is a file with nothing to reach.
34
+ //
35
+ // Usage: twin-standard coverage <vendor> [--write | --out <file>] [--prune] (from the pack repository's root; twin-world's
36
+ // bun scripts/life-coverage.ts runs the same) (--prune removes the stale records) (--write keeps journeys/coverage.json)
37
+ import { spawnSync } from 'node:child_process';
38
+ import ts from 'typescript';
39
+ import { existsSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
40
+ import { tmpdir } from 'node:os';
41
+ import { join, normalize } from 'node:path';
42
+ import { functionIndex, stateCoverage } from "../walk/journey-kit.js";
43
+ import { sharingLanesIn } from "../lanes.js";
44
+ import { packsRoot, toolPath } from "./packs-root.js";
45
+ const PACKS = packsRoot();
46
+ const sharingLanes = (v) => (v.includes('/') ? [] : sharingLanesIn(join(PACKS, v), v));
47
+ const vendor = process.argv[2];
48
+ if (!vendor || vendor.startsWith('-')) {
49
+ console.error('usage: twin-standard coverage <vendor> [--write | --out <file>] [--prune] (from the pack repository\'s root)');
50
+ process.exit(1);
51
+ }
52
+ const PKG = join(PACKS, vendor);
53
+ // An unsuccessful current walk replaces stale retained coverage with explicit failed measurement evidence.
54
+ const invalidMeasurement = (error) => {
55
+ const outAt = process.argv.includes('--out') ? process.argv[process.argv.indexOf('--out') + 1] : undefined;
56
+ if (process.argv.includes('--write') || outAt)
57
+ writeFileSync(outAt ?? join(PKG, 'journeys', 'coverage.json'), `${JSON.stringify({ vendor, measured: false, error }, null, 2)}\n`);
58
+ console.error(error);
59
+ process.exit(1);
60
+ };
61
+ // a lane of a multi-API vendor (`aws/secretsmanager`) is named with a slash
62
+ const dir = mkdtempSync(join(tmpdir(), `life-coverage-${vendor.replaceAll('/', '-')}-`));
63
+ const transitionsPath = join(dir, 'transitions.json');
64
+ // Bun measures coverage only inside `bun test`: a test file of the scratch directory's registers the walks
65
+ // (life-coverage-walk.ts), which a published package holds as a module, never as a test file of its own
66
+ const walkTest = join(dir, 'life-coverage.walk.test.ts');
67
+ writeFileSync(walkTest, `import { coverageWalks } from ${JSON.stringify(toolPath('life-coverage-walk'))};\nawait coverageWalks(${JSON.stringify(PACKS)}, ${JSON.stringify(vendor)});\n`);
68
+ const child = spawnSync(process.execPath, ['test', '--coverage', '--coverage-reporter=lcov', `--coverage-dir=${dir}`, walkTest], {
69
+ // run from the pack repository, so the pack's files lie under the walk's directory and bun measures them
70
+ cwd: PACKS, stdio: ['ignore', 'ignore', 'inherit'], env: { ...process.env, LIFE_TRANSITIONS: transitionsPath },
71
+ });
72
+ const lcovPath = join(dir, 'lcov.info');
73
+ if (!existsSync(lcovPath)) {
74
+ invalidMeasurement(`${vendor}: the walk left no coverage`);
75
+ }
76
+ // a walk that failed measured only what loaded before it failed: its coverage is no measure of the pack (a vendor of
77
+ // lanes' walk once threw before its fetch loaded, and the manifests alone read as all of its code, all of it run)
78
+ if (child.status !== 0) {
79
+ invalidMeasurement(`${vendor}: the coverage walk failed (journey instrument exited ${child.status}); its coverage measures nothing`);
80
+ }
81
+ /** Why no World reaches it (architecture, "Coverage"), each record naming one in `world`: a record
82
+ * naming none, or another, is no exemption and its code counts as missing. */
83
+ const WORLDS = new Set(['host-process', 'door', 'configured-otherwise', 'hostile-peer', 'bug-guard', 'vendor-backed', 'sealed-refusal']);
84
+ const ruled = (u) => u.world !== undefined && WORLDS.has(u.world);
85
+ const unreachablePath = join(PKG, 'journeys', 'unreachable.json');
86
+ const unreachable = existsSync(unreachablePath) ? JSON.parse(readFileSync(unreachablePath, 'utf8')) : {};
87
+ const recordedCode = (file, fn) => (unreachable.code ?? []).some((u) => ruled(u) && u.file === file && u.declarations.includes(fn ?? '(module scope)'));
88
+ const recordedState = (item) => (unreachable.state ?? []).some((u) => ruled(u) && u.item === item);
89
+ // ── code ─────────────────────────────────────────────────────────────────────────────────────
90
+ const lcov = readFileSync(lcovPath, 'utf8');
91
+ const prefix = `${vendor}/src/`;
92
+ // the lanes over this vendor's state that its serve factory serves ("Pack layout"): their code and states are this
93
+ // life's to reach, measured here beside the vendor's own
94
+ const sharedLanes = sharingLanes(vendor);
95
+ const lanePrefixes = sharedLanes.map((l) => `${vendor}/${l}/src/`);
96
+ const inScope = (f) => [prefix, ...lanePrefixes].find((p) => f.startsWith(p) && !f.startsWith(`${p}generated/`));
97
+ // the scope is the pack's hand-written files ON DISK (its src/ and its sharing lanes'), never the files the walk happened to
98
+ // load: a screen not wired into fetch.ts or a family missing from the handler map is in scope and measures nothing
99
+ const onDisk = (rel) => {
100
+ const at = join(PACKS, rel);
101
+ if (!existsSync(at))
102
+ return [];
103
+ return readdirSync(at, { withFileTypes: true }).flatMap((e) => (e.isDirectory() ? onDisk(`${rel}${e.name}/`) : /\.tsx?$/.test(e.name) ? [`${rel}${e.name}`] : []));
104
+ };
105
+ const handWritten = [prefix, ...lanePrefixes].flatMap(onDisk)
106
+ .filter((f) => inScope(f) !== undefined && !/\.(test|uitest)\.tsx?$/.test(f) && !/\.d\.ts$/.test(f))
107
+ // the command line a person runs the twin with (a unit's src/cli.ts) is code no World's fetch runs; everything else is
108
+ // in scope, a unit's budget.ts (D8's data) and seed.ts (data) passing as data below
109
+ .filter((f) => !/^[^/]+(?:\/[^/]+)?\/src\/cli\.ts$/.test(f));
110
+ const readSource = (file) => (existsSync(join(PACKS, file)) ? readFileSync(join(PACKS, file), 'utf8') : undefined);
111
+ const measuredFiles = stateCoverage(lcov, handWritten, readSource);
112
+ // a file in scope that measures no line is not dropped: one that declares code and reads 0 lines found was not loaded by
113
+ // the walk or not measured, an error of the measurement, never a file with nothing to reach. Data is not code: a module
114
+ // of only imports, re-exports, types and constants whose values are data (literals, with no function, call, `new`,
115
+ // accessor or tagged template in them, as the grade holds a manifest's data: twin-standard's manifest-data) has nothing a
116
+ // walk could run, loaded or not: `semantics/seed.ts`'s `seed: SeedCall[]`, which `volter-world init` copies into a World
117
+ // and sends as requests, never a module the twin's fetch loads. A function, a class, an enum or any other statement is
118
+ // code, so no handler hides as data.
119
+ const isCode = (n) => ts.isCallExpression(n) || ts.isNewExpression(n) || ts.isArrowFunction(n) || ts.isFunctionExpression(n)
120
+ || ts.isClassExpression(n) || ts.isTaggedTemplateExpression(n) || ts.isGetAccessor(n) || ts.isSetAccessor(n) || ts.isMethodDeclaration(n)
121
+ || ts.forEachChild(n, isCode) === true;
122
+ const declaresCode = (file, text) => ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS).statements.some((st) => {
123
+ if (ts.isImportDeclaration(st) || ts.isImportEqualsDeclaration(st) || ts.isTypeAliasDeclaration(st) || ts.isInterfaceDeclaration(st))
124
+ return false;
125
+ if (ts.isExportDeclaration(st))
126
+ return false;
127
+ // an ambient declaration (`declare const x: T`) is a type, nothing that runs
128
+ if (ts.canHaveModifiers(st) && ts.getModifiers(st)?.some((m) => m.kind === ts.SyntaxKind.DeclareKeyword))
129
+ return false;
130
+ // `export default <data>` is data as a constant's value is
131
+ if (ts.isExportAssignment(st))
132
+ return isCode(st.expression);
133
+ if (ts.isVariableStatement(st))
134
+ return st.declarationList.declarations.some((d) => !d.initializer || isCode(d.initializer));
135
+ return true;
136
+ });
137
+ const unmeasured = measuredFiles.filter((c) => c.lines.found === 0 && declaresCode(c.file, readSource(c.file) ?? ''));
138
+ if (unmeasured.length) {
139
+ invalidMeasurement(`${vendor}: ${unmeasured.length} file(s) in the pack's scope declare code and measure no line (the walk did not load them, or bun did not measure them): ${unmeasured.map((c) => c.file).join(', ')}`);
140
+ }
141
+ const files = measuredFiles.filter((c) => c.lines.found > 0).map((c) => {
142
+ // a lane's file is named from the vendor's src/ (`../fapi/src/semantics/client.ts`), as unreachable.json records it
143
+ const file = c.file.startsWith(prefix) ? c.file.slice(prefix.length) : `../${c.file.slice(`${vendor}/`.length)}`;
144
+ const recorded = c.never.filter((n) => recordedCode(file, n.fn));
145
+ const missing = c.never.filter((n) => !recordedCode(file, n.fn));
146
+ const size = (ns) => ns.reduce((s, n) => s + n.to - n.from + 1, 0);
147
+ return { file, lines: c.lines, recorded: size(recorded), missing, missingLines: size(missing) };
148
+ });
149
+ const total = files.reduce((t, c) => ({ found: t.found + c.lines.found, hit: t.hit + c.lines.hit, recorded: t.recorded + c.recorded, missing: t.missing + c.missingLines }), { found: 0, hit: 0, recorded: 0, missing: 0 });
150
+ // ── state logic ──────────────────────────────────────────────────────────────────────────────
151
+ const { manifest } = (await import(__rewriteRelativeImportExtension(join(PKG, 'src', 'manifest.ts'))));
152
+ const laneManifests = await Promise.all(sharedLanes.map(async (l) => (await import(__rewriteRelativeImportExtension(join(PKG, l, 'src', 'manifest.ts')))).manifest));
153
+ const observed = existsSync(transitionsPath) ? JSON.parse(readFileSync(transitionsPath, 'utf8')) : { seen: [], undeclared: [] };
154
+ const seen = new Set(observed.seen);
155
+ const was = (at, outcome, from) => [...seen].some((s) => s.startsWith(`${at}|${outcome}|`) && (from === undefined || s.endsWith(`|${from}`)));
156
+ const state = [];
157
+ for (const [resource, decl] of [manifest, ...laneManifests].flatMap((mf) => Object.entries(mf.resources ?? {}))) {
158
+ for (const [field, m] of Object.entries(decl.state ?? {})) {
159
+ // transitions of one operation from the same states refuse as one rule (a confirm that lands on one of
160
+ // four outcomes refuses with the first of its four identical refusals), so their refusal is one item
161
+ const refusalOf = new Map();
162
+ m.transitions.forEach((t, i) => {
163
+ const at = `${resource}.${field}#${i}`;
164
+ const name = `${resource}.${field}: ${t.operation ?? t.actor ?? 'api'} ${t.from === '*' ? '*' : t.from.join('|')} → ${t.to ?? '(stays)'}`;
165
+ if (!(t.to === undefined && t.refusal !== undefined))
166
+ state.push({ item: `move ${name}`, reached: was(at, 'moved') });
167
+ const refusals = [...(t.refusal ? [['', t.refusal]] : []), ...Object.entries(t.refusals ?? {})];
168
+ for (const [from, r] of refusals) {
169
+ const key = JSON.stringify([t.operation ?? t.actor, t.from, from, r]);
170
+ const known = refusalOf.get(key);
171
+ const reached = from ? was(at, 'refused', from) : was(at, 'refused');
172
+ if (known) {
173
+ known.reached ||= reached;
174
+ continue;
175
+ }
176
+ const item = { item: `refusal ${name}${from ? ` from ${from}` : ''}`, reached };
177
+ refusalOf.set(key, item);
178
+ state.push(item);
179
+ }
180
+ });
181
+ }
182
+ }
183
+ const stateMissing = state.filter((s) => !s.reached && !recordedState(s.item)).map((s) => s.item);
184
+ // ── stale records ────────────────────────────────────────────────────────────────────────────
185
+ // a record is a claim that no World reaches something: one that names a declaration, a file or a state item the pack
186
+ // no longer has, or one the walks now reach, is false, and counts as missing until it is corrected
187
+ const stale = [];
188
+ /** Records whose declaration may have run in part: to read, and split where it did (architecture.md, Coverage). */
189
+ const partlyRun = [];
190
+ const keptCode = [];
191
+ for (const u of unreachable.code ?? []) {
192
+ const keep = [];
193
+ const source = readSource(`${prefix}${u.file}`);
194
+ const names = new Set(source === undefined ? [] : functionIndex(source).map((f) => f.name));
195
+ const measured = files.find((f) => f.file === u.file);
196
+ const coverage = stateCoverage(lcov, [normalize(`${prefix}${u.file}`)], readSource)[0];
197
+ const unrun = new Set([...(measured?.missing ?? []), ...(coverage?.never ?? [])].map((n) => n.fn ?? '(module scope)'));
198
+ // a record is by declaration: one part of whose body ran is not unreachable, its unrun branch is (architecture.md,
199
+ // Coverage: "the branch moves into its own function first")
200
+ const partly = new Set(coverage?.ran ?? []);
201
+ for (const d of u.declarations) {
202
+ if (source === undefined)
203
+ stale.push(`${u.file}: no such file`);
204
+ else if (d !== '(module scope)' && !names.has(d))
205
+ stale.push(`${u.file}: no declaration ${d}`);
206
+ else if (measured && !unrun.has(d))
207
+ stale.push(`${u.file}: ${d} is reached`);
208
+ else {
209
+ // advice, not a count: bun's line hits cannot always tell a declaration that ran in part (see the header)
210
+ if (measured && partly.has(d))
211
+ partlyRun.push(`${u.file}: ${d}`);
212
+ keep.push(d);
213
+ }
214
+ }
215
+ if (keep.length)
216
+ keptCode.push({ ...u, declarations: keep });
217
+ }
218
+ const keptState = [];
219
+ for (const u of unreachable.state ?? []) {
220
+ if (!state.some((s) => s.item === u.item))
221
+ stale.push(`state: no item ${u.item}`);
222
+ else if (state.some((s) => s.item === u.item && s.reached))
223
+ stale.push(`state: ${u.item} is reached`);
224
+ else
225
+ keptState.push(u);
226
+ }
227
+ // `--prune` removes exactly the false records: a record removed wrongly shows again as missing, so pruning hides nothing
228
+ if (stale.length && process.argv.includes('--prune')) {
229
+ writeFileSync(unreachablePath, `${JSON.stringify({ ...unreachable, code: keptCode, state: keptState }, null, 2)}\n`);
230
+ console.log(`${vendor}: pruned ${stale.length} stale records from journeys/unreachable.json`);
231
+ stale.length = 0;
232
+ }
233
+ const stateRecorded = state.filter((s) => !s.reached && recordedState(s.item)).length;
234
+ // ── the report ───────────────────────────────────────────────────────────────────────────────
235
+ const pct = (hit, found) => (found ? `${((hit / found) * 100).toFixed(1)}%` : 'n/a');
236
+ const missing = (child.status === 0 ? 0 : 1) + total.missing + stateMissing.length + observed.undeclared.length + stale.length;
237
+ console.log(`${vendor}: ${missing === 0 ? 'meets the standard' : `${missing} missing`}${child.status === 0 ? '' : ' (the walk did not hold)'}`);
238
+ // a record whose reason is none the standard names exempts nothing: say which, so the author reaches it or names its World
239
+ const unruled = [...(unreachable.code ?? []).map((u) => `${u.file} ${u.declarations.join(', ')}`), ...(unreachable.state ?? []).map((u) => u.item)]
240
+ .filter((_, i) => !ruled([...(unreachable.code ?? []), ...(unreachable.state ?? [])][i]));
241
+ if (unruled.length)
242
+ console.log(` ${unruled.length} record(s) name no World of the standard (world: ${[...WORLDS].join(' | ')}), so they exempt nothing: ${unruled.slice(0, 6).join('; ')}${unruled.length > 6 ? ' …' : ''}`);
243
+ console.log(` code: the life and the vendor's examples run ${pct(total.hit, total.found)} of ${total.found} hand-written lines; ${total.recorded} recorded unreachable; ${total.missing} missing`);
244
+ console.log(` state: ${state.filter((s) => s.reached).length}/${state.length} declared moves and refusals reached; ${stateRecorded} recorded unreachable; ${stateMissing.length} missing; ${observed.undeclared.length} moves no declared transition allows`);
245
+ for (const u of observed.undeclared)
246
+ console.log(` undeclared: ${u}`);
247
+ if (stale.length)
248
+ console.log(` records: ${stale.length} stale`);
249
+ for (const r of stale)
250
+ console.log(` stale ${r}`);
251
+ for (const s of stateMissing)
252
+ console.log(` missing ${s}`);
253
+ if (partlyRun.length)
254
+ console.log(` advice: ${partlyRun.length} recorded declaration(s) may have run in part (read each; split where it did):\n${partlyRun.map((p) => ` ${p}`).join('\n')}`);
255
+ for (const f of files.filter((c) => c.missingLines).sort((a, b) => b.missingLines - a.missingLines)) {
256
+ const byFn = new Map();
257
+ for (const n of f.missing)
258
+ byFn.set(n.fn ?? '(module scope)', (byFn.get(n.fn ?? '(module scope)') ?? 0) + n.to - n.from + 1);
259
+ console.log(` ${f.file} (${f.missingLines} lines): ${[...byFn].sort((a, b) => b[1] - a[1]).map(([fn, n]) => `${fn} ${n}`).join(', ')}`);
260
+ }
261
+ // `--write` keeps journeys/coverage.json; `--out <file>` writes the same report elsewhere (score-pack's own measurement,
262
+ // which leaves the checkout as it found it)
263
+ const outAt = process.argv.includes('--out') ? process.argv[process.argv.indexOf('--out') + 1] : undefined;
264
+ if (process.argv.includes('--write') || outAt) {
265
+ writeFileSync(outAt ?? join(PKG, 'journeys', 'coverage.json'), `${JSON.stringify({
266
+ vendor, missing, stale,
267
+ code: { lines: total.found, run: total.hit, recorded: total.recorded, missing: total.missing },
268
+ state: { declared: state.length, reached: state.filter((s) => s.reached).length, recorded: stateRecorded, missing: stateMissing, undeclared: observed.undeclared },
269
+ files: files.map((f) => ({ file: f.file, lines: f.lines, missing: f.missing.map((n) => ({ from: n.from, to: n.to, fn: n.fn })) })),
270
+ }, null, 2)}\n`);
271
+ }
@@ -0,0 +1,9 @@
1
+ /** The pack repository the tools read (`<root>/<vendor>/`). */
2
+ export declare const packsRoot: () => string;
3
+ export declare function setPacksRoot(dir: string): void;
4
+ /** The checkout whose kernel a walk measures, when one is required (twin-world's own scripts); a contributor's walk
5
+ * measures whichever kernel its repository resolves, and says which. */
6
+ export declare const kernelCheckout: () => string | undefined;
7
+ export declare function setKernelCheckout(dir: string): void;
8
+ /** A tool of this directory, by name, in the form this module was loaded as (source `.ts`, or the published `.js`). */
9
+ export declare const toolPath: (name: string) => string;
@@ -0,0 +1,17 @@
1
+ // Where the contributor's tools (walk, examples, coverage, judge-brief) find the pack they are given: a vendor's directory
2
+ // under the pack repository they run from. A contributor runs them from their own repository's root (`twin-standard walk
3
+ // <vendor>`), so that root is the working directory; twin-world's scripts, which find Volter's pack repository beside
4
+ // their checkout, name it before loading a tool (setPacksRoot), and the kernel checkout a walk must measure
5
+ // (setKernelCheckout). Each tool reads both once, when it loads.
6
+ import { join } from 'node:path';
7
+ let packs = process.cwd();
8
+ let kernel;
9
+ /** The pack repository the tools read (`<root>/<vendor>/`). */
10
+ export const packsRoot = () => packs;
11
+ export function setPacksRoot(dir) { packs = dir; }
12
+ /** The checkout whose kernel a walk measures, when one is required (twin-world's own scripts); a contributor's walk
13
+ * measures whichever kernel its repository resolves, and says which. */
14
+ export const kernelCheckout = () => kernel;
15
+ export function setKernelCheckout(dir) { kernel = dir; }
16
+ /** A tool of this directory, by name, in the form this module was loaded as (source `.ts`, or the published `.js`). */
17
+ export const toolPath = (name) => join(import.meta.dir, `${name}${import.meta.path.endsWith('.js') ? '.js' : '.ts'}`);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ // Runs one contributor tool in a process of its own, as the tool that started it names the pack repository and the
10
+ // kernel checkout (packs-root.ts): bun run-tool.ts <packs root> <kernel checkout or ''> <tool path> <tool args…>
11
+ import { setKernelCheckout, setPacksRoot } from "./packs-root.js";
12
+ const [packs, checkout, tool, ...args] = process.argv.slice(2);
13
+ setPacksRoot(packs);
14
+ if (checkout)
15
+ setKernelCheckout(checkout);
16
+ // a tool is a script over process.argv: its first argument is its own
17
+ process.argv = [process.argv[0], tool, ...args];
18
+ await import(__rewriteRelativeImportExtension(tool));
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export {};
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env bun
2
+ // score-pack — a Protocol 3 pack's report (docs/contributing/architecture.md, "Protocol 3", Scoring). One customer
3
+ // life per pack is walked through the pack's own fetch on the in-memory World, and the report says:
4
+ // - the life: whether a judge's plausible verdict names its current content (a verdict its author wrote is not one);
5
+ // - correctness: every check on every step (status, expectations, captures, SHAPE, the beats) and the life
6
+ // answering byte for byte the same on a second fresh World;
7
+ // - coverage: the hand-written code the life and the vendor's published examples run and the declared state logic
8
+ // they reach (life-coverage.ts), with what they never reach;
9
+ // - the vendor's published examples: replayed and judged (vendor-examples.ts);
10
+ // - breadth: the spec's operations (a line protocol's commands) by what serves them (a handler, the generated
11
+ // core, a legacy fallback, nothing).
12
+ // This is source-journey evidence, not installed readiness or pack completion. pack-done owns completion and consumes
13
+ // this report alongside the exact artifact's installed customer assessment in the one final run.
14
+ //
15
+ // Usage: twin-standard walk <vendor> [--write] [--report <path>] (from the pack repository's root; twin-world's
16
+ // bun scripts/score-pack.ts <vendor> runs the same)
17
+ // --write keeps journeys/score.json and journeys/coverage.json; --report writes the report and what it lacks for done
18
+ // to <path> as JSON and changes nothing in the checkout
19
+ import { parseExactJson } from '@volter/world-core/exact-json';
20
+ import { notIndependent } from "../verdict.js";
21
+ import { spawnSync } from 'node:child_process';
22
+ import { existsSync, mkdtempSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
23
+ import { createRequire } from 'node:module';
24
+ import { tmpdir } from 'node:os';
25
+ import { dirname, join, sep } from 'node:path';
26
+ import { inMemoryWorld, walk, withPinnedWorldDraws, usePacks } from "../walk/behavior-journey.js";
27
+ import { expandFills, waitMs } from "../walk/journey-kit.js";
28
+ import { vendorFetch, vendorStream } from "../walk/vendor-fetch.js";
29
+ import { existenceProbe } from "../walk/journey-kit.js";
30
+ import { kernelCheckout, packsRoot, toolPath } from "./packs-root.js";
31
+ const PACKS = packsRoot();
32
+ usePacks(PACKS);
33
+ const vendor = process.argv[2];
34
+ if (!vendor || vendor.startsWith('-')) {
35
+ console.error('usage: twin-standard walk <vendor> [--write] [--report <path>] (from the pack repository\'s root)');
36
+ process.exit(1);
37
+ }
38
+ // The surface measured: the kernel the pack repository resolves, named in the report. twin-world's own scripts require
39
+ // it to be their checkout's (linked in development, decision 0029): a pack repository whose node_modules resolves
40
+ // @volter/* into another checkout scores the pack against someone else's kernel, no measurement of this one.
41
+ const kernel = (() => {
42
+ try {
43
+ return realpathSync(join(PACKS, 'node_modules', '@volter', 'world-core'));
44
+ }
45
+ catch { /* resolved through a parent's node_modules */ }
46
+ try {
47
+ let at = dirname(realpathSync(createRequire(join(PACKS, 'package.json')).resolve('@volter/world-core')));
48
+ while (!existsSync(join(at, 'package.json')) && dirname(at) !== at)
49
+ at = dirname(at);
50
+ return at;
51
+ }
52
+ catch {
53
+ return '(unresolved)';
54
+ }
55
+ })();
56
+ const checkout = kernelCheckout();
57
+ if (checkout && !kernel.startsWith(realpathSync(checkout) + sep)) { // the platform's separator: '/' never prefixes a Windows path
58
+ console.error(`score-pack: @volter/world-core resolves to ${kernel}, outside this checkout (${realpathSync(checkout)}); link the pack repository's node_modules/@volter/* to this checkout's packages before scoring`);
59
+ process.exit(2);
60
+ }
61
+ const PKG = join(PACKS, vendor);
62
+ const lifePath = join(PKG, 'journeys', 'customer-life.json');
63
+ if (!existsSync(lifePath)) {
64
+ console.error(`${vendor}: no journeys/customer-life.json`);
65
+ process.exit(1);
66
+ }
67
+ // ── the life and its verdict ─────────────────────────────────────────────────────────────────
68
+ const text = readFileSync(lifePath, 'utf8');
69
+ // its fills written out once, so the steps the walk hands back are the life's own (a sitting is found by position)
70
+ const life = expandFills(parseExactJson(text));
71
+ const verdictPath = join(PKG, 'journeys', 'customer-life.verdict.json');
72
+ const verdict = existsSync(verdictPath) ? parseExactJson(readFileSync(verdictPath, 'utf8')) : undefined;
73
+ // A verdict is a judge's in another session: one its author wrote (marked self-judged) rules nothing, however it reads.
74
+ // The dispatcher stamps the judge’s author name (`judge.agent`) and every author of the life
75
+ // (`authors`) consistently when it commits the verdict; a verdict without them,
76
+ // or whose judge is among the authors, is not independent.
77
+ // one judging per pack: the verdict stands, and the life's changes since (its rulings applied, a review's fixes, steps for
78
+ // coverage) keep it (architecture, "Writing a life", Judge)
79
+ const dependent = verdict ? notIndependent(verdict, life.author) : undefined;
80
+ const judged = life.steps.length === 0 || !verdict ? 'unjudged' : dependent ? dependent : verdict.plausible ? 'plausible' : 'rejected';
81
+ // ── correctness ──────────────────────────────────────────────────────────────────────────────
82
+ const world = inMemoryWorld(`score-${vendor}`);
83
+ const failures = [];
84
+ await walk(life, world.root(), failures, { beats: true });
85
+ const answers = async () => {
86
+ const seen = [];
87
+ // the World's own draws (its seed, its keys) are the same in both: two Worlds differ only by what is beyond them (R9)
88
+ await withPinnedWorldDraws(() => walk(life, world.root(), [], { beats: false, hooks: { after: (step, p, a) => seen.push(`${step.name ?? ''}\u0000${p} ${a.status} ${JSON.stringify(a.body)}`) } }));
89
+ return seen;
90
+ };
91
+ const [first, second] = [await answers(), await answers()];
92
+ const flags = [];
93
+ // CRED: a life where steps act with different credentials carries no life-wide default one; a default leaks one actor's
94
+ // credential into every step that forgot its own (a judge found octocat's token on a server's call).
95
+ {
96
+ const auth = (h) => Object.entries(h ?? {}).find(([k]) => k.toLowerCase() === 'authorization')?.[1];
97
+ const own = new Set(life.steps.flatMap((st) => { const a = auth(st.headers); return a ? [a] : []; }));
98
+ const fallback = auth(life.headers);
99
+ if (fallback && own.size > 0) {
100
+ const leaning = life.steps.filter((st) => 'method' in st && !auth(st.headers));
101
+ if (leaning.length)
102
+ flags.push({ step: leaning[0].name, pattern: 'CRED', detail: `${leaning.length} steps act with the life-wide default credential in a life of ${own.size + 1} credentials; give each its actor's own` });
103
+ }
104
+ }
105
+ {
106
+ const walked = [];
107
+ const refusedAnswer = (a) => {
108
+ const b = a.body;
109
+ return a.status >= 400 || (!!b && typeof b === 'object' && (b.ok === false || (Array.isArray(b.errors) && b.errors.length > 0 && b.data === undefined) || typeof b.error === 'string'));
110
+ };
111
+ // CRED too: a credential a person was shown once (a key on a dashboard, a token in a mail) is read off what showed it
112
+ // (a capture), never typed in: a literal equal to what an earlier answer showed means the life knows how the twin mints it
113
+ const shown = new Set();
114
+ let literalFlags = 0;
115
+ await walk(life, inMemoryWorld(`flags-${vendor}`).root(), [], { beats: false, hooks: { after: (step, path, answer) => {
116
+ const s = step;
117
+ for (const [k, v] of Object.entries(s.headers ?? {})) {
118
+ if (!/^(authorization|x-api-key|api-key|cookie)$/i.test(k) || v.includes('{'))
119
+ continue;
120
+ const secret = v.replace(/^(bearer|token|basic)\s+/i, '').replace(/^[a-z_]+=/i, '');
121
+ if (secret.length >= 16 && shown.has(secret) && literalFlags++ < 3)
122
+ flags.push({ step: s.name, pattern: 'CRED', detail: `sends as a literal the credential an earlier answer showed (${secret.slice(0, 12)}…): capture it where it was shown` });
123
+ }
124
+ const text = typeof answer.body === 'string' ? answer.body : JSON.stringify(answer.body ?? '');
125
+ // a token ends before trailing punctuation (a key closing a sentence); a JWT's inner dots stay
126
+ for (const t of text.match(/[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*/g) ?? [])
127
+ if (t.length >= 16)
128
+ shown.add(t);
129
+ const method = s.method ?? (s.mysql !== undefined || s.signIn ? `MySQL ${s.link ?? 'mysql'}` : s.connection ? `RESP ${s.connection}` : s.socket ? `socket ${s.open ? 'open' : s.close ? 'close' : 'send'}` : s.git ? `git ${s.git}${s.delete ? ' delete' : ''}` : `line ${s.on}`);
130
+ const body = JSON.stringify(s.mysql !== undefined || s.signIn ? { mysql: s.mysql ?? null, values: s.values ?? null, signIn: s.signIn?.user ?? null } : s.resp ?? s.body ?? s.send ?? (s.ref ? { ref: s.ref } : null));
131
+ // an operation named in a header (AWS's X-Amz-Target) is part of what the request is; who sends it is not
132
+ const named = Object.entries(s.headers ?? {}).filter(([k]) => !/^(authorization|stripe-account|x-slack-|cookie)/i.test(k)).sort().map(([k, v]) => `${k}=${v}`).join('&');
133
+ // and so is the region an AWS request is signed for (its credential scope), which says where it goes
134
+ const region = /Credential=[^/]+\/\d{8}\/([a-z0-9-]+)\//.exec((s.headers ?? {}).authorization ?? (life.headers ?? {}).authorization ?? '')?.[1];
135
+ const where = named || region ? `${path} [${[named, region ? `region=${region}` : ''].filter(Boolean).join('&')}]` : path;
136
+ walked.push({ index: life.steps.indexOf(step), name: s.name, key: `${method} ${where} ${body}`, path: where, method, body, refused: s.socket ? (answer.status !== 101 && answer.status !== 1000) : refusedAnswer(answer), status: answer.status });
137
+ } } });
138
+ const sitting = (i) => life.steps.slice(0, i).filter((x) => 'wait' in x).length;
139
+ // a client asking whether something exists before it makes it is not a refusal (existenceProbe, journey-kit)
140
+ const probe = (n) => existenceProbe(walked.map((w) => ({ ...w, sitting: sitting(w.index) })), n);
141
+ let run = [];
142
+ const endRun = () => { if (run.length >= 3)
143
+ flags.push({ step: run[0].name, pattern: 'RUN', detail: `${run.length} refusals in a row, from "${run[0].name.slice(0, 50)}" to "${run.at(-1).name.slice(0, 50)}"` }); run = []; };
144
+ walked.forEach((w, n) => {
145
+ if (w.refused && !probe(n))
146
+ run.push(w);
147
+ else if (!w.refused)
148
+ endRun();
149
+ const earlier = walked.slice(0, n).filter((e) => sitting(e.index) === sitting(w.index));
150
+ if (w.refused && earlier.some((e) => !e.refused && e.key === w.key))
151
+ flags.push({ step: w.name, pattern: 'REPEAT', detail: `repeats a request that succeeded earlier in the sitting (${w.method} ${w.path.slice(0, 60)})` });
152
+ if (!w.refused && /^(POST|PUT|PATCH)$/.test(w.method)) {
153
+ const writes = earlier.filter((e) => !e.refused && e.method === w.method && e.path === w.path);
154
+ if (writes.length >= 2 && writes.at(-2).body === w.body && writes.at(-1).body !== w.body)
155
+ flags.push({ step: w.name, pattern: 'TOGGLE', detail: `writes ${w.path.slice(0, 60)} back to what it was two writes before, in one sitting` });
156
+ }
157
+ if (!w.refused && w.method === 'DELETE' && earlier.some((e) => !e.refused && (e.method === 'PUT' || e.method === 'POST') && e.path === w.path))
158
+ flags.push({ step: w.name, pattern: 'TOGGLE', detail: `removes ${w.path.slice(0, 60)}, made earlier in the same sitting` });
159
+ });
160
+ endRun();
161
+ // a time of day a step names that its hour on the World clock contradicts (the clock starts at startsAt or the default and
162
+ // moves only by `wait` steps; a wait's name tells what happens once it has passed)
163
+ const hours = [[/\bmorning\b/i, 5, 12], [/\blunch\b/i, 11, 15], [/\bafternoon\b/i, 12, 18], [/\bevening\b/i, 17, 23]];
164
+ const WEEKDAYS = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
165
+ const MONTHS = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];
166
+ const DATED = /\b(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),? (\d{1,2}) (January|February|March|April|May|June|July|August|September|October|November|December)(?:,? (\d{4}))?/g;
167
+ let clock = Date.parse(life.startsAt ?? '2026-01-01T00:00:00Z');
168
+ for (const step of life.steps) {
169
+ if ('wait' in step)
170
+ clock += waitMs(step.wait);
171
+ const hour = new Date(clock).getUTCHours();
172
+ for (const [word, from, to] of hours) {
173
+ if (word.test(step.name) && (hour < from || hour >= to))
174
+ flags.push({ step: step.name, pattern: 'TIME', detail: `says ${word.source.replace(/\\b/g, '')} at ${String(hour).padStart(2, '0')}:00 on the World clock` });
175
+ }
176
+ // a weekday named with its date is a fact of the calendar, and a wait's name says the date the clock reaches
177
+ for (const m of step.name.matchAll(DATED)) {
178
+ const [, weekday, day, month, year] = m;
179
+ const at = new Date(Date.UTC(year ? Number(year) : new Date(clock).getUTCFullYear(), MONTHS.indexOf(month.toLowerCase()), Number(day)));
180
+ if (WEEKDAYS[at.getUTCDay()] !== weekday.toLowerCase())
181
+ flags.push({ step: step.name, pattern: 'TIME', detail: `says ${weekday} ${day} ${month}, which is a ${WEEKDAYS[at.getUTCDay()]}` });
182
+ else if ('wait' in step && at.toISOString().slice(0, 10) !== new Date(clock).toISOString().slice(0, 10))
183
+ flags.push({ step: step.name, pattern: 'TIME', detail: `its wait reaches ${new Date(clock).toISOString().slice(0, 10)}, not ${weekday} ${day} ${month}` });
184
+ }
185
+ }
186
+ }
187
+ const differs = first.findIndex((line, i) => line !== second[i]);
188
+ if (differs >= 0 || first.length !== second.length)
189
+ failures.push({ step: '(whole life)', beat: 'REPLAYABLE', detail: `a second fresh World answered differently at "${(first[differs] ?? second[differs] ?? first.at(-1) ?? '').split('\u0000')[0]}" (the ${differs + 1}th answered step; waits are not counted)` });
190
+ // ── breadth ──────────────────────────────────────────────────────────────────────────────────
191
+ const factory = await vendorFetch(PACKS, vendor);
192
+ const fetch = factory?.(world.root());
193
+ // a line protocol's commands are served on its stream
194
+ const stream = (await vendorStream(PACKS, vendor))?.(world.root());
195
+ const owners = fetch?.owners?.() ?? stream?.owners?.() ?? {};
196
+ world.restore();
197
+ const breadth = {};
198
+ for (const o of Object.values(owners))
199
+ breadth[o] = (breadth[o] ?? 0) + 1;
200
+ const operations = Object.keys(owners).length;
201
+ // ── coverage ─────────────────────────────────────────────────────────────────────────────────
202
+ // the measurement is kept (journeys/coverage.json) only with --write; a plain run leaves the checkout as it found it
203
+ const keep = process.argv.includes('--write');
204
+ const coveragePath = keep ? join(PKG, 'journeys', 'coverage.json') : join(mkdtempSync(join(tmpdir(), 'score-coverage-')), 'coverage.json');
205
+ // each tool in a process of its own, run from the pack repository, naming it as this one does
206
+ const tool = (name, args) => spawnSync(process.execPath, [toolPath('run-tool'), PACKS, checkout ?? '', toolPath(name), ...args], { cwd: PACKS, stdio: ['ignore', 'pipe', 'pipe'] });
207
+ const measured = tool('life-coverage', [vendor, ...(keep ? ['--write'] : ['--out', coveragePath])]);
208
+ // a measurement that failed reports so, never the last one it kept
209
+ const coverage = measured.status === 0 && existsSync(coveragePath) ? parseExactJson(readFileSync(coveragePath, 'utf8')) : undefined;
210
+ // ── the vendor's published examples (vendor-examples.ts) ────────────────────────────────────────
211
+ const replay = tool('vendor-examples', [vendor]);
212
+ const replayOut = String(replay.stdout).trim().split('\n');
213
+ const replayLine = replayOut.at(-1) ?? '';
214
+ // replayed and judged are two lacks: the replay is the twin's, the verdict a judge's (pack-done's (d) and (f))
215
+ const examples = {
216
+ done: replay.status === 0,
217
+ replayed: replayOut.some((l) => l.trim() === 'replayed: yes'),
218
+ verdict: replayOut.find((l) => l.trim().startsWith('verdict: '))?.trim().slice('verdict: '.length) ?? 'not measured',
219
+ failures: replayOut.filter((line) => /^\s*(?:FAIL|GAP)\s/.test(line)).map((line) => line.trim()),
220
+ line: replayLine.replace(/^\S+\s+/, ''),
221
+ };
222
+ const pct = (hit, found) => (found ? `${((hit / found) * 100).toFixed(1)}%` : 'n/a');
223
+ const score = {
224
+ scope: 'source-customer-life-and-published-examples',
225
+ vendor,
226
+ life: { steps: life.steps.length, verdict: judged, judge: verdict?.judge?.agent ?? verdict?.judge?.name ?? null, authors: verdict?.authors ?? [] },
227
+ failures: failures.map((f) => ({ step: f.step, beat: f.beat, detail: f.detail })),
228
+ flags,
229
+ coverage: coverage ?? null,
230
+ examples,
231
+ breadth: { operations, ...breadth },
232
+ };
233
+ console.log(` measured through: ${kernel}`);
234
+ console.log(`${vendor}: life of ${life.steps.length} steps, ${judged}; ${failures.length} checks failing`);
235
+ if (!coverage)
236
+ console.log(` coverage: NOT MEASURED — life-coverage failed: ${String(measured.stderr).trim().split('\n').slice(-3).join(' ')}`);
237
+ if (coverage)
238
+ console.log(` coverage: ${coverage.missing === 0 ? 'meets the standard' : `${coverage.missing} missing`} — hand-written lines ${pct(coverage.code.run, coverage.code.lines)} run, ${coverage.code.recorded} recorded unreachable, ${coverage.code.missing} missing; state logic ${coverage.state.reached}/${coverage.state.declared} reached, ${coverage.state.recorded} recorded, ${coverage.state.missing.length} missing, ${coverage.state.undeclared.length} undeclared moves`);
239
+ console.log(` ${examples.line || 'examples: not measured'}`);
240
+ console.log(` breadth: ${operations} spec operations — ${Object.entries(breadth).map(([k, n]) => `${n} ${k}`).join(', ')}`);
241
+ for (const f of failures.slice(0, 20))
242
+ console.log(` FAIL ${f.beat} ${f.step.slice(0, 60)}: ${f.detail.slice(0, 140)}`);
243
+ console.log(` judge flags: ${flags.length ? `${flags.length} (${['REPEAT', 'RUN', 'TOGGLE', 'TIME', 'CRED'].map((p) => `${flags.filter((f) => f.pattern === p).length} ${p}`).join(', ')}) — what judges reject on sight; resolve each or give it a cause the story shows` : 'none'}`);
244
+ for (const f of flags.slice(0, 30))
245
+ console.log(` FLAG ${f.pattern} ${f.step.slice(0, 60)}: ${f.detail}`);
246
+ // Source requirements used by pack-done's report step; keep the field name for existing report consumers.
247
+ const missingForDone = [
248
+ judged !== 'plausible' ? `a plausible verdict from another session (now: ${judged})` : '',
249
+ failures.length ? `${failures.length} checks failing` : '',
250
+ flags.length ? `${flags.length} judge flags` : '',
251
+ !coverage ? 'coverage not measured' : coverage.missing ? `${coverage.missing} coverage items missing` : '',
252
+ breadth.legacy ? `${breadth.legacy} operations on a legacy fallback` : '',
253
+ examples.replayed ? '' : "the vendor's published examples not all replayed",
254
+ examples.verdict === 'plausible' ? '' : `a plausible verdict on the vendor's published examples (now: ${examples.verdict})`,
255
+ ].filter(Boolean);
256
+ console.log(` source journey: ${missingForDone.length ? `incomplete — ${missingForDone.join('; ')}` : 'meets the source requirements'}`);
257
+ console.log(' pack completion: not assessed — pack-done consumes the exact installed assessment in the final run');
258
+ const reportAt = process.argv.indexOf('--report');
259
+ if (reportAt >= 0 && process.argv[reportAt + 1]) {
260
+ writeFileSync(process.argv[reportAt + 1], `${JSON.stringify({ ...score, missingForDone }, null, 2)}\n`);
261
+ }
262
+ if (process.argv.includes('--write')) {
263
+ writeFileSync(join(PKG, 'journeys', 'score.json'), `${JSON.stringify(score, null, 2)}\n`);
264
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export {};