@deftai/directive-core 0.103.0 → 0.104.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 +167 -4
- package/dist/check/gate-lists.d.ts +3 -2
- package/dist/check/gate-lists.js +5 -2
- package/dist/check/index.d.ts +1 -0
- package/dist/check/index.js +1 -0
- package/dist/check/session-completed-ac.d.ts +39 -0
- package/dist/check/session-completed-ac.js +198 -0
- package/dist/doctor/main.js +12 -7
- package/dist/fs/contained-write.js +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/intake/clause-derivation.d.ts +37 -0
- package/dist/intake/clause-derivation.js +207 -0
- package/dist/intake/index.d.ts +1 -0
- package/dist/intake/index.js +1 -0
- package/dist/intake/issue-ingest.js +2 -38
- package/dist/preflight/evaluate.d.ts +5 -0
- package/dist/preflight/evaluate.js +14 -0
- package/dist/preflight/index.d.ts +1 -1
- package/dist/product-first-done-gate/empty-resolution.d.ts +1 -1
- package/dist/product-first-done-gate/empty-resolution.js +1 -1
- package/dist/product-first-done-gate/evaluate.d.ts +17 -0
- package/dist/product-first-done-gate/evaluate.js +61 -10
- package/dist/product-first-done-gate/index.d.ts +1 -1
- package/dist/product-first-done-gate/index.js +1 -1
- package/dist/run-summary/emit.d.ts +42 -2
- package/dist/run-summary/emit.js +250 -25
- package/dist/run-summary/index.d.ts +1 -1
- package/dist/run-summary/index.js +1 -1
- package/dist/run-summary/share.d.ts +1 -1
- package/dist/run-summary/share.js +2 -2
- package/dist/run-summary/types.d.ts +10 -3
- package/dist/scope/acceptance-activate-gate.d.ts +1 -1
- package/dist/scope/acceptance-activate-gate.js +4 -4
- package/dist/scope/acceptance-evidence.d.ts +15 -0
- package/dist/scope/acceptance-evidence.js +39 -0
- package/dist/scope/capacity-stamp.d.ts +2 -0
- package/dist/scope/capacity-stamp.js +4 -0
- package/dist/scope/delivery-evidence.d.ts +6 -0
- package/dist/scope/delivery-evidence.js +17 -0
- package/dist/scope/transition.js +66 -4
- package/dist/session/ceremony-dial-evidence.d.ts +42 -0
- package/dist/session/ceremony-dial-evidence.js +194 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/session-start.js +21 -2
- package/dist/slice/existing.js +12 -1
- package/dist/slice/record.d.ts +8 -5
- package/dist/slice/record.js +21 -13
- package/dist/telemetry-coverage/evaluate.d.ts +42 -0
- package/dist/telemetry-coverage/evaluate.js +148 -0
- package/dist/telemetry-coverage/fake-trial.d.ts +45 -0
- package/dist/telemetry-coverage/fake-trial.js +194 -0
- package/dist/telemetry-coverage/index.d.ts +5 -0
- package/dist/telemetry-coverage/index.js +5 -0
- package/dist/telemetry-coverage/kinds.d.ts +19 -0
- package/dist/telemetry-coverage/kinds.js +52 -0
- package/dist/telemetry-coverage/scan-callers.d.ts +25 -0
- package/dist/telemetry-coverage/scan-callers.js +168 -0
- package/dist/vbrief-reconcile/index.d.ts +2 -0
- package/dist/vbrief-reconcile/index.js +1 -0
- package/dist/vbrief-reconcile/origin-freshness.d.ts +44 -0
- package/dist/vbrief-reconcile/origin-freshness.js +221 -0
- package/dist/verify-source/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-authored briefs run #3323 clause derivation on activate/promote (#3360).
|
|
3
|
+
*
|
|
4
|
+
* issue:ingest already stamps clauses. The dominant consumer path authors the
|
|
5
|
+
* brief by hand; #3334 only required plan.acceptance to exist. This module is
|
|
6
|
+
* the missing derive step.
|
|
7
|
+
*/
|
|
8
|
+
import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter } from "../run-summary/index.js";
|
|
9
|
+
import { deriveAcceptanceClauses, readAcceptanceClauses, serializeAcceptanceClauses, } from "../verify-ac/clauses.js";
|
|
10
|
+
const NARRATIVE_KEYS = [
|
|
11
|
+
"Overview",
|
|
12
|
+
"Description",
|
|
13
|
+
"Acceptance",
|
|
14
|
+
"AcceptanceCriteria",
|
|
15
|
+
"Acceptance sketch",
|
|
16
|
+
"AcceptanceSketch",
|
|
17
|
+
"Test",
|
|
18
|
+
"Verification",
|
|
19
|
+
"ImplementationPlan",
|
|
20
|
+
];
|
|
21
|
+
function asRecord(value) {
|
|
22
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function isNonEmptyString(value) {
|
|
28
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
29
|
+
}
|
|
30
|
+
/** True when activate/promote must run #3323 (absent, empty none_stated, or command-only). */
|
|
31
|
+
export function needsClauseDerivation(acceptance) {
|
|
32
|
+
if (acceptance === undefined || acceptance === null) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const rec = asRecord(acceptance);
|
|
36
|
+
if (rec === null) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return !(Array.isArray(rec.clauses) && rec.clauses.length > 0);
|
|
40
|
+
}
|
|
41
|
+
/** Task-statement text from title, narratives, and item Acceptance fields. */
|
|
42
|
+
export function collectTaskStatementFromPlan(plan) {
|
|
43
|
+
const parts = [];
|
|
44
|
+
if (isNonEmptyString(plan.title)) {
|
|
45
|
+
parts.push(plan.title.trim());
|
|
46
|
+
}
|
|
47
|
+
const narratives = asRecord(plan.narratives);
|
|
48
|
+
if (narratives !== null) {
|
|
49
|
+
const seen = new Set();
|
|
50
|
+
for (const key of NARRATIVE_KEYS) {
|
|
51
|
+
const value = narratives[key];
|
|
52
|
+
if (isNonEmptyString(value)) {
|
|
53
|
+
parts.push(value.trim());
|
|
54
|
+
seen.add(key.toLowerCase());
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const [key, value] of Object.entries(narratives)) {
|
|
58
|
+
if (seen.has(key.toLowerCase()) || !isNonEmptyString(value)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
parts.push(value.trim());
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(plan.items)) {
|
|
65
|
+
for (const item of plan.items) {
|
|
66
|
+
const rec = asRecord(item);
|
|
67
|
+
if (rec === null) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const narrative = asRecord(rec.narrative);
|
|
71
|
+
const text = narrative?.Acceptance;
|
|
72
|
+
if (isNonEmptyString(text)) {
|
|
73
|
+
parts.push(text.trim());
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return parts.join("\n\n");
|
|
78
|
+
}
|
|
79
|
+
function formatAmbiguousClauseNotice(clauses) {
|
|
80
|
+
const flagged = clauses.filter((clause) => clause.ambiguous);
|
|
81
|
+
const lines = [
|
|
82
|
+
`#3323 clause derivation stamped ${clauses.length} clause(s)` +
|
|
83
|
+
(flagged.length > 0 ? `; flagged-ambiguous: ${flagged.length}` : ""),
|
|
84
|
+
];
|
|
85
|
+
for (const clause of flagged) {
|
|
86
|
+
const chosen = clause.chosen_reading ?? 0;
|
|
87
|
+
const chosenPath = clause.readings?.[chosen]?.artifact_path ?? clause.artifact_path ?? "(none)";
|
|
88
|
+
lines.push(` clause ${clause.id}: two readings; chosen_reading=${chosen} (${chosenPath}) [headless #3323]`);
|
|
89
|
+
for (const [index, reading] of (clause.readings ?? []).entries()) {
|
|
90
|
+
lines.push(` reading ${index}: ${reading.artifact_path ?? "(none)"}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Stamp derived clauses onto plan.acceptance in place. Headless: records
|
|
97
|
+
* chosen_reading, never blocks on a question.
|
|
98
|
+
*/
|
|
99
|
+
export function applyClauseDerivationToPlan(plan, options = {}) {
|
|
100
|
+
if (!needsClauseDerivation(plan.acceptance)) {
|
|
101
|
+
return {
|
|
102
|
+
applied: false,
|
|
103
|
+
clauses: readAcceptanceClauses(plan.acceptance),
|
|
104
|
+
notice: "",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const clauses = deriveAcceptanceClauses(collectTaskStatementFromPlan(plan));
|
|
108
|
+
if (clauses.length === 0) {
|
|
109
|
+
return { applied: false, clauses: [], notice: "" };
|
|
110
|
+
}
|
|
111
|
+
const existing = asRecord(plan.acceptance);
|
|
112
|
+
const commands = existing !== null && Array.isArray(existing.commands) ? existing.commands : [];
|
|
113
|
+
const hasCommands = commands.length > 0;
|
|
114
|
+
const noneStated = hasCommands ? existing?.none_stated === true : true;
|
|
115
|
+
const sourceRung = hasCommands && existing?.none_stated !== true
|
|
116
|
+
? isNonEmptyString(existing?.source_rung)
|
|
117
|
+
? existing.source_rung
|
|
118
|
+
: "stated"
|
|
119
|
+
: "derived";
|
|
120
|
+
plan.acceptance = {
|
|
121
|
+
...(existing ?? {}),
|
|
122
|
+
commands: hasCommands ? commands : [],
|
|
123
|
+
none_stated: noneStated,
|
|
124
|
+
source_rung: sourceRung,
|
|
125
|
+
derived_reason: `derived ${clauses.length} independently testable clauses from the task statement before product edit (#3323)`,
|
|
126
|
+
clauses: serializeAcceptanceClauses(clauses),
|
|
127
|
+
};
|
|
128
|
+
if (options.emitStamp !== false && options.projectRoot !== undefined) {
|
|
129
|
+
emitAcceptanceStampFromPlan(options.projectRoot, plan);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
applied: true,
|
|
133
|
+
clauses,
|
|
134
|
+
notice: formatAmbiguousClauseNotice(clauses),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** Stable fingerprint of plan.acceptance for first-write / material-change detection (#3355). */
|
|
138
|
+
export function acceptanceFingerprint(acceptance) {
|
|
139
|
+
const rec = asRecord(acceptance);
|
|
140
|
+
if (rec === null) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const commands = Array.isArray(rec.commands) ? rec.commands.length : 0;
|
|
144
|
+
const clauses = Array.isArray(rec.clauses)
|
|
145
|
+
? rec.clauses.map((entry, index) => {
|
|
146
|
+
const row = asRecord(entry);
|
|
147
|
+
if (row === null) {
|
|
148
|
+
return `${index}`;
|
|
149
|
+
}
|
|
150
|
+
return `${row.id ?? index}:${row.text ?? ""}:${row.artifact_path ?? ""}`;
|
|
151
|
+
})
|
|
152
|
+
: [];
|
|
153
|
+
const rung = typeof rec.source_rung === "string" ? rec.source_rung : "";
|
|
154
|
+
return `${rung}|${rec.none_stated === true}|${commands}|${clauses.join(";")}`;
|
|
155
|
+
}
|
|
156
|
+
/** True when next is a first write or a material change versus previous (#3355). */
|
|
157
|
+
export function isMaterialAcceptanceChange(previous, next) {
|
|
158
|
+
const nextFp = acceptanceFingerprint(next);
|
|
159
|
+
if (nextFp === null) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
return acceptanceFingerprint(previous) !== nextFp;
|
|
163
|
+
}
|
|
164
|
+
/** Fail-open acceptance_stamp emission (same contract as issue:ingest / #3355). */
|
|
165
|
+
export function emitAcceptanceStampFromPlan(projectRoot, plan, env = process.env) {
|
|
166
|
+
const rec = asRecord(plan);
|
|
167
|
+
if (rec === null) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const acceptance = asRecord(rec.acceptance);
|
|
171
|
+
if (acceptance === null) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const dest = env[ENV_RUN_SUMMARY_PATH];
|
|
175
|
+
if (dest === undefined || dest.trim().length === 0) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const sessionId = typeof env.DEFT_SESSION_ID === "string" && env.DEFT_SESSION_ID.trim().length > 0
|
|
180
|
+
? env.DEFT_SESSION_ID.trim()
|
|
181
|
+
: "clause-derivation";
|
|
182
|
+
const emitter = new RunSummaryEmitter({ projectRoot, sessionId, env });
|
|
183
|
+
const commands = Array.isArray(acceptance.commands) ? acceptance.commands.length : 0;
|
|
184
|
+
const clauses = Array.isArray(acceptance.clauses) ? acceptance.clauses.length : 0;
|
|
185
|
+
emitter.emitAcceptanceStamp({
|
|
186
|
+
rung: typeof acceptance.source_rung === "string" ? acceptance.source_rung : "project_floor",
|
|
187
|
+
none_stated: acceptance.none_stated === true,
|
|
188
|
+
command_count: commands,
|
|
189
|
+
clause_count: clauses,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// fail-open
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Emit acceptance_stamp when plan.acceptance is first written or materially changed.
|
|
198
|
+
* State-observed: callers pass the before/after blocks from any lifecycle verb (#3355).
|
|
199
|
+
*/
|
|
200
|
+
export function maybeEmitAcceptanceStampFromChange(projectRoot, previous, next, env = process.env) {
|
|
201
|
+
if (!isMaterialAcceptanceChange(previous, next)) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
emitAcceptanceStampFromPlan(projectRoot, { acceptance: next }, env);
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=clause-derivation.js.map
|
package/dist/intake/index.d.ts
CHANGED
package/dist/intake/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/project
|
|
|
6
6
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
7
7
|
import { captureAndAttachLiteralAcceptance } from "../literal-acceptance/index.js";
|
|
8
8
|
import { stampAcceptanceFromLiteralCapture } from "../product-first-done-gate/index.js";
|
|
9
|
-
import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter } from "../run-summary/index.js";
|
|
10
9
|
import { call } from "../scm/call.js";
|
|
11
10
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
12
11
|
import { resolveProjectRepo } from "../slice/project-context.js";
|
|
@@ -15,6 +14,7 @@ import { slugify, TODAY } from "../vbrief-build/build.js";
|
|
|
15
14
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
16
15
|
import { stampDerivedClausesOnAcceptance } from "../verify-ac/clauses.js";
|
|
17
16
|
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";
|
|
17
|
+
import { emitAcceptanceStampFromPlan } from "./clause-derivation.js";
|
|
18
18
|
import { findAcHeading, parseCheckboxItems, parseListItems, sliceAcSection, stripCodeBlocks, stripFencedCodeBlocks, } from "./markdown-scanners.js";
|
|
19
19
|
import { detectRepo, extractReferencesFromVbrief, fetchOpenIssues, GITHUB_ISSUE_REF_TYPES, LIFECYCLE_FOLDERS, parseIssueNumber, } from "./reconcile-issues.js";
|
|
20
20
|
/** Reference type pointing at the canonical current-shape comment permalink (#1870). */
|
|
@@ -723,45 +723,9 @@ export function ingestOne(issue, options) {
|
|
|
723
723
|
assertWriteTargetSafe(projectRoot, target);
|
|
724
724
|
mkdirSync(folderPath, { recursive: true });
|
|
725
725
|
writeFileSync(target, `${JSON.stringify(vbrief, null, 2)}\n`, "utf8");
|
|
726
|
-
|
|
726
|
+
emitAcceptanceStampFromPlan(projectRoot, vbrief.plan);
|
|
727
727
|
return ["created", target, `CREATED ${folder}/${filename}`];
|
|
728
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
|
-
}
|
|
765
729
|
export function ingestBulk(issues, options) {
|
|
766
730
|
let filtered = issues;
|
|
767
731
|
if (options.label !== undefined && options.label !== null) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ParentLineageResult } from "../scope/parent-lineage.js";
|
|
2
|
+
import { type FetchOriginUpdatedAt } from "../vbrief-reconcile/origin-freshness.js";
|
|
2
3
|
/** Canonical eligibility folder — only vbrief/active/ may spawn implementation. */
|
|
3
4
|
export declare const ACTIVE_FOLDER = "active";
|
|
4
5
|
/** Canonical eligibility status — only `running` signals an active handoff. */
|
|
@@ -20,6 +21,10 @@ export interface EvaluateOptions {
|
|
|
20
21
|
readonly projectRoot?: string;
|
|
21
22
|
/** Skip parent-lineage check (tests / opt-out). Default false. */
|
|
22
23
|
readonly skipParentLineage?: boolean;
|
|
24
|
+
/** Skip origin timestamp freshness (#3363). Default false. */
|
|
25
|
+
readonly skipOriginFreshness?: boolean;
|
|
26
|
+
/** Injected origin fetch for tests. Default: live `gh api` REST. */
|
|
27
|
+
readonly fetchOriginUpdatedAt?: FetchOriginUpdatedAt;
|
|
23
28
|
}
|
|
24
29
|
/** Substitute `{path}` without `$`-pattern expansion in user paths (#1721). */
|
|
25
30
|
export declare function formatActivateHint(path: string): string;
|
|
@@ -2,6 +2,7 @@ import { readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { basename, dirname } from "node:path";
|
|
3
3
|
import { evaluateIntentCeilingFromEnv } from "../policy/intent-ceiling.js";
|
|
4
4
|
import { evaluateParentLineage, formatParentLineageLine, } from "../scope/parent-lineage.js";
|
|
5
|
+
import { evaluateOriginFreshness, } from "../vbrief-reconcile/origin-freshness.js";
|
|
5
6
|
/** Canonical eligibility folder — only vbrief/active/ may spawn implementation. */
|
|
6
7
|
export const ACTIVE_FOLDER = "active";
|
|
7
8
|
/** Canonical eligibility status — only `running` signals an active handoff. */
|
|
@@ -154,6 +155,19 @@ export function evaluate(vbriefPath, options = {}) {
|
|
|
154
155
|
message: buildReject(path, `${lineage.message}${defect}\n ${formatParentLineageLine(lineage)}`),
|
|
155
156
|
};
|
|
156
157
|
}
|
|
158
|
+
// #3363: fail closed when the live GitHub origin is newer than the brief.
|
|
159
|
+
const originFreshness = evaluateOriginFreshness(record, {
|
|
160
|
+
skip: options.skipOriginFreshness === true,
|
|
161
|
+
fetchOriginUpdatedAt: options.fetchOriginUpdatedAt,
|
|
162
|
+
cwd: options.projectRoot,
|
|
163
|
+
});
|
|
164
|
+
if (!originFreshness.ok) {
|
|
165
|
+
return {
|
|
166
|
+
exitCode: 1,
|
|
167
|
+
parentLineage: lineage,
|
|
168
|
+
message: buildReject(path, originFreshness.message),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
157
171
|
// Keep the historical OK line when lineage is N/A (backward-compatible tests / agents).
|
|
158
172
|
const message = lineage.applicable
|
|
159
173
|
? `OK ${path} -- ready for implementation. parent lineage OK ` +
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { EvaluateResult } from "./evaluate.js";
|
|
1
|
+
export type { EvaluateOptions, EvaluateResult } from "./evaluate.js";
|
|
2
2
|
export { ACTIVATE_HINT, ACTIVE_FOLDER, ELIGIBLE_LIFECYCLE_DIRS, ELIGIBLE_STATUS, emitJson, evaluate, formatActivateHint, PREFLIGHT_USAGE_HINT, } from "./evaluate.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import type { PlanAcceptance } from "./types.js";
|
|
8
8
|
export declare const EMPTY_AC_OUTCOME: "soft_empty";
|
|
9
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 (
|
|
10
|
+
export declare const EMPTY_AC_REMEDY = "stamp plan.acceptance via clause derivation (scope:activate / scope:promote runs #3323, or task issue:ingest)";
|
|
11
11
|
export type VerifyAcResolution = "verified-pass" | "empty-pass" | "soft_empty" | "fail" | "config" | "skipped";
|
|
12
12
|
/** True when this project check composition includes a suite gate (#3188). */
|
|
13
13
|
export declare function projectHasSuiteFloor(projectRoot: string): boolean;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { isFrameworkRepoRoot } from "../check/context.js";
|
|
8
8
|
export const EMPTY_AC_OUTCOME = "soft_empty";
|
|
9
9
|
export const EMPTY_AC_CAUSE = "no acceptance stamped — floor is empty in this project";
|
|
10
|
-
export const EMPTY_AC_REMEDY = "stamp plan.acceptance (
|
|
10
|
+
export const EMPTY_AC_REMEDY = "stamp plan.acceptance via clause derivation (scope:activate / scope:promote runs #3323, or task issue:ingest)";
|
|
11
11
|
/** True when this project check composition includes a suite gate (#3188). */
|
|
12
12
|
export function projectHasSuiteFloor(projectRoot) {
|
|
13
13
|
return isFrameworkRepoRoot(projectRoot);
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* floor, empty resolution is soft_empty — not a green run (#3334).
|
|
9
9
|
*/
|
|
10
10
|
import { type EvaluateLiteralAcceptanceOptions, type LiteralAcceptanceGateResult } from "../literal-acceptance/index.js";
|
|
11
|
+
import { type AcceptanceRunSummaryOutcome } from "../run-summary/index.js";
|
|
11
12
|
import { type ClauseWalkResult } from "../verify-ac/clauses.js";
|
|
12
13
|
import { type VerifyAcResolution } from "./empty-resolution.js";
|
|
13
14
|
import type { AcSourceRung, PlanAcceptance } from "./types.js";
|
|
@@ -62,6 +63,11 @@ export interface EvaluateVerifyAcOptions extends EvaluateLiteralAcceptanceOption
|
|
|
62
63
|
* emit after the #3285 bank checkpoint.
|
|
63
64
|
*/
|
|
64
65
|
readonly skipAcceptanceEmit?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Raw plan.acceptance as observed on the brief (#3355). Distinct from the
|
|
68
|
+
* synthesized floor: stamp only when the plan actually carries a block.
|
|
69
|
+
*/
|
|
70
|
+
readonly observedAcceptance?: unknown;
|
|
65
71
|
/**
|
|
66
72
|
* Active scope key for product-oracle check_id namespacing (#3337).
|
|
67
73
|
* Prefer plan.id; path stem when id is missing. Multi-active verify:ac
|
|
@@ -73,6 +79,17 @@ export interface EvaluateVerifyAcOptions extends EvaluateLiteralAcceptanceOption
|
|
|
73
79
|
* Evaluate product AC from an in-memory plan.
|
|
74
80
|
*/
|
|
75
81
|
export declare function evaluateVerifyAcFromPlan(plan: Record<string, unknown>, options?: EvaluateVerifyAcOptions): VerifyAcResult;
|
|
82
|
+
/**
|
|
83
|
+
* CLI-only early returns that never reach evaluate still emit an acceptance
|
|
84
|
+
* outcome so field streams see config-error / soft-missing (#3355).
|
|
85
|
+
*/
|
|
86
|
+
export declare function emitVerifyAcTerminalOutcome(input: {
|
|
87
|
+
readonly projectRoot: string;
|
|
88
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
89
|
+
readonly outcome: AcceptanceRunSummaryOutcome;
|
|
90
|
+
readonly sourceRung?: AcSourceRung;
|
|
91
|
+
readonly noneStated?: boolean;
|
|
92
|
+
}): void;
|
|
76
93
|
/**
|
|
77
94
|
* Evaluate from xBRIEF path.
|
|
78
95
|
*/
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { existsSync, readFileSync } from "node:fs";
|
|
11
11
|
import { basename, isAbsolute, relative, resolve } from "node:path";
|
|
12
|
+
import { emitAcceptanceStampFromPlan } from "../intake/clause-derivation.js";
|
|
12
13
|
import { evaluateLiteralAcceptanceFromPlan, runLiteralAcceptanceCommands, } from "../literal-acceptance/index.js";
|
|
13
14
|
import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter, } from "../run-summary/index.js";
|
|
14
15
|
import { maybeBankOnAcPass } from "../session/ac-pass-banking.js";
|
|
@@ -30,6 +31,7 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
|
|
|
30
31
|
const optionsWithScope = {
|
|
31
32
|
...options,
|
|
32
33
|
oracleScopeKey: options.oracleScopeKey?.trim() || planId || null,
|
|
34
|
+
observedAcceptance: options.observedAcceptance !== undefined ? options.observedAcceptance : plan.acceptance,
|
|
33
35
|
};
|
|
34
36
|
const acceptance = readPlanAcceptance(plan);
|
|
35
37
|
const schemaErrors = validatePlanAcceptance(plan.acceptance ?? acceptance);
|
|
@@ -181,9 +183,11 @@ function acceptanceOutcomeOf(result) {
|
|
|
181
183
|
return "soft_empty";
|
|
182
184
|
if (result.resolution === "fail")
|
|
183
185
|
return "fail";
|
|
184
|
-
|
|
186
|
+
if (result.resolution === "config")
|
|
187
|
+
return "config-error";
|
|
188
|
+
return "soft-missing";
|
|
185
189
|
}
|
|
186
|
-
function
|
|
190
|
+
function emitAcceptanceObservedStamp(options, projectRoot) {
|
|
187
191
|
if (options.env === undefined) {
|
|
188
192
|
return;
|
|
189
193
|
}
|
|
@@ -191,8 +195,14 @@ function emitAcceptanceOutcome(result, options, projectRoot) {
|
|
|
191
195
|
if (dest === undefined || dest.trim().length === 0) {
|
|
192
196
|
return;
|
|
193
197
|
}
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
emitAcceptanceStampFromPlan(projectRoot, { acceptance: options.observedAcceptance }, options.env);
|
|
199
|
+
}
|
|
200
|
+
function emitAcceptanceOutcome(result, options, projectRoot) {
|
|
201
|
+
if (options.env === undefined) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const dest = options.env[ENV_RUN_SUMMARY_PATH];
|
|
205
|
+
if (dest === undefined || dest.trim().length === 0) {
|
|
196
206
|
return;
|
|
197
207
|
}
|
|
198
208
|
try {
|
|
@@ -204,7 +214,7 @@ function emitAcceptanceOutcome(result, options, projectRoot) {
|
|
|
204
214
|
});
|
|
205
215
|
emitter.emitAcceptance({
|
|
206
216
|
resolved_command_count: result.resolvedCommandCount,
|
|
207
|
-
outcome,
|
|
217
|
+
outcome: acceptanceOutcomeOf(result),
|
|
208
218
|
source_rung: result.sourceRung,
|
|
209
219
|
none_stated: result.noneStated,
|
|
210
220
|
clause_count: result.clauseOutcomes?.length,
|
|
@@ -218,6 +228,43 @@ function emitAcceptanceOutcome(result, options, projectRoot) {
|
|
|
218
228
|
// fail-open
|
|
219
229
|
}
|
|
220
230
|
}
|
|
231
|
+
/** Outcome on every terminal path; stamp from observed plan.acceptance (#3355). */
|
|
232
|
+
function emitAcceptanceTelemetry(result, options, projectRoot) {
|
|
233
|
+
emitAcceptanceObservedStamp(options, projectRoot);
|
|
234
|
+
emitAcceptanceOutcome(result, options, projectRoot);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* CLI-only early returns that never reach evaluate still emit an acceptance
|
|
238
|
+
* outcome so field streams see config-error / soft-missing (#3355).
|
|
239
|
+
*/
|
|
240
|
+
export function emitVerifyAcTerminalOutcome(input) {
|
|
241
|
+
emitAcceptanceOutcome({
|
|
242
|
+
ok: input.outcome !== "config-error" && input.outcome !== "fail",
|
|
243
|
+
code: input.outcome === "config-error" ? 2 : input.outcome === "fail" ? 1 : 0,
|
|
244
|
+
message: "",
|
|
245
|
+
commands: [],
|
|
246
|
+
runs: [],
|
|
247
|
+
sourceRung: input.sourceRung ?? "project_floor",
|
|
248
|
+
noneStated: input.noneStated ?? true,
|
|
249
|
+
acceptance: {
|
|
250
|
+
commands: [],
|
|
251
|
+
none_stated: input.noneStated ?? true,
|
|
252
|
+
source_rung: input.sourceRung ?? "project_floor",
|
|
253
|
+
},
|
|
254
|
+
resolution: input.outcome === "config-error"
|
|
255
|
+
? "config"
|
|
256
|
+
: input.outcome === "soft-missing"
|
|
257
|
+
? "skipped"
|
|
258
|
+
: input.outcome === "fail"
|
|
259
|
+
? "fail"
|
|
260
|
+
: input.outcome === "soft_empty"
|
|
261
|
+
? "soft_empty"
|
|
262
|
+
: input.outcome === "verified-pass"
|
|
263
|
+
? "verified-pass"
|
|
264
|
+
: "empty-pass",
|
|
265
|
+
resolvedCommandCount: 0,
|
|
266
|
+
}, { projectRoot: input.projectRoot, env: input.env }, resolve(input.projectRoot));
|
|
267
|
+
}
|
|
221
268
|
function applyClauseWalk(result, options) {
|
|
222
269
|
const clauses = result.acceptance.clauses ?? [];
|
|
223
270
|
if (clauses.length === 0 || result.resolution === "config" || result.resolution === "skipped") {
|
|
@@ -268,7 +315,7 @@ function applyOracle(result, options) {
|
|
|
268
315
|
}
|
|
269
316
|
}
|
|
270
317
|
if (options.skipAcceptanceEmit !== true) {
|
|
271
|
-
|
|
318
|
+
emitAcceptanceTelemetry(next, options, projectRoot);
|
|
272
319
|
}
|
|
273
320
|
return next;
|
|
274
321
|
}
|
|
@@ -338,13 +385,17 @@ export function evaluateVerifyAcFromPath(xbriefPath, options = {}) {
|
|
|
338
385
|
const projectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
339
386
|
// Path-relative keys stay unique across xbrief/ vs vbrief/ and duplicate plan.id (#3337 Greptile).
|
|
340
387
|
const oracleScopeKey = options.oracleScopeKey?.trim() || resolveOracleScopeKey(plan, abs, projectRoot);
|
|
341
|
-
const
|
|
388
|
+
const emitOptions = {
|
|
342
389
|
...options,
|
|
343
|
-
skipAcceptanceEmit: true,
|
|
344
390
|
oracleScopeKey,
|
|
391
|
+
observedAcceptance: plan.acceptance,
|
|
392
|
+
};
|
|
393
|
+
const result = evaluateVerifyAcFromPlan(plan, {
|
|
394
|
+
...emitOptions,
|
|
395
|
+
skipAcceptanceEmit: true,
|
|
345
396
|
});
|
|
346
|
-
const banked = maybeAttachAcPassBank(result, plan, abs,
|
|
347
|
-
|
|
397
|
+
const banked = maybeAttachAcPassBank(result, plan, abs, emitOptions);
|
|
398
|
+
emitAcceptanceTelemetry(banked, emitOptions, projectRoot);
|
|
348
399
|
return banked;
|
|
349
400
|
}
|
|
350
401
|
/**
|
|
@@ -7,6 +7,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
9
|
export { EMPTY_AC_CAUSE, EMPTY_AC_OUTCOME, EMPTY_AC_REMEDY, formatSoftEmptyMessage, isEmptyAcResolution, isSoftEmptyAcText, projectHasSuiteFloor, type VerifyAcResolution, } from "./empty-resolution.js";
|
|
10
|
-
export { type EvaluateVerifyAcOptions, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, type VerifyAcResult, } from "./evaluate.js";
|
|
10
|
+
export { type EvaluateVerifyAcOptions, emitVerifyAcTerminalOutcome, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, type VerifyAcResult, } from "./evaluate.js";
|
|
11
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";
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
export { attachPlanAcceptance, buildAcceptanceFromIntakeCapture, readPlanAcceptance, stampAcceptanceFromLiteralCapture, validatePlanAcceptance, } from "./acceptance.js";
|
|
8
8
|
export { applyProductFirstGateMode, isHygieneGate, isProductAcGate, resolveProductFirstCheckMode, } from "./check-mode.js";
|
|
9
9
|
export { EMPTY_AC_CAUSE, EMPTY_AC_OUTCOME, EMPTY_AC_REMEDY, formatSoftEmptyMessage, isEmptyAcResolution, isSoftEmptyAcText, projectHasSuiteFloor, } from "./empty-resolution.js";
|
|
10
|
-
export { evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, } from "./evaluate.js";
|
|
10
|
+
export { emitVerifyAcTerminalOutcome, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, } from "./evaluate.js";
|
|
11
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";
|
|
12
12
|
//# sourceMappingURL=index.js.map
|
|
@@ -25,8 +25,27 @@ export interface EmitRunSummaryResult {
|
|
|
25
25
|
readonly line: RunSummaryLine | null;
|
|
26
26
|
readonly warning: boolean;
|
|
27
27
|
}
|
|
28
|
+
/** Owner token stored in `.seq.lock` so reclaim is not age-based (#3361). */
|
|
29
|
+
export interface SeqLockOwner {
|
|
30
|
+
readonly pid: number;
|
|
31
|
+
readonly nonce: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Reclaim `.seq.lock` only when the owner process is dead or the token is
|
|
35
|
+
* missing/unreadable/corrupt (unknown owner). Live holders are never stolen
|
|
36
|
+
* by mtime (#3361).
|
|
37
|
+
*/
|
|
38
|
+
export declare function tryReclaimSeqLock(lockPath: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Unlink `.seq.lock` only when the on-disk token still matches this holder
|
|
41
|
+
* (ownership-blind release must not delete a successor's lock).
|
|
42
|
+
*/
|
|
43
|
+
export declare function releaseSeqLockIfOwner(lockPath: string, owner: SeqLockOwner): boolean;
|
|
28
44
|
/**
|
|
29
|
-
* Stateful emitter: one instance
|
|
45
|
+
* Stateful emitter: one instance tracks seq and write-warning once.
|
|
46
|
+
* File destinations seed seq from the current JSONL line count so multiple
|
|
47
|
+
* CLI processes appending to one DEFT_RUN_SUMMARY_PATH share 1..N (#3350).
|
|
48
|
+
* Stdout destinations stay per-process (each constructor starts at 0).
|
|
30
49
|
*/
|
|
31
50
|
export declare class RunSummaryEmitter {
|
|
32
51
|
private seq;
|
|
@@ -41,6 +60,7 @@ export declare class RunSummaryEmitter {
|
|
|
41
60
|
private readonly writeStderr;
|
|
42
61
|
constructor(options: RunSummaryEmitterOptions);
|
|
43
62
|
getDestination(): RunSummaryDestination;
|
|
63
|
+
private buildLine;
|
|
44
64
|
emit(event: RunSummaryEventKind, payload: RunSummaryPayload): EmitRunSummaryResult;
|
|
45
65
|
emitSessionStart(payload: SessionStartRunSummaryPayload): EmitRunSummaryResult;
|
|
46
66
|
emitDialTransition(payload: DialTransitionRunSummaryPayload): EmitRunSummaryResult;
|
|
@@ -52,12 +72,32 @@ export declare class RunSummaryEmitter {
|
|
|
52
72
|
emitAcceptanceStamp(payload: AcceptanceStampRunSummaryPayload): EmitRunSummaryResult;
|
|
53
73
|
/** Emit the harness-supplied denominator when DEFT_TOTAL_TOOL_TURNS is set. */
|
|
54
74
|
emitKnownToolTurnDenominator(): EmitRunSummaryResult;
|
|
75
|
+
/**
|
|
76
|
+
* Always emit a session denominator when the destination is live (#3356).
|
|
77
|
+
* `emitKnownToolTurnDenominator` stays silent unless DEFT_TOTAL_TOOL_TURNS is
|
|
78
|
+
* set; this caller records host planned turns or the session:start CLI floor.
|
|
79
|
+
*/
|
|
80
|
+
emitSessionToolTurnDenominator(hostMaxTurns?: number | null): EmitRunSummaryResult;
|
|
55
81
|
}
|
|
56
82
|
/** Parse harness-supplied DEFT_TOTAL_TOOL_TURNS (integer > 0). Invalid/unset → omit. */
|
|
57
83
|
export declare function readEnvToolTurnDenominator(env: NodeJS.ProcessEnv): number | undefined;
|
|
84
|
+
/** Canonical host planned-turn budget recorded at session:start (#3356). */
|
|
85
|
+
export declare const ENV_MAX_TURNS_DENOMINATOR = "DEFT_MAX_TURNS";
|
|
86
|
+
/** session:start is one CLI invocation when no host/harness count is known (#3356). */
|
|
87
|
+
export declare const SESSION_START_CLI_INVOCATION_DENOMINATOR: 1;
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the session tool/turn denominator (#3356).
|
|
90
|
+
*
|
|
91
|
+
* Prefer harness actuals (`DEFT_TOTAL_TOOL_TURNS`), then a host planned-turn
|
|
92
|
+
* budget (`DEFT_MAX_TURNS` or `hostMaxTurns` from the session:start descriptor),
|
|
93
|
+
* else this CLI invocation (1). An emitted proxy beats a perfect unused kind.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolveSessionToolTurnDenominator(env: NodeJS.ProcessEnv, hostMaxTurns?: number | null): number;
|
|
58
96
|
/**
|
|
59
97
|
* One-shot helper for call sites that do not hold an emitter (e.g. dial escalate).
|
|
60
|
-
* Still fail-open
|
|
98
|
+
* Still fail-open. File destinations seed seq from the existing JSONL line
|
|
99
|
+
* count so successive one-shot calls into the same path continue 1..N (#3350).
|
|
100
|
+
* Stdout destinations stay per-process (each call starts at seq=1).
|
|
61
101
|
*/
|
|
62
102
|
export declare function emitRunSummaryEvent(options: RunSummaryEmitterOptions & {
|
|
63
103
|
readonly event: RunSummaryEventKind;
|