omp-conductor 0.19.7 → 0.20.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/REFERENCE.md +10 -1
- package/agents/to-spec.md +76 -9
- package/package.json +1 -1
- package/schema/config.schema.json +4 -0
- package/src/arm-challenge.ts +204 -85
- package/src/ask.ts +130 -615
- package/src/board.ts +7 -1
- package/src/brief-upgrade.ts +24 -0
- package/src/briefs/console.md +253 -0
- package/src/briefs/correction.md +203 -0
- package/src/briefs/orchestrator.md +167 -97
- package/src/briefs/policy.md +19 -16
- package/src/briefs/to-spec.md +76 -9
- package/src/briefs/worker.md +50 -16
- package/src/cli.ts +4 -0
- package/src/command-manifest.ts +54 -8
- package/src/commands/arm.ts +113 -49
- package/src/commands/console.ts +70 -0
- package/src/commands/context.ts +2 -0
- package/src/commands/epic.ts +132 -0
- package/src/commands/extend.ts +9 -1
- package/src/commands/intake.ts +44 -14
- package/src/commands/stats.ts +19 -4
- package/src/commands/worker.ts +9 -1
- package/src/config-schema.ts +13 -0
- package/src/config.ts +27 -0
- package/src/daemon/ack.ts +159 -0
- package/src/daemon/admission-pass.ts +135 -0
- package/src/daemon/brief.ts +461 -0
- package/src/daemon/deps.ts +539 -0
- package/src/daemon/dispatch.ts +1779 -0
- package/src/daemon/drain.ts +185 -0
- package/src/daemon/groom-pass.ts +412 -0
- package/src/daemon/http.ts +417 -0
- package/src/daemon/integrity.ts +108 -0
- package/src/daemon/panes.ts +180 -0
- package/src/daemon/review.ts +1888 -0
- package/src/daemon/runtime.ts +736 -0
- package/src/daemon/settle-pass.ts +589 -0
- package/src/daemon/supervision.ts +438 -0
- package/src/daemon/tick.ts +968 -0
- package/src/daemon/views.ts +751 -0
- package/src/daemon.ts +105 -7923
- package/src/dashboard/app.js +58 -0
- package/src/dashboard/controls.ts +22 -3
- package/src/dashboard/server.ts +4 -0
- package/src/diff-flags.ts +24 -3
- package/src/failure-class.ts +75 -1
- package/src/fleet.ts +290 -164
- package/src/groom.ts +461 -0
- package/src/http-token.ts +142 -0
- package/src/knowledge.ts +229 -0
- package/src/mining.ts +316 -0
- package/src/orchestrator-tick.ts +428 -1681
- package/src/ready-gate.ts +267 -0
- package/src/settlement.ts +72 -6
- package/src/setup-host.ts +32 -9
- package/src/setup-wizard.ts +55 -7
- package/src/setup.ts +229 -3
- package/src/stats.ts +257 -2
- package/src/status-render.ts +158 -7
- package/src/store.ts +604 -26
- package/src/to-spec.ts +194 -21
- package/src/tracker/github.ts +50 -0
- package/src/types.ts +416 -15
- package/src/verbs/protocol.ts +28 -0
- package/src/verbs/server.ts +330 -39
- package/src/wake.ts +19 -2
- package/src/worker.ts +456 -1
package/src/status-render.ts
CHANGED
|
@@ -19,7 +19,13 @@
|
|
|
19
19
|
|
|
20
20
|
import { formatZonedMinute } from "./availability.ts";
|
|
21
21
|
import { spendTelemetryDetail, type SpendTelemetryVerdict } from "./spend-telemetry.ts";
|
|
22
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
DaemonStop,
|
|
24
|
+
GroomingRecord,
|
|
25
|
+
InstallSurfaceObservation,
|
|
26
|
+
ReviewCorrectionRound,
|
|
27
|
+
RunRecord,
|
|
28
|
+
} from "./types.ts";
|
|
23
29
|
import { settlementFlagSummary } from "./diff-flags.ts";
|
|
24
30
|
import type { CodeGraphHealth } from "./graph-health.ts";
|
|
25
31
|
import { formatDigestBacklog, formatOpenReports } from "./reports.ts";
|
|
@@ -28,6 +34,12 @@ import { planUsageLine } from "./usage.ts";
|
|
|
28
34
|
import { HEALTH_TIMEOUT_MS, SYSTEMD_UNIT, type UnitOwnership } from "./lifecycle.ts";
|
|
29
35
|
import type { WorkerPauseView } from "./fleet.ts";
|
|
30
36
|
import { formatRss, rssBytesFromHealthz } from "./host.ts";
|
|
37
|
+
// One constant, two readers: the durable in-flight to-spec launch marker
|
|
38
|
+
// (#777) is written by the daemon's grooming launcher and read here, so the
|
|
39
|
+
// reason string is imported rather than hand-copied — the copy that used to
|
|
40
|
+
// live in this file was a second source of truth for a row shape nobody
|
|
41
|
+
// audits.
|
|
42
|
+
import { TO_SPEC_IN_FLIGHT_REASON } from "./groom.ts";
|
|
31
43
|
import {
|
|
32
44
|
formatBaseHealth,
|
|
33
45
|
formatDispatchSummary,
|
|
@@ -252,10 +264,6 @@ const REFUSED_GROOMING_REASONS: Record<string, true> = {
|
|
|
252
264
|
"stale-source": true,
|
|
253
265
|
};
|
|
254
266
|
|
|
255
|
-
/** The durable in-flight launch marker (#777) — a batch is running right now.
|
|
256
|
-
* Mirrors `TO_SPEC_IN_FLIGHT_REASON` in `orchestrator-tick.ts`. */
|
|
257
|
-
const GROOMING_IN_FLIGHT_REASON = "in-flight";
|
|
258
|
-
|
|
259
267
|
/**
|
|
260
268
|
* One project's durable grooming state as status lines, or nothing when there
|
|
261
269
|
* is nothing to report (#809).
|
|
@@ -332,7 +340,7 @@ export function formatGroomingStatus(input: GroomingStatusInput): string | undef
|
|
|
332
340
|
row.verdict === "blocked" &&
|
|
333
341
|
MECHANICAL_GROOMING_REASONS[row.reason] !== true &&
|
|
334
342
|
REFUSED_GROOMING_REASONS[row.reason] !== true &&
|
|
335
|
-
row.reason !==
|
|
343
|
+
row.reason !== TO_SPEC_IN_FLIGHT_REASON,
|
|
336
344
|
);
|
|
337
345
|
const mechanical = records.filter(
|
|
338
346
|
(row) => row.verdict === "blocked" && MECHANICAL_GROOMING_REASONS[row.reason] === true,
|
|
@@ -341,7 +349,7 @@ export function formatGroomingStatus(input: GroomingStatusInput): string | undef
|
|
|
341
349
|
(row) => row.verdict === "blocked" && REFUSED_GROOMING_REASONS[row.reason] === true,
|
|
342
350
|
);
|
|
343
351
|
const inFlight = records.filter(
|
|
344
|
-
(row) => row.verdict === "blocked" && row.reason ===
|
|
352
|
+
(row) => row.verdict === "blocked" && row.reason === TO_SPEC_IN_FLIGHT_REASON,
|
|
345
353
|
);
|
|
346
354
|
const awaiting = Math.max(0, routed - records.length);
|
|
347
355
|
const lines: string[] = [];
|
|
@@ -662,6 +670,143 @@ function formatSiblingLive(siblings: { project: string; live: number }[]): strin
|
|
|
662
670
|
return `shared daemon also serves ${siblings.length} other project(s): ${counts}`;
|
|
663
671
|
}
|
|
664
672
|
|
|
673
|
+
// review-correction provenance (#1048)
|
|
674
|
+
// ---------------------------------------------------------------------------
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* How many rounds of one run's correction chain print. A review ceiling is low
|
|
678
|
+
* single digits, so this bounds a pathological row rather than routinely
|
|
679
|
+
* truncating — and the last line printed says how many rounds it stands for,
|
|
680
|
+
* because a silently shortened chain is how a repeating pattern hides.
|
|
681
|
+
*/
|
|
682
|
+
const CORRECTION_CHAIN_LINES = 3;
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* A session lineage as a status row carries it. A ref is a session file path,
|
|
686
|
+
* and the leading directories are the same for every session in a fleet: the
|
|
687
|
+
* identifying half is the run and the file, so the line prints those and marks
|
|
688
|
+
* what it dropped with `…/` — the same honesty `firstLine` uses above, and the
|
|
689
|
+
* full ref stays in the durable row this line is projected from. Two lineages
|
|
690
|
+
* plus two models on one row is already at the width `omp-conductor status`
|
|
691
|
+
* can be read at; a full 60-character path twice is not.
|
|
692
|
+
*
|
|
693
|
+
* An absent ref reads `unrecorded`: rows written before #1045 know nothing
|
|
694
|
+
* about their own launch, and saying so is the only honest projection of one.
|
|
695
|
+
*/
|
|
696
|
+
function correctionRef(ref: string | undefined): string {
|
|
697
|
+
const value = ref?.trim() ?? "";
|
|
698
|
+
if (value === "") return "unrecorded";
|
|
699
|
+
const segments = value.split("/").filter((segment) => segment !== "");
|
|
700
|
+
const tail = segments.slice(-2).join("/");
|
|
701
|
+
return firstLine(segments.length > 2 ? `…/${tail}` : value, 60);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* The models of one correction round: what the daemon asked for and what the
|
|
706
|
+
* harness actually resolved. Those two differing is the whole finding — in
|
|
707
|
+
* #1035 the fleet had already switched worker models while every correction
|
|
708
|
+
* round replayed the exhausted session under the old one — so the arrow form is
|
|
709
|
+
* kept whenever they differ and collapsed only when they agree.
|
|
710
|
+
*
|
|
711
|
+
* A resumed round is worded differently on purpose. `resolvedModel` is written
|
|
712
|
+
* only by `Store.recordReviewCorrectionSession`, which refuses a round decided
|
|
713
|
+
* `resume-original` (a resumed round *is* its origin session), so a resume
|
|
714
|
+
* durably has a request and no resolution. Rendering that through the fresh
|
|
715
|
+
* wording — `requested X → ran unrecorded` — would read as a launch that lost
|
|
716
|
+
* its model, when what actually happened is that the daemon asked the harness
|
|
717
|
+
* to continue an existing transcript on X and nothing re-recorded the
|
|
718
|
+
* resolution.
|
|
719
|
+
*
|
|
720
|
+
* Every branch reads the round's OWN recorded values. The project config is
|
|
721
|
+
* deliberately not a parameter: rendering it would make a round dispatched
|
|
722
|
+
* under a since-replaced model report the replacement, which is exactly the
|
|
723
|
+
* silent fake #1048 names.
|
|
724
|
+
*/
|
|
725
|
+
function correctionModels(round: ReviewCorrectionRound): string {
|
|
726
|
+
const { requestedModel: requested, resolvedModel: resolved } = round;
|
|
727
|
+
if (round.launchMode === "resume-original") {
|
|
728
|
+
if (resolved !== undefined)
|
|
729
|
+
return requested === undefined || requested === resolved
|
|
730
|
+
? `ran ${resolved}`
|
|
731
|
+
: `ran ${resolved} (asked ${requested})`;
|
|
732
|
+
if (requested !== undefined)
|
|
733
|
+
return round.state === "pending"
|
|
734
|
+
? `to continue on ${requested}, not launched yet`
|
|
735
|
+
: `asked to continue on ${requested}`;
|
|
736
|
+
return "model unrecorded";
|
|
737
|
+
}
|
|
738
|
+
if (requested !== undefined && resolved !== undefined)
|
|
739
|
+
return requested === resolved ? `model ${resolved} as requested` : `requested ${requested} → ran ${resolved}`;
|
|
740
|
+
if (requested !== undefined)
|
|
741
|
+
return round.state === "pending"
|
|
742
|
+
? `requested ${requested}, not launched yet`
|
|
743
|
+
: `requested ${requested} → ran unrecorded`;
|
|
744
|
+
// A resolution with no request: the harness named what it ran, the decision
|
|
745
|
+
// row that asked for it predates #1045. Still not a guess in either direction.
|
|
746
|
+
if (resolved !== undefined) return `ran ${resolved} (request unrecorded)`;
|
|
747
|
+
return "model unrecorded";
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* One run's review-correction chain as status lines (#1048), newest round
|
|
752
|
+
* first, six-space indented like every other run annotation.
|
|
753
|
+
*
|
|
754
|
+
* Before this, a correction round rendered as `review-revision N` and a
|
|
755
|
+
* dispatch age: an operator could not tell whether conductor had resumed the
|
|
756
|
+
* diagnosed implementation transcript or launched a fresh correction, nor under
|
|
757
|
+
* which model — which is why #1035 read as idle while an exhausted session
|
|
758
|
+
* consumed every round the fleet's new model was never asked to run.
|
|
759
|
+
*
|
|
760
|
+
* The per-line `current`/`prior` marker is where the runtime/history split
|
|
761
|
+
* lives: `current` is the newest round that has not settled, the one whose
|
|
762
|
+
* elapsed time and cumulative turns the run line above reports; every other
|
|
763
|
+
* line is chain history and carries no runtime at all. That is deliberately a
|
|
764
|
+
* property of each line rather than a header, because a header explaining how
|
|
765
|
+
* to read the following lines is a line nobody reads.
|
|
766
|
+
*
|
|
767
|
+
* Exported so the projection can be asserted directly on hand-built rounds:
|
|
768
|
+
* the interesting cases (a differing requested/resolved pair, a legacy row with
|
|
769
|
+
* no provenance) are properties of one row, not of a whole fleet.
|
|
770
|
+
*/
|
|
771
|
+
export function formatReviewCorrections(rounds: readonly ReviewCorrectionRound[]): string[] {
|
|
772
|
+
if (rounds.length === 0) return [];
|
|
773
|
+
const newestFirst = [...rounds].sort((a, b) => b.round - a.round);
|
|
774
|
+
const shown = newestFirst.slice(0, CORRECTION_CHAIN_LINES);
|
|
775
|
+
const hidden = newestFirst.length - shown.length;
|
|
776
|
+
return shown.map((round, index) => {
|
|
777
|
+
const current = index === 0 && round.state !== "settled";
|
|
778
|
+
// The round's lifecycle as one phrase. `queued` covers both a round nobody
|
|
779
|
+
// has dispatched yet and one a restart returned to the queue — durably they
|
|
780
|
+
// are the same row, and status must not invent a distinction the store does
|
|
781
|
+
// not keep.
|
|
782
|
+
const state =
|
|
783
|
+
round.state === "settled"
|
|
784
|
+
? `settled ${round.outcome ?? "outcome unrecorded"}`
|
|
785
|
+
: round.state === "pending"
|
|
786
|
+
? "queued"
|
|
787
|
+
: "dispatched";
|
|
788
|
+
const provenance =
|
|
789
|
+
round.launchMode === undefined
|
|
790
|
+
? // A row written before #1045 recorded no launch decision. Status says
|
|
791
|
+
// so and stops: the alternative — reading today's configuration back
|
|
792
|
+
// as though it had been this round's — is a fabricated provenance,
|
|
793
|
+
// and an operator auditing #1035 would have believed it.
|
|
794
|
+
"provenance unknown (pre-#1045 round)"
|
|
795
|
+
: round.launchMode === "resume-original"
|
|
796
|
+
? // A resume continues one lineage, so there is one session to name:
|
|
797
|
+
// the implementation transcript the review diagnosed.
|
|
798
|
+
`resume-original resumed session ${correctionRef(round.originSessionRef)} ${correctionModels(round)}`
|
|
799
|
+
: // A fresh correction has two: the transcript that was reviewed, and
|
|
800
|
+
// the new session doing the correcting. Naming only one of them is
|
|
801
|
+
// how "fresh" became indistinguishable from "resumed".
|
|
802
|
+
`fresh-correction new session ${correctionRef(round.correctionSessionRef)} (diagnosed ${correctionRef(
|
|
803
|
+
round.originSessionRef,
|
|
804
|
+
)}) ${correctionModels(round)}`;
|
|
805
|
+
const tail = index === shown.length - 1 && hidden > 0 ? ` (+${hidden} earlier round(s))` : "";
|
|
806
|
+
return ` correction round ${round.round} ${current ? "current" : "prior"} (${state}) ${provenance}${tail}`;
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
665
810
|
|
|
666
811
|
function formatProjectBody(
|
|
667
812
|
s: StatusSnapshot,
|
|
@@ -867,6 +1012,12 @@ function formatProjectBody(
|
|
|
867
1012
|
: "failed at this head"
|
|
868
1013
|
} — unresolved findings; push a corrected head, or record a conductor_pr_review_clear for this exact head, before merge`,
|
|
869
1014
|
);
|
|
1015
|
+
// The correction chain (#1048), rendered from the same annotation helper as
|
|
1016
|
+
// the merge blocker above so it reaches BOTH surfaces that show a run: the
|
|
1017
|
+
// live lease and the preserved artifact. A round whose worker has finished
|
|
1018
|
+
// leaves the lease list, and that is precisely when an operator asks what
|
|
1019
|
+
// the last correction actually did.
|
|
1020
|
+
out.push(...formatReviewCorrections(s.reviewCorrections?.[r.id] ?? []));
|
|
870
1021
|
return out;
|
|
871
1022
|
};
|
|
872
1023
|
if (s.activeRuns.length === 0) {
|