Skip to content

release: 2026-09-25 (v0.8.5) — vendored sources you can trust, and nine contributor fixes - #184

Merged
chuycepeda merged 59 commits into
mainfrom
release/2026-09-25
Sep 25, 2026
Merged

chuycepeda merged 59 commits into
mainfrom
release/2026-09-25

Conversation

@chuycepeda

Copy link
Copy Markdown
Member

Release branch for 2026-09-25 (v0.8.5). One merge to main; the CHANGELOG entry is written on the branch.

Contributions (#175–#183), each merged with authorship kept, fixed on top where review found gaps

Framework

  • GitHub MCP retired. Slack pinned to 5.0.0, vendored byte for byte; Atlassian pinned to 0.23.1. Both were smoke-tested live.
  • Python MCP pins now reach existing installs; notebooklm-py is pinned to 0.8.2, and the helpers follow its profile folder.
  • superpowers v6.4.1 (14 skills and its LICENSE), with AIOS's INTENT stop conditions stated above upstream's continuous execution.
  • Windows registers the Anthropic skills; a parity test covers both registrars.
  • Headless Bash inherits the project layer's allow rules too; MODEL-ROUTING.md and Bucket 29 now cover it.
  • CLAUDE.md § Spawning: a bus kill can still need approval.
  • Manifests go to 0.8.5, and the release map is filled in from v0.8.0.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7

matiasmacera and others added 30 commits September 25, 2026 12:16
… skip

The idempotency gate took the precise (trailer-matching) path whenever any
commit in range carried an AIOS-Session trailer, including when this
session's own SID was `unknown`. aios-commit omits the trailer when
CLAUDE_CODE_SESSION_ID is empty, so for such a session MINE was 0 by
construction and every close after the first skipped the block, the
observed-context routing and the commit, silently.

`unknown` is not an identity: provenance is unknowable, so the gate now
falls back to the coarse rule it already uses for untagged ranges. Tests
17-18 prove the new case and reproduce the defect against the old logic.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
…arry, reconcile, plugin sync

Five places in /aios:update turned a measurement that never completed
into a confident verdict, each one silent:

- h_file / h_git returned rc=0 with an empty hash when `shasum` was
  missing (a pipeline's status is its last command, `cut`), so an empty
  LOCAL equalled an empty BASE and the three-way compare answered
  "identical → overwrite silently, no backup". They now probe for the
  tool and refuse on an empty hash — in both copies of h_file (the
  self-update compare in Step 2.5 still carried the old one).
- The legacy .gitignore migration re-ordered the operator's rules
  (`sort -u` + `comm`), so a `!exception` sorted above the pattern it
  excepts and stopped working. The carry now subtracts the baseline
  with `grep -vxF -f`, which keeps the operator's order.
- `{ cat canonical; awk operator; } > new && mv` took awk's status, so
  an unreadable canonical .gitignore installed a file holding only the
  operator's lines. The merge now refuses when canonical is unreadable
  and chains cat's status into the group.
- `diff -rq vault/x clone/x 2>/dev/null` with vault/x MISSING printed
  to stderr only (exit 2), dropped by the redirect and the trailing
  `|| true`, so a bundled folder absent from the vault read as "0
  drift" and the tracker advanced. A missing layer dir is now reported
  as `Only in <clone>: <dir>`, the same shape the root-file loop uses.
- `cp $HOME/aios/...` with $HOME unquoted: a home directory containing a
  space (common on Git Bash) split the path and nothing was copied, with
  no error path. Quoted.

tests/update-measure-fail-closed.test.sh runs each replaced shape
against the new one so a case that cannot fail is caught as such.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
…-snapshot

CLAUDE.md § Session End replaced the hand-rolled observed-context snapshot
(copy, then pick the "next free letter" by hand) with hooks/aios-snapshot,
which takes the decision under a lock and compares against every archive
already written that day. /close-day and /aios:housekeeping kept the old
instruction, so a session following the command ran the non-atomic
protocol the helper retires. Both now call the helper and treat
`identical` as success.

tests/commands-snapshot-helper.test.sh anchors it: a command that names
the dated snapshot destination must name the helper, and none may
prescribe the by-hand letter step.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
- Bucket 18 compared the seven-character vendored hash with the
  forty-character sha the API returns, by equality: every source read
  as behind on every run. Compare by prefix.
- Bucket 22's duplicate-frontmatter scan ran through `python3` (nothing
  on Windows, silence reads as clean) and printed nothing on an empty
  glob. Run through `uv`, always end with a count of files checked.
- Bucket 13 skipped already-graduated patterns by a phrase that is not
  a substring of the mark it writes, so nothing was ever skipped. Match
  on the prefix the written mark carries.

tests/housekeeping-checks-that-cannot-fail.test.sh runs each old shape
next to the new one and executes the scan script extracted from the
spec.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
…or reconcile filters, sweep both residue shapes

- Two concurrent /aios:update runs shared one fixed clone path and each
  rm -rf'd it; the second wiped the tree the first was still comparing.
  A lock directory guards the clone — its own mtime is the start time,
  so there is no timestamp-writing window a peer could misread (no pid:
  the command runs as separate shell calls); a lock older than 30
  minutes is reported with the command that clears it, never cleared
  by the run that finds it (two finders would both proceed); a refused
  run is told not to clean up what it never owned; released with the
  clone in Step 7.
- Skill-folder dedup judged a stray folder by SKILL.md alone and deleted
  it whole. Compare the whole folder; back it up first when anything
  differs.
- The reconcile's filters were bare substrings: `oauth` hid
  skills/anthropic/doc-coauthoring/ and oauth.json.template, whose drift
  was never reported. Anchored to whole path elements.
- The residue sweep matched space-joined names only; zsh leaves
  newline-joined ones, and `find -print | read` fragmented them. Each
  directory is handed whole to a child shell and both shapes match.

tests/update-clone-lock-skills-filters-sweep.test.sh extracts the lock
block, the filter chain and the sweep from the spec and runs them beside
the shapes they replace.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
…ecklist; one rule for empty fields

Three instructions a session obeys in order contradicted each other:

- Step 5 appends AND commits the session block through the helper,
  which only appends; steps 8 and 9 then asked the operator questions
  whose answers belong in that block. A new step 4.7 gathers both
  (the comprehension recap-and-offer, "what was most useful?") before
  the block is written; steps 8 and 9 now only record them.
- Step 9's checklist demanded direct observed-context writes with no
  --auto exception, while --auto defers every such write to /close-day
  because the observed files have no lock. The checklist now says the
  candidates captured in the block satisfy those items under --auto.
- The block format said "Skip" for an empty field while § Rules said
  every field must be present. Every field is present; an empty one
  reads "None" ("None — not asked" for Most useful), in both Mode A
  and the Mode B report.

tests/close-session-order-and-fields.test.sh anchors the three; run
against the previous copy it fails on every one.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
…ry carry

- The close-day precondition excluded only today's date, so a
  future-dated note won; a missing or unreadable calendar folder read
  as a first run. It now compares dates and reports both states. The
  guard hands /close-day the date it found, and /close-day closes
  exactly that note when given one — left to itself it picked today's,
  once /7plan had created it.
- The backup probe checked `origin` unless a push remote was set, and
  read an empty URL as "ok". It now resolves the remote the way
  `git push` does (explicit, else a lone remote, else origin), checks
  every push URL, reports an empty one as "none", and names the remote
  to remove when it is the framework.
- The last-note search covered two months; the drop check compared
  counts; three rules disagreed on escalating an untagged, undated
  carry. The search spans every month, the check goes item by item
  (parked and verified compass-hidden are the only exits), and the
  count-based table applies to exactly those carries.

tests/today-carries-and-probes.test.sh runs both probes, extracted from
the spec, under bash and zsh against temp vaults, and proves each carry
rule can fail with a mutated copy.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
…A routed, reports by closed date, snapshots by section

- The ecosystem.md redraw waited for `updated:` to pass 21 days, but
  every atomic append resets `updated:`, so a map receiving a note
  every few weeks was never redrawn. It now keys on its own
  `rederived:` date, which only a redraw moves (ecosystem.md only;
  profile.md has no full redraw defined).
- Tier A runs first and excises the Reinforced entries it routes; the
  growth.md feed-in then read them from the buffer. It now also reads
  those routed today, where they landed, via the ROUTED markers.
- Session reports were looked up by the clock's date; closing
  yesterday after midnight skipped yesterday's. Keyed on the date
  being closed.
- The first project snapshot read `limit:40` per note and stamped the
  snapshot current. It now reads Current State and To-Dos in full.

tests/close-day-derivation-and-sources.test.sh checks each property and
proves each can fail with a mutated copy.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
- aios-commit scanned the committed paths but not the commit message;
  a token in -m went into history. The message is now scanned too.
- aios-commit and pre-commit guarded the scan with `[ -x "$SCAN" ] &&`:
  a missing scanner, or one that lost +x, meant an unscanned commit.
  A missing scanner now refuses; one without +x runs through bash.
- secret-scan.sh used `grep -I`, which skips a file with a NUL byte;
  a token next to one passed. Now `grep -a`.
- aios-commit --vault discarded the sweep's git errors, and "no paths"
  read as "nothing to commit". Each failing sweep call now leaves a
  sentinel that refuses the commit and names why.

tests/commit-scan-fail-closed.test.sh reproduces each defect against
hooks rebuilt from a pinned pre-change commit (skipped, with a message,
when that commit is absent) and uses a PATH git shim to fail one sweep
call at a time.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
@modelcontextprotocol/server-github is deprecated on npm and was archived
upstream. No AIOS command, agent or skill calls its tools; sessions use
the gh CLI. Removes mcps/github-mcp, its setup block, the /mcps-setup
walkthrough, its SECURITY.md row and the lint's allowlist entry.
/aios:company now detects the GitHub substrate with `gh auth status`
instead of an active MCP.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
…m-py 0.8.2

Every Python MCP in setup.sh was guarded by `[ ! -d .venv ]`, so it
installed once and never again, whatever requirements.txt later pinned.
notebooklm-py was not pinned at all and sat at 0.3.4 on a live machine
while 0.8.2 shipped. Installs now stamp a CRLF-normalised hash of
requirements.txt in the venv and reinstall when it changes; an existing
venv with no stamp reinstalls once.

notebooklm-py is pinned to 0.8.2. 0.8 moves the session under
~/.notebooklm/profiles/<name>/ on first run, so manual_login.py and
save_storage.py now resolve that folder and fall back to the old root
(which 0.8 then migrates).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
…w what runs

Slack ran `npx -y @jtalk22/slack-mcp` unpinned (5.0.0 on a live machine)
while the repo vendored a 3.2.5 subset no registration executed. The
complete published 5.0.0 package is now vendored byte for byte, and every
AIOS-authored invocation pins the same version. Its dependencies stay
^-ranged, stated in SECURITY.md and UPSTREAM.md.

Atlassian is pinned to 0.23.1 (run.sh, setup.sh, README). Both versions
were smoke-tested against live accounts before pinning; both leave the
lint's known-unpinned list.

vendored-pins learns two record fields: package=<name>@<version> for a
runtime package (--upstream proves it against `npm pack`), and local=
for the framework's own files beside a vendored copy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
setup.sh stopped skipping skills/anthropic/ once it was measured that no
marketplace provides them; setup.ps1 still skipped them, so on Windows
every agent that declares one (doc-coauthoring, internal-comms,
theme-factory, mcp-builder, skill-creator) silently got nothing. A parity
test now fails if the two skip lists differ, and update.md's description
of the scan matches what both scripts do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
… too

Reported privately by an operator after v0.8.4. --setting-sources
user,project drops only the local layer, so a broad project rule such as
Bash(python3 <hooks dir>:*) reaches every headless Bash call outside its
--allowedTools; reproduced with a canary. MODEL-ROUTING.md now covers
both layers and gives the two ways out with their cost: narrow the rule
(interactive sessions then prompt more), or run the routine from its own
folder with --setting-sources project (measured: user rules no longer
apply). Bucket 29 reports broad interpreter/script-dir rules in both
layers and names both options.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
§ Spawning said "Writing a file is not gated." A kill of a live session
was held for the operator's approval in the session writing it; a resume
naming a session that never existed was not (probed; the surface
dead-lettered it as designed). The permission check judges what a
request does. CLAUDE.md now says so in the same bytes; the
orchestration-ladder skill carries the detail, what a brief planning a
kill must say, and the optional inbox-write permission rule, which stays
the operator's call.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
chuycepeda and others added 26 commits September 25, 2026 09:24
# Conflicts:
#	.github/workflows/validate.yml
#	CHANGELOG.md
…os-snapshot

# Conflicts:
#	.github/workflows/validate.yml
#	CHANGELOG.md
… false

# Conflicts:
#	.github/workflows/validate.yml
#	CHANGELOG.md
…keep every carry

# Conflicts:
#	.github/workflows/validate.yml
#	CHANGELOG.md
…at Tier A routed

# Conflicts:
#	.github/workflows/validate.yml
#	CHANGELOG.md
…p removes

On top of #179.
- Step 7's own cleanup line now removes the lock with the clone (clone
  first, lock last); before, only the closing Rules section did, so a
  model following Step 7 left the lock and blocked the next update.
- A lock older than 30 minutes is reclaimed by an atomic mv to a name
  unique to the run, so exactly one of two racing runs wins. /today and
  /close-day fire this command unattended; a crash no longer wedges every
  later update until someone clears it by hand.
- The residue sweep removes what it finds itself. Its report prints a
  newline inside a name as ^J, which cannot be typed back into rmdir.
  No {} inside the inner script: BSD find substitutes it mid-argument.
- The test's sweep extractor ends on a fixed string; BSD awk read the
  old \{\} as an interval, so extraction always ran to the end of the
  spec. New checks: the reclaim, the two-run race, removal itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
…above it

Re-vendors 14 of the 15 v6.4.1 skills unmodified (5bf4e78, 56/56 files
proven against upstream) and adds the MIT LICENSE the v5 copy lacked.
diagnosing-superpowers is left out: it files bug reports upstream and
searches GitHub with session terms without an approval step.

v6 runs plans continuously and rules on ambiguities ("Rulings, not
stalls"). skills/_index.md and technical-cofounder now state that
INTENT.md ask/escalate items still stop the run, which upstream's own
precedence rule (CLAUDE.md outranks skills) supports, and name what it
still does unasked: dependency installs in using-git-worktrees, and bash
scripts in executing-plans / subagent-driven-development. Stale
"checkpoints" descriptions are corrected. Extensionless scripts/ files
pin LF.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
…writing Comprehension

On top of #180. Its step 4.7 skipped both questions under --auto, which also skipped the scan behind the Comprehension field, so an unattended close could write "All operator-authored" and erase real comprehension debt. The scan now runs; only the question and the offer are skipped. "Everything the block must contain" narrows to "every answer the block needs from the operator" (Tier B candidates still form after step 5). A check pins the scan.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
…-closed gaps

On top of #176.
- Every hash helper (Step 3's and Step 3.4's) now uses shasum or, failing
  that, sha256sum. Refusing an empty hash alone left a machine without
  shasum backing up every file on every sync, permanently.
- A missing vault/.obsidian, like a missing layer, now reads as drift
  instead of an empty (clean) compare.
- A file with no local copy is added and never reported as backed up;
  the "any hash empty" row caught it before.
- The test simulates "no hash tool" with a restricted PATH, since a
  function override is found by `command -v`. It adds the sha256sum-only
  case, and disables commit signing in its fixture repo.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
… empty repo

On top of #183.
- `--vault` in a repo with no commits yet: `git diff HEAD` has no HEAD,
  so the sweep read as failed and the first commit refused. It now reads
  the staged paths there.
- `-a` reads a binary in full: a 200 MB attachment took ~12 s (main
  0.04 s). One grep pass carries every pattern, and files over 20 MB
  (AIOS_SECRET_SCAN_CAP_MB) are scanned as text only and named in the
  output, so the gap is visible. Now 0.38 s.
- Pre-existing: `| head -3` exited early, and once grep's output
  overflowed the pipe buffer grep died of SIGPIPE (141) under pipefail,
  so a real hit read as "FAILED to scan". Output now goes through `cut`,
  which reads it all and caps each reported line.
- Tests: the empty repo, the size cap, and ~1.3 MB of hits (fails on
  main with exit 141).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
- housekeeping (#178): an empty hash= was a prefix of every sha and read
  `current`; it now reads `unrecorded`. A package= record is checked
  against the registry's version, not a commit.
- close-day (#177): the "already edited" case archives the committed
  version through the helper, which keys the archive by file name.
- close-day (#181): without a date, the note to close is the most recent
  one dated on or before today, so the future-dated skeletons /7plan
  writes are never closed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
…les on a seed

On top of #182.
- The redraw threshold points at observed-staleness.py's AGGREGATE_DAYS
  instead of restating "21 days".
- An attempt that finds nothing to redraw (a seed ecosystem.md) sets
  rederived: alone, so a new vault is not redrawn at every close.
- The duplicate "reset updated:" is gone, and the closing sentence no
  longer credits the staleness alarm with a redraw it no longer triggers.
- Tier B finds routed entries by the marker prefix that is actually
  written and follows its [[target]] link; the quoted marker never
  matched the real one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
… v0.8.0

Steps 1 and 6 of the release cut were skipped for v0.8.0–v0.8.4: both manifests still read 0.7.1 and the release map still said "Unreleased after 2026-09-13". Both catch up in this release.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
- context-load-audit.py recognised primary sessions by a regex of one
  operator's session names. Primaries now come from USER.md's
  `## Identity` table (its first GFM table; italic example rows skipped)
  plus --primary. With none declared it aborts and says how to declare one.
- Its fit check (outward action without a declared/ read) only looked at
  the first --cap calls and ignored order. It now reads the whole
  transcript and asks whether a declared/ read came before the first
  outward action, within a single call too. --cap and --min-tools still
  measure the loading window.
- context-rungs.py priced rung 1 as the last five entries of every
  observed file, while the floor emits a rule library's index and the
  newest entries by date. The floor's per-file selection is now one
  function, recent_slice(), and rungs imports it. Floor output is
  byte-identical.

Tests: rung 1 equals the floor's emitted words per file; primaries,
parser counterexamples and fit ordering cases; old-hook reproductions
pinned to the pre-change sha. The parity test accepts the shared
recency constant.

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

AIOS-Session: 4ce35e79-7182-45d3-a479-d25e0004fda0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ontributor fixes

Replaces #175's early entry with one for the day. Action items are check-then-act: NotebookLM reinstall + login, GitHub MCP removal, Slack re-pin; session restart last.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
…ntity

CI runners have no git identity, so the fixture commit failed there and read as the regression it guards against. Identity and no-signing are passed per command, never set globally; reproduced with HOME emptied.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
.ps1 is checked out with CRLF by design, so on Linux CI the parsed name ended in \r and never equalled setup.sh's. Stripped before parsing; checked against a CRLF copy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
Found in a pre-merge rehearsal of this release against a copy of a live
vault. In a UTF-8 locale macOS's `tr -d '\r'` stops at the first byte
that is not valid UTF-8 and prints nothing, so every such binary (the
Slack icon, a PDF in skills/anthropic) hashed as the empty stream. Any
two of them compared "identical": an update to one never landed, and
Step 3.4's deletion test could read an edited copy as untouched. All
ten `tr` sites in update.md now run with LC_ALL=C. Test: two different
binaries hash differently, to the whole CR-stripped file; both checks
fail on the old helpers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
- `trap release EXIT INT TERM` released the lock on a signal and the loop
  kept archiving the remaining files with no exclusion. INT and TERM now
  release and exit (130 / 143).
- Copies went straight to the final name, so a copy cut short stayed as a
  valid-looking snapshot that every later run compared against. `put`
  copies to a hidden temp file in the same directory and renames it into
  place; the temp file is removed on failure and on release.

tests/aios-snapshot.test.sh: a cp shim that fails halfway, one that
SIGKILLs the archiver mid-copy, and TERM/INT during a slow copy (job
control on so INT reaches the background job). Each case also runs
against the hook pinned at the pre-change commit, where it reproduces
the defect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
@chuycepeda
chuycepeda marked this pull request as ready for review September 25, 2026 16:08
@chuycepeda
chuycepeda merged commit a1bdd93 into main Sep 25, 2026
16 checks passed
@chuycepeda
chuycepeda deleted the release/2026-09-25 branch September 25, 2026 16:08
@chuycepeda

Copy link
Copy Markdown
Member Author

Released as v0.8.5. Main moved once. A rehearsal of /aios:update against a copy of a live vault ran before the merge, and it caught binaries hashing as empty, fixed on the branch. The real dogfood came back clean: 133 files, zero backups, zero drift with a positive control, and NotebookLM moved 0.3.4 → 0.8.2 through the real action item.

Thank you for eleven contributions in one day (#175–#183, #185, #186). Each one fixed a check that could fail without saying so.

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