<feed xmlns='http://www.w3.org/2005/Atom'>
<title>soryu, branch doc-mode-stage3-revisions</title>
<subtitle>soryu-co/soryu mirror</subtitle>
<id>http://src.eirin.xyz/soryu/atom?h=doc-mode-stage3-revisions</id>
<link rel='self' href='http://src.eirin.xyz/soryu/atom?h=doc-mode-stage3-revisions'/>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/'/>
<updated>2026-04-30T15:01:52+00:00</updated>
<entry>
<title>feat(directives): per-PR revision snapshots + sidebar history</title>
<updated>2026-04-30T15:01:52+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-30T15:01:52+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=db284439a74093a316ba6da52ad82e5cae1246ef'/>
<id>urn:sha1:db284439a74093a316ba6da52ad82e5cae1246ef</id>
<content type='text'>
Stage 3 of the doc-mode revamp. Builds the foundation for treating contracts
as living specifications by freezing their content into a revision every
time a PR is raised.

## directive_revisions table (new migration)

(id, directive_id, content, pr_url, pr_branch, pr_state, version, frozen_at)
with UNIQUE(directive_id, version) and a partial index on pr_state='open'
so the next reconciler iteration can poll only what's still in flight.

pr_state is constrained to 'open' | 'merged' | 'closed' to mirror GitHub's
PR lifecycle. For Stage 3 we only freeze on PR creation; pr_state poll is
deferred to a follow-up.

## Repository helpers

- create_directive_revision: idempotent on (directive_id, pr_url) so a
  re-run of the orchestrator's completion task can't double-snapshot.
  Auto-assigns version = MAX(existing) + 1 per directive.
- list_directive_revisions_for_owner: scoped through the directive join
  so users can only read their own contract history.
- update_directive_revision_pr_state: stub for the upcoming poller.
- get_latest_merged_revision: returns the most recent merged revision —
  this is what Stage 4 will diff against on amendments.

## Snapshot trigger

update_directive handler now reads the BEFORE pr_url before the update.
If pr_url transitions None → Some, it snapshots the directive's current
goal as a revision tied to the new pr_url. Failures log and continue —
the directive update itself is unaffected.

## API + OpenAPI

GET /api/v1/directives/{id}/revisions returns DirectiveRevisionListResponse
(revisions newest-first). Schemas registered in OpenAPI.

## Frontend: revisions/ subfolder + read-only viewer

Each contract folder now has a third subfolder ("revisions/") that lazily
fetches and lists past revisions when the parent directive folder is open.
Empty contracts skip the subfolder entirely so brand-new ones aren't
cluttered. Each row shows v&lt;N&gt;.md plus a small pill ('open'/'merged'/
'closed').

Selecting a revision encodes itself into the existing ?task= param as
"revision:&lt;id&gt;", so EditorShell can route between the live task stream
(realTaskId), the read-only RevisionViewer (revisionId), or the editor
itself (neither). The viewer renders the frozen markdown verbatim with
a deep-link to the PR — these are immutable historical records, not edit
surfaces.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(doc-mode): rename surfaced label "Directive" → "Contract"</title>
<updated>2026-04-30T14:50:36+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-30T14:50:36+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=efec60a5f3b6f729c8e3dafead6badb8038adf4c'/>
<id>urn:sha1:efec60a5f3b6f729c8e3dafead6badb8038adf4c</id>
<content type='text'>
Stage 2: cosmetic UI rename only. Database tables, API paths, hook names,
and CLI commands all stay as 'directive' for now — we'll phase out the
existing standalone /contracts table later.

Surface changes:
- Sidebar header "Documents" → "Contracts"; root folder "directives/" →
  "contracts/"; empty state "No directives yet" → "No contracts yet".
- Editor placeholders, breadcrumb header, "back to document" → "back to
  contract", right-click menu header.
- NavStrip: when documentModeEnabled is on, the "Directives" link renders
  as "Contracts" (href stays /directives so links don't break).
- Save bar message "saving will replan the directive" → "the contract".

No backend changes — directive.goal etc. all still work the same.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(doc-mode): autosave robustness, draft→active flip, save-now, sidebar context menus (#108)</title>
<updated>2026-04-30T14:48:26+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-30T14:48:26+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=2dafe938f41edbb8ceb7c6a3655c9533bb50e47d'/>
<id>urn:sha1:2dafe938f41edbb8ceb7c6a3655c9533bb50e47d</id>
<content type='text'>
Stage 1 of the planned doc-mode revamp — bug fixes + UX polish ahead of the
larger contract-revisioning architecture work.

## Backend: 'draft' included in goal-update status flip

repository::update_directive_goal previously flipped only idle/paused → active
on a goal save, leaving 'draft' alone. That meant brand-new directives got
their goal persisted on save but never spawned a planner — exactly the
"orchestrator never runs" report. Extended the CASE clause so 'draft' also
flips to 'active' on save. The status remains visible to users; this just
makes the implicit "first goal save = start" behaviour work end-to-end.

## Autosave robustness (DocumentEditor.tsx)

The synchronous-write fix from the previous PR was correct in principle but
not visible enough for users to confirm it was working, and could still drop
the very last edit on an abrupt tab close. This change:

- Adds beforeunload / pagehide / visibilitychange handlers that synchronously
  flush pendingGoalRef → localStorage (skipping if it matches the persisted
  value). Backed by a persistedGoalRef that tracks directive.goal in real
  time so the handler doesn't capture a stale closure.
- Tracks the timestamp of every successful draft write (draftSavedAt) and
  surfaces it as a "Draft saved Ns ago" stamp in the bar — refreshed on a
  1Hz ticker so users can SEE the autosave is alive.
- Logs a console.warn on localStorage write failure (was silently swallowed)
  so quota / storage-disabled environments are diagnosable.

## Always-visible save bar + Save now button

The bar now renders in every state (was hiding when idle/pending-with-time-
remaining). Idle shows a quiet "Up to date." Pending outside the last 10s
shows "Unsaved changes — auto-save soon." Save now is always present;
disabled only when truly idle.

## EXEC and CONTRACTS hidden in document mode

NavStrip filters Contracts and Exec links when settings.documentModeEnabled
is true. Those areas are subsumed by the directive-document interface; the
nav strip stops surfacing them so document mode users have one canonical
place to work.

## Right-click context menus on sidebar

Right-clicking a directive folder header opens DirectiveContextMenu with
start / pause / archive / delete / Go-to-PR — same component the legacy
list page uses. Right-clicking a task row inside the tasks/ subfolder opens
a smaller TaskContextMenu with Interrupt (for orchestrator/completion/
running steps) and Mark complete / failed / skipped (for step rows).
Step lifecycle calls require the directive_step.id, so FolderTaskRow now
carries stepId alongside taskId.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat(document-mode): folder layout v2, glow on pending, inline formatting, autosave fix (#107)</title>
<updated>2026-04-30T11:12:48+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-30T11:12:48+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=a2148d4e3117cdda2e1d0a8e3df289bfe04789a3'/>
<id>urn:sha1:a2148d4e3117cdda2e1d0a8e3df289bfe04789a3</id>
<content type='text'>
## Autosave bug fix (top priority)

The 250ms debounce on the localStorage draft write was racing the unmount
cleanup: typing then navigating within 250ms cleared the pending timer
*before* it flushed, which is exactly when we needed the draft saved.
Drafts are now written synchronously on every keystroke. localStorage
.setItem on a small string is sub-millisecond — the debounce was a
premature optimisation.

## Sidebar v2 (document-directives.tsx)

- Tasks now live in a `tasks/` subfolder inside each directive folder
  (orchestrator, completion, and started step tasks). The pinned `.md`
  document remains at the top of the directive folder.
- Status circles moved to the RIGHT side only (previously rendered on
  both sides, which the user found noisy).
- New `StatusDot` component composes the status colour with two optional
  modifiers: a "live" pulse when the orchestrator is running, and a
  GLOW (amber ring + pulse) when there is a pending user question for
  that directive or task. The glow is sourced from the existing
  SupervisorQuestionsContext, indexed by `directiveId` and `taskId`.
- New `TaskIcon` (terminal) and `CompletionIcon` (PR-bracket) so
  orchestrator/step/completion entries look distinct from the .md file.

## Inline formatting in the editor (DocumentEditor.tsx)

- New `MarkdownShortcutPlugin` (scoped to TEXT_FORMAT_TRANSFORMERS only)
  so typing `**foo**`, `*foo*`, `` `foo` ``, `~~foo~~` auto-formats inline.
  Block-level shortcuts (# heading, - list) are intentionally excluded so
  the document shape (H1 / goal / StepsBlock / trailing para) stays intact.
- New `FloatingFormatToolbar` appears above any non-collapsed selection
  inside the goal paragraph, with B / I / U / S / &lt;/&gt; buttons that
  dispatch FORMAT_TEXT_COMMAND. Buttons highlight when the corresponding
  format is active. Standard ⌘B / ⌘I / ⌘U keyboard shortcuts also work
  via the existing RichTextPlugin.
- Round-trip via a small inline-only markdown serializer/parser so
  formatting persists across saves. Supported markers: `\``code\``,
  `***bold-italic***`, `**bold**`, `*italic* / _italic_`, `~~strike~~`.
  Underline survives within the editor session (toolbar / shortcut) but
  has no markdown syntax so it does not round-trip — by design.
- No backend schema change: `directive.goal` is still a TEXT column, it
  just contains inline markdown now.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>fix(directive): cancel orphaned planner and kick reconciler on goal update (#104)</title>
<updated>2026-04-30T09:43:31+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-30T09:43:31+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=c3e97bbcc32bd18d9344dd44cc54dfcdce32100b'/>
<id>urn:sha1:c3e97bbcc32bd18d9344dd44cc54dfcdce32100b</id>
<content type='text'>
Resolves the user-visible bug where editing a directive's goal mid-flight
shows "saved" but does not actually replan: the running planner kept emitting
add-step calls based on the OLD goal while a fresh planner was supposed to
take over, and the user had to wait up to 15s for the next reconciler tick
before any replanning even started.

## What was happening

PUT /api/v1/directives/{id}/goal already had two paths:
  - Small change + planner running → SendMessage interrupt + KEEP orchestrator.
  - Everything else → clear orchestrator_task_id and let phase_replanning
    spawn a new planner on the next 15s tick.

The "everything else" path cleared the directive's pointer to the planner
task but never cancelled the task itself. The task kept executing and could
race the new planner by adding more steps from the stale plan. Worse, those
new steps could push MAX(steps.created_at) past the just-bumped
goal_updated_at, suppressing phase_replanning entirely.

## Fix

1. New helper `try_cancel_running_planner()` (orchestration/directive.rs):
   sends `InterruptTask { graceful: true }` to the daemon owning the
   orchestrator task and marks the task `interrupted` in the DB. All errors
   are logged and swallowed so the goal update still completes.

2. update_goal handler calls the helper whenever it is about to take the
   "clear orchestrator_task_id" branch, so the orphaned planner stops
   producing stale-plan steps before its DB linkage is cut.

3. New `AppState::directive_kick` (tokio::sync::Notify) lets the handler
   signal the reconciler to run a tick immediately. The reconciler loop in
   server/mod.rs now selects between its 15s interval and the notify, so the
   user no longer waits up to 15s after editing a goal before replanning
   actually starts. update_goal calls `kick_directive_reconciler()` after
   the goal is persisted (both paths).

## Why not also loosen `get_directives_needing_replanning`

The query already covers the common cases once the orphan-cancel lands —
without a still-running orphan adding fresh steps, goal_updated_at reliably
exceeds MAX(steps.created_at) after a goal edit. Loosening the predicate
risked spurious replans for directives that legitimately have no steps yet
(those are handled by `phase_planning`).

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat(document-mode): longer goal save countdown, per-directive folders, live task stream (#103)</title>
<updated>2026-04-30T08:57:07+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-30T08:57:07+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=2b695485753d55f956746b73c31c2deba0ed0a29'/>
<id>urn:sha1:2b695485753d55f956746b73c31c2deba0ed0a29</id>
<content type='text'>
Three connected UX changes for the document-mode directive UI.

## Goal save UX (DocumentEditor.tsx)
- Replace the old 3-second countdown with 60s when no orchestrator is running
  and 10s when one is, so editing a goal mid-flight does not feel rushed.
- The countdown bar is hidden until the last 10 seconds; users see "Saved" /
  "Unsaved changes" indicators rather than a constantly-ticking clock.
- Continuously persist work-in-progress to localStorage on every keystroke
  (debounced 250ms). On mount, if a draft for the directive exists in
  localStorage and differs from the persisted goal, restore it and put the
  editor in dirty/pending state — leaving the page no longer loses work.
- localStorage-backed "Live start" toggle in the bar. When off, the editor
  stays in "dirty" instead of auto-firing; user clicks "Save now" to commit.
- Discard button reverts the editor to the persisted goal and clears the draft.

## Sidebar restructure (document-directives.tsx)
- Drop the active/idle/archived top-level grouping; show one folder per
  directive instead. Folders sort by lifecycle (active, paused, idle, draft,
  archived) then alphabetically.
- Each folder header shows a colored status dot on BOTH sides (left as the
  primary status icon, right as a mirror plus a pulse when the orchestrator
  is live), replacing the previous "/active", "/idle" text labels.
- Inside each open folder: the directive's document is pinned at the top
  (with a small star icon), then the orchestrator task (if running), then
  the completion task, then any step tasks that have started.
- The currently-selected directive's folder is auto-opened so deep links
  always land somewhere visible.

## Live document task stream (DocumentTaskStream.tsx, new)
- Selecting a task in a folder navigates to ?task=&lt;id&gt; and replaces the
  Lexical editor with a document-styled live transcript: assistant prose as
  flowing paragraphs, tool calls as marginalia, results as a closing block.
  No log/code box.
- Comment textarea at the bottom calls sendTaskMessage on submit, the same
  wire the existing TaskOutput input bar uses for interrupts. ⌘/Ctrl-Enter
  submits.
- Header breadcrumb gains a "back to document" affordance to return to the
  pinned doc view without reopening the sidebar.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>fix(ci): regenerate pnpm-lock.yaml for lexical deps added by #101 (#102)</title>
<updated>2026-04-29T23:05:16+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-29T23:05:16+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=8d864f83a1d8c2ad47cf194b70d802e366a146b0'/>
<id>urn:sha1:8d864f83a1d8c2ad47cf194b70d802e366a146b0</id>
<content type='text'>
PR #101 added @lexical/* and lexical to makima/frontend/package.json but did
not refresh the lockfile, breaking CI installs that run with the default
--frozen-lockfile setting.

Regenerated via `pnpm install --no-frozen-lockfile` and verified
`pnpm install --frozen-lockfile` now resolves cleanly. `npx tsc --noEmit`
also passes.

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat: document-mode directive UI proof of concept (Lexical) (#101)</title>
<updated>2026-04-29T00:10:11+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-29T00:10:11+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=4b1d608b839769052634b4facc345b891d468926'/>
<id>urn:sha1:4b1d608b839769052634b4facc345b891d468926</id>
<content type='text'>
* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: Backend: feature flag + goal-edit interrupt messaging

* WIP: heartbeat checkpoint

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: Frontend: Lexical document editor with step blocks, context menu, countdown</content>
</entry>
<entry>
<title>revert PRs #93-#98; enforce strict-linear-DAG + mandatory directive verify (#100)</title>
<updated>2026-04-28T20:26:11+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-28T20:26:11+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=5bde7c2d7e099fd9c8b2615602ab1d096bd9b6be'/>
<id>urn:sha1:5bde7c2d7e099fd9c8b2615602ab1d096bd9b6be</id>
<content type='text'>
* revert: roll back PRs #93-#98 to pre-Lexical baseline

Reverts the entire chain of directive document UI work and the homepage redesign,
restoring the working tree to the state at 3679ceb (before c8b169d / PR #93).

PRs reverted:
- #93 c8b169d feat: Document UI for directive orchestration with Lexical editor
- #94 d6f01a6 fix: compilation error and warnings already merged via PR #93
- #95 5aa3faf fix: resolve compilation error and warnings in Rust backend
- #97 d513f93 feat: document UI with contract blocks, expandable logs, and interaction controls
- #96 6366941 feat: Redesign homepage with professional PC-98 styling
- #98 d1fdfb1 feat: revert broken directive PRs, re-implement Lexical document orchestrator

The directive Document UI experiments produced fragile output and merge artifacts;
follow-up commits in this PR change orchestration to favor strictly linear DAGs and
add goal/conflict verification so future runs do not require this kind of cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;

* feat(directive): strict-linear-DAG planning + mandatory `directive verify`

Tightens directive orchestration so the final PR almost never needs a hand-merge:

1. Planning prompts now strongly bias toward strictly linear DAGs.
   Parallel steps are reserved for genuinely independent work (e.g. disjoint
   modules); the default for "in doubt" is sequential. Linear chains inherit
   each previous step's worktree, so the final merge is typically just a
   rebase against the base branch.

2. New CLI command `makima directive verify` does a local in-memory
   `git merge-tree` of HEAD against `&lt;remote&gt;/&lt;base&gt;` and exits non-zero
   with a list of conflicting files if the PR would not merge cleanly.
   Pure-local — no API call, no working-tree mutation.

3. Completion / PR-creation prompts now mandate three pre-push checks:
   a. build (`cargo check` and/or `tsc --noEmit`),
   b. `makima directive verify --base &lt;base_branch&gt;` must exit 0, and
   c. an explicit goal-alignment self-check against the diff.
   The orchestrator is told NOT to push, create the PR, or call
   `makima directive update` until all three pass. Skipping any of them
   is documented as a directive failure.

The combination means that with a linear DAG the final PR-creation task
should almost never see a real conflict — when it does, that is treated as
a planning bug to escalate rather than something to paper over with
`-X theirs`.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;

* fix(frontend): TS errors pre-existing on master

- TaskSlideOutPanel: declare missing `selectedFileDiff` / `selectedFilePath`
  state hooks that were referenced everywhere but never created, and
  re-balance the JSX so the `&lt;&gt;...&lt;/&gt;` fragment in the non-diff branch is
  closed (the previous indentation/braces would not parse).
- api.ts: add a `getWorktreeDiff` thin wrapper around `getTaskDiff` so
  TaskDetail's per-file click handler type-checks (the per-file slice is a
  future improvement; today both return the full task diff).
- WorktreeFilesPanel: remove unused `isClickable` local; the gating already
  reads `onFileClick` directly inline.

Run after revert: `npx tsc --noEmit` exits 0.

Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;

---------

Co-authored-by: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat: revert broken directive PRs, re-implement Lexical document orchestrator (#98)</title>
<updated>2026-04-28T18:12:52+00:00</updated>
<author>
<name>soryu</name>
<email>soryu@soryu.co</email>
</author>
<published>2026-04-28T18:12:52+00:00</published>
<link rel='alternate' type='text/html' href='http://src.eirin.xyz/soryu/commit/?id=d1fdfb140cc440664f77a24886172f9976a05a31'/>
<id>urn:sha1:d1fdfb140cc440664f77a24886172f9976a05a31</id>
<content type='text'>
* feat: soryu-co/soryu - makima: Revert broken directive PRs and verify clean build

* feat: soryu-co/soryu - makima: Re-implement frontend: Lexical document editor with feature flag and base components

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: Add contract blocks, expandable log rows, and interaction controls

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: End-to-end build verification and integration polish</content>
</entry>
</feed>
