Skip to content

docs: enforce audience-specific documentation ownership#944

Closed
kunchenguid wants to merge 1 commit into
mainfrom
fm/fm-documentation-audience-cleanup-r1
Closed

docs: enforce audience-specific documentation ownership#944
kunchenguid wants to merge 1 commit into
mainfrom
fm/fm-documentation-audience-cleanup-r1

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

Intent

The developer wanted a single no-mistakes validation run for the committed documentation-audience cleanup, carried through all synchronous gates, push, pull request creation, and green CI unless a genuinely new decision required escalation. They required quantified, reproducible before-and-after prompt reduction measurements and a concise preservation map tracing removed durable facts to their current authoritative owners. The evidence needed to stay current without adding historical clutter to operator documentation, while preserving the documentation audience policy and one-owner rule. They explicitly required the pipeline to make the fix, prohibited hand edits and --yes, and said not to start a second run while the current one was active.

What Changed

  • Classify maintained prose by audience and add structural enforcement for inventory coverage, ownership pointers, setup routing, and format-aware local links and anchors.
  • Refocus public and operator guides on current behavior, setup, and limits while consolidating backend and supervision evidence into dedicated verification records.
  • Reduce prompt-bearing supervision content, clarify documentation ownership across agent and contributor guidance, and record quantified reductions with a preservation map.

Risk Assessment

✅ Low: The documentation migration is well-bounded, its preservation evidence is coherent, and the final shared link extractor now closes all previously identified Markdown, reStructuredText, anchor, parsed-literal, and raw-HTML paths with focused regression coverage.

Testing

After inspecting the base-to-target diff, I ran the focused documentation policy tests and repository checker, manually audited the preservation map, rendered actual before-and-after harness prompts, reproduced every published size reduction, confirmed unchanged harness prompts remained byte-identical, captured reviewer-visible transcript artifacts, removed snapshot scaffolding, and verified the worktree remained clean. No screenshot was produced because this change has no graphical UI; the actual rendered prompt text and documentation-check transcripts are the end-user surfaces.

Evidence: Documentation audience tests

All focused classification, ownership-pointer, Markdown/reStructuredText link, anchor, and code-example fixtures passed.

ok - documentation inventory classifies every maintained prose surface exactly once
ok - classification, setup routing, and maintained-prose scope fail safely
ok - required documentation owner pointers cannot silently disappear
ok - local links resolve while dates, versions, commands, and incident prose remain semantically reviewed
ok - untracked maintained prose cannot bypass audience classification
ok - no-mistakes uses the supported trusted document.instructions schema
Evidence: Audience and link check

fm-doc-audience-check: ok surfaces=55 local_links=155

fm-doc-audience-check: ok surfaces=55 local_links=155
Evidence: Prompt and guide reductions

Reproduced committed before-and-after prompt and guide measurements.

Always-loaded AGENTS.md (lines words bytes)
before      485    6856   50500
after       485    6851   50441
Rendered OpenCode supervision block (lines words bytes)
before       31     371    2818
after        26     300    2266
Rendered Pi supervision block (lines words bytes)
before       58     840    6560
after        31     413    3259
Runtime backend guides (lines words)
before     2109   35525
after       718    5987
Current supervision guides (lines words)
before      476    5992
after       217    2528
README.md (words)
before     2074
after      1938
Evidence: OpenCode prompt before

Actual base and target OpenCode prompt outputs for reviewer comparison.

================================================================================
SUPERVISION OPERATING INSTRUCTIONS - primary harness: opencode
================================================================================
Current state:
- Lock: held by this session; this session owns normal supervision unless away mode says otherwise.
- Away mode: inactive.
- X mode: inactive; use the default watcher cadence.
- Ordinary wake: the OpenCode TUI plugin already owns watcher continuity; do not arm manually.

Mode: OpenCode TUI plugin background wake.

When this session owns supervision and away mode is not active:
1. Drain first with `bin/fm-wake-drain.sh`.
2. First cycle: let `.opencode/plugins/fm-primary-watch-arm.js` arm supervision after the OpenCode session goes idle.
3. The plugin listens for `session.idle`, spawns `bin/fm-watch-arm.sh --restart` without awaiting it in the idle handler, and owns every later successor launch.
4. After an actionable child close, the plugin rechecks session-lock ownership and verifies one singleton successor before it calls `client.session.promptAsync`; its bounded fallback is defined in `docs/watcher-continuity.md`.
5. Ordinary wake: do not ask the model to re-arm because continuity is plugin-owned.
6. An unexpected child close enters bounded exponential retry, and an exhausted retry or lost session lock is surfaced as a watcher failure instead of disappearing.
7. Failure or missing cycle only: if the plugin reports a watcher failure, drain queued wakes, inspect the failure text, and use `bin/fm-watch-arm.sh` manually only as a short recovery probe.
8. Never use shell `&` for watcher supervision.
   The arm mechanism above is plugin-owned, not a model tool call, but a manual recovery probe that backgrounds, pipes, or bundles the arm is denied automatically by the PreToolUse seatbelt (`.opencode/plugins/fm-primary-pretool-check.js`, `bin/fm-arm-pretool-check.sh`).
9. Do not rely on this plugin in headless `opencode run`; firstmate primary supervision targets persistent OpenCode TUI sessions.

OpenCode's persistent TUI plugin runtime is the wake mechanism.
The plugin applies in the main primary checkout and a secondmate's own home, and stays silent only in child crewmate and scout worktrees.

Continuity verification on 2026-07-17 used OpenCode 1.17.18 in a dedicated tmux socket with an isolated project and `FM_HOME` while retaining the existing managed authentication.
An actionable child close was followed by a ledger-linked successor before prompt handling, the model issued no watcher-arm command, and the turn-end guard did not fire.
Command: `FM_OPENCODE_LIVE_E2E=1 tests/fm-opencode-primary-live-e2e.test.sh`.
Observed output: `ok - OpenCode 1.17.18 live E2E auto-started one successor before prompt handling without a model re-arm`.
Evidence: OpenCode prompt after

Actual target OpenCode prompt output.

================================================================================
SUPERVISION OPERATING INSTRUCTIONS - primary harness: opencode
================================================================================
Current state:
- Lock: held by this session; this session owns normal supervision unless away mode says otherwise.
- Away mode: inactive.
- X mode: inactive; use the default watcher cadence.
- Ordinary wake: the OpenCode TUI plugin already owns watcher continuity; do not arm manually.

Mode: OpenCode TUI plugin background wake.

When this session owns supervision and away mode is not active:
1. Drain first with `bin/fm-wake-drain.sh`.
2. First cycle: let `.opencode/plugins/fm-primary-watch-arm.js` arm supervision after the OpenCode session goes idle.
3. The plugin listens for `session.idle`, spawns `bin/fm-watch-arm.sh --restart` without awaiting it in the idle handler, and owns every later successor launch.
4. After an actionable child close, the plugin rechecks session-lock ownership and verifies one singleton successor before it calls `client.session.promptAsync`; its bounded fallback is defined in `docs/watcher-continuity.md`.
5. Ordinary wake: do not ask the model to re-arm because continuity is plugin-owned.
6. An unexpected child close enters bounded exponential retry, and an exhausted retry or lost session lock is surfaced as a watcher failure instead of disappearing.
7. Failure or missing cycle only: if the plugin reports a watcher failure, drain queued wakes, inspect the failure text, and use `bin/fm-watch-arm.sh` manually only as a short recovery probe.
8. Never use shell `&` for watcher supervision.
   The arm mechanism above is plugin-owned, not a model tool call, but a manual recovery probe that backgrounds, pipes, or bundles the arm is denied automatically by the PreToolUse seatbelt (`.opencode/plugins/fm-primary-pretool-check.js`, `bin/fm-arm-pretool-check.sh`).
9. Do not rely on this plugin in headless `opencode run`; firstmate primary supervision targets persistent OpenCode TUI sessions.

OpenCode's persistent TUI plugin runtime is the wake mechanism.
The plugin applies in the main primary checkout and a secondmate's own home, and stays silent only in child crewmate and scout worktrees.
Evidence: Pi prompt before

Actual base Pi prompt output.

================================================================================
SUPERVISION OPERATING INSTRUCTIONS - primary harness: pi
================================================================================
Current state:
- Lock: held by this session; this session owns normal supervision unless away mode says otherwise.
- Away mode: inactive.
- X mode: inactive; use the default watcher cadence.
- Ordinary wake: the Pi extension already owns watcher continuity; do not arm another cycle.

Mode: Pi extension background wake.

When this session owns supervision and away mode is not active:
1. Drain first with `bin/fm-wake-drain.sh`.
2. Confirm the Pi primary auto-loaded both project extensions (plain `pi`, after approving project trust once per clone); if not, restart with `-e /firstmate/.pi/extensions/fm-primary-turnend-guard.ts -e /firstmate/.pi/extensions/fm-primary-pi-watch.ts` as a trust-free fallback.
3. First cycle only: make the one required `fm_watch_arm_pi` call.
   Use `/fm-watch-arm-pi` only as a human-entered fallback.
   Never run `bin/fm-watch-arm.sh` through Pi's bash tool because that foreground arm can wedge the agent and bypasses extension-owned cleanup.
4. If the extension says no live session holds the lock, run `bin/fm-session-start.sh` to reclaim the session lock, then call `fm_watch_arm_pi` again.
5. The extension starts `bin/fm-watch-arm.sh --restart`, keeps the child attached to the live Pi process, and owns every later successor launch.
6. After an actionable child close, the extension rechecks session-lock ownership and verifies one successor before it delivers the follow-up wake; its bounded fallback is defined in `docs/watcher-continuity.md`.
7. Ordinary work, turn completion, and ordinary signal, stale, check, heartbeat, or other wake handling: do not call `fm_watch_arm_pi` again because continuity is extension-owned rather than model-memory-owned.
8. An unexpected child close enters bounded exponential retry, and an exhausted retry or lost session lock is surfaced as a watcher failure instead of disappearing.
9. Missing, failed, or unhealthy cycle only: if a later notification explicitly reports one of those repair conditions, drain queued wakes, inspect the failure text, call `fm_watch_arm_pi`, and restart Pi with both extensions loaded if needed.
   A redundant call while the extension owns an arm child or scheduled retry is an ownership-based `watcher: unchanged` no-op, not an independent health claim.
10. Never use shell `&` for watcher supervision.
   The arm mechanism above is extension-owned, not a model tool call, but a manual recovery probe that backgrounds, pipes, or bundles the arm is denied automatically by the PreToolUse seatbelt (`bin/fm-arm-pretool-check.sh`, wired into the turn-end guard extension at `/firstmate/.pi/extensions/fm-primary-turnend-guard.ts`).

The turn-end guard extension lives at `/firstmate/.pi/extensions/fm-primary-turnend-guard.ts`.
The watcher extension lives at `/firstmate/.pi/extensions/fm-primary-pi-watch.ts`.
Both are tracked, project-local `.pi/extensions/*.ts` files that Pi auto-discovers once the project is trusted; `bin/fm-session-start.sh` reports when the running Pi session has not loaded both required extensions.

Verification on 2026-07-09 used Pi 0.80.5, an isolated `PI_CODING_AGENT_DIR`, an isolated `FM_HOME`, and the dedicated tmux socket `fm-pi-q6-lab`.
The command `Use the fm_watch_arm_pi custom tool now. Do not use bash.` rendered `watcher: started Pi extension arm child 1`, then the model returned `DONE` without the prior `result.content.filter(...)` crash.
The extension tool returned Pi's required text `content` plus structured `details` and used `Type.Object({})` for its parameter schema.
The human command `/fm-watch-arm-pi` notified through `ctx.ui.notify(...)` and returned no value.
The clean-exit probe ran `/quit`, printed `PI_EXIT=0`, and confirmed that both the attached arm process and watcher child were gone.
That cleanup is owned by a one-shot process `exit` listener because Pi 0.80.5 did not reliably emit `session_shutdown` for `/quit`; the listener is removed when `session_shutdown` does run.
Command run for the complete interactive regression: `FM_PI_LIVE_E2E=1 tests/fm-pi-primary-live-e2e.test.sh`.
Observed output: `ok - Pi 0.80.5 live E2E rendered the tool, guarded once, woke, re-armed, and cleaned up on exit`.
Command run for the installed-type contract: `tests/fm-pi-primary-types.test.sh`.
Observed output: `ok - Pi primary extensions pass strict no-emit typecheck against Pi 0.80.5`.

Continuity verification on 2026-07-17 used Pi 0.80.10 with the existing shared Pi credential store and the explicit `openai-codex/gpt-5.6-sol` provider/model pin.
The isolated live test copied no credential material and created no account.
The model called `fm_watch_arm_pi` exactly once, an actionable status closed that cycle, the extension ledger-linked a verified successor before the handling turn ended, the turn-end guard never fired, and `/quit` cleaned up both child processes.
Command: `FM_PI_LIVE_E2E=1 tests/fm-pi-primary-live-e2e.test.sh`.
Observed output: `ok - Pi 0.80.10 live E2E used shared Codex auth, auto-started one successor before turn end, and cleaned up`.

Continuity and Calm verification on 2026-07-23 used Pi 0.81.1 with the existing shared Pi credential store and the explicit `openai-codex/gpt-5.6-sol` provider/model pin.
The isolated live test activated Calm, proved Pi's native `Working...` row remained visible during a credentialed provider request, proved no `calm transcript` status appeared, restored Calm off, and then completed the unchanged watcher successor and clean-exit lifecycle.
Command: `FM_PI_LIVE_E2E=1 tests/fm-pi-primary-live-e2e.test.sh`.
Observed output: `ok - Pi 0.81.1 live E2E covered native Calm Working visibility, Ahoy first/later messages, legacy transcripts, near misses, and watcher continuity`.

Operational follow-up verification on 2026-07-23 used Pi 0.81.1's native TUI and a deterministic in-process provider without credential material.
It proved one captain answer, exact user-role monitoring envelopes, one intended processing result, Calm loaded on, Calm loaded off, Calm absent, adjacent coalesced notifications, and restart with persisted Calm state.
Command: `tests/fm-calm-pi-extension.test.sh`.
Observed output: `ok - Pi operational follow-up E2E preserves one captain answer, exact user-role monitoring turns, Calm on/off/absent behavior, adjacent coalescing, genuine prompts, and restart persistence`.
Evidence: Pi prompt after

Actual target Pi prompt output.

================================================================================
SUPERVISION OPERATING INSTRUCTIONS - primary harness: pi
================================================================================
Current state:
- Lock: held by this session; this session owns normal supervision unless away mode says otherwise.
- Away mode: inactive.
- X mode: inactive; use the default watcher cadence.
- Ordinary wake: the Pi extension already owns watcher continuity; do not arm another cycle.

Mode: Pi extension background wake.

When this session owns supervision and away mode is not active:
1. Drain first with `bin/fm-wake-drain.sh`.
2. Confirm the Pi primary auto-loaded both project extensions (plain `pi`, after approving project trust once per clone); if not, restart with `-e /firstmate/.pi/extensions/fm-primary-turnend-guard.ts -e /firstmate/.pi/extensions/fm-primary-pi-watch.ts` as a trust-free fallback.
3. First cycle only: make the one required `fm_watch_arm_pi` call.
   Use `/fm-watch-arm-pi` only as a human-entered fallback.
   Never run `bin/fm-watch-arm.sh` through Pi's bash tool because that foreground arm can wedge the agent and bypasses extension-owned cleanup.
4. If the extension says no live session holds the lock, run `bin/fm-session-start.sh` to reclaim the session lock, then call `fm_watch_arm_pi` again.
5. The extension starts `bin/fm-watch-arm.sh --restart`, keeps the child attached to the live Pi process, and owns every later successor launch.
6. After an actionable child close, the extension rechecks session-lock ownership and verifies one successor before it delivers the follow-up wake; its bounded fallback is defined in `docs/watcher-continuity.md`.
7. Ordinary work, turn completion, and ordinary signal, stale, check, heartbeat, or other wake handling: do not call `fm_watch_arm_pi` again because continuity is extension-owned rather than model-memory-owned.
8. An unexpected child close enters bounded exponential retry, and an exhausted retry or lost session lock is surfaced as a watcher failure instead of disappearing.
9. Missing, failed, or unhealthy cycle only: if a later notification explicitly reports one of those repair conditions, drain queued wakes, inspect the failure text, call `fm_watch_arm_pi`, and restart Pi with both extensions loaded if needed.
   A redundant call while the extension owns an arm child or scheduled retry is an ownership-based `watcher: unchanged` no-op, not an independent health claim.
10. Never use shell `&` for watcher supervision.
   The arm mechanism above is extension-owned, not a model tool call, but a manual recovery probe that backgrounds, pipes, or bundles the arm is denied automatically by the PreToolUse seatbelt (`bin/fm-arm-pretool-check.sh`, wired into the turn-end guard extension at `/firstmate/.pi/extensions/fm-primary-turnend-guard.ts`).

The turn-end guard extension lives at `/firstmate/.pi/extensions/fm-primary-turnend-guard.ts`.
The watcher extension lives at `/firstmate/.pi/extensions/fm-primary-pi-watch.ts`.
Both are tracked, project-local `.pi/extensions/*.ts` files that Pi auto-discovers once the project is trusted; `bin/fm-session-start.sh` reports when the running Pi session has not loaded both required extensions.
Evidence: Unchanged harness preservation

Claude, Codex, Grok, and unknown-harness prompts are byte-identical across the change.

claude: byte-identical before and after
codex: byte-identical before and after
grok: byte-identical before and after
unknown: byte-identical before and after

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (4) ✅
  • ⚠️ bin/fm-doc-audience-check.sh:358 - Local-link enforcement ignores standard syntax in formats declared in scope. The extractor recognizes inline Markdown links and HTML attributes only, so broken reference-style Markdown links ([label][id] plus [id]: missing.md) pass. .rst files are also sent through this Markdown parser, so broken reStructuredText links pass too. Dispatch extraction by file type and validate reference definitions at this shared boundary.
  • ⚠️ bin/fm-doc-audience-check.sh:381 - Anchor extraction scans raw lines without masking code blocks, so a heading appearing only inside a fenced or indented code example is accepted as a real target anchor. A link to target.md#example therefore passes when # Example exists only in sample code. Mask code before extracting headings or use the same Markdown parse representation for links and anchors.

🔧 Fix: Validate format-aware documentation links and anchors
2 warnings still open:

  • ⚠️ bin/fm-doc-audience-check.sh:29 - The durable reference-link fix remains bypassable. CommonMark permits one line ending between [id]: and its destination, but this regex only accepts same-line destinations. reStructuredText also permits the alternate anonymous target form __ missing.rst, while the new extractor only recognizes __:. Either valid form can still contain a broken local target that passes validation. Extend reference-token extraction at this shared boundary.
  • ⚠️ bin/fm-doc-audience-check.sh:383 - rst_without_code treats every line ending in :: as the start of literal code, including directives such as .. note::. Admonition content is parsed as ordinary reStructuredText, so a broken link inside an indented note body is rendered but masked from validation. Distinguish ordinary parsed directives from literal-block introductions, masking only actual code or literal content.

🔧 Fix: Cover multiline and parsed directive links
1 warning still open:

  • ⚠️ bin/fm-doc-audience-check.sh:379 - The reStructuredText fix still masks rendered links. parsed-literal explicitly parses inline markup, so .. parsed-literal:: containing `Broken <missing.rst>`_ produces a real link, but this branch classifies the directive as literal code and removes its body before extraction. The same durable broken-link path therefore remains reachable. Keep parsed directives in prose and mask only directives whose contents are genuinely non-rendered code.

🔧 Fix: Validate links inside parsed literals
1 warning still open:

  • ⚠️ bin/fm-doc-audience-check.sh:379 - Rendered raw HTML still bypasses local-link validation. A reStructuredText block such as .. raw:: html containing <a href="missing.html"> emits a real link, but raw is classified as literal code and its body is masked. The reStructuredText branch also never applies HTML_LINK_RE, so merely retaining the body would still miss it. Keep raw content available and extract HTML link attributes at the shared local_links boundary.

🔧 Fix: Validate raw HTML links in reStructuredText
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • git diff --stat d89a1b69115525f9c3b1cef03615afabee186ff7 a963e1999ba68980c70473a68ada7f93e7eb4388 plus changed-file and commit-history inspection
  • tests/fm-documentation-audiences.test.sh
  • bin/fm-doc-audience-check.sh
  • Rendered OpenCode and Pi supervision blocks from both commits with FM_ROOT_OVERRIDE=/firstmate FM_HOME=/firstmate --read-only 0 --afk 0 --x-mode 0
  • Counted committed AGENTS.md, backend guides, supervision guides, and README.md with wc at both revisions
  • Rendered and byte-compared Claude, Codex, Grok, and unknown-harness supervision blocks across both revisions
  • Manually audited preservation-map owners against current Calm, backend, supervision, and verification documentation
  • git status --short after tests and evidence cleanup
✅ **Document** - passed

✅ No issues found.

🔧 **Lint** - 1 issue found → auto-fixed ✅
  • ⚠️ linter found issues (exit code 1)

🔧 Fix: Silence intentional reStructuredText fixture lint warnings
✅ Re-checked - no issues remain.

✅ **Push** - passed

✅ No issues found.

@kunchenguid
kunchenguid force-pushed the fm/fm-documentation-audience-cleanup-r1 branch from a62322c to 331c0e0 Compare July 23, 2026 22:12
@kunchenguid
kunchenguid force-pushed the fm/fm-documentation-audience-cleanup-r1 branch from 331c0e0 to 3a45d9d Compare July 24, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant