@gate-forge/core 0.7.0 → 0.8.0

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 (141) hide show
  1. package/dist/classifier/bind.d.ts +2 -0
  2. package/dist/classifier/bind.d.ts.map +1 -1
  3. package/dist/classifier/bind.js +8 -0
  4. package/dist/classifier/bind.js.map +1 -1
  5. package/dist/classifier/classify.d.ts +2 -0
  6. package/dist/classifier/classify.d.ts.map +1 -1
  7. package/dist/classifier/classify.js +127 -2
  8. package/dist/classifier/classify.js.map +1 -1
  9. package/dist/classifier/index.d.ts +2 -2
  10. package/dist/classifier/index.d.ts.map +1 -1
  11. package/dist/classifier/index.js +1 -1
  12. package/dist/classifier/index.js.map +1 -1
  13. package/dist/classifier/schema.d.ts +54 -0
  14. package/dist/classifier/schema.d.ts.map +1 -1
  15. package/dist/classifier/schema.js +17 -0
  16. package/dist/classifier/schema.js.map +1 -1
  17. package/dist/config/index.d.ts +173 -0
  18. package/dist/config/index.d.ts.map +1 -1
  19. package/dist/config/index.js +296 -1
  20. package/dist/config/index.js.map +1 -1
  21. package/dist/graph/schema.d.ts +34 -0
  22. package/dist/graph/schema.d.ts.map +1 -1
  23. package/dist/index.d.ts +80 -10
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +58 -6
  26. package/dist/index.js.map +1 -1
  27. package/dist/mapping/resolve.d.ts +22 -1
  28. package/dist/mapping/resolve.d.ts.map +1 -1
  29. package/dist/mapping/resolve.js +71 -25
  30. package/dist/mapping/resolve.js.map +1 -1
  31. package/dist/policy/evaluate.d.ts +30 -0
  32. package/dist/policy/evaluate.d.ts.map +1 -1
  33. package/dist/policy/evaluate.js +22 -12
  34. package/dist/policy/evaluate.js.map +1 -1
  35. package/dist/quarantine/index.d.ts +82 -0
  36. package/dist/quarantine/index.d.ts.map +1 -0
  37. package/dist/quarantine/index.js +194 -0
  38. package/dist/quarantine/index.js.map +1 -0
  39. package/dist/receipt/index.d.ts +65 -1
  40. package/dist/receipt/index.d.ts.map +1 -1
  41. package/dist/receipt/index.js +115 -1
  42. package/dist/receipt/index.js.map +1 -1
  43. package/dist/report/human-message.d.ts +33 -0
  44. package/dist/report/human-message.d.ts.map +1 -0
  45. package/dist/report/human-message.js +33 -0
  46. package/dist/report/human-message.js.map +1 -0
  47. package/dist/report/index.d.ts +183 -9
  48. package/dist/report/index.d.ts.map +1 -1
  49. package/dist/report/index.js +296 -22
  50. package/dist/report/index.js.map +1 -1
  51. package/dist/schemas/adoption.d.ts +2 -0
  52. package/dist/schemas/adoption.d.ts.map +1 -1
  53. package/dist/schemas/adoption.js +6 -0
  54. package/dist/schemas/adoption.js.map +1 -1
  55. package/dist/schemas/behavior-catalog.d.ts +12 -0
  56. package/dist/schemas/behavior-catalog.d.ts.map +1 -1
  57. package/dist/schemas/behavior-evidence.d.ts +40 -0
  58. package/dist/schemas/behavior-evidence.d.ts.map +1 -1
  59. package/dist/schemas/behavior-evidence.js +9 -0
  60. package/dist/schemas/behavior-evidence.js.map +1 -1
  61. package/dist/schemas/behavior-policy.d.ts +26 -0
  62. package/dist/schemas/behavior-policy.d.ts.map +1 -1
  63. package/dist/schemas/behavior-policy.js +37 -1
  64. package/dist/schemas/behavior-policy.js.map +1 -1
  65. package/dist/schemas/execution-result.d.ts +17 -5
  66. package/dist/schemas/execution-result.d.ts.map +1 -1
  67. package/dist/schemas/execution-result.js +57 -2
  68. package/dist/schemas/execution-result.js.map +1 -1
  69. package/dist/schemas/gate-receipt.d.ts +25 -2
  70. package/dist/schemas/gate-receipt.d.ts.map +1 -1
  71. package/dist/schemas/gate-receipt.js +181 -2
  72. package/dist/schemas/gate-receipt.js.map +1 -1
  73. package/dist/schemas/index.d.ts +2 -0
  74. package/dist/schemas/index.d.ts.map +1 -1
  75. package/dist/schemas/index.js +2 -0
  76. package/dist/schemas/index.js.map +1 -1
  77. package/dist/schemas/policy.d.ts +19 -0
  78. package/dist/schemas/policy.d.ts.map +1 -1
  79. package/dist/schemas/policy.js +36 -0
  80. package/dist/schemas/policy.js.map +1 -1
  81. package/dist/schemas/quarantine.d.ts +25 -0
  82. package/dist/schemas/quarantine.d.ts.map +1 -0
  83. package/dist/schemas/quarantine.js +34 -0
  84. package/dist/schemas/quarantine.js.map +1 -0
  85. package/dist/schemas/queue-observer.d.ts +195 -0
  86. package/dist/schemas/queue-observer.d.ts.map +1 -0
  87. package/dist/schemas/queue-observer.js +190 -0
  88. package/dist/schemas/queue-observer.js.map +1 -0
  89. package/dist/schemas/run-record.d.ts +57 -0
  90. package/dist/schemas/run-record.d.ts.map +1 -0
  91. package/dist/schemas/run-record.js +108 -0
  92. package/dist/schemas/run-record.js.map +1 -0
  93. package/dist/schemas/runtime-config.d.ts +67 -0
  94. package/dist/schemas/runtime-config.d.ts.map +1 -1
  95. package/dist/schemas/runtime-config.js +75 -1
  96. package/dist/schemas/runtime-config.js.map +1 -1
  97. package/dist/schemas/signature-profile.d.ts +99 -0
  98. package/dist/schemas/signature-profile.d.ts.map +1 -0
  99. package/dist/schemas/signature-profile.js +197 -0
  100. package/dist/schemas/signature-profile.js.map +1 -0
  101. package/dist/schemas/test-map.d.ts +4 -0
  102. package/dist/schemas/test-map.d.ts.map +1 -1
  103. package/dist/schemas/test-map.js +12 -0
  104. package/dist/schemas/test-map.js.map +1 -1
  105. package/dist/schemas/verdict.d.ts +19 -4
  106. package/dist/schemas/verdict.d.ts.map +1 -1
  107. package/dist/schemas/verdict.js +38 -4
  108. package/dist/schemas/verdict.js.map +1 -1
  109. package/dist/strictness.d.ts +102 -0
  110. package/dist/strictness.d.ts.map +1 -0
  111. package/dist/strictness.js +92 -0
  112. package/dist/strictness.js.map +1 -0
  113. package/dist/supervision/index.js +6 -6
  114. package/dist/supervision/index.js.map +1 -1
  115. package/dist/twin-paths.d.ts +125 -0
  116. package/dist/twin-paths.d.ts.map +1 -0
  117. package/dist/twin-paths.js +233 -0
  118. package/dist/twin-paths.js.map +1 -0
  119. package/dist/verdict/behavior.d.ts.map +1 -1
  120. package/dist/verdict/behavior.js +284 -10
  121. package/dist/verdict/behavior.js.map +1 -1
  122. package/dist/verdict/cause.d.ts.map +1 -1
  123. package/dist/verdict/cause.js +18 -0
  124. package/dist/verdict/cause.js.map +1 -1
  125. package/dist/verdict/evaluate.d.ts +54 -0
  126. package/dist/verdict/evaluate.d.ts.map +1 -1
  127. package/dist/verdict/evaluate.js +278 -12
  128. package/dist/verdict/evaluate.js.map +1 -1
  129. package/dist/verdict/index.d.ts +3 -3
  130. package/dist/verdict/index.d.ts.map +1 -1
  131. package/dist/verdict/index.js +3 -3
  132. package/dist/verdict/index.js.map +1 -1
  133. package/dist/verdict/pack-verifiers.d.ts +17 -3
  134. package/dist/verdict/pack-verifiers.d.ts.map +1 -1
  135. package/dist/verdict/pack-verifiers.js +47 -8
  136. package/dist/verdict/pack-verifiers.js.map +1 -1
  137. package/dist/verdict/registry.d.ts +19 -0
  138. package/dist/verdict/registry.d.ts.map +1 -1
  139. package/dist/verdict/registry.js +21 -0
  140. package/dist/verdict/registry.js.map +1 -1
  141. package/package.json +1 -1
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Flaky-test quarantine loading.
3
+ *
4
+ * Owner-approved, always-expiring files in `.gateforge/quarantine/*.yml`
5
+ * that remove ONE test from the required set. The same trust pattern as
6
+ * waivers, with one difference in force: a quarantined test never
7
+ * FORGIVES anything — it never blocks and its evidence is never used,
8
+ * so an obligation only it covered stays `missing`.
9
+ *
10
+ * Fail-closed rules:
11
+ * - ALL FOUR attribution fields (testKey, owner, approver, reason) plus
12
+ * the expiry are mandatory; a file missing any of them is a
13
+ * configuration error, never a partially-applied exception.
14
+ * - Expiry is judged against the INJECTED clock, never the wall clock
15
+ * (invariant 7).
16
+ * - A maximum duration is enforced: a quarantine may never outlive the
17
+ * documented ceiling, so a "temporary" escape hatch cannot become
18
+ * permanent by hand-editing the expiry.
19
+ * - Two quarantines on the same test key are ambiguous → error.
20
+ *
21
+ * Loading is synchronous, matching the waivers loader.
22
+ */
23
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
24
+ import { basename, dirname, join } from 'node:path';
25
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
26
+ import { compareStrings } from '../graph/util.js';
27
+ import { QuarantineSchema } from '../schemas/quarantine.js';
28
+ import { parseInstant } from '../verdict/index.js';
29
+ /** Maximum quarantine lifetime in days: a quarantine always expires. */
30
+ export const QUARANTINE_MAX_DAYS = 14;
31
+ /** The documented ceiling in milliseconds. */
32
+ export const QUARANTINE_MAX_DURATION_MS = QUARANTINE_MAX_DAYS * 86_400_000;
33
+ /** Repo-relative quarantine directory. */
34
+ export const QUARANTINE_DIR = '.gateforge/quarantine';
35
+ /** Error raised for any fail-closed quarantine configuration problem. */
36
+ export class GateforgeQuarantineError extends Error {
37
+ /** Every problem found, file-scoped, in deterministic order. */
38
+ problems;
39
+ constructor(problems) {
40
+ const rendered = problems.map((problem) => ` ${problem.file}: ${problem.detail}`).join('\n');
41
+ super(`quarantine configuration failed closed with ${problems.length} problem(s):\n${rendered}`);
42
+ this.name = 'GateforgeQuarantineError';
43
+ this.problems = problems;
44
+ }
45
+ }
46
+ /**
47
+ * Parses one quarantine document (fail-closed: an empty or non-mapping
48
+ * document is a problem, never an empty quarantine).
49
+ *
50
+ * Args:
51
+ * text: the raw file content.
52
+ *
53
+ * Returns:
54
+ * unknown: the parsed YAML mapping.
55
+ *
56
+ * @throws Error when the bytes are unparsable or not a mapping.
57
+ */
58
+ function parseYamlDocument(text) {
59
+ const parsed = parseYaml(text);
60
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
61
+ throw new Error('quarantine document must be a mapping with schemaVersion, testKey, owner, approver, reason, expiresAt');
62
+ }
63
+ return parsed;
64
+ }
65
+ /**
66
+ * Loads and validates every `*.yml` quarantine file in `dir` (sorted by
67
+ * filename for determinism). A missing directory yields an empty result
68
+ * — most repositories have no quarantines. Any problem (unparsable
69
+ * YAML, missing mandatory field, duplicate test key, over-long duration)
70
+ * fails closed with {@link GateforgeQuarantineError}.
71
+ *
72
+ * Args:
73
+ * dir: the quarantine directory (`.gateforge/quarantine`).
74
+ * options: injected clock and optional maximum duration.
75
+ *
76
+ * Returns:
77
+ * QuarantineLoadResult: active and expired partitions, both sorted by
78
+ * test key.
79
+ *
80
+ * @throws GateforgeQuarantineError when any file fails closed.
81
+ */
82
+ export function loadQuarantines(dir, options) {
83
+ const now = parseInstant(options.now);
84
+ const maxDurationMs = options.maxDurationMs ?? QUARANTINE_MAX_DURATION_MS;
85
+ if (!existsSync(dir))
86
+ return { active: [], expired: [] };
87
+ const files = readdirSync(dir, { withFileTypes: true })
88
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.yml'))
89
+ .map((entry) => entry.name)
90
+ .sort(compareStrings);
91
+ const problems = [];
92
+ const parsed = [];
93
+ for (const file of files) {
94
+ let document;
95
+ try {
96
+ document = parseYamlDocument(readFileSync(join(dir, file), 'utf8'));
97
+ }
98
+ catch (error) {
99
+ problems.push({
100
+ file,
101
+ detail: `not parsable YAML: ${error instanceof Error ? error.message : String(error)}`,
102
+ });
103
+ continue;
104
+ }
105
+ const result = QuarantineSchema.safeParse(document);
106
+ if (!result.success) {
107
+ for (const issue of result.error.issues) {
108
+ problems.push({ file, detail: `${issue.path.join('.') || '<quarantine>'}: ${issue.message}` });
109
+ }
110
+ continue;
111
+ }
112
+ const duration = Date.parse(result.data.expiresAt) - now.getTime();
113
+ if (duration > maxDurationMs) {
114
+ problems.push({
115
+ file,
116
+ detail: `expiresAt '${result.data.expiresAt}' exceeds the maximum quarantine duration ` +
117
+ `(${String(Math.round(maxDurationMs / 86_400_000))} days from now)`,
118
+ });
119
+ continue;
120
+ }
121
+ parsed.push({ file, quarantine: result.data });
122
+ }
123
+ const byTestKey = new Map();
124
+ for (const entry of parsed) {
125
+ const firstFile = byTestKey.get(entry.quarantine.testKey);
126
+ if (firstFile !== undefined) {
127
+ problems.push({
128
+ file: entry.file,
129
+ detail: `test key '${entry.quarantine.testKey}' is already quarantined by '${firstFile}' — ` +
130
+ 'one quarantine per test, never two overlapping exceptions',
131
+ });
132
+ continue;
133
+ }
134
+ byTestKey.set(entry.quarantine.testKey, entry.file);
135
+ }
136
+ if (problems.length > 0)
137
+ throw new GateforgeQuarantineError(problems);
138
+ const active = [];
139
+ const expired = [];
140
+ for (const entry of parsed) {
141
+ if (now.getTime() >= Date.parse(entry.quarantine.expiresAt))
142
+ expired.push(entry);
143
+ else
144
+ active.push(entry);
145
+ }
146
+ const byTest = (a, b) => compareStrings(a.quarantine.testKey, b.quarantine.testKey);
147
+ return { active: [...active].sort(byTest), expired: [...expired].sort(byTest) };
148
+ }
149
+ /**
150
+ * Serializes one quarantine document in the stable on-disk form
151
+ * (block YAML, trailing newline).
152
+ *
153
+ * Args:
154
+ * quarantine: the schema-valid document.
155
+ *
156
+ * Returns:
157
+ * string: the file content.
158
+ */
159
+ export function serializeQuarantine(quarantine) {
160
+ const body = stringifyYaml({
161
+ schemaVersion: quarantine.schemaVersion,
162
+ testKey: quarantine.testKey,
163
+ owner: quarantine.owner,
164
+ approver: quarantine.approver,
165
+ reason: quarantine.reason,
166
+ expiresAt: quarantine.expiresAt,
167
+ });
168
+ return `${body.endsWith('\n') ? body : `${body}\n`}`;
169
+ }
170
+ /**
171
+ * Writes one quarantine document, creating parent directories as needed.
172
+ * Fail-closed: a write failure surfaces as a {@link GateforgeQuarantineError}.
173
+ *
174
+ * Args:
175
+ * path: destination file path.
176
+ * quarantine: the schema-valid document to write.
177
+ *
178
+ * @throws GateforgeQuarantineError when the file cannot be written.
179
+ */
180
+ export function writeQuarantine(path, quarantine) {
181
+ try {
182
+ mkdirSync(dirname(path), { recursive: true });
183
+ writeFileSync(path, serializeQuarantine(quarantine), 'utf8');
184
+ }
185
+ catch (error) {
186
+ throw new GateforgeQuarantineError([
187
+ {
188
+ file: basename(path),
189
+ detail: `could not be written: ${error instanceof Error ? error.message : String(error)}`,
190
+ },
191
+ ]);
192
+ }
193
+ }
194
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/quarantine/index.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  import { type JsonValue } from '../canonical-json.js';
2
- import { type GateReceipt } from '../schemas/gate-receipt.js';
3
2
  import { EMPTY_BEHAVIOR_CATALOG_DIGEST } from '../policy/behavior.js';
3
+ import { type GateReceipt } from '../schemas/gate-receipt.js';
4
+ import type { ExecutedOutcome } from '../schemas/execution-result.js';
5
+ import { type RunRecord, type RunRecordBody } from '../schemas/run-record.js';
4
6
  /** Domain tag binding receipt MACs to the gate-receipt envelope format. */
5
7
  export declare const RECEIPT_DOMAIN = "gateforge.receipt.v2";
6
8
  /** The only receipt envelope version this code produces or honors. */
@@ -63,6 +65,68 @@ export { EMPTY_BEHAVIOR_CATALOG_DIGEST };
63
65
  * TypeError: when the verifier key is empty.
64
66
  */
65
67
  export declare function gateReceiptMac(verifierKey: string, body: GateReceiptBody): string;
68
+ /**
69
+ * Digest over a run's per-test outcomes, in the sealed order. A run
70
+ * record binds it so the record can never claim a cleaner run than the
71
+ * execution result it names, and a consumer recomputes it from that
72
+ * execution result when it recomputes the re-seal.
73
+ *
74
+ * Args:
75
+ * outcomes: the sealed execution result's per-test outcomes.
76
+ *
77
+ * Returns:
78
+ * string: 64-char lowercase hex digest.
79
+ */
80
+ export declare function testOutcomesDigestOf(outcomes: readonly ExecutedOutcome[]): string;
81
+ /** Domain tag binding run-record MACs to the run-record envelope format. */
82
+ export declare const RUN_RECORD_DOMAIN = "gateforge.run-record.v1";
83
+ /** The only run-record envelope version this code produces or honors. */
84
+ export declare const RUN_RECORD_VERSION = 1;
85
+ /**
86
+ * Computes the run-record MAC: HMAC-SHA256 over the GF-canonical JSON
87
+ * of `{domain: 'gateforge.run-record.v1', ...body}` keyed by the same
88
+ * witness verifier key a receipt uses. The distinct domain tag makes
89
+ * cross-format acceptance impossible in both directions: a run record
90
+ * can never verify as a gate receipt, and a receipt MAC can never
91
+ * verify as a run record.
92
+ *
93
+ * Args:
94
+ * verifierKey: the witness verifier secret (non-empty).
95
+ * body: the unsigned run-record body.
96
+ *
97
+ * Returns:
98
+ * string: 64-char lowercase hex HMAC.
99
+ *
100
+ * Throws:
101
+ * TypeError: when the verifier key is empty.
102
+ */
103
+ export declare function runRecordMac(verifierKey: string, body: RunRecordBody): string;
104
+ /** Typed reasons a run-record candidate failed verification (fail closed). */
105
+ export type RunRecordRejection = 'missing' | 'malformed' | 'mac-fail' | 'not-whole-suite';
106
+ /** The verification outcome: a validated run record or a typed rejection. */
107
+ export type RunRecordVerification = {
108
+ ok: true;
109
+ record: RunRecord;
110
+ } | {
111
+ ok: false;
112
+ rejection: RunRecordRejection;
113
+ detail: string;
114
+ };
115
+ /**
116
+ * Verifies a run-record candidate end to end (fail closed): the strict
117
+ * schema first, then the MAC under the given verifier key. A run record
118
+ * authenticates EXACTLY this much and no more — it carries no gate
119
+ * verdict, so there is nothing here to grade, compare against a policy
120
+ * pin, or accept as proof for a blocked gate.
121
+ *
122
+ * Args:
123
+ * verifierKey: the witness verifier secret the record must carry a MAC for.
124
+ * candidate: the parsed run-state document.
125
+ *
126
+ * Returns:
127
+ * RunRecordVerification: the validated record or the typed rejection.
128
+ */
129
+ export declare function verifyRunRecord(verifierKey: string, candidate: unknown): RunRecordVerification;
66
130
  /** Typed reasons a receipt candidate failed verification (fail closed). */
67
131
  export type ReceiptRejection = 'missing' | 'malformed' | 'mac-fail' | 'input-digest-mismatch' | 'policy-digest-mismatch' | 'selection-digest-mismatch' | 'catalog-digest-mismatch' | 'execution-digest-mismatch' | 'attestation-digest-mismatch' | 'tree-mismatch' | 'behavior-digest-mismatch' | 'case-set-mismatch' | 'case-execution-mismatch' | 'engine-bundle-mismatch' | 'boundary-mismatch' | 'artifact-mismatch' | 'not-clean';
68
132
  /** The verification outcome: a validated receipt or a typed rejection. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"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"}
@@ -15,8 +15,9 @@
15
15
  */
16
16
  import { createHmac, timingSafeEqual } from 'node:crypto';
17
17
  import { canonicalJson, sha256Canonical } from '../canonical-json.js';
18
- import { GateReceiptSchema } from '../schemas/gate-receipt.js';
19
18
  import { EMPTY_BEHAVIOR_CATALOG_DIGEST } from '../policy/behavior.js';
19
+ import { GateReceiptSchema } from '../schemas/gate-receipt.js';
20
+ import { RunRecordSchema } from '../schemas/run-record.js';
20
21
  /** Domain tag binding receipt MACs to the gate-receipt envelope format. */
21
22
  export const RECEIPT_DOMAIN = 'gateforge.receipt.v2';
22
23
  /** The only receipt envelope version this code produces or honors. */
@@ -127,6 +128,119 @@ export function gateReceiptMac(verifierKey, body) {
127
128
  }))
128
129
  .digest('hex');
129
130
  }
131
+ /**
132
+ * Digest over a run's per-test outcomes, in the sealed order. A run
133
+ * record binds it so the record can never claim a cleaner run than the
134
+ * execution result it names, and a consumer recomputes it from that
135
+ * execution result when it recomputes the re-seal.
136
+ *
137
+ * Args:
138
+ * outcomes: the sealed execution result's per-test outcomes.
139
+ *
140
+ * Returns:
141
+ * string: 64-char lowercase hex digest.
142
+ */
143
+ export function testOutcomesDigestOf(outcomes) {
144
+ return sha256Canonical(outcomes);
145
+ }
146
+ /** Domain tag binding run-record MACs to the run-record envelope format. */
147
+ export const RUN_RECORD_DOMAIN = 'gateforge.run-record.v1';
148
+ /** The only run-record envelope version this code produces or honors. */
149
+ export const RUN_RECORD_VERSION = 1;
150
+ /**
151
+ * Computes the run-record MAC: HMAC-SHA256 over the GF-canonical JSON
152
+ * of `{domain: 'gateforge.run-record.v1', ...body}` keyed by the same
153
+ * witness verifier key a receipt uses. The distinct domain tag makes
154
+ * cross-format acceptance impossible in both directions: a run record
155
+ * can never verify as a gate receipt, and a receipt MAC can never
156
+ * verify as a run record.
157
+ *
158
+ * Args:
159
+ * verifierKey: the witness verifier secret (non-empty).
160
+ * body: the unsigned run-record body.
161
+ *
162
+ * Returns:
163
+ * string: 64-char lowercase hex HMAC.
164
+ *
165
+ * Throws:
166
+ * TypeError: when the verifier key is empty.
167
+ */
168
+ export function runRecordMac(verifierKey, body) {
169
+ if (typeof verifierKey !== 'string' || verifierKey.length === 0) {
170
+ throw new TypeError('runRecordMac: verifier key must be a non-empty string');
171
+ }
172
+ return createHmac('sha256', verifierKey)
173
+ .update(canonicalJson({
174
+ domain: RUN_RECORD_DOMAIN,
175
+ ...body,
176
+ }))
177
+ .digest('hex');
178
+ }
179
+ /**
180
+ * Verifies a run-record candidate end to end (fail closed): the strict
181
+ * schema first, then the MAC under the given verifier key. A run record
182
+ * authenticates EXACTLY this much and no more — it carries no gate
183
+ * verdict, so there is nothing here to grade, compare against a policy
184
+ * pin, or accept as proof for a blocked gate.
185
+ *
186
+ * Args:
187
+ * verifierKey: the witness verifier secret the record must carry a MAC for.
188
+ * candidate: the parsed run-state document.
189
+ *
190
+ * Returns:
191
+ * RunRecordVerification: the validated record or the typed rejection.
192
+ */
193
+ export function verifyRunRecord(verifierKey, candidate) {
194
+ if (candidate === undefined || candidate === null) {
195
+ return { ok: false, rejection: 'missing', detail: 'run record is missing' };
196
+ }
197
+ const parsed = RunRecordSchema.safeParse(candidate);
198
+ if (!parsed.success) {
199
+ const issue = parsed.error.issues[0];
200
+ const path = issue === undefined ? '' : ` at '${issue.path.map(String).join('.')}':`;
201
+ return {
202
+ ok: false,
203
+ rejection: 'malformed',
204
+ detail: `run record is malformed${path} ${issue?.message ?? 'unknown schema error'}`,
205
+ };
206
+ }
207
+ const record = parsed.data;
208
+ const { mac, ...body } = record;
209
+ if (typeof verifierKey !== 'string' || verifierKey.length === 0) {
210
+ return {
211
+ ok: false,
212
+ rejection: 'mac-fail',
213
+ detail: 'run record cannot verify without a verifier key (fail closed)',
214
+ };
215
+ }
216
+ if (!MAC_PATTERN.test(mac)) {
217
+ return { ok: false, rejection: 'mac-fail', detail: 'run record mac is malformed' };
218
+ }
219
+ let expectedMac;
220
+ let claimedMac;
221
+ try {
222
+ expectedMac = Buffer.from(runRecordMac(verifierKey, body), 'hex');
223
+ claimedMac = Buffer.from(mac, 'hex');
224
+ }
225
+ catch {
226
+ return { ok: false, rejection: 'mac-fail', detail: 'run record mac could not be recomputed' };
227
+ }
228
+ if (expectedMac.length !== claimedMac.length || !timingSafeEqual(expectedMac, claimedMac)) {
229
+ return {
230
+ ok: false,
231
+ rejection: 'mac-fail',
232
+ detail: 'run record signature fails; the record was forged or tampered with (fail closed)',
233
+ };
234
+ }
235
+ if (record.plannedTests === 0) {
236
+ return {
237
+ ok: false,
238
+ rejection: 'not-whole-suite',
239
+ detail: 'run record holds no planned test, so it proves no whole-suite run',
240
+ };
241
+ }
242
+ return { ok: true, record };
243
+ }
130
244
  /**
131
245
  * Verifies a receipt candidate end-to-end (fail closed):
132
246
  * 1. structural — the value must parse against the strict v1 schema;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAkB,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,4BAA4B,CAAC;AAEjF,OAAO,EAAE,eAAe,EAAsC,MAAM,0BAA0B,CAAC;AAE/F,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAErD,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,mDAAmD;AACnD,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAKrC,gFAAgF;AAChF,MAAM,CAAC,MAAM,8BAA8B,GAAG,eAAe,CAAC;IAC5D,MAAM,EAAE,6BAA6B;IACrC,cAAc,EAAE,EAAE;CACnB,CAAC,CAAC;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,2BAA2B,GAAG,eAAe,CAAC;IACzD,MAAM,EAAE,6BAA6B;IACrC,UAAU,EAAE,EAAE;CACf,CAAC,CAAC;AAEH,wFAAwF;AACxF,MAAM,CAAC,MAAM,yBAAyB,GAAG,kBAAkB,CAAC;AAE5D;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,cAAoC;IAC1E,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,8BAA8B,CAAC;IACvE,OAAO,eAAe,CAAC;QACrB,MAAM,EAAE,6BAA6B;QACrC,cAAc,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAChD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAChC,OAAO,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAA0B;IAC9D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,2BAA2B,CAAC;IAC7D,OAAO,eAAe,CAAC;QACrB,MAAM,EAAE,6BAA6B;QACrC,UAAU,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE;KACzC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,aAAqB,EAAE,mBAA2B;IACrF,OAAO,eAAe,CAAC;QACrB,MAAM,EAAE,4BAA4B;QACpC,aAAa;QACb,mBAAmB;KACpB,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAe;IACvD,OAAO,eAAe,CAAC,EAAE,MAAM,EAAE,iCAAiC,EAAE,OAAO,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,gFAAgF;AAChF,MAAM,CAAC,MAAM,gCAAgC,GAAG,yBAAyB,CAAC,yBAAyB,CAAC,CAAC;AAErG;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,eAA8B;IACnE,OAAO,eAAe,CAAC;QACrB,MAAM,EAAE,8BAA8B;QACtC,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,eAAe;KACtB,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAEzC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,IAAqB;IACvE,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CAAC,yDAAyD,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;SACrC,MAAM,CACL,aAAa,CAAC;QACZ,MAAM,EAAE,cAAc;QACtB,GAAG,IAAI;KACR,CAAC,CACH;SACA,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAoC;IACvE,OAAO,eAAe,CAAC,QAAgC,CAAC,CAAC;AAC3D,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,iBAAiB,GAAG,yBAAyB,CAAC;AAE3D,yEAAyE;AACzE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAEpC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAAC,WAAmB,EAAE,IAAmB;IACnE,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;SACrC,MAAM,CACL,aAAa,CAAC;QACZ,MAAM,EAAE,iBAAiB;QACzB,GAAG,IAAI;KACR,CAAC,CACH;SACA,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAUD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,SAAkB;IACrE,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QAClD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;IAC9E,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QACrF,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,WAAW;YACtB,MAAM,EAAE,0BAA0B,IAAI,IAAI,KAAK,EAAE,OAAO,IAAI,sBAAsB,EAAE;SACrF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAiB,CAAC;IACxC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAChC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,UAAU;YACrB,MAAM,EAAE,+DAA+D;SACxE,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC;IACrF,CAAC;IACD,IAAI,WAAmB,CAAC;IACxB,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;QAClE,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,wCAAwC,EAAE,CAAC;IAChG,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,CAAC;QAC1F,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,UAAU;YACrB,MAAM,EAAE,kFAAkF;SAC3F,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,YAAY,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,iBAAiB;YAC5B,MAAM,EAAE,mEAAmE;SAC5E,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC;AA2BD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,SAAkB,EAClB,WAcI,EAAE;IAEN,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QAClD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;IAChF,CAAC;IACD,IACE,OAAO,SAAS,KAAK,QAAQ;QAC7B,SAAS,KAAK,IAAI;QACjB,SAAqC,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAC9D,CAAC;QACD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,WAAW;YACtB,MAAM,EACJ,2FAA2F;gBAC3F,oFAAoF;SACvF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QACrF,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,WAAW;YACtB,MAAM,EACJ,yFAAyF;gBACzF,GAAG,IAAI,IAAI,KAAK,EAAE,OAAO,IAAI,sBAAsB,EAAE;SACxD,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAmB,CAAC;IAC3C,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACjC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,UAAU;YACrB,MAAM,EAAE,iEAAiE;SAC1E,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC;IACpB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,+BAA+B,EAAE,CAAC;IACvF,CAAC;IACD,IAAI,WAAmB,CAAC;IACxB,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;QACpE,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,0CAA0C,EAAE,CAAC;IAClG,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,CAAC;QAC1F,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,UAAU;YACrB,MAAM,EAAE,qFAAqF;SAC9F,CAAC;IACJ,CAAC;IACD,MAAM,YAAY,GAMb;QACH,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,OAAO,CAAC,WAAW,EAAE,aAAa,EAAE,QAAQ,CAAC,WAAW,EAAE,SAAS,EAAE,uBAAuB,EAAE,KAAK,EAAE,cAAc,EAAE;QAC3J,EAAE,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,OAAO,CAAC,mBAAmB,EAAE,aAAa,EAAE,QAAQ,CAAC,mBAAmB,EAAE,SAAS,EAAE,wBAAwB,EAAE,KAAK,EAAE,uBAAuB,EAAE;QAC7L,EAAE,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,OAAO,CAAC,eAAe,EAAE,aAAa,EAAE,QAAQ,CAAC,eAAe,EAAE,SAAS,EAAE,2BAA2B,EAAE,KAAK,EAAE,kBAAkB,EAAE;QAC/K,EAAE,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,OAAO,CAAC,aAAa,EAAE,aAAa,EAAE,QAAQ,CAAC,aAAa,EAAE,SAAS,EAAE,yBAAyB,EAAE,KAAK,EAAE,gBAAgB,EAAE;QACrK,EAAE,KAAK,EAAE,uBAAuB,EAAE,YAAY,EAAE,OAAO,CAAC,qBAAqB,EAAE,aAAa,EAAE,QAAQ,CAAC,qBAAqB,EAAE,SAAS,EAAE,2BAA2B,EAAE,KAAK,EAAE,yBAAyB,EAAE;QACxM;YACE,KAAK,EAAE,2BAA2B;YAClC,YAAY,EAAE,OAAO,CAAC,yBAAyB;YAC/C,aAAa,EAAE,QAAQ,CAAC,yBAAyB,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,yBAAyB;YAChH,SAAS,EAAE,6BAA6B;YACxC,KAAK,EAAE,6BAA6B;SACrC;QACD;YACE,KAAK,EAAE,iBAAiB;YACxB,YAAY,EAAE,OAAO,CAAC,eAAe;YACrC,aAAa,EAAE,QAAQ,CAAC,eAAe;YACvC,SAAS,EAAE,eAAe;YAC1B,KAAK,EAAE,mBAAmB;SAC3B;QACD;YACE,KAAK,EAAE,uBAAuB;YAC9B,YAAY,EAAE,OAAO,CAAC,qBAAqB;YAC3C,aAAa,EAAE,QAAQ,CAAC,qBAAqB;YAC7C,SAAS,EAAE,0BAA0B;YACrC,KAAK,EAAE,yBAAyB;SACjC;QACD;YACE,KAAK,EAAE,uBAAuB;YAC9B,YAAY,EAAE,OAAO,CAAC,qBAAqB;YAC3C,aAAa,EAAE,QAAQ,CAAC,qBAAqB;YAC7C,SAAS,EAAE,mBAAmB;YAC9B,KAAK,EAAE,0BAA0B;SAClC;QACD;YACE,KAAK,EAAE,qBAAqB;YAC5B,YAAY,EAAE,OAAO,CAAC,mBAAmB;YACzC,aAAa,EAAE,QAAQ,CAAC,mBAAmB;YAC3C,SAAS,EAAE,yBAAyB;YACpC,KAAK,EAAE,uBAAuB;SAC/B;QACD;YACE,KAAK,EAAE,oBAAoB;YAC3B,YAAY,EAAE,OAAO,CAAC,kBAAkB;YACxC,aAAa,EAAE,QAAQ,CAAC,kBAAkB;YAC1C,SAAS,EAAE,wBAAwB;YACnC,KAAK,EAAE,sBAAsB;SAC9B;QACD;YACE,KAAK,EAAE,yBAAyB;YAChC,YAAY,EAAE,OAAO,CAAC,uBAAuB;YAC7C,aAAa,EAAE,QAAQ,CAAC,uBAAuB;YAC/C,SAAS,EAAE,mBAAmB;YAC9B,KAAK,EAAE,2BAA2B;SACnC;QACD;YACE,KAAK,EAAE,sBAAsB;YAC7B,YAAY,EAAE,OAAO,CAAC,oBAAoB;YAC1C,aAAa,EAAE,QAAQ,CAAC,oBAAoB;YAC5C,SAAS,EAAE,mBAAmB;YAC9B,KAAK,EAAE,wBAAwB;SAChC;KACF,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS;YAAE,SAAS;QAChD,IAAI,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,aAAa,EAAE,CAAC;YAC/C,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,MAAM,EACJ,gBAAgB,KAAK,CAAC,KAAK,kCAAkC;oBAC7D,KAAK,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM;oBACzE,8CAA8C;aACjD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,WAAW;YACtB,MAAM,EACJ,kDAAkD,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,cAAc;gBACvG,+DAA+D;SAClE,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { CauseCode } from '../schemas/verdict.js';
2
+ /** Minimal report data required to build a consistent human-readable message. */
3
+ export interface HumanMessageEntry {
4
+ /** The stable cause code, when the entry has one. */
5
+ cause?: CauseCode | null;
6
+ /** Plain-language detail or verdict reason. */
7
+ detail?: string;
8
+ /** Resource or obligation identity used to choose a useful command. */
9
+ id?: string;
10
+ /** Suggested action associated with the cause. */
11
+ nextAction?: string | null;
12
+ /** Verdict reason, when the entry represents an obligation. */
13
+ reason?: string | null;
14
+ /** Blocking kind or verdict name used when no cause is available. */
15
+ type?: string;
16
+ }
17
+ /**
18
+ * Prefix of the refusal a receipt sealed by an older engine produces. The
19
+ * receipt records the version that sealed it, so a version disagreement is
20
+ * reportable fact, and it is the one cause a user acts on by re-sealing.
21
+ */
22
+ export declare const ENGINE_UPGRADE_REFUSAL_PREFIX = "require-e2e: this receipt was sealed by Gateforge ";
23
+ /**
24
+ * Builds the common sentence, copyable command, and final cause marker for a report entry.
25
+ *
26
+ * Args:
27
+ * entry: report detail, identity, cause, and suggested action.
28
+ *
29
+ * Returns:
30
+ * string: one deterministic sentence followed by a runnable command and `[CODE]`.
31
+ */
32
+ export declare function humanMessage(entry: HumanMessageEntry): string;
33
+ //# sourceMappingURL=human-message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"human-message.d.ts","sourceRoot":"","sources":["../../src/report/human-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,uDACY,CAAC;AAEvD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAiB7D"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Prefix of the refusal a receipt sealed by an older engine produces. The
3
+ * receipt records the version that sealed it, so a version disagreement is
4
+ * reportable fact, and it is the one cause a user acts on by re-sealing.
5
+ */
6
+ export const ENGINE_UPGRADE_REFUSAL_PREFIX = 'require-e2e: this receipt was sealed by Gateforge ';
7
+ /**
8
+ * Builds the common sentence, copyable command, and final cause marker for a report entry.
9
+ *
10
+ * Args:
11
+ * entry: report detail, identity, cause, and suggested action.
12
+ *
13
+ * Returns:
14
+ * string: one deterministic sentence followed by a runnable command and `[CODE]`.
15
+ */
16
+ export function humanMessage(entry) {
17
+ // The sentence gets its own period below; a detail that already ends in
18
+ // one would print `.. Run`.
19
+ const sentence = (entry.reason ?? entry.detail ?? 'Gateforge found an unresolved item').trim().replace(/\.$/, '');
20
+ const command = entry.nextAction?.startsWith('gateforge ')
21
+ ? entry.nextAction
22
+ : entry.cause === 'TEST_MAPPING_MISSING' ||
23
+ entry.cause === 'TEST_MAPPING_AMBIGUOUS' ||
24
+ entry.cause === 'TEST_MAPPING_STALE' ||
25
+ entry.cause === 'TEST_KIND_UNKNOWN'
26
+ ? 'gateforge tests suggest'
27
+ : entry.id !== undefined
28
+ ? `gateforge explain ${entry.id}`
29
+ : 'gateforge discover --json';
30
+ const code = entry.cause ?? entry.type?.toUpperCase().replaceAll('-', '_') ?? 'BLOCKING_FINDING';
31
+ return `${sentence}. Run \`${command}\`. [${code}]`;
32
+ }
33
+ //# sourceMappingURL=human-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"human-message.js","sourceRoot":"","sources":["../../src/report/human-message.ts"],"names":[],"mappings":"AAkBA;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,oDAAoD,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,KAAwB;IACnD,wEAAwE;IACxE,4BAA4B;IAC5B,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,oCAAoC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAClH,MAAM,OAAO,GACX,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC;QACxC,CAAC,CAAC,KAAK,CAAC,UAAU;QAClB,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,sBAAsB;YACpC,KAAK,CAAC,KAAK,KAAK,wBAAwB;YACxC,KAAK,CAAC,KAAK,KAAK,oBAAoB;YACpC,KAAK,CAAC,KAAK,KAAK,mBAAmB;YACrC,CAAC,CAAC,yBAAyB;YAC3B,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,SAAS;gBACtB,CAAC,CAAC,qBAAqB,KAAK,CAAC,EAAE,EAAE;gBACjC,CAAC,CAAC,2BAA2B,CAAC;IACtC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,kBAAkB,CAAC;IACjG,OAAO,GAAG,QAAQ,WAAW,OAAO,QAAQ,IAAI,GAAG,CAAC;AACtD,CAAC"}