@deftai/directive-core 0.101.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.
- package/dist/authz/classify.js +97 -8
- package/dist/check/cached-orchestrator.d.ts +4 -0
- package/dist/check/cached-orchestrator.js +70 -5
- package/dist/check/cli-native-gates.d.ts +43 -0
- package/dist/check/cli-native-gates.js +84 -0
- package/dist/check/index.d.ts +1 -0
- package/dist/check/index.js +1 -0
- package/dist/check/named-cause.js +10 -0
- package/dist/doctor/checks.d.ts +5 -6
- package/dist/doctor/checks.js +13 -56
- package/dist/eval/later-graduation.d.ts +39 -0
- package/dist/eval/later-graduation.js +91 -0
- package/dist/eval/report.d.ts +6 -0
- package/dist/eval/report.js +27 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/intake/issue-ingest.js +42 -0
- package/dist/policy/ceremony-dial-escalation.d.ts +52 -0
- package/dist/policy/ceremony-dial-escalation.js +92 -0
- package/dist/policy/ceremony-dial.d.ts +3 -1
- package/dist/policy/ceremony-dial.js +52 -1
- package/dist/policy/check-resume.d.ts +9 -38
- package/dist/policy/check-resume.js +18 -156
- package/dist/policy/coverage-debt.d.ts +18 -37
- package/dist/policy/coverage-debt.js +38 -147
- package/dist/policy/index.d.ts +1 -1
- package/dist/policy/index.js +1 -1
- package/dist/product-first-done-gate/acceptance.js +22 -0
- package/dist/product-first-done-gate/empty-resolution.d.ts +26 -0
- package/dist/product-first-done-gate/empty-resolution.js +38 -0
- package/dist/product-first-done-gate/evaluate.d.ts +28 -2
- package/dist/product-first-done-gate/evaluate.js +209 -53
- package/dist/product-first-done-gate/index.d.ts +1 -0
- package/dist/product-first-done-gate/index.js +1 -0
- package/dist/product-first-done-gate/types.d.ts +3 -0
- package/dist/product-first-done-gate/types.js +0 -7
- package/dist/run-summary/emit.d.ts +11 -1
- package/dist/run-summary/emit.js +48 -2
- package/dist/run-summary/index.d.ts +2 -1
- package/dist/run-summary/index.js +2 -1
- package/dist/run-summary/path.d.ts +1 -1
- package/dist/run-summary/path.js +1 -1
- package/dist/run-summary/share.d.ts +25 -0
- package/dist/run-summary/share.js +106 -0
- package/dist/run-summary/types.d.ts +60 -2
- package/dist/run-summary/types.js +7 -0
- package/dist/scope/acceptance-activate-gate.d.ts +23 -0
- package/dist/scope/acceptance-activate-gate.js +70 -0
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/transition.js +5 -0
- package/dist/session/index.d.ts +0 -1
- package/dist/session/index.js +0 -1
- package/dist/session/orientation-compression.js +19 -9
- package/dist/session/session-start.d.ts +6 -0
- package/dist/session/session-start.js +44 -22
- package/dist/session/toolchain-preflight.d.ts +15 -0
- package/dist/session/toolchain-preflight.js +65 -5
- package/dist/verify-ac/clauses.d.ts +51 -0
- package/dist/verify-ac/clauses.js +490 -0
- package/dist/verify-ac/evaluate.d.ts +55 -0
- package/dist/verify-ac/evaluate.js +145 -0
- package/dist/verify-ac/flag.d.ts +35 -0
- package/dist/verify-ac/flag.js +104 -0
- package/dist/verify-ac/index.d.ts +10 -0
- package/dist/verify-ac/index.js +10 -0
- package/package.json +7 -3
- package/dist/policy/coverage-check-resume-presets.d.ts +0 -46
- package/dist/policy/coverage-check-resume-presets.js +0 -228
- package/dist/session/coverage-check-resume-nudge.d.ts +0 -34
- package/dist/session/coverage-check-resume-nudge.js +0 -66
package/dist/run-summary/emit.js
CHANGED
|
@@ -11,7 +11,7 @@ import { readCorePackageVersion } from "../engine-version.js";
|
|
|
11
11
|
import { containedWrite } from "../fs/contained-write.js";
|
|
12
12
|
import { assertWriteTargetSafe } from "../fs/projection-containment.js";
|
|
13
13
|
import { resolveRunSummaryDestination } from "./path.js";
|
|
14
|
-
import { RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
|
|
14
|
+
import { ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
|
|
15
15
|
function sortKeysDeep(value) {
|
|
16
16
|
if (Array.isArray(value)) {
|
|
17
17
|
return value.map(sortKeysDeep);
|
|
@@ -90,6 +90,7 @@ export class RunSummaryEmitter {
|
|
|
90
90
|
frameworkVersion;
|
|
91
91
|
now;
|
|
92
92
|
destination;
|
|
93
|
+
env;
|
|
93
94
|
writeStdout;
|
|
94
95
|
writeStderr;
|
|
95
96
|
constructor(options) {
|
|
@@ -97,10 +98,11 @@ export class RunSummaryEmitter {
|
|
|
97
98
|
this.sessionId = options.sessionId;
|
|
98
99
|
this.frameworkVersion = options.frameworkVersion ?? readCorePackageVersion();
|
|
99
100
|
this.now = options.now ?? (() => new Date());
|
|
101
|
+
this.env = options.env ?? process.env;
|
|
100
102
|
this.destination =
|
|
101
103
|
options.destination ??
|
|
102
104
|
resolveRunSummaryDestination(options.projectRoot, {
|
|
103
|
-
env:
|
|
105
|
+
env: this.env,
|
|
104
106
|
gitignoreCovers: options.gitignoreCovers,
|
|
105
107
|
});
|
|
106
108
|
this.writeStdout = options.writeStdout ?? ((line) => process.stdout.write(`${line}\n`));
|
|
@@ -115,6 +117,7 @@ export class RunSummaryEmitter {
|
|
|
115
117
|
return { emitted: false, destination: this.destination, line: null, warning: false };
|
|
116
118
|
}
|
|
117
119
|
this.seq += 1;
|
|
120
|
+
const denominator = readPayloadToolTurnDenominator(payload) ?? readEnvToolTurnDenominator(this.env);
|
|
118
121
|
const line = {
|
|
119
122
|
schema_version: RUN_SUMMARY_SCHEMA_VERSION,
|
|
120
123
|
session_id: this.sessionId,
|
|
@@ -123,6 +126,7 @@ export class RunSummaryEmitter {
|
|
|
123
126
|
ts: this.now().toISOString(),
|
|
124
127
|
event,
|
|
125
128
|
payload,
|
|
129
|
+
...(denominator !== undefined ? { total_tool_turns: denominator } : {}),
|
|
126
130
|
};
|
|
127
131
|
const text = lineToJson(line);
|
|
128
132
|
if (this.destination.kind === "stdout") {
|
|
@@ -156,9 +160,51 @@ export class RunSummaryEmitter {
|
|
|
156
160
|
emitDialTransition(payload) {
|
|
157
161
|
return this.emit("dial_transition", payload);
|
|
158
162
|
}
|
|
163
|
+
emitDialEscalationEvaluation(payload) {
|
|
164
|
+
return this.emit("dial_escalation_evaluation", payload);
|
|
165
|
+
}
|
|
159
166
|
emitCheckInvocation(payload) {
|
|
160
167
|
return this.emit("check_invocation", payload);
|
|
161
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;
|
|
162
208
|
}
|
|
163
209
|
/**
|
|
164
210
|
* One-shot helper for call sites that do not hold an emitter (e.g. dial escalate).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { type EmitRunSummaryResult, emitRunSummaryEvent, RunSummaryEmitter, type RunSummaryEmitterOptions, } from "./emit.js";
|
|
2
2
|
export { gitignoreCoversRunSummary, type ResolveRunSummaryDestinationOptions, resolveRunSummaryDestination, } from "./path.js";
|
|
3
|
-
export {
|
|
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";
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { emitRunSummaryEvent, RunSummaryEmitter, } from "./emit.js";
|
|
2
2
|
export { gitignoreCoversRunSummary, resolveRunSummaryDestination, } from "./path.js";
|
|
3
|
-
export {
|
|
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";
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - unset → repo-root `.deft-run-summary.json` only when .gitignore covers it; else silent
|
|
7
7
|
*/
|
|
8
8
|
import { type RunSummaryDestination } from "./types.js";
|
|
9
|
-
export { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } 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
10
|
/**
|
|
11
11
|
* True when a .gitignore line covers the default run-summary basename.
|
|
12
12
|
* Matches exact line or trailing slash variants; ignores comments.
|
package/dist/run-summary/path.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
9
9
|
import { isAbsolute, join, resolve } from "node:path";
|
|
10
10
|
import { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, } from "./types.js";
|
|
11
|
-
export { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } 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
12
|
/**
|
|
13
13
|
* True when a .gitignore line covers the default run-summary basename.
|
|
14
14
|
* Matches exact line or trailing slash variants; ignores comments.
|
|
@@ -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
|
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
export declare const RUN_SUMMARY_SCHEMA_VERSION: 1;
|
|
8
8
|
/** Env var that opts into / redirects run-summary JSONL (#3282). */
|
|
9
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";
|
|
10
12
|
/** Repo-root default collectible path when gitignore coverage is present. */
|
|
11
13
|
export declare const DEFAULT_RUN_SUMMARY_BASENAME = ".deft-run-summary.json";
|
|
12
14
|
/** Prefix for stdout lines when `DEFT_RUN_SUMMARY_PATH=-`. */
|
|
13
15
|
export declare const RUN_SUMMARY_STDOUT_PREFIX = "DEFT-TLM:";
|
|
14
16
|
/** One warning when an explicitly requested write fails. */
|
|
15
17
|
export declare const RUN_SUMMARY_WRITE_WARNING = "[deft run-summary] failed to write DEFT_RUN_SUMMARY_PATH telemetry (fail-open; exit codes unchanged)";
|
|
16
|
-
export declare const RUN_SUMMARY_EVENT_KINDS: readonly ["session_start", "dial_transition", "check_invocation"];
|
|
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"];
|
|
17
19
|
export type RunSummaryEventKind = (typeof RUN_SUMMARY_EVENT_KINDS)[number];
|
|
18
20
|
export interface RunSummaryBaseFields {
|
|
19
21
|
readonly schema_version: typeof RUN_SUMMARY_SCHEMA_VERSION;
|
|
@@ -23,6 +25,11 @@ export interface RunSummaryBaseFields {
|
|
|
23
25
|
readonly seq: number;
|
|
24
26
|
readonly ts: string;
|
|
25
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;
|
|
26
33
|
}
|
|
27
34
|
export interface SessionStartRunSummaryPayload {
|
|
28
35
|
readonly ceremony_dial?: Record<string, unknown>;
|
|
@@ -43,6 +50,14 @@ export interface DialTransitionRunSummaryPayload {
|
|
|
43
50
|
readonly reason: string;
|
|
44
51
|
readonly evidence?: string;
|
|
45
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
|
+
}
|
|
46
61
|
export interface CheckGateOutcome {
|
|
47
62
|
readonly id: string;
|
|
48
63
|
readonly status: "run" | "skipped" | "failed";
|
|
@@ -51,13 +66,56 @@ export interface CheckGateOutcome {
|
|
|
51
66
|
readonly remedy?: string;
|
|
52
67
|
readonly from_cache?: boolean;
|
|
53
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
|
+
}
|
|
54
74
|
export interface CheckInvocationRunSummaryPayload {
|
|
55
75
|
readonly target: string;
|
|
56
76
|
readonly exit_code: number;
|
|
57
77
|
readonly degraded?: boolean;
|
|
58
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;
|
|
59
117
|
}
|
|
60
|
-
export type RunSummaryPayload = SessionStartRunSummaryPayload | DialTransitionRunSummaryPayload | CheckInvocationRunSummaryPayload;
|
|
118
|
+
export type RunSummaryPayload = SessionStartRunSummaryPayload | DialTransitionRunSummaryPayload | DialEscalationEvaluationRunSummaryPayload | CheckInvocationRunSummaryPayload | ToolTurnDenominatorRunSummaryPayload | VerificationRunSummaryPayload | AcceptanceRunSummaryPayload | AcceptanceStampRunSummaryPayload;
|
|
61
119
|
export type RunSummaryLine = RunSummaryBaseFields & {
|
|
62
120
|
readonly payload: RunSummaryPayload;
|
|
63
121
|
};
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
export const RUN_SUMMARY_SCHEMA_VERSION = 1;
|
|
8
8
|
/** Env var that opts into / redirects run-summary JSONL (#3282). */
|
|
9
9
|
export const ENV_RUN_SUMMARY_PATH = "DEFT_RUN_SUMMARY_PATH";
|
|
10
|
+
/** Harness-supplied session tool/turn total stamped onto run-summary lines (#3320). */
|
|
11
|
+
export const ENV_TOTAL_TOOL_TURNS = "DEFT_TOTAL_TOOL_TURNS";
|
|
10
12
|
/** Repo-root default collectible path when gitignore coverage is present. */
|
|
11
13
|
export const DEFAULT_RUN_SUMMARY_BASENAME = ".deft-run-summary.json";
|
|
12
14
|
/** Prefix for stdout lines when `DEFT_RUN_SUMMARY_PATH=-`. */
|
|
@@ -16,6 +18,11 @@ export const RUN_SUMMARY_WRITE_WARNING = "[deft run-summary] failed to write DEF
|
|
|
16
18
|
export const RUN_SUMMARY_EVENT_KINDS = [
|
|
17
19
|
"session_start",
|
|
18
20
|
"dial_transition",
|
|
21
|
+
"dial_escalation_evaluation",
|
|
19
22
|
"check_invocation",
|
|
23
|
+
"tool_turn_denominator",
|
|
24
|
+
"verification",
|
|
25
|
+
"acceptance",
|
|
26
|
+
"acceptance_stamp",
|
|
20
27
|
];
|
|
21
28
|
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scope:activate refuses acceptance-shaped narratives without plan.acceptance (#3334).
|
|
3
|
+
*
|
|
4
|
+
* Agents author Test / AcceptanceCriteria / Verification prose in narratives
|
|
5
|
+
* and then skip the executable plan.acceptance block. Activation fails closed
|
|
6
|
+
* with one remediation: stamp the acceptance block.
|
|
7
|
+
*/
|
|
8
|
+
export interface AcceptanceActivateHit {
|
|
9
|
+
readonly key: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AcceptanceActivateGateResult {
|
|
12
|
+
readonly ok: boolean;
|
|
13
|
+
readonly message: string;
|
|
14
|
+
readonly hits: readonly AcceptanceActivateHit[];
|
|
15
|
+
}
|
|
16
|
+
/** Narrative keys that look like authored acceptance (non-empty string values). */
|
|
17
|
+
export declare function collectAcceptanceShapedNarrativeKeys(narratives: Record<string, unknown>): AcceptanceActivateHit[];
|
|
18
|
+
/**
|
|
19
|
+
* Fail closed when narratives carry acceptance-shaped keys and plan.acceptance
|
|
20
|
+
* is absent. A stamped (even empty/none_stated) block is enough.
|
|
21
|
+
*/
|
|
22
|
+
export declare function evaluateAcceptanceActivateGate(plan: Record<string, unknown>): AcceptanceActivateGateResult;
|
|
23
|
+
//# sourceMappingURL=acceptance-activate-gate.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scope:activate refuses acceptance-shaped narratives without plan.acceptance (#3334).
|
|
3
|
+
*
|
|
4
|
+
* Agents author Test / AcceptanceCriteria / Verification prose in narratives
|
|
5
|
+
* and then skip the executable plan.acceptance block. Activation fails closed
|
|
6
|
+
* with one remediation: stamp the acceptance block.
|
|
7
|
+
*/
|
|
8
|
+
import { validatePlanAcceptance } from "../product-first-done-gate/acceptance.js";
|
|
9
|
+
function asRecord(value) {
|
|
10
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const ACCEPTANCE_SHAPED_KEYS = new Set(["test", "acceptancecriteria", "verification"]);
|
|
16
|
+
function normalizeNarrativeKey(key) {
|
|
17
|
+
return key.replace(/[\s_-]+/g, "").toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
/** Narrative keys that look like authored acceptance (non-empty string values). */
|
|
20
|
+
export function collectAcceptanceShapedNarrativeKeys(narratives) {
|
|
21
|
+
const hits = [];
|
|
22
|
+
for (const key of Object.keys(narratives)) {
|
|
23
|
+
if (!ACCEPTANCE_SHAPED_KEYS.has(normalizeNarrativeKey(key))) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const value = narratives[key];
|
|
27
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
hits.push({ key });
|
|
31
|
+
}
|
|
32
|
+
return hits;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Fail closed when narratives carry acceptance-shaped keys and plan.acceptance
|
|
36
|
+
* is absent. A stamped (even empty/none_stated) block is enough.
|
|
37
|
+
*/
|
|
38
|
+
export function evaluateAcceptanceActivateGate(plan) {
|
|
39
|
+
const narratives = asRecord(plan.narratives);
|
|
40
|
+
if (narratives === null) {
|
|
41
|
+
return { ok: true, message: "", hits: [] };
|
|
42
|
+
}
|
|
43
|
+
const hits = collectAcceptanceShapedNarrativeKeys(narratives);
|
|
44
|
+
if (hits.length === 0) {
|
|
45
|
+
return { ok: true, message: "", hits: [] };
|
|
46
|
+
}
|
|
47
|
+
if (plan.acceptance !== undefined && plan.acceptance !== null) {
|
|
48
|
+
const schemaErrors = validatePlanAcceptance(plan.acceptance);
|
|
49
|
+
if (schemaErrors.length > 0) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
message: `Refusing activate: plan.acceptance is present but invalid (#3334): ` +
|
|
53
|
+
`${schemaErrors.join("; ")}. Stamp a valid plan.acceptance block ` +
|
|
54
|
+
`(author commands or none_stated via intake capture / task issue:ingest).`,
|
|
55
|
+
hits,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return { ok: true, message: "", hits };
|
|
59
|
+
}
|
|
60
|
+
const keys = hits.map((h) => h.key).join(", ");
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
message: `Refusing activate: narratives contain acceptance-shaped keys (${keys}) ` +
|
|
64
|
+
`but plan.acceptance is absent (#3334). Stamp plan.acceptance ` +
|
|
65
|
+
`(author commands or none_stated via intake capture / task issue:ingest) — ` +
|
|
66
|
+
`the criteria are in the wrong, non-executable field.`,
|
|
67
|
+
hits,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=acceptance-activate-gate.js.map
|
package/dist/scope/index.d.ts
CHANGED
package/dist/scope/index.js
CHANGED
package/dist/scope/transition.js
CHANGED
|
@@ -5,6 +5,7 @@ import { assertProjectionContained, ProjectionContainmentError, } from "../fs/pr
|
|
|
5
5
|
import { hasArtifactSuffix } from "../layout/resolve.js";
|
|
6
6
|
import { evaluateCompletedPlanConsistency } from "../lifecycle/completed-consistency.js";
|
|
7
7
|
import { evaluateLiteralAcceptanceFromPlan } from "../literal-acceptance/index.js";
|
|
8
|
+
import { evaluateAcceptanceActivateGate } from "./acceptance-activate-gate.js";
|
|
8
9
|
import { evaluateAcceptanceEvidenceGate, formatAcceptanceCompletionListing, } from "./acceptance-evidence.js";
|
|
9
10
|
import { append, canonicalLogPath, newDecisionId } from "./audit-log.js";
|
|
10
11
|
import { atomicWriteBrief, formatBriefJson, readBriefForMutation } from "./brief-io.js";
|
|
@@ -148,6 +149,10 @@ export function runTransition(action, filePath, now = new Date(), options = {})
|
|
|
148
149
|
if (!effortGate.ok) {
|
|
149
150
|
return { ok: false, message: effortGate.message };
|
|
150
151
|
}
|
|
152
|
+
const acceptanceGate = evaluateAcceptanceActivateGate(planObj);
|
|
153
|
+
if (!acceptanceGate.ok) {
|
|
154
|
+
return { ok: false, message: acceptanceGate.message };
|
|
155
|
+
}
|
|
151
156
|
}
|
|
152
157
|
// #3041: fail closed before mutating a code-bearing complete without delivery evidence.
|
|
153
158
|
if (act === "complete") {
|
package/dist/session/index.d.ts
CHANGED
package/dist/session/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* agents:refresh and verify:cache-fresh; formats compact (opt-in) vs verbose
|
|
7
7
|
* (default). Does NOT implement unified `deft orient` (Later remains open).
|
|
8
8
|
*/
|
|
9
|
+
import { isFrameworkRepoRoot } from "../check/context.js";
|
|
9
10
|
import { decideThrottle, readState as readDoctorState, renderDoctorStatusLine, } from "../doctor/doctor-state.js";
|
|
10
11
|
import { cmdDoctor } from "../doctor/main.js";
|
|
11
12
|
import { applyAgentsRefresh } from "../platform/agents-md.js";
|
|
@@ -271,16 +272,23 @@ export function runCacheFreshOrientationSection(projectRoot, options) {
|
|
|
271
272
|
void options.now;
|
|
272
273
|
try {
|
|
273
274
|
const evaluate = options.evaluateFn ?? evaluateCacheFresh;
|
|
274
|
-
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
//
|
|
275
|
+
const consumerDeposit = !isFrameworkRepoRoot(projectRoot);
|
|
276
|
+
// Consumer deposits often have no triage cache. Treat that as a named
|
|
277
|
+
// bootstrap cause (dirty), never a generic orientation `error` (#3335).
|
|
278
|
+
// Framework source still evaluates live; missing cache stays non-green
|
|
279
|
+
// (#3286) so gated verify re-runs cache_fresh.
|
|
278
280
|
const result = evaluate(projectRoot, {
|
|
279
281
|
allowMissingBootstrap: false,
|
|
280
282
|
autoPopulateEmpty: false,
|
|
281
283
|
...options.evaluateOptions,
|
|
282
284
|
});
|
|
283
285
|
const ok = result.code === 0;
|
|
286
|
+
const missingCache = result.code === 2 && /not found|bootstrap/i.test(result.message);
|
|
287
|
+
const namedCause = missingCache
|
|
288
|
+
? consumerDeposit
|
|
289
|
+
? `[deft cache-fresh] triage cache not populated (consumer deposit) — not a toolchain failure. Recovery: run \`deft triage:bootstrap\` if you need the queue.`
|
|
290
|
+
: result.message
|
|
291
|
+
: result.message;
|
|
284
292
|
// Tag as sha_match only when message already uses the canonical phrase
|
|
285
293
|
// (e.g. tests inject it) — never invent a shortcut past evaluate.
|
|
286
294
|
const shaMatch = ok && result.message.includes(DEPOSIT_SHA_MATCH_NOOP);
|
|
@@ -288,19 +296,21 @@ export function runCacheFreshOrientationSection(projectRoot, options) {
|
|
|
288
296
|
? "sha_match"
|
|
289
297
|
: ok
|
|
290
298
|
? "ok"
|
|
291
|
-
:
|
|
292
|
-
? "
|
|
293
|
-
:
|
|
299
|
+
: missingCache
|
|
300
|
+
? "dirty"
|
|
301
|
+
: result.code === 2
|
|
302
|
+
? "error"
|
|
303
|
+
: "dirty";
|
|
294
304
|
return {
|
|
295
305
|
name: "cache_fresh",
|
|
296
306
|
status,
|
|
297
307
|
ok,
|
|
298
308
|
exitCode: result.code,
|
|
299
|
-
lines: [
|
|
309
|
+
lines: [namedCause],
|
|
300
310
|
shaMatch,
|
|
301
311
|
depositSha: options.depositSha,
|
|
302
312
|
durationMs: elapsedMs(started),
|
|
303
|
-
detail: { code: result.code },
|
|
313
|
+
detail: { code: result.code, missing_cache: missingCache },
|
|
304
314
|
};
|
|
305
315
|
}
|
|
306
316
|
catch (exc) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type HostContentSurfaceSeams } from "../platform/host-content-surface.js";
|
|
2
2
|
import { type EnvironmentContext } from "../platform/shell-context.js";
|
|
3
3
|
import { type CeremonyDialInputs, type CeremonyDialSelection, type ProvisionalCeremonyEstimateHints } from "../policy/ceremony-dial.js";
|
|
4
|
+
import { type CeremonyStartTierProvenance } from "../policy/ceremony-dial-escalation.js";
|
|
4
5
|
import { type ProbeScmReadinessOptions, type ScmReadinessReport } from "../scm/readiness.js";
|
|
5
6
|
import { type ResolveUserMdResult } from "../user-config/resolve-user-md.js";
|
|
6
7
|
import { type DetectHardEffortBudgetInput } from "./effort-budget.js";
|
|
@@ -125,6 +126,11 @@ export interface SessionStartOptions {
|
|
|
125
126
|
* loads plan.policy.ceremonyDial and applies inputs (after provisional fill).
|
|
126
127
|
*/
|
|
127
128
|
readonly ceremonyDial?: CeremonyDialSelection;
|
|
129
|
+
/**
|
|
130
|
+
* #3319: start-tier provenance hint. CLI `--ceremony-depth` / harness pin
|
|
131
|
+
* should pass `external-pin`. Policy override is inferred as `operator`.
|
|
132
|
+
*/
|
|
133
|
+
readonly ceremonyDialStartTierProvenance?: CeremonyStartTierProvenance;
|
|
128
134
|
/**
|
|
129
135
|
* #3282: done-gate toolchain preflight seams (tests inject result or probe).
|
|
130
136
|
* When omitted, runs live preflight after verify_tools on cold path.
|