<feed xmlns='http://www.w3.org/2005/Atom'>
<title>soryu/makima/frontend, branch master</title>
<subtitle>soryu-co/soryu mirror</subtitle>
<id>http://src.eirin.xyz/soryu/atom?h=master</id>
<link rel='self' href='http://src.eirin.xyz/soryu/atom?h=master'/>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/'/>
<updated>2026-05-17T20:23:20+00:00</updated>
<entry>
<title>chore: remove LLM module + all dependent surfaces (#135)</title>
<updated>2026-05-17T20:23:20+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-17T20:23:20+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=0d996cf7590e3e52f424859c7d6f0e68640f119e'/>
<id>urn:sha1:0d996cf7590e3e52f424859c7d6f0e68640f119e</id>
<content type='text'>
Wholesale removal of the LLM integration layer. ~14,200 LOC deleted
across backend and frontend. All chat-driven UIs go with it.

## Backend
- Delete `src/llm/` (7,400 LOC): claude/groq clients, contract_tools,
  contract_evaluator, discuss_tools, mesh_tools, phase_guidance,
  task_output, templates, markdown round-trip, tools, transcript_analyzer.
- Delete handlers wholly dependent on LLM:
  - `chat.rs` (file-level LLM chat at /files/{id}/chat)
  - `mesh_chat.rs` (mesh &amp; task LLM chat + history)
  - `templates.rs` (/contract-types listing)
- Strip LLM uses from `mesh_daemon.rs`:
  - `compute_action_directive` (used phase_guidance::check_deliverables_met
    to nudge supervisors with "all tasks done" messages). The auto-PR
    path below still fires when all tasks finish, so no behaviour lost.
  - `crate::llm::markdown_to_body` → inline 1-line replacement that
    wraps markdown content in a single BodyElement::Markdown. The
    editor re-parses on display, so round-trip is preserved.
- Drop routes: /files/{id}/chat, /mesh/chat, /mesh/chat/history,
  /mesh/tasks/{id}/chat, /contract-types.
- Drop the matching openapi registrations.

## Frontend
- Delete components that were LLM-only:
  - `mesh/UnifiedMeshChatInput.tsx`
  - `listen/DiscussContractModal.tsx`
  - `listen/TranscriptAnalysisPanel.tsx`
  - `listen/ContractPickerModal.tsx`
  - `files/CliInput.tsx`
- Delete the entire /listen page (its primary value-add was
  voice → LLM analysis → contract creation; without LLM the page is
  just a transcript display with no obvious user purpose).
- Delete `hooks/useMeshChatHistory.ts` and `lib/listenApi.ts`
  (transcript-analysis API client to the already-Phase-5-removed
  listen handlers).
- Strip api.ts of LLM exports: LlmModel, ChatMessage/Request/Response,
  UserQuestion/Answer, chatWithFile, MeshChat* types &amp; functions,
  getMeshChatHistory, clearMeshChatHistory, chatWithMeshContext,
  ContractTypeTemplate, listContractTypes, chatWithContract,
  getContractChatHistory, clearContractChatHistory, discussContract,
  PhaseDefinition, DeliverableDefinition.
- mesh.tsx: drop UnifiedMeshChatInput render + the chatContext memo +
  handleTaskUpdatedFromCli (only consumer was the input).
- files.tsx: drop CliInput render + handleGenerateFromElement +
  handleBodyUpdate + handleClearFocus + suggestedPrompt state (all
  CliInput-only).
- NavStrip: drop the /listen link.
- main.tsx: drop the /listen route.

## Net diff: 37 files changed, 58 insertions, 14,281 deletions.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat(directives): strict orchestration flow + sidebar overhaul + task page rewrite (#134)</title>
<updated>2026-05-16T18:56:21+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-16T18:56:21+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=ce29ae801bcc5a0ba76d5a8d1565242ab267a47d'/>
<id>urn:sha1:ce29ae801bcc5a0ba76d5a8d1565242ab267a47d</id>
<content type='text'>
End-to-end rewrite addressing the issues from the user's UX review.
The system now feels like a daemon-orchestration tool: lock a contract
and the orchestrator just goes; PR raised → auto-ship → reopen for
amendments. The sidebar tree shows real entities only (no duplicates,
no inline action buttons polluting the file list), and every entity
gets a right-click context menu. Task page matches the old /exec
layout (diff on the left, feed + composer on the right).

## Backend — strict lifecycle (the orchestrator-never-spawned bug)

Root cause: `phase_planning()` gates on `directive.status='active'`, but
`start_contract()` only flipped the contract row — the parent directive
stayed in whatever state it was. So locking a contract did nothing
visible.

Fix: contract lifecycle now drives directive status in the same
transaction.

  start_contract   → if contract becomes active, flip directive
                     draft|paused|idle|inactive → active
  pause_contract   → after promote, if no active contract left,
                     directive → paused
  complete_contract→ after promote, if no active left, directive →
                     inactive (also fires on auto-ship from PR detect)
  unlock_contract  → if was active and no active left, directive →
                     paused
  reopen_contract  → NEW. shipped → active. Directive → active,
                     orchestrator_task_id/pr_url/pr_branch cleared so
                     the reconciler spawns a fresh planner. The
                     planner reads get_latest_merged_revision and
                     frames the new plan as an amendment.

handlers::directive_documents lifts state.kick_directive_reconciler()
into run_contract_transition so every successful transition wakes the
reconciler immediately (no 15s wait).

handlers::directives `update_directive` (PR-detection branch) calls
`complete_contract(active_contract_id, pr_url, pr_branch)` instead of
`set_directive_inactive`. The contract auto-ships; the directive
follows via the sync above. No more manual "Mark complete" click.

POST /api/v1/contracts/{id}/reopen added + wired through openapi.

Spawn task names dropped the directive-title prefix that looked
redundant in the sidebar:
  "Plan: &lt;title&gt;"      → "orchestrator"
  "Re-plan: &lt;title&gt;"   → "orchestrator (re-plan)"
  "PR: &lt;title&gt;"        → "completion"
  "Update PR: &lt;title&gt;" → "completion (update)"

## Frontend — sidebar

* De-dupe: DocumentTasksFolder filters tasks[] to exclude any task
  whose id already appears in steps[].taskId. Single row per task,
  single highlight on click.
* Generic SidebarContextMenu (new) replaces the directive-only
  DirectiveContextMenu (deleted). Per-entity item arrays built at the
  page level — directive, contract, step, task each have their own
  contextual actions.
* Right-click works on every sidebar entity now (was directive-only).
* `+ New document` / `+ New ephemeral task` inline buttons removed.
  Reachable via the directive folder right-click OR the hover-only
  `+` button on the directive folder row.
* ContractHeader: dropped "Mark complete" button (auto-fires on PR).
  Added "Reopen for amendment" button when contract is shipped.

## Frontend — task page rewrite

TaskPage.tsx replaces DocumentTaskStream.tsx (deleted). Two-column
layout matches the old /exec page that the user preferred:

  ┌────────────────────────┬──────────────────────────────────┐
  │  Changed files (~30%)  │  Transcript feed (scrollable)    │
  │  ──────────────────    │  ──────────────────────          │
  │  src/foo.rs            │  [user] do thing                 │
  │  src/bar.rs            │  [tool] Read foo.rs              │
  │                        │                                  │
  │  Diff (selected file)  │                                  │
  │                        ├──────────────────────────────────┤
  │                        │  Composer (sticky bottom)        │
  └────────────────────────┴──────────────────────────────────┘

Diff comes from getTaskDiff(); parseDiff + DiffFileView exported from
OverlayDiffViewer for reuse (no duplication). Diff auto-refreshes
when the task transitions to a terminal state. Transcript styling +
sticky composer keep the parts the user liked. "Open in task page"
button removed — the right pane IS the task page.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>fix(directives): tasks folder visibility, circular auto-save timer, unlock-to-edit (#133)</title>
<updated>2026-05-16T17:04:17+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-16T17:04:17+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=8e2bbcab1a7b3b9005803d7ce3bfce7fa483a4d7'/>
<id>urn:sha1:8e2bbcab1a7b3b9005803d7ce3bfce7fa483a4d7</id>
<content type='text'>
* feat: soryu - makima: Fix tasks/ folder visibility and rename for multi-contract directives

* feat: soryu - makima: Replace auto-save countdown text/bar with a circular timer

* feat: soryu - makima: Require Unlock before editing a locked contract body</content>
</entry>
<entry>
<title>feat(directives): drop directives.goal — orchestration reads contract body (#132)</title>
<updated>2026-05-08T15:34:11+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-08T15:34:11+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=dce7f50e503dc374aaf879df33e725af16c4cc78'/>
<id>urn:sha1:dce7f50e503dc374aaf879df33e725af16c4cc78</id>
<content type='text'>
Hard cut. The unified contracts surface owns spec text now; the
directive itself is just a folder. The orchestrator daemon reads the
active contract's body when it spawns, replans, or runs completion.

Schema (migration 20260510000000):
  - DROP TABLE directive_goal_history
  - ALTER TABLE directives DROP COLUMN goal
  - ALTER TABLE directives DROP COLUMN goal_updated_at

New repo helper:
  - get_active_contract_body(directive_id) — picks the
    active|queued|draft contract (in that order), most-recent first.

Backend cuts:
  - Directive / DirectiveSummary / CreateDirectiveRequest /
    UpdateDirectiveRequest lose goal &amp; goalUpdatedAt.
  - CreateDirectiveRequest gains optional `contractBody` — when
    provided, create_directive_for_owner auto-creates a first contract
    with that body in the same transaction.
  - Removed: update_directive_goal, update_directive_goal_keep_orchestrator,
    save_directive_goal_history, get_directive_goal_history,
    DirectiveGoalHistory model, UpdateGoalRequest.
  - Removed handlers::directives::update_goal + the
    /directives/{id}/goal route.
  - orchestration::directive::build_planning_prompt /
    build_completion_prompt / build_order_pickup_prompt now take a
    `contract_body: &amp;str` instead of `goal_history`. classify_goal_change
    + try_interrupt_planner_with_goal_edit + GoalChangeKind +
    GoalEditInterruptResult removed (they were only useful for the
    small-vs-large goal-edit interrupt cycle).

CLI:
  - `makima directive update-goal` removed (UpdateGoalArgs deleted,
    Commands enum trimmed, ApiClient::directive_update_goal +
    UpdateGoalRequest deleted).

Frontend:
  - Directive / DirectiveSummary / CreateDirectiveRequest types lose
    goal &amp; goalUpdatedAt; CreateDirectiveRequest gains `contractBody`.
  - useDirective drops updateGoal helper.
  - api.ts updateDirectiveGoal removed.
  - Legacy DirectiveList + DirectiveDetail components deleted; the
    /directives route now always renders the document-mode page.
    The user-settings documentModeEnabled flag is no longer
    consulted at the route level.
  - NewContractModal passes body via contractBody.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>refactor(frontend): DocumentEditor takes explicit body/title/documentId props (#131)</title>
<updated>2026-05-08T12:43:17+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-08T12:43:17+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=e4f1622a0f0ac74707cc1c9810e0b99e948d1319'/>
<id>urn:sha1:e4f1622a0f0ac74707cc1c9810e0b99e948d1319</id>
<content type='text'>
Stops shadowing directive.goal with the contract body via a synthesised
directive object. DocumentEditor now accepts:

  * documentId — scopes the localStorage draft key per contract so
    switching contracts under the same directive doesn't clobber the
    other's unsaved edits.
  * title      — the contract title rendered as the H1.
  * body       — the contract body, used to seed the editor.
  * onUpdateBody (was onUpdateGoal)

The `directive` prop stays for orchestrator state + embedded steps
panel. document-directives.tsx drops the directiveAsDocument synthesis
hack and passes body/title from the contract directly.

This is the prep-work for dropping `directives.goal` from the schema —
once nothing reads it, the column can be dropped in a follow-up
without touching the editor.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat(contracts): drag-to-reorder active contract rows in sidebar (#130)</title>
<updated>2026-05-08T11:17:07+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-08T11:17:07+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=2dda1f96a30eee2fda86be9a8a59ce5cb26dad7f'/>
<id>urn:sha1:2dda1f96a30eee2fda86be9a8a59ce5cb26dad7f</id>
<content type='text'>
HTML5 drag/drop on active contract rows. Dragging a row over
another in the same directive folder shows a green top-border drop
indicator; dropping calls reorderDirectiveContract(id, targetPosition)
and refreshes the folder. Shipped/archived rows aren't draggable
(historical, ordering is fixed).

Implementation:
- DocumentRow gains optional draggable + drag event props.
- DirectiveFolder owns the drag/over state and handleReorder
  callback; computes target position from the drop-target row's
  current position.
- Repository's reorder endpoint already exists from the backbone PR
  and handles sibling shift in a transaction.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat(contracts): lifecycle — Lock/Start/Pause/Complete/Unlock + queue scheduler (#129)</title>
<updated>2026-05-08T11:12:51+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-08T11:12:51+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=6690b714c64aaef5781bc0aac41b777ab72e9070'/>
<id>urn:sha1:6690b714c64aaef5781bc0aac41b777ab72e9070</id>
<content type='text'>
Adds the contract lifecycle layer on top of the unified-contracts
backbone (#128). State machine:

    draft → queued → active → shipped → archived

At most one contract per directive sits in `active` at any time —
the queue is serialised because each directive owns a single shared
worktree. Repository helpers handle the transition checks AND
auto-promote the next-up `queued` contract whenever the active slot
frees (pause / complete / unlock-from-active / archive-from-active).

Endpoints (all under /api/v1/contracts/{id}):
  POST /start    draft → queued | active (depending on slot)
  POST /pause    active → queued; promotes next queued
  POST /complete active → shipped; optional pr_url + pr_branch
  POST /unlock   queued | active → draft; promotes if was active

Frontend wiring:
  * `DirectiveContractStatus` now includes `queued`.
  * Migration adds `queued` to the CHECK constraint on
    directive_documents.status.
  * `ContractHeader` component renders breadcrumb + status pill +
    status-driven action buttons + a merge-mode (shared / own_pr)
    radio. Merge mode is editable only while draft / queued so a
    running flow's branch target can't change mid-stream.
  * RepositoryError gains a `Validation(String)` arm; the three
    existing exhaustive matches (files, mesh, versions) get a
    400 BAD_REQUEST response for it.

Drag-to-reorder UI deferred to a small follow-up — the backend
endpoint already exists from the backbone PR.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat(directives): unified contracts surface — backbone (#128)</title>
<updated>2026-05-08T10:29:56+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-08T10:29:56+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=e00be74c8b575c725829677aadeb755ee81454d0'/>
<id>urn:sha1:e00be74c8b575c725829677aadeb755ee81454d0</id>
<content type='text'>
This is the backbone PR for the unified directive workflow. A directive
holds a sequence of contracts; each contract is a spec body whose
execution drives tasks in the directive's shared worktree. Lifecycle
(Lock &amp; Start, queue scheduler, drag-reorder) lands in follow-ups.

What's in this PR:
- Migration adds `position` (queue order) and `merge_mode`
  (shared|own_pr) columns to directive_documents. The actual table
  rename is deferred — the legacy `contracts` table from the old
  contracts system still exists, and the rename collision waits for
  Phase 5 to drop legacy contracts.
- Repository: list orders by position; create assigns next-position;
  update accepts merge_mode; new reorder_directive_document_position
  shifts siblings inside a transaction.
- HTTP: endpoints aliased under /api/v1/directives/{id}/contracts and
  /api/v1/contracts/{id}/... with a new /contracts/{id}/reorder.
- Frontend: api types renamed `DirectiveContract*` (avoiding the
  legacy `Contract` type collision); document-directives.tsx imports
  via aliases so the rest of the file is untouched.

Internal struct + table names stay `DirectiveDocument` /
`directive_documents` until the legacy contracts cleanup.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>fix(doc-mode): make task rows clickable and render live transcript (#125)</title>
<updated>2026-05-05T15:30:46+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-05T15:30:46+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=928598b1b8399a95918dc1b315274a9d175eb8d9'/>
<id>urn:sha1:928598b1b8399a95918dc1b315274a9d175eb8d9</id>
<content type='text'>
Task rows in the directive sidebar's `tasks/` subfolder were rendered as
inert `&lt;div&gt;` elements with no click handler, and EditorShell had no
branch for `selection.taskId` — so clicking a task did nothing visible.

- StepRow and TaskRow are now `&lt;button&gt;` elements that call
  `onSelect(directiveId, taskId)` and navigate to
  `/directives/&lt;dirId&gt;?task=&lt;taskId&gt;`.
- EditorShell renders DocumentTaskStream with a breadcrumb when
  `selection.taskId` is set (winning over the document path).
- Step rows whose backing task hasn't been spawned yet stay disabled.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>fix(doc-mode): flatten sidebar list + restore right-click context menu (#124)</title>
<updated>2026-05-02T16:47:24+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-05-02T16:47:24+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=cd09103183139788ba4297eaaa6f75d51a154c8a'/>
<id>urn:sha1:cd09103183139788ba4297eaaa6f75d51a154c8a</id>
<content type='text'>
Two regressions reported by the user:

1. The sidebar was grouping contracts under per-status sub-folders
   (active/idle/archived). The user explicitly does not want that —
   they want a flat list with status indicated by a colored dot on the
   right of each row.

2. The right-click context menu on contract rows was missing —
   start/pause/archive/delete/create-PR/update-PR are no longer
   reachable through the UI.

Fixes:

* Drop the SidebarGroup/bucketOf/GROUP_LABEL helpers; replace the
  grouped render with a flat sort by status precedence
  (active → paused → idle → draft → inactive → archived) then alpha
  by title within the same status. The existing dot-color palette is
  unchanged; the dot just moved from the LEFT of the contract title
  to the RIGHT (after the orchestrator-running pulse, when present).

* Wire `onContextMenu` through SidebarProps → DirectiveFolderProps →
  the folder header `&lt;button&gt;`. Page-level state captures the click
  position and the targeted directive; the existing
  DirectiveContextMenu component (start/pause/archive/delete/PR/
  Advance/Cleanup/PickUpOrders) renders on top.

* runAction helper centralises error handling: dispatches the API
  call, refreshes the sidebar list + bumps the document-folder
  refresh nonce on success, surfaces an alert on failure.

* Delete confirms via window.confirm and clears the URL when the
  deleted contract was the one selected.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
</feed>
