Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
186 lines
9.3 KiB
Markdown
186 lines
9.3 KiB
Markdown
# Contributing to the Mosaic Framework
|
||
|
||
The Mosaic framework is the open-source agent-operating layer that deploys to
|
||
`~/.config/mosaic/`. It is designed to be **forked and customized** — but the
|
||
shared core must stay operator-neutral, deduplicated, and upgrade-safe. This
|
||
guide is the contract for changing framework-owned files.
|
||
|
||
> Governance model and layer rationale: `constitution/LAYER-MODEL.md` (source-only).
|
||
> Requirements & phase history: `docs/design/framework-constitution/`.
|
||
|
||
---
|
||
|
||
## 1. The layer model (where does my change go?)
|
||
|
||
| Layer | What | Owner | On upgrade | File(s) |
|
||
| ------ | ------------------------------------------------------------- | ---------------- | --------------------------------------- | -------------------------------------------- |
|
||
| **L0** | Constitution — the non-negotiable law (hard gates) | Framework | **Overwritten** | `CONSTITUTION.md` |
|
||
| **L1** | Standards & guides — how to do the work well | Framework | Overwritten; user delta → `*.local.md` | `STANDARDS.md`, `guides/*` |
|
||
| **L2** | Persona (SOUL) — agent name, tone, role | User (init) | **Never overwritten** | `SOUL.md` (+ optional `SOUL.local.md`) |
|
||
| **L3** | Operator (USER) — human identity, prefs, policy | User (init) | **Never overwritten** | `USER.md` (+ optional `USER.local.md`) |
|
||
| **L4** | Project / runtime mechanism — per-repo deltas; harness wiring | Repo / framework | Project user-owned; runtime overwritten | `<repo>/AGENTS.md`, `runtime/<h>/RUNTIME.md` |
|
||
|
||
**The one sentence a user can rely on:** edit `SOUL.md` / `USER.md` and the
|
||
`.local.md` overlays — they survive every upgrade. To change framework behavior,
|
||
add a `.local.md` overlay; never edit a framework-owned file in place.
|
||
|
||
---
|
||
|
||
## 2. Operator hygiene (PII / secrets prohibition) — **blocking**
|
||
|
||
Framework-owned files ship publicly. They **must not** contain:
|
||
|
||
- Operator or personal identity (names, handles, pronouns, accessibility notes).
|
||
- Private `$HOME` paths, private hostnames, or domains.
|
||
- Secrets, tokens, or credentials (use `~/.config/mosaic/credentials.json`; the
|
||
hook URL soft-degrades via `${OPENBRAIN_URL}`).
|
||
|
||
This is enforced by `tools/quality/scripts/verify-sanitized.sh`, wired **blocking**
|
||
in CI (`.woodpecker/ci.yml`). It runs two rule classes: structural (private-`$HOME`
|
||
defaults, dead paths, unrendered tokens) and a labeled current-contaminant denylist.
|
||
Run it locally before pushing:
|
||
|
||
```bash
|
||
bash packages/mosaic/framework/tools/quality/scripts/verify-sanitized.sh
|
||
```
|
||
|
||
Operator-specific behavior belongs in **your** `SOUL.md`/`USER.md`/`*.local.md`,
|
||
never in the shared core. (The "framework-PR firewall" in `CONSTITUTION.md` §4
|
||
states this as law for agents opening framework PRs.)
|
||
|
||
---
|
||
|
||
## 3. Dedup rule — one source, everyone references it
|
||
|
||
Hard gates live in **`CONSTITUTION.md` (L0) only**. `AGENTS.md`, `STANDARDS.md`,
|
||
and every `runtime/<h>/RUNTIME.md` **reference** the law — they never restate it.
|
||
Restating a gate is a defect: it creates two sources that drift. If you find a
|
||
gate duplicated outside L0, delete the copy and point to L0.
|
||
|
||
`AGENTS.md` is a thin dispatcher (load order + guide router + the tier-aware
|
||
self-load). Keep it that way; new procedure goes in `guides/*` (on-demand), not
|
||
in the resident core.
|
||
|
||
---
|
||
|
||
## 4. Resident line-count ceiling — **blocking**
|
||
|
||
The framework-owned files injected by value (`CONSTITUTION.md`, `AGENTS.md`, each
|
||
`runtime/<h>/RUNTIME.md`) are budgeted by **line count** — never by word count
|
||
(a word cap forces paraphrasing the law, the exact drift vector we removed).
|
||
|
||
```bash
|
||
bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh
|
||
```
|
||
|
||
Wired blocking in CI. Gate **wording** stays intact; if a file legitimately needs
|
||
more lines, raise its ceiling in the script deliberately (in the same PR, with
|
||
rationale). The per-harness _total_ resident prompt (which also sums the user's
|
||
`SOUL.md`/`USER.md`) is a `mosaic doctor` runtime advisory — CI cannot see user
|
||
files, so it is out of CI scope by design (DESIGN §7).
|
||
|
||
---
|
||
|
||
## 5. Dual-installer parity rule
|
||
|
||
Two installers seed and migrate `~/.config/mosaic/`:
|
||
|
||
- **`framework/install.sh`** (bash) — the canonical installer.
|
||
- **`packages/mosaic/src/config/file-adapter.ts`** (TS) — the wizard path.
|
||
|
||
**Any change to seed lists, overwrite/preserve semantics, or migration MUST land
|
||
in BOTH**, validated by the **shared fixture suite**:
|
||
|
||
- `framework/tools/quality/scripts/test-install-migration.sh` (bash matrix)
|
||
- `packages/mosaic/src/config/file-adapter.test.ts` (vitest)
|
||
|
||
Both assert the same behavior: framework-owned files overwrite (backup-once to
|
||
`*.pre-constitution.bak`); user-seeded files seed-if-absent; `SOUL.md`/`USER.md`/
|
||
`*.local.md`/`credentials` are preserved. A change in one installer without the
|
||
other (and its fixtures) is incomplete.
|
||
|
||
---
|
||
|
||
## 6. Adding a harness adapter
|
||
|
||
A harness (runtime) is wired by:
|
||
|
||
1. `runtime/<h>/RUNTIME.md` — **mechanism only** (subagent syntax, hook/MCP wiring,
|
||
injection method). No restated gates (see §3).
|
||
2. Launcher emission in `src/commands/launch.ts` — how the composed contract reaches
|
||
the harness (system-prompt append vs. instructions file). Add the harness to the
|
||
`RuntimeName` union and the runtime-path map.
|
||
3. `mosaic compose-contract <harness>` works automatically once the runtime path
|
||
exists (it composes base + `*.local.md` overlays for that harness).
|
||
|
||
Then add a row to the compliance matrix (§8) and mark which gates are mechanical
|
||
vs. resident-only for the new harness.
|
||
|
||
---
|
||
|
||
## 7. Re-contamination rule
|
||
|
||
A green sanitization gate is not permanent. Before every PR:
|
||
|
||
- Do not reintroduce operator identity, private paths, or secrets (§2).
|
||
- Do not copy a gate out of L0 (§3).
|
||
- Do not add an unrendered template token or a dead path to a shipped file.
|
||
|
||
If `verify-sanitized.sh` goes red, that diff **is** your worklist — fix it, don't
|
||
suppress it.
|
||
|
||
---
|
||
|
||
## 8. Harness × gate compliance matrix
|
||
|
||
How each gate is enforced per harness. **Mechanical** = a hook/CI check the agent
|
||
cannot bypass. **Resident** = injected contract prose (strong, but not a hard stop).
|
||
**CI** = repo-side, harness-independent.
|
||
|
||
| Gate / mechanism | Claude | Codex | OpenCode | Pi |
|
||
| --------------------------------------------- | ----------- | ---------------- | ---------------- | ---------------- |
|
||
| Contract injection (resident-by-value) | append SP | instructions | `AGENTS.md` | append SP |
|
||
| Operator overlays (`*.local`, composed) | ✅ | ✅ | ✅ | ✅ |
|
||
| Bare-launch self-load (Tier-3, read L0) | ✅ | ✅ | ✅ | ✅ |
|
||
| Sanitization (no PII) — `verify-sanitized` | CI ✅ | CI ✅ | CI ✅ | CI ✅ |
|
||
| Resident budget ceiling | CI ✅ | CI ✅ | CI ✅ | CI ✅ |
|
||
| Migration parity (5-fixture, both installers) | CI ✅ | CI ✅ | CI ✅ | CI ✅ |
|
||
| `no-memory-write` (PreToolUse hook) | **mech ✅** | resident-only ⚠️ | resident-only ⚠️ | resident-only ⚠️ |
|
||
| QA / typecheck (PostToolUse hooks) | **mech ✅** | resident-only ⚠️ | resident-only ⚠️ | resident-only ⚠️ |
|
||
| Native heartbeat (fleet `ps` model/status) | sidecar | sidecar | sidecar | **native ✅** |
|
||
|
||
⚠️ **Hook-parity gap (tracked, v2):** the mechanical PreToolUse/PostToolUse hooks
|
||
exist for Claude Code only. On Codex/OpenCode/Pi those gates are currently enforced
|
||
by the resident contract + CI, not by a per-tool hook. Closing hook parity is a
|
||
**v2** item, not part of this alpha.
|
||
|
||
---
|
||
|
||
## 9. Known limitations (accepted residual risks)
|
||
|
||
These are accepted with rationale (DESIGN §9); they are documented, not bugs:
|
||
|
||
- **Bare-launch overlays are base-only.** A harness started without `mosaic` never
|
||
ran the composer, so `*.local.md` overlays are not applied. Mitigated by the
|
||
unconditional Tier-3 self-load + the `mosaic doctor` nudge in `AGENTS.md`; not
|
||
eliminated. Relaunch via `mosaic <harness>` to pick up overlays.
|
||
- **Bare-launch drift is undetected by `mosaic doctor`** (the launcher never ran).
|
||
- **Codex/OpenCode/Pi hook parity** is a tracked v2 gap (§8).
|
||
- **Live-launch cross-harness verification** is v2; the alpha verifies the composer
|
||
by unit test (per-tier anchor + Tier-3 byte-equality), not a live launch.
|
||
|
||
**Deferred to v2 (explicit):** `constitution/` deploy directory; capability JSON
|
||
adapters; 3-way merge; `policy/*.md` composition; per-layer version stamps as a
|
||
migration driver.
|
||
|
||
---
|
||
|
||
## 10. PR checklist
|
||
|
||
- [ ] No operator identity / private paths / secrets (`verify-sanitized.sh` green).
|
||
- [ ] No gate restated outside `CONSTITUTION.md` (§3).
|
||
- [ ] Resident budget green (`check-resident-budget.sh`).
|
||
- [ ] Seed/migration changes landed in **both** installers + shared fixtures (§5).
|
||
- [ ] New harness → compliance-matrix row updated (§8).
|
||
- [ ] `prettier --check` + `pnpm lint` + `pnpm typecheck` + `pnpm test` green.
|