tmt-ops-dev · diff

git:20261023.2e85bad to git:20261030.9186e1f

14 added, 13 removed. Audit A to A.

---
name: tmt-ops-dev
description: Build and verify the Ops extension (`tmt-ops`, the board, notebook, cron library, embedded lead skill and playbooks). Load when changing extensions/tmt-ops or running its native and E2E checks. Owner - tmt-ux; core-ops may implement. Not the shipped lead skill `tmt-ops`.
---
# Ops development
## Board surface ownership
`board/view.rs` owns frame orchestration and one frame-level hit/scroll-map reset.
The interactive board acquires the global workspace once and selects its
Agents or Digest destination through `shell::OpsShell`.
- | Module | Responsibility |
- | --------------------- | --------------------------------------------------------------- |
- | `shell` | Scoped destination selection, outside editors and overlays |
- | `workspace::observe` | Roster, room and document projection from one acquisition |
- | `workspace::render` | Dense tree, clipped hits, squad detail and management dialog |
- | `workspace::manage` | Frozen management operations and guarded effects |
- | `new_member` | Async new-member form, frozen host launch and bound-UUID result |
- | `app::workspace` | UUID selection, effective bindings and workspace routing |
- | `conversation` | Shared selected-agent response, source and follow state |
- | `view::waiting` | WP1 composer and completion geometry |
- | `view::header/footer` | Summary, meters and effective action hints |
- | `home::limits/paint` | Existing Usage-band quota presentation over held data |
- | `view::overlays` | Shared overlay dispatch |
+ | Module | Responsibility |
+ | --------------------- | ----------------------------------------------------------------------------------------- |
+ | `shell` | Scoped destination selection, outside editors and overlays |
+ | `workspace::observe` | Roster, room and document projection from one acquisition |
+ | `workspace::render` | Dense tree, clipped hits, squad detail and management dialog |
+ | `workspace::manage` | Frozen management operations and guarded effects |
+ | `new_member` | Async new-member form, frozen host launch and bound-UUID result |
+ | `app::workspace` | UUID selection, effective bindings and workspace routing |
+ | `conversation` | Shared selected-agent response, source and follow state |
+ | `view::waiting` | WP1 composer and completion geometry |
+ | `view::header/footer` | Summary, meters and effective action hints |
+ | `home::limits/paint` | Existing Usage-band quota presentation over held data |
+ | `view::overlays` | Shared overlay dispatch |
+ | `unread_overlay` | Scoped read evidence, list/detail selection and confirmation; child surface/input modules |
`board/row_detail.rs` owns the shared in-place detail projection and renderer,
expansion reconciliation, worker-acquired reply cache and read-only full-reply
reader. The workspace conversation and cron surfaces reuse its source and reply
projection. Notebook/source utilities retain their independent CLI/data owners;
they are not a separate interactive Notes destination.
Every strip, border and scroll line is painted through `tmt-tui` (`Strip`,
`Outline`, `Modal`): no board module names a ratatui widget. The action menu is a
`tmt-modal` list surface built per menu in `board/menu_surface.rs` (its title is
the row or request name); `Action::order` orders its entries, the selected row's
actions before the board's.
`board/reload.rs` and `board/resume.rs` own the in-place reload trigger, `exec`
and snapshot file; `board/app/carry.rs` owns what `App` captures and restores
([Reload in place](references/board.md#reload-in-place)).
`App`, terminal/worker lifecycle, acquisition, `Scrolls` (position math; it paints
its lines and overflow indicator through `Strip`), home and shared TUI
components keep their separate owners. Workspace dispatch owns interactive row
starts and hits. `home` retains shared counts, rates, limits and admitted scene
utilities. Integrated renderer tests live in `view/tests.rs` and workspace tests;
the frozen baseline gate remains in the TUI development reference.
Raw ratatui widget enforcement and its verification belong to the
[tmt-tui skill](../tmt-tui/SKILL.md). A surface split preserves captured cells,
styles, hits and list bytes; it grants no parity-regeneration permission.
## Digest ownership
`management.rs` shares active-actor and user-or-current-lead admission with cron;
cron retains its original errors and tests. `digest_command.rs` owns bounded compound
s/m/h duration parsing (1s–24h), command output and Core revision-conflict guidance.
`digest.rs` owns the typed optional policy projection, UUID deduplication and one
bounded `digest.policy.show` (up to 256 identities) per list/board acquisition.
Eligible UUIDs come from the acquired `rooms.roster` snapshots, whose contract
excludes retired/nonmembers; document-shaped objects cannot add eligible identities.
Presence (including offline/unknown) is independent of identity retirement.
Command writes pass exactly the [digest contract](../../../contracts/extension-api.md#digest-policy-and-checklist)
fields: target, owner (the user's identity), setter, expected revision and set-only expiry.
No policy storage or retry lives in Squad. `ls` enriches once after all source
observations; aggregate/HOME acquisition shares one read across source and flat rows.
Unsupported, failed or malformed policy reads silently omit digest. Overflow UUIDs
beyond the one bounded batch omit digest. The existing worker acquires board values;
paint/input never read Core. The board clock advances minute labels and hides expiry,
and the existing row cache keys include labels. Shared row detail shows clipped digest
information. Use the native row-shape/management cases, focused parser/projection
and injected-clock renderer checks alongside unchanged cron tests.
## Row labels
`labels.rs` owns what extensions supply for member rows: the status-document reader
(contract: the supplier's own, `extensions/<name>/contracts`), its validation, and the
background `Reader`. It names no extension; `[labels] sources` is data. `board/row_chips.rs`
is the only place that decides what a row's chips are (supplied labels, else the digest
policy chip) and how many fit; every surface asks it for the room left after the member's
name and whole state, and paints roles, never sources.
A label's one possible action is a `choose` (`labels/action.rs`): the source lists the
options and the argv that applies one, with the namespace of the supplying source as
`argv[0]` and exactly one whole `{value}` element. `Choose::argv` is the only place a
value enters an argv; `Core::succeeds` spawns it with no shell. `run` actions have no
consumer, so such a label stays display-only. `board/digest.rs` owns the change: `grant`
(on the lane, never the board thread) resolves the person with `management::actor` and
admits them for `management::DIGEST`; `apply` admits again, runs the argv and words the
result, so a refused or failed change is never reported as saved. The menu and the
composer are the existing `Menu` and `Compose`; the row menu lists `digest` only for a
member whose labels offer a choice. `row_chips` ends that label's chip with `▾`
(`row_chips::offers_choice` finds it again); each painter reports where the chip landed
(`scene::Painted::spot` for the template painters, `RowPaint::choices` for the Rows pane)
into `App::chip_hits`, which `App::mouse` reads before row hits.
Details of the reader thread: [refresh-and-meter.md](references/refresh-and-meter.md#refresh-worker).
## References
- [references/development.md](references/development.md): build, test and verification commands moved from DEVELOPMENT.md.
[ARCHITECTURE](../../../ARCHITECTURE.md#ops-extension) owns the seam, dependency direction and
public-contract index. The user-facing row and config reference is the embedded lead skill
(`extensions/tmt-ops/skills/tmt-ops/SKILL.md`); do not copy its field lists, state-pattern
grammar or key tables into the references below.
## Reference files
| Topic | File |
| ------------------------------------------------------------------------------------ | --------------------------------------------------------- |
| Membership, leadership marker, `me`, field providers, staleness, reminders, cron | [data-and-state.md](references/data-and-state.md) |
| `ops.toml` layering and writes, themes, views, settings, link and action effects | [config-and-effects.md](references/config-and-effects.md) |
| Board frame, row grid, composition, tabs, scrolling, composers, overlays, validation | [board.md](references/board.md) |
| Workspace projection, acquired HOME summaries and header cache | [board-home.md](references/board-home.md) |
| Inert agent text, notebook/source utilities, conversation and reply authority | [board-notebooks.md](references/board-notebooks.md) |
| Board cron acquisition, scoped Timers modal, forms and input | [board-cron.md](references/board-cron.md) |
| Refresh worker, change detection, token meter, shutdown | [refresh-and-meter.md](references/refresh-and-meter.md) |
## Invariants
- Core reachability: public JSON output and `tmt api` only, through
`TMT_EXECUTABLE` (or `tmt` on PATH). `resume --show` emits JSON itself and
rejects `--json`. `runner` maps read results and errors onto `tmt-invoke` for
bounded capture. An explicitly confirmed member-session handoff restores the
terminal and stops workers before execing public `tmt resume` in this pane. No TMT crate depends on Squad; the
architecture guard enforces both directions for Cargo dependencies and source
references. Runtime TMT dependencies are the neutral leaves `tmt-cli-style`,
`tmt-extension-state`, `tmt-invoke` and `tmt-tui`.
- A squad is the core room `squad-<name>`. Member fields are identity metadata
`squad.<name>.<field>`; Squad has no membership store of its own.
- Squad-owned data lives under `<dataRoot>/ops` (`storage.root` from `tmt api`),
plus disposable caches under `$XDG_CACHE_HOME/tmt-ops/`. `ops.toml` is the
user's file; agents never write it, and no cron data goes into it or the core
database.
- Squad never writes `config.json`, a provider directory or tmux state except
through core commands; `jump` and `back` use `tmt focus`.
- Board-only data (the home model and token-rate meter state, including any
`usage.*` observation) never enters public `ls --json` or the other public
documents. Public documents carry display-ready strings; consumers must not
format them again.
- Paint and input perform no core reads; refresh, providers and notebook reads run
on workers (see [refresh-and-meter.md](references/refresh-and-meter.md)).
- Command grammar, help and human output go through `tmt-cli-style`
(`CommandSpec`, `Interaction`); `ui` runs only when `Interaction::view()` is
`Interactive`, decided once in `main`, otherwise it is `ls`. Bare `tmt ops squad` lists members and adds
`tmt ops ui opens the board`; bare squad `--json` is identical to squad `ls --json`. Bare Ops shows help; help and version have no JSON form (`JSON_UNSUPPORTED`, exit 1), and every usage error exits 1, a JSON one carrying clap's text. Consent for hotkeys and playbooks is a `Consent` decided in
`main` from `--yes` and `prompt()`.
- Squad's dependencies must not change the CLI product: prove it package-scoped
(`cargo ... -p tmt-cli` alone), because combined workspace builds can unify
shared-dependency features.
- Squad is versioned and released independently (`tmt-ops-v<version>` tags). Its
archive also carries `skills/tmt-ops/`, the same source as the embedded lead
skill; playbooks under `extensions/tmt-ops/playbooks/` are deliberately outside
`skills/` (see [config-and-effects.md](references/config-and-effects.md#playbooks)).