@deftai/directive-core 0.100.0 → 0.102.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 (120) hide show
  1. package/dist/authz/classify.js +193 -27
  2. package/dist/authz/decompose-apply.d.ts +14 -0
  3. package/dist/authz/decompose-apply.js +93 -18
  4. package/dist/check/cached-orchestrator.d.ts +30 -5
  5. package/dist/check/cached-orchestrator.js +297 -10
  6. package/dist/check/cli-native-gates.d.ts +43 -0
  7. package/dist/check/cli-native-gates.js +84 -0
  8. package/dist/check/gate-lists.d.ts +10 -4
  9. package/dist/check/gate-lists.js +19 -5
  10. package/dist/check/index.d.ts +4 -2
  11. package/dist/check/index.js +3 -1
  12. package/dist/check/named-cause.d.ts +45 -0
  13. package/dist/check/named-cause.js +131 -0
  14. package/dist/check/orchestrator.js +6 -1
  15. package/dist/doctor/checks.d.ts +5 -6
  16. package/dist/doctor/checks.js +13 -56
  17. package/dist/doctor/help.d.ts +5 -0
  18. package/dist/doctor/help.js +31 -0
  19. package/dist/doctor/index.d.ts +2 -0
  20. package/dist/doctor/index.js +2 -0
  21. package/dist/doctor/main.js +58 -0
  22. package/dist/doctor/session-coda.d.ts +82 -0
  23. package/dist/doctor/session-coda.js +151 -0
  24. package/dist/doctor/types.d.ts +16 -0
  25. package/dist/eval/later-graduation.d.ts +39 -0
  26. package/dist/eval/later-graduation.js +91 -0
  27. package/dist/eval/report.d.ts +6 -0
  28. package/dist/eval/report.js +27 -8
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.js +3 -0
  31. package/dist/init-deposit/gitignore.js +2 -0
  32. package/dist/intake/issue-ingest.js +49 -0
  33. package/dist/policy/ac-pass-banking.d.ts +58 -0
  34. package/dist/policy/ac-pass-banking.js +150 -0
  35. package/dist/policy/ceremony-dial-escalation.d.ts +52 -0
  36. package/dist/policy/ceremony-dial-escalation.js +92 -0
  37. package/dist/policy/ceremony-dial.d.ts +32 -1
  38. package/dist/policy/ceremony-dial.js +128 -1
  39. package/dist/policy/check-resume.d.ts +9 -38
  40. package/dist/policy/check-resume.js +18 -156
  41. package/dist/policy/coverage-debt.d.ts +18 -37
  42. package/dist/policy/coverage-debt.js +38 -147
  43. package/dist/policy/index.d.ts +2 -1
  44. package/dist/policy/index.js +16 -2
  45. package/dist/preflight-cache/evaluate.d.ts +3 -0
  46. package/dist/preflight-cache/evaluate.js +3 -0
  47. package/dist/product-first-done-gate/acceptance.d.ts +25 -0
  48. package/dist/product-first-done-gate/acceptance.js +269 -0
  49. package/dist/product-first-done-gate/check-mode.d.ts +45 -0
  50. package/dist/product-first-done-gate/check-mode.js +148 -0
  51. package/dist/product-first-done-gate/empty-resolution.d.ts +26 -0
  52. package/dist/product-first-done-gate/empty-resolution.js +38 -0
  53. package/dist/product-first-done-gate/evaluate.d.ts +76 -0
  54. package/dist/product-first-done-gate/evaluate.js +411 -0
  55. package/dist/product-first-done-gate/index.d.ts +12 -0
  56. package/dist/product-first-done-gate/index.js +12 -0
  57. package/dist/product-first-done-gate/types.d.ts +67 -0
  58. package/dist/product-first-done-gate/types.js +54 -0
  59. package/dist/run-summary/emit.d.ts +66 -0
  60. package/dist/run-summary/emit.js +217 -0
  61. package/dist/run-summary/index.d.ts +5 -0
  62. package/dist/run-summary/index.js +5 -0
  63. package/dist/run-summary/path.d.ts +25 -0
  64. package/dist/run-summary/path.js +78 -0
  65. package/dist/run-summary/share.d.ts +25 -0
  66. package/dist/run-summary/share.js +106 -0
  67. package/dist/run-summary/types.d.ts +134 -0
  68. package/dist/run-summary/types.js +28 -0
  69. package/dist/scope/acceptance-activate-gate.d.ts +23 -0
  70. package/dist/scope/acceptance-activate-gate.js +70 -0
  71. package/dist/scope/acceptance-evidence.d.ts +34 -0
  72. package/dist/scope/acceptance-evidence.js +84 -10
  73. package/dist/scope/decompose.d.ts +5 -0
  74. package/dist/scope/decompose.js +11 -2
  75. package/dist/scope/index.d.ts +1 -0
  76. package/dist/scope/index.js +1 -0
  77. package/dist/scope/transition.js +5 -0
  78. package/dist/session/ac-pass-banking.d.ts +221 -0
  79. package/dist/session/ac-pass-banking.js +761 -0
  80. package/dist/session/deposit-sha.d.ts +49 -0
  81. package/dist/session/deposit-sha.js +121 -0
  82. package/dist/session/effort-budget.d.ts +6 -0
  83. package/dist/session/effort-budget.js +21 -3
  84. package/dist/session/index.d.ts +5 -1
  85. package/dist/session/index.js +5 -1
  86. package/dist/session/orientation-compression.d.ts +127 -0
  87. package/dist/session/orientation-compression.js +435 -0
  88. package/dist/session/orientation-state.d.ts +29 -0
  89. package/dist/session/orientation-state.js +85 -0
  90. package/dist/session/session-start.d.ts +31 -0
  91. package/dist/session/session-start.js +219 -22
  92. package/dist/session/toolchain-preflight.d.ts +79 -0
  93. package/dist/session/toolchain-preflight.js +211 -0
  94. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  95. package/dist/triage/bootstrap/gitignore.js +8 -2
  96. package/dist/triage/bootstrap/index.js +15 -3
  97. package/dist/triage/classify/index.d.ts +1 -0
  98. package/dist/triage/classify/index.js +2 -0
  99. package/dist/triage/classify/label-mirror.js +16 -0
  100. package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
  101. package/dist/triage/classify/mirror-discovery-tip.js +234 -0
  102. package/dist/triage/reconcile/reconcile.js +28 -6
  103. package/dist/triage/summary/index.js +12 -4
  104. package/dist/triage/welcome/default-mode.js +16 -0
  105. package/dist/ts-check-lane/run-lane.js +21 -4
  106. package/dist/vbrief-validate/conformance.js +7 -0
  107. package/dist/verify-ac/clauses.d.ts +51 -0
  108. package/dist/verify-ac/clauses.js +490 -0
  109. package/dist/verify-ac/evaluate.d.ts +55 -0
  110. package/dist/verify-ac/evaluate.js +145 -0
  111. package/dist/verify-ac/flag.d.ts +35 -0
  112. package/dist/verify-ac/flag.js +104 -0
  113. package/dist/verify-ac/index.d.ts +10 -0
  114. package/dist/verify-ac/index.js +10 -0
  115. package/dist/vitest-runner/coverage-debt-teardown.js +16 -4
  116. package/package.json +15 -3
  117. package/dist/policy/coverage-check-resume-presets.d.ts +0 -46
  118. package/dist/policy/coverage-check-resume-presets.js +0 -228
  119. package/dist/session/coverage-check-resume-nudge.d.ts +0 -34
  120. package/dist/session/coverage-check-resume-nudge.js +0 -66
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Fail-open run-summary JSONL emitter (#3282).
3
+ *
4
+ * Emission never changes exit codes, never throws to callers, and prints at
5
+ * most one warning when an *explicit* DEFT_RUN_SUMMARY_PATH write fails.
6
+ * Symlink destinations are refused (no-follow / containment) — fail-open.
7
+ */
8
+ import { type ResolveRunSummaryDestinationOptions } from "./path.js";
9
+ import { type AcceptanceRunSummaryPayload, type AcceptanceStampRunSummaryPayload, type CheckInvocationRunSummaryPayload, type DialEscalationEvaluationRunSummaryPayload, type DialTransitionRunSummaryPayload, type RunSummaryDestination, type RunSummaryEventKind, type RunSummaryLine, type RunSummaryPayload, type SessionStartRunSummaryPayload, type ToolTurnDenominatorRunSummaryPayload, type VerificationRunSummaryPayload } from "./types.js";
10
+ export interface RunSummaryEmitterOptions extends ResolveRunSummaryDestinationOptions {
11
+ readonly projectRoot: string;
12
+ readonly sessionId: string;
13
+ readonly frameworkVersion?: string;
14
+ /** Clock seam for tests. */
15
+ readonly now?: () => Date;
16
+ /** Destination override (tests). */
17
+ readonly destination?: RunSummaryDestination;
18
+ /** stdout/stderr seams (tests). */
19
+ readonly writeStdout?: (line: string) => void;
20
+ readonly writeStderr?: (line: string) => void;
21
+ }
22
+ export interface EmitRunSummaryResult {
23
+ readonly emitted: boolean;
24
+ readonly destination: RunSummaryDestination;
25
+ readonly line: RunSummaryLine | null;
26
+ readonly warning: boolean;
27
+ }
28
+ /**
29
+ * Stateful emitter: one instance per session tracks seq and write-warning once.
30
+ */
31
+ export declare class RunSummaryEmitter {
32
+ private seq;
33
+ private warned;
34
+ private readonly projectRoot;
35
+ private readonly sessionId;
36
+ private readonly frameworkVersion;
37
+ private readonly now;
38
+ private readonly destination;
39
+ private readonly env;
40
+ private readonly writeStdout;
41
+ private readonly writeStderr;
42
+ constructor(options: RunSummaryEmitterOptions);
43
+ getDestination(): RunSummaryDestination;
44
+ emit(event: RunSummaryEventKind, payload: RunSummaryPayload): EmitRunSummaryResult;
45
+ emitSessionStart(payload: SessionStartRunSummaryPayload): EmitRunSummaryResult;
46
+ emitDialTransition(payload: DialTransitionRunSummaryPayload): EmitRunSummaryResult;
47
+ emitDialEscalationEvaluation(payload: DialEscalationEvaluationRunSummaryPayload): EmitRunSummaryResult;
48
+ emitCheckInvocation(payload: CheckInvocationRunSummaryPayload): EmitRunSummaryResult;
49
+ emitToolTurnDenominator(payload: ToolTurnDenominatorRunSummaryPayload): EmitRunSummaryResult;
50
+ emitVerification(payload: VerificationRunSummaryPayload): EmitRunSummaryResult;
51
+ emitAcceptance(payload: AcceptanceRunSummaryPayload): EmitRunSummaryResult;
52
+ emitAcceptanceStamp(payload: AcceptanceStampRunSummaryPayload): EmitRunSummaryResult;
53
+ /** Emit the harness-supplied denominator when DEFT_TOTAL_TOOL_TURNS is set. */
54
+ emitKnownToolTurnDenominator(): EmitRunSummaryResult;
55
+ }
56
+ /** Parse harness-supplied DEFT_TOTAL_TOOL_TURNS (integer > 0). Invalid/unset → omit. */
57
+ export declare function readEnvToolTurnDenominator(env: NodeJS.ProcessEnv): number | undefined;
58
+ /**
59
+ * One-shot helper for call sites that do not hold an emitter (e.g. dial escalate).
60
+ * Still fail-open; creates a fresh seq=1 emitter unless `seqSeed` is passed via reuse.
61
+ */
62
+ export declare function emitRunSummaryEvent(options: RunSummaryEmitterOptions & {
63
+ readonly event: RunSummaryEventKind;
64
+ readonly payload: RunSummaryPayload;
65
+ }): EmitRunSummaryResult;
66
+ //# sourceMappingURL=emit.d.ts.map
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Fail-open run-summary JSONL emitter (#3282).
3
+ *
4
+ * Emission never changes exit codes, never throws to callers, and prints at
5
+ * most one warning when an *explicit* DEFT_RUN_SUMMARY_PATH write fails.
6
+ * Symlink destinations are refused (no-follow / containment) — fail-open.
7
+ */
8
+ import { mkdirSync, realpathSync } from "node:fs";
9
+ import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
10
+ import { readCorePackageVersion } from "../engine-version.js";
11
+ import { containedWrite } from "../fs/contained-write.js";
12
+ import { assertWriteTargetSafe } from "../fs/projection-containment.js";
13
+ import { resolveRunSummaryDestination } from "./path.js";
14
+ import { ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
15
+ function sortKeysDeep(value) {
16
+ if (Array.isArray(value)) {
17
+ return value.map(sortKeysDeep);
18
+ }
19
+ if (value !== null && typeof value === "object") {
20
+ const sorted = {};
21
+ for (const key of Object.keys(value).sort()) {
22
+ sorted[key] = sortKeysDeep(value[key]);
23
+ }
24
+ return sorted;
25
+ }
26
+ return value;
27
+ }
28
+ function lineToJson(line) {
29
+ return JSON.stringify(sortKeysDeep(line));
30
+ }
31
+ function isNestedUnder(parent, child) {
32
+ const rel = relative(resolve(parent), resolve(child));
33
+ return rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel);
34
+ }
35
+ /**
36
+ * Post-write realpath check: refuse parent-component TOCTOU where a parent was
37
+ * replaced with a symlink between validation and open (#3282 Greptile residual).
38
+ */
39
+ function assertRealpathStillContained(root, targetAbs) {
40
+ const realRoot = realpathSync(resolve(root));
41
+ const realTarget = realpathSync(targetAbs);
42
+ const prefix = realRoot.endsWith(sep) ? realRoot : `${realRoot}${sep}`;
43
+ if (realTarget !== realRoot && !realTarget.startsWith(prefix)) {
44
+ throw new Error(`run-summary write refused: realpath escaped containment (${realTarget} not under ${realRoot})`);
45
+ }
46
+ // Re-assert path components are not symlinks after the write.
47
+ assertWriteTargetSafe(realRoot, realTarget);
48
+ }
49
+ /**
50
+ * Append (or replace) a run-summary line with symlink refusal + containment.
51
+ * Mirrors lifecycle/events.ts appendEventLogLine (#2766 / #2951 / #3282 Greptile P1).
52
+ * Post-write realpath re-check closes parent-replacement TOCTOU window.
53
+ */
54
+ function writeRunSummaryLine(projectRoot, targetPath, textLine, mode) {
55
+ const targetAbs = resolve(targetPath);
56
+ const data = `${textLine}\n`;
57
+ if (isNestedUnder(projectRoot, targetAbs)) {
58
+ const rootAbs = resolve(projectRoot);
59
+ assertWriteTargetSafe(rootAbs, targetAbs);
60
+ containedWrite({
61
+ root: rootAbs,
62
+ target: targetAbs,
63
+ data,
64
+ mode,
65
+ });
66
+ assertRealpathStillContained(rootAbs, targetAbs);
67
+ return;
68
+ }
69
+ // Explicit path outside project: contain under the log parent (no symlink follow).
70
+ const parent = dirname(targetAbs);
71
+ mkdirSync(parent, { recursive: true });
72
+ const parentAbs = resolve(parent);
73
+ assertWriteTargetSafe(parentAbs, targetAbs);
74
+ containedWrite({
75
+ root: parentAbs,
76
+ target: basename(targetAbs),
77
+ data,
78
+ mode,
79
+ });
80
+ assertRealpathStillContained(parentAbs, targetAbs);
81
+ }
82
+ /**
83
+ * Stateful emitter: one instance per session tracks seq and write-warning once.
84
+ */
85
+ export class RunSummaryEmitter {
86
+ seq = 0;
87
+ warned = false;
88
+ projectRoot;
89
+ sessionId;
90
+ frameworkVersion;
91
+ now;
92
+ destination;
93
+ env;
94
+ writeStdout;
95
+ writeStderr;
96
+ constructor(options) {
97
+ this.projectRoot = options.projectRoot;
98
+ this.sessionId = options.sessionId;
99
+ this.frameworkVersion = options.frameworkVersion ?? readCorePackageVersion();
100
+ this.now = options.now ?? (() => new Date());
101
+ this.env = options.env ?? process.env;
102
+ this.destination =
103
+ options.destination ??
104
+ resolveRunSummaryDestination(options.projectRoot, {
105
+ env: this.env,
106
+ gitignoreCovers: options.gitignoreCovers,
107
+ });
108
+ this.writeStdout = options.writeStdout ?? ((line) => process.stdout.write(`${line}\n`));
109
+ this.writeStderr = options.writeStderr ?? ((line) => process.stderr.write(`${line}\n`));
110
+ }
111
+ getDestination() {
112
+ return this.destination;
113
+ }
114
+ emit(event, payload) {
115
+ try {
116
+ if (this.destination.kind === "silent") {
117
+ return { emitted: false, destination: this.destination, line: null, warning: false };
118
+ }
119
+ this.seq += 1;
120
+ const denominator = readPayloadToolTurnDenominator(payload) ?? readEnvToolTurnDenominator(this.env);
121
+ const line = {
122
+ schema_version: RUN_SUMMARY_SCHEMA_VERSION,
123
+ session_id: this.sessionId,
124
+ framework_version: this.frameworkVersion,
125
+ seq: this.seq,
126
+ ts: this.now().toISOString(),
127
+ event,
128
+ payload,
129
+ ...(denominator !== undefined ? { total_tool_turns: denominator } : {}),
130
+ };
131
+ const text = lineToJson(line);
132
+ if (this.destination.kind === "stdout") {
133
+ this.writeStdout(`${RUN_SUMMARY_STDOUT_PREFIX}${text}`);
134
+ return { emitted: true, destination: this.destination, line, warning: false };
135
+ }
136
+ // file destination — no-follow containment (#3282 Greptile P1 security)
137
+ const { path, truncateOnSessionStart, explicit } = this.destination;
138
+ try {
139
+ const mode = event === "session_start" && truncateOnSessionStart ? "replace" : "append";
140
+ writeRunSummaryLine(this.projectRoot, path, text, mode);
141
+ return { emitted: true, destination: this.destination, line, warning: false };
142
+ }
143
+ catch {
144
+ // Symlink / containment refusal and I/O both fail-open.
145
+ if (explicit && !this.warned) {
146
+ this.warned = true;
147
+ this.writeStderr(RUN_SUMMARY_WRITE_WARNING);
148
+ return { emitted: false, destination: this.destination, line, warning: true };
149
+ }
150
+ return { emitted: false, destination: this.destination, line, warning: false };
151
+ }
152
+ }
153
+ catch {
154
+ return { emitted: false, destination: this.destination, line: null, warning: false };
155
+ }
156
+ }
157
+ emitSessionStart(payload) {
158
+ return this.emit("session_start", payload);
159
+ }
160
+ emitDialTransition(payload) {
161
+ return this.emit("dial_transition", payload);
162
+ }
163
+ emitDialEscalationEvaluation(payload) {
164
+ return this.emit("dial_escalation_evaluation", payload);
165
+ }
166
+ emitCheckInvocation(payload) {
167
+ return this.emit("check_invocation", payload);
168
+ }
169
+ emitToolTurnDenominator(payload) {
170
+ return this.emit("tool_turn_denominator", payload);
171
+ }
172
+ emitVerification(payload) {
173
+ return this.emit("verification", payload);
174
+ }
175
+ emitAcceptance(payload) {
176
+ return this.emit("acceptance", payload);
177
+ }
178
+ emitAcceptanceStamp(payload) {
179
+ return this.emit("acceptance_stamp", payload);
180
+ }
181
+ /** Emit the harness-supplied denominator when DEFT_TOTAL_TOOL_TURNS is set. */
182
+ emitKnownToolTurnDenominator() {
183
+ const n = readEnvToolTurnDenominator(this.env);
184
+ if (n === undefined) {
185
+ return { emitted: false, destination: this.destination, line: null, warning: false };
186
+ }
187
+ return this.emitToolTurnDenominator({ total_tool_turns: n });
188
+ }
189
+ }
190
+ function readPayloadToolTurnDenominator(payload) {
191
+ if (!("total_tool_turns" in payload)) {
192
+ return undefined;
193
+ }
194
+ const value = payload.total_tool_turns;
195
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
196
+ }
197
+ /** Parse harness-supplied DEFT_TOTAL_TOOL_TURNS (integer > 0). Invalid/unset → omit. */
198
+ export function readEnvToolTurnDenominator(env) {
199
+ const raw = env[ENV_TOTAL_TOOL_TURNS];
200
+ if (raw === undefined || raw.trim().length === 0) {
201
+ return undefined;
202
+ }
203
+ const n = Number(raw.trim());
204
+ if (!Number.isInteger(n) || !Number.isFinite(n) || n <= 0) {
205
+ return undefined;
206
+ }
207
+ return n;
208
+ }
209
+ /**
210
+ * One-shot helper for call sites that do not hold an emitter (e.g. dial escalate).
211
+ * Still fail-open; creates a fresh seq=1 emitter unless `seqSeed` is passed via reuse.
212
+ */
213
+ export function emitRunSummaryEvent(options) {
214
+ const emitter = new RunSummaryEmitter(options);
215
+ return emitter.emit(options.event, options.payload);
216
+ }
217
+ //# sourceMappingURL=emit.js.map
@@ -0,0 +1,5 @@
1
+ export { type EmitRunSummaryResult, emitRunSummaryEvent, RunSummaryEmitter, type RunSummaryEmitterOptions, } from "./emit.js";
2
+ export { gitignoreCoversRunSummary, type ResolveRunSummaryDestinationOptions, resolveRunSummaryDestination, } from "./path.js";
3
+ export { computeRitualGateShare, parseRunSummaryJsonl, type RitualGateShare, readToolTurnDenominator, } from "./share.js";
4
+ export { type AcceptanceClauseOutcomeRow, type AcceptanceRunSummaryOutcome, type AcceptanceRunSummaryPayload, type AcceptanceStampRunSummaryPayload, type CheckGateOutcome, type CheckInvocationRunSummaryPayload, DEFAULT_RUN_SUMMARY_BASENAME, type DialEscalationEvaluationOutcome, type DialEscalationEvaluationRunSummaryPayload, type DialTransitionRunSummaryPayload, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_EVENT_KINDS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, type RunSummaryBaseFields, type RunSummaryDestination, type RunSummaryEventKind, type RunSummaryLine, type RunSummaryPayload, type SessionStartRunSummaryPayload, type ToolTurnDenominatorRunSummaryPayload, type VerificationOutcome, type VerificationRunSummaryPayload, } from "./types.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,5 @@
1
+ export { emitRunSummaryEvent, RunSummaryEmitter, } from "./emit.js";
2
+ export { gitignoreCoversRunSummary, resolveRunSummaryDestination, } from "./path.js";
3
+ export { computeRitualGateShare, parseRunSummaryJsonl, readToolTurnDenominator, } from "./share.js";
4
+ export { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_EVENT_KINDS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Resolve where run-summary JSONL should go (#3282 emission matrix).
3
+ *
4
+ * - `DEFT_RUN_SUMMARY_PATH=<path>` → append JSONL there (explicit; warn once on write fail)
5
+ * - `DEFT_RUN_SUMMARY_PATH=-` → stdout lines prefixed `DEFT-TLM:`
6
+ * - unset → repo-root `.deft-run-summary.json` only when .gitignore covers it; else silent
7
+ */
8
+ import { type RunSummaryDestination } from "./types.js";
9
+ export { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
10
+ /**
11
+ * True when a .gitignore line covers the default run-summary basename.
12
+ * Matches exact line or trailing slash variants; ignores comments.
13
+ */
14
+ export declare function gitignoreCoversRunSummary(projectRoot: string, basename?: string): boolean;
15
+ export interface ResolveRunSummaryDestinationOptions {
16
+ readonly env?: NodeJS.ProcessEnv;
17
+ /** Test seam for gitignore coverage. */
18
+ readonly gitignoreCovers?: (projectRoot: string) => boolean;
19
+ }
20
+ /**
21
+ * Resolve emission destination for the run-summary matrix (#3282).
22
+ * Never throws — fail-open consumers treat resolution errors as silent.
23
+ */
24
+ export declare function resolveRunSummaryDestination(projectRoot: string, options?: ResolveRunSummaryDestinationOptions): RunSummaryDestination;
25
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Resolve where run-summary JSONL should go (#3282 emission matrix).
3
+ *
4
+ * - `DEFT_RUN_SUMMARY_PATH=<path>` → append JSONL there (explicit; warn once on write fail)
5
+ * - `DEFT_RUN_SUMMARY_PATH=-` → stdout lines prefixed `DEFT-TLM:`
6
+ * - unset → repo-root `.deft-run-summary.json` only when .gitignore covers it; else silent
7
+ */
8
+ import { existsSync, readFileSync } from "node:fs";
9
+ import { isAbsolute, join, resolve } from "node:path";
10
+ import { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, } from "./types.js";
11
+ export { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
12
+ /**
13
+ * True when a .gitignore line covers the default run-summary basename.
14
+ * Matches exact line or trailing slash variants; ignores comments.
15
+ */
16
+ export function gitignoreCoversRunSummary(projectRoot, basename = DEFAULT_RUN_SUMMARY_BASENAME) {
17
+ const giPath = join(projectRoot, ".gitignore");
18
+ if (!existsSync(giPath)) {
19
+ return false;
20
+ }
21
+ let text;
22
+ try {
23
+ text = readFileSync(giPath, "utf8");
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ const targets = new Set([basename, basename.replace(/^\.\//, "")]);
29
+ for (const raw of text.split(/\r?\n/)) {
30
+ const line = raw.trim();
31
+ if (line.length === 0 || line.startsWith("#"))
32
+ continue;
33
+ // Strip trailing inline comments carefully (space + #).
34
+ const bare = line.replace(/\s+#.*$/, "").trim();
35
+ if (targets.has(bare) || targets.has(bare.replace(/^\//, ""))) {
36
+ return true;
37
+ }
38
+ }
39
+ return false;
40
+ }
41
+ /**
42
+ * Resolve emission destination for the run-summary matrix (#3282).
43
+ * Never throws — fail-open consumers treat resolution errors as silent.
44
+ */
45
+ export function resolveRunSummaryDestination(projectRoot, options = {}) {
46
+ try {
47
+ const env = options.env ?? process.env;
48
+ const raw = env[ENV_RUN_SUMMARY_PATH];
49
+ if (raw !== undefined && raw.trim() === "-") {
50
+ return { kind: "stdout" };
51
+ }
52
+ if (raw !== undefined && raw.trim().length > 0) {
53
+ const trimmed = raw.trim();
54
+ const path = isAbsolute(trimmed) ? resolve(trimmed) : resolve(projectRoot, trimmed);
55
+ return {
56
+ kind: "file",
57
+ path,
58
+ truncateOnSessionStart: false,
59
+ explicit: true,
60
+ };
61
+ }
62
+ // Unset: default path only with gitignore coverage.
63
+ const covers = options.gitignoreCovers?.(projectRoot) ?? gitignoreCoversRunSummary(projectRoot);
64
+ if (!covers) {
65
+ return { kind: "silent" };
66
+ }
67
+ return {
68
+ kind: "file",
69
+ path: resolve(projectRoot, DEFAULT_RUN_SUMMARY_BASENAME),
70
+ truncateOnSessionStart: true,
71
+ explicit: false,
72
+ };
73
+ }
74
+ catch {
75
+ return { kind: "silent" };
76
+ }
77
+ }
78
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Ritual+gate share from a #3282 run-summary stream (#3320).
3
+ *
4
+ * Share is ritual+gate invocations over the emitted tool/turn denominator.
5
+ * Absence of the denominator is unevaluable — never invent a share from counts.
6
+ */
7
+ import { type RunSummaryLine } from "./types.js";
8
+ export interface RitualGateShare {
9
+ readonly evaluable: boolean;
10
+ readonly ritualGateCount: number;
11
+ readonly totalToolTurns: number | null;
12
+ readonly share: number | null;
13
+ }
14
+ /** True when n is a finite integer usable as a share denominator. */
15
+ export declare function isValidToolTurnDenominator(value: unknown): value is number;
16
+ /** Read total_tool_turns from a line (top-level field or payload). */
17
+ export declare function readToolTurnDenominator(line: unknown): number | undefined;
18
+ /** Parse JSONL (or DEFT-TLM: prefixed stdout capture) into run-summary lines. */
19
+ export declare function parseRunSummaryJsonl(text: string): RunSummaryLine[];
20
+ /**
21
+ * Compute ritual+gate share from summary lines alone.
22
+ * Uses the latest session in the stream. Missing/invalid denominator → unevaluable.
23
+ */
24
+ export declare function computeRitualGateShare(lines: readonly RunSummaryLine[]): RitualGateShare;
25
+ //# sourceMappingURL=share.d.ts.map
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Ritual+gate share from a #3282 run-summary stream (#3320).
3
+ *
4
+ * Share is ritual+gate invocations over the emitted tool/turn denominator.
5
+ * Absence of the denominator is unevaluable — never invent a share from counts.
6
+ */
7
+ import { RUN_SUMMARY_STDOUT_PREFIX } from "./types.js";
8
+ function asRecord(value) {
9
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
10
+ return null;
11
+ }
12
+ return value;
13
+ }
14
+ /** True when n is a finite integer usable as a share denominator. */
15
+ export function isValidToolTurnDenominator(value) {
16
+ return (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value) && value > 0);
17
+ }
18
+ /** Read total_tool_turns from a line (top-level field or payload). */
19
+ export function readToolTurnDenominator(line) {
20
+ const rec = asRecord(line);
21
+ if (rec === null) {
22
+ return undefined;
23
+ }
24
+ if (isValidToolTurnDenominator(rec.total_tool_turns)) {
25
+ return rec.total_tool_turns;
26
+ }
27
+ const payload = asRecord(rec.payload);
28
+ if (payload !== null && isValidToolTurnDenominator(payload.total_tool_turns)) {
29
+ return payload.total_tool_turns;
30
+ }
31
+ return undefined;
32
+ }
33
+ function parseLine(raw) {
34
+ const trimmed = raw.trim();
35
+ if (trimmed.length === 0) {
36
+ return null;
37
+ }
38
+ const body = trimmed.startsWith(RUN_SUMMARY_STDOUT_PREFIX)
39
+ ? trimmed.slice(RUN_SUMMARY_STDOUT_PREFIX.length)
40
+ : trimmed;
41
+ try {
42
+ const parsed = JSON.parse(body);
43
+ const rec = asRecord(parsed);
44
+ if (rec === null) {
45
+ return null;
46
+ }
47
+ if (typeof rec.event !== "string" || typeof rec.session_id !== "string") {
48
+ return null;
49
+ }
50
+ return parsed;
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ /** Parse JSONL (or DEFT-TLM: prefixed stdout capture) into run-summary lines. */
57
+ export function parseRunSummaryJsonl(text) {
58
+ const lines = [];
59
+ for (const raw of text.split(/\r?\n/)) {
60
+ const line = parseLine(raw);
61
+ if (line !== null) {
62
+ lines.push(line);
63
+ }
64
+ }
65
+ return lines;
66
+ }
67
+ function lastSessionId(lines) {
68
+ let lastStart = null;
69
+ let lastAny = null;
70
+ for (const line of lines) {
71
+ lastAny = line.session_id;
72
+ if (line.event === "session_start") {
73
+ lastStart = line.session_id;
74
+ }
75
+ }
76
+ return lastStart ?? lastAny;
77
+ }
78
+ /**
79
+ * Compute ritual+gate share from summary lines alone.
80
+ * Uses the latest session in the stream. Missing/invalid denominator → unevaluable.
81
+ */
82
+ export function computeRitualGateShare(lines) {
83
+ const sessionId = lastSessionId(lines);
84
+ const sessionLines = sessionId === null ? [] : lines.filter((line) => line.session_id === sessionId);
85
+ let ritualGateCount = 0;
86
+ let totalToolTurns = null;
87
+ for (const line of sessionLines) {
88
+ if (line.event === "check_invocation") {
89
+ ritualGateCount += 1;
90
+ }
91
+ const denom = readToolTurnDenominator(line);
92
+ if (denom !== undefined) {
93
+ totalToolTurns = denom;
94
+ }
95
+ }
96
+ if (totalToolTurns === null) {
97
+ return { evaluable: false, ritualGateCount, totalToolTurns: null, share: null };
98
+ }
99
+ return {
100
+ evaluable: true,
101
+ ritualGateCount,
102
+ totalToolTurns,
103
+ share: ritualGateCount / totalToolTurns,
104
+ };
105
+ }
106
+ //# sourceMappingURL=share.js.map
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Run-summary telemetry types (#3282).
3
+ *
4
+ * Event-driven JSONL lines for harness collection. Silent by default unless
5
+ * `DEFT_RUN_SUMMARY_PATH` is set (or the gitignored default path is writable).
6
+ */
7
+ export declare const RUN_SUMMARY_SCHEMA_VERSION: 1;
8
+ /** Env var that opts into / redirects run-summary JSONL (#3282). */
9
+ export declare const ENV_RUN_SUMMARY_PATH = "DEFT_RUN_SUMMARY_PATH";
10
+ /** Harness-supplied session tool/turn total stamped onto run-summary lines (#3320). */
11
+ export declare const ENV_TOTAL_TOOL_TURNS = "DEFT_TOTAL_TOOL_TURNS";
12
+ /** Repo-root default collectible path when gitignore coverage is present. */
13
+ export declare const DEFAULT_RUN_SUMMARY_BASENAME = ".deft-run-summary.json";
14
+ /** Prefix for stdout lines when `DEFT_RUN_SUMMARY_PATH=-`. */
15
+ export declare const RUN_SUMMARY_STDOUT_PREFIX = "DEFT-TLM:";
16
+ /** One warning when an explicitly requested write fails. */
17
+ export declare const RUN_SUMMARY_WRITE_WARNING = "[deft run-summary] failed to write DEFT_RUN_SUMMARY_PATH telemetry (fail-open; exit codes unchanged)";
18
+ export declare const RUN_SUMMARY_EVENT_KINDS: readonly ["session_start", "dial_transition", "dial_escalation_evaluation", "check_invocation", "tool_turn_denominator", "verification", "acceptance", "acceptance_stamp"];
19
+ export type RunSummaryEventKind = (typeof RUN_SUMMARY_EVENT_KINDS)[number];
20
+ export interface RunSummaryBaseFields {
21
+ readonly schema_version: typeof RUN_SUMMARY_SCHEMA_VERSION;
22
+ readonly session_id: string;
23
+ readonly framework_version: string;
24
+ /** Monotonic per-emitter sequence (1-based). */
25
+ readonly seq: number;
26
+ readonly ts: string;
27
+ readonly event: RunSummaryEventKind;
28
+ /**
29
+ * Session total tool/turn count (#3320). Present so ritual+gate share is
30
+ * computable from the summary alone. Absence means the #3286 trigger is unevaluable.
31
+ */
32
+ readonly total_tool_turns?: number;
33
+ }
34
+ export interface SessionStartRunSummaryPayload {
35
+ readonly ceremony_dial?: Record<string, unknown>;
36
+ readonly preflight?: Record<string, unknown>;
37
+ readonly ceremony_tier?: string;
38
+ readonly ready?: boolean;
39
+ readonly exit_code?: number;
40
+ /** #3286: orientation surfaces composed into session:start (graduation trigger). */
41
+ readonly orientation_call_count?: number;
42
+ readonly orientation_compact?: boolean;
43
+ readonly deposit_sha?: string;
44
+ readonly orientation_later_status?: string;
45
+ readonly orientation_sections?: readonly Record<string, unknown>[];
46
+ }
47
+ export interface DialTransitionRunSummaryPayload {
48
+ readonly from: string | null;
49
+ readonly to: string;
50
+ readonly reason: string;
51
+ readonly evidence?: string;
52
+ }
53
+ /** One evaluation of escalate-on-evidence (#3319). Absence of this event ≠ declined. */
54
+ export type DialEscalationEvaluationOutcome = "escalated" | "declined";
55
+ export interface DialEscalationEvaluationRunSummaryPayload {
56
+ /** Ceremony depth considered (the start/current tier under evaluation). */
57
+ readonly tier: string;
58
+ readonly outcome: DialEscalationEvaluationOutcome;
59
+ readonly reason: string;
60
+ }
61
+ export interface CheckGateOutcome {
62
+ readonly id: string;
63
+ readonly status: "run" | "skipped" | "failed";
64
+ readonly exit_code?: number;
65
+ readonly cause?: string;
66
+ readonly remedy?: string;
67
+ readonly from_cache?: boolean;
68
+ }
69
+ /** Per-clause coverage row carried on verify:ac telemetry (#3323). */
70
+ export interface AcceptanceClauseOutcomeRow {
71
+ readonly id: number;
72
+ readonly outcome: "verified" | "unverifiable" | "failed";
73
+ }
74
+ export interface CheckInvocationRunSummaryPayload {
75
+ readonly target: string;
76
+ readonly exit_code: number;
77
+ readonly degraded?: boolean;
78
+ readonly gates: readonly CheckGateOutcome[];
79
+ /** Additive verify:ac fields (#3323). Consumers ignore unknown keys. */
80
+ readonly source_rung?: string;
81
+ readonly none_stated?: boolean;
82
+ readonly clause_outcomes?: readonly AcceptanceClauseOutcomeRow[];
83
+ }
84
+ /** Total tool/turn count for the session (#3320). */
85
+ export interface ToolTurnDenominatorRunSummaryPayload {
86
+ readonly total_tool_turns: number;
87
+ }
88
+ /** Product-oracle attempt (#3322). Same emitter as #3319 / #3320. */
89
+ export type VerificationOutcome = "pass" | "fail";
90
+ /** verify:ac resolution telemetry (#3334). Distinct from verification pass/fail. */
91
+ export type AcceptanceRunSummaryOutcome = "verified-pass" | "empty-pass" | "soft_empty" | "fail";
92
+ export interface AcceptanceRunSummaryPayload {
93
+ readonly resolved_command_count: number;
94
+ readonly outcome: AcceptanceRunSummaryOutcome;
95
+ readonly source_rung?: string;
96
+ readonly none_stated?: boolean;
97
+ readonly clause_count?: number;
98
+ readonly clause_outcomes?: readonly AcceptanceClauseOutcomeRow[];
99
+ }
100
+ /** Intake-time stamp: which rung locked, whether commands were stated, counts (#3323). */
101
+ export interface AcceptanceStampRunSummaryPayload {
102
+ readonly rung: string;
103
+ readonly none_stated: boolean;
104
+ readonly command_count: number;
105
+ readonly clause_count: number;
106
+ }
107
+ export interface VerificationRunSummaryPayload {
108
+ readonly check_id: string;
109
+ readonly method_fingerprint: string;
110
+ readonly outcome: VerificationOutcome;
111
+ /**
112
+ * True when both sides of the comparison were rebuilt from scratch by a
113
+ * different method than the one that failed. Required to resolve
114
+ * fail → method-change → pass (#3322).
115
+ */
116
+ readonly independent_rederivation?: boolean;
117
+ }
118
+ export type RunSummaryPayload = SessionStartRunSummaryPayload | DialTransitionRunSummaryPayload | DialEscalationEvaluationRunSummaryPayload | CheckInvocationRunSummaryPayload | ToolTurnDenominatorRunSummaryPayload | VerificationRunSummaryPayload | AcceptanceRunSummaryPayload | AcceptanceStampRunSummaryPayload;
119
+ export type RunSummaryLine = RunSummaryBaseFields & {
120
+ readonly payload: RunSummaryPayload;
121
+ };
122
+ export type RunSummaryDestination = {
123
+ readonly kind: "silent";
124
+ } | {
125
+ readonly kind: "stdout";
126
+ } | {
127
+ readonly kind: "file";
128
+ readonly path: string;
129
+ /** Truncate default-path file on session_start only. */
130
+ readonly truncateOnSessionStart: boolean;
131
+ /** Explicit path (env set) vs default gitignored path. */
132
+ readonly explicit: boolean;
133
+ };
134
+ //# sourceMappingURL=types.d.ts.map