@defrex/autobuild 0.9.0 → 0.10.1
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 +97 -113
- package/SPEC.md +17 -4
- package/docs/architecture.md +109 -0
- package/docs/configuration.md +26 -7
- package/docs/remote-store-protocol.md +35 -0
- package/docs/website.md +4 -0
- package/package.json +6 -4
- package/packages/core/src/cli/dashboard/facts.ts +215 -0
- package/packages/core/src/cli/dashboard/frame-parse.ts +223 -0
- package/packages/core/src/cli/dashboard/model.ts +85 -147
- package/packages/core/src/cli/dashboard/poll.ts +7 -2
- package/packages/core/src/cli/dispatch-process.ts +1 -13
- package/packages/core/src/cli/dispatch.ts +80 -37
- package/packages/core/src/cli/help.ts +15 -1
- package/packages/core/src/cli/init-validation.ts +18 -0
- package/packages/core/src/cli/init.ts +3 -2
- package/packages/core/src/cli/installation.ts +50 -8
- package/packages/core/src/cli/self-update.ts +77 -2
- package/packages/core/src/cli/status.ts +1 -1
- package/packages/core/src/cli/ticket.ts +146 -1
- package/packages/core/src/cli/wait.ts +10 -2
- package/packages/core/src/cli/watch.ts +10 -2
- package/packages/core/src/config/live.ts +1 -0
- package/packages/core/src/config/pipeline-source.ts +17 -5
- package/packages/core/src/config/schema.ts +21 -0
- package/packages/core/src/integration/harness.ts +113 -1
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +1369 -0
- package/packages/core/src/kernel/auto-merge-default.ts +43 -9
- package/packages/core/src/kernel/auto-merge.ts +99 -22
- package/packages/core/src/kernel/dispatch-settings.ts +42 -17
- package/packages/core/src/kernel/dispatch-status.ts +23 -0
- package/packages/core/src/kernel/engine.ts +9 -438
- package/packages/core/src/kernel/generators/build-log.ts +259 -0
- package/packages/core/src/kernel/generators/repository-journals.ts +206 -0
- package/packages/core/src/kernel/generators/session-log.ts +72 -0
- package/packages/core/src/kernel/harvest.ts +62 -18
- package/packages/core/src/kernel/incremental-contract.ts +75 -0
- package/packages/core/src/kernel/incremental-fixtures.ts +211 -0
- package/packages/core/src/kernel/incremental.ts +53 -0
- package/packages/core/src/kernel/log-index.ts +620 -0
- package/packages/core/src/kernel/pr-attachments.ts +164 -46
- package/packages/core/src/kernel/reducer-registry.ts +164 -0
- package/packages/core/src/kernel/reducer.ts +216 -74
- package/packages/core/src/operator/query.ts +68 -28
- package/packages/core/src/operator/read-view.ts +98 -0
- package/packages/core/src/orchestrator/prompt.ts +3 -1
- package/packages/core/src/ports/tickets/fake.ts +5 -1
- package/packages/core/src/ports/tickets/file.ts +169 -48
- package/packages/core/src/ports/tickets/linear.ts +33 -1
- package/packages/core/src/ports/tickets/operations.ts +1 -1
- package/packages/core/src/ports/tickets/remote.ts +8 -1
- package/packages/core/src/ports/types.ts +32 -1
- package/packages/core/src/processes/build-execution-state.ts +31 -15
- package/packages/core/src/processes/build-runner.ts +184 -90
- package/packages/core/src/processes/dispatcher-selectors.ts +123 -0
- package/packages/core/src/processes/dispatcher.ts +270 -176
- package/packages/core/src/processes/execution-settlement.ts +72 -39
- package/packages/core/src/processes/harvest-execution-state.ts +41 -29
- package/packages/core/src/processes/infrastructure-failure-budget.ts +31 -18
- package/packages/core/src/processes/orchestrator-tick.ts +44 -3
- package/packages/core/src/processes/publication-state.ts +164 -52
- package/packages/core/src/processes/repo-view.ts +1436 -0
- package/packages/core/src/processes/sandbox-state.ts +26 -2
- package/packages/core/src/processes/tick-state.ts +33 -0
- package/packages/core/src/store/adapter.ts +10 -1
- package/packages/core/src/store/build-scope.ts +26 -0
- package/packages/core/src/store/contract.ts +337 -0
- package/packages/core/src/store/digest.ts +71 -18
- package/packages/core/src/store/local/schema.ts +17 -0
- package/packages/core/src/store/local/store.ts +111 -4
- package/packages/core/src/store/memory.ts +68 -1
- package/packages/core/src/store/phase-session.ts +23 -0
- package/packages/core/src/store/remote/client.ts +50 -1
- package/packages/core/src/store/remote/index.ts +2 -0
- package/packages/core/src/store/remote/protocol.ts +15 -0
- package/packages/core/src/store/repo-state-events.ts +72 -7
- package/packages/core/src/store/session-handle.ts +30 -0
- package/packages/core/src/store/session-reducer.ts +52 -11
- package/packages/core/src/store/snapshots.ts +88 -0
- package/packages/core/src/store/ticket-assets.ts +41 -8
- package/packages/core/src/store/types.ts +38 -0
- package/skills/guide/SKILL.md +38 -21
- package/skills/guide/references/remote-store-protocol.md +35 -0
- package/skills/operate/SKILL.md +146 -19
- package/skills/tickets/SKILL.md +57 -5
|
@@ -23,11 +23,9 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import type { Config } from '../config/schema'
|
|
25
25
|
import type { AbEvent } from '../events/catalog'
|
|
26
|
-
import { normalizeVerifyCompletion, type EventPayload } from '../events/payloads'
|
|
27
26
|
import {
|
|
28
27
|
isVerifyPhase,
|
|
29
28
|
verifyPhase,
|
|
30
|
-
type ArtifactRef,
|
|
31
29
|
type CorePhase,
|
|
32
30
|
type EscalationSource,
|
|
33
31
|
type EscalationTarget,
|
|
@@ -35,10 +33,9 @@ import {
|
|
|
35
33
|
type Finding,
|
|
36
34
|
type Phase,
|
|
37
35
|
type PolicyEscalationCause,
|
|
38
|
-
type ReviewVerdictKind,
|
|
39
|
-
type VerifyOutcome,
|
|
40
36
|
} from '../ontology'
|
|
41
|
-
import {
|
|
37
|
+
import { indexLog, type LogIndex, type LoopIndex } from './log-index'
|
|
38
|
+
import { reduceBuild, type AnsweredEscalation, type BuildState } from './reducer'
|
|
42
39
|
import { stalledChains, type FindingChain } from './stall'
|
|
43
40
|
|
|
44
41
|
// ── The decision contract ────────────────────────────────────────────────────
|
|
@@ -110,153 +107,15 @@ export type Decision =
|
|
|
110
107
|
refs?: string[]
|
|
111
108
|
}
|
|
112
109
|
|
|
113
|
-
// ── Raw-log index ────────────────────────────────────────────────────────────
|
|
114
|
-
//
|
|
115
|
-
// Every field here is a projection the reducer does not carry (or carries
|
|
116
|
-
// without the seq the engine routes on). Loop and verify progress counts only
|
|
117
|
-
// events with seq > restartSeq (§6.3 immutability: every reviewer approves
|
|
118
|
-
// conformance to ONE spec revision — a drifting spec silently converts
|
|
119
|
-
// approvals into approvals-of-something-else, so approvals of an old spec do
|
|
120
|
-
// not carry across `spec.revised`).
|
|
121
|
-
|
|
122
|
-
interface VerdictRecord {
|
|
123
|
-
seq: number
|
|
124
|
-
round: number
|
|
125
|
-
verdict: ReviewVerdictKind
|
|
126
|
-
findings: Finding[]
|
|
127
|
-
reason?: string
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
interface PlanCompletionRecord {
|
|
131
|
-
seq: number
|
|
132
|
-
artifact: ArtifactRef
|
|
133
|
-
verifySteps?: string[]
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
interface RoundRecord {
|
|
137
|
-
startedSeq?: number
|
|
138
|
-
completedSeq?: number
|
|
139
|
-
/** Plan only. Kept separately so approval snapshots this exact completion. */
|
|
140
|
-
planCompletion?: PlanCompletionRecord
|
|
141
|
-
verdict?: VerdictRecord
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
interface LoopIndex {
|
|
145
|
-
/**
|
|
146
|
-
* Max round in any of this loop's events over the FULL log. Round numbers
|
|
147
|
-
* continue monotonically across spec restarts (next round = max round ever
|
|
148
|
-
* seen + 1) so the log stays unambiguous: "plan round 1" must name exactly
|
|
149
|
-
* one producer run, not one per spec revision.
|
|
150
|
-
*/
|
|
151
|
-
maxRoundEver: number
|
|
152
|
-
/** Max round with post-restart events; 0 when the loop is untouched. */
|
|
153
|
-
maxRound: number
|
|
154
|
-
/** Post-restart per-round event records (latest occurrence wins). */
|
|
155
|
-
rounds: Map<number, RoundRecord>
|
|
156
|
-
/** Latest post-restart revise verdict — the findings-feedback source. */
|
|
157
|
-
latestRevise?: VerdictRecord
|
|
158
|
-
/** seq of the latest post-restart approve verdict (verify cycle boundary). */
|
|
159
|
-
latestApproveSeq: number
|
|
160
|
-
/** Plan only: the completion present when the latest approve verdict landed.
|
|
161
|
-
* A later orphan or superseded completion cannot replace this snapshot. */
|
|
162
|
-
approvedPlan?: PlanCompletionRecord
|
|
163
|
-
/** Findings per round (index round-1), post-restart — stalledChains input.
|
|
164
|
-
* The reducer's reviewFindings spans restarts; this one must not. */
|
|
165
|
-
findingsByRound: Finding[][]
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
interface VerifyRecord {
|
|
169
|
-
seq: number
|
|
170
|
-
step: string
|
|
171
|
-
attempt: number
|
|
172
|
-
outcome: VerifyOutcome
|
|
173
|
-
report?: ArtifactRef
|
|
174
|
-
reason?: string
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
interface GuidanceDelivery {
|
|
178
|
-
escalation: string
|
|
179
|
-
/** seq of the matching `session.started` that made the carrier actionable. */
|
|
180
|
-
seq: number
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
interface ReconcileStartRecord {
|
|
184
|
-
seq: number
|
|
185
|
-
attempt: number
|
|
186
|
-
baseSha: string
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
interface ReconcileCompletionRecord extends ReconcileStartRecord {
|
|
190
|
-
completedSeq: number
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
interface ReconcileProgressRecord {
|
|
194
|
-
seq: number
|
|
195
|
-
conflictSeq: number
|
|
196
|
-
attempt: number
|
|
197
|
-
baseSha: string
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
interface LogIndex {
|
|
201
|
-
/** seq of the latest `spec.revised`, else 0 — the restart boundary (§6.3). */
|
|
202
|
-
restartSeq: number
|
|
203
|
-
plan: LoopIndex
|
|
204
|
-
code: LoopIndex
|
|
205
|
-
/** Post-restart `verify.completed` facts with seq, for the same sequence-based
|
|
206
|
-
* cycle boundary query exposed by the reducer (§15.6-A). */
|
|
207
|
-
verifyCompleted: VerifyRecord[]
|
|
208
|
-
/** Post-restart `verify.started` facts — a crashed step re-runs at the SAME
|
|
209
|
-
* attempt (§15.6-C), so the current cycle's attempt must be readable from
|
|
210
|
-
* its start events even before any completion lands. */
|
|
211
|
-
verifyStarted: { seq: number; attempt: number }[]
|
|
212
|
-
/**
|
|
213
|
-
* Max verify attempt in any `verify.started`/`verify.completed` over the
|
|
214
|
-
* FULL log. Attempt numbers continue monotonically across spec restarts and
|
|
215
|
-
* reconcile cycles — the same rationale as LoopIndex.maxRoundEver: the log
|
|
216
|
-
* stays unambiguous ("verify attempt 2" names exactly one cycle) and D5
|
|
217
|
-
* failure keys (verify:<step>, round = attempt) never collide across cycles.
|
|
218
|
-
* This high-water allocates attempt numbers; current-cycle membership is
|
|
219
|
-
* independently sequence-based.
|
|
220
|
-
*/
|
|
221
|
-
maxVerifyAttemptEver: number
|
|
222
|
-
/** seq of the latest post-restart `reconcile.completed` — cycle boundary
|
|
223
|
-
* input: reconciliation changed code, verify re-runs in full (§15.7). */
|
|
224
|
-
lastReconcileCompletedSeq: number
|
|
225
|
-
/** seq of the latest post-restart `implement.completed` — a verify failure
|
|
226
|
-
* with an implement round after it was already routed (§15.6-A). */
|
|
227
|
-
lastImplementCompletedSeq: number
|
|
228
|
-
/** Post-restart `finalize.completed` seen (the reducer only projects pr). */
|
|
229
|
-
finalizeCompleted: boolean
|
|
230
|
-
/** Post-restart `finalize.step-completed` steps, ok true OR false —
|
|
231
|
-
* post-steps are failure-tolerant (§5), so any completion counts. */
|
|
232
|
-
finalizeStepsDone: Set<string>
|
|
233
|
-
/** Latest `pr.conflicted` (full log — the epilogue is restart-orthogonal);
|
|
234
|
-
* only its seq is needed for policy/dedupe. Its baseSha is detection-time
|
|
235
|
-
* evidence, not the reconcile merge target (§15.7). */
|
|
236
|
-
lastConflict?: { seq: number }
|
|
237
|
-
/** A `reconcile.started` after lastConflict without its completion — a
|
|
238
|
-
* crashed reconcile re-runs the SAME attempt from its start (§15.6-C). */
|
|
239
|
-
conflictReconcileStarted?: { attempt: number }
|
|
240
|
-
/** Most recent completed reconcile, paired with the latest same-occurrence
|
|
241
|
-
* start so crash retries use the base that actually completed. */
|
|
242
|
-
lastReconcileCompleted?: ReconcileCompletionRecord
|
|
243
|
-
/** Explicit authoritative observation for the current repeat conflict. */
|
|
244
|
-
lastConflictProgressCheck?: ReconcileProgressRecord
|
|
245
|
-
/** Distinct completed attempts observed still conflicted at the same base.
|
|
246
|
-
* A later attempt start supplies the observation for pre-event logs. */
|
|
247
|
-
reconcileNoProgressCount: number
|
|
248
|
-
/** Guidance carriers that reached a matching durable session launch.
|
|
249
|
-
* Plan, implement, and agent-verify starts may cite feedback (§15.3), but a
|
|
250
|
-
* citation alone remains recoverable across the pre-launch crash boundary.
|
|
251
|
-
* Only a later `session.started` for the same phase and round/attempt marks
|
|
252
|
-
* that answer delivered (§15.6-B). */
|
|
253
|
-
guidanceDeliveries: GuidanceDelivery[]
|
|
254
|
-
}
|
|
255
|
-
|
|
256
110
|
export function decideNext(events: AbEvent[], config: Config): Decision {
|
|
257
|
-
|
|
258
|
-
|
|
111
|
+
return decideNextFromFacts(reduceBuild(events), indexLog(events), config)
|
|
112
|
+
}
|
|
259
113
|
|
|
114
|
+
/** `decideNext` over already-reduced facts: a caller holding the build state
|
|
115
|
+
* and log index (a reducer snapshot advanced to the log tail) decides without
|
|
116
|
+
* the event array. `decideNext(events, config)` is exactly this over
|
|
117
|
+
* `reduceBuild(events)` and `indexLog(events)`. */
|
|
118
|
+
export function decideNextFromFacts(state: BuildState, log: LogIndex, config: Config): Decision {
|
|
260
119
|
// ── 1. Terminal (§15.5: terminal wins, latest wins) ────────────────────────
|
|
261
120
|
if (state.status === 'aborted') return { kind: 'wait', reason: 'aborted' }
|
|
262
121
|
if (state.status === 'done') return { kind: 'wait', reason: 'done' }
|
|
@@ -757,291 +616,3 @@ function guidanceDestination(
|
|
|
757
616
|
if (isVerifyPhase(phase)) return source === 'agent' ? phase : 'code'
|
|
758
617
|
return 'other'
|
|
759
618
|
}
|
|
760
|
-
|
|
761
|
-
// ── Raw-log indexing ─────────────────────────────────────────────────────────
|
|
762
|
-
|
|
763
|
-
type ReviewVerdictPayload = EventPayload<'plan-review.verdict'>
|
|
764
|
-
|
|
765
|
-
function emptyLoop(): LoopIndex {
|
|
766
|
-
return {
|
|
767
|
-
maxRoundEver: 0,
|
|
768
|
-
maxRound: 0,
|
|
769
|
-
rounds: new Map(),
|
|
770
|
-
latestApproveSeq: 0,
|
|
771
|
-
findingsByRound: [],
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
function indexLog(events: AbEvent[]): LogIndex {
|
|
776
|
-
// Pass 1: the restart boundary — seq of the latest spec.revised (§6.3).
|
|
777
|
-
let restartSeq = 0
|
|
778
|
-
for (const event of events) {
|
|
779
|
-
if (event.type === 'spec.revised') restartSeq = event.seq
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
const plan = emptyLoop()
|
|
783
|
-
const code = emptyLoop()
|
|
784
|
-
const verifyCompleted: VerifyRecord[] = []
|
|
785
|
-
const verifyStarted: { seq: number; attempt: number }[] = []
|
|
786
|
-
let maxVerifyAttemptEver = 0
|
|
787
|
-
const finalizeStepsDone = new Set<string>()
|
|
788
|
-
const guidanceDeliveries: GuidanceDelivery[] = []
|
|
789
|
-
const pendingGuidanceStarts = new Map<string, { escalation: string; seq: number }>()
|
|
790
|
-
let lastReconcileCompletedSeq = 0
|
|
791
|
-
let lastImplementCompletedSeq = 0
|
|
792
|
-
let finalizeCompleted = false
|
|
793
|
-
let lastConflict: { seq: number } | undefined
|
|
794
|
-
let conflictReconcileStarted: { attempt: number } | undefined
|
|
795
|
-
const conflictSeqs = new Set<number>()
|
|
796
|
-
const reconcileStarts: ReconcileStartRecord[] = []
|
|
797
|
-
const reconcileCompletions: ReconcileCompletionRecord[] = []
|
|
798
|
-
const reconcileProgressChecks: ReconcileProgressRecord[] = []
|
|
799
|
-
let activeReconcileStart: ReconcileStartRecord | undefined
|
|
800
|
-
|
|
801
|
-
/** Track a loop round: maxRoundEver over the full log; the per-round record
|
|
802
|
-
* only for post-restart events (returns undefined pre-restart). */
|
|
803
|
-
const roundRecord = (loop: LoopIndex, r: number, post: boolean): RoundRecord | undefined => {
|
|
804
|
-
if (r > loop.maxRoundEver) loop.maxRoundEver = r
|
|
805
|
-
if (!post) return undefined
|
|
806
|
-
if (r > loop.maxRound) loop.maxRound = r
|
|
807
|
-
let record = loop.rounds.get(r)
|
|
808
|
-
if (record === undefined) {
|
|
809
|
-
record = {}
|
|
810
|
-
loop.rounds.set(r, record)
|
|
811
|
-
}
|
|
812
|
-
return record
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
const noteGuidanceStart = (key: string, feedback: Feedback | undefined, seq: number): void => {
|
|
816
|
-
// A newer start is the authoritative carrier for this exact occurrence.
|
|
817
|
-
// In particular, a guidance-free retry must not let its later session
|
|
818
|
-
// consume an older citation that never launched.
|
|
819
|
-
if (feedback !== undefined && 'guidance' in feedback) {
|
|
820
|
-
pendingGuidanceStarts.set(key, { escalation: feedback.guidance.escalation, seq })
|
|
821
|
-
} else {
|
|
822
|
-
pendingGuidanceStarts.delete(key)
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
const noteVerdict = (
|
|
827
|
-
loop: LoopIndex,
|
|
828
|
-
payload: ReviewVerdictPayload,
|
|
829
|
-
seq: number,
|
|
830
|
-
post: boolean,
|
|
831
|
-
): void => {
|
|
832
|
-
const record = roundRecord(loop, payload.round, post)
|
|
833
|
-
if (record === undefined) return
|
|
834
|
-
const verdict: VerdictRecord = {
|
|
835
|
-
seq,
|
|
836
|
-
round: payload.round,
|
|
837
|
-
verdict: payload.verdict,
|
|
838
|
-
findings: payload.findings,
|
|
839
|
-
reason: payload.reason,
|
|
840
|
-
}
|
|
841
|
-
record.verdict = verdict
|
|
842
|
-
if (payload.verdict === 'revise') loop.latestRevise = verdict
|
|
843
|
-
if (payload.verdict === 'approve') {
|
|
844
|
-
loop.latestApproveSeq = seq
|
|
845
|
-
// Snapshot at verdict time. A plan.completed appended later — even for
|
|
846
|
-
// this round — was never reviewed and therefore has no authority.
|
|
847
|
-
if (loop === plan) loop.approvedPlan = record.planCompletion
|
|
848
|
-
}
|
|
849
|
-
// Findings per round, reducer-style padding (rounds without verdicts stay
|
|
850
|
-
// empty — including every pre-restart round, which is the point).
|
|
851
|
-
while (loop.findingsByRound.length < payload.round) loop.findingsByRound.push([])
|
|
852
|
-
loop.findingsByRound[payload.round - 1] = payload.findings
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
for (const event of events) {
|
|
856
|
-
const post = event.seq > restartSeq
|
|
857
|
-
switch (event.type) {
|
|
858
|
-
case 'plan.started': {
|
|
859
|
-
noteGuidanceStart(`plan@${event.payload.round}`, event.payload.feedback, event.seq)
|
|
860
|
-
const record = roundRecord(plan, event.payload.round, post)
|
|
861
|
-
if (record !== undefined) record.startedSeq = event.seq
|
|
862
|
-
break
|
|
863
|
-
}
|
|
864
|
-
case 'plan.completed': {
|
|
865
|
-
const record = roundRecord(plan, event.payload.round, post)
|
|
866
|
-
if (record !== undefined) {
|
|
867
|
-
record.completedSeq = event.seq
|
|
868
|
-
record.planCompletion = {
|
|
869
|
-
seq: event.seq,
|
|
870
|
-
artifact: event.payload.artifact,
|
|
871
|
-
...(event.payload.verifySteps !== undefined
|
|
872
|
-
? { verifySteps: [...event.payload.verifySteps] }
|
|
873
|
-
: {}),
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
break
|
|
877
|
-
}
|
|
878
|
-
case 'plan-review.started':
|
|
879
|
-
roundRecord(plan, event.payload.round, post)
|
|
880
|
-
break
|
|
881
|
-
case 'plan-review.verdict':
|
|
882
|
-
noteVerdict(plan, event.payload, event.seq, post)
|
|
883
|
-
break
|
|
884
|
-
|
|
885
|
-
case 'implement.started': {
|
|
886
|
-
noteGuidanceStart(`implement@${event.payload.round}`, event.payload.feedback, event.seq)
|
|
887
|
-
const record = roundRecord(code, event.payload.round, post)
|
|
888
|
-
if (record !== undefined) record.startedSeq = event.seq
|
|
889
|
-
break
|
|
890
|
-
}
|
|
891
|
-
case 'implement.completed': {
|
|
892
|
-
const record = roundRecord(code, event.payload.round, post)
|
|
893
|
-
if (record !== undefined) record.completedSeq = event.seq
|
|
894
|
-
if (post) lastImplementCompletedSeq = event.seq
|
|
895
|
-
break
|
|
896
|
-
}
|
|
897
|
-
case 'code-review.started':
|
|
898
|
-
roundRecord(code, event.payload.round, post)
|
|
899
|
-
break
|
|
900
|
-
case 'code-review.verdict':
|
|
901
|
-
noteVerdict(code, event.payload, event.seq, post)
|
|
902
|
-
break
|
|
903
|
-
|
|
904
|
-
case 'verify.started': {
|
|
905
|
-
noteGuidanceStart(
|
|
906
|
-
`${verifyPhase(event.payload.step)}@${event.payload.attempt}`,
|
|
907
|
-
event.payload.feedback,
|
|
908
|
-
event.seq,
|
|
909
|
-
)
|
|
910
|
-
maxVerifyAttemptEver = Math.max(maxVerifyAttemptEver, event.payload.attempt)
|
|
911
|
-
if (post) verifyStarted.push({ seq: event.seq, attempt: event.payload.attempt })
|
|
912
|
-
break
|
|
913
|
-
}
|
|
914
|
-
case 'session.started': {
|
|
915
|
-
if (event.payload.round === undefined) break
|
|
916
|
-
const key = `${event.payload.phase}@${event.payload.round}`
|
|
917
|
-
const carrier = pendingGuidanceStarts.get(key)
|
|
918
|
-
if (carrier !== undefined && carrier.seq < event.seq) {
|
|
919
|
-
guidanceDeliveries.push({ escalation: carrier.escalation, seq: event.seq })
|
|
920
|
-
pendingGuidanceStarts.delete(key)
|
|
921
|
-
}
|
|
922
|
-
break
|
|
923
|
-
}
|
|
924
|
-
case 'verify.completed': {
|
|
925
|
-
const result = normalizeVerifyCompletion(event.payload)
|
|
926
|
-
maxVerifyAttemptEver = Math.max(maxVerifyAttemptEver, result.attempt)
|
|
927
|
-
if (post) {
|
|
928
|
-
verifyCompleted.push({
|
|
929
|
-
seq: event.seq,
|
|
930
|
-
step: result.step,
|
|
931
|
-
attempt: result.attempt,
|
|
932
|
-
outcome: result.outcome,
|
|
933
|
-
...(result.report !== undefined ? { report: result.report } : {}),
|
|
934
|
-
...(result.reason !== undefined ? { reason: result.reason } : {}),
|
|
935
|
-
})
|
|
936
|
-
}
|
|
937
|
-
break
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
case 'finalize.completed':
|
|
941
|
-
if (post) finalizeCompleted = true
|
|
942
|
-
break
|
|
943
|
-
case 'finalize.step-completed':
|
|
944
|
-
if (post) finalizeStepsDone.add(event.payload.step)
|
|
945
|
-
break
|
|
946
|
-
|
|
947
|
-
case 'pr.conflicted':
|
|
948
|
-
lastConflict = { seq: event.seq }
|
|
949
|
-
conflictSeqs.add(event.seq)
|
|
950
|
-
conflictReconcileStarted = undefined
|
|
951
|
-
break
|
|
952
|
-
case 'reconcile.progress-checked':
|
|
953
|
-
reconcileProgressChecks.push({ seq: event.seq, ...event.payload })
|
|
954
|
-
break
|
|
955
|
-
case 'reconcile.started': {
|
|
956
|
-
const start = { seq: event.seq, ...event.payload }
|
|
957
|
-
reconcileStarts.push(start)
|
|
958
|
-
activeReconcileStart = start
|
|
959
|
-
if (lastConflict !== undefined && event.seq > lastConflict.seq) {
|
|
960
|
-
conflictReconcileStarted = { attempt: event.payload.attempt }
|
|
961
|
-
}
|
|
962
|
-
break
|
|
963
|
-
}
|
|
964
|
-
case 'reconcile.completed':
|
|
965
|
-
if (post) lastReconcileCompletedSeq = event.seq
|
|
966
|
-
if (activeReconcileStart !== undefined) {
|
|
967
|
-
reconcileCompletions.push({
|
|
968
|
-
...activeReconcileStart,
|
|
969
|
-
completedSeq: event.seq,
|
|
970
|
-
})
|
|
971
|
-
activeReconcileStart = undefined
|
|
972
|
-
}
|
|
973
|
-
conflictReconcileStarted = undefined
|
|
974
|
-
break
|
|
975
|
-
|
|
976
|
-
default:
|
|
977
|
-
break
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// One completed occurrence per monotonic attempt. A later duplicate
|
|
982
|
-
// completion is safer to pair with its own latest start than with an older
|
|
983
|
-
// aggregate high-water, and malformed unmatched completions classify nothing.
|
|
984
|
-
const completedByAttempt = new Map<number, ReconcileCompletionRecord>()
|
|
985
|
-
for (const completion of reconcileCompletions) {
|
|
986
|
-
completedByAttempt.set(completion.attempt, completion)
|
|
987
|
-
}
|
|
988
|
-
const completed = [...completedByAttempt.values()].sort(
|
|
989
|
-
(left, right) => left.completedSeq - right.completedSeq,
|
|
990
|
-
)
|
|
991
|
-
|
|
992
|
-
let reconcileNoProgressCount = 0
|
|
993
|
-
for (const completion of completed) {
|
|
994
|
-
const nextStart = reconcileStarts.find(
|
|
995
|
-
(start) => start.seq > completion.completedSeq && start.attempt > completion.attempt,
|
|
996
|
-
)
|
|
997
|
-
const observationLimit = nextStart?.seq ?? Number.POSITIVE_INFINITY
|
|
998
|
-
// Explicit checks win over the historical fallback. Use the latest one
|
|
999
|
-
// that could have routed this next occurrence; duplicate conflict facts do
|
|
1000
|
-
// not count one completed reconcile more than once.
|
|
1001
|
-
const explicit = reconcileProgressChecks
|
|
1002
|
-
.filter(
|
|
1003
|
-
(check) =>
|
|
1004
|
-
check.attempt === completion.attempt &&
|
|
1005
|
-
check.seq > completion.completedSeq &&
|
|
1006
|
-
check.seq < observationLimit &&
|
|
1007
|
-
check.conflictSeq > completion.completedSeq &&
|
|
1008
|
-
check.seq > check.conflictSeq &&
|
|
1009
|
-
conflictSeqs.has(check.conflictSeq),
|
|
1010
|
-
)
|
|
1011
|
-
.at(-1)
|
|
1012
|
-
const observedBase = explicit?.baseSha ?? nextStart?.baseSha
|
|
1013
|
-
if (observedBase === completion.baseSha) reconcileNoProgressCount += 1
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
const lastReconcileCompleted = completed.at(-1)
|
|
1017
|
-
const lastConflictProgressCheck =
|
|
1018
|
-
lastConflict === undefined || lastReconcileCompleted === undefined
|
|
1019
|
-
? undefined
|
|
1020
|
-
: reconcileProgressChecks
|
|
1021
|
-
.filter(
|
|
1022
|
-
(check) =>
|
|
1023
|
-
check.conflictSeq === lastConflict.seq &&
|
|
1024
|
-
check.attempt === lastReconcileCompleted.attempt &&
|
|
1025
|
-
check.seq > lastConflict.seq,
|
|
1026
|
-
)
|
|
1027
|
-
.at(-1)
|
|
1028
|
-
|
|
1029
|
-
return {
|
|
1030
|
-
restartSeq,
|
|
1031
|
-
plan,
|
|
1032
|
-
code,
|
|
1033
|
-
verifyCompleted,
|
|
1034
|
-
verifyStarted,
|
|
1035
|
-
maxVerifyAttemptEver,
|
|
1036
|
-
lastReconcileCompletedSeq,
|
|
1037
|
-
lastImplementCompletedSeq,
|
|
1038
|
-
finalizeCompleted,
|
|
1039
|
-
finalizeStepsDone,
|
|
1040
|
-
lastConflict,
|
|
1041
|
-
conflictReconcileStarted,
|
|
1042
|
-
lastReconcileCompleted,
|
|
1043
|
-
lastConflictProgressCheck,
|
|
1044
|
-
reconcileNoProgressCount,
|
|
1045
|
-
guidanceDeliveries,
|
|
1046
|
-
}
|
|
1047
|
-
}
|