Skip to content

Operations Integration (260628): inspect-diff-converse cockpit, task_reopen, provider caps — MCP 3.0.0rc1#98

Merged
Foxfire1st merged 25 commits into
mainfrom
ar/260628-operations-integration
Jul 3, 2026
Merged

Operations Integration (260628): inspect-diff-converse cockpit, task_reopen, provider caps — MCP 3.0.0rc1#98
Foxfire1st merged 25 commits into
mainfrom
ar/260628-operations-integration

Conversation

@Foxfire1st

Copy link
Copy Markdown
Owner

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)

  • L1 (9afc304): read-only serving/ files API + shared sidecar-pairing kernel module.
  • L2 (027d61d): File Viewer page + reusable dual-pane (Headless Tree + CodeMirror).
  • L3 (ee7dabb): change-set computation backend (counts, before/after, master accumulation).
  • L4/L4a (29322d9, 9a360ac, 7fbe9ee): Change-Set Viewer takeover (CodeMirror merge diff) + change-set entries on the doc reader (series / committed / working views).
  • L5 (fb51755): leaf-keyed sidebar chat attachments.

Terminal + chat hardening (L6–L9)

  • L6 (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.
  • L7 (987e58a): cgc_dependencies dispatches the native analyze deps subcommand.
  • L8/L8-r1 (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.
  • L9 (e086407): hosted chat leaf bindings are movable, with live catalog sync across open dashboards.

Bindings, reopen semantics, and stability (L10–L13)

  • L10 (82fa54b): active-enclosure task rows bind to their documents case-insensitively (slugified leaf ids vs authored doc labels).
  • L11 (7645649): task_reopen — reopening a completed leaf reuses its exact leaf id: contract + doc reset to planning, worktree_start recreates fresh and restamps the doc's lifecycle; the -rN fork workaround and its dashboard heuristic are gone. worktree_abandon ends its anchored lifecycle; the reducer projects abandon terminality from contracts.
  • L12 (4c9be9f): every provider container is memory-capped in the compose templates (watchers 512m, falkordb/ollama 2g, runner 1g, postgres 512m; OOM self-recycles under unless-stopped), the enriched .cgcignore now 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 image ar2). Root incident: ~10 GB of uncapped CGC memory drove swap exhaustion.
  • L13 (af81a4c, 819fc7f): live-fire reopen drill — one leaf completed, reopened, and completed again under the same leaf id.

Release + gate (L14–L15)

  • L14 (d0c5b24): version 3.0.0rc1 in pyproject.toml + SERVER_VERSION; README Status states the shipped 3.0 arc (the cockpit served from the MCP package) with the rc caveat.
  • L15 (db107fd): the series passes its own pre-push quality gate over the whole tree (pyright-visible Path narrowing in changeset serving, test import hygiene, typed reopen-test fixtures).
  • Terminal commit (2d4c9a7): merge of origin/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.0rc1 to publish.

🤖 Generated with Claude Code

Readone Mohamed added 25 commits June 28, 2026 23:47
…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.
…d cgcignore reaches the watch context, bundle exclusion, timer-pop patch
…erving, test import hygiene, typed reopen-test fixtures
@Foxfire1st
Foxfire1st merged commit ad30dd3 into main Jul 3, 2026
4 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +658 to +660
conflict = _claim_leaf_or_409(
catalog, request.leaf_key, session, role=role_for_kind(kind)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant