Skip to content

Session currency notice: zero-token due-file writer and daily user timer (unit A2) - #539

Merged
seathatflowsinourveins merged 11 commits into
mainfrom
claude/sota-defaults-a2-20260930
Sep 30, 2026
Merged

seathatflowsinourveins merged 11 commits into
mainfrom
claude/sota-defaults-a2-20260930

Conversation

@seathatflowsinourveins

Copy link
Copy Markdown
Owner

Scope

  • What this PR changes: a daily user timer (stack-currency.timer) runs scripts/currency_due.py, which aggregates four zero-token currency counts (pins behind, stale receipts, due layers, reopen triggers) from this checkout's read-only checks and writes a small mode-0600 due-file only while something is due (removing it otherwise). The one-line SessionStart notice that prints it ships in a separate frozen-surface unit (F2). Decision record and the amended startup rule in docs/token-practice.md.
  • Base commit: 11227bfd (origin/main at rebase)
  • Lane: lane:foundation
  • Owned paths touched: scripts/currency_due.py, tests/test_currency_due.py, adoption/templates/systemd/stack-currency.{service,timer}, adoption/lifecycle.md, docs/token-practice.md, docs/decisions/2026-09-30-session-currency-notice.md; manifests/evidence.json (re-registration only, last commit).
  • Frozen surfaces (Gate A): none touched. The timer is not enabled on any host by this PR; the host enable waits for the Gate A owner's go and is stopped during every run window with no due-file present at window start.

SOTA sources

Evidence-class table

Claim Evidence class Command / receipt
The three review findings hold against source source_review runtime_skill_freshness.py:77-81, adoption_status.py:1258-1263
Incomplete check keeps the file; invalid pin counts; notice command reproduces the run; ExecStart flags equal the command; malformed fields exit 2 synthetic tests/test_currency_due.py: 43 tests; 15 of the 18 new or changed fail on the unrepaired script (19 failures, 13 errors), all pass after
Tests catch the bugs they target local_integration 13 patched mutants, each killed by its test
This checkout's real checks run under 5 s local_integration time python3 scripts/currency_due.py --dry-run --json: exit 0, 1.5 s; counts 1 pin behind / 7 stale receipts / 12 reopen triggers
Units parse and verify local_integration (syntactic) systemd-analyze --user verify on units rendered with the header's sed recipe: exit 0, no output; broken copies exit 1
Repository gates local_integration python3 scripts/validate.py passed; 170 tests OK (2 pre-existing environment skips); three registry tests OK

Not tested: a real gh api call, the timer on a live user manager, the SessionStart hook (unit F2).

Local commands run

$ HOME=<scratch> XDG_CONFIG_HOME=<scratch> TMPDIR=/dev/shm/ntci-tmp python3 -m unittest tests.test_currency_due tests.test_receipt_staleness tests.test_saturation_ledger tests.test_adoption_docs_consistency
Ran 170 tests   OK (skipped=2)
$ python3 scripts/validate.py
{"components": 69, "hashed_files": 8515, "profiles": 4, "receipts": 176, "status": "passed"}
$ time python3 scripts/currency_due.py --dry-run --json
exit 0, real 1.5 s
$ systemd-analyze --user verify <rendered stack-currency.service> <stack-currency.timer>
exit 0

Decision record

docs/decisions/2026-09-30-session-currency-notice.md (alternatives: UserPromptSubmit hook, SessionStart running the checks, weekly CI only; overturn: a measured cost above the 60-token gate or a missed due item).

Host evidence

No files under evidence/hosts/ changed.

Checklist

  • No GitHub Actions changed.
  • No workflows changed.
  • No secrets printed, logged or committed.
  • No paid hosting, subscription or billing surface.
  • Peer-owned untracked files and worktrees preserved.

🤖 Generated with Claude Code

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Sep 30, 2026
@seathatflowsinourveins

Copy link
Copy Markdown
Owner Author

Cross-family review (GPT-6 through the OmniRoute gateway, read-only). Note: the review diff was taken against the unit base e45328d after the branch had been rebuilt on 11227bf, so its "high" scope finding (346/327 files outside the allowlist, manifests/stack.json) is an artifact of the stale review base, not a change in this PR (git diff origin/main...HEAD is the real scope). The medium/low findings stand and are repaired before merge; a re-review against the correct base follows.

high, manifests/stack.json:1905, The supplied diff changes 327 files outside A2’s allowlist, including unrelated OmniRoute configuration, OpenHands dependencies, workflow code, and the explicitly restricted stack manifest; fix: regenerate an A2-only diff containing its deliverables and necessary evidence/checksum registrations, leaving other lane changes in separate reviews.

medium, scripts/currency_due.py:190, The notice’s details command depends on the caller’s working directory and discards `--root`: outside the stack checkout it exits 2, and inside another checkout it inspects different inputs; the reproduction tests conceal this by reinjecting the fixture root; fix: emit a checkout-aware command that preserves the inspected root within the 160-character limit, and test the literal command from another working directory.

reason: The review includes substantial unauthorized scope, and the notice’s advertised command does not reliably inspect the checkout that generated it.
verdict: needs_changes

@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/sota-defaults-a2-20260930 branch from 94d3a99 to eca8a93 Compare September 30, 2026 16:29
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
… path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/sota-defaults-a2-20260930 branch from eca8a93 to 3832223 Compare September 30, 2026 16:29
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
… path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins

Copy link
Copy Markdown
Owner Author

New head b1267e44 (repair 2, cross-family review round 2). The history was rebuilt on origin/main 11227bf so that manifests/evidence.json changes only in the last commit; the tree is the reviewed tree plus the repair below.

  • Medium (scripts/currency_due.py, the notice's details command depended on the cwd and dropped --root): fixed. The command that ends summary_line now names the inspected checkout's own copy of the script by its path, written as ~/... under the home directory (every shell expands an unquoted leading ~; a path that would need quoting stays absolute), so the command works from whatever project a session starts in, which is where the user-scope SessionStart hook prints the line. A checkout without the script is inspected by this script with --root naming it. The cwd-relative form remains only as the last resort when the absolute command would leave the counts fewer than 12 characters in the 160-character line. report_options() still repeats --network and a non-default --sweep-cadence-days.
  • High (327 files outside the allowlist): not a change in this PR. That review diffed against a stale base after the rebase onto 11227bf, so it saw main's own commits; the PR diff against its merge-base is the A2 file set only (the review helper now refuses a base that is not the merge-base).
  • Tests: tests/test_currency_due.py reproduces the notice's command as a separate process from an unrelated working directory and compares the counts, checks the --root form for a checkout without the script, the ~/ rule (with and without a path that needs quoting), and the short-form fallback for an over-long checkout path. Failing first: against the previous head the new and changed tests fail (6 failures, 3 errors in tests.test_currency_due); on this head the module passes under two layouts (fixtures outside HOME, and fixtures under HOME so that the ~/ form is exercised).
  • Record: docs/decisions/2026-09-30-session-currency-notice.md documents the path rule and the fallback.

Checks on this head (fixtures under a scratch TMPDIR, HOME and XDG dirs): python3 -m unittest tests.test_currency_due tests.test_receipt_staleness tests.test_saturation_ledger tests.test_adoption_docs_consistency OK (2 skips); python3 scripts/validate.py passed; the three registry tests OK (pre-push); privacy scan of the added lines 0. On a checkout at ~/code/native-agent-stack the line with three nonzero counts is 157 characters with the full counts.

🤖 Generated with Claude Code

@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/sota-defaults-a2-20260930 branch from 3832223 to b1267e4 Compare September 30, 2026 16:32
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
… path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…o the due-file's own path (review round 3 of #539)

When the absolute command leaves the counts no room in the 160-character line, the line now ends with the path of
the due-file itself instead of a cwd-relative command. The document gains root (the inspected checkout) and
details_command (the full command), so a reader of the file runs the right checkout from anywhere. Tests run the
literal command or the pointed file's command as a process from an unrelated working directory for the primary,
--root and long-path cases, under fixtures outside and inside the home directory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins

Copy link
Copy Markdown
Owner Author

New head ed3c45af (repair 3, GPT-6 cross-family review round 3). History rebuilt on origin/main 11227bf; manifests/evidence.json only in the last commit; the tree is the previous head plus the repair below.

  • Medium (the long-path fallback printed the cwd-relative command): fixed, no cwd-relative form remains. When the absolute command would leave the counts fewer than 12 characters in the 160-character line, the line now ends with the path of the due-file itself (~/.local/state/native-agent-stack/currency-due.json by default). The document gains root (the inspected checkout) and details_command (the full command), so a reader of the pointed file runs the right checkout from anywhere; the primary form (python3 ~/<checkout>/scripts/currency_due.py --dry-run) and the --root form are unchanged.
  • Tests (tests/test_currency_due.py): the literal emitted command, or the pointed file's details_command, is run as a separate process from an unrelated working directory for the primary, --root and long-path cases, under fixtures outside and inside the home directory (so the ~/ form is exercised, with word-initial tildes expanded as a shell would). Failing first against the previous head: 1 failure, 5 errors in DetailsCommandTests.
  • Record: the pointer rule, the two new document keys, and the stack-currency launcher noted as the host-side way to a short command (not part of this change).

Checks on this head: python3 -m unittest tests.test_currency_due tests.test_receipt_staleness tests.test_saturation_ledger tests.test_adoption_docs_consistency 175 tests OK (2 skips), tests.test_currency_due 48 OK under both layouts; python3 scripts/validate.py passed; the three registry tests OK (pre-push); privacy scan of the added lines 0. From this worktree (a 125-character checkout path) the real dry run prints a 136-character line ending with the due-file path.

🤖 Generated with Claude Code

@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/sota-defaults-a2-20260930 branch from b1267e4 to ed3c45a Compare September 30, 2026 16:45
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
… path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…o the due-file's own path (review round 3 of #539)

When the absolute command leaves the counts no room in the 160-character line, the line now ends with the path of
the due-file itself instead of a cwd-relative command. The document gains root (the inspected checkout) and
details_command (the full command), so a reader of the file runs the right checkout from anywhere. Tests run the
literal command or the pointed file's command as a process from an unrelated working directory for the primary,
--root and long-path cases, under fixtures outside and inside the home directory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…`, and the line length is enforced (review round 4 of #539)

When the absolute command leaves the counts no room, the line now ends with `cat <due-file>`, a short command that
prints the document (root and details_command included), and with a constant pointer when a state-directory path
is too long even for that; the writer refuses to emit a line over 160 characters. Tests run the literal printed
command from an unrelated directory for the primary, --root and long-path cases, and cover a 140-character
state-directory basename and a long XDG_STATE_HOME.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/sota-defaults-a2-20260930 branch from ed3c45a to d2e2fbf Compare September 30, 2026 17:03
@seathatflowsinourveins

Copy link
Copy Markdown
Owner Author

New head d2e2fbf7 (repair 4, GPT-6 cross-family review round 4). History rebuilt on origin/main 11227bf; manifests/evidence.json only in the last commit; the tree is the previous head plus the repair below.

  • Medium (the long-path fallback was a bare file path, not a runnable command): fixed. When the absolute command would leave the counts fewer than 12 characters, the line now ends with cat <due-file> (cat ~/.local/state/native-agent-stack/currency-due.json by default): a short runnable command that prints the document, whose details_command and root fields carry the full command and the checkout. The primary and --root forms are unchanged; nothing cwd-relative is ever printed.
  • Medium (the fallback did not bound the line): fixed. After each fallback the length is checked again: a state-directory path too long even for cat gives way to the constant currency-due.json in the state directory, and the writer refuses (exit 2) rather than emit a line over 160 characters. Regression tests cover a 140-character --state-dir basename and a long XDG_STATE_HOME (the writer exits 0, writes the file, the line stays within 160).
  • Tests now run the literal printed command as a process from an unrelated working directory in every case: the primary form, the --root form, and cat <due-file> for the long-path case (then the document's details_command from the same directory). Failing first against the previous head: 4 failures, 1 error in DetailsCommandTests.

Checks on this head: python3 -m unittest tests.test_currency_due tests.test_receipt_staleness tests.test_saturation_ledger tests.test_adoption_docs_consistency 176 tests OK (2 skips); tests.test_currency_due 49 OK under fixtures outside and inside HOME; python3 scripts/validate.py passed; the three registry tests OK (pre-push); privacy scan of the added lines 0. From this worktree (a 125-character checkout path) the real notice is a 140-character line ending with cat <due-file>, and that command run from / prints the document.

🤖 Generated with Claude Code

seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
… path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…o the due-file's own path (review round 3 of #539)

When the absolute command leaves the counts no room in the 160-character line, the line now ends with the path of
the due-file itself instead of a cwd-relative command. The document gains root (the inspected checkout) and
details_command (the full command), so a reader of the file runs the right checkout from anywhere. Tests run the
literal command or the pointed file's command as a process from an unrelated working directory for the primary,
--root and long-path cases, under fixtures outside and inside the home directory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/sota-defaults-a2-20260930 branch from d2e2fbf to 747d6af Compare September 30, 2026 17:23
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…`, and the line length is enforced (review round 4 of #539)

When the absolute command leaves the counts no room, the line now ends with `cat <due-file>`, a short command that
prints the document (root and details_command included), and with a constant pointer when a state-directory path
is too long even for that; the writer refuses to emit a line over 160 characters. Tests run the literal printed
command from an unrelated directory for the primary, --root and long-path cases, and cover a 140-character
state-directory basename and a long XDG_STATE_HOME.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…olic XDG pointer, over-long --state-dir refused (review round 5 of #539)

The constant last-resort text is gone. When the resolved due-file path is too long for `cat <path>` and the state
directory came from XDG_STATE_HOME, the line ends with `cat "$XDG_STATE_HOME"/native-agent-stack/currency-due.json`,
which the session that prints the line resolves with the variable the hook used; an explicit --state-dir too long
for any runnable pointer is refused as a usage error before the checks run. Tests run the literal printed command
from an unrelated directory in every case, including the symbolic pointer with the variable inherited.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins

Copy link
Copy Markdown
Owner Author

New head 747d6af0 (repair 5, GPT-6 cross-family review round 5). History rebuilt on origin/main 11227bf; manifests/evidence.json only in the last commit; the tree is the previous head plus the repair below.

  • Medium (a long checkout plus a long state directory still produced non-executable text): fixed; every emitted line now ends with a runnable command. The constant last-resort text is gone. When the resolved due-file path is too long for cat <path> and the state directory came from XDG_STATE_HOME, the line ends with cat "$XDG_STATE_HOME"/native-agent-stack/currency-due.json, which the session that prints the line resolves with the same variable the SessionStart hook used to find the file. An explicit --state-dir too long for any runnable pointer is refused as a usage error (exit 2, nothing written, no check run) before the checks start; the unit passes none. The primary form, the --root form and cat <path> are unchanged, and the writer still refuses rather than emit a line over 160 characters.
  • Tests run the literal printed command as a process from an unrelated working directory in every case: the primary form, the --root form, cat <path>, and the symbolic pointer with a 140-character XDG_STATE_HOME basename (the process inherits the variable and passes no --state-dir); the over-long explicit --state-dir is asserted refused up front. Failing first against the previous head: 1 failure, 2 errors in DetailsCommandTests.

Checks on this head: python3 -m unittest tests.test_currency_due tests.test_receipt_staleness tests.test_saturation_ledger tests.test_adoption_docs_consistency 177 tests OK (2 skips); tests.test_currency_due 50 OK under fixtures outside and inside HOME; python3 scripts/validate.py passed; the three registry tests OK (pre-push); privacy scan of the added lines 0.

🤖 Generated with Claude Code

seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
… path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…o the due-file's own path (review round 3 of #539)

When the absolute command leaves the counts no room in the 160-character line, the line now ends with the path of
the due-file itself instead of a cwd-relative command. The document gains root (the inspected checkout) and
details_command (the full command), so a reader of the file runs the right checkout from anywhere. Tests run the
literal command or the pointed file's command as a process from an unrelated working directory for the primary,
--root and long-path cases, under fixtures outside and inside the home directory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…`, and the line length is enforced (review round 4 of #539)

When the absolute command leaves the counts no room, the line now ends with `cat <due-file>`, a short command that
prints the document (root and details_command included), and with a constant pointer when a state-directory path
is too long even for that; the writer refuses to emit a line over 160 characters. Tests run the literal printed
command from an unrelated directory for the primary, --root and long-path cases, and cover a 140-character
state-directory basename and a long XDG_STATE_HOME.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…olic XDG pointer, over-long --state-dir refused (review round 5 of #539)

The constant last-resort text is gone. When the resolved due-file path is too long for `cat <path>` and the state
directory came from XDG_STATE_HOME, the line ends with `cat "$XDG_STATE_HOME"/native-agent-stack/currency-due.json`,
which the session that prints the line resolves with the variable the hook used; an explicit --state-dir too long
for any runnable pointer is refused as a usage error before the checks run. Tests run the literal printed command
from an unrelated directory in every case, including the symbolic pointer with the variable inherited.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…run that has no runnable form (review round 6 of #539)

The up-front --state-dir refusal is gone: a long explicit state directory beside a short checkout path keeps the
primary command, and a run with nothing due always removes an obsolete due-file. Only when something is due and
neither the command nor any pointer fits the 160-character line does the run fail with exit 2 and write nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…`, and the line length is enforced (review round 4 of #539)

When the absolute command leaves the counts no room, the line now ends with `cat <due-file>`, a short command that
prints the document (root and details_command included), and with a constant pointer when a state-directory path
is too long even for that; the writer refuses to emit a line over 160 characters. Tests run the literal printed
command from an unrelated directory for the primary, --root and long-path cases, and cover a 140-character
state-directory basename and a long XDG_STATE_HOME.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…olic XDG pointer, over-long --state-dir refused (review round 5 of #539)

The constant last-resort text is gone. When the resolved due-file path is too long for `cat <path>` and the state
directory came from XDG_STATE_HOME, the line ends with `cat "$XDG_STATE_HOME"/native-agent-stack/currency-due.json`,
which the session that prints the line resolves with the variable the hook used; an explicit --state-dir too long
for any runnable pointer is refused as a usage error before the checks run. Tests run the literal printed command
from an unrelated directory in every case, including the symbolic pointer with the variable inherited.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…run that has no runnable form (review round 6 of #539)

The up-front --state-dir refusal is gone: a long explicit state directory beside a short checkout path keeps the
primary command, and a run with nothing due always removes an obsolete due-file. Only when something is due and
neither the command nor any pointer fits the 160-character line does the run fail with exit 2 and write nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/sota-defaults-a2-20260930 branch from 0386201 to 72bf988 Compare September 30, 2026 18:41
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…ests; the inert Codex trust entry is dropped)

Requires #539 merged first, stated in the addendum. The stack-researcher Skill grant is described as held (H3,
Amendment 4), not applied. The config template no longer ships a trust entry for the hand-appended Codex group,
which stays untrusted until it is reviewed in /hooks; the hooks template description and the test say so. The
wall-clock budget is asserted only on CI runners, every timed run must exit 0, the import test judges only what
the hook itself loads, the StrictModes wording names the file-level check only, and the Codex-native example role
is recorded as a follow-up with unit F4's worker role.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins

Copy link
Copy Markdown
Owner Author

Rebased onto origin/main 8fc8611 (after #546) with the hot-file protocol: main's manifests/evidence.json taken and this unit's files re-registered in the last commit; python3 scripts/validate.py passed; new head 72bf9888, tree otherwise unchanged.

🤖 Generated with Claude Code

Scout and others added 11 commits September 30, 2026 15:58
scripts/currency_due.py runs receipt_staleness.py --json, adoption_status.py --pinned-versions --json and saturation_ledger.py --report --json (plus runtime_skill_freshness.py with --network, off by default) as subprocesses with the workflows' arguments, and writes ${XDG_STATE_HOME:-~/.local/state}/native-agent-stack/currency-due.json atomically (mode 0600, os.replace) only while pins_behind, stale_receipts, due_layers or reopen_triggers is nonzero; otherwise it removes the file. due_layers follows the sweep recipe's monthly cadence (--sweep-cadence-days, default 30; 0 gives the raw count). Exit 2 on an internal error leaves the state directory as it was.

adoption/templates/systemd/stack-currency.{service,timer}: OnCalendar=daily, Persistent=true, RandomizedDelaySec=15m; oneshot with an explicit PATH so the version probes resolve under the user manager.

tests/test_currency_due.py: fake checks in a temporary checkout, a dry run of this checkout's real checks, and the units' settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… record

docs/token-practice.md item 4: ordinary startup still runs no audits, model trials or network calls; one read-only SessionStart line of at most 160 characters from the timer's due-file is allowed, fail-open.

adoption/lifecycle.md: render, verify and enable the stack-currency units (added after v2026.09.26.2).

docs/decisions/2026-09-30-session-currency-notice.md: context, alternatives (UserPromptSubmit hook, SessionStart running the checks, weekly CI only, raw saturation count, dated-manifest pins, monotonic timer), the decision with the hook contract and gate for unit F2, overturn conditions and sources. The hook script and any AGENTS.md:28 amendment ship in the frozen units F2 and F1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, and the notice command reproduces the run

Repair of the cross-family review of 3d1cfada (three findings, each checked against the source before the change).

scripts/currency_due.py
- A --network skill check that answered incompletely (an error in its report, a skill left unfetched or in a state this
  script does not know, an unfetched skills CLI release) is unknown, and unknown is not nothing due: the run keeps the
  earlier due-file (no removal, no new file), exit 0, and writes what it found when a count is nonzero, with the gap in
  the coverage detail (skills_complete, skills_fetch_errors, skills_unresolved, the first five error strings). An
  invalid-pin is a fetched answer and counts in pins_behind as its own detail kind. Sources:
  runtime_skill_freshness.py:77-81,100-103,115,134.
- The command that ends summary_line repeats the options that change what a run reports (--network, a non-default
  --sweep-cadence-days; the latter bounded to 36500), so running it reproduces the notice. The next-step line names
  adoption_status.py only for a pin mismatch, not for a skill pin.
- pinned_versions must be a list of objects with a string id; a wrong type is CheckError (exit 2), not a TypeError.

tests/test_currency_due.py: failing-first against the unchanged script (42 tests, 18 failures, 14 errors: the earlier
file deleted, TypeError at currency_due.py:199, the command without --network); incomplete-check, invalid-pin,
reproduced-command, ExecStart-flag, malformed-field and 2,277-case shape tests.

adoption/templates/systemd/stack-currency.service: header comment only; the directives are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… invalid pin, notice command options)

docs/decisions/2026-09-30-session-currency-notice.md
- Alternatives: counting an incomplete skill check as nothing due (rejected; the first draft did), exit 2 for an
  incomplete check (not adopted: no gh login is an expected condition), a details command that prints the saved file
  (not adopted: it must confirm what is still due).
- Decision item 2: invalid-pin counts in pins_behind; an incomplete check never removes the file and writes what it
  found; the notice command repeats --network and a non-default --sweep-cadence-days; a wrong-typed report field is exit 2.
- Evidence: the failing-first run against the unrepaired script (43 tests, 19 failures, 13 errors in 15 tests), the new
  tests, thirteen mutants, the re-measured dry run (scratch HOME, 1.59 s) and the systemd check. The first draft's
  seven-mutant claim is now scoped to that draft.
- Sources: the line ranges of runtime_skill_freshness.py and adoption_status.py that the repair reads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…o the due-file's own path (review round 3 of #539)

When the absolute command leaves the counts no room in the 160-character line, the line now ends with the path of
the due-file itself instead of a cwd-relative command. The document gains root (the inspected checkout) and
details_command (the full command), so a reader of the file runs the right checkout from anywhere. Tests run the
literal command or the pointed file's command as a process from an unrelated working directory for the primary,
--root and long-path cases, under fixtures outside and inside the home directory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…`, and the line length is enforced (review round 4 of #539)

When the absolute command leaves the counts no room, the line now ends with `cat <due-file>`, a short command that
prints the document (root and details_command included), and with a constant pointer when a state-directory path
is too long even for that; the writer refuses to emit a line over 160 characters. Tests run the literal printed
command from an unrelated directory for the primary, --root and long-path cases, and cover a 140-character
state-directory basename and a long XDG_STATE_HOME.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…olic XDG pointer, over-long --state-dir refused (review round 5 of #539)

The constant last-resort text is gone. When the resolved due-file path is too long for `cat <path>` and the state
directory came from XDG_STATE_HOME, the line ends with `cat "$XDG_STATE_HOME"/native-agent-stack/currency-due.json`,
which the session that prints the line resolves with the variable the hook used; an explicit --state-dir too long
for any runnable pointer is refused as a usage error before the checks run. Tests run the literal printed command
from an unrelated directory in every case, including the symbolic pointer with the variable inherited.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…run that has no runnable form (review round 6 of #539)

The up-front --state-dir refusal is gone: a long explicit state directory beside a short checkout path keeps the
primary command, and a run with nothing due always removes an obsolete due-file. Only when something is due and
neither the command nor any pointer fits the 160-character line does the run fail with exit 2 and write nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…cOS runners' long TMPDIR changed the notice's form)

On macos-15 the runner's TMPDIR sits under /private/var/folders/..., long enough to push a fixture checkout's absolute
command out of the 160-character line, so five exact-text assertions saw the `cat <due-file>` form instead. The
fixture now picks the shorter of TMPDIR and /tmp; the tests that need a long path still build one on purpose. No
change to the script.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…(hot-file protocol)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/sota-defaults-a2-20260930 branch from 72bf988 to 86fe8c3 Compare September 30, 2026 20:00
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…ests; the inert Codex trust entry is dropped)

Requires #539 merged first, stated in the addendum. The stack-researcher Skill grant is described as held (H3,
Amendment 4), not applied. The config template no longer ships a trust entry for the hand-appended Codex group,
which stays untrusted until it is reviewed in /hooks; the hooks template description and the test say so. The
wall-clock budget is asserted only on CI runners, every timed run must exit 0, the import test judges only what
the hook itself loads, the StrictModes wording names the file-level check only, and the Codex-native example role
is recorded as a follow-up with unit F4's worker role.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins

Copy link
Copy Markdown
Owner Author

Stacked merge train after #542 merged (main 1f2cdce): this branch is rebased onto main 1f2cdce with the hot-file protocol applied against that head (its evidence.json taken, this unit's files re-registered in the last commit; python3 scripts/validate.py passed). New head 86fe8c3d; the PR's own delta is the commits above that base, the earlier commits belong to the PR(s) before it in the train and vanish from this diff as they merge. Merge order: #539 → #545 → #540 → #547 → #557 → #553 (after its repair) → #549 → #541; each with --match-head-commit once its eight required checks pass.

🤖 Generated with Claude Code

seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…ests; the inert Codex trust entry is dropped)

Requires #539 merged first, stated in the addendum. The stack-researcher Skill grant is described as held (H3,
Amendment 4), not applied. The config template no longer ships a trust entry for the hand-appended Codex group,
which stays untrusted until it is reviewed in /hooks; the hooks template description and the test say so. The
wall-clock budget is asserted only on CI runners, every timed run must exit 0, the import test judges only what
the hook itself loads, the StrictModes wording names the file-level check only, and the Codex-native example role
is recorded as a follow-up with unit F4's worker role.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins merged commit 4720b20 into main Sep 30, 2026
25 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/sota-defaults-a2-20260930 branch September 30, 2026 20:36
seathatflowsinourveins added a commit that referenced this pull request Sep 30, 2026
…onfigure-full-profile (unit A3) (#545)

* Currency due-file writer and its daily user timer (drafted units)

scripts/currency_due.py runs receipt_staleness.py --json, adoption_status.py --pinned-versions --json and saturation_ledger.py --report --json (plus runtime_skill_freshness.py with --network, off by default) as subprocesses with the workflows' arguments, and writes ${XDG_STATE_HOME:-~/.local/state}/native-agent-stack/currency-due.json atomically (mode 0600, os.replace) only while pins_behind, stale_receipts, due_layers or reopen_triggers is nonzero; otherwise it removes the file. due_layers follows the sweep recipe's monthly cadence (--sweep-cadence-days, default 30; 0 gives the raw count). Exit 2 on an internal error leaves the state directory as it was.

adoption/templates/systemd/stack-currency.{service,timer}: OnCalendar=daily, Persistent=true, RandomizedDelaySec=15m; oneshot with an explicit PATH so the version probes resolve under the user manager.

tests/test_currency_due.py: fake checks in a temporary checkout, a dry run of this checkout's real checks, and the units' settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Session currency notice: startup rule, install paragraph and decision record

docs/token-practice.md item 4: ordinary startup still runs no audits, model trials or network calls; one read-only SessionStart line of at most 160 characters from the timer's due-file is allowed, fail-open.

adoption/lifecycle.md: render, verify and enable the stack-currency units (added after v2026.09.26.2).

docs/decisions/2026-09-30-session-currency-notice.md: context, alternatives (UserPromptSubmit hook, SessionStart running the checks, weekly CI only, raw saturation count, dated-manifest pins, monotonic timer), the decision with the hook contract and gate for unit F2, overturn conditions and sources. The hook script and any AGENTS.md:28 amendment ship in the frozen units F2 and F1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Currency due-file: an incomplete skill check never removes the notice, and the notice command reproduces the run

Repair of the cross-family review of 3d1cfada (three findings, each checked against the source before the change).

scripts/currency_due.py
- A --network skill check that answered incompletely (an error in its report, a skill left unfetched or in a state this
  script does not know, an unfetched skills CLI release) is unknown, and unknown is not nothing due: the run keeps the
  earlier due-file (no removal, no new file), exit 0, and writes what it found when a count is nonzero, with the gap in
  the coverage detail (skills_complete, skills_fetch_errors, skills_unresolved, the first five error strings). An
  invalid-pin is a fetched answer and counts in pins_behind as its own detail kind. Sources:
  runtime_skill_freshness.py:77-81,100-103,115,134.
- The command that ends summary_line repeats the options that change what a run reports (--network, a non-default
  --sweep-cadence-days; the latter bounded to 36500), so running it reproduces the notice. The next-step line names
  adoption_status.py only for a pin mismatch, not for a skill pin.
- pinned_versions must be a list of objects with a string id; a wrong type is CheckError (exit 2), not a TypeError.

tests/test_currency_due.py: failing-first against the unchanged script (42 tests, 18 failures, 14 errors: the earlier
file deleted, TypeError at currency_due.py:199, the command without --network); incomplete-check, invalid-pin,
reproduced-command, ExecStart-flag, malformed-field and 2,277-case shape tests.

adoption/templates/systemd/stack-currency.service: header comment only; the directives are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Session currency notice: record the repair (incomplete network check, invalid pin, notice command options)

docs/decisions/2026-09-30-session-currency-notice.md
- Alternatives: counting an incomplete skill check as nothing due (rejected; the first draft did), exit 2 for an
  incomplete check (not adopted: no gh login is an expected condition), a details command that prints the saved file
  (not adopted: it must confirm what is still due).
- Decision item 2: invalid-pin counts in pins_behind; an incomplete check never removes the file and writes what it
  found; the notice command repeats --network and a non-default --sweep-cadence-days; a wrong-typed report field is exit 2.
- Evidence: the failing-first run against the unrepaired script (43 tests, 19 failures, 13 errors in 15 tests), the new
  tests, thirteen mutants, the re-measured dry run (scratch HOME, 1.59 s) and the systemd check. The first draft's
  seven-mutant claim is now scoped to that draft.
- Sources: the line ranges of runtime_skill_freshness.py and adoption_status.py that the repair reads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* currency_due.py: the notice's command names the inspected checkout by path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* currency_due.py: no cwd-relative fallback; a long command gives way to the due-file's own path (review round 3 of #539)

When the absolute command leaves the counts no room in the 160-character line, the line now ends with the path of
the due-file itself instead of a cwd-relative command. The document gains root (the inspected checkout) and
details_command (the full command), so a reader of the file runs the right checkout from anywhere. Tests run the
literal command or the pointed file's command as a process from an unrelated working directory for the primary,
--root and long-path cases, under fixtures outside and inside the home directory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* currency_due.py: the long-path fallback is a runnable `cat <due-file>`, and the line length is enforced (review round 4 of #539)

When the absolute command leaves the counts no room, the line now ends with `cat <due-file>`, a short command that
prints the document (root and details_command included), and with a constant pointer when a state-directory path
is too long even for that; the writer refuses to emit a line over 160 characters. Tests run the literal printed
command from an unrelated directory for the primary, --root and long-path cases, and cover a 140-character
state-directory basename and a long XDG_STATE_HOME.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* currency_due.py: every notice line ends with a runnable command; symbolic XDG pointer, over-long --state-dir refused (review round 5 of #539)

The constant last-resort text is gone. When the resolved due-file path is too long for `cat <path>` and the state
directory came from XDG_STATE_HOME, the line ends with `cat "$XDG_STATE_HOME"/native-agent-stack/currency-due.json`,
which the session that prints the line resolves with the variable the hook used; an explicit --state-dir too long
for any runnable pointer is refused as a usage error before the checks run. Tests run the literal printed command
from an unrelated directory in every case, including the symbolic pointer with the variable inherited.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* currency_due.py: consider the primary command first; fail only a due run that has no runnable form (review round 6 of #539)

The up-front --state-dir refusal is gone: a long explicit state directory beside a short checkout path keeps the
primary command, and a run with nothing due always removes an obsolete due-file. Only when something is due and
neither the command nor any pointer fits the 160-character line does the run fail with exit 2 and write nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test_currency_due: fixtures under the shortest writable temp base (macOS runners' long TMPDIR changed the notice's form)

On macos-15 the runner's TMPDIR sits under /private/var/folders/..., long enough to push a fixture checkout's absolute
command out of the 160-character line, so five exact-text assertions saw the `cat <due-file>` form instead. The
fixture now picks the shorter of TMPDIR and /tmp; the tests that need a long path still build one on purpose. No
change to the script.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Re-register the changed hash-listed files in manifests/evidence.json (hot-file protocol)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reusable sota-sources gate and the new-repository scaffold

.github/workflows/sota-sources-gate.yml is validate.yml's required
sota-sources job as a workflow_call workflow; from its if: line on the
job is byte-identical (tests/test_sota_sources_gate.py), and both copies
run the same inline script in node the way actions/github-script v9.0.0
does (src/async-function.ts). validate.yml is untouched.

adoption/scaffold/ holds AGENTS.md (the Codex template's top-rule block
byte for byte), CLAUDE.md (@AGENTS.md import), .agents/skills/README.md,
a pull-request template and the caller workflow. The caller is kept as
sota-sources.yml.template: zizmor 1.30.1 collects nested
.github/workflows directories, and a literal @<sha> is an unpinned-uses
High finding that would fail validate.yml's repository-wide zizmor gate.

tools/adoption/scaffold_repo.py writes the scaffold idempotently
(created/unchanged/skipped, exit 3 on refusal, --force, --dry-run),
fills <sha> from --main-sha or git ls-remote origin refs/heads/main,
refuses a local main commit without the gate, and renders .codex/
config.toml through render_config.render_one.

Registers the new workflow in tests/test_workflow_security_coverage.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* bootstrap-linux.sh --configure-full-profile; adoption_status --launcher-resolution

--configure-full-profile (opt-in; the default path is unchanged) runs,
each through the repository's own tool and each skippable with --skip:
install_claude_profile.py; apply_claude_settings.py with the rendered
template, plus the WSL overlay under WSL_DISTRO_NAME; the ~/.claude/
CLAUDE.md managed block; install_skills.py (the pinned skills CLI first,
through install_npm's sha256 check); render_config.py and
apply_codex_lane.py, applying exactly the --expect-*-sha256 hashes its
own dry run printed; a managed PATH block in ~/.profile; and the
login-shell read-back. It refuses (exit 1, both commits printed) unless
HEAD equals git ls-remote origin refs/heads/main, before installing
anything; a failed step is reported, the rest still run, exit 6.

tools/adoption/managed_block.py holds the two blocks: apply_codex_lane's
block merge with the markers as parameters (the end marker must start
after the begin marker), apply_claude_settings' backup and atomic write,
rtk's @RTK.md kept outside the block, an unmanaged copy of the example
replaced only when current and otherwise refused.

scripts/adoption_status.py --launcher-resolution is a separate opt-in,
so --login-shell stays a metadata-only check: one bounded bash -l -c
from a fixed environment reports where command -v claude resolves
(shown under $ECO_ROOT, $HOME or a system directory, else withheld),
whether it is $ECO_ROOT/bin/claude, and that launcher's sha256; claude
never runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Docs: new repositories from the scaffold, the full profile in one run

adoption/bootstrap.md step 2 documents --configure-full-profile (step
table, --host, --skip, the origin/main refusal, exit 6, the managed
blocks) and step 4 points a new repository's .codex/config.toml at the
scaffold; a "New repositories" section documents scaffold_repo.py and
the reusable sota-sources gate. adoption/update.md gains "Refresh the
user profile from main" and "Start a new repository"; docs/activation.md
names the scaffold for new repositories. adoption/manifest.json sources
gains "scaffold" (sources is a name-to-file map, so no schema change;
tests/test_adoption_contract.py resolves it). Units that use the new
paths say "added after v2026.09.26.2".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Point the operator at a render that outlives the run; name harden-runner

The codex-lane step printed the staging directory's rendered templates
as the place to review them, but the EXIT trap removes that directory;
it now says to render with render_config.py --host <name> --out <dir>.
The scaffold's caller workflow and bootstrap.md now say a selected-
actions policy must allow step-security/harden-runner as well as the
reusable workflow (actions/github-script is GitHub-owned).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* scaffold_repo.py: --force names the file it overwrites; --dry-run plans a missing target

--force PATH (repeatable, the path as the table prints it) overwrites only the named
scaffold files; every other file that differs is still skipped. A bare --force is an
argparse usage error and a path outside the scaffold is refused before any write, so the
update.md recipe that moves a workflow to a newer gate can no longer replace a filled-in
AGENTS.md, CLAUDE.md or .codex/config.toml. Copier's all-files `overwrite` plus
`skip_if_exists` runs the other way round (copier v9.18.2 docs/configuring.md), which the
docstring now says instead of calling --force copier's overwrite.

--dry-run also plans a --target that does not exist yet (it writes nothing); a real run
still refuses one, and a file or dangling symlink in its place is refused either way. The
docs state that the pinned reusable workflow resolves only once that commit on GitHub
carries the gate file. Test fixtures use HOME=/opt/example, so no added line matches the
/home/<name>/ scan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* adoption_status --login-shell states launcher_resolution as not run

--login-shell stays metadata-only (it never opens or runs a login file), and resolving
`command -v claude` needs one bounded login shell, so that check stays behind its own
--launcher-resolution flag. A consumer of `--login-shell --json` now reads why the result
is missing instead of an absent key: launcher_resolution is
{"status": "not_run", "flag": "--launcher-resolution"}. A report without --login-shell is
unchanged. bootstrap.md step 6 documents both flags; the tests pin the not_run object in
the JSON and text forms and for an invalid manifest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* --configure-full-profile codex-lane: meet the lane's preconditions on a fresh Codex home

apply_codex_lane.py refuses a Codex home without config.toml, without a derivable HOST_PATH
and without features.daemon_auto_start = false (Plan.preconditions), so the step could never
succeed on a fresh host: it discarded the rendered user config and called the lane anyway.

tools/adoption/codex_home.py now runs first. A home without config.toml gets the rendered
user config (render_config.py's codex.config.toml for --host) minus the source host's trust
state: every [projects.*] trust grant and [hooks.state.*] hook approval, the two sections
bootstrap.md step 4 says were never reviewed on the target, with the comments directly above
them. The cut is checked semantically (the parse must equal the render minus exactly those
tables, daemon_auto_start the boolean false, shell_environment_policy PATH under this run's
ecosystem root) and written create-only (temp file + os.link, apply_codex_lane.atomic_write),
0600 in a 0700 home. An existing config.toml is never replaced; when it lacks the feature it
is backed up (apply_claude_settings.write_backup) and set through Codex's own writer,
`codex features disable daemon_auto_start` (codex-rs/cli/src/main.rs L1902-1911 at
rust-v0.157.1, ConfigEditsBuilder; recipes/README.md codex row), then read back.

The step passes the host file's HOST_PATH as --host-path and $bin_dir/codex as --codex to
both the dry run and the apply, with $bin_dir first on PATH, since the npm-installed codex
needs node and the ecosystem bin directory is not yet on a fresh shell's PATH.

Tests run the step function verbatim with the real render_config.py, codex_home.py and
apply_codex_lane.py dry run (stub codex at the pin): its own [ok] lines for config.toml,
HOST_PATH and features.daemon_auto_start for (a) a fresh HOME, (b) a home holding the whole
render (left byte for byte) and (c) a config without the feature; on 4e5652c4's step (a)
prints [fail] for all three. Measured with the real codex-cli 0.157.1 in scratch homes:
`features disable` creates a 0600 config.toml in an empty home and keeps comments and other
keys in an existing one; a rerun leaves identical bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* bootstrap-linux.sh --configure-full-profile: origin/main check before any package command

The origin/main guard ran after `sudo apt-get update/install`, so a rejected
checkout could still change the host although bootstrap.md and --help promised
a refusal before installing anything (cross-family review of PR 545, reproduced
with stubbed package commands).

The guard now sits right after the platform checks, where nothing has written
to the host yet, and before the system-package step. A host without git is
refused there (exit 1) with the instruction to install it first, instead of
being given packages before the check. Without the flag nothing changes: the
package step still runs first and git is never asked.

tests/test_bootstrap_full_profile.py puts stubs for sudo and dpkg-query first on
PATH (sudo appends its command line to a log): a checkout ahead of origin main,
an unreachable origin and a host without git are each refused with an empty
log, and the same stubs do log the package commands at origin main and on the
default path, so the empty log is the ordering and not a stub that never ran.
A source-order test pins the git prerequisite and the single ls-remote before
the apt step and the first mkdir. Against the previous head the three
behavioural tests and the order test fail; the log held `sudo apt-get update`
and the install line.

bootstrap.md documents the order of the flag's checks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* codex_home.py: no config.toml write while codex runs; name the helper in the release note

apply_codex_lane.py --apply refuses while a process named codex runs (Plan.preconditions,
codex_processes), since a running Codex writes the same config.toml. The feature write that
precedes it now follows the same rule (--codex-process-name, default codex): with a codex
process running it refuses before the backup and before `codex features disable`, and the
step stops before the lane. The docstring and bootstrap.md say the backup is that key's only
undo (apply_codex_lane.py --rollback does not cover it). The real-home tests stub pgrep in
the bin directory the step puts first on PATH, so the host's own Codex sessions do not decide
them; the pre-guard helper wrote under a reported running codex (exit 0, one codex call).

bootstrap.md lists tools/adoption/codex_home.py among the step-2 additions after
v2026.09.26.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 30, 2026
… role bodies (unit F2, frozen wiring) (#547)

* Currency due-file writer and its daily user timer (drafted units)

scripts/currency_due.py runs receipt_staleness.py --json, adoption_status.py --pinned-versions --json and saturation_ledger.py --report --json (plus runtime_skill_freshness.py with --network, off by default) as subprocesses with the workflows' arguments, and writes ${XDG_STATE_HOME:-~/.local/state}/native-agent-stack/currency-due.json atomically (mode 0600, os.replace) only while pins_behind, stale_receipts, due_layers or reopen_triggers is nonzero; otherwise it removes the file. due_layers follows the sweep recipe's monthly cadence (--sweep-cadence-days, default 30; 0 gives the raw count). Exit 2 on an internal error leaves the state directory as it was.

adoption/templates/systemd/stack-currency.{service,timer}: OnCalendar=daily, Persistent=true, RandomizedDelaySec=15m; oneshot with an explicit PATH so the version probes resolve under the user manager.

tests/test_currency_due.py: fake checks in a temporary checkout, a dry run of this checkout's real checks, and the units' settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Session currency notice: startup rule, install paragraph and decision record

docs/token-practice.md item 4: ordinary startup still runs no audits, model trials or network calls; one read-only SessionStart line of at most 160 characters from the timer's due-file is allowed, fail-open.

adoption/lifecycle.md: render, verify and enable the stack-currency units (added after v2026.09.26.2).

docs/decisions/2026-09-30-session-currency-notice.md: context, alternatives (UserPromptSubmit hook, SessionStart running the checks, weekly CI only, raw saturation count, dated-manifest pins, monotonic timer), the decision with the hook contract and gate for unit F2, overturn conditions and sources. The hook script and any AGENTS.md:28 amendment ship in the frozen units F2 and F1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Currency due-file: an incomplete skill check never removes the notice, and the notice command reproduces the run

Repair of the cross-family review of 3d1cfada (three findings, each checked against the source before the change).

scripts/currency_due.py
- A --network skill check that answered incompletely (an error in its report, a skill left unfetched or in a state this
  script does not know, an unfetched skills CLI release) is unknown, and unknown is not nothing due: the run keeps the
  earlier due-file (no removal, no new file), exit 0, and writes what it found when a count is nonzero, with the gap in
  the coverage detail (skills_complete, skills_fetch_errors, skills_unresolved, the first five error strings). An
  invalid-pin is a fetched answer and counts in pins_behind as its own detail kind. Sources:
  runtime_skill_freshness.py:77-81,100-103,115,134.
- The command that ends summary_line repeats the options that change what a run reports (--network, a non-default
  --sweep-cadence-days; the latter bounded to 36500), so running it reproduces the notice. The next-step line names
  adoption_status.py only for a pin mismatch, not for a skill pin.
- pinned_versions must be a list of objects with a string id; a wrong type is CheckError (exit 2), not a TypeError.

tests/test_currency_due.py: failing-first against the unchanged script (42 tests, 18 failures, 14 errors: the earlier
file deleted, TypeError at currency_due.py:199, the command without --network); incomplete-check, invalid-pin,
reproduced-command, ExecStart-flag, malformed-field and 2,277-case shape tests.

adoption/templates/systemd/stack-currency.service: header comment only; the directives are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Session currency notice: record the repair (incomplete network check, invalid pin, notice command options)

docs/decisions/2026-09-30-session-currency-notice.md
- Alternatives: counting an incomplete skill check as nothing due (rejected; the first draft did), exit 2 for an
  incomplete check (not adopted: no gh login is an expected condition), a details command that prints the saved file
  (not adopted: it must confirm what is still due).
- Decision item 2: invalid-pin counts in pins_behind; an incomplete check never removes the file and writes what it
  found; the notice command repeats --network and a non-default --sweep-cadence-days; a wrong-typed report field is exit 2.
- Evidence: the failing-first run against the unrepaired script (43 tests, 19 failures, 13 errors in 15 tests), the new
  tests, thirteen mutants, the re-measured dry run (scratch HOME, 1.59 s) and the systemd check. The first draft's
  seven-mutant claim is now scoped to that draft.
- Sources: the line ranges of runtime_skill_freshness.py and adoption_status.py that the repair reads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* currency_due.py: the notice's command names the inspected checkout by path (review of #539)

The details command that ends summary_line was cwd-relative, so a session that starts in another project (the
SessionStart hook is user scope) ran it in the wrong checkout or none, and an explicit --root was dropped. The
command now names the inspected checkout's own copy of the script by its path (~/... under the home directory,
else absolute), falls back to this script with --root for a checkout without the script, and gives way to the
cwd-relative form only when the absolute one would leave the counts no room in the 160-character line. Tests
reproduce the notice from an unrelated working directory as a separate process; the record documents the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* currency_due.py: no cwd-relative fallback; a long command gives way to the due-file's own path (review round 3 of #539)

When the absolute command leaves the counts no room in the 160-character line, the line now ends with the path of
the due-file itself instead of a cwd-relative command. The document gains root (the inspected checkout) and
details_command (the full command), so a reader of the file runs the right checkout from anywhere. Tests run the
literal command or the pointed file's command as a process from an unrelated working directory for the primary,
--root and long-path cases, under fixtures outside and inside the home directory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* currency_due.py: the long-path fallback is a runnable `cat <due-file>`, and the line length is enforced (review round 4 of #539)

When the absolute command leaves the counts no room, the line now ends with `cat <due-file>`, a short command that
prints the document (root and details_command included), and with a constant pointer when a state-directory path
is too long even for that; the writer refuses to emit a line over 160 characters. Tests run the literal printed
command from an unrelated directory for the primary, --root and long-path cases, and cover a 140-character
state-directory basename and a long XDG_STATE_HOME.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* currency_due.py: every notice line ends with a runnable command; symbolic XDG pointer, over-long --state-dir refused (review round 5 of #539)

The constant last-resort text is gone. When the resolved due-file path is too long for `cat <path>` and the state
directory came from XDG_STATE_HOME, the line ends with `cat "$XDG_STATE_HOME"/native-agent-stack/currency-due.json`,
which the session that prints the line resolves with the variable the hook used; an explicit --state-dir too long
for any runnable pointer is refused as a usage error before the checks run. Tests run the literal printed command
from an unrelated directory in every case, including the symbolic pointer with the variable inherited.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* currency_due.py: consider the primary command first; fail only a due run that has no runnable form (review round 6 of #539)

The up-front --state-dir refusal is gone: a long explicit state directory beside a short checkout path keeps the
primary command, and a run with nothing due always removes an obsolete due-file. Only when something is due and
neither the command nor any pointer fits the 160-character line does the run fail with exit 2 and write nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test_currency_due: fixtures under the shortest writable temp base (macOS runners' long TMPDIR changed the notice's form)

On macos-15 the runner's TMPDIR sits under /private/var/folders/..., long enough to push a fixture checkout's absolute
command out of the 160-character line, so five exact-text assertions saw the `cat <due-file>` form instead. The
fixture now picks the shorter of TMPDIR and /tmp; the tests that need a long path still build one on purpose. No
change to the script.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Re-register the changed hash-listed files in manifests/evidence.json (hot-file protocol)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reusable sota-sources gate and the new-repository scaffold

.github/workflows/sota-sources-gate.yml is validate.yml's required
sota-sources job as a workflow_call workflow; from its if: line on the
job is byte-identical (tests/test_sota_sources_gate.py), and both copies
run the same inline script in node the way actions/github-script v9.0.0
does (src/async-function.ts). validate.yml is untouched.

adoption/scaffold/ holds AGENTS.md (the Codex template's top-rule block
byte for byte), CLAUDE.md (@AGENTS.md import), .agents/skills/README.md,
a pull-request template and the caller workflow. The caller is kept as
sota-sources.yml.template: zizmor 1.30.1 collects nested
.github/workflows directories, and a literal @<sha> is an unpinned-uses
High finding that would fail validate.yml's repository-wide zizmor gate.

tools/adoption/scaffold_repo.py writes the scaffold idempotently
(created/unchanged/skipped, exit 3 on refusal, --force, --dry-run),
fills <sha> from --main-sha or git ls-remote origin refs/heads/main,
refuses a local main commit without the gate, and renders .codex/
config.toml through render_config.render_one.

Registers the new workflow in tests/test_workflow_security_coverage.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* bootstrap-linux.sh --configure-full-profile; adoption_status --launcher-resolution

--configure-full-profile (opt-in; the default path is unchanged) runs,
each through the repository's own tool and each skippable with --skip:
install_claude_profile.py; apply_claude_settings.py with the rendered
template, plus the WSL overlay under WSL_DISTRO_NAME; the ~/.claude/
CLAUDE.md managed block; install_skills.py (the pinned skills CLI first,
through install_npm's sha256 check); render_config.py and
apply_codex_lane.py, applying exactly the --expect-*-sha256 hashes its
own dry run printed; a managed PATH block in ~/.profile; and the
login-shell read-back. It refuses (exit 1, both commits printed) unless
HEAD equals git ls-remote origin refs/heads/main, before installing
anything; a failed step is reported, the rest still run, exit 6.

tools/adoption/managed_block.py holds the two blocks: apply_codex_lane's
block merge with the markers as parameters (the end marker must start
after the begin marker), apply_claude_settings' backup and atomic write,
rtk's @RTK.md kept outside the block, an unmanaged copy of the example
replaced only when current and otherwise refused.

scripts/adoption_status.py --launcher-resolution is a separate opt-in,
so --login-shell stays a metadata-only check: one bounded bash -l -c
from a fixed environment reports where command -v claude resolves
(shown under $ECO_ROOT, $HOME or a system directory, else withheld),
whether it is $ECO_ROOT/bin/claude, and that launcher's sha256; claude
never runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Docs: new repositories from the scaffold, the full profile in one run

adoption/bootstrap.md step 2 documents --configure-full-profile (step
table, --host, --skip, the origin/main refusal, exit 6, the managed
blocks) and step 4 points a new repository's .codex/config.toml at the
scaffold; a "New repositories" section documents scaffold_repo.py and
the reusable sota-sources gate. adoption/update.md gains "Refresh the
user profile from main" and "Start a new repository"; docs/activation.md
names the scaffold for new repositories. adoption/manifest.json sources
gains "scaffold" (sources is a name-to-file map, so no schema change;
tests/test_adoption_contract.py resolves it). Units that use the new
paths say "added after v2026.09.26.2".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Point the operator at a render that outlives the run; name harden-runner

The codex-lane step printed the staging directory's rendered templates
as the place to review them, but the EXIT trap removes that directory;
it now says to render with render_config.py --host <name> --out <dir>.
The scaffold's caller workflow and bootstrap.md now say a selected-
actions policy must allow step-security/harden-runner as well as the
reusable workflow (actions/github-script is GitHub-owned).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* scaffold_repo.py: --force names the file it overwrites; --dry-run plans a missing target

--force PATH (repeatable, the path as the table prints it) overwrites only the named
scaffold files; every other file that differs is still skipped. A bare --force is an
argparse usage error and a path outside the scaffold is refused before any write, so the
update.md recipe that moves a workflow to a newer gate can no longer replace a filled-in
AGENTS.md, CLAUDE.md or .codex/config.toml. Copier's all-files `overwrite` plus
`skip_if_exists` runs the other way round (copier v9.18.2 docs/configuring.md), which the
docstring now says instead of calling --force copier's overwrite.

--dry-run also plans a --target that does not exist yet (it writes nothing); a real run
still refuses one, and a file or dangling symlink in its place is refused either way. The
docs state that the pinned reusable workflow resolves only once that commit on GitHub
carries the gate file. Test fixtures use HOME=/opt/example, so no added line matches the
/home/<name>/ scan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* adoption_status --login-shell states launcher_resolution as not run

--login-shell stays metadata-only (it never opens or runs a login file), and resolving
`command -v claude` needs one bounded login shell, so that check stays behind its own
--launcher-resolution flag. A consumer of `--login-shell --json` now reads why the result
is missing instead of an absent key: launcher_resolution is
{"status": "not_run", "flag": "--launcher-resolution"}. A report without --login-shell is
unchanged. bootstrap.md step 6 documents both flags; the tests pin the not_run object in
the JSON and text forms and for an invalid manifest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* --configure-full-profile codex-lane: meet the lane's preconditions on a fresh Codex home

apply_codex_lane.py refuses a Codex home without config.toml, without a derivable HOST_PATH
and without features.daemon_auto_start = false (Plan.preconditions), so the step could never
succeed on a fresh host: it discarded the rendered user config and called the lane anyway.

tools/adoption/codex_home.py now runs first. A home without config.toml gets the rendered
user config (render_config.py's codex.config.toml for --host) minus the source host's trust
state: every [projects.*] trust grant and [hooks.state.*] hook approval, the two sections
bootstrap.md step 4 says were never reviewed on the target, with the comments directly above
them. The cut is checked semantically (the parse must equal the render minus exactly those
tables, daemon_auto_start the boolean false, shell_environment_policy PATH under this run's
ecosystem root) and written create-only (temp file + os.link, apply_codex_lane.atomic_write),
0600 in a 0700 home. An existing config.toml is never replaced; when it lacks the feature it
is backed up (apply_claude_settings.write_backup) and set through Codex's own writer,
`codex features disable daemon_auto_start` (codex-rs/cli/src/main.rs L1902-1911 at
rust-v0.157.1, ConfigEditsBuilder; recipes/README.md codex row), then read back.

The step passes the host file's HOST_PATH as --host-path and $bin_dir/codex as --codex to
both the dry run and the apply, with $bin_dir first on PATH, since the npm-installed codex
needs node and the ecosystem bin directory is not yet on a fresh shell's PATH.

Tests run the step function verbatim with the real render_config.py, codex_home.py and
apply_codex_lane.py dry run (stub codex at the pin): its own [ok] lines for config.toml,
HOST_PATH and features.daemon_auto_start for (a) a fresh HOME, (b) a home holding the whole
render (left byte for byte) and (c) a config without the feature; on 4e5652c4's step (a)
prints [fail] for all three. Measured with the real codex-cli 0.157.1 in scratch homes:
`features disable` creates a 0600 config.toml in an empty home and keeps comments and other
keys in an existing one; a rerun leaves identical bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* bootstrap-linux.sh --configure-full-profile: origin/main check before any package command

The origin/main guard ran after `sudo apt-get update/install`, so a rejected
checkout could still change the host although bootstrap.md and --help promised
a refusal before installing anything (cross-family review of PR 545, reproduced
with stubbed package commands).

The guard now sits right after the platform checks, where nothing has written
to the host yet, and before the system-package step. A host without git is
refused there (exit 1) with the instruction to install it first, instead of
being given packages before the check. Without the flag nothing changes: the
package step still runs first and git is never asked.

tests/test_bootstrap_full_profile.py puts stubs for sudo and dpkg-query first on
PATH (sudo appends its command line to a log): a checkout ahead of origin main,
an unreachable origin and a host without git are each refused with an empty
log, and the same stubs do log the package commands at origin main and on the
default path, so the empty log is the ordering and not a stub that never ran.
A source-order test pins the git prerequisite and the single ls-remote before
the apt step and the first mkdir. Against the previous head the three
behavioural tests and the order test fail; the log held `sudo apt-get update`
and the install line.

bootstrap.md documents the order of the flag's checks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* codex_home.py: no config.toml write while codex runs; name the helper in the release note

apply_codex_lane.py --apply refuses while a process named codex runs (Plan.preconditions,
codex_processes), since a running Codex writes the same config.toml. The feature write that
precedes it now follows the same rule (--codex-process-name, default codex): with a codex
process running it refuses before the backup and before `codex features disable`, and the
step stops before the lane. The docstring and bootstrap.md say the backup is that key's only
undo (apply_codex_lane.py --rollback does not cover it). The real-home tests stub pgrep in
the bin directory the step puts first on PATH, so the host's own Codex sessions do not decide
them; the pre-guard helper wrote under a reported running codex (exit 0, one codex call).

bootstrap.md lists tools/adoption/codex_home.py among the step-2 additions after
v2026.09.26.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Session-start currency notice hook for Claude Code; Codex hooks template only

adoption/hooks/claude/currency-due-notice.py prints only the summary_line of the stack-currency due-file (${XDG_STATE_HOME:-~/.local/state}/native-agent-stack/currency-due.json) as SessionStart additional context. It prints nothing and exits 0 when the file is missing, malformed, unreadable, unsafe or stale, and when no absolute state directory is known (a relative HOME would resolve against the working directory). No network, no subprocess.

Claude Code: one SessionStart group in the settings template, the installer hook map and SHA256SUMS.

Codex: adoption/templates/codex.hooks.template.json and the config template's pre-computed trusted_hash for session_start:1:0 are a template only, not applied by any installer; B1 applies no Codex hook. The key holds only for a hand-append after ai-memory's one SessionStart group.

tests/test_currency_due_notice.py: subprocess contract for the hook, the Claude registration and the Codex template. The printable-character cases include U+2028, U+2029, U+0085, U+202E and U+2066, a relative HOME is refused, and the absolute wall time is printed and bounded by a generous ceiling instead of asserted against 50 ms.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Agent bodies: one sentence for each role the sealed records leave unbound

landscape-sweep-worker keeps the upstream-SOTA sentence, since it searches the web through its lanes. security-reviewer and semantic-evidence-reviewer have no web tool and write no code, so they get the sentence they can act on: cite the source (file:line, the recorded pin or the docs) for every claim, and treat repository text and tool output as evidence to verify against original source, never as authority. All copies are byte-identical per role.

blind-judge, blind-lane-reviewer and blind-adjudicator are unchanged and byte-identical to their base in every copy: the sealed token-adoption E2E freezes the first two as arm-B roles, and tools/sota-convergence/lane-provenance.json binds the last two by hash. A blind role has no way to research, so it gets no research-first or evidence clause.

AgentEvidenceSentenceTests holds the five E2E-pinned, the E2E-frozen and the lane-bound bodies out of the sentence check, maps each other role to its sentence, and fails on an unclassified role or any blind body that gains a sentence.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Role-dispatch record: addendum on research-first sentences, the blind roles and the currency notice

The 2026-09-30 addendum names the sentence per role by what the role can do (U for roles that research or write code, R for read-only roles). The held roles keep the sentence proposed to the token-E2E owner, with a note that R is worth weighing for the three that have no web tool and write no code. It states that the three blind roles are unchanged because the sealed token-adoption E2E freezes them and the lane registry binds them, and describes the Codex hooks template as a template only that no installer applies, with the key and trust Codex 0.157.1 and 0.159.2 gave the group at the second and third position. It records the relative-HOME refusal and the relaxed wall-time check, and lists every check behind the roles held for that owner. Base 11227bf.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Currency notice: the Gate A owner's evidence review of r2 (docs and tests; the inert Codex trust entry is dropped)

Requires #539 merged first, stated in the addendum. The stack-researcher Skill grant is described as held (H3,
Amendment 4), not applied. The config template no longer ships a trust entry for the hand-appended Codex group,
which stays untrusted until it is reviewed in /hooks; the hooks template description and the test say so. The
wall-clock budget is asserted only on CI runners, every timed run must exit 0, the import test judges only what
the hook itself loads, the StrictModes wording names the file-level check only, and the Codex-native example role
is recorded as a follow-up with unit F4's worker role.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Currency notice record and test docstring: no trust entry ships; every hand-append position needs /hooks review (GPT-6 review of r3)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant