chore: consolidate new foundation and archive v1 (#1495)
This commit is contained in:
@@ -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 0018–0020
|
||||
(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).
|
||||
@@ -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]]
|
||||
Reference in New Issue
Block a user