|
|
@@ -28,20 +28,23 @@ arrives within the lost-wake guard window. Telemetry is push-based
|
|
|
(phase 2): daemons flush raw PTY batches to `POST /api/saddle/chunks`
|
|
|
and round results with state transitions to `POST /api/saddle/results` —
|
|
|
`blocked` raises the question as an answerable block and parks the
|
|
|
-saddle, `suspended` parks for the subtask-event resume, `done`/`failed`
|
|
|
+saddle, `suspended` parks until the goal agent's next terminal-event
|
|
|
+resume, `done`/`failed`
|
|
|
complete or fail the task. Nothing polls for output; `GET /status`
|
|
|
against a daemon is exceptional only — the staleness probe (a silent
|
|
|
runner owes one probe after `SADDLE_STALE_S`; a failed probe requeues
|
|
|
its task and marks the saddle offline) and the manual refresh button.
|
|
|
-A recovered daemon's ask flips an offline row back to idle.
|
|
|
-Capability awareness (phase 3): every assignment carries a
|
|
|
+A recovered daemon's ask flips an offline row back to idle. Which
|
|
|
+saddle runs what derives from open sessions — busy iff an open session
|
|
|
+exists, idle otherwise; only the offline probe state is stored.
|
|
|
+Capability awareness: every assignment carries a
|
|
|
`capabilities` context — `saddle_tags` (the allocated saddle's own
|
|
|
tags) and `fleet_tags` (every tag name known across the fleet) — which
|
|
|
the daemon renders into its system prompt; `GET
|
|
|
/api/saddle/capabilities` (per-saddle token) feeds its `saddle
|
|
|
-capabilities` CLI, and the subtask callback's `capabilities: [tags]`
|
|
|
-attach explicit, validated requirements (unknown tags reject with the
|
|
|
-valid list). The home dashboard pins OPEN blocks (questions, RFCs,
|
|
|
+capabilities` CLI, and the goal agent's task-create callback attaches
|
|
|
+explicit, validated capability requirements (unknown tags reject with
|
|
|
+the valid list). The home dashboard pins OPEN blocks (questions, RFCs,
|
|
|
reviews) as newest-first link-through cards above a cross-task activity
|
|
|
timeline (last ~50 events, newest first); the task page leads with its
|
|
|
status block and orders blocking sections newest-first.
|
|
|
@@ -49,7 +52,7 @@ status block and orders blocking sections newest-first.
|
|
|
| Component | Lives | Role |
|
|
|
|---|---|---|
|
|
|
| Web app | `src/` | pages, actions, services, dispatcher, saddle API |
|
|
|
-| Saddle daemon | `../saddle` | bash PTY-free session, LLM loop, control server |
|
|
|
+| Saddle daemon | `../saddle` | bash PTY session, LLM loop, control server |
|
|
|
| Mock LLM | `../saddle/test/mock_llm.py` | scripted endpoint for testing |
|
|
|
|
|
|
Orchestration behaviour to know:
|
|
|
@@ -140,29 +143,32 @@ with ephemeral keys.
|
|
|
filesystem (clone it into the workspace volume; the agent does not
|
|
|
clone). Link related projects as *co-developed* (agent may modify) or
|
|
|
*reference* (read-only) from the project page.
|
|
|
-2. **Task** — `/tasks` → project, title, description, priority (1 is
|
|
|
- highest) and the plan-11 options ("auto-approve plan",
|
|
|
- "auto-enqueue agent-created subtasks", "auto-approve subtasks"). The
|
|
|
- task is created in **draft**: edit every field, attach rules and
|
|
|
- capabilities, then press **Enqueue** (dependencies still in draft
|
|
|
- block the enqueue with an error naming them; nothing depends on a
|
|
|
- draft before you may delete it). Chain work with dependencies: an
|
|
|
- enqueued task waits until everything it depends on is done.
|
|
|
+2. **Task** — `/tasks` → project, title, **mandate** (the description),
|
|
|
+ mode (implement / investigate / review / test) and priority (1 is
|
|
|
+ the highest). The task is created in **draft**: edit every field,
|
|
|
+ attach rules and capabilities, then press **Enqueue** (only the
|
|
|
+ creator enqueues — humans their own, the goal agent its own).
|
|
|
+ Chain human work with **auto-enqueue after**: pick a predecessor
|
|
|
+ from the global task list; the dependent enqueues itself when the
|
|
|
+ predecessor completes (a failed or cancelled predecessor leaves it
|
|
|
+ in draft).
|
|
|
3. **Saddle** — `/saddles` (admin) → name plus the daemon's host:port.
|
|
|
Registration mints a per-saddle token and delivers it to the daemon's
|
|
|
- `POST /register` (with this app's base URL); a phase-1 daemon then
|
|
|
- asks for work whenever idle-awake and sleeps on a 204 answer, so a
|
|
|
- ready task reaches it within one dispatcher tick (wake → ask →
|
|
|
- allocate). Pre-flip daemons without `/register` still register — copy
|
|
|
- the token from the saddle card instead.
|
|
|
-
|
|
|
-Tasks can also arrive from the agent itself: `saddle subtask` lines
|
|
|
-create child tasks (through the synchronous `POST /api/saddle/subtask`
|
|
|
-callback), and push-style daemons report through `POST
|
|
|
-/api/saddle/results` (round entries plus state transitions) and `POST
|
|
|
-/api/saddle/chunks` (raw PTY batches) — every `/api/saddle/*` call is
|
|
|
-authenticated by the per-saddle bearer token (removing the saddle
|
|
|
-revokes it).
|
|
|
+ `POST /register` (with this app's base URL); the daemon then asks
|
|
|
+ for work whenever idle-awake and sleeps on a 204 answer, so a ready
|
|
|
+ task reaches it within one dispatcher tick (wake → ask → allocate).
|
|
|
+
|
|
|
+**Goals** (`/goals`) are the orchestrated path: write the Initial Brief
|
|
|
+and a branch name, start it, and a goal agent investigates (asking
|
|
|
+pre-plan questions liberally), submits its plan for review, fans flat
|
|
|
+tasks out through the `POST /api/saddle/task/{create,delete,enqueue}`
|
|
|
+callbacks (a per-task worktree per involved repo on an ephemeral
|
|
|
+branch), merges finished branches into the goal branch, pushes, and
|
|
|
+submits a completion report for your review. Push-style daemons report
|
|
|
+through `POST /api/saddle/results` (round entries plus state
|
|
|
+transitions) and `POST /api/saddle/chunks` (raw PTY batches) — every
|
|
|
+`/api/saddle/*` call is authenticated by the per-saddle bearer token
|
|
|
+(removing the saddle revokes it).
|
|
|
|
|
|
## Monitoring agent progress
|
|
|
|
|
|
@@ -195,14 +201,17 @@ the dispatcher wakes the affine saddle, and the daemon's ask returns
|
|
|
the assignment carrying the verdict as its `resume_notice` (the stable
|
|
|
wordings — "Human selected: …", "Plan approved. Proceed with
|
|
|
implementation.", "Review approved. Continue with the task." &c.) with
|
|
|
-a **fresh session id**; the parked session closes as `resumed`. A goal
|
|
|
-agent's blocks attach to the GOAL (dual-rowed: the goal block plus a
|
|
|
-quiet mirror on the run task) — one answer resolves both and resumes
|
|
|
-the run. `saddle plan` parks with the goal's plan-review block:
|
|
|
-Approve answers "Plan approved. Proceed with implementation." and
|
|
|
-applies the markdown as the goal's plan; the feedback textarea returns
|
|
|
-the agent to planning to revise. The completion review answers "Review
|
|
|
-approved. Continue with the task." to complete the goal.
|
|
|
+a **fresh session id**; the parked session closes as `parked`. A goal
|
|
|
+agent's blocks ride its RUN TASK's single block row — one answer
|
|
|
+resolves it and resumes the run. `saddle plan` parks with the goal's
|
|
|
+plan-review block: Approve answers "Plan approved. Proceed with
|
|
|
+implementation." and applies the markdown as the goal's plan (documents
|
|
|
+land on the owning entity only at acceptance — the pending report lives
|
|
|
+in its block alone); the feedback textarea returns the agent to
|
|
|
+planning to revise. The completion review renders as the rich attention
|
|
|
+block (highlight-to-remark annotations queue onto the rejection);
|
|
|
+answering "Review approved. Continue with the task." completes the goal
|
|
|
+and stores the report.
|
|
|
|
|
|
Resumed assignments also carry an optional `context_update` (markdown
|
|
|
string): the delta of the instruction context that changed since the
|
|
|
@@ -334,7 +343,8 @@ awareness + UI (the assignment `capabilities` context,
|
|
|
with the valid-tag list in the error, the task page's top status block
|
|
|
and newest-first blocking sections, the dashboard's pile and timeline,
|
|
|
and the slow-ask lost-wake regression), and the plan-12 goal era
|
|
|
-(lifecycle, plan/completion reviews with dual-rowed goal blocks and the
|
|
|
+(lifecycle, plan/completion reviews with the run-task's single block row
|
|
|
+and the
|
|
|
agreed verdict wordings, task callbacks, coalesced terminal-event
|
|
|
resumes, pause queueing, cancel cascade, auto-enqueue-after, goal
|
|
|
notifications and the goal context of worker assignments).
|
|
|
@@ -362,11 +372,13 @@ HTTP/2 removes the cap entirely.
|
|
|
construction (one goal agent, one worker at a time on the one
|
|
|
registered saddle); multi-saddle fleets must mount the same volume
|
|
|
everywhere before fanning goal work out.
|
|
|
-- **Database tear-down-and-recreate.** The plan-12 migrations flatten
|
|
|
- the schema again (a fresh `M0001_Initial`: goals, goal links/comments/
|
|
|
- blocks/events, the flattened tasks). There is NO upgrade path from a
|
|
|
- plan-11 database — delete the SQLite file (or point `SPRY_DB_PATH` at
|
|
|
- a fresh path) and let the app recreate it.
|
|
|
+- **Database tear-down-and-recreate.** The schema is a single flattened
|
|
|
+ `M0001_Initial` (goals with links/comments, the flattened tasks, and
|
|
|
+ the plan-16 deletions already applied: no `api_tokens`, no
|
|
|
+ `working_branch`, no saddle-row work cache — blocks, comments and
|
|
|
+ narrative ride the run task's own structures). There is NO upgrade
|
|
|
+ path from an earlier era's database — delete the SQLite file (or
|
|
|
+ point `SPRY_DB_PATH` at a fresh path) and let the app recreate it.
|
|
|
- **Restart both sides.** The daemon (`saddle`) and this app must both
|
|
|
restart onto the goal-era builds together — the wire contract
|
|
|
(assignment `kind`/`mode`/`creator`/`goal`/`ephemeral_branch`, the
|