Operations Integration (260628): inspect-diff-converse cockpit, task_reopen, provider caps — MCP 3.0.0rc1#98
Conversation
…red sidecar-pairing kernel module Add serving/files.py: a localhost, GET-only files API bridging the dashboard to the kernel CoordinationContext — repo/enclosure catalog, lazy list-dir (code + onboarding), read-file (content + drift metadata), and forward/reverse code<->onboarding sidecar pairing. Registered in create_app before the greedy static mount. Allow-listed roots only, realpath-confined (no traversal). A memory-less repo degrades to code-only browsing; a missing sidecar is a normal "missing" result, not an error. Extract the pure sidecar-pairing + path-confinement helpers from controllers/read_files.py into kernel/sidecar_pairing.py so the read_ar_files tool and the files API share one source of truth (no side-effecting controller import). Behavior-preserving — read_ar_files re-imports them. Tests: mcp/tests/test_serving_files.py (path-guard incl. symlink escape, list/read/drift/binary/ oversize, both pairing directions, catalog, route + memory-less degrade). 152 tests green; ruff + pyright clean.
…ane (Headless Tree + CodeMirror) Add a full-bleed File Viewer center tab (between Operations and Engine Room) over the L1 read-only files API, plus the reusable FilePane/DualPane the Change-Set Viewer (L4) will reuse: - data/files.ts: same-origin typed client (repos/list/read/onboarding) + FilesApiError. - panels/file-viewer/: FileViewer (page) + FileTree + useFilesTree (two Headless Tree explorers: code + onboarding) + FilePane (read-only CodeMirror 6) + DualPane (split, bidirectional code<->sidecar pairing, stable placeholders) + codemirrorTheme + langByExtension + usePersistedFlag. - cockpit/Cockpit.tsx: register the File Viewer view, kept mounted (CSS-hidden) like Chats so its state survives a tab switch. - deps: @headless-tree/react+core, @codemirror/* (+lang packs, @lezer/highlight), react-resizable-panels@3. - Rebuilt + re-synced the shipped dashboard bundle (package_data/dashboard + fingerprint). Tests: data/files.test.ts + panels/file-viewer/FileViewer.test.tsx (270 green).
…unts + before/after + master accumulation)
Add a read-only change-set API over the L1 scope resolution, feeding the L4 Change-Set Viewer:
- worktrees/modules/git.py: changed_files_with_counts (+ _rename_aware_path) — per-file
{path, insertions, deletions, status} via git diff --numstat/--name-status, keeps deletions,
binary->null, untracked->A, rename-aware (the existing changed_*_paths are name-only).
- serving/scope.py: shared scope resolution + error map extracted from files.py
(FileScope, resolve_scope, run_scoped, language_for); files.py re-imports + re-exports them.
- serving/changeset.py: task_changeset (base->worktree code+memory w/ counts + hasSidecar),
file_diff (BEFORE/AFTER content for CodeMirror MergeView a/b), master_changeset (sum leaf
change-sets; completed leaves diff base->code_commit on the source repo).
- serving/app.py: register_changeset_routes before the static mount -> GET /api/changeset/{task,file-diff,master}.
Tests: mcp/tests/test_serving_changeset.py (full mcp suite 1267 passed; L1 files tests unchanged).
…or merge diff + 3-column takeover) Build the dashboard Change-Set Viewer over the L3 /api/changeset/* API, reusing L2's panes: - data/changeset.ts: typed client (taskChangeset/fileDiff/masterChangeset) reusing files.ts's getJson/qs/FilesApiError (now exported). - panels/changeset/: ChangeSetViewer (up-to-3-column screen: changed code/onboarding rows, diff pane, code<->sidecar split, counters + back link; master = accumulated summary) + ChangeSetPane (per-column change-set/full-file/highlight toggles, persisted) + DiffPane (read-only @codemirror/merge: MergeView split / unifiedMergeView inline, reusing FilePane's extension set). - cockpit/Cockpit.tsx: a task-scoped change-set TAKEOVER state (full-bleed; backlink restores Operations). - panels/DetailPanel.tsx: change-set + series buttons with line counters in the enclosure spine (task gated on active worktree-group; master via masterChangeset). - deps: @codemirror/merge; rebuilt + re-synced the dashboard bundle. Tests: panels/changeset/ChangeSetViewer.test.tsx + data/changeset.test.ts (full vitest 277 passed).
…inspectable) + diff/code-view polish Review follow-up to the L4 Change-Set Viewer, folded in before integration: - serving/changeset.py: master_changeset is now the NET series diff (git diff <master-base> <source-tip>) for code + memory -- one coherent, per-file-inspectable range -- not the sum-of-leaves; adds master_file_diff (base->tip) + a master param on /api/changeset/file-diff; keeps the per-leaf counter breakdown. data/changeset.ts + ChangeSetViewer: series rows are now clickable (masterFileDiff). - codemirrorTheme.ts: comments + operators/punctuation use a readable ink blend (were --grid, ~invisible). - DiffPane.tsx: the split MergeView is the bounded scroll container (long diffs scroll, not clip). Tests: net change-set backend tests + master-inspection frontend test (pytest 1270 / vitest 277). Bundle re-synced.
…ies / committed / working leaf views + diff-highlight rectangle + viewer UX (back-to-origin, live working refresh)
…xt draft paste boot-safe Wheel: every hosted session sits behind a tmux client that always presents the alternate screen, so synthesized PageUp/PageDown only scrolled TUIs that bind those keys (Claude Code yes, Codex no). The Terminal wheel handler now defers to xterm's native mouse-report path when the app tracks the mouse, and the terminal host asserts per-session `tmux set-option mouse on` (new injectable TmuxConfigurer seam) on ensure and attach — tmux scrolls pane history for normal-buffer TUIs and passes wheel through to mouse-aware ones. Paste: a booting Claude Code discards stdin until its composer mounts (reproduced live), and tmux masks every readiness signal from the client. The leaf-context draft paste now goes through pasteAndConfirm: quiet-gated attempts confirmed by the paste's own echo, retried over a 30s boot deadline, never sending Enter; unconfirmed delivery still surfaces the rail note. Includes the rebuilt package_data dashboard bundle.
Wheel-up over a non-mouse pane (Codex) enters tmux copy-mode to scroll history, and copy-mode captures the keyboard: typed letters are swallowed until the operator scrolls back to the bottom. tmux has no any-key-cancels binding, but the terminal host owns the stdin path: SGR mouse-report frames now arm a per-connection flag, and the first non-mouse input afterwards issues `tmux send-keys -X cancel` (new injectable TmuxModeCanceller seam) before the keystrokes are written — typing anywhere in the scrollback snaps the view to the live bottom and lands in the app's composer. At most one cancel per scroll-then-type cycle; a no-op for mouse-aware panes (Claude Code) that never enter copy-mode.
CodeGraphContext 0.4.10 exposes the dependency analyzer as `analyze deps`; the typed wrapper was dispatching the stale `analyze dependencies`, which fails with "No such command" even on a ready provider. Updates the CGC guidance table at the canonical skills root (synced to every harness package via sync-skills) and locks the argv contract with a focused dry-run assertion (analyze deps, not analyze dependencies).
… the in-task response box Selections inside a task reader now carry the leaf's qualified key (data-task-leaf-key on the doc container); when the right-rail leaf chat is active and that leaf has a bound running chat, the highlighted context is pasted directly into its draft (echo-confirmed pasteDraftToSession, no Enter) with no selector popover — the generic HighlightComposer remains as the fallback for selections without a confident leaf-chat target, and a failed direct paste falls back to it. Response-UI cleanup: proto ask-only items no longer raise the message-box responder, and GateResponder's message-only Chat mode is removed — conversational follow-up belongs in the adjacent leaf chat while durable approve/reject/dismiss gate decisions stay explicit. Includes the rebuilt package_data dashboard bundle.
A shared leaf-assignment policy (serving/terminal_leaf_assignment.py) moves an existing catalog session to a new durable leafKey under the same (leaf, role) uniqueness rules as attach — attached / leaf-taken / unknown-session, no respawn — and both the FastAPI attach-leaf route and the new agent-facing attach_terminal_session_to_leaf MCP tool go through it, so conflict behavior cannot drift between callers. Open dashboards follow out-of-band moves: the terminal catalog is polled on a 2.5s cadence and same-browser mutations broadcast a new "leaf" invalidation reason; applyLeafAssignment applies server-confirmed moves to the session store and clears stale same-role local owners. The leaf picker stays visible for attached chats as a Move control — successful moves keep the same xterm/WebSocket session and draft the destination leaf's context through the L6 paste path, while leaf-taken leaves local state untouched. Includes the rebuilt package_data dashboard bundle.
The L8 direct path auto-pasted every task-reader highlight into the leaf chat and hid the "Add to chat" pill, making the interaction invisible and firing on unintended selections. Every selection now raises the same pill; only the click routes: with an obvious leaf-chat target it pastes straight into that chat's draft (echo-confirmed, no selector, no message box, no Enter), otherwise it opens the generic composer, and an unconfirmed direct paste falls back to the composer instead of dying silently. The pill label stays "Add to chat" in both cases. Includes the rebuilt package_data dashboard bundle.
Series leaf docs carry no enclosures[] refs and no lifecycleId, and the
projection's (taskRoot, leafId) fallback compared the enclosure leaf id
against the doc FILENAME stem — which never matches — while the sidebar's
enclosureForDoc compared enclosure leaf ids (slugified lowercase directory
names, 260628-l7) against doc ids (authored uppercase labels, 260628-L7)
case-sensitively. Active-enclosure rows therefore rendered as doc-less
runtime lifecycles ("No task document bound to this task") and the
viewed-leaf chat chain never fired from them.
read_task_documents now joins (taskRoot, enclosure.leafId.lower()) against
doc.id.lower() (stem kept as a lowercased legacy alternative), and every
enclosureForDoc leafId comparison — stem, doc id, and the lifecycle-guarded
reopen-suffix rule — is case-insensitive, matching the normalization
RailChat and the change-set bar already use.
Includes the rebuilt package_data dashboard bundle.
…, abandoned-enclosure terminalization
…d cgcignore reaches the watch context, bundle exclusion, timer-pop patch
…erving, test import hygiene, typed reopen-test fixtures
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d4c9a786b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| base, head = contract.memory_base_commit, contract.memory_content_commit | ||
| else: | ||
| live, repo = contract.code_worktree, contract.code_repo_path | ||
| base, head = contract.code_base_commit, contract.code_commit |
There was a problem hiding this comment.
Use integrated commits for landed leaf diffs
When a leaf is integrated with replay, integrate_result records the rebased landed commit in integrated_code_commit while leaving code_commit as the closeout commit. This completed-leaf path is documented as diffing against the integrated commit, but it uses code_commit, so after cleanup the master leaf breakdown (and the same closeout-vs-integrated choice repeated in committed leaf views) can show the pre-replay delta instead of what actually landed on the source branch. Prefer integrated_code_commit / integrated_memory_content_commit when integration has completed.
Useful? React with 👍 / 👎.
| """ | ||
| if not master or "/" in master or "\\" in master or master.startswith("."): | ||
| return None | ||
| path = config.coordination_root / "tasks" / repo_id / master / "series-contract.md" |
There was a problem hiding this comment.
Enforce the repo allow-list before loading master contracts
The master change-set path only validates master; repo_id is used directly to build a filesystem path, so /api/changeset/master and masterFileDiff can load tasks/<repo>/<master>/series-contract.md for repos that are not in config.allowed_repo_ids (or even ../-style repo values). The files/scope APIs enforce the configured repo allow-list before serving content, so these master routes should call require_repo or otherwise confine the repo component before loading the contract.
Useful? React with 👍 / 👎.
| conflict = _claim_leaf_or_409( | ||
| catalog, request.leaf_key, session, role=role_for_kind(kind) | ||
| ) |
There was a problem hiding this comment.
Make leaf claims atomic with the catalog write
FastAPI runs these sync handlers concurrently, but the leaf uniqueness check is separate from the later catalog upsert. Two near-simultaneous opens for the same leafKey can both pass this check before either writes, spawn two tmux sessions, and persist two running same-role owners; the catalog lock currently protects only individual writes, not the check+write claim. Move the conflict check and insertion into one locked/atomic catalog operation.
Useful? React with 👍 / 👎.
| kind, | ||
| ...(harness ? { harness } : {}), | ||
| ...(lifecycleId ? { lifecycleId } : {}), | ||
| ...(leafKey ? { leafKey } : {}), |
There was a problem hiding this comment.
Do not bind a leaf after the server refuses it
When opening a leaf-bound session, a backend 409 leaf-taken makes openTerminalSession return false, but this code still upserts a local running session carrying that leafKey. In a real cockpit (e.g. another tab already owns the leaf), RailChat then treats the refused leaf as claimed and may try to deliver context to a WebSocket for a session the server never created. Preserve the dev-bench fallback without assigning the leaf on a failed persisted open, or distinguish 409 from no-backend failures.
Useful? React with 👍 / 👎.
Operations Integration (master series 260628) — and MCP 3.0.0rc1
Turns the shipped 3.0 browser-dashboard Operations surface into an integrated inspect–diff–converse cockpit, hardens the machinery around it, and cuts MCP
3.0.0rc1— the first version that serves the mission-control dashboard from the MCP package (agents-remember dashboard). Built as a master + 15 leaves, merge-committed so the distinct commits stay bisectable;origin/main(#96) is merged as the series' terminal commit.Cockpit surfaces (L1–L5)
9afc304): read-onlyserving/files API + shared sidecar-pairing kernel module.027d61d): File Viewer page + reusable dual-pane (Headless Tree + CodeMirror).ee7dabb): change-set computation backend (counts, before/after, master accumulation).29322d9,9a360ac,7fbe9ee): Change-Set Viewer takeover (CodeMirror merge diff) + change-set entries on the doc reader (series / committed / working views).fb51755): leaf-keyed sidebar chat attachments.Terminal + chat hardening (L6–L9)
c4d1801,e220702,35b99ec,adaf436,5ca57b5,2888133): leaf-chat context handoff on bind; wheel scrolling fixed via tmux mouse mode + alternate-buffer handling; boot-safe draft paste (echo-confirmed retries); typing auto-exits copy-mode after wheel scrolling.987e58a):cgc_dependenciesdispatches the native analyze deps subcommand.8d7381e,da5804d): task highlights route straight into the adjacent leaf chat as a draft — pill-click-triggered, never automatic; the obsolete in-task response box retired.e086407): hosted chat leaf bindings are movable, with live catalog sync across open dashboards.Bindings, reopen semantics, and stability (L10–L13)
82fa54b): active-enclosure task rows bind to their documents case-insensitively (slugified leaf ids vs authored doc labels).7645649):task_reopen— reopening a completed leaf reuses its exact leaf id: contract + doc reset to planning,worktree_startrecreates fresh and restamps the doc's lifecycle; the-rNfork workaround and its dashboard heuristic are gone.worktree_abandonends its anchored lifecycle; the reducer projects abandon terminality from contracts.4c9be9f): every provider container is memory-capped in the compose templates (watchers 512m, falkordb/ollama 2g, runner 1g, postgres 512m; OOM self-recycles underunless-stopped), the enriched.cgcignorenow reaches the live watch context, the committed dashboard bundle is excluded from watch/index work, and a maintained cgc patch pops fired debounce timers (runner imagear2). Root incident: ~10 GB of uncapped CGC memory drove swap exhaustion.af81a4c,819fc7f): live-fire reopen drill — one leaf completed, reopened, and completed again under the same leaf id.Release + gate (L14–L15)
d0c5b24): version3.0.0rc1inpyproject.toml+SERVER_VERSION; README Status states the shipped 3.0 arc (the cockpit served from the MCP package) with the rc caveat.db107fd): the series passes its own pre-push quality gate over the whole tree (pyright-visiblePathnarrowing in changeset serving, test import hygiene, typed reopen-test fixtures).2d4c9a7): merge oforigin/main(Post-landing cleanup: task_doc master/leaf-only + CRUD remove_subtask + find_worktree/worktree_start fixes #96 post-landing cleanup) — conflict-free; merged suite green (backend 1347, dashboard 353 + tsc).Post-merge follow-ups: memory carryover onto the official memory main, then tag
mcp-v3.0.0rc1to publish.🤖 Generated with Claude Code