Selaa lähdekoodia

ui+docs: clanker-saddle favicon + touch-icon, emblem beside the heading; READMEs swept for the plan-16 world (single-row blocks, parked outcomes, derived saddle state, no subtask/legacy-wire remnants, 924-check count)

clanker 1 kuukausi sitten
vanhempi
sitoutus
b1176f3cf8
5 muutettua tiedostoa jossa 76 lisäystä ja 45 poistoa
  1. 54 42
      README.md
  2. 15 1
      meson.build
  3. 2 0
      src/main.vala
  4. 2 1
      src/pages/app.css
  5. 3 1
      src/pages/main.html

+ 54 - 42
README.md

@@ -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

+ 15 - 1
meson.build

@@ -122,6 +122,20 @@ app_css = custom_target('app-css',
     command: [statum_mkres, '-o', '@OUTPUT@', '-n', 'app.css', '--class-name', 'AppCss',
               '--ns', 'ClankerSaddle', '-c', 'text/css', '@INPUT@'],
 )
+favicon_png = custom_target('favicon-png',
+    input: 'src/clanker-saddle.png',
+    output: 'FaviconResource.vala',
+    command: [statum_mkres, '-o', '@OUTPUT@', '-n', 'clanker-saddle.png',
+              '--class-name', 'FaviconResource', '--ns', 'ClankerSaddle',
+              '-c', 'image/png', '@INPUT@'],
+)
+emblem_png = custom_target('emblem-png',
+    input: 'src/clanker-saddle-emblem.png',
+    output: 'EmblemResource.vala',
+    command: [statum_mkres, '-o', '@OUTPUT@', '-n', 'clanker-saddle-emblem.png',
+              '--class-name', 'EmblemResource', '--ns', 'ClankerSaddle',
+              '-c', 'image/png', '@INPUT@'],
+)
 
 # =============================================================================
 # Sources
@@ -192,7 +206,7 @@ app_generated = [
 ]
 
 clanker_saddle_exe = executable('clanker-saddle',
-    app_sources + app_generated + [app_css, push_crypto],
+    app_sources + app_generated + [app_css, favicon_png, emblem_png, push_crypto],
     include_directories: include_directories('src/Push'),
     dependencies: [
         glib_dep,

+ 2 - 0
src/main.vala

@@ -161,6 +161,8 @@ int main(string[] args) {
 
         // spry:resources-begin
         statum.add_resource<ClankerSaddle.AppCss>();
+        statum.add_resource<ClankerSaddle.FaviconResource>();
+        statum.add_resource<ClankerSaddle.EmblemResource>();
         // spry:resources-end
 
         // spry:auth-begin

+ 2 - 1
src/pages/app.css

@@ -46,7 +46,8 @@ header {
     padding: var(--grid-2) 0;
 }
 
-header h1 { font-size: 1.25rem; margin: 0; }
+header h1 { font-size: 1.25rem; margin: 0; display: flex; align-items: center; gap: 0.5rem; }
+header h1 .brand-emblem { height: 1.6rem; width: auto; margin-block: -0.2rem; }
 header nav { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--grid-1); }
 
 main { padding: var(--grid-2) 0 var(--grid-4); }

+ 3 - 1
src/pages/main.html

@@ -7,6 +7,8 @@
     <pstm-title>ClankerSaddle</pstm-title>
     <script pstm-res-src="statum.js"></script>
     <link rel="stylesheet" pstm-res-href="app.css">
+    <link rel="icon" type="image/png" pstm-res-href="clanker-saddle.png">
+    <link rel="apple-touch-icon" pstm-res-href="clanker-saddle.png">
     <link rel="stm-guard" require="auth != null || location.pathname == '/login'" href="/login">
     <script>
         // Shared page plumbing. showToast: slide-in notifications (used in
@@ -333,7 +335,7 @@
 </head>
 <body>
     <header>
-        <h1>ClankerSaddle</h1>
+        <h1><img class="brand-emblem" alt="" pstm-res-src="clanker-saddle-emblem.png">ClankerSaddle</h1>
         <nav>
             <a href="/">Dashboard</a>
             <!-- spry:nav-begin -->