@telora/daemon 0.22.91 → 0.22.104
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/build-info.json +6 -3
- package/dist/cli/verify-setup-config.d.ts +4 -3
- package/dist/cli/verify-setup-config.d.ts.map +1 -1
- package/dist/cli/verify-setup-config.js.map +1 -1
- package/dist/completion/event-phases.js +5 -5
- package/dist/completion/event-phases.js.map +1 -1
- package/dist/completion/event.d.ts +7 -0
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +2 -1
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/delivery/external-hold.js +1 -1
- package/dist/delivery/external-hold.js.map +1 -1
- package/dist/delivery/operator-hold.d.ts +14 -0
- package/dist/delivery/operator-hold.d.ts.map +1 -1
- package/dist/delivery/operator-hold.js +31 -1
- package/dist/delivery/operator-hold.js.map +1 -1
- package/dist/directive/directive-dispatch.js +2 -2
- package/dist/directive/directive-dispatch.js.map +1 -1
- package/dist/directive/directive-executor.d.ts.map +1 -1
- package/dist/directive/directive-executor.js +2 -1
- package/dist/directive/directive-executor.js.map +1 -1
- package/dist/directive/directive-verify-hold-gate.d.ts +32 -8
- package/dist/directive/directive-verify-hold-gate.d.ts.map +1 -1
- package/dist/directive/directive-verify-hold-gate.js +20 -21
- package/dist/directive/directive-verify-hold-gate.js.map +1 -1
- package/dist/focus/focus-engine.d.ts.map +1 -1
- package/dist/focus/focus-engine.js +6 -9
- package/dist/focus/focus-engine.js.map +1 -1
- package/dist/focus/listener-spawn.d.ts +31 -0
- package/dist/focus/listener-spawn.d.ts.map +1 -1
- package/dist/focus/listener-spawn.js +44 -22
- package/dist/focus/listener-spawn.js.map +1 -1
- package/dist/focus/listener.d.ts.map +1 -1
- package/dist/focus/listener.js +4 -0
- package/dist/focus/listener.js.map +1 -1
- package/dist/focus/team-actionable-work.d.ts +76 -20
- package/dist/focus/team-actionable-work.d.ts.map +1 -1
- package/dist/focus/team-actionable-work.js +85 -20
- package/dist/focus/team-actionable-work.js.map +1 -1
- package/dist/git/exec.d.ts.map +1 -1
- package/dist/git/exec.js +2 -1
- package/dist/git/exec.js.map +1 -1
- package/dist/nexus/incoming.js +1 -1
- package/dist/nexus/incoming.js.map +1 -1
- package/dist/nexus/pending-returns.d.ts +124 -0
- package/dist/nexus/pending-returns.d.ts.map +1 -0
- package/dist/nexus/pending-returns.js +194 -0
- package/dist/nexus/pending-returns.js.map +1 -0
- package/dist/nexus/return-routing.d.ts +89 -0
- package/dist/nexus/return-routing.d.ts.map +1 -0
- package/dist/nexus/return-routing.js +148 -0
- package/dist/nexus/return-routing.js.map +1 -0
- package/dist/nexus/return.d.ts +66 -89
- package/dist/nexus/return.d.ts.map +1 -1
- package/dist/nexus/return.js +116 -279
- package/dist/nexus/return.js.map +1 -1
- package/dist/prompt/focus-prompt-builder.d.ts.map +1 -1
- package/dist/prompt/focus-prompt-builder.js +1 -0
- package/dist/prompt/focus-prompt-builder.js.map +1 -1
- package/dist/queries/delivery-gate-state.d.ts +31 -0
- package/dist/queries/delivery-gate-state.d.ts.map +1 -1
- package/dist/queries/delivery-gate-state.js +31 -4
- package/dist/queries/delivery-gate-state.js.map +1 -1
- package/dist/queries/focus-discovery.d.ts +7 -1
- package/dist/queries/focus-discovery.d.ts.map +1 -1
- package/dist/queries/focus-discovery.js +9 -3
- package/dist/queries/focus-discovery.js.map +1 -1
- package/dist/queries/requests.d.ts +51 -3
- package/dist/queries/requests.d.ts.map +1 -1
- package/dist/queries/requests.js +63 -10
- package/dist/queries/requests.js.map +1 -1
- package/dist/queries/shared.js +1 -1
- package/dist/queries/verification.d.ts +15 -0
- package/dist/queries/verification.d.ts.map +1 -1
- package/dist/queries/verification.js +2 -0
- package/dist/queries/verification.js.map +1 -1
- package/dist/shell/config.d.ts.map +1 -1
- package/dist/shell/config.js +10 -9
- package/dist/shell/config.js.map +1 -1
- package/dist/shell/unified-shell-status.d.ts +6 -0
- package/dist/shell/unified-shell-status.d.ts.map +1 -1
- package/dist/shell/unified-shell-status.js +9 -2
- package/dist/shell/unified-shell-status.js.map +1 -1
- package/dist/spawn/prompt-assembly.js +1 -1
- package/dist/spawn/prompt-assembly.js.map +1 -1
- package/dist/spawn/respawn-cycle-escalation.d.ts.map +1 -1
- package/dist/spawn/respawn-cycle-escalation.js +11 -3
- package/dist/spawn/respawn-cycle-escalation.js.map +1 -1
- package/dist/spawn/review-hold-park.d.ts +31 -0
- package/dist/spawn/review-hold-park.d.ts.map +1 -0
- package/dist/spawn/review-hold-park.js +120 -0
- package/dist/spawn/review-hold-park.js.map +1 -0
- package/dist/spawn/spawn-cooldown.d.ts +27 -4
- package/dist/spawn/spawn-cooldown.d.ts.map +1 -1
- package/dist/spawn/spawn-cooldown.js +88 -3
- package/dist/spawn/spawn-cooldown.js.map +1 -1
- package/dist/spawn/spawn-decision.d.ts.map +1 -1
- package/dist/spawn/spawn-decision.js +6 -0
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/types/config.d.ts +9 -6
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/update/auto-update-idle.d.ts.map +1 -1
- package/dist/update/auto-update-idle.js +2 -1
- package/dist/update/auto-update-idle.js.map +1 -1
- package/dist/verify/failure-signature.d.ts.map +1 -1
- package/dist/verify/failure-signature.js +1 -0
- package/dist/verify/failure-signature.js.map +1 -1
- package/dist/verify/verification-baseline.d.ts +63 -16
- package/dist/verify/verification-baseline.d.ts.map +1 -1
- package/dist/verify/verification-baseline.js +74 -34
- package/dist/verify/verification-baseline.js.map +1 -1
- package/dist/verify/verification-checkout.d.ts +71 -0
- package/dist/verify/verification-checkout.d.ts.map +1 -0
- package/dist/verify/verification-checkout.js +353 -0
- package/dist/verify/verification-checkout.js.map +1 -0
- package/dist/verify/verification-config.d.ts +76 -0
- package/dist/verify/verification-config.d.ts.map +1 -1
- package/dist/verify/verification-config.js +100 -12
- package/dist/verify/verification-config.js.map +1 -1
- package/dist/verify/verification-default-on.d.ts +102 -11
- package/dist/verify/verification-default-on.d.ts.map +1 -1
- package/dist/verify/verification-default-on.js +252 -16
- package/dist/verify/verification-default-on.js.map +1 -1
- package/dist/verify/verification-deps-default.js +1 -1
- package/dist/verify/verification-deps-default.js.map +1 -1
- package/dist/verify/verification-deps.d.ts +28 -3
- package/dist/verify/verification-deps.d.ts.map +1 -1
- package/dist/verify/verification-deterministic.d.ts +63 -1
- package/dist/verify/verification-deterministic.d.ts.map +1 -1
- package/dist/verify/verification-deterministic.js +38 -29
- package/dist/verify/verification-deterministic.js.map +1 -1
- package/dist/verify/verification-engine.d.ts.map +1 -1
- package/dist/verify/verification-engine.js +20 -20
- package/dist/verify/verification-engine.js.map +1 -1
- package/dist/verify/verification-evidence-unavailable-feedback.d.ts.map +1 -1
- package/dist/verify/verification-evidence-unavailable-feedback.js +6 -1
- package/dist/verify/verification-evidence-unavailable-feedback.js.map +1 -1
- package/dist/verify/verification-feedback.d.ts +3 -0
- package/dist/verify/verification-feedback.d.ts.map +1 -1
- package/dist/verify/verification-feedback.js +5 -2
- package/dist/verify/verification-feedback.js.map +1 -1
- package/dist/verify/verification-manifest.d.ts.map +1 -1
- package/dist/verify/verification-manifest.js +1 -0
- package/dist/verify/verification-manifest.js.map +1 -1
- package/dist/verify/verification-timeout-fault.d.ts +147 -0
- package/dist/verify/verification-timeout-fault.d.ts.map +1 -0
- package/dist/verify/verification-timeout-fault.js +211 -0
- package/dist/verify/verification-timeout-fault.js.map +1 -0
- package/dist/verify/verify-gate-stall.d.ts +5 -1
- package/dist/verify/verify-gate-stall.d.ts.map +1 -1
- package/dist/verify/verify-gate-stall.js +10 -1
- package/dist/verify/verify-gate-stall.js.map +1 -1
- package/dist/worktree/verify-interlock.d.ts +9 -1
- package/dist/worktree/verify-interlock.d.ts.map +1 -1
- package/dist/worktree/verify-interlock.js +40 -3
- package/dist/worktree/verify-interlock.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads a timed-out deterministic run's captured output for a COMPLETE
|
|
3
|
+
* test-runner summary.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: when the deterministic verify command outlives its timeout
|
|
6
|
+
* the gate SIGKILLs it, and both the gate rationale and the verification
|
|
7
|
+
* engine's "missing" evidence used to be fixed strings -- "run did not
|
|
8
|
+
* complete" / "killed by timeout before completion" -- that never looked at the
|
|
9
|
+
* output. Observed 2026-09-14: vitest printed `Test Files 441 passed (441)` at
|
|
10
|
+
* 624.49s and the kill landed ~3s later, so a run that had FINISHED its test
|
|
11
|
+
* pass was described as one that never got there. That misdescription sends
|
|
12
|
+
* the reader (human or agent) hunting a slow/hung test that does not exist,
|
|
13
|
+
* when the real fault is the time spent after the tests (teardown, a lingering
|
|
14
|
+
* handle, a post-test stage) or a timeout sized below the suite.
|
|
15
|
+
*
|
|
16
|
+
* This module is the evidence read those descriptions lack: given the stdout /
|
|
17
|
+
* stderr tails, it returns the runner's own totals lines when the runner got as
|
|
18
|
+
* far as printing them, else null. It does NOT decide pass/fail -- a timed-out
|
|
19
|
+
* run still fails the gate; this only lets the wording say what the output
|
|
20
|
+
* shows.
|
|
21
|
+
*
|
|
22
|
+
* The two describers built on it keep that wording in one place:
|
|
23
|
+
* `describeTimeout` (the deterministic strategy's rationale) and
|
|
24
|
+
* `buildTimeoutFaultHold` (the verification engine's instrument-fault hold).
|
|
25
|
+
* Both allow for a multi-stage gate command (`npm run verify:gate` chains lint,
|
|
26
|
+
* typecheck, and tests): a printed summary means THAT RUNNER finished, never that
|
|
27
|
+
* the command did, so they say the command had not exited -- never that it
|
|
28
|
+
* completed. Without a summary both return their legacy strings byte-for-byte.
|
|
29
|
+
*
|
|
30
|
+
* Recognised totals (progress lines such as `✓ src/foo.test.ts (3 tests)`,
|
|
31
|
+
* `ok 4 - name`, `# Subtest: x` are deliberately NOT summaries):
|
|
32
|
+
* - vitest: `Test Files 2 failed | 439 passed (441)` (sufficient
|
|
33
|
+
* alone) with its `Tests N passed (N)` line; a `Tests`
|
|
34
|
+
* totals line alone also counts.
|
|
35
|
+
* - node:test TAP: `# tests N` AND `# pass N` (+ `# fail N` when present).
|
|
36
|
+
* - node:test spec: `ℹ tests N` AND `ℹ pass N` (+ `ℹ fail N` when present).
|
|
37
|
+
*
|
|
38
|
+
* Each tail is the last 4000 chars of a stream, so it may begin mid-line; the
|
|
39
|
+
* matchers are anchored at line start, so a truncated first line simply fails
|
|
40
|
+
* to match rather than matching a fragment. A tail can also have LOST the
|
|
41
|
+
* summary: a gate that keeps printing after its tests evicts it past 4000
|
|
42
|
+
* chars. So the strategy tracks each stream's summary while it streams
|
|
43
|
+
* (createRunnerSummaryTracker, fed before each cut) and hands the latest one to
|
|
44
|
+
* describeTimeout. PURE (no I/O).
|
|
45
|
+
*
|
|
46
|
+
* @module verification-timeout-fault
|
|
47
|
+
*/
|
|
48
|
+
import type { StrategyResult } from './verification-deterministic.js';
|
|
49
|
+
import type { EvidenceUnavailableDetail } from '../queries/delivery-gate-state.js';
|
|
50
|
+
/**
|
|
51
|
+
* The complete test-runner summary line(s) in a run's captured output, or null.
|
|
52
|
+
*
|
|
53
|
+
* Searches stdout first, then stderr (each independently -- a summary is never
|
|
54
|
+
* assembled from lines split across the two streams).
|
|
55
|
+
*
|
|
56
|
+
* @param stdoutTail The captured stdout tail (last ~4000 chars) of the run.
|
|
57
|
+
* @param stderrTail The captured stderr tail (last ~4000 chars) of the run.
|
|
58
|
+
* @returns The runner's totals lines, trimmed and in output order; null when no
|
|
59
|
+
* complete summary is present.
|
|
60
|
+
*/
|
|
61
|
+
export declare function findRunnerSummary(stdoutTail?: string, stderrTail?: string): string[] | null;
|
|
62
|
+
/** The latest complete runner summary one stream has printed, however much output followed it. */
|
|
63
|
+
export interface RunnerSummaryTracker {
|
|
64
|
+
/**
|
|
65
|
+
* Observe the stream after `chunk` was appended to `buffer`, BEFORE the buffer
|
|
66
|
+
* is cut to its tail: whatever is still in `buffer` has not been evicted yet.
|
|
67
|
+
*/
|
|
68
|
+
observe(buffer: string, chunk: string): void;
|
|
69
|
+
/** The most recent complete summary observed, or null. */
|
|
70
|
+
latest(): string[] | null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Track one stream's runner summary while it streams. The strategy keeps only a
|
|
74
|
+
* 4000-char tail per stream, and a multi-stage gate keeps printing after its
|
|
75
|
+
* tests (the next stage, teardown logs), so by the time the kill lands the
|
|
76
|
+
* summary may be gone from the tail. Each observation scans the not-yet-evicted
|
|
77
|
+
* buffer -- only when the new text could complete a summary -- and remembers the
|
|
78
|
+
* latest found, so a later re-printed summary still supersedes an earlier one.
|
|
79
|
+
*/
|
|
80
|
+
export declare function createRunnerSummaryTracker(): RunnerSummaryTracker;
|
|
81
|
+
/**
|
|
82
|
+
* The rationale for a deterministic run killed by its timeout, read from what
|
|
83
|
+
* the run's output shows.
|
|
84
|
+
*
|
|
85
|
+
* With a runner summary the kill landed after the tests had finished, so the
|
|
86
|
+
* rationale quotes it and names what actually ran out: the command had not
|
|
87
|
+
* exited within the limit. Without one it is the legacy "run did not complete"
|
|
88
|
+
* string, byte-identical. Either way the run stays a failed timeout -- this only
|
|
89
|
+
* chooses the words.
|
|
90
|
+
*
|
|
91
|
+
* A `timeoutSource` label is named right after the value (`after 600000ms (from
|
|
92
|
+
* /repo/telora.verify.json)`), because a timeout sized below the suite is fixed
|
|
93
|
+
* in whichever layer supplied it and the reader otherwise has to guess which. With
|
|
94
|
+
* no label both strings are unchanged.
|
|
95
|
+
*
|
|
96
|
+
* @param timeoutMs The timeout the run was killed at.
|
|
97
|
+
* @param stdoutTail The captured stdout tail of the killed run.
|
|
98
|
+
* @param stderrTail The captured stderr tail of the killed run.
|
|
99
|
+
* @param timeoutSource Where `timeoutMs` was read (formatVerifyConfigSource label), when known.
|
|
100
|
+
* @param observedSummary The latest summary tracked while the run streamed, used
|
|
101
|
+
* when later output evicted it from the tails (createRunnerSummaryTracker).
|
|
102
|
+
* @returns The rationale, plus the matched summary lines when one was found.
|
|
103
|
+
*/
|
|
104
|
+
export declare function describeTimeout(timeoutMs: number, stdoutTail?: string, stderrTail?: string, timeoutSource?: string, observedSummary?: readonly string[] | null): {
|
|
105
|
+
rationale: string;
|
|
106
|
+
runnerSummary?: string[];
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* The instrument-fault hold for a timed-out deterministic run, with its
|
|
110
|
+
* `missing` evidence named from the captured output.
|
|
111
|
+
*
|
|
112
|
+
* Routing is the caller's and does not change here: a timeout is never a
|
|
113
|
+
* verdict about the work, so this is always a `deterministicFault` of kind
|
|
114
|
+
* 'timeout'. What a summary changes is the NAME of the missing evidence -- the
|
|
115
|
+
* test output is there; the clean exit is not -- so the hold stops pointing a
|
|
116
|
+
* reader at a hung test that does not exist. Prefers the summary the strategy
|
|
117
|
+
* already matched; falls back to reading the tails (a result built elsewhere).
|
|
118
|
+
*
|
|
119
|
+
* The fault also records the timeout the run applied and where it was read,
|
|
120
|
+
* taken from the run's `appliedConfig`, so the gate state (and every human
|
|
121
|
+
* surface rendered from it) names the value and the layer to change. A result
|
|
122
|
+
* that recorded no applied config leaves both out rather than guessing.
|
|
123
|
+
*
|
|
124
|
+
* @param result The timed-out run's exit code, matched summary, output tails, and applied config.
|
|
125
|
+
* @returns The hold detail, minus the `heldAt` stamp the requeue path adds.
|
|
126
|
+
*/
|
|
127
|
+
export declare function buildTimeoutFaultHold(result: Pick<StrategyResult, 'exitCode' | 'runnerSummary' | 'stdoutTail' | 'stderrTail' | 'appliedConfig'>): Omit<EvidenceUnavailableDetail, 'heldAt'>;
|
|
128
|
+
/** A recorded deterministic instrument fault (gate_state.evidenceUnavailable.deterministicFault). */
|
|
129
|
+
type DeterministicFault = NonNullable<EvidenceUnavailableDetail['deterministicFault']>;
|
|
130
|
+
/**
|
|
131
|
+
* The `Timeout applied: <ms>ms from <source>` line for a timeout fault that
|
|
132
|
+
* recorded its applied timeout, else null.
|
|
133
|
+
*
|
|
134
|
+
* One formatter so the team inject message and the verify_gate_unclearable
|
|
135
|
+
* escalation print the identical line: a reader who sees it in one place can grep
|
|
136
|
+
* for it in the other. Null for every other hold, so those bodies stay unchanged.
|
|
137
|
+
*/
|
|
138
|
+
export declare function formatTimeoutAppliedLine(fault: DeterministicFault | undefined): string | null;
|
|
139
|
+
/**
|
|
140
|
+
* Read a timeout fault that recorded its applied timeout out of an untyped
|
|
141
|
+
* gate_state (FocusDeliveryInfo.gateState is `unknown`), or undefined. Defensive
|
|
142
|
+
* at every level: a legacy or foreign gate_state shape reads as "no timeout fault",
|
|
143
|
+
* never a throw.
|
|
144
|
+
*/
|
|
145
|
+
export declare function readTimeoutFault(gateState: unknown): DeterministicFault | undefined;
|
|
146
|
+
export {};
|
|
147
|
+
//# sourceMappingURL=verification-timeout-fault.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-timeout-fault.d.ts","sourceRoot":"","sources":["../../src/verify/verification-timeout-fault.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AA8DnF;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAE3F;AAOD,kGAAkG;AAClG,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,0DAA0D;IAC1D,MAAM,IAAI,MAAM,EAAE,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,IAAI,oBAAoB,CAWjE;AAKD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,EACtB,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GACzC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAajD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,eAAe,GAAG,YAAY,GAAG,YAAY,GAAG,eAAe,CAAC,GACzG,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAkB3C;AAED,qGAAqG;AACrG,KAAK,kBAAkB,GAAG,WAAW,CAAC,yBAAyB,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAEvF;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,kBAAkB,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAG7F;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,OAAO,GAAG,kBAAkB,GAAG,SAAS,CAcnF"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/verify/verification-deterministic.test.ts,
|
|
3
|
+
// packages/daemon/src/verify/verification-timeout-fault.test.ts,
|
|
4
|
+
// packages/daemon/src/verify/verify-evidence-unavailable.test.ts
|
|
5
|
+
// PINNED-BY-END
|
|
6
|
+
const ESC = String.fromCharCode(27);
|
|
7
|
+
/** SGR colour/style codes; the ESC byte is optional so an already-stripped `[32m` also goes. */
|
|
8
|
+
const ANSI_SGR = new RegExp(`${ESC}?\\[[0-9;]*m`, 'g');
|
|
9
|
+
/** vitest counts: `2 failed | 439 passed | 1 skipped (442)` -- segments, then a parenthesised total. */
|
|
10
|
+
const VITEST_SEGMENT = String.raw `\d+\s+(?:failed|passed|skipped|todo)`;
|
|
11
|
+
const VITEST_COUNTS = String.raw `${VITEST_SEGMENT}(?:\s*\|\s*${VITEST_SEGMENT})*\s+\(\d+\)`;
|
|
12
|
+
const VITEST_TEST_FILES = new RegExp(String.raw `^\s*Test Files\s+${VITEST_COUNTS}\s*$`);
|
|
13
|
+
const VITEST_TESTS = new RegExp(String.raw `^\s*Tests\s+${VITEST_COUNTS}\s*$`);
|
|
14
|
+
const totalsLine = (prefix, label) => new RegExp(String.raw `^\s*${prefix} ${label} \d+\s*$`);
|
|
15
|
+
/** First matcher that is satisfied wins; order is most-specific first. */
|
|
16
|
+
const SUMMARY_MATCHERS = [
|
|
17
|
+
{ required: [VITEST_TEST_FILES], optional: [VITEST_TESTS] },
|
|
18
|
+
{ required: [VITEST_TESTS], optional: [] },
|
|
19
|
+
{ required: [totalsLine('#', 'tests'), totalsLine('#', 'pass')], optional: [totalsLine('#', 'fail')] },
|
|
20
|
+
{ required: [totalsLine('ℹ', 'tests'), totalsLine('ℹ', 'pass')], optional: [totalsLine('ℹ', 'fail')] },
|
|
21
|
+
];
|
|
22
|
+
/** Index of the LAST line matching `pattern` (a re-printed summary supersedes an earlier one), or -1. */
|
|
23
|
+
function lastMatchIndex(lines, pattern) {
|
|
24
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
25
|
+
if (pattern.test(lines[i] ?? ''))
|
|
26
|
+
return i;
|
|
27
|
+
}
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
30
|
+
/** The matcher's lines (trimmed, output order) when all its required lines are present, else null. */
|
|
31
|
+
function applyMatcher(lines, matcher) {
|
|
32
|
+
const indices = [];
|
|
33
|
+
for (const pattern of matcher.required) {
|
|
34
|
+
const idx = lastMatchIndex(lines, pattern);
|
|
35
|
+
if (idx < 0)
|
|
36
|
+
return null;
|
|
37
|
+
indices.push(idx);
|
|
38
|
+
}
|
|
39
|
+
for (const pattern of matcher.optional) {
|
|
40
|
+
const idx = lastMatchIndex(lines, pattern);
|
|
41
|
+
if (idx >= 0)
|
|
42
|
+
indices.push(idx);
|
|
43
|
+
}
|
|
44
|
+
return [...new Set(indices)].sort((a, b) => a - b).map((i) => (lines[i] ?? '').trim());
|
|
45
|
+
}
|
|
46
|
+
function findInStream(tail) {
|
|
47
|
+
if (typeof tail !== 'string' || tail.length === 0)
|
|
48
|
+
return null;
|
|
49
|
+
const lines = tail.replace(ANSI_SGR, '').split(/\r\n|\n|\r/);
|
|
50
|
+
for (const matcher of SUMMARY_MATCHERS) {
|
|
51
|
+
const found = applyMatcher(lines, matcher);
|
|
52
|
+
if (found)
|
|
53
|
+
return found;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The complete test-runner summary line(s) in a run's captured output, or null.
|
|
59
|
+
*
|
|
60
|
+
* Searches stdout first, then stderr (each independently -- a summary is never
|
|
61
|
+
* assembled from lines split across the two streams).
|
|
62
|
+
*
|
|
63
|
+
* @param stdoutTail The captured stdout tail (last ~4000 chars) of the run.
|
|
64
|
+
* @param stderrTail The captured stderr tail (last ~4000 chars) of the run.
|
|
65
|
+
* @returns The runner's totals lines, trimmed and in output order; null when no
|
|
66
|
+
* complete summary is present.
|
|
67
|
+
*/
|
|
68
|
+
export function findRunnerSummary(stdoutTail, stderrTail) {
|
|
69
|
+
return findInStream(stdoutTail) ?? findInStream(stderrTail);
|
|
70
|
+
}
|
|
71
|
+
/** Text a summary line carries; a window without one cannot hold a new summary, so it is not scanned. */
|
|
72
|
+
const SUMMARY_HINT = /Test Files|Tests|[#ℹ] (?:tests|pass|fail)/;
|
|
73
|
+
/** Chars before a chunk rescanned for the hint, so a summary line split across two chunks is still seen. */
|
|
74
|
+
const HINT_OVERLAP = 200;
|
|
75
|
+
/**
|
|
76
|
+
* Track one stream's runner summary while it streams. The strategy keeps only a
|
|
77
|
+
* 4000-char tail per stream, and a multi-stage gate keeps printing after its
|
|
78
|
+
* tests (the next stage, teardown logs), so by the time the kill lands the
|
|
79
|
+
* summary may be gone from the tail. Each observation scans the not-yet-evicted
|
|
80
|
+
* buffer -- only when the new text could complete a summary -- and remembers the
|
|
81
|
+
* latest found, so a later re-printed summary still supersedes an earlier one.
|
|
82
|
+
*/
|
|
83
|
+
export function createRunnerSummaryTracker() {
|
|
84
|
+
let found = null;
|
|
85
|
+
return {
|
|
86
|
+
observe(buffer, chunk) {
|
|
87
|
+
const window = buffer.slice(-(chunk.length + HINT_OVERLAP));
|
|
88
|
+
if (!SUMMARY_HINT.test(window))
|
|
89
|
+
return;
|
|
90
|
+
const summary = findInStream(buffer);
|
|
91
|
+
if (summary)
|
|
92
|
+
found = summary;
|
|
93
|
+
},
|
|
94
|
+
latest: () => found,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** A summary's lines as one quotable span. */
|
|
98
|
+
const quoteSummary = (summary) => `"${summary.join(' / ')}"`;
|
|
99
|
+
/**
|
|
100
|
+
* The rationale for a deterministic run killed by its timeout, read from what
|
|
101
|
+
* the run's output shows.
|
|
102
|
+
*
|
|
103
|
+
* With a runner summary the kill landed after the tests had finished, so the
|
|
104
|
+
* rationale quotes it and names what actually ran out: the command had not
|
|
105
|
+
* exited within the limit. Without one it is the legacy "run did not complete"
|
|
106
|
+
* string, byte-identical. Either way the run stays a failed timeout -- this only
|
|
107
|
+
* chooses the words.
|
|
108
|
+
*
|
|
109
|
+
* A `timeoutSource` label is named right after the value (`after 600000ms (from
|
|
110
|
+
* /repo/telora.verify.json)`), because a timeout sized below the suite is fixed
|
|
111
|
+
* in whichever layer supplied it and the reader otherwise has to guess which. With
|
|
112
|
+
* no label both strings are unchanged.
|
|
113
|
+
*
|
|
114
|
+
* @param timeoutMs The timeout the run was killed at.
|
|
115
|
+
* @param stdoutTail The captured stdout tail of the killed run.
|
|
116
|
+
* @param stderrTail The captured stderr tail of the killed run.
|
|
117
|
+
* @param timeoutSource Where `timeoutMs` was read (formatVerifyConfigSource label), when known.
|
|
118
|
+
* @param observedSummary The latest summary tracked while the run streamed, used
|
|
119
|
+
* when later output evicted it from the tails (createRunnerSummaryTracker).
|
|
120
|
+
* @returns The rationale, plus the matched summary lines when one was found.
|
|
121
|
+
*/
|
|
122
|
+
export function describeTimeout(timeoutMs, stdoutTail, stderrTail, timeoutSource, observedSummary) {
|
|
123
|
+
const summary = findRunnerSummary(stdoutTail, stderrTail)
|
|
124
|
+
?? (observedSummary && observedSummary.length > 0 ? [...observedSummary] : null);
|
|
125
|
+
const limit = timeoutSource ? `${timeoutMs}ms (from ${timeoutSource})` : `${timeoutMs}ms`;
|
|
126
|
+
if (!summary) {
|
|
127
|
+
return { rationale: `deterministic check timed out after ${limit} (SIGKILL) -- run did not complete` };
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
rationale: `deterministic check killed by the timeout after ${limit} (SIGKILL) -- the test runner had ` +
|
|
131
|
+
`already printed its summary (${quoteSummary(summary)}), but the command had not exited within the limit`,
|
|
132
|
+
runnerSummary: summary,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The instrument-fault hold for a timed-out deterministic run, with its
|
|
137
|
+
* `missing` evidence named from the captured output.
|
|
138
|
+
*
|
|
139
|
+
* Routing is the caller's and does not change here: a timeout is never a
|
|
140
|
+
* verdict about the work, so this is always a `deterministicFault` of kind
|
|
141
|
+
* 'timeout'. What a summary changes is the NAME of the missing evidence -- the
|
|
142
|
+
* test output is there; the clean exit is not -- so the hold stops pointing a
|
|
143
|
+
* reader at a hung test that does not exist. Prefers the summary the strategy
|
|
144
|
+
* already matched; falls back to reading the tails (a result built elsewhere).
|
|
145
|
+
*
|
|
146
|
+
* The fault also records the timeout the run applied and where it was read,
|
|
147
|
+
* taken from the run's `appliedConfig`, so the gate state (and every human
|
|
148
|
+
* surface rendered from it) names the value and the layer to change. A result
|
|
149
|
+
* that recorded no applied config leaves both out rather than guessing.
|
|
150
|
+
*
|
|
151
|
+
* @param result The timed-out run's exit code, matched summary, output tails, and applied config.
|
|
152
|
+
* @returns The hold detail, minus the `heldAt` stamp the requeue path adds.
|
|
153
|
+
*/
|
|
154
|
+
export function buildTimeoutFaultHold(result) {
|
|
155
|
+
const summary = result.runnerSummary && result.runnerSummary.length > 0
|
|
156
|
+
? result.runnerSummary
|
|
157
|
+
: findRunnerSummary(result.stdoutTail, result.stderrTail);
|
|
158
|
+
const missing = summary
|
|
159
|
+
? `a clean exit within the timeout (the test runner printed its summary -- ${quoteSummary(summary)} -- ` +
|
|
160
|
+
'but the command had not exited when it was killed)'
|
|
161
|
+
: 'complete deterministic run output (killed by timeout before completion)';
|
|
162
|
+
const applied = result.appliedConfig;
|
|
163
|
+
return {
|
|
164
|
+
missing: [missing],
|
|
165
|
+
commitsAhead: 0,
|
|
166
|
+
deterministicFault: {
|
|
167
|
+
kind: 'timeout',
|
|
168
|
+
exitCode: result.exitCode ?? null,
|
|
169
|
+
...(applied ? { timeoutMs: applied.timeoutMs, timeoutSource: applied.timeoutSource } : {}),
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The `Timeout applied: <ms>ms from <source>` line for a timeout fault that
|
|
175
|
+
* recorded its applied timeout, else null.
|
|
176
|
+
*
|
|
177
|
+
* One formatter so the team inject message and the verify_gate_unclearable
|
|
178
|
+
* escalation print the identical line: a reader who sees it in one place can grep
|
|
179
|
+
* for it in the other. Null for every other hold, so those bodies stay unchanged.
|
|
180
|
+
*/
|
|
181
|
+
export function formatTimeoutAppliedLine(fault) {
|
|
182
|
+
if (fault?.kind !== 'timeout' || typeof fault.timeoutMs !== 'number')
|
|
183
|
+
return null;
|
|
184
|
+
return `Timeout applied: ${fault.timeoutMs}ms from ${fault.timeoutSource ?? 'an unrecorded source'}`;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Read a timeout fault that recorded its applied timeout out of an untyped
|
|
188
|
+
* gate_state (FocusDeliveryInfo.gateState is `unknown`), or undefined. Defensive
|
|
189
|
+
* at every level: a legacy or foreign gate_state shape reads as "no timeout fault",
|
|
190
|
+
* never a throw.
|
|
191
|
+
*/
|
|
192
|
+
export function readTimeoutFault(gateState) {
|
|
193
|
+
if (gateState == null || typeof gateState !== 'object')
|
|
194
|
+
return undefined;
|
|
195
|
+
const held = gateState.evidenceUnavailable;
|
|
196
|
+
if (held == null || typeof held !== 'object')
|
|
197
|
+
return undefined;
|
|
198
|
+
const fault = held.deterministicFault;
|
|
199
|
+
if (fault == null || typeof fault !== 'object')
|
|
200
|
+
return undefined;
|
|
201
|
+
const f = fault;
|
|
202
|
+
if (f.kind !== 'timeout' || typeof f.timeoutMs !== 'number')
|
|
203
|
+
return undefined;
|
|
204
|
+
return {
|
|
205
|
+
kind: 'timeout',
|
|
206
|
+
exitCode: typeof f.exitCode === 'number' ? f.exitCode : null,
|
|
207
|
+
timeoutMs: f.timeoutMs,
|
|
208
|
+
...(typeof f.timeoutSource === 'string' ? { timeoutSource: f.timeoutSource } : {}),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=verification-timeout-fault.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-timeout-fault.js","sourceRoot":"","sources":["../../src/verify/verification-timeout-fault.ts"],"names":[],"mappings":"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"}
|
|
@@ -106,7 +106,11 @@ export interface VerifyGateStallEscalationText {
|
|
|
106
106
|
* Build the verify_gate_unclearable escalation body for a stalled delivery. It
|
|
107
107
|
* asserts only what the evidence shows: the repeated signature and its consecutive
|
|
108
108
|
* count on the signature path; on the iteration-cap fallback, that the stall is
|
|
109
|
-
* inferred from a count alone and the delivery may still be converging.
|
|
109
|
+
* inferred from a count alone and the delivery may still be converging. When the
|
|
110
|
+
* live hold is a timeout that recorded its applied timeout, both bodies name it
|
|
111
|
+
* and its source in an explicit line, rather than leaving it buried in the
|
|
112
|
+
* gate_state JSON -- the reader's first question about a timeout stall is which
|
|
113
|
+
* layer set the limit. Pure.
|
|
110
114
|
*/
|
|
111
115
|
export declare function buildVerifyGateStallEscalation(focus: {
|
|
112
116
|
focusId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-gate-stall.d.ts","sourceRoot":"","sources":["../../src/verify/verify-gate-stall.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"verify-gate-stall.d.ts","sourceRoot":"","sources":["../../src/verify/verify-gate-stall.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAK3D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kCAAkC,IAAI,CAAC;AAEpD,KAAK,YAAY,GAAG,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,WAAW,CAAC,CAAC;AA8BhG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAIhE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAInF;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,GAClB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,oBAAoB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,uBAAuB,EAAE;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAElF;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,MAAM,EACX,eAAe,GAAE,MAA2C,GAC3D,uBAAuB,CAYzB;AAED,MAAM,WAAW,6BAA6B;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAC7C,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,MAAM,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,WAAW,CAAC,EACrG,KAAK,EAAE,eAAe,GACrB,6BAA6B,CA2D/B"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// PINNED-BY-END
|
|
6
6
|
import { isStatusTerminal } from '../workflow/stage-classifier.js';
|
|
7
7
|
import { readRecordedSignatureRun } from './failure-signature.js';
|
|
8
|
+
import { formatTimeoutAppliedLine, readTimeoutFault } from './verification-timeout-fault.js';
|
|
8
9
|
/**
|
|
9
10
|
* Consecutive rejections carrying ONE failure signature that mark the verify gate
|
|
10
11
|
* as stalled rather than converging.
|
|
@@ -116,7 +117,11 @@ export function decideVerifyGateStall(delivery, cap, repeatThreshold = VERIFY_GA
|
|
|
116
117
|
* Build the verify_gate_unclearable escalation body for a stalled delivery. It
|
|
117
118
|
* asserts only what the evidence shows: the repeated signature and its consecutive
|
|
118
119
|
* count on the signature path; on the iteration-cap fallback, that the stall is
|
|
119
|
-
* inferred from a count alone and the delivery may still be converging.
|
|
120
|
+
* inferred from a count alone and the delivery may still be converging. When the
|
|
121
|
+
* live hold is a timeout that recorded its applied timeout, both bodies name it
|
|
122
|
+
* and its source in an explicit line, rather than leaving it buried in the
|
|
123
|
+
* gate_state JSON -- the reader's first question about a timeout stall is which
|
|
124
|
+
* layer set the limit. Pure.
|
|
120
125
|
*/
|
|
121
126
|
export function buildVerifyGateStallEscalation(focus, delivery, stall) {
|
|
122
127
|
const guards = blockingGuardNames(delivery.gateState);
|
|
@@ -129,6 +134,8 @@ export function buildVerifyGateStallEscalation(focus, delivery, stall) {
|
|
|
129
134
|
`**Delivery:** "${delivery.name}" (${delivery.id})\n` +
|
|
130
135
|
`**Status:** ${delivery.executionStatus ?? 'unknown'}\n` +
|
|
131
136
|
`**iteration_count:** ${iterationCount}\n`;
|
|
137
|
+
const timeoutLine = formatTimeoutAppliedLine(readTimeoutFault(delivery.gateState));
|
|
138
|
+
const timeoutFact = timeoutLine ? `${timeoutLine}\n` : '';
|
|
132
139
|
const footer = `**gate_state:** ${formatGateState(delivery.gateState)}\n\n` +
|
|
133
140
|
`Inspect the gate_state / last failing condition above (gate_state.verification.logPath ` +
|
|
134
141
|
`holds the full run log). See docs/runbook-verify-gate.md.`;
|
|
@@ -143,6 +150,7 @@ export function buildVerifyGateStallEscalation(focus, delivery, stall) {
|
|
|
143
150
|
`${stall.signature}. ${guardClause} The re-work between those cycles did not change ` +
|
|
144
151
|
`what fails, so another automatic cycle is unlikely to clear it.\n\n` +
|
|
145
152
|
facts +
|
|
153
|
+
timeoutFact +
|
|
146
154
|
`**failure signature:** ${stall.signature} (${stall.consecutive} consecutive)\n` +
|
|
147
155
|
footer,
|
|
148
156
|
whatWasTried: `The delivery was re-queued for re-work after each rejection; its last ${stall.consecutive} ` +
|
|
@@ -159,6 +167,7 @@ export function buildVerifyGateStallEscalation(focus, delivery, stall) {
|
|
|
159
167
|
`unparseable verification log), so this stall is inferred from the cycle count alone -- the ` +
|
|
160
168
|
`delivery may still be converging through different failures.\n\n` +
|
|
161
169
|
facts +
|
|
170
|
+
timeoutFact +
|
|
162
171
|
footer,
|
|
163
172
|
whatWasTried: `The delivery re-entered verify_failed ${iterationCount} times; each verify pass was ` +
|
|
164
173
|
`rejected by the gate and re-queued.`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-gate-stall.js","sourceRoot":"","sources":["../../src/verify/verify-gate-stall.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,oFAAoF;AACpF,6EAA6E;AAC7E,uDAAuD;AACvD,gBAAgB;AA4BhB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"verify-gate-stall.js","sourceRoot":"","sources":["../../src/verify/verify-gate-stall.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,8 +6,16 @@
|
|
|
6
6
|
* than once is a no-op, so a spawn that fires both `'error'` and `'close'` cannot
|
|
7
7
|
* under-count the hold. Acquire BEFORE the spawn and release in a path that runs
|
|
8
8
|
* on every exit (a `finish`/`finally`), so the hold can never outlive the process.
|
|
9
|
+
*
|
|
10
|
+
* `deliveryId` names the delivery the verify is judging, so the actionable-work
|
|
11
|
+
* predicate can withhold exactly that delivery (see {@link verifyHeldDeliveryIds}).
|
|
12
|
+
*/
|
|
13
|
+
export declare function acquireVerifyHold(worktreePath: string, deliveryId?: string): () => void;
|
|
14
|
+
/**
|
|
15
|
+
* The delivery ids the in-flight verifies in `worktreePath` are judging. Empty
|
|
16
|
+
* when the worktree is not held, or when its holds named no delivery.
|
|
9
17
|
*/
|
|
10
|
-
export declare function
|
|
18
|
+
export declare function verifyHeldDeliveryIds(worktreePath: string): ReadonlySet<string>;
|
|
11
19
|
/**
|
|
12
20
|
* True iff a verify command is currently running in `worktreePath` (the hold is
|
|
13
21
|
* held). Consulted by every daemon-authored commit path before it stages/commits,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-interlock.d.ts","sourceRoot":"","sources":["../../src/worktree/verify-interlock.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"verify-interlock.d.ts","sourceRoot":"","sources":["../../src/worktree/verify-interlock.ts"],"names":[],"mappings":"AAiFA;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI,CAkBvF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAE/E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,gDAAgD;AAChD,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAED,2EAA2E;AAC3E,MAAM,WAAW,6BAA6B;IAC5C,6EAA6E;IAC7E,qBAAqB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CAC3D;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,CAAC,CAAC,IAChC;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAClB;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC;AAEnC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iCAAiC,CAAC,CAAC,EACjD,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,CAAC,EACf,IAAI,GAAE,6BAAkC,GACvC,sBAAsB,CAAC,CAAC,CAAC,CAM3B"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
2
|
// Pinned by: packages/daemon/src/directive/directive-verify-hold-disposal-convergence.test.ts,
|
|
3
3
|
// packages/daemon/src/directive/directive-verify-hold-gate.test.ts,
|
|
4
|
-
// packages/daemon/src/focus/pending-spawn-verify-hold.test.ts
|
|
4
|
+
// packages/daemon/src/focus/pending-spawn-verify-hold.test.ts,
|
|
5
|
+
// packages/daemon/src/verify/verification-checkout.test.ts
|
|
5
6
|
// PINNED-BY-END
|
|
6
7
|
/**
|
|
7
8
|
* Per-worktree verify interlock.
|
|
@@ -45,6 +46,27 @@ const log = createLogger({ module: 'worktree' });
|
|
|
45
46
|
* releases.
|
|
46
47
|
*/
|
|
47
48
|
const holds = new Map();
|
|
49
|
+
/**
|
|
50
|
+
* Which deliveries each hold is judging: canonical worktree path -> delivery id
|
|
51
|
+
* -> count of in-flight verifies for it. Every delivery of a focus shares the
|
|
52
|
+
* focus worktree, so the path alone cannot say WHICH delivery a verify is judging
|
|
53
|
+
* -- and "held" must withhold only that delivery from team execution, never a
|
|
54
|
+
* sibling carrying open work the team could fix (f1, 2026-09-13). A hold acquired
|
|
55
|
+
* with no delivery id (a caller that judges none) records nothing here.
|
|
56
|
+
*/
|
|
57
|
+
const heldDeliveries = new Map();
|
|
58
|
+
function adjustDeliveryHold(key, deliveryId, delta) {
|
|
59
|
+
const byDelivery = heldDeliveries.get(key) ?? new Map();
|
|
60
|
+
const next = (byDelivery.get(deliveryId) ?? 0) + delta;
|
|
61
|
+
if (next <= 0)
|
|
62
|
+
byDelivery.delete(deliveryId);
|
|
63
|
+
else
|
|
64
|
+
byDelivery.set(deliveryId, next);
|
|
65
|
+
if (byDelivery.size === 0)
|
|
66
|
+
heldDeliveries.delete(key);
|
|
67
|
+
else
|
|
68
|
+
heldDeliveries.set(key, byDelivery);
|
|
69
|
+
}
|
|
48
70
|
/** Canonicalize a worktree path so verify cwd and commit worktreePath match. */
|
|
49
71
|
function keyFor(worktreePath) {
|
|
50
72
|
try {
|
|
@@ -62,11 +84,16 @@ function keyFor(worktreePath) {
|
|
|
62
84
|
* than once is a no-op, so a spawn that fires both `'error'` and `'close'` cannot
|
|
63
85
|
* under-count the hold. Acquire BEFORE the spawn and release in a path that runs
|
|
64
86
|
* on every exit (a `finish`/`finally`), so the hold can never outlive the process.
|
|
87
|
+
*
|
|
88
|
+
* `deliveryId` names the delivery the verify is judging, so the actionable-work
|
|
89
|
+
* predicate can withhold exactly that delivery (see {@link verifyHeldDeliveryIds}).
|
|
65
90
|
*/
|
|
66
|
-
export function acquireVerifyHold(worktreePath) {
|
|
91
|
+
export function acquireVerifyHold(worktreePath, deliveryId) {
|
|
67
92
|
const key = keyFor(worktreePath);
|
|
68
93
|
holds.set(key, (holds.get(key) ?? 0) + 1);
|
|
69
|
-
|
|
94
|
+
if (deliveryId)
|
|
95
|
+
adjustDeliveryHold(key, deliveryId, 1);
|
|
96
|
+
log.debug(`verify-interlock: hold acquired for ${key} (depth ${holds.get(key)}${deliveryId ? `, delivery ${deliveryId}` : ''})`);
|
|
70
97
|
let released = false;
|
|
71
98
|
return () => {
|
|
72
99
|
if (released)
|
|
@@ -79,9 +106,18 @@ export function acquireVerifyHold(worktreePath) {
|
|
|
79
106
|
else {
|
|
80
107
|
holds.set(key, next);
|
|
81
108
|
}
|
|
109
|
+
if (deliveryId)
|
|
110
|
+
adjustDeliveryHold(key, deliveryId, -1);
|
|
82
111
|
log.debug(`verify-interlock: hold released for ${key} (depth ${next <= 0 ? 0 : next})`);
|
|
83
112
|
};
|
|
84
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* The delivery ids the in-flight verifies in `worktreePath` are judging. Empty
|
|
116
|
+
* when the worktree is not held, or when its holds named no delivery.
|
|
117
|
+
*/
|
|
118
|
+
export function verifyHeldDeliveryIds(worktreePath) {
|
|
119
|
+
return new Set(heldDeliveries.get(keyFor(worktreePath))?.keys() ?? []);
|
|
120
|
+
}
|
|
85
121
|
/**
|
|
86
122
|
* True iff a verify command is currently running in `worktreePath` (the hold is
|
|
87
123
|
* held). Consulted by every daemon-authored commit path before it stages/commits,
|
|
@@ -95,6 +131,7 @@ export function isWorktreeUnderVerify(worktreePath) {
|
|
|
95
131
|
/** Test seam: clear all holds between cases. */
|
|
96
132
|
export function __resetVerifyHolds() {
|
|
97
133
|
holds.clear();
|
|
134
|
+
heldDeliveries.clear();
|
|
98
135
|
}
|
|
99
136
|
/**
|
|
100
137
|
* The ONE boundary every daemon path that REWRITES an existing worktree passes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-interlock.js","sourceRoot":"","sources":["../../src/worktree/verify-interlock.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,+FAA+F;AAC/F,sEAAsE;AACtE,
|
|
1
|
+
{"version":3,"file":"verify-interlock.js","sourceRoot":"","sources":["../../src/worktree/verify-interlock.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telora/daemon",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.104",
|
|
4
4
|
"description": "Agent orchestration daemon for Telora - spawns and manages Claude Code instances",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"//testRunner": "Uses Node.js built-in test runner (node:test) via tsx for TypeScript support. The root package uses Vitest; this is a deliberate choice for the daemon package to avoid framework dependencies. Test files live beside the modules they exercise (owner-directory layout); the runner discovers them recursively.",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@telora/daemon-core": "^0.2.97",
|
|
41
|
-
"@telora/mcp-products": "^0.22.
|
|
41
|
+
"@telora/mcp-products": "^0.22.148",
|
|
42
42
|
"commander": "^14.0.3",
|
|
43
43
|
"yaml": "^2.4.0",
|
|
44
44
|
"zod": "^4.3.6"
|