@ai-dossier/sched 0.53.0 → 0.54.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/README.md CHANGED
@@ -37,6 +37,7 @@ ai-dossier sched stop --batch b1 # terminate every batch agent (incl. parallel
37
37
  ai-dossier sched abandon --issue 42 --reason "operator abort"
38
38
  ai-dossier sched abandon --batch b1 # dissolve; every unshipped member — parked and validated ones included — requeues as full-cycle
39
39
  ai-dossier sched requeue --issue 42 # a PARKED batch member (evicted / handed-back) → full-cycle from its member branch, on the batch profile (#810)
40
+ ai-dossier sched attach-pr --batch b1 4270 # record a blocked batch's own MERGED PR by hand (resolves pr-detect-ambiguous, #824)
40
41
  ai-dossier sched stats --issues 4..9 # per-issue tokens/cost from ~/.dossier/runs.jsonl (#524)
41
42
  ai-dossier sched stats --batch b1 --project owner-repo # batch member/tail/report/fix costs from raw dispatch logs (#564)
42
43
  ```
@@ -1133,8 +1134,9 @@ import {
1133
1134
  createSpawnDeps, // real detached-spawn process I/O
1134
1135
  createExecGroundTruth, // runstate/gh/git ground truth via subprocesses (injectable exec);
1135
1136
  // since #468 also gh pr view PR state + setup info from comments;
1136
- // since #789 also mergedPrForBranch when constructed with a
1137
- // verified `repo` (resolveProjectRepo)
1137
+ // since #789 also mergedPrForBranch, and since #824
1138
+ // batchPrCandidate, when constructed with a verified `repo`
1139
+ // (resolveProjectRepo) — which also pins prState with -R
1138
1140
  resolveDispatch, // config → resolved command/prompt/report-prompt/tier-models/timers/
1139
1141
  // per-tier spawn specs (tiers — #527)
1140
1142
  buildTierCommand, // resolved dispatch + tier + issue → argv, using that tier's OWN
@@ -1168,6 +1170,16 @@ import {
1168
1170
  type MergedPrLookup, // { kind: 'found', pr, mergedAt } | { kind: 'none' } |
1169
1171
  // { kind: 'ambiguous', matches } — GroundTruth.mergedPrForBranch's
1170
1172
  // own return type (#789)
1173
+ checkBatchPrCandidate, // the #789 candidate checks (fork/head/base/merged/created-after)
1174
+ // → { ok: true, pr, mergedAt } | { ok: false, check } — shared by
1175
+ // parseMergedPrListJson and attachBatchPr (#824)
1176
+ type BatchPrCheck, // which check failed: fork|head|base|unmerged|created-before-batch|number
1177
+ parsePrCandidateJson, // gh pr view --json → the PR object (GroundTruth.batchPrCandidate, #824)
1178
+ attachBatchPr, // sched attach-pr: verify an operator-named PR, record batch.pr,
1179
+ // clear the pr-detect-ambiguous streak, journal pr-attached (#824)
1180
+ type AttachPrDeps, // { store, journal, groundTruth (repo-verified), repo? }
1181
+ type AttachPrResult, // { outcome: 'attached', pr, mergedAt, clearedAmbiguousTicks } |
1182
+ // { outcome: 'already-attached', pr }
1171
1183
  parseSetupInfo, // gh issue view --json comments → teardown inputs
1172
1184
  runTeardown, // #468 script teardown for a merged unit (pool return / worktree remove)
1173
1185
  isSafeWorktree, // worktree-path containment check (CWE-22)
@@ -1547,7 +1559,8 @@ predicate in `anchor-close.ts`) over `blocked`/`done` batches touched within the
1547
1559
  batch that same tick is the ambiguity journaled (`pr-detect-ambiguous`, deduped like
1548
1560
  `pr-watch-failed`: once per `blocked` stretch, re-announced every
1549
1561
  `JOURNAL_DEDUP_REANNOUNCE_TICKS`; a batch that leaves `blocked` any way — this
1550
- reconcile's own success, `sched resume --batch`, `sched abandon` — clears the marker,
1562
+ reconcile's own success, `sched resume --batch`, `sched abandon` — or `sched
1563
+ attach-pr` recording the batch's PR clears the marker,
1551
1564
  so a later re-block never inherits a stale streak). A batch that still reconciles via
1552
1565
  `commits-in-base`/`members-closed` does so without a PR and finishes inline, same as
1553
1566
  before #789. On a match, `batch.pr` is recorded as part of the SAME `blocked →
@@ -1557,9 +1570,34 @@ predicate in `anchor-close.ts`) over `blocked`/`done` batches touched within the
1557
1570
  per-issue-only despite the section's origin) dispatches the report agent exactly as if the
1558
1571
  fleet had opened the PR itself, and `sched status`'s existing `pr` column shows it.
1559
1572
  Recording `batch.pr` never closes the anchor by itself — that stays #768's own
1560
- evidence-gated `reconcileAnchorClosure`, unchanged. An ambiguous match with no
1561
- explicit way to resolve it today (#824 tracks a `sched attach-pr` verb) still lets the
1562
- batch reconcile via its other evidence, or falls to `sched abandon --batch`.
1573
+ evidence-gated `reconcileAnchorClosure`, unchanged. An ambiguous match still lets the
1574
+ batch reconcile via its other evidence; otherwise the operator resolves it with
1575
+ `sched attach-pr` (below), or falls to `sched abandon --batch`.
1576
+ - **`sched attach-pr --batch <id> <pr>` (#824).** The operator's explicit, auditable
1577
+ answer to `pr-detect-ambiguous` — and the scripted form of the `batch-integrate`
1578
+ dossier's manual-recovery step that hand-records a batch PR (Step 6b of that registry
1579
+ dossier; `<pr>` also accepts `#4270`). Positive evidence only: the project repository is verified first
1580
+ (`resolveProjectRepo`; unverifiable → refused before any PR is read), the PR is read
1581
+ with `gh pr view <pr> -R <owner/name>` (`GroundTruth.batchPrCandidate`, repo-gated like
1582
+ `mergedPrForBranch`), and it must pass the SAME candidate checks #789's automatic path
1583
+ applies (`checkBatchPrCandidate`: not a fork, head = `batch.branch`, base =
1584
+ `batch.base_branch`, a real merge timestamp, created at or after the batch) plus an
1585
+ explicit `state == MERGED` and a positively-read `isCrossRepository == false`. Any
1586
+ mismatch, a failed or unusable gh read, a batch that is not `blocked` (or has no
1587
+ branch), or a batch whose `batch.pr` is already a DIFFERENT PR refuses with a message
1588
+ naming why — nothing written, nothing journaled; the same PR already recorded is a
1589
+ no-op. There is no `--force`: every check reads a field `gh pr view` always returns.
1590
+ On success `batch.pr` is recorded and the `pr_detect_ambiguous_*` streak cleared in
1591
+ one locked write (`updated_at` is touched, which re-arms the 7-day reconcile window),
1592
+ journaled as its own `pr-attached` event — never `stale-failure-reconciled` /
1593
+ `pr_detected`, so `events.jsonl` always tells operator from automatic apart. Nothing
1594
+ else happens in the command: the next tick's stale-blocked reconcile settles the batch
1595
+ on its ordinary `pr-merged` evidence (report + teardown follow; that read, `prState`,
1596
+ is `-R`-pinned too whenever the engine's repo is verified), and the anchor closes only
1597
+ through `reconcileAnchorClosure`, which never reads `batch.pr`. A batch with a surviving
1598
+ member that was never dispatched is refused too — the reconcile would never settle it
1599
+ on PR evidence. `--json` emits `{batch, repo, outcome: "attached", pr, mergedAt,
1600
+ clearedAmbiguousTicks}` or `{batch, repo, outcome: "already-attached", pr}`.
1563
1601
 
1564
1602
  Everything else is surfaced, never closed: `sched status --anchors` (opt-in; `status`
1565
1603
  makes no GitHub call without it) lists each still-open anchor of a batch no longer in
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `sched attach-pr --batch <id> <pr>` (#824): the operator's explicit, auditable
3
+ * way to record `batch.pr` when #789's automatic detection refused to guess
4
+ * (`pr-detect-ambiguous` — two or more merged PRs share the batch branch), and
5
+ * the scripted form of the `batch-integrate` dossier's manual-recovery step
6
+ * that hand-records a batch PR (the registry dossier's Step 6b).
7
+ *
8
+ * Trust boundary: the operator's word is not evidence. The named PR must pass
9
+ * the SAME candidate checks #789's automatic path applies
10
+ * (`checkBatchPrCandidate`) plus an explicit `state === 'MERGED'` and a
11
+ * positively-read same-repo flag, read through a ground truth pinned with `-R`
12
+ * to the verified project repository — absent that pin, nothing is read and
13
+ * nothing is recorded. There is no `--force`: every check reads a field
14
+ * `gh pr view` always returns, so an unreadable one refuses rather than
15
+ * inviting an override.
16
+ *
17
+ * Recording is ALL this does: `batch.pr` is set (and any
18
+ * `pr_detect_ambiguous_*` streak cleared) under the store lock, with no status
19
+ * transition and no GitHub write. The next engine tick's
20
+ * `reconcileStaleBlockedBatches` then settles the batch on its ordinary
21
+ * `pr-merged` evidence, and the anchor closes (or not) only through #768's own
22
+ * evidence-gated `reconcileAnchorClosure` — which never reads `batch.pr`.
23
+ */
24
+ import type { GroundTruth } from './groundtruth';
25
+ import type { Journal } from './journal';
26
+ import type { SchedStore } from './persist';
27
+ export interface AttachPrDeps {
28
+ store: SchedStore;
29
+ journal: Journal;
30
+ /** Must come from `createExecGroundTruth` with a verified `repo` — else `batchPrCandidate` is absent and the attach refuses. */
31
+ groundTruth: GroundTruth;
32
+ /** The verified `owner/name` the ground truth is pinned to — named in refusals and the journal line. */
33
+ repo?: string;
34
+ }
35
+ export type AttachPrResult = {
36
+ outcome: 'attached';
37
+ pr: number;
38
+ mergedAt: string;
39
+ /** Ticks of the `pr-detect-ambiguous` streak this attach cleared (0 when none was open). */
40
+ clearedAmbiguousTicks: number;
41
+ } | {
42
+ outcome: 'already-attached';
43
+ pr: number;
44
+ };
45
+ /**
46
+ * Record an operator-named PR as `batch.pr` after verifying it (see the module
47
+ * comment). Throws `SchedNotFoundError` — nothing written, nothing journaled —
48
+ * on every refusal; returns `already-attached` (also nothing written) when the
49
+ * same PR is already recorded.
50
+ */
51
+ export declare function attachBatchPr(deps: AttachPrDeps, batchId: string, pr: number, now?: Date): AttachPrResult;
52
+ //# sourceMappingURL=attach-pr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach-pr.d.ts","sourceRoot":"","sources":["../src/attach-pr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAI5C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,gIAAgI;IAChI,WAAW,EAAE,WAAW,CAAC;IACzB,wGAAwG;IACxG,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,cAAc,GACtB;IACE,OAAO,EAAE,UAAU,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,qBAAqB,EAAE,MAAM,CAAC;CAC/B,GACD;IAAE,OAAO,EAAE,kBAAkB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AA6HhD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,EACV,GAAG,GAAE,IAAiB,GACrB,cAAc,CAqEhB"}
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ /**
3
+ * `sched attach-pr --batch <id> <pr>` (#824): the operator's explicit, auditable
4
+ * way to record `batch.pr` when #789's automatic detection refused to guess
5
+ * (`pr-detect-ambiguous` — two or more merged PRs share the batch branch), and
6
+ * the scripted form of the `batch-integrate` dossier's manual-recovery step
7
+ * that hand-records a batch PR (the registry dossier's Step 6b).
8
+ *
9
+ * Trust boundary: the operator's word is not evidence. The named PR must pass
10
+ * the SAME candidate checks #789's automatic path applies
11
+ * (`checkBatchPrCandidate`) plus an explicit `state === 'MERGED'` and a
12
+ * positively-read same-repo flag, read through a ground truth pinned with `-R`
13
+ * to the verified project repository — absent that pin, nothing is read and
14
+ * nothing is recorded. There is no `--force`: every check reads a field
15
+ * `gh pr view` always returns, so an unreadable one refuses rather than
16
+ * inviting an override.
17
+ *
18
+ * Recording is ALL this does: `batch.pr` is set (and any
19
+ * `pr_detect_ambiguous_*` streak cleared) under the store lock, with no status
20
+ * transition and no GitHub write. The next engine tick's
21
+ * `reconcileStaleBlockedBatches` then settles the batch on its ordinary
22
+ * `pr-merged` evidence, and the anchor closes (or not) only through #768's own
23
+ * evidence-gated `reconcileAnchorClosure` — which never reads `batch.pr`.
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.attachBatchPr = attachBatchPr;
27
+ const batch_dispatch_1 = require("./batch-dispatch");
28
+ const groundtruth_1 = require("./groundtruth");
29
+ const journal_1 = require("./journal");
30
+ const state_1 = require("./state");
31
+ const types_1 = require("./types");
32
+ /**
33
+ * Why each shared candidate check refuses, in operator words. `number` and
34
+ * `fork` are pre-empted by `verifyCandidate`'s own stricter checks and exist
35
+ * for the record's exhaustiveness (`fork` is also used directly there).
36
+ */
37
+ const CHECK_REFUSALS = {
38
+ fork: () => 'it is from a fork (isCrossRepository=true), not the project repository',
39
+ head: (pr, batch) => `its head is ${JSON.stringify(pr.headRefName)}, not the batch branch ${JSON.stringify(batch.branch)}`,
40
+ base: (pr, batch) => `its base is ${JSON.stringify(pr.baseRefName)}, not the batch base ${JSON.stringify(batch.base_branch)}`,
41
+ unmerged: () => 'it has no merge timestamp — not merged',
42
+ 'created-before-batch': (pr, batch) => `it was created ${JSON.stringify(pr.createdAt)}, before the batch itself (${batch.created_at}) — a PR from an earlier use of the branch`,
43
+ number: () => 'gh returned no usable PR number',
44
+ };
45
+ /** The one refusal shape every PR-level refusal uses. */
46
+ function refusal(batchId, pr, why) {
47
+ return new types_1.SchedNotFoundError(`Refusing to attach PR #${pr} to batch ${batchId}: ${why}`);
48
+ }
49
+ /**
50
+ * The ledger-only preconditions, re-run under the lock on fresh state.
51
+ * Throws `SchedNotFoundError` naming why on every refusal.
52
+ */
53
+ function precheck(state, batchId, pr) {
54
+ const batch = (0, state_1.findBatch)(state, batchId);
55
+ if (!batch)
56
+ throw new types_1.SchedNotFoundError(`Batch not found: ${batchId}`);
57
+ if (batch.pr === pr)
58
+ return { noop: true };
59
+ if (batch.pr !== null) {
60
+ throw new types_1.SchedNotFoundError(`Batch ${batchId} already has PR #${batch.pr} recorded — refusing to replace it with #${pr}`);
61
+ }
62
+ if (batch.status !== 'blocked') {
63
+ throw new types_1.SchedNotFoundError(`Batch ${batchId} is ${batch.status} — attach-pr only applies to a blocked batch (the state whose merge evidence reconcileStaleBlockedBatches settles from a recorded PR)`);
64
+ }
65
+ const { branch } = batch;
66
+ if (branch === null) {
67
+ throw new types_1.SchedNotFoundError(`Batch ${batchId} has no branch recorded — nothing to check PR #${pr}'s head against`);
68
+ }
69
+ // The reconcile never settles a batch with a surviving member that was never
70
+ // dispatched (it would call an issue `done` that shipped nothing), so a PR
71
+ // recorded here would sit unused forever — refuse instead of reporting a
72
+ // reconcile that will not happen.
73
+ const undispatched = (0, batch_dispatch_1.undispatchedSurvivingMembers)(state, batch);
74
+ if (undispatched.length > 0) {
75
+ throw new types_1.SchedNotFoundError(`Batch ${batchId} has member(s) ${undispatched.map((m) => `#${m}`).join(', ')} that were never dispatched — the stale-blocked reconcile will not settle it on PR evidence (it would mark unshipped work done); \`sched abandon --batch ${batchId}\` requeues them instead`);
76
+ }
77
+ return { noop: false, batch: { ...batch, branch } };
78
+ }
79
+ /** Refuse unless `candidate` is provably the batch's own merged PR `pr`; returns its merge time. */
80
+ function verifyCandidate(candidate, batch, pr) {
81
+ if (candidate.number !== pr) {
82
+ throw refusal(batch.id, pr, `gh answered for PR ${JSON.stringify(candidate.number)}, not #${pr}`);
83
+ }
84
+ if (candidate.state !== 'MERGED') {
85
+ throw refusal(batch.id, pr, `it is ${JSON.stringify(candidate.state)}, not MERGED`);
86
+ }
87
+ // Positive evidence only: `checkBatchPrCandidate` treats a MISSING flag as
88
+ // "not a fork" (its #789 list convention); an operator-named PR must be
89
+ // read as same-repo explicitly.
90
+ if (candidate.isCrossRepository !== false) {
91
+ throw refusal(batch.id, pr, candidate.isCrossRepository === true
92
+ ? CHECK_REFUSALS.fork(candidate, batch)
93
+ : 'gh did not report whether it is from the project repository or a fork (isCrossRepository unreadable)');
94
+ }
95
+ const verdict = (0, groundtruth_1.checkBatchPrCandidate)(candidate, batch.branch, batch.base_branch, Date.parse(batch.created_at));
96
+ if (!verdict.ok)
97
+ throw refusal(batch.id, pr, CHECK_REFUSALS[verdict.check](candidate, batch));
98
+ return verdict.mergedAt;
99
+ }
100
+ /** The `pr-attached` journal line's `detail`. */
101
+ function attachedDetail(prRef, mergedAt, batch, clearedAmbiguousTicks) {
102
+ return (`operator attached ${prRef} (MERGED ${mergedAt}, head=${batch.branch}, base=${batch.base_branch}, same repository) — recorded batch.pr` +
103
+ (clearedAmbiguousTicks > 0
104
+ ? `; cleared the pr-detect-ambiguous streak (${clearedAmbiguousTicks} tick(s))`
105
+ : '') +
106
+ '; the next engine tick reconciles the batch on pr-merged evidence — the anchor is left to #768');
107
+ }
108
+ /**
109
+ * Record an operator-named PR as `batch.pr` after verifying it (see the module
110
+ * comment). Throws `SchedNotFoundError` — nothing written, nothing journaled —
111
+ * on every refusal; returns `already-attached` (also nothing written) when the
112
+ * same PR is already recorded.
113
+ */
114
+ function attachBatchPr(deps, batchId, pr, now = new Date()) {
115
+ if (!Number.isInteger(pr) || pr <= 0) {
116
+ throw new types_1.SchedNotFoundError(`PR must be a positive integer, got ${pr}`);
117
+ }
118
+ const first = precheck(deps.store.load(), batchId, pr);
119
+ if (first.noop)
120
+ return { outcome: 'already-attached', pr };
121
+ const repo = deps.repo ?? 'the project repository';
122
+ const read = deps.groundTruth.batchPrCandidate;
123
+ if (read === undefined) {
124
+ throw refusal(batchId, pr, "the project's GitHub repository is not verified, so the PR cannot be read with a pinned -R — run from the project's own checkout (or check --project)");
125
+ }
126
+ const candidate = read(pr);
127
+ if (candidate === undefined) {
128
+ throw refusal(batchId, pr, `could not read it from ${repo} (\`gh pr view ${pr} -R ${repo}\` failed or returned an unusable payload — see the stderr line above) — nothing recorded`);
129
+ }
130
+ verifyCandidate(candidate, first.batch, pr);
131
+ // The GitHub read ran outside the lock: re-run the ledger checks AND the
132
+ // candidate checks against the fresh batch, so a batch that left `blocked`,
133
+ // had a PR recorded, or changed its branch/base meanwhile is refused rather
134
+ // than written over.
135
+ const applied = deps.store.withLock((s) => {
136
+ const fresh = precheck(s, batchId, pr);
137
+ if (fresh.noop)
138
+ return { state: s, result: null };
139
+ const mergedAt = verifyCandidate(candidate, fresh.batch, pr);
140
+ // `updated_at` IS touched (unlike the dedup writes): an operator attach is
141
+ // real activity, and it re-arms the stale-blocked reconcile window
142
+ // (measured from `updated_at`), so the PR is acted on even when the batch
143
+ // had been blocked longer than that window.
144
+ return {
145
+ state: (0, state_1.patchBatch)(s, batchId, { pr, ...state_1.CLEARED_PR_DETECT_AMBIGUOUS_FIELDS }, now),
146
+ result: {
147
+ batch: fresh.batch,
148
+ mergedAt,
149
+ clearedAmbiguousTicks: fresh.batch.pr_detect_ambiguous_ticks,
150
+ },
151
+ };
152
+ });
153
+ if (applied === null)
154
+ return { outcome: 'already-attached', pr };
155
+ deps.journal.append((0, journal_1.unitEvent)('pr-attached', `batch:${batchId}`, {
156
+ pr,
157
+ mergedAt: applied.mergedAt,
158
+ branch: applied.batch.branch,
159
+ detail: attachedDetail(deps.repo !== undefined ? `${deps.repo}#${pr}` : `PR #${pr}`, applied.mergedAt, applied.batch, applied.clearedAmbiguousTicks),
160
+ }), now);
161
+ return {
162
+ outcome: 'attached',
163
+ pr,
164
+ mergedAt: applied.mergedAt,
165
+ clearedAmbiguousTicks: applied.clearedAmbiguousTicks,
166
+ };
167
+ }
168
+ //# sourceMappingURL=attach-pr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach-pr.js","sourceRoot":"","sources":["../src/attach-pr.ts"],"names":[],"mappings":"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"}
@@ -250,6 +250,14 @@ export declare function resumeBlockedGate(deps: BatchDispatchDeps, config: Sched
250
250
  * ~375k tokens, stopped only by hand).
251
251
  */
252
252
  export declare const MAX_BATCH_AGENT_RESPAWNS = 2;
253
+ /**
254
+ * The surviving members `everySurvivingMemberDispatched` finds NOT yet
255
+ * dispatched — the members that keep a blocked batch out of
256
+ * `reconcileStaleBlockedBatches` (#686). Exported for `sched attach-pr`
257
+ * (#824), which refuses to record a PR the reconcile would then never act on,
258
+ * and names these members in the refusal.
259
+ */
260
+ export declare function undispatchedSurvivingMembers(state: SchedState, batch: BatchEntry): number[];
253
261
  /**
254
262
  * #686: reconcile a `blocked` batch whose work demonstrably shipped anyway.
255
263
  * `reconcilePrWatch` above only watches `awaiting-merge` batches, and
@@ -1 +1 @@
1
- {"version":3,"file":"batch-dispatch.d.ts","sourceRoot":"","sources":["../src/batch-dispatch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"batch-dispatch.d.ts","sourceRoot":"","sources":["../src/batch-dispatch.ts"],"names":[],"mappings":"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"}
@@ -100,6 +100,7 @@ exports.MAX_BATCH_AGENT_RESPAWNS = exports.tailMembersRefusal = void 0;
100
100
  exports.memberBranchFor = memberBranchFor;
101
101
  exports.evictMemberAndContinue = evictMemberAndContinue;
102
102
  exports.resumeBlockedGate = resumeBlockedGate;
103
+ exports.undispatchedSurvivingMembers = undispatchedSurvivingMembers;
103
104
  exports.reconcileStaleBlockedBatches = reconcileStaleBlockedBatches;
104
105
  exports.memberDispatchModeFor = memberDispatchModeFor;
105
106
  exports.runBatchTick = runBatchTick;
@@ -3141,7 +3142,18 @@ const MEMBER_RECONCILE_CHAIN = {
3141
3142
  * and the branch is exactly what the merge evidence below covers.
3142
3143
  */
3143
3144
  function everySurvivingMemberDispatched(state, batch) {
3145
+ return undispatchedSurvivingMembers(state, batch).length === 0;
3146
+ }
3147
+ /**
3148
+ * The surviving members `everySurvivingMemberDispatched` finds NOT yet
3149
+ * dispatched — the members that keep a blocked batch out of
3150
+ * `reconcileStaleBlockedBatches` (#686). Exported for `sched attach-pr`
3151
+ * (#824), which refuses to record a PR the reconcile would then never act on,
3152
+ * and names these members in the refusal.
3153
+ */
3154
+ function undispatchedSurvivingMembers(state, batch) {
3144
3155
  const evicted = new Set(batch.evictions.map((e) => e.issue));
3156
+ const undispatched = [];
3145
3157
  for (const issue of batch.members) {
3146
3158
  const entry = (0, state_1.findEntry)(state, issue);
3147
3159
  if (!entry)
@@ -3159,9 +3171,9 @@ function everySurvivingMemberDispatched(state, batch) {
3159
3171
  // completion — everything the entry can reach afterwards means the
3160
3172
  // member was dispatched and its work is on the branch.
3161
3173
  if (!DISPATCHED_MEMBER_STATUSES.has(entry.status))
3162
- return false;
3174
+ undispatched.push(issue);
3163
3175
  }
3164
- return true;
3176
+ return undispatched;
3165
3177
  }
3166
3178
  /** Slot-line statuses that prove a member was actually dispatched (see above). */
3167
3179
  const DISPATCHED_MEMBER_STATUSES = new Set([
@@ -3228,6 +3240,26 @@ function clearPrDetectAmbiguous(deps, batch, now) {
3228
3240
  */
3229
3241
  function recordPrDetectAmbiguous(deps, batch, matches, now) {
3230
3242
  const { since, ticks, announce } = advanceBatchDedupStreak(batch.pr_detect_ambiguous_reason, batch.pr_detect_ambiguous_since, batch.pr_detect_ambiguous_ticks, state_1.PR_DETECT_AMBIGUOUS_REASON, now);
3243
+ // #824 review: the lookup above ran outside the lock — an operator's
3244
+ // `sched attach-pr` (or a resume/abandon) may have landed meanwhile. Write
3245
+ // the streak (and journal it) only while the batch is STILL blocked with no
3246
+ // PR; never rewrite the marker an attach just cleared, nor tell the
3247
+ // operator to run `sched attach-pr` after they already did.
3248
+ const written = deps.store.withLock((s) => {
3249
+ const b = (0, state_1.findBatch)(s, batch.id);
3250
+ if (!b || b.status !== 'blocked' || b.pr !== null)
3251
+ return { state: s, result: false };
3252
+ return {
3253
+ state: (0, state_1.patchBatch)(s, batch.id, {
3254
+ pr_detect_ambiguous_reason: state_1.PR_DETECT_AMBIGUOUS_REASON,
3255
+ pr_detect_ambiguous_since: since,
3256
+ pr_detect_ambiguous_ticks: ticks,
3257
+ }, now, false),
3258
+ result: true,
3259
+ };
3260
+ });
3261
+ if (!written)
3262
+ return;
3231
3263
  if (announce) {
3232
3264
  journalEvent(deps, 'pr-detect-ambiguous', unit(batch.id), {
3233
3265
  reason: state_1.PR_DETECT_AMBIGUOUS_REASON,
@@ -3236,21 +3268,14 @@ function recordPrDetectAmbiguous(deps, batch, matches, now) {
3236
3268
  at: now.toISOString(),
3237
3269
  since,
3238
3270
  ticks_persisted: ticks,
3239
- // #789 review: neither "record it explicitly" (no command exists) nor
3240
- // "close the extras" (a MERGED PR cannot be closed, so it never leaves
3241
- // `gh pr list --state merged`) was ever an actionable remedy — state
3242
- // what actually resolves it instead. #824 tracks the missing verb.
3243
- detail: `${matches.length} MERGED PRs match head=${batch.branch ?? '?'} (#${matches.join(', #')}) — refusing to guess which is ours; nothing recorded. The batch still reconciles via commits-in-base/members-closed evidence if either holds; otherwise inspect and \`sched abandon --batch ${batch.id}\`, or use \`sched attach-pr\` once #824 ships it`,
3271
+ // #789 review: "close the extras" (a MERGED PR cannot be closed, so it
3272
+ // never leaves `gh pr list --state merged`) was never an actionable
3273
+ // remedy — state what actually resolves it instead: the operator names
3274
+ // the batch's own PR with `sched attach-pr` (#824), which holds it to
3275
+ // these same candidate checks.
3276
+ detail: `${matches.length} MERGED PRs match head=${batch.branch ?? '?'} (#${matches.join(', #')}) — refusing to guess which is ours; nothing recorded. The batch still reconciles via commits-in-base/members-closed evidence if either holds; otherwise inspect the candidates and record the batch's own with \`sched attach-pr --batch ${batch.id} <pr>\`, or \`sched abandon --batch ${batch.id}\``,
3244
3277
  });
3245
3278
  }
3246
- deps.store.withLock((s) => ({
3247
- state: (0, state_1.patchBatch)(s, batch.id, {
3248
- pr_detect_ambiguous_reason: state_1.PR_DETECT_AMBIGUOUS_REASON,
3249
- pr_detect_ambiguous_since: since,
3250
- pr_detect_ambiguous_ticks: ticks,
3251
- }, now, false),
3252
- result: undefined,
3253
- }));
3254
3279
  }
3255
3280
  /**
3256
3281
  * #686: reconcile a `blocked` batch whose work demonstrably shipped anyway.
@@ -3382,7 +3407,7 @@ function reconcileStaleBlockedBatches(deps, now, result, anchorCtx) {
3382
3407
  // #789 review: what the lock ACTUALLY applied, not what the outer
3383
3408
  // snapshot predicted — re-derived from `b` (read under the lock) below,
3384
3409
  // since `batch.pr` could in principle have changed between the snapshot
3385
- // above and the lock acquiring (e.g. a future `sched attach-pr`, #824).
3410
+ // above and the lock acquiring (e.g. `sched attach-pr`, #824).
3386
3411
  let effectivePr = null;
3387
3412
  let prWasRecorded = false;
3388
3413
  deps.store.withLock((s) => {