Compare commits

..

5 Commits

Author SHA1 Message Date
Jarvis
2708de55b9 docs(prd): fold 2026-07-09 debate-pass findings into Mosaic Platform PRD
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
All 24 panel findings (P0#1-5, P1#6-13, P2/P3#14-24) accepted and folded:
- DEBATE-FINDINGS.md: disposition record + open-disagreement judgment calls
- north-star: NS-14, J2a/J2b split, J6 wake router, K3 push pipeline,
  M1 memory subsystem, workstream L (MALS lineage), real DAG edges
- J: workspace lease J-R16, resume protocol J-R18, write-side trust,
  needs-decision lifecycle, atomic card publish
- P: CAS approval state machine, gateway-only credentials, default-closed
  gating, principal resolution + policy snapshot (Codex #2/#7)
- W: butt-in exclusive lease, structured control plane, break-glass
  doctrine, trial metric panels
- Q: external_refs table, pending-link, echo-loop guard, crash barriers
- X: storage authority (Postgres sole record), machine-generated census,
  memory retrieval-eval retirement gate, honest rollback scope,
  4 artifact-existence machine gates, bounded day-30 review
- README: Gate Zero, conflict register, DoD line, silent-roster rule

One item OPEN for Jason at ratification: gate superstructure (§3.1).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RMoEx7hfdFGjUiCHuN1RRi
2026-07-09 16:29:37 -05:00
Jason Woltje
6b8c3c5d3a docs(fleet): refine Mosaic Platform PRD per Jason's review (R1–R7)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
Post-review refinements to the DRAFT PRD set (decisions D1–D12 unchanged):

- R1: PRD-permission-relay is now self-contained — its cited design source
  (guard-rails-capability-permissions.md) is absent from origin/main, so the
  essential model (prepare-freely/execute-with-approval, permission levels,
  resource:action grants) is folded in as the authoritative spec.
- R2: Phase-1 conversation channel is tmux/CLI via the f4 Phase-1 tmux
  connector (operator issues /remote-control); Matrix room (J5/K1) is Phase 2.
- R3: Jarvis is a Level-0 orchestrator (delegation + subagents, never executes
  coding/infra); AC-NS-8 latency isolation is a separate-model-capacity
  guarantee, not just a separate process (NS-10, AC-NS-8, ASM-5).
- R4: jarvis-brain reframed as the P0 (prototype) Mosaic Stack; tenant-1
  migration targets the proper stack storage layer (vector DB + enhanced
  memory + flat-file backends). Agent memory/runbooks migrate live into the
  memory subsystem and are NOT frozen; repo retires read-only only after both
  PA data and agent memory are verified migrated (X2/X-R7).
- R5: homelab→USC promotion is an explicit owner sign-off gate, not an
  automated metric (X deployment-scope).
- R6: verified K1 is covered by f4-matrix-connector.md Phase 2 (no new doc).
- R7: clarified always-available Opus ≠ standing cost (idle = no bill) (J-R2).
- Also normalized PRD-backlog-providers.md and PRD-webui-fleet-control.md with
  prettier (whitespace-only) — they were non-conformant as originally pushed
  and would have re-broken repo-wide format:check on merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RMoEx7hfdFGjUiCHuN1RRi
2026-07-09 14:50:54 -05:00
Hermes Agent
d1f69bfd37 docs(fleet): DRAFT PRD proposal — Jarvis HMI main agent, Matrix-first transport, webUI fleet control, permission relay, backlog providers, Hermes decommission
Some checks failed
ci/woodpecker/push/ci Pipeline failed
Ratification draft (Jason, 2026-07-09) extending NORTH_STAR.yaml with NS-10..NS-13
and workstreams J/K/W/P/Q/X. Written against origin/main's fleet suite (F4/F6
anchors, H2 profiles, native backlog per ASM-1). Homelab is the trial
environment (D12); USC/web1 adopts post-trial. See proposals/mosaic-platform-prd/README.md
for the full decision record D1-D12.

Not yet merged into NORTH_STAR.yaml — landing and card decomposition are the
homelab orchestrator's mission.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:19:05 -05:00
4e9e053800 fix(tools/tmux): unique per-invocation paste buffer; track auto-submit-drafts.sh (#697)
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-07-09 17:37:40 +00:00
851c67c27b docs(bootstrap): add python-is-python3 to agent-host prerequisites (#694)
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-07-03 09:38:40 +00:00
12 changed files with 919 additions and 3 deletions

View File

@@ -0,0 +1,61 @@
# Debate Findings & Dispositions — Mosaic Platform PRD
> **Convener:** mos-claude-1 · **Date:** 2026-07-09 · **Panel:** 9 personas × 2 rounds (8 Claude lenses + independent Codex runtime), 20 agents, ~1.05M tokens
> **Artifacts:** `jarvis-brain:docs/scratchpads/mosaic-platform-prd-debate/` (THREAD.md — full transcript · SYNTHESIS.md — Principal-Engineer close-out)
> **Mandate (Jason, 2026-07-09):** "debate and make judgment calls." D1D12 were held fixed; the panel attacked only the implementing structure. Dispositions below are the convener's judgment calls, folded into the sibling docs in this directory. Items marked **OPEN — Jason** need his call at ratification.
## How to read this
Every synthesis finding (SYNTHESIS.md §1, items 124) is dispositioned here. **Accepted** findings are folded into the PRDs/YAML as inline fixes or "Debate-accepted deltas" rows; this file is the traceability record. Severity labels are the panel's (P0 blocker → P3 note).
## P0 findings — all accepted, folded inline
| # | Finding | Disposition |
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Storage-authority contradiction (X2 "relational + flat-file backends" vs Q "Postgres sole record") | **Accepted.** X2 rewritten: Postgres authoritative for all product entities; flat-file backend is a derived, regenerated, **read-only projection**. |
| 2 | Phase-1 Jarvis executes external PA ops before the relay exists (J2 vs NS-11) | **Accepted.** J2 split: **J2a** workspace-internal entities (phase 1) / **J2b** external integrations (phase 2, `depends_on: [P2]`). Phase-1 has no external credential path at all. |
| 3 | `phase` vs `depends_on` disagree; dispatcher obedience undefined | **Accepted.** Phases encoded as real DAG edges (`X2 depends_on [J2a, P2]`, J2b gate above); sandbox dispatch-test added to ratification checklist (README Gate Zero §). |
| 4 | Four load-bearing upstream artifacts unverified (memory subsystem, Hermes-MCP tool equivalents, push pipeline, wake/event router) | **Accepted.** README gains **Gate Zero** (pre-ratification artifact audit); presumed-MISSING rows get goal cards now: **M1** (memory subsystem), **J6** (event/wake router), **K3** (push pipeline). Parity map's MCP row re-pointed at concrete deliverables. |
| 5 | X-R4 migration manifest wrong about its own source tree (phantom `tickets.json`, unlisted dirs, six divergent memory stores, live Vikunja sync unmapped) | **Accepted.** X-R4 rewritten: migrator stage 1 = machine-generated source census (incl. untracked paths + all memory stores), per-path disposition, any `unknown` blocks; re-point list generated from `tools/sync_*.py`; Vikunja disposition line added to X-R6. |
## P1 findings — all accepted
| # | Finding | Disposition (folded as deltas) |
| --- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 6 | Events have no provider join key; re-point duplicates the calendar | **Accepted.** Provider-sourced events migrate **from the provider**; flat files supply only brain-native events. DST/recurrence fixture is the AC (X-R6 delta). |
| 7 | Approval pipeline has no state machine (double-execute / approve≠execute) | **Accepted.** P deltas: CAS on one durable row, terminal states, consumed-event dedupe table (shared substrate with bridge + wake dedupe), payload persisted at request time, poll/ack outcomes, per-capability retry class, staleness bound, `approved_unexecuted` alarm, re-surface = re-**prepare** with machine diff. |
| 8 | "Cannot bypass by construction" false: agent-readable vault creds + host-resident creds outside the relay | **Accepted.** Credentials gateway-only + scoped capability tokens; P1 gains host credential inventory with continuous scheduled scan in the health floor; clean-host AC passes with empty exemption list. |
| 9 | Silent auto-deny steady state (fail-closed TTL + single surface + unprovisioned push) | **Accepted.** K gains homeserver ops + monitored push (K3); CLI approval surface ships **with** P2; delivered/seen tracking + TTL/2 escalation; `denied` / `expired_seen` / `expired_unseen` distinct terminal states. |
| 10 | Prompt injection → durable memory; wake turns add system-role injection | **Accepted.** J2 write-side trust rule (transitive `source_trust=external`; standing-instruction-shaped content needs user ratification before retrievable); wake turns templated with provenance-tagged data fields. |
| 11 | Exactly-one-Jarvis has no fencing incl. the Matrix send path | **Accepted.** New **J-R16** workspace lease `(workspace_id, epoch)` CAS row in product Postgres; epoch on every write; pre-send lease re-check; takeover notice; degraded = mute-with-notice. NS-10 amended: one main agent per **workspace**. |
| 12 | Matrix principal resolution undefined (Codex #2, unanswered in R2) | **Accepted.** K/P delta: immutable Matrix user ID + bridge provenance + workspace membership → product principal; unlinked/bridged-unlinked identities read-only, cannot approve/butt-in/trigger external writes. All four Codex fixtures = deny + audit. |
| 13 | Policy evaluation time undefined (Codex #7, unanswered in R2) | **Accepted.** Immutable policy snapshot on every prepared action/card; execution revalidates or fails `policy_changed`; delegated effects gated by grant **intersection**. |
## P2/P3 findings — accepted (see per-PRD delta sections)
14 Hermes evidence snapshot **before** stop (machine gate) · 15 Q1 crash barriers + `external_refs` unique-index table (one mechanism, three consumers) · 16 rollback honestly scoped (transport-only, point-of-no-return = first native card) + bounded day-30 review with three recorded outcomes · 17 human-attention budget (rate limits, quotas, deferrable flag, away state) · 18 self-referential-loop containment (provenance labels, source-grounded retrieval preference, retrieval eval gate ≥50 queries / ≥90% baseline recall@5 + negative queries, tombstones, priority budget, day-1 trend telemetry) · 19 butt-in exclusive lease + structured control-plane API + break-glass doctrine · 20 default-closed capability gating with `unclassified` third state · 21 per-agent atomic approval-routing cutover table · 22 `needs-decision` card lifecycle (immutable spec + typed amendments; `ratified_by` authorizes dispatch, never merge) · 23 retention class per durable table; dedupe pruning checkpoint-coupled · 24 AC-NS-8 made measurable (distinct quota pools pinned in profile; TTFT p95 ≤ 1.2× baseline, ≥30 interleaved turns).
**All accepted.** None conflicts with D1D12.
## Open disagreements — convener judgment calls
| § | Question | Call |
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 3.1 | Gate architecture: tiered blocking pack (Moonshot+Ops+ML-Cal) vs 4 artifact-existence machine gates + pass/fail-free pre-registered dossier (Contrarian) | **Adopted the agreed floor now** (4 machine gates on irreversible transitions, pre-registration, priced amendments, day-1 emission — folded into X). Superstructure choice is **OPEN — Jason**. Convener recommendation: **Contrarian's dossier + dumb gates** — the tier demonstrably gamed itself within one debate round (13+ claims vs cap 10, slot-riding, zero demotions); simple machine gates don't degrade under pressure. |
| 3.2 | Audit schema: additive-only typed schema (Coder-Data) vs six-field mandatory envelope + typed payloads + pinned checked-in queries (Contrarian) | **Adopted: minimal envelope + schema-on-read** (folded into P1 delta). Rationale: preserves "can't add data later" essentials without a god-schema by committee; an additive typed layer can be grown later where query pain proves it. |
| 3.3 | Unclassified capability: reject at call time vs version-scoped activation hold vs capability-scoped hold | **Adopted: capability-scoped hold** (Contrarian R2#6a, synthesis editor concurs) — gate the capability, ship the version; security patches are never pinned behind classification. |
| 3.4 | Away mode: fail-closed expiry labeled `expired_during_away` vs suppress preparation while away | **Adopted: suppress preparation** of non-deferrable requests while away + audited suppressed-preparations list swept on return. Cleaner than labeling corpses; nothing expires that was never surfaced. |
| 3.5 | Memory-exclusion scope for measurement artifacts | **Adopted: normative/parametric split** (Contrarian R2#4) — rules the agent is scored against stay retrievable; thresholds/seeds/drill timings/canary templates are excluded. |
| 3.6 | Statistical instruments | **Adopted:** deterministic named crash barriers as the gate; residual randomized soak is **trace-directed**, not wall-clock-uniform. |
| 3.7 | K2 scope narrowing by Hermes traffic audit | **Adopted as a Gate Zero action:** run the audit pre-ratification; platforms with live traffic become the must-have subset gating X3. |
## Process rules adopted (README)
- **Gate Zero** — pre-ratification upstream-artifact audit (`present @ SHA` or MISSING → goal card).
- **Conflict register** — spec contradictions block the requirement, not the mission ("alert, don't auto-resolve" promoted to specs).
- **DoD line** on every goal card (runbook, health-floor alerts, AGENTS.md).
- **Silent-roster rule** — a panel member's silent round records their open findings as open items, never consensus (Codex's R2 silence on #2/#7 is the instance; both were folded as P1 items 1213 above, explicitly not consensus-resolved).
## Addendum — logging & telemetry (Jason, 2026-07-09, post-debate)
Requirement raised outside the panel, folded in the same pass: Mosaic Stack must support **inbound error reporting/logging** from agents and installs, plus **optional anonymous agentic-efficiency telemetry** — no IP or PII capture, opt-in. The P0 of this capability already exists: **MALS** (Mosaic Agent Log System, FastAPI+Postgres, `~/src/mals`), currently dark because its k3s migration landed without an Ingress (tracked: infrastructure #135). Folded as new **workstream L** (L1 restore MALS · L2 Mosaic-native ingestion · L3 anonymous telemetry) + standing objective **NS-14**. Day-1 trial-metric emission (finding 18) targets MALS until W3 panels exist.

View File

@@ -0,0 +1,58 @@
# PRD — Backlog Provider Sync Adapters · Workstream Q
> **Status:** DRAFT for ratification · **Goals:** Q1Q3 · **Doctrine:** NS-12 (ratified D3)
> **Debate pass 2026-07-09:** panel findings folded — see `DEBATE-FINDINGS.md`.
## Mission
Users choose where they _see and touch_ work — Gitea, GitHub, a local kanban — while the **Mosaic Backlog on native Postgres stays the sole record and dispatch engine** (upholds ASM-1; NS-3/NS-4/NS-5 guarantees never depend on an external provider). Providers attach as bidirectional sync adapters.
## Requirements
### Adapter interface + Gitea (Q1)
| ID | Requirement |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Q-R1 | A `BacklogProviderAdapter` interface: map card ⇄ external item (create/update/close/comment/label), with stable external-id linkage stored on the card. |
| Q-R2 | Sync is bidirectional and conflict-safe: native record wins on divergence; external edits arrive as proposed mutations (applied if non-conflicting, else surfaced). |
| Q-R3 | Claims, TTLs, depends_on DAG, and dispatch state live **only** in the native record; adapters project them (e.g. as labels/comments) but never own them. |
| Q-R4 | Gitea adapter first (webhook + API), configured per workspace: repo mapping, label conventions, direction (mirror-out / mirror-in / full). |
| Q-R5 | Adapter enable/disable is workspace configuration; zero adapters is a fully supported mode. |
### GitHub (Q2)
| ID | Requirement |
| ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Q-R6 | Same interface, GitHub Issues backend. Existing `packages/cli-tools` platform detection informs but does not implement this (that is dev tooling, not product runtime). |
### Local kanban (Q3)
| ID | Requirement |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Q-R7 | A webUI kanban board over the native backlog (no external provider needed) — the "local kanban" choice. Builds on W3's card views and/or the existing `KanbanBoard` component upgraded from demo-grade to live data. |
## Debate-accepted deltas (2026-07-09) — normative
| ID | Requirement |
| ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Q-R8 | **External-ref linkage is one shared mechanism:** a unique-indexed `external_refs(entity_id, system, external_id)` table serves adapter linkage (Q-R1), migration idempotency (X-R4 anti-join), and re-point verification (X-R6) — built once, three consumers. Idempotent upsert; a violated unique index is a converge signal, never an overwrite. |
| Q-R9 | **Crash-safe external creates:** the adapter writes a `pending-link` row **before** any external create and embeds a deterministic card-id marker in the created item, so a crash between create and link-back is recovered by scan, never by duplicate creation. |
| Q-R10 | **Echo-loop guard:** both directions carry revision counters; adapter-authored external edits are tagged (marker/actor) and skipped on read-back. A sync cycle that would re-import its own write is a hard test failure. |
| Q-R11 | The sync engine is a **level-triggered reconciler** over desired-vs-observed state (same doctrine as J6), not a webhook-only event chase: webhooks accelerate convergence, the reconciler guarantees it. Missed webhooks are a latency event, not a correctness event. |
| Q-R12 | Card spec immutability (J-R20) projects cleanly: the mirrored issue body is the pinned spec revision; amendments append as **ordered provider comments**, never body rewrites, so external watchers see the same amendment history as the native record. |
## Acceptance criteria
1. A card created by Jarvis (J3) appears as a Gitea issue within one sync interval; closing the issue in Gitea marks the card for review, not silent closure; dispatch/claims never round-trip through Gitea.
2. Killing the adapter mid-mission: dispatch continues unaffected (record is native); on restart, sync converges without duplicates.
3. The same mission can be mirrored to Gitea and viewed on the local kanban simultaneously without state divergence.
4. **Named crash barriers** at every external-call boundary (`before_external_create`, `after_create_before_link`, `after_link_before_ack`): kill the adapter at each; zero duplicate external items, zero orphaned cards. CI rule: a new external call site without a named barrier + kill test **fails the build**. Residual randomized soak is trace-directed (§3.6 disposition).
## Non-goals
- External provider AS the backlog (vetoed — "truly swappable backends" option declined 2026-07-09).
- Two-way sync of claims/TTL semantics (external systems can't express them; projection only).
## Assumptions
- ASSUMPTION: the delivery fleet's _engineering_ PR/issue flow on the stack repo itself continues to use `cli-tools`/Gitea directly — workstream Q is the product feature for user workspaces, not a replacement for the dev workflow.

View File

@@ -0,0 +1,83 @@
# PRD — Hermes Decommission & Tenant-1 Migration · Workstream X
> **Status:** DRAFT for ratification · **Goals:** X1X3 · **Doctrine:** NS-13, ASM-8 (Hermes untouched until verified parity)
> Ratified direction (D2, 2026-07-09): Mosaic absorbs **all four** Hermes functions — messaging bridge, task board, permission relay, multi-platform reach.
> **Debate pass 2026-07-09:** panel findings folded — this PRD took the heaviest rewrite (storage authority, migration census, memory-store hygiene, honest rollback scope). See `DEBATE-FINDINGS.md`.
## Deployment scope (D12/ASM-9)
The trial runs in the **homelab**. Hermes and the primitive-era stack (`mos-claude.service`, jarvis-brain boards) live in the **USC/web1 environment**, which is untouched during the trial. This workstream therefore lands in two stages: **X-in-homelab** (prove parity where the fleet is native — mainly K/P/Q verification plus tenant-1 migration) and **X-at-USC** (post-trial adoption: apply the parity checklist to web1, migrate Mos-on-web1 to `mosaic-agent@orchestrator`, then decommission Hermes there, with `/src/infrastructure` GitOps updates in the same delivery set).
**Trial go/no-go (D12/ASM-9 gate):** the homelab→USC promotion is **owner-judgment**, not an automated metric. The stage gate is: _Jason instantiates and operates the split-agent stack in the homelab and is satisfied with its operation._ Only on that explicit sign-off does X-at-USC begin. The capability ACs (AC-NS-8…11) are the evidence Jason weighs; they inform the decision but do not auto-trigger USC deployment.
## Mission
Retire Hermes entirely. Mosaic becomes the platform for transport (Matrix connector), task board (native backlog + webUI/adapters), approvals (permission relay), and multi-platform reach (mautrix bridges). In the same arc, Jason's jarvis-brain flat-file data migrates into the product as **tenant #1**, making the product's PA feature set the dogfooded default.
## Parity map (what replaces what)
| Hermes function | Mosaic replacement | Workstream |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| Messaging bridge (Discord/Telegram/…) | Matrix connector + mautrix bridges | K1, K2 |
| Kanban / task board | Native Mosaic Backlog + webUI board + provider adapters | A\*, Q, W3 |
| Permission relay (`permissions_*`) | Guard-rails engine + approval queue (Matrix + webUI) | P1P3 |
| Cross-platform user reach | mautrix bridges (agents speak Matrix only) | K2 |
| Hermes MCP tools in agent sessions | **Per-tool equivalence table** (Gate Zero artifact): approvals → P2, board ops → Q1/A\*, messaging → K1 — not a generic "gateway API" gesture | P2, Q1, K1 |
## Requirements
### Parity checklist + cutover plan (X1)
| ID | Requirement |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| X-R1 | A written, testable parity checklist per row above; each item verified in production before its Hermes counterpart is disabled. The checklist is seeded from a **pre-trial usage audit**: per-MCP-tool and per-capability call counts over a trailing window, plus an inventory of Hermes-held provider callbacks/webhooks and secrets. Parity rows with `n < 5` real invocations in the window cannot be "verified by traffic" — they get **scheduled drills in weeks 13** of the observation window instead of silent green. |
| X-R1a | **Approval-routing cutover is per-agent atomic:** a cutover table states, per agent, the single moment its `permissions_*` path flips from Hermes to the P relay. No agent ever has two live approval paths; no approval window where neither path is live. |
| X-R2 | Cutover is staged with rollback at every stage; Hermes runs untouched until AC-NS-11 is verified (ASM-8). **Rollback is honestly scoped (debate #16): it restores _transport_ (Hermes services + MCP registrations) — board/approval state created natively during the trial does NOT back-migrate.** The **point of no return is the first native-only card**; the runbook says so explicitly, and the abort path (below) is written before cutover, not during an incident. |
| X-R3 | The Matrix charter's live-cutover rules apply: stated window, announce before/after, rollback ready. |
### Tenant-1 migration (X2)
**Framing (ratified 2026-07-09, storage authority clarified by debate P0 #1):** jarvis-brain **is the P0 (prototype) Mosaic Stack** — its flat-file data layer is the zeroth implementation of what the product does properly. Migration is therefore _P0 → proper Mosaic Stack_. **Native Postgres is the sole authoritative store for every product entity** (consistent with workstream Q's "sole record" doctrine and NS-3/NS-4/NS-5). A flat-file backend, where offered, is a **derived, regenerated, read-only projection** — the same relationship generated views have to JSON in the P0 today — never a co-equal write target. Two distinct data classes migrate — they are not the same destination and neither is frozen:
- **(a) PA data** (projects, tasks, events, tickets, knowledge) → product entities in the Jason workspace (Project/Task/Event/KnowledgeEntry), authoritative in Postgres; flat-file export available as a read-only projection.
- **(b) Agent memory & operational knowledge** (runbooks, digests, scratchpads, OpenBrain thoughts) → the **enhanced memory subsystem (goal M1: vector DB + memory service)**. This flow stays **live and writable** throughout — it was never Hermes and must not be frozen by the PA cutover.
| ID | Requirement |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| X-R4 | **Stage 1 of the migrator is a machine-generated source census, not a hand-written file list** (debate P0 #5 — the hand list was already wrong about its own tree). The census walks the live repo (tracked + untracked), inventories every data path — `data/**`, all memory stores (`memory/`, `memories/`, `memory_store*`, `memory.md`, `brain.jsonl`, `jarvis.db`, digests), scratchpads, notes, prior-generation artifacts — and assigns each a disposition: `migrate-as-PA(entity type)` / `migrate-as-memory(M1 class)` / `derived-regenerate` / `retire-with-history`. **Any path dispositioned `unknown` blocks the run.** Stage 2 migrates per-disposition, preserving source ids in metadata; idempotency via anti-join on the shared `external_refs` table (Q-R8); named crash barriers at each phase boundary. A **field-map table** covers the full `brain.py` query surface (status, progress, due, priority, domain, notes, staleness) → product entity fields, so AC 2 is checkable field-by-field. |
| X-R5 | Dry-run mode with a diffable report **generated from the census** (counts per disposition, per-entity field mapping, unmapped-field list — must be empty or explicitly waived); Jason ratifies the report before the real run (canonical-data gate — this is the one migration step that is his call). |
| X-R6 | External sync jobs (GLPI, Google Calendar, ICS, Gmail, **Vikunja — disposition decided here: re-point or retire, not silently dropped**) each get a **written per-integration transition protocol**: freeze flat-file sync job → verify product integration live → re-point → verify → retire old job. **Provider-sourced events migrate FROM the provider, not from flat files** (flat files supply only brain-native events) — the provider join key is authoritative, so re-pointing cannot duplicate the calendar (debate #6). Calendar fixture (Codex): a DST-crossing recurring event with one moved and one cancelled occurrence, plus an all-day event, round-trips with zero duplicates and correct local times. |
| X-R7 | Agent memory/operational knowledge (b) is migrated into the M1 memory subsystem **before** any jarvis-brain retirement; the memory write path stays continuously available (no read-only freeze of an active substrate). The census classifies every memory item: `ratified` / `superseded` / `draft` / `rejected` / `debate-artifact` / `protocol-normative` / `protocol-parametric`. **Parametric measurement artifacts (thresholds, seeds, drill timings, canary templates) are excluded from embedding** (§3.5 disposition — rules the agent is scored against stay retrievable; the knobs do not). Superseded/rejected items get **tombstones**, and supersession triggers re-embedding of affected summaries. **Retirement gate: a retrieval eval — ≥50 representative queries, ≥90% of baseline recall@5, plus negative queries (rejected/superseded content must NOT surface) — passes against M1 before any flat-file store goes read-only.** After cutover: write paths to retired stores are killed and a CI lint fails any reintroduction. Only once **both** (a) and (b) are migrated and verified is the jarvis-brain repo retired read-only (history preserved); generated views and brain.py retire. This closes the P0 prototype. |
### Decommission (X3)
| ID | Requirement |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| X-R8 | **Pre-stop evidence snapshot is a machine gate:** before Hermes stops, a checksummed snapshot of its state (board items, pending approvals, bridge registrations, per-tool usage counts) is captured and stored with the trial artifacts. The drill schedule (X-R1) and the credential-revocation report both **cite the snapshot checksum** — no snapshot, no stop. Then: services stopped, disabled, and removed from infra (GitOps: `/src/infrastructure` updated in the same delivery set); credentials revoked — the revocation report cites a **final scan** showing zero live references; MCP registrations removed from agent runtimes. |
| X-R9 | **Bounded day-30 review** between stop and removal, pre-registered before cutover (dossier: what will be measured, panels cited, drill results attached — see W-R15/L1 for where the metrics live, `stack_version`-segmented so mid-window upgrades don't blur rates). The review records exactly one of three outcomes: **promote** (remove Hermes), **extend with named blockers** (each blocker a card with an owner), or **abort** (execute the pre-written abort runbook; transport-only rollback per X-R2). "Insufficient data" is a recordable outcome that forces extend — never a shrug into promote. Any in-window regression flips back per X-R2. |
## Machine gates on irreversible transitions (debate §3.1 agreed floor)
Four **artifact-existence gates** — dumb, checkable, ungameable — sit on the irreversible transitions. Each is "the artifact exists and passes its check", not a scored rubric:
1. **Pre-stop snapshot** exists with valid checksum (X-R8) — gates Hermes stop.
2. **Census with zero `unknown` rows** exists (X-R4) — gates the PA migration run.
3. **Retrieval eval pass record** exists (X-R7) — gates memory-store retirement.
4. **All parity rows green-or-drilled** (X-R1: verified by traffic or by scheduled drill; no silent low-n green) — gates Hermes removal at day-30.
The gate _superstructure_ beyond this floor (tiered blocking pack vs pre-registered dossier) is **OPEN — Jason** at ratification; convener recommendation in `DEBATE-FINDINGS.md` §3.1.
## Acceptance criteria
1. AC-NS-11: with Hermes stopped, no fleet or main-agent capability regresses.
2. `python tools/brain.py today`'s information content is fully answerable by Jarvis from the product workspace post-X2, verified field-by-field against the X-R4 field-map table.
3. Zero references to Hermes MCP tools in any active agent runtime config after X3.
4. The X-R6 calendar fixture (DST-crossing recurrence, moved + cancelled occurrence, all-day event) round-trips with zero duplicates.
5. The X-R7 retrieval eval passes against M1 before any memory store goes read-only; negative queries return no superseded/rejected content.
6. All four machine gates above have their artifacts on record before their respective transitions execute.
## Sequencing note
X depends on the longest chains (K1→K2, P2, Q1, J2a→J2b, **M1** — X2 cannot complete class (b) without the memory subsystem existing). Dependencies are real DAG edges in `north-star-additions.yaml` (`X2 depends_on [J2a, P2, M1]`), not prose phases (debate P0 #3). Expected order of value delivery: J1J4 (Jarvis on existing transport interim) → K1/J5 (Matrix room) → P2, W1W3, Q1 in parallel → X1 checklist → X2 migration → K2 bridges → X3 decommission.
- ASSUMPTION (interim transport): until K1 lands, Jarvis may run against the tmux connector (CLI/`agent send`) rather than standing up any Discord channel — keeps D1 (Matrix-first, no #jarvis Discord) intact.

View File

@@ -0,0 +1,89 @@
# PRD — HMI Main Agent ("Jarvis") · Workstream J
> **Status:** DRAFT for ratification · **Source of truth once landed:** NORTH_STAR.yaml goals J1J6
> **Depends on upstream:** H2 (system-type profiles), A3a (card lifecycle), B1 (supervisor tick), F4/K1 (Matrix connector)
> **Debate pass 2026-07-09:** panel findings folded — see `DEBATE-FINDINGS.md` for dispositions.
## Mission
Every Mosaic **workspace** gets exactly one always-on human-machine-interface agent — default alias **Jarvis**, unit `mosaic-agent@main.service` — that owns the human relationship: conversation, idea development, schedule, email, tasks, knowledge. It delegates all engineering/research/ops work to the orchestrator (**Mos**, `mosaic-agent@orchestrator.service`) through the Mosaic Backlog, and reports fleet status to the user without ever interrupting the orchestrator.
Jarvis is a **Level-0 orchestrator**: it accomplishes its own work through _delegation and subagents_, never by executing coding/infra tasks itself. PA mutations (tasks/events/knowledge) are direct API calls; everything heavier is either a spawned subagent (research, drafting, analysis) or a backlog card handed to Mos (engineering/infra/fleet). This keeps the main agent's context conversational and light.
This solves the observed failure mode: a busy orchestrator that can't respond, accumulates conversational context rot, and derails over time. Post-split, the orchestrator's context is execution-only.
Because Jarvis and Mos are **separate agents with separate model capacity** (D11: Jarvis on Opus, Mos on Fable; independent inference quota), orchestrator load cannot degrade conversational latency — the isolation in AC-NS-8 is a capacity guarantee, not merely a separate process.
## Requirements
### Persona & runtime (J1)
| ID | Requirement |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| J-R1 | Jarvis is provisioned from the personal-assistant persona baseline via system-type profiles (H2/H3); alias, model tier, host, and channel are profile fields, not code. |
| J-R2 | Default model tier **Opus** (ratified D11); the orchestrator's tier is independent. Always-_available_ ≠ always-_billed_: Opus is provisioned 24/7 but cost is per-interaction — an idle Jarvis (no user turn in flight) incurs no model spend, so "always-on" carries no standing token bill. |
| J-R3 | Jarvis survives reboot under systemd (`mosaic-agent@main`), participates in the fleet heartbeat protocol, and is counted in the supervisor's health floor. |
| J-R4 | Persona customization is update-surviving per H4 (override layer wins on merge). |
| J-R16 | **Workspace lease (exactly-one fencing):** Jarvis acquires `workspace_lease(workspace_id, epoch)` — a CAS row in product Postgres — before processing any turn. Every PA write, card, and approval carries the epoch; stale-epoch writes are rejected server-side; the connector re-checks the lease immediately before every outbound send. Takeover posts an in-room/in-channel epoch notice. Degraded mode (lease unobtainable) = **mute-with-notice**, never conversational-while-unfenced. Clean shutdown releases the lease. This primitive also excludes homelab/USC split-brain during adoption. |
| J-R17 | Per-agent spend metering with a daily budget alarm for `mosaic-agent@main`; a main-agent crash-loop is a distinct, escalated supervisor condition (not a generic restart count). |
| J-R18 | **Resume protocol (promoted from open item):** resume context is reconstructed from authoritative queries (board, heartbeats, workspace entities), with narrative summary layered on top; a session-start divergence check flags contradictions between narrative and authoritative state. Jarvis is never re-instantiated from its own lossy summaries alone. |
### PA toolchain (J2a workspace-internal · J2b external)
**Split (debate P0 #2):** phase-1 Jarvis operates only on workspace-internal entities — **no external-write credential path exists** until the permission relay (P2) is live. External integrations arrive in phase 2 as J2b, `depends_on: [J2a, P2]`. Test: in a phase-1 deployment, `email:send` is _impossible_ (no credential provisioned), not merely unapproved.
| ID | Requirement |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| J-R5 | (J2a) Jarvis executes personal-assistant mutations **directly** in the user's workspace via the product API: tasks, events/calendar, knowledge entries, ideas. No delegation for PA ops (ratified D4). |
| J-R6 | (J2b) External PA integrations (email, external calendars, helpdesk) are workspace-scoped integrations; **raw credentials are held exclusively by the gateway** — Jarvis receives scoped capability tokens, never provider secrets; actions flagged `requires_approval` route through the permission relay (workstream P). |
| J-R7 | Until tenant-1 migration (X2) completes, Jarvis may read/write the jarvis-brain flat files as a transitional adapter; the adapter is deleted after the **last verified X-R6 re-point** (not at a nominal "X2 cutover" date). A per-phase, per-entity-type source-of-truth table in the J1 profile states which store is authoritative at every moment. |
| J-R19 | **Write-side trust rule:** externally-sourced content (email bodies, bridged messages, webhook payloads) written into workspace entities or memory inherits `source_trust=external` **transitively through summarization**. Standing-instruction-shaped external content requires explicit user ratification before it becomes retrievable. This closes the injection→durable-memory channel. |
### Delegation contract (J3)
| ID | Requirement |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| J-R8 | The Jarvis→Mos handoff is **only** via Mosaic Backlog cards: goal, acceptance criteria, priority, depends_on, advisory budget. Never via chat messages to the orchestrator. |
| J-R9 | Jarvis translates conversation outcomes into card sets; ambiguity is resolved with the user _before_ card creation — the orchestrator receives only decision-complete work. |
| J-R10 | Card authorship is attributed (author=main-agent, ratified-by=user where applicable) for audit. |
| J-R11 | Authority line: Mos holds all execution and merge authority (NS-4). Jarvis relays the user's GO/NO-GO gates as card state, and never acquires fleet mutation, merge, or dispatch rights. `ratified_by=user` authorizes **dispatch only** — it never substitutes for the reviewer-of-record merge gate. |
| J-R20 | Card sets are drafted then **published atomically** with client idempotency keys (no partial card sets on crash). Card spec is **immutable after publish**; changes arrive as typed, ordered amendments with a revision counter; reviewer sign-offs pin the spec revision; scope-expanding amendments re-enter ratification. |
| J-R21 | **`needs-decision` lifecycle:** a worker hitting genuine ambiguity sets `needs-decision(question, options)` on the card **with a durable checkpoint** (pushed branch + card note) — resume after days is a re-dispatch, not a context continuation. Jarvis relays the question to the user and writes the answer back as an amendment. This is the sanctioned clarification path; J-R8's no-chat rule stands. |
| J-R22 | Jarvis-authored cards draw from a **priority budget** (quota per priority class per window) — priority inflation by the card author degrades the field for the whole fleet and is structurally capped, not policed by review. |
### Passive observability (J4)
| ID | Requirement |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| J-R12 | Jarvis answers "what's the fleet doing" from read-only sources: heartbeat files, `mosaic fleet ps` JSON, backlog card states, CI status. Zero messages to the orchestrator for status. |
| J-R13 | Jarvis proactively surfaces to the user: blocked cards, failed CI on user-ratified missions, approval requests pending, budget advisories. (PDA-friendly phrasing per SOUL.md.) Delivered via the **J6 event/wake router** — per-agent polling is forbidden. |
| J-R23 | **Event/wake router (J6):** one shared, level-triggered reconciler over durable state (heartbeats, card states, approval queue) wakes Jarvis on state _change_ with hysteretic per-condition suppression (wake once, then only on change or declared backoff; suppression survives restarts). Wake turns are **templated** — fixed instruction frame, workspace data only in delimited, provenance-tagged data fields (closes the injection→system-role channel). Idempotent on source event id via the shared consumed-events table. Router is in the health floor; its cost model and latency bound are stated in the J6 card. Reconciles J-R2: an idle Jarvis costs nothing _because waking is event-driven, not poll-driven_. |
### Channel (J5)
| ID | Requirement |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| J-R14 | **Phase 2 (target channel):** Jarvis's conversation lives in a dedicated Matrix room on the self-hosted homeserver via `OrchestratorConnector(matrix)` (K1 = f4 Phase 2). Matrix-first: no Discord channel is created for Jarvis (ratified D1). |
| J-R14a | **Phase 1 (interim channel, ratified):** Jarvis runs on the **tmux/CLI connector** — the f4 Phase-1 default connector. The operator launches the `mosaic-agent@main` tmux session and issues `/remote-control` to grant interactive access; this is the day-one conversation surface. No Discord, no Matrix dependency in Phase 1 (keeps D1 intact and unblocks J1J4 before K1 lands). |
| J-R15 | Multi-platform user reach arrives via mautrix bridges (K2); Jarvis's code path is Matrix-only (from Phase 2 onward). |
## Acceptance criteria
1. AC-NS-8 **(made measurable)**: distinct credential/quota pools for Jarvis and Mos are pinned in the J1 profile; scripted suite of ≥30 interleaved turns under full orchestrator load; TTFT p95 ≤ 1.2× idle baseline with bootstrap CI; orchestrator receives zero conversational traffic.
2. AC-NS-9: a conversationally-agreed mission round-trips (cards → drained → completed → reported by Jarvis) with no chat handoff.
3. Kill the orchestrator mid-conversation: Jarvis conversation is unaffected; Jarvis reports the outage from heartbeat state. (Directly exercises the separate-capacity guarantee.)
4. `!sys`-equivalent admin verbs work in Jarvis's active channel — the tmux/CLI session in Phase 1, the Matrix room in Phase 2 (status/logs/clear/restart of the main agent).
5. **Phase-1 channel:** operator launches the `mosaic-agent@main` tmux session, issues `/remote-control`, and holds a full conversation with Jarvis over CLI with no Matrix/Discord dependency.
6. **Fencing:** start a second `mosaic-agent@main` by hand — it fails to acquire the workspace lease, posts a notice, and stays mute; zero duplicate writes or cards reach the workspace (J-R16).
7. **Phase-1 credential surface:** audit of a phase-1 install finds no external-provider credential readable by the Jarvis runtime (J2a/J2b split holds by construction).
## Non-goals
- Jarvis executing code/infra changes (that is Mos + fleet).
- Horizontal sharding of the main agent (rejected in the Matrix charter: split-brain).
- Per-workspace fleets (post-MVP per ASM-6).
## Open items (for Mos's planner)
- ~~Context hygiene / resume protocol~~ — promoted to J-R18 by the 2026-07-09 debate pass.
- Reconcile the old `apps/api` matrix-bot-sdk workspace bridge with the F4 connector design (one Matrix stack, not two). NOTE (verified 2026-07-09): no matrix dependency remains in `apps/api` on `origin/main` — this item is likely already moot; confirm before K1 build.

View File

@@ -0,0 +1,98 @@
# PRD — Permission Relay · Workstream P
> **Status:** DRAFT for ratification · **Goals:** P1P3
> **Debate pass 2026-07-09:** panel findings folded — see `DEBATE-FINDINGS.md`. The relay was the panel's densest target; the deltas below are normative.
> **Design origin (historical):** `docs/3-architecture/guard-rails-capability-permissions.md` — the "prepare freely, execute with approval" snapshot. **Not present on `origin/main`** (survives only in the stale `/src/mosaic-stack` clone), so its essential model is folded into this PRD below; **this document is the authoritative, self-contained spec for P.**
> **Replaces:** Hermes `permissions_list_open` / `permissions_respond` relay (Hermes exit prerequisite, NS-13)
## Mission
A human-in-the-loop approval mechanism for agent actions: any capability listed as `requires_approval` is prepared by the agent, queued, and executed only after an explicit human approve — from the Matrix room or the webUI. Today this exists only as a bare `applyGuardRails()` method; Hermes currently fills the gap and must be replaced before decommission.
## Design model (folded in — the authoritative spec, since the origin snapshot is off-main)
**Doctrine — "prepare freely, execute with approval":** an agent may plan, draft, and stage any action without friction; only the _committing_ step of a `requires_approval` capability blocks on a human decision.
**Permission levels (least→most):** `read``organize``draft``execute``admin`. A capability grant names a level; `requires_approval` gates the transition into `execute`/`admin` for the capabilities a workspace marks sensitive.
**Grant shape:** `resource:action` (e.g. `email:send`, `git:push_main`, `dns:update`), scoped per workspace and per agent-persona, stored as configuration (profile field) so a user tightens/loosens without a code change.
## Requirements
### Guard-rails engine (P1)
| ID | Requirement |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P-R1 | Capabilities are `resource:action` grants (e.g. `email:send`, `git:push_main`, `dns:update`) with permission levels (read / organize / draft / execute / admin) per the existing design doc. |
| P-R2 | Each integration declares its `requires_approval` list; grants are workspace-scoped and per-agent-persona. |
| P-R3 | Enforcement sits in the gateway/API dispatch path — an agent cannot bypass it by construction; bypass attempts are audited and denied. |
| P-R4 | Policy is configuration (profile field), honoring the configurability pillar: a user can tighten/loosen per capability without code change. |
### Approval queue + chat approvals (P2)
| ID | Requirement |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P-R5 | A pending approval is a durable queue record: requesting agent, capability, human-readable intent summary, prepared payload reference, TTL. |
| P-R6 | Approve/deny from the Matrix room (message action or reply verb); the requesting agent is notified of the outcome and proceeds/aborts. |
| P-R7 | Timeout = deny (fail-closed), with **per-capability TTLs** and distinct terminal states: `denied` / `expired_seen` / `expired_unseen` — agents must not reason about an expiry as a human "no". Deny and timeout leave the system unchanged. |
| P-R8 | Full audit trail: who approved what, when, from which surface (AC-NS-10). |
| P-R9 | The main agent (Jarvis) surfaces pending approvals conversationally (J-R13) but approval authority is the human's — Jarvis never auto-approves. |
### webUI surface (P3)
| ID | Requirement |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------- |
| P-R10 | Pending-approval queue view in `apps/web` with one-click approve/deny, filterable per workspace/agent (depends W3 dashboard shell). |
## Debate-accepted deltas (2026-07-09) — normative
### State machine & delivery (extends P-R5P-R7)
| ID | Requirement |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P-R11 | Approval lifecycle is a **CAS state machine on one durable row**: `pending → approved \| denied \| expired_seen \| expired_unseen`, all terminal. Concurrent surfaces (Matrix, webUI, CLI, TTL reaper) contend via compare-and-swap; exactly one wins. |
| P-R12 | The **prepared payload is persisted in the record at request time** — never a reference to requesting-agent process state (the agent may be dead when approval lands). Outcome delivery is **poll/ack**, not push-only; `approved_unexecuted > N min` raises an alarm. |
| P-R13 | A **consumed-event dedupe table** (shared substrate with bridged-message and wake-turn dedupe — built once, three consumers) makes approval consumption idempotent under Matrix at-least-once replay. Dedupe retention is **checkpoint-coupled**: events older than the durable sync token are dropped before lookup, so pruning never reopens the replay window. |
| P-R14 | Each capability declares `retry: safe \| at-most-once`; a prepare→execute **staleness bound** rejects execution of stale payloads. Re-surfacing an expired item **re-prepares** (new linked record with a rendered machine diff against the original) — never re-queues a stale payload. |
| P-R15 | **CLI approval surface** (`mosaic approvals list\|approve\|deny`) ships **with P2** as must-have — the newest infra (Matrix) is never the only approval path. Per-request delivered/seen tracking; TTL/2 escalation via a second path. |
### Identity & policy (closes Codex #2/#7)
| ID | Requirement |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| P-R16 | **Principal resolution:** every inbound approve/deny maps to a product principal via immutable Matrix user ID + bridge provenance + workspace membership. Unlinked identities and bridged puppets without an account link converse read-only and **cannot approve, butt-in, or trigger external writes**. Fixtures: bridged puppet, renamed user, invited non-admin, removed-member-with-lagging-room-membership — all deny + audit with reason. |
| P-R17 | **Policy snapshot:** every prepared action and card stores an immutable snapshot (profile id/version, grants evaluated). Execution **revalidates against current policy** or fails with explicit `policy_changed`. Profile changes are audited with schema validation + dry-run impact report. Delegated work (subagent, Jarvis-authored card) executes under the **intersection** of originator and executor grants. |
### Credential boundary (makes P-R3 true)
| ID | Requirement |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P-R18 | Raw provider credentials are held **exclusively by the gateway**; agents receive scoped capability tokens; the gateway injects secrets server-side. If the agent runtime can read the provider secret, P-R3 is decoration. |
| P-R19 | P1 delivers a **fleet-host credential inventory**: every host-resident credential (SSH keys, kubeconfigs, tool tokens) classified `moved-behind-gateway` or `explicitly-exempt(reason, owner, expiry)`. Enforced by a **continuous scheduled scan** of agent-readable paths (alert on unclassified), registered in the health floor. The clean-host install AC passes with an **empty exemption list**. Break-glass is one standing exempt row (owner: operator; audited post-hoc). |
### Gating defaults & load (closes default-open + human-overdraw)
| ID | Requirement |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P-R20 | **Default-closed inversion:** `execute`/`admin` capabilities on external integrations require approval **unless** workspace-allowlisted. Third state `unclassified`: a capability with no classification **rejects** at the gate. Classification happens at integration-version activation, scoped to the **capability, not the version** — a security patch bundling one new capability ships same-day; only the new capability rejects until classified. Manifest-less capabilities reject + integrity alert. |
| P-R21 | Rate limits + `max_pending_per_agent_per_capability`; queue depth and human decision latency are exported metrics with thresholds (rubber-stamping guard). Capabilities carry a `deferrable` flag; a declared **away state** pauses deferrable TTLs, batches pings, and **suppresses preparation** of non-deferrable requests (suppressed list swept, priority-ordered and paginated, on return). |
| P-R22 | Approval summaries render **machine-extracted payload facts unconditionally** (recipient, amount, target host — no tunable similarity threshold); agent prose is secondary. **Canary approvals** are gateway-generated, short-circuited at the gateway (never executable), immediately disclosed after decision, timing seeded outside agent-readable stores; the gated criterion is machine-flag correctness — human catch rate is reported with binomial CI, non-blocking. |
| P-R23 | Every durable table introduced by P declares a **retention class**, linted in CI; pruning jobs are health-floor registered. Minimal mandatory audit envelope: `ts, workspace, actor, correlation_id, stack_version, schema_version` — payloads are typed free-form with pinned, checked-in queries (debate §3.2 disposition). |
## Acceptance criteria
1. AC-NS-10 end-to-end: a `requires_approval` action executes only post-approve; deny/timeout paths verified unchanged + audited.
2. Approval round-trip from a phone Matrix client (Element) in under 3 taps — **and** the same round-trip via `mosaic approvals` CLI with the homeserver stopped.
3. With Hermes stopped, permission flow fully served by Mosaic (feeds AC-NS-11).
4. Crash-consistency: kill the gateway at each named barrier (`before_persist`, `after_approve_before_execute`, `after_execute_before_ack`); zero double-executions, zero lost approvals across the suite.
5. All four principal-resolution fixtures (P-R16) deny + audit; policy-change race (P-R17) fails `policy_changed`, never executes under stale grants.
## Non-goals
- Automated quality gates (coordinator/CI approvals) — different system, already exists.
- Fine-grained LLM output moderation — out of scope; this governs _actions_.
## Assumptions
- ASSUMPTION: the durable queue rides the native Postgres storage service (same substrate as the backlog), not a new datastore.
- ASSUMPTION: routine delivery operations already hard-gated as no-confirmation (push/merge per Mosaic contract) are NOT routed through the relay — the relay is for `requires_approval` capabilities only, so it does not reintroduce routine confirmation prompts.

View File

@@ -0,0 +1,64 @@
# PRD — webUI Fleet Control · Workstream W (realizes F6)
> **Status:** DRAFT for ratification · **Goals:** W1W3 · **Upstream anchor:** `PRD-fleet-suite.md` Phase F6 ("webUI hooks — stable JSON contract + terminate/attach(butt-in) surface")
> Confirmed gap: zero xterm/pty/tmux code in `apps/web` on either the old snapshot or `origin/main`.
> **Debate pass 2026-07-09:** panel findings folded — see `DEBATE-FINDINGS.md`.
## Mission
The user can pop in on **any** agentic tmux session from the web, and get a full top-down view of the system — fleet roster, health, work in flight, spend — without touching a terminal. This is the product surface for "user has ability to pop in on any agent session; full top-down view available."
## Requirements
### Attach service (W1)
| ID | Requirement |
| ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| W-R1 | A gateway service exposes per-agent session streams over WebSocket: **watch** (read-only pane view, cannot type) and **butt-in** (interactive takeover), mirroring the existing CLI verbs `mosaic agent watch/attach`. |
| W-R2 | Authz is workspace-scoped through the product auth stack (BetterAuth/Authentik); watch and butt-in are separate grants; butt-in may be `requires_approval` per workspace policy (workstream P). |
| W-R3 | Every attach (watch or butt-in) is audited: who, which agent, when, duration. |
| W-R4 | Butt-in visibly flags the session to the agent runtime and other viewers (no silent takeover). |
| W-R5 | Contract is stable JSON + streaming frames per F6's "stable JSON contract" requirement, so TUI/CLI and webUI share it. |
### Web terminal (W2)
| ID | Requirement |
| ---- | ----------------------------------------------------------------------------------------------------------- |
| W-R6 | xterm.js view in `apps/web` wired to W1: session list → click → live pane; toggle watch↔butt-in per grants. |
| W-R7 | Reconnect-safe (network blips resume the stream), mobile-usable read-only view. |
### Top-down dashboard (W3)
| ID | Requirement |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| W-R8 | Fleet dashboard: roster with per-agent state (systemd + tmux + heartbeat join, as `fleet ps` provides), current card/task, last activity, drift/boot-enable warnings. |
| W-R9 | Work-in-flight view: backlog cards by state with depends_on DAG rendering; advisory spend per card (NS-2/NS-5). |
| W-R10 | Operator controls: PAUSE kill-switch (NS-8), per-agent terminate (killswitch service), queue pause/resume — each gated + audited; destructive controls confirm. |
| W-R11 | Existing widget framework (`AgentStatusWidget`, `OrchestratorEventsWidget`, SSE proxy routes) is the starting point, upgraded to the fleet contract rather than rebuilt. |
## Debate-accepted deltas (2026-07-09) — normative
| ID | Requirement |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| W-R12 | **Butt-in is an exclusive lease** with explicit, visible takeover: at most one interactive holder per session at any instant; a second client must take the lease and both parties see the transfer. Input frames are sequenced and deduped so a reconnect never double-sends; a heartbeat/idle timeout closes both the lease and its audit span — audit spans always terminate (extends W-R1/W-R3/W-R4). |
| W-R13 | **Structured control plane:** PAUSE, terminate, restart, approve/deny, and queue operations are typed API verbs with RBAC and audit — never bytes typed into a tmux pane. Raw terminal input via butt-in is **rescue-only** and a separately-grantable permission from the control verbs. |
| W-R14 | **Break-glass doctrine (documented, not prevented):** SSH + `tmux attach` on the fleet host bypasses W1 and P **by design**; it is inventoried under P-R19, audited post-hoc from host logs, and never treated as a product path. PAUSE additionally has an **on-host file/CLI actuator** so a dead gateway can never lock the operator out of the control that fixes the gateway. |
| W-R15 | W3 ships the **trial metric panels** as workspace-scoped product views over product storage: canary machine-flag correctness, approval decision latency, card priority distribution, wake→ack rate, agent-authored memory retrieval fraction, human interaction load, fixed-input probe stability. The X-R9 trial evidence pack cites these panels; until W3 exists, day-1 emission targets MALS (L1). Workspace isolation fixture: workspace-2 admin sees zero workspace-1 rows. |
## Acceptance criteria
1. From a browser (desktop + phone), the user watches a live coder-agent pane read-only, then butt-ins with the right grant, types a message, detaches; agent session continues; audit log shows both.
2. Dashboard reflects an agent crash within one heartbeat interval; PAUSE flip halts dispatch within one tick (AC-NS-5) from the UI.
3. A user without butt-in grant can watch but cannot type (enforced server-side).
4. Two clients contend for butt-in: exactly one holds the lease at any instant, the takeover is visible to both, and after a forced reconnect the input stream shows zero duplicated or interleaved frames (W-R12).
5. With the gateway stopped, the operator PAUSEs the fleet via the on-host actuator; the bypass appears in the post-hoc audit (W-R14).
## Non-goals
- Replacing tmux as the session substrate (tmux remains the transport; web is a view).
- Cross-host federation of the dashboard (rides the existing federation workstream later, per upstream note "Phase 5 rides federation").
## Assumptions
- ASSUMPTION: pty bridging terminates at the gateway on the fleet host (web1), not in `apps/web`; Next.js only speaks WebSocket to the gateway.
- ASSUMPTION: the jarvis-brain dashboard's node-pty/xterm work (`dashboard/server/terminal.ts`) serves as reference implementation only; code is not ported wholesale into the multi-tenant product without the authz layer above.

View File

@@ -0,0 +1,62 @@
# Mosaic Platform PRD — Jarvis HMI + Hermes Decommission (DRAFT for ratification)
**Date:** 2026-07-09 · **Author:** proto-Jarvis session with Jason · **Status:** Decisions D1D12 **ratified** (fixed inputs); implementing PRD structure **DRAFT** — refined 2026-07-09 post-review, then stress-tested by a 9-persona × 2-round debate panel the same day; all 24 panel findings dispositioned and folded (see `DEBATE-FINDINGS.md`; one item **OPEN — Jason**: gate superstructure, §3.1)
**Target home:** `mosaicstack/stack``docs/fleet/` (NORTH_STAR.yaml additions + per-phase PRDs)
> **For the homelab orchestrator:** D1D12 below are settled constraints, not open questions — do not reopen them. What is under review is only the _implementation_ (workstreams, goals, sequencing) that realizes them.
> **Execution:** hand to the **homelab orchestrator** as orchestrated missions once ratified (D12). Land in `docs/fleet/` from `origin/main` — the `/src/mosaic-stack` clone on web1 is 5 months stale and must not be the base. The USC/web1 environment is out of scope for the trial; its cutover (workstream X applied to web1's Hermes + mos-claude) is a post-trial phase.
## Ratified decisions (Jason, 2026-07-09)
| # | Decision |
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| D1 | Jarvis conversation channel is **Matrix-first** — no #jarvis Discord channel is ever created. |
| D2 | Mosaic absorbs **all four** Hermes functions before decommission: messaging bridge, Kanban/task board, permission relay, multi-platform reach. |
| D3 | Task handoff: **native Mosaic Backlog is the record; Gitea/GitHub/local-kanban attach as bidirectional sync adapters** (upholds ASM-1). |
| D4 | Jarvis executes **PA ops directly** (email, calendar, tasks, knowledge, tickets, research); all code/infra/fleet work is delegated to Mos via the backlog. |
| D5 | Mosaic Stack is a **product from day one** — multi-user, Authentik tenancy, per-workspace isolation. |
| D6 | Multi-platform reach via **Matrix + mautrix bridges** (telegram/signal/whatsapp/slack/discord); agents only ever speak Matrix. |
| D7 | webUI builds on the existing `mosaicstack/stack` monorepo (`apps/web`), realizing the already-scoped F6 phase. |
| D8 | **PRD first, then Mos runs it** as orchestrated missions. |
| D9 | jarvis-brain flat-file data **migrates into the product as tenant #1** (workspace = Jason); brain.py/flat files retire after cutover. |
| D10 | PRD form: **extend NORTH_STAR.yaml + per-phase docs in docs/fleet/** (NS-1 compliant). |
| D11 | Jarvis runs **Opus**; Fable stays exclusive to Mos per the standing cost directive. Model tier is a persona/profile field. |
| D12 | **Trial in the homelab** (the proper mosaic-fleet deployment, built by the homelab agents from `origin/main`), NOT at USC. The USC/web1 environment runs the primitive-era implementation (`mos-claude.service`, Hermes, jarvis-brain boards) and adopts only after the homelab trial validates. Jason relays this PRD to the homelab agent for implementation. |
## Artifacts in this draft
| File | Content |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `north-star-additions.yaml` | Proposed NORTH_STAR.yaml merge: NS-10…NS-14, workstreams J/K/W/P/Q/X + **M** (memory subsystem) + **L** (logging/telemetry), goal cards with DAG |
| `PRD-jarvis-main-agent.md` | Workstream J — the HMI main agent |
| `PRD-permission-relay.md` | Workstream P — human-in-the-loop approvals |
| `PRD-webui-fleet-control.md` | Workstream W — tmux pop-in + top-down view (realizes F6) |
| `PRD-backlog-providers.md` | Workstream Q — provider sync adapters |
| `PRD-hermes-decommission.md` | Workstream X — parity checklist, tenant-1 migration, cutover |
| `DEBATE-FINDINGS.md` | 2026-07-09 debate pass — all 24 findings dispositioned, open-disagreement judgment calls, process rules |
Workstreams **M** (M1 enhanced memory subsystem) and **L** (L1 restore MALS · L2 Mosaic-native log ingestion · L3 anonymous agentic telemetry, NS-14) carry no standalone PRD doc: M1's scope is defined by its consumers (X-R7 retrieval eval, J memory rules) and L's by the MALS lineage (`~/src/mals`, infrastructure #135); both live as goal cards in the YAML.
Workstream K (Matrix connector + mautrix bridges) intentionally has no new PRD doc: it extends the existing `docs/fleet/f4-matrix-connector.md`; its deltas are captured as K-goals in the YAML additions and referenced from the J/P/X PRDs.
## Relationship to existing upstream work
- Fleet CLI, persona library, system-type profiles (H1H4), supervisor/dispatch (B), native backlog (A): **already exist or in flight upstream — not re-specified here.**
- `f4-matrix-connector.md`: K1 = its Phase 2 implementation (verified present on `origin/main`; Phase 1 already ships the **tmux-default connector** that serves the P1 CLI channel per J-R14a). K2 (mautrix bridges) is additive infra.
- F6 (webUI hooks) in `PRD-fleet-suite.md`: realized by workstream W (verified present on `origin/main`).
- `docs/3-architecture/guard-rails-capability-permissions.md`: original design snapshot for workstream P — **not present on `origin/main`** (lives only in the stale `/src/mosaic-stack` clone). Its essential model is therefore folded into `PRD-permission-relay.md`, which is now the **self-contained authoritative spec** for P; the old path is cited as historical origin only.
## Gate Zero — pre-ratification checklist (debate P0 #3/#4, §3.7)
Ratification does not proceed on presumption. Before D-level sign-off, run and record:
1. **Upstream-artifact audit.** Every artifact this PRD load-bears on is verified `present @ <SHA>` on `origin/main` or marked **MISSING → goal card**. Presumed-missing rows already carded by this draft: **M1** (enhanced memory subsystem — nothing on main provides vector DB + memory service today), **J6** (event/wake router), **K3** (Matrix push pipeline + homeserver ops). If an audit finds one of these actually exists, retire the card and pin the SHA; if it finds _another_ gap, card it — no silent presumption in either direction.
2. **Hermes traffic audit** (§3.7): per-platform bridge traffic + per-MCP-tool call counts over a trailing window. Platforms with live traffic become the must-have K2 subset gating X3; zero-traffic platforms become post-trial nice-to-haves. Feeds the X-R1 parity checklist and its low-n drill list.
3. **Sandbox dispatch-test** of the DAG: load `north-star-additions.yaml` into a sandbox backlog and verify the dispatcher's actual claim order respects every `depends_on` edge (phases are documentation; edges are law).
## Process rules adopted from the debate pass (normative for this PRD's execution)
- **Conflict register:** a discovered contradiction between spec documents **blocks the affected requirement** (not the whole mission) until a register entry records the resolution. "Alert, don't auto-resolve" is promoted from data conflicts to spec conflicts. A CI lint greps for register references in amended docs.
- **DoD line on every goal card:** each card states its definition-of-done additions — runbook updated, health-floor alert registered, AGENTS.md touched — so operational debt can't silently accrue card-by-card.
- **Silent-roster rule:** in any panel/review round, a member's silence records their open findings as **open items, never consensus** (instance: Codex's unanswered principal-resolution and policy-evaluation-time findings were folded as P-R16/P-R17, explicitly not consensus-resolved).
- **Immutable spec + typed amendments** (J-R20) applies to these PRD docs themselves post-ratification: changes arrive as amendments with a revision counter, and sign-offs pin the revision.

View File

@@ -0,0 +1,279 @@
# Proposed additions to docs/fleet/NORTH_STAR.yaml — DRAFT (Jason ratification pending, 2026-07-09)
#
# Merge these entries into the existing NORTH_STAR.yaml sections, then regenerate
# NORTH_STAR.md via renderNorthStarMarkdown (packages/mosaic/src/commands/fleet.ts).
# Ids chosen to avoid collision with existing workstreams AH and goals.
standing_objectives:
- id: NS-10
text: >-
Every Mosaic workspace runs exactly one always-on HMI main agent (default
alias "Jarvis", unit mosaic-agent@main) that owns all human conversation
and user-level personal-assistant work (ideas, schedule, email, tasks,
knowledge) and delegates engineering/research/ops missions to the
orchestrator as Mosaic Backlog cards. It is a Level-0 orchestrator:
it accomplishes work through delegation and subagents, never by executing
coding/infra tasks itself, and it runs on model capacity separate from the
orchestrator so its conversational latency is isolated from fleet load. The
main agent never executes fleet work itself and never interrupts the
orchestrator for status. Exactly-one-per-workspace is enforced by a
workspace lease (J-R16), not by convention.
- id: NS-11
text: >-
Irreversible or externally-visible agent actions pass a human-in-the-loop
permission relay (approve/deny from chat or webUI) governed by
per-capability guard rails; prepare freely, execute with approval.
- id: NS-12
text: >-
The Mosaic Backlog remains the sole backlog of record; external providers
(Gitea, GitHub, local kanban, …) attach as bidirectional sync adapters,
never as the record.
- id: NS-13
text: >-
Hermes is fully decommissioned once Mosaic reaches verified parity on
transport (Matrix connector), task board (native backlog + webUI),
permission relay, and multi-platform reach (mautrix bridges).
- id: NS-14
text: >-
Mosaic Stack accepts inbound structured error/log reporting from every
agent and install (MALS-lineage logging service); agentic-efficiency
telemetry is optional, opt-in, and anonymous by construction — no IP or
PII is captured or derivable from the ingestion path.
success_criteria:
- id: AC-NS-8
text: >-
A user converses with the main agent in its channel while the
orchestrator is under full load; because the main agent runs on separate
model capacity (distinct credential/quota pools pinned in the J1
profile), its response latency is unaffected and the orchestrator
receives zero conversational traffic. Measured, not vibes: scripted
suite, >=30 interleaved turns, TTFT p95 <= 1.2x idle baseline with
bootstrap CI.
- id: AC-NS-9
text: >-
A mission agreed in the main-agent conversation appears as a backlog card
set with acceptance criteria, is drained by the orchestrator without
chat-level handoff, and its completion is reported back to the user by the
main agent from board/heartbeat state alone.
- id: AC-NS-10
text: >-
An action listed as requires_approval executes only after an explicit
human approve from Matrix or webUI; deny and timeout paths leave the
system unchanged and audited.
- id: AC-NS-11
text: >-
With Hermes stopped, no fleet or main-agent capability regresses
(transport, board, approvals, multi-platform reach all served by Mosaic).
workstreams:
- id: J
title: HMI main agent ("Jarvis") — persona, PA toolchain, delegation contract
- id: K
title: Connectors & multi-platform reach — F4 Matrix implementation + mautrix bridges
- id: W
title: webUI fleet control — tmux pop-in, top-down view (realizes F6)
- id: P
title: Permission relay — capability guard rails + human approval queue
- id: Q
title: Backlog provider sync adapters — Gitea/GitHub/local kanban
- id: X
title: Hermes decommission & tenant-1 migration
- id: M
title: Enhanced memory subsystem — vector DB + memory service + provenance/tombstones (Gate Zero MISSING artifact)
- id: L
title: Logging & telemetry — MALS-lineage inbound error reporting + optional anonymous efficiency telemetry
goals:
# J — HMI main agent
- id: J1
title: Main-agent persona + profile — instantiate personal-assistant system type as mosaic-agent@main (alias Jarvis), model tier a profile field (Opus default)
phase: 1
priority: must-have
depends_on: [H2]
# J2 split (debate P0 #2): phase-1 Jarvis must not hold an external-write
# credential path before the permission relay (P2) exists — NS-11 by DAG.
- id: J2a
title: PA toolchain (workspace-internal) — tasks, events, knowledge, ideas executed directly against the product API in the user's workspace; no external credentials provisioned
phase: 1
priority: must-have
depends_on: [J1]
- id: J2b
title: PA toolchain (external integrations) — email, external calendars, helpdesk via workspace-scoped integrations; credentials gateway-held; requires_approval routes through the relay
phase: 2
priority: must-have
depends_on: [J2a, P2]
- id: J3
title: Delegation contract — main agent authors mission cards (goal, acceptance criteria, budget advisory) onto the backlog; orchestrator drains; no chat-level handoff
phase: 1
priority: must-have
depends_on: [J1, A3a]
- id: J4
title: Passive fleet observability — main agent answers status from heartbeats, fleet ps JSON, and board state; zero orchestrator interrupts
phase: 1
priority: must-have
depends_on: [J1, B1]
- id: J5
title: Main-agent Matrix room via OrchestratorConnector(matrix)
phase: 2
priority: must-have
depends_on: [J1, K1]
- id: J6
title: Shared event/wake router — level-triggered reconciler over durable state (heartbeats, cards, approvals) with hysteretic per-condition suppression; templated provenance-tagged wake turns; per-agent polling forbidden (Gate Zero MISSING artifact; J-R13 depends on it)
phase: 1
priority: must-have
depends_on: [J1]
# K — connectors & reach (extends f4-matrix-connector.md)
- id: K1
title: Matrix connector implementation — CS-API client factory per f4 Phase 2, self-hosted homeserver
phase: 2
priority: must-have
depends_on: []
# K2 scope (debate §3.7): a pre-ratification Hermes traffic audit narrows the
# must-have bridge subset to platforms with live traffic; only that subset
# gates X3 — remaining bridges are genuinely optional post-X3.
- id: K2
title: mautrix bridge deployment (telegram/signal/whatsapp/slack/discord) as GitOps-managed infra; agents speak only Matrix; must-have subset = platforms carrying live Hermes traffic per Gate Zero audit
phase: 3
priority: should-have
depends_on: [K1]
- id: K3
title: Push pipeline (Sygnal or equivalent) + homeserver ops — monitored delivery checks, health-floor registration, rehearsed backup/restore (Gate Zero MISSING artifact; P2 phone-approval AC depends on it)
phase: 2
priority: must-have
depends_on: [K1]
# W — webUI fleet control (realizes F6)
- id: W1
title: Gateway pty/tmux attach service — read-only watch and interactive butt-in verbs, workspace-scoped authz, audit log
phase: 2
priority: must-have
depends_on: []
- id: W2
title: xterm.js session view in apps/web wired to W1 (watch + butt-in)
phase: 2
priority: must-have
depends_on: [W1]
- id: W3
title: Top-down fleet dashboard — roster, heartbeats, cards in flight, advisory spend, PAUSE control
phase: 2
priority: must-have
depends_on: [B1]
# P — permission relay
- id: P1
title: Capability guard-rails engine — resource:action grants, permission levels, requires_approval list per integration
phase: 2
priority: must-have
depends_on: []
- id: P2
title: Approval queue + approve/deny from the Matrix room (timeout = deny; full audit)
phase: 2
priority: must-have
depends_on: [P1, K1]
- id: P3
title: Approval surface in webUI (pending queue, one-click approve/deny)
phase: 3
priority: should-have
depends_on: [P1, W3]
# Q — backlog provider sync adapters
- id: Q1
title: Provider adapter interface + Gitea adapter (bidirectional card↔issue sync; native backlog stays record)
phase: 2
priority: must-have
depends_on: [A2]
- id: Q2
title: GitHub adapter
phase: 3
priority: should-have
depends_on: [Q1]
- id: Q3
title: Local kanban surface — webUI board view over the native backlog (no external provider required)
phase: 2
priority: should-have
depends_on: [A2, W3]
# X — Hermes decommission & tenant-1 migration
- id: X1
title: Hermes parity checklist + cutover plan (transport, board, approvals, reach) with rollback
phase: 3
priority: must-have
depends_on: [K1, P2, Q1]
# jarvis-brain is the P0 (prototype) Mosaic Stack; migration targets the proper
# stack storage layer. Storage authority (debate P0 #1): Postgres is
# authoritative for ALL product entities; the flat-file backend is a derived,
# regenerated, READ-ONLY projection — never a second writable store.
- id: X2
title: 'Tenant-1 migration — P0 (jarvis-brain) into the proper Mosaic Stack: (a) PA data (projects/tasks/events/knowledge) into the Jason workspace, (b) agent memory/runbooks into the enhanced memory subsystem M1 (kept live, not frozen); jarvis-brain retires read-only only after both are verified'
phase: 3
priority: must-have
depends_on: [J2a, P2, M1]
- id: X3
title: Hermes decommission — stop and remove Hermes services after AC-NS-11 verified; pre-stop evidence snapshot (logs/config/callback inventory + checksum) is a machine gate
phase: 4
priority: must-have
depends_on: [X1, X2, K2]
# M — enhanced memory subsystem (Gate Zero: cited by X2(b) but not built by
# any prior workstream — this card closes that gap)
- id: M1
title: Enhanced memory subsystem — vector DB + memory service with mandatory provenance (human/agent/external), source-grounded retrieval preference, tombstones + re-embedding on source update, normative/parametric corpus split
phase: 2
priority: must-have
depends_on: []
# L — logging & telemetry (Jason 2026-07-09; NS-14). MALS (~/src/mals) is the
# P0 of this capability; restoring it is infra work tracked as
# infrastructure#135 (k3s migration landed without an Ingress).
- id: L1
title: MALS restored & exposed — k3s Ingress for mals.mosaicstack.dev, health verified, authenticated write smoke-tested (infrastructure#135); trial day-1 metric emission targets MALS until W3 panels exist
phase: 1
priority: must-have
depends_on: []
- id: L2
title: Mosaic-native log ingestion — gateway/API endpoint for structured inbound error reporting from agents and installs (MALS-compatible schema; levels, categories, trace ids), workspace-scoped keys
phase: 2
priority: must-have
depends_on: [L1]
- id: L3
title: Anonymous agentic-efficiency telemetry — opt-in, aggregate-only, no IP/PII captured or derivable at ingestion (NS-14); feeds W3 trend panels
phase: 3
priority: should-have
depends_on: [L2, W3]
assumptions:
- id: ASM-5
vetoable: true
text: >-
The main agent initially runs on the homelab fleet host alongside the
orchestrator under mosaic-agent@main.service; host placement is a config
field, not a code assumption. Host co-location does NOT imply shared
inference: Jarvis (Opus) and Mos (Fable) hold separate model capacity/quota
so orchestrator load cannot degrade conversational latency (AC-NS-8).
- id: ASM-6
vetoable: true
text: >-
Product multi-tenancy at MVP means self-hosted installs with multiple
workspaces per install (Authentik OIDC); per-tenant isolated FLEETS
(agents per workspace) are post-MVP.
- id: ASM-7
vetoable: true
text: >-
mautrix bridges are deployed as infrastructure (GitOps), not as Mosaic
application code; Mosaic's only conversational protocol is Matrix.
- id: ASM-8
vetoable: true
text: >-
During migration (before X3), Hermes remains running untouched; no
Hermes-dependent capability is removed until its Mosaic replacement is
verified in production.
- id: ASM-9
vetoable: true
text: >-
The trial environment is the homelab fleet deployment (D12). Environments
running the primitive-era implementation (USC/web1: mos-claude.service,
Hermes, jarvis-brain boards) are untouched during the trial; workstream X
executes there as a post-trial adoption phase, environment by
environment.

View File

@@ -87,6 +87,10 @@ message crosses the wire as base64 (`-b`) to avoid all shell-quoting hazards.
- `agent-send.sh` — inter-agent wrapper (preamble + local/remote dispatch).
- `send-message.sh` — low-level reliable single-pane submitter (`-b` base64 input).
- `auto-submit-drafts.sh` — watchdog that flushes stable unsubmitted prompt
drafts on a coordinator pane (default target `mos-claude`); run it as a
long-lived process alongside the coordinator session.
- `agent-send.test.sh` — regression + grammar lock for `agent-send.sh`.
- `test-send-message-socket.sh` — smoke test for named-socket isolation.
## Distribution

View File

@@ -0,0 +1,80 @@
#!/usr/bin/env bash
# auto-submit-drafts.sh — watchdog for Claude Code panes that receive channel
# messages but leave them as unsubmitted prompt drafts. Intended for Mos only.
set -uo pipefail
TARGET="${1:-mos-claude}"
INTERVAL="${INTERVAL:-2}"
STABLE_SECONDS="${STABLE_SECONDS:-4}"
LOG_PREFIX="[auto-submit-drafts:$TARGET]"
last_prompt=""
first_seen=0
prompt_text() {
tmux capture-pane -t "$TARGET" -p 2>/dev/null | python3 -c '
import sys, re
lines = sys.stdin.read().splitlines()
idx = None
for i in range(len(lines)-1, -1, -1):
if "" in lines[i]:
idx = i
break
if idx is None:
raise SystemExit
parts = []
after = lines[idx].split("", 1)[1]
parts.append(after)
for line in lines[idx+1:]:
# Stop at Claude Code separator/border lines.
if "─" in line or "╰" in line or "╭" in line:
break
s = line.replace("\u00a0", " ")
s = re.sub(r"[\x00-\x1f\x7f]", "", s).strip()
if s:
parts.append(s)
text = " ".join(parts).replace("\u00a0", " ")
text = re.sub(r"[\x00-\x1f\x7f]", "", text).strip()
print(text)
'
}
while true; do
if ! tmux has-session -t "$TARGET" 2>/dev/null; then
echo "$LOG_PREFIX target missing; waiting" >&2
sleep "$INTERVAL"
last_prompt=""
first_seen=0
continue
fi
current="$(prompt_text || true)"
now="$(date +%s)"
if [[ -z "$current" ]]; then
last_prompt=""
first_seen=0
sleep "$INTERVAL"
continue
fi
if [[ "$current" != "$last_prompt" ]]; then
last_prompt="$current"
first_seen="$now"
sleep "$INTERVAL"
continue
fi
age=$(( now - first_seen ))
if (( age >= STABLE_SECONDS )); then
echo "$LOG_PREFIX submitting stable draft after ${age}s: ${current:0:120}" >&2
tmux send-keys -t "$TARGET" C-j
sleep 0.8
tmux send-keys -t "$TARGET" C-m
sleep 2
last_prompt=""
first_seen=0
else
sleep "$INTERVAL"
fi
done

View File

@@ -77,10 +77,20 @@ snippet=$(printf '%s' "$MSG" | tr '\n' ' ' | tr -s ' ' | sed 's/[^[:print:]]//g'
# 1) Paste the body as a bracketed paste so multi-line content does not submit
# line-by-line. load-buffer/paste-buffer is far safer than `send-keys -l`.
printf '%s' "$MSG" | "${tmux_cmd[@]}" load-buffer -b __mosaic_send -
# Buffer name MUST be unique per invocation: concurrent senders on the shared
# tmux server race a fixed name (load overwrites load, -d deletes underneath),
# cross-delivering or dropping messages — bit the fleet on the 2026-07-09
# simultaneous restart (briefs swapped between sessions).
BUF="__mosaic_send_$$_$(date +%s%N)"
printf '%s' "$MSG" | "${tmux_cmd[@]}" load-buffer -b "$BUF" -
# -p = bracketed paste when the client supports it; fall back if not.
"${tmux_cmd[@]}" paste-buffer -d -p -b __mosaic_send -t "$EFFECTIVE_TARGET" 2>/dev/null \
|| "${tmux_cmd[@]}" paste-buffer -d -b __mosaic_send -t "$EFFECTIVE_TARGET"
"${tmux_cmd[@]}" paste-buffer -d -p -b "$BUF" -t "$EFFECTIVE_TARGET" 2>/dev/null \
|| "${tmux_cmd[@]}" paste-buffer -d -b "$BUF" -t "$EFFECTIVE_TARGET" \
|| "${tmux_cmd[@]}" delete-buffer -b "$BUF" 2>/dev/null
# ^ -d deletes the buffer only on a SUCCESSFUL paste; if both attempts fail
# (e.g. the target vanished since the liveness check), delete explicitly —
# named buffers are exempt from tmux's buffer-limit eviction, so orphans
# would otherwise accumulate forever.
sleep 0.5
# 2) Submit, then verify; flush with another Enter if it is still a draft.

View File

@@ -47,4 +47,32 @@ if capture_default | grep -qF "agent socket hello"; then
fail "agent-send.sh leaked named-socket message to default tmux server"
fi
# Concurrency: parallel senders on one server must not cross-deliver or drop.
# Locks the unique-per-invocation paste buffer (a fixed buffer name raced:
# load overwrote load, -d deleted underneath — messages swapped between panes).
CONC_N=5
for i in $(seq 1 "$CONC_N"); do
tmux -L "$SOCKET" new-session -d -s "conc-$i" -c "$TMPDIR" 'bash --noprofile --norc -i'
done
pids=()
for i in $(seq 1 "$CONC_N"); do
"$SEND_MESSAGE" -L "$SOCKET" -t "=conc-$i" -m "CONCPAYLOAD-${i}-END" >/dev/null &
pids+=($!)
done
for pid in "${pids[@]}"; do
wait "$pid" || fail "concurrent send-message.sh invocation exited non-zero"
done
sleep 0.2
for i in $(seq 1 "$CONC_N"); do
pane=$(tmux -L "$SOCKET" capture-pane -t "=conc-$i:0.0" -p)
printf '%s' "$pane" | grep -qF "CONCPAYLOAD-${i}-END" \
|| fail "concurrent send dropped payload for pane conc-$i"
for j in $(seq 1 "$CONC_N"); do
[ "$j" = "$i" ] && continue
if printf '%s' "$pane" | grep -qF "CONCPAYLOAD-${j}-END"; then
fail "concurrent send cross-delivered payload $j to pane conc-$i"
fi
done
done
echo "ok - named tmux socket send tools"