@autodidacticon/orchestrator 0.0.0-stage → 0.1.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 (122) hide show
  1. package/README.md +144 -2
  2. package/dist/src/agent/api.js +14 -0
  3. package/dist/src/agent/api.js.map +1 -0
  4. package/dist/src/agent/mcp-shim.js +104 -0
  5. package/dist/src/agent/mcp-shim.js.map +1 -0
  6. package/dist/src/agent/seal.js +28 -0
  7. package/dist/src/agent/seal.js.map +1 -0
  8. package/dist/src/agent/server.js +96 -0
  9. package/dist/src/agent/server.js.map +1 -0
  10. package/dist/src/brief/compose.js +114 -0
  11. package/dist/src/brief/compose.js.map +1 -0
  12. package/dist/src/brief/remediate.js +14 -0
  13. package/dist/src/brief/remediate.js.map +1 -0
  14. package/dist/src/brief/template.js +35 -0
  15. package/dist/src/brief/template.js.map +1 -0
  16. package/dist/src/brief/validate.js +3 -0
  17. package/dist/src/brief/validate.js.map +1 -0
  18. package/dist/src/config.js +141 -0
  19. package/dist/src/config.js.map +1 -0
  20. package/dist/src/crash.js +68 -0
  21. package/dist/src/crash.js.map +1 -0
  22. package/dist/src/entry.js +21 -0
  23. package/dist/src/entry.js.map +1 -0
  24. package/dist/src/graph/diff.js +54 -0
  25. package/dist/src/graph/diff.js.map +1 -0
  26. package/dist/src/graph/load.js +125 -0
  27. package/dist/src/graph/load.js.map +1 -0
  28. package/dist/src/graph/model.js +15 -0
  29. package/dist/src/graph/model.js.map +1 -0
  30. package/dist/src/graph/query.js +275 -0
  31. package/dist/src/graph/query.js.map +1 -0
  32. package/dist/src/graph/source.js +43 -0
  33. package/dist/src/graph/source.js.map +1 -0
  34. package/dist/src/health/budgets.js +49 -0
  35. package/dist/src/health/budgets.js.map +1 -0
  36. package/dist/src/health/progress.js +43 -0
  37. package/dist/src/health/progress.js.map +1 -0
  38. package/dist/src/health/stuck.js +58 -0
  39. package/dist/src/health/stuck.js.map +1 -0
  40. package/dist/src/import/mapping.js +191 -0
  41. package/dist/src/import/mapping.js.map +1 -0
  42. package/dist/src/index.js +31 -0
  43. package/dist/src/index.js.map +1 -0
  44. package/dist/src/launch/launch.js +114 -0
  45. package/dist/src/launch/launch.js.map +1 -0
  46. package/dist/src/launch/recovery.js +115 -0
  47. package/dist/src/launch/recovery.js.map +1 -0
  48. package/dist/src/loop/core.js +2 -0
  49. package/dist/src/loop/core.js.map +1 -0
  50. package/dist/src/loop/orchestrator.js +1420 -0
  51. package/dist/src/loop/orchestrator.js.map +1 -0
  52. package/dist/src/loop/schedule.js +143 -0
  53. package/dist/src/loop/schedule.js.map +1 -0
  54. package/dist/src/loop/tick.js +418 -0
  55. package/dist/src/loop/tick.js.map +1 -0
  56. package/dist/src/main.js +184 -0
  57. package/dist/src/main.js.map +1 -0
  58. package/dist/src/operator/cli.js +237 -0
  59. package/dist/src/operator/cli.js.map +1 -0
  60. package/dist/src/operator/mcp-server.js +315 -0
  61. package/dist/src/operator/mcp-server.js.map +1 -0
  62. package/dist/src/operator/mcp.js +31 -0
  63. package/dist/src/operator/mcp.js.map +1 -0
  64. package/dist/src/operator/routes.js +365 -0
  65. package/dist/src/operator/routes.js.map +1 -0
  66. package/dist/src/runner/claude-code.js +82 -0
  67. package/dist/src/runner/claude-code.js.map +1 -0
  68. package/dist/src/runner/inproc.js +43 -0
  69. package/dist/src/runner/inproc.js.map +1 -0
  70. package/dist/src/runner/runner.js +2 -0
  71. package/dist/src/runner/runner.js.map +1 -0
  72. package/dist/src/runner/subprocess.js +131 -0
  73. package/dist/src/runner/subprocess.js.map +1 -0
  74. package/dist/src/runs/ancestry.js +34 -0
  75. package/dist/src/runs/ancestry.js.map +1 -0
  76. package/dist/src/runs/lifecycle.js +183 -0
  77. package/dist/src/runs/lifecycle.js.map +1 -0
  78. package/dist/src/runs/runDir.js +124 -0
  79. package/dist/src/runs/runDir.js.map +1 -0
  80. package/dist/src/state/sqlite-warning.js +30 -0
  81. package/dist/src/state/sqlite-warning.js.map +1 -0
  82. package/dist/src/state/sqlite.js +384 -0
  83. package/dist/src/state/sqlite.js.map +1 -0
  84. package/dist/src/state/status.js +53 -0
  85. package/dist/src/state/status.js.map +1 -0
  86. package/dist/src/state/store.js +15 -0
  87. package/dist/src/state/store.js.map +1 -0
  88. package/dist/src/state/transitions.js +94 -0
  89. package/dist/src/state/transitions.js.map +1 -0
  90. package/dist/src/tracker/comments.js +48 -0
  91. package/dist/src/tracker/comments.js.map +1 -0
  92. package/dist/src/tracker/effects.js +117 -0
  93. package/dist/src/tracker/effects.js.map +1 -0
  94. package/dist/src/tracker/policy.js +36 -0
  95. package/dist/src/tracker/policy.js.map +1 -0
  96. package/dist/src/verify/fake.js +29 -0
  97. package/dist/src/verify/fake.js.map +1 -0
  98. package/dist/src/verify/job.js +2 -0
  99. package/dist/src/verify/job.js.map +1 -0
  100. package/dist/src/verify/verifier.js +162 -0
  101. package/dist/src/verify/verifier.js.map +1 -0
  102. package/dist/src/workspace/fake.js +71 -0
  103. package/dist/src/workspace/fake.js.map +1 -0
  104. package/dist/src/workspace/git-workspace.js +132 -0
  105. package/dist/src/workspace/git-workspace.js.map +1 -0
  106. package/dist/src/workspace/git.js +113 -0
  107. package/dist/src/workspace/git.js.map +1 -0
  108. package/dist/src/workspace/workspace.js +2 -0
  109. package/dist/src/workspace/workspace.js.map +1 -0
  110. package/hosts/claude-code/.claude-plugin/plugin.json +9 -0
  111. package/hosts/claude-code/.mcp.json +9 -0
  112. package/hosts/claude-code/bin/orchestrator.mjs +134 -0
  113. package/hosts/claude-code/hooks/hooks.json +16 -0
  114. package/hosts/claude-code/hooks/session-end.mjs +96 -0
  115. package/hosts/claude-code/hooks/session-start.mjs +114 -0
  116. package/hosts/claude-code/package.json +7 -0
  117. package/hosts/claude-code/skills/import/SKILL.md +80 -0
  118. package/hosts/claude-code/skills/review/SKILL.md +48 -0
  119. package/hosts/claude-code/skills/run/SKILL.md +145 -0
  120. package/hosts/claude-code/skills/work/SKILL.md +64 -0
  121. package/hosts/claude-code/workflows/work.js +101 -0
  122. package/package.json +60 -4
package/README.md CHANGED
@@ -1,3 +1,145 @@
1
- # Temporary Holding Version
1
+ # orchestrator
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An orchestrator that runs many coding agents in parallel from a dependency graph of tasks against one repository. Dependencies gate admission. Each agent works in its own git worktree and reports through a local API. Humans resolve blockers and review milestones. A task is done only after its sealed work has been rebased, checked, and published to the integration branch. The orchestrator itself is a static TypeScript daemon with named hook points for model-backed judgment, designed to converge to the same outcome after a crash at any point. Task graphs are imported from Linear or Jira through the session's own tracker connector, and progress is mirrored back to the tracker.
4
+
5
+ ## Running locally
6
+
7
+ Prerequisites: Node 22.13 or newer (the store uses Node's built-in SQLite), git, and, for real agents, the Claude Code CLI (`claude`) logged in. Scripted agents need nothing else.
8
+
9
+ ```
10
+ git clone git@github.com:autodidacticon/orchestrator.git && cd orchestrator
11
+ npm install
12
+ npm run build
13
+ npm link # puts orchestrator, orchestrator-ctl, orchestrator-mcp, orchestrator-mcp-shim on PATH
14
+ npm test # unit, slice, and crash-injection suites with scripted agents; no model calls
15
+ ```
16
+
17
+ ### From a terminal
18
+
19
+ 1. In the repository you want to work on, write `orchestrator.yaml` (every key is optional):
20
+
21
+ ```yaml
22
+ integrationBranch: main
23
+ checkCommand: npm test # run on every rebased submission before it is published
24
+ capacity: 2 # agents in parallel
25
+ scheduling:
26
+ order: [criticalPath, priority, fifo] # admission order; ties keep graph file order
27
+ scopes: true # never run two tasks whose declared scopes overlap
28
+ budgets:
29
+ maxLaunchMs: 7200000 # 2 h per launch
30
+ maxRunCostUsd: 20 # pause admission when the run has spent this much
31
+ ```
32
+
33
+ 2. Start the daemon for that repository. It records run data under `~/.orchestrator/runs/<id>/` and prints the graph file path:
34
+
35
+ ```
36
+ cd /path/to/your/repo
37
+ orchestrator-ctl start # {"started": true, "url": "http://127.0.0.1:...", "graphPath": "~/.orchestrator/runs/<id>/graph.yaml", ...}
38
+ ```
39
+
40
+ 3. Write the task graph to that `graphPath`. The daemon re-reads it on change. Every task needs a description and acceptance criteria, or it is blocked as under-specified:
41
+
42
+ ```yaml
43
+ nodes:
44
+ - id: T1
45
+ title: Add the schema migration
46
+ description: Create the users table migration and the model.
47
+ acceptanceCriteria: [migration applies cleanly, model has unit tests]
48
+ deps: []
49
+ - id: T2
50
+ title: Expose the users API
51
+ description: Add GET and POST /users on top of T1's model.
52
+ acceptanceCriteria: [endpoints documented, integration test passes]
53
+ deps: [T1]
54
+ scope: ["src/api/**", "package.json"] # optional: path globs the work stays within
55
+ priority: 1 # optional: lower is more urgent
56
+ - id: MVP
57
+ kind: milestone
58
+ gated: true # a human reviews and accepts the pinned commit
59
+ deps: [T2]
60
+ ```
61
+
62
+ 4. Operate the run from the same terminal:
63
+
64
+ ```
65
+ orchestrator-ctl status # sets by state, stall roots, open questions, stuck tasks, deferred tasks, cost
66
+ orchestrator-ctl answer T1 "use sqlite" # answer an agent's question
67
+ orchestrator-ctl logs T1 # the agent's captured output
68
+ orchestrator-ctl nudge T1 "commit what you have"
69
+ orchestrator-ctl accept MVP --sha <sha> --version 1 # or: reject MVP --note "..."
70
+ orchestrator-ctl reopen T1 [--cascade]
71
+ orchestrator-ctl proposals | accept-proposal <id> | reject-proposal <id> --note "..."
72
+ orchestrator-ctl pause | resume [--override-budget]
73
+ orchestrator-ctl mode pull | claim w1 | release <launchId> --reason "..."
74
+ orchestrator-ctl stop [--reap] # graceful: agents keep running and are adopted on the next start
75
+ orchestrator-ctl runs # every run under ~/.orchestrator
76
+ ```
77
+
78
+ The daemon exits on its own after `daemon.idleExitMs` (default 10 min, 0 disables) with nothing live and no session attached (`daemon.exitWhileAttached: true` lets it exit while one is), records why in `daemon.json` (`runs` shows `exit` for a daemon that is gone, or nothing after a crash), and any `orchestrator-ctl` command or plugin tool revives it on demand; only an explicit `stop` is final until the next `start`. `status` reports `idle` and `attachments`. To run the daemon in the foreground instead, `orchestrator --repo .` (SIGTERM stops it gracefully). To keep everything inside one directory, use a config with explicit paths and `orchestrator --config orchestrator.yaml` with `repoPath`, `graphPath`, and `dataDir` set; then pass `--data-dir` to `orchestrator-ctl`.
79
+
80
+ By default agents are headless Claude Code sessions (`runnerCommand`), each in its own git worktree with the brief on stdin and the agent API as an MCP server. For a dry run without model calls, point `runnerCommand` at the scripted agent: `node --import <repo>/node_modules/tsx/dist/loader.mjs <repo>/test/agents/scripted.ts {briefJson}`, and give each task a `behavior` such as `commitAndSubmit`, `blockAndWait:<question>`, or `fail`.
81
+
82
+ ### From a Claude Code session
83
+
84
+ Load the plugin and use its skills: `import` turns a Linear or Jira epic into the graph with the session's own tracker connector, `run` starts the daemon, has the session's own subagents execute the launches through a bundled workflow, and monitors (questions, reviews, stuck tasks, proposals, tracker mirroring), `work` adds workers from another session or switches the mode, and `review` judges a milestone's pinned commit. Nothing needs to be on PATH: the plugin's launcher (`hosts/claude-code/bin/orchestrator.mjs`) runs the package from the checkout when loaded with `--plugin-dir`, from the plugin's own `node_modules` after a marketplace install (Claude Code installs the published package `@autodidacticon/orchestrator` pinned in `hosts/claude-code/package.json`), and through `npx` as a fallback.
85
+
86
+ ```
87
+ npm install && npm run build
88
+ claude --plugin-dir /path/to/orchestrator/hosts/claude-code # from a checkout
89
+ claude plugin marketplace add autodidacticon/orchestrator # or install from this repository's marketplace
90
+ claude plugin install orchestrator@orchestrator
91
+ ```
92
+
93
+ Who executes launches is `runner.mode`: `pull` parks each prepared launch until a worker claims it, and is what a run started from the plugin defaults to, with the `run` skill's workflow claiming launches for visible session subagents; `spawn` has the daemon run headless Claude Code agents, and is the default for `orchestrator-ctl start` from a terminal, where no session is attached; `mixed` parks for `runner.claimWindowMs` (default 60 s) and then spawns. `runner.mode` in `orchestrator.yaml`, `orchestrator-ctl mode <mode>`, or `start` with `mode` are explicit choices that the plugin default never overrides. A claimed launch has no process: its lease is renewed by the worker's API calls and by progress in the worktree, and `release` hands it back (untouched: no attempt spent; with `--keep`, or `keep: true` on the tool, uncommitted work is committed on the launch branch, the branch is kept, and the task returns to unstarted at no cost with an `interrupted` context in its next brief, up to `daemon.maxKeptReleases` times, default 3).
94
+
95
+ ### Tests
96
+
97
+ `npm test` runs everything that needs no model: unit tests, slices with real subprocess agents, git, and the HTTP API, and the crash-injection suite in `test/crash`, which is the project gate. Three opt-in tests use a real model: `ORCH_E2E_CLAUDE=1 npm test -- test/e2e/claude` runs three Claude Code agents, `ORCH_E2E_CLAUDE_PLUGIN=1 npm test -- test/e2e/plugin` has a Claude Code session operate a run through the plugin, and `ORCH_E2E_CLAUDE_WORK=1 npm test -- test/e2e/work` has a session execute a run in pull mode with two workers through the work skill.
98
+
99
+ ## Behaviour
100
+
101
+ A task is done once its sealed work has been rebased onto the integration branch, checked, and published as one squash commit with `Orchestrator-Task` and `Orchestrator-Launch` trailers; a gated milestone then waits for a human to accept its pinned commit. The daemon converges to the same outcome after a crash at any named point, and surviving agents are adopted on restart.
102
+
103
+ Tracker sync. After an import that names its tracker, the daemon derives tracker-bound effects from its own transitions into a durable outbox, in the same transaction as the transition: an issue goes in progress on its first launch, done when its work is published (or, with `tracker.doneAt: milestoneAccepted`, when its gated milestone is accepted), cancelled on cancel; the parent issue follows its first starting child and closes when the milestone is accepted. Comments are posted where a human is needed: an agent's question, a block with its reason, a milestone ready for review, accepted, or rejected (`tracker.commentOnDone` and `tracker.commentOnFail` add completion and failure comments, both off by default). The daemon never talks to a tracker: the `run` skill drains the outbox after every `wait` with the session's Linear or Atlassian tools, mapping states by type, and acknowledges each effect (`outbox`, `ack` tools; `GET /v1/outbox`, `POST /v1/outbox/:id/{ack,nack}` routes; `orchestrator-ctl outbox | ack`). Every comment ends with a marker line, `— orchestrator run <id> · event <n>`, so a drainer can recognise its own comments and never posts one twice. A human reply under a question comment is relayed as the answer (`tracker.answersFromComments`, default on); the first answer wins. Config keys: `tracker.enabled` (`auto` | `always` | `never`), `maxAttempts`, `parentInProgress`, `doneAt`, `commentOnDone`, `commentOnFail`, `answersFromComments`. Status reports `tracker`, `outboxPending`, and `trackerErrors`. Manual check against a sandbox workspace: import an epic with the `import` skill in a Claude Code session connected to Linear or Jira, run it with the `run` skill, and watch the child issues move and the question comment appear; this is not part of `npm test`.
104
+
105
+ The daemon's life. A session attaches through the plugin's operator server, which registers its process ancestry with the daemon on first contact and ends the attachment when its transport closes; the plugin's `SessionEnd` hook posts the ending session's ancestry as well, and the daemon ends any attachment whose own process has died. A claim made through a session carries its attachment, and when the attachment ends the daemon releases that launch with `keep` on the next tick: uncommitted work becomes a work-in-progress commit on the launch branch, the worktree goes, the branch stays, the task returns to `unstarted` with `attempts` unchanged and an `interrupted` context (prior launch, branch, head, the WIP commit) in its next brief, counted in `interruptions` and bounded by `daemon.maxKeptReleases`. With nothing live, no verifier job pending, no cascade pending, no request in flight, and no session attached, the daemon exits after `daemon.idleExitMs`, writes an exit record, and is revived by the next operator call; the plugin's `SessionStart` hook names runs whose daemon is alive or whose last exit left parked launches, open questions, a milestone under review, or a stall. Releasing a new version: bump the version in `package.json`, `hosts/claude-code/.claude-plugin/plugin.json`, and the pin in `hosts/claude-code/package.json`; `npm publish` (which builds); regenerate `hosts/claude-code/package-lock.json` with `npm install --package-lock-only` in that directory against the published version; then push.
106
+
107
+ Scheduling. Ready tasks are admitted in the order of `scheduling.order`, a chain of stages applied as one stable sort (default `[criticalPath, priority, fifo]`): `criticalPath` puts the task with the longest chain of tasks behind it first, so the chain that gates the milestone starts first; `priority` reads the task's `priority` as a number, lower first, missing last (the import passes the tracker's number through, dropping Linear's 0); `fifo` is graph file order, which also breaks any remaining tie. Waiting tasks whose answer has arrived are resumed before anything is ordered; `order: [fifo]` restores the v1 order. A task may declare `scope`, a list of path globs (in the graph file, or under a `Scope` heading in an imported description, one glob per bullet; `options.scopeHeading` renames it). While a task is running, waiting, submitted, or has a claimable launch, no task whose scope overlaps it is launched: two globs overlap when the static prefix of one (the leading path segments before any `*`, `?`, `[`, `{`) is a prefix of the other's, so `src/**` holds `src/api/x.ts`, `src/api/**` and `src/ui/**` run together, and `**` or `*` overlaps everything. Held-back tasks appear as `deferred: [{ id, reason: scopeOverlap, with }]` in status and as a count in the digest; they launch on the first tick after the blocker publishes. `scheduling.scopes: false` turns the filter off; a malformed `scope` fails graph validation.
108
+
109
+ Health. The daemon renews a launch's lease every tick while its process is alive, so a quiet agent is never failed for silence; lease expiry applies only to launches the runner cannot observe. Progress is a worktree fingerprint (head plus status and diff), any API call, or a changed heartbeat text. A launch idle past `health.stuckAfterMs` (default 15 min) is flagged (`stuck` in status and the digest, a `stuck` notice in its inbox, event `launch.stuck`) and idle past `health.stuckFailAfterMs` (default 45 min) is reaped and failed with reason `stuck`, spending an attempt; either threshold at 0 disables that step. `orchestrator-ctl nudge <task> <message>` (also the `nudge` tool) queues a message for the agent's inbox. Budgets: `budgets.maxLaunchMs` (default 2 h) fails an overlong launch with reason `timeBudget`, waiting time excluded; `budgets.maxRunCostUsd` (default 0, unlimited) pauses admission with `pausedReason: costBudget` once the run's cost, read from Claude Code's final JSON per launch, reaches the ceiling, and `resume` is refused until `orchestrator-ctl resume --override-budget` (or `resume` with `overrideBudget`) raises the ceiling by one budget. When a task is published, every other running agent gets a `baseMoved` notice naming the new head and the files it touched, and may rebase its branch onto it; the verifier rebases from the merge base so such branches integrate cleanly (`health.baseMovedNotice` turns the notice off).
110
+
111
+ Graph mutation. Milestones hold no work, so they never block a reopen: reopening a done task resets every done or submitted milestone downstream (`milestone.reset`), which is reviewed again at its next version once the revised work is published, and dependency edits to such milestones are compatible reloads that reset them the same way. A reopen whose target has started task dependents is refused with the blockers; `orchestrator-ctl reopen <task> --cascade` (or `reopen` with `cascade: true`) records a plan, returns it as `reset: [{ id, from }]`, and the next tick kills running dependents, drops queued verification, and returns done dependents and the target to unstarted with budgets reset. Nothing on the integration branch is reverted: each relaunch is briefed with a revision context naming the prior launch, branch, published commit, and report, and publishes a further trailer commit. Agents may put structured `proposals` (title, description, acceptance criteria, optional deps, scope, labels) in their submit report; they are held pending (`proposals` in status and the digest) until `accept-proposal <id> [--node-id <tracker key>]` appends a task node depending on the proposer, joining its gated milestone when `proposals.joinMilestone` is true (default), or `reject-proposal <id> --note`. A compatible reload that edits a running task's payload sends its agent a `payloadChanged` inbox notice (`graph.payloadChangedNotice`, default true); the pinned brief stays and the next launch uses the new payload.
112
+
113
+ ## Contents
114
+
115
+ ### Requirements
116
+
117
+ Current specs in `openspec/specs/`, synced from the archived changes `add-orchestrator-v1`, `add-session-operator`, `add-tracker-sync`, `add-health-monitoring`, `add-graph-mutation`, `add-pull-runner`, `add-scheduling-policy`, and `replace-sqlite-driver`:
118
+
119
+ - Capability specs
120
+ - [task-graph](openspec/specs/task-graph/spec.md): loading, validation, external nodes, readiness, milestones, stall detection, revisions, reload compatibility, revocability through milestones and the cascade plan, critical path length
121
+ - [task-lifecycle](openspec/specs/task-lifecycle/spec.md): states, transitions by actor, admission, counters, leases, retry, reopen guard, cascade reopen, milestone reset and review, cancellation
122
+ - [agent-protocol](openspec/specs/agent-protocol/spec.md): the brief, the API, deduplication, sealing, no-op and acceptance checks, blocking and resume, fallback, fencing, MCP façade, brief validation
123
+ - [integration-pipeline](openspec/specs/integration-pipeline/spec.md): worktrees, the verifier, publication, conflicts, foreign commits, milestone tags
124
+ - [crash-recovery](openspec/specs/crash-recovery/spec.md): durable records, recovery per boundary, adoption, fencing, sweeps, crash points, invariants, store transactions
125
+ - [operator-control](openspec/specs/operator-control/spec.md): operator routes shared by the CLI and the MCP server, status, run directories, daemon lifecycle, event stream, review bundle, graph import, pause and resume, shutdown, logs
126
+ - [operator-mcp](openspec/specs/operator-mcp/spec.md): the operator MCP server a host session drives: tool set, wait, start, review
127
+ - [graph-import](openspec/specs/graph-import/spec.md): normalized issue records, mapping rules, acceptance criteria and scope extraction, priority normalisation, import outcome
128
+ - [host-integration](openspec/specs/host-integration/spec.md): package entrypoints, per-host wrappers, skills, no native dependencies, quiet SQLite binding
129
+ - [tracker-sync](openspec/specs/tracker-sync/spec.md): the durable outbox of tracker effects, effect kinds and marker, mirroring policy, done semantics, the drainer contract
130
+ - [health-monitoring](openspec/specs/health-monitoring/spec.md): process liveness, progress signals, stuck detection and escalation, the diagnosis hook, launch and run budgets, usage accounting, the base-moved notice
131
+ - [pull-runner](openspec/specs/pull-runner/spec.md): runner modes and the claim window, claim and release, worker identity and idempotent re-claim, access modes with remote reserved
132
+ - [scheduling-policy](openspec/specs/scheduling-policy/spec.md): the admission ordering chain and its stages, declared scopes, the overlap rule, the scope filter at admission
133
+ - Archived change `add-orchestrator-v1` (the v1 runtime): [Proposal](openspec/changes/archive/2026-10-12-add-orchestrator-v1/proposal.md), [Design](openspec/changes/archive/2026-10-12-add-orchestrator-v1/design.md), [Tasks](openspec/changes/archive/2026-10-12-add-orchestrator-v1/tasks.md)
134
+ - Archived change `add-session-operator` (operate from a Claude Code session): [Proposal](openspec/changes/archive/2026-10-12-add-session-operator/proposal.md), [Design](openspec/changes/archive/2026-10-12-add-session-operator/design.md), [Tasks](openspec/changes/archive/2026-10-12-add-session-operator/tasks.md)
135
+ - Archived change `add-tracker-sync` (mirror the run to the tracker): [Proposal](openspec/changes/archive/2026-10-12-add-tracker-sync/proposal.md), [Design](openspec/changes/archive/2026-10-12-add-tracker-sync/design.md), [Tasks](openspec/changes/archive/2026-10-12-add-tracker-sync/tasks.md)
136
+ - Archived change `add-health-monitoring` (liveness, stuck detection, budgets): [Proposal](openspec/changes/archive/2026-10-12-add-health-monitoring/proposal.md), [Design](openspec/changes/archive/2026-10-12-add-health-monitoring/design.md), [Tasks](openspec/changes/archive/2026-10-12-add-health-monitoring/tasks.md)
137
+ - Archived change `add-graph-mutation` (transparent milestones, cascade reopen, proposals): [Proposal](openspec/changes/archive/2026-10-12-add-graph-mutation/proposal.md), [Design](openspec/changes/archive/2026-10-12-add-graph-mutation/design.md), [Tasks](openspec/changes/archive/2026-10-12-add-graph-mutation/tasks.md)
138
+ - Archived change `add-pull-runner` (workers claim launches; the work skill and workflow): [Proposal](openspec/changes/archive/2026-10-13-add-pull-runner/proposal.md), [Design](openspec/changes/archive/2026-10-13-add-pull-runner/design.md), [Tasks](openspec/changes/archive/2026-10-13-add-pull-runner/tasks.md)
139
+ - Archived change `add-scheduling-policy` (ordering chain, scopes, deferrals): [Proposal](openspec/changes/archive/2026-10-13-add-scheduling-policy/proposal.md), [Design](openspec/changes/archive/2026-10-13-add-scheduling-policy/design.md), [Tasks](openspec/changes/archive/2026-10-13-add-scheduling-policy/tasks.md)
140
+ - Archived change `replace-sqlite-driver` (Node's built-in SQLite, no native dependency): [Proposal](openspec/changes/archive/2026-10-13-replace-sqlite-driver/proposal.md), [Design](openspec/changes/archive/2026-10-13-replace-sqlite-driver/design.md), [Tasks](openspec/changes/archive/2026-10-13-replace-sqlite-driver/tasks.md)
141
+
142
+ Active changes, planned 2026-10-13 from the first end-to-end walkthrough, in apply order:
143
+
144
+ 1. `add-daemon-lifecycle`: visible session subagents as the plugin's default, a daemon that exits when idle and revives on demand, session attachments with a kept release, a self-contained plugin launcher, session hooks. [Proposal](openspec/changes/add-daemon-lifecycle/proposal.md), [Design](openspec/changes/add-daemon-lifecycle/design.md), [Tasks](openspec/changes/add-daemon-lifecycle/tasks.md)
145
+ 2. `add-toolchain-discovery`: the check command, integration branch, and spawned agents' allowed tools detected from the repository with provenance, runtime overrides through a `configure` tool, no `orchestrator.yaml` required. [Proposal](openspec/changes/add-toolchain-discovery/proposal.md), [Design](openspec/changes/add-toolchain-discovery/design.md), [Tasks](openspec/changes/add-toolchain-discovery/tasks.md)
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Agent API schemas. The contract is the operations and their semantics; the
3
+ * transport is loopback HTTP (server.ts) with an MCP façade (mcp-shim.ts).
4
+ */
5
+ import { z } from 'zod';
6
+ export { failBodySchema, questionSchema, submitBodySchema } from '../loop/orchestrator.js';
7
+ export const AGENT_OPS = ['heartbeat', 'report', 'block', 'inbox', 'log'];
8
+ export const fallbackFileSchema = z.object({
9
+ launchId: z.string(),
10
+ seq: z.number().int().nonnegative(),
11
+ kind: z.enum(['submit', 'fail', 'block']),
12
+ body: z.record(z.unknown()),
13
+ });
14
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/agent/api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3F,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAU,CAAC;AAEnF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACnC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;CAC5B,CAAC,CAAC"}
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP façade: a stdio MCP server exposing the five agent operations as tools
4
+ * and forwarding them to the daemon's HTTP API with the launch token from the
5
+ * environment. Retries through daemon restarts for the retry window.
6
+ *
7
+ * Env: ORCH_API, ORCH_LAUNCH_ID, ORCH_TOKEN, ORCH_RETRY_WINDOW_MS (default 120000).
8
+ */
9
+ import { z } from 'zod';
10
+ import { isEntrypoint } from '../entry.js';
11
+ export function envFromProcess(env = process.env) {
12
+ const api = env['ORCH_API'];
13
+ const launchId = env['ORCH_LAUNCH_ID'];
14
+ const token = env['ORCH_TOKEN'];
15
+ if (!api || !launchId || !token)
16
+ throw new Error('ORCH_API, ORCH_LAUNCH_ID and ORCH_TOKEN are required');
17
+ return { api, launchId, token, retryWindowMs: Number(env['ORCH_RETRY_WINDOW_MS'] ?? 120_000) };
18
+ }
19
+ /** Forward one operation, retrying connection failures with backoff for the retry window. */
20
+ export async function forward(env, op, body, method = 'POST') {
21
+ const url = `${env.api}/v1/launches/${env.launchId}/${op}`;
22
+ const deadline = Date.now() + env.retryWindowMs;
23
+ let delay = 100;
24
+ for (;;) {
25
+ try {
26
+ const res = await fetch(url, {
27
+ method,
28
+ headers: { authorization: `Bearer ${env.token}`, 'content-type': 'application/json' },
29
+ body: method === 'POST' ? JSON.stringify(body) : undefined,
30
+ });
31
+ return { status: res.status, body: await res.json() };
32
+ }
33
+ catch (err) {
34
+ if (Date.now() + delay > deadline)
35
+ throw err;
36
+ await new Promise((r) => setTimeout(r, delay));
37
+ delay = Math.min(delay * 2, 2000);
38
+ }
39
+ }
40
+ }
41
+ export const TOOL_NAMES = ['heartbeat', 'report', 'block', 'inbox', 'log'];
42
+ export async function startShim(env) {
43
+ const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
44
+ const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
45
+ const server = new McpServer({ name: 'orchestrator-agent-api', version: '0.1.0' });
46
+ const text = (r) => ({
47
+ content: [{ type: 'text', text: JSON.stringify({ status: r.status, ...(typeof r.body === 'object' && r.body ? r.body : { body: r.body }) }) }],
48
+ isError: r.status >= 400,
49
+ });
50
+ server.registerTool('heartbeat', { description: 'Renew the lease and report progress; a changed progress text counts as progress for stuck detection.', inputSchema: { progress: z.string().optional() } }, async (args) => text(await forward(env, 'heartbeat', args)));
51
+ server.registerTool('report', {
52
+ description: 'Final report. kind=submit requires a committed, clean worktree; kind=fail gives up. seq must increase per call.',
53
+ inputSchema: {
54
+ seq: z.number().int().nonnegative(),
55
+ kind: z.enum(['submit', 'fail']),
56
+ summary: z.string().optional(),
57
+ interfaces: z.array(z.string()).optional(),
58
+ decisions: z.array(z.string()).optional(),
59
+ deviations: z.array(z.string()).optional(),
60
+ followUps: z.array(z.string()).optional(),
61
+ tests: z.array(z.string()).optional(),
62
+ acceptance: z.array(z.object({ criterion: z.string(), evidence: z.string() })).optional(),
63
+ noChanges: z.object({ justification: z.string() }).optional(),
64
+ proposals: z
65
+ .array(z.object({ title: z.string(), description: z.string(), acceptanceCriteria: z.array(z.string()), deps: z.array(z.string()).optional(), scope: z.array(z.string()).optional(), labels: z.array(z.string()).optional() }))
66
+ .optional()
67
+ .describe('Follow-up tasks you discovered; a human accepts them into the graph. followUps stays free text.'),
68
+ tried: z.string().optional(),
69
+ why: z.string().optional(),
70
+ whatWouldUnblock: z.string().optional(),
71
+ },
72
+ }, async (args) => text(await forward(env, 'report', args)));
73
+ server.registerTool('block', {
74
+ description: 'Ask a human a question and wait for the answer. Returns {answer} or {timeout:true}.',
75
+ inputSchema: {
76
+ seq: z.number().int().nonnegative(),
77
+ kind: z.enum(['needsDecision', 'needsAccess', 'impossible', 'dependencyCancelled', 'outOfScope', 'insufficientSpec', 'milestoneRejected']),
78
+ message: z.string(),
79
+ options: z.array(z.string()).optional(),
80
+ },
81
+ }, async (args) => text(await forward({ ...env, retryWindowMs: Math.max(env.retryWindowMs, 24 * 3600_000) }, 'block', args)));
82
+ server.registerTool('inbox', { description: 'Fetch pending notices from the orchestrator: stuck (no progress observed), nudge (a message from the operator), baseMoved (the integration branch advanced; head and files). Check after long steps.', inputSchema: {} }, async () => text(await forward(env, 'inbox', {}, 'GET')));
83
+ server.registerTool('log', { description: 'Append a log line to the launch log.', inputSchema: { text: z.string(), seq: z.number().int().optional() } }, async (args) => text(await forward(env, 'log', args)));
84
+ await server.connect(new StdioServerTransport());
85
+ }
86
+ export const USAGE = `orchestrator-mcp-shim: MCP façade for an agent launch.
87
+
88
+ orchestrator-mcp-shim
89
+
90
+ A stdio MCP server exposing heartbeat, report, block, inbox, and log, forwarded
91
+ to the daemon with the launch token. Env: ORCH_API, ORCH_LAUNCH_ID, ORCH_TOKEN,
92
+ ORCH_RETRY_WINDOW_MS. The orchestrator writes this into each launch's mcp.json.
93
+ `;
94
+ const isEntry = isEntrypoint(import.meta.url);
95
+ if (isEntry && (process.argv.includes('--help') || process.argv.includes('-h'))) {
96
+ process.stdout.write(USAGE);
97
+ }
98
+ else if (isEntry) {
99
+ startShim(envFromProcess()).catch((err) => {
100
+ process.stderr.write(`${String(err)}\n`);
101
+ process.exit(1);
102
+ });
103
+ }
104
+ //# sourceMappingURL=mcp-shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-shim.js","sourceRoot":"","sources":["../../../src/agent/mcp-shim.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Criteria are compared after dropping markdown code spans and emphasis, a
3
+ * leading list marker such as the brief's "3.", and trailing punctuation,
4
+ * collapsing whitespace, and ignoring case, so an agent that quotes a criterion
5
+ * loosely still covers it. See specs/agent-protocol "Acceptance evidence".
6
+ */
7
+ export function normaliseCriterion(text) {
8
+ return text
9
+ .replace(/[`*_~]/g, '')
10
+ .replace(/\s+/g, ' ')
11
+ .trim()
12
+ .replace(/^(?:\d+[.)]|[-+])\s*/, '')
13
+ .replace(/[.;:,]+$/, '')
14
+ .toLowerCase();
15
+ }
16
+ export function checkSubmission(input) {
17
+ if (input.sealedSha === input.baseSha && !input.noChanges) {
18
+ return { ok: false, reason: 'emptySubmission', message: 'no commits ahead of base and no noChanges declaration' };
19
+ }
20
+ // A criterion is covered by its normalised text or by its 1-based position in the brief's list.
21
+ const covered = new Set(input.covered.map(normaliseCriterion));
22
+ const uncovered = input.criteria.filter((c, i) => !covered.has(normaliseCriterion(c)) && !covered.has(String(i + 1)));
23
+ if (uncovered.length) {
24
+ return { ok: false, reason: 'missingAcceptanceEvidence', message: `no evidence for: ${uncovered.join('; ')}`, uncovered };
25
+ }
26
+ return { ok: true };
27
+ }
28
+ //# sourceMappingURL=seal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seal.js","sourceRoot":"","sources":["../../../src/agent/seal.ts"],"names":[],"mappings":"AAiBA;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI;SACR,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE;SACN,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC;SACnC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QAC1D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,uDAAuD,EAAE,CAAC;IACpH,CAAC;IACD,gGAAgG;IAChG,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACtH,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,EAAE,OAAO,EAAE,oBAAoB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC;IAC5H,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Loopback HTTP server for agents and operators.
3
+ * POST /v1/launches/:id/{heartbeat,report,block,log}
4
+ * GET /v1/launches/:id/inbox
5
+ * Operator routes are registered by operator/routes.ts.
6
+ */
7
+ import http from 'node:http';
8
+ export class ApiServer {
9
+ orch;
10
+ server;
11
+ routes = [];
12
+ port = 0;
13
+ constructor(orch) {
14
+ this.orch = orch;
15
+ this.server = http.createServer((req, res) => void this.dispatch(req, res));
16
+ this.server.keepAliveTimeout = 5_000;
17
+ this.addRoute('POST', /^\/v1\/launches\/([^/]+)\/(heartbeat|report|block|log)$/, async (req, body, [launchId, op]) => {
18
+ const token = bearer(req);
19
+ return this.orch.agentCall(launchId, token, op, body);
20
+ });
21
+ this.addRoute('GET', /^\/v1\/launches\/([^/]+)\/inbox$/, async (req, _body, [launchId]) => {
22
+ return this.orch.agentCall(launchId, bearer(req), 'inbox', {});
23
+ });
24
+ }
25
+ addRoute(method, pattern, handler) {
26
+ this.routes.push({ method, pattern, handler });
27
+ }
28
+ async listen(port = 0) {
29
+ await new Promise((resolve, reject) => {
30
+ const onError = (err) => reject(err);
31
+ this.server.once('error', onError);
32
+ this.server.listen(port, '127.0.0.1', () => {
33
+ this.server.off('error', onError);
34
+ resolve();
35
+ });
36
+ });
37
+ this.port = this.server.address().port;
38
+ const url = `http://127.0.0.1:${this.port}`;
39
+ this.orch.apiBaseUrl = url;
40
+ return url;
41
+ }
42
+ async close() {
43
+ await new Promise((resolve) => this.server.close(() => resolve()));
44
+ this.server.closeAllConnections();
45
+ }
46
+ async dispatch(req, res) {
47
+ const url = new URL(req.url ?? '/', 'http://127.0.0.1');
48
+ for (const r of this.routes) {
49
+ if (r.method !== req.method)
50
+ continue;
51
+ const m = r.pattern.exec(url.pathname);
52
+ if (!m)
53
+ continue;
54
+ // A probe is a liveness check that must not count as activity. See specs/operator-control "Idle exit".
55
+ const probe = req.method === 'GET' && url.pathname === '/v1/status' && url.searchParams.get('probe') === '1';
56
+ const attachment = req.headers['x-orch-attachment'];
57
+ const attachmentId = typeof attachment === 'string' && attachment ? attachment : undefined;
58
+ this.orch.requestBegan();
59
+ try {
60
+ const body = req.method === 'POST' || req.method === 'PUT' ? await readJson(req) : Object.fromEntries(url.searchParams.entries());
61
+ const out = await r.handler(req, body, m.slice(1));
62
+ // The operation stands; the caller learns its attachment is unknown and registers again. See specs/operator-control "Session attachments".
63
+ const headers = attachmentId && !this.orch.isLiveAttachment(attachmentId) ? { 'x-orch-attachment': 'unknown' } : undefined;
64
+ send(res, out.status, out.body, headers);
65
+ }
66
+ catch (err) {
67
+ send(res, 500, { error: String(err) });
68
+ }
69
+ finally {
70
+ this.orch.requestEnded(probe);
71
+ }
72
+ return;
73
+ }
74
+ send(res, 404, { error: 'not found' });
75
+ }
76
+ }
77
+ function bearer(req) {
78
+ const h = req.headers.authorization ?? '';
79
+ return h.startsWith('Bearer ') ? h.slice(7).trim() : '';
80
+ }
81
+ function send(res, status, body, headers = {}) {
82
+ const text = JSON.stringify(body ?? {});
83
+ res.writeHead(status, { 'content-type': 'application/json', 'content-length': Buffer.byteLength(text), ...headers });
84
+ res.end(text);
85
+ }
86
+ async function readJson(req) {
87
+ const chunks = [];
88
+ for await (const c of req)
89
+ chunks.push(c);
90
+ const text = Buffer.concat(chunks).toString('utf8');
91
+ if (!text.trim())
92
+ return {};
93
+ const parsed = JSON.parse(text);
94
+ return parsed && typeof parsed === 'object' ? parsed : {};
95
+ }
96
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/agent/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ export function acceptanceCriteriaOf(payload) {
2
+ const raw = payload['acceptanceCriteria'];
3
+ if (Array.isArray(raw))
4
+ return raw.map(String);
5
+ if (typeof raw === 'string' && raw.trim())
6
+ return raw.split('\n').map((s) => s.replace(/^[-*]\s*/, '').trim()).filter(Boolean);
7
+ return [];
8
+ }
9
+ export function composeBrief(input, opts) {
10
+ const { launch, node } = input;
11
+ const criteria = acceptanceCriteriaOf(node.payload);
12
+ const json = {
13
+ launchId: launch.launchId,
14
+ taskId: node.id,
15
+ token: launch.token,
16
+ apiBaseUrl: input.apiBaseUrl,
17
+ worktree: launch.worktree,
18
+ branch: launch.branch,
19
+ baseSha: launch.baseSha,
20
+ graphRev: launch.graphRev,
21
+ integrationBranch: input.integrationBranch,
22
+ checkCommand: input.checkCommand,
23
+ retryWindowMs: input.retryWindowMs,
24
+ fallbackPath: opts.fallbackPath,
25
+ attempt: launch.attempt,
26
+ launchNo: launch.launchNo,
27
+ task: node.payload,
28
+ acceptanceCriteria: criteria,
29
+ dependencies: input.depReports.map((d) => ({
30
+ taskId: d.taskId,
31
+ summary: d.report ? String(d.report.body['summary'] ?? '') : undefined,
32
+ report: d.report?.body,
33
+ cancelled: d.cancelled,
34
+ external: d.external,
35
+ })),
36
+ context: input.carry,
37
+ };
38
+ const title = String(node.payload['title'] ?? node.id);
39
+ const description = String(node.payload['description'] ?? '');
40
+ const lines = [];
41
+ lines.push(`# Task ${node.id}: ${title}`, '');
42
+ if (description)
43
+ lines.push(description, '');
44
+ lines.push('## Acceptance criteria', '');
45
+ if (criteria.length)
46
+ criteria.forEach((c, i) => lines.push(`${i + 1}. ${c}`));
47
+ else
48
+ lines.push('- (none declared)');
49
+ lines.push('', '## Definition of done', '');
50
+ lines.push(`- Commit all work on branch \`${launch.branch}\` in this worktree. Uncommitted or untracked files make your submission be rejected with 409 and the file list; commit and resubmit.`, `- Never merge into, push to, or check out \`${input.integrationBranch}\`. You may rebase your launch branch onto it when a baseMoved notice says it advanced; the orchestrator integrates your branch after verification.`, `- Run the check command before submitting: \`${input.checkCommand}\`.`, '- If the task needs no changes, say so explicitly with `noChanges: { justification }` in your report.', '- Your report must give evidence for every acceptance criterion above: one `acceptance` entry per criterion, whose `criterion` quotes the criterion text (markdown and case do not matter) or gives its number.', '- Terminate as soon as your report is accepted.', '');
51
+ lines.push('## Workspace', '');
52
+ lines.push(`- Worktree: \`${launch.worktree}\``, `- Branch: \`${launch.branch}\``, `- Base commit: \`${launch.baseSha}\``, `- Integration branch: \`${input.integrationBranch}\``);
53
+ lines.push('- Notices may arrive in your inbox (the `inbox` operation): `stuck` when no progress has been observed for a while, `nudge` with a message from the operator, `baseMoved` with the new integration `head` and the `files` that changed, and `payloadChanged` when your task\'s description was edited (re-read the brief file). Check the inbox after long steps.', '');
54
+ lines.push('## Dependencies', '');
55
+ if (!input.depReports.length)
56
+ lines.push('- none');
57
+ for (const d of input.depReports) {
58
+ if (d.external) {
59
+ lines.push(`- **${d.taskId}**${d.external.title ? ` (${d.external.title})` : ''} is outside this run and is ${d.external.done ? 'done' : 'not done'}. Its work was not produced by an agent here; look at the repository for what it delivered.`);
60
+ }
61
+ else if (d.cancelled) {
62
+ lines.push(`- **${d.taskId}** was cancelled: ${d.cancelled.reason}. Its work does not exist; plan accordingly.`);
63
+ }
64
+ else if (d.report) {
65
+ const b = d.report.body;
66
+ lines.push(`- **${d.taskId}**: ${String(b['summary'] ?? '')}`);
67
+ for (const key of ['interfaces', 'decisions', 'deviations']) {
68
+ const arr = b[key];
69
+ if (Array.isArray(arr) && arr.length)
70
+ lines.push(` - ${key}: ${arr.map(String).join('; ')}`);
71
+ }
72
+ }
73
+ else {
74
+ lines.push(`- **${d.taskId}**: no completion report available; see \`git log ${input.integrationBranch}\`.`);
75
+ }
76
+ }
77
+ lines.push('');
78
+ if (input.carry && Object.keys(input.carry).length) {
79
+ lines.push('## Context from previous attempts', '');
80
+ const c = input.carry;
81
+ if (c['priorFailure'])
82
+ lines.push(`- Previous attempt failed: ${JSON.stringify(c['priorFailure'])}`);
83
+ if (c['verifierOutput'])
84
+ lines.push('- Verifier output:', '', '```', String(c['verifierOutput']).slice(0, 8000), '```');
85
+ if (c['conflict']) {
86
+ const k = c['conflict'];
87
+ lines.push(`- Your previous branch \`${k.previousBranch ?? ''}\` conflicted on rebase in: ${(k.files ?? []).join(', ')}. Tasks published since your base: ${(k.since ?? []).join(', ') || 'none'}. Rebase that branch or start over from the new base.`);
88
+ }
89
+ if (c['interrupted']) {
90
+ const k = c['interrupted'];
91
+ lines.push(`- Your previous launch \`${k.priorLaunchId}\` was interrupted (${k.reason}); this is interruption ${k.interruptions} for this task. Its branch \`${k.priorBranch}\` holds the work so far at \`${k.headSha}\`${k.wipCommit ? `, and commit \`${k.wipCommit}\` is a work-in-progress commit of changes it had not committed` : ''}. Continue from that branch by merging or cherry-picking it onto your base, or inspect it and start over${k.wipCommit ? '; squash the work-in-progress commit before submitting' : ''}.`);
92
+ }
93
+ if (c['question'] || c['answer'])
94
+ lines.push(`- Question asked earlier: ${JSON.stringify(c['question'])}`, `- Human answer: ${String(c['answer'] ?? '')}`);
95
+ lines.push('');
96
+ }
97
+ const revision = input.carry?.['revision'];
98
+ if (revision) {
99
+ lines.push('## Revision', '');
100
+ lines.push(revision.cause === 'upstreamReopened'
101
+ ? `- This task is being revised because its upstream task \`${revision.upstream ?? ''}\` was reopened and its work may change.`
102
+ : '- This task was reopened for revision.');
103
+ lines.push(`- Your prior launch \`${revision.priorLaunchId}\` worked on branch \`${revision.priorBranch}\`${revision.priorPublishedSha
104
+ ? ` and published commit \`${revision.priorPublishedSha}\`, which remains on \`${input.integrationBranch}\` and is contained in your base commit \`${launch.baseSha}\``
105
+ : ' and did not publish'}.`);
106
+ if (revision.priorReportSummary)
107
+ lines.push(`- Prior report: ${revision.priorReportSummary}`);
108
+ lines.push('- Revise the prior work rather than redoing it: build on what the base commit already contains, and commit the delta.', '');
109
+ }
110
+ lines.push('## Reporting', '');
111
+ lines.push(`- API: \`${input.apiBaseUrl}/v1/launches/${launch.launchId}/...\` with header \`Authorization: Bearer <token from brief.json>\`.`, '- Operations: heartbeat, report (kind submit|fail, with seq), block (a question; waits for the answer), inbox, log.', `- If the API fails for more than ${Math.round(input.retryWindowMs / 1000)}s, write your report to \`${opts.fallbackPath}\` and exit.`, '- If any call returns 401, stop immediately: your launch has been revoked.', '');
112
+ return { markdown: lines.join('\n'), json };
113
+ }
114
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../src/brief/compose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export const noRemediation = { remediate: async () => undefined };
2
+ /** Returns a fixed candidate for every missing slot. Tests and dry runs. */
3
+ export class StaticRemediator {
4
+ fill;
5
+ calls = [];
6
+ constructor(fill = (slot) => `candidate ${slot}`) {
7
+ this.fill = fill;
8
+ }
9
+ async remediate(node, _payload, ctx) {
10
+ this.calls.push({ taskId: node.id, missing: ctx.missing });
11
+ return Object.fromEntries(ctx.missing.map((m) => [m, this.fill(m, node)]));
12
+ }
13
+ }
14
+ //# sourceMappingURL=remediate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remediate.js","sourceRoot":"","sources":["../../../src/brief/remediate.ts"],"names":[],"mappings":"AAiBA,MAAM,CAAC,MAAM,aAAa,GAAe,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;AAE9E,4EAA4E;AAC5E,MAAM,OAAO,gBAAgB;IAEE;IADpB,KAAK,GAAiD,EAAE,CAAC;IAClE,YAA6B,OAAmD,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,IAAI,EAAE;QAAhF,SAAI,GAAJ,IAAI,CAA4E;IAAG,CAAC;IACjH,KAAK,CAAC,SAAS,CAAC,IAAe,EAAE,QAAiC,EAAE,GAAuB;QACzF,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,OAAO,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC;CACF"}