Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ca192baaa | ||
|
|
dc6db5f14e | ||
|
|
24462f460e | ||
|
|
a480ee83dc | ||
|
|
fd43ed5420 | ||
|
|
1d84bc3f3d | ||
|
|
6306914965 | ||
|
|
af43a7a63e | ||
|
|
ca97b885b0 | ||
|
|
6db0bead44 | ||
|
|
c671290d77 | ||
|
|
6a9b2cf6c1 | ||
|
|
6bd93a621d | ||
|
|
e9485c3d96 | ||
|
|
d2f0846dcc | ||
|
|
4f22a58041 | ||
|
|
9b6869fab7 | ||
|
|
20ad89c86b | ||
|
|
a55d1a1812 | ||
|
|
9abd7e386f | ||
|
|
9af456c240 | ||
|
|
cb9a0d1642 | ||
|
|
420507da77 | ||
|
|
2508f0aa99 | ||
|
|
d339e8fd21 | ||
|
|
018d96a412 | ||
|
|
b01950e92f | ||
|
|
1bdeed62eb | ||
|
|
840c2b0d96 | ||
|
|
95d5cb32d4 | ||
|
|
d5f3fae896 | ||
|
|
1556982dbc | ||
|
|
1a822493ba | ||
|
|
d2eeb64433 | ||
|
|
5e58597dbe | ||
|
|
fe4fa20309 | ||
|
|
5e93ef70bd | ||
|
|
3884f2de4d | ||
|
|
a3c50d91ca | ||
|
|
2fd102e6af | ||
|
|
efb3c3a10c | ||
|
|
d4d32a80b2 | ||
|
|
c703cc50eb | ||
|
|
3d2b712355 | ||
|
|
245e0c427d | ||
|
|
ff45f7b5d0 | ||
|
|
64350892e7 | ||
|
|
6e9df3c640 | ||
|
|
f5ba042dfa | ||
|
|
7c7dab3898 | ||
|
|
d92de53399 | ||
|
|
d7e303d3c0 | ||
|
|
726d2ad3a2 | ||
|
|
e4ee1acf24 | ||
|
|
5c5a25e4de | ||
|
|
7669321ea2 | ||
|
|
d8e0aec950 | ||
|
|
49d6136b02 | ||
|
|
a80bae950d | ||
|
|
7102ccb93e | ||
|
|
afdaa6d0e6 | ||
|
|
41749bbd33 | ||
|
|
120af4e193 | ||
|
|
ec260e678f | ||
|
|
b590a5c3d8 | ||
|
|
540ec5b6ef | ||
|
|
563d1ac053 | ||
|
|
4d8ddb9a0a | ||
|
|
722163671f | ||
|
|
f158be8003 | ||
|
|
b0f7d26dd9 | ||
|
|
3a1203b2f8 | ||
|
|
df4c591ab4 | ||
|
|
4fa2768962 | ||
|
|
aa0a7b5fa2 | ||
|
|
42ac19af48 | ||
|
|
f744f32214 | ||
|
|
8ff7aac0ca | ||
|
|
80a45b1e1c | ||
|
|
85d2108e4e | ||
|
|
16f91157a1 | ||
|
|
2fa6bcd576 | ||
|
|
1afe2b36dc | ||
|
|
63e77887a8 | ||
|
|
809ca9a1d9 | ||
|
|
57435bb879 | ||
|
|
8c51bf7575 | ||
|
|
c3d2179ad8 | ||
|
|
b47c4024cc | ||
|
|
74d1cdc7c1 | ||
|
|
8cae9e0883 | ||
|
|
2c524b6da2 | ||
|
|
b4f2019529 | ||
|
|
b1eb1fb2f9 | ||
|
|
dfeb4d9692 | ||
|
|
7ea13332ed | ||
|
|
b032d23889 | ||
|
|
ecde74439c |
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"integration_trunk": "next",
|
||||
"release_branch": "main"
|
||||
}
|
||||
@@ -22,9 +22,9 @@ steps:
|
||||
image: gcr.io/kaniko-project/executor:debug
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: gitea_username
|
||||
from_secret: REGISTRY_USERNAME
|
||||
REGISTRY_PASS:
|
||||
from_secret: gitea_password
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
|
||||
|
||||
+74
-7
@@ -2,16 +2,38 @@
|
||||
# node:24-alpine + python3/make/g++/postgresql-client + pnpm + a warm pnpm
|
||||
# store. The install step resolves from the baked store (--prefer-offline)
|
||||
# instead of paying a ~731s cold fetch + native compile every run.
|
||||
#
|
||||
# PINNED to an immutable lock-tag (#1328, brain D27): ci-image.yml pushes
|
||||
# lock-<sha256(pnpm-lock.yaml)[:12]> atomically with :latest, so the two are
|
||||
# byte-identical at push time. A mutable :latest resolves per-pod at pull time
|
||||
# on the k8s backend, which made CI verdicts non-reproducible (same tree, same
|
||||
# config, different images across runs; see #1324 comment 23382/23386). The pin
|
||||
# changes ONLY through reviewed commits; a wrong tag fails loudly at image pull.
|
||||
#
|
||||
# Bump procedure: when a recipe change (pnpm-lock.yaml / Dockerfile.ci) lands on
|
||||
# main, ci-image.yml pushes lock-<new>; a follow-up PR updates this anchor.
|
||||
# Until then pipelines keep the old pin: reproducible, with the documented
|
||||
# network-fallback lag (frozen-lockfile resolves missing packages from network).
|
||||
# Known limitation: lock- addresses the lockfile only, so a Dockerfile-only
|
||||
# change re-pushes the same tag with new content (#1328 follow-up: recipe-hash).
|
||||
variables:
|
||||
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest'
|
||||
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:lock-9cb7ffcd8828'
|
||||
- &enable_pnpm 'corepack enable'
|
||||
|
||||
when:
|
||||
# PR + manual CI run on any branch — the pull_request pipeline is the merge gate.
|
||||
# push CI is restricted to protected branches (main) so a feature-branch push no
|
||||
# longer fires a redundant SECOND pipeline alongside its PR pipeline. This ~halves
|
||||
# CI load on the storage-constrained runner with zero loss of gating (branch
|
||||
# protection requires no push/ci status context; main still gets full push CI).
|
||||
# PR + manual CI run on any branch: the pull_request pipeline is the merge
|
||||
# gate (next is protected and the default branch since 2026-08-19).
|
||||
# Push CI runs on main only. next deliberately runs NO push ci: post-merge
|
||||
# verification on next is carried by publish.yml's `verify` step
|
||||
# (pnpm verify:release), which mirrors this pipeline's complete mandatory
|
||||
# set step-for-step, enforced by scripts/verify-release.test.mjs. PR CI
|
||||
# tests the PR HEAD tree (refs/pull/N/head, measured 2026-08-19), not a
|
||||
# merge ref, so if next advances before a merge the landed tree differs
|
||||
# from the tested one; publish verify re-runs the full set on the landed
|
||||
# tree (PGlite path). Measured 2026-08-19: the 21 most recent push events
|
||||
# on next each ran exactly one pipeline (publish), zero ci.
|
||||
# Keeping push ci off next also avoids a redundant second full-suite run
|
||||
# per merge on the storage-constrained runner.
|
||||
- event: [pull_request, manual]
|
||||
- event: push
|
||||
branch: main
|
||||
@@ -30,6 +52,19 @@ steps:
|
||||
# the baked pnpm store.
|
||||
- pnpm install --frozen-lockfile --prefer-offline
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The steps below (sanitization, upgrade-guard, typecheck, lint, format,
|
||||
# test) are the COMPLETE mandatory verification set. SDLC-D-034 mirrors them
|
||||
# one-for-one in the canonical terminal verification command — root
|
||||
# `pnpm verify:release` (scripts/verify-release.mjs) — which the publish
|
||||
# pipeline (.woodpecker/publish.yml `verify` step) runs before ANY publish
|
||||
# effect. These lines stay direct (not routed through the runner) because the
|
||||
# #1017 test-enumeration guard audits framework tool paths through THIS
|
||||
# surface; scripts/verify-release.test.mjs enforces that the runner's stage
|
||||
# table keeps matching these commands exactly, so the two cannot drift.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Canonical verify:release stage `sanitization`.
|
||||
# Blocking gate: public framework package must contain no operator-specific
|
||||
# personal data or private $HOME defaults. Runs early (no node_modules needed).
|
||||
sanitization:
|
||||
@@ -46,7 +81,30 @@ steps:
|
||||
# [0] of the pnpm chain, so severing that chain would silence it together
|
||||
# with everything it guards; this direct line keeps one instrument running.
|
||||
- bash packages/mosaic/framework/tools/quality/scripts/check-test-enumeration.sh
|
||||
# Tool-index gate: a shipped wrapper that appears in no resident index doc
|
||||
# is undiscoverable from inside a session, and an agent that cannot learn a
|
||||
# wrapper exists reaches for raw curl instead — which is how a Gitea review
|
||||
# got filed PENDING three times. Ships-and-documented is one commit, or red.
|
||||
- bash packages/mosaic/framework/tools/quality/scripts/check-tools-index.sh --self-test
|
||||
- bash packages/mosaic/framework/tools/quality/scripts/check-tools-index.sh
|
||||
# Hermetic regression for issue-close.sh (#1081): mocks tea/curl onto PATH
|
||||
# and sandboxes a throwaway git repo, so it resolves no real credentials and
|
||||
# joins CI directly rather than the exclusions file.
|
||||
- bash packages/mosaic/framework/tools/git/test-issue-close-fail-closed.sh
|
||||
# Hermetic behavioural regression for the PreToolUse wrapper guard: proves
|
||||
# it still blocks the three mistakes AND still lets reads, unwrapped
|
||||
# endpoints and ordinary commands through. Both directions are asserted —
|
||||
# a guard that over-blocks gets routed around, which fails just as hard.
|
||||
- bash packages/mosaic/framework/tools/git/test-wrapper-guard.sh
|
||||
# Hermetic regression for mosaic-worktree.sh at fleet scale: stubs git onto
|
||||
# PATH so `list` faces ~450 KB of porcelain. The defect it pins is invisible
|
||||
# at small size — `git … | awk '…exit'` gives the producer SIGPIPE, which
|
||||
# under `set -euo pipefail` aborts the caller silently with rc=141 and no
|
||||
# output. A repo only reaches that once it has enough worktrees, so the
|
||||
# stub supplies the scale instead of the host's own checkout.
|
||||
- bash packages/mosaic/framework/tools/git/test-mosaic-worktree-large-repo.sh
|
||||
|
||||
# Canonical verify:release stage `upgrade-guard`.
|
||||
# Blocking gate (#791): a framework upgrade must never write or delete an
|
||||
# operator-owned path. The HARD GATE proves an unanticipated operator sentinel
|
||||
# survives a keep-mode reseed byte-identical (with rsync present AND absent —
|
||||
@@ -68,6 +126,8 @@ steps:
|
||||
- bash packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh
|
||||
- bash packages/mosaic/framework/tools/quality/scripts/test-install-migration.sh
|
||||
|
||||
# Canonical verify:release stage `typecheck` — the same `pnpm typecheck`
|
||||
# invocation (which runs the checkout preflight first, then turbo).
|
||||
typecheck:
|
||||
image: *node_image
|
||||
commands:
|
||||
@@ -78,7 +138,8 @@ steps:
|
||||
- sanitization
|
||||
- upgrade-guard
|
||||
|
||||
# lint, format, and test are independent — run in parallel after typecheck
|
||||
# lint, format, and test are independent — run in parallel after typecheck.
|
||||
# Each runs exactly its canonical verify:release stage command.
|
||||
lint:
|
||||
image: *node_image
|
||||
commands:
|
||||
@@ -95,6 +156,12 @@ steps:
|
||||
depends_on:
|
||||
- typecheck
|
||||
|
||||
# Canonical verify:release stage `test` — the `pnpm test` line below is the
|
||||
# shared command; everything else in this step is PIPELINE-LEVEL
|
||||
# prerequisite the canonical command expects its caller to provide (SDLC-D-034):
|
||||
# the ci-postgres service + pg_isready wait + db:migrate (postgres path),
|
||||
# `apk add openssl`, and the pinned pi install. None of those can move into
|
||||
# the runner (it must also work locally on the PGlite path with no database).
|
||||
test:
|
||||
image: *node_image
|
||||
environment:
|
||||
|
||||
+72
-7
@@ -1,10 +1,29 @@
|
||||
# Build, publish npm packages, and push Docker images
|
||||
# Runs on main for stable publishes and on next for integration-line prereleases/images
|
||||
#
|
||||
# SDLC-D-034 publish gate: every publish effect (publish-npm, publish-next-npm,
|
||||
# and every image build/push step) depends DIRECTLY on the `verify` step below.
|
||||
# `verify` (a) asserts the provider's commit identity matches the actual
|
||||
# checkout (CI_COMMIT_SHA == git rev-parse HEAD, fail closed on mismatch or
|
||||
# emptiness) and (b) runs the canonical terminal verification command
|
||||
# (`pnpm verify:release`), which mirrors the PR CI pipeline's complete
|
||||
# mandatory set (sanitization, upgrade-guard, preflight+typecheck, lint,
|
||||
# format:check, test, build) — see scripts/verify-release.mjs. A missing,
|
||||
# failed, skipped, cancelled, or inconclusive verification therefore skips the
|
||||
# dependent publish effects (fail closed). Path-filtered short-circuits may
|
||||
# skip publish EFFECTS (e.g. docs-only merges) but never bypass `verify` for a
|
||||
# publish that does run: `verify` itself carries no path filter.
|
||||
# scripts/verify-release.test.mjs enforces this DAG invariant at checkout time.
|
||||
|
||||
variables:
|
||||
# Pre-baked CI base (see .woodpecker/ci-image.yml): node:24-alpine +
|
||||
# toolchain + warm pnpm store. Kills the second cold install publish pays.
|
||||
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest'
|
||||
# PINNED to the immutable lock-tag, not :latest (#1328, brain D27): a mutable
|
||||
# tag resolves per-pod at pull time on the k8s backend and made CI verdicts
|
||||
# non-reproducible (#1324). Byte-identical to :latest at pin time (pushed
|
||||
# atomically by the same kaniko run, main 712c770, 2026-07-26). Bump only via
|
||||
# reviewed PR, per the procedure in .woodpecker/ci.yml's header comment.
|
||||
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:lock-9cb7ffcd8828'
|
||||
- &enable_pnpm 'corepack enable'
|
||||
# Heavy kaniko image builds (~25 min) — gate them so a merge that only touches
|
||||
# the npm-only CLI (@mosaicstack/mosaic) or docs does NOT rebuild the platform
|
||||
@@ -48,6 +67,45 @@ steps:
|
||||
# Resolve from the baked pnpm store instead of a cold network fetch.
|
||||
- pnpm install --frozen-lockfile --prefer-offline
|
||||
|
||||
# SDLC-D-034 exact-commit publish gate. No `when`/path filter on purpose: it
|
||||
# runs for every event this pipeline serves so no publish effect can ever
|
||||
# start without it. Fails closed on commit-identity mismatch (or either SHA
|
||||
# being empty) and on any incomplete verification.
|
||||
verify:
|
||||
image: *node_image
|
||||
commands:
|
||||
- *enable_pnpm
|
||||
# (a) Commit identity: the provider's claimed SHA must equal the actual
|
||||
# checkout HEAD — verification of anything else must never authorize a
|
||||
# publish of this commit.
|
||||
- |
|
||||
if [ -z "$CI_COMMIT_SHA" ]; then
|
||||
echo "[verify] FATAL: CI_COMMIT_SHA is empty — cannot certify commit identity" >&2
|
||||
exit 1
|
||||
fi
|
||||
CHECKOUT_SHA="$(git rev-parse HEAD 2>/dev/null || true)"
|
||||
if [ -z "$CHECKOUT_SHA" ]; then
|
||||
echo "[verify] FATAL: git rev-parse HEAD returned nothing — cannot certify commit identity" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$CI_COMMIT_SHA" != "$CHECKOUT_SHA" ]; then
|
||||
echo "[verify] FATAL: provider commit ($CI_COMMIT_SHA) != checkout HEAD ($CHECKOUT_SHA)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[verify] commit identity confirmed: $CHECKOUT_SHA"
|
||||
# (b) Canonical terminal verification. Caller-provided prerequisites the
|
||||
# runner expects (see .woodpecker/ci.yml comments): bash/rsync for the
|
||||
# guard stages, openssl + the pinned pi binary for the test stage. git is
|
||||
# baked into ci-base but re-asserted here so the identity check above can
|
||||
# never silently depend on a stale baked image. DATABASE_URL is
|
||||
# deliberately NOT set: the canonical command must hold on the PGlite
|
||||
# path too and never sets or requires a database itself.
|
||||
- apk add --no-cache bash rsync openssl git
|
||||
- npm install -g @earendil-works/[email protected]
|
||||
- pnpm verify:release
|
||||
depends_on:
|
||||
- install
|
||||
|
||||
build:
|
||||
image: *node_image
|
||||
commands:
|
||||
@@ -55,6 +113,7 @@ steps:
|
||||
- pnpm build
|
||||
depends_on:
|
||||
- install
|
||||
- verify
|
||||
|
||||
publish-npm:
|
||||
image: *node_image
|
||||
@@ -114,6 +173,7 @@ steps:
|
||||
exit 1
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
publish-next-npm:
|
||||
image: *node_image
|
||||
@@ -192,6 +252,7 @@ steps:
|
||||
echo "[publish-next] @mosaicstack/mosaic@next resolves to $RESOLVED_VERSION"
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
# TODO: Uncomment when ready to publish to npmjs.org
|
||||
# publish-npmjs:
|
||||
@@ -205,6 +266,7 @@ steps:
|
||||
# - bash scripts/publish-npmjs.sh
|
||||
# depends_on:
|
||||
# - build
|
||||
# - verify
|
||||
# when:
|
||||
# - event: [tag]
|
||||
|
||||
@@ -213,9 +275,9 @@ steps:
|
||||
when: *image_build_when
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: gitea_username
|
||||
from_secret: REGISTRY_USERNAME
|
||||
REGISTRY_PASS:
|
||||
from_secret: gitea_password
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
|
||||
@@ -242,15 +304,16 @@ steps:
|
||||
/kaniko/executor --context . --dockerfile docker/gateway.Dockerfile $DESTINATIONS
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
build-appservice:
|
||||
image: gcr.io/kaniko-project/executor:debug
|
||||
when: *main_image_build_when
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: gitea_username
|
||||
from_secret: REGISTRY_USERNAME
|
||||
REGISTRY_PASS:
|
||||
from_secret: gitea_password
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
|
||||
@@ -268,15 +331,16 @@ steps:
|
||||
/kaniko/executor --context . --dockerfile docker/appservice.Dockerfile $DESTINATIONS
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
build-web:
|
||||
image: gcr.io/kaniko-project/executor:debug
|
||||
when: *main_image_build_when
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: gitea_username
|
||||
from_secret: REGISTRY_USERNAME
|
||||
REGISTRY_PASS:
|
||||
from_secret: gitea_password
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
|
||||
@@ -294,3 +358,4 @@ steps:
|
||||
/kaniko/executor --context . --dockerfile docker/web.Dockerfile $DESTINATIONS
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
@@ -81,6 +81,73 @@ pnpm format:check # Prettier check
|
||||
pnpm build # Build all packages and applications
|
||||
```
|
||||
|
||||
## Branch Model and Merge Process — `main` and `next` (CANONICAL)
|
||||
|
||||
**Every contribution targets `next` first. No exceptions.** Features, fixes, tests,
|
||||
docs, and policy changes all take the same route; urgency changes queue priority,
|
||||
never the route. Agents never commit to or merge into `main`.
|
||||
|
||||
| Branch | Role | Who merges into it |
|
||||
| ------ | ---------------------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| `next` | Integration trunk — the only PR target for contributions | The designated merge-gate agent, after all gates pass. Never the PR author. |
|
||||
| `main` | Stable/release line — receives promotion merges from `next` only | Jason only (or an agent he explicitly delegates for a named promotion). |
|
||||
|
||||
### Contribution sequencing (in order, no skipping)
|
||||
|
||||
1. **Issue first.** Work is tracked in a Gitea issue before a branch exists. The
|
||||
issue number appears in the branch name and the PR body.
|
||||
2. **Branch from the current `origin/next` head.** Name it
|
||||
`feat/…`, `fix/…`, `docs/…`, or `test/…` with the issue number
|
||||
(e.g. `docs/1214-branch-process`). Record the base SHA in the PR body.
|
||||
3. **Develop with evidence.** Applicable tests accompany the change. Hooks are
|
||||
never bypassed (`--no-verify` is prohibited). Stage explicit paths — never
|
||||
`git add -A`.
|
||||
4. **Open the PR against `next`.** The body states: scope, base SHA,
|
||||
verification commands with results, and any known pre-existing failures on
|
||||
the base — documented, not retried to green and not absorbed silently.
|
||||
5. **CI must be terminal-green on the exact head.** All bounded Woodpecker
|
||||
steps succeed (`verify-terminal-green` contract). Pipelines for fork PRs
|
||||
start `blocked`; a maintainer approves the run — approving CI is not
|
||||
approving the PR.
|
||||
6. **Independent review. Self-merge is prohibited** — for every agent, on every
|
||||
PR, including trivial ones. Where the change touches protected or
|
||||
contract-bearing content, the reviewer verifies the exact head
|
||||
(exact-byte/exact-blob comparison), not a description of it. An `AMEND`
|
||||
verdict returns the PR to its author; the reviewer's gate stays held until
|
||||
a fresh exact head passes.
|
||||
7. **Merge into `next`** happens only after CI green + review pass, pinned to
|
||||
the reviewed head SHA (a post-review push voids the review).
|
||||
8. **Promotion `next` → `main`** is a deliberate, Jason-owned reconciliation
|
||||
merge — not part of any contribution's lifecycle. Contributors are done at
|
||||
step 7.
|
||||
|
||||
### Responsibilities
|
||||
|
||||
- **Contributor** — base pinning, green CI, evidence in the PR body,
|
||||
responding to AMEND verdicts, never merging own work.
|
||||
- **Reviewer / merge gate** — independent verification on the exact head;
|
||||
holds and lifts gates; executes the merge into `next`.
|
||||
- **Orchestrator / adjudicator** — cross-PR sequencing, disposition when PRs
|
||||
collide, conflict adjudication.
|
||||
- **Jason** — `next` → `main` promotions, merge-authority grants, collaborator
|
||||
and token provisioning. Agents cannot grant themselves or each other any of
|
||||
these.
|
||||
|
||||
### Hotfixes and divergence
|
||||
|
||||
- A hotfix follows the same path: branch from `next`, PR to `next`, gates,
|
||||
merge, then an expedited Jason-owned promotion if `main` needs it urgently.
|
||||
Committing the fix to `main` directly is prohibited even under pressure.
|
||||
- **Never land work on `main` that is not on `next`.** This has happened
|
||||
(issue #1152's goal controller reached `main` without reaching `next`) and
|
||||
every later PR paid for it. If it happens anyway: transplant the work onto
|
||||
a `next`-based branch with provenance-preserving commits
|
||||
(`git cherry-pick -x` or explicit SHA references in the messages), PR it
|
||||
through the normal gates, and let promotion re-align `main`. Do not
|
||||
hand-patch `main` to compensate.
|
||||
- Force-pushing a branch you do not own is prohibited; rebasing your own PR
|
||||
branch is fine before review, and voids any review already given.
|
||||
|
||||
## Database and Local Runtime Safety
|
||||
|
||||
- Current local data-layer work uses in-process PGlite; leave `DATABASE_URL` unset.
|
||||
|
||||
@@ -74,6 +74,14 @@ The launcher verifies your config, checks for `SOUL.md`, injects your `AGENTS.md
|
||||
|
||||
Pi launches default to a token-lean skill posture: `mosaic pi` passes `--no-skills` so Pi does not preload every global skill description into the system prompt. Use `MOSAIC_PI_SKILL_MODE=all mosaic pi` for the legacy all-skills catalog, or `MOSAIC_PI_SKILL_MODE=discover mosaic pi` to let Pi use its native settings/project skill discovery.
|
||||
|
||||
Mosaic also loads its Pi extensions from `~/.config/mosaic/runtime/pi/`. Inside Pi,
|
||||
`/goal set <statement>` starts a bounded persistent loop that checks every turn and successful
|
||||
compaction, requires two evidence-bearing completion reports, and can be inspected or stopped with
|
||||
`/goal status`, `/goal pause`, `/goal resume`, and `/goal cancel`. Controller-owned goal-state
|
||||
entries redact common credential shapes, but Pi's model/tool-call history is separate, so goals and
|
||||
evidence must never contain secrets or raw sensitive output. Mosaic does not install this extension
|
||||
into `~/.pi/agent/extensions/`.
|
||||
|
||||
### TUI & Gateway
|
||||
|
||||
```bash
|
||||
@@ -138,9 +146,9 @@ mosaic brain tasks
|
||||
mosaic brain conversations
|
||||
|
||||
# Agent forge pipeline
|
||||
mosaic forge run
|
||||
mosaic forge run [--simulate] # fails closed (FORGE_NO_EXECUTOR) with no executor wired; --simulate for typed simulated runs
|
||||
mosaic forge status
|
||||
mosaic forge resume
|
||||
mosaic forge resume [--simulate] # same fail-closed rule as forge run
|
||||
mosaic forge personas
|
||||
|
||||
# Structured logging
|
||||
@@ -331,7 +339,7 @@ The framework is the bash-based standards layer installed to every developer mac
|
||||
├── bin/mosaic ← Unified launcher (claude, codex, opencode, pi, yolo)
|
||||
├── guides/ ← E2E delivery, orchestrator protocol, PRD, etc.
|
||||
├── runtime/ ← Per-runtime configs (claude/, codex/, opencode/, pi/)
|
||||
├── skills/ ← Universal skills (synced from agent-skills repo)
|
||||
├── skills/ ← Universal skills (shipped with the framework package)
|
||||
├── tools/ ← Tool suites (orchestrator, git, quality, prdy, etc.)
|
||||
└── memory/ ← Persistent agent memory (preserved across upgrades)
|
||||
```
|
||||
|
||||
@@ -190,7 +190,13 @@ beforeEach((ctx) => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (!handle) return;
|
||||
// Cleanup only when the fixture actually installed rows. `handle` is set
|
||||
// before the first query (createDb connects lazily), so on an unreachable
|
||||
// database `handle` is truthy while nothing was inserted — cleanup must
|
||||
// honor `dbAvailable` or the skip path fails the file with ECONNREFUSED in
|
||||
// afterAll (caught live by the publish pipeline's no-DATABASE_URL verify
|
||||
// step, pipeline 2486).
|
||||
if (!handle || !dbAvailable) return;
|
||||
const db = handle.db;
|
||||
|
||||
// Delete in dependency order (FK constraints)
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
import { type Type } from '@nestjs/common';
|
||||
import { Test, type TestingModule } from '@nestjs/testing';
|
||||
import type { SlashCommandPayload } from '@mosaicstack/types';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { AgentService, type AgentSession } from '../agent/agent.service.js';
|
||||
import { ProviderService } from '../agent/provider.service.js';
|
||||
import { AppModule } from '../app.module.js';
|
||||
import { CommandAuthorizationService } from '../commands/command-authorization.service.js';
|
||||
import { CommandExecutorService } from '../commands/command-executor.service.js';
|
||||
import { CommandsModule } from '../commands/commands.module.js';
|
||||
import { CommandRuntimeApprovalVerifier } from '../commands/runtime-approval-verifier.js';
|
||||
import { PreferencesModule } from '../preferences/preferences.module.js';
|
||||
import { SystemOverrideService } from '../preferences/system-override.service.js';
|
||||
|
||||
const fakeDb = {
|
||||
$client: { exec: async (): Promise<void> => {} },
|
||||
execute: async (): Promise<{ rows: unknown[] }> => ({ rows: [] }),
|
||||
select: () => ({
|
||||
from: () => ({
|
||||
where: async (): Promise<Array<{ count: number }>> => [{ count: 1 }],
|
||||
}),
|
||||
}),
|
||||
insert: () => ({ values: async (): Promise<void> => {} }),
|
||||
};
|
||||
|
||||
const fakeProviderService = {
|
||||
onModuleInit: async (): Promise<void> => {},
|
||||
onModuleDestroy: (): void => {},
|
||||
getRegistry: () => ({ getAvailable: () => [], getAll: () => [], find: () => undefined }),
|
||||
getDefaultModel: () => undefined,
|
||||
listAvailableModels: () => [],
|
||||
listProviders: () => [],
|
||||
getAdapter: () => undefined,
|
||||
getProvidersHealth: () => [],
|
||||
};
|
||||
|
||||
function compileRealAppGraph(): Promise<TestingModule> {
|
||||
return Test.createTestingModule({ imports: [AppModule] })
|
||||
.overrideProvider('DB_HANDLE')
|
||||
.useValue({ db: fakeDb, close: async (): Promise<void> => {} })
|
||||
.overrideProvider('DB')
|
||||
.useValue(fakeDb)
|
||||
.overrideProvider('STORAGE_ADAPTER')
|
||||
.useValue({
|
||||
name: 'required-security-wiring-test',
|
||||
migrate: async (): Promise<void> => {},
|
||||
close: async (): Promise<void> => {},
|
||||
})
|
||||
.overrideProvider('AUTH')
|
||||
.useValue({})
|
||||
.overrideProvider('BRAIN')
|
||||
.useValue({ conversations: {}, agents: {} })
|
||||
.overrideProvider('LOG_SERVICE')
|
||||
.useValue({})
|
||||
.overrideProvider('MEMORY')
|
||||
.useValue({})
|
||||
.overrideProvider('MEMORY_ADAPTER')
|
||||
.useValue({})
|
||||
.overrideProvider(ProviderService)
|
||||
.useValue(fakeProviderService)
|
||||
.compile();
|
||||
}
|
||||
|
||||
function providerToken(provider: unknown): unknown {
|
||||
return typeof provider === 'function' ? provider : (provider as { provide?: unknown })?.provide;
|
||||
}
|
||||
|
||||
interface MaskingConsumer {
|
||||
moduleType: Type<unknown>;
|
||||
token: Type<unknown>;
|
||||
useValue: object;
|
||||
}
|
||||
|
||||
async function compileWithoutProvider(
|
||||
moduleType: Type<unknown>,
|
||||
missingToken: Type<unknown>,
|
||||
maskingConsumer: MaskingConsumer,
|
||||
): Promise<{ error: unknown; moduleRef: TestingModule | undefined }> {
|
||||
const touchedModules = new Set([moduleType, maskingConsumer.moduleType]);
|
||||
const originals = Array.from(touchedModules, (touchedModule: Type<unknown>) => ({
|
||||
moduleType: touchedModule,
|
||||
providers: (Reflect.getMetadata('providers', touchedModule) ?? []) as unknown[],
|
||||
exports: (Reflect.getMetadata('exports', touchedModule) ?? []) as unknown[],
|
||||
}));
|
||||
|
||||
for (const original of originals) {
|
||||
const providers = original.providers.flatMap((provider: unknown): unknown[] => {
|
||||
const token = providerToken(provider);
|
||||
if (original.moduleType === moduleType && token === missingToken) return [];
|
||||
if (original.moduleType === maskingConsumer.moduleType && token === maskingConsumer.token) {
|
||||
return [{ provide: maskingConsumer.token, useValue: maskingConsumer.useValue }];
|
||||
}
|
||||
return [provider];
|
||||
});
|
||||
const exports = original.exports.filter(
|
||||
(exported: unknown): boolean =>
|
||||
original.moduleType !== moduleType || providerToken(exported) !== missingToken,
|
||||
);
|
||||
Reflect.defineMetadata('providers', providers, original.moduleType);
|
||||
Reflect.defineMetadata('exports', exports, original.moduleType);
|
||||
}
|
||||
|
||||
let moduleRef: TestingModule | undefined;
|
||||
let error: unknown;
|
||||
try {
|
||||
moduleRef = await compileRealAppGraph();
|
||||
} catch (caught: unknown) {
|
||||
error = caught;
|
||||
} finally {
|
||||
for (const original of originals) {
|
||||
Reflect.defineMetadata('providers', original.providers, original.moduleType);
|
||||
Reflect.defineMetadata('exports', original.exports, original.moduleType);
|
||||
}
|
||||
}
|
||||
return { error, moduleRef };
|
||||
}
|
||||
|
||||
async function closeIfCompiled(moduleRef: TestingModule | undefined): Promise<void> {
|
||||
if (moduleRef) await moduleRef.close();
|
||||
}
|
||||
|
||||
describe('required security wiring — real AppModule startup refusal', () => {
|
||||
it('FL-01 positive control: the real graph compiles when CommandAuthorizationService is bound', async () => {
|
||||
const moduleRef = await compileRealAppGraph();
|
||||
try {
|
||||
expect(moduleRef.get(CommandAuthorizationService, { strict: false })).toBeInstanceOf(
|
||||
CommandAuthorizationService,
|
||||
);
|
||||
} finally {
|
||||
await moduleRef.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('FL-01 negative control: absence read as permission is refused at module compilation', async () => {
|
||||
const { error, moduleRef } = await compileWithoutProvider(
|
||||
CommandsModule,
|
||||
CommandAuthorizationService,
|
||||
{
|
||||
moduleType: CommandsModule,
|
||||
token: CommandRuntimeApprovalVerifier,
|
||||
useValue: {},
|
||||
},
|
||||
);
|
||||
await closeIfCompiled(moduleRef);
|
||||
|
||||
expect(
|
||||
error,
|
||||
'absence read as permission: AppModule compilation accepted a missing CommandAuthorizationService binding',
|
||||
).toBeInstanceOf(Error);
|
||||
if (!(error instanceof Error)) return;
|
||||
expect(error.message).toContain('CommandExecutorService');
|
||||
expect(error.message).toContain('CommandAuthorizationService');
|
||||
});
|
||||
|
||||
it('FL-11 positive control: the real graph compiles when SystemOverrideService is bound', async () => {
|
||||
const moduleRef = await compileRealAppGraph();
|
||||
try {
|
||||
expect(moduleRef.get(SystemOverrideService, { strict: false })).toBeInstanceOf(
|
||||
SystemOverrideService,
|
||||
);
|
||||
} finally {
|
||||
await moduleRef.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('FL-11 negative control: absence read as permission is refused at module compilation', async () => {
|
||||
const { error, moduleRef } = await compileWithoutProvider(
|
||||
PreferencesModule,
|
||||
SystemOverrideService,
|
||||
{
|
||||
moduleType: CommandsModule,
|
||||
token: CommandExecutorService,
|
||||
useValue: {},
|
||||
},
|
||||
);
|
||||
await closeIfCompiled(moduleRef);
|
||||
|
||||
expect(
|
||||
error,
|
||||
'absence read as permission: AppModule compilation accepted a missing SystemOverrideService binding',
|
||||
).toBeInstanceOf(Error);
|
||||
if (!(error instanceof Error)) return;
|
||||
expect(error.message).toContain('AgentService');
|
||||
expect(error.message).toContain('SystemOverrideService');
|
||||
});
|
||||
});
|
||||
|
||||
const actorScope = { userId: 'security-user', tenantId: 'security-tenant' };
|
||||
const conversationId = 'security-conversation';
|
||||
|
||||
function directExecutorWithoutAuthorization(systemOverrideSet: ReturnType<typeof vi.fn>) {
|
||||
const registry = {
|
||||
getManifest: vi.fn(() => ({
|
||||
version: 1,
|
||||
commands: [
|
||||
{
|
||||
name: 'system',
|
||||
aliases: [],
|
||||
description: 'Set instruction authority',
|
||||
scope: 'agent' as const,
|
||||
execution: 'socket' as const,
|
||||
available: true,
|
||||
},
|
||||
],
|
||||
skills: [],
|
||||
})),
|
||||
};
|
||||
return new CommandExecutorService(
|
||||
registry as never,
|
||||
{ getSession: vi.fn() } as never,
|
||||
{ set: systemOverrideSet, clear: vi.fn() } as never,
|
||||
{ collect: vi.fn() } as never,
|
||||
null,
|
||||
{ agents: {} } as never,
|
||||
null,
|
||||
null,
|
||||
{ getServerStatuses: vi.fn(() => []), getToolDefinitions: vi.fn(() => []) } as never,
|
||||
undefined as never,
|
||||
);
|
||||
}
|
||||
|
||||
function directAgentWithoutSystemOverride(piPrompt: ReturnType<typeof vi.fn>): {
|
||||
service: AgentService;
|
||||
session: AgentSession;
|
||||
} {
|
||||
const service = new AgentService(
|
||||
{
|
||||
getDefaultModel: vi.fn(() => null),
|
||||
getRegistry: vi.fn(() => ({})),
|
||||
findModel: vi.fn(),
|
||||
listAvailableModels: vi.fn(() => []),
|
||||
} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
{ available: false } as never,
|
||||
{} as never,
|
||||
{ getToolDefinitions: vi.fn(() => []) } as never,
|
||||
{ loadForSession: vi.fn(async () => ({ metaTools: [], promptAdditions: [] })) } as never,
|
||||
undefined as never,
|
||||
null,
|
||||
{ collect: vi.fn().mockResolvedValue(undefined) } as never,
|
||||
null,
|
||||
);
|
||||
const session = {
|
||||
id: conversationId,
|
||||
provider: 'test-provider',
|
||||
modelId: 'test-model',
|
||||
piSession: { prompt: piPrompt },
|
||||
listeners: new Set(),
|
||||
unsubscribe: vi.fn(),
|
||||
createdAt: Date.now(),
|
||||
promptCount: 0,
|
||||
channels: new Set(),
|
||||
skillPromptAdditions: [],
|
||||
sandboxDir: process.cwd(),
|
||||
allowedTools: null,
|
||||
userId: actorScope.userId,
|
||||
tenantId: actorScope.tenantId,
|
||||
metrics: {
|
||||
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
modelSwitches: 0,
|
||||
messageCount: 0,
|
||||
lastActivityAt: new Date(0).toISOString(),
|
||||
},
|
||||
} as unknown as AgentSession;
|
||||
const internals = service as unknown as { sessions: Map<string, AgentSession> };
|
||||
internals.sessions.set(conversationId, session);
|
||||
return { service, session };
|
||||
}
|
||||
|
||||
describe('required security wiring — malformed direct absence has zero effects', () => {
|
||||
it('FL-01 refuses command execution before any command effect when authorization is absent', async () => {
|
||||
const systemOverrideSet = vi.fn().mockResolvedValue(undefined);
|
||||
const executor = directExecutorWithoutAuthorization(systemOverrideSet);
|
||||
const payload: SlashCommandPayload = {
|
||||
command: 'system',
|
||||
args: 'authority that must not be stored',
|
||||
conversationId,
|
||||
};
|
||||
let error: unknown;
|
||||
|
||||
try {
|
||||
await executor.execute(payload, actorScope);
|
||||
} catch (caught: unknown) {
|
||||
error = caught;
|
||||
}
|
||||
|
||||
expect
|
||||
.soft(
|
||||
error,
|
||||
'absence read as permission: direct executor accepted missing command authorization',
|
||||
)
|
||||
.toBeInstanceOf(Error);
|
||||
expect
|
||||
.soft(
|
||||
systemOverrideSet,
|
||||
'absence read as permission: command effect occurred without command authorization',
|
||||
)
|
||||
.not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('FL-11 refuses prompt execution before any provider or session effect when system override authority is absent', async () => {
|
||||
const piPrompt = vi.fn().mockResolvedValue(undefined);
|
||||
const { service, session } = directAgentWithoutSystemOverride(piPrompt);
|
||||
let error: unknown;
|
||||
|
||||
try {
|
||||
await service.prompt(conversationId, 'must not reach provider', actorScope);
|
||||
} catch (caught: unknown) {
|
||||
error = caught;
|
||||
}
|
||||
|
||||
expect
|
||||
.soft(
|
||||
error,
|
||||
'absence read as permission: direct session accepted missing system override authority',
|
||||
)
|
||||
.toBeInstanceOf(Error);
|
||||
expect
|
||||
.soft(
|
||||
piPrompt,
|
||||
'absence read as permission: provider prompt occurred without system override authority',
|
||||
)
|
||||
.not.toHaveBeenCalled();
|
||||
expect
|
||||
.soft(
|
||||
session.promptCount,
|
||||
'absence read as permission: session state changed without system override authority',
|
||||
)
|
||||
.toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -26,7 +26,7 @@ function makeService(operatorMemory: unknown = null): AgentService {
|
||||
{} as never,
|
||||
{ getToolDefinitions: vi.fn(() => []) } as never,
|
||||
{ loadForSession: vi.fn(async () => ({ metaTools: [], promptAdditions: [] })) } as never,
|
||||
null,
|
||||
{ get: vi.fn().mockResolvedValue(null), renew: vi.fn().mockResolvedValue(undefined) } as never,
|
||||
null,
|
||||
{ collect: vi.fn().mockResolvedValue(undefined) } as never,
|
||||
operatorMemory as never,
|
||||
|
||||
@@ -132,9 +132,8 @@ export class AgentService implements OnModuleDestroy {
|
||||
@Inject(CoordService) private readonly coordService: CoordService,
|
||||
@Inject(McpClientService) private readonly mcpClientService: McpClientService,
|
||||
@Inject(SkillLoaderService) private readonly skillLoaderService: SkillLoaderService,
|
||||
@Optional()
|
||||
@Inject(SystemOverrideService)
|
||||
private readonly systemOverride: SystemOverrideService | null,
|
||||
private readonly systemOverride: SystemOverrideService,
|
||||
@Optional()
|
||||
@Inject(PreferencesService)
|
||||
private readonly preferencesService: PreferencesService | null,
|
||||
@@ -709,23 +708,22 @@ export class AgentService implements OnModuleDestroy {
|
||||
throw new Error(`No agent session found: ${sessionId}`);
|
||||
}
|
||||
this.assertSessionScope(session, scope);
|
||||
session.promptCount += 1;
|
||||
|
||||
// Channel attachments are untrusted URI references. Preserve exact,
|
||||
// authenticated metadata for the agent without treating it as authority.
|
||||
const attachmentContext = this.attachmentContext(attachments);
|
||||
|
||||
// Prepend session-scoped system override if present (renew TTL on each turn)
|
||||
// Prepend session-scoped system override if present (renew TTL on each turn).
|
||||
// Required instruction-authority wiring is consulted before session/provider effects.
|
||||
let effectiveMessage = `${message}${attachmentContext}`;
|
||||
if (this.systemOverride) {
|
||||
const override = await this.systemOverride.get(sessionId, scope);
|
||||
if (override) {
|
||||
effectiveMessage = `[System Override]\n${override}\n\n${effectiveMessage}`;
|
||||
await this.systemOverride.renew(sessionId, scope);
|
||||
this.logger.debug(`Applied system override for session ${sessionId}`);
|
||||
}
|
||||
const override = await this.systemOverride.get(sessionId, scope);
|
||||
if (override) {
|
||||
effectiveMessage = `[System Override]\n${override}\n\n${effectiveMessage}`;
|
||||
await this.systemOverride.renew(sessionId, scope);
|
||||
this.logger.debug(`Applied system override for session ${sessionId}`);
|
||||
}
|
||||
|
||||
session.promptCount += 1;
|
||||
try {
|
||||
await session.piSession.prompt(effectiveMessage);
|
||||
} catch (err) {
|
||||
|
||||
@@ -80,6 +80,10 @@ const mockMcpClient = {
|
||||
getToolDefinitions: vi.fn(() => []),
|
||||
};
|
||||
|
||||
const allowAuthorization = {
|
||||
authorize: vi.fn().mockResolvedValue({ allowed: true }),
|
||||
};
|
||||
|
||||
function buildService(
|
||||
redis: typeof mockRedis | null = mockRedis,
|
||||
mcpClient: {
|
||||
@@ -98,6 +102,7 @@ function buildService(
|
||||
null,
|
||||
mockChatGateway as never,
|
||||
mcpClient as never,
|
||||
allowAuthorization as never,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,8 @@ export class CommandExecutorService {
|
||||
@Inject(forwardRef(() => ChatGateway))
|
||||
private readonly chatGateway: ChatGateway | null,
|
||||
@Inject(McpClientService) private readonly mcpClient: McpClientService,
|
||||
@Optional()
|
||||
@Inject(CommandAuthorizationService)
|
||||
private readonly authorization: CommandAuthorizationService | null = null,
|
||||
private readonly authorization: CommandAuthorizationService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
@@ -57,13 +56,13 @@ export class CommandExecutorService {
|
||||
};
|
||||
}
|
||||
|
||||
const authorization = await this.authorization?.authorize(
|
||||
const authorization = await this.authorization.authorize(
|
||||
def,
|
||||
payload,
|
||||
userId,
|
||||
payload.approvalId,
|
||||
);
|
||||
if (authorization && !authorization.allowed) {
|
||||
if (!authorization.allowed) {
|
||||
return { command, conversationId, success: false, message: authorization.reason };
|
||||
}
|
||||
|
||||
@@ -171,7 +170,7 @@ export class CommandExecutorService {
|
||||
const def = this.registry
|
||||
.getManifest()
|
||||
.commands.find((command) => command.name === payload.command);
|
||||
if (!def || !this.authorization) return null;
|
||||
if (!def) return null;
|
||||
return this.authorization.createApproval(def, payload, scope.userId);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,10 @@ const mockMcpClient = {
|
||||
reconnectServer: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
const allowAuthorization = {
|
||||
authorize: vi.fn().mockResolvedValue({ allowed: true }),
|
||||
};
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
function buildRegistry(): CommandRegistryService {
|
||||
@@ -74,6 +78,7 @@ function buildExecutor(registry: CommandRegistryService): CommandExecutorService
|
||||
null, // reloadService (optional)
|
||||
null, // chatGateway (optional)
|
||||
mockMcpClient as never,
|
||||
allowAuthorization as never,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -245,9 +245,21 @@ describe('EnrollmentService.createToken', () => {
|
||||
const after = Date.now();
|
||||
|
||||
const expiresMs = new Date(result.expiresAt).getTime();
|
||||
// Should be at most 900s from now
|
||||
expect(expiresMs - before).toBeLessThanOrEqual(900_000 + 100);
|
||||
|
||||
// The property under test is CLAMPING: a 9999s request must come back as 900s.
|
||||
// The gap between clamped and unclamped is 9_099_000 ms, so the tolerance below
|
||||
// only has to exceed CI scheduling jitter — it does not need to be tight to keep
|
||||
// the assertion discriminating. A 5s allowance consumes 0.05% of that margin and
|
||||
// an unclamped result still misses by three orders of magnitude.
|
||||
//
|
||||
// It was 100ms and failed on a loaded agent at 900_106 — 6ms over (#1090). A
|
||||
// wall-clock budget sized to a fast machine is a flake, not a tighter test.
|
||||
const CI_JITTER_MS = 5_000;
|
||||
expect(expiresMs - before).toBeLessThanOrEqual(900_000 + CI_JITTER_MS);
|
||||
expect(expiresMs - after).toBeGreaterThanOrEqual(0);
|
||||
// Explicitly pin the clamp itself, independent of any timing allowance:
|
||||
// unclamped (9999s) would exceed this by ~9_099_000 ms.
|
||||
expect(expiresMs - before).toBeLessThan(1_000_000);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -159,6 +159,7 @@ describe('ReloadService — /reload command sanitizes plugin errors', () => {
|
||||
reloadService,
|
||||
mockChatGateway as never,
|
||||
mockMcpClient as never,
|
||||
{ authorize: vi.fn().mockResolvedValue({ allowed: true }) } as never,
|
||||
);
|
||||
|
||||
const payload: SlashCommandPayload = { command: 'reload', conversationId: 'conv-1' };
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Administrator Guide
|
||||
|
||||
> **Status:** Partially migrated. Current SSO and local upgrade/recovery procedures are available; held procedures are labeled non-operative.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Administrator Operations
|
||||
|
||||
> **Status:** Partially migrated. Procedures explicitly identify whether they are current or held.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Mos Connector Lease Operations — M1
|
||||
|
||||
> **Status:** Held / non-operative.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Upgrade safety and recovery
|
||||
|
||||
> **Supported route:** an already installed `mosaic` CLI using the local PGlite
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Security
|
||||
|
||||
> **Status:** Partially migrated. The SSO provider and Discord ingress security pages are current.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Discord ingress security
|
||||
|
||||
> **Status:** Current Discord behavior only. Telegram shared-contract parity, Matrix channel ingress, and a gateway-wide shared adapter registry are not implemented or are not proven by the current source/tests.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
title: SSO Providers
|
||||
type: runbook
|
||||
audience: admin
|
||||
status: current
|
||||
source_of_truth: false
|
||||
---
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# API Documentation
|
||||
|
||||
> **Status:** Scaffold only. The canonical gateway contract has not yet been migrated into this directory.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Developer Guide
|
||||
|
||||
> **Status:** Partially migrated. Architecture, lease-broker verification, and channel-adapter authoring pages are current; other contributor chapters remain unmigrated.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Architecture
|
||||
|
||||
> **Status:** Partially migrated. The lease-broker security-contract pages below are current references; the remaining architecture pages are still being classified.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Channel protocol architecture
|
||||
|
||||
> **Status:** Current shared type contract and Discord compatibility baseline. The shared gateway registry, Telegram parity, Matrix integration, identity-linking, and multi-surface multiplexing described below are draft or unimplemented.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Compaction observer revocation and runtime generations
|
||||
|
||||
> **Status:** Current contract reference.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Architecture Decisions
|
||||
|
||||
> **Status:** Current decision index. A decision describes an implemented and accepted boundary; draft proposals belong under `rfcs/` or `docs/plans/`.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: record
|
||||
status: active
|
||||
---
|
||||
|
||||
# Mos Runtime Portability M1 — Logical Identity and Fencing
|
||||
|
||||
> **Decision status:** Current implemented decision (M1).
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Authenticated external lease broker protocol
|
||||
|
||||
> **Status:** Current contract reference.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# WI-1 lease broker security notes
|
||||
|
||||
> **Status:** Current contract reference.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Whole mutator-class lease gate
|
||||
|
||||
> **Status:** Current contract reference.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Architecture RFCs
|
||||
|
||||
> **Status:** Current proposal index. RFCs are draft design material and have no operational or implementation authority until an approved decision and implementation evidence supersede them.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
---
|
||||
|
||||
# RFC: Optional AI Egress Gateways
|
||||
|
||||
> **Status:** Draft / proposed — not approved, not current, and not integrated.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Channel adapters
|
||||
|
||||
> **Status:** Current shared channel types plus the Discord reference/compatibility implementation. A shared gateway adapter registry, Telegram parity, and Matrix channel integration remain unimplemented or unproven.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
title: Lease-broker operations
|
||||
type: runbook
|
||||
audience: developer
|
||||
status: current
|
||||
source_of_truth: false
|
||||
---
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: tracking
|
||||
status: active
|
||||
---
|
||||
|
||||
# Mission Manifest — MVP
|
||||
|
||||
> Top-level rollup tracking Mosaic Stack MVP execution.
|
||||
|
||||
+286
@@ -1,5 +1,19 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
---
|
||||
|
||||
# PRD: Mosaic Stack v0.1.0
|
||||
|
||||
## Current addendum: #1194 — Installed framework-tool drift detection
|
||||
|
||||
- Compare the framework tools shipped with the executing Mosaic package against the deployed `$MOSAIC_HOME/tools` tree by content hash.
|
||||
- Treat every shipped `tools/**` file as framework-owned/required according to `framework-manifest.txt`, while excluding the explicit operator-owned credential carve-out and preserving installed-only operator/unknown files.
|
||||
- Distinguish and count `IN_SYNC`, `STALE`, `NOT_INSTALLED`, and installed-only classifications; fail non-zero when shipped tools are stale or absent and refuse self-comparison that would make drift unobservable.
|
||||
- Surface the observational check through `mosaic doctor`; do not refresh files, restart seats, or mutate live tooling.
|
||||
- Document identity/messaging/gate behavior changes in the current stale set, the reviewed quiet-window keep-mode refresh command, and post-refresh probes against the installed path.
|
||||
- Prove by construction that a stale and missing deployed tool are detected; that regression must fail before this checker exists.
|
||||
|
||||
## Metadata
|
||||
|
||||
- **Owner:** Jason Woltje
|
||||
@@ -102,6 +116,128 @@ Context compaction, session replacement, and same-PID runtime reloads can leave
|
||||
|
||||
---
|
||||
|
||||
## Pi Persistent Goal Loop (#1150)
|
||||
|
||||
### Problem and objective
|
||||
|
||||
A Pi agent can stop after a plausible-looking answer even when the operator's broader objective is
|
||||
not complete, and ordinary compaction can weaken or omit the original objective. Mosaic needs an
|
||||
optional, operator-controlled goal loop that keeps a Pi session oriented, checks progress at native
|
||||
lifecycle boundaries, and resumes work until completion is verified or a bounded safety state is
|
||||
reached.
|
||||
|
||||
The objective is a Mosaic-owned Pi extension deployed from the framework into
|
||||
`~/.config/mosaic/runtime/pi/`. It must not install into or depend on `~/.pi/agent/extensions/`.
|
||||
|
||||
### Scope
|
||||
|
||||
#### In scope
|
||||
|
||||
1. `PGL-REQ-01`: The framework SHALL ship a dedicated Pi goal extension under
|
||||
`packages/mosaic/framework/runtime/pi/`, seed it under `$MOSAIC_HOME/runtime/pi/`, and make
|
||||
`mosaic pi` load it alongside the core Mosaic extension when present.
|
||||
2. `PGL-REQ-02`: `/goal` SHALL support setting a goal plus status, pause, resume, cancel, and help
|
||||
operations without silently replacing an active goal.
|
||||
3. `PGL-REQ-03`: Active branch-specific goal state SHALL be persisted in Pi custom session entries,
|
||||
restored on session start and tree navigation, and never rely on a compaction summary as its
|
||||
source of truth.
|
||||
4. `PGL-REQ-04`: A hidden goal contract SHALL be injected through Pi's `context` event before every
|
||||
model request so it remains effective across tool turns, retries, and post-compaction requests.
|
||||
5. `PGL-REQ-05`: The harness SHALL inspect every `turn_end` and successful `session_compact` event.
|
||||
A structured terminating goal-report tool SHALL capture `continue`, evidence-bearing `achieved`,
|
||||
or `blocked` status without requiring a redundant model turn.
|
||||
6. `PGL-REQ-06`: An achievement claim SHALL remain provisional until a second consecutive
|
||||
evidence-bearing verification report. Any continuation report or successful compaction during
|
||||
verification SHALL reset the verification sequence.
|
||||
7. `PGL-REQ-07`: Continuation SHALL be initiated at safe lifecycle boundaries, primarily
|
||||
`agent_settled`; manual compaction and restored active sessions may schedule a deferred idle
|
||||
continuation without re-entering compaction handlers.
|
||||
8. `PGL-REQ-08`: The loop SHALL have operator cancellation plus bounded turn and repeated-no-progress
|
||||
limits. Exhausted or blocked goals pause rather than continuing indefinitely.
|
||||
9. `PGL-REQ-09`: Framework installation and update SHALL preserve normal manifest ownership: the
|
||||
goal extension is framework-owned under `runtime/**`, while no goal extension or configuration
|
||||
asset is created or modified under the operator's main Pi configuration. Pi remains the owner of
|
||||
its native session files used by `appendEntry()`.
|
||||
|
||||
#### Out of scope
|
||||
|
||||
1. A mathematical guarantee that an arbitrary natural-language goal is semantically complete.
|
||||
2. Automatically executing user-supplied shell predicates or accepting executable validation code in
|
||||
`/goal` arguments.
|
||||
3. Restarting Pi after process, host, or supervisor failure; the existing Mosaic fleet/runtime
|
||||
supervisor owns process durability.
|
||||
4. Gateway, database, web UI, Discord, or cross-harness goal orchestration in this slice.
|
||||
|
||||
### User and stakeholder requirements
|
||||
|
||||
- An operator can start a goal from Pi and see its current phase, evidence, limits, and latest report.
|
||||
- The agent remains oriented after each turn and compaction until verified, paused, blocked,
|
||||
exhausted, or cancelled.
|
||||
- Local testing uses a file under `~/.config/mosaic/runtime/pi/`; the feature never writes an
|
||||
extension asset to `~/.pi/agent/extensions/`.
|
||||
- Framework updates deploy the same reviewed extension source through Mosaic's existing manifest
|
||||
sync path.
|
||||
|
||||
### Non-functional requirements
|
||||
|
||||
1. **Safety:** bounded continuation, explicit cancellation, no arbitrary command execution, and no
|
||||
completion without non-empty reported evidence.
|
||||
2. **Reliability:** serialized continuation scheduling, branch-aware restoration, compaction-safe
|
||||
context injection, and stale-timer cancellation on session shutdown.
|
||||
3. **Performance:** no extra nested judge-model request on every turn; structured reporting uses the
|
||||
active agent's final terminating tool call.
|
||||
4. **Observability:** Pi status/notifications expose phase and bounded counters without recording
|
||||
credentials or hidden model reasoning.
|
||||
5. **Maintainability:** the state machine is deterministic and behavior-tested independently from Pi
|
||||
provider/network access.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
1. `AC-PGL-01`: A framework-sync fixture installs the extension at
|
||||
`$MOSAIC_HOME/runtime/pi/goal-extension.ts`, and launcher tests prove both Mosaic Pi extensions are
|
||||
emitted in deterministic order while absent optional files remain backward-compatible.
|
||||
2. `AC-PGL-02`: Command tests prove set/status/pause/resume/cancel behavior, active-goal replacement
|
||||
refusal, and bounded input handling.
|
||||
3. `AC-PGL-03`: Lifecycle tests prove every turn is recorded, active context is injected on every
|
||||
request, two evidence-bearing achievement reports are required, and `agent_settled` continues an
|
||||
unmet goal without duplicate scheduling.
|
||||
4. `AC-PGL-04`: Compaction and restoration tests prove goal state survives, verification is reset and
|
||||
rechecked after compaction, manual compaction continuation is deferred until idle, and tree/session
|
||||
branch state is reconstructed correctly.
|
||||
5. `AC-PGL-05`: Limit tests prove max-turn and repeated-no-progress exhaustion stop autonomous
|
||||
continuation, while pause/cancel/blocked states do not restart.
|
||||
6. `AC-PGL-06`: Focused tests, package typecheck/lint/test, repository quality gates, a local Pi load
|
||||
smoke test from `~/.config/mosaic/runtime/pi/`, independent review, and terminal-green CI pass before
|
||||
issue #1150 closes.
|
||||
|
||||
### Constraints, risks, and assumptions
|
||||
|
||||
- Dependency: Pi's extension API must continue to provide `registerCommand`, `registerTool`,
|
||||
`context`, `turn_end`, `agent_settled`, `session_compact`, session custom entries, and terminating
|
||||
tool results.
|
||||
- Risk: the working agent can overstate completion. Mitigation: structured evidence, a mandatory
|
||||
second verification pass, explicit semantic limitations, and operator-visible reports.
|
||||
- Risk: an impossible goal can consume unbounded resources. Mitigation: hard turn/no-progress bounds
|
||||
and paused terminal states.
|
||||
- Risk: automatic continuation can race compaction or session replacement. Mitigation: drive from
|
||||
`agent_settled`, defer idle restarts, generation-check timers, and clear timers on shutdown.
|
||||
- `ASSUMPTION:` Two consecutive evidence-bearing reports are the initial local verification policy;
|
||||
rationale: it provides a real recheck without doubling every turn's model cost. Future policy may
|
||||
add independent or deterministic validators.
|
||||
- `ASSUMPTION:` Default limits are 40 turns and 6 repeated no-progress reports, configurable only by
|
||||
bounded Mosaic environment settings; rationale: useful persistence with a finite autonomous budget.
|
||||
- `ASSUMPTION:` Documentation remains canonical in-repo for this slice; no external docs publication
|
||||
is requested.
|
||||
|
||||
### Testing and delivery intent
|
||||
|
||||
Use TDD for the deterministic controller and lifecycle invariants. Test with fake Pi lifecycle
|
||||
objects first, then run a local load/smoke test from the deployed Mosaic path. Deliver source, tests,
|
||||
launcher wiring, framework/runtime documentation, user/developer guides, and sitemap updates in one
|
||||
reviewed squash PR to `main` with terminal-green CI.
|
||||
|
||||
---
|
||||
|
||||
## Fleet Declarative Configuration Management Workstream (FCM, #758)
|
||||
|
||||
### Problem and objective
|
||||
@@ -146,6 +282,68 @@ lands. M0 consists only of these normative requirements, the complete task DAG,
|
||||
documentation IA checklist, and the legacy example/profile disposition inventory. Subsequent cards
|
||||
are defined in [docs/TASKS.md](./TASKS.md) and must remain one card/one PR.
|
||||
|
||||
### Fleet git identity launch propagation (#1043)
|
||||
|
||||
#### Problem and objective
|
||||
|
||||
A fleet seat can have a registered per-agent Git credential while its launched runtime process lacks
|
||||
`MOSAIC_GIT_IDENTITY`. The credential resolver then cannot select the seat identity reliably, which
|
||||
blocks repository operations on fail-closed estates and can fall through to an unrelated identity on
|
||||
estates where that refusal is not active. The objective is to make Git identity a deterministic,
|
||||
roster-derived part of the generated launch projection and prove it reaches the launched process.
|
||||
|
||||
#### Normative requirements
|
||||
|
||||
1. `FGI-REQ-01`: Every generated fleet agent projection SHALL declare
|
||||
`MOSAIC_GIT_IDENTITY=<MOSAIC_AGENT_NAME>`; a differing or unsafe identity SHALL fail closed before
|
||||
tmux launch.
|
||||
2. `FGI-REQ-02`: The clean `/usr/bin/env -i` pane boundary SHALL pass every variable declared by the
|
||||
generated projection, including `MOSAIC_GIT_IDENTITY`, to the launched runtime process.
|
||||
3. `FGI-REQ-03`: A behavioral integration test SHALL set-compare the complete generated projection
|
||||
against the launched process environment. Source-text/string-presence assertions are insufficient.
|
||||
4. `FGI-REQ-04`: Verification SHALL include RED-first evidence and a delete-the-subject mutation that
|
||||
removes Git-identity pane propagation and makes the behavioral test fail.
|
||||
|
||||
#### Acceptance criteria
|
||||
|
||||
1. `AC-FGI-01`: A launched seat process contains every key/value pair declared by its generated
|
||||
environment projection, including the roster-derived Git identity.
|
||||
2. `AC-FGI-02`: Missing, unsafe, or split Git identity is rejected before a tmux session is created.
|
||||
3. `AC-FGI-03`: Focused launcher and generated-environment tests, repository quality gates,
|
||||
independent review, and the required RED/green/R7 evidence are recorded before push.
|
||||
|
||||
### Framework shell assertion portability (#1098)
|
||||
|
||||
#### Problem and objective
|
||||
|
||||
The blocking framework-shell chain can report that a pane command omitted `/usr/bin/env -i` even when
|
||||
`-i` matched successfully. A short-circuiting `grep -q` under `set -o pipefail` may close its pipe after
|
||||
the match and cause an upstream producer to exit with SIGPIPE, turning a valid semantic result into a
|
||||
nonzero aggregate pipeline. The objective is to inspect the captured NUL-delimited argv directly and
|
||||
make failures carry the observed records needed for diagnosis.
|
||||
|
||||
#### Normative requirements
|
||||
|
||||
1. `FSP-REQ-01`: The pane-boundary test SHALL validate an adjacent `/usr/bin/env`, `-i` argv pair from
|
||||
the authoritative NUL-delimited tmux capture without a short-circuit pipeline whose upstream status
|
||||
can override a successful match.
|
||||
2. `FSP-REQ-02`: Missing, reversed, or non-adjacent boundary tokens SHALL fail, while valid boundaries
|
||||
SHALL remain valid regardless of trailing argv size, pipe capacity, process scheduling, or host/CI
|
||||
utility implementation.
|
||||
3. `FSP-REQ-03`: A failed boundary check SHALL print stable indexed, shell-escaped observed argv records
|
||||
before exiting nonzero; the fixture SHALL continue to contain generated non-secret launch data only.
|
||||
4. `FSP-REQ-04`: Verification SHALL include RED-first large-payload evidence, negative token-order
|
||||
controls, the complete focused launcher suite, canonical Woodpecker CI, and independent review.
|
||||
|
||||
#### Acceptance criteria
|
||||
|
||||
1. `AC-FSP-01`: A large captured argv with adjacent `/usr/bin/env`, `-i` passes even when the former
|
||||
`grep -q` pipeline returns nonzero from an upstream SIGPIPE.
|
||||
2. `AC-FSP-02`: Missing executable, missing flag, and detached/reversed flag fixtures return nonzero and
|
||||
emit the indexed observed argv.
|
||||
3. `AC-FSP-03`: The focused suite passes on the development host and CI image, and the merged-main
|
||||
Woodpecker pipeline is terminal green before #1098 closes.
|
||||
|
||||
---
|
||||
|
||||
## Exact Cross-Harness Fleet Communications Contract (#766)
|
||||
@@ -1345,6 +1543,59 @@ All work is **alpha** (< 0.1.0) until Jason approves 0.1.0 beta release.
|
||||
|
||||
---
|
||||
|
||||
## Workspace placement guard hardening (#1174)
|
||||
|
||||
### Problem and objective
|
||||
|
||||
The Bash pre-tool guard must prevent Git checkouts and repository state from being placed under
|
||||
`$HOME` without refusing ordinary Git commands merely because a source, option value, branch name,
|
||||
or metadata mentions `$HOME`. A guard that over-blocks routine work is unsafe because operators
|
||||
will route around it.
|
||||
|
||||
### Scope and requirements
|
||||
|
||||
1. `WPG-REQ-01`: `git clone` and `git worktree add` placement SHALL be judged from their placement
|
||||
operands, not from every HOME-shaped word in the command.
|
||||
2. `WPG-REQ-02`: Clone sources, references, templates, environment assignments, and non-placement
|
||||
worktree metadata MAY resolve under HOME when all placement operands resolve elsewhere.
|
||||
3. `WPG-REQ-03`: Both attached and separate-value `--separate-git-dir` forms SHALL remain placement
|
||||
operands and SHALL be refused when they resolve under HOME.
|
||||
4. `WPG-REQ-04`: Option classification SHALL account for Git's rule-generated boolean negations
|
||||
without relying on an enumerable allowlist of flag spellings.
|
||||
5. `WPG-REQ-05`: Quote removal, escapes, shell command boundaries, redirections, and end-of-options
|
||||
handling SHALL preserve existing fail-closed checkout coverage.
|
||||
6. `WPG-REQ-06`: Absolute placement aliases SHALL resolve shell-known HOME spellings, dot segments,
|
||||
repeated separators, and existing symlink parents before the HOME boundary comparison.
|
||||
7. Relative targets whose effective path depends on the shell cwd are out of scope and tracked by
|
||||
#1197.
|
||||
|
||||
### Acceptance and verification
|
||||
|
||||
1. Git's own option parser accepts each tested flag, including generated `--no-*` forms, while the
|
||||
guard allows a HOME-valued source with an explicit safe destination.
|
||||
2. Equivalent clone and worktree fixtures cover rule-generated negations and remain discriminating
|
||||
against the prior head where the defect existed.
|
||||
3. Real HOME destinations and both `--separate-git-dir` forms remain blocked, including placements
|
||||
after shell command boundaries.
|
||||
4. The full hermetic guard suite, syntax/static checks, adversarial probes, independent review, and
|
||||
terminal-green CI pass before merge.
|
||||
5. Any option-classification residual is documented with its deliberate failure direction.
|
||||
|
||||
### Constraints, risks, and assumptions
|
||||
|
||||
- Security and usability are co-equal: neither a placement bypass nor routine over-block is an
|
||||
acceptable repair.
|
||||
- `ASSUMPTION:` The value-taking option surface exposed by the installed Git version is closed and
|
||||
measurable through Git's own parser/help output; rationale: boolean flags are rule-generated,
|
||||
while separate-value options have explicit grammar and must be classified as such.
|
||||
- Risk: a future Git release may add a new value-taking placement option. Mitigation: document the
|
||||
chosen residual direction and pin every currently supported placement option in behavior tests.
|
||||
- Risk: a symlink can be replaced after pre-execution canonicalization. Mitigation: resolve every
|
||||
existing parent physically and document the remaining inherent TOCTOU window; the worktree helper
|
||||
remains the authoritative path-derivation mechanism, with atomic closure tracked by #1199.
|
||||
|
||||
---
|
||||
|
||||
## Assumptions
|
||||
|
||||
1. RESOLVED: **pgvector is sufficient** for semantic search at v0.1.0 scale (personal/family/team = thousands to low hundreds-of-thousands of vectors). `@mosaicstack/memory` defines a `VectorStore` interface with pgvector as the default adapter. The interface boundary makes Qdrant a drop-in migration if PG resource contention or scale demands it later. Zero additional infrastructure for v0.1.0. Rationale: Reduces ops burden; pgvector HNSW indexes are fast at this scale; interface abstraction costs almost nothing now.
|
||||
@@ -1368,3 +1619,38 @@ All work is **alpha** (< 0.1.0) until Jason approves 0.1.0 beta release.
|
||||
10. ASSUMPTION: **Conversations and messages get their own PG tables** (not stored in brain's entity model). They follow a chat-specific schema with proper foreign keys to users and projects. Rationale: Chat has different access patterns (streaming, pagination, search) than brain entities.
|
||||
|
||||
11. RESOLVED: **Pi handles all target LLM providers natively.** Anthropic, OpenAI/Codex, Z.ai, Ollama, LM Studio, and llama.cpp are all supported via Pi's built-in providers or `models.json` configuration with `openai-completions` API type. No custom provider adapters needed in @mosaicstack/agent — only configuration management.
|
||||
|
||||
---
|
||||
|
||||
## Release Integrity Workstream (RI, #1275)
|
||||
|
||||
### Problem and objective
|
||||
|
||||
At `next` 476db12b (review of 2026-08-17), publication from `next` is not bound to the full verification pipeline for the same commit: the publish pipeline's publish steps depend on `build` only, while ordinary push CI excludes `next`. Public Forge/MACP paths contain false-success placeholders: a stub executor that reports `completed` with exit zero, planning/remediation gates that execute literal `true`, a review gate that echoes an approving verdict, and a gate runner that treats empty commands and unimplemented CI-provider gates as passing. Shipping UI surfaces can render a failed fetch as an empty, healthy collection.
|
||||
|
||||
Objective: for alpha 0.0.50, the release cannot publish, report, or display work state that the repository has not actually verified. Decisions SDLC-D-033 through SDLC-D-038 (Jason, 2026-08-17) scope this floor; full decision text and required-behavior lists live in jarvis-brain `docs/plans/2026-08-16_mosaic-stack-sdlc-protocol.md` and `data/decisions/mosaic-stack-sdlc-protocol.json`. This section restates only the normative requirements.
|
||||
|
||||
### Normative requirements
|
||||
|
||||
1. **RI-N1 Exact-commit publication verification (SDLC-D-034).** One canonical terminal verification command performs self-contained re-verification in the publish pipeline against the job's checked-out commit before any external publication effect. The command contains or invokes the complete mandatory verification set (semantic parity with the PR merge gate, including sanitization, upgrade-guard, typecheck, lint, format check, tests, and build); CI and publication do not maintain separate semantic checklists. Every publish step depends on the verification step in the executable pipeline DAG. Provider commit identity and `git rev-parse HEAD` must identify the same commit. Missing, skipped, cancelled, stale, or inconclusive checks fail closed. Documentation-only runs may skip publication but cannot bypass verification when a publication effect will occur. A negative control must prove that a broken check blocks every publish step.
|
||||
|
||||
2. **RI-N2 Fail-closed Forge/MACP with explicit simulation (SDLC-D-035).** Simulation requires explicit caller intent (e.g. `--simulate`) and produces a distinct typed `simulated` state that can never satisfy dependencies, acceptance criteria, gates, merge, or release. Normal execution exits nonzero with a typed capability failure when a required executor, reviewer, command, or CI provider is absent — no stub completion, no literal-`true` gates, no synthetic approvals, no empty-command passes. A manual gate with no automation enters a waiting state; it does not pass. Positive tests prove explicit simulation still works; negative controls prove simulation and every missing-provider case cannot advance lifecycle state.
|
||||
|
||||
3. **RI-N3 One transitional PRD authority (SDLC-D-036).** `@mosaicstack/prdy` structured storage under `docs/prdy/`, driven by `mosaic mission --plan`, is the authoritative PRD representation for the alpha. `mosaic prdy` either routes through the same application service or operates only as an explicit, named Markdown import/export adapter; `docs/PRD.md` is not a peer authority. `mission --plan` must persist the mission↔PRD linkage (mission id/version, PRD id/version, selected requirements). Markdown output is a generated view carrying source identity; editing it cannot mutate authority silently. Import is explicit, validated, and conflict-aware (proposed successor, never overwrite). Structural validity is separate from approval.
|
||||
|
||||
4. **RI-N4 One quality-rails evaluator (SDLC-D-037).** The TypeScript quality-rails package is the sole authoritative evaluator. A complete probe inventory maps every current TypeScript and shell check to one canonical check with disposition (preserve/strengthen/retire, each named). Effective shell enforcement probes are absorbed before their independent paths retire; expected-file presence alone is not parity. The evaluator returns typed results (`passed`/`failed`/`blocked`/`error`/`not-applicable`) with check version, subject, and reason; missing implementation, missing input, unknown check, process error, timeout, or malformed output can never become `passed` or an unqualified skip. Check definitions and policy are versioned and digested. Shell commands become thin adapters with no separate verdict logic. The canonical terminal verification command (RI-N1) invokes this evaluator rather than duplicating its logic. Contract, parity, and negative-control tests are required, plus independent review of probe equivalence.
|
||||
|
||||
5. **RI-N5 Consequence-aware stale UI (SDLC-D-038).** Mission Control distinguishes typed freshness states (`current`, `stale`, `partial`, `unknown`, `unavailable`) rather than inferring from empty arrays or null. A failed fetch never renders as an empty healthy collection. Last-known data may display for situational awareness only with source identity, version, and age visibly labeled; any derived completion/assurance/release verdict whose inputs are stale becomes `unknown`; all state-changing actions are disabled until fresh state loads and is revalidated. With no verified snapshot, surfaces show an explicit unavailable state. Cache corruption, cross-workspace data, schema mismatch, and version regression invalidate the snapshot. Tests cover the failure matrix (network, auth, malformed, partial, corruption, stale age, schema mismatch, recovery, stale-action rejection) with negative controls proving no case yields a current green verdict or enabled mutation.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- AC-RI-1: A push to `next` that fails any mandatory verification step publishes nothing (no npm package, no image), demonstrated by a checked-in negative control and by pipeline evidence on a real `next` publish run where the verification step is green and every publish step depends on it.
|
||||
- AC-RI-2: With no executor/reviewer/CI provider wired, Forge and MACP normal runs exit nonzero with typed capability failures; with `--simulate`, runs complete but every result is typed `simulated` and cannot satisfy any gate, dependency, or completion state — proven by unit tests including negative controls.
|
||||
- AC-RI-3: A PRD created or revised through either `mosaic mission --plan` or `mosaic prdy` resolves to one authority under `docs/prdy/` with stable identities and versions; the mission↔PRD linkage survives restart; a Markdown export is labeled as generated and cannot silently become a second writer; divergent legacy content blocks baseline claims until explicitly resolved — proven by contract tests.
|
||||
- AC-RI-4: `quality-rails check` through any entry point (TS CLI, framework shell adapter) returns the same typed verdict for the same subject; the probe inventory names every legacy check's disposition; a deliberately broken probe fails closed — proven by contract/parity/negative-control tests and independent review of probe equivalence.
|
||||
- AC-RI-5: No shipping surface renders a failed fetch as an empty healthy state; stale/partial/unavailable states are typed, labeled, and mutation-disabled — proven by the failure-matrix tests.
|
||||
- AC-RI-6: All cards merged to `next` via squash PR with terminal-green CI; release evidence for 0.0.50 records commit, verification run, and published artifacts.
|
||||
|
||||
### Out of scope
|
||||
|
||||
The canonical dispatcher/control-plane vertical slice (work graph, execution attempts, fenced leases, typed check-in, independent verifier dispatch) is decided post-alpha (SDLC-D-033, option B). Multi-pipeline verification certificates (SDLC-D-034 option B) are post-alpha. Full AF-1..AF-4 objective matrices and Mission Control portfolio surfaces are post-alpha.
|
||||
|
||||
+44
-8
@@ -1,3 +1,9 @@
|
||||
---
|
||||
kind: spec
|
||||
source_of_truth: true
|
||||
status: active
|
||||
---
|
||||
|
||||
# Mosaic Stack Documentation
|
||||
|
||||
This directory is the canonical home for Mosaic Stack product, architecture, API, operations, and delivery documentation.
|
||||
@@ -146,21 +152,51 @@ Every canonical page should:
|
||||
7. Include an owner or maintenance responsibility for operationally sensitive content.
|
||||
8. Link to the relevant book index and related canonical pages.
|
||||
|
||||
Recommended front matter for canonical pages:
|
||||
Required front matter for every canonical page:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Human-readable page title
|
||||
type: guide
|
||||
audience: developer
|
||||
status: current
|
||||
source_of_truth: false
|
||||
kind: tracking | projection | spec | guide | record | superseded
|
||||
status: active # or: completed | superseded-by: <path>
|
||||
source_of_truth: false # optional, defaults false
|
||||
audience: developer # optional: user | admin | developer | all
|
||||
title: Human-readable page title # optional
|
||||
---
|
||||
```
|
||||
|
||||
Allowed `type` values include `guide`, `concept`, `reference`, `decision`, `rfc`, and `runbook`. Allowed `audience` values are `user`, `admin`, `developer`, and `all`. Allowed `status` values are `current`, `draft`, `deprecated`, and `historical`.
|
||||
`kind` says what the document **is**. One value, required, and it follows the document's content,
|
||||
never its filename: a file named `TASKS.md` whose body says "this is a build plan, not a task
|
||||
tracker" is a `spec`.
|
||||
|
||||
Indexes may omit front matter when their purpose is self-evident. A page with normative authority must explicitly identify the authority it owns and the boundaries of that authority.
|
||||
| kind | rule |
|
||||
| ---------- | ---------------------------------------------------------------- |
|
||||
| tracking | Live state, single-writer. Never a spec |
|
||||
| projection | Generated. Never hand-edited. MUST have a drift test |
|
||||
| spec | How to build one goal or workstream |
|
||||
| guide | Explains use. Decides nothing |
|
||||
| record | What happened. Never authoritative, never updated after the fact |
|
||||
| superseded | Kept for history, and NAMES its replacement |
|
||||
|
||||
`source_of_truth` is a separate boolean because authority is **orthogonal to kind**. A document can
|
||||
be a `spec` and still be the thing everything else answers to;
|
||||
`docs/requirements/native-kanban-sot.md` is exactly that. Folding authority into `kind` forced one
|
||||
field to carry two independent facts, which is why an earlier draft of this contract could not
|
||||
classify that file at all.
|
||||
|
||||
`status` has three values. `active` means in force. `completed` means the work the document
|
||||
describes landed and the document is now finished rather than stale; executed implementation plans
|
||||
take this. `superseded-by: <path>` replaces `status` entirely and names the replacement.
|
||||
|
||||
**This contract covers `.md` files only.** It is not an omission: a YAML document cannot carry YAML
|
||||
front matter. The repository's own `[email protected]` throws `Source contains multiple documents` on a
|
||||
front-mattered `.yaml`, and `parseNorthStar` (`packages/mosaic/src/commands/fleet.ts:242`) is a live
|
||||
consumer that would break. `.yaml` sources declare their own kind inside the document or not at all.
|
||||
|
||||
A `parent` field is planned and is deliberately not yet required; it lands once the docs flatten
|
||||
settles the paths it would point at.
|
||||
|
||||
Indexes may omit front matter when their purpose is self-evident. A page with normative authority
|
||||
must explicitly identify the authority it owns and the boundaries of that authority.
|
||||
|
||||
## Obsidian and link conventions
|
||||
|
||||
|
||||
+15
-1
@@ -1,5 +1,19 @@
|
||||
# Tasks — MVP (Top-Level Rollup)
|
||||
|
||||
> ---
|
||||
>
|
||||
> **STATUS: SUPERSEDED — 2026-08-20.** kind `tracking` · superseded by `docs/fleet/NORTH_STAR.yaml`
|
||||
>
|
||||
> This file is the pre-backlog tracking mechanism. `NS-2` in the north star declares the
|
||||
> replacement: every backlog item is a Mosaic Backlog card projected from the YAML. That
|
||||
> model replaced this one and nobody retired the old file, so it kept reading as
|
||||
> authoritative while going stale.
|
||||
>
|
||||
> **Do not trust a status in this file.** Verified 2026-08-20: it was already behind the
|
||||
> code when it froze five weeks ago.
|
||||
>
|
||||
> Kept as a record of what was believed. Do not update it; update the YAML.
|
||||
|
||||
> Single-writer: orchestrator only. Workers read but never modify.
|
||||
>
|
||||
> **Mission:** mvp-20260312
|
||||
@@ -108,7 +122,7 @@ Active workstream is **W1 — Federation v1**. Workers should:
|
||||
|
||||
## north-star doctrine consolidation — doc PR — feat/north-star-doctrine
|
||||
|
||||
- Status: applied Mos's consolidated merge-map to docs/fleet/north-star.md (budget governance + control plane/central register + 200k cap + delegation + unified-identity Fleet + role-based naming + tmux security + drift re-captures). Doctrine only; #622/#623/#625/#628 out-of-scope. Conflict checklist green. Detail: scratchpads/north-star-doctrine.md.
|
||||
- Status: applied Mos's consolidated merge-map to docs/fleet/FLEET-DOCTRINE.md (budget governance + control plane/central register + 200k cap + delegation + unified-identity Fleet + role-based naming + tmux security + drift re-captures). Doctrine only; #622/#623/#625/#628 out-of-scope. Conflict checklist green. Detail: scratchpads/north-star-doctrine.md.
|
||||
|
||||
## #631 — re-seed preserves user fleet data (CRITICAL) — fix/631-reseed-preserves-fleet-data
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# User Guide
|
||||
|
||||
> **Status:** Partially migrated. The quickstart, web-dashboard reference, and Discord conversation workflow are current.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
title: Mosaic Stack Quickstart
|
||||
type: guide
|
||||
audience: user
|
||||
status: current
|
||||
source_of_truth: false
|
||||
---
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
title: Mosaic web dashboard
|
||||
type: guide
|
||||
audience: user
|
||||
status: current
|
||||
source_of_truth: false
|
||||
---
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Discord conversations
|
||||
|
||||
> **Status:** Current Discord workflow for an administrator-provisioned, authorized guild channel.
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
3. [Provider Configuration](#provider-configuration)
|
||||
4. [MCP Server Configuration](#mcp-server-configuration)
|
||||
5. [Environment Variables Reference](#environment-variables-reference)
|
||||
6. [Local Fleet Canary](./fleet-local-canary.md)
|
||||
6. [Pi Goal Loop Operations](#pi-goal-loop-operations)
|
||||
7. [Local Fleet Canary](./fleet-local-canary.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -264,6 +265,16 @@ Each OIDC provider requires its client ID, client secret, and issuer URL togethe
|
||||
| `AGENT_SYSTEM_PROMPT` | — | Platform-level system prompt injected into all sessions |
|
||||
| `AGENT_USER_TOOLS` | all tools | Comma-separated allowlist of tools for non-admin users |
|
||||
|
||||
### Mosaic Pi goal loop
|
||||
|
||||
| Variable | Default | Description |
|
||||
| ----------------------------- | ------- | -------------------------------------------------------------------- |
|
||||
| `MOSAIC_GOAL_MAX_TURNS` | `40` | Per-goal autonomous turn limit; accepted range `1..500` |
|
||||
| `MOSAIC_GOAL_MAX_NO_PROGRESS` | `6` | Consecutive identical progress-report limit; accepted range `1..100` |
|
||||
|
||||
These variables are consumed by the framework-owned Pi goal extension at goal creation. Invalid or
|
||||
out-of-range values fall back to the defaults; they do not disable the bounds.
|
||||
|
||||
### Providers
|
||||
|
||||
| Variable | Default | Description |
|
||||
@@ -374,3 +385,29 @@ Session cleanup is scoped to one session identifier and only removes that sessio
|
||||
| Variable | Default | Description |
|
||||
| ----------------------- | ----------------------------- | ------------------------------------------ |
|
||||
| `MOSAIC_WORKSPACE_ROOT` | monorepo root (auto-detected) | Root path for mission workspace operations |
|
||||
|
||||
---
|
||||
|
||||
## Pi Goal Loop Operations
|
||||
|
||||
The reviewed runtime asset is deployed at
|
||||
`~/.config/mosaic/runtime/pi/goal-extension.ts` by framework install/update. Do not install another
|
||||
copy under `~/.pi/agent/extensions/`; duplicate registration can create suffixed commands and two
|
||||
competing lifecycle controllers.
|
||||
|
||||
Operational checks:
|
||||
|
||||
1. Run `mosaic pi` and verify `/goal help` is available.
|
||||
2. Use `/goal status` to inspect phase, turn/no-progress limits, compaction checks, and evidence.
|
||||
Reports persist in Pi session data; controller-owned state redacts common credential shapes, but
|
||||
Pi's model/tool-call history is separate. Operators must not place secrets or raw sensitive output
|
||||
in goals, pause reasons, or evidence.
|
||||
3. Use `/goal pause <reason>` before planned maintenance or manual investigation. Pause and cancel
|
||||
abort the current goal-driven run when Pi is busy.
|
||||
4. Use `/goal resume` only after addressing a blocker; counters restart with the configured bounds.
|
||||
5. Use `/goal cancel` before replacing an unfinished goal.
|
||||
|
||||
A blocked or exhausted goal remains stopped and visible; Mosaic does not automatically raise its
|
||||
limits or restart the process. Framework sync owns file deployment, while Pi's native session file
|
||||
owns branch replay. Process/host restart remains the responsibility of the existing runtime or fleet
|
||||
supervisor.
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
4. [Tasks](#tasks)
|
||||
5. [Settings](#settings)
|
||||
6. [CLI Usage](#cli-usage)
|
||||
7. [Sub-package Commands](#sub-package-commands)
|
||||
8. [Telemetry](#telemetry)
|
||||
9. [Local Fleet Canary](./fleet-local-canary.md)
|
||||
7. [Pi Persistent Goals](#pi-persistent-goals)
|
||||
8. [Sub-package Commands](#sub-package-commands)
|
||||
9. [Telemetry](#telemetry)
|
||||
10. [Local Fleet Canary](./fleet-local-canary.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -317,6 +318,57 @@ mosaic prdy
|
||||
mosaic quality-rails
|
||||
```
|
||||
|
||||
## Pi Persistent Goals
|
||||
|
||||
`mosaic pi` loads a Mosaic-owned goal extension from
|
||||
`~/.config/mosaic/runtime/pi/goal-extension.ts`. It is deliberately not installed in
|
||||
`~/.pi/agent/extensions/`; framework installation and updates manage it with the rest of the Mosaic
|
||||
runtime assets.
|
||||
|
||||
Start Pi, then set a goal:
|
||||
|
||||
```text
|
||||
/goal set Deliver the feature, tests, documentation, and verification evidence
|
||||
# Shorthand:
|
||||
/goal Deliver the feature, tests, documentation, and verification evidence
|
||||
```
|
||||
|
||||
Control and inspect the loop with:
|
||||
|
||||
| Command | Behavior |
|
||||
| ---------------------- | ------------------------------------------------------------------ |
|
||||
| `/goal status` | Show phase, limits, compaction checks, latest report, and evidence |
|
||||
| `/goal pause [reason]` | Stop autonomous continuation while preserving the goal |
|
||||
| `/goal resume` | Resume with fresh turn and no-progress counters |
|
||||
| `/goal cancel` | Cancel the goal and remove its active status |
|
||||
| `/goal help` | Show command help |
|
||||
|
||||
While a goal is active, Mosaic injects its contract before every Pi model request and checks every
|
||||
completed model/tool turn. The agent ends each work cycle with the structured
|
||||
`mosaic_goal_report` tool. `achieved` is provisional until a second consecutive report rechecks the
|
||||
whole goal with evidence. A continuation report or a successful compaction resets provisional
|
||||
verification.
|
||||
|
||||
Goal statements and reports are stored in Pi session data. Mosaic redacts common credential shapes
|
||||
before appending its goal-state entries and before goal tool output or `/goal status`, but
|
||||
pattern-based redaction is not a secret store. Pi's own model-message and tool-call records are
|
||||
outside that redactor. Never put tokens, passwords, private keys, connection strings, or raw
|
||||
sensitive output in a goal or report; cite the command, artifact, and pass/fail result instead.
|
||||
|
||||
The loop stops instead of running forever when it is paused, blocked, cancelled, verified, reaches
|
||||
its turn limit, or repeats the same no-progress report too many times. Defaults are 40 turns and 6
|
||||
repeated no-progress reports. Operators may lower or raise them within enforced bounds before
|
||||
launching Pi:
|
||||
|
||||
```bash
|
||||
MOSAIC_GOAL_MAX_TURNS=60 MOSAIC_GOAL_MAX_NO_PROGRESS=8 mosaic pi
|
||||
```
|
||||
|
||||
Goal state is branch-specific Pi session data. It survives compaction and session resume, but Pi's
|
||||
process still must be relaunched or supervised after a process/host failure. This initial verifier
|
||||
checks structured evidence twice; it cannot mathematically prove every arbitrary natural-language
|
||||
goal. Use explicit acceptance criteria and inspect `/goal status` for consequential work.
|
||||
|
||||
---
|
||||
|
||||
### Claude Code Skill Registration
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: tracking
|
||||
status: active
|
||||
---
|
||||
|
||||
# Mission Manifest — Federation v1
|
||||
|
||||
> Persistent document tracking full mission scope, status, and session history.
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Tasks — Federation v1
|
||||
|
||||
> ---
|
||||
>
|
||||
> **STATUS: SUPERSEDED — 2026-08-20.** kind `tracking` · superseded by `docs/fleet/NORTH_STAR.yaml`
|
||||
>
|
||||
> This file is the pre-backlog tracking mechanism. `NS-2` in the north star declares the
|
||||
> replacement: every backlog item is a Mosaic Backlog card projected from the YAML. That
|
||||
> model replaced this one and nobody retired the old file, so it kept reading as
|
||||
> authoritative while going stale.
|
||||
>
|
||||
> **Do not trust a status in this file.** Verified 2026-08-20: it was already behind the
|
||||
> code when it froze five weeks ago. `FED-M3-06` is marked not-started and `get.controller.ts`
|
||||
> has existed for eight weeks; `FED-M3-10/11` claim no tests exist while fifteen spec files
|
||||
> do. `FED-M2-DEPLOY-IMG-FIX` names `apps/gateway/Dockerfile`, which is not in the repo.
|
||||
>
|
||||
> Kept as a record of what was believed. Do not update it; update the YAML.
|
||||
|
||||
> Single-writer: orchestrator only. Workers read but never modify.
|
||||
>
|
||||
> **Mission:** federation-v1-20260419
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
# Mosaic Fleet — North Star
|
||||
---
|
||||
kind: spec
|
||||
parent: docs/fleet/NORTH_STAR.yaml
|
||||
status: active
|
||||
---
|
||||
|
||||
# Mosaic Fleet — Doctrine
|
||||
|
||||
> **This is the WHY. `NORTH_STAR.yaml` is the WHAT and WHEN.**
|
||||
> Renamed from `north-star.md` on 2026-08-20. It sat one character away from the
|
||||
> generated `NORTH_STAR.md` in the same directory, and the two are read by different
|
||||
> populations — the PRDs and TASKS files cite this one, while the agent role contracts
|
||||
> and the generator spec cite the YAML pair. Same-name-different-thing was the confusion;
|
||||
> the content was never in conflict.
|
||||
>
|
||||
> **Nothing here overrides `NORTH_STAR.yaml`.** Where this document states a plan item,
|
||||
> the YAML is authoritative. Where it states a decision, a rationale, or a role
|
||||
> definition, this document is the record and the YAML carries none of it.
|
||||
>
|
||||
> **Workstream:** W-FLEET (Fleet) under mission `mvp-20260312`
|
||||
> **Umbrella:** [docs/MISSION-MANIFEST.md](../MISSION-MANIFEST.md) · [docs/PRD.md](../PRD.md) (Mosaic Stack v0.1.0)
|
||||
> **Status:** doctrine — authored 2026-06-20. Owner of this file: Fleet workstream lead.
|
||||
> This document does **not** modify the MVP rollup; a rollup row is proposed, not written here.
|
||||
> **Umbrella:** [docs/MISSION-MANIFEST.md](../MISSION-MANIFEST.md)
|
||||
> **Authored:** 2026-06-20. Owner: Fleet workstream lead.
|
||||
> This document does **not** modify the MVP rollup.
|
||||
|
||||
## Vision
|
||||
|
||||
@@ -264,15 +281,17 @@ Dedicated Postgres **instance** vs. dedicated **schema** in the existing instanc
|
||||
Recommendation: dedicated schema, existing instance (a migration file, not new infra);
|
||||
re-evaluate if isolation or write-volume demands it.
|
||||
|
||||
## Phased roadmap
|
||||
## Phased roadmap — SUPERSEDED
|
||||
|
||||
| Phase | Outcome | Status |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- |
|
||||
| 0–1 | tmux PoC, hardening, published CLI v0.0.34 (#565–#568) | ✅ done |
|
||||
| **2 — Observability** | fleet ps (host+tenant aware join), heartbeat protocol + dogfood stub answers it, agent watch (read-only), agent send --verify receipts | ▶ now |
|
||||
| 3 — Real runtimes | claude/codex/pi/opencode answer heartbeat; **hybrid lifecycle** (core always-on: **orchestrator + enhancer**; ephemeral workers per lane) | planned |
|
||||
| 4 — Unified definition | one agent schema in gateway; mosaic agent --new → materialized per-tenant session; uid-tenant provisioning; **`fleet` schema migration + `forge-exec` TaskExecutor adapter (forge → `agent-send.sh`)** | planned |
|
||||
| 5 — Control plane | federation-backed cross-host × cross-tenant fleet view; **webUI** (surface chosen then) for MVP-X1 parity; **central register live (spend ledger, docs-as-projections, multi-host Kanban)** | planned |
|
||||
Superseded 2026-08-20 by [`NORTH_STAR.yaml`](./NORTH_STAR.yaml), whose `goals` carry both
|
||||
a `phase` (build order) and a `tier` (which promise the goal delivers). The five-phase
|
||||
table that stood here could not express those as separate axes, and its "Phase 2 —
|
||||
Observability ▶ now" row stayed unfalsified for two months because a phase has no exit
|
||||
test. Tiers do: see `AC-NS-0` through `AC-NS-7`.
|
||||
|
||||
The phase-2 content itself is not lost — it is specified in
|
||||
[`PRD.md`](./PRD.md) (Fleet Phase 2: Operator Observability) and is now tracked as
|
||||
goals `I1`–`I5` at tier 0.
|
||||
|
||||
## Decisions of record (2026-06-20, with Jason)
|
||||
|
||||
+13
-10
@@ -5,14 +5,14 @@ Generated environment files are rebuildable projections, not an operator-editabl
|
||||
|
||||
## Launch chain
|
||||
|
||||
| Layer | Responsibility |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Roster | `fleet/roster.yaml` supplies the agent name, class, supported runtime, model, reasoning, tool policy, workdir, and tmux socket. |
|
||||
| Projection writer | Renders deterministic fleet/agents/<name>.env.generated from the roster. |
|
||||
| Optional local data | Reads a strict, data-only fleet/agents/<name>.env.local; it cannot shadow generated keys. |
|
||||
| systemd | Starts the launcher with env -i and fixed bootstrap data. It does not preload either environment file. |
|
||||
| session launcher | Validates generated and local data before it queries, creates, or stops an exact tmux session. |
|
||||
| runtime launch | Derives the fixed mosaic yolo <runtime> argument array from validated roster data, then seeds the runtime contract. |
|
||||
| Layer | Responsibility |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Roster | `fleet/roster.yaml` supplies the agent name, class, supported runtime, model, reasoning, tool policy, workdir, and tmux socket; Git identity is derived from the exact agent name. |
|
||||
| Projection writer | Renders deterministic fleet/agents/<name>.env.generated from the roster. |
|
||||
| Optional local data | Reads a strict, data-only fleet/agents/<name>.env.local; it cannot shadow generated keys. |
|
||||
| systemd | Starts the launcher with env -i and fixed bootstrap data. It does not preload either environment file. |
|
||||
| session launcher | Validates generated and local data before it queries, creates, or stops an exact tmux session. |
|
||||
| runtime launch | Derives the fixed mosaic yolo <runtime> argument array from validated roster data, then seeds the runtime contract. |
|
||||
|
||||
The launcher never `source`s or `eval`s an environment file and never accepts an environment-supplied
|
||||
command. `MOSAIC_AGENT_COMMAND`, command/channel overrides, unknown keys, generated-key shadowing,
|
||||
@@ -24,6 +24,7 @@ secret-like key names, duplicate keys, comments, quoted/export syntax, and unsaf
|
||||
|
||||
```dotenv
|
||||
MOSAIC_AGENT_NAME=<roster name>
|
||||
MOSAIC_GIT_IDENTITY=<roster name>
|
||||
MOSAIC_AGENT_CLASS=<roster class>
|
||||
MOSAIC_AGENT_RUNTIME=<roster runtime>
|
||||
MOSAIC_AGENT_MODEL=<roster model hint>
|
||||
@@ -33,8 +34,10 @@ MOSAIC_AGENT_WORKDIR=<absolute roster work directory>
|
||||
MOSAIC_TMUX_SOCKET=<roster socket or empty>
|
||||
```
|
||||
|
||||
The generated launch contract supports `claude`, `codex`, `opencode`, and `pi`. mosaic fleet add
|
||||
rejects another runtime before it writes the roster or modifies generated, local, or quarantine state.
|
||||
`MOSAIC_GIT_IDENTITY` is not independently configurable: it must equal `MOSAIC_AGENT_NAME`, preventing
|
||||
split runtime and repository identity authority. The generated launch contract supports `claude`,
|
||||
`codex`, `opencode`, and `pi`. mosaic fleet add rejects another runtime before it writes the roster or
|
||||
modifies generated, local, or quarantine state.
|
||||
The legacy dogfood stub remains an observability-only canary on its separate `mosaic-factory` socket;
|
||||
it has no generated-launch adapter and cannot be added through this path.
|
||||
|
||||
|
||||
+45
-25
@@ -3,7 +3,7 @@
|
||||
> **Generated file — do not edit by hand.**
|
||||
> Projected deterministically from [`NORTH_STAR.yaml`](./NORTH_STAR.yaml) by the pure
|
||||
> generator in `packages/mosaic/src/commands/fleet.ts` (`renderNorthStarMarkdown`).
|
||||
> Edit the YAML, then regenerate. Self-contained Mosaic — no Hermes dependency.
|
||||
> Edit the YAML, then regenerate. Self-contained Mosaic.
|
||||
|
||||
## Mission
|
||||
|
||||
@@ -11,7 +11,7 @@ A self-driving Mosaic system that 24/7 unattended converts a machine-readable go
|
||||
|
||||
## Substrate
|
||||
|
||||
The Mosaic Backlog is the backlog of record + dispatch engine, built on Mosaic's native Postgres storage service (@mosaicstack/db drizzle; PGlite-embedded by default, full Postgres by config). NOT Hermes.
|
||||
The Mosaic Backlog is the backlog of record + dispatch engine, built on Mosaic's native Postgres storage service (@mosaicstack/db drizzle; PGlite-embedded by default, full Postgres by config).
|
||||
|
||||
## Standing objectives
|
||||
|
||||
@@ -24,16 +24,18 @@ The Mosaic Backlog is the backlog of record + dispatch engine, built on Mosaic's
|
||||
- **NS-7** — Meta-loop (session-review + enhancer) continuously proposes small fleet-improvement PRs.
|
||||
- **NS-8** — Single operator-flippable PAUSE kill-switch (fleet/run/PAUSED) honored before every dispatch and every merge.
|
||||
- **NS-9** — Mosaic is a general-purpose multi-agent system: the user declares the SYSTEM TYPE to run (e.g. software delivery, personal assistant, research, business/operations) and the orchestrator provisions the matching persona roster and org structure from a cross-domain baseline persona library; the delivery/coding fleet is one profile among many.
|
||||
- **NS-10** — An adoption is not complete until the mechanism it replaces is removed. Two live conventions for one concern is the defect, not a transition state. Measured 2026-08-20: brain-home adopted by 9 modules and not 10; MOSAIC_HOME honored in 4 places, each re-deriving it; backlog cards declared while TASKS.md files stayed authoritative. Every one was decided correctly and left half-applied.
|
||||
|
||||
## Success criteria
|
||||
|
||||
- **AC-NS-1** — The supervisor keeps a two-agent floor (1 orchestrator + >=1 enhancer) healthy across reboot.
|
||||
- **AC-NS-2** — A goal added to this YAML is decomposed to cards and either merged or escalated, with no human in the loop.
|
||||
- **AC-NS-3** — No PR merges with failure/error/no-status/timeout CI, and none bypass pr-merge.sh.
|
||||
- **AC-NS-4** — TTL is enforced on claims; token caps remain advisory until a real meter exists.
|
||||
- **AC-NS-5** — Flipping fleet/run/PAUSED halts dispatch and merges within one tick.
|
||||
- **AC-NS-6** — A user can declare a system type and the fleet provisions the matching persona roster + topology from the baseline library, with no code change.
|
||||
- **AC-NS-7** — A user-customized persona (edited or added via the orchestrator) survives mosaic update: baseline reseed never clobbers user overrides.
|
||||
- **AC-NS-0** (tier 0) — The operator launches an agent on any configured harness with one command, observes its state and sends it work without attaching to a terminal multiplexer.
|
||||
- **AC-NS-1** (tier 1) — The supervisor keeps a two-agent floor (1 orchestrator + >=1 enhancer) healthy across reboot.
|
||||
- **AC-NS-2** (tier 1) — A goal added to this YAML is decomposed to cards and either merged or escalated, with no human in the loop.
|
||||
- **AC-NS-3** (tier 1) — No PR merges with failure/error/no-status/timeout CI, and none bypass pr-merge.sh.
|
||||
- **AC-NS-4** (tier 1) — TTL is enforced on claims; token caps remain advisory until a real meter exists.
|
||||
- **AC-NS-5** (tier 1) — Flipping fleet/run/PAUSED halts dispatch and merges within one tick.
|
||||
- **AC-NS-6** (tier 2) — A user can declare a system type and the fleet provisions the matching persona roster + topology from the baseline library, with no code change.
|
||||
- **AC-NS-7** (tier 2) — A user-customized persona (edited or added via the orchestrator) survives mosaic update: baseline reseed never clobbers user overrides.
|
||||
|
||||
## Workstreams
|
||||
|
||||
@@ -45,26 +47,44 @@ The Mosaic Backlog is the backlog of record + dispatch engine, built on Mosaic's
|
||||
| D | Merge-gate — single approver, pr-merge.sh after CI wait |
|
||||
| E | Meta-loop — session-review + enhancer improvement PRs |
|
||||
| F | Safety-rails — TTL claims, advisory spend, PAUSE kill-switch |
|
||||
| G | Kill-switch — operator PAUSE honored before dispatch and merge |
|
||||
| H | Personas & system profiles — cross-domain library, system-type provisioning, update-surviving customization |
|
||||
| I | Operator surface — launcher, fleet visibility, reliable steering (tier 0) |
|
||||
| J | Web control plane — browser surface over the gateway (tier 1) |
|
||||
| K | Clients — desktop and mobile over the same backend (tier 2) |
|
||||
| L | Auth profiles — per-provider accounts, per-session selection (tier 2) |
|
||||
|
||||
## Goals (backlog projection)
|
||||
|
||||
| id | title | phase | priority | depends_on |
|
||||
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ----------- | ---------- |
|
||||
| A1 | Machine-readable NORTH_STAR.yaml + Markdown projection | 1 | must-have | — |
|
||||
| A2 | Mosaic Backlog schema + storage-service card store (drizzle/PGlite) | 1 | must-have | A1 |
|
||||
| A3a | Card lifecycle — create/claim/release with stable ids + depends_on DAG | 1 | must-have | A2 |
|
||||
| A3b | TTL-bounded claim enforcement (wall-clock) on cards | 1 | must-have | A3a |
|
||||
| A4 | Advisory spend projection per card (degrades to TTL, no real meter) | 1 | should-have | A3a |
|
||||
| B1 | Supervisor tick — readiness scan, two-agent-floor health check | 2 | must-have | A3a |
|
||||
| B2 | Native dispatch/claim — assign ready dependency-satisfied work | 2 | must-have | A3b, B1 |
|
||||
| B3a | Planner decompose — goal added to YAML → cards | 2 | must-have | A2, B1 |
|
||||
| B3b | Replan request on empty backlog; escalate on no-decompose | 2 | should-have | B3a |
|
||||
| G1 | PAUSE kill-switch + merge-gate honored before dispatch and merge | 2 | must-have | B2 |
|
||||
| H1 | Cross-domain baseline persona library (exec, marketing, ops, research, assistant + engineering roles) | 1 | must-have | A1 |
|
||||
| H2 | System-type profiles — declarative mapping of system type to persona roster + topology | 2 | must-have | H1 |
|
||||
| H3 | System-type provisioning — user declares type; orchestrator instantiates the matching roster + structure | 2 | must-have | H2 |
|
||||
| H4 | Update-surviving persona customization — ad-hoc edits/additions persisted in a PRESERVE-protected override layer (baseline merged with overrides) | 2 | must-have | H1 |
|
||||
| id | title | tier | phase | priority | depends_on |
|
||||
| --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ----- | ----------- | -------------- |
|
||||
| A1 | Machine-readable NORTH_STAR.yaml + Markdown projection | 1 | 1 | must-have | — |
|
||||
| A2 | Mosaic Backlog schema + storage-service card store (drizzle/PGlite) | 1 | 1 | must-have | A1 |
|
||||
| A3a | Card lifecycle — create/claim/release with stable ids + depends_on DAG | 1 | 1 | must-have | A2 |
|
||||
| A3b | TTL-bounded claim enforcement (wall-clock) on cards | 1 | 1 | must-have | A3a |
|
||||
| A4 | Advisory spend projection per card (degrades to TTL, no real meter) | 1 | 1 | should-have | A3a |
|
||||
| B1 | Supervisor tick — readiness scan, two-agent-floor health check | 1 | 2 | must-have | A3a |
|
||||
| B2 | Native dispatch/claim — assign ready dependency-satisfied work | 1 | 2 | must-have | A3b, B1 |
|
||||
| B3a | Planner decompose — goal added to YAML → cards | 1 | 2 | must-have | A2, B1 |
|
||||
| B3b | Replan request on empty backlog; escalate on no-decompose | 1 | 2 | should-have | B3a |
|
||||
| G1 | PAUSE kill-switch + merge-gate honored before dispatch and merge | 1 | 2 | must-have | B2 |
|
||||
| H1 | Cross-domain baseline persona library (exec, marketing, ops, research, assistant + engineering roles) | 2 | 1 | must-have | A1 |
|
||||
| H2 | System-type profiles — declarative mapping of system type to persona roster + topology | 2 | 2 | must-have | H1 |
|
||||
| H3 | System-type provisioning — user declares type; orchestrator instantiates the matching roster + structure | 2 | 2 | must-have | H2 |
|
||||
| H4 | Update-surviving persona customization — ad-hoc edits/additions persisted in a PRESERVE-protected override layer (baseline merged with overrides) | 2 | 2 | must-have | H1 |
|
||||
| A5 | NORTH_STAR schema validator — every goal's workstream declared, every workstream has a goal, every depends_on id exists, every tier has a success criterion; runs in CI beside the Markdown regeneration check | 0 | 1 | must-have | A1 |
|
||||
| I1 | One home resolver — a single function resolving MOSAIC_HOME with a sane default, adopted by every module. Today brain-home.ts is imported by 9 modules while 10 still use DEFAULT_MOSAIC_HOME, and MOSAIC_HOME is re-derived ad hoc in 4 places. NS-10 applies - finish the adoption and delete the second path | 0 | 1 | must-have | — |
|
||||
| I2 | mosaic fleet ps sees the fleet that is actually running. Three measured blockers: the roster declares socket `mosaic-fleet` which does not exist, the 18 live sessions are on the default socket, and nothing writes a roster because seats are launched outside the CLI. Make the socket configurable and the roster written at launch, or make ps read tmux + seat dirs directly | 0 | 1 | must-have | I1 |
|
||||
| I3 | Migrate fleet steering onto mosaic agent send --verify (exists, FLEET-OBS-005, spec FR-5) and retire tools/tmux/agent-send.sh, which forges the sender (D33) and returns an uninformative rc (D16, D34). FR-5 predates those defects by a month | 0 | 1 | must-have | I1 |
|
||||
| I4 | mosaic fleet absorbs what launch-seat.sh does and launch-seat.sh is deprecated: compose the prompt file set, force the skill set, wire the style hook, fail closed on any unreadable input, export per-seat git identity, and register the seat in the roster. launch-seat.sh was a manual method; it is the reference implementation, not the destination | 0 | 1 | must-have | I1, I5 |
|
||||
| I5 | Harness probe matrix — verify a working prompt-injection path for claude, codex, opencode and pi, and refuse any runtime whose path is unverified. AC-NS-0 clause 1 ("any configured harness") rests on this. The probe work in docs/plans/2026-08-19_launch-seat-multi-runtime.md (brain, untracked) transfers; its launch-seat.sh target does not | 0 | 1 | must-have | — |
|
||||
| I6 | Finish the heartbeat responder (FLEET-OBS-002, the only Phase-2 task still in-progress; spec FR-2). Health must mean "answered a heartbeat", not "pane alive" — pane state measured two seats wrong on 2026-08-20 | 0 | 1 | must-have | — |
|
||||
| I7 | Independent review and live-fleet dogfood of the Phase-2 verbs (FLEET-OBS-008), then land them (FLEET-OBS-009). Implementation is done and verification is not; "done" in a task file frozen five weeks is not evidence | 0 | 1 | must-have | I2, I3, I4, I6 |
|
||||
| I8 | Neutralize misleading documentation — supersede headers on docs that state a stale status, a false blocker or a retired mechanism. Cheap, and it is tier 0 because a stale doc does not merely fail to help an agent, it actively misroutes one. Rebuilding the documentation is a separate and later job | 0 | 1 | must-have | — |
|
||||
| I9 | Study t3code's agent-attach and multi-provider auth methods and record what transfers. Reference only — Mosaic implements its own within the stack, never adopts the code and never takes the dependency. Informs HOW I/J/K/L are built, not whether | 0 | 1 | should-have | — |
|
||||
| J1 | Web control plane over the gateway — fleet visibility and steering in a browser, same data source as I2 | 1 | 3 | must-have | I2 |
|
||||
| K1 | Desktop and mobile clients against the gateway, authenticated | 2 | 4 | must-have | J1 |
|
||||
| L1 | Per-provider auth profiles with per-session selection | 2 | 4 | must-have | I4 |
|
||||
|
||||
## Assumptions (vetoable)
|
||||
|
||||
|
||||
+129
-2
@@ -6,7 +6,7 @@
|
||||
# packages/mosaic/src/commands/fleet.ts (renderNorthStarMarkdown). Edit the YAML,
|
||||
# never the .md.
|
||||
#
|
||||
# Self-contained Mosaic. NO Hermes runtime dependency. The backlog of record is
|
||||
# Self-contained Mosaic. The backlog of record is
|
||||
# the Mosaic Backlog on Mosaic's OWN native Postgres storage service.
|
||||
|
||||
version: 1
|
||||
@@ -24,7 +24,7 @@ substrate:
|
||||
note: >-
|
||||
The Mosaic Backlog is the backlog of record + dispatch engine, built on
|
||||
Mosaic's native Postgres storage service (@mosaicstack/db drizzle;
|
||||
PGlite-embedded by default, full Postgres by config). NOT Hermes.
|
||||
PGlite-embedded by default, full Postgres by config).
|
||||
|
||||
standing_objectives:
|
||||
- id: NS-1
|
||||
@@ -69,32 +69,53 @@ standing_objectives:
|
||||
business/operations) and the orchestrator provisions the matching persona
|
||||
roster and org structure from a cross-domain baseline persona library; the
|
||||
delivery/coding fleet is one profile among many.
|
||||
- id: NS-10
|
||||
text: >-
|
||||
An adoption is not complete until the mechanism it replaces is removed.
|
||||
Two live conventions for one concern is the defect, not a transition
|
||||
state. Measured 2026-08-20: brain-home adopted by 9 modules and not 10;
|
||||
MOSAIC_HOME honored in 4 places, each re-deriving it; backlog cards
|
||||
declared while TASKS.md files stayed authoritative. Every one was decided
|
||||
correctly and left half-applied.
|
||||
|
||||
success_criteria:
|
||||
- id: AC-NS-0
|
||||
tier: 0
|
||||
text: >-
|
||||
The operator launches an agent on any configured harness with one
|
||||
command, observes its state and sends it work without attaching to a
|
||||
terminal multiplexer.
|
||||
- id: AC-NS-1
|
||||
tier: 1
|
||||
text: >-
|
||||
The supervisor keeps a two-agent floor (1 orchestrator + >=1 enhancer)
|
||||
healthy across reboot.
|
||||
- id: AC-NS-2
|
||||
tier: 1
|
||||
text: >-
|
||||
A goal added to this YAML is decomposed to cards and either merged or
|
||||
escalated, with no human in the loop.
|
||||
- id: AC-NS-3
|
||||
tier: 1
|
||||
text: >-
|
||||
No PR merges with failure/error/no-status/timeout CI, and none bypass
|
||||
pr-merge.sh.
|
||||
- id: AC-NS-4
|
||||
tier: 1
|
||||
text: >-
|
||||
TTL is enforced on claims; token caps remain advisory until a real meter
|
||||
exists.
|
||||
- id: AC-NS-5
|
||||
tier: 1
|
||||
text: >-
|
||||
Flipping fleet/run/PAUSED halts dispatch and merges within one tick.
|
||||
- id: AC-NS-6
|
||||
tier: 2
|
||||
text: >-
|
||||
A user can declare a system type and the fleet provisions the matching
|
||||
persona roster + topology from the baseline library, with no code change.
|
||||
- id: AC-NS-7
|
||||
tier: 2
|
||||
text: >-
|
||||
A user-customized persona (edited or added via the orchestrator) survives
|
||||
mosaic update: baseline reseed never clobbers user overrides.
|
||||
@@ -112,80 +133,186 @@ workstreams:
|
||||
title: Meta-loop — session-review + enhancer improvement PRs
|
||||
- id: F
|
||||
title: Safety-rails — TTL claims, advisory spend, PAUSE kill-switch
|
||||
- id: G
|
||||
title: Kill-switch — operator PAUSE honored before dispatch and merge
|
||||
- id: H
|
||||
title: Personas & system profiles — cross-domain library, system-type provisioning, update-surviving customization
|
||||
- id: I
|
||||
title: Operator surface — launcher, fleet visibility, reliable steering (tier 0)
|
||||
- id: J
|
||||
title: Web control plane — browser surface over the gateway (tier 1)
|
||||
- id: K
|
||||
title: Clients — desktop and mobile over the same backend (tier 2)
|
||||
- id: L
|
||||
title: Auth profiles — per-provider accounts, per-session selection (tier 2)
|
||||
|
||||
# NOTE: workstreams C, D, E and F are declared but currently project no goals.
|
||||
# That is planning debt, not an editing error: their goals have not been written
|
||||
# yet. The A5 validator below reports it rather than letting it stay invisible.
|
||||
|
||||
goals:
|
||||
- id: A1
|
||||
title: Machine-readable NORTH_STAR.yaml + Markdown projection
|
||||
phase: 1
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: []
|
||||
- id: A2
|
||||
title: Mosaic Backlog schema + storage-service card store (drizzle/PGlite)
|
||||
phase: 1
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: [A1]
|
||||
- id: A3a
|
||||
title: Card lifecycle — create/claim/release with stable ids + depends_on DAG
|
||||
phase: 1
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: [A2]
|
||||
- id: A3b
|
||||
title: TTL-bounded claim enforcement (wall-clock) on cards
|
||||
phase: 1
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: [A3a]
|
||||
- id: A4
|
||||
title: Advisory spend projection per card (degrades to TTL, no real meter)
|
||||
phase: 1
|
||||
tier: 1
|
||||
priority: should-have
|
||||
depends_on: [A3a]
|
||||
- id: B1
|
||||
title: Supervisor tick — readiness scan, two-agent-floor health check
|
||||
phase: 2
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: [A3a]
|
||||
- id: B2
|
||||
title: Native dispatch/claim — assign ready dependency-satisfied work
|
||||
phase: 2
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: [A3b, B1]
|
||||
- id: B3a
|
||||
title: Planner decompose — goal added to YAML → cards
|
||||
phase: 2
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: [A2, B1]
|
||||
- id: B3b
|
||||
title: Replan request on empty backlog; escalate on no-decompose
|
||||
phase: 2
|
||||
tier: 1
|
||||
priority: should-have
|
||||
depends_on: [B3a]
|
||||
- id: G1
|
||||
title: PAUSE kill-switch + merge-gate honored before dispatch and merge
|
||||
phase: 2
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: [B2]
|
||||
- id: H1
|
||||
title: Cross-domain baseline persona library (exec, marketing, ops, research, assistant + engineering roles)
|
||||
phase: 1
|
||||
tier: 2
|
||||
priority: must-have
|
||||
depends_on: [A1]
|
||||
- id: H2
|
||||
title: System-type profiles — declarative mapping of system type to persona roster + topology
|
||||
phase: 2
|
||||
tier: 2
|
||||
priority: must-have
|
||||
depends_on: [H1]
|
||||
- id: H3
|
||||
title: System-type provisioning — user declares type; orchestrator instantiates the matching roster + structure
|
||||
phase: 2
|
||||
tier: 2
|
||||
priority: must-have
|
||||
depends_on: [H2]
|
||||
- id: H4
|
||||
title: Update-surviving persona customization — ad-hoc edits/additions persisted in a PRESERVE-protected override layer (baseline merged with overrides)
|
||||
phase: 2
|
||||
tier: 2
|
||||
priority: must-have
|
||||
depends_on: [H1]
|
||||
- id: A5
|
||||
title: NORTH_STAR schema validator — every goal's workstream declared, every workstream has a goal, every depends_on id exists, every tier has a success criterion; runs in CI beside the Markdown regeneration check
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: [A1]
|
||||
- id: I1
|
||||
title: One home resolver — a single function resolving MOSAIC_HOME with a sane default, adopted by every module. Today brain-home.ts is imported by 9 modules while 10 still use DEFAULT_MOSAIC_HOME, and MOSAIC_HOME is re-derived ad hoc in 4 places. NS-10 applies - finish the adoption and delete the second path
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: []
|
||||
- id: I2
|
||||
title: 'mosaic fleet ps sees the fleet that is actually running. Three measured blockers: the roster declares socket `mosaic-fleet` which does not exist, the 18 live sessions are on the default socket, and nothing writes a roster because seats are launched outside the CLI. Make the socket configurable and the roster written at launch, or make ps read tmux + seat dirs directly'
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: [I1]
|
||||
- id: I3
|
||||
title: Migrate fleet steering onto mosaic agent send --verify (exists, FLEET-OBS-005, spec FR-5) and retire tools/tmux/agent-send.sh, which forges the sender (D33) and returns an uninformative rc (D16, D34). FR-5 predates those defects by a month
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: [I1]
|
||||
- id: I4
|
||||
title: 'mosaic fleet absorbs what launch-seat.sh does and launch-seat.sh is deprecated: compose the prompt file set, force the skill set, wire the style hook, fail closed on any unreadable input, export per-seat git identity, and register the seat in the roster. launch-seat.sh was a manual method; it is the reference implementation, not the destination'
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: [I1, I5]
|
||||
- id: I5
|
||||
title: Harness probe matrix — verify a working prompt-injection path for claude, codex, opencode and pi, and refuse any runtime whose path is unverified. AC-NS-0 clause 1 ("any configured harness") rests on this. The probe work in docs/plans/2026-08-19_launch-seat-multi-runtime.md (brain, untracked) transfers; its launch-seat.sh target does not
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: []
|
||||
- id: I6
|
||||
title: Finish the heartbeat responder (FLEET-OBS-002, the only Phase-2 task still in-progress; spec FR-2). Health must mean "answered a heartbeat", not "pane alive" — pane state measured two seats wrong on 2026-08-20
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: []
|
||||
- id: I7
|
||||
title: Independent review and live-fleet dogfood of the Phase-2 verbs (FLEET-OBS-008), then land them (FLEET-OBS-009). Implementation is done and verification is not; "done" in a task file frozen five weeks is not evidence
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: [I2, I3, I4, I6]
|
||||
- id: I8
|
||||
title: Neutralize misleading documentation — supersede headers on docs that state a stale status, a false blocker or a retired mechanism. Cheap, and it is tier 0 because a stale doc does not merely fail to help an agent, it actively misroutes one. Rebuilding the documentation is a separate and later job
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: must-have
|
||||
depends_on: []
|
||||
- id: I9
|
||||
title: Study t3code's agent-attach and multi-provider auth methods and record what transfers. Reference only — Mosaic implements its own within the stack, never adopts the code and never takes the dependency. Informs HOW I/J/K/L are built, not whether
|
||||
phase: 1
|
||||
tier: 0
|
||||
priority: should-have
|
||||
depends_on: []
|
||||
- id: J1
|
||||
title: Web control plane over the gateway — fleet visibility and steering in a browser, same data source as I2
|
||||
phase: 3
|
||||
tier: 1
|
||||
priority: must-have
|
||||
depends_on: [I2]
|
||||
- id: K1
|
||||
title: Desktop and mobile clients against the gateway, authenticated
|
||||
phase: 4
|
||||
tier: 2
|
||||
priority: must-have
|
||||
depends_on: [J1]
|
||||
- id: L1
|
||||
title: Per-provider auth profiles with per-session selection
|
||||
phase: 4
|
||||
tier: 2
|
||||
priority: must-have
|
||||
depends_on: [I4]
|
||||
|
||||
assumptions:
|
||||
- id: ASM-1
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
---
|
||||
|
||||
# PRD — Mosaic Fleet Suite (init, configure, operate)
|
||||
|
||||
> **Workstream:** W-FLEET (Fleet) under mission `mvp-20260312` · **Phase:** 3→4 productization
|
||||
> **North star:** [docs/fleet/north-star.md](./north-star.md) · prior: Phase-2 observability (#579), durable launch (#581), real-agent enablement (#583/#584/#586), releases 0.0.35–0.0.37
|
||||
> **North star:** [docs/fleet/FLEET-DOCTRINE.md](./FLEET-DOCTRINE.md) · prior: Phase-2 observability (#579), durable launch (#581), real-agent enablement (#583/#584/#586), releases 0.0.35–0.0.37
|
||||
> **Lead:** Jarvis @ `w-jarvis`. **Collaborator:** coder agent @ `dragon-lin` (jwoltje@10.1.10.37:coder0-0).
|
||||
> Owner of this file: Fleet workstream lead. Does not modify MVP single-writer control-plane files.
|
||||
|
||||
|
||||
+6
-1
@@ -1,7 +1,12 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
---
|
||||
|
||||
# PRD — Fleet Phase 2: Operator Observability
|
||||
|
||||
> **Workstream:** W-FLEET under `mvp-20260312` · **Phase:** 2
|
||||
> **North star:** [docs/fleet/north-star.md](./north-star.md)
|
||||
> **North star:** [docs/fleet/FLEET-DOCTRINE.md](./FLEET-DOCTRINE.md)
|
||||
> **Source umbrella PRD:** [docs/PRD.md](../PRD.md) (Mosaic Stack v0.1.0)
|
||||
> **Tracks task:** `fleet-observability-1` — restore operator observability into fleet agent sessions.
|
||||
|
||||
|
||||
+16
-1
@@ -1,10 +1,25 @@
|
||||
# Tasks — W-FLEET (Fleet) Phase 2: Observability
|
||||
|
||||
> ---
|
||||
>
|
||||
> **STATUS: SUPERSEDED — 2026-08-20.** kind `tracking` · superseded by `docs/fleet/NORTH_STAR.yaml`
|
||||
>
|
||||
> This file is the pre-backlog tracking mechanism. `NS-2` in the north star declares the
|
||||
> replacement: every backlog item is a Mosaic Backlog card projected from the YAML. That
|
||||
> model replaced this one and nobody retired the old file, so it kept reading as
|
||||
> authoritative while going stale.
|
||||
>
|
||||
> **Do not trust a status in this file.** Verified 2026-08-20: it was already behind the
|
||||
> code when it froze five weeks ago. The `FLEET-OBS` series was the one thing worth salvaging and
|
||||
> is now carried as goals `I2`, `I3`, `I6` and `I7` at tier 0.
|
||||
>
|
||||
> Kept as a record of what was believed. Do not update it; update the YAML.
|
||||
|
||||
> Workstream task file for the Fleet. Single-writer: Fleet workstream lead (orchestrator).
|
||||
> Workers read but never modify. This is **not** the MVP rollup (`docs/TASKS.md`) — a
|
||||
> rollup row is proposed to the MVP orchestrator, not written here.
|
||||
>
|
||||
> Mission: `mvp-20260312` · PRD: [docs/fleet/PRD.md](./PRD.md) · North star: [docs/fleet/north-star.md](./north-star.md)
|
||||
> Mission: `mvp-20260312` · PRD: [docs/fleet/PRD.md](./PRD.md) · North star: [docs/fleet/FLEET-DOCTRINE.md](./FLEET-DOCTRINE.md)
|
||||
> Status: `not-started` | `in-progress` | `done` | `blocked` | `failed`
|
||||
|
||||
| id | status | description | depends_on | agent | pr | notes |
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Desired, Derived, and Observed Fleet State
|
||||
|
||||
## One writable authority
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Generated Environment Launch Chain
|
||||
|
||||
The launcher consumes validated data, not shell configuration.
|
||||
|
||||
1. Read and validate the canonical roster.
|
||||
2. Render deterministic <name>.env.generated data from that roster.
|
||||
2. Render deterministic <name>.env.generated data from that roster, including `MOSAIC_GIT_IDENTITY` derived exactly from the roster agent name.
|
||||
3. Parse optional <name>.env.local through a strict allowlist.
|
||||
4. Reject generated-key shadowing, unknown or sensitive-looking keys, unsafe paths/values, duplicates, malformed lines, shell syntax, and command overrides.
|
||||
5. Derive the runtime command from validated runtime/model/reasoning data.
|
||||
6. Target only the exact configured tmux socket and roster session after ownership checks.
|
||||
5. Reject a Git identity that is unsafe or differs from the generated agent name.
|
||||
6. Derive the runtime command from validated runtime/model/reasoning data and pass every generated projection entry through the clean process environment boundary.
|
||||
7. Target only the exact configured tmux socket and roster session after ownership checks.
|
||||
|
||||
## File precedence and ownership
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Fleet Identity, Class, and Runtime
|
||||
|
||||
Each roster field has one job. Do not use names or model strings as authority shortcuts.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Fleet Role Authority and Leases
|
||||
|
||||
Role content describes behavior; protected authority is immutable code metadata derived only from the canonical class.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# F4 — Orchestrator chat connector + Matrix (local homeserver)
|
||||
|
||||
> **Issue:** #616 · **Doctrine:** `docs/fleet/north-star.md` (#613) — orchestrator-chat-connector decision.
|
||||
> **Issue:** #616 · **Doctrine:** `docs/fleet/FLEET-DOCTRINE.md` (#613) — orchestrator-chat-connector decision.
|
||||
> **Status:** Phase 1 (abstraction + scaffold) in this PR; Phase 2+ are follow-ups (below).
|
||||
|
||||
## Goal
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Configure an Interaction Instance
|
||||
|
||||
An interaction instance is a configurable local roster member with canonical class: interaction and matching tool_policy: interaction. “Tess” may be used as a display alias, but neither that alias nor the stable name is required or authority-bearing.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Configure a Validator Instance
|
||||
|
||||
A validator instance is a configurable local roster member with canonical class: validator and matching tool_policy: validator. “Ultron” may be used as a display alias, but it is not a required identity, class alias, product name, or source of authority.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Create, Inspect, Update, and Delete a Local Fleet Agent
|
||||
|
||||
Use the local roster-v2 control plane only. These commands change desired state and derived environment projections; they never start, stop, reconcile, inspect, or otherwise act on systemd, tmux, sessions, or runtimes.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Customize Fleet Roles
|
||||
|
||||
Mosaic resolves persona contracts through two layers:
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Safely Reconcile and Control a Local Fleet Agent
|
||||
|
||||
Use the canonical local roster-v2 command surface:
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Executable Fleet Example, Profile, and Service-Preset Dispositions
|
||||
|
||||
**Issue:** #758 · **Card:** FCM-M1-003 · **Status:** M1 executable disposition evidence
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Legacy Fleet Class Aliases
|
||||
|
||||
Fleet class compatibility is intentionally narrow. The shared resolver accepts exactly three legacy
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Previewing a Fleet Roster v1-to-v2 Migration
|
||||
|
||||
**Issue:** #758 · **Card:** FCM-M4-001 · **Effect boundary:** preview only
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Fleet Configuration Backup and Restore Boundary
|
||||
|
||||
**Issue:** #758 · **Card:** FCM-M4-001
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Environment Quarantine Operations
|
||||
|
||||
Legacy <name>.env is input evidence, never current launch authority. Projection preparation classifies it deterministically:
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Reconcile and Recover a Local Fleet
|
||||
|
||||
## Safe sequence
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Systemd and tmux Troubleshooting
|
||||
|
||||
Start with read-only mosaic fleet status, `doctor`, and `verify`. Do not manually adopt, rename, terminate, or recreate sessions while ownership is ambiguous.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Upgrade and Installed-Asset Drift
|
||||
|
||||
Fleet source assets and installed assets can differ after an update, but FCM-M5-001 does not add a trustworthy source-versus-installed revision detector or refresh command. Do not infer freshness from checkout presence, timestamps, generated environment files, running sessions, or a ready migration preview.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Local Fleet Agent Mutations
|
||||
|
||||
FCM-M2-002 provides local roster-v2 create, get, update, delete, and plan operations. They only change desired state and derived environment projections. They never start, stop, inspect, reconcile, or otherwise act on runtimes, systemd units, tmux sessions, or heartbeats.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Fleet Control-Plane CLI
|
||||
|
||||
The local desired-state surface is mosaic fleet. It is distinct from the gateway-backed mosaic agent catalog and from legacy compatibility commands that act on roster v1.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Fleet Generated Environment Boundary
|
||||
|
||||
**Card:** FCM-M2-001 · **Issue:** #758 · **Status:** merged contract
|
||||
@@ -35,6 +40,7 @@ values, credential material, or command text.
|
||||
|
||||
```dotenv
|
||||
MOSAIC_AGENT_NAME=<roster name>
|
||||
MOSAIC_GIT_IDENTITY=<roster name>
|
||||
MOSAIC_AGENT_CLASS=<roster class>
|
||||
MOSAIC_AGENT_RUNTIME=<roster runtime>
|
||||
MOSAIC_AGENT_MODEL=<roster model hint>
|
||||
@@ -44,8 +50,9 @@ MOSAIC_AGENT_WORKDIR=<absolute roster work directory>
|
||||
MOSAIC_TMUX_SOCKET=<roster socket or empty>
|
||||
```
|
||||
|
||||
The generated launch contract supports only `claude`, `codex`, `opencode`, and `pi`. fleet add
|
||||
uses that same runtime authority and rejects any other runtime before it writes the roster or changes
|
||||
`MOSAIC_GIT_IDENTITY` is derived from and must equal `MOSAIC_AGENT_NAME`; it is not a separate
|
||||
operator-controlled identity authority. The generated launch contract supports only `claude`, `codex`,
|
||||
`opencode`, and `pi`. fleet add uses that same runtime authority and rejects any other runtime before it writes the roster or changes
|
||||
projection, local, or quarantine files. The legacy dogfood stub on its separate `mosaic-factory`
|
||||
socket remains an observability canary; it has no generated-launch adapter and cannot be added through
|
||||
this projection path.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Local Fleet Lifecycle Transitions
|
||||
|
||||
Roster-v2 `lifecycle.enabled` and `lifecycle.desired_state` are the only persisted lifecycle authority. Systemd, tmux, generated environment, and heartbeat state are derived or observed.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Fleet Role Classes and Authority
|
||||
|
||||
A fleet role class is a machine identity resolved from the persona library. Resolution uses the
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Fleet Roster v2 Structural Contract
|
||||
|
||||
**Status:** FCM-M1-001 local-tmux structural compiler contract. This document describes parsing,
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Local Fleet Status and Drift
|
||||
|
||||
mosaic fleet status [<name>], `verify`, and `doctor` are observational roster-v2 commands. They emit one JSON result and do not write projections, mutate desired state, operate lifecycle, or change tmux.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Deployment Guide
|
||||
|
||||
> **Status: non-operative for PostgreSQL, federated, and bare-metal production.** The checked-in
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Mosaic Stack — Developer Guide
|
||||
|
||||
## Table of Contents
|
||||
@@ -9,8 +14,9 @@
|
||||
5. [Adding New MCP Tools](#adding-new-mcp-tools)
|
||||
6. [Database Schema and Migrations](#database-schema-and-migrations)
|
||||
7. [Claude Code Skill Bridge](#claude-code-skill-bridge)
|
||||
8. [API Endpoint Reference](#api-endpoint-reference)
|
||||
9. [Local Fleet Canary](./fleet-local-canary.md)
|
||||
8. [Pi Persistent Goal Extension](#pi-persistent-goal-extension)
|
||||
9. [API Endpoint Reference](#api-endpoint-reference)
|
||||
10. [Local Fleet Canary](./fleet-local-canary.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -396,6 +402,85 @@ M1 intentionally manages Claude Code only. Pi's Mosaic launcher can discover the
|
||||
canonical root directly. Codex still relies on the existing full skill-sync
|
||||
linker and needs separate parity analysis before this lifecycle API is extended.
|
||||
|
||||
## Pi Persistent Goal Extension
|
||||
|
||||
The source of the Mosaic-owned Pi goal controller is:
|
||||
|
||||
```text
|
||||
packages/mosaic/framework/runtime/pi/goal-extension.ts
|
||||
```
|
||||
|
||||
The framework manifest classifies `runtime/**` as framework-owned. Both the bash installer and the
|
||||
TypeScript file adapter therefore deploy the same reviewed source to:
|
||||
|
||||
```text
|
||||
$MOSAIC_HOME/runtime/pi/goal-extension.ts
|
||||
# default: ~/.config/mosaic/runtime/pi/goal-extension.ts
|
||||
```
|
||||
|
||||
Do not copy or link this extension into `~/.pi/agent/extensions/`. The launcher function
|
||||
`discoverPiExtensionArgs()` emits the core `mosaic-extension.ts` first and the optional
|
||||
`goal-extension.ts` second, preserving compatibility with an older installed framework that does
|
||||
not have the goal file yet.
|
||||
|
||||
### Lifecycle design
|
||||
|
||||
| Pi API | Goal-controller responsibility |
|
||||
| ------------------------------ | --------------------------------------------------------------------------------- |
|
||||
| `registerCommand('goal')` | Set, inspect, pause, resume, or cancel one branch-specific goal |
|
||||
| `registerTool(...)` | Record a terminating structured progress report with evidence |
|
||||
| `context` | Inject the active goal contract before every provider request |
|
||||
| `turn_end` | Record every turn, reject mixed final reports, and enforce the turn bound |
|
||||
| `agent_settled` | Start one deduplicated continuation only after Pi has no retry/compact/queue work |
|
||||
| `session_compact` | Record the compact check, reset provisional verification, and defer idle work |
|
||||
| `session_start`/`session_tree` | Rebuild state from custom entries on the active branch |
|
||||
| `session_shutdown` | Invalidate deferred callbacks and clear UI state |
|
||||
|
||||
State is appended as `mosaic-goal-state` custom entries, which do not enter model context. The
|
||||
`context` hook creates a fresh hidden `mosaic-goal-context` message for each request instead of
|
||||
trusting compaction summaries. The `mosaic_goal_report` result uses `terminate: true`; when it is the
|
||||
sole final tool call, Pi avoids an unnecessary model response before the controller decides whether
|
||||
to verify, continue, or stop.
|
||||
|
||||
Before state is appended or displayed, the controller applies bounded credential-pattern redaction
|
||||
to the goal statement, report summary/evidence/next step, and stop reason. Fingerprints are computed
|
||||
over redacted report content. Pi session entries are append-only, so a credential-bearing legacy
|
||||
entry cannot honestly be erased by the extension: restoration fails closed, emits a warning, and
|
||||
requires removal of the affected session before setting a new goal. This is defense-in-depth rather
|
||||
than a secret-storage contract, and it does not rewrite Pi's separate model-message/tool-call
|
||||
history. Goal prompts tell the agent not to submit credentials or raw sensitive output, and tests use
|
||||
canaries to prove known forms do not reach new custom entries, status text, context, or tool details
|
||||
while ordinary typed fields such as `token: string` remain intact.
|
||||
|
||||
Completion remains evidence-gated but semantic: two consecutive `achieved` reports are required,
|
||||
and the second run is explicitly a verification pass. This avoids an extra judge-model request after
|
||||
every turn. Deterministic validator commands are intentionally not accepted as `/goal` input in this
|
||||
slice, so never describe this mechanism as proof of arbitrary natural-language completion.
|
||||
|
||||
### Tests and local smoke workflow
|
||||
|
||||
```bash
|
||||
pnpm --filter @mosaicstack/mosaic exec vitest run \
|
||||
src/runtime/pi-goal-extension.spec.ts \
|
||||
src/commands/launch.spec.ts \
|
||||
src/config/file-adapter.test.ts
|
||||
|
||||
bash packages/mosaic/framework/tools/quality/scripts/test-install-migration.sh
|
||||
```
|
||||
|
||||
For an additive local smoke test without reseeding unrelated live framework files:
|
||||
|
||||
```bash
|
||||
install -D -m 0644 \
|
||||
packages/mosaic/framework/runtime/pi/goal-extension.ts \
|
||||
~/.config/mosaic/runtime/pi/goal-extension.ts
|
||||
|
||||
pi --extension ~/.config/mosaic/runtime/pi/goal-extension.ts
|
||||
```
|
||||
|
||||
Use `/goal help`, `/goal set ...`, and `/goal status` in that test session. A released framework
|
||||
sync installs the file, and a released Mosaic CLI loads it automatically through `mosaic pi`.
|
||||
|
||||
## API Endpoint Reference
|
||||
|
||||
All endpoints are served by the gateway at `http://localhost:14242` by default.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Local Fleet Canary
|
||||
|
||||
The local fleet canary runs a small tmux-backed Mosaic agent fleet on an
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Migrating to the Federated Tier
|
||||
|
||||
> **KBN-101-07 ownership:** This active documentation is a **non-operative KBN-101
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: tracking
|
||||
status: active
|
||||
---
|
||||
|
||||
# Mission Manifest — Mosaic Native Kanban and Canonical Task SOT P0–P3
|
||||
|
||||
**Mission status:** CANON INDEPENDENTLY APPROVED; publication in progress under issue [#751](https://git.mosaicstack.dev/mosaicstack/stack/issues/751)
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
---
|
||||
|
||||
# Native Kanban/SOT P0–P3 — Dependency-Ordered Build Slices
|
||||
|
||||
**Status:** CANON INDEPENDENTLY APPROVED; PUBLICATION IN PROGRESS
|
||||
**Tracking:** [Mosaic Stack issue #751](https://git.mosaicstack.dev/mosaicstack/stack/issues/751)
|
||||
**Execution:** USC web1 only; collision-free GPT coder2/3/4/5 lanes
|
||||
**Contract:** `SHARED-CONTRACT.md` + four `contracts/*.v1.ts` files
|
||||
**Implementation hold:** no feature slice starts until the canon PR is merged to `main` with terminal-green CI; after merge, each slice remains held until every declared KBN prerequisite is complete.
|
||||
|
||||
> **HOLD LIFTED — 2026-08-20.** The stated condition ("no feature slice starts until the
|
||||
> canon PR is merged to `main` with terminal-green CI") was **met on 2026-07-14** by
|
||||
> `49e8a541` — _docs(#751): Publish native Kanban/SOT canon (#752)_ — which is on both
|
||||
> `main` and `next`, and `docs/requirements/native-kanban-sot.md` is present on `main`.
|
||||
> The line below was never updated, so for five weeks this file advertised a blocker that
|
||||
> did not exist. The status line above ("PUBLICATION IN PROGRESS") is stale for the same
|
||||
> reason. This file is a build plan, not a task tracker, and is NOT superseded.
|
||||
|
||||
**Implementation hold (SATISFIED — see note above):** no feature slice starts until the canon PR is merged to `main` with terminal-green CI; after merge, each slice remains held until every declared KBN prerequisite is complete.
|
||||
|
||||
> This publication file is not a runtime task authority. After cutover, repository `TASKS.md` is generated read-only and never imported.
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
---
|
||||
|
||||
# CI Queue Guard Purpose Semantics
|
||||
|
||||
- **Issue:** #1146
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: spec
|
||||
status: completed
|
||||
---
|
||||
|
||||
# CI Queue Guard Purpose Semantics Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
---
|
||||
|
||||
# Documentation Catalog and Truth Audit Plan
|
||||
|
||||
**Task:** DOCS-IA-002
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
---
|
||||
|
||||
# Documentation Information Architecture Design
|
||||
|
||||
**Status:** Approved
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: spec
|
||||
status: completed
|
||||
---
|
||||
|
||||
# Documentation Structure README Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
---
|
||||
kind: tracking
|
||||
status: active
|
||||
---
|
||||
|
||||
# W4 — document contract worklist
|
||||
|
||||
Companion to `2026-08-20_stack-docs-flatten-and-alignment.md`. That document proposes the
|
||||
contract; this one records what was applied, what was held, and what still needs a decision.
|
||||
|
||||
Measured on `origin/next` at `63069149`. Author: veronica. Review: fred (Gate-16, author is
|
||||
not reviewer), then a pi seat for the adversarial pass.
|
||||
|
||||
## What "live" means here
|
||||
|
||||
All `*.md` under `docs/`, minus `docs/archive/`, minus `docs/_old_structure/`. That is **127**
|
||||
files. The flatten plan says 130; the arithmetic does not close (318 total = 134 archive +
|
||||
57 `_old_structure` + 127 live, not 130).
|
||||
|
||||
## Applied
|
||||
|
||||
**These are the FIRST-PASS counts and they are superseded. The current tree is counted in
|
||||
"Verification arithmetic, re-closed" at the end of this document.** They are left standing rather
|
||||
than overwritten, for the same reason every other correction here is: a record of what a pass
|
||||
produced is worth more than a number silently updated to still look right.
|
||||
|
||||
| bucket | count | note |
|
||||
| --------------------------- | ----- | -------------------------------------- |
|
||||
| stamped `kind` + `status` | 107 | this pass |
|
||||
| held, operator judgement | 17 | section "Needs a decision" below |
|
||||
| held, cites the moving path | 3 | the three `SUPERSEDED` TASKS.md stamps |
|
||||
| held, generated file | 1 | `docs/fleet/NORTH_STAR.md`, see below |
|
||||
|
||||
128 live `.md` under `docs/`, which is the 127 baseline plus this document. 107 + 17 + 3 + 1 = 128.
|
||||
|
||||
Kinds: 53 `guide`, 34 `record`, 13 `spec`, 7 `tracking`. Status: 105 `active`, 2 `completed`.
|
||||
|
||||
After fred's Q1 ruling stamped `docs/README.md`, one file moved from the held bucket to the stamped
|
||||
one and nothing else changed: **108 stamped + 16 + 3 + 1 = 128**, kinds 54 `guide`, 34 `record`,
|
||||
13 `spec`, 7 `tracking`, status 106 `active`, 2 `completed`. Two files carry
|
||||
`source_of_truth: true`, `docs/README.md` and `docs/requirements/native-kanban-sot.md`.
|
||||
|
||||
`parent` is **not** applied. It points at `docs/fleet/NORTH_STAR.yaml`, which the flatten moves
|
||||
to `docs/NORTH_STAR.yaml`. Stamping it now means re-pointing 127 files by hand later. It lands
|
||||
after the move, in one pass, with the post-move path.
|
||||
|
||||
## Held: the three superseded stamps
|
||||
|
||||
`docs/TASKS.md:5`, `docs/federation/TASKS.md:5`, and `docs/fleet/TASKS.md:5` each carry a W1
|
||||
stamp of the form:
|
||||
|
||||
> **STATUS: SUPERSEDED — 2026-08-20.** kind `tracking` · superseded by `docs/fleet/NORTH_STAR.yaml`
|
||||
|
||||
Two problems, both real:
|
||||
|
||||
1. **The tooling cannot read them.** That line is a blockquote below the H1, not YAML front
|
||||
matter. Plan section 6 check 5 ("every live document has a header; no document is
|
||||
unclassified") parses front matter, so all three read as unclassified. The control that the
|
||||
parse itself works is `docs/fleet/FLEET-DOCTRINE.md:3`, which is genuine front matter.
|
||||
2. **They cite the moving path.** Three of the six citations of `docs/fleet/NORTH_STAR.yaml`
|
||||
are these stamps. Converting them before the flatten lands makes them stale on merge.
|
||||
|
||||
Converted after the flatten, not before.
|
||||
|
||||
## Held: the one generated file
|
||||
|
||||
`docs/fleet/NORTH_STAR.md` is a `projection` and was stamped in the first pass. The stamp was
|
||||
**reverted before review**, because stamping it is self-contradictory in a way that is not
|
||||
merely theoretical:
|
||||
|
||||
- The contract says a `projection` is "Generated. Never hand-edited." Adding front matter by
|
||||
hand is a hand edit of a generated file, and the file's own banner says
|
||||
"**Generated file — do not edit by hand.**"
|
||||
- `renderNorthStarMarkdown()` at `packages/mosaic/src/commands/fleet.ts:373` emits the H1 as
|
||||
its first line and no front matter at all.
|
||||
- `fleet-north-star.spec.ts:110-114` asserts full-string equality between the renderer's output
|
||||
and the committed file: `expect(rendered).toBe(committed)`. Before the stamp, line 1 of the
|
||||
committed file was `# Mosaic Fleet — NORTH STAR`, matching the renderer. After it, line 1 was
|
||||
`---`. The assertion fails.
|
||||
|
||||
So the header for a projection cannot live in the file. It has to be emitted by
|
||||
`renderNorthStarMarkdown()`, which is a code change and belongs in the flatten PR alongside the
|
||||
`resolveNorthStarPaths()` fix, not in a documentation pass.
|
||||
|
||||
This generalises: **the contract as written cannot classify any generated document without a
|
||||
matching change to its generator.** `NORTH_STAR.md` is the only projection today, so the cost
|
||||
is one function. It will not stay one.
|
||||
|
||||
## Needs a decision
|
||||
|
||||
Nineteen rows. Seventeen are the plan's section 9 list, minus `docs/fleet/north-star.md`
|
||||
(renamed to `FLEET-DOCTRINE.md` by W1, so the row is closed) and minus the two `.yaml` rows,
|
||||
which are a different problem — see the next section. Two rows are new, found while
|
||||
classifying.
|
||||
|
||||
Fill the `kind` column with a value from the contract, or `superseded-by: <path>`.
|
||||
|
||||
| path | proposed | why it is not mechanical |
|
||||
| --------------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `docs/README.md` | `guide` | It also **prescribes** the competing front-matter convention (see below). Whatever kind it gets, its body needs an edit. |
|
||||
| `docs/SITEMAP.md` | `guide` or `projection` | If it is generated from the tree it is a projection and needs a drift test. If hand-maintained it is a guide that goes stale silently. Nobody has said which. |
|
||||
| `docs/federation/SETUP.md` | `guide` | Reads as a guide. Federation tier status is the open part: if the tier is shelved this is `superseded`. |
|
||||
| `docs/fleet/FLEET-CONFIG-DOCS-IA-CHECKLIST.md` | `record` | An acceptance checklist is evidence of a past gate, which is `record`. But if it is still being checked against, it is `tracking`. |
|
||||
| `docs/fleet/FLEET-LAUNCH.md` | `guide` | Runbook. Depends on whether `launch-seat.sh` is deprecated in favour of `mosaic fleet` (campaign W3). If so, `superseded`. |
|
||||
| `docs/fleet/LEGACY-EXAMPLE-PROFILE-DISPOSITION-INVENTORY.md` | `record` | An inventory of dispositions taken. `record` unless dispositions are still pending, which the title implies they were once. |
|
||||
| `docs/fleet/README.md` | `guide` | Section index. Low risk; listed only because the plan lists it. |
|
||||
| `docs/fleet/backlog-conventions.md` | `guide` | Conventions decide things, and `guide` explicitly decides nothing. May be `spec`. |
|
||||
| `docs/fleet/f4-matrix-connector.md` | `spec` | F4 is a workstream. Whether it is live or abandoned decides `spec` versus `superseded`. |
|
||||
| `docs/native-kanban-sot/DOCUMENTATION-CHECKLIST.md` | `record` | Same question as the fleet IA checklist. |
|
||||
| `docs/native-kanban-sot/INDEX.md` | `guide` | Index of the canon. If it is generated from the canon it is a projection. |
|
||||
| `docs/native-kanban-sot/KBN-010-THREAT-AUTH-CONSTRAINT-GATE.md` | `spec` | 415 lines of normative gate. `spec` fits; confirm it is not superseded by the shared contract. |
|
||||
| `docs/native-kanban-sot/KBN-101-DB-ROLE-SPLIT.md` | `spec` | As above. |
|
||||
| `docs/native-kanban-sot/KBN-101-ENVELOPE-A.md` | `spec` | Title says "v6, FINAL". If v6 supersedes v1-v5 elsewhere, those need `superseded-by` pointing here. |
|
||||
| `docs/native-kanban-sot/SHARED-CONTRACT.md` | `spec` | "Remediated Shared Contract v1". Same versioning question. |
|
||||
| `docs/release-integrity/probe-inventory.md` | `record` | An inventory. `tracking` if probes are still being added to it. |
|
||||
| `docs/webui/PHASE-P-STRUCTURE.md` | `spec` | Zero inbound references (plan section 5.4). Either wire it in or mark it superseded; the kind is the smaller question. |
|
||||
| `docs/native-kanban-sot/TASKS.md` | **conflict** | The file says of itself: "This file is a build plan, not a task tracker, and is NOT superseded." The contract says `tracking` is "live state, single-writer (manifests, `TASKS.md`). **Never a spec**." The file claims to be the thing its name forbids. Either the file is wrong or the contract's `TASKS.md` shorthand is. |
|
||||
| `docs/requirements/native-kanban-sot.md` | **conflict** | Plan section 5.2 says if it is hand-authored canon it is `source-of-truth`. The contract's own table says `source-of-truth` is **machine-readable**. This is prose markdown with normative MUST/MUST NOT, RATIFIED 2026-07-14, decision owner Jason. It cannot satisfy both rules. Either it is a `spec` or the machine-readable criterion is wrong. |
|
||||
|
||||
The last two are not slow rows, they are contradictions inside the contract. They want an
|
||||
answer about the contract, not about the file.
|
||||
|
||||
## The contract collides with an existing one
|
||||
|
||||
`docs/README.md` lines 150-160 already document a front-matter convention, with its own
|
||||
allowed values:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Human-readable page title
|
||||
type: guide # guide | concept | reference | decision | rfc | runbook
|
||||
audience: developer # user | admin | developer | all
|
||||
status: current # current | draft | deprecated | historical
|
||||
source_of_truth: false
|
||||
---
|
||||
```
|
||||
|
||||
Adoption is 4 of 127 files: `docs/ADMIN-GUIDE/security/sso-providers.md`,
|
||||
`docs/DEVELOPER-GUIDE/testing/lease-broker-operations.md`,
|
||||
`docs/USER-GUIDE/getting-started/quickstart.md`, `docs/USER-GUIDE/product/web-dashboard.md`.
|
||||
|
||||
`status` is in both schemas with **disjoint vocabularies**. `type` and `kind` are two names for
|
||||
one idea with different value sets. `source_of_truth: false` is a boolean spelling of
|
||||
`kind: source-of-truth`.
|
||||
|
||||
What this pass did, and it is a decision someone should ratify or reverse: the new contract
|
||||
wins. Those 4 files had `status: current` rewritten to `status: active` and gained `kind:`.
|
||||
Their `title`, `type`, `audience` and `source_of_truth` keys were left alone. Nothing reads any
|
||||
of them — `git grep source_of_truth` outside `docs/` returns zero hits — so no consumer broke.
|
||||
|
||||
`docs/README.md` still prescribes the old convention. It is an operator row above, so this pass
|
||||
did not edit it. Until it is edited, the repository documents two conflicting header
|
||||
conventions and points authors at the one being retired.
|
||||
|
||||
## The contract has no form for a YAML document
|
||||
|
||||
Two of the plan's 20 rows are not markdown: `docs/fleet/examples/roster-v2.yaml` and
|
||||
`docs/openapi-tess.yaml`. Front matter is a markdown convention. A `.yaml` file can carry a
|
||||
leading `---` document, but then it is two YAML documents and every existing parser of that
|
||||
file sees a change.
|
||||
|
||||
This is not an edge case. It applies to **`docs/fleet/NORTH_STAR.yaml`, the source of truth
|
||||
itself**, which is the one file the contract most needs to classify. Section 6 check 5 says no
|
||||
document is unclassified. As written, the source of truth cannot comply.
|
||||
|
||||
Options, none of them chosen here: exclude `.yaml` from the contract and say so; carry their
|
||||
metadata in a sidecar; or add a top-level `kind:` key inside the YAML body rather than as front
|
||||
matter, which for `NORTH_STAR.yaml` is a schema change.
|
||||
|
||||
## Method, and what it cannot tell you
|
||||
|
||||
Classification is per-file, by title and path, recorded with a confidence. It is not a regex
|
||||
sweep. The plan's own warning stands and is why the 19 rows above are held rather than guessed:
|
||||
the first classifier pass classed a RATIFIED requirements document as a projection.
|
||||
|
||||
Rows marked `med` in the working manifest and not listed above: the five `docs/plans/*` specs,
|
||||
`docs/fleet/migration/example-profile-disposition.md`, the one ADR
|
||||
(`decisions/mos-runtime-portability-m1.md`, called `record` because an ADR records a decision
|
||||
taken), and the one RFC (`rfcs/optional-ai-egress-gateways.md`, called `spec` because it
|
||||
proposes work not yet built). Those eight are stamped and are the most likely to be wrong.
|
||||
|
||||
## Verification
|
||||
|
||||
- 103 of 103 files parse with the expected `kind` and `status` in front matter.
|
||||
- The check was shown to reject a wrong kind before it was trusted (asserting
|
||||
`kind: record` on a file stamped `guide` fails).
|
||||
- The whole diff removes 4 lines, all of them `status: current`.
|
||||
- 24 files untouched, matching 19 + 3 + 1 + 1.
|
||||
|
||||
## Response to the W5 adversarial pass
|
||||
|
||||
vision ran a refute-first pass on this branch at `37cd00e` from a fresh worktree. Three of its
|
||||
points changed the branch. Everything is re-measured here before being acted on; where my
|
||||
measurement disagrees with its stated evidence I say so.
|
||||
|
||||
### 1. `docs/fleet/NORTH_STAR.md` stamp reverted (`bea47543`)
|
||||
|
||||
vision raised this as **latent** and scoped to the flatten PR. It is **live in this PR**, so it
|
||||
could not wait.
|
||||
|
||||
`fleet-north-star.spec.ts:110-114` is a drift test that reads the committed file from disk and
|
||||
asserts full-string equality against `renderNorthStarMarkdown()`, whose first emitted line is the
|
||||
H1 and which emits no front matter. Stamping changed line 1 from the H1 to `---`.
|
||||
|
||||
CI 2589 confirms it directly, no longer by construction:
|
||||
|
||||
```
|
||||
× renderNorthStarMarkdown > matches the committed NORTH_STAR.md projection (regenerate if this fails)
|
||||
→ expected '# Mosaic Fleet — NORTH STAR\n\n> **Ge…' to be '---\nkind: projection\nstatus: active…'
|
||||
```
|
||||
|
||||
Reverted to `origin/next` verbatim. `git diff origin/next -- docs/fleet/NORTH_STAR.md` is 0 lines;
|
||||
control on `docs/fleet/reference/cli.md` returns 13, so the diff command does report differences.
|
||||
|
||||
The consequence is a contract-level one, recorded in the worklist: **the contract as written
|
||||
cannot classify any generated document without a matching change to its generator.** A `projection`
|
||||
is "Generated. Never hand-edited," so its header has to come out of the renderer. That is a code
|
||||
change and belongs in the flatten PR beside the `resolveNorthStarPaths()` fix. vision's
|
||||
recommendation, and I agree with it.
|
||||
|
||||
Counts: stamped 104 → **103**. Untouched 23 → **24**.
|
||||
|
||||
### 2. The `docs/` qualifier (vision's C1)
|
||||
|
||||
Stated as asked. **"127 live documentation files" is true for `docs/` only.** Definition: all
|
||||
`*.md` under `docs/`, minus `docs/archive/` and `docs/_old_structure/`. 318 total = 134 archive +
|
||||
57 `_old_structure` + 127 live.
|
||||
|
||||
Repo-wide the phrase undercounts: 21 live markdown files sit outside `docs/` and outside any named
|
||||
exclusion (17 under `guides/`, plus `README.md`, `AGENTS.md`, `CLAUDE.md`, `REPORT-A1207.md` at
|
||||
root). This PR does not stamp them and does not claim to.
|
||||
|
||||
### 3. `format` failure, and what it says about the header (`8a55c041`)
|
||||
|
||||
CI 2589 also failed `prettier --check` on **exactly one file**: the plan document I hand-wrote.
|
||||
Reproduced locally, rc=1, same single file. Fixed; the diff is 31 lines in, 31 out, all table
|
||||
column padding.
|
||||
|
||||
Worth stating for the review rather than burying: **the other 103 stamped documents pass
|
||||
`prettier --check` unchanged.** The `---\nkind:\nstatus:\n---` block is prettier-clean as applied.
|
||||
The formatting failure was in my prose, not in the contract header.
|
||||
|
||||
### 4. R1: the evidence inverts, the finding gets stronger
|
||||
|
||||
vision asks for a `kind` change on `docs/DEVELOPER-GUIDE/architecture/channel-protocol.md`, on the
|
||||
grounds that it "carries 7 normative MUSTs" while the contract says `guide` "decides nothing."
|
||||
|
||||
**The MUST count does not hold.** Uppercase RFC2119 terms (`MUST`, `MUST NOT`, `SHOULD`,
|
||||
`SHOULD NOT`, `SHALL`, `MAY`, `REQUIRED`) in that file: **0 lines**. Control: the identical grep
|
||||
returns 25 lines in `docs/requirements/native-kanban-sot.md`, so it finds them where they exist.
|
||||
The seven lowercase "must" occurrences all _disclaim_ authority rather than assert it: "must not be
|
||||
used as instructions", "must not be treated as current behavior", "must remain planned work", "must
|
||||
first specify", "before it can become architecture guidance." The file's own banner reads "it is
|
||||
not a runtime registry, an API contract, a requirements document."
|
||||
|
||||
**The citation half holds, and is larger than stated.** vision's line numbers are exact. I
|
||||
restated them earlier with wrong directories, which is worth naming because it is the same method
|
||||
failure fred and I already wrote up as C3 in the flatten plan: I matched on basename and assumed
|
||||
the path. The real ones, plus the two vision did not list:
|
||||
|
||||
| citing document | line | words used |
|
||||
| ------------------------------------------------------- | ---- | --------------------------------------------------- |
|
||||
| `docs/ADMIN-GUIDE/security/discord-ingress.md` | 141 | "**canonical** shared-contract and parity boundary" |
|
||||
| `docs/DEVELOPER-GUIDE/integrations/channel-adapters.md` | 28 | "The **canonical** architecture summary is" |
|
||||
| `docs/DEVELOPER-GUIDE/integrations/channel-adapters.md` | 183 | "**Canonical** channel protocol architecture" |
|
||||
| `docs/USER-GUIDE/workflows/discord-conversations.md` | 127 | "current shared types ... explicit parity boundary" |
|
||||
| `docs/SITEMAP.md` | 54 | index entry |
|
||||
| `docs/DEVELOPER-GUIDE/architecture/README.md` | 18 | index entry |
|
||||
|
||||
So the tension is real. Three live documents outside the two indexes cite it, across four
|
||||
citations, and three of those four use the word "canonical" for a document that spends its own
|
||||
banner denying it is canonical. **It is just not a MUST problem, and that
|
||||
changes what the fix is.** If the file is telling the truth about itself, the three "canonical"
|
||||
citations are wrong and the edit belongs in those three files, not in this one's `kind`.
|
||||
|
||||
**Left as `kind: guide` in this PR and flagged for the reviewer's call.** Restamping on evidence
|
||||
that inverts on reading would be worse than leaving it stamped and named.
|
||||
|
||||
### Unchanged
|
||||
|
||||
vision's C2 (no consumer), C4 (holding `parent` blocks nothing) and C5 (a front-mattered `.yaml`
|
||||
throws in `YAML.parse`) all reverified. C1's arithmetic closes at the stated ref.
|
||||
|
||||
## fred's six decisions, applied
|
||||
|
||||
Ruled on PR #1350 as comment 23693. Each is applied here; each is his call, not mine, and any of
|
||||
them is one line to reverse.
|
||||
|
||||
| # | decision | applied as |
|
||||
| --- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| A | New contract wins; `docs/README.md` is rewritten in this PR and the 4 old-schema files convert in the same pass | `docs/README.md:149-190` rewritten; `type:` dropped from the 4 files, `title`/`audience`/`source_of_truth` kept |
|
||||
| B | `source-of-truth` leaves the `kind` enum and returns as an orthogonal boolean | enum is now 6 values; `docs/requirements/native-kanban-sot.md` stamped `kind: spec` + `source_of_truth: true` |
|
||||
| C | `status` gains a third value, `completed` | the two executed plans take it (evidence below) |
|
||||
| D | Kind follows content, never filename | `docs/native-kanban-sot/TASKS.md` stamped `kind: spec`, because its body says "a build plan, not a task tracker" |
|
||||
| E | The contract covers `.md` only, stated as a decision rather than left as a gap | written into `docs/README.md` with vision's `YAML.parse` measurement as the reason |
|
||||
| F | `channel-protocol.md` becomes `spec` | applied, with one correction and one consequence below |
|
||||
|
||||
### C: evidence the two plans are complete
|
||||
|
||||
Neither plan self-declares completion, so this is measured from the artifacts rather than taken
|
||||
from the documents:
|
||||
|
||||
- `2026-08-10-ci-queue-purpose-implementation.md` — the shipped guard carries the flag the plan
|
||||
specifies: `ci-queue-wait.sh --purpose push|merge`, exercised in this session at rc=0.
|
||||
- `2026-08-10-docs-structure-readme.md` — every section the plan specifies exists in
|
||||
`docs/README.md` today, including the Obsidian conventions and the source-of-truth precedence
|
||||
block. This PR is editing the artifact that plan produced.
|
||||
|
||||
### F: the MUST count does not hold, and the fix changes shape
|
||||
|
||||
Applied as ruled. But the ruling's stated grounds are half wrong, and the half that survives points
|
||||
somewhere else, so this is the one to look at again.
|
||||
|
||||
**Zero** uppercase RFC2119 terms in `channel-protocol.md`, not seven. Control: the identical grep
|
||||
returns 25 lines in `docs/requirements/native-kanban-sot.md`. The seven lowercase "must"
|
||||
occurrences all _disclaim_ authority: "must not be used as instructions", "must not be treated as
|
||||
current behavior", "must remain planned work".
|
||||
|
||||
**The citation half holds and is bigger than stated** (table in the section above).
|
||||
|
||||
**The consequence of applying F:** the file is now stamped `spec` while its own banner says "it is
|
||||
not a runtime registry, an API contract, a requirements document." Header and body now contradict
|
||||
each other, which is the defect this pass exists to remove. Either the banner is edited in this PR,
|
||||
or the three documents calling it canonical are the ones that are wrong. That is a content call and
|
||||
it is left to the reviewer rather than folded into a stamping pass.
|
||||
|
||||
## Q1 — the one question this pass cannot answer for itself
|
||||
|
||||
`docs/README.md` now **prescribes** the document contract, and it is the only live document under
|
||||
`docs/` with no `kind`. It is still on the operator-held list, so it is left unstamped.
|
||||
|
||||
By decision B it is arguably `kind: spec` with `source_of_truth: true` for the subject "document
|
||||
contract". The reason this is not applied unilaterally: it decides which document outranks the
|
||||
other when `docs/README.md` and `docs/plans/2026-08-20_stack-docs-flatten-and-alignment.md` disagree
|
||||
about the contract, and they already differ (the plan's enum has 7 values, the README's now has 6).
|
||||
That is an authority question, not a classification one.
|
||||
|
||||
## The old schema DID have a consumer, and CI found it
|
||||
|
||||
vision's C2 concluded "no consumer found" after searching by parsing primitive rather than by key
|
||||
name. fred's decision A rested on "no installed base to protect." I accepted both. **All three of
|
||||
us were wrong, and the full test suite is what proved it.**
|
||||
|
||||
`packages/mosaic/src/installation-documentation.spec.ts:39` asserted:
|
||||
|
||||
```ts
|
||||
expect(markdown).toMatch(/^---\n[\s\S]*?\nstatus: current\n[\s\S]*?\n---\n/);
|
||||
```
|
||||
|
||||
That is a raw regex over the markdown text, not a front-matter parse and not a key lookup, which is
|
||||
exactly why a search organised around parsing primitives could not see it. It pins
|
||||
`docs/USER-GUIDE/getting-started/quickstart.md` to the old vocabulary. Replacing `status: current`
|
||||
with `status: active` turned it red.
|
||||
|
||||
Updated to `status: active`, the contract's value for "in force", with the reason in a comment
|
||||
beside it. Verified by evaluating both regexes against the real file: old `false`, new `true`.
|
||||
Control: a page carrying `superseded-by` still fails the new regex, so the assertion still asserts
|
||||
something rather than matching anything with front matter.
|
||||
|
||||
**The method point, which outlives this file.** CI 2592 ran the whole suite against the stamped
|
||||
tree, 46 turbo tasks, and returned exactly one failing spec: this one. That is a stronger consumer
|
||||
search than any grep the three of us ran, because it does not depend on guessing how a consumer
|
||||
reads the file.
|
||||
|
||||
An earlier draft of this paragraph put a test count here, "1617 tests, 1 failed". **That number was
|
||||
wrong and it is withdrawn.** Extracting per-test totals from these pipeline logs is not reliable:
|
||||
the same regex over the same log format returns 1003 for 2592, 1022 for 2593 and 3471 for 2594,
|
||||
which are runs of the same suite. Three irreconcilable answers from one method is proof the method
|
||||
does not measure what it claims. What the log does carry reliably is the FAIL list and the turbo
|
||||
task line, so the claim is stated in those terms instead. The point never needed the count: one
|
||||
named failing spec is what refuted three hand-searches. **Run the suite before concluding a
|
||||
documentation change has no code consumers.** Two independent seats and a reviewer searching by
|
||||
hand missed the one that existed.
|
||||
|
||||
## fred's second pass: F withdrawn, Q1 answered
|
||||
|
||||
Both changes below are fred's rulings, applied. Neither is my judgement and I record whose it is.
|
||||
|
||||
### F is withdrawn: `channel-protocol.md` returns to `kind: guide`
|
||||
|
||||
Reverted. fred re-measured the file himself with a control and reached the count I reported: zero
|
||||
uppercase RFC2119 terms case-sensitive, seven lowercase `must`, every one disclaiming authority,
|
||||
under a banner that refuses requirements status. A page like that is a guide.
|
||||
|
||||
The reason this matters past one stamp is fred's own: F rested on "the doc graph outranks the
|
||||
page's own banner", which contradicts his decision D, "kind follows content, never the filename and
|
||||
never what other documents say about it". D is the rule. F was a counterexample to it, written in
|
||||
the same comment.
|
||||
|
||||
Neither rescue was taken. vision's adjective edit on the three citing docs and an edit to the
|
||||
page's banner would both have preserved a stamp that should not have been made. The three citing
|
||||
documents stay as they are: under vision's index-pointer reading, "canonical" claims the best page
|
||||
on a subject rather than normative force, so there is nothing to correct.
|
||||
|
||||
**What this costs the contract: nothing, and that is the point.** A kind that survives only by
|
||||
editing the evidence around it is not a classification.
|
||||
|
||||
### Q1 answered: `docs/README.md` is stamped `kind: spec`, `source_of_truth: true`
|
||||
|
||||
Applied. By D, a document that prescribes the contract has spec content. By B, `source_of_truth` is
|
||||
an orthogonal boolean and this is the authoritative statement of the contract, so it carries both.
|
||||
|
||||
The authority half of Q1 was whether stamping the README settles a conflict with the flatten plan,
|
||||
whose enum still has seven values against the README's six. fred's ruling: **a plan never outranks
|
||||
the artifact it planned.** The plan's enum is pre-decision-B staleness, not a competing authority.
|
||||
The plan is fred's file and he corrects it there.
|
||||
|
||||
The contract now applies to the document that states it. That was the only state in which it could
|
||||
be credible.
|
||||
|
||||
### Verification arithmetic, re-closed
|
||||
|
||||
128 live `.md` under `docs/` = **108 stamped** + 16 operator-held + 3 supersede deferrals + 1
|
||||
generated. The stamped count rose by one and the operator-held set fell by one, both because of the
|
||||
README; every other row is unchanged. Control unchanged: the verifier reports `valid=False` when a
|
||||
kind is corrupted to `nonsense`.
|
||||
|
||||
### One consumer finding that is not a defect
|
||||
|
||||
A sweep of every spec reading a path under `docs/` returns 10 files. Four read a live file:
|
||||
`fleet-north-star.spec.ts` and `installation-documentation.spec.ts` (both already caught by CI),
|
||||
`mutator-gate.acceptance.spec.ts` reading `compaction-revocation.md`, which passes under its `guide`
|
||||
stamp, and `roster-v2.spec.ts:366`, which reads `docs/fleet/reference/roster-v2.schema.json`.
|
||||
|
||||
The fourth is a real read of a real live file under `docs/` and is untouched only because decision E
|
||||
scopes the contract to `.md`. Had the contract covered every file under `docs/`, front matter in
|
||||
that JSON schema would have broken the spec, the same failure as the quickstart regex. E was
|
||||
load-bearing beyond the YAML-parse grounds it was decided on. No action; recorded so the `.md`
|
||||
boundary is not widened later without re-running this sweep.
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Documentation Plans
|
||||
|
||||
> **Status:** Current artifact index. Plans record approved intent and execution approach; they are not current product behavior or operational authority.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
kind: tracking
|
||||
status: active
|
||||
---
|
||||
|
||||
# Tasks — Release Integrity Workstream (RI-050, #1275)
|
||||
|
||||
> Single-writer: the RI-050 orchestrator (jarvis, dragon-lin) only. Workers read but never modify.
|
||||
>
|
||||
> **Mission:** alpha 0.0.50 release-integrity floor (decisions SDLC-D-033..038).
|
||||
> **PRD:** [docs/PRD.md § Release Integrity Workstream](../PRD.md#release-integrity-workstream-ri-1275)
|
||||
> **Issue:** #1275 (remains open until RI-V-001 closes)
|
||||
> **Base branch:** `next` (all cards branch from `origin/next`, squash-merge via PR)
|
||||
>
|
||||
> **Execution note:** the `agent` column uses `pi-glm-5.3` — outside the pipeline-cron model
|
||||
> table on purpose. This workstream is executed by jarvis on dragon-lin with local pi workers
|
||||
> (`pi --model zai/glm-5.3:high`); pipeline crons must not auto-claim these rows.
|
||||
>
|
||||
> **Status values:** `not-started` | `in-progress` | `done` | `blocked` | `failed` | `needs-qa`
|
||||
> `done` requires: repo quality gates green, independent review recorded, terminal-green CI on
|
||||
> the PR head, squash merge to `next`, and acceptance evidence in notes.
|
||||
|
||||
| id | status | description | issue | agent | repo | branch | depends_on | estimate | notes |
|
||||
| -------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---------- | ----------------- | --------------------------------- | ---------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| RI-0-001 | done | Bootstrap: issue #1275, PRD section, this DAG, scratchpad (docs only) | #1275 | pi-glm-5.3 | mosaicstack/stack | docs/ri-050-mission-bootstrap | — | 6K | PR #1276 (head 758659dd): docs-only, CI green (2475). Review requested from fargo. Merges first (no publish run). |
|
||||
| RI-1-001 | done | RI-N1: canonical terminal verification command + publish-pipeline exact-commit gate (every publish step depends on verify; commit identity check; fail closed) | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-publish-gate | RI-0-001 | 25K | PR #1277 (head 46784c8d): CI GREEN at head after serialized retry (pipeline 2476, 2026-08-18) - earlier red was CI-agent contention (web SPA timeouts under concurrent pipelines), not code. Review requested from fargo at pinned head (comms 20260818T021025Z). |
|
||||
| RI-1-002 | done | RI-N1 negative control: checked-in tests proving a broken mandatory check blocks every publish step and that DAG edges cannot be bypassed | #1275 | pi-glm-5.3 | mosaicstack/stack | test/ri-050-publish-gate-negative | RI-1-001 | 12K | |
|
||||
| RI-2-001 | done | RI-N2 (Forge): remove stub-executor false success; `--simulate` typed `simulated` results that satisfy nothing; literal-`true` gates and echo-review replaced with real gates or typed waiting-for-authority | #1275 | pi-glm-5.3 | mosaicstack/stack | fix/ri-050-forge-fail-closed | RI-0-001 | 20K | Independent review APPROVED 2026-08-17 (Gitea review 172 on PR #1278, head 99b8f6ea; reviewing seat fargo — recorded under shared host principal mos-dt-0, provenance correction posted by fred; wrapper gap filed by fred). Executed at head: forge tests 116/116, lint green, typecheck green after building macp dist (minimal-install artifact, not a defect), workspace typecheck 45/45, no external type consumers of the changed interfaces. CI red = known lane-wide fleet-test failure only, carries no information about this change (fred, log-content analysis, pipelines 2456-2458). Non-blocking finding: README L141-143 + skills/mosaic-forge/SKILL.md document bare forge run/resume, which now fails closed — fast-follow docs touch. Merge queued behind #1270. UPDATE 2026-08-18: #1270 merged; CI GREEN at head 4917df1f via serialized retry (pipeline 2477) - root cause of prior reds was CI-agent contention (web SPA timeouts under concurrent pipelines), superseding the fleet-test-failure theory. |
|
||||
| RI-2-002 | done | RI-N2 (MACP): gate runner fails closed on empty commands, stub executors, and unimplemented CI-provider gates unless explicit simulate; typed capability failures | #1275 | pi-glm-5.3 | mosaicstack/stack | fix/ri-050-macp-fail-closed | RI-0-001 | 15K | PR #1293 (head 2097379e): CI green (pipeline 2465), independent review APPROVED (Gitea review 173, jarvis seat, 2026-08-17) - macp 109/109 verified at head. Merge queued behind #1276/#1277/#1278. |
|
||||
| RI-3-001 | done | RI-N4: complete probe inventory mapping every TS and shell quality-rail check to one canonical check with disposition (preserve/strengthen/retire, each named) | #1275 | pi-glm-5.3 | mosaicstack/stack | docs/ri-050-qr-probe-inventory | RI-0-001 | 12K | PR #1302 (head e06a47fac591): CI green (2484), independent review APPROVED (Gitea review 187, fargo seat, 2026-08-18) — 54 rows / 21 canonical checks / dispositions 43-2-9-0 verified by row-count and code spot-checks. Merged by fargo at pinned head. |
|
||||
| RI-3-002 | not-started | RI-N4: TS evaluator absorbs effective shell probes; typed results (passed/failed/blocked/error/not-applicable) with versioned digested check definitions; shell commands become thin adapters; contract/parity/negative-control tests | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-qr-evaluator | RI-3-001 | 30K | |
|
||||
| RI-4-001 | in-progress | RI-N3: one PRD application service — `mission --plan` persists mission↔PRD linkage (ids/versions/selected requirements); `mosaic prdy` routes through the service or becomes a named import/export adapter; Markdown is a labeled generated view; explicit conflict-aware import | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-prd-authority | RI-0-001 | 35K | PR #1294 (head 8d258e1d): CI green (pipeline 2466), independent review APPROVED (Gitea review 174, jarvis seat, 2026-08-17) - prdy 20/20 + command specs 9/9 at head. Merge queued behind #1276/#1277/#1278. |
|
||||
| RI-5-001 | done | RI-N5: typed freshness states (current/stale/partial/unknown/unavailable); no failed-fetch-renders-empty; stale derived verdicts → unknown; mutations disabled when stale; failure-matrix tests | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-web-stale-safety | RI-0-001 | 25K | |
|
||||
| RI-V-001 | not-started | Final verification + release evidence: all cards verified merged, negative controls demonstrated, real `next` publish run green on exact commit, evidence pack recorded | #1275 | pi-glm-5.3 | mosaicstack/stack | docs/ri-050-release-evidence | RI-1-002, RI-2-001, RI-2-002, RI-3-002, RI-4-001, RI-5-001 | 10K | |
|
||||
|
||||
## Dispatch waves (max 2 parallel workers)
|
||||
|
||||
1. RI-1-001 + RI-2-001
|
||||
2. RI-2-002 + RI-4-001
|
||||
3. RI-3-001 + RI-5-001
|
||||
4. RI-1-002 + RI-3-002
|
||||
5. RI-V-001
|
||||
|
||||
## Budget
|
||||
|
||||
Derived soft cap: 250K tokens (no explicit cap given). Projected total: 190K.
|
||||
Conservative mode (1 worker) above 70% projected; freeze above 90%.
|
||||
@@ -0,0 +1,186 @@
|
||||
# Quality-Rails Probe Inventory — RI-3-001
|
||||
|
||||
- **Task:** RI-3-001 (SDLC-D-037 first half; PRD § Release Integrity Workstream, RI-N4)
|
||||
- **Date:** 2026-08-18
|
||||
- **Base:** `origin/next` @ `8199261c` (branch `docs/ri-050-qr-probe-inventory`)
|
||||
- **Follow-up:** RI-3-002 consumes the dispositions here when building the single TS evaluator.
|
||||
|
||||
## 0. Scope and method
|
||||
|
||||
Every mechanism in this repository that verifies a quality, integrity, safety, or release
|
||||
property — TypeScript checks, shell probes, pipeline steps, git hooks, and installer-side
|
||||
assertions — gets one row. Each row's "what it actually verifies" was written from the
|
||||
probe's **code**, not its name or docs. Framework tool unit/regression suites (git wrappers,
|
||||
wake, tmux, orchestrator, …) are treated as one enforcement surface (`test:framework-shell`)
|
||||
because they test tool behavior rather than repo quality; their wiring integrity is itself
|
||||
guarded by `check-test-enumeration.sh`, and the quality-relevant members are rowed
|
||||
individually.
|
||||
|
||||
**Kinds:** `ts` (TypeScript/Node check), `shell` (bash/python probe), `pipeline-step`
|
||||
(exists only inside a Woodpecker pipeline).
|
||||
|
||||
**Enforcement points:** `local` (operator-invoked), `pre-commit`, `pre-push`,
|
||||
`CI ci.yml#<step>`, `publish.yml#<step>` (CI on push to main/next), `turbo <task>`,
|
||||
`agent-runtime` (framework hooks on an agent host), `installer` (host install path),
|
||||
`unwired`.
|
||||
|
||||
**Dispositions** (recommendations for RI-3-002): `preserve` (keep as-is; already the
|
||||
canonical or a correct guard-of-the-guard), `strengthen` (keep, but a concrete gap must
|
||||
close — usually absorption into the TS evaluator), `strengthen (review)` (viable retirement
|
||||
candidate once the evaluator absorbs it; do not retire yet). Note: RI-N4 requires that
|
||||
effective shell probes be **absorbed before** their independent paths retire — no row here
|
||||
is marked `retire` because no absorption exists yet.
|
||||
|
||||
## 1. Inventory
|
||||
|
||||
### 1.1 Repo-level gate tasks (pnpm / turbo)
|
||||
|
||||
| check | location | kind | what it actually verifies | enforcement point | canonical check | disposition | rationale |
|
||||
| ------------------------------------- | ------------------------------------------------------------------------------------ | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `pnpm preflight` (checkout preflight) | `scripts/preflight.mjs` | ts | Six gate binaries (eslint, husky, prettier, tsc, turbo, vitest) exist and are executable in `node_modules/.bin` (exit 42 if not); no stale `.mosaic-test-work/web-build.lock` (exit 43); `apps/web/.next` is a real directory (not a symlink), every entry owned by the current uid, and its `.mosaic-source-hash` fingerprint + `.mosaic-symlink-manifest` hash match the certified build written by `scripts/build-web.mjs` | `pre-push`; inside `pnpm typecheck` (→ `CI ci.yml#typecheck`, verify-release `typecheck` stage) | QC-1 Checkout integrity | preserve | Blocks a poisoned/stale generated `.next` from faking a green typecheck (the five-month-stale-`.next` class); trust chain is self-contained per-checkout. |
|
||||
| `pnpm typecheck` | root `package.json` → `turbo run typecheck` | ts | Per-package `tsc --noEmit` (all 20 packages); turbo `typecheck` depends on `^build`, so package builds must succeed first; prefixed by checkout preflight | `CI ci.yml#typecheck`; `pre-push`; verify-release `typecheck` stage; `turbo typecheck` | QC-2 Workspace typecheck | preserve | The single workspace-wide type gate; CI and hooks invoke the same task, no divergent checklist. |
|
||||
| `pnpm lint` | root `package.json` → `turbo run lint` | ts | Per-package `eslint src` under root `eslint.config.mjs` (ignores `dist`, `.next`, `framework/**`, etc.) | `CI ci.yml#lint`; `pre-push`; verify-release `lint` stage; `turbo lint` | QC-3 Workspace lint | preserve | Same-task invocation from every surface; no second lint definition. |
|
||||
| `pnpm format:check` | root `package.json` → `prettier --check` | ts | Prettier parse/format equality over `**/*.{ts,tsx,js,jsx,json,md}` minus `.prettierignore` (generated trees, `docs/scratchpads/`, venvs, …) | `CI ci.yml#format`; `pre-push`; verify-release `format` stage | QC-4 Format check | preserve | Single formatter, single ignore list, enforced identically everywhere. |
|
||||
| `pnpm test` | root `package.json` `test` = `test:checkout` && `turbo run test` && `test:installer` | ts | (a) `node --test scripts/*.test.mjs` — checkout-tool units; (b) per-package `vitest run` (mosaic appends the 47-command `test:framework-shell` chain); (c) `tools/install-next-lane.test.sh`; turbo `test` declares DB env vars and depends on `^build` | `CI ci.yml#test` (with `DATABASE_URL` + `db:migrate` first); verify-release `test` stage; `turbo test` | QC-5 Test suite execution | preserve | One composed test command; the chain property (any link red ⇒ step red) is the gate. |
|
||||
| `pnpm build` | root `package.json` → `turbo run build` | ts | Per-package build (`tsc`/Next) with `^build` dependency and `dist/**` outputs | `publish.yml#build`; verify-release `build` stage; `turbo build` | QC-6 Workspace build | preserve | Publish artifacts derive from the same build task CI verifies. |
|
||||
|
||||
### 1.2 Framework quality shell probes (`packages/mosaic/framework/tools/quality/`)
|
||||
|
||||
| check | location | kind | what it actually verifies | enforcement point | canonical check | disposition | rationale |
|
||||
| ------------------------------------------- | ----------------------------------------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| Sanitization gate | `scripts/verify-sanitized.sh` | shell | Built-in self-test first (planted identity/structural/YAML+service fixtures; exit 2 if the regexes or extension coverage break), then: (1) identity denylist grep (`jarvis\|jason\|woltje\|brain.woltje.com\|/home/jwoltje\|\bPDA\b`) over all shipped text files **including** `examples/`; (2) structural grep for private `$HOME/src` defaults in shipped scripts **excluding** `examples/`. Any hit ⇒ exit 1 | `CI ci.yml#sanitization`; verify-release `sanitization` stage | QC-7 Framework sanitization | preserve | Labeled one-time regression guard with a self-test that prevents silent no-op; correctly scoped (identity vs structural) and documented as not a general PII detector. |
|
||||
| Resident-context budget | `scripts/check-resident-budget.sh` (+ `--self-test`) | shell | Self-test of the comparator, then `wc -l` vs per-file ceilings (CONSTITUTION 120, AGENTS 120, each RUNTIME.md 90); missing file ⇒ fail; over ceiling ⇒ exit 1 | `CI ci.yml#sanitization` (both modes); verify-release `sanitization` stage | QC-8 Resident-context budget | preserve | Caps the container (lines), never the wording — the deliberate anti-drift design (DESIGN §7); CI-enforceable half only, by design. |
|
||||
| Test-membership enumeration guard (#1017) | `scripts/check-test-enumeration.sh` + `test-enumeration-exclusions.txt` | shell | Parses surface S1 (`packages/mosaic` `test:framework-shell` via JSON+shlex) and S2 (every `framework/tools/\*.sh | .py`token in`ci.yml`, comment lines stripped); population = `_test_.sh`under`framework/tools`; FAILS on: suite-shaped file on disk neither enumerated nor signed-excluded; surface naming a path missing on disk (both directions); exclusion without reason / stale / outside population / contradicting enumeration. Proves **naming, not reachability** (stated in-file) | `CI ci.yml#sanitization` (direct line); link [0] of `test:framework-shell` (thus `CI ci.yml#test`); verify-release `sanitization` stage | QC-9 Test-membership enumeration | preserve | Makes silent under-run impossible; invoked from both surfaces it audits so severing the chain cannot silence it. |
|
||||
| Enumeration-guard needles | `scripts/test-check-test-enumeration.sh` | shell | Needle/control fixtures driven through `--root`: every promised failure mode must trip the guard **on its own words**, plus controls that must pass (null-case defense); covers commented-out ci.yml lines (F1) and line-range parsing (n2b) | `test:framework-shell` → `CI ci.yml#test`; verify-release `test` stage | QC-9 Test-membership enumeration | preserve | Guard-of-the-guard with both polarities; same canonical check by design. |
|
||||
| Upgrade manifest guard (#791 HARD GATE) | `scripts/test-upgrade-manifest-guard.sh` | shell | Keep-mode `install.sh` upgrade against seeded throwaway `MOSAIC_HOME`: every operator sentinel — including an **unanticipated** one — survives byte-identical with unchanged mtime; framework files still update; retired framework files pruned; matrix run with rsync present AND absent (keep path must be rsync-independent); fail-closed matrix (empty/operator-only/malformed/missing manifest aborts loudly, operator files untouched); operator secret never appears in installer output | `CI ci.yml#upgrade-guard`; verify-release `upgrade-guard` stage | QC-10 Upgrade/install safety | preserve | The operator-data hard gate for the `mosaic update` path; negative controls are load-bearing and documented. |
|
||||
| Upgrade rollback gate (#791 B1) | `scripts/test-upgrade-rollback.sh` | shell | Mid-sync failure (PATH-shadowing `cp` shim) must trigger snapshot restore: restore message fires, corrupted file restored, target byte-identical to pre-upgrade; control installer with `set -E` stripped must NOT roll back (proves errtrace is load-bearing); plus signal/exit-guard controls | `CI ci.yml#upgrade-guard`; verify-release `upgrade-guard` stage | QC-10 Upgrade/install safety | preserve | Proves the rollback trap actually fires; the `-E`-stripped control keeps Part A honest. |
|
||||
| Durable-snapshot gate (#791 PR2) | `scripts/test-upgrade-durable-snapshot.sh` | shell | Pre-update snapshot taken before any mutation (0700/0600 perms, secret never logged, retention-pruned); post-sync verify net restores operator files a manifest bug lets the sync touch; CWE-59 symlink-leaf guard proven with a portable cp shim in both polarities (write-through-link must not happen); v1→v2 migration semantics (intended `bin/` removal not healed) | `CI ci.yml#upgrade-guard`; verify-release `upgrade-guard` stage | QC-10 Upgrade/install safety | preserve | Covers tampering and leak vectors the manifest guard cannot see; the shim rationale (busybox vs GNU cp) is documented in-file. |
|
||||
| Install migration matrix (v2→v3) | `scripts/test-install-migration.sh` | shell | Fixture matrix running the real installer with `MOSAIC_SYNC_ONLY=1`: fresh install seeds + stamps version 3; legacy user-edited AGENTS overwritten with `.pre-constitution.bak` preserved (and idempotent); tuned STANDARDS overwritten; operator files (SOUL, credentials) preserved. Mirrors the TS suite `packages/mosaic/src/config/file-adapter.test.ts` — both installers must behave identically | `CI ci.yml#upgrade-guard`; verify-release `upgrade-guard` stage | QC-10 Upgrade/install safety | preserve | Pins the shell/TS installer parity contract; removal would orphan that parity requirement. |
|
||||
| Enforcement verification probe (bash) | `scripts/verify.sh` | shell | Attempts **real commits** in the target repo: planted type error must produce a commit blocked with `error`; planted `any` must trip `no-explicit-any`; planted lint error must trip `prettier`; gitleaks binary must exist (3a) and detect a planted AWS key via `gitleaks git --pre-commit --staged --redact` (3b). Verdicts are output-grep matches on hook stderr | `local` via installed `mosaic-quality-verify` on scaffolded target projects; **not run in this repo's CI** | QC-20 Downstream enforcement verification | strengthen (review) | Mechanism is genuinely behavioral (stronger than file presence) but verdict logic is grep-on-output and it is unwired here; absorb as the evaluator's enforcement-probe check (the RI-N4 evaluator invokes it or reimplements it) before retiring the shell path. |
|
||||
| Enforcement verification probe (PowerShell) | `scripts/verify.ps1` | shell | Windows port of `verify.sh`: same planted-commit tests with `$output -match` matching; no gitleaks self-test parity beyond the same checks | `local` (Windows operator); no Windows CI runner exists | QC-20 Downstream enforcement verification | strengthen (review) | A hand-maintained twin of `verify.sh` with no CI coverage — exactly the drift shape the single evaluator removes; retire after the TS evaluator owns the probe. |
|
||||
| Quality template installer (bash) | `scripts/install.sh` | shell | Copies template files (`.husky/pre-commit` incl. mandatory gitleaks, `.lintstagedrc.js`, `.eslintrc.js`, `tsconfig.json`, `.woodpecker.yml`, `.gitleaks.toml`) into a target project; **warns** (does not verify) about `package.json` snippet merge; no post-condition check | `local` / via `mosaic-quality-apply` | QC-21 Downstream rails scaffolding | strengthen (review) | Duplicates the TS `quality-rails init` scaffolder for a different template set; converging on one scaffolder (with post-scaffold verification) is prerequisite to retiring this path. |
|
||||
| Quality template installer (PowerShell) | `scripts/install.ps1` | shell | Windows twin of the template copy above | `local` (Windows operator) | QC-21 Downstream rails scaffolding | strengthen (review) | Same twin-drift risk as `verify.ps1`; no runner exercises it. |
|
||||
| `mosaic-quality-verify` adapter | `framework/tools/_scripts/mosaic-quality-verify` | shell | Thin adapter: validates target dir exists, asserts `verify.sh` present+executable, `cd` target, exec it. No verdict logic of its own | `local` (installed framework bin) | QC-20 Downstream enforcement verification | preserve | Already the thin-adapter shape RI-N4 prescribes for shell surfaces. |
|
||||
| `mosaic-quality-apply` adapter | `framework/tools/_scripts/mosaic-quality-apply` | shell | Thin adapter: arg validation then exec of quality `install.sh --template … --target …` | `local` (installed framework bin) | QC-21 Downstream rails scaffolding | preserve | Thin adapter, no separate verdict; disposition follows its target script's convergence. |
|
||||
| Roster schema regression | `scripts/test-roster-schema.py` | shell | jsonschema `Draft202012Validator` over `fleet/roster.schema.json` with valid/invalid connector-kind fixtures (tmux/discord/matrix conditional fields) | **unwired** — not on S1 or S2, not signed-excluded; also outside the enumeration guard's `*.sh` population, so the guard cannot see it | QC-5 Test suite execution | strengthen (review) | A real regression suite that currently runs nowhere; wire it into a CI surface or sign an exclusion — leaving it invisible re-arms the exact gap #1017 closed. |
|
||||
| Framework shell chain (S1) | `packages/mosaic/package.json` `test:framework-shell` | shell | 47-command `&&` chain: enumeration guard + needles, 14 lease-broker/mutator-gate python unitests, `check-runtime-launches.py`, and ~30 framework-tool shell suites (git wrappers, wake, woodpecker, tmux, glpi, orchestrator, `_scripts`). Quality-relevant members rowed separately below | `turbo test` → `CI ci.yml#test`; verify-release `test` stage | QC-5 Test suite execution | preserve | The chain is the execution surface the enumeration guard audits; known residuals: a failing link stops later suites (measured in #1270 — suites after position 44 had not run), and the guard proves naming, not reachability. |
|
||||
|
||||
### 1.3 Framework runtime hooks and their harnesses (agent-host enforcement)
|
||||
|
||||
| check | location | kind | what it actually verifies | enforcement point | canonical check | disposition | rationale |
|
||||
| ------------------------------------- | ----------------------------------------------------------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | --------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| QA edit hook seam | `framework/tools/qa/qa-hook-stdin.sh` (+ `qa-hook-handler.sh`) | shell | PostToolUse stdin hook: extracts edited file from the tool JSON (jq or grep fallback), skips non-JS/TS, then the deps-preflight gate — exits 1 with the legible sentinel `deps not installed — run pnpm install` when `node_modules/.bin` is missing/empty (the #856 false-red class); the downstream handler only files QA remediation **report templates** (no verification logic) | `agent-runtime` (framework `runtime/claude/settings.json` PostToolUse); never CI | QC-16 Agent-runtime edit-time checks | strengthen (review) | The sentinel gate is real enforcement; the handler's report-filing adds no verdict and its name promises more than the code does — evaluator absorption should keep the sentinel, drop the report theater. |
|
||||
| Typecheck-on-edit hook | `framework/tools/qa/typecheck-hook.sh` | shell | PostToolUse: for edited `.ts/.tsx`, finds nearest `tsconfig.json` and runs `tsc --noEmit`, surfacing errors nonzero to the agent immediately | `agent-runtime` (framework `runtime/claude/settings.json` PostToolUse) | QC-16 Agent-runtime edit-time checks | strengthen (review) | Edit-time duplicate of QC-2 with independent invocation logic; keep behavior, converge invocation through the evaluator adapter. |
|
||||
| Deps-preflight harness | `framework/tools/qa/test-deps-preflight.sh` | shell | Five assertions against the seam incl. a documented RED control (raw `not found`), sentinel behavior for missing and empty `.bin`, and no-false-positive once populated | `test:framework-shell` → `CI ci.yml#test` | QC-16 Agent-runtime edit-time checks | preserve | Guard-of-the-check with a red control; keeps the sentinel from regressing. |
|
||||
| Prompt-helper RCE regression | `framework/tools/_scripts/test-mosaic-init-rce.sh` | shell | Sources the prompt helpers and proves a literal `$(touch /tmp/pwned)` answer round-trips verbatim and never executes (no `/tmp/pwned` created) | `test:framework-shell` → `CI ci.yml#test` | QC-5 Test suite execution | preserve | Cheap, load-bearing security regression on the installer's input path. |
|
||||
| Install-ordering harness (#869 C2) | `framework/tools/_scripts/test-install-ordering-guard.sh` | shell | Drives `mosaic-link-runtime-assets` with a fake `mosaic` on PATH: probe ok ⇒ settings copied + exit 0; probe fail ⇒ exit 1 with degraded outcome but all other runtime files still copied; `--allow-inactive-enforcement` forwarded; no-mosaic-on-PATH ⇒ python3 fallback strips enforcement hooks and exits 1; fallback + flag ⇒ wires as-is, exit 0 | `test:framework-shell` → `CI ci.yml#test` | QC-17 Lease-enforcement wiring safety | preserve | Exercises the shell wiring seam independently of the TS guard's own spec suite (complementary coverage, by design). |
|
||||
| Fleet-transport harness (#1240) | `framework/tools/_scripts/test-fleet-transport-check.sh` | shell | Extracts the shipped `check_fleet_transport`/`fleet_declared_transport` functions **from the shipped scripts** (fails loud if extraction yields nothing) and drives both implementations (mosaic-doctor + `tools/install.sh`) from one case table | `test:framework-shell` → `CI ci.yml#test` | QC-18 Operator-host drift audit | preserve | The anti-drift harness for the one rule shipped twice; extraction-from-source keeps it from testing a stale copy. |
|
||||
| Terminal-green contract (RM-61/#1000) | `framework/tools/woodpecker/test-terminal-green-contract.sh` + `verify-terminal-green.py` | shell | Red-first fixtures: pipeline JSON variants (service failure, step failure, cancelled, etc.) must produce the correct terminal-green verdict; controls must pass | `test:framework-shell` → `CI ci.yml#test` | QC-5 Test suite execution | preserve | Keeps the CI-wait wrapper's green-detection honest; a false green here would poison every merge gate that trusts `pr-ci-wait.sh`. |
|
||||
| Lease-gate launch invariant | `framework/tools/lease-broker/check-runtime-launches.py` | shell | Scans production roots (`packages/`, `apps/`, `plugins/`, `tools/`) across sh/py/ts/yaml suffixes for Claude/Pi process launches **outside** the lease gate; allowlist-based; fails CI on violation | `test:framework-shell` → `CI ci.yml#test` | QC-15 Lease-gate architecture invariant | preserve | The only architectural "no ungated launches" rail; grep+allowlist is the right cost/benefit for this invariant. |
|
||||
|
||||
### 1.4 TypeScript quality logic (`@mosaicstack/quality-rails` + mosaic CLI)
|
||||
|
||||
| check | location | kind | what it actually verifies | enforcement point | canonical check | disposition | rationale |
|
||||
| ---------------------------------------- | ---------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `quality-rails check` | `packages/quality-rails/src/cli.ts` (`mosaic quality-rails check --project`) | ts | **Expected-file presence only**: loops `expectedFilesForKind` (node: `.eslintrc`, `biome.json`, `.githooks/pre-commit`, `PR-CHECKLIST.md`; python: `pyproject.toml`+hooks+checklist; rust: `rustfmt.toml`+…) and exits 1 listing missing paths. Does not execute any linter, formatter, hook, or scanner | `local` (operator CLI); **no CI wiring in this repo** | QC-19 Downstream rails presence check | strengthen | This is the RI-N4 evaluator seed. Today presence ≠ parity (explicitly called out by RI-N4): it must grow typed verdicts (`passed/failed/blocked/error/not-applicable`), check versioning/subject/reason, digested definitions, and absorb the effective shell probes (QC-20 first). |
|
||||
| `quality-rails doctor` | `packages/quality-rails/src/cli.ts` | ts | Same presence data as `check`, printed with ok/missing lines; **cannot fail** (no nonzero exit on missing files) | `local` (operator CLI) | QC-19 Downstream rails presence check | strengthen | A doctor that cannot fail is advisory; fold into `check` (or return typed states) when the evaluator lands. |
|
||||
| `quality-rails init` | `packages/quality-rails/src/cli.ts` + `scaffolder.ts`/`templates.ts` | ts | Scaffolds rails files per detected kind/profile (linters/formatters lists are advisory strings; hooks flag always true); writes files, prints follow-ups — no post-condition verification | `local` (operator CLI) | QC-21 Downstream rails scaffolding | strengthen (review) | Second scaffolding path alongside quality `install.sh` (§1.2); converge on one with post-scaffold verification before retiring either. |
|
||||
| Lease activation probe (#869 C1, hidden) | `packages/mosaic/src/commands/lease-activation-probe.ts` | ts | Real capability probe, not file presence: resolves the installed mosaic CLI and requires it to advertise the exact `{name, version}` activation contract; all deps injectable; registered as hidden CLI command and consumed by C2/C5 | `local` (hidden CLI + consumed by C2/C5); spec-tested via `lease-activation-probe.spec.ts` in `turbo test` | QC-17 Lease-enforcement wiring safety | preserve | The versioned-contract probe is precisely the fail-closed capability check RI-N2 generalizes; already typed and injectable. |
|
||||
| Install-ordering guard (#869 C2, hidden) | `packages/mosaic/src/commands/install-ordering-guard.ts` | ts | Decides whether enforcement hook entries are written into the `~/.claude/settings.json` the framework reseed ships: not activatable ⇒ strip hooks + nonzero loud outcome (default); explicit per-invocation `--allow-inactive-enforcement` opt-out wires-with-warning. Never touches the runtime gate's own fail-closed behavior | `installer` (framework reseed via `mosaic-link-runtime-assets`); spec + shell harness coverage in `turbo test` | QC-17 Lease-enforcement wiring safety | preserve | Correct default-deny with an explicit, non-env opt-out; test-locked from both the TS and shell sides. |
|
||||
| Lease doctor check (#869 C5) | `packages/mosaic/src/commands/lease-doctor-check.ts` | ts | Combines hook-wiring detection in `~/.claude/settings.json` with C1 activatable and C3 broker-supervisor health: wired ∧ (¬activatable ∨ ¬healthy) ⇒ loud `[ERROR]` that forces `mosaic doctor` exit 1 regardless of the bash audit's own exit | `local` (inside `mosaic doctor`); spec coverage in `turbo test` | QC-17 Lease-enforcement wiring safety | preserve | Closes the "bricked host looks green" hole; cannot be masked by the bash script — that composition is the point. |
|
||||
| `mosaic doctor` (framework drift audit) | `packages/mosaic/src/commands/launch.ts` (`doctor`) + `framework/tools/_scripts/mosaic-doctor` | shell+ts | Bash audit of the installed framework home: ~40 expected files/dirs present; runtime files are copies (not symlinks) matching source (`cmp`) or composed runtime-contract markers; hard-gates block present in AGENTS.md; sequential-thinking MCP configured; fleet transport binary present per roster (warn); legacy symlink trees gone; skills synced — **warn-based, exit 1 only with `--fail-on-warn`**, plus C5's forced error | `local` (operator audit) | QC-18 Operator-host drift audit | preserve | Host-state audit CI cannot see (user files by design, DESIGN §7); advisory exit is the documented contract — do not silently change it. |
|
||||
| `mosaic gateway doctor` | `packages/mosaic/src/commands/gateway-doctor.ts` | ts | Probes per-service health (PostgreSQL, Valkey, pgvector) via `@mosaicstack/storage`, reports tier and JSON; exit 1 only when at least one **required** service fails (yellow stays 0) | `local` (operator) | QC-18 Operator-host drift audit | preserve | Service health with correct red/yellow exit semantics; JSON mode exists for scripting. |
|
||||
| `mosaic gateway verify` | `packages/mosaic/src/commands/gateway/verify.ts` | ts | Post-install liveness: daemon meta via HTTP with retries, admin token on file, bootstrap endpoint reachable; aggregated pass/fail | `local`; consumed by `tools/e2e-install-test.sh` | QC-18 Operator-host drift audit | preserve | The first-run proof the installer E2E relies on; retry-aware so startup races don't false-red. |
|
||||
| `mosaic fleet doctor` | `packages/mosaic/src/commands/fleet-reconciler-command.ts` | ts | Classifies local roster-owned drift (no mutation) from the parsed v2 roster | `local` (operator) | QC-18 Operator-host drift audit | preserve | Dry-run classification is the correct non-mutating audit shape. |
|
||||
|
||||
### 1.5 Git hooks (developer machine)
|
||||
|
||||
| check | location | kind | what it actually verifies | enforcement point | canonical check | disposition | rationale |
|
||||
| ------------------------- | --------------------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| Pre-commit staged hygiene | `.husky/pre-commit` → `npx lint-staged` (`.lintstagedrc`) | shell | On staged files only: `prettier --write` + `eslint --fix` for ts/tsx/js/jsx; `prettier --write` for json/md/yaml/yml. **Mutating** (fixes and re-stages); commit blocks only if a fixer itself fails | `pre-commit` (every local commit; hooks activated by `install-hooks.mjs` via `core.hooksPath .husky/_`) | QC-13 Staged-change hygiene | preserve | Correct scoped fast gate; note it auto-fixes rather than rejects (deliberate). Gap: no secret scan here — see §3. |
|
||||
| Pre-push gate | `.husky/pre-push` | shell | `pnpm preflight && pnpm typecheck && pnpm lint && pnpm format:check` (no test run — documented in AGENTS.md) | `pre-push` | QC-14 Pre-push gate | preserve | Composes QC-1..4 exactly as specified in AGENTS.md; tests intentionally left to CI. |
|
||||
| Hook installer | `scripts/install-hooks.mjs` (`pnpm prepare`) | ts | Stages husky hooks into a scratch repo first, asserts husky produced its `h` shim, quarantines incomplete previous sets, verifies idempotence via full directory snapshot comparison, then sets `core.hooksPath`; skips cleanly with `HUSKY=0` or no git | `installer` (runs on `pnpm install`) | QC-13 Staged-change hygiene | preserve | Self-verifying wiring for the hook gates — a corrupted half-install cannot silently disable them. |
|
||||
|
||||
### 1.6 CI pipeline steps (`.woodpecker/`)
|
||||
|
||||
Step-to-probe mapping for container steps: `ci.yml#sanitization` = QC-7+QC-8+QC-9 (rows §1.2, plus `apk add bash` env prep); `ci.yml#upgrade-guard` = QC-10 (rows §1.2, plus `apk add rsync`); `ci.yml#typecheck`/`#lint`/`#format`/`#test` = QC-2/3/4/5 (rows §1.1). Rows below are mechanisms that exist only in a pipeline.
|
||||
|
||||
| check | location | kind | what it actually verifies | enforcement point | canonical check | disposition | rationale |
|
||||
| -------------------------------------- | -------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- | ----------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| Frozen install | `ci.yml#install` | pipeline-step | `pnpm install --frozen-lockfile --prefer-offline` against the baked ci-base store — lockfile supply integrity; a drifted lockfile fails the build before any gate runs | `CI ci.yml#install` | QC-1 Checkout integrity | preserve | Lockfile-pinned dep resolution is the supply-chain floor under every later gate. |
|
||||
| Test-step readiness prelude | `ci.yml#test` prologue | pipeline-step | Installs pinned `@earendil-works/[email protected]` (Invariant R suite requires the real binary) + openssl; waits up to 60×1s on `pg_isready` for the `ci-postgres` service and fails fast if it never comes up; runs `db:migrate` before tests | `CI ci.yml#test` | QC-5 Test suite execution | preserve | Fail-fast environment preconditions — a missing service produces a legible failure, not a wall of red tests. |
|
||||
| Publish verify step (pending RI-1-001) | `publish.yml#verify` (branch `feat/ri-050-publish-gate` @ `46784c8d`, not yet on next) | pipeline-step | (a) Commit identity: fails closed if `CI_COMMIT_SHA` empty, `git rev-parse HEAD` empty, or the two differ; (b) runs the canonical `pnpm verify:release`. **Every publish effect depends on this step; it carries no path filter** | `publish.yml#verify` | QC-11 Terminal release verification | preserve | The RI-N1 exact-commit binding; until it merges, publish steps on next depend on `build` only (see §3 gap 1). |
|
||||
| Publish error classification | `publish.yml#publish-npm` | pipeline-step | Publishes `@mosaicstack/*` (minus web) and classifies outcome: success, or the **only tolerated failure** = already-published (EPUBLISHCONFLICT / "cannot publish over" / "previously published"); explicit fatal on npm `E404/E401/ENEEDAUTH/ECONNREFUSED/ETIMEDOUT/ENOTFOUND` and on any unrecognized failure (replacing the old ` | | echo` that hid a registry 404) | `publish.yml#publish-npm` (main/tags, path-filtered on `packages/**`) | QC-12 Publish-effect integrity | preserve | Converts silent publish fall-on-floor into loud failure; allowlist-of-one error tolerance is the right shape. |
|
||||
| Next-lane publish assertions | `publish.yml#publish-next-npm` | pipeline-step | Guards: branch must be `next`, `CI_PIPELINE_NUMBER` required; registry dist-tags JSON must be usable; walks all manifests, strictly parses stable semver, rewrites `X.Y.(Z+1)-next.<N>`; publishes with `--tag next` (never latest); post-publish asserts `npm view @mosaicstack/mosaic@next` resolves to the exact expected version | `publish.yml#publish-next-npm` (push/manual on next) | QC-12 Publish-effect integrity | preserve | Durable prerelease lane with end-to-end resolution proof — the published artifact is verified, not assumed. |
|
||||
| Image destination policy | `publish.yml#build-gateway` / `#build-appservice` / `#build-web` | pipeline-step | Kaniko builds with destination policy: `next` ⇒ sha-tag only (fatal if a tag event sneaks in); `main` ⇒ sha + `latest`; tag events ⇒ sha + `<tag>`; anything else fatal. Path filters only skip **effects**, never the verify step | `publish.yml#build-*` | QC-12 Publish-effect integrity | preserve | Fail-closed tagging matrix; the exclude-list default-safe design keeps stale images impossible. |
|
||||
|
||||
Adjacent pipeline surface (not a probe): `.woodpecker/ci-image.yml` rebuilds the ci-base image on `pnpm-lock.yaml`/`Dockerfile.ci` change with an immutable `lock-<hash>` tag; pipelines consume `:latest`. Recorded for completeness — no code-quality property is checked.
|
||||
|
||||
### 1.7 Root installer tooling (`tools/`)
|
||||
|
||||
| check | location | kind | what it actually verifies | enforcement point | canonical check | disposition | rationale |
|
||||
| --------------------------- | --------------------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Next-lane installer test | `tools/install-next-lane.test.sh` (`pnpm test:installer`) | shell | Drives `tools/install.sh --next` with faked `node`/`npm` binaries (no network): Node 20 must be rejected; installs must pin **exact** versions (mutable `@next` forbidden); fast path must not unexpectedly fall back to source; gateway-install failure takes the documented fallback | `turbo`-external tail of `pnpm test` → `CI ci.yml#test` | QC-5 Test suite execution | preserve | Hermetic (shimmed) regression net for the installer lane; runs as part of the standard test command. |
|
||||
| Clean-container install E2E | `tools/e2e-install-test.sh` | shell | Full first-run flow in a node:22-alpine container: `install.sh --yes` → `mosaic wizard` (non-interactive) → `mosaic gateway install` → `mosaic gateway verify` exit check (with EXPECTED-SKIP if the installed CLI predates `gateway verify`); skips gracefully without Docker | `local` (manual; requires Docker); **not wired in CI** | QC-5 Test suite execution | strengthen (review) | The only end-to-end proof of the install→verify path; currently operator-initiated only — wire into a periodic/manual CI lane or sign its exclusion explicitly. |
|
||||
| Host installer advisories | `tools/install.sh` (`--check`; `check_fleet_transport`) | shell | `--check` = version comparison only, no install; `check_fleet_transport` warns (non-blocking, by design — tmux is the fleet's dependency, not mosaic's) when the roster-declared transport binary is absent, naming exactly what it blocks; PATH-persistence warnings | `installer` (operator-run) | QC-18 Operator-host drift audit | preserve | Advisory-by-design warnings; the parallel doctor check is drift-tested by §1.3's harness. |
|
||||
|
||||
### 1.8 Pending workstream additions (branch `feat/ri-050-publish-gate` @ `46784c8d`)
|
||||
|
||||
| check | location | kind | what it actually verifies | enforcement point | canonical check | disposition | rationale |
|
||||
| ------------------------------- | ---------------------------------------------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------- | ----------- | ---------------------------------------------------------------------------------------------- |
|
||||
| Canonical terminal verification | `scripts/verify-release.mjs` (`pnpm verify:release`) | ts | One command replaying the full mandatory set as stages — sanitization, upgrade-guard, typecheck (incl. preflight), lint, format, test, build — mirroring `ci.yml` step-for-step; fail-fast on first failing command; requires `bash`+`rsync` on PATH; `--stage <name>` for wiring smoke-tests only | `publish.yml#verify` (pending); `local` (`pnpm verify:release`) | QC-11 Terminal release verification | preserve | The RI-N1 canonical command — CI and publication share one semantic checklist by construction. |
|
||||
| Verify-parity contract test | `scripts/verify-release.test.mjs` | ts | Parses the real `ci.yml`/`publish.yml`: stage table must match ci.yml step-for-step; every publish-effect step (name `publish*` or image-pushing) must transitively depend on `verify`; commit-identity assertion must be present; `verify` must carry no path filter | `test:checkout` → `CI ci.yml#test` (once merged) | QC-11 Terminal release verification | preserve | Guard-of-the-guard at checkout time — the two surfaces cannot drift apart silently. |
|
||||
|
||||
## 2. Canonical check set
|
||||
|
||||
The deduplicated checks every row above maps onto. IDs are stable for RI-3-002 to consume.
|
||||
|
||||
- **QC-1 Checkout integrity.** Owns: the checkout can run its gates — frozen-lockfile dependency resolution, required gate binaries present, no stale build lock, and the `apps/web/.next` generated-state trust chain (real directory, uid ownership, certified source fingerprint, certified symlink manifest). Implemented by `scripts/preflight.mjs` + frozen install steps.
|
||||
- **QC-2 Workspace typecheck.** Owns workspace-wide TypeScript soundness: per-package `tsc --noEmit` over built dependencies (`turbo typecheck`). The single definition invoked by CI, pre-push, and terminal verification.
|
||||
- **QC-3 Workspace lint.** Owns static-analysis policy: per-package ESLint under the root config. One config, one task, every surface.
|
||||
- **QC-4 Format check.** Owns formatting uniformity: Prettier check with the repo ignore list. (The pre-commit variant additionally fixes; the verdict form is this check.)
|
||||
- **QC-5 Test suite execution.** Owns execution of all test surfaces: checkout script units (`node --test`), per-package Vitest suites (including the framework shell chain and its python unitests), the installer-lane shim test, and — once wired — `test-roster-schema.py` and container E2E. Also owns guards-of-the-gate that live inside the chain (terminal-green contract, RCE regression).
|
||||
- **QC-6 Workspace build.** Owns artifact buildability: `turbo build` producing the artifacts publication consumes.
|
||||
- **QC-7 Framework sanitization.** Owns the open-source guarantee for the shipped framework package: no operator-identity tokens anywhere (examples included), no private `$HOME` defaults in shipped scripts, with a self-test that keeps the regexes honest.
|
||||
- **QC-8 Resident-context budget.** Owns the line-count ceilings on framework files injected into every agent's context (Constitution, dispatcher, RUNTIME.md slices) — the CI-enforceable half of the resident-prompt budget.
|
||||
- **QC-9 Test-membership enumeration.** Owns the property that no test suite can silently fall out of CI: disk population vs parsed enumeration surfaces, both-directions staleness, and signed exclusions with reasons. Includes its needle/control harness.
|
||||
- **QC-10 Upgrade/install safety.** Owns the #791 family: operator-path byte-identity across keep-mode upgrades (manifest guard), mid-failure rollback (errtrace-proven), durable pre-update snapshot + verify net + CWE-59 leaf guard, and the v2→v3 migration matrix with shell/TS parity.
|
||||
- **QC-11 Terminal release verification.** Owns the RI-N1 exact-commit binding: commit-identity assertion plus one canonical command (`pnpm verify:release`) replaying the complete mandatory set, with every publish effect depending on it; plus the checkout-time parity/DAG contract test that keeps pipeline and command in sync.
|
||||
- **QC-12 Publish-effect integrity.** Owns publication correctness: npm publish error classification (only already-published tolerated), next-lane versioning and post-publish resolution proof, and image destination/tag policy.
|
||||
- **QC-13 Staged-change hygiene.** Owns commit-time hygiene on staged files (prettier/eslint fix-and-restage) and the self-verifying hook wiring that guarantees the gates are actually installed.
|
||||
- **QC-14 Pre-push gate.** Owns the local push composition: preflight + typecheck + lint + format:check (tests deliberately deferred to CI).
|
||||
- **QC-15 Lease-gate architecture invariant.** Owns "no ungated runtime launches in production code": the scan + allowlist over `packages/`, `apps/`, `plugins/`, `tools/`.
|
||||
- **QC-16 Agent-runtime edit-time checks.** Owns edit-time feedback on agent hosts: the deps-preflight legibility sentinel and typecheck-on-edit, plus their regression harnesses.
|
||||
- **QC-17 Lease-enforcement wiring safety.** Owns the #869 C1/C2/C5 trio: activation capability probe (versioned contract), enforcement-hook wiring gate (default-deny with explicit opt-out), and the doctor check that surfaces a bricked host — with their shell/TS harnesses.
|
||||
- **QC-18 Operator-host drift audit.** Owns host-state health CI cannot see: `mosaic doctor` drift audit (+ fleet transport, both implementations), `fleet doctor` roster classification, `gateway doctor`/`gateway verify` service health, and installer advisories. Advisory exits are part of the contract.
|
||||
- **QC-19 Downstream rails presence check.** Owns "does a scaffolded project still carry its rails files" — today the TS `quality-rails check/doctor` presence loop; per RI-N4 this is the seed that must become the typed evaluator (presence alone is explicitly not parity).
|
||||
- **QC-20 Downstream enforcement verification.** Owns "do the rails actually block" on scaffolded projects: the behavioral planted-commit probe (type error, `any`, lint, gitleaks secret) currently in `verify.sh`/`verify.ps1` behind the `mosaic-quality-verify` adapter.
|
||||
- **QC-21 Downstream rails scaffolding.** Owns putting rails files into a target project: the shell template installer (+ PowerShell twin) and the TS `quality-rails init` scaffolder — currently two paths that must converge.
|
||||
|
||||
## 3. Coverage gaps
|
||||
|
||||
Enforced nowhere but implied, or named in docs/tooling but not wired:
|
||||
|
||||
1. **Publication not yet bound to verification on `next`.** At this base (`8199261c`), `publish.yml` publish steps depend on `build` only; the `verify` step and `scripts/verify-release.mjs` exist on `feat/ri-050-publish-gate` (`46784c8d`) but are not merged. Until RI-1-001 lands, AC-RI-1's negative control cannot hold on the real pipeline.
|
||||
2. **Playwright E2E unwired.** `apps/web` ships `test:e2e` (`playwright test`) with real suites (`admin/auth/chat/navigation.spec.ts`); neither `pnpm test` nor any CI step invokes it. The web UI's user flows are verified only when an operator runs them manually.
|
||||
3. **No secret scanning on this repo.** The framework's own template pre-commit makes gitleaks **required**, and `verify.sh` proves detection with a planted key — but this repository's `.husky/pre-commit` (lint-staged only) and CI run no secret scan. The repo ships the control it does not use.
|
||||
4. **No dependency audit.** The quality `.woodpecker.yml` templates and `docs/CI-SETUP.md` specify `npm audit --audit-level=high` as a pipeline stage; nothing equivalent runs for this repo.
|
||||
5. **No coverage thresholds.** Templates enforce 80% Jest coverage thresholds; this repo's Vitest configs collect coverage with no thresholds — coverage is measured nowhere and enforced nowhere.
|
||||
6. **`test-roster-schema.py` invisible.** A real jsonschema regression suite wired to no surface and invisible to the enumeration guard (its population is `*.sh`; the suite is `.py`). Either enumerate it or sign an exclusion — silence here is the #1017 defect shape.
|
||||
7. **Presence-checker expectations ≠ this repo.** `quality-rails check` expects `.eslintrc`, `biome.json`, `.githooks/pre-commit`, `PR-CHECKLIST.md` for node projects — none describe this monorepo (husky, flat eslint config, no biome, no PR-CHECKLIST.md). The evaluator's check set must be per-subject (versioned, digested), not one global file list.
|
||||
8. **Chain-ordering residual (documented).** `test:framework-shell` is one `&&` chain: a failing link skips every later suite while the step still fails (measured in #1270 — four suites after position 44 had not run since a prior merge). The enumeration guard proves naming, not reachability; both residuals are in-file documented but structurally unfixed.
|
||||
9. **Signed-exclusion burndown open.** 16 signed exclusions remain in `test-enumeration-exclusions.txt`; several are "unmeasured in CI image" or blocked on missing CI tooling (tmux, setsid) — tracked under #1017/#1271. Each is an enforcement promise deferred, not delivered.
|
||||
10. **Windows twins unexercised.** `verify.ps1`, `install.ps1`, `mosaic-doctor.ps1` have no runner anywhere (no Windows CI); behavioral drift from their bash twins is undetectable by construction.
|
||||
11. **QA hook name vs behavior.** `qa-hook-handler.sh` files remediation report templates but performs no verification; the seam's actual gate value is only the deps-preflight sentinel. Anything relying on "QA automation hook" as a check is relying on report-filing.
|
||||
12. **Two test paths, one gated.** CI runs tests against ci-postgres (`DATABASE_URL` set); the local PGlite path is the documented default (AGENTS.md) until KBN-101-02/101-05. Only the CI path is enforced by pipeline.
|
||||
|
||||
## 4. Disposition summary
|
||||
|
||||
| disposition | rows | checks |
|
||||
| ------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| preserve | 43 | Every canonical owner (QC-1..QC-18) plus correct guards-of-the-guard and thin adapters: all of §1.1, the CI-invoked framework probes and adapters in §1.2, all of §1.3, the C1/C2/C5 trio and doctors in §1.4, all of §1.5, all pipeline-only steps in §1.6, §1.7 rows 1 and 3, and §1.8. |
|
||||
| strengthen | 2 | `quality-rails check` and `quality-rails doctor` (QC-19) — the RI-N4 evaluator seed: typed verdicts, versioned/digested check definitions, per-subject check sets. |
|
||||
| strengthen (review) | 9 | `verify.sh` + `verify.ps1` (QC-20), quality `install.sh`/`install.ps1` + `quality-rails init` (QC-21 — scaffold-path convergence), `test-roster-schema.py` (QC-5 — wire or sign), `qa-hook-stdin.sh` seam + `typecheck-hook.sh` (QC-16), `tools/e2e-install-test.sh` (QC-5 — CI lane). |
|
||||
| retire | 0 | None meet the bar: RI-N4 requires effective shell probes be **absorbed before** their paths retire, and no absorption exists yet. The `strengthen (review)` rows are the retirement candidates for RI-3-002 once the evaluator owns their behavior. |
|
||||
|
||||
Row total: 54. Canonical checks: 21 (QC-1..QC-21).
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: guide
|
||||
status: active
|
||||
---
|
||||
|
||||
# Documentation Reports
|
||||
|
||||
> **Status:** Current evidence index. Reports record reviews, tests, audits, and deferred findings; they are not requirements or operational instructions by themselves.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: record
|
||||
status: active
|
||||
---
|
||||
|
||||
# Independent Code Review — #756 Official Discord Channel Plugin
|
||||
|
||||
**Verdict: APPROVE**
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: record
|
||||
status: active
|
||||
---
|
||||
|
||||
# Code Review Report — Gateway Security Hardening
|
||||
|
||||
## Scope Reviewed
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: record
|
||||
status: active
|
||||
---
|
||||
|
||||
# #830 Documentation Completion Checklist
|
||||
|
||||
## Required artifacts
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
kind: record
|
||||
status: active
|
||||
---
|
||||
|
||||
# FCM-M5-001 Fleet Documentation Deferrals and Holds
|
||||
|
||||
**Issue:** #758 · **Branch:** `docs/758-fleet-config-operator-docs`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user