@deftai/directive-core 0.100.0 → 0.102.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/dist/authz/classify.js +193 -27
  2. package/dist/authz/decompose-apply.d.ts +14 -0
  3. package/dist/authz/decompose-apply.js +93 -18
  4. package/dist/check/cached-orchestrator.d.ts +30 -5
  5. package/dist/check/cached-orchestrator.js +297 -10
  6. package/dist/check/cli-native-gates.d.ts +43 -0
  7. package/dist/check/cli-native-gates.js +84 -0
  8. package/dist/check/gate-lists.d.ts +10 -4
  9. package/dist/check/gate-lists.js +19 -5
  10. package/dist/check/index.d.ts +4 -2
  11. package/dist/check/index.js +3 -1
  12. package/dist/check/named-cause.d.ts +45 -0
  13. package/dist/check/named-cause.js +131 -0
  14. package/dist/check/orchestrator.js +6 -1
  15. package/dist/doctor/checks.d.ts +5 -6
  16. package/dist/doctor/checks.js +13 -56
  17. package/dist/doctor/help.d.ts +5 -0
  18. package/dist/doctor/help.js +31 -0
  19. package/dist/doctor/index.d.ts +2 -0
  20. package/dist/doctor/index.js +2 -0
  21. package/dist/doctor/main.js +58 -0
  22. package/dist/doctor/session-coda.d.ts +82 -0
  23. package/dist/doctor/session-coda.js +151 -0
  24. package/dist/doctor/types.d.ts +16 -0
  25. package/dist/eval/later-graduation.d.ts +39 -0
  26. package/dist/eval/later-graduation.js +91 -0
  27. package/dist/eval/report.d.ts +6 -0
  28. package/dist/eval/report.js +27 -8
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.js +3 -0
  31. package/dist/init-deposit/gitignore.js +2 -0
  32. package/dist/intake/issue-ingest.js +49 -0
  33. package/dist/policy/ac-pass-banking.d.ts +58 -0
  34. package/dist/policy/ac-pass-banking.js +150 -0
  35. package/dist/policy/ceremony-dial-escalation.d.ts +52 -0
  36. package/dist/policy/ceremony-dial-escalation.js +92 -0
  37. package/dist/policy/ceremony-dial.d.ts +32 -1
  38. package/dist/policy/ceremony-dial.js +128 -1
  39. package/dist/policy/check-resume.d.ts +9 -38
  40. package/dist/policy/check-resume.js +18 -156
  41. package/dist/policy/coverage-debt.d.ts +18 -37
  42. package/dist/policy/coverage-debt.js +38 -147
  43. package/dist/policy/index.d.ts +2 -1
  44. package/dist/policy/index.js +16 -2
  45. package/dist/preflight-cache/evaluate.d.ts +3 -0
  46. package/dist/preflight-cache/evaluate.js +3 -0
  47. package/dist/product-first-done-gate/acceptance.d.ts +25 -0
  48. package/dist/product-first-done-gate/acceptance.js +269 -0
  49. package/dist/product-first-done-gate/check-mode.d.ts +45 -0
  50. package/dist/product-first-done-gate/check-mode.js +148 -0
  51. package/dist/product-first-done-gate/empty-resolution.d.ts +26 -0
  52. package/dist/product-first-done-gate/empty-resolution.js +38 -0
  53. package/dist/product-first-done-gate/evaluate.d.ts +76 -0
  54. package/dist/product-first-done-gate/evaluate.js +411 -0
  55. package/dist/product-first-done-gate/index.d.ts +12 -0
  56. package/dist/product-first-done-gate/index.js +12 -0
  57. package/dist/product-first-done-gate/types.d.ts +67 -0
  58. package/dist/product-first-done-gate/types.js +54 -0
  59. package/dist/run-summary/emit.d.ts +66 -0
  60. package/dist/run-summary/emit.js +217 -0
  61. package/dist/run-summary/index.d.ts +5 -0
  62. package/dist/run-summary/index.js +5 -0
  63. package/dist/run-summary/path.d.ts +25 -0
  64. package/dist/run-summary/path.js +78 -0
  65. package/dist/run-summary/share.d.ts +25 -0
  66. package/dist/run-summary/share.js +106 -0
  67. package/dist/run-summary/types.d.ts +134 -0
  68. package/dist/run-summary/types.js +28 -0
  69. package/dist/scope/acceptance-activate-gate.d.ts +23 -0
  70. package/dist/scope/acceptance-activate-gate.js +70 -0
  71. package/dist/scope/acceptance-evidence.d.ts +34 -0
  72. package/dist/scope/acceptance-evidence.js +84 -10
  73. package/dist/scope/decompose.d.ts +5 -0
  74. package/dist/scope/decompose.js +11 -2
  75. package/dist/scope/index.d.ts +1 -0
  76. package/dist/scope/index.js +1 -0
  77. package/dist/scope/transition.js +5 -0
  78. package/dist/session/ac-pass-banking.d.ts +221 -0
  79. package/dist/session/ac-pass-banking.js +761 -0
  80. package/dist/session/deposit-sha.d.ts +49 -0
  81. package/dist/session/deposit-sha.js +121 -0
  82. package/dist/session/effort-budget.d.ts +6 -0
  83. package/dist/session/effort-budget.js +21 -3
  84. package/dist/session/index.d.ts +5 -1
  85. package/dist/session/index.js +5 -1
  86. package/dist/session/orientation-compression.d.ts +127 -0
  87. package/dist/session/orientation-compression.js +435 -0
  88. package/dist/session/orientation-state.d.ts +29 -0
  89. package/dist/session/orientation-state.js +85 -0
  90. package/dist/session/session-start.d.ts +31 -0
  91. package/dist/session/session-start.js +219 -22
  92. package/dist/session/toolchain-preflight.d.ts +79 -0
  93. package/dist/session/toolchain-preflight.js +211 -0
  94. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  95. package/dist/triage/bootstrap/gitignore.js +8 -2
  96. package/dist/triage/bootstrap/index.js +15 -3
  97. package/dist/triage/classify/index.d.ts +1 -0
  98. package/dist/triage/classify/index.js +2 -0
  99. package/dist/triage/classify/label-mirror.js +16 -0
  100. package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
  101. package/dist/triage/classify/mirror-discovery-tip.js +234 -0
  102. package/dist/triage/reconcile/reconcile.js +28 -6
  103. package/dist/triage/summary/index.js +12 -4
  104. package/dist/triage/welcome/default-mode.js +16 -0
  105. package/dist/ts-check-lane/run-lane.js +21 -4
  106. package/dist/vbrief-validate/conformance.js +7 -0
  107. package/dist/verify-ac/clauses.d.ts +51 -0
  108. package/dist/verify-ac/clauses.js +490 -0
  109. package/dist/verify-ac/evaluate.d.ts +55 -0
  110. package/dist/verify-ac/evaluate.js +145 -0
  111. package/dist/verify-ac/flag.d.ts +35 -0
  112. package/dist/verify-ac/flag.js +104 -0
  113. package/dist/verify-ac/index.d.ts +10 -0
  114. package/dist/verify-ac/index.js +10 -0
  115. package/dist/vitest-runner/coverage-debt-teardown.js +16 -4
  116. package/package.json +15 -3
  117. package/dist/policy/coverage-check-resume-presets.d.ts +0 -46
  118. package/dist/policy/coverage-check-resume-presets.js +0 -228
  119. package/dist/session/coverage-check-resume-nudge.d.ts +0 -34
  120. package/dist/session/coverage-check-resume-nudge.js +0 -66
@@ -0,0 +1,150 @@
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
+ import { readPlanPolicy } from "./plan-extensions.js";
11
+ import { loadProjectDefinition } from "./resolve.js";
12
+ /** Canonical dotted path for policy:show / PROJECT-DEFINITION. */
13
+ export const FIELD_AC_PASS_BANKING = "plan.policy.acPassBanking";
14
+ /** CLI alias for `task policy:show --field=acPassBanking`. */
15
+ export const FIELD_AC_PASS_BANKING_CLI_ALIAS = "acPassBanking";
16
+ /**
17
+ * Default surplus fraction of max turns/cost that must remain after bank
18
+ * before self-imposed deepening is allowed (#3285).
19
+ */
20
+ export const DEFAULT_SURPLUS_THRESHOLD = 0.2;
21
+ /** Floor / ceiling for surplusThreshold (inclusive). */
22
+ export const SURPLUS_THRESHOLD_MIN = 0;
23
+ export const SURPLUS_THRESHOLD_MAX = 1;
24
+ /** Optional env override (fraction 0–1 or percent 0–100). */
25
+ export const ENV_SURPLUS_THRESHOLD = "DEFT_AC_PASS_SURPLUS_THRESHOLD";
26
+ function defaultResolved(source, error = null, override) {
27
+ return {
28
+ enabled: override?.enabled ?? true,
29
+ surplusThreshold: override?.surplusThreshold ?? DEFAULT_SURPLUS_THRESHOLD,
30
+ source,
31
+ error,
32
+ };
33
+ }
34
+ /** Parse a 0–1 fraction or a 0–100 percent string/number into a fraction. */
35
+ export function parseSurplusThreshold(raw) {
36
+ if (typeof raw === "number" && Number.isFinite(raw)) {
37
+ if (raw >= 0 && raw <= 1)
38
+ return raw;
39
+ // Allow 20 meaning 20% when clearly a percent.
40
+ if (raw > 1 && raw <= 100)
41
+ return raw / 100;
42
+ return null;
43
+ }
44
+ if (typeof raw === "string") {
45
+ const text = raw.trim();
46
+ if (!text)
47
+ return null;
48
+ const percent = text.endsWith("%");
49
+ const n = Number(percent ? text.slice(0, -1).trim() : text);
50
+ if (!Number.isFinite(n))
51
+ return null;
52
+ if (percent) {
53
+ if (n < 0 || n > 100)
54
+ return null;
55
+ return n / 100;
56
+ }
57
+ if (n >= 0 && n <= 1)
58
+ return n;
59
+ if (n > 1 && n <= 100)
60
+ return n / 100;
61
+ return null;
62
+ }
63
+ return null;
64
+ }
65
+ /** Validate a `plan.policy.acPassBanking` payload. */
66
+ export function validateAcPassBanking(value) {
67
+ if (value === null || value === undefined) {
68
+ return [];
69
+ }
70
+ if (typeof value !== "object" || Array.isArray(value)) {
71
+ return [`${FIELD_AC_PASS_BANKING} must be an object; got ${typeof value}`];
72
+ }
73
+ const rec = value;
74
+ const errors = [];
75
+ if ("enabled" in rec && typeof rec.enabled !== "boolean") {
76
+ errors.push(`${FIELD_AC_PASS_BANKING}.enabled must be a boolean`);
77
+ }
78
+ if ("surplusThreshold" in rec) {
79
+ const parsed = parseSurplusThreshold(rec.surplusThreshold);
80
+ if (parsed === null) {
81
+ errors.push(`${FIELD_AC_PASS_BANKING}.surplusThreshold must be a fraction 0–1 ` +
82
+ `(or percent 0–100); got ${JSON.stringify(rec.surplusThreshold)}`);
83
+ }
84
+ }
85
+ return errors;
86
+ }
87
+ /**
88
+ * Resolve surplus policy from typed plan.policy.acPassBanking, then env,
89
+ * then defaults (#3285).
90
+ */
91
+ export function resolveAcPassBanking(projectRoot, environ = process.env) {
92
+ if (projectRoot !== undefined && projectRoot !== null && projectRoot.length > 0) {
93
+ const [data, err] = loadProjectDefinition(projectRoot);
94
+ if (data !== null) {
95
+ const policyBlock = readPlanPolicy(data.plan);
96
+ if (typeof policyBlock === "object" &&
97
+ policyBlock !== null &&
98
+ !Array.isArray(policyBlock) &&
99
+ "acPassBanking" in policyBlock) {
100
+ const raw = policyBlock.acPassBanking;
101
+ const errors = validateAcPassBanking(raw);
102
+ if (errors.length > 0) {
103
+ return defaultResolved("default-on-error", errors[0] ?? "invalid acPassBanking");
104
+ }
105
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
106
+ const rec = raw;
107
+ const enabled = typeof rec.enabled === "boolean" ? rec.enabled : true;
108
+ const threshold = "surplusThreshold" in rec
109
+ ? (parseSurplusThreshold(rec.surplusThreshold) ?? DEFAULT_SURPLUS_THRESHOLD)
110
+ : DEFAULT_SURPLUS_THRESHOLD;
111
+ return {
112
+ enabled,
113
+ surplusThreshold: threshold,
114
+ source: "typed",
115
+ error: null,
116
+ };
117
+ }
118
+ }
119
+ }
120
+ else if (err !== null && err.length > 0) {
121
+ // Fall through to env/default; record nothing as fatal.
122
+ }
123
+ }
124
+ const envRaw = environ[ENV_SURPLUS_THRESHOLD];
125
+ if (envRaw !== undefined && envRaw.trim() !== "") {
126
+ const parsed = parseSurplusThreshold(envRaw);
127
+ if (parsed !== null) {
128
+ return defaultResolved("env", null, { surplusThreshold: parsed });
129
+ }
130
+ return defaultResolved("default-on-error", `${ENV_SURPLUS_THRESHOLD} is not a valid fraction/percent: ${envRaw}`);
131
+ }
132
+ return defaultResolved("default");
133
+ }
134
+ /** Inspector row for `task policy:show --field=acPassBanking` (#3285). */
135
+ export function inspectAcPassBanking(_data, projectRoot) {
136
+ const resolved = resolveAcPassBanking(projectRoot);
137
+ return {
138
+ name: FIELD_AC_PASS_BANKING,
139
+ current: {
140
+ enabled: resolved.enabled,
141
+ surplusThreshold: resolved.surplusThreshold,
142
+ },
143
+ default: {
144
+ enabled: true,
145
+ surplusThreshold: DEFAULT_SURPLUS_THRESHOLD,
146
+ },
147
+ source: resolved.source,
148
+ };
149
+ }
150
+ //# sourceMappingURL=ac-pass-banking.js.map
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Ceremony-dial escalation evaluation + start-tier provenance (#3319).
3
+ *
4
+ * Observability only: does not change escalate-on-evidence thresholds or the
5
+ * default start tier. A declined evaluation is distinct from never-evaluated.
6
+ */
7
+ import type { DialEscalationEvaluationOutcome } from "../run-summary/types.js";
8
+ import { type CeremonyDepth, type CeremonyDialInputs, type CeremonyDialSelection, type CeremonyModelTier } from "./ceremony-dial.js";
9
+ export declare const CEREMONY_START_TIER_PROVENANCES: readonly ["cold-start", "external-pin", "operator"];
10
+ export type CeremonyStartTierProvenance = (typeof CEREMONY_START_TIER_PROVENANCES)[number];
11
+ export declare const CEREMONY_DEPTH_RANK: Readonly<Record<CeremonyDepth, number>>;
12
+ export interface ResolveCeremonyStartTierProvenanceInput {
13
+ readonly selection: CeremonyDialSelection;
14
+ /** True when session:start received a pre-resolved selection (CLI/harness). */
15
+ readonly injectedSelection: boolean;
16
+ readonly hint?: CeremonyStartTierProvenance;
17
+ }
18
+ export declare function resolveCeremonyStartTierProvenance(input: ResolveCeremonyStartTierProvenanceInput): CeremonyStartTierProvenance;
19
+ export declare function isCeremonyStartTierPinned(provenance: CeremonyStartTierProvenance): boolean;
20
+ /** Session-start fail-closed line when a pin bypasses #3274 cold-start selection. */
21
+ export declare function formatCeremonyDialPinBypassLine(provenance: CeremonyStartTierProvenance): string | null;
22
+ export interface CeremonyDialEscalationEvaluation {
23
+ readonly tier: CeremonyDepth;
24
+ readonly outcome: DialEscalationEvaluationOutcome;
25
+ readonly reason: string;
26
+ readonly from: CeremonyDepth;
27
+ readonly to: CeremonyDepth;
28
+ }
29
+ export interface EvaluateCeremonyDialEscalationInput {
30
+ readonly from: CeremonyDepth;
31
+ readonly to: CeremonyDepth;
32
+ readonly inputs?: CeremonyDialInputs;
33
+ }
34
+ /**
35
+ * Pure escalate-on-evidence evaluation. `to > from` → escalated; else declined.
36
+ * Does not persist a transition.
37
+ */
38
+ export declare function evaluateCeremonyDialEscalation(input: EvaluateCeremonyDialEscalationInput): CeremonyDialEscalationEvaluation;
39
+ /** Cold-start vs selected depth at session:start (unpinned path only). */
40
+ export declare function evaluateSessionStartCeremonyDialEscalation(input: {
41
+ readonly selection: CeremonyDialSelection;
42
+ readonly modelTier?: CeremonyModelTier | null;
43
+ }): CeremonyDialEscalationEvaluation;
44
+ export interface EmitCeremonyDialEscalationEvaluationOptions {
45
+ readonly projectRoot: string;
46
+ readonly sessionId: string;
47
+ readonly env?: NodeJS.ProcessEnv;
48
+ readonly evaluation: CeremonyDialEscalationEvaluation;
49
+ }
50
+ /** Fail-open emit of a #3319 evaluation event. */
51
+ export declare function emitCeremonyDialEscalationEvaluation(options: EmitCeremonyDialEscalationEvaluationOptions): void;
52
+ //# sourceMappingURL=ceremony-dial-escalation.d.ts.map
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Ceremony-dial escalation evaluation + start-tier provenance (#3319).
3
+ *
4
+ * Observability only: does not change escalate-on-evidence thresholds or the
5
+ * default start tier. A declined evaluation is distinct from never-evaluated.
6
+ */
7
+ import { RunSummaryEmitter } from "../run-summary/emit.js";
8
+ import { selectCeremonyColdStartDepth, } from "./ceremony-dial.js";
9
+ export const CEREMONY_START_TIER_PROVENANCES = ["cold-start", "external-pin", "operator"];
10
+ export const CEREMONY_DEPTH_RANK = {
11
+ minimal: 0,
12
+ rapid: 1,
13
+ standard: 2,
14
+ elevated: 3,
15
+ };
16
+ export function resolveCeremonyStartTierProvenance(input) {
17
+ if (input.hint !== undefined) {
18
+ return input.hint;
19
+ }
20
+ if (input.selection.source === "override") {
21
+ return input.injectedSelection ? "external-pin" : "operator";
22
+ }
23
+ return "cold-start";
24
+ }
25
+ export function isCeremonyStartTierPinned(provenance) {
26
+ return provenance === "external-pin" || provenance === "operator";
27
+ }
28
+ /** Session-start fail-closed line when a pin bypasses #3274 cold-start selection. */
29
+ export function formatCeremonyDialPinBypassLine(provenance) {
30
+ if (!isCeremonyStartTierPinned(provenance)) {
31
+ return null;
32
+ }
33
+ const pinLabel = provenance === "external-pin" ? "external-pin" : "operator pin";
34
+ return (`[deft ceremony-dial] #3274 cold-start selection is bypassed (${pinLabel}). ` +
35
+ "Unset the pin (--ceremony-depth or plan.policy.ceremonyDial.override) to exercise #3274.");
36
+ }
37
+ /**
38
+ * Pure escalate-on-evidence evaluation. `to > from` → escalated; else declined.
39
+ * Does not persist a transition.
40
+ */
41
+ export function evaluateCeremonyDialEscalation(input) {
42
+ const size = input.inputs?.taskSize ?? null;
43
+ const modelTier = input.inputs?.modelTier ?? null;
44
+ const evidence = `size=${size ?? "-"} modelTier=${modelTier ?? "-"}`;
45
+ const raised = CEREMONY_DEPTH_RANK[input.to] > CEREMONY_DEPTH_RANK[input.from];
46
+ if (raised) {
47
+ return {
48
+ tier: input.to,
49
+ outcome: "escalated",
50
+ reason: `evidence raised ${input.from} -> ${input.to} (${evidence})`,
51
+ from: input.from,
52
+ to: input.to,
53
+ };
54
+ }
55
+ return {
56
+ tier: input.from,
57
+ outcome: "declined",
58
+ reason: `insufficient evidence to raise above ${input.from} (${evidence})`,
59
+ from: input.from,
60
+ to: input.to,
61
+ };
62
+ }
63
+ /** Cold-start vs selected depth at session:start (unpinned path only). */
64
+ export function evaluateSessionStartCeremonyDialEscalation(input) {
65
+ const modelTier = input.modelTier ?? input.selection.inputs.modelTier;
66
+ const from = selectCeremonyColdStartDepth(modelTier);
67
+ return evaluateCeremonyDialEscalation({
68
+ from,
69
+ to: input.selection.depth,
70
+ inputs: input.selection.inputs,
71
+ });
72
+ }
73
+ /** Fail-open emit of a #3319 evaluation event. */
74
+ export function emitCeremonyDialEscalationEvaluation(options) {
75
+ try {
76
+ const emitter = new RunSummaryEmitter({
77
+ projectRoot: options.projectRoot,
78
+ sessionId: options.sessionId,
79
+ env: options.env,
80
+ });
81
+ emitter.emitDialEscalationEvaluation({
82
+ tier: options.evaluation.tier,
83
+ outcome: options.evaluation.outcome,
84
+ reason: options.evaluation.reason,
85
+ });
86
+ emitter.emitKnownToolTurnDenominator();
87
+ }
88
+ catch {
89
+ // fail-open
90
+ }
91
+ }
92
+ //# sourceMappingURL=ceremony-dial-escalation.js.map
@@ -161,7 +161,9 @@ export interface ResolveCeremonyDialOptions {
161
161
  */
162
162
  export declare function resolveCeremonyDial(projectRoot: string, options?: ResolveCeremonyDialOptions): CeremonyDialSelection;
163
163
  /** Human-readable status line for session:start / policy:show. */
164
- export declare function formatCeremonyDialStatusLine(selection: CeremonyDialSelection): string;
164
+ export declare function formatCeremonyDialStatusLine(selection: CeremonyDialSelection, extras?: {
165
+ readonly startTierProvenance?: string;
166
+ }): string;
165
167
  /** JSON-serializable dial snapshot for ritual-state / session payload. */
166
168
  export declare function ceremonyDialToDict(selection: CeremonyDialSelection): Record<string, unknown>;
167
169
  export interface CeremonyDialPolicyField {
@@ -286,4 +288,33 @@ export interface CeremonyDialAuditSnapshot {
286
288
  export declare function readCeremonyDialAudit(projectRoot: string): CeremonyDialAuditSnapshot;
287
289
  /** One-line operator summary for audit tooling / failed-task forensics (#3263). */
288
290
  export declare function formatCeremonyDialAuditLine(audit: CeremonyDialAuditSnapshot): string;
291
+ /**
292
+ * Reified dial transition for run-summary telemetry (#3282).
293
+ *
294
+ * IMPLEMENTATION PREREQUISITE from the issue: dial transitions must be
295
+ * engine-managed events, not prose-only policy. This function is the CLI-facing
296
+ * transition hook (`policy:set-ceremony-dial` / dial escalate): it persists the
297
+ * override (when confirm) and always emits a `dial_transition` run-summary line
298
+ * when a depth change is applied.
299
+ */
300
+ export interface EscalateCeremonyDialOptions {
301
+ readonly to: CeremonyDepth;
302
+ readonly reason: string;
303
+ readonly evidence?: string;
304
+ readonly sessionId?: string;
305
+ readonly confirm?: boolean;
306
+ readonly actor?: string;
307
+ readonly note?: string;
308
+ /** When false, skip run-summary emission (tests). Default true. */
309
+ readonly emitRunSummary?: boolean;
310
+ readonly env?: NodeJS.ProcessEnv;
311
+ }
312
+ export interface EscalateCeremonyDialResult {
313
+ readonly exitCode: number;
314
+ readonly from: CeremonyDepth | null;
315
+ readonly to: CeremonyDepth;
316
+ readonly changed: boolean;
317
+ readonly lines: readonly string[];
318
+ }
319
+ export declare function escalateCeremonyDial(projectRoot: string, options: EscalateCeremonyDialOptions): EscalateCeremonyDialResult;
289
320
  //# sourceMappingURL=ceremony-dial.d.ts.map
@@ -19,6 +19,7 @@
19
19
  */
20
20
  import { existsSync, readFileSync, statSync } from "node:fs";
21
21
  import { join } from "node:path";
22
+ import { RunSummaryEmitter } from "../run-summary/emit.js";
22
23
  import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
23
24
  import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
24
25
  import { policyColonInvocation } from "./policy-invocation.js";
@@ -400,7 +401,7 @@ export function resolveCeremonyDial(projectRoot, options = {}) {
400
401
  return selected;
401
402
  }
402
403
  /** Human-readable status line for session:start / policy:show. */
403
- export function formatCeremonyDialStatusLine(selection) {
404
+ export function formatCeremonyDialStatusLine(selection, extras) {
404
405
  const parts = [
405
406
  `[deft ceremony-dial] depth=${selection.depth}`,
406
407
  `source=${selection.source}`,
@@ -408,6 +409,10 @@ export function formatCeremonyDialStatusLine(selection) {
408
409
  `modelTier=${selection.inputs.modelTier ?? "-"}`,
409
410
  `projectShape=${selection.inputs.projectShape ?? "-"}`,
410
411
  ];
412
+ if (extras?.startTierProvenance !== undefined && extras.startTierProvenance.length > 0) {
413
+ parts.push(`start-tier=${selection.depth}`);
414
+ parts.push(`provenance=${extras.startTierProvenance}`);
415
+ }
411
416
  if (selection.composition.rapidStrategy) {
412
417
  parts.push(`compose=${selection.composition.rapidStrategy}`);
413
418
  }
@@ -977,4 +982,126 @@ export function formatCeremonyDialAuditLine(audit) {
977
982
  }
978
983
  return parts.join(" ");
979
984
  }
985
+ export function escalateCeremonyDial(projectRoot, options) {
986
+ const prior = resolveCeremonyDial(projectRoot);
987
+ const from = prior.depth;
988
+ const emitEscalationEvaluation = (applied, reasonSuffix) => {
989
+ if (options.emitRunSummary === false) {
990
+ return;
991
+ }
992
+ try {
993
+ const audit = readCeremonyDialAudit(projectRoot);
994
+ const rawSid = audit.raw?.session_id;
995
+ const sid = options.sessionId ??
996
+ (typeof rawSid === "string" && rawSid.length > 0
997
+ ? rawSid
998
+ : `dial-${Date.now().toString(36)}`);
999
+ const rank = {
1000
+ minimal: 0,
1001
+ rapid: 1,
1002
+ standard: 2,
1003
+ elevated: 3,
1004
+ };
1005
+ const raised = rank[options.to] > rank[from];
1006
+ const outcome = applied && raised ? "escalated" : "declined";
1007
+ const baseReason = options.confirm === true
1008
+ ? options.reason
1009
+ : `${options.reason}; not applied (need --confirm)`;
1010
+ const reason = reasonSuffix !== undefined && reasonSuffix.length > 0
1011
+ ? `${baseReason}; ${reasonSuffix}`
1012
+ : baseReason;
1013
+ const emitter = new RunSummaryEmitter({
1014
+ projectRoot,
1015
+ sessionId: sid,
1016
+ env: options.env,
1017
+ });
1018
+ emitter.emitDialEscalationEvaluation({
1019
+ tier: outcome === "escalated" ? options.to : from,
1020
+ outcome,
1021
+ reason,
1022
+ });
1023
+ emitter.emitKnownToolTurnDenominator();
1024
+ }
1025
+ catch {
1026
+ // fail-open
1027
+ }
1028
+ };
1029
+ if (options.confirm !== true) {
1030
+ emitEscalationEvaluation(false);
1031
+ return {
1032
+ exitCode: 1,
1033
+ from,
1034
+ to: options.to,
1035
+ changed: false,
1036
+ lines: [
1037
+ "Ceremony dial escalate changes ritual depth for future session:start.",
1038
+ " Re-run with --confirm to apply.",
1039
+ ` Proposed: ${from} -> ${options.to} (reason: ${oneLineAuditToken(options.reason)})`,
1040
+ ],
1041
+ };
1042
+ }
1043
+ const setResult = setCeremonyDial(projectRoot, {
1044
+ override: options.to,
1045
+ confirm: true,
1046
+ actor: options.actor ?? policyColonInvocation("set-ceremony-dial"),
1047
+ note: options.note ?? `escalate: ${options.reason}`,
1048
+ });
1049
+ if (setResult.exitCode !== 0) {
1050
+ const persistDetail = oneLineAuditToken(setResult.stdout.trim().split("\n")[0] ?? `exit ${setResult.exitCode}`);
1051
+ emitEscalationEvaluation(false, `persist failed (exit ${setResult.exitCode}): ${persistDetail}`);
1052
+ return {
1053
+ exitCode: setResult.exitCode,
1054
+ from,
1055
+ to: options.to,
1056
+ changed: false,
1057
+ lines: setResult.stdout
1058
+ .split("\n")
1059
+ .map((l) => l.trimEnd())
1060
+ .filter((l) => l.length > 0),
1061
+ };
1062
+ }
1063
+ // #3319: evaluation event on every escalate path (applied or not).
1064
+ emitEscalationEvaluation(true);
1065
+ // #3282: event-driven dial_transition line (fail-open).
1066
+ if (options.emitRunSummary !== false) {
1067
+ try {
1068
+ const audit = readCeremonyDialAudit(projectRoot);
1069
+ const rawSid = audit.raw?.session_id;
1070
+ const sid = options.sessionId ??
1071
+ (typeof rawSid === "string" && rawSid.length > 0
1072
+ ? rawSid
1073
+ : `dial-${Date.now().toString(36)}`);
1074
+ const emitter = new RunSummaryEmitter({
1075
+ projectRoot,
1076
+ sessionId: sid,
1077
+ env: options.env,
1078
+ });
1079
+ emitter.emitDialTransition({
1080
+ from,
1081
+ to: options.to,
1082
+ reason: options.reason,
1083
+ evidence: options.evidence,
1084
+ });
1085
+ emitter.emitKnownToolTurnDenominator();
1086
+ }
1087
+ catch {
1088
+ // fail-open
1089
+ }
1090
+ }
1091
+ return {
1092
+ exitCode: 0,
1093
+ from,
1094
+ to: options.to,
1095
+ changed: setResult.changed || from !== options.to,
1096
+ lines: [
1097
+ `✓ ceremony dial escalate: ${from} -> ${options.to}`,
1098
+ ` reason: ${oneLineAuditToken(options.reason)}`,
1099
+ ...(options.evidence ? [` evidence: ${oneLineAuditToken(options.evidence)}`] : []),
1100
+ ...setResult.stdout
1101
+ .split("\n")
1102
+ .map((l) => l.trimEnd())
1103
+ .filter((l) => l.length > 0),
1104
+ ],
1105
+ };
1106
+ }
980
1107
  //# sourceMappingURL=ceremony-dial.js.map
@@ -1,52 +1,36 @@
1
1
  /**
2
- * Check-resume / local suite-stamp policy (#3189).
2
+ * Check-resume / local suite-stamp policy (#3189 / #3314).
3
3
  *
4
- * Consent / SoT surface for consumer expansion of local suite-stamp resume
5
- * (#3188). Unset is fail-closed (localStamp off). CI never trusts a local
6
- * stamp in v1 — `ciTrustsLocalStamp` is fixed false.
4
+ * Plain optional setting. Default off. Fail-closed when absent or invalid.
5
+ * Reserved consumer expansion not implemented. CI never trusts a laptop stamp.
7
6
  */
8
7
  /** Canonical registered policy field name. */
9
8
  export declare const FIELD_CHECK_RESUME = "plan.policy.checkResume";
10
9
  /** Short alias for `policy:show --field=checkResume`. */
11
10
  export declare const FIELD_CHECK_RESUME_CLI_ALIAS = "checkResume";
12
- export declare const CHECK_RESUME_STATUSES: readonly ["unset", "decided"];
13
- export type CheckResumeStatus = (typeof CHECK_RESUME_STATUSES)[number];
14
11
  export declare const CHECK_RESUME_LOCAL_STAMP: readonly ["off", "on"];
15
12
  export type CheckResumeLocalStamp = (typeof CHECK_RESUME_LOCAL_STAMP)[number];
16
- /** Fail-closed when unset / missing. */
13
+ /** Fail-closed when absent / missing / invalid. */
17
14
  export declare const DEFAULT_CHECK_RESUME_LOCAL_STAMP: CheckResumeLocalStamp;
18
- /**
19
- * FIXED false in v1 (#3189 non-goal). Separate RFC required to ever allow CI
20
- * to trust a laptop suite stamp.
21
- */
22
- export declare const CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1: false;
23
15
  export interface CheckResumeConfig {
24
- readonly status: CheckResumeStatus;
25
16
  readonly localStamp: CheckResumeLocalStamp;
26
- /** Always false in v1; present for schema visibility. */
27
- readonly ciTrustsLocalStamp: false;
28
- /** Present when decided via dismiss-with-reason. */
29
- readonly dismissReason: string | null;
30
17
  }
31
18
  export type CheckResumeSource = "typed" | "default" | "default-on-error";
32
19
  export interface CheckResumeResolved extends CheckResumeConfig {
33
20
  readonly source: CheckResumeSource;
34
21
  readonly error: string | null;
35
22
  }
36
- /** Validate a `plan.policy.checkResume` payload. */
23
+ /** Validate a `plan.policy.checkResume` payload. Extra keys are ignored. */
37
24
  export declare function validateCheckResume(value: unknown): string[];
38
25
  /**
39
- * Resolve a typed block. Missing/invalid → localStamp off, status unset.
40
- * `ciTrustsLocalStamp` is always forced false regardless of input truthiness
41
- * (malformed non-false is validation error → fail-closed default).
26
+ * Resolve a typed block. Missing/invalid → localStamp off.
27
+ * Leftover ritual fields (status/dismissReason/ciTrustsLocalStamp) are ignored.
42
28
  */
43
29
  export declare function resolveCheckResumeFromTypedBlock(raw: unknown): CheckResumeResolved;
44
- /** Resolve `plan.policy.checkResume` from PROJECT-DEFINITION (#3189). */
30
+ /** Resolve `plan.policy.checkResume` from PROJECT-DEFINITION (#3314). */
45
31
  export declare function resolveCheckResume(projectRoot: string): CheckResumeResolved;
46
- /** Local suite stamp resume only when decided localStamp=on. */
32
+ /** Local suite stamp resume only when localStamp=on. Reserved — unused by check/release. */
47
33
  export declare function isLocalStampResumeAllowed(policy: CheckResumeResolved): boolean;
48
- /** Always false in v1 — CI must re-run suite, never trust a laptop stamp. */
49
- export declare function isCiTrustsLocalStampAllowed(_policy?: CheckResumeResolved): false;
50
34
  export declare function formatCheckResumeStatusLine(policy: CheckResumeResolved): string;
51
35
  export interface CheckResumePolicyField {
52
36
  readonly name: typeof FIELD_CHECK_RESUME;
@@ -56,17 +40,4 @@ export interface CheckResumePolicyField {
56
40
  }
57
41
  /** Inspector row for `policy:show --field=checkResume`. */
58
42
  export declare function inspectCheckResume(data: Record<string, unknown> | null, projectRoot?: string): CheckResumePolicyField;
59
- export interface WriteCheckResumeOptions {
60
- readonly localStamp: CheckResumeLocalStamp;
61
- readonly dismissReason?: string | null;
62
- readonly actor?: string;
63
- readonly note?: string;
64
- }
65
- export interface WriteCheckResumeResult {
66
- readonly exitCode: 0 | 2;
67
- readonly stdout: string;
68
- readonly changed: boolean;
69
- }
70
- /** Persist decided checkResume (preset or dismiss-with-reason). */
71
- export declare function writeCheckResume(projectRoot: string, options: WriteCheckResumeOptions): WriteCheckResumeResult;
72
43
  //# sourceMappingURL=check-resume.d.ts.map