Skip to content

build: expose local RC integration and compatibility fixes for review - #1733

Closed
chr1syy wants to merge 228 commits into
RunMaestro:rcfrom
chr1syy:fix/rc-local-feature-integration
Closed

chr1syy wants to merge 228 commits into
RunMaestro:rcfrom
chr1syy:fix/rc-local-feature-integration

Conversation

@chr1syy

@chr1syy chr1syy commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

This draft exposes the previously local-only RC integration and compatibility resolutions for review. It preserves all features in the latest verified local Linux package, based on upstream rc 0c81052, including host media settings, Relay/progress/permissions, terminal notifications, Board/Profiles, TTSR and the other pending Chris PRs.

Included dependency PRs: #1720, #1695, #1691, #1666, #1653, #1652, #1644, #1354, #1298, #1273 and #1272; host media settings are also submitted separately. This cumulative review branch gives PR coverage to every remaining local merge resolution, including notification compatibility and shared web-contents guarding. It is intentionally a draft aggregate while its dependencies remain open. All PRs target rc.

The prior exact head 4a135ca passed 2618 integration tests, 34 SDK tests, type checks, lint and packaged native/media/settings/PR-feature checks. It produced the prior .deb SHA256 d9e18b014a26fcfdb382674ce850358d896ff9221bd8bae95da0838e80e840f6. Provenance and conflict notes are in the local delivery records.

This review PR is not an authorization to merge upstream. The new Relay conversation change is reviewed in a separate dependent PR and merged only into the local test build.

chr1syy and others added 30 commits July 10, 2026 05:52
Introduce lightweight Profiles: a named override bundle layered on an
existing base agent that overrides model, reasoning effort, role
system-prompt, and optionally extra CLI args - same binary, cwd, and SSH
config as the base agent. Profiles are the "assignee" primitive the Board
(later phases) wires cards to.

- src/shared/profiles/types.ts: AgentProfile type, pure
  resolveProfileSpawnOverrides (profile -> base agent -> undefined) mapping
  onto existing SpawnAgentOptions fields (no new spawn params), and a
  reusable validateAgentProfile guard.
- src/main/profiles/profile-storage.ts: per-project .maestro/profiles.yaml
  load/save/list/upsert/delete using the shared js-yaml dep; skips
  malformed/duplicate entries with a logged warning.
- IPC: profiles:list/upsert/delete handler + preload bridge +
  window.maestro.profiles namespace, following the Cue handler pattern.
- UI: minimal ProfilesModal (list + create by base agent/model/effort/role),
  registered with useModalLayer and select-none.
- Tests: resolution-order + validation unit tests; storage round-trip and
  malformed-skip tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce the persistent Board: a per-project task DAG stored in
.maestro/board.yaml (human-diffable, git-trackable, mirroring the Cue and
Phase 1 Profiles conventions). This phase is the data foundation only - no
dispatcher (Phase 3) and no rich UI (Phase 4).

- src/shared/board/types.ts: pure BoardCard / CardStatus / CardRun /
  WorktreeRef / Board contracts with per-status meaning comments, plus
  validateBoardCard/validateBoard guards mirroring Phase 1's profile validator.
- src/shared/board/graph.ts: pure DAG helpers consumed by the Phase 3
  dispatcher - getEligibleCards (todo cards with all parents done), getBlockers
  (not-done parents), hasCycle (iterative 3-color DFS; self-parent = cycle,
  dangling parent id ignored). Fully unit-tested (22 cases).
- src/main/board/board-storage.ts: single owner of .maestro/board.yaml using
  the shared js-yaml dep (no new dependency). load/save/list/get + card
  mutations (addCard, updateCard, updateCardStatus, deleteCard); save rejects a
  cyclic graph via hasCycle, load skips malformed cards/boards with a logged
  warning. Adds BOARD_CONFIG_PATH to shared/maestro-paths.ts. 13 tests.
- IPC: board:list/get/addCard/updateCard/setCardStatus/deleteCard handler +
  preload bridge + typed window.maestro.board namespace, following the Cue and
  Profiles handler pattern; registered in the main process.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the Board actually run: a board-dispatch pass on the existing Cue
engine heartbeat tick drains each project's task DAG - promote eligible
cards, spawn the assignee profile through the existing executeCuePrompt
path (SSH/custom config honored), enforce a WIP cap, and move cards to
done/blocked from completion markers (marker wins, else exit status).

- src/shared/board/cardMarkers.ts: parseCardMarkers (card-complete /
  card-block), modeled on goalMarkers.
- src/main/board/board-dispatcher.ts: pure-ish orchestrator with all
  side effects injected (testable without Electron); promote / WIP-cap
  claim / marker+exit completion / circuit breaker (2 failures) /
  stale-running reclaim.
- src/main/board/board-spawn.ts: resolve card profile -> base agent and
  run via executeCuePrompt.
- src/main/cue/cue-engine.ts: boardDispatchTick on the shared heartbeat
  tick (no second timer), one dispatcher per board, gated on BOTH
  maestroCue && board (Board requires Cue) read fresh each tick.
- src/shared/plugins/first-party.ts: BOARD_FIRST_PARTY_PLUGIN (mirrors
  Cue; dispatch/spawn stay host-owned).
- renderer: board Encore flag + default + Extensions beta tile.
- tests: cardMarkers + dispatcher lifecycle (A->B->C under cap 1, block
  marker, circuit breaker) with fakes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Visual kanban surface over the persistent task DAG (Phase 2 IPC),
dispatched on the Cue tick (Phase 3). Six status columns, a card
create/edit editor, parent-dependency badges, and manual drag-to-move.

- src/renderer/components/BoardModal.tsx: six-column kanban, live-polled
  board state, inline card editor (title/body/assignee-profile/status/
  optional worktree/parents), client-side hasCycle guard with inline
  error, HTML5 drag-to-move calling setCardStatus (coarse-pointer haptic),
  "waiting on N" blocker badges via getBlockers.
- board:create IPC: createBoard in board-storage + handler + preload +
  global.d.ts, so the UI can stand up the first board on demand.
- modalStore/AppStandaloneModals/App.tsx: boardModal state, lazy render
  gated on encoreFeatures.board, force-close when board OR maestroCue
  turns off (Board depends on Cue), entry point gated on both flags.
- Entry points: Quick Actions "Board" command (threaded onOpenBoard
  through the AppModals chain) + "Open Board" button in the Extensions
  Board tile (mirrors Pianola).
- Extensions dependency gate: BUILTIN_DEPENDENCIES (board -> maestroCue,
  first dependent flag) + isDependencyMet; ExtensionDetails disables the
  Board Enable toggle with a "Requires Maestro Cue" tooltip when Cue is off.
- Fixed the Phase 1 ProfilesModal captureException signature bug.
- Tests: BoardModal card-create + cycle-rejection interaction, createBoard
  storage, extensionModel dependency gate; updated stale BUILTIN_FEATURES
  enumeration to include board.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Phase 5)

Round out the Board with headless CLI control, an OPTIONAL off-by-default
auto-decompose layer, structured completion-handoff metadata, and docs. The
manual Board (Phases 1-4) stays fully useful without any of the new layers.

- CLI: new `maestro-cli board` (list / show / add-card / set-status / tick) and
  `maestro-cli profile` (list / create / delete) commands, registered in
  src/cli/index.ts. They delegate to the SAME Electron-free board/profile
  storage the IPC uses (no drift). `board tick` reuses the Phase 3 pure
  dispatcher helpers (promote/claim/apply/reclaim) and the existing CLI
  spawnAgent path - honoring SSH + profile model/effort/role/args overrides - so
  no second spawn implementation. Repo try/catch-at-boundary convention.

- Auto-decompose (off by default): board-level `autoDecompose?: boolean` flag +
  new framework-free src/main/board/board-decompose.ts. Gated strictly on the
  flag; takes up to DEFAULT_AUTO_DECOMPOSE_PER_TICK (3) triage cards per pass,
  runs one LLM pass each, parses fenced/bare JSON, clamps dependsOn to
  earlier/in-range/non-self indices (always acyclic), wires children to the
  triage umbrella + siblings, and retires the triage card to done so it is never
  re-expanded. Editable prompt src/prompts/board-decompose.md registered as
  PROMPT_IDS.BOARD_DECOMPOSE. Wired into CLI `board tick` and the desktop engine
  (cue-engine maybeAutoDecompose, guarded fire-and-forget; index.ts wiring).

- Handoff: shared CARD_HANDOFF_REMINDER (one copy for CLI + desktop) prepended
  to every card prompt, asking for a `card-complete | summary` marker covering
  the four handoff questions. Summary capture already lands via applyCardResult;
  BoardModal now shows an expandable "Last run summary". Optional metadata.

- Docs: new docs/agent-guides/BOARD.md + user-facing docs/board.md (registered
  in docs.json under Encore Features), plus two CLAUDE.md key-files rows. No
  em/en-dashes; docs/releases.md untouched.

- Tests: board-decompose (flag-off never expands + cap/wiring/retirement),
  board + profile CLI round-trips, autoDecompose YAML round-trip. tsc clean,
  eslint clean, 108 board/profile/CLI/UI tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Let any opted-in agent in a board's project directory pick up cards, instead
of pinning every card to one fixed agent. The board stays per-project and
git-tracked; the "who runs it" binding changes from one agent to a free pool.

Model:
- Pool = agents with `boardWorker: true` whose cwd is inside the board's
  project dir (or a sub-folder). Opt-in via a toggle on EditAgentModal
  (default OFF, so interactive agents are never hijacked).
- A card names a role (`assigneeProfileId`) and/or pins an agent
  (`assigneeAgentId`); at least one is required. Role-only cards float to any
  free worker; pinned cards run on that agent with its own settings.
- `AgentProfile.baseAgentId` is now optional: absent = a pure pool role.
- One card per worker (tracked via `CardRun.workerAgentId`, recomputed from the
  persisted board each tick so it survives restarts). All workers busy -> the
  card stays `ready` and retries next tick (no waiting/reservation).

Dispatcher: additive `assign` dep + `claimReadyCardsPooled` + pooled tick path;
the legacy single-agent path is untouched when `assign` is absent. Pool
selection lives in one shared, pure module (`shared/board/pool.ts`) used by the
desktop engine, the CLI (`board tick`), and BoardModal.

UI: BoardModal card editor gains Role + "Pin to agent" selects and a
role/pin/pool badge; ProfilesModal base agent gains a "None (pool role)"
option; the board-worker toggle on EditAgentModal.

CLI: `board add-card --assignee <role> --assignee-agent <pin>`, and
`profile create --pool` for base-agent-less roles.

Also fixes auto-decompose to inherit a pinned agent (agent-only triage cards
would otherwise fan into children that validation drops), and a latent
getBlockers() arg-order bug in `board show`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	src/__tests__/renderer/components/Settings/Extensions/extensionModel.test.ts
#	src/renderer/components/Settings/Extensions/extensionModel.ts
#	src/renderer/stores/settingsStore.ts
#	src/renderer/types/index.ts
#	src/shared/plugins/first-party.ts
…erywhere

The toolbar pill displayed "Full Access" for a tab whose permissionMode was
never set (defaulting the DISPLAY to 'full'), but the spawn path passed the raw
undefined through to buildAgentArgs, which only grants the bypass on a literal
'full'. So Claude Code (whose --dangerously-skip-permissions lives only in
fullAccessArgs) spawned in the default permission model and had every tool call
denied - the box said Full Access while the agent was actually denied, and
goal-driven / document Auto Run deadlocked.

Introduce resolveTabPermissionMode() as the single source of truth for what an
unset permissionMode means and call it from both the toolbar (display) and the
interactive spawn path so they can no longer drift. Auto Run, which has no
active tab, now spawns with permissionMode: 'full' explicitly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the static foundation for Time-Traveling Stream Rules:

- TTSR_CONFIG_PATH / TTSR_RULES_DIR / ttsrRuleFilePath() in the shared
  maestro-paths module (no scattered path strings).
- src/shared/ttsr-types.ts: serializable rule schema, the four validated
  enums, project settings, and the Gate A per-agent capability matrix
  (verified per parser, not inferred from agents/capabilities.ts).
- src/main/ttsr/config/: repository (single fs owner of .maestro/rules
  and .maestro/ttsr.yaml, chokidar watch), normalizer (js-yaml
  frontmatter, regex compile-check, enum validation, Gate A agent
  defaulting), and the loader facade with name-collision first-wins.
- 13 unit tests covering normalization, invalid-regex drop, shadowing,
  enum fallback, disabledRules, missing/unparseable/invalid configs.

Also vendors the authoritative plan at plans/ttsr-implementation-plan.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gin, settings section

Completes Phase 1 of plans/ttsr-implementation-plan.md:

- 4-file settings pipeline for the `ttsr` group (ttsrEnabled,
  ttsrDisabledRules, ttsrContextMode, ttsrBuiltinRules) across
  settingsMetadata.ts, main/stores/defaults.ts, settingsStore.ts
  (state + setters + validated hydrate) and useSettings.
- `ttsr` Encore flag on EncoreFeatureFlags + TTSR_FIRST_PARTY_PLUGIN
  (com.maestro.ttsr) registered in FIRST_PARTY_PLUGINS, so the
  FirstPartyEncoreFlag compile-assert covers it. Marked beta in the
  marketplace tile list.
- TtsrSettingsSection (chromeless extension-detail body) wired into
  EncoreTab's settingsBodies with the data-setting-id="encore-ttsr"
  wrapper, plus the matching searchableSettings registry entry.
- isTtsrContextMode() guard in shared/ttsr-types.ts, used by the
  settings-store hydrate path.

npm run lint (all 3 tsconfigs), eslint, prettier and the touched suites
(Settings 488, plugins/cli/ttsr/settingsStore 542) are green.
Adds the detection half of Phase 2 as a standalone, injectable subsystem:

- ttsr-state-store.ts: main-authoritative repeat/injection state (Gate B),
  keyed by (maestroSessionId, providerSessionId, ruleName), with a pending
  bucket folded in once the session-id event lands, plus snapshot/hydrate
  for the Phase 3 persistence seam.
- ttsr-matcher.ts: pure predicates - agents gate, scope narrowing, picomatch
  glob path gate (relativizes absolute paths, normalizes separators), regex
  evaluation, and the interruptMode x source classification table.
- ttsr-tool-extract.ts: edit/write snapshots from both parser event shapes
  (toolUseBlocks and toolName + toolState.input), including codex patch
  additions recovery per Gate A's partial AST support.
- ttsr-manager.ts: the per-session matcher - prose/thinking buffers with
  boundary overlap, tool-content matching, repeat policy, interrupt vs
  deferred buckets, and the ttsr:matched payload. Dependencies are injected
  so the process manager never imports TTSR directly.

67 tests green across the TTSR suite; all three tsconfigs and eslint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wires the Phase 2 detection core into the live agent stream.

- ParsedEventObserver seam in StdoutHandler.handleParsedEvent, right after
  parseJsonObject: the only place that sees partial prose (dropped by the
  public thinking-chunk/data events for several agents), thinking, and tool
  calls with full input payloads for every structured agent. Injected via
  ProcessManager.setParsedEventObserver so process-manager keeps no TTSR
  import. A throwing observer is contained and reported, never taken out on
  the agent's output stream.
- TtsrSpawnRegistry: main-authoritative record of each in-flight turn
  (agent, project root, original prompt, provider session id), fed from the
  public spawn / session-id / exit events. ManagedProcess holds no prompt,
  so this is Phase 3's only source for originalGoal.
- TtsrRuntime: composes registry + rule cache + TtsrManager, reads the
  ttsrEnabled AND encoreFeatures.ttsr gate live, and reports ttsr:matched.
  A broken rule directory degrades to "no rules", never to a broken stream.
- installTtsrRuntime() called once from main; the OpenCode SDK spawn path
  now emits the spawn event too, so its turns are observed (and agent-run
  capture no longer misses them).

18 new tests; three tsconfigs, eslint, and the process-manager / agent-run /
opencode-server / process-listeners suites green.
Completes Phase 2 by wiring the AST half of the matcher:

- `ttsr-ast.ts`: lazy-loaded `@ast-grep/napi` matcher (same lazy-require
  degradation pattern as `@napi-rs/keyring`), plus the extension -> grammar
  map. Only the five grammars bundled with the core napi package are
  supported (ts/tsx/js/jsx/css/html); other file types skip AST rather than
  guess, since Maestro does not ship dynamic grammars.
- `TtsrManager.observeAst`: async structural pass over edit/write snapshots,
  sharing the agents gate, glob path gate, repeat policy and interrupt/defer
  buckets with the regex path. Identical consecutive snapshots are skipped.
- `TtsrManager.needsAstCheck`: sync gate so the stream tap allocates no
  promise for AST-free turns.
- `TtsrRuntime`: drives the AST pass off the synchronous path and exposes
  `flushAst()` so Phase 3 (and tests) can settle it deterministically.
- packaging: `@ast-grep/napi` added to `asarUnpack` next to the other native
  modules (`npmRebuild: false` holds - napi-rs prebuilds are ABI-stable).

Verified: 399 tests green (ttsr + process-manager), tsc clean on all three
configs, eslint clean.
…d payload

Turns a detected interrupting match into a real abort: signal the in-flight
process (interrupt for contextMode keep, kill for discard), wait for its exit
so stdout is drained, then emit the ttsr:triggered payload the renderer needs
to respawn the corrective turn.

- ttsr-injection.ts: <system-interrupt>/<system-reminder> rendering, per-rule
  dedupe, attribute escaping, and the degraded fresh-mode goal restatement.
- ttsr-interrupt-driver.ts: abort lifecycle, ttsrAbortPending flag, late-match
  folding, exit timeout, and the Gate A resume-vs-degraded split.
- TtsrRuntime drains the manager's interrupt bucket after every sync
  observation and after each AST pass settles; noteExit runs before the spawn
  registry entry is dropped so the payload keeps its meta.
- TtsrTriggeredPayload added to shared/ttsr-types.

25 new tests; 417 TTSR + process-manager tests green, tsc clean on all three
configs.
…ive respawn

Closes the interrupt loop: main aborts the offending turn, the renderer
continues the conversation with the <system-interrupt> prompt.

- ttsr:abortPending push (new, emitted before the signal) so the exit
  listener treats the abort as a TTSR interruption instead of a failed
  turn - it would otherwise idle the tab and dequeue onto the same
  process id before the corrective spawn lands.
- src/main/preload/ttsr.ts push-event bridge (onAbortPending/onTriggered/
  onMatched), registered in preload/index.ts and typed in global.d.ts.
- ttsrStore: renderer display cache + the ttsrAbortPending flag (Gate B -
  main stays authoritative).
- ttsrRespawn: resolveTtsrTarget via the shared session-id parser and
  buildTtsrRespawnConfig, which reuses the normal spawn rules (permission
  mode, YOLO filtering, per-session overrides, SSH, Windows stdin) and
  takes providerSessionId from the payload, not the tab cache.
- useTtsr, mounted in App.tsx behind the ttsr Encore flag, runs the
  corrective turn and marks the tab busy with a transcript notice.

211 tests green (TTSR suites + agent listeners), tsc clean on all three
configs.
Non-interrupting matches queue a <system-reminder> main-side; the spawn
path now drains that queue and prepends it to the conversation's next
prompt (Maestro has no tool-result hook to fold guidance in-band).

- TtsrRuntime.takeDeferredReminders() renders + clears the queue, gated
  on the global TTSR switch.
- applyTtsrReminders() applies it in handle-spawn via an injected drain,
  so the process handlers keep no TTSR import.
- Bound the manager's retained state: reminders are capped per
  conversation and a turn that ends with an empty queue drops its
  session state (Auto Run mints a fresh session id per task).
`once` and `after-gap` only mean anything if they outlive the process, so
the main-authoritative state store now round-trips through disk:

- TtsrStateStore stamps `updatedAt` per conversation and fires an
  `onChange` callback on every mutation (hydrate deliberately does not).
- New ttsr-state-persistence.ts owns the `ttsr-state` electron-store
  namespace behind a swappable backend: debounced writes, TTL (30d) plus
  a 500-conversation cap prune on read and write, and disk errors logged
  and swallowed so a failing write never breaks the output stream.
- TtsrRuntime builds the state store, hydrates it before the first
  observation, and exposes flushState()/dispose(); installTtsrRuntime
  wires the real backend (pass `persistence: null` for in-memory).
- main/index.ts flushes on `will-quit` so a rule that fired seconds
  before shutdown is still remembered next launch.

137 TTSR tests green, tsc clean across lint/main configs.
An interrupted turn was silent unless the user happened to be watching that
tab. The interrupt path now raises a sticky orange toast on the existing
remote:notifyToast channel (no new notification primitive), unwrapping the
composite process id to the bare agent id so the renderer can resolve the
agent and jump to the interrupted tab.

ttsr:triggered is sent first: the corrective respawn should already be in
flight before the user is told about it. Toast failures are logged, never
thrown - the reinject matters more.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runs each in-scope agent's real provider stdout through its real parser,
the runtime tap, the matcher and the interrupt driver, then records the
prose / ast / resume axes from what the pipeline actually did and compares
that with the plan's scope table and TTSR_AGENT_CAPABILITIES.

- claude-code, codex, opencode, factory-droid: pass (clean resume)
- copilot-cli, grok: degraded fresh reinject, as Gate A specifies
- terminal: excluded, the spawn registry never registers it
- feature-off is a total no-op per agent, rules are not even read

Also corrects the StdoutHandler tap comment: the observer is always
installed and gates itself live, rather than being installed conditionally.
Eight findings from reviewing the goal-driven Auto Run output.

Blocking:
- Only AI-tab spawns are registered. TTSR aborts in main but can only
  respawn into an AI tab, so registering Auto Run tasks, synopsis, tab
  naming or group-chat participants let it kill an unattended turn it
  could never restart. Adds parseAiTabSpawnId() alongside the existing
  coworking regex; tabId now comes from the spawn id (no caller sets
  config.tabId).
- ttsrContextMode had no consumer. TtsrProjectSettings.contextMode is
  now optional so "unset" differs from "keep", resolving project yaml ->
  global setting -> keep.
- The corrective respawn dropped the Auto Run read-only gate, so a rule
  firing during a non-worktree run handed the new turn write access the
  aborted one lacked. Mirrors useInputProcessing, forced-parallel exempt.

Correctness:
- Carry the original goal across a corrective respawn, so a second
  interrupt on a degraded agent restates the user's request rather than
  the previous injection.
- Bound interrupts per conversation (MAX_TTSR_INTERRUPTS, persisted).
  Past it, matches defer to reminders instead of killing turns forever.
- Drop the 5s rule cache TTL: loading is synchronous disk IO and ran
  inside StdoutHandler. Rules load at spawn time and invalidate via the
  watcher that was already written but never wired.
- A throwing interrupt()/kill() left abortPending set forever, wedging
  the tab busy. The driver now withdraws over a new ttsr:abortCleared
  channel, which is what clearAbortPending existed for.

Also removes the unused ttsrBuiltinRules setting and endTurn's dead
finalText param, and fixes a pre-existing failure in the first-party
plugin registry test (never updated when com.maestro.ttsr landed).

The acceptance matrix report now states that its evidence is an
in-process run with a stubbed ProcessManager, not a live per-agent E2E.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
"Stop the agent doing X" was mostly not expressible. The scope vocabulary
covered prose and file content only, and extractEditSnapshots classified
just the file-mutating tools, so shell calls were invisible to TTSR. A
rule like "never force-push" could only match the agent talking about it,
which fires on "I won't force-push" and misses the actual command.

Adds a tool:bash scope matching the command a shell tool is about to run,
sourced from Bash (claude-code), shell (codex), bash (opencode) and the
run_* spellings, with argv arrays joined into one matchable line. Gate A
gains a shellEvents axis; factory-droid and grok report no tool calls, so
the loader keeps command rules off them and says why.

globs now gate only the file-bearing scopes. A command has no path, so a
bash rule with globs previously matched nothing at all; it is now ignored
with a load-time warning rather than silently never firing.

The acceptance matrix measures the new axis per agent from real parser
output, so a parser that stops surfacing commands fails the suite instead
of leaving those rules quietly inert. Renames the extractor to
extractToolSnapshots/TtsrToolSnapshot now that it covers more than edits.

Interrupting stays corrective, not preventive: the command is matched as
the tool call streams, so a fast one may already have run. Documented in
the acceptance record rather than implied away.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rules were already project-scoped on disk but had no UI, so the only way
to add one was to hand-write a file the user had no way to discover.

Adds a Rules tab to the Right Bar, which is implicitly scoped to the
agent being viewed: its cwd is the project root every call names. That
is why this is not in Settings, which is global and cannot express "in
this repo". The tab lists the project's rules, surfaces load warnings
(the only signal a rule that can never fire otherwise gives), and
exposes the per-project `enabled` and `contextMode` from
.maestro/ttsr.yaml. Leaving contextMode unset hands the choice back to
the global setting rather than pinning it.

Authoring is delegated to the agent instead of a form. Rule files are
markdown and the agent already writes files, so a new prompt teaches it
the schema and the tab sends that brief plus the user's request as a
normal turn; the agent writes the file, the watcher notices, the list
refreshes. Nothing to outgrow, and the user can iterate conversationally
("narrower", "it fires too often"). Settings writes merge rather than
overwrite so a hand-written config survives a toggle.

Also fixes a bug this path would have hit constantly: a rule file
necessarily contains the text its own rule looks for, so writing
.maestro/rules/no-console-log.md tripped a tool:write console.log rule.
Verified before fixing, now covered. TTSR no longer matches its own
config at the one extraction choke point.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fullaccess

# Conflicts:
#	src/__tests__/renderer/hooks/useAgentExecution.test.ts
#	src/__tests__/shared/agentMetadata.test.ts
#	src/main/preload/index.ts
#	src/renderer/App.tsx
#	src/renderer/components/AppModals/AppSessionModals.tsx
#	src/renderer/components/NewInstanceModal/EditAgentModal.tsx
#	src/renderer/components/NewInstanceModal/types.ts
#	src/renderer/hooks/session/useSessionLifecycle.ts
…eload, breaker fairness (Board 10x Phase 1)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… priority (Board 10x Phase 2)

- board:changed push event replaces the 2.5s kanban poll. board-storage
  exposes setBoardSavedListener; index.ts broadcasts { projectRoot } to
  every window plus the web-desktop bridge. Storage stays host-agnostic
  so the CLI (no webContents) is unaffected.
- Cancellation: board-spawn remembers the Cue runId per card and kills it
  via stopCueRun; the dispatcher gains cancelSpawn + cancelCard and a
  cancel tombstone so the killed run's late resolution cannot re-finalize
  the card as a failure. New 'canceled' run outcome returns the card to
  todo and is excluded from the circuit breaker. board:cancelCard IPC
  routes through the live dispatcher, with a storage-only finalize
  fallback for cards left running by a previous app run. UI: stop button
  on running tiles, drag-out of the running column now refuses with an
  explanatory toast.
- Terminal transitions notify through the existing remote:notifyToast
  relay (green done / red sticky blocked, sourceAgent 'Board').
- Safe destructive actions: two-click tile delete that names how many
  dependents get re-parented, a ConfirmModal discard gate for unsaved
  editor changes on Escape/backdrop/Back, and a delete button visible on
  focus-within and always visible on coarse pointers.
- Card priority (high/normal/low, normal never serialized) drives one
  shared ready-card dispatch order for both claim paths; surfaced in the
  card editor, as a tile badge, and via `board add-card --priority`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s, bash self-trip

- Interrupt budget is charged once per real abort: matches that drain while an
  abort is already pending are folded, not gated and charged again.
- A withdrawn abort refunds the charge, re-arms the matched rules and re-queues
  their guidance as deferred reminders; a corrective turn that never spawns is
  recovered on the next spawn of that session.
- Conversation state keys on the parsed tab identity, so a forced-parallel
  spawn id no longer mints a fresh conversation per turn.
- Shell commands that write TTSR's own config no longer trip tool:bash rules.
- Deferred reminders drain transactionally: the queue is cleared only after the
  spawn succeeds, so a throwing spawn no longer destroys queued guidance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Board 10x Phase 3)

Close the headless-parity gap so a CI box or a headless host can drive the
Board end to end without ever opening the desktop app.

New CLI surface:
  board create | rename | delete        (delete refuses non-done cards without --force)
  board update-card | remove-card       (running cards are refused / need --force)
  board watch                           (board tick on a timer, SIGINT to stop)
  profile update | show                 (update keeps the id, so cards keep resolving)

Storage gains renameBoard / deleteBoard and an options bag on createBoard
(maxInProgress, autoDecompose). The non-done guard rail lives in storage, not
the CLI, so the desktop path inherits it; board:rename / board:delete IPC,
preload, and renderer types are plumbed for the Phase 6 UI.

board tick and board watch now share one tickBoardsOnce pass. The CLI has no
handle on an in-flight card process, so remove-card --force says plainly that
the run was not canceled rather than implying a kill it cannot perform.

docs/cli-reference.md regenerated (npm run gen:cli-reference); BOARD.md and
board.md refreshed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s panel

Renderer robustness:
- useTtsr gates the corrective respawn behind useOwnedSessionGate. TTSR pushes
  are broadcast to every window and web-desktop client, so two renderers were
  both respawning; the second spawn killed the first mid-flight and could
  double the <system-interrupt> in the transcript. Non-owning windows still
  record display state.
- A failed respawn now releases the whole session, not just the tab. The
  aborted turn's exit is suppressed by the abort-pending flag, so nothing else
  could clear session.state / busySource / thinkingStartTime - the agent span
  forever with queue dispatch blocked. releaseAfterFailedRespawn() idles both,
  clears the abort mark, and toasts red with the rules and the spawn error.
- Abort-pending marks can no longer suppress exits forever: entries carry a
  timestamp, isTtsrAbortPending() drops anything older than 30s, and useTtsr's
  cleanup wipes standing marks on unmount / when the Encore flag flips off.

Rules panel:
- New ttsr:rulesChanged push (runtime -> safeSend -> preload -> service ->
  panel, debounced 300ms) so the agent-driven authoring loop ends on a fresh
  list instead of a stale one.
- listRules returns every rule with a `disabled` flag instead of hiding
  disabled ones; the panel dims them and adds an always-visible on/off toggle
  that writes the project ttsr.yaml or clears the global ttsrDisabledRules
  setting, whichever holds the rule. The matcher still only ever sees enabled
  rules - loadTtsrConfigDetailed.rules is unchanged and the new disabledRules
  list is for the management surface alone.
- Delete uses the codebase's existing two-step arm pattern (4s auto-disarm)
  instead of firing on one click in a hover cluster.

App:
- handleSendPromptToAgent toasts yellow when no agent input is available
  instead of silently dropping the click. No existing test renders App.tsx or
  covers this path, so the added coverage is panel-side only: the Rules panel
  does not offer the hand-off when no handler is wired.

Suites run: useTtsr, TtsrRulesPanel, services/ttsr, ttsrRespawn,
ipc/handlers/ttsr (74 green), plus ttsr-runtime (38) and ttsr-config-loader
(18) to confirm matcher-side filtering is unchanged.
- extract the local git worktree provisioning out of the git:worktreeSetup
  IPC handler into src/main/utils/git-worktree.ts so Auto Run and the Board
  share one implementation
- add pure branch/path naming (src/shared/board/worktree.ts) and the Board
  provisioning wrapper (src/main/board/board-worktree.ts, Electron-free so
  the CLI uses the identical path)
- spawn worktree cards with the checkout as cwd (desktop + CLI), record
  worktreePath/worktreeBranch on the CardRun, refuse isolation on SSH remotes
- add the 'Run in isolated worktree' toggle, a branch badge on card tiles,
  the branch in the completion toast and last run summary, and a
  'Worktree cards' section in docs/board.md with the merge-back commands

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pedramamini and others added 28 commits October 5, 2026 08:36
Adapt local commit 83c579b99 to the current Relay host contract, preserving receipt support and cancellation checks. Include Relay filter migration, provider session metadata, and CLI History labels.
Restore the production receipt call and its two regression tests from local commit 21d04b5f1 without reverting the newer CLI socket cancellation path.
… credit

Codifies how GHSA-q8p2-cpg2-fhpc was handled: intake across email, GHSAs and
issues (with a weekly sweep for advisories stuck in triage), validation on
both branches, realistic CVSS scoring, the draft advisory and CVE request,
fix on main and hand port to rc, stable-then-RC release, publish-then-announce
ordering with the conductor's go for irreversible sends, credits, and
reporter email templates. Linked from CLAUDE.md, SECURITY.md and the release
runbook.
The advisory page keeps showing Request CVE after a request, and the API
returns 202 on every call, so the runbook's "confirm on the page" step could
never pass. Request once, note the date, poll cve_id, and open a Support
ticket after 3 working days.
# Conflicts:
#	src/renderer/hooks/settings/useSettings.ts
@coderabbitai

coderabbitai Bot commented Oct 6, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chr1syy chr1syy closed this Oct 6, 2026
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.

2 participants