Skip to content

feat(bin): compile and cap startup working-memory bundle - #2641

Open
BohnBawerick wants to merge 64 commits into
kunchenguid:mainfrom
BohnBawerick:fm/fm-memory-build
Open

feat(bin): compile and cap startup working-memory bundle#2641
BohnBawerick wants to merge 64 commits into
kunchenguid:mainfrom
BohnBawerick:fm/fm-memory-build

Conversation

@BohnBawerick

Copy link
Copy Markdown

Intent

Build Memory Slice 1 for firstmate: the working-memory compiler, the startup budget cap, the data/memory/ storage layout, and the migration of existing operational memory.

PROBLEM. Session start dumps 35,526 estimated tokens, 4.7x over the 7,500-token startup budget, because bin/fm-session-start.sh unconditionally prints data/captain.md and data/learnings.md in full. The captain approved the memory architecture in data/fm-memory-design/report.md sections 5, 13 and 16, and this task implements Slice 1 of it.

DELIVERABLES.

  1. New layout under data/memory/: core.md holding standing preferences, authority boundaries and core guidelines with a 1,500-2,500 token target; notes/*.md holding atomic notes with clear claim titles, triggers, dates and citations or provenance; catalog.md as the compiled index with one line per note carrying title, trigger, path and updated date; and drop/ as a drop tray the compiler ignores.

  2. bin/fm-memory-compile.sh, the mechanical compiler. It selects the standing constitution (data/memory/core.md, falling back to the standing data/captain.md), the catalog, and the trigger-matched hot notes from data/memory/notes/*.md whose triggers match active projects, backlog items or current runtime context. It accounts estimated tokens using the bin/fm-startup-memory-budget.sh rules, meaning the ceil(UTF-8 bytes / 3) conservative estimate. It enforces the budget cap: when the total exceeds the budget it drops hot notes first while keeping the catalog in place, and when core.md alone exceeds the budget it prints the core and emits an explicit over-budget warning line without failing silently. It emits the compiled markdown bundle to stdout.

  3. Session start integration. bin/fm-session-start.sh invokes bin/fm-memory-compile.sh to inject the compiled memory block rather than dumping raw captain.md and learnings.md, and preserves a safe fallback when data/memory/ is absent.

  4. Data migration. The 51 headings in data/learnings.md become individual atomic notes under data/memory/notes/, the initial data/memory/catalog.md is generated, and originals and historical archives are retained under data/memory-archive.md so history is never deleted.

  5. Automated tests under tests/fm-memory-compile.test.sh or a colocated test script, covering bundle compilation (core plus catalog plus trigger-matched notes), trigger filtering against active keywords, strict budget cap enforcement and note dropping under budget pressure, graceful fallback when memory files are missing, and integration with fm-session-start.sh. All scripts must pass bin/fm-lint.sh shellcheck clean.

CONSTRAINTS. Follow the firstmate-coding-guidelines skill strictly, because this changes firstmate's shared tracked material. Keep changes clean, self-contained and tested. Use one sentence per line in markdown files. Never use em dashes; use plain hyphens. Never add agent co-author lines. Local main is authoritative, origin is upstream kunchenguid/firstmate, and fork is BohnBawerick/firstmate.

TWO JUDGEMENT CALLS MADE DURING IMPLEMENTATION, both deliberate and to be reviewed as part of the contract.

First, deliverable 4 is delivered as bin/fm-memory-migrate.sh, a tested idempotent migration tool, rather than as a committed data change. data/ is gitignored and lives in each FM_HOME outside this worktree, so the live-home split cannot be a PR artifact and is firstmate's to run. The tool was verified end to end against a copy of the real 51-heading data/learnings.md: it produced 51 notes, published the catalog, froze the original under data/memory/raw/ and appended it to data/memory-archive.md before removing it, and the resulting startup surface measured 7,490 estimated tokens against the 7,500 budget, down from 35,526.

Second, data/memory/core.md is fully supported, takes precedence, and is covered by tests including the over-budget path, but the migration does not mechanically fabricate one by copying data/captain.md. Two files holding standing preferences would drift the moment the captain states one, since firstmate inspect-then-updates captain.md. The compiler therefore treats data/captain.md as the standing constitution until a home authors core.md, so no home loses anything by waiting and the split stays a captain judgement rather than a script's guess.

ALSO IN SCOPE, found and fixed rather than left. Two fixtures in tests/fm-session-start.test.sh forced a MISSING tool diagnostic by removing node from the fake bin, which proves nothing on a host that also ships /usr/bin/node and made the suite fail here before any of this work. They now shadow gh-axi, which cannot exist outside the fake bin, so the assertion means the same thing on every host.

What Changed

  • Added bin/fm-memory-compile.sh to compile startup working-memory bundles from data/memory/core.md, data/memory/catalog.md, and keyword-matched data/memory/notes/*.md, enforcing startup token budgets and dropping hot notes when over capacity.
  • Added bin/fm-memory-migrate.sh to split data/learnings.md into atomic notes with front matter, generate the catalog index, and preserve raw originals under data/memory-archive.md and data/memory/raw/.
  • Updated bin/fm-session-start.sh, agent instructions, and skills to inject the compiled memory block with fallback to raw memory files when data/memory/ is absent, covered by new test suites in tests/fm-memory-compile.test.sh.

Risk Assessment

✅ Low: The memory compilation, budget enforcement, migration tooling, and documentation changes are well-bounded, comprehensively covered by behavioral tests, and cleanly resolve all previous review findings.

Testing

Exercised targeted automated test suites covering memory compilation, migration, session start digest injection, bootstrap fixtures, and startup memory budgets (all assertions passed). Performed end-to-end product verification generating complete CLI transcripts for migration, compilation, budget capping, and session startup integration in /tmp/no-mistakes-evidence/01M0DGRCBQ3MAVMB4RGNM3B5DC.

Evidence: Memory Slice 1 Verification Overview

Source: Memory Slice 1 Verification Overview

# Firstmate Memory Slice 1 - End-to-End Verification Evidence

This directory contains product-level verification transcripts demonstrating all deliverables and acceptance criteria of Memory Slice 1:

1. **`01-fm-memory-migrate.transcript.txt`**:
   - Mechanical split of `data/learnings.md` into atomic notes under `data/memory/notes/`.
   - Derivation of YAML front matter (titles, triggers, updated dates, tiers, source).
   - Generation and publishing of `data/memory/catalog.md`.
   - Freezing original to `data/memory/raw/learnings-YYYY-MM-DD.md` and appending to `data/memory-archive.md`.
   - Verification of idempotent re-run behavior.

2. **`02-fm-memory-compile.transcript.txt`**:
   - Working memory bundle compilation (`bin/fm-memory-compile.sh compile`).
   - Constitution selection (`data/memory/core.md` taking precedence over `data/captain.md` fallback).
   - Dynamic trigger matching against active projects and backlog keywords.
   - Ignore behavior for drop tray (`data/memory/drop/`).
   - On-demand and fresh catalog publishing (`bin/fm-memory-compile.sh catalog`).

3. **`03-fm-memory-budget-cap.transcript.txt`**:
   - Strict budget cap accounting using conservative `ceil(UTF-8 bytes / 3)` token formula.
   - Within-budget execution (`status=within-budget`).
   - Capped execution dropping hot notes to preserve catalog and core under pressure (`status=capped`).
   - Loud over-budget warning when core exceeds budget without silent failure.
   - Refusal on unreadable or corrupted budget values.

4. **`04-fm-session-start.transcript.txt`**:
   - End-to-end integration into `bin/fm-session-start.sh` startup digest.
   - Capped curated memory injection replacing unconditional raw file dumps.
   - Startup token consumption dramatically reduced from >35k tokens down to within the 7,500 token budget.
Evidence: End-to-End Migration CLI Transcript

Source: End-to-End Migration CLI Transcript

========================================================================
1. PRE-MIGRATION STATE: data/learnings.md exists
========================================================================
-rw-rw-r-- 1 paiva paiva 873 Aug 20 01:56 /tmp/fm-memory-evidence.z3Mhhv/home-migrate/data/learnings.md

========================================================================
2. EXECUTING: bin/fm-memory-migrate.sh
========================================================================
created notes/treehouse-lease-timeouts-must-be-strictly-bounded.md
created notes/herdr-workspace-focus-and-presentation-lifecycle.md
created notes/memory-budget-calculation-rules-fm-startup-memory-budget-sh.md
created notes/sqlite-database-locking-in-multi-agent-environments-2026-08.md
migrate: 4 note(s) created, 0 already present
catalog: published data/memory/catalog.md (4 note(s))
migrate: froze the original at data/memory/raw/learnings-2026-08-19.md
migrate: appended the original to data/memory-archive.md
migrate: removed data/learnings.md (frozen at data/memory/raw/learnings-2026-08-19.md and archived in data/memory-archive.md)

========================================================================
3. RESULTING DIRECTORY LAYOUT: data/memory/
========================================================================
/tmp/fm-memory-evidence.z3Mhhv/home-migrate/data/memory/catalog.md
/tmp/fm-memory-evidence.z3Mhhv/home-migrate/data/memory/drop/README.md
/tmp/fm-memory-evidence.z3Mhhv/home-migrate/data/memory/notes/herdr-workspace-focus-and-presentation-lifecycle.md
/tmp/fm-memory-evidence.z3Mhhv/home-migrate/data/memory/notes/memory-budget-calculation-rules-fm-startup-memory-budget-sh.md
/tmp/fm-memory-evidence.z3Mhhv/home-migrate/data/memory/notes/sqlite-database-locking-in-multi-agent-environments-2026-08.md
/tmp/fm-memory-evidence.z3Mhhv/home-migrate/data/memory/notes/treehouse-lease-timeouts-must-be-strictly-bounded.md
/tmp/fm-memory-evidence.z3Mhhv/home-migrate/data/memory/raw/learnings-2026-08-19.md

========================================================================
4. GENERATED CATALOG (data/memory/catalog.md)
========================================================================
<!-- compiled by bin/fm-memory-compile.sh from data/memory/notes/ -->

- Herdr workspace focus and presentation lifecycle (herdr-workspace-focus-and-presentation-lifecycle.md | herdr | 2026-08-12)
- Memory budget calculation rules (`fm-startup-memory-budget.sh`) (memory-budget-calculation-rules-fm-startup-memory-budget-sh.md | fm-startup-memory-budget.sh, memory | ?)
- SQLite database locking in multi-agent environments (2026-08-17) (sqlite-database-locking-in-multi-agent-environments-2026-08.md | multi-agent, sqlite | 2026-08-17)
- `treehouse` lease timeouts must be strictly bounded (treehouse-lease-timeouts-must-be-strictly-bounded.md | treehouse | 2026-08-10)

========================================================================
5. ATOMIC NOTE WITH EXTRACTED METADATA
========================================================================
---
title: `treehouse` lease timeouts must be strictly bounded
triggers: treehouse
updated: 2026-08-10
tier: <!--a:2026-08-10-->
source: data/learnings.md
---

# `treehouse` lease timeouts must be strictly bounded
Treehouse lease acquisitions can hang if network health checks stall.
Always wrap lease acquisition with bounded timeouts.


========================================================================
6. RAW ARCHIVE (data/memory/raw/learnings-YYYY-MM-DD.md)
========================================================================
total 4.0K
-rw-rw-r-- 1 paiva paiva 873 Aug 20 01:56 learnings-2026-08-19.md

========================================================================
7. DURABLE MEMORY ARCHIVE (data/memory-archive.md)
========================================================================

<!-- archived from data/learnings.md by bin/fm-memory-migrate.sh on 2026-08-19 -->

<!-- memory tiers: see the stow skill -->

## `treehouse` lease timeouts must be strictly bounded <!--a:2026-08-10-->
Treehouse lease acquisitions can hang if network health checks stall.
Always wrap lease acquisition with bounded timeouts.

## Herdr workspace focus and presentation lifecycle <!--a:2026-08-12-->
Herdr panes must be focused cleanly without flashing.
Workspace presentation helpers must check for existing active panes before spawning new ones.

## Memory budget calculation rules (`fm-startup-memory-budget.sh`) <!--P-->
The conservative token estimation formula is `ceil(UTF-8 bytes / 3)`.
Framing and delimiters are excluded when computing memory content bytes.

## SQLite database locking in multi-agent environments (2026-08-17)
SQLite requires busy timeouts when multiple worker processes contend for the state database.
Use WAL mode where possible.

========================================================================
8. IDEMPOTENCY VERIFICATION (RE-RUNNING MIGRATION)
========================================================================
kept    notes/treehouse-lease-timeouts-must-be-strictly-bounded.md
kept    notes/herdr-workspace-focus-and-presentation-lifecycle.md
kept    notes/memory-budget-calculation-rules-fm-startup-memory-budget-sh.md
kept    notes/sqlite-database-locking-in-multi-agent-environments-2026-08.md
migrate: 0 note(s) created, 4 already present
catalog: published data/memory/catalog.md (4 note(s))
migrate: data/memory/raw/learnings-2026-08-19.md already exists; the original was frozen by an earlier run
migrate: data/memory-archive.md already carries this run banner
migrate: removed data/learnings.md (frozen at data/memory/raw/learnings-2026-08-19.md and archived in data/memory-archive.md)
Evidence: Working Memory Compilation and Trigger Matching Transcript

Source: Working Memory Compilation and Trigger Matching Transcript

========================================================================
1. COMPILE WITH CAPTAIN.MD FALLBACK (NO CORE.MD YET)
========================================================================
COMPILED WORKING MEMORY (data/memory)
--------------------------------------------------------------------------------
Compiled by bin/fm-memory-compile.sh against a 7500-estimated-token cap.
This is the whole startup memory surface: it is selected and capped, not dumped.
The catalog lists every note that exists, so when a title below matches what
this turn needs and its body was not injected, read that file by its path.

core: data/captain.md (standing constitution; data/memory/core.md is ABSENT)
--------------------------------------------------------------------------------
# Captain Standing Preferences
- Local main is authoritative.
- Verify all changes.

catalog (compiled from data/memory/notes/)
--------------------------------------------------------------------------------
<!-- compiled by bin/fm-memory-compile.sh from data/memory/notes/ -->

- Herdr focus and presentation lifecycle (herdr-focus.md | herdr, workspace | 2026-08-12)
- Treehouse lease timeout handling (treehouse-leases.md | treehouse, lease | 2026-08-10)
- Unrelated Python pip packaging note (unrelated-topic.md | python, pip | 2026-08-01)

hot note: notes/herdr-focus.md
--------------------------------------------------------------------------------
---
title: Herdr focus and presentation lifecycle
triggers: herdr, workspace
updated: 2026-08-12
source: data/learnings.md
---

# Herdr focus and presentation lifecycle
Herdr panes must be focused cleanly without flashing.
Workspace presentation helpers must check for existing active panes before spawning new ones.

hot note: notes/treehouse-leases.md
--------------------------------------------------------------------------------
---
title: Treehouse lease timeout handling
triggers: treehouse, lease
updated: 2026-08-10
source: data/learnings.md
---

# Treehouse lease timeout handling
Treehouse lease acquisitions can hang if network health checks stall.
Always wrap lease acquisition with bounded timeouts.

MEMORY_NOTICE: data/memory/catalog.md is ABSENT. The catalog in this bundle was rendered fresh from data/memory/notes/; publish the file with bin/fm-memory-compile.sh catalog.

MEMORY_ACCOUNTING: budget=7500 core=28 catalog=112 hot_notes=2 hot_notes_tokens=200 notes_total=3 hot_dropped=0 injected_total=340 status=within-budget

========================================================================
2. AUTHORING DATA/MEMORY/CORE.MD (TAKES PRECEDENCE OVER CAPTAIN.MD)
========================================================================
COMPILED WORKING MEMORY (data/memory)
--------------------------------------------------------------------------------
Compiled by bin/fm-memory-compile.sh against a 7500-estimated-token cap.
This is the whole startup memory surface: it is selected and capped, not dumped.
The catalog lists every note that exists, so when a title below matches what
this turn needs and its body was not injected, read that file by its path.

core: data/memory/core.md
--------------------------------------------------------------------------------
# Standing Constitution
- Local main is authoritative.
- Never delete historical records without an archive.
- Stay within startup memory budget.

catalog (compiled from data/memory/notes/)
--------------------------------------------------------------------------------
<!-- compiled by bin/fm-memory-compile.sh from data/memory/notes/ -->

- Herdr focus and presentation lifecycle (herdr-focus.md | herdr, workspace | 2026-08-12)
- Treehouse lease timeout handling (treehouse-leases.md | treehouse, lease | 2026-08-10)
- Unrelated Python pip packaging note (unrelated-topic.md | python, pip | 2026-08-01)

hot note: notes/herdr-focus.md
--------------------------------------------------------------------------------
---
title: Herdr focus and presentation lifecycle
triggers: herdr, workspace
updated: 2026-08-12
source: data/learnings.md
---

# Herdr focus and presentation lifecycle
Herdr panes must be focused cleanly without flashing.
Workspace presentation helpers must check for existing active panes before spawning new ones.

hot note: notes/treehouse-leases.md
--------------------------------------------------------------------------------
---
title: Treehouse lease timeout handling
triggers: treehouse, lease
updated: 2026-08-10
source: data/learnings.md
---

# Treehouse lease timeout handling
Treehouse lease acquisitions can hang if network health checks stall.
Always wrap lease acquisition with bounded timeouts.

MEMORY_NOTICE: data/captain.md is still present (28 estimated tokens) and is NOT injected because data/memory/core.md takes precedence. Fold standing preferences into data/memory/core.md, or remove data/captain.md.

MEMORY_NOTICE: data/memory/catalog.md is ABSENT. The catalog in this bundle was rendered fresh from data/memory/notes/; publish the file with bin/fm-memory-compile.sh catalog.

MEMORY_ACCOUNTING: budget=7500 core=49 catalog=112 hot_notes=2 hot_notes_tokens=200 notes_total=3 hot_dropped=0 injected_total=361 status=within-budget

========================================================================
3. REPUBLISHING CATALOG (bin/fm-memory-compile.sh catalog)
========================================================================
catalog: published data/memory/catalog.md (3 note(s))
<!-- compiled by bin/fm-memory-compile.sh from data/memory/notes/ -->

- Herdr focus and presentation lifecycle (herdr-focus.md | herdr, workspace | 2026-08-12)
- Treehouse lease timeout handling (treehouse-leases.md | treehouse, lease | 2026-08-10)
- Unrelated Python pip packaging note (unrelated-topic.md | python, pip | 2026-08-01)
Evidence: Budget Cap and Over-Budget Enforcement Transcript

Source: Budget Cap and Over-Budget Enforcement Transcript

========================================================================
CASE A: GENEROUS BUDGET (7500 tokens) -> STATUS: within-budget
========================================================================
MEMORY_ACCOUNTING: budget=7500 core=19 catalog=144 hot_notes=5 hot_notes_tokens=370 notes_total=5 hot_dropped=0 injected_total=533 status=within-budget

========================================================================
CASE B: TIGHT BUDGET (180 tokens) -> STATUS: capped
========================================================================
COMPILED WORKING MEMORY (data/memory)
--------------------------------------------------------------------------------
Compiled by bin/fm-memory-compile.sh against a 180-estimated-token cap.
This is the whole startup memory surface: it is selected and capped, not dumped.
The catalog lists every note that exists, so when a title below matches what
this turn needs and its body was not injected, read that file by its path.

core: data/memory/core.md
--------------------------------------------------------------------------------
# Standing Constitution
- Local main is authoritative.

catalog (compiled from data/memory/notes/)
--------------------------------------------------------------------------------
<!-- compiled by bin/fm-memory-compile.sh from data/memory/notes/ -->

- Note number 1 on active task handling (note-1.md | task | 2026-08-11)
- Note number 2 on active task handling (note-2.md | task | 2026-08-12)
- Note number 3 on active task handling (note-3.md | task | 2026-08-13)
- Note number 4 on active task handling (note-4.md | task | 2026-08-14)
- Note number 5 on active task handling (note-5.md | task | 2026-08-15)

MEMORY_BUDGET_NOTICE: 5 trigger-matched note(s) did not fit the budget and were not injected. Every one of them is still listed in the catalog above; read it by path when its title matches.

MEMORY_NOTICE: data/memory/catalog.md is ABSENT. The catalog in this bundle was rendered fresh from data/memory/notes/; publish the file with bin/fm-memory-compile.sh catalog.

MEMORY_ACCOUNTING: budget=180 core=19 catalog=144 hot_notes=0 hot_notes_tokens=0 notes_total=5 hot_dropped=5 injected_total=163 status=capped

========================================================================
CASE C: CORE ALONE EXCEEDS BUDGET (20 tokens) -> STATUS: over-budget warning
========================================================================
COMPILED WORKING MEMORY (data/memory)
--------------------------------------------------------------------------------
Compiled by bin/fm-memory-compile.sh against a 20-estimated-token cap.
This is the whole startup memory surface: it is selected and capped, not dumped.
The catalog lists every note that exists, so when a title below matches what
this turn needs and its body was not injected, read that file by its path.

core: data/memory/core.md
--------------------------------------------------------------------------------
# Standing Constitution
- Local main is authoritative.

MEMORY_BUDGET_WARNING: the core plus catalog is 163 estimated tokens against a 20 budget, so the catalog and every note were dropped. Trim the core or raise config/startup-memory-budget; until then this session cannot see what notes exist.

MEMORY_NOTICE: data/memory/catalog.md is ABSENT. The catalog in this bundle was rendered fresh from data/memory/notes/; publish the file with bin/fm-memory-compile.sh catalog.

MEMORY_ACCOUNTING: budget=20 core=19 catalog=0 hot_notes=0 hot_notes_tokens=0 notes_total=5 hot_dropped=5 injected_total=19 status=capped

========================================================================
CASE D: CORRUPTED/INVALID BUDGET FILE -> REFUSES EXECUTION
========================================================================
fm-memory-compile: invalid config/startup-memory-budget - value must be one positive decimal integer
Exit status: 1
Evidence: Session Start Integration Transcript

Source: Session Start Integration Transcript

========================================================================
fm-session-start.sh WITH COMPILED WORKING MEMORY INJECTION
========================================================================

================================================================================
SESSION START - /tmp/fm-sess-start.bm05cs/home
================================================================================

LOCK
--------------------------------------------------------------------------------
lock acquired: harness pid 3689

BOOTSTRAP
--------------------------------------------------------------------------------
MISSING: quota-axi (install: npm install -g quota-axi)
MISSING: tasks-axi (install: npm install -g tasks-axi)

WAKE QUEUE
--------------------------------------------------------------------------------
(no queued wakes)
PI_WATCH_EXTENSION: not loaded - approve Pi project trust once per clone, then restart plain pi so /tmp/fm-wake-tangle-root.kTX9xq/.pi/extensions/fm-primary-turnend-guard.ts and /tmp/fm-wake-tangle-root.kTX9xq/.pi/extensions/fm-primary-pi-watch.ts auto-load for turn-end guard and background wake coverage; use -e /tmp/fm-wake-tangle-root.kTX9xq/.pi/extensions/fm-primary-turnend-guard.ts -e /tmp/fm-wake-tangle-root.kTX9xq/.pi/extensions/fm-primary-pi-watch.ts only if project hooks are not trusted
================================================================================
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`.
   After handling all emitted wakes and reconciling open decisions and unread status lines, run the exact `--ack-through` command printed as `WAKE_ACK_REQUIRED`; until then the work remains durable for idempotent re-handling after interruption.
2. Confirm the Pi primary auto-loaded both project extensions (plain `pi` or `pi-signed`, after approving project trust once per clone); if not, restart the selected executable with `-e /tmp/fm-wake-tangle-root.kTX9xq/.pi/extensions/fm-primary-turnend-guard.ts -e /tmp/fm-wake-tangle-root.kTX9xq/.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. Ordinary same-process session replacement (`/new`, `/resume`, `/fork`, reload) retires only the prior generation; call `fm_watch_arm_pi` once for the first cycle of the replacement session without restarting Pi.
   The generation-owner contract lives in `.pi/extensions/fm-primary-pi-watch.ts`.
7. 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`.
8. 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.
9. 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.
10. 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 the selected Pi-family executable 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.
11. 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 `/tmp/fm-wake-tangle-root.kTX9xq/.pi/extensions/fm-primary-turnend-guard.ts`).

The turn-end guard extension lives at `/tmp/fm-wake-tangle-root.kTX9xq/.pi/extensions/fm-primary-turnend-guard.ts`.
The watcher extension lives at `/tmp/fm-wake-tangle-root.kTX9xq/.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.


================================================================================
READ-ONCE CONTRACT
================================================================================
Everything below is printed in full for this session start: every state/*.meta,
a compact data/backlog.md listing, a bounded tail of every state/*.status,
data/projects.md, data/secondmates.md, data/captain-shared.md, and the curated
memory for this session.
Do NOT re-read any of them after reading this digest, and do NOT bulk-read
data/backlog.md or state/*.status: re-reading everything defeats the entire
point of this command.
Curated memory is SELECTED AND CAPPED, not dumped: the bundle carries a core, a
catalog of every note that exists, and the notes whose triggers matched this
fleet's live work. Reading one more note BY ITS CATALOG PATH when its title
matches what this turn needs is expected and is not a re-read.

Go to a source directly only when:
  - this digest flagged it ABSENT (then rebuild or create it per AGENTS.md),
  - its contents looked unparseable or corrupt,
  - an individual full status log is needed for older wake-event history, or a
    status line was capped and its tail matters (each task's full log path is
    printed with its tail),
  - a full task body is needed (tasks-axi show <id> --full, or data/backlog.md),
  - the backlog listing disclosed omitted queued items and this turn needs them,
  - the NETWORK CHECKS section reported its checks still IN PROGRESS and this
    turn needs their verdict (bin/fm-startup-network.sh report),
  - or a STARTUP TRUNCATED banner named the stage that would have printed it, in
    which case that stage's sources were never emitted and must be reconciled.

================================================================================
FLEET STATE
================================================================================

data/backlog.md
--------------------------------------------------------------------------------
ABSENT

Work under way (state/*.meta)
--------------------------------------------------------------------------------
(none)

Orphan status logs (state/*.status without matching .meta)
--------------------------------------------------------------------------------
(none)

AFK
--------------------------------------------------------------------------------
absent

================================================================================
NETWORK CHECKS
================================================================================
not started - no deferred network checks have run for this home yet.

================================================================================
CONTEXT
================================================================================

data/projects.md
--------------------------------------------------------------------------------
- healthlog [no-mistakes] - web application (added 2026-07-01)
- memory-compiler [active] - working memory system

data/secondmates.md
--------------------------------------------------------------------------------
ABSENT

curated memory (compiled, capped - bin/fm-memory-compile.sh)
--------------------------------------------------------------------------------
COMPILED WORKING MEMORY (data/memory)
--------------------------------------------------------------------------------
Compiled by bin/fm-memory-compile.sh against a 7500-estimated-token cap.
This is the whole startup memory surface: it is selected and capped, not dumped.
The catalog lists every note that exists, so when a title below matches what
this turn needs and its body was not injected, read that file by its path.

core: data/memory/core.md
--------------------------------------------------------------------------------
# Core Constitution
- Local main is authoritative.
- Firstmate operates under human supervision.

catalog (compiled from data/memory/notes/)
--------------------------------------------------------------------------------
<!-- compiled by bin/fm-memory-compile.sh from data/memory/notes/ -->

- Healthlog test concurrency rules (healthlog-concurrency.md | healthlog | 2026-08-18)
- Unrelated docker note (unrelated.md | docker | 2026-08-01)

hot note: notes/healthlog-concurrency.md
--------------------------------------------------------------------------------
---
title: Healthlog test concurrency rules
triggers: healthlog
updated: 2026-08-18
source: data/learnings.md
---

# Healthlog test concurrency rules
Healthlog browser tests cannot run concurrently due to fixed port allocations.

MEMORY_NOTICE: data/captain.md is still present (21 estimated tokens) and is NOT injected because data/memory/core.md takes precedence. Fold standing preferences into data/memory/core.md, or remove data/captain.md.

MEMORY_NOTICE: data/memory/catalog.md is ABSENT. The catalog in this bundle was rendered fresh from data/memory/notes/; publish the file with bin/fm-memory-compile.sh catalog.

MEMORY_ACCOUNTING: budget=7500 core=33 catalog=73 hot_notes=1 hot_notes_tokens=77 notes_total=2 hot_dropped=0 injected_total=183 status=within-budget

data/captain-shared.md (shared, main-authoritative, read-only in secondmate homes)
--------------------------------------------------------------------------------
ABSENT

================================================================================
NEXT STEP
================================================================================
Follow the supervision operating instructions block above for harness 'pi'.
This script never starts supervision itself.

The digest above is complete for this session start. The READ-ONCE CONTRACT
section near the top of it governs what may still be read from disk.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 8 issues found → auto-fixed ✅
  • 🚨 bin/fm-memory-migrate.sh:266 - Two learnings headings that slugify to the same name silently lose the second claim. The first heading writes notes/<slug>.md; the second sees the target exists, counts it as KEPT, reports 'already present' (identical to an idempotent re-run), and its body never becomes a note. The run then freezes, archives and deletes data/learnings.md, so the operator sees a success report while the working-memory layout is missing a claim. Reproduced: a fixture with '## The gh CLI needs auth <!--a:2026-08-01-->' and '## The gh CLI needs auth. <!--a:2026-08-02-->' produced '1 note(s) created, 1 already present' and the second body was absent from data/memory/notes/. Collisions are not exotic: slugify() truncates at 60 characters (cut -c1-60), so any two headings sharing their first 60 slug characters collide too. The header comment at lines 124-125 claims 'a duplicate slug can be disambiguated', but no disambiguation is implemented. Fix: track slugs claimed during this run and suffix a within-run collision (slug-2, slug-3), counting it as created rather than kept.
  • ⚠️ bin/fm-memory-compile.sh:260 - die() for a missing --context-file runs inside build_context, which is the left side of the pipeline at line 270, so it only exits that subshell. Verified: fm-memory-compile.sh compile --no-auto-context --context-file /nope/missing.md prints 'fm-memory-compile: context file not found' to stderr, exits 0, and emits a full bundle built from a truncated match context. Any --context/--context-file argument after the failing one is also discarded, so hot-note selection silently changes while the caller sees success. Fix: validate every CONTEXT_FILES path before the pipeline (or check PIPESTATUS[0]) and exit non-zero.
  • ⚠️ bin/fm-session-start.sh:922 - The compiled bundle is capped at config/startup-memory-budget, but data/captain-shared.md is still printed whole immediately after it and is not accounted by fm-memory-compile.sh, while bin/fm-startup-memory-budget.sh report still counts it against the same budget value. In a secondmate home the compiler can legitimately fill the entire 7500-token cap with core plus catalog plus notes, and captain-shared.md then adds an unbounded amount on top, so the real startup surface exceeds the allowance the cap is supposed to enforce and the two accounting paths disagree about the same number. Not a regression (the old path dumped everything uncapped), but it leaves a documented hole in Slice 1's cap. Decide whether captain-shared.md should be accounted by the compiler or explicitly carved out of the budget's definition in docs/configuration.md.
  • ⚠️ bin/fm-memory-compile.sh:336 - Once data/memory/core.md exists, a non-empty data/captain.md is neither injected nor mentioned anywhere in the bundle. Verified: with both files present, the compiled output contains zero occurrences of the string 'captain'. This is asymmetric with data/learnings.md, which gets a loud MEMORY_NOTICE at line 360 for exactly the same situation (present, not injected), and it contradicts the header's own stated invariant that everything degrades to a printed notice. It also conflicts with AGENTS.md, which this change leaves unmodified: 'Home-domain captain preferences and working style belong in data/captain.md after inspect-then-update.' A home that authors core.md and keeps following that rule writes preferences that no session start will ever read, with no signal. Either emit a MEMORY_NOTICE naming captain.md's estimated tokens when core.md shadows it, or update the AGENTS.md routing rule to point at core.md once the layout exists.
  • ℹ️ docs/configuration.md:148 - The intent's deliverable 1 requires 'core.md holding standing preferences, authority boundaries and core guidelines with a 1,500-2,500 token target'. The 1,500-2,500 range appears nowhere in the change (docs/configuration.md, AGENTS.md, the stow skill, or the compiler header), so a home that later authors core.md has no stated size target and only learns it went too far when the whole budget is blown. Deferring the file itself is explicitly authorized by the intent's second judgement call; the target for whoever writes it is not covered by that deferral. One sentence in the 'Compiled working memory' section would close it.
  • ℹ️ bin/fm-memory-compile.sh:179 - Every memory FILE is symlink-checked (core.md, catalog.md, each note, captain.md, projects.md, backlog.md, state metas), and the budget library even has a dedicated config-directory symlink check, but the containing directories are not: [ -d &#34;$NOTES_DIR&#34; ] and [ -d &#34;$DATA/memory&#34; ] in fm-session-start.sh both succeed on a symlink to a directory. A symlinked data/memory or data/memory/notes bypasses every per-file guard in one step, since the files inside the target are ordinary regular files. Adding [ ! -L ] to the two directory tests makes the guard match the model the rest of the script already applies.
  • ℹ️ bin/fm-session-start.sh:902 - mktemp creates MEMORY_TMP safely, but the sibling stderr file is opened as "$MEMORY_TMP.err", a name mktemp never created and never held. In a world-writable /tmp a local attacker who reads the directory after mktemp lands can plant a symlink at that exact path before the redirect opens it, and the compiler's stderr is then written through it. Both other scripts in this change already use mktemp -d and keep their working files inside; using a directory here removes the race.
  • ℹ️ bin/fm-memory-compile.sh:215 - render_catalog spawns printf, cut and sed per note, and tokens_of_file spawns a subshell plus wc per note, so a 51-note home costs roughly 250 processes on the session-start critical path; measured 0.68s for 51 notes with no hot notes selected (WSL2). The trigger truncation at line 215 is plain string work that fits in the awk pass that already builds the inventory, and the per-note byte counts can come from a single wc -c over all note files. Both are behavior-preserving and would cut most of the forks.

🔧 Fix: fix memory migration collisions and compiler edge cases
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • ./bin/fm-test-run.sh tests/fm-memory-compile.test.sh
  • ./bin/fm-test-run.sh tests/fm-session-start.test.sh
  • ./bin/fm-test-run.sh tests/fm-bootstrap.test.sh
  • ./bin/fm-test-run.sh tests/fm-startup-memory-budget.test.sh
  • FM_HOME=&lt;scratch&gt; ./bin/fm-memory-migrate.sh (tested atomic note generation, front matter parsing, catalog publishing, raw archiving, durable history appending, and rerun idempotency)
  • FM_HOME=&lt;scratch&gt; ./bin/fm-memory-compile.sh compile (tested core.md precedence over captain.md fallback, trigger matching against active projects/backlog keywords, drop tray exclusion, and fresh catalog rendering)
  • FM_HOME=&lt;scratch&gt; ./bin/fm-memory-compile.sh catalog (tested manual catalog republishing)
  • Budget cap enforcement verification across within-budget (7500 tokens), capped under budget pressure (180 tokens dropping notes while preserving catalog and core), over-budget core warning (20 tokens), and malformed budget rejection
  • FM_HOME=&lt;scratch&gt; ./bin/fm-session-start.sh (tested end-to-end session start digest injection of compiled capped working memory and safe fallback behavior)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Kimi 0.36.0 prompts Trust this folder? on every untrusted worktree path,
with Don't trust preselected. The readiness gate waited only for Welcome
or an empty composer, so spawn aborted and the pane died.

Detect that exact dialog, send Up then Enter, and keep the existing
readiness and delivery checks. Do not write ~/.kimi-code/workspace-trust/.
Checked kimi-cli and kimi-code CLI/config docs plus kimi --help.
--auto and --yolo do not skip Trust this folder?, so spawn still
accepts the dialog with Up then Enter.
Applied locally at the captain's explicit instruction so kimi is dispatchable
in this home today. The same change is open upstream as PR kunchenguid#2328, held by
GitHub pending first-time-contributor workflow approval.

This diverges the primary from origin/main until that PR lands. Reconcile by
dropping this merge once upstream carries the change.
Verify Antigravity CLI as a crewmate/scout adapter. Pin the launch to
gemini-3.1-pro-high, omit --effort (it conflicts with *-high model ids),
accept the workspace trust dialog, and install a gated global Stop hook.
Secondmate, primary turn-end, and watcher-arm remain unverified.
A separated greater-than pair is unknown on zellij, cmux, and orca
because agy is not verified there. Identity-capable backends keep
the existing probe-then-shortcut path.
Verify Antigravity CLI as a crewmate/scout adapter. Pin the launch to
gemini-3.1-pro-high, omit --effort (it conflicts with *-high model ids),
accept the workspace trust dialog, and install a gated global Stop hook.
Secondmate, primary turn-end, and watcher-arm remain unverified.
A separated greater-than pair is unknown on zellij, cmux, and orca
because agy is not verified there. Identity-capable backends keep
the existing probe-then-shortcut path.
Applies the same pattern as the Kimi 0.36.0 workspace-trust fix: the adapter
works and the runtime is installed and funded, so the running copy gets it now
rather than waiting on the PR to land. Reconcile by dropping this merge once
the upstream PR merges.

Adopted at fm/hz-agy-adapter e474f5a; the review fix round still in flight is
not included and will arrive with the PR.

# Conflicts:
#	bin/fm-spawn.sh
#	docs/configuration.md
BohnBawerick and others added 29 commits August 16, 2026 12:38
Its substantive work is already on main: 13 of its 14 commits have equivalents
here, and the two that were missing were brought across as 48952de and 2c2b7b2.
The only remaining difference is one docs/configuration.md paragraph, where the
branch's wording named the superseded gemini-3.1-pro-high pin; main's wording is
kept deliberately and points at harness-adapters as the pin's owner instead.

Recorded rather than discarded so nothing is lost and the upstream PR 2366 can
stay open on its own without this task holding a local copy open to watch it.
- The mode=no-mistakes ship scaffold told the worker to invoke the
  /no-mistakes skill three times. A crewmate runs in a project worktree,
  not the firstmate home, so that skill is unreachable and the worker
  stalls exactly where it has just finished implementing. Overnight
  2026-08-17/18 three workers each burned a supervisor round trip there
  and reported done: for a commit with no PR.
- The scaffold now names the interface the worker actually has: the
  no-mistakes CLI on PATH, with the concrete run and respond commands,
  and it notes that firstmate's trigger may still be worded as a skill
  invocation so either wording lands on the same command.
- Harden the failure mode the defect lands on: completion for this mode
  is now stated as a green PR, the implementation handoff line says in
  its own text that nothing has shipped yet, and a run that cannot start
  is routed to blocked: rather than done:.
- direct-PR refused "/no-mistakes"; it now refuses the pipeline by name.
  local-only, scout and the secondmate charter carried no such premise.
- Extend tests/fm-brief.test.sh: every variant is generated and checked
  for skill-invocation instructions, and the no-mistakes definition of
  done is pinned to the CLI commands and the PR-bound done: gate.
- Add an away supervision model to bin/fm-wake-lib.sh: while state/.afk
  exists the away daemon owns supervision for every primary harness and
  runs the watcher one cycle at a time, so an unheld watcher lock is the
  healthy state and the daemon itself is what must be tested.
- fm_turnend_supervision_healthy keeps the PID-strict watcher check
  everywhere except away mode, where fm_away_daemon_healthy requires a
  live identity-matched daemon plus a turning loop; a dead, recycled-pid
  or wedged daemon still blocks the turn end.
- Bound daemon-tick freshness with FM_AWAY_TICK_GRACE (180s), derived
  from the daemon's housekeeping cadence and crash backoff rather than
  guessed, and read the freshest of its housekeeping tick, its watcher
  child's beat, and its startup stamp.
- Give the pull guard the same answer, and name the daemon in both
  banners instead of telling the session to arm a watcher.
- Let bin/fm-afk-start.sh delegate its already-running check to the same
  predicate so entering away mode and guarding it cannot disagree.
- Away mode outranks a pinned FM_SUPERVISION_MODEL harness model, which
  bin/fm-spawn.sh bakes into every secondmate launch.
Suppress the possible-wedge escalation when the busy source affirmatively
reports a pane as working. Away-mode housekeeping was aging a stale marker
into a wedge alarm without ever asking whether the worker was busy, which
produced repeated false alarms on panes that were provably mid-task.

Landed locally on the captain's word so the fleet gets the fix now; upstream
PR 2554 stays open on its own at kunchenguid/firstmate.
Stop the turn-end guard raising TURN WOULD END BLIND on a healthy fleet under
away mode. The guard demanded a live long-lived watcher process, which away
mode deliberately does not run - the away daemon owns supervision and runs the
watcher one-shot. The supervision-model vocabulary in fm-wake-lib.sh now covers
that fourth shape, so a healthy away fleet stops quietly while a dead or
stalled daemon still blocks. Measured 3/200 false blocks before, 0/200 after.

Landed locally on the captain's word so the fleet gets the fix now; upstream
PR 2557 stays open on its own at kunchenguid/firstmate.
A run's recorded head is the head the PIPELINE has advanced to as it applies
its own fix commits, and those commits are pushed to the configured target
rather than fetched into the crew's worktree - so during a run the head does
not resolve in that repository at all. The attribution rule treated an
unresolvable head as a mismatch, rejected the branch's own live row, and
matched the newest stale row underneath it: a failed run recorded at the very
head the worktree still held. A healthy validation therefore read as `failed`,
which routes firstmate into recovery and can restart a run that is working.

- Code identity is now ternary: match, mismatch, and unverified for a real run
  head this copy cannot resolve. The strict predicate is unchanged, so
  teardown's abort authority still requires a verified binding.
- An unverified run is bound by its submitted head - the head it was launched
  against - read from `axi sync --check`, the one read-only surface that
  reports it on the installed v1.48.0.
- When nothing binds the run it may still report work under way, but never a
  terminal verdict: `failed` and `done` become `unknown`, which is not itself
  an instruction to act.
- The coarse runs list stops at the branch's newest row, so a superseded run
  can no longer answer for the current one.
Generated ship briefs told the worker to invoke the /no-mistakes skill, which a
worker cannot reach: the skill lives in the firstmate home and loads for a
firstmate session, while a worker runs in an isolated project copy. Every brief
variant now names the CLI path instead.

Also narrows the accompanying "never report done: for work that has no PR" line
to the case where a validation run cannot start, resolving the contradiction
with the section 7 handoff. The captain approved that scoping (option a) and
separately approved removing the intermediate handoff altogether, filed as
fm-validation-self-start.

Landed locally on the captain's word; upstream PR 2566 stays open at
kunchenguid/firstmate with all 13 checks green.
fm-crew-state.sh reported a live, healthy validation run as failed. It matched
runs to a task by comparing the run's head against the worktree head, but a
running pipeline commits its fix rounds to the gate repository, so that head is
absent from the worktree's object store. The live row was rejected and an older
failed row at the unchanged worktree head answered instead - a confident wrong
verdict that routes firstmate into recovery and risks restarting healthy work.

Now uses ternary code identity bound to the run's submitted head, withholds a
terminal verdict when identity cannot be proven, and takes the newest matching
row. 7 new regression assertions.

Landed locally on the captain's word. Upstream PR 2569 stays open at
kunchenguid/firstmate; its checks are held pending the repository owner's
approval of outside-contributor workflow runs, which never arrived. Our own
pipeline completed review, test, document and lint with no findings.
… main

Firstmate's delivery path on a fork repository left tasks unable to land
where the running fleet actually executes. For ordinary projects,
no-mistakes tasks merge remotely via PR and sync down via fleet-sync. For
Firstmate's own repository, local main is authoritative for the fleet while
upstream PRs remain open outward contributions. Because fm-merge-local.sh
refused mode!=local-only tasks and fm-fleet-sync.sh diverged on upstream
origin/main, changes were stranded.

- Teach bin/fm-merge-local.sh to fast-forward local main for Firstmate's
  own repository tasks (where project is FM_ROOT/FM_HOME) in any mode.
- Teach bin/fm-pr-merge.sh to fast-forward local main when merging a
  Firstmate repository task remotely.
- Teach bin/fm-fleet-sync.sh to skip FM_ROOT/FM_HOME gracefully rather
  than falsely alarming STUCK on upstream origin/main.
- Teach bin/fm-teardown.sh to recognize work landed in the local default
  branch.
- Add docs/verification/fork-reconciliation.md with the empirical evidence
  and reconciliation plan for fork/main.
- Add tests/fm-merge-local.test.sh and expand fleet-sync, pr-merge, and
  teardown test suites with full RED-GREEN coverage.
Away-mode housekeeping treated every failed capture as a gone pane and
dropped the marker with no escalation. A redraw, timeout, or backend
hiccup then silently stopped watching a worker that was still there,
which is the failure this path exists to prevent.

Both the stale-wedge and pause-resurface sites now share
stale_window_recheck: retry the capture twice (0.4s apart) before
verdict, then ask fm_backend_agent_state. Only an authoritatively
missing endpoint is gone. A present dead shell is ordinary idle. Every
other state, including an unreadable or unverified probe, escalates and
keeps the marker on the same cadence because the watcher cannot
recapture an unreadable pane. target_exists is not used as a gone proof:
tmux can fall back to the active window, and Orca's check is itself a
capture.

Tests cover gone, unreadable-present (alive/unreadable/unverified),
retry-then-ordinary, and dead-is-not-gone at both call sites.
Remove the intermediate pre-PR done: handoff. The implementation worker
starts its own no-mistakes CLI run immediately after the commit, reports
working: when that run starts, and reports done: only with a PR.
Firstmate's own tasks could not land where the running fleet executes. On this
fork, local main is authoritative and upstream PRs are an outward courtesy, but
the local merge path refused any task that was not local-only and fleet sync
falsely alarmed on upstream origin/main, so finished work was stranded.

Teaches the local merge path, the PR merge path, fleet sync, and teardown to
recognize firstmate's own repository and its authoritative local main. Adds
docs/verification/fork-reconciliation.md plus a new merge-local test suite and
expanded fleet-sync, pr-merge and teardown coverage.

Landed locally on the captain's explicit word. Our own pipeline completed
review, test, document and lint with no findings. The upstream PR 2597 check
failure is the known fm-watcher-lock fork/execve flake, tracked separately as
fm-racy-watcher-lock-test.
A worker that finished writing code stopped and waited for firstmate to tell it
to start validation. That handoff is a message that can fail to land: one worker
sat finished and idle for eighty minutes today because the start message was
never submitted, and every task paid the round trip even when it worked.

The worker now starts its own no-mistakes run immediately after the
implementation commit, announces the start with a nonterminal working line, and
must report failed or blocked if the run dies mid-pipeline, so firstmate still
learns start and failure without a handoff. It may no longer claim done before a
PR exists. Review, tests, gates, ask-user escalation and merge authority are
untouched: the pipeline's own separate agent still does the reviewing, the author
still cannot answer its own ask-user finding, and --yes remains banned.

Landed locally on the captain's explicit word.
Task ids become free again after cleanup, but the previous task's data directory
was left on disk. Reusing the id then handed the new worker the old task's brief,
silently, and it would build the wrong thing.

Scaffolding now refuses a reused id while that directory still exists, rather
than writing over or reading through it.

Landed locally on the captain's explicit word.
While the captain is away, the background supervisor treated every failed screen
capture as proof the worker was gone, dropped its marker and silently stopped
watching it. A redraw, timeout or backend hiccup was enough. That is precisely
the failure the away-mode supervisor exists to prevent.

Both the stale-wedge and pause-resurface sites now retry the capture twice
before deciding, then ask the backend whether the endpoint actually exists. Only
an authoritatively missing endpoint counts as gone; a present dead shell is
ordinary idle; every other state, including unreadable or unverified, escalates
and keeps the marker. Endpoint presence alone is not accepted as proof, because
tmux can fall back to the active window and Orca's own check is a capture.

Landed locally on the captain's explicit word.
Session start printed data/captain.md and data/learnings.md whole, so the
startup memory surface grew with no read path that could refuse it: 35,526
estimated tokens against a 7,500-token budget on the reference home.

- bin/fm-memory-compile.sh compiles the bundle session start injects: a
  standing core, a catalog of every note, and the notes whose triggers match
  live fleet work, capped against config/startup-memory-budget. Core is never
  dropped, the catalog outranks every note, and a note that does not fit is
  skipped rather than ending selection.
- bin/fm-memory-migrate.sh splits a home's data/learnings.md into one atomic
  cited note per heading, publishes the catalog, and freezes plus archives the
  original before removing it.
- fm-session-start.sh injects the compiled bundle when data/memory/ exists and
  keeps the whole-file print when it does not, or when the compile fails.
- Two session-start fixtures forced a MISSING diagnostic by removing node from
  the fake bin, which proves nothing on a host that also ships /usr/bin/node.
  They now shadow gh-axi, which cannot exist outside the fake bin.

On the reference home the surface goes from 35,526 tokens to 7,490, and to
7,480 with four hot notes once the core is trimmed to its target size.
- Each compiler mode now accepts only its own flags, so `catalog --context`
  or `compile --dry-run` is a usage error instead of a silently ignored
  option that reads as a compile which simply matched nothing.
- Session start says so when it cannot create the temporary file for the
  compiled bundle, rather than falling back to the whole-file print with no
  word about why.
Trigger derivation stemmed a trailing `s` off every proper noun, so a heading
about `Windows` produced the trigger `window` - which then matched nothing,
because trigger matching is whole-token. The stopword test now tries the
singular while the trigger keeps its original spelling.

The possessive-stripping `\b` is a GNU sed extension that does nothing on BSD
sed. It was also redundant: the following substitution already splits
`Firstmate's` into `Firstmate` and a one-character `s` the length filter drops.
The network-partition fixture removed node from the fake bin to produce a
local-half diagnostic, which proves nothing on a host that also ships
/usr/bin/node: the base PATH still satisfies `command -v`, no MISSING line is
emitted, and the partition assertion fails for a reason that has nothing to do
with the partition. gh-axi cannot exist outside the fake bin, so the assertion
now means the same thing on every host.

Same change as the two session-start fixtures in this branch's first commit.
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.

2 participants