chore: consolidate new foundation and archive v1 (#1495)

This commit is contained in:
2026-09-07 12:32:57 -05:00
3511 changed files with 727899 additions and 10 deletions
@@ -0,0 +1,38 @@
---
kind: spec
status: active
---
# CI Queue Guard Purpose Semantics
- **Issue:** #1146
- **Target branch:** `next`
## Problem
`ci-queue-wait.sh` treats any result other than terminal success as asserted non-readiness. That is correct for merge readiness, but incorrect for the pre-push queue guard: a terminal failure or an empty status set means no pipeline is queued or running, so the queue is clear.
## Design
Make final-state handling purpose-sensitive while preserving the existing provider and payload safeguards:
- `--purpose push`
- wait while state is `pending`;
- return success for `terminal-success`, `terminal-failure`, and `no-status`;
- continue rejecting `malformed`, `unknown`, and unrecognized states.
- `--purpose merge`
- return success only for `terminal-success`;
- continue rejecting `terminal-failure`, `no-status`, malformed, unknown, and unrecognized states.
- `--require-status` remains authoritative: `no-status` fails for either purpose when it is supplied.
Diagnostics will explicitly distinguish a queue-clear push result from successful CI so callers cannot mistake an old failure for a green pipeline.
## Testing
Extend the process-level tri-state regression harness with separate push and merge assertions:
1. Push passes for terminal success, terminal failure, and no status.
2. Push still fails for pending, malformed, and unknown states.
3. `--require-status` makes push/no-status fail.
4. Merge behavior remains fail-closed except for terminal success.
5. Existing provider-unavailable audit behavior remains unchanged.
@@ -0,0 +1,213 @@
---
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.
**Goal:** Make the pre-push CI queue guard pass when no pipeline is queued or running while preserving fail-closed merge readiness.
**Architecture:** Keep provider lookup and tri-state classification unchanged. Make only the final state dispatch purpose-sensitive: push treats valid non-pending states as queue-clear, while merge continues to require terminal success. Preserve `--require-status`, malformed-payload rejection, unknown-state rejection, and audited provider-unavailable behavior.
**Tech Stack:** Bash, process-level shell regression harnesses, Gitea/GitHub status APIs.
---
### Task 1: Freeze Purpose-Specific State Semantics
**Files:**
- Modify: `packages/mosaic/framework/tools/git/test-ci-queue-wait-tristate.sh`
- Test: `packages/mosaic/framework/tools/git/test-ci-queue-wait-tristate.sh`
**Step 1: Add failing push assertions**
Change push expectations so `terminal-failure` and `no-status` require exit 0 plus an explicit `queue-clear` diagnostic. Add a `--require-status` assertion that keeps push/no-status non-zero.
**Step 2: Add failing merge assertions**
Invoke the same harness with `MOSAIC_TEST_PURPOSE=merge` and assert terminal failure and no status remain non-zero while terminal success remains zero.
**Step 3: Add unknown-state coverage**
Add a stub payload with a syntactically valid but unsupported status value and assert both purposes reject it.
**Step 4: Run the focused test and verify RED**
Run:
```bash
bash packages/mosaic/framework/tools/git/test-ci-queue-wait-tristate.sh
```
Expected: failures showing push terminal-failure and no-status returned exit 3 instead of exit 0 or lacked `queue-clear` diagnostics.
**Step 5: Commit the failing tests**
```bash
git add packages/mosaic/framework/tools/git/test-ci-queue-wait-tristate.sh
git commit -m "test(ci): define purpose-aware queue readiness"
```
### Task 2: Implement Purpose-Sensitive Final-State Dispatch
**Files:**
- Modify: `packages/mosaic/framework/tools/git/ci-queue-wait.sh:458-481`
- Test: `packages/mosaic/framework/tools/git/test-ci-queue-wait-tristate.sh`
- Test: `packages/mosaic/framework/tools/git/test-ci-queue-wait-github-checks.sh`
**Step 1: Implement push queue-clear behavior**
For `no-status`, retain the existing `--require-status` failure. Otherwise, return success for push with an explicit diagnostic such as:
```text
[ci-queue-wait] queue-clear state=no-status purpose=push branch=<branch>; no queued or running CI.
```
For `terminal-failure`, return success only for push with the same queue-clear wording. Merge must continue returning asserted non-readiness.
**Step 2: Preserve malformed and unknown rejection**
Keep `malformed`, `unknown`, and unrecognized states non-zero for both purposes.
**Step 3: Run focused tests and verify GREEN**
Run:
```bash
bash packages/mosaic/framework/tools/git/test-ci-queue-wait-tristate.sh
bash packages/mosaic/framework/tools/git/test-ci-queue-wait-github-checks.sh
```
Expected: both scripts exit 0 and report their regression suites passed.
**Step 4: Commit implementation**
```bash
git add packages/mosaic/framework/tools/git/ci-queue-wait.sh
git commit -m "fix(ci): separate push queue clearance from merge readiness"
```
### Task 3: Verify, Review, and Document Evidence
**Files:**
- Modify: `docs/scratchpads/1146-ci-queue-purpose.md`
**Step 1: Run shell syntax and focused regressions**
```bash
bash -n packages/mosaic/framework/tools/git/ci-queue-wait.sh
bash -n packages/mosaic/framework/tools/git/test-ci-queue-wait-tristate.sh
bash packages/mosaic/framework/tools/git/test-ci-queue-wait-tristate.sh
bash packages/mosaic/framework/tools/git/test-ci-queue-wait-github-checks.sh
```
**Step 2: Run repository quality gates**
```bash
pnpm preflight
pnpm typecheck
pnpm lint
pnpm test
pnpm format:check
```
Expected: every command exits 0.
**Step 3: Obtain independent review**
Request review of the exact branch head. Remediate all blocking findings and rerun focused and baseline gates.
**Step 4: Record evidence and commit**
Update the scratchpad with test output, review result, and residual risk, then commit it:
```bash
git add docs/scratchpads/1146-ci-queue-purpose.md
git commit -m "docs(ci): record queue guard verification"
```
### Task 4: Keep the Merge Wrapper Aligned with the `next` Lane
**Files:**
- Modify: `packages/mosaic/framework/tools/git/pr-merge.sh:97-101`
- Test: `packages/mosaic/framework/tools/git/test-pr-merge-head-pin.sh`
**Step 1: Write the failing regression**
Run the exact-head merge regression with its Gitea fixture targeting `next` and confirm the current wrapper rejects it because it only permits `main`.
**Step 2: Allow only documented integration targets**
Permit `main` and `next`; reject every other target. Do not alter exact-head pinning, queue-guard invocation, provider selection, or merge method enforcement.
**Step 3: Run focused merge regressions**
```bash
bash packages/mosaic/framework/tools/git/test-pr-merge-head-pin.sh
bash packages/mosaic/framework/tools/git/test-pr-merge-queue-branch.sh
bash packages/mosaic/framework/tools/git/test-pr-merge-gitea-empty-uid.sh
```
Expected: all pass, including a Gitea merge fixture targeting `next`.
**Step 4: Commit**
```bash
git add packages/mosaic/framework/tools/git/pr-merge.sh packages/mosaic/framework/tools/git/test-pr-merge-head-pin.sh
git commit -m "fix(ci): allow reviewed merges into next"
```
### Task 5: Activate and Deliver Through `next`
**Files:**
- Installed output: `~/.config/mosaic/tools/git/ci-queue-wait.sh`
**Step 1: Activate through the canonical installer**
From the reviewed worktree, run the framework installer in sync-only keep mode so operator files remain protected:
```bash
MOSAIC_SYNC_ONLY=1 MOSAIC_INSTALL_MODE=keep MOSAIC_SKIP_SKILLS_SYNC=1 \
bash packages/mosaic/framework/install.sh
```
**Step 2: Verify installed/source parity**
```bash
cmp -s \
packages/mosaic/framework/tools/git/ci-queue-wait.sh \
~/.config/mosaic/tools/git/ci-queue-wait.sh
```
Expected: exit 0.
**Step 3: Run mandatory pre-push queue guard**
```bash
~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push -B fix/1146-ci-queue-purpose
```
Expected: branch-absent or queue-clear success.
**Step 4: Push and open a PR against `next`**
```bash
git push -u origin fix/1146-ci-queue-purpose
~/.config/mosaic/tools/git/pr-create.sh \
-t "fix(ci): make queue guard purpose-sensitive" \
-b "Closes #1146" \
-B next \
-H fix/1146-ci-queue-purpose \
-i 1146
```
**Step 5: Complete reviewed integration**
Wait for exact-head terminal-green CI, obtain the required review, merge via the Mosaic wrapper, verify merged CI, and close #1146. Do not bypass any gate.
@@ -0,0 +1,60 @@
---
kind: spec
status: active
---
# Documentation Catalog and Truth Audit Plan
**Task:** DOCS-IA-002
**Internal reference:** `TASKS:DOCS-IA-002`
**Goal:** Catalog the existing Mosaic Stack documentation, identify its intended destination in the new structure, and audit validity/truthfulness against repository evidence before moving or rewriting content.
## Scope
- Current root-level documentation and newly established structure files.
- All Markdown and relevant YAML/API artifacts under `docs/_old_structure/`.
- Repository references from source, tests, scripts, guides, and root README files.
- Static truth checks for paths, commands, package names, environment variables, API artifacts, and explicit document status.
## Non-goals
- Do not move, delete, or rewrite documentation.
- Do not decide product requirements that belong in `docs/PRD.md`.
- Do not mark a claim true solely because it appears in a document.
- Do not modify active `docs/TASKS.md` because it has a single-writer orchestrator policy.
## Parallel discovery lanes
1. **File catalog:** path, title, type, size, line count, current/archive location, last repository change.
2. **Navigation audit:** Markdown and Obsidian links, target resolution, broken-link clusters, source references.
3. **Code-surface audit:** package names, scripts, entry points, referenced docs, paths used by tests and source.
4. **Truth triage:** compare current claims against executable code/config/tests and label evidence strength.
Parallel lanes produce findings only. The coordinator reconciles them into one report so truth labels remain consistent.
## Evidence statuses
- `verified`: directly supported by current source/config/tests or a reproducible command.
- `partially-verified`: some claims are supported, but the page contains unverified or time-sensitive claims.
- `contradicted`: current repository evidence conflicts with a material claim.
- `stale`: formerly meaningful but no longer aligned with current paths, APIs, or state.
- `historical`: intentionally retained record of past state; not a current instruction.
- `draft`: normative proposal or requirement, not a statement of shipped behavior.
- `unverified`: not yet checked or insufficient evidence exists.
- `incomplete`: empty or structurally insufficient for its stated role.
## Deliverables
- `docs/reports/documentation/2026-08-10-docs-catalog-audit.md` — human-readable catalog, findings, evidence, and migration recommendations.
- `docs/scratchpads/DOCS-IA-002-catalog-audit.md` — task progress and command evidence.
A machine-readable intermediate inventory may remain under `/tmp`; it is not canonical unless explicitly copied into the report.
## Acceptance criteria
- Every current documentation file and every archived documentation file is counted and assigned a preliminary disposition.
- Broken internal links and repository references are enumerated with evidence.
- Truth labels distinguish current behavior, normative intent, historical evidence, and unresolved claims.
- High-risk contradictions and source/test dependencies are called out before any migration.
- The report recommends migration order and identifies pages requiring human/product-owner validation.
- No existing documentation or unrelated working-tree state is modified.
@@ -0,0 +1,187 @@
---
kind: spec
status: active
---
# Documentation Information Architecture Design
**Status:** Approved
**Date:** 2026-08-10
**Scope:** Establish the canonical structure and authoring rules for `docs/` before migrating or rewriting existing documentation.
## Goal
Create a clean, human-readable, Obsidian-compatible documentation system for Mosaic Stack. The system must make the relationships between requirements, architecture, guides, API contracts, operational procedures, evidence, and work tracking visible without duplicating canonical content.
## Decision
Use a single root-level documentation atlas in `docs/README.md`, organized around audience-specific guide books and dedicated artifact directories. Retire `docs/mosaic-stack/` as a content boundary; it adds no useful ownership distinction once the documentation system has explicit root guides and cross-links.
The target structure is:
```text
docs/
├── README.md
├── PRD.md
├── TASKS.md
├── SITEMAP.md
├── USER-GUIDE/
│ ├── README.md
│ ├── getting-started/
│ ├── concepts/
│ ├── workflows/
│ └── troubleshooting/
├── ADMIN-GUIDE/
│ ├── README.md
│ ├── installation/
│ ├── configuration/
│ ├── deployment/
│ ├── operations/
│ ├── security/
│ └── recovery/
├── DEVELOPER-GUIDE/
│ ├── README.md
│ ├── architecture/
│ │ ├── README.md
│ │ ├── system-overview.md
│ │ ├── component-map.md
│ │ ├── data-flow.md
│ │ ├── security-model.md
│ │ ├── decisions/
│ │ └── rfcs/
│ ├── packages/
│ ├── local-development/
│ ├── testing/
│ ├── contributing/
│ └── integrations/
├── API/
│ ├── README.md
│ ├── OPENAPI.yaml
│ └── ENDPOINTS.md
├── assets/
├── reports/
│ ├── code-review/
│ ├── documentation/
│ ├── qa/
│ ├── security/
│ └── deferred/
├── tasks/
├── plans/
├── scratchpads/
├── releases/
├── archive/
└── _old_structure/ # temporary migration quarantine; read-only
```
`docs/plans/` is a workflow directory for approved design and implementation plans. It is not a substitute for the canonical requirements document, active task ledger, or guide books.
## Information architecture
### Root control documents
- `docs/README.md` is the documentation contract, placement guide, and top-level entry point.
- `docs/PRD.md` is the canonical product and requirements source. Requirements must not be silently redefined in guides or reports.
- `docs/TASKS.md` is the active orchestrator rollup. Its single-writer policy remains authoritative.
- `docs/SITEMAP.md` is the complete human navigation index. It must be updated when canonical pages are added, moved, renamed, or retired.
### Audience books
- `USER-GUIDE/` contains end-user workflows, user-visible behavior, concepts needed to operate the product, and user troubleshooting.
- `ADMIN-GUIDE/` contains installation, configuration, deployment, operations, security controls, recovery, and incident procedures.
- `DEVELOPER-GUIDE/` contains architecture, package/component documentation, local development, testing, contribution rules, and integration authoring.
- `API/` contains the machine-readable OpenAPI contract and its human-readable endpoint index.
Audience books are task-oriented. They link to canonical architecture, requirements, API, and operational pages rather than copying those pages.
### Artifact directories
- `assets/` contains diagrams and documentation media referenced by canonical pages.
- `reports/` contains evidence and findings. Reports are informative and do not override the PRD or normative contracts.
- `tasks/` contains archived task snapshots and orchestrator learnings. Active orchestration remains in root `TASKS.md`.
- `plans/` contains approved design and implementation plans.
- `scratchpads/` contains active, task-specific working notes and verification evidence. Scratchpads are not product documentation.
- `releases/` contains release notes and release-specific migration or compatibility notes.
- `archive/` contains superseded but intentionally retained documentation. Archived pages must state their replacement or reason for retention.
- `_old_structure/` is a temporary migration quarantine. It is read-only, is not indexed as current documentation, and is not an authoring destination.
## Placement rules
| Content | Required location | Do not place it in |
| ----------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------- |
| Product requirements and acceptance criteria | `docs/PRD.md` or an explicitly scoped PRD under a guide/workstream | A scratchpad, report, or README-only note |
| Active task status | `docs/TASKS.md` | A guide page or personal scratchpad |
| User workflow | `docs/USER-GUIDE/<chapter>/` | The docs root |
| Installation, deployment, or recovery procedure | `docs/ADMIN-GUIDE/<chapter>/` | `README.md` or a report |
| Architecture, component, package, ADR, or RFC content | `docs/DEVELOPER-GUIDE/architecture/` or its relevant developer chapter | `docs/mosaic-stack/` or the docs root |
| API contract | `docs/API/OPENAPI.yaml` and `docs/API/ENDPOINTS.md` | A guide-only description |
| Documentation navigation | `docs/SITEMAP.md` | A duplicated ad-hoc index |
| Design or implementation plan | `docs/plans/` | `docs/scratchpads/` |
| Active task working notes | `docs/scratchpads/<task-id>-<slug>.md` | The docs root or a canonical guide |
| Review, QA, audit, security, or deferral evidence | `docs/reports/<category>/` | A canonical guide page |
| Archived task snapshot | `docs/tasks/` | Root `TASKS.md` unless it is active |
| Release notes | `docs/releases/` | The docs root |
| Diagram or image | `docs/assets/` or an owning chapter asset directory | An external personal path |
| Superseded documentation | `docs/archive/` | `_old_structure/` after migration completes |
When a page appears to fit multiple locations, classify it by its primary reader and purpose, then link it from the other relevant indexes. Do not create copies to satisfy multiple audiences.
## Page conventions
Every canonical Markdown page should:
1. Cover one concern or workflow.
2. Use a descriptive, lowercase kebab-case filename, except for established root control files and required API filenames.
3. Begin with a clear title and a short purpose statement.
4. Declare status and audience when the page is more than a simple index.
5. Identify prerequisites, source-of-truth dependencies, and related pages.
6. State whether examples and commands are current, illustrative, or held/non-operative.
7. Include an owner or maintenance responsibility when the content is operationally sensitive.
Recommended front matter for canonical pages:
```yaml
---
title: Human-readable page title
type: guide
audience: developer
status: current
---
```
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`.
## Obsidian and link conventions
- Use Obsidian wikilinks for relationship-oriented internal references, for example `[[DEVELOPER-GUIDE/architecture/component-map|Component map]]`.
- Use normal relative Markdown links in `SITEMAP.md` and book `README.md` indexes so links render on Git hosting platforms. Obsidian can resolve these links too.
- Use `Related`, `Depends on`, and `Referenced by` sections when a page participates in a meaningful documentation relationship.
- Link to stable page paths, not transient line numbers or branch URLs.
- Omit `.md` in wikilinks. Include an alias when the file path is not a readable label.
- Use standard Markdown links for external URLs, source files, commands, and API paths.
- Do not rely on a link to `_old_structure/` as a current navigation path. Historical references must explain why the archived page is retained and point to its replacement.
## Migration rules
This design phase does not move or rewrite content. During later migration:
1. Inventory current pages and classify each by audience, purpose, status, and source-of-truth role.
2. Move canonical content into the target tree without changing meaning unless the migration task explicitly includes a rewrite.
3. Update all repository links, source comments, tests, and `SITEMAP.md` in the same logical change.
4. Preserve historical evidence in `reports/`, `tasks/`, `releases/`, or `archive/` rather than mixing it into current guides.
5. Treat `_old_structure/` as read-only during migration. It may be removed only after all required links and source references are resolved.
6. Do not add new content to `docs/mosaic-stack/`; the empty directory is retired by this design.
7. For documents referenced by executable tests or source code, update those references deliberately and verify them before deleting the old path.
## Acceptance criteria
- `docs/README.md` defines the complete target tree and placement rules.
- The target tree has no `docs/mosaic-stack/` content boundary.
- Agents can determine where to put product docs, plans, task notes, reports, scratchpads, releases, and archives without guessing.
- The rules support both Obsidian graph navigation and Git-hosted Markdown navigation.
- The design distinguishes normative sources from evidence and working notes.
- Migration can proceed incrementally without treating `_old_structure/` as current documentation.
@@ -0,0 +1,140 @@
---
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.
**Goal:** Replace the starter `docs/README.md` with the normative documentation structure, placement rules, source-of-truth policy, and Obsidian-compatible navigation conventions approved for Mosaic Stack.
**Architecture:** Keep `docs/README.md` as the root documentation atlas and authoring contract. Use audience books for current user, administrator, and developer content; keep API contracts and operational artifacts in dedicated directories; retain `_old_structure/` as a read-only migration quarantine. Do not move or rewrite existing documentation in this slice.
**Tech Stack:** Markdown, YAML front matter examples, Obsidian wikilinks, relative Markdown links, Prettier.
---
### Task 1: Write the documentation structure contract
**Files:**
- Modify: `docs/README.md`
- Reference: `docs/plans/2026-08-10-docs-information-architecture-design.md`
**Step 1: Confirm the approved design and current transition constraints**
Verify that the README preserves these decisions:
- `docs/mosaic-stack/` is not a target content directory.
- `docs/README.md` is the documentation atlas and placement contract.
- Existing files are not moved or rewritten yet.
- `_old_structure/` is read-only migration quarantine.
- Root control files, audience books, API, reports, tasks, plans, scratchpads, releases, archive, and assets have distinct responsibilities.
**Step 2: Replace the starter README**
Write `docs/README.md` with these sections:
1. Purpose and scope.
2. Reader entry points.
3. Complete target directory tree, including the optional `.obsidian/` vault configuration boundary and the workflow-only `plans/` directory.
4. Root control document responsibilities.
5. Guide book responsibilities and chapter rules.
6. Artifact directory responsibilities.
7. Placement matrix for agents.
8. Source-of-truth and precedence rules.
9. Page naming and front matter conventions.
10. Obsidian wikilink and Git-hosted Markdown link conventions.
11. Authoring workflow for new or changed documentation.
12. Migration rules for `_old_structure/`, legacy root files, and repository references.
13. Current transitional exceptions and explicit non-goals.
Use future target paths as a blueprint, but clearly label directories that are not populated yet so readers do not mistake the blueprint for completed migration.
**Step 3: Preserve the existing Obsidian configuration boundary**
Document `.obsidian/` as optional vault metadata only. Do not place Markdown content, scratchpads, reports, or source-of-truth files under it, and do not modify its existing files in this task.
**Step 4: Keep the README portable**
Use ordinary relative Markdown links for indexes and Git-hosted navigation. Use Obsidian wikilinks for graph-oriented relationships such as `Related`, `Depends on`, and `Referenced by`. Do not make a current navigation path depend solely on a Git-host-incompatible wikilink.
**Step 5: Review the resulting document**
Check that an agent can answer all of these without inspecting another file:
- Where does a user guide go?
- Where does an admin runbook go?
- Where does architecture or an RFC go?
- Where does an API contract go?
- Where does an active scratchpad go?
- Where does a review or QA report go?
- Where does an approved design or implementation plan go?
- Which files are normative, working notes, evidence, or historical?
- What may be added directly under `docs/`?
**Step 6: Commit only the README**
Because `docs/GETTING_STARTED.md` is an unrelated pre-staged deletion, stage and commit only `docs/README.md`:
```bash
git add docs/README.md
git commit --only docs/README.md -m "docs: codify documentation structure"
```
Expected: the commit contains only the README change; the existing staged deletion and orchestrator state remain outside the commit.
### Task 2: Verify the README-only change
**Files:**
- Verify: `docs/README.md`
**Step 1: Run Markdown formatting validation**
Run:
```bash
pnpm exec prettier --check docs/README.md
```
Expected: Prettier reports the file is formatted.
**Step 2: Run whitespace validation**
Run:
```bash
git diff --check HEAD^ -- docs/README.md
```
Expected: no whitespace errors.
**Step 3: Validate required structural anchors**
Run a focused search or script confirming the README names:
- `PRD.md`, `TASKS.md`, and `SITEMAP.md`;
- `USER-GUIDE/`, `ADMIN-GUIDE/`, `DEVELOPER-GUIDE/`, and `API/`;
- `reports/`, `tasks/`, `plans/`, `scratchpads/`, `releases/`, `archive/`, and `assets/`;
- `_old_structure/` as read-only quarantine;
- `docs/mosaic-stack/` as retired/non-authoring;
- Obsidian wikilinks and Git-compatible Markdown links.
Expected: all anchors are present and no section instructs agents to create content under `docs/mosaic-stack/`.
**Step 4: Confirm scope isolation**
Run:
```bash
git status --short
git show --stat --oneline HEAD
```
Expected: the new commit contains only `docs/README.md`; pre-existing `.mosaic/orchestrator/*`, `docs/GETTING_STARTED.md`, and `docs/.obsidian/` states remain untouched.
**Step 5: Record verification evidence**
Update the task scratchpad at `docs/scratchpads/DOCS-IA-001.md` with commands, results, known transitional gaps, and the next migration slice. Do not modify active `docs/TASKS.md`; its single-writer policy belongs to the orchestrator.
@@ -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.
@@ -0,0 +1,315 @@
---
kind: spec
status: active
audience: developer
---
# Agent Enrollment Command Family — v1 Design (M4-4-0)
Status: design note (implementation-facing; amends no contract).
Authority chain: tool-gateway-mapping.md §3.1 rank-4 row + §4 envelope
(ruled 2026-08-27), onboarding-wizard.md §3.5 (D11 minimal enrollment),
custody-schema.md §5.2 at revision 13 (agent-grantee FK bound to the
live `agents` table — a binding introduced at rev 4 and standing
verbatim), PRD §9 D11. Where this note and a ratified contract disagree,
the contract wins.
## 1. What the contracts bind (and what they leave open)
There is no standalone enrollment contract. The rank-4 family is defined
by composition:
1. **Contract 5 §3.1 rank 4:** "Enroll one agent: harness, credential
reference/API-key intake (values never echoed), name/persona,
assignment scope (contract 3 §3.5)."
2. **Contract 5 §4 — all five sub-clauses:** §4.1 typed request/result
DTOs validated at the Gateway boundary (expected-version only where
an owning contract defines one); §4.2 closed per-family error enum
(validation, authentication, authorization, not-found, conflict,
precondition, internal) with HTTP mappings; §4.3 audit linkage — the
envelope contributes correlation: every request accepts/generates a
correlation id, carried into the audit events **and returned in the
result**, with no second audit stream; §4.4 fail-closed — an
operation that cannot evaluate its authorization or reach its owning
tool refuses, never degrading to a fallback read or direct data
access; §4.5 CLI parity — the family MUST be invocable through the
official CLI against the same Gateway commands with the same
request/result/error contracts (a Gateway command without CLI
exposure is a tracked conformance gap).
**Idempotency keys are NOT contract 5 §4.3:** the idempotency-key
envelope is contract 3 §4.3, ratified as a drafting addition to
contract 5 §4's command envelope via contract 3 §7 item 4. Its fence
and replay rules bind as written there; §3.1 rule 5 below designs to
them.
3. **Contract 3 §3.5:** the wizard's enrollment step is minimal (one
harness, API-key login, agent name and persona — D11), uses ONLY this
family, and is skippable. Wizard witness §6.10: a run that skips the
step produces zero enrollment-family mutations.
4. **Custody-schema §5.2 (rev 13; binding introduced at rev 4):**
contract 7's agent-grantee FK references the live `agents` table
(`agents.id`, uuid); an enrollment surface with its own table would
force a contract-7 amendment.
**Assignment scope (open point, pinned here):** the rank-4 row cites
contract 3 §3.5, which defines no assignment semantics; the PRD's full
enrollment vision (Part I, Standalone flow) includes "account
assignment", but the D11 v1 slice is exactly "one harness, API key,
name/persona". v1 therefore scopes assignment to the two bindings the
minimal slice already implies — the enrolling user becomes the agent's
owner (`agents.owner_id`), and the credential reference names which of
that user's stored provider credentials the agent uses. Richer
assignment (multi-account, comms auto-enroll, workspace placement) is
deferred with the rest of the PRD's full flow (D11); when a contract
defines it, this family extends by ordinary amendment of the design.
The deferral rests on contract 3 §3.5's explicit delegation of
enrollment specifics to this family — not on reading the D11 list as
exhaustive (it is not: the §3.1 `model`/`provider` fields are required
by the live table's NOT NULL columns, though D11 does not name them).
## 2. Current state (measured 2026-08-29 at `origin/next` = `94d626df`)
- `agents` table (packages/db `schema.ts`): id uuid PK, name, provider,
model, status enum, project_id (legacy `projects`, ON DELETE SET
NULL), owner_id → users, system_prompt, allowed_tools, skills,
is_system, config jsonb, timestamps. No harness column (provider and
model describe the LLM backend, not the harness), no audit coupling.
- Sole write path: `packages/brain/src/agents.ts` repository (the only
module issuing `insert(agents)`), with three write consumers: the
legacy `/api/agents` CRUD controller
(`apps/gateway/src/agent/agent-configs.controller.ts`), the `/agent
new` chat command (`apps/gateway/src/commands/command-executor.service.ts`
`brain.agents.create`), and workspace bootstrap
(`apps/gateway/src/workspace/project-bootstrap.service.ts`). All
three keep serving existing consumers; none is touched by M4-4.
- Sealed credential store exists: `ProviderCredentialsService`
(apps/gateway/src/agent/) — one row per (userId, provider), values
sealed at rest, decrypt server-side only, summaries never carry
values.
- Harness registry exists (`apps/gateway/src/harness/`), the validation
source for the harness field.
- Implementation pattern: the merged hierarchy module (M4-1) —
transaction-scoped command context, in-tx authorization, discriminated
result unions, same-transaction semantic audit event + transactional
outbox, no-oracle not_found folding.
**F1 — contract-5 mapping note (disposition, not an amendment):**
`/api/agents` appears nowhere in contract 5 — neither as a P0 row nor in
the §3.2 legacy non-substitutes list (the ruled §3.2 freeze names
specific endpoints, and `/api/agents` is not among them). The operative
constraints are §3.3's amendment-only rule for new mapping rows and §5's
closure rule: this design adds no new consumer to `/api/agents` and
builds the rank-4 family as the P1 path for enrollment. Adding the
missing P0 row is a contract amendment for a future S2 pass; nothing in
M4-4 depends on it.
## 3. Command family surface (v1)
One command, one query. Module: `apps/gateway/src/enrollment/`
(`enrollment.module.ts`), mirroring the hierarchy module's shape.
### 3.1 `agent.enroll` (mutation)
Request DTO (shared types package, class-validator at the boundary):
| Field | Type | Rule |
| ---------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `harness` | string | syntactically invalid (empty/malformed) → `validation_failed`; well-formed but not in the harness registry → `precondition_failed` |
| `correlationId` | string (uuid) | optional; generated when absent (contract 5 §4.3); carried into audit events and returned in the result |
| `replayMode` | 'actor-bound' | optional, default `actor-bound`. `shared` is seed-only (contract 3 §4.3 binds it to the §3.4 canonical seed key set and "no other operation can carry a shared declaration"; §7 item 4 closes it); a `shared` declaration here is refused `validation_failed`, executes nothing, and records no fence row |
| `name` | string | non-empty, trimmed, ≤ 200 chars |
| `persona` | string \| null | optional; stored as the agent's system prompt |
| `model` | string | non-empty (provider-qualified model id) |
| `provider` | string | non-empty; names the credential's provider |
| `credential` | discriminated union | `{ mode: 'reference' }` — a credential for (actor, provider) MUST already exist; `{ mode: 'intake', type: 'api_key', value: string }` — value is sealed into the credential store in the same flow |
| `idempotencyKey` | string (uuid) | required (contract 3 §4.3, ratified into contract 5 §4 via contract 3 §7 item 4) |
Rules:
1. **Never echoed.** The credential value appears in no result DTO, no
audit event, no outbox payload, and no log line. The result carries
only `{ provider, credentialMode }`.
2. **Intake = the existing sealed store, inside the transaction.**
`intake` writes through the sealed-store path
(`ProviderCredentialsService.store` semantics: seal-at-rest, upsert
per (userId, provider)) **in the same transaction** as the agent
insert — a failure after the credential write rolls everything back,
leaving no orphan credential. Enrollment persists no second copy and
no plaintext.
3. **Reference must resolve.** `reference` with no stored credential for
(actor, provider) refuses with `precondition_failed` (nothing is
created).
4. **Ownership.** `owner_id` = the authenticated actor. v1 authorization
is AuthGuard-authenticated user; no hierarchy grant is required
because v1 enrollment binds no hierarchy node (§1 assignment-scope
pin). `is_system` is never settable through this command.
5. **Idempotency fence (contract 3 §4.3, in full).** The command layer
records, in a uniqueness-constrained fence table in the same
transaction as the mutation and its audit event: the key, the
operation identifier (`agent.enroll`), the acting principal, the
authorization scope, a digest of the canonicalized request payload
(the digest input EXCLUDES the credential value — it covers
provider + credentialMode, never plaintext), the declared replay
mode (always `actor-bound` for this family — the `shared` refusal
in the table above means no shared fence row can exist here; the
column is kept for envelope-shape fidelity and mode-mismatch
collision checks), and a reference to the committed outcome (the
agent id). The recorded **authorization scope** for this family is
pinned to the acting principal's platform-user scope (v1
authorization is grant-free per rule 4, so the scope is the
authenticated-user identity domain — recorded so the §4.3
scope-equality check has a defined value). Fence uniqueness is the
pair (operation identifier, key). **Replay:** a submission whose
(operation, key) is recorded is first authorized exactly as a fresh
submission; then replay-mode, scope, and digest equality are
checked (a mismatch on any — including scope — is a collision);
then **target-result authorization** — the submitter must hold, at
replay time, read authority on the referenced agent row under
§3.2's rule (owner or admin) — plus recorded-actor equality
(`actor-bound`). A passing replay executes nothing, returns the
recorded outcome, and appends a replay access event (non-mutation
audit class: accessing principal, current correlation id,
fence-row reference). Any equality or authorization failure refuses
with the single bounded `conflict` shape — constant, identifying no
record — preserving the no-existence-oracle rule. **Concurrency
(contract 3 §4.3's rule, ratified via §7 item 4):** two submissions
with the same (operation, key) serialize on the fence's unique
constraint — exactly one executes; the loser waits for the winner's
transaction, and is then handled as a replay if it committed
(through the full replay path above) or executes afresh if it
aborted. A unique-violation race never surfaces as an unhandled
internal fault.
6. **Audit + outbox, same transaction.** Insert into `agents` +
sealed credential write (intake mode) + fence row + semantic audit
event (`agent.enrolled`: actor, agent id, harness, provider, name,
credentialMode — no credential material) + outbox row commit
atomically, hierarchy-pattern style. Audit rows reference the agent
by **snapshot id, not FK** — mirroring the hierarchy audit tables'
deliberate FK-free linkage so audit history survives agent deletion
through the legacy CRUD DELETE path.
Result union: `enrolled { agent, correlationId }` | refusal from the
§3.3 enum (refusals also carry the correlation id, per contract 5
§4.3's end-to-end traceability). `agent` in the result is the persisted
row minus nothing sensitive (the table stores no credential material).
### 3.2 `agent.enrollment.get` (query)
By agent id; actor must be the owner (or admin). Unauthorized and
missing fold to the same `not_found` wire shape (contract 2
no-existence-oracle rule, applied family-wide for uniformity).
The query carries the same non-state envelope as the mutation
(contract 5 §4.3; contract 3's envelope reconciliation confirms closed
query responses carry it): typed request DTO with an optional
`correlationId` (generated when absent) and a typed result —
`found { agent, correlationId }` | `not_found` (the folded shape,
also carrying the correlation id). Queries take no idempotency key
(the fence binds mutations).
### 3.3 Error enum (closed, §4.2)
`validation_failed` 400 · `authentication_failed` 401 ·
`authorization_refused` 403 (owner-only paths; folded to `not_found`
where §3.2 applies) · `not_found` 404 · `conflict` 409 (the single
bounded idempotency refusal shape of §3.1 rule 5) · `precondition_failed`
422 (unresolvable credential reference; well-formed harness not in the
registry — syntactic invalidity is `validation_failed` per the §3.1
table) · `internal_fault` 500 (also the §4.4 fail-closed class when the
owning tool is unreachable; unauthorized-fallback behavior is
prohibited).
## 4. Schema delta (migration 0021, additive-only)
Extend `agents` — no new agent table, preserving custody-schema §5.2's
FK binding without amendment:
- `harness` text NULL — registered harness name; NULL for pre-existing
rows (legacy rows predate the concept).
- `enrolled_at` timestamptz NULL — set by `agent.enroll`; NULL marks a
legacy (non-enrolled) row. No backfill: enrollment is a fact this
command creates, not one to invent for existing rows.
New tables, mirroring the hierarchy audit/outbox pair (pattern reuse,
separate store): `agent_audit_events` (append-only: id, event_type,
actor id, agent id — snapshot value, no FK, per §3.1 rule 6 —
correlation id, causation id, payload jsonb, created_at; per-agent
ordering index), `agent_outbox` (hierarchy-outbox shape), and
`agent_idempotency_fence` (contract 3 §4.3 shape: operation identifier,
key, acting principal, authorization scope, canonicalized-payload
digest, replay mode, committed-outcome reference (agent id), created_at;
UNIQUE (operation identifier, key)). Persona reuses the existing
`system_prompt` column; no version column (no ratified expected-version
rule names `agents` — §4.1 binds only where the owning contract defines
one).
Witnesses (real PostgreSQL, lane standard): append-only enforcement,
same-tx atomicity (agent row + credential write + fence row + audit +
outbox all-or-nothing under injected failure at multiple points,
including after the credential write), fence uniqueness on
(operation, key).
Sequencing: additive DDL via the same migration path as 00180020
(hierarchy). The docs/native-kanban-sot/SHARED-CONTRACT.md §5.3 DDL
gate binds the kanban lane's audit/proposal DDL, not this lane; if a
pending operator ruling on migration sequencing changes mechanics
lane-wide, re-check before generating 0021.
## 5. Witnesses the implementation slice must ship
1. Never-echo: enroll via `intake`, assert the value string is absent
from the HTTP result, the audit row, the outbox payload, and captured
logs.
2. Sealed-store single-copy: after intake, the credential exists only in
`provider_credentials` (sealed), and `agents` has no credential
column at all.
3. Reference-resolution refusal (`precondition_failed`, no row created).
4. Harness refusals, both codes: syntactically invalid →
`validation_failed`; well-formed registry miss →
`precondition_failed` (against the live registry).
5. Idempotency (contract 3 §4.3 set): actor-bound replay returns the
recorded outcome and executes nothing (no new agent/audit/outbox
mutation rows; a replay access event is appended); payload-digest
mismatch, replay-mode mismatch, scope mismatch, and different-actor
actor-bound replay each refuse with the single bounded `conflict`
shape; a replay is re-authorized fresh (a submitter whose
authorization was revoked since the original is refused, not
replayed); a `shared` declaration on `agent.enroll` is refused
`validation_failed` with nothing executed and no fence row
recorded (seed-only rule); two concurrent same-(operation, key)
submissions produce exactly one mutation, the loser resolving
through the replay path (no unhandled unique-violation fault).
6. Same-tx atomicity fault injection (agent / credential write / fence
/ audit / outbox), including a failure injected after the intake
credential write commits its statement — everything rolls back, no
orphan credential.
7. Wizard-facing zero-mutation witness (contract 3 §6.10 shape): no
call → zero rows in `agents`/`agent_audit_events`/`agent_outbox`/
`agent_idempotency_fence` attributable to the family.
8. `is_system` injection attempt is rejected by DTO validation.
9. Correlation-id witness (contract 5 §6.3): a correlation id submitted
on `agent.enroll` appears in its audit event(s) and in the result;
the same holds for `agent.enrollment.get`'s result; the §6.3 static
companions (no `any`-typed boundary pass-through; single audit
emitter) apply. §6.3's no-existence-oracle probe: an unauthorized
`agent.enrollment.get` of an existing agent and a get of a
nonexistent id return indistinguishable results.
10. CLI-parity witness (contract 5 §6.4): a CLI smoke invocation of
`agent.enroll` and `agent.enrollment.get` against the Gateway
succeeds with the same typed results the web client receives. The
implementation slice therefore SHIPS CLI exposure for both
operations (contract 5 §4.5 — a Gateway command without CLI
exposure is a tracked conformance gap; this design refuses to open
one).
11. Fail-closed witness (contract 5 §6.5): with the owning tool or
grant state unreachable (fault injection), the operation returns
the internal-fault or authorization-refusal class and performs no
fallback read/write.
## 6. Out of scope
Wizard orchestration (M4-6); any UI (D8/D12); un-enroll/update lifecycle
(no contract requires it in v1 — the legacy write surfaces named in §2
keep serving existing consumers); OAuth login, multi-account, comms
auto-enroll, model recommendation (PRD full flow, deferred by D11);
contract amendments (F1 recorded above for a future S2 pass). CLI
exposure is explicitly IN scope (witness 10 — contract 5 §4.5 binds it).
@@ -0,0 +1,99 @@
# Plan — Stack Containerization (tiered deployment)
Status: DRAFT for review. Charter: fleet/lanes/stack-containerization
(brain) NORTH-STAR.md; PRD amendment in the same PR adds D15.
Supersedes nothing; sequences the absorbed M4 remainder per its lane.
## Measured baseline (origin/next @ 143ba0f5, 2026-08-30)
- `docker-compose.yml`: dev infrastructure only — postgres (pgvector),
valkey, otel-collector, jaeger. No application services.
- `docker-compose.federated.yml`: standalone overlay for the FEDERATED
storage tier (own postgres/valkey; port-conflicts the base stack by
design). Not an app deployment.
- `docker/gateway.Dockerfile`, `docker/appservice.Dockerfile`:
multi-stage production builds (node:22-alpine) EXIST; the gateway image
includes the web SPA bundle (#1444).
- CI (`publish.yml`) builds and publishes these images (next-channel
prereleases + main stable), and runs `verify:release` fail-closed.
- Gap: no stack-level composition wires gateway+appservice+data plane
into one deployable unit; no blessed install/upgrade path; no
in-container agent-runtime story for the dogfood loop.
## Target (PRD D15 amendment)
Tiered deployment, additive to the existing architecture:
1. **Standalone tier (v1 bar)**: `docker compose up` on one host brings
postgres, valkey, openbao, gateway, appservice (and the webUI the
gateway serves) to healthy; migrations apply; the webUI hosts agent
chat; an in-stack agent can read this repo and open a PR; CI
validates; the deployment adopts merged images (pull + restart).
2. **Enterprise tier (post-v1)**: Kubernetes manifests (or Helm) for the
same service set, phase-gated on the standalone bar holding.
## Phases
### Phase A — blessed standalone compose
- A1 Compose service definitions for gateway + appservice joining the
existing infra compose (profiles: `dev` keeps today's behavior;
`stack` adds the app tier), with health checks and dependency order.
- A2 Migrations on boot (or an explicit migrate step) with idempotency
and version pinning; init-db.sql folded into pg-init.
- A3 Openbao in the compose set (secret plumbing for the app tier).
- A4 `.env.example` + `mosaic.config.json` defaults documented for the
standalone mode; mode recorded per the mode-conversion contract.
- A5 Smoke: `docker compose --profile stack up` green on a scratch host;
webUI served; agent chat reachable; failures catalogued and fixed.
- Acceptance: the five-point NORTH-STAR bar measured live.
### Phase B — component completion
- Interface assumption (velma verdict A1, P5-RM-005/006): in-stack
dogfood agents inherit SEAT-GRADE identity — credential-slot
isolation, wrapper-first enforcement, no privileged coordination
identity, evidence by references that resolve outside the container
lifetime.
- Decompose JIT from A5's catalogue. Known candidates: agent runtime
bits (brain/tool access paths in-container), repo credentials for the
dogfood agent, watch/comms surfaces inside the deployment.
### Phase C — CI/CD parity
- Publish pipeline is the only image source (already true); add the
deployment-side pull/upgrade path (compose pull + migrate + restart =
next iteration); document the promotion flow next -> registry ->
deployment.
### Phase D — coordinator integration (GATED)
- Gate (velma verdict C2): blocked until the checkpoint-and-lease child
of the guides-proposed control-plane refactor — core + WU-P1-CHECKPOINT
(schema, freshness, incarnation, clean-replacement resume; D57-D60
lineage) — carries an independent target-bound PASS. Wiring restarts
against the core alone re-creates the stale-incarnation failure class
D57-D60 closed. Transitive: inherits the T108 gates (P0 exit + Jason
P1 authorization).
- Scope (velma verdict C1): lifecycle actions (start/stop/restart/
health/recovery) executed by the SHIPPED coord client over the one
typed coordination contract (request id, actor identity, epoch,
revision, lease, correlation; typed stale rejection; worker role
boundary). No second coordination interface gets designed here —
containerization consumes the coordination contract, never defines it.
### Phase E — enterprise tier
- k8s manifests/Helm for the same set; phase-gated on Phase A holding.
### Absorbed M4 remainder
- M4-3 pivot: KBN-101 foundation first (per ruling R6), then expand DDL.
- M4-5: lands inside Phase B/C where natural.
- M4-6 (composes M4-1+M4-4): last, as designed.
## Non-goals (v1)
- No Kubernetes in v1; no multi-host federation; no replacement of the
fleet's brain-based seats (the stack is an additional operator
surface); no on-host image builds for deployment (registry only).
+26
View File
@@ -0,0 +1,26 @@
---
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.
## Documentation migration plans
- [Information architecture design](2026-08-10-docs-information-architecture-design.md) — approved audience books, artifact boundaries, source-of-truth rules, and migration model.
- [Documentation structure README implementation](2026-08-10-docs-structure-readme.md) — completed implementation plan for the documentation contract and atlas.
- [Documentation catalog and truth audit](2026-08-10-docs-catalog-audit.md) — audit method, evidence statuses, deliverables, and acceptance criteria.
## Feature design plans
- [Agent enrollment command design](2026-08-29-agent-enrollment-command-design.md) — v1 rank-4 enrollment command family: contract composition, command surface, schema delta, witnesses (M4-4-0).
After a plan is delivered, update the canonical guide, contract, decision, or index. Do not cite a plan as proof that intended behavior shipped.
## Related
- [[README|Documentation contract]]
- [[SITEMAP|Documentation sitemap]]
- [[scratchpads/README|Documentation scratchpads]]