@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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Interoperates with #3267 plan.metadata.literal_acceptance_commands.
|
|
6
6
|
*/
|
|
7
7
|
import { attachLiteralAcceptanceCommands, readStoredLiteralAcceptanceCommands, } from "../literal-acceptance/index.js";
|
|
8
|
+
import { readAcceptanceClauses, serializeAcceptanceClauses } from "../verify-ac/clauses.js";
|
|
8
9
|
import { PLAN_ACCEPTANCE_KEY, } from "./types.js";
|
|
9
10
|
function asRecord(value) {
|
|
10
11
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
@@ -73,6 +74,22 @@ export function validatePlanAcceptance(value) {
|
|
|
73
74
|
if ("source_rung" in rec && rec.source_rung !== undefined && !isAcSourceRung(rec.source_rung)) {
|
|
74
75
|
errors.push('plan.acceptance.source_rung must be "stated" | "derived" | "project_floor"');
|
|
75
76
|
}
|
|
77
|
+
if ("clauses" in rec && rec.clauses !== undefined && !Array.isArray(rec.clauses)) {
|
|
78
|
+
errors.push("plan.acceptance.clauses must be an array");
|
|
79
|
+
}
|
|
80
|
+
if (Array.isArray(rec.clauses)) {
|
|
81
|
+
rec.clauses.forEach((entry, index) => {
|
|
82
|
+
const row = asRecord(entry);
|
|
83
|
+
if (row === null) {
|
|
84
|
+
errors.push(`plan.acceptance.clauses[${index}] must be an object`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const text = isNonEmptyString(row.text) ? row.text.trim() : "";
|
|
88
|
+
if (text.length === 0) {
|
|
89
|
+
errors.push(`plan.acceptance.clauses[${index}].text must be a non-empty string`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
76
93
|
const commands = coerceCommands(rec.commands);
|
|
77
94
|
const noneStated = rec.none_stated === true;
|
|
78
95
|
if (commands.length === 0 && !noneStated) {
|
|
@@ -110,6 +127,7 @@ export function readPlanAcceptance(plan) {
|
|
|
110
127
|
if (noneStated && commands.length === 0) {
|
|
111
128
|
sourceRung = isAcSourceRung(rec.source_rung) ? rec.source_rung : "project_floor";
|
|
112
129
|
}
|
|
130
|
+
const clauses = readAcceptanceClauses(rec);
|
|
113
131
|
return {
|
|
114
132
|
commands,
|
|
115
133
|
none_stated: noneStated,
|
|
@@ -119,6 +137,7 @@ export function readPlanAcceptance(plan) {
|
|
|
119
137
|
: isNonEmptyString(rec.derivedReason)
|
|
120
138
|
? rec.derivedReason
|
|
121
139
|
: null,
|
|
140
|
+
...(clauses.length > 0 ? { clauses } : {}),
|
|
122
141
|
};
|
|
123
142
|
}
|
|
124
143
|
}
|
|
@@ -181,6 +200,9 @@ export function attachPlanAcceptance(plan, acceptance) {
|
|
|
181
200
|
if (acceptance.derived_reason) {
|
|
182
201
|
serializable.derived_reason = acceptance.derived_reason;
|
|
183
202
|
}
|
|
203
|
+
if (acceptance.clauses !== undefined && acceptance.clauses.length > 0) {
|
|
204
|
+
serializable.clauses = serializeAcceptanceClauses(acceptance.clauses);
|
|
205
|
+
}
|
|
184
206
|
let next = {
|
|
185
207
|
...plan,
|
|
186
208
|
[PLAN_ACCEPTANCE_KEY]: serializable,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Empty verify:ac resolution is not a green run when there is no suite floor (#3334).
|
|
3
|
+
*
|
|
4
|
+
* Framework source keeps today's floor-pass (suite gates own the floor).
|
|
5
|
+
* Consumer / no-suite projects fail closed with a distinct soft_empty outcome.
|
|
6
|
+
*/
|
|
7
|
+
import type { PlanAcceptance } from "./types.js";
|
|
8
|
+
export declare const EMPTY_AC_OUTCOME: "soft_empty";
|
|
9
|
+
export declare const EMPTY_AC_CAUSE = "no acceptance stamped \u2014 floor is empty in this project";
|
|
10
|
+
export declare const EMPTY_AC_REMEDY = "stamp plan.acceptance (author commands or run intake capture / task issue:ingest)";
|
|
11
|
+
export type VerifyAcResolution = "verified-pass" | "empty-pass" | "soft_empty" | "fail" | "config" | "skipped";
|
|
12
|
+
/** True when this project check composition includes a suite gate (#3188). */
|
|
13
|
+
export declare function projectHasSuiteFloor(projectRoot: string): boolean;
|
|
14
|
+
export declare function isSoftEmptyAcText(text: string): boolean;
|
|
15
|
+
export interface EmptyAcResolutionInput {
|
|
16
|
+
readonly ok: boolean;
|
|
17
|
+
readonly code: number;
|
|
18
|
+
readonly runsLength: number;
|
|
19
|
+
readonly commandCount: number;
|
|
20
|
+
readonly rejectedCount: number;
|
|
21
|
+
readonly resolution?: VerifyAcResolution;
|
|
22
|
+
}
|
|
23
|
+
/** Zero executable commands, no rejected/unpromoted ledger, not already classified. */
|
|
24
|
+
export declare function isEmptyAcResolution(input: EmptyAcResolutionInput): boolean;
|
|
25
|
+
export declare function formatSoftEmptyMessage(acceptance: PlanAcceptance, quiet?: boolean): string;
|
|
26
|
+
//# sourceMappingURL=empty-resolution.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Empty verify:ac resolution is not a green run when there is no suite floor (#3334).
|
|
3
|
+
*
|
|
4
|
+
* Framework source keeps today's floor-pass (suite gates own the floor).
|
|
5
|
+
* Consumer / no-suite projects fail closed with a distinct soft_empty outcome.
|
|
6
|
+
*/
|
|
7
|
+
import { isFrameworkRepoRoot } from "../check/context.js";
|
|
8
|
+
export const EMPTY_AC_OUTCOME = "soft_empty";
|
|
9
|
+
export const EMPTY_AC_CAUSE = "no acceptance stamped — floor is empty in this project";
|
|
10
|
+
export const EMPTY_AC_REMEDY = "stamp plan.acceptance (author commands or run intake capture / task issue:ingest)";
|
|
11
|
+
/** True when this project check composition includes a suite gate (#3188). */
|
|
12
|
+
export function projectHasSuiteFloor(projectRoot) {
|
|
13
|
+
return isFrameworkRepoRoot(projectRoot);
|
|
14
|
+
}
|
|
15
|
+
export function isSoftEmptyAcText(text) {
|
|
16
|
+
return /soft_empty\s*\(#3334\)/i.test(text);
|
|
17
|
+
}
|
|
18
|
+
/** Zero executable commands, no rejected/unpromoted ledger, not already classified. */
|
|
19
|
+
export function isEmptyAcResolution(input) {
|
|
20
|
+
if (input.resolution === "config" || input.resolution === "skipped") {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (input.code === 2) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (input.rejectedCount > 0 || input.commandCount > 0 || input.runsLength > 0) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
export function formatSoftEmptyMessage(acceptance, quiet) {
|
|
32
|
+
if (quiet) {
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
return (`verify:ac ${EMPTY_AC_OUTCOME} (#3334) [rung=${acceptance.source_rung}]: ` +
|
|
36
|
+
`${EMPTY_AC_CAUSE}. ${EMPTY_AC_REMEDY}.`);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=empty-resolution.js.map
|
|
@@ -3,15 +3,22 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runs plan.acceptance.commands (or #3267 literal ledger) via the shared
|
|
5
5
|
* literal-acceptance runner. Records source_rung in the result message.
|
|
6
|
-
* Project floor with empty commands is a soft pass
|
|
7
|
-
* inside full `task check`)
|
|
6
|
+
* Project floor with empty commands is a soft pass only when a suite floor
|
|
7
|
+
* exists (suite gates own the floor inside full `task check`). With no suite
|
|
8
|
+
* floor, empty resolution is soft_empty — not a green run (#3334).
|
|
8
9
|
*/
|
|
9
10
|
import { type EvaluateLiteralAcceptanceOptions, type LiteralAcceptanceGateResult } from "../literal-acceptance/index.js";
|
|
11
|
+
import { type ClauseWalkResult } from "../verify-ac/clauses.js";
|
|
12
|
+
import { type VerifyAcResolution } from "./empty-resolution.js";
|
|
10
13
|
import type { AcSourceRung, PlanAcceptance } from "./types.js";
|
|
11
14
|
export interface VerifyAcResult extends LiteralAcceptanceGateResult {
|
|
12
15
|
readonly sourceRung: AcSourceRung;
|
|
13
16
|
readonly noneStated: boolean;
|
|
14
17
|
readonly acceptance: PlanAcceptance;
|
|
18
|
+
readonly resolution: VerifyAcResolution;
|
|
19
|
+
readonly resolvedCommandCount: number;
|
|
20
|
+
readonly clauseOutcomes?: readonly ClauseWalkResult[];
|
|
21
|
+
readonly clauseWalked?: boolean;
|
|
15
22
|
}
|
|
16
23
|
export interface EvaluateVerifyAcOptions extends EvaluateLiteralAcceptanceOptions {
|
|
17
24
|
/**
|
|
@@ -36,6 +43,25 @@ export interface EvaluateVerifyAcOptions extends EvaluateLiteralAcceptanceOption
|
|
|
36
43
|
readonly bankOnPass?: boolean;
|
|
37
44
|
/** Optional scope id override for the bank ledger (default plan.id / path). */
|
|
38
45
|
readonly bankScopeId?: string | null;
|
|
46
|
+
/**
|
|
47
|
+
* Injected run-summary JSONL for product-oracle integrity (#3322).
|
|
48
|
+
* Undefined → read DEFT_RUN_SUMMARY_PATH / default dest; null → skip disk.
|
|
49
|
+
*/
|
|
50
|
+
readonly runSummaryText?: string | null;
|
|
51
|
+
/** When false, skip #3322 oracle integrity. Default true. */
|
|
52
|
+
readonly applyOracleIntegrity?: boolean;
|
|
53
|
+
/** Env seam for run-summary dest resolution (#3322 / #3334). */
|
|
54
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
55
|
+
/**
|
|
56
|
+
* Inject suite-floor detection (tests). Default: framework source has a
|
|
57
|
+
* suite floor; consumer projects do not (#3334).
|
|
58
|
+
*/
|
|
59
|
+
readonly hasSuiteFloor?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* When true, skip the acceptance run-summary emit so the path helper can
|
|
62
|
+
* emit after the #3285 bank checkpoint.
|
|
63
|
+
*/
|
|
64
|
+
readonly skipAcceptanceEmit?: boolean;
|
|
39
65
|
}
|
|
40
66
|
/**
|
|
41
67
|
* Evaluate product AC from an in-memory plan.
|
|
@@ -3,14 +3,19 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runs plan.acceptance.commands (or #3267 literal ledger) via the shared
|
|
5
5
|
* literal-acceptance runner. Records source_rung in the result message.
|
|
6
|
-
* Project floor with empty commands is a soft pass
|
|
7
|
-
* inside full `task check`)
|
|
6
|
+
* Project floor with empty commands is a soft pass only when a suite floor
|
|
7
|
+
* exists (suite gates own the floor inside full `task check`). With no suite
|
|
8
|
+
* floor, empty resolution is soft_empty — not a green run (#3334).
|
|
8
9
|
*/
|
|
9
10
|
import { existsSync, readFileSync } from "node:fs";
|
|
10
11
|
import { basename, resolve } from "node:path";
|
|
11
12
|
import { evaluateLiteralAcceptanceFromPlan, runLiteralAcceptanceCommands, } from "../literal-acceptance/index.js";
|
|
13
|
+
import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter, } from "../run-summary/index.js";
|
|
12
14
|
import { maybeBankOnAcPass } from "../session/ac-pass-banking.js";
|
|
15
|
+
import { formatClauseWalkMessage, walkAcceptanceClauses, } from "../verify-ac/clauses.js";
|
|
16
|
+
import { emitVerifyAcAttempts, evaluateProductOracleIntegrity, mergeOracleVerdict, } from "../verify-ac/evaluate.js";
|
|
13
17
|
import { readPlanAcceptance, validatePlanAcceptance } from "./acceptance.js";
|
|
18
|
+
import { formatSoftEmptyMessage, isEmptyAcResolution, projectHasSuiteFloor, } from "./empty-resolution.js";
|
|
14
19
|
function asRecord(value) {
|
|
15
20
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
16
21
|
return value;
|
|
@@ -25,7 +30,7 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
|
|
|
25
30
|
const schemaErrors = validatePlanAcceptance(plan.acceptance ?? acceptance);
|
|
26
31
|
// Only hard-fail schema when an explicit plan.acceptance object exists.
|
|
27
32
|
if (plan.acceptance !== undefined && schemaErrors.length > 0) {
|
|
28
|
-
return {
|
|
33
|
+
return applyOracle({
|
|
29
34
|
ok: false,
|
|
30
35
|
code: 2,
|
|
31
36
|
message: `verify:ac config error (#3284): ${schemaErrors.join("; ")}`,
|
|
@@ -34,7 +39,9 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
|
|
|
34
39
|
sourceRung: acceptance.source_rung,
|
|
35
40
|
noneStated: acceptance.none_stated,
|
|
36
41
|
acceptance,
|
|
37
|
-
|
|
42
|
+
resolution: "config",
|
|
43
|
+
resolvedCommandCount: 0,
|
|
44
|
+
}, options);
|
|
38
45
|
}
|
|
39
46
|
const projectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
40
47
|
// Prefer shared literal-acceptance path so safety / promotion rules stay one place.
|
|
@@ -45,7 +52,10 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
|
|
|
45
52
|
const base = evaluateLiteralAcceptanceFromPlan(plan, {
|
|
46
53
|
projectRoot,
|
|
47
54
|
runner: options.runner,
|
|
48
|
-
|
|
55
|
+
// Check composition uses the stamped ledger only. Re-scanning issue prose
|
|
56
|
+
// during `task check` re-captures backtick `verify:ac` lines as rejected
|
|
57
|
+
// and deadlocks the graph (#3323 / #3284 check-integrated).
|
|
58
|
+
captureFromNarratives: options.captureFromNarratives ?? (options.checkIntegrated === true ? false : undefined),
|
|
49
59
|
quiet: options.quiet,
|
|
50
60
|
});
|
|
51
61
|
// When literal path had nothing executable but plan.acceptance has derived commands,
|
|
@@ -63,7 +73,7 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
|
|
|
63
73
|
source: "explicit",
|
|
64
74
|
sourceSpan: "plan.acceptance.commands",
|
|
65
75
|
})), { projectRoot, runner, allowTaskStatement: options.allowTaskStatement });
|
|
66
|
-
return annotate(direct, acceptance, options.quiet);
|
|
76
|
+
return applyOracle(annotate(direct, acceptance, options.quiet), options);
|
|
67
77
|
}
|
|
68
78
|
// Check composition: mid-story unpromoted capture-only may soft-pass so the
|
|
69
79
|
// framework graph is not deadlocked before agents promote peers.
|
|
@@ -72,12 +82,12 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
|
|
|
72
82
|
if (options.checkIntegrated === true && !base.ok && base.runs.length === 0) {
|
|
73
83
|
const hasRejected = (base.rejected?.length ?? 0) > 0;
|
|
74
84
|
if (hasRejected) {
|
|
75
|
-
return annotate(base, acceptance, options.quiet);
|
|
85
|
+
return applyOracle(annotate(base, acceptance, options.quiet), options);
|
|
76
86
|
}
|
|
77
87
|
const unpromoted = /capture-only|task_statement|no matching agent-promoted/i.test(base.message) ||
|
|
78
88
|
(base.commands.length > 0 && base.commands.every((c) => c.source === "task_statement"));
|
|
79
89
|
if (unpromoted || base.commands.length === 0) {
|
|
80
|
-
return {
|
|
90
|
+
return applyOracle({
|
|
81
91
|
ok: true,
|
|
82
92
|
code: 0,
|
|
83
93
|
message: options.quiet
|
|
@@ -92,10 +102,164 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
|
|
|
92
102
|
sourceRung: acceptance.source_rung,
|
|
93
103
|
noneStated: acceptance.none_stated,
|
|
94
104
|
acceptance,
|
|
95
|
-
|
|
105
|
+
resolution: classifyResolution({
|
|
106
|
+
ok: true,
|
|
107
|
+
code: 0,
|
|
108
|
+
runsLength: 0,
|
|
109
|
+
commandCount: base.commands.length,
|
|
110
|
+
rejectedCount: base.rejected?.length ?? 0,
|
|
111
|
+
}),
|
|
112
|
+
resolvedCommandCount: base.commands.length,
|
|
113
|
+
}, options);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return applyOracle(annotate(base, acceptance, options.quiet), options);
|
|
117
|
+
}
|
|
118
|
+
function classifyResolution(input) {
|
|
119
|
+
if (input.resolution !== undefined) {
|
|
120
|
+
return input.resolution;
|
|
121
|
+
}
|
|
122
|
+
if (input.code === 2) {
|
|
123
|
+
return "config";
|
|
124
|
+
}
|
|
125
|
+
if (!input.ok) {
|
|
126
|
+
return "fail";
|
|
127
|
+
}
|
|
128
|
+
if (input.runsLength > 0) {
|
|
129
|
+
return "verified-pass";
|
|
130
|
+
}
|
|
131
|
+
if (isEmptyAcResolution(input)) {
|
|
132
|
+
return "empty-pass";
|
|
133
|
+
}
|
|
134
|
+
return "empty-pass";
|
|
135
|
+
}
|
|
136
|
+
function applyEmptyFloorPolicy(result, options) {
|
|
137
|
+
if (!isEmptyAcResolution({
|
|
138
|
+
ok: result.ok,
|
|
139
|
+
code: result.code,
|
|
140
|
+
runsLength: result.runs.length,
|
|
141
|
+
commandCount: Math.max(result.commands.length, result.acceptance.commands.length),
|
|
142
|
+
rejectedCount: result.rejected?.length ?? 0,
|
|
143
|
+
resolution: result.resolution,
|
|
144
|
+
})) {
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
const projectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
148
|
+
const suiteFloor = options.hasSuiteFloor ?? projectHasSuiteFloor(projectRoot);
|
|
149
|
+
if (suiteFloor) {
|
|
150
|
+
return {
|
|
151
|
+
...result,
|
|
152
|
+
resolution: "empty-pass",
|
|
153
|
+
resolvedCommandCount: 0,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
...result,
|
|
158
|
+
ok: false,
|
|
159
|
+
code: 1,
|
|
160
|
+
message: formatSoftEmptyMessage(result.acceptance),
|
|
161
|
+
resolution: "soft_empty",
|
|
162
|
+
resolvedCommandCount: 0,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function acceptanceOutcomeOf(result) {
|
|
166
|
+
if (result.resolution === "verified-pass")
|
|
167
|
+
return "verified-pass";
|
|
168
|
+
if (result.resolution === "empty-pass")
|
|
169
|
+
return "empty-pass";
|
|
170
|
+
if (result.resolution === "soft_empty")
|
|
171
|
+
return "soft_empty";
|
|
172
|
+
if (result.resolution === "fail")
|
|
173
|
+
return "fail";
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
function emitAcceptanceOutcome(result, options, projectRoot) {
|
|
177
|
+
if (options.env === undefined) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const dest = options.env[ENV_RUN_SUMMARY_PATH];
|
|
181
|
+
if (dest === undefined || dest.trim().length === 0) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const outcome = acceptanceOutcomeOf(result);
|
|
185
|
+
if (outcome === null) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const emitter = new RunSummaryEmitter({
|
|
190
|
+
projectRoot,
|
|
191
|
+
sessionId: (typeof options.env.DEFT_SESSION_ID === "string" && options.env.DEFT_SESSION_ID.trim()) ||
|
|
192
|
+
"verify-ac",
|
|
193
|
+
env: options.env,
|
|
194
|
+
});
|
|
195
|
+
emitter.emitAcceptance({
|
|
196
|
+
resolved_command_count: result.resolvedCommandCount,
|
|
197
|
+
outcome,
|
|
198
|
+
source_rung: result.sourceRung,
|
|
199
|
+
none_stated: result.noneStated,
|
|
200
|
+
clause_count: result.clauseOutcomes?.length,
|
|
201
|
+
clause_outcomes: result.clauseOutcomes?.map((row) => ({
|
|
202
|
+
id: row.id,
|
|
203
|
+
outcome: row.outcome,
|
|
204
|
+
})),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// fail-open
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function applyClauseWalk(result, options) {
|
|
212
|
+
const clauses = result.acceptance.clauses ?? [];
|
|
213
|
+
if (clauses.length === 0 || result.resolution === "config" || result.resolution === "skipped") {
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
const projectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
217
|
+
const report = walkAcceptanceClauses(clauses, projectRoot);
|
|
218
|
+
const message = options.quiet ? result.message : formatClauseWalkMessage(report, result.message);
|
|
219
|
+
const ok = result.ok && report.ok;
|
|
220
|
+
return {
|
|
221
|
+
...result,
|
|
222
|
+
ok,
|
|
223
|
+
code: ok ? result.code : result.code === 2 ? 2 : 1,
|
|
224
|
+
message,
|
|
225
|
+
resolution: ok
|
|
226
|
+
? result.resolution === "empty-pass"
|
|
227
|
+
? "verified-pass"
|
|
228
|
+
: result.resolution
|
|
229
|
+
: "fail",
|
|
230
|
+
clauseOutcomes: report.clauses,
|
|
231
|
+
clauseWalked: true,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function applyOracle(result, options) {
|
|
235
|
+
const projectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
236
|
+
const walked = applyClauseWalk(result, options);
|
|
237
|
+
const gated = walked.clauseWalked === true ? walked : applyEmptyFloorPolicy(walked, options);
|
|
238
|
+
// Emit/read disk only when the caller supplied env (CLI passes process.env).
|
|
239
|
+
// Tests stay isolated unless they opt in with env or runSummaryText.
|
|
240
|
+
if (options.env !== undefined) {
|
|
241
|
+
emitVerifyAcAttempts({
|
|
242
|
+
projectRoot,
|
|
243
|
+
runs: gated.runs,
|
|
244
|
+
env: options.env,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
let next = gated;
|
|
248
|
+
if (options.applyOracleIntegrity !== false) {
|
|
249
|
+
const verdict = evaluateProductOracleIntegrity({
|
|
250
|
+
projectRoot,
|
|
251
|
+
runSummaryText: options.runSummaryText,
|
|
252
|
+
env: options.env,
|
|
253
|
+
});
|
|
254
|
+
next = mergeOracleVerdict(gated, verdict);
|
|
255
|
+
if (!verdict.ok && next.resolution !== "soft_empty" && next.resolution !== "config") {
|
|
256
|
+
next = { ...next, resolution: "fail" };
|
|
96
257
|
}
|
|
97
258
|
}
|
|
98
|
-
|
|
259
|
+
if (options.skipAcceptanceEmit !== true) {
|
|
260
|
+
emitAcceptanceOutcome(next, options, projectRoot);
|
|
261
|
+
}
|
|
262
|
+
return next;
|
|
99
263
|
}
|
|
100
264
|
function annotate(result, acceptance, quiet) {
|
|
101
265
|
const prefix = result.ok
|
|
@@ -116,12 +280,21 @@ function annotate(result, acceptance, quiet) {
|
|
|
116
280
|
else if (result.ok) {
|
|
117
281
|
message = "";
|
|
118
282
|
}
|
|
283
|
+
const commandCount = Math.max(result.commands.length, acceptance.commands.length);
|
|
119
284
|
return {
|
|
120
285
|
...result,
|
|
121
286
|
message,
|
|
122
287
|
sourceRung: acceptance.source_rung,
|
|
123
288
|
noneStated: acceptance.none_stated,
|
|
124
289
|
acceptance,
|
|
290
|
+
resolution: classifyResolution({
|
|
291
|
+
ok: result.ok,
|
|
292
|
+
code: result.code,
|
|
293
|
+
runsLength: result.runs.length,
|
|
294
|
+
commandCount,
|
|
295
|
+
rejectedCount: result.rejected?.length ?? 0,
|
|
296
|
+
}),
|
|
297
|
+
resolvedCommandCount: result.runs.length > 0 ? result.runs.length : commandCount,
|
|
125
298
|
};
|
|
126
299
|
}
|
|
127
300
|
/**
|
|
@@ -131,18 +304,9 @@ export function evaluateVerifyAcFromPath(xbriefPath, options = {}) {
|
|
|
131
304
|
const abs = resolve(xbriefPath);
|
|
132
305
|
if (!existsSync(abs)) {
|
|
133
306
|
if (options.softMissingXbrief) {
|
|
134
|
-
return softSkip(`xBRIEF not found: ${abs}`, options.quiet);
|
|
307
|
+
return applyOracle(softSkip(`xBRIEF not found: ${abs}`, options.quiet), options);
|
|
135
308
|
}
|
|
136
|
-
return {
|
|
137
|
-
ok: false,
|
|
138
|
-
code: 2,
|
|
139
|
-
message: `verify:ac: xBRIEF not found: ${abs}`,
|
|
140
|
-
commands: [],
|
|
141
|
-
runs: [],
|
|
142
|
-
sourceRung: "project_floor",
|
|
143
|
-
noneStated: true,
|
|
144
|
-
acceptance: { commands: [], none_stated: true, source_rung: "project_floor" },
|
|
145
|
-
};
|
|
309
|
+
return applyOracle(configResult(`verify:ac: xBRIEF not found: ${abs}`), options);
|
|
146
310
|
}
|
|
147
311
|
let parsed;
|
|
148
312
|
try {
|
|
@@ -150,45 +314,20 @@ export function evaluateVerifyAcFromPath(xbriefPath, options = {}) {
|
|
|
150
314
|
}
|
|
151
315
|
catch (err) {
|
|
152
316
|
const msg = err instanceof Error ? err.message : String(err);
|
|
153
|
-
return {
|
|
154
|
-
ok: false,
|
|
155
|
-
code: 2,
|
|
156
|
-
message: `verify:ac: unreadable xBRIEF (${msg}): ${abs}`,
|
|
157
|
-
commands: [],
|
|
158
|
-
runs: [],
|
|
159
|
-
sourceRung: "project_floor",
|
|
160
|
-
noneStated: true,
|
|
161
|
-
acceptance: { commands: [], none_stated: true, source_rung: "project_floor" },
|
|
162
|
-
};
|
|
317
|
+
return applyOracle(configResult(`verify:ac: unreadable xBRIEF (${msg}): ${abs}`), options);
|
|
163
318
|
}
|
|
164
319
|
const data = asRecord(parsed);
|
|
165
320
|
if (data === null) {
|
|
166
|
-
return {
|
|
167
|
-
ok: false,
|
|
168
|
-
code: 2,
|
|
169
|
-
message: `verify:ac: xBRIEF top-level is not an object: ${abs}`,
|
|
170
|
-
commands: [],
|
|
171
|
-
runs: [],
|
|
172
|
-
sourceRung: "project_floor",
|
|
173
|
-
noneStated: true,
|
|
174
|
-
acceptance: { commands: [], none_stated: true, source_rung: "project_floor" },
|
|
175
|
-
};
|
|
321
|
+
return applyOracle(configResult(`verify:ac: xBRIEF top-level is not an object: ${abs}`), options);
|
|
176
322
|
}
|
|
177
323
|
const plan = asRecord(data.plan);
|
|
178
324
|
if (plan === null) {
|
|
179
|
-
return {
|
|
180
|
-
ok: false,
|
|
181
|
-
code: 2,
|
|
182
|
-
message: `verify:ac: xBRIEF missing plan object: ${abs}`,
|
|
183
|
-
commands: [],
|
|
184
|
-
runs: [],
|
|
185
|
-
sourceRung: "project_floor",
|
|
186
|
-
noneStated: true,
|
|
187
|
-
acceptance: { commands: [], none_stated: true, source_rung: "project_floor" },
|
|
188
|
-
};
|
|
325
|
+
return applyOracle(configResult(`verify:ac: xBRIEF missing plan object: ${abs}`), options);
|
|
189
326
|
}
|
|
190
|
-
const result = evaluateVerifyAcFromPlan(plan, options);
|
|
191
|
-
|
|
327
|
+
const result = evaluateVerifyAcFromPlan(plan, { ...options, skipAcceptanceEmit: true });
|
|
328
|
+
const banked = maybeAttachAcPassBank(result, plan, abs, options);
|
|
329
|
+
emitAcceptanceOutcome(banked, options, resolve(options.projectRoot ?? process.cwd()));
|
|
330
|
+
return banked;
|
|
192
331
|
}
|
|
193
332
|
/**
|
|
194
333
|
* After executable AC pass, FINALIZE the banking checkpoint (#3285).
|
|
@@ -231,11 +370,26 @@ function maybeAttachAcPassBank(result, plan, xbriefPath, options) {
|
|
|
231
370
|
...result,
|
|
232
371
|
ok: false,
|
|
233
372
|
code: 1,
|
|
373
|
+
resolution: "fail",
|
|
234
374
|
message: `verify:ac bank checkpoint failed (#3285): ${msg}` +
|
|
235
375
|
(result.message ? `\n${result.message}` : ""),
|
|
236
376
|
};
|
|
237
377
|
}
|
|
238
378
|
}
|
|
379
|
+
function configResult(message) {
|
|
380
|
+
return {
|
|
381
|
+
ok: false,
|
|
382
|
+
code: 2,
|
|
383
|
+
message,
|
|
384
|
+
commands: [],
|
|
385
|
+
runs: [],
|
|
386
|
+
sourceRung: "project_floor",
|
|
387
|
+
noneStated: true,
|
|
388
|
+
acceptance: { commands: [], none_stated: true, source_rung: "project_floor" },
|
|
389
|
+
resolution: "config",
|
|
390
|
+
resolvedCommandCount: 0,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
239
393
|
function softSkip(detail, quiet) {
|
|
240
394
|
return {
|
|
241
395
|
ok: true,
|
|
@@ -246,6 +400,8 @@ function softSkip(detail, quiet) {
|
|
|
246
400
|
sourceRung: "project_floor",
|
|
247
401
|
noneStated: true,
|
|
248
402
|
acceptance: { commands: [], none_stated: true, source_rung: "project_floor" },
|
|
403
|
+
resolution: "skipped",
|
|
404
|
+
resolvedCommandCount: 0,
|
|
249
405
|
};
|
|
250
406
|
}
|
|
251
407
|
/** Pure: product AC is required at every ceremony depth (#3284 / #3267 / #3156). */
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { attachPlanAcceptance, buildAcceptanceFromIntakeCapture, readPlanAcceptance, stampAcceptanceFromLiteralCapture, validatePlanAcceptance, } from "./acceptance.js";
|
|
8
8
|
export { applyProductFirstGateMode, isHygieneGate, isProductAcGate, type ProductFirstCheckModeResolution, type ResolveProductFirstCheckModeInput, resolveProductFirstCheckMode, } from "./check-mode.js";
|
|
9
|
+
export { EMPTY_AC_CAUSE, EMPTY_AC_OUTCOME, EMPTY_AC_REMEDY, formatSoftEmptyMessage, isEmptyAcResolution, isSoftEmptyAcText, projectHasSuiteFloor, type VerifyAcResolution, } from "./empty-resolution.js";
|
|
9
10
|
export { type EvaluateVerifyAcOptions, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, type VerifyAcResult, } from "./evaluate.js";
|
|
10
11
|
export { type AcceptanceCommand, type AcSourceRung, ENV_CHECK_AC_ONLY, ENV_CHECK_MODE, ENV_HYGIENE_ADVISORY, HYGIENE_GATE_ID_PREFIXES, PLAN_ACCEPTANCE_KEY, type PlanAcceptance, PRODUCT_AC_GATE_ID, type ProductFirstCheckMode, } from "./types.js";
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { attachPlanAcceptance, buildAcceptanceFromIntakeCapture, readPlanAcceptance, stampAcceptanceFromLiteralCapture, validatePlanAcceptance, } from "./acceptance.js";
|
|
8
8
|
export { applyProductFirstGateMode, isHygieneGate, isProductAcGate, resolveProductFirstCheckMode, } from "./check-mode.js";
|
|
9
|
+
export { EMPTY_AC_CAUSE, EMPTY_AC_OUTCOME, EMPTY_AC_REMEDY, formatSoftEmptyMessage, isEmptyAcResolution, isSoftEmptyAcText, projectHasSuiteFloor, } from "./empty-resolution.js";
|
|
9
10
|
export { evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, } from "./evaluate.js";
|
|
10
11
|
export { ENV_CHECK_AC_ONLY, ENV_CHECK_MODE, ENV_HYGIENE_ADVISORY, HYGIENE_GATE_ID_PREFIXES, PLAN_ACCEPTANCE_KEY, PRODUCT_AC_GATE_ID, } from "./types.js";
|
|
11
12
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AcceptanceClause } from "../verify-ac/clauses.js";
|
|
1
2
|
/**
|
|
2
3
|
* Product-first done-gate (#3284): stated acceptance criteria run FIRST and are
|
|
3
4
|
* never skippable; hygiene gates run second and may degrade under pressure.
|
|
@@ -31,6 +32,8 @@ export interface PlanAcceptance {
|
|
|
31
32
|
readonly source_rung: AcSourceRung;
|
|
32
33
|
/** Why derived/floor was chosen (required for derived when recorded). */
|
|
33
34
|
readonly derived_reason?: string | null;
|
|
35
|
+
/** Rung-2 independently testable clauses (#3323). Parallel to commands[]. */
|
|
36
|
+
readonly clauses?: readonly AcceptanceClause[];
|
|
34
37
|
}
|
|
35
38
|
/** How `task check` composes product vs hygiene gates under pressure (#3284). */
|
|
36
39
|
export type ProductFirstCheckMode =
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Product-first done-gate (#3284): stated acceptance criteria run FIRST and are
|
|
3
|
-
* never skippable; hygiene gates run second and may degrade under pressure.
|
|
4
|
-
*
|
|
5
|
-
* Architecture for #3267 (literal acceptance-command mechanism). Composes
|
|
6
|
-
* #3214 ceremony dial (rapid = AC-only) and #3266 bank-the-pass.
|
|
7
|
-
*/
|
|
8
1
|
/** Canonical product AC gate id used in check gate lists. */
|
|
9
2
|
export const PRODUCT_AC_GATE_ID = "verify:ac";
|
|
10
3
|
/** plan.acceptance object key on the plan root. */
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Symlink destinations are refused (no-follow / containment) — fail-open.
|
|
7
7
|
*/
|
|
8
8
|
import { type ResolveRunSummaryDestinationOptions } from "./path.js";
|
|
9
|
-
import { type CheckInvocationRunSummaryPayload, type DialTransitionRunSummaryPayload, type RunSummaryDestination, type RunSummaryEventKind, type RunSummaryLine, type RunSummaryPayload, type SessionStartRunSummaryPayload } from "./types.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
10
|
export interface RunSummaryEmitterOptions extends ResolveRunSummaryDestinationOptions {
|
|
11
11
|
readonly projectRoot: string;
|
|
12
12
|
readonly sessionId: string;
|
|
@@ -36,6 +36,7 @@ export declare class RunSummaryEmitter {
|
|
|
36
36
|
private readonly frameworkVersion;
|
|
37
37
|
private readonly now;
|
|
38
38
|
private readonly destination;
|
|
39
|
+
private readonly env;
|
|
39
40
|
private readonly writeStdout;
|
|
40
41
|
private readonly writeStderr;
|
|
41
42
|
constructor(options: RunSummaryEmitterOptions);
|
|
@@ -43,8 +44,17 @@ export declare class RunSummaryEmitter {
|
|
|
43
44
|
emit(event: RunSummaryEventKind, payload: RunSummaryPayload): EmitRunSummaryResult;
|
|
44
45
|
emitSessionStart(payload: SessionStartRunSummaryPayload): EmitRunSummaryResult;
|
|
45
46
|
emitDialTransition(payload: DialTransitionRunSummaryPayload): EmitRunSummaryResult;
|
|
47
|
+
emitDialEscalationEvaluation(payload: DialEscalationEvaluationRunSummaryPayload): EmitRunSummaryResult;
|
|
46
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;
|
|
47
55
|
}
|
|
56
|
+
/** Parse harness-supplied DEFT_TOTAL_TOOL_TURNS (integer > 0). Invalid/unset → omit. */
|
|
57
|
+
export declare function readEnvToolTurnDenominator(env: NodeJS.ProcessEnv): number | undefined;
|
|
48
58
|
/**
|
|
49
59
|
* One-shot helper for call sites that do not hold an emitter (e.g. dial escalate).
|
|
50
60
|
* Still fail-open; creates a fresh seq=1 emitter unless `seqSeed` is passed via reuse.
|