omp-conductor 0.18.0 → 0.18.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 +34 -0
- package/REFERENCE.md +60 -10
- package/agents/to-spec.md +90 -0
- package/package.json +2 -1
- package/schema/config.schema.json +29 -0
- package/src/admission.ts +204 -75
- package/src/ask.ts +268 -7
- package/src/board.ts +17 -3
- package/src/briefs/orchestrator.md +42 -14
- package/src/briefs/to-spec.md +84 -0
- package/src/briefs/worker.md +2 -1
- package/src/cli.ts +2 -0
- package/src/command-help.ts +11 -0
- package/src/command-manifest.ts +22 -0
- package/src/commands/context.ts +1 -0
- package/src/commands/drain.ts +176 -0
- package/src/commands/extend.ts +6 -10
- package/src/commands/status.ts +5 -1
- package/src/commands/watch.ts +50 -2
- package/src/commands/worker.ts +9 -10
- package/src/config-schema.ts +24 -0
- package/src/config.ts +42 -1
- package/src/daemon.ts +965 -36
- package/src/dashboard/app.js +4 -1
- package/src/dashboard/server.ts +5 -2
- package/src/decisions.ts +235 -17
- package/src/diff-flags.ts +75 -1
- package/src/doctor.ts +52 -0
- package/src/escalate.ts +9 -3
- package/src/failure-class.ts +28 -2
- package/src/fleet.ts +146 -22
- package/src/gitops.ts +188 -81
- package/src/graph-health.ts +35 -1
- package/src/graph.ts +66 -1
- package/src/harness-loader.ts +59 -0
- package/src/host.ts +567 -2
- package/src/lifecycle.ts +122 -1
- package/src/omp.ts +227 -20
- package/src/orchestrator-tick.ts +1386 -15
- package/src/orchestrator.ts +12 -0
- package/src/privileged.ts +1 -4
- package/src/release-policy.ts +503 -9
- package/src/session-host.ts +99 -5
- package/src/settlement.ts +69 -17
- package/src/setup-host.ts +1205 -6
- package/src/setup-install.ts +28 -0
- package/src/setup-wizard.ts +13 -2
- package/src/setup.ts +29 -13
- package/src/shell.ts +15 -0
- package/src/status-render.ts +78 -11
- package/src/store.ts +443 -42
- package/src/to-spec.ts +387 -0
- package/src/tracker/github.ts +104 -14
- package/src/types.ts +343 -13
- package/src/upgrade-verify.ts +209 -2
- package/src/upgrade.ts +175 -1
- package/src/verbs/protocol.ts +39 -0
- package/src/verbs/server.ts +730 -56
- package/src/verbs/socket.ts +24 -5
- package/src/worker.ts +25 -2
- package/src/worktree.ts +29 -12
package/src/types.ts
CHANGED
|
@@ -690,6 +690,21 @@ export interface ReviewRevisionRecord {
|
|
|
690
690
|
outcome?: ReviewRevisionOutcome;
|
|
691
691
|
}
|
|
692
692
|
|
|
693
|
+
/**
|
|
694
|
+
* The outcome of recording one `conductor_pr_review` finding against a run's
|
|
695
|
+
* review-revision outbox (#786). `created` opened a new revision round for the
|
|
696
|
+
* run; `appended` folded the finding into the revision already pending
|
|
697
|
+
* undelivered on the same reviewed head — one round, one row, one worker
|
|
698
|
+
* attempt either way, and the resumed session reads both findings. `refused`
|
|
699
|
+
* names the one in-flight shape that cannot be amended: a pending revision at
|
|
700
|
+
* a different head, which is a review of a moved PR and must dispatch and
|
|
701
|
+
* settle before another finding for the same run is recorded.
|
|
702
|
+
*/
|
|
703
|
+
export type ReviewRevisionEnqueue =
|
|
704
|
+
| { kind: "created"; record: ReviewRevisionRecord }
|
|
705
|
+
| { kind: "appended"; record: ReviewRevisionRecord }
|
|
706
|
+
| { kind: "refused"; block: "pending-different-head" };
|
|
707
|
+
|
|
693
708
|
/**
|
|
694
709
|
* A model-supplied justification: one value out of a closed set, plus prose that
|
|
695
710
|
* is written down and read by nothing that decides.
|
|
@@ -926,6 +941,38 @@ export const CONFIG_VERSION = 2;
|
|
|
926
941
|
*/
|
|
927
942
|
export const READABLE_CONFIG_VERSIONS = [1, CONFIG_VERSION] as const;
|
|
928
943
|
|
|
944
|
+
/**
|
|
945
|
+
* Operator-authored host facts rendered into every worker brief (#721) — the
|
|
946
|
+
* typed replacement for host prose hand-written into an untracked agent
|
|
947
|
+
* context file. Only what cannot be derived lives here: cores and RAM are
|
|
948
|
+
* read from the host by `host.ts` at render time, and the guarded shell
|
|
949
|
+
* suites stay `SHARED_HOST_SCRIPTS`'s single source — nothing in this object
|
|
950
|
+
* may re-name a refused command.
|
|
951
|
+
*
|
|
952
|
+
* Every key is optional; a fleet that never fills the object loads a config
|
|
953
|
+
* with no `host` field at all and dispatches briefs byte-for-byte as before.
|
|
954
|
+
*/
|
|
955
|
+
export interface HostConstraints {
|
|
956
|
+
/**
|
|
957
|
+
* What this host is and what else it runs, in the operator's words ("a
|
|
958
|
+
* shared 4-core VPS that also runs Langfuse and the fleet"). The renderer
|
|
959
|
+
* folds the derived core/RAM count into this line.
|
|
960
|
+
*/
|
|
961
|
+
description?: string;
|
|
962
|
+
/**
|
|
963
|
+
* The non-interactive PATH a script or `ssh host '<cmd>'` invocation must
|
|
964
|
+
* export (`/root/.bun/bin:/root/.local/bin:$PATH` on this fleet) — commands
|
|
965
|
+
* run from an ssh session do not see the interactive shell's PATH.
|
|
966
|
+
*/
|
|
967
|
+
path?: string;
|
|
968
|
+
/**
|
|
969
|
+
* Per-repo command conventions, keyed by the repo slug the brief renders
|
|
970
|
+
* as its `{{REPO}}` (`owner/repo`). A repo without an entry renders nothing
|
|
971
|
+
* for it.
|
|
972
|
+
*/
|
|
973
|
+
conventions?: Record<string, string>;
|
|
974
|
+
}
|
|
975
|
+
|
|
929
976
|
/**
|
|
930
977
|
* On-disk root config. `version` is present from day one so a format change
|
|
931
978
|
* can be migrated instead of silently misread by an older daemon.
|
|
@@ -938,6 +985,9 @@ export interface ConductorConfig {
|
|
|
938
985
|
* `<stateDir()>/backups/db` when omitted. A snapshot that cannot land there
|
|
939
986
|
* is `doctor`'s `db-backup` failure. */
|
|
940
987
|
dbBackupDir?: string;
|
|
988
|
+
/** Host facts every worker brief renders (#721). Absent or empty renders
|
|
989
|
+
* nothing — no section — and the brief stays exactly what it always was. */
|
|
990
|
+
host?: HostConstraints;
|
|
941
991
|
}
|
|
942
992
|
|
|
943
993
|
/**
|
|
@@ -1025,6 +1075,11 @@ export const SETTLEMENT_FLAG_KINDS = [
|
|
|
1025
1075
|
"assertions-removed",
|
|
1026
1076
|
/** A named timeout in a test file went up. */
|
|
1027
1077
|
"test-timeout-raised",
|
|
1078
|
+
/** The PR diff touches files outside the effective declared file lane —
|
|
1079
|
+
* the declaration admission enforced and the brief rendered, so a widened
|
|
1080
|
+
* lane is named on evidence rather than found by reading the file list by
|
|
1081
|
+
* hand, and the queued work it blocks is explainable (#739). */
|
|
1082
|
+
"lane-escape",
|
|
1028
1083
|
/** A command the PR body claims as proof, with no matching command in the
|
|
1029
1084
|
* run's transcript — the claim has no attempt behind it at all. */
|
|
1030
1085
|
"claimed-proof-missing",
|
|
@@ -1046,8 +1101,8 @@ export type SettlementFlagKind = (typeof SETTLEMENT_FLAG_KINDS)[number];
|
|
|
1046
1101
|
*/
|
|
1047
1102
|
export interface SettlementFlag {
|
|
1048
1103
|
kind: SettlementFlagKind;
|
|
1049
|
-
/** Repo-relative path, `(report)` for report evidence,
|
|
1050
|
-
* a post-merge workflow result. */
|
|
1104
|
+
/** Repo-relative path, `(report)` for report evidence, `(lane)` for a
|
|
1105
|
+
* lane-escape finding, or `(base branch)` for a post-merge workflow result. */
|
|
1051
1106
|
file: string;
|
|
1052
1107
|
/** 1-based line in the side of the diff the evidence came from: the
|
|
1053
1108
|
* post-image for an added line, the pre-image for a removed one. */
|
|
@@ -1193,8 +1248,19 @@ export interface Tracker {
|
|
|
1193
1248
|
issueBody(issue: number): Promise<string | undefined>;
|
|
1194
1249
|
/**
|
|
1195
1250
|
* The state of one specific pull request, or undefined when this adapter
|
|
1196
|
-
* could not tell — a network failure, a
|
|
1197
|
-
* Undefined never means
|
|
1251
|
+
* could not tell — a network failure, a revoked token, a URL it cannot
|
|
1252
|
+
* parse, or a 404 the adapter could not corroborate. Undefined never means
|
|
1253
|
+
* "no". The one exception is a definitively missing PR: a REST 404 on the
|
|
1254
|
+
* individual read that a same-repository pulls-list read corroborates (a
|
|
1255
|
+
* deleted or never-created number in a repository the credential can still
|
|
1256
|
+
* read) is reported by throwing a classified `GhPrMissingError`. A
|
|
1257
|
+
* repository hidden from the credential answers the same 404 bytes, so a
|
|
1258
|
+
* bare 404 — token scope/SSO loss included — is never "missing": it stays
|
|
1259
|
+
* undefined and the row stays retryable. Every other tracker implementation
|
|
1260
|
+
* keeps the "never throws, undefined is could-not-tell" contract, and every
|
|
1261
|
+
* caller not acting on a missing PR keeps its existing fail-closed handling
|
|
1262
|
+
* by catching the throw exactly as it already catches any other tracker
|
|
1263
|
+
* failure.
|
|
1198
1264
|
*
|
|
1199
1265
|
* Deliberately separate from {@link Tracker.openCloserFor}, which asks
|
|
1200
1266
|
* whether any OPEN closing PR exists and answers undefined for merged,
|
|
@@ -1203,6 +1269,11 @@ export interface Tracker {
|
|
|
1203
1269
|
* is how a PR a human rejected gets recorded as merged.
|
|
1204
1270
|
*/
|
|
1205
1271
|
prState(url: string): Promise<PrState | undefined>;
|
|
1272
|
+
/** The live head commit SHA of one pull request, or undefined when the
|
|
1273
|
+
* tracker could not tell. `pr-checks-green` binds its green verdict to the
|
|
1274
|
+
* head it was read at, so a head read that cannot answer is "not yet
|
|
1275
|
+
* satisfied" for that pass, never a green verdict (#808). */
|
|
1276
|
+
prHead(url: string): Promise<string | undefined>;
|
|
1206
1277
|
/** The merge commit and base ref for a merged pull request. */
|
|
1207
1278
|
mergedPrInfo(url: string): Promise<MergedPrInfo | undefined>;
|
|
1208
1279
|
/** Workflow runs GitHub associated with one exact commit SHA. */
|
|
@@ -1292,9 +1363,9 @@ export interface Tracker {
|
|
|
1292
1363
|
/** Issues carrying `label`, open or closed, bounded. Empty on any failure —
|
|
1293
1364
|
* a reconcile that cannot list must remove no labels. */
|
|
1294
1365
|
listLabeled(label: string, limit?: number): Promise<{ number: number; state: IssueState }[]>;
|
|
1295
|
-
/** Sub-issues of `issue`, with their states.
|
|
1296
|
-
*
|
|
1297
|
-
*
|
|
1366
|
+
/** Sub-issues of `issue`, with their states. Propagates lookup failures so
|
|
1367
|
+
* callers can choose the safe polarity: settlement catches and treats an
|
|
1368
|
+
* unread probe as no positive evidence, while launch selection fails closed. */
|
|
1298
1369
|
childrenOf(issue: number): Promise<{ number: number; state: IssueState }[]>;
|
|
1299
1370
|
}
|
|
1300
1371
|
|
|
@@ -1432,6 +1503,72 @@ export type RunState =
|
|
|
1432
1503
|
/** In flight when its daemon process died; reconciled at the next startup. */
|
|
1433
1504
|
| "orphaned";
|
|
1434
1505
|
|
|
1506
|
+
/** One parsed `File lane:` declaration: the paths and the verbatim source
|
|
1507
|
+
* line, so a renderer can reproduce the declaration itself rather than a
|
|
1508
|
+
* summary of it. Exported for the brief's guarantee that the gate's effective
|
|
1509
|
+
* lane is always visible to the worker (#608). */
|
|
1510
|
+
export interface LaneDeclaration {
|
|
1511
|
+
files: string[];
|
|
1512
|
+
/** The declaration line verbatim, as written on the surface it came from. */
|
|
1513
|
+
source: string;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
/**
|
|
1517
|
+
* The effective file lane as both admission and the worker brief must read it
|
|
1518
|
+
* (#608): the latest `File lane:` declaration among the issue body and every
|
|
1519
|
+
* comment, in the tracker's oldest-first order. This is the "later correction
|
|
1520
|
+
* visibly supersedes" contract applied across both surfaces at once, and it is
|
|
1521
|
+
* the single source of truth the gate enforces and the brief renders — so a
|
|
1522
|
+
* declaration can never control admission while staying invisible to the
|
|
1523
|
+
* worker. `at` records which surface won (`"body"`, or the 0-based comment
|
|
1524
|
+
* index), letting the brief reproduce the declaration verbatim when the
|
|
1525
|
+
* winning comment sits beyond its rendered discussion budget.
|
|
1526
|
+
*/
|
|
1527
|
+
export interface FileLane extends LaneDeclaration {
|
|
1528
|
+
at: "body" | number;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/** One parsed `Model:` declaration (#535): the selector and the verbatim
|
|
1532
|
+
* source line, so a renderer can reproduce the declaration itself rather
|
|
1533
|
+
* than a summary of it. */
|
|
1534
|
+
export interface ModelDeclaration {
|
|
1535
|
+
/** The selector in omp's own model syntax — a role alias (`@slow`) or a
|
|
1536
|
+
* concrete `provider/model`, either optionally `:thinking`-suffixed —
|
|
1537
|
+
* passed straight through as the session's `modelPattern`, for omp's
|
|
1538
|
+
* resolver to interpret. */
|
|
1539
|
+
model: string;
|
|
1540
|
+
/** The declaration line verbatim, as written on the surface it came from. */
|
|
1541
|
+
source: string;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
/**
|
|
1545
|
+
* The effective model as both dispatch and the worker brief must read it
|
|
1546
|
+
* (#535): the latest `Model:` declaration among the issue body and every
|
|
1547
|
+
* comment, in the tracker's oldest-first order — the same "later correction
|
|
1548
|
+
* visibly supersedes" contract applied across both surfaces at once as
|
|
1549
|
+
* {@link FileLane}, so a pre-dispatch comment can change the model a
|
|
1550
|
+
* promotion brief named in the body. `at` records which surface won
|
|
1551
|
+
* (`"body"`, or the 0-based comment index).
|
|
1552
|
+
*/
|
|
1553
|
+
export interface EffectiveModel extends ModelDeclaration {
|
|
1554
|
+
at: "body" | number;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/**
|
|
1558
|
+
* One dispatched run's observation of the code-graph MCP tools (#726): a
|
|
1559
|
+
* runtime fact read off the session's own registry at session start, never a
|
|
1560
|
+
* config guess. `present: true` and `present: false` are both observations —
|
|
1561
|
+
* an absent field (no `graphTools` on the row at all) means no observation was
|
|
1562
|
+
* recorded, which is deliberately distinct from "tools absent": the whole
|
|
1563
|
+
* point of the boolean is that "the model ignored a tool it had" and "the tool
|
|
1564
|
+
* was missing" no longer look identical from outside the session.
|
|
1565
|
+
*/
|
|
1566
|
+
export interface GraphToolsObservation {
|
|
1567
|
+
present: boolean;
|
|
1568
|
+
/** Session-start wall clock (ms epoch), the moment the registry was read. */
|
|
1569
|
+
at: number;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1435
1572
|
/**
|
|
1436
1573
|
* One attempt at one issue. Persisted so a daemon restart can reconcile
|
|
1437
1574
|
* orphaned worktrees and branches instead of leaking them.
|
|
@@ -1447,6 +1584,17 @@ export interface RunRecord {
|
|
|
1447
1584
|
state: RunState;
|
|
1448
1585
|
/** 1-based attempt number, checked against `Caps.maxAttemptsPerIssue`. */
|
|
1449
1586
|
attempt: number;
|
|
1587
|
+
/**
|
|
1588
|
+
* The file-lane declaration admission resolved for this run, persisted at
|
|
1589
|
+
* dispatch from the carried `Admission.lane` (#744). It is the exact
|
|
1590
|
+
* snapshot the overlap gate enforced — the same value the brief renders —
|
|
1591
|
+
* and it is durable on purpose: the next dispatch pass builds lane occupancy
|
|
1592
|
+
* from it, so a run admitted with a declared lane it has not yet written
|
|
1593
|
+
* still holds those files across passes, until the row reaches a terminal
|
|
1594
|
+
* state and leaves the active set. Absent means the run predates the column
|
|
1595
|
+
* or was admitted with no declaration (fail open) — never "empty lane".
|
|
1596
|
+
*/
|
|
1597
|
+
lane?: FileLane;
|
|
1450
1598
|
turns: number;
|
|
1451
1599
|
/** Effective turn ceiling for this run; operators may only raise it. */
|
|
1452
1600
|
maxTurns: number;
|
|
@@ -1519,6 +1667,18 @@ export interface RunRecord {
|
|
|
1519
1667
|
* (#286).
|
|
1520
1668
|
*/
|
|
1521
1669
|
model?: string;
|
|
1670
|
+
/**
|
|
1671
|
+
* The code-graph session observation (#726): whether the graph MCP tools were
|
|
1672
|
+
* in this run's own session registry when the session started. Read off the
|
|
1673
|
+
* session itself, once, at dispatch — never derived from `mcp.json`, which
|
|
1674
|
+
* only says what a session *should* mount. It is the difference between "the
|
|
1675
|
+
* model ignored a tool it had" and "the tool was missing", which look
|
|
1676
|
+
* identical from outside a session. Absent means the run predates the column
|
|
1677
|
+
* or no worker session recorded an observation — never "graph tools were
|
|
1678
|
+
* absent": `present: false` is the only absence claim, and it is a runtime
|
|
1679
|
+
* fact, not an inference.
|
|
1680
|
+
*/
|
|
1681
|
+
graphTools?: GraphToolsObservation;
|
|
1522
1682
|
/** When an operator accepted the loss or recovered the tree by hand
|
|
1523
1683
|
* (`unblock --force`). Clears the hold without erasing what happened. */
|
|
1524
1684
|
salvageAckAt?: number;
|
|
@@ -1530,6 +1690,18 @@ export interface RunRecord {
|
|
|
1530
1690
|
* Absent means never quarantined, or no longer — never "quarantine not
|
|
1531
1691
|
* checked", because every retained-tree pass checks. */
|
|
1532
1692
|
quarantineDetail?: string;
|
|
1693
|
+
/** Failed-attempt budget charges this row has accumulated through review
|
|
1694
|
+
* claims (#795 review round 2). Every chargeable terminal event that was
|
|
1695
|
+
* claimed into a review round adds one here, so `failuresFor` keeps
|
|
1696
|
+
* counting each event even after the round leaves the row `pushed-green`;
|
|
1697
|
+
* repeated failed rounds therefore keep consuming the failed-attempt
|
|
1698
|
+
* budget. Absent means zero charges were ever preserved on this row. */
|
|
1699
|
+
failureCharges?: number;
|
|
1700
|
+
/** Continuation budget charges this row has accumulated through review
|
|
1701
|
+
* claims, same semantics as {@link RunRecord.failureCharges}: each
|
|
1702
|
+
* chargeable cap or returned-for-revision event claimed into a round adds
|
|
1703
|
+
* one, and `continuationsFor` sums the column. */
|
|
1704
|
+
continuationCharges?: number;
|
|
1533
1705
|
startedAt: number;
|
|
1534
1706
|
endedAt?: number;
|
|
1535
1707
|
/** Last failure text, surfaced verbatim in escalations. */
|
|
@@ -1640,6 +1812,14 @@ export interface DispatchSummary {
|
|
|
1640
1812
|
* sweep and by classification recovery. Optional: persisted old rows lack
|
|
1641
1813
|
* it, so readers use `?? 0`. (#497) */
|
|
1642
1814
|
settled?: number;
|
|
1815
|
+
/**
|
|
1816
|
+
* Queue-labelled candidates the operator's park label disqualified — the
|
|
1817
|
+
* same eligibility read the claim gate uses, so the status number cannot
|
|
1818
|
+
* disagree with what admission would hold (#507). Distinguishes "0 claimable,
|
|
1819
|
+
* 12 parked" from "0 claimable, nothing to do". Optional: persisted old rows
|
|
1820
|
+
* lack it, and a pass with nothing parked omits it, so readers use `?? 0`.
|
|
1821
|
+
*/
|
|
1822
|
+
parked?: number;
|
|
1643
1823
|
/** True for a held pass's own record (#497): the queue was never routed, so
|
|
1644
1824
|
* ready/routed/claimed are absent queue facts, not an empty queue. */
|
|
1645
1825
|
paused?: boolean;
|
|
@@ -1879,6 +2059,49 @@ export interface IntakeDraft {
|
|
|
1879
2059
|
at: number;
|
|
1880
2060
|
}
|
|
1881
2061
|
|
|
2062
|
+
/**
|
|
2063
|
+
* The verdict surface of per-issue grooming state (#735). Admission writes
|
|
2064
|
+
* `blocked` when it holds a candidate for a file-lane or dependency reason —
|
|
2065
|
+
* the durable half of the misleading "groom the backlog" fix, because the tick
|
|
2066
|
+
* reads these instead of re-deriving why the runway cannot move. `promotable`
|
|
2067
|
+
* and `considered` are the outcomes #679's scout loop writes into the same
|
|
2068
|
+
* table, so grooming knowledge lives in one store rather than a second memory.
|
|
2069
|
+
*/
|
|
2070
|
+
export const GROOMING_VERDICTS = ["promotable", "blocked", "considered"] as const;
|
|
2071
|
+
|
|
2072
|
+
export type GroomingVerdict = (typeof GROOMING_VERDICTS)[number];
|
|
2073
|
+
|
|
2074
|
+
/**
|
|
2075
|
+
* One issue's current grooming verdict, durable across restarts and keyed by
|
|
2076
|
+
* project + issue — one row per issue, replaced in place by upsert, never a
|
|
2077
|
+
* history. Deliberately NOT a decision row: a verdict is neither owed to the
|
|
2078
|
+
* operator nor TTL-exempt, so it sits outside `decisions` (7-day expiry,
|
|
2079
|
+
* operator-facing) and `material_events` (append-only digest outbox) alike.
|
|
2080
|
+
*/
|
|
2081
|
+
export interface GroomingRecord {
|
|
2082
|
+
project: string;
|
|
2083
|
+
issue: number;
|
|
2084
|
+
verdict: GroomingVerdict;
|
|
2085
|
+
/** Why this verdict — admission's hold reason for `blocked` (`file-lane`,
|
|
2086
|
+
* `depends-on`), or the groomer's own label for #679's verdicts. */
|
|
2087
|
+
reason: string;
|
|
2088
|
+
/** What proved it: the hold detail naming the overlapping file/holder run,
|
|
2089
|
+
* or a scout summary. Free text, bounded at the write site. */
|
|
2090
|
+
evidence: string;
|
|
2091
|
+
recordedAt: number;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
/** What a caller hands over. The store owns the timestamp and the replace-in-
|
|
2095
|
+
* place semantics, nothing else. */
|
|
2096
|
+
export interface GroomingDraft {
|
|
2097
|
+
project: string;
|
|
2098
|
+
issue: number;
|
|
2099
|
+
verdict: GroomingVerdict;
|
|
2100
|
+
reason: string;
|
|
2101
|
+
evidence: string;
|
|
2102
|
+
at: number;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
1882
2105
|
/**
|
|
1883
2106
|
* Where one operator decision stands (#136).
|
|
1884
2107
|
*
|
|
@@ -1930,6 +2153,12 @@ export interface DecisionRecord {
|
|
|
1930
2153
|
/** When that precondition was first observed true. Set once; the digest
|
|
1931
2154
|
* promotes the row from "parked" to "act on this now". */
|
|
1932
2155
|
conditionMetAt?: number;
|
|
2156
|
+
/** For `pr-checks-green` / `pr-review-ready`: the exact PR head whose checks
|
|
2157
|
+
* satisfied the condition, read in the same pass as the green verdict. A
|
|
2158
|
+
* head change invalidates the verdict, so a met row whose PR head has moved
|
|
2159
|
+
* returns to pending instead of rendering `[CONDITION MET]` for a stale
|
|
2160
|
+
* head (#808). */
|
|
2161
|
+
conditionHead?: string;
|
|
1933
2162
|
state: DecisionState;
|
|
1934
2163
|
resolvedAt?: number;
|
|
1935
2164
|
/** The answer, the withdrawal reason, or the expiry note. */
|
|
@@ -1987,6 +2216,17 @@ export interface Store {
|
|
|
1987
2216
|
/** Partial patch; an explicit `null` clears a column, `undefined`/absence leaves it alone (#468). */
|
|
1988
2217
|
updateRun(id: string, patch: RunPatch): void;
|
|
1989
2218
|
getRun(id: string): RunRecord | undefined;
|
|
2219
|
+
/**
|
|
2220
|
+
* Counts of this project's runs that recorded a graph-tools session
|
|
2221
|
+
* observation (#726), split by the observed truth value. The doctor reads
|
|
2222
|
+
* these to report what dispatched sessions actually saw, and reports nothing
|
|
2223
|
+
* as "observed" when no run has recorded one.
|
|
2224
|
+
*/
|
|
2225
|
+
graphToolsObservationCounts(project: string): {
|
|
2226
|
+
recorded: number;
|
|
2227
|
+
present: number;
|
|
2228
|
+
absent: number;
|
|
2229
|
+
};
|
|
1990
2230
|
/** Runs whose issue is occupied: a live worker, or a green PR awaiting merge. */
|
|
1991
2231
|
activeRuns(project: string): RunRecord[];
|
|
1992
2232
|
/** Runs backed by a worker process — what capacity counts. Subset of {@link Store.activeRuns}. */
|
|
@@ -2002,10 +2242,20 @@ export interface Store {
|
|
|
2002
2242
|
* predecessor opened. Merged rows keep the same recent-history bound. */
|
|
2003
2243
|
runsForProjectPr(project: string, prUrl: string, mergedSinceEpochMs: number): RunRecord[];
|
|
2004
2244
|
/** Persist one review-revision request (#677), durably, before anything is
|
|
2005
|
-
* woken.
|
|
2006
|
-
* pending undelivered —
|
|
2007
|
-
*
|
|
2008
|
-
|
|
2245
|
+
* woken. A same-run, same-head finding folds into the revision already
|
|
2246
|
+
* pending undelivered (`appended` — one round, one row, one worker
|
|
2247
|
+
* attempt, and the resumed session gets both findings); otherwise it opens
|
|
2248
|
+
* a new round (`created`). A revision already pending at a different head
|
|
2249
|
+
* cannot be amended (`refused`) — that round must dispatch and settle
|
|
2250
|
+
* first. The duplicate in-flight guard is the same single transaction, so
|
|
2251
|
+
* two concurrent same-head findings cannot both create a row or overwrite
|
|
2252
|
+
* each other (the run row's state is the other half once the revision is
|
|
2253
|
+
* dispatched). */
|
|
2254
|
+
enqueueReviewRevision(draft: Omit<ReviewRevisionRecord, "id">): ReviewRevisionEnqueue;
|
|
2255
|
+
/** The pending revision for one run, if any — the header the verb reads to
|
|
2256
|
+
* tell "this call folds into the in-flight round" from "this call opens a
|
|
2257
|
+
* new round" before the round-ceiling gate applies. */
|
|
2258
|
+
pendingReviewForRun(project: string, runId: string): ReviewRevisionRecord | undefined;
|
|
2009
2259
|
/** Review revisions not yet handed to a worker, oldest first — what the
|
|
2010
2260
|
* daemon's dispatch pass wakes on its next tick. */
|
|
2011
2261
|
pendingReviewRevisions(project: string): ReviewRevisionRecord[];
|
|
@@ -2155,6 +2405,28 @@ export interface Store {
|
|
|
2155
2405
|
/** Resolve one idea to `groomed` (recording the issue URL #300 chose) or
|
|
2156
2406
|
* `dismissed`. `false` when the id is unknown. */
|
|
2157
2407
|
resolveIntake(id: string, state: "groomed" | "dismissed", issueUrl?: string): boolean;
|
|
2408
|
+
/** Record the current grooming verdict for one issue, replacing any prior
|
|
2409
|
+
* row — one row per project + issue, never a history (#735). */
|
|
2410
|
+
upsertGrooming(draft: GroomingDraft): void;
|
|
2411
|
+
/** The current grooming verdict for one issue, or undefined when none. */
|
|
2412
|
+
grooming(project: string, issue: number): GroomingRecord | undefined;
|
|
2413
|
+
/** Current grooming verdicts for a project, issue-ascending; `verdict`
|
|
2414
|
+
* narrows to one verdict — the tick's known-blocked read is
|
|
2415
|
+
* `groomingVerdicts(project, "blocked")`. */
|
|
2416
|
+
groomingVerdicts(project: string, verdict?: GroomingVerdict): GroomingRecord[];
|
|
2417
|
+
/**
|
|
2418
|
+
* Reconcile admission's lane/dependency holds against the persisted blocked
|
|
2419
|
+
* verdicts: each currently held issue is recorded as `blocked`, and in the
|
|
2420
|
+
* same transaction every `blocked` row whose lane/dependency hold this pass
|
|
2421
|
+
* does not name is deleted — the hold no longer applies, so the state
|
|
2422
|
+
* self-heals instead of lingering as a cache someone must invalidate. Runs
|
|
2423
|
+
* at the end of every admission pass. Never touches `promotable` or
|
|
2424
|
+
* `considered` rows, which belong to #679's scout loop.
|
|
2425
|
+
*/
|
|
2426
|
+
reconcileGrooming(
|
|
2427
|
+
project: string,
|
|
2428
|
+
holds: readonly { issue: number; reason: AdmissionHoldReason; detail?: string }[],
|
|
2429
|
+
): void;
|
|
2158
2430
|
/** Count and age source for status and digest prompt bounds. */
|
|
2159
2431
|
digestBacklog(project: string): DigestBacklog;
|
|
2160
2432
|
/** Add one bounded observation to the per-day friction rollup. */
|
|
@@ -2340,8 +2612,21 @@ export interface Store {
|
|
|
2340
2612
|
/** Answer or withdraw one. `false` when the id is unknown or already closed,
|
|
2341
2613
|
* so a double-resolve cannot overwrite the first answer. */
|
|
2342
2614
|
resolveDecision(id: string, state: "answered" | "withdrawn", resolution: string, at: number): boolean;
|
|
2343
|
-
/**
|
|
2344
|
-
|
|
2615
|
+
/**
|
|
2616
|
+
* First observation that a row's condition came true. Idempotent per head:
|
|
2617
|
+
* a second call changes nothing, but a `pr-checks-green` / `pr-review-ready`
|
|
2618
|
+
* row whose PR head moved is cleared first and then re-marks against the new
|
|
2619
|
+
* head.
|
|
2620
|
+
* `head` binds the verdict to the PR head it was observed at (#808).
|
|
2621
|
+
*/
|
|
2622
|
+
markDecisionConditionMet(id: string, at: number, head?: string): boolean;
|
|
2623
|
+
/**
|
|
2624
|
+
* Drop a condition's met state — timestamp and any head binding — when the
|
|
2625
|
+
* observation it was set from stopped holding. The head-bound conditions
|
|
2626
|
+
* call this when the PR head changed, so the row returns to pending instead
|
|
2627
|
+
* of rendering `[CONDITION MET]` for checks that are still running (#808).
|
|
2628
|
+
*/
|
|
2629
|
+
clearDecisionConditionMet(id: string): boolean;
|
|
2345
2630
|
/** Close every open row past its deadline and return them, so the caller can
|
|
2346
2631
|
* say what it just closed rather than reporting a count. */
|
|
2347
2632
|
expireDueDecisions(project: string, now: number): DecisionRecord[];
|
|
@@ -2588,6 +2873,16 @@ export const VERB_NAMES = [
|
|
|
2588
2873
|
* OMP session on the existing branch and PR. No close, no reopen, no
|
|
2589
2874
|
* redispatch. */
|
|
2590
2875
|
"conductor_pr_review",
|
|
2876
|
+
/** The orchestrator-only, settled-run recovery operation (#806): open (or
|
|
2877
|
+
* adopt) the missing pull request for a terminal run whose stored branch
|
|
2878
|
+
* exists at its exact recorded head. The recovery proves everything a
|
|
2879
|
+
* worker's own `conductor_pr_create` took for granted from a live channel —
|
|
2880
|
+
* no worker is live, the routed repo matches, the remote branch is at the
|
|
2881
|
+
* exact recorded head, the issue is still open, and no PR already
|
|
2882
|
+
* represents that head — so a stranded `pushed-pending` / lost-PR run can
|
|
2883
|
+
* re-enter normal review/merge without a fresh clone. Idempotent: an
|
|
2884
|
+
* already-matching PR is returned, not duplicated. */
|
|
2885
|
+
"conductor_pr_recover",
|
|
2591
2886
|
/** The one read verb. It answers with the merge gate's own verdict, so
|
|
2592
2887
|
* "pushed-green" is the dispatcher's reading of the PR rather than a claim the
|
|
2593
2888
|
* worker makes about itself from whatever it happened to run. */
|
|
@@ -2653,6 +2948,32 @@ export const VERB_REFUSALS = [
|
|
|
2653
2948
|
"pr-not-this-run",
|
|
2654
2949
|
/** An unrecorded recovery PR was authorized, but not for these exact inputs. */
|
|
2655
2950
|
"recovery-authorization-mismatch",
|
|
2951
|
+
/** The recovery target resolved to a run that does not exist in the store. */
|
|
2952
|
+
"recovery-no-run",
|
|
2953
|
+
/** The run the recovery was pointed at still has a live worker on it. */
|
|
2954
|
+
"recovery-run-live",
|
|
2955
|
+
/** The run is terminal but not in a state the settlement sweep owns, so a
|
|
2956
|
+
* recovered PR would never re-enter verification/review/merge. */
|
|
2957
|
+
"recovery-unsettled-state",
|
|
2958
|
+
/** The tracker could not say whether the issue is open. Recovery fails
|
|
2959
|
+
* closed rather than opening a PR beside (or for) a possibly-closed
|
|
2960
|
+
* issue. */
|
|
2961
|
+
"issue-state-unreadable",
|
|
2962
|
+
/** The recovered work has already landed or been declined: the issue is
|
|
2963
|
+
* closed, the run settled merged, or its recorded PR merged. Nothing to
|
|
2964
|
+
* recover, and recovery must not reopen it. */
|
|
2965
|
+
"recovery-issue-resolved",
|
|
2966
|
+
/** The run records no exact 40-hex head to compare its stored branch
|
|
2967
|
+
* against, so there is nothing durable to verify a recovered PR would
|
|
2968
|
+
* publish. */
|
|
2969
|
+
"recovery-unrecorded-head",
|
|
2970
|
+
/** The branch exists but not at the run's recorded head (replaced,
|
|
2971
|
+
* force-pushed or superseded by a live PR), so a created PR would publish
|
|
2972
|
+
* work the daemon never verified. */
|
|
2973
|
+
"recovery-head-mismatch",
|
|
2974
|
+
/** The routed repository a terminal run recorded no longer has an entry in
|
|
2975
|
+
* this project's routing, so the daemon cannot create a PR for it. */
|
|
2976
|
+
"recovery-repo-unrouted",
|
|
2656
2977
|
/** The run has no pull request to act on. */
|
|
2657
2978
|
"pr-missing",
|
|
2658
2979
|
/**
|
|
@@ -2695,6 +3016,15 @@ export const VERB_REFUSALS = [
|
|
|
2695
3016
|
"label-not-in-vocabulary",
|
|
2696
3017
|
/** The label is a lifecycle label; those transitions stay the daemon's (#26). */
|
|
2697
3018
|
"label-is-lifecycle",
|
|
3019
|
+
/** The label is the operator's park gesture; only the operator may set or
|
|
3020
|
+
* clear it, orchestrator and worker alike (#507). */
|
|
3021
|
+
"label-is-operator-owned",
|
|
3022
|
+
/** Adding the queue label was refused because the issue carries a clearly
|
|
3023
|
+
* delimited write-lane section heading that parsed no path-like files
|
|
3024
|
+
* (#825): promoting it would fail open beside overlapping work, so the
|
|
3025
|
+
* verb refuses with an actionable syntax error instead of echoing a
|
|
3026
|
+
* fail-open the heading contradicts. */
|
|
3027
|
+
"file-lane-unparseable",
|
|
2698
3028
|
/** The release grant does not permit this shape for this caller. */
|
|
2699
3029
|
"release-not-granted",
|
|
2700
3030
|
/** The artefact or environment is not one this project declared (#129). */
|