@deftai/directive-core 0.103.0 → 0.105.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 +682 -35
- package/dist/check/cached-orchestrator.d.ts +1 -1
- package/dist/check/cached-orchestrator.js +2 -2
- 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/named-cause.d.ts +1 -1
- package/dist/check/named-cause.js +33 -3
- package/dist/check/session-completed-ac.d.ts +39 -0
- package/dist/check/session-completed-ac.js +198 -0
- package/dist/codebase/default-extractor.d.ts +5 -0
- package/dist/codebase/default-extractor.js +7 -15
- package/dist/deposit/copy-tree.js +60 -0
- package/dist/deposit/python-free.js +26 -18
- package/dist/doctor/checks.d.ts +8 -0
- package/dist/doctor/checks.js +37 -25
- package/dist/doctor/doctor-state.d.ts +4 -0
- package/dist/doctor/doctor-state.js +7 -1
- package/dist/doctor/main.js +9 -14
- package/dist/doctor/types.d.ts +9 -0
- package/dist/fs/contained-write.d.ts +115 -1
- package/dist/fs/contained-write.js +242 -5
- package/dist/fs/mutation-ledger.d.ts +54 -0
- package/dist/fs/mutation-ledger.js +133 -0
- package/dist/fs/non-product-dirs.d.ts +79 -0
- package/dist/fs/non-product-dirs.js +96 -0
- package/dist/hooks/dispatcher.d.ts +3 -1
- package/dist/hooks/dispatcher.js +14 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/agent-hooks.js +17 -15
- package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
- package/dist/init-deposit/core-guard-pin-content.js +393 -0
- package/dist/init-deposit/gitignore.js +9 -0
- package/dist/init-deposit/hygiene.d.ts +47 -11
- package/dist/init-deposit/hygiene.js +172 -37
- package/dist/init-deposit/legacy-detect.js +6 -2
- package/dist/init-deposit/refresh.d.ts +21 -1
- package/dist/init-deposit/refresh.js +106 -9
- package/dist/init-deposit/scaffold.js +28 -279
- package/dist/init-deposit/slash-deposit.js +13 -7
- package/dist/init-deposit/xbrief-projections.d.ts +8 -1
- package/dist/init-deposit/xbrief-projections.js +24 -11
- package/dist/intake/clause-derivation.d.ts +74 -0
- package/dist/intake/clause-derivation.js +409 -0
- package/dist/intake/index.d.ts +1 -0
- package/dist/intake/index.js +1 -0
- package/dist/intake/issue-ingest.d.ts +3 -1
- package/dist/intake/issue-ingest.js +36 -43
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
- package/dist/literal-acceptance/capture.d.ts +10 -0
- package/dist/literal-acceptance/capture.js +224 -75
- package/dist/literal-acceptance/evaluate.d.ts +29 -4
- package/dist/literal-acceptance/evaluate.js +70 -2
- package/dist/literal-acceptance/index.d.ts +3 -3
- package/dist/literal-acceptance/index.js +3 -3
- package/dist/literal-acceptance/safety.d.ts +41 -0
- package/dist/literal-acceptance/safety.js +126 -2
- package/dist/literal-acceptance/types.d.ts +8 -0
- package/dist/orphan-active/evaluate.d.ts +7 -1
- package/dist/orphan-active/evaluate.js +102 -23
- package/dist/platform/platform-capabilities.js +2 -3
- package/dist/policy/base-branch.d.ts +37 -0
- package/dist/policy/base-branch.js +114 -0
- package/dist/policy/branch-sync.d.ts +80 -0
- package/dist/policy/branch-sync.js +211 -0
- package/dist/policy/ceremony-dial-escalation.js +1 -0
- package/dist/policy/ceremony-dial.js +4 -14
- package/dist/policy/delivery-branch.d.ts +5 -3
- package/dist/policy/delivery-branch.js +7 -3
- package/dist/policy/file-size-thresholds.d.ts +24 -0
- package/dist/policy/file-size-thresholds.js +23 -0
- package/dist/policy/forge-outage-retry.d.ts +47 -0
- package/dist/policy/forge-outage-retry.js +157 -0
- package/dist/policy/index.d.ts +7 -0
- package/dist/policy/index.js +66 -7
- package/dist/policy/project-invariants.d.ts +50 -0
- package/dist/policy/project-invariants.js +205 -0
- package/dist/policy/sync-default.d.ts +129 -0
- package/dist/policy/sync-default.js +452 -0
- package/dist/policy/sync-max-files.d.ts +93 -0
- package/dist/policy/sync-max-files.js +192 -0
- package/dist/pr-monitor/absent-required.d.ts +11 -0
- package/dist/pr-monitor/absent-required.js +61 -0
- package/dist/pr-monitor/constants.d.ts +7 -0
- package/dist/pr-monitor/constants.js +7 -0
- package/dist/pr-monitor/index.d.ts +2 -1
- package/dist/pr-monitor/index.js +2 -1
- package/dist/pr-monitor/main.js +4 -0
- package/dist/pr-monitor/monitor.js +24 -1
- package/dist/pr-wait-mergeable/classify.js +6 -0
- package/dist/preflight/evaluate.d.ts +9 -0
- package/dist/preflight/evaluate.js +49 -1
- package/dist/preflight/index.d.ts +5 -1
- package/dist/preflight/index.js +2 -0
- package/dist/preflight/intended-placement.d.ts +62 -0
- package/dist/preflight/intended-placement.js +275 -0
- package/dist/preflight/project-invariants-gate.d.ts +29 -0
- package/dist/preflight/project-invariants-gate.js +82 -0
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
- package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
- package/dist/product-first-done-gate/acceptance.d.ts +7 -4
- package/dist/product-first-done-gate/acceptance.js +79 -8
- package/dist/product-first-done-gate/empty-resolution.d.ts +5 -2
- package/dist/product-first-done-gate/empty-resolution.js +13 -2
- package/dist/product-first-done-gate/evaluate.d.ts +29 -0
- package/dist/product-first-done-gate/evaluate.js +307 -33
- package/dist/product-first-done-gate/index.d.ts +3 -1
- package/dist/product-first-done-gate/index.js +2 -1
- package/dist/release/build-dist.d.ts +101 -0
- package/dist/release/build-dist.js +428 -46
- package/dist/run-summary/emit.d.ts +50 -4
- package/dist/run-summary/emit.js +262 -26
- package/dist/run-summary/index.d.ts +3 -2
- package/dist/run-summary/index.js +2 -1
- package/dist/run-summary/session-id.d.ts +21 -0
- package/dist/run-summary/session-id.js +92 -0
- package/dist/run-summary/share.d.ts +1 -1
- package/dist/run-summary/share.js +2 -2
- package/dist/run-summary/types.d.ts +38 -6
- package/dist/run-summary/types.js +1 -0
- 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 +27 -0
- package/dist/scope/acceptance-evidence.js +66 -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 +9 -3
- package/dist/scope/delivery-evidence.js +39 -27
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/parent-lineage.js +7 -6
- package/dist/scope/project-invariant-coverage.d.ts +54 -0
- package/dist/scope/project-invariant-coverage.js +322 -0
- package/dist/scope/transition.js +66 -4
- package/dist/scope-provenance/compare-intent.d.ts +27 -0
- package/dist/scope-provenance/compare-intent.js +233 -0
- package/dist/scope-provenance/digest.d.ts +18 -1
- package/dist/scope-provenance/digest.js +16 -11
- package/dist/scope-provenance/evaluate.d.ts +5 -1
- package/dist/scope-provenance/evaluate.js +64 -4
- package/dist/scope-provenance/extract-intent.d.ts +43 -0
- package/dist/scope-provenance/extract-intent.js +262 -0
- package/dist/scope-provenance/index.d.ts +7 -1
- package/dist/scope-provenance/index.js +7 -1
- package/dist/scope-provenance/intent-digest.d.ts +12 -0
- package/dist/scope-provenance/intent-digest.js +33 -0
- package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
- package/dist/scope-provenance/intent-evaluate.js +149 -0
- package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
- package/dist/scope-provenance/json-tokenizer.js +225 -0
- package/dist/scope-provenance/known-machine.d.ts +38 -0
- package/dist/scope-provenance/known-machine.js +157 -0
- package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
- package/dist/scope-provenance/mint-artifacts.js +425 -0
- package/dist/session/ac-pass-banking.d.ts +4 -36
- package/dist/session/ac-pass-banking.js +30 -77
- package/dist/session/ac-pass-reuse.d.ts +53 -0
- package/dist/session/ac-pass-reuse.js +95 -0
- package/dist/session/ceremony-dial-evidence.d.ts +42 -0
- package/dist/session/ceremony-dial-evidence.js +194 -0
- package/dist/session/git.js +19 -4
- package/dist/session/index.d.ts +5 -0
- package/dist/session/index.js +5 -0
- package/dist/session/occupancy-stress-worker.d.ts +7 -0
- package/dist/session/occupancy-stress-worker.js +36 -0
- package/dist/session/occupancy.d.ts +97 -0
- package/dist/session/occupancy.js +422 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
- package/dist/session/product-state-hash.d.ts +26 -0
- package/dist/session/product-state-hash.js +683 -0
- package/dist/session/session-ready.d.ts +2 -0
- package/dist/session/session-ready.js +52 -30
- package/dist/session/session-start.d.ts +7 -0
- package/dist/session/session-start.js +88 -5
- package/dist/session/verify-ac-session-cache.d.ts +42 -0
- package/dist/session/verify-ac-session-cache.js +126 -0
- package/dist/session/verify-session-ritual.d.ts +5 -0
- package/dist/session/verify-session-ritual.js +9 -0
- package/dist/slash/openclaw-deposit.js +13 -9
- package/dist/slice/constants.d.ts +2 -2
- package/dist/slice/constants.js +2 -2
- package/dist/slice/existing.js +21 -4
- package/dist/slice/lock.d.ts +16 -1
- package/dist/slice/lock.js +137 -11
- package/dist/slice/record.d.ts +8 -5
- package/dist/slice/record.js +21 -13
- package/dist/story-ready/evaluate.d.ts +2 -0
- package/dist/story-ready/evaluate.js +13 -0
- package/dist/swarm/complete-cohort.d.ts +3 -0
- package/dist/swarm/complete-cohort.js +59 -11
- package/dist/swarm/finalize-cohort.js +33 -2
- package/dist/swarm/launch.d.ts +22 -0
- package/dist/swarm/launch.js +155 -0
- package/dist/swarm/routing-set-cli.d.ts +11 -1
- package/dist/swarm/routing-set-cli.js +13 -3
- package/dist/swarm/routing-verify.d.ts +12 -0
- package/dist/swarm/routing-verify.js +59 -20
- package/dist/swarm/routing.d.ts +14 -0
- package/dist/swarm/routing.js +53 -3
- 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 +204 -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 +54 -0
- package/dist/telemetry-coverage/scan-callers.d.ts +25 -0
- package/dist/telemetry-coverage/scan-callers.js +168 -0
- package/dist/triage/actions/index.js +4 -1
- package/dist/ts-check-lane/index.d.ts +3 -0
- package/dist/ts-check-lane/index.js +2 -0
- package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
- package/dist/ts-check-lane/progress-reporter.js +38 -0
- package/dist/ts-check-lane/progress.d.ts +32 -0
- package/dist/ts-check-lane/progress.js +57 -0
- package/dist/ts-check-lane/run-lane.d.ts +3 -1
- package/dist/ts-check-lane/run-lane.js +8 -6
- package/dist/validate-content/validate-links.d.ts +8 -0
- package/dist/validate-content/validate-links.js +9 -15
- package/dist/value/feedback-file.js +12 -2
- package/dist/vbrief-build/index.d.ts +1 -0
- package/dist/vbrief-build/index.js +1 -0
- package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
- package/dist/vbrief-build/project-invariants-io.js +41 -0
- package/dist/vbrief-reconcile/graph.js +18 -1
- package/dist/vbrief-reconcile/index.d.ts +3 -1
- package/dist/vbrief-reconcile/index.js +2 -1
- package/dist/vbrief-reconcile/origin-freshness.d.ts +44 -0
- package/dist/vbrief-reconcile/origin-freshness.js +221 -0
- package/dist/vbrief-reconcile/types.d.ts +13 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
- package/dist/vbrief-reconcile/umbrellas.js +261 -26
- package/dist/vbrief-validate/index.d.ts +1 -1
- package/dist/vbrief-validate/index.js +1 -1
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +68 -0
- package/dist/verify-ac/evaluate.d.ts +7 -1
- package/dist/verify-ac/evaluate.js +34 -26
- package/dist/verify-ac/flag.d.ts +4 -0
- package/dist/verify-ac/flag.js +16 -0
- package/dist/verify-ac/index.d.ts +2 -2
- package/dist/verify-ac/index.js +2 -2
- package/dist/verify-source/contained-writes.js +2 -0
- package/dist/verify-source/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -0
- package/dist/verify-source/verify-stubs.d.ts +15 -1
- package/dist/verify-source/verify-stubs.js +20 -16
- package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
- package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
- package/dist/xbrief-migrate/migrate-project.js +2 -5
- package/package.json +3 -3
package/dist/run-summary/emit.js
CHANGED
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
* most one warning when an *explicit* DEFT_RUN_SUMMARY_PATH write fails.
|
|
6
6
|
* Symlink destinations are refused (no-follow / containment) — fail-open.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { randomBytes } from "node:crypto";
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, unlinkSync } from "node:fs";
|
|
9
10
|
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
10
11
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
11
|
-
import { containedWrite } from "../fs/contained-write.js";
|
|
12
|
+
import { ContainedWriteError, ContainedWriteErrorCode, containedWrite, } from "../fs/contained-write.js";
|
|
12
13
|
import { assertWriteTargetSafe } from "../fs/projection-containment.js";
|
|
13
14
|
import { resolveRunSummaryDestination } from "./path.js";
|
|
15
|
+
import { resolveRunSummarySessionId } from "./session-id.js";
|
|
14
16
|
import { ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
|
|
15
17
|
function sortKeysDeep(value) {
|
|
16
18
|
if (Array.isArray(value)) {
|
|
@@ -80,13 +82,176 @@ function writeRunSummaryLine(projectRoot, targetPath, textLine, mode) {
|
|
|
80
82
|
assertRealpathStillContained(parentAbs, targetAbs);
|
|
81
83
|
}
|
|
82
84
|
/**
|
|
83
|
-
*
|
|
85
|
+
* Count non-empty lines in an existing append-only JSONL. Missing/unreadable
|
|
86
|
+
* files seed at 0 (fail-open). Used so a fresh CLI process continues seq
|
|
87
|
+
* from the destination file instead of resetting to 1 (#3350).
|
|
88
|
+
*/
|
|
89
|
+
function countExistingJsonlLines(path) {
|
|
90
|
+
try {
|
|
91
|
+
if (!existsSync(path)) {
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
const text = readFileSync(path, "utf8");
|
|
95
|
+
if (text.length === 0) {
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
let count = 0;
|
|
99
|
+
for (const line of text.split(/\r?\n/)) {
|
|
100
|
+
if (line.trim().length > 0) {
|
|
101
|
+
count += 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return count;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function seedSeqFromDestination(destination) {
|
|
111
|
+
if (destination.kind !== "file") {
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
return countExistingJsonlLines(destination.path);
|
|
115
|
+
}
|
|
116
|
+
const SEQ_LOCK_WAIT_MS = 2_000;
|
|
117
|
+
const SEQ_LOCK_SPIN_MS = 15;
|
|
118
|
+
function isErrno(err, code) {
|
|
119
|
+
return Boolean(err && typeof err === "object" && "code" in err && err.code === code);
|
|
120
|
+
}
|
|
121
|
+
function isPidAlive(pid) {
|
|
122
|
+
try {
|
|
123
|
+
process.kill(pid, 0);
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
// EPERM: process exists but we cannot signal it — treat as live.
|
|
128
|
+
return isErrno(err, "EPERM");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function parseSeqLockOwner(raw) {
|
|
132
|
+
try {
|
|
133
|
+
const parsed = JSON.parse(raw);
|
|
134
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const pid = parsed.pid;
|
|
138
|
+
const nonce = parsed.nonce;
|
|
139
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
if (typeof nonce !== "string" || nonce.length === 0) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return { pid, nonce };
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function newSeqLockOwner() {
|
|
152
|
+
return { pid: process.pid, nonce: randomBytes(8).toString("hex") };
|
|
153
|
+
}
|
|
154
|
+
function serializeSeqLockOwner(owner) {
|
|
155
|
+
return `${JSON.stringify({ pid: owner.pid, nonce: owner.nonce })}\n`;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Reclaim `.seq.lock` only when the owner process is dead or the token is
|
|
159
|
+
* missing/unreadable/corrupt (unknown owner). Live holders are never stolen
|
|
160
|
+
* by mtime (#3361).
|
|
161
|
+
*/
|
|
162
|
+
export function tryReclaimSeqLock(lockPath) {
|
|
163
|
+
try {
|
|
164
|
+
const owner = parseSeqLockOwner(readFileSync(lockPath, "utf8"));
|
|
165
|
+
if (owner !== null && isPidAlive(owner.pid)) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
unlinkSync(lockPath);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Unlink `.seq.lock` only when the on-disk token still matches this holder
|
|
177
|
+
* (ownership-blind release must not delete a successor's lock).
|
|
178
|
+
*/
|
|
179
|
+
export function releaseSeqLockIfOwner(lockPath, owner) {
|
|
180
|
+
try {
|
|
181
|
+
const current = parseSeqLockOwner(readFileSync(lockPath, "utf8"));
|
|
182
|
+
if (current === null || current.pid !== owner.pid || current.nonce !== owner.nonce) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
unlinkSync(lockPath);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Cross-process exclusive lock so count-then-append is one critical section
|
|
194
|
+
* (#3350 / #3361). Fail-open: if the lock cannot be acquired within the wait
|
|
195
|
+
* window, emit still proceeds with a best-effort recount. Reclaim is allowed
|
|
196
|
+
* only when the owner process is dead or the token is unknown — never by
|
|
197
|
+
* mtime of a live holder.
|
|
198
|
+
*/
|
|
199
|
+
function acquireSeqLock(targetPath) {
|
|
200
|
+
const noop = () => {
|
|
201
|
+
/* fail-open: no lock held */
|
|
202
|
+
};
|
|
203
|
+
try {
|
|
204
|
+
const targetAbs = resolve(targetPath);
|
|
205
|
+
const parent = dirname(targetAbs);
|
|
206
|
+
mkdirSync(parent, { recursive: true });
|
|
207
|
+
const lockPath = `${targetAbs}.seq.lock`;
|
|
208
|
+
const deadline = Date.now() + SEQ_LOCK_WAIT_MS;
|
|
209
|
+
while (true) {
|
|
210
|
+
const owner = newSeqLockOwner();
|
|
211
|
+
try {
|
|
212
|
+
containedWrite({
|
|
213
|
+
root: parent,
|
|
214
|
+
target: lockPath,
|
|
215
|
+
data: serializeSeqLockOwner(owner),
|
|
216
|
+
mode: "create",
|
|
217
|
+
});
|
|
218
|
+
return () => {
|
|
219
|
+
releaseSeqLockIfOwner(lockPath, owner);
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
if (err instanceof ContainedWriteError && err.code === ContainedWriteErrorCode.EXISTS) {
|
|
224
|
+
if (tryReclaimSeqLock(lockPath)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (Date.now() < deadline) {
|
|
228
|
+
const spinEnd = Date.now() + SEQ_LOCK_SPIN_MS;
|
|
229
|
+
while (Date.now() < spinEnd) {
|
|
230
|
+
/* brief spin */
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return noop;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return noop;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Stateful emitter: one instance tracks seq and write-warning once.
|
|
245
|
+
* File destinations seed seq from the current JSONL line count so multiple
|
|
246
|
+
* CLI processes appending to one DEFT_RUN_SUMMARY_PATH share 1..N (#3350).
|
|
247
|
+
* Stdout destinations stay per-process (each constructor starts at 0).
|
|
84
248
|
*/
|
|
85
249
|
export class RunSummaryEmitter {
|
|
86
250
|
seq = 0;
|
|
87
251
|
warned = false;
|
|
88
252
|
projectRoot;
|
|
89
253
|
sessionId;
|
|
254
|
+
component;
|
|
90
255
|
frameworkVersion;
|
|
91
256
|
now;
|
|
92
257
|
destination;
|
|
@@ -95,7 +260,12 @@ export class RunSummaryEmitter {
|
|
|
95
260
|
writeStderr;
|
|
96
261
|
constructor(options) {
|
|
97
262
|
this.projectRoot = options.projectRoot;
|
|
98
|
-
this.sessionId =
|
|
263
|
+
this.sessionId = resolveRunSummarySessionId({
|
|
264
|
+
projectRoot: options.projectRoot,
|
|
265
|
+
explicit: options.sessionId,
|
|
266
|
+
env: options.env,
|
|
267
|
+
});
|
|
268
|
+
this.component = options.component?.trim() || undefined;
|
|
99
269
|
this.frameworkVersion = options.frameworkVersion ?? readCorePackageVersion();
|
|
100
270
|
this.now = options.now ?? (() => new Date());
|
|
101
271
|
this.env = options.env ?? process.env;
|
|
@@ -105,43 +275,52 @@ export class RunSummaryEmitter {
|
|
|
105
275
|
env: this.env,
|
|
106
276
|
gitignoreCovers: options.gitignoreCovers,
|
|
107
277
|
});
|
|
278
|
+
this.seq = seedSeqFromDestination(this.destination);
|
|
108
279
|
this.writeStdout = options.writeStdout ?? ((line) => process.stdout.write(`${line}\n`));
|
|
109
280
|
this.writeStderr = options.writeStderr ?? ((line) => process.stderr.write(`${line}\n`));
|
|
110
281
|
}
|
|
111
282
|
getDestination() {
|
|
112
283
|
return this.destination;
|
|
113
284
|
}
|
|
285
|
+
buildLine(event, payload) {
|
|
286
|
+
const denominator = readPayloadToolTurnDenominator(payload) ?? readEnvToolTurnDenominator(this.env);
|
|
287
|
+
return {
|
|
288
|
+
schema_version: RUN_SUMMARY_SCHEMA_VERSION,
|
|
289
|
+
session_id: this.sessionId,
|
|
290
|
+
framework_version: this.frameworkVersion,
|
|
291
|
+
seq: this.seq,
|
|
292
|
+
ts: this.now().toISOString(),
|
|
293
|
+
event,
|
|
294
|
+
payload,
|
|
295
|
+
...(this.component !== undefined ? { component: this.component } : {}),
|
|
296
|
+
...(denominator !== undefined ? { total_tool_turns: denominator } : {}),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
114
299
|
emit(event, payload) {
|
|
115
300
|
try {
|
|
116
301
|
if (this.destination.kind === "silent") {
|
|
117
302
|
return { emitted: false, destination: this.destination, line: null, warning: false };
|
|
118
303
|
}
|
|
119
|
-
this.seq += 1;
|
|
120
|
-
const denominator = readPayloadToolTurnDenominator(payload) ?? readEnvToolTurnDenominator(this.env);
|
|
121
|
-
const line = {
|
|
122
|
-
schema_version: RUN_SUMMARY_SCHEMA_VERSION,
|
|
123
|
-
session_id: this.sessionId,
|
|
124
|
-
framework_version: this.frameworkVersion,
|
|
125
|
-
seq: this.seq,
|
|
126
|
-
ts: this.now().toISOString(),
|
|
127
|
-
event,
|
|
128
|
-
payload,
|
|
129
|
-
...(denominator !== undefined ? { total_tool_turns: denominator } : {}),
|
|
130
|
-
};
|
|
131
|
-
const text = lineToJson(line);
|
|
132
304
|
if (this.destination.kind === "stdout") {
|
|
133
|
-
this.
|
|
305
|
+
this.seq += 1;
|
|
306
|
+
const line = this.buildLine(event, payload);
|
|
307
|
+
this.writeStdout(`${RUN_SUMMARY_STDOUT_PREFIX}${lineToJson(line)}`);
|
|
134
308
|
return { emitted: true, destination: this.destination, line, warning: false };
|
|
135
309
|
}
|
|
136
|
-
// file destination —
|
|
310
|
+
// file destination — lock count-then-append so concurrent CLI processes
|
|
311
|
+
// cannot share the same next seq (#3350 Greptile P1).
|
|
137
312
|
const { path, truncateOnSessionStart, explicit } = this.destination;
|
|
313
|
+
const release = acquireSeqLock(path);
|
|
138
314
|
try {
|
|
139
|
-
const
|
|
140
|
-
|
|
315
|
+
const replace = event === "session_start" && truncateOnSessionStart;
|
|
316
|
+
this.seq = replace ? 1 : countExistingJsonlLines(path) + 1;
|
|
317
|
+
const line = this.buildLine(event, payload);
|
|
318
|
+
writeRunSummaryLine(this.projectRoot, path, lineToJson(line), replace ? "replace" : "append");
|
|
141
319
|
return { emitted: true, destination: this.destination, line, warning: false };
|
|
142
320
|
}
|
|
143
321
|
catch {
|
|
144
322
|
// Symlink / containment refusal and I/O both fail-open.
|
|
323
|
+
const line = this.buildLine(event, payload);
|
|
145
324
|
if (explicit && !this.warned) {
|
|
146
325
|
this.warned = true;
|
|
147
326
|
this.writeStderr(RUN_SUMMARY_WRITE_WARNING);
|
|
@@ -149,6 +328,9 @@ export class RunSummaryEmitter {
|
|
|
149
328
|
}
|
|
150
329
|
return { emitted: false, destination: this.destination, line, warning: false };
|
|
151
330
|
}
|
|
331
|
+
finally {
|
|
332
|
+
release();
|
|
333
|
+
}
|
|
152
334
|
}
|
|
153
335
|
catch {
|
|
154
336
|
return { emitted: false, destination: this.destination, line: null, warning: false };
|
|
@@ -178,6 +360,9 @@ export class RunSummaryEmitter {
|
|
|
178
360
|
emitAcceptanceStamp(payload) {
|
|
179
361
|
return this.emit("acceptance_stamp", payload);
|
|
180
362
|
}
|
|
363
|
+
emitAcPassBank(payload) {
|
|
364
|
+
return this.emit("ac_pass_bank", payload);
|
|
365
|
+
}
|
|
181
366
|
/** Emit the harness-supplied denominator when DEFT_TOTAL_TOOL_TURNS is set. */
|
|
182
367
|
emitKnownToolTurnDenominator() {
|
|
183
368
|
const n = readEnvToolTurnDenominator(this.env);
|
|
@@ -186,6 +371,18 @@ export class RunSummaryEmitter {
|
|
|
186
371
|
}
|
|
187
372
|
return this.emitToolTurnDenominator({ total_tool_turns: n });
|
|
188
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Emit a session denominator only when a host/harness value is known (#3399).
|
|
376
|
+
* `emitKnownToolTurnDenominator` stays silent unless DEFT_TOTAL_TOOL_TURNS is
|
|
377
|
+
* set; this caller also records DEFT_MAX_TURNS / host maxTurns.
|
|
378
|
+
*/
|
|
379
|
+
emitSessionToolTurnDenominator(hostMaxTurns) {
|
|
380
|
+
const resolved = resolveSessionToolTurnDenominator(this.env, hostMaxTurns);
|
|
381
|
+
if (resolved === undefined) {
|
|
382
|
+
return { emitted: false, destination: this.destination, line: null, warning: false };
|
|
383
|
+
}
|
|
384
|
+
return this.emitToolTurnDenominator(resolved);
|
|
385
|
+
}
|
|
189
386
|
}
|
|
190
387
|
function readPayloadToolTurnDenominator(payload) {
|
|
191
388
|
if (!("total_tool_turns" in payload)) {
|
|
@@ -196,19 +393,58 @@ function readPayloadToolTurnDenominator(payload) {
|
|
|
196
393
|
}
|
|
197
394
|
/** Parse harness-supplied DEFT_TOTAL_TOOL_TURNS (integer > 0). Invalid/unset → omit. */
|
|
198
395
|
export function readEnvToolTurnDenominator(env) {
|
|
199
|
-
|
|
396
|
+
return readPositiveIntegerEnv(env, ENV_TOTAL_TOOL_TURNS);
|
|
397
|
+
}
|
|
398
|
+
/** Canonical host planned-turn budget recorded at session:start (#3356). */
|
|
399
|
+
export const ENV_MAX_TURNS_DENOMINATOR = "DEFT_MAX_TURNS";
|
|
400
|
+
function isPositiveIntegerDenominator(value) {
|
|
401
|
+
return (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value) && value > 0);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Host descriptors (#3266) accept any finite number >= 0. Keep a positive
|
|
405
|
+
* planned-turn budget as-is so share uses the host value, not a floored proxy.
|
|
406
|
+
*/
|
|
407
|
+
function coercePositiveHostDenominator(value) {
|
|
408
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
return value;
|
|
412
|
+
}
|
|
413
|
+
function readPositiveIntegerEnv(env, key) {
|
|
414
|
+
const raw = env[key];
|
|
200
415
|
if (raw === undefined || raw.trim().length === 0) {
|
|
201
416
|
return undefined;
|
|
202
417
|
}
|
|
203
418
|
const n = Number(raw.trim());
|
|
204
|
-
|
|
205
|
-
|
|
419
|
+
return isPositiveIntegerDenominator(n) ? n : undefined;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Resolve the session tool/turn denominator (#3399).
|
|
423
|
+
*
|
|
424
|
+
* Prefer harness actuals (`DEFT_TOTAL_TOOL_TURNS`), then a host planned-turn
|
|
425
|
+
* budget (`DEFT_MAX_TURNS` or `hostMaxTurns`). Unset → undefined (no event).
|
|
426
|
+
* Share is unevaluable without a host/harness value.
|
|
427
|
+
*/
|
|
428
|
+
export function resolveSessionToolTurnDenominator(env, hostMaxTurns) {
|
|
429
|
+
const known = readEnvToolTurnDenominator(env);
|
|
430
|
+
if (known !== undefined) {
|
|
431
|
+
return { total_tool_turns: known, denominator_source: "harness_actual" };
|
|
432
|
+
}
|
|
433
|
+
const planned = readPositiveIntegerEnv(env, ENV_MAX_TURNS_DENOMINATOR);
|
|
434
|
+
if (planned !== undefined) {
|
|
435
|
+
return { total_tool_turns: planned, denominator_source: "host_planned" };
|
|
436
|
+
}
|
|
437
|
+
const host = coercePositiveHostDenominator(hostMaxTurns);
|
|
438
|
+
if (host !== undefined) {
|
|
439
|
+
return { total_tool_turns: host, denominator_source: "host_planned" };
|
|
206
440
|
}
|
|
207
|
-
return
|
|
441
|
+
return undefined;
|
|
208
442
|
}
|
|
209
443
|
/**
|
|
210
444
|
* One-shot helper for call sites that do not hold an emitter (e.g. dial escalate).
|
|
211
|
-
* Still fail-open
|
|
445
|
+
* Still fail-open. File destinations seed seq from the existing JSONL line
|
|
446
|
+
* count so successive one-shot calls into the same path continue 1..N (#3350).
|
|
447
|
+
* Stdout destinations stay per-process (each call starts at seq=1).
|
|
212
448
|
*/
|
|
213
449
|
export function emitRunSummaryEvent(options) {
|
|
214
450
|
const emitter = new RunSummaryEmitter(options);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { type EmitRunSummaryResult, emitRunSummaryEvent, RunSummaryEmitter, type RunSummaryEmitterOptions, } from "./emit.js";
|
|
1
|
+
export { type EmitRunSummaryResult, ENV_MAX_TURNS_DENOMINATOR, emitRunSummaryEvent, type ResolvedSessionToolTurnDenominator, RunSummaryEmitter, type RunSummaryEmitterOptions, resolveSessionToolTurnDenominator, } from "./emit.js";
|
|
2
2
|
export { gitignoreCoversRunSummary, type ResolveRunSummaryDestinationOptions, resolveRunSummaryDestination, } from "./path.js";
|
|
3
|
+
export { type ResolveRunSummarySessionIdInput, resolveRunSummarySessionId, } from "./session-id.js";
|
|
3
4
|
export { computeRitualGateShare, parseRunSummaryJsonl, type RitualGateShare, readToolTurnDenominator, } from "./share.js";
|
|
4
|
-
export { type AcceptanceClauseOutcomeRow, type AcceptanceRunSummaryOutcome, type AcceptanceRunSummaryPayload, type AcceptanceStampRunSummaryPayload, type CheckGateOutcome, type CheckInvocationRunSummaryPayload, DEFAULT_RUN_SUMMARY_BASENAME, type DialEscalationEvaluationOutcome, type DialEscalationEvaluationRunSummaryPayload, type DialTransitionRunSummaryPayload, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_EVENT_KINDS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, type RunSummaryBaseFields, type RunSummaryDestination, type RunSummaryEventKind, type RunSummaryLine, type RunSummaryPayload, type SessionStartRunSummaryPayload, type ToolTurnDenominatorRunSummaryPayload, type VerificationOutcome, type VerificationRunSummaryPayload, } from "./types.js";
|
|
5
|
+
export { type AcceptanceClauseOutcomeRow, type AcceptanceRunSummaryOutcome, type AcceptanceRunSummaryPayload, type AcceptanceStampRunSummaryPayload, type AcPassBankRunSummaryPayload, type CheckGateOutcome, type CheckInvocationRunSummaryPayload, DEFAULT_RUN_SUMMARY_BASENAME, type DialEscalationEvaluationOutcome, type DialEscalationEvaluationRunSummaryPayload, type DialTransitionRunSummaryPayload, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_EVENT_KINDS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, type RunSummaryBaseFields, type RunSummaryDestination, type RunSummaryEventKind, type RunSummaryLine, type RunSummaryPayload, type SessionStartRunSummaryPayload, type ToolTurnDenominatorRunSummaryPayload, type ToolTurnDenominatorSource, type VerificationOutcome, type VerificationRunSummaryPayload, } from "./types.js";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { emitRunSummaryEvent, RunSummaryEmitter, } from "./emit.js";
|
|
1
|
+
export { ENV_MAX_TURNS_DENOMINATOR, emitRunSummaryEvent, RunSummaryEmitter, resolveSessionToolTurnDenominator, } from "./emit.js";
|
|
2
2
|
export { gitignoreCoversRunSummary, resolveRunSummaryDestination, } from "./path.js";
|
|
3
|
+
export { resolveRunSummarySessionId, } from "./session-id.js";
|
|
3
4
|
export { computeRitualGateShare, parseRunSummaryJsonl, readToolTurnDenominator, } from "./share.js";
|
|
4
5
|
export { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_EVENT_KINDS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared workspace session identity for run-summary emitters (#3399).
|
|
3
|
+
*
|
|
4
|
+
* Ritual-state holds the workspace session id written by session:start.
|
|
5
|
+
* #3350 persisted seq by counting JSONL lines; it is not the identity store.
|
|
6
|
+
*/
|
|
7
|
+
export interface ResolveRunSummarySessionIdInput {
|
|
8
|
+
readonly projectRoot: string;
|
|
9
|
+
readonly explicit?: string | null;
|
|
10
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
11
|
+
readonly mint?: () => string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve one workspace session id for a run-summary line.
|
|
15
|
+
*
|
|
16
|
+
* Order: explicit option → DEFT_SESSION_ID → ritual-state session_id →
|
|
17
|
+
* last session_id already in the destination JSONL → mint only when no
|
|
18
|
+
* workspace session exists yet.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveRunSummarySessionId(input: ResolveRunSummarySessionIdInput): string;
|
|
21
|
+
//# sourceMappingURL=session-id.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared workspace session identity for run-summary emitters (#3399).
|
|
3
|
+
*
|
|
4
|
+
* Ritual-state holds the workspace session id written by session:start.
|
|
5
|
+
* #3350 persisted seq by counting JSONL lines; it is not the identity store.
|
|
6
|
+
*/
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
|
+
import { readRitualState } from "../session/ritual-sentinel.js";
|
|
11
|
+
import { resolveRunSummaryDestination } from "./path.js";
|
|
12
|
+
import { parseRunSummaryJsonl } from "./share.js";
|
|
13
|
+
const mintedByProjectRoot = new Map();
|
|
14
|
+
function trimNonEmpty(value) {
|
|
15
|
+
if (typeof value !== "string") {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const trimmed = value.trim();
|
|
19
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
20
|
+
}
|
|
21
|
+
function lastSessionIdFromJsonl(path) {
|
|
22
|
+
try {
|
|
23
|
+
if (!existsSync(path)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const lines = parseRunSummaryJsonl(readFileSync(path, "utf8"));
|
|
27
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
28
|
+
const id = trimNonEmpty(lines[i]?.session_id);
|
|
29
|
+
if (id !== undefined) {
|
|
30
|
+
return id;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function ritualSessionId(projectRoot) {
|
|
40
|
+
try {
|
|
41
|
+
const [state] = readRitualState(projectRoot);
|
|
42
|
+
return trimNonEmpty(state?.sessionId);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve one workspace session id for a run-summary line.
|
|
50
|
+
*
|
|
51
|
+
* Order: explicit option → DEFT_SESSION_ID → ritual-state session_id →
|
|
52
|
+
* last session_id already in the destination JSONL → mint only when no
|
|
53
|
+
* workspace session exists yet.
|
|
54
|
+
*/
|
|
55
|
+
export function resolveRunSummarySessionId(input) {
|
|
56
|
+
const explicit = trimNonEmpty(input.explicit);
|
|
57
|
+
if (explicit !== undefined) {
|
|
58
|
+
return explicit;
|
|
59
|
+
}
|
|
60
|
+
const env = input.env ?? process.env;
|
|
61
|
+
const fromEnv = trimNonEmpty(env.DEFT_SESSION_ID);
|
|
62
|
+
if (fromEnv !== undefined) {
|
|
63
|
+
return fromEnv;
|
|
64
|
+
}
|
|
65
|
+
const fromRitual = ritualSessionId(input.projectRoot);
|
|
66
|
+
if (fromRitual !== undefined) {
|
|
67
|
+
return fromRitual;
|
|
68
|
+
}
|
|
69
|
+
const dest = resolveRunSummaryDestination(input.projectRoot, { env });
|
|
70
|
+
if (dest.kind === "file") {
|
|
71
|
+
const fromJsonl = lastSessionIdFromJsonl(dest.path);
|
|
72
|
+
if (fromJsonl !== undefined) {
|
|
73
|
+
return fromJsonl;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const rootKey = resolve(input.projectRoot);
|
|
77
|
+
if (input.mint !== undefined) {
|
|
78
|
+
const minted = trimNonEmpty(input.mint());
|
|
79
|
+
if (minted !== undefined) {
|
|
80
|
+
mintedByProjectRoot.set(rootKey, minted);
|
|
81
|
+
return minted;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const cached = mintedByProjectRoot.get(rootKey);
|
|
85
|
+
if (cached !== undefined) {
|
|
86
|
+
return cached;
|
|
87
|
+
}
|
|
88
|
+
const minted = randomUUID();
|
|
89
|
+
mintedByProjectRoot.set(rootKey, minted);
|
|
90
|
+
return minted;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=session-id.js.map
|
|
@@ -11,7 +11,7 @@ export interface RitualGateShare {
|
|
|
11
11
|
readonly totalToolTurns: number | null;
|
|
12
12
|
readonly share: number | null;
|
|
13
13
|
}
|
|
14
|
-
/** True when n is a finite
|
|
14
|
+
/** True when n is a finite positive number usable as a share denominator. */
|
|
15
15
|
export declare function isValidToolTurnDenominator(value: unknown): value is number;
|
|
16
16
|
/** Read total_tool_turns from a line (top-level field or payload). */
|
|
17
17
|
export declare function readToolTurnDenominator(line: unknown): number | undefined;
|
|
@@ -11,9 +11,9 @@ function asRecord(value) {
|
|
|
11
11
|
}
|
|
12
12
|
return value;
|
|
13
13
|
}
|
|
14
|
-
/** True when n is a finite
|
|
14
|
+
/** True when n is a finite positive number usable as a share denominator. */
|
|
15
15
|
export function isValidToolTurnDenominator(value) {
|
|
16
|
-
return
|
|
16
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
17
17
|
}
|
|
18
18
|
/** Read total_tool_turns from a line (top-level field or payload). */
|
|
19
19
|
export function readToolTurnDenominator(line) {
|
|
@@ -15,16 +15,28 @@ export declare const DEFAULT_RUN_SUMMARY_BASENAME = ".deft-run-summary.json";
|
|
|
15
15
|
export declare const RUN_SUMMARY_STDOUT_PREFIX = "DEFT-TLM:";
|
|
16
16
|
/** One warning when an explicitly requested write fails. */
|
|
17
17
|
export declare const RUN_SUMMARY_WRITE_WARNING = "[deft run-summary] failed to write DEFT_RUN_SUMMARY_PATH telemetry (fail-open; exit codes unchanged)";
|
|
18
|
-
export declare const RUN_SUMMARY_EVENT_KINDS: readonly ["session_start", "dial_transition", "dial_escalation_evaluation", "check_invocation", "tool_turn_denominator", "verification", "acceptance", "acceptance_stamp"];
|
|
18
|
+
export declare const RUN_SUMMARY_EVENT_KINDS: readonly ["session_start", "dial_transition", "dial_escalation_evaluation", "check_invocation", "tool_turn_denominator", "verification", "acceptance", "acceptance_stamp", "ac_pass_bank"];
|
|
19
19
|
export type RunSummaryEventKind = (typeof RUN_SUMMARY_EVENT_KINDS)[number];
|
|
20
20
|
export interface RunSummaryBaseFields {
|
|
21
21
|
readonly schema_version: typeof RUN_SUMMARY_SCHEMA_VERSION;
|
|
22
22
|
readonly session_id: string;
|
|
23
23
|
readonly framework_version: string;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Monotonic 1-based sequence for this destination (#3350).
|
|
26
|
+
* File destinations continue from the existing JSONL line count so multiple
|
|
27
|
+
* CLI processes appending to one DEFT_RUN_SUMMARY_PATH share 1..N. Concurrent
|
|
28
|
+
* file writers serialize count-then-append via a sibling lock; reclaim is
|
|
29
|
+
* allowed only when the lock owner process is dead or the token is unknown
|
|
30
|
+
* (#3361). Stdout destinations stay per-process (each process starts at 1).
|
|
31
|
+
*/
|
|
25
32
|
readonly seq: number;
|
|
26
33
|
readonly ts: string;
|
|
27
34
|
readonly event: RunSummaryEventKind;
|
|
35
|
+
/**
|
|
36
|
+
* Optional emitter label (verify-ac, clause-derivation, …). Not a closed
|
|
37
|
+
* union — new callers add a string without a schema bump (#3399).
|
|
38
|
+
*/
|
|
39
|
+
readonly component?: string;
|
|
28
40
|
/**
|
|
29
41
|
* Session total tool/turn count (#3320). Present so ritual+gate share is
|
|
30
42
|
* computable from the summary alone. Absence means the #3286 trigger is unevaluable.
|
|
@@ -81,14 +93,32 @@ export interface CheckInvocationRunSummaryPayload {
|
|
|
81
93
|
readonly none_stated?: boolean;
|
|
82
94
|
readonly clause_outcomes?: readonly AcceptanceClauseOutcomeRow[];
|
|
83
95
|
}
|
|
84
|
-
/**
|
|
96
|
+
/** How a tool_turn_denominator value was obtained (#3399). */
|
|
97
|
+
export type ToolTurnDenominatorSource = "harness_actual" | "host_planned";
|
|
98
|
+
/** Total tool/turn count for the session (#3320 / #3399). */
|
|
85
99
|
export interface ToolTurnDenominatorRunSummaryPayload {
|
|
86
100
|
readonly total_tool_turns: number;
|
|
101
|
+
readonly denominator_source?: ToolTurnDenominatorSource;
|
|
102
|
+
}
|
|
103
|
+
/** Bank checkpoint JSONL payload. Disk ledger stays camelCase (#3387). */
|
|
104
|
+
export interface AcPassBankRunSummaryPayload {
|
|
105
|
+
readonly scope_id: string;
|
|
106
|
+
readonly banked_at: string;
|
|
107
|
+
readonly next_action?: string;
|
|
108
|
+
readonly had_surplus?: boolean;
|
|
109
|
+
readonly surplus_threshold?: number;
|
|
110
|
+
readonly remaining_fraction?: number | null;
|
|
111
|
+
readonly remaining_turns?: number | null;
|
|
112
|
+
readonly remaining_budget?: number | null;
|
|
113
|
+
readonly max_turns?: number | null;
|
|
114
|
+
readonly max_budget?: number | null;
|
|
115
|
+
readonly head_sha?: string | null;
|
|
116
|
+
readonly path?: string;
|
|
87
117
|
}
|
|
88
118
|
/** Product-oracle attempt (#3322). Same emitter as #3319 / #3320. */
|
|
89
119
|
export type VerificationOutcome = "pass" | "fail";
|
|
90
|
-
/** verify:ac resolution telemetry (#3334). Distinct from verification pass/fail. */
|
|
91
|
-
export type AcceptanceRunSummaryOutcome = "verified-pass" | "empty-pass" | "soft_empty" | "fail";
|
|
120
|
+
/** verify:ac resolution telemetry (#3334 / #3355). Distinct from verification pass/fail. */
|
|
121
|
+
export type AcceptanceRunSummaryOutcome = "verified-pass" | "empty-pass" | "soft_empty" | "fail" | "config-error" | "soft-missing" | "rejected-noop";
|
|
92
122
|
export interface AcceptanceRunSummaryPayload {
|
|
93
123
|
readonly resolved_command_count: number;
|
|
94
124
|
readonly outcome: AcceptanceRunSummaryOutcome;
|
|
@@ -96,6 +126,8 @@ export interface AcceptanceRunSummaryPayload {
|
|
|
96
126
|
readonly none_stated?: boolean;
|
|
97
127
|
readonly clause_count?: number;
|
|
98
128
|
readonly clause_outcomes?: readonly AcceptanceClauseOutcomeRow[];
|
|
129
|
+
/** bank | cache | executed — how verify:ac obtained this result (#3387). */
|
|
130
|
+
readonly served_from?: "bank" | "cache" | "executed";
|
|
99
131
|
}
|
|
100
132
|
/** Intake-time stamp: which rung locked, whether commands were stated, counts (#3323). */
|
|
101
133
|
export interface AcceptanceStampRunSummaryPayload {
|
|
@@ -115,7 +147,7 @@ export interface VerificationRunSummaryPayload {
|
|
|
115
147
|
*/
|
|
116
148
|
readonly independent_rederivation?: boolean;
|
|
117
149
|
}
|
|
118
|
-
export type RunSummaryPayload = SessionStartRunSummaryPayload | DialTransitionRunSummaryPayload | DialEscalationEvaluationRunSummaryPayload | CheckInvocationRunSummaryPayload | ToolTurnDenominatorRunSummaryPayload | VerificationRunSummaryPayload | AcceptanceRunSummaryPayload | AcceptanceStampRunSummaryPayload;
|
|
150
|
+
export type RunSummaryPayload = SessionStartRunSummaryPayload | DialTransitionRunSummaryPayload | DialEscalationEvaluationRunSummaryPayload | CheckInvocationRunSummaryPayload | ToolTurnDenominatorRunSummaryPayload | VerificationRunSummaryPayload | AcceptanceRunSummaryPayload | AcceptanceStampRunSummaryPayload | AcPassBankRunSummaryPayload;
|
|
119
151
|
export type RunSummaryLine = RunSummaryBaseFields & {
|
|
120
152
|
readonly payload: RunSummaryPayload;
|
|
121
153
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Agents author Test / AcceptanceCriteria / Verification prose in narratives
|
|
5
5
|
* and then skip the executable plan.acceptance block. Activation fails closed
|
|
6
|
-
* with one remediation: stamp
|
|
6
|
+
* with one remediation: stamp plan.acceptance via clause derivation.
|
|
7
7
|
*/
|
|
8
8
|
export interface AcceptanceActivateHit {
|
|
9
9
|
readonly key: string;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Agents author Test / AcceptanceCriteria / Verification prose in narratives
|
|
5
5
|
* and then skip the executable plan.acceptance block. Activation fails closed
|
|
6
|
-
* with one remediation: stamp
|
|
6
|
+
* with one remediation: stamp plan.acceptance via clause derivation.
|
|
7
7
|
*/
|
|
8
8
|
import { validatePlanAcceptance } from "../product-first-done-gate/acceptance.js";
|
|
9
9
|
function asRecord(value) {
|
|
@@ -51,7 +51,7 @@ export function evaluateAcceptanceActivateGate(plan) {
|
|
|
51
51
|
ok: false,
|
|
52
52
|
message: `Refusing activate: plan.acceptance is present but invalid (#3334): ` +
|
|
53
53
|
`${schemaErrors.join("; ")}. Stamp a valid plan.acceptance block ` +
|
|
54
|
-
`
|
|
54
|
+
`via clause derivation (scope:activate / scope:promote runs #3323).`,
|
|
55
55
|
hits,
|
|
56
56
|
};
|
|
57
57
|
}
|
|
@@ -61,8 +61,8 @@ export function evaluateAcceptanceActivateGate(plan) {
|
|
|
61
61
|
return {
|
|
62
62
|
ok: false,
|
|
63
63
|
message: `Refusing activate: narratives contain acceptance-shaped keys (${keys}) ` +
|
|
64
|
-
`but plan.acceptance is absent (#3334). Stamp plan.acceptance ` +
|
|
65
|
-
`(
|
|
64
|
+
`but plan.acceptance is absent (#3334). Stamp plan.acceptance via clause derivation ` +
|
|
65
|
+
`(scope:activate / scope:promote runs #3323, or task issue:ingest) — ` +
|
|
66
66
|
`the criteria are in the wrong, non-executable field.`,
|
|
67
67
|
hits,
|
|
68
68
|
};
|