@llm4ts/flow 2.8.0 → 2.9.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.
Files changed (51) hide show
  1. package/dist/Approval.d.ts +1 -1
  2. package/dist/Approval.d.ts.map +1 -1
  3. package/dist/Artifacts.d.ts +2 -2
  4. package/dist/Artifacts.d.ts.map +1 -1
  5. package/dist/BenchReport.d.ts +2 -2
  6. package/dist/BenchReport.d.ts.map +1 -1
  7. package/dist/CostLedger.d.ts +2 -2
  8. package/dist/CostLedger.d.ts.map +1 -1
  9. package/dist/Equiv.d.ts +3 -3
  10. package/dist/Equiv.d.ts.map +1 -1
  11. package/dist/Flow.d.ts +1 -1
  12. package/dist/Flow.d.ts.map +1 -1
  13. package/dist/FlowContext.d.ts +15 -1
  14. package/dist/FlowContext.d.ts.map +1 -1
  15. package/dist/FlowContext.js.map +1 -1
  16. package/dist/FlowError.d.ts +24 -1
  17. package/dist/FlowError.d.ts.map +1 -1
  18. package/dist/FlowError.js +29 -0
  19. package/dist/FlowError.js.map +1 -1
  20. package/dist/Judgment.d.ts +2 -2
  21. package/dist/Judgment.d.ts.map +1 -1
  22. package/dist/Persistence.d.ts +2 -2
  23. package/dist/Persistence.d.ts.map +1 -1
  24. package/dist/PlanExecution.d.ts +1 -1
  25. package/dist/PlanExecution.d.ts.map +1 -1
  26. package/dist/ProgramJudge.d.ts +1 -1
  27. package/dist/ProgramJudge.d.ts.map +1 -1
  28. package/dist/Replay.d.ts +2 -2
  29. package/dist/Replay.d.ts.map +1 -1
  30. package/dist/Review.d.ts +3 -3
  31. package/dist/Review.d.ts.map +1 -1
  32. package/dist/ReviewCache.d.ts +1 -1
  33. package/dist/ReviewCache.d.ts.map +1 -1
  34. package/dist/Roster.d.ts +163 -0
  35. package/dist/Roster.d.ts.map +1 -0
  36. package/dist/Roster.js +560 -0
  37. package/dist/Roster.js.map +1 -0
  38. package/dist/RosterSeats.d.ts +71 -0
  39. package/dist/RosterSeats.d.ts.map +1 -0
  40. package/dist/RosterSeats.js +144 -0
  41. package/dist/RosterSeats.js.map +1 -0
  42. package/dist/Stories.d.ts +9 -5
  43. package/dist/Stories.d.ts.map +1 -1
  44. package/dist/Stories.js +70 -10
  45. package/dist/Stories.js.map +1 -1
  46. package/package.json +4 -2
  47. package/src/FlowContext.ts +19 -1
  48. package/src/FlowError.ts +32 -0
  49. package/src/Roster.ts +824 -0
  50. package/src/RosterSeats.ts +306 -0
  51. package/src/Stories.ts +97 -14
@@ -0,0 +1,71 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Scope from "effect/Scope";
3
+ import type { LlmServiceShape } from "@llm4ts/core/LlmService";
4
+ import { type ConnectorCapabilities } from "@llm4ts/core/Models";
5
+ import { type FlowError } from "./FlowError.ts";
6
+ import { type FlowEventsShape } from "./FlowEvents.ts";
7
+ import { type ExecutorSpec, type Role, type RosterShape } from "./Roster.ts";
8
+ /** An executor's connector, as the runner resolved it for a role and a directory. */
9
+ export type RosterSeat = LlmServiceShape & {
10
+ readonly capabilities: ConnectorCapabilities;
11
+ };
12
+ export interface SeatSource {
13
+ readonly seatFor: (executor: ExecutorSpec, role: Role, workDir: string) => Effect.Effect<RosterSeat, FlowError>;
14
+ }
15
+ /** What a flow context knows about the roster behind its seats. */
16
+ export interface RosterView {
17
+ /** A seat for `role`, leased per call (independent of this context's coder where possible). */
18
+ readonly forRole: (role: Role) => LlmServiceShape;
19
+ /** Free slots for `role` right now. */
20
+ readonly available: (role: Role) => Effect.Effect<number>;
21
+ /** Configured slots for `role`. */
22
+ readonly slots: (role: Role) => number;
23
+ /** The executor holding this context's coder, once leased. */
24
+ readonly executor: Effect.Effect<string | undefined>;
25
+ /** Every executor that held this context's coder, in order (handovers). */
26
+ readonly history: Effect.Effect<ReadonlyArray<string>>;
27
+ }
28
+ export interface RosterSeatOptions {
29
+ readonly events: FlowEventsShape;
30
+ /** Executors this seat must not use: the context's coder, for independence. */
31
+ readonly avoid?: Effect.Effect<ReadonlyArray<string>>;
32
+ /** Used without a slot when nobody outside `avoid` can ever serve the role. */
33
+ readonly borrow?: Effect.Effect<ExecutorSpec | undefined>;
34
+ /** Who is asking, for the events. */
35
+ readonly label?: string;
36
+ }
37
+ /**
38
+ * A seat for `role` that leases an executor for each call and releases it
39
+ * when the call (or its stream) ends. A failure is reported to the roster,
40
+ * which excludes the executor when the failure says it is out of quota or
41
+ * down; the call itself still fails — a per-call seat has nothing to hand
42
+ * over, and the retry layer below has already had its turn.
43
+ */
44
+ export declare const rosterSeat: (roster: RosterShape, source: SeatSource, role: Role, workDir: string, options: RosterSeatOptions) => LlmServiceShape;
45
+ export interface HeldCoderOptions {
46
+ readonly events: FlowEventsShape;
47
+ /** Lease now (a story waiting for capacity), or on the first call (a flow's root coder). */
48
+ readonly eager: boolean;
49
+ /** The executor this context held before, taken first while free. */
50
+ readonly prefer?: string;
51
+ /** Handovers before a failure surfaces. Default 2. */
52
+ readonly maxHandovers?: number;
53
+ readonly label?: string;
54
+ }
55
+ export interface HeldCoder {
56
+ readonly service: LlmServiceShape;
57
+ readonly executor: Effect.Effect<string | undefined>;
58
+ readonly lease: Effect.Effect<ExecutorSpec | undefined>;
59
+ readonly history: Effect.Effect<ReadonlyArray<string>>;
60
+ }
61
+ export declare const takeoverNote: (from: string, reason: string) => string;
62
+ /**
63
+ * The coder of one context (a story, or a flow's root): one executor held
64
+ * for the context's lifetime. When a call fails in a way that takes that
65
+ * executor out of the round (a usage limit, a dead serving engine), the
66
+ * lease is released, the next coder leased, and the call repeated there
67
+ * with a takeover note — the chat history the flow replays, the task
68
+ * checkpoint and the working tree carry the work across.
69
+ */
70
+ export declare const makeHeldCoder: (roster: RosterShape, source: SeatSource, workDir: string, options: HeldCoderOptions) => Effect.Effect<HeldCoder, import("./FlowError.ts").PersistenceError | import("./FlowError.ts").PlanParseError | import("./FlowError.ts").UnsupportedSchemaVersion | import("./FlowError.ts").WorkspacePathError | import("./FlowError.ts").WorkspaceLimitError | import("./FlowError.ts").WorkspaceIoError | import("./FlowError.ts").FlowAborted | import("./FlowError.ts").ProcessError | import("./FlowError.ts").FlowLlmError | import("./FlowError.ts").FlowCapabilityDenied | import("./FlowError.ts").ColumnNotFound | import("./FlowError.ts").BudgetExceeded | import("./FlowError.ts").StoryPlanInvalid | import("./FlowError.ts").PerimeterViolation | import("./FlowError.ts").MissingDependency | import("./FlowError.ts").MergeConflict | import("./FlowError.ts").EpicCheckoutDirty | import("./FlowError.ts").RosterInvalid | import("./FlowError.ts").RosterExhausted | import("./FlowError.ts").StoryFailed | import("./FlowError.ts").DecisionsInvalid | import("./FlowError.ts").OpenPointsPending | import("./FlowError.ts").ContractConflict, Scope.Scope>;
71
+ //# sourceMappingURL=RosterSeats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RosterSeats.d.ts","sourceRoot":"","sources":["../src/RosterSeats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ // Seats over the executor roster (ADR 0019). A flow keeps calling an
2
+ // `LlmServiceShape`; behind it, a reasoning seat leases an executor for each
3
+ // call, and a coder seat holds one executor for its context and hands over
4
+ // to the next when that one is taken out of the round. The runner supplies
5
+ // `seatFor`, which turns an executor into a connector rooted in a directory.
6
+ import * as Effect from "effect/Effect";
7
+ import * as Ref from "effect/Ref";
8
+ import * as Scope from "effect/Scope";
9
+ import * as Semaphore from "effect/Semaphore";
10
+ import * as Stream from "effect/Stream";
11
+ import { ConfigError } from "@llm4ts/core/Errors";
12
+ import { Message } from "@llm4ts/core/Models";
13
+ import { describeFlowError } from "./FlowError.js";
14
+ import { Info } from "./FlowEvents.js";
15
+ import { describeExclusion, hasRole } from "./Roster.js";
16
+ /** A roster failure seen through an `LlmServiceShape`, whose failures are `LlmError`s. */
17
+ const asLlmError = (error) => ConfigError.make({ message: describeFlowError(error) });
18
+ const say = (events, message) => events.publish(Info.make({ message: `roster: ${message}` }));
19
+ const withNote = (note, prompt) => note === undefined ? prompt : `${note}\n\n${prompt}`;
20
+ const withNoteOnLast = (note, messages) => {
21
+ const last = messages.at(-1);
22
+ if (note === undefined || last === undefined) {
23
+ return messages;
24
+ }
25
+ return [
26
+ ...messages.slice(0, -1),
27
+ Message.make({ ...last, content: withNote(note, last.content) })
28
+ ];
29
+ };
30
+ const serviceOver = (runner) => ({
31
+ executeStream: (prompt) => runner.stream((seat, note) => seat.executeStream(withNote(note, prompt))),
32
+ executeStreamWithHistory: (messages) => runner.stream((seat, note) => seat.executeStreamWithHistory(withNoteOnLast(note, messages))),
33
+ executeWithTools: (prompt, tools) => runner.run((seat, note) => seat.executeWithTools(withNote(note, prompt), tools)),
34
+ executeStructured: (prompt, schema, jsonSchema) => runner.run((seat, note) => seat.executeStructured(withNote(note, prompt), schema, jsonSchema)),
35
+ executeStructuredWithUsage: (prompt, schema, jsonSchema) => runner.run((seat, note) => seat.executeStructuredWithUsage(withNote(note, prompt), schema, jsonSchema)),
36
+ scoreLabels: (prompt, labels) => runner.run((seat) => seat.scoreLabels(prompt, labels)),
37
+ isAvailable: runner.isAvailable
38
+ });
39
+ /**
40
+ * A seat for `role` that leases an executor for each call and releases it
41
+ * when the call (or its stream) ends. A failure is reported to the roster,
42
+ * which excludes the executor when the failure says it is out of quota or
43
+ * down; the call itself still fails — a per-call seat has nothing to hand
44
+ * over, and the retry layer below has already had its turn.
45
+ */
46
+ export const rosterSeat = (roster, source, role, workDir, options) => {
47
+ const acquire = Effect.gen(function* () {
48
+ const avoid = options.avoid === undefined ? [] : yield* options.avoid;
49
+ if (avoid.length > 0 && !(yield* roster.canEverServe(role, avoid))) {
50
+ const borrowed = options.borrow === undefined ? undefined : yield* options.borrow;
51
+ if (borrowed !== undefined && hasRole(borrowed, role)) {
52
+ yield* say(options.events, `${borrowed.id} takes ${role}${options.label === undefined ? "" : ` for ${options.label}`} on its own coder's slot — not independent (no other executor can take ${role})`);
53
+ return borrowed;
54
+ }
55
+ }
56
+ const lease = yield* roster
57
+ .lease(role, { avoid, ...(options.label === undefined ? {} : { label: options.label }) })
58
+ .pipe(Effect.mapError(asLlmError));
59
+ return lease.executor;
60
+ });
61
+ const seatOf = (executor) => source.seatFor(executor, role, workDir).pipe(Effect.mapError(asLlmError));
62
+ const reportOn = (executor) => (error) => Effect.asVoid(roster.report(executor.id, error));
63
+ return serviceOver({
64
+ run: (use) => Effect.scoped(Effect.gen(function* () {
65
+ const executor = yield* acquire;
66
+ const seat = yield* seatOf(executor);
67
+ return yield* use(seat, undefined).pipe(Effect.tapError(reportOn(executor)));
68
+ })),
69
+ stream: (use) => Stream.unwrap(Effect.gen(function* () {
70
+ const executor = yield* acquire;
71
+ const seat = yield* seatOf(executor);
72
+ return use(seat, undefined).pipe(Stream.tapError(reportOn(executor)));
73
+ })),
74
+ isAvailable: roster.canEverServe(role)
75
+ });
76
+ };
77
+ export const takeoverNote = (from, reason) => [
78
+ `(You are taking over this work from another coding agent ('${from}'), which had to stop:`,
79
+ `${reason}. Its changes so far are in the working tree: inspect them (git status, git diff)`,
80
+ "before continuing, and carry on from there.)"
81
+ ].join(" ");
82
+ /**
83
+ * The coder of one context (a story, or a flow's root): one executor held
84
+ * for the context's lifetime. When a call fails in a way that takes that
85
+ * executor out of the round (a usage limit, a dead serving engine), the
86
+ * lease is released, the next coder leased, and the call repeated there
87
+ * with a takeover note — the chat history the flow replays, the task
88
+ * checkpoint and the working tree carry the work across.
89
+ */
90
+ export const makeHeldCoder = Effect.fn("@llm4ts/flow/RosterSeats.heldCoder")(function* (roster, source, workDir, options) {
91
+ const scope = yield* Effect.scope;
92
+ const current = yield* Ref.make(undefined);
93
+ const history = yield* Ref.make([]);
94
+ const handovers = yield* Ref.make(0);
95
+ const prefer = yield* Ref.make(options.prefer);
96
+ const gate = yield* Semaphore.make(1);
97
+ const maxHandovers = options.maxHandovers ?? 2;
98
+ const acquire = gate.withPermit(Effect.gen(function* () {
99
+ const held = yield* Ref.get(current);
100
+ if (held !== undefined) {
101
+ return held;
102
+ }
103
+ const preferred = yield* Ref.get(prefer);
104
+ const lease = yield* roster
105
+ .lease("coder", {
106
+ ...(preferred === undefined ? {} : { prefer: preferred }),
107
+ ...(options.label === undefined ? {} : { label: options.label })
108
+ })
109
+ .pipe(Scope.provide(scope));
110
+ const seat = yield* source.seatFor(lease.executor, "coder", workDir);
111
+ const next = { lease, seat };
112
+ yield* Ref.set(current, next);
113
+ yield* Ref.update(history, (ids) => ids.at(-1) === lease.executor.id ? ids : [...ids, lease.executor.id]);
114
+ return next;
115
+ }));
116
+ if (options.eager) {
117
+ yield* acquire;
118
+ }
119
+ /** Decides whether a failure hands over; returns the takeover note, or fails with the error. */
120
+ const handover = (held, error) => Effect.gen(function* () {
121
+ const exclusion = yield* roster.report(held.lease.executor.id, error);
122
+ if (exclusion === undefined || (yield* Ref.get(handovers)) >= maxHandovers) {
123
+ return yield* Effect.fail(error);
124
+ }
125
+ yield* Ref.update(handovers, (count) => count + 1);
126
+ yield* Ref.set(prefer, undefined);
127
+ yield* held.lease.release;
128
+ yield* Ref.set(current, undefined);
129
+ yield* say(options.events, `${options.label ?? workDir}: handing the coder over from ${held.lease.executor.id} (${describeExclusion(exclusion)})`);
130
+ return takeoverNote(held.lease.executor.id, exclusion.reason);
131
+ });
132
+ const run = (use, note) => Effect.gen(function* () {
133
+ const held = yield* acquire.pipe(Effect.mapError(asLlmError));
134
+ return yield* use(held.seat, note).pipe(Effect.catch((error) => Effect.flatMap(handover(held, error), (next) => run(use, next))));
135
+ });
136
+ const stream = (use, note) => Stream.unwrap(Effect.map(acquire.pipe(Effect.mapError(asLlmError)), (held) => use(held.seat, note).pipe(Stream.catchIf(() => true, (error) => Stream.unwrap(Effect.map(handover(held, error), (next) => stream(use, next)))))));
137
+ return {
138
+ service: serviceOver({ run, stream, isAvailable: roster.canEverServe("coder") }),
139
+ executor: Effect.map(Ref.get(current), (held) => held?.lease.executor.id),
140
+ lease: Effect.map(Ref.get(current), (held) => held?.lease.executor),
141
+ history: Ref.get(history)
142
+ };
143
+ });
144
+ //# sourceMappingURL=RosterSeats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RosterSeats.js","sourceRoot":"","sources":["../src/RosterSeats.ts"],"names":[],"mappings":"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"}
package/dist/Stories.d.ts CHANGED
@@ -5,7 +5,7 @@ import type * as Scope from "effect/Scope";
5
5
  import type { LlmServiceShape } from "@llm4ts/core/LlmService";
6
6
  import type { TokenUsage } from "@llm4ts/core/Models";
7
7
  import { type BoardSyncShape } from "./BoardSync.ts";
8
- import type { FlowContextShape } from "./FlowContext.ts";
8
+ import type { ContextOptions, FlowContextShape } from "./FlowContext.ts";
9
9
  import { EpicCheckoutDirty, MissingDependency, PerimeterViolation, StoryFailed, type FlowError } from "./FlowError.ts";
10
10
  import { type PlainFileStoreShape } from "./Persistence.ts";
11
11
  import { Plan } from "./Plan.ts";
@@ -28,6 +28,8 @@ declare const StoryState_base: Schema.Class<StoryState, Schema.Struct<{
28
28
  readonly branch: Schema.String;
29
29
  readonly worktree: Schema.String;
30
30
  readonly status: Schema.Literals<readonly ["started", "merged", "failed"]>;
31
+ /** The roster executor that last held the story's coder (ADR 0019); preferred on resume. */
32
+ readonly executor: Schema.optionalKey<Schema.String>;
31
33
  }>, {}>;
32
34
  /** What the executor remembers about a story between runs. */
33
35
  export declare class StoryState extends StoryState_base {
@@ -44,6 +46,8 @@ declare const StoryOutcome_base: Schema.Class<StoryOutcome, Schema.Struct<{
44
46
  /** Failure reason, or what a waiting story waits for. */
45
47
  readonly reason: Schema.optionalKey<Schema.String>;
46
48
  readonly judge: Schema.optionalKey<Schema.String>;
49
+ /** The roster executor(s) that coded the story, in order ("codex → claude"). */
50
+ readonly executor: Schema.optionalKey<Schema.String>;
47
51
  /** ESTIMATES, never measurements (ADR 0012). */
48
52
  readonly estimatedTokens: Schema.optionalKey<Schema.Int>;
49
53
  readonly estimatedCostUsd: Schema.optionalKey<Schema.Number>;
@@ -122,7 +126,7 @@ export interface StoriesOptions {
122
126
  /** Default `epic/<epicId>`. */
123
127
  readonly epicBranch?: string;
124
128
  /** Seats rooted in a worktree; the executor never resolves seats itself. */
125
- readonly contextFor: (workDir: string) => Effect.Effect<StorySeats, FlowError, Scope.Scope>;
129
+ readonly contextFor: (workDir: string, options?: ContextOptions) => Effect.Effect<StorySeats, FlowError, Scope.Scope>;
126
130
  readonly board: BoardSyncShape;
127
131
  /**
128
132
  * Prepares a worktree before its coder runs — a fresh checkout has no
@@ -133,7 +137,7 @@ export interface StoriesOptions {
133
137
  /** The target's gates, run in a worktree per task and on the epic checkout after each merge. */
134
138
  readonly gates: (workDir: string) => Effect.Effect<ReviewResult, FlowError>;
135
139
  /** Story-level judge over the branch's diff against the epic branch; omit to skip. */
136
- readonly judge?: (story: Story, diff: string) => Effect.Effect<ReviewResult, FlowError>;
140
+ readonly judge?: (story: Story, diff: string, seats: StorySeats) => Effect.Effect<ReviewResult, FlowError>;
137
141
  /** Judge attempts, each but the last followed by one coder feedback round. Default 2. */
138
142
  readonly judgeRounds?: number;
139
143
  /** Extra system context per story (house rules, shared read-only excerpts). */
@@ -151,7 +155,7 @@ export interface StoriesOptions {
151
155
  * story owns, or a parallel story's). `real: false` sends the coder back
152
156
  * once with the reason. Omit to accept every such claim.
153
157
  */
154
- readonly verifyBlocked?: (story: Story, need: string, workDir: string) => Effect.Effect<BlockedVerdict, FlowError>;
158
+ readonly verifyBlocked?: (story: Story, need: string, workDir: string, seats: StorySeats) => Effect.Effect<BlockedVerdict, FlowError>;
155
159
  /**
156
160
  * Waits until a serving engine that went down (`isOutageMessage`) is back;
157
161
  * the story it interrupted is then retried once. Omit, or fail it, and the
@@ -159,6 +163,6 @@ export interface StoriesOptions {
159
163
  */
160
164
  readonly awaitRecovery?: (reason: string) => Effect.Effect<void, FlowError>;
161
165
  }
162
- export declare const implementStoriesFlow: (context: FlowContextShape, options: StoriesOptions) => Effect.Effect<EpicReport, import("./FlowError.ts").PersistenceError | import("./FlowError.ts").PlanParseError | import("./FlowError.ts").UnsupportedSchemaVersion | import("./FlowError.ts").WorkspacePathError | import("./FlowError.ts").WorkspaceLimitError | import("./FlowError.ts").WorkspaceIoError | import("./FlowError.ts").FlowAborted | import("./FlowError.ts").ProcessError | import("./FlowError.ts").FlowLlmError | import("./FlowError.ts").FlowCapabilityDenied | import("./FlowError.ts").ColumnNotFound | import("./FlowError.ts").BudgetExceeded | import("./FlowError.ts").StoryPlanInvalid | PerimeterViolation | MissingDependency | import("./FlowError.ts").MergeConflict | EpicCheckoutDirty | StoryFailed | import("./FlowError.ts").DecisionsInvalid | import("./FlowError.ts").OpenPointsPending | import("./FlowError.ts").ContractConflict, never>;
166
+ export declare const implementStoriesFlow: (context: FlowContextShape, options: StoriesOptions) => Effect.Effect<EpicReport, import("./FlowError.ts").PersistenceError | import("./FlowError.ts").PlanParseError | import("./FlowError.ts").UnsupportedSchemaVersion | import("./FlowError.ts").WorkspacePathError | import("./FlowError.ts").WorkspaceLimitError | import("./FlowError.ts").WorkspaceIoError | import("./FlowError.ts").FlowAborted | import("./FlowError.ts").ProcessError | import("./FlowError.ts").FlowLlmError | import("./FlowError.ts").FlowCapabilityDenied | import("./FlowError.ts").ColumnNotFound | import("./FlowError.ts").BudgetExceeded | import("./FlowError.ts").StoryPlanInvalid | PerimeterViolation | MissingDependency | import("./FlowError.ts").MergeConflict | EpicCheckoutDirty | import("./FlowError.ts").RosterInvalid | import("./FlowError.ts").RosterExhausted | StoryFailed | import("./FlowError.ts").DecisionsInvalid | import("./FlowError.ts").OpenPointsPending | import("./FlowError.ts").ContractConflict, never>;
163
167
  export {};
164
168
  //# sourceMappingURL=Stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Stories.d.ts","sourceRoot":"","sources":["../src/Stories.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Stories.d.ts","sourceRoot":"","sources":["../src/Stories.ts"],"names":[],"mappings":"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"}
package/dist/Stories.js CHANGED
@@ -14,7 +14,7 @@ import * as Stream from "effect/Stream";
14
14
  import { BoardItem } from "./BoardSync.js";
15
15
  import { makeChat } from "./Chat.js";
16
16
  import { implementPlanFlow } from "./Flow.js";
17
- import { describeFlowError, EpicCheckoutDirty, MissingDependency, PerimeterViolation, StoryFailed } from "./FlowError.js";
17
+ import { describeFlowError, rosterExhaustedPrefix, EpicCheckoutDirty, MissingDependency, PerimeterViolation, StoryFailed } from "./FlowError.js";
18
18
  import { Info } from "./FlowEvents.js";
19
19
  import { statusPaths } from "./GitTool.js";
20
20
  import { checkPerimeter, enforcePerimeter, isWithinPerimeter, perimeterGate, strayTasks } from "./Perimeter.js";
@@ -36,7 +36,9 @@ export class StoryState extends Schema.Class("StoryState")({
36
36
  hash: Schema.String,
37
37
  branch: Schema.String,
38
38
  worktree: Schema.String,
39
- status: StoryStatus
39
+ status: StoryStatus,
40
+ /** The roster executor that last held the story's coder (ADR 0019); preferred on resume. */
41
+ executor: Schema.optionalKey(Schema.String)
40
42
  }) {
41
43
  }
42
44
  export const EpicReportVersion = 1;
@@ -50,6 +52,8 @@ export class StoryOutcome extends Schema.Class("StoryOutcome")({
50
52
  /** Failure reason, or what a waiting story waits for. */
51
53
  reason: Schema.optionalKey(Schema.String),
52
54
  judge: Schema.optionalKey(Schema.String),
55
+ /** The roster executor(s) that coded the story, in order ("codex → claude"). */
56
+ executor: Schema.optionalKey(Schema.String),
53
57
  /** ESTIMATES, never measurements (ADR 0012). */
54
58
  estimatedTokens: Schema.optionalKey(Schema.Int),
55
59
  estimatedCostUsd: Schema.optionalKey(Schema.Number)
@@ -93,6 +97,25 @@ export const renderEpicReport = (report) => {
93
97
  if (cost.length > 0) {
94
98
  lines.push(`- Estimated cost: ${money(cost.reduce((sum, value) => sum + value, 0))}`);
95
99
  }
100
+ const coded = report.stories.filter((story) => story.executor !== undefined);
101
+ if (coded.length > 0) {
102
+ // A story is counted for the executor that finished it; a handover chain
103
+ // is shown in full next to the story.
104
+ const byExecutor = new Map();
105
+ for (const story of coded) {
106
+ const chain = story.executor ?? "";
107
+ const last = chain.split(" → ").at(-1) ?? chain;
108
+ const entry = byExecutor.get(last) ?? { stories: [], tokens: 0, cost: 0 };
109
+ entry.stories.push(chain === last ? story.id : `${story.id} (${chain})`);
110
+ entry.tokens += story.estimatedTokens ?? 0;
111
+ entry.cost += story.estimatedCostUsd ?? 0;
112
+ byExecutor.set(last, entry);
113
+ }
114
+ lines.push("", "## By executor", "");
115
+ for (const [executor, entry] of byExecutor) {
116
+ lines.push(`- ${executor}: ${entry.stories.join(", ")} — ~${entry.tokens} tokens, ${money(entry.cost)} (estimated)`);
117
+ }
118
+ }
96
119
  lines.push("");
97
120
  return lines.join("\n");
98
121
  };
@@ -416,7 +439,15 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
416
439
  });
417
440
  /** Everything that happens inside a story's worktree: plan, implement, judge, perimeter. */
418
441
  const implementStory = Effect.fn("@llm4ts/flow/Stories.implementStory")(function* (story, state) {
419
- const seats = yield* options.contextFor(state.worktree);
442
+ const seats = yield* options.contextFor(state.worktree, {
443
+ label: `story ${story.id}`,
444
+ ...(state.executor === undefined ? {} : { prefer: state.executor })
445
+ });
446
+ const roster = seats.context.roster;
447
+ const coderExecutor = roster === undefined ? undefined : yield* roster.executor;
448
+ if (coderExecutor !== undefined && coderExecutor !== state.executor) {
449
+ yield* saveVersioned(files, statePath(story), StoryStateVersion, StoryState, StoryState.make({ ...state, executor: coderExecutor }));
450
+ }
420
451
  const blocked = yield* Ref.make(undefined);
421
452
  const pushbacks = yield* Ref.make(0);
422
453
  const storyContext = {
@@ -474,7 +505,7 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
474
505
  let rebuttal = blockedRebuttal(plan, story, need);
475
506
  if (rebuttal === undefined && options.verifyBlocked !== undefined) {
476
507
  const verdict = yield* options
477
- .verifyBlocked(story, need, state.worktree)
508
+ .verifyBlocked(story, need, state.worktree, watchedSeats)
478
509
  .pipe(Effect.catch(() => Effect.succeed(undefined)));
479
510
  rebuttal = verdict === undefined || verdict.real ? undefined : verdict.reason;
480
511
  }
@@ -564,7 +595,7 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
564
595
  // a model asked to score an empty diff scores the prompt instead.
565
596
  return yield* failed(story, "the story branch has no changes against the epic branch");
566
597
  }
567
- const verdict = yield* judge(story, diff);
598
+ const verdict = yield* judge(story, diff, watchedSeats);
568
599
  if (verdict.isClean) {
569
600
  judgeNote = `judge cleared (round ${round})`;
570
601
  break;
@@ -584,7 +615,12 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
584
615
  const changed = yield* git.changedFilesVsBase(epicBranch);
585
616
  yield* enforcePerimeter(changed, story);
586
617
  const totals = seats.totals === undefined ? undefined : yield* seats.totals;
587
- return { judge: judgeNote, totals };
618
+ const history = roster === undefined ? [] : yield* roster.history;
619
+ return {
620
+ judge: judgeNote,
621
+ totals,
622
+ executor: history.length === 0 ? undefined : history.join(" → ")
623
+ };
588
624
  });
589
625
  const runStory = Effect.fn("@llm4ts/flow/Stories.runStory")(function* (story) {
590
626
  yield* board.start(story.id);
@@ -601,13 +637,19 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
601
637
  }
602
638
  const result = yield* Effect.scoped(implementStory(story, state));
603
639
  yield* integrate(story, state.branch);
604
- yield* saveVersioned(files, statePath(story), StoryStateVersion, StoryState, StoryState.make({ ...state, status: "merged" }));
640
+ const last = result.executor?.split(" → ").at(-1);
641
+ yield* saveVersioned(files, statePath(story), StoryStateVersion, StoryState, StoryState.make({
642
+ ...state,
643
+ status: "merged",
644
+ ...(last === undefined ? {} : { executor: last })
645
+ }));
605
646
  return StoryOutcome.make({
606
647
  id: story.id,
607
648
  title: story.title,
608
649
  status: "done",
609
650
  branch: state.branch,
610
651
  ...(result.judge === undefined ? {} : { judge: result.judge }),
652
+ ...(result.executor === undefined ? {} : { executor: result.executor }),
611
653
  ...(result.totals === undefined ? {} : { estimatedTokens: result.totals.total }),
612
654
  ...(result.totals?.costUsd === undefined ? {} : { estimatedCostUsd: result.totals.costUsd })
613
655
  });
@@ -620,7 +662,9 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
620
662
  if (stored !== undefined && stored.status !== "merged") {
621
663
  yield* saveVersioned(files, path, StoryStateVersion, StoryState, StoryState.make({ ...stored, status: "failed" })).pipe(Effect.ignore);
622
664
  }
623
- const interruption = error._tag === "EpicCheckoutDirty"
665
+ const interruption = error._tag === "EpicCheckoutDirty" ||
666
+ error._tag === "RosterExhausted" ||
667
+ reason.includes(rosterExhaustedPrefix)
624
668
  ? "halt"
625
669
  : isOutageMessage(reason)
626
670
  ? "outage"
@@ -677,7 +721,14 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
677
721
  if (halted === undefined) {
678
722
  const current = yield* progress;
679
723
  const ready = readyStories(plan, current);
680
- const slots = concurrency - current.running.size;
724
+ // With a roster, never launch more stories than there are free
725
+ // coders; with nothing running, launch one anyway — its lease waits
726
+ // for the first executor to come back (ADR 0019).
727
+ const free = context.roster === undefined
728
+ ? Number.POSITIVE_INFINITY
729
+ : yield* context.roster.available("coder");
730
+ const budget = Math.min(concurrency - current.running.size, free);
731
+ const slots = current.running.size === 0 && ready.length > 0 ? Math.max(1, budget) : budget;
681
732
  for (const story of ready.slice(0, Math.max(0, slots))) {
682
733
  yield* Ref.update(running, (set) => new Set([...set, story.id]));
683
734
  yield* Effect.forkScoped(attempt(story).pipe(Effect.flatMap((completion) => Queue.offer(completions, completion))));
@@ -718,7 +769,16 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
718
769
  if (outcome.status === "done") {
719
770
  yield* board.complete(outcome.id, {
720
771
  ...(outcome.branch === undefined ? {} : { branch: outcome.branch }),
721
- ...(outcome.judge === undefined ? {} : { detail: outcome.judge }),
772
+ ...(outcome.judge === undefined && outcome.executor === undefined
773
+ ? {}
774
+ : {
775
+ detail: [
776
+ outcome.judge,
777
+ outcome.executor === undefined ? undefined : `coder ${outcome.executor}`
778
+ ]
779
+ .filter((part) => part !== undefined)
780
+ .join(" · ")
781
+ }),
722
782
  ...(outcome.estimatedTokens === undefined
723
783
  ? {}
724
784
  : { estimatedTokens: outcome.estimatedTokens }),