@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.
- package/dist/authz/classify.js +193 -27
- package/dist/authz/decompose-apply.d.ts +14 -0
- package/dist/authz/decompose-apply.js +93 -18
- package/dist/check/cached-orchestrator.d.ts +30 -5
- package/dist/check/cached-orchestrator.js +297 -10
- package/dist/check/cli-native-gates.d.ts +43 -0
- package/dist/check/cli-native-gates.js +84 -0
- package/dist/check/gate-lists.d.ts +10 -4
- package/dist/check/gate-lists.js +19 -5
- package/dist/check/index.d.ts +4 -2
- package/dist/check/index.js +3 -1
- package/dist/check/named-cause.d.ts +45 -0
- package/dist/check/named-cause.js +131 -0
- package/dist/check/orchestrator.js +6 -1
- package/dist/doctor/checks.d.ts +5 -6
- package/dist/doctor/checks.js +13 -56
- package/dist/doctor/help.d.ts +5 -0
- package/dist/doctor/help.js +31 -0
- package/dist/doctor/index.d.ts +2 -0
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/main.js +58 -0
- package/dist/doctor/session-coda.d.ts +82 -0
- package/dist/doctor/session-coda.js +151 -0
- package/dist/doctor/types.d.ts +16 -0
- 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 +3 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/gitignore.js +2 -0
- package/dist/intake/issue-ingest.js +49 -0
- package/dist/policy/ac-pass-banking.d.ts +58 -0
- package/dist/policy/ac-pass-banking.js +150 -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 +32 -1
- package/dist/policy/ceremony-dial.js +128 -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 +2 -1
- package/dist/policy/index.js +16 -2
- package/dist/preflight-cache/evaluate.d.ts +3 -0
- package/dist/preflight-cache/evaluate.js +3 -0
- package/dist/product-first-done-gate/acceptance.d.ts +25 -0
- package/dist/product-first-done-gate/acceptance.js +269 -0
- package/dist/product-first-done-gate/check-mode.d.ts +45 -0
- package/dist/product-first-done-gate/check-mode.js +148 -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 +76 -0
- package/dist/product-first-done-gate/evaluate.js +411 -0
- package/dist/product-first-done-gate/index.d.ts +12 -0
- package/dist/product-first-done-gate/index.js +12 -0
- package/dist/product-first-done-gate/types.d.ts +67 -0
- package/dist/product-first-done-gate/types.js +54 -0
- package/dist/run-summary/emit.d.ts +66 -0
- package/dist/run-summary/emit.js +217 -0
- package/dist/run-summary/index.d.ts +5 -0
- package/dist/run-summary/index.js +5 -0
- package/dist/run-summary/path.d.ts +25 -0
- package/dist/run-summary/path.js +78 -0
- package/dist/run-summary/share.d.ts +25 -0
- package/dist/run-summary/share.js +106 -0
- package/dist/run-summary/types.d.ts +134 -0
- package/dist/run-summary/types.js +28 -0
- package/dist/scope/acceptance-activate-gate.d.ts +23 -0
- package/dist/scope/acceptance-activate-gate.js +70 -0
- package/dist/scope/acceptance-evidence.d.ts +34 -0
- package/dist/scope/acceptance-evidence.js +84 -10
- package/dist/scope/decompose.d.ts +5 -0
- package/dist/scope/decompose.js +11 -2
- 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/ac-pass-banking.d.ts +221 -0
- package/dist/session/ac-pass-banking.js +761 -0
- package/dist/session/deposit-sha.d.ts +49 -0
- package/dist/session/deposit-sha.js +121 -0
- package/dist/session/effort-budget.d.ts +6 -0
- package/dist/session/effort-budget.js +21 -3
- package/dist/session/index.d.ts +5 -1
- package/dist/session/index.js +5 -1
- package/dist/session/orientation-compression.d.ts +127 -0
- package/dist/session/orientation-compression.js +435 -0
- package/dist/session/orientation-state.d.ts +29 -0
- package/dist/session/orientation-state.js +85 -0
- package/dist/session/session-start.d.ts +31 -0
- package/dist/session/session-start.js +219 -22
- package/dist/session/toolchain-preflight.d.ts +79 -0
- package/dist/session/toolchain-preflight.js +211 -0
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +8 -2
- package/dist/triage/bootstrap/index.js +15 -3
- package/dist/triage/classify/index.d.ts +1 -0
- package/dist/triage/classify/index.js +2 -0
- package/dist/triage/classify/label-mirror.js +16 -0
- package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
- package/dist/triage/classify/mirror-discovery-tip.js +234 -0
- package/dist/triage/reconcile/reconcile.js +28 -6
- package/dist/triage/summary/index.js +12 -4
- package/dist/triage/welcome/default-mode.js +16 -0
- package/dist/ts-check-lane/run-lane.js +21 -4
- package/dist/vbrief-validate/conformance.js +7 -0
- 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/dist/vitest-runner/coverage-debt-teardown.js +16 -4
- package/package.json +15 -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
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional deterministic session coda for interactive doctor success (#2712).
|
|
3
|
+
*
|
|
4
|
+
* Human TTY affordance only: never JSON, never AGENTS/skills, never exit-code
|
|
5
|
+
* changes. Env three-state DEFT_SESSION_CODA (unset / 1 / 0). Copy lives in
|
|
6
|
+
* content/doctor/session-coda.json — not embedded as an engine literal.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { contentRoot } from "../content-root.js";
|
|
11
|
+
/** Env var name (env-only v1; no USER.md key). */
|
|
12
|
+
export const SESSION_CODA_ENV = "DEFT_SESSION_CODA";
|
|
13
|
+
/**
|
|
14
|
+
* Fixed discoverability line when env is unset and gates pass (D3).
|
|
15
|
+
* Snapshot-stable; not selected from the pack; no ✦ prefix.
|
|
16
|
+
*/
|
|
17
|
+
export const SESSION_CODA_OFF_HINT = "Session coda: off (set DEFT_SESSION_CODA=1 to enable)";
|
|
18
|
+
/** U+2726 FOUR RAYED STAR + space — stable; do not localize. */
|
|
19
|
+
export const SESSION_CODA_STAR_PREFIX = "\u2726 ";
|
|
20
|
+
/** Max body length; longer pack entries are skipped (fail-open). */
|
|
21
|
+
export const SESSION_CODA_MAX_CHARS = 100;
|
|
22
|
+
/** Relative path under contentRoot (source: content/doctor/…; deposit: doctor/…). */
|
|
23
|
+
export const SESSION_CODA_REL_PATH = join("doctor", "session-coda.json");
|
|
24
|
+
/**
|
|
25
|
+
* Map DEFT_SESSION_CODA to the three-state mode (D3).
|
|
26
|
+
* - undefined / "" → unset (discoverability off-hint)
|
|
27
|
+
* - "1" → on (✦ coda)
|
|
28
|
+
* - "0" → off (silent)
|
|
29
|
+
* - any other non-empty value → unset (show correct enable form)
|
|
30
|
+
*/
|
|
31
|
+
export function sessionCodaMode(envValue) {
|
|
32
|
+
if (envValue === undefined || envValue === "")
|
|
33
|
+
return "unset";
|
|
34
|
+
if (envValue === "1")
|
|
35
|
+
return "on";
|
|
36
|
+
if (envValue === "0")
|
|
37
|
+
return "off";
|
|
38
|
+
return "unset";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether any extra line (off-hint or coda) is allowed (D1 + surface gates).
|
|
42
|
+
* TTY = stdout.isTTY; CI truthy blocks; json blocks; exitOk = errorCount === 0.
|
|
43
|
+
*/
|
|
44
|
+
export function shouldEmitSessionCodaLine(input) {
|
|
45
|
+
return input.exitOk === true && input.tty === true && input.ci !== true && input.json !== true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* FNV-1a 32-bit (#2712). Documented choice for hash_u32(date + "\\0" + root).
|
|
49
|
+
* No Math.random; same inputs ⇒ same u32 on every platform.
|
|
50
|
+
*/
|
|
51
|
+
export function hashU32(input) {
|
|
52
|
+
let hash = 0x811c9dc5;
|
|
53
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
54
|
+
hash ^= input.charCodeAt(i);
|
|
55
|
+
hash = Math.imul(hash, 0x01000193);
|
|
56
|
+
}
|
|
57
|
+
return hash >>> 0;
|
|
58
|
+
}
|
|
59
|
+
/** UTC calendar date yyyy-mm-dd from a Date (stable for 24h across a team day). */
|
|
60
|
+
export function utcDateYmd(date) {
|
|
61
|
+
return date.toISOString().slice(0, 10);
|
|
62
|
+
}
|
|
63
|
+
/** Best-effort realpath; falls back to the given path when realpath fails. */
|
|
64
|
+
export function projectRootRealpath(projectRoot) {
|
|
65
|
+
try {
|
|
66
|
+
return realpathSync(projectRoot);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return projectRoot;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Select one coda body from the pack (enabled path only).
|
|
74
|
+
* index = hash_u32(utc_date + "\\0" + project_root_realpath) % len(codas)
|
|
75
|
+
* Empty list → null (omit ✦; doctor still 0).
|
|
76
|
+
*/
|
|
77
|
+
export function selectSessionCoda(input) {
|
|
78
|
+
const { date, projectRoot, codas } = input;
|
|
79
|
+
if (codas.length === 0)
|
|
80
|
+
return null;
|
|
81
|
+
const key = `${date}\0${projectRoot}`;
|
|
82
|
+
const index = hashU32(key) % codas.length;
|
|
83
|
+
return codas[index] ?? null;
|
|
84
|
+
}
|
|
85
|
+
/** Skip non-strings and bodies over SESSION_CODA_MAX_CHARS. */
|
|
86
|
+
export function filterCodaEntries(raw) {
|
|
87
|
+
if (!Array.isArray(raw))
|
|
88
|
+
return [];
|
|
89
|
+
const out = [];
|
|
90
|
+
for (const entry of raw) {
|
|
91
|
+
if (typeof entry !== "string")
|
|
92
|
+
continue;
|
|
93
|
+
if (entry.length === 0 || entry.length > SESSION_CODA_MAX_CHARS)
|
|
94
|
+
continue;
|
|
95
|
+
out.push(entry);
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Load + filter coda lines from content. Fail-open: missing/unreadable/invalid
|
|
101
|
+
* / empty after filter → [] (omit ✦; no error noise).
|
|
102
|
+
*/
|
|
103
|
+
export function loadSessionCodas(frameworkRoot, readText) {
|
|
104
|
+
const packPath = join(contentRoot(frameworkRoot), SESSION_CODA_REL_PATH);
|
|
105
|
+
try {
|
|
106
|
+
const text = readText !== undefined ? readText(packPath) : readFileSync(packPath, { encoding: "utf8" });
|
|
107
|
+
if (text === null || text === "")
|
|
108
|
+
return [];
|
|
109
|
+
return filterCodaEntries(JSON.parse(text));
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Format enabled coda line: ✦ + space + body. */
|
|
116
|
+
export function formatSessionCodaEnabledLine(body) {
|
|
117
|
+
return `${SESSION_CODA_STAR_PREFIX}${body}`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve the single extra line (or null) after final success footer.
|
|
121
|
+
* Does not print; caller places after blank line post footer (D2).
|
|
122
|
+
*/
|
|
123
|
+
export function resolveSessionCodaLine(input) {
|
|
124
|
+
if (!input.shouldEmit)
|
|
125
|
+
return null;
|
|
126
|
+
if (input.mode === "off")
|
|
127
|
+
return null;
|
|
128
|
+
if (input.mode === "unset")
|
|
129
|
+
return SESSION_CODA_OFF_HINT;
|
|
130
|
+
const body = selectSessionCoda({
|
|
131
|
+
date: input.date,
|
|
132
|
+
projectRoot: input.projectRoot,
|
|
133
|
+
codas: input.codas,
|
|
134
|
+
});
|
|
135
|
+
if (body === null)
|
|
136
|
+
return null;
|
|
137
|
+
return formatSessionCodaEnabledLine(body);
|
|
138
|
+
}
|
|
139
|
+
/** Help / env-docs fragment for DEFT_SESSION_CODA three-state (#2712). */
|
|
140
|
+
export function sessionCodaHelpText() {
|
|
141
|
+
return [
|
|
142
|
+
"Environment:",
|
|
143
|
+
` ${SESSION_CODA_ENV} Session coda after interactive success (exit 0, TTY, not CI, not --json):`,
|
|
144
|
+
" unset Print discoverability line:",
|
|
145
|
+
` ${SESSION_CODA_OFF_HINT}`,
|
|
146
|
+
" =1 Print one curated line, e.g.:",
|
|
147
|
+
` ${formatSessionCodaEnabledLine("Doctor first. Then continue.")}`,
|
|
148
|
+
" =0 Silent (no hint, no coda)",
|
|
149
|
+
].join("\n");
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=session-coda.js.map
|
package/dist/doctor/types.d.ts
CHANGED
|
@@ -123,6 +123,22 @@ export interface DoctorSeams {
|
|
|
123
123
|
now?: Date;
|
|
124
124
|
}) => string | null;
|
|
125
125
|
readonly isTty?: () => boolean;
|
|
126
|
+
/**
|
|
127
|
+
* stdout TTY probe for session coda (#2712). Defaults to
|
|
128
|
+
* `process.stdout.isTTY === true` (not stdin). Separate from `isTty` (stdin
|
|
129
|
+
* used by --fix prompts).
|
|
130
|
+
*/
|
|
131
|
+
readonly stdoutIsTty?: () => boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Override DEFT_SESSION_CODA for tests (#2712). `undefined` means read
|
|
134
|
+
* process.env; pass `""` or omit via explicit nullish env simulation with
|
|
135
|
+
* `undefined` from a seam that returns undefined.
|
|
136
|
+
*/
|
|
137
|
+
readonly sessionCodaEnv?: string | undefined;
|
|
138
|
+
/** Override CI env string for session coda gates (#2712). */
|
|
139
|
+
readonly ciEnv?: string | undefined;
|
|
140
|
+
/** Override session-coda pack loader for fail-open tests (#2712). */
|
|
141
|
+
readonly loadSessionCodas?: (frameworkRoot: string) => string[];
|
|
126
142
|
readonly readYn?: (prompt: string, defaultYes: boolean) => boolean;
|
|
127
143
|
readonly writeText?: (path: string, content: string) => void;
|
|
128
144
|
readonly now?: () => Date;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #3286 Later graduation trigger from a #3282 run summary (#3320).
|
|
3
|
+
*
|
|
4
|
+
* Named surface: eval:report prints graduate / do-not-graduate / unevaluable.
|
|
5
|
+
* A graduation or decline MUST cite an emitted share. Counts without a
|
|
6
|
+
* denominator are unevaluable — never invent a ratio.
|
|
7
|
+
*/
|
|
8
|
+
import { type RitualGateShare } from "../run-summary/share.js";
|
|
9
|
+
/** #3286 Later trigger: graduate when ritual+gate share is at least this fraction. */
|
|
10
|
+
export declare const LATER_GRADUATION_SHARE_THRESHOLD = 0.25;
|
|
11
|
+
export type LaterGraduationVerdict = "graduate" | "do-not-graduate" | "unevaluable";
|
|
12
|
+
export interface LaterGraduationTrigger {
|
|
13
|
+
readonly evaluable: boolean;
|
|
14
|
+
readonly verdict: LaterGraduationVerdict;
|
|
15
|
+
readonly summary: string;
|
|
16
|
+
readonly ritualGateCount: number;
|
|
17
|
+
readonly totalToolTurns: number | null;
|
|
18
|
+
readonly share: number | null;
|
|
19
|
+
readonly threshold: number;
|
|
20
|
+
}
|
|
21
|
+
export interface EvaluateLaterGraduationOptions {
|
|
22
|
+
readonly projectRoot: string;
|
|
23
|
+
readonly runSummaryPath?: string;
|
|
24
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
25
|
+
readonly threshold?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function laterGraduationFromShare(share: RitualGateShare, threshold?: number): LaterGraduationTrigger;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve a readable run-summary path. Unset env + missing default file → null
|
|
30
|
+
* (fail-open / unevaluable). stdout dest (`-`) is not a file.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveRunSummaryReadPath(projectRoot: string, options?: {
|
|
33
|
+
readonly runSummaryPath?: string;
|
|
34
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
35
|
+
}): string | null;
|
|
36
|
+
/** Evaluate #3286 Later from the run-summary file (or unevaluable). */
|
|
37
|
+
export declare function evaluateLaterGraduationTrigger(options: EvaluateLaterGraduationOptions): LaterGraduationTrigger;
|
|
38
|
+
export declare function formatLaterGraduationLine(trigger: LaterGraduationTrigger): string;
|
|
39
|
+
//# sourceMappingURL=later-graduation.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #3286 Later graduation trigger from a #3282 run summary (#3320).
|
|
3
|
+
*
|
|
4
|
+
* Named surface: eval:report prints graduate / do-not-graduate / unevaluable.
|
|
5
|
+
* A graduation or decline MUST cite an emitted share. Counts without a
|
|
6
|
+
* denominator are unevaluable — never invent a ratio.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { isAbsolute, resolve } from "node:path";
|
|
10
|
+
import { computeRitualGateShare, parseRunSummaryJsonl, } from "../run-summary/share.js";
|
|
11
|
+
import { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH } from "../run-summary/types.js";
|
|
12
|
+
/** #3286 Later trigger: graduate when ritual+gate share is at least this fraction. */
|
|
13
|
+
export const LATER_GRADUATION_SHARE_THRESHOLD = 0.25;
|
|
14
|
+
function formatPercent(value) {
|
|
15
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
16
|
+
}
|
|
17
|
+
export function laterGraduationFromShare(share, threshold = LATER_GRADUATION_SHARE_THRESHOLD) {
|
|
18
|
+
if (!share.evaluable || share.share === null || share.totalToolTurns === null) {
|
|
19
|
+
return {
|
|
20
|
+
evaluable: false,
|
|
21
|
+
verdict: "unevaluable",
|
|
22
|
+
summary: "trigger unevaluable (no tool/turn denominator on run summary)",
|
|
23
|
+
ritualGateCount: share.ritualGateCount,
|
|
24
|
+
totalToolTurns: share.totalToolTurns,
|
|
25
|
+
share: null,
|
|
26
|
+
threshold,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const graduate = share.share >= threshold;
|
|
30
|
+
const verdict = graduate ? "graduate" : "do-not-graduate";
|
|
31
|
+
return {
|
|
32
|
+
evaluable: true,
|
|
33
|
+
verdict,
|
|
34
|
+
summary: `ritual+gate share ${formatPercent(share.share)} (${share.ritualGateCount}/${share.totalToolTurns}) — ${verdict} (threshold ${formatPercent(threshold)})`,
|
|
35
|
+
ritualGateCount: share.ritualGateCount,
|
|
36
|
+
totalToolTurns: share.totalToolTurns,
|
|
37
|
+
share: share.share,
|
|
38
|
+
threshold,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a readable run-summary path. Unset env + missing default file → null
|
|
43
|
+
* (fail-open / unevaluable). stdout dest (`-`) is not a file.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveRunSummaryReadPath(projectRoot, options = {}) {
|
|
46
|
+
if (options.runSummaryPath !== undefined && options.runSummaryPath.trim().length > 0) {
|
|
47
|
+
return resolve(options.runSummaryPath.trim());
|
|
48
|
+
}
|
|
49
|
+
const env = options.env ?? process.env;
|
|
50
|
+
const raw = env[ENV_RUN_SUMMARY_PATH];
|
|
51
|
+
if (raw !== undefined && raw.trim() === "-") {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (raw !== undefined && raw.trim().length > 0) {
|
|
55
|
+
const trimmed = raw.trim();
|
|
56
|
+
return isAbsolute(trimmed) ? resolve(trimmed) : resolve(projectRoot, trimmed);
|
|
57
|
+
}
|
|
58
|
+
const fallback = resolve(projectRoot, DEFAULT_RUN_SUMMARY_BASENAME);
|
|
59
|
+
return existsSync(fallback) ? fallback : null;
|
|
60
|
+
}
|
|
61
|
+
function readSummaryText(path) {
|
|
62
|
+
try {
|
|
63
|
+
if (!existsSync(path)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return readFileSync(path, "utf8");
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Evaluate #3286 Later from the run-summary file (or unevaluable). */
|
|
73
|
+
export function evaluateLaterGraduationTrigger(options) {
|
|
74
|
+
const threshold = options.threshold ?? LATER_GRADUATION_SHARE_THRESHOLD;
|
|
75
|
+
const path = resolveRunSummaryReadPath(options.projectRoot, {
|
|
76
|
+
runSummaryPath: options.runSummaryPath,
|
|
77
|
+
env: options.env,
|
|
78
|
+
});
|
|
79
|
+
if (path === null) {
|
|
80
|
+
return laterGraduationFromShare({ evaluable: false, ritualGateCount: 0, totalToolTurns: null, share: null }, threshold);
|
|
81
|
+
}
|
|
82
|
+
const text = readSummaryText(path);
|
|
83
|
+
if (text === null) {
|
|
84
|
+
return laterGraduationFromShare({ evaluable: false, ritualGateCount: 0, totalToolTurns: null, share: null }, threshold);
|
|
85
|
+
}
|
|
86
|
+
return laterGraduationFromShare(computeRitualGateShare(parseRunSummaryJsonl(text)), threshold);
|
|
87
|
+
}
|
|
88
|
+
export function formatLaterGraduationLine(trigger) {
|
|
89
|
+
return `#3286 Later: ${trigger.summary}`;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=later-graduation.js.map
|
package/dist/eval/report.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LaterGraduationTrigger } from "./later-graduation.js";
|
|
1
2
|
import { type GoldenRunRecord } from "./run.js";
|
|
2
3
|
import { type CellVersionPurity, type MixedVersionPolicy } from "./version-pin.js";
|
|
3
4
|
export declare const REPORT_SCHEMA_VERSION: 1;
|
|
@@ -39,6 +40,8 @@ export interface GoldenEvalReport {
|
|
|
39
40
|
readonly holdoutTripwire: HoldoutTripwire;
|
|
40
41
|
/** Cell-level framework version purity for the reported model (#3215). */
|
|
41
42
|
readonly versionPurity: VersionPurityEvidence;
|
|
43
|
+
/** #3286 Later graduation trigger from the run-summary denominator (#3320). */
|
|
44
|
+
readonly laterGraduation: LaterGraduationTrigger;
|
|
42
45
|
}
|
|
43
46
|
export interface ReportGoldenEvalOptions {
|
|
44
47
|
readonly projectRoot?: string;
|
|
@@ -50,6 +53,9 @@ export interface ReportGoldenEvalOptions {
|
|
|
50
53
|
* ledger runs for this model disagree on framework version within a treatment.
|
|
51
54
|
*/
|
|
52
55
|
readonly mixedVersionPolicy?: MixedVersionPolicy;
|
|
56
|
+
/** Override run-summary JSONL path (#3320). Default: DEFT_RUN_SUMMARY_PATH or repo default. */
|
|
57
|
+
readonly runSummaryPath?: string;
|
|
58
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
53
59
|
}
|
|
54
60
|
export interface ReportGoldenEvalResult {
|
|
55
61
|
readonly code: 0 | 1 | 2;
|
package/dist/eval/report.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { resolveEvalPath } from "../layout/resolve.js";
|
|
3
|
+
import { evaluateLaterGraduationTrigger, formatLaterGraduationLine, } from "./later-graduation.js";
|
|
3
4
|
import { GOLDEN_RUNS_HISTORY_REL } from "./run.js";
|
|
4
5
|
import { aggregateCellWithVersionPurity, evaluateLedgerVersionPurity, } from "./version-pin.js";
|
|
5
6
|
export const REPORT_SCHEMA_VERSION = 1;
|
|
@@ -107,17 +108,33 @@ export function goldenRunToVersionedRun(record) {
|
|
|
107
108
|
}
|
|
108
109
|
/** Diff two directive versions with metric deltas and significance (#1703 Tier 2). */
|
|
109
110
|
export function reportGoldenEval(options) {
|
|
110
|
-
|
|
111
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
112
|
+
const laterGraduation = evaluateLaterGraduationTrigger({
|
|
113
|
+
projectRoot,
|
|
114
|
+
runSummaryPath: options.runSummaryPath,
|
|
115
|
+
env: options.env,
|
|
116
|
+
});
|
|
117
|
+
const laterLine = formatLaterGraduationLine(laterGraduation);
|
|
118
|
+
const championMissing = !options.championVersion.trim();
|
|
119
|
+
const challengerMissing = !options.challengerVersion.trim();
|
|
120
|
+
const modelMissing = !options.model.trim();
|
|
121
|
+
if (championMissing && challengerMissing && modelMissing) {
|
|
122
|
+
return {
|
|
123
|
+
code: 0,
|
|
124
|
+
report: null,
|
|
125
|
+
message: `eval:report ${laterLine}`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (championMissing || challengerMissing) {
|
|
111
129
|
return {
|
|
112
130
|
code: 2,
|
|
113
131
|
report: null,
|
|
114
|
-
message:
|
|
132
|
+
message: `eval:report: --champion and --challenger are required\n ${laterLine}`,
|
|
115
133
|
};
|
|
116
134
|
}
|
|
117
|
-
if (
|
|
118
|
-
return { code: 2, report: null, message:
|
|
135
|
+
if (modelMissing) {
|
|
136
|
+
return { code: 2, report: null, message: `eval:report: --model is required\n ${laterLine}` };
|
|
119
137
|
}
|
|
120
|
-
const projectRoot = options.projectRoot ?? process.cwd();
|
|
121
138
|
const policy = options.mixedVersionPolicy ?? "refuse";
|
|
122
139
|
const records = readGoldenRuns(projectRoot);
|
|
123
140
|
const modelRecords = records.filter((r) => r.model === options.model);
|
|
@@ -157,7 +174,7 @@ export function reportGoldenEval(options) {
|
|
|
157
174
|
return {
|
|
158
175
|
code: 1,
|
|
159
176
|
report: null,
|
|
160
|
-
message: `eval:report: mixed framework versions in treatment cell(s) — aggregation refused (#3215)\n ${versionPurity.summary}`,
|
|
177
|
+
message: `eval:report: mixed framework versions in treatment cell(s) — aggregation refused (#3215)\n ${versionPurity.summary}\n ${laterLine}`,
|
|
161
178
|
};
|
|
162
179
|
}
|
|
163
180
|
const champion = findLatestGoldenRun(records, options.championVersion, options.model);
|
|
@@ -166,14 +183,14 @@ export function reportGoldenEval(options) {
|
|
|
166
183
|
return {
|
|
167
184
|
code: 1,
|
|
168
185
|
report: null,
|
|
169
|
-
message: `eval:report: no golden run found for champion v${options.championVersion} model=${options.model}`,
|
|
186
|
+
message: `eval:report: no golden run found for champion v${options.championVersion} model=${options.model}\n ${laterLine}`,
|
|
170
187
|
};
|
|
171
188
|
}
|
|
172
189
|
if (challenger === null) {
|
|
173
190
|
return {
|
|
174
191
|
code: 1,
|
|
175
192
|
report: null,
|
|
176
|
-
message: `eval:report: no golden run found for challenger v${options.challengerVersion} model=${options.model}`,
|
|
193
|
+
message: `eval:report: no golden run found for challenger v${options.challengerVersion} model=${options.model}\n ${laterLine}`,
|
|
177
194
|
};
|
|
178
195
|
}
|
|
179
196
|
const championPrimary = champion.results.filter((r) => !r.holdout);
|
|
@@ -208,6 +225,7 @@ export function reportGoldenEval(options) {
|
|
|
208
225
|
deltas,
|
|
209
226
|
holdoutTripwire,
|
|
210
227
|
versionPurity,
|
|
228
|
+
laterGraduation,
|
|
211
229
|
};
|
|
212
230
|
const lines = [
|
|
213
231
|
`eval:report champion=v${options.championVersion} challenger=v${options.challengerVersion} model=${options.model}`,
|
|
@@ -223,6 +241,7 @@ export function reportGoldenEval(options) {
|
|
|
223
241
|
` version purity: ${versionPurity.pure ? "ok" : "mixed"} — ${versionPurity.summary}`,
|
|
224
242
|
` champion cell pin: v${champion.frameworkVersionPin?.frameworkVersion ?? champion.directiveVersion}`,
|
|
225
243
|
` challenger cell pin: v${challenger.frameworkVersionPin?.frameworkVersion ?? challenger.directiveVersion}`,
|
|
244
|
+
` ${laterLine}`,
|
|
226
245
|
];
|
|
227
246
|
const code = holdoutTripwire.triggered ? 1 : 0;
|
|
228
247
|
return { code, report, message: lines.join("\n") };
|
package/dist/index.d.ts
CHANGED
|
@@ -48,12 +48,14 @@ export * as prMonitor from "./pr-monitor/index.js";
|
|
|
48
48
|
export * as prProtectedIssues from "./pr-protected-issues/index.js";
|
|
49
49
|
export * as prWaitMergeable from "./pr-wait-mergeable/index.js";
|
|
50
50
|
export * as preflight from "./preflight/index.js";
|
|
51
|
+
export * as productFirstDoneGate from "./product-first-done-gate/index.js";
|
|
51
52
|
export * as release from "./release/index.js";
|
|
52
53
|
export * as releaseE2e from "./release-e2e/index.js";
|
|
53
54
|
export * as releasePublish from "./release-publish/index.js";
|
|
54
55
|
export * as releaseRollback from "./release-rollback/index.js";
|
|
55
56
|
export * as render from "./render/index.js";
|
|
56
57
|
export * as resolution from "./resolution/index.js";
|
|
58
|
+
export * as runSummary from "./run-summary/index.js";
|
|
57
59
|
export * as scm from "./scm/index.js";
|
|
58
60
|
export * as scope from "./scope/index.js";
|
|
59
61
|
export * as scopeProvenance from "./scope-provenance/index.js";
|
|
@@ -72,6 +74,7 @@ export * as vbriefBuild from "./vbrief-build/index.js";
|
|
|
72
74
|
export * as vbriefReconcile from "./vbrief-reconcile/index.js";
|
|
73
75
|
export * as vbriefValidate from "./vbrief-validate/index.js";
|
|
74
76
|
export * as vbriefValidation from "./vbrief-validation/index.js";
|
|
77
|
+
export * as verifyAc from "./verify-ac/index.js";
|
|
75
78
|
export * as verifyEnv from "./verify-env/index.js";
|
|
76
79
|
export * as verifySource from "./verify-source/index.js";
|
|
77
80
|
export * as wipCap from "./wip-cap/index.js";
|
package/dist/index.js
CHANGED
|
@@ -49,12 +49,14 @@ export * as prMonitor from "./pr-monitor/index.js";
|
|
|
49
49
|
export * as prProtectedIssues from "./pr-protected-issues/index.js";
|
|
50
50
|
export * as prWaitMergeable from "./pr-wait-mergeable/index.js";
|
|
51
51
|
export * as preflight from "./preflight/index.js";
|
|
52
|
+
export * as productFirstDoneGate from "./product-first-done-gate/index.js";
|
|
52
53
|
export * as release from "./release/index.js";
|
|
53
54
|
export * as releaseE2e from "./release-e2e/index.js";
|
|
54
55
|
export * as releasePublish from "./release-publish/index.js";
|
|
55
56
|
export * as releaseRollback from "./release-rollback/index.js";
|
|
56
57
|
export * as render from "./render/index.js";
|
|
57
58
|
export * as resolution from "./resolution/index.js";
|
|
59
|
+
export * as runSummary from "./run-summary/index.js";
|
|
58
60
|
export * as scm from "./scm/index.js";
|
|
59
61
|
export * as scope from "./scope/index.js";
|
|
60
62
|
export * as scopeProvenance from "./scope-provenance/index.js";
|
|
@@ -73,6 +75,7 @@ export * as vbriefBuild from "./vbrief-build/index.js";
|
|
|
73
75
|
export * as vbriefReconcile from "./vbrief-reconcile/index.js";
|
|
74
76
|
export * as vbriefValidate from "./vbrief-validate/index.js";
|
|
75
77
|
export * as vbriefValidation from "./vbrief-validation/index.js";
|
|
78
|
+
export * as verifyAc from "./verify-ac/index.js";
|
|
76
79
|
export * as verifyEnv from "./verify-env/index.js";
|
|
77
80
|
export * as verifySource from "./verify-source/index.js";
|
|
78
81
|
export * as wipCap from "./wip-cap/index.js";
|
|
@@ -30,6 +30,8 @@ export const CANONICAL_GITIGNORE_BASELINE = [
|
|
|
30
30
|
".deft/ritual-state.json",
|
|
31
31
|
".deft/last-session.json",
|
|
32
32
|
".deft/routing.local.json",
|
|
33
|
+
// #3282: opt-in default run-summary JSONL at repo root (collectible; must not dirty trees).
|
|
34
|
+
".deft-run-summary.json",
|
|
33
35
|
// Temporary test/local kill-switch — must stay untracked (#3039).
|
|
34
36
|
DEFT_DIRECTIVE_DISABLE_GITIGNORE_LINE,
|
|
35
37
|
"vbrief/.triage-cache/candidates.jsonl",
|
|
@@ -5,12 +5,15 @@ import { scan } from "../cache/scanner.js";
|
|
|
5
5
|
import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/projection-containment.js";
|
|
6
6
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
7
7
|
import { captureAndAttachLiteralAcceptance } from "../literal-acceptance/index.js";
|
|
8
|
+
import { stampAcceptanceFromLiteralCapture } from "../product-first-done-gate/index.js";
|
|
9
|
+
import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter } from "../run-summary/index.js";
|
|
8
10
|
import { call } from "../scm/call.js";
|
|
9
11
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
10
12
|
import { resolveProjectRepo } from "../slice/project-context.js";
|
|
11
13
|
import { countMaintainerCurrentShapeComments, mapIssueCommentEntry, RAW_ISSUE_COMMENTS_KEY, selectCurrentShapeComment, } from "../umbrella-current-shape/index.js";
|
|
12
14
|
import { slugify, TODAY } from "../vbrief-build/build.js";
|
|
13
15
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
16
|
+
import { stampDerivedClausesOnAcceptance } from "../verify-ac/clauses.js";
|
|
14
17
|
import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, VBRIEF_VERSION, } from "../xbrief-migrate/constants.js";
|
|
15
18
|
import { findAcHeading, parseCheckboxItems, parseListItems, sliceAcSection, stripCodeBlocks, stripFencedCodeBlocks, } from "./markdown-scanners.js";
|
|
16
19
|
import { detectRepo, extractReferencesFromVbrief, fetchOpenIssues, GITHUB_ISSUE_REF_TYPES, LIFECYCLE_FOLDERS, parseIssueNumber, } from "./reconcile-issues.js";
|
|
@@ -472,6 +475,15 @@ export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
|
|
|
472
475
|
}
|
|
473
476
|
}
|
|
474
477
|
Object.assign(plan, attached.plan);
|
|
478
|
+
// #3284: stamp plan.acceptance from captured literals (stated or none_stated floor).
|
|
479
|
+
Object.assign(plan, stampAcceptanceFromLiteralCapture(plan));
|
|
480
|
+
// #3323: when no commands were stated, derive numbered clauses before product edit.
|
|
481
|
+
const derived = stampDerivedClausesOnAcceptance(plan, [title, overviewSource].filter((s) => s.length > 0).join("\n\n"));
|
|
482
|
+
Object.assign(plan, derived.plan);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
// No body: still record none_stated acceptance so absence is a decision.
|
|
486
|
+
Object.assign(plan, stampAcceptanceFromLiteralCapture(plan));
|
|
475
487
|
}
|
|
476
488
|
const infoRootKey = options.infoRootKey ?? LEGACY_INFO_ROOT_KEY;
|
|
477
489
|
const infoVersion = options.infoVersion ?? EMITTED_VBRIEF_VERSION;
|
|
@@ -711,8 +723,45 @@ export function ingestOne(issue, options) {
|
|
|
711
723
|
assertWriteTargetSafe(projectRoot, target);
|
|
712
724
|
mkdirSync(folderPath, { recursive: true });
|
|
713
725
|
writeFileSync(target, `${JSON.stringify(vbrief, null, 2)}\n`, "utf8");
|
|
726
|
+
emitIntakeAcceptanceStamp(projectRoot, vbrief.plan);
|
|
714
727
|
return ["created", target, `CREATED ${folder}/${filename}`];
|
|
715
728
|
}
|
|
729
|
+
function emitIntakeAcceptanceStamp(projectRoot, plan) {
|
|
730
|
+
const rec = plan !== null && typeof plan === "object" && !Array.isArray(plan)
|
|
731
|
+
? plan
|
|
732
|
+
: null;
|
|
733
|
+
if (rec === null) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const acceptance = rec.acceptance !== null && typeof rec.acceptance === "object" && !Array.isArray(rec.acceptance)
|
|
737
|
+
? rec.acceptance
|
|
738
|
+
: null;
|
|
739
|
+
if (acceptance === null) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
const env = process.env;
|
|
743
|
+
const dest = env[ENV_RUN_SUMMARY_PATH];
|
|
744
|
+
if (dest === undefined || dest.trim().length === 0) {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
try {
|
|
748
|
+
const sessionId = typeof env.DEFT_SESSION_ID === "string" && env.DEFT_SESSION_ID.trim().length > 0
|
|
749
|
+
? env.DEFT_SESSION_ID.trim()
|
|
750
|
+
: "issue-ingest";
|
|
751
|
+
const emitter = new RunSummaryEmitter({ projectRoot, sessionId, env });
|
|
752
|
+
const commands = Array.isArray(acceptance.commands) ? acceptance.commands.length : 0;
|
|
753
|
+
const clauses = Array.isArray(acceptance.clauses) ? acceptance.clauses.length : 0;
|
|
754
|
+
emitter.emitAcceptanceStamp({
|
|
755
|
+
rung: typeof acceptance.source_rung === "string" ? acceptance.source_rung : "project_floor",
|
|
756
|
+
none_stated: acceptance.none_stated === true,
|
|
757
|
+
command_count: commands,
|
|
758
|
+
clause_count: clauses,
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
catch {
|
|
762
|
+
// fail-open
|
|
763
|
+
}
|
|
764
|
+
}
|
|
716
765
|
export function ingestBulk(issues, options) {
|
|
717
766
|
let filtered = issues;
|
|
718
767
|
if (options.label !== undefined && options.label !== null) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed plan.policy.acPassBanking (#3285).
|
|
3
|
+
*
|
|
4
|
+
* Surplus-threshold policy for post-AC-pass deepening: after the first
|
|
5
|
+
* stated-AC bank, agents may deepen only when remaining budget is at least
|
|
6
|
+
* this fraction of the hard max (default 0.2 = 20%).
|
|
7
|
+
*
|
|
8
|
+
* Composes #3266 bank-the-pass and product-first done-gate (#3284).
|
|
9
|
+
*/
|
|
10
|
+
/** Canonical dotted path for policy:show / PROJECT-DEFINITION. */
|
|
11
|
+
export declare const FIELD_AC_PASS_BANKING = "plan.policy.acPassBanking";
|
|
12
|
+
/** CLI alias for `task policy:show --field=acPassBanking`. */
|
|
13
|
+
export declare const FIELD_AC_PASS_BANKING_CLI_ALIAS = "acPassBanking";
|
|
14
|
+
/**
|
|
15
|
+
* Default surplus fraction of max turns/cost that must remain after bank
|
|
16
|
+
* before self-imposed deepening is allowed (#3285).
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_SURPLUS_THRESHOLD = 0.2;
|
|
19
|
+
/** Floor / ceiling for surplusThreshold (inclusive). */
|
|
20
|
+
export declare const SURPLUS_THRESHOLD_MIN = 0;
|
|
21
|
+
export declare const SURPLUS_THRESHOLD_MAX = 1;
|
|
22
|
+
/** Optional env override (fraction 0–1 or percent 0–100). */
|
|
23
|
+
export declare const ENV_SURPLUS_THRESHOLD = "DEFT_AC_PASS_SURPLUS_THRESHOLD";
|
|
24
|
+
export interface AcPassBankingConfig {
|
|
25
|
+
/**
|
|
26
|
+
* When false, surplus gate is inactive (unbounded / optional discipline only).
|
|
27
|
+
* Default true — the gate still only binds under a hard effort budget.
|
|
28
|
+
*/
|
|
29
|
+
readonly enabled: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Minimum remaining fraction of max turns/cost required to deepen after bank.
|
|
32
|
+
* Default {@link DEFAULT_SURPLUS_THRESHOLD} (0.2).
|
|
33
|
+
*/
|
|
34
|
+
readonly surplusThreshold: number;
|
|
35
|
+
}
|
|
36
|
+
export type AcPassBankingSource = "typed" | "env" | "default" | "default-on-error";
|
|
37
|
+
export interface AcPassBankingResolved extends AcPassBankingConfig {
|
|
38
|
+
readonly source: AcPassBankingSource;
|
|
39
|
+
readonly error: string | null;
|
|
40
|
+
}
|
|
41
|
+
/** Parse a 0–1 fraction or a 0–100 percent string/number into a fraction. */
|
|
42
|
+
export declare function parseSurplusThreshold(raw: unknown): number | null;
|
|
43
|
+
/** Validate a `plan.policy.acPassBanking` payload. */
|
|
44
|
+
export declare function validateAcPassBanking(value: unknown): string[];
|
|
45
|
+
/**
|
|
46
|
+
* Resolve surplus policy from typed plan.policy.acPassBanking, then env,
|
|
47
|
+
* then defaults (#3285).
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveAcPassBanking(projectRoot?: string | null, environ?: Readonly<Record<string, string | undefined>>): AcPassBankingResolved;
|
|
50
|
+
export interface AcPassBankingPolicyField {
|
|
51
|
+
readonly name: typeof FIELD_AC_PASS_BANKING;
|
|
52
|
+
readonly current: AcPassBankingConfig;
|
|
53
|
+
readonly default: AcPassBankingConfig;
|
|
54
|
+
readonly source: string;
|
|
55
|
+
}
|
|
56
|
+
/** Inspector row for `task policy:show --field=acPassBanking` (#3285). */
|
|
57
|
+
export declare function inspectAcPassBanking(_data: Record<string, unknown> | null, projectRoot?: string): AcPassBankingPolicyField;
|
|
58
|
+
//# sourceMappingURL=ac-pass-banking.d.ts.map
|