Skip to content

feat: add read-only Baby Menu quota fallback for stale quota-axi readings - #1175

Closed
19991-cmd wants to merge 17 commits into
kunchenguid:mainfrom
19991-cmd:fm/baby-menu-kiintiolahde
Closed

feat: add read-only Baby Menu quota fallback for stale quota-axi readings#1175
19991-cmd wants to merge 17 commits into
kunchenguid:mainfrom
19991-cmd:fm/baby-menu-kiintiolahde

Conversation

@19991-cmd

Copy link
Copy Markdown

Intent

Add an independent Baby Menu SQLite fallback for provider quota readings when quota-axi reports stale or auth_required. Normal fresh quota-axi behavior must remain unchanged. Read ~/.baby-menu/baby-menu.db strictly read-only and remain optional and failure-silent when Baby Menu, sqlite3, the database, or a usable row is unavailable. Every fallback reading must identify Baby Menu SQLite as its source and report snapshot age. Preserve Baby Menu window identifiers instead of claiming unverified equivalence with quota-axi windows, document that the singleton/current-result tables provide no consumption history, and keep the implementation to one small direct helper with no cache, background process, or extra layer. Cover fresh, fallback, missing/unopenable, and no-write behavior, and avoid exposing account identity from stored payloads.

What Changed

  • Adds bin/fm-quota-fallback.sh, a single direct helper that reads one aged Baby Menu quota snapshot for claude, codex, grok, or kimi only when the primary quota-axi status is stale or auth_required. Fresh readings never touch the database. Output is one JSON object marked source: "baby-menu-sqlite" with savedAt/ageSeconds, Baby Menu's own window identifiers preserved, historyAvailable: false, and quota-only fields so stored account identity is never printed. A missing sqlite3, database, table, row, or unreadable/future-dated timestamp exits 0 with no output. The database is opened read-only with PRAGMA query_only, and because Baby Menu stores it in WAL mode the open follows the writer: plain -readonly when a -wal/-shm sidecar exists, immutable=1 when neither does, so a closed-writer database is readable without the read creating sidecars; a sidecar appearing mid-read drops the reading.
  • Updates the dispatch contract and docs: AGENTS.md section 4 now names the helper as the only sanctioned supplementary source, counts its snapshot only when newer than the candidate's own primary refreshedAt, and keeps a candidate with neither a fresh primary nor a newer snapshot in the stop-and-report path. docs/configuration.md documents the invocation condition, the read-only/optional behavior, the age semantics, the observed Baby Menu 0.1.24 window identifiers, and the singleton/current-result tables' lack of consumption history; docs/architecture.md and docs/scripts.md are aligned.
  • Adds tests/fm-quota-fallback.test.sh covering fresh (no database access), fallback readings, missing/unopenable databases, and no-write behavior against a WAL fixture, and registers the new test plus bin/fm-quota-fallback.sh in bin/fm-test-run.sh's pure-contract-unit lane so the file cannot be silently skipped. The pipeline's Test phase passed both the new suite and --check-coverage, alongside live CLI runs against the real ~/.baby-menu/baby-menu.db; Review's remaining open note is an informational bound on the immutable=1 path (rollback-journal mode is unreachable today since that database is WAL).

Risk Assessment

✅ Low: Every finding from the prior two rounds is resolved and independently verified - the helper reads live, closed-writer, missing, unopenable, and read-only databases correctly on both the Apple and standard SQLite builds without creating or modifying any file, and the remaining note is a documented, non-reachable property rather than a defect.

Testing

Ran the two targeted suites covering the new helper and the dispatch-contract wording (all 18 checks pass) plus the runner's lane-coverage guard, then demonstrated the feature end-to-end as an operator would experience it by invoking bin/fm-quota-fallback.sh against the real ~/.baby-menu/baby-menu.db: a fresh primary status produced no output and never touched the database, stale and auth_required produced single JSON readings tagged source=baby-menu-sqlite with real ageSeconds, historyAvailable=false and Baby Menu's own window identifiers preserved (including Kimi's window-less auth_required case), a missing database and a stripped PATH both stayed silent with exit 0, and byte/mtime/sha comparison of the database plus its -wal/-shm sidecars before and after showed no writes; a payload-key comparison confirmed the stored accountEmail and accountBinding fields never appear in the emitted output. No failures or flakiness; this is a shell CLI change with no rendered UI surface, so the reviewer-visible evidence is CLI transcripts rather than screenshots.

Evidence: Live CLI transcript: fallback against the real Baby Menu database

--- 1. quota-axi reports a FRESH primary: helper stays out of the way --- $ bin/fm-quota-fallback.sh claude fresh (stdout: <empty>) exit=0 --- 2. state of the real database BEFORE any fallback read --- baby-menu.db 24576 bytes mtime=1785205242 sha=776eb4b58a32ccb8 baby-menu.db-wal 57712 bytes mtime=1785226540 sha=ecc1c17a912f89c6 baby-menu.db-shm 32768 bytes mtime=1785205246 sha=d73a3b693b322ffc --- 3. quota-axi reports STALE for claude --- $ bin/fm-quota-fallback.sh claude stale { "schemaVersion": 1, "provider": "claude", "primaryStatus": "stale", "source": "baby-menu-sqlite", "sourceTable": "claude_code_quota_snapshot", "savedAt": "2026-07-22T22:42:23.230Z", "ageSeconds": 466850, "historyAvailable": false, "snapshot": { "plan": "max", "upstreamSource": "oauth", "windows": [ {"id": "five_hour", "label": "session · 5h", "percentUsed": 52}, {"id": "seven_day", "label": "week", "percentUsed": 52} ], "credits": null, "errorCode": null } } --- 4. AUTH_REQUIRED for codex/grok/kimi: Baby Menu window identifiers preserved --- {"source":"baby-menu-sqlite","sourceTable":"codex_quota_snapshot","ageSeconds":453,"historyAvailable":false,"windows":["weekly"],"status":null} {"source":"baby-menu-sqlite","sourceTable":"grok_quota_snapshot","ageSeconds":454,"historyAvailable":false,"windows":["credits","product:Grok Build","product:Voice","product:Imagine"],"status":null} {"source":"baby-menu-sqlite","sourceTable":"kimi_quota_cache","ageSeconds":21863,"historyAvailable":false,"windows":[],"status":"auth_required"} --- 5. state of the real database AFTER four fallback reads --- baby-menu.db 24576 bytes mtime=1785205242 sha=776eb4b58a32ccb8 baby-menu.db-wal 57712 bytes mtime=1785226540 sha=ecc1c17a912f89c6 baby-menu.db-shm 32768 bytes mtime=1785205246 sha=d73a3b693b322ffc --- 6. missing database / absent sqlite3 --- $ bin/fm-quota-fallback.sh claude stale /nonexistent/baby-menu.db (stdout: <empty>) exit=0 $ PATH=<no sqlite3> bin/fm-quota-fallback.sh claude stale ~/.baby-menu/baby-menu.db (stdout: <empty>) exit=0

=== Firstmate Baby Menu quota fallback — live run against the operator's real ~/.baby-menu/baby-menu.db ===
run at 2026-07-28T08:23:12Z

--- 1. quota-axi reports a FRESH primary: helper stays out of the way (no output, exit 0) ---
$ bin/fm-quota-fallback.sh claude fresh
(stdout: <empty>)  exit=0

--- 2. state of the real database BEFORE any fallback read ---
  baby-menu.db                             24576 bytes  mtime=1785205242  sha=776eb4b58a32ccb8
  baby-menu.db-wal                         57712 bytes  mtime=1785226540  sha=ecc1c17a912f89c6
  baby-menu.db-shm                         32768 bytes  mtime=1785205246  sha=d73a3b693b322ffc

--- 3. quota-axi reports STALE for claude: one sourced, dated Baby Menu reading ---
$ bin/fm-quota-fallback.sh claude stale
{
  "schemaVersion": 1,
  "provider": "claude",
  "primaryStatus": "stale",
  "source": "baby-menu-sqlite",
  "sourceTable": "claude_code_quota_snapshot",
  "savedAt": "2026-07-22T22:42:23.230Z",
  "ageSeconds": 466850,
  "historyAvailable": false,
  "snapshot": {
    "status": null,
    "plan": "max",
    "upstreamSource": "oauth",
    "refreshedAt": "2026-07-22T22:42:23.230Z",
    "checkedAt": null,
    "stale": 0,
    "windows": [
      {
        "id": "five_hour",
        "label": "session · 5h",
        "percentUsed": 52,
        "resetAt": "2026-07-23T00:10:00.281006+00:00",
        "resetText": "resets in 1h 28m"
      },
      {
        "id": "seven_day",
        "label": "week",
        "percentUsed": 52,
        "resetAt": "2026-07-26T04:00:00.281030+00:00",
        "resetText": "resets in 3d 5h"
      }
    ],
    "credits": null,
    "errorCode": null
  }
}

--- 4. quota-axi reports AUTH_REQUIRED for codex/grok/kimi: Baby Menu window identifiers preserved as-is ---
$ bin/fm-quota-fallback.sh codex auth_required   | jq -c '{source,sourceTable,ageSeconds,historyAvailable,windows:[.snapshot.windows[]?.id],status:.snapshot.status}'
{"source":"baby-menu-sqlite","sourceTable":"codex_quota_snapshot","ageSeconds":453,"historyAvailable":false,"windows":["weekly"],"status":null}
$ bin/fm-quota-fallback.sh grok auth_required   | jq -c '{source,sourceTable,ageSeconds,historyAvailable,windows:[.snapshot.windows[]?.id],status:.snapshot.status}'
{"source":"baby-menu-sqlite","sourceTable":"grok_quota_snapshot","ageSeconds":454,"historyAvailable":false,"windows":["credits","product:Grok Build","product:Voice","product:Imagine"],"status":null}
$ bin/fm-quota-fallback.sh kimi auth_required   | jq -c '{source,sourceTable,ageSeconds,historyAvailable,windows:[.snapshot.windows[]?.id],status:.snapshot.status}'
{"source":"baby-menu-sqlite","sourceTable":"kimi_quota_cache","ageSeconds":21863,"historyAvailable":false,"windows":[],"status":"auth_required"}

--- 5. state of the real database AFTER four fallback reads (must be byte-identical, no new sidecars) ---
  baby-menu.db                             24576 bytes  mtime=1785205242  sha=776eb4b58a32ccb8
  baby-menu.db-wal                         57712 bytes  mtime=1785226540  sha=ecc1c17a912f89c6
  baby-menu.db-shm                         32768 bytes  mtime=1785205246  sha=d73a3b693b322ffc

--- 6. Baby Menu absent / database missing: silent, exit 0, primary reading stays the only evidence ---
$ bin/fm-quota-fallback.sh claude stale /nonexistent/baby-menu.db
(stdout: <empty>)  exit=0
$ PATH=<no sqlite3> bin/fm-quota-fallback.sh claude stale ~/.baby-menu/baby-menu.db
(stdout: <empty>)  exit=0
Evidence: Account-identity filtering verified against the real stored payloads

### Real Baby Menu database: keys present in the stored payloads (values redacted) claude_code_quota_snapshot plan, refreshedAt, source, stale, windows codex_quota_snapshot accountEmail, credits, plan, refreshedAt, source, stale, windows grok_quota_snapshot accountBinding, operation, period, refreshedAt, schemaVersion, source, sourceVersion, stale, windows kimi_quota_cache checkedAt, error, source, stale, status ### Keys the helper actually emits under .snapshot for the same rows claude checkedAt, credits, errorCode, plan, refreshedAt, stale, status, upstreamSource, windows codex checkedAt, credits, errorCode, plan, refreshedAt, stale, status, upstreamSource, windows grok checkedAt, credits, errorCode, plan, refreshedAt, stale, status, upstreamSource, windows kimi checkedAt, credits, errorCode, plan, refreshedAt, stale, status, upstreamSource, windows => accountEmail and accountBinding exist in the stored data and never appear in the emitted reading.

### Real Baby Menu database: identity keys present in the stored payloads (values redacted)
claude_code_quota_snapshot   raw payload keys: plan, refreshedAt, source, stale, windows
codex_quota_snapshot         raw payload keys: accountEmail, credits, plan, refreshedAt, source, stale, windows
grok_quota_snapshot          raw payload keys: accountBinding, operation, period, refreshedAt, schemaVersion, source, sourceVersion, stale, windows
kimi_quota_cache             raw payload keys: checkedAt, error, source, stale, status

### Keys the helper actually emits under .snapshot for the same rows
claude   checkedAt, credits, errorCode, plan, refreshedAt, stale, status, upstreamSource, windows
codex    checkedAt, credits, errorCode, plan, refreshedAt, stale, status, upstreamSource, windows
grok     checkedAt, credits, errorCode, plan, refreshedAt, stale, status, upstreamSource, windows
kimi     checkedAt, credits, errorCode, plan, refreshedAt, stale, status, upstreamSource, windows
Evidence: Targeted test run
$ ./bin/fm-test-run.sh tests/fm-quota-fallback.test.sh tests/fm-instruction-owners.test.sh
ok - fresh primary result bypasses the fallback unchanged
ok - stale and auth-required primaries expose sourced, aged Baby Menu snapshots
ok - an absent sqlite3 leaves the primary quota result as the only evidence
ok - future-dated and unparseable snapshot timestamps produce no reading
ok - a WAL database whose writer has closed is read without creating sidecars
ok - missing and unopenable Baby Menu databases leave primary behavior unchanged
ok - fallback reads a non-writable database without modifying it
FM_TEST_SUMMARY total=2 failed=0 skipped_gate=0 duration_ms=456

$ ./bin/fm-test-run.sh --check-coverage
FM_TEST_COVERAGE ok total=103 parallel=29 serial=64 herdr=10

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

🔧 **Rebase** - 1 issue found → auto-fixed ✅
  • ⚠️ .agents/skills/afk/SKILL.md - branch carries 12 commit(s) that exist on your local main branch but were never pushed to origin/main; rebasing would bundle this unrelated work (72 file(s)) into the PR:
  • f78d240 fix: classify docs/freellmapi-lane.md in documentation audience inventory
  • 3d33233 feat(fm-lock): use flock(2) for the short session-lock mutexes
  • 68f9862 docs(agents): slim always-loaded contract (S1) and formalize light path (S4)
  • c976be6 merge: session-lock reclaim hardening onto current main
  • dee5ae6 merge: fm-freellmapi lane + tests + docs
  • b74c9b1 merge: gitignore env.bak protection + test
  • 6ecbc14 fix: make fm-brief.sh parse under macOS bash 3.2
  • 3479f37 no-mistakes(document): note .env key backups in CONTRIBUTING ignored-file contract
  • dafb513 no-mistakes(review): harden gitignore env-bak test hermeticity and changed-path selection
  • dec5868 fix: ignore .env.bak key backups in shared gitignore
  • 7dacfa1 fix: align FreeLLMAPI lane claims with real secret and status behavior
  • f562cad feat: add pinned localhost-only FreeLLMAPI lane tooling

Push main to origin, or rebase your branch onto origin/main, before gating.

🔧 Fix applied.
✅ Re-checked - no issues remain.

⚠️ **Review** - 1 info
  • ⚠️ AGENTS.md:138 - The new clause "then evaluate every configured candidate against the available explicitly sourced output" replaces "against that current output" and weakens the fail-closed rule still stated on line 139 ("if any harness/model/provider relationship, applicable quota data, or interpretation cannot be established, stop and report that candidate instead of omitting it, guessing, falling back, or calling the result quota-informed"). When a candidate's primary status is stale/auth_required AND the fallback yields nothing (no sqlite3, no DB, no row - all silent exit 0), the only remaining evidence is a stale primary; line 138 now reads as "evaluate it anyway" while line 139 demands stop-and-report, and the same paragraph both sanctions and forbids "falling back". Verified reachable: quota-axi currently reports claude=stale, kimi=auth_required on this machine, and the helper is silent whenever Baby Menu is absent. Recommend spelling out in line 138 what happens when the fallback produces nothing, so the two sentences cannot be read against each other.
  • ⚠️ AGENTS.md:138 - The contract tells firstmate to evaluate candidates "against the available explicitly sourced output" and "choose the candidate with the most real headroom", but gives no precedence rule between the two aged readings it now permits. Concretely observed here: quota-axi's stale Claude reading has refreshedAt 2026-07-26T01:16:36Z, while bin/fm-quota-fallback.sh returns savedAt 2026-07-22T22:42:23Z with ageSeconds 460744 - the fallback is three days OLDER than the stale primary it supplements, yet carries the same five_hour/seven_day window ids and so looks directly substitutable. Nothing in AGENTS.md or the helper compares the two ages, so headroom can be decided on the older numbers. The helper correctly reports age (intent satisfied); what is missing is the consuming rule, e.g. that a Baby Menu snapshot only adds evidence when it is newer than the primary's refreshedAt.
  • ℹ️ bin/fm-quota-fallback.sh:43 - age_seconds is unguarded in two directions. (1) If saved_at is ever not strftime-parseable (format drift in a third-party DB this helper does not own), strftime returns NULL, SQLite's two-argument max(0, NULL) returns NULL, and the helper emits a full reading with "ageSeconds": null - contradicting both this file's header ("or valid snapshot also produces no output") and the requirement that every fallback reading report snapshot age. (2) max(0, ...) clamps negative ages to 0, so a future-dated or non-UTC saved_at (clock skew, local-time storage) presents an arbitrarily old snapshot as brand new - the most dangerous direction for a headroom decision. I confirmed the current Baby Menu 0.1.24 values parse correctly (ISO-8601 with Z for the three snapshot tables, epoch-ms for kimi), so this is drift/skew hardening, not a live break. Suggested fix: filter the outer SELECT to rows where the computed age is a non-negative non-NULL integer, keeping the documented silent-no-output behavior.
  • ℹ️ docs/scripts.md:88 - bin/fm-quota-fallback.sh is not listed in the bin/ toolbelt index, even though AGENTS.md section 4 now instructs firstmate to invoke it by path at profile-array intake - exactly the "the first mate drives these" class this table documents. The other unlisted scripts are libs and internal checks (fm-*-lib.sh, fm-lint.sh, fm-doc-audience-check.sh), not agent-invoked entrypoints. Add one row next to fm-harness.sh/fm-spawn.sh.
  • ℹ️ tests/fm-quota-fallback.test.sh:63 - Two documented behaviors are uncovered while the harness for them already exists in this file. (1) "sqlite3 optional": the fresh test builds a fakebin to prove sqlite3 is never invoked, but no test runs a stale status with sqlite3 absent from PATH, so the command -v sqlite3 || exit 0 guard is untested. (2) The fixture DB is created in the default rollback-journal mode, while the deployed Baby Menu DB is WAL (baby-menu.db-wal/-shm exist; pragma journal_mode = wal), so test_database_is_never_written does not exercise the journal mode that actually ships. I checked the WAL case manually and the helper left baby-menu.db, -wal, and -shm mtimes unchanged, so the claim holds - the test just does not guarantee it. Adding PRAGMA journal_mode=WAL; to make_fixture would close the gap.

🔧 Fix: harden quota fallback age validation and dispatch contract
3 issues (1 warning, 2 infos) still open:

  • ⚠️ bin/fm-quota-fallback.sh:113 - sqlite3 -readonly cannot open a WAL-mode database whose -shm/-wal sidecars are absent, and ~/.baby-menu/baby-menu.db is WAL mode (verified: pragma journal_mode = wal). Verified on this machine: the default sqlite3 is Apple's /usr/bin/sqlite3 3.51.0apl, and against a WAL database with no sidecars it returns Error: in prepare, unable to open database file (14) (reproduced 4x, fresh dirs); the helper then emits nothing with exit 0, so the whole fallback silently disappears. Also verified: a standard (non-Apple) SQLite deletes -wal/-shm when the last connection closes cleanly (Python libsqlite3 3.50.4 and the brew 3.53.2 CLI both left only the main file), while Apple's build keeps them - and a standard build's -readonly open of a sidecar-less WAL database CREATES &lt;db&gt;-shm and a 0-byte &lt;db&gt;-wal next to the database (observed on two copies), which is a filesystem write inside ~/.baby-menu/ and sits awkwardly against the criterion "Read ~/.baby-menu/baby-menu.db strictly read-only" and the no-write test. Not verified (I did not quit the app): whether Baby Menu, which bundles its own standard SQLite, leaves the database sidecar-less after a clean quit - if it does, the fallback is dead on macOS exactly when Baby Menu is closed, which is when a cached snapshot matters most. Right now the sidecars exist (49KB -wal from 05:55), so the feature works today. One cheap remedy I verified: when neither -wal nor -shm exists (i.e. no active writer), retry as file:&lt;db&gt;?immutable=1, which read the same sidecar-less database successfully with Apple's sqlite3 and created no files; note immutable must NOT be used while a writer is active. Documenting the limitation instead is also a legitimate choice.
  • ℹ️ tests/fm-quota-fallback.test.sh:161 - The new WAL guard is environment-dependent rather than a guarantee. wal_before/wal_after are only populated when $DB-wal exists after make_fixture: on Apple's SQLite the sidecars persist so the comparison is real, but on a standard build the writer's clean close deletes them, leaving both values '' so [ &#34;$wal_before&#34; = &#34;$wal_after&#34; ] can never fail - the test reads as a WAL guarantee on CI while asserting nothing there. Separately, file_signature is size:mtime:sha256, which cannot see metadata changes: I observed that after chmod 444 db a read-only open propagated the database's mode to the -wal file (644 -> 444), so a permission-level side effect on the sidecar would pass this check silently. Asserting on sidecar existence/absence around the read (e.g. assert_absent "$DB-wal" / "$DB-shm" when they did not exist before) would test the property that actually matters and behave the same on both builds.
  • ℹ️ AGENTS.md:139 - Noting the operational consequence of the added rule, since it is a real behavior change rather than only a clarification: "treat a candidate left with neither a fresh primary nor a newer snapshot as one whose quota data cannot be established" combined with line 140's stop-and-report means array dispatch now halts in cases it previously would have resolved. Concretely, with today's state on this machine (quota-axi claude=stale with refreshedAt 2026-07-26T01:16:36Z, Baby Menu claude snapshot savedAt 2026-07-22T22:42:23Z - older, so it adds no evidence), a profile array containing a claude candidate must stop and report instead of dispatching. That is a coherent fail-closed reading and matches what was asked for in review; flagging only so the stricter halting is a deliberate choice rather than a surprise.

🔧 Fix: read closed-writer WAL quota database without creating sidecars
1 info still open:

  • ℹ️ bin/fm-quota-fallback.sh:140 - Noting two bounded properties of the new immutable path, both consistent with the header's documented scope - no action needed unless Baby Menu's storage changes. (1) immutable=1 disables SQLite's locking and hot-journal recovery, and has_writer_sidecar deliberately tests only -wal/-shm, so a database in rollback-journal mode would always take the immutable path and ignore a hot -journal. I built that case (killed a writer mid-BEGIN IMMEDIATE, leaving h.db-journal) and the helper still returned the correct committed row, matching a plain read-only open, so this is a property rather than a demonstrated defect; it is also unreachable today since ~/.baby-menu/baby-menu.db is WAL (verified). If that ever changes, adding [ -e &#34;$database-journal&#34; ] to has_writer_sidecar keeps the guard true to its own "the open follows the writer" description. (2) The post-read sidecar re-check catches a writer that arrives and stays, but not one that opens, commits, checkpoints and cleans up entirely inside the few-millisecond read window; Baby Menu writes seconds apart (observed refreshes 274 s apart during this review), so the window is not material.
✅ **Test** - passed

✅ No issues found.

  • ./bin/fm-test-run.sh tests/fm-quota-fallback.test.sh tests/fm-instruction-owners.test.sh — 7 fallback behavior tests plus the dispatch-contract owner test, all passing
  • ./bin/fm-test-run.sh --check-coverage — confirms the new tests/fm-quota-fallback.test.sh is claimed by a CI lane and cannot be silently skipped
  • Live CLI run: bin/fm-quota-fallback.sh claude fresh against the real ~/.baby-menu/baby-menu.db — empty stdout, exit 0, no database access
  • Live CLI run: bin/fm-quota-fallback.sh claude stale and bin/fm-quota-fallback.sh {codex,grok,kimi} auth_required against the real database — sourced, dated readings with preserved Baby Menu window ids (five_hour/seven_day, weekly, credits + product:*, and Kimi's window-less auth_required status)
  • Manual immutability check: size/mtime/sha256 of ~/.baby-menu/baby-menu.db, -wal and -shm captured before and after four fallback reads — identical, no sidecars created or removed
  • Live CLI run: bin/fm-quota-fallback.sh claude stale /nonexistent/baby-menu.db and the same command with sqlite3 removed from PATH — empty stdout, exit 0
  • Manual identity-leak check: compared json_each key sets of the stored payloads in the real database against the keys the helper emits under .snapshot for all four providers
  • git status --porcelain — no transient artifacts left in the worktree
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

19991-cmd and others added 17 commits July 25, 2026 06:35
Add bin/fm-freellmapi.sh, the single owner of the fleet's vetted
FreeLLMAPI install: exact-commit pin (526c8634), risk-acknowledged
install, secure ENCRYPTION_KEY generation, loopback-only start with
mandatory bind verification, .env-sourced key seeding that keeps
secret values out of stdout, logs, and argv, and guarded stop.

The lane is opt-in tooling only; docs/freellmapi-lane.md owns the
usage policy, known risks, and removal, and nothing routes to the
lane automatically. Behavior tests use PATH shims and invented fake
keys only.
External review found two overclaims: process-listing immunity (ENCRYPTION_KEY
still lives in process env) and status stopping non-loopback binds (status only
warned). State the honest secret boundary, document xtrace leak risk, and make
header/docs match status-alerts-only behavior; add a regression test.
lisaa-avain.sh writes .env.bak-* copies that hold API keys. Track
.gitignore rules for .env.bak and .env.bak-* next to .env, and add a
hermetic regression test so a plain git add -A cannot stage them.
Replace `VAR=$(cat <<EOF ...)` multi-line captures with statement-level
`read -r -d '' VAR <<EOF` so bash 3.2 no longer chokes on apostrophes in
Definition-of-done and Herdr declaration bodies (issue kunchenguid#958). Scaffold text
is unchanged. Add an explicit /bin/bash parse-and-run regression test.
# Conflicts:
#	bin/fm-test-run.sh
Integrate fm/lukko-korjauskierros over kunchenguid's kunchenguid#996/kunchenguid#997:
- typed lock outcomes (OWNED, LIVE_OTHER, STALE_RECLAIMABLE,
  IDENTITY_UNAVAILABLE, RECLAIM_BUSY) with LOCK_RESULT= on every path,
  atomic stale-owner reclaim, and the Codex thread-identity fallback
- reclaim mutex records owner pid and started timestamp; a live owner is
  never overridden and only a provably abandoned mutex is recovered
- keep main's shared session-lock lib identity (kimi included), the
  .lock.acquire acquisition serialization, the state-write probe, the
  non-regular-file lock refusal, and publish read-back verification
- session start and supervision instructions withhold mutation on every
  non-owned outcome while keeping the atomic reclaim path offered
- make the concurrent-winner test bash 3.2 compatible: contenders run as
  separate bash processes because macOS bash has no BASHPID
- update branch test assertions to the current Stop-hook-owned Claude
  protocol line and the typed non-mutating banners
- drop the TILANNE.md scratch note
…th (S4)

Move opt-in detail out of the always-loaded AGENTS.md to its canonical
owners and leave one-line owner pointers behind:

- section 2 config knob annotations -> docs/configuration.md sections and
  secondmate-provisioning (inheritance contract)
- section 2 state artifact annotations (turn-ended, grok token, meta
  fields, herdr presentation, check-trust, pr-poll records, PR-check
  migration artifacts, X-mode artifacts, pending-replies, wake-queue
  format, watcher/guard/sub-supervisor internals) -> producing script
  headers, docs/configuration.md, docs/architecture.md,
  docs/herdr-backend.md, and bootstrap-diagnostics, keeping a compact
  core tree plus a never-touch rule for unlisted state entries
- section 3 bootstrap sweep enumeration and liveness sweep detail ->
  bin/fm-bootstrap.sh header (already the owner)
- section 6 secondmate routing/reconciliation duplicates of sections 5
  and 7 -> deduplicated to the section 7 and 5 owners
- section 14 duplicate fmx-respond trigger and completion-follow-up rule
  -> section 13 trigger and section 8 supervision rule

Add the section 7 light path for bounded work: at most 3 files,
local-only, no concurrent task on the project, no new public API,
dependency, or abstraction, nothing destructive, irreversible, or
security-sensitive; minimal brief, same spawn isolation, supervision,
and merge authority.

Record the previously AGENTS-only watch-triage.log deletion-safety fact
in docs/architecture.md so no moved fact is lost.

AGENTS.md: 503 -> 473 lines, 53584 -> 47989 bytes; the section 2
layout block alone: 61 -> 30 lines.

Safety boundaries are unchanged: every section 1 hard rule, merge
authority, unlanded-work, discard, isolation, lock, ask-user, and
X-mode consent boundary is retained verbatim or strengthened in its
single owner; tests/fm-instruction-owners.test.sh and the related
contract suites pass unchanged except for the section 8 sentence now
carrying the exact completion-follow-up phrase previously in section 14.
Prefer a kernel flock(2) on state/.lock.acquire and state/.lock-reclaim,
taken through a small perl helper (macOS has flock(2) but no flock(1) CLI;
perl is already a soft dependency of bin/fm-wake-lib.sh). The kernel
releases the lock when the holding process dies, removing the orphaned
reclaim-mutex jam class at its root; a leftover mutex file is unlocked and
harmless. A post-flock inode and regular-file recheck closes the classic
unlink-and-recreate sharing race.

The mkdir/symlink locks remain the complete fallback when the helper is
unavailable (FM_LOCK_NO_FLOCK=1 forces this) and the recovery path for a
legacy directory-form mutex already on disk, so no new hard dependency is
added. The main session lock state/.lock stays a durable on-disk identity
and is deliberately untouched. All typed outcomes (OWNED, LIVE_OTHER,
STALE_RECLAIMABLE, IDENTITY_UNAVAILABLE, RECLAIM_BUSY) are preserved.

Tests: flock mutex is busy while held and auto-released on holder death;
crash-leftover mutex files never jam; the mkdir fallback still recovers
abandoned mutexes and keeps typed results without the helper; concurrent
acquisitions still admit exactly one winner.
…tory

Regression from the fm-freellmapi lane merge (dee5ae6): the lane policy doc
was added without an audience entry, failing fm-documentation-audiences.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…chenguid#1)

* docs(session-close): conditional close instruction with an explicit handoff path

Session closing previously had no instruction at all: only the recovery
side existed (AGENTS.md sections 3 and 5), so a session ending with work
under way had no defined handoff and relied on the model improvising.

Add a new user-invocable internal skill .agents/skills/session-close that
branches on whether work is under way: an empty fleet closes straight
away, while live work requires reconciling durable records, filing
unanswered captain decisions as held work items, a /stow sweep, leaving
running work and monitoring untouched, and a closing summary that tells
the captain what is under way, what awaits them, and how the next
session resumes. It also states the lock facts (automatic stale reclaim
for numeric owners, captain confirmation for codex-thread owners) and
the close-versus-/afk distinction.

Owner choice: neither stow nor afk is the right home. stow is a
knowledge-routing sweep (and becomes one step of the handoff), and afk
is the opposite contract - supervision stays live there, while closing
ends it. A dedicated skill keeps one owner, with only a three-line
trigger stub in AGENTS.md section 8 beside the away-mode stub it
contrasts with, per the inline-stub pattern.

No lock, watcher, or supervision machinery behavior changes; this is
instruction-surface work only. The audience inventory and README
built-in-skill table gain matching entries.

Verified: bin/fm-doc-audience-check.sh ok; tests
fm-documentation-audiences, fm-instruction-owners, and
fm-captain-translation-contract pass.

* no-mistakes(review): harden session-close wording, lane pid guard, reclaim fallback

* no-mistakes(document): verify session-close doc ownership; no stale facts found

* no-mistakes(review): scope close branch to ordinary tasks; handle active away mode

* no-mistakes(review): defer away-mode exit at close to the afk return owner

* no-mistakes(document): note away-mode return in README session-close row

* no-mistakes(review): name pre-upgrade lane pid, gate catch-up promise, /session-close trigger

* no-mistakes(review): translate close-summary gate wording, add section 9 pointer
@19991-cmd
19991-cmd force-pushed the fm/baby-menu-kiintiolahde branch from 1197b9d to 19902c7 Compare July 28, 2026 08:31
@kunchenguid

Copy link
Copy Markdown
Owner

Automated reminder: thanks for the PR! This branch currently has a merge conflict with the base branch.

When you get a chance, please rebase onto (or merge) the latest base branch, resolve the conflict, and push. After that, checks will re-run and the PR will get looked at again.

Noted for firstmate#1175 at 19902c79.

@19991-cmd 19991-cmd closed this Jul 28, 2026
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