Skip to content

fix: BOM-tolerant JSON readers, decay persistence on no-match, Windows-safe Python detection (v4.6.2)#25

Open
Luispitik wants to merge 9 commits into
mainfrom
fix/bom-decay-python
Open

fix: BOM-tolerant JSON readers, decay persistence on no-match, Windows-safe Python detection (v4.6.2)#25
Luispitik wants to merge 9 commits into
mainfrom
fix/bom-decay-python

Conversation

@Luispitik

Copy link
Copy Markdown
Owner

Summary

Three independently verified fixes, found during a full audit of the learning pipeline (each reproduced A/B against main before fixing):

1. UTF-8 BOM silently disabled the whole pipeline — closes #16

JSON.parse throws on a UTF-8 BOM and every JSON reader wrapped it in a catch { exit 0 }, so an _instincts-index.json (or rules/registry/proposals file) saved by a Windows editor or a PowerShell redirect stopped occurrence tracking, learning, passive rules, dream, eod and the project-context bridge — with no error anywhere. All 6 core readers now strip the BOM before parsing; _generate-dashboard.py reads with encoding='utf-8-sig'. Writers never emit a BOM, so the first atomic write self-heals the file. Reported by @juanparisma — thanks for the precise repro.

2. Confidence-decay demotions were discarded on the no-match path

The v4.4 decay pass demotes stale instincts before matching, but the only index write sat after the no-match early-exit: on every tool use that matched nothing, demotions were recomputed and thrown away. Stale instincts effectively never decayed unless an unrelated instinct matched in the same invocation. The atomic write is now extracted into persistIndex() (dream-lock check and archived filtering preserved) and runs before the early-exit, logging demotions to _instinct.log as the matched path already did.

3. Microsoft Store python3 shim aborted install.sh and silenced observe.sh

Aligned with #24 (credit: @juanparisma for the diagnosis and the candidate-loop approach). One deliberate deviation: #24 pins minors 3.9-3.13, which silently rejects Python 3.14+ — reproduced locally, where 3.14.0 made observe.sh a no-op again, the same failure class the fix targets. This branch accepts any Python 3. and extracts the first word with native ${candidate%% *} expansion (no awk forks in the per-tool-use hot path).

Tests

  • New tests/test-bom-decay-python.sh: 12 tests (A/B BOM repros, decay persistence/archival on no-match, no spurious rewrites, fake Store shim functional run).
  • Full suite: 12/12 suites green on Windows Git Bash, including test-install-upgrade, test-security and test-dashboard.

🤖 Generated with Claude Code

Luispitik and others added 6 commits June 10, 2026 08:51
JSON.parse throws on a UTF-8 BOM, and every reader wrapped it in a
catch that exits 0 — so an index, registry, rules or proposals file
saved with a BOM (Windows editors, PowerShell redirects) silently
disabled the whole pipeline with no error anywhere.

Strip the BOM before parsing in _instinct-activator.sh,
_session-learner.sh (5 read sites via a readJson helper), _dream.sh,
_eod-gather.sh (canonical + legacy reads), _passive-activator.sh and
_project-context.sh. _generate-dashboard.py now reads JSON with
encoding='utf-8-sig', which accepts both BOM and BOM-less files.

Fixes #16

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v4.4 decay pass demotes stale instincts (confirmed 60d inactive
to draft, draft 90d inactive to archived) before matching, but the
only index write sat after the no-match early-exit. On every tool use
that matched nothing, demotions were recomputed and then discarded —
so in practice stale instincts never actually decayed unless some
other instinct happened to match in the same invocation.

Extract the atomic index write into persistIndex() (dream-lock check
and archived filtering preserved) and call it before the early-exit
when decayDirty is set, logging the demotions to _instinct.log as the
matched path already did.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On Windows, python3 commonly resolves to the Microsoft Store alias
shim: it answers command -v but does not execute (prints an install
notice and exits non-zero). install.sh then aborted the whole install
under set -e at PYTHON_VER=$(python3 --version), and observe.sh
silently piped every observation into the shim, recording nothing.

Iterate candidates — py -3 first (the real Windows launcher), then
python3/python/python3.x — and only accept a command whose --version
output actually starts with "Python 3.". The version echo in
install.sh is guarded with || true so a stray shim cannot abort under
set -e. No behaviour change on macOS/Linux, where py does not exist
and python3 is accepted on the second iteration.

Aligned with PR #24 (credit: juanparisma) with one deliberate
deviation: the PR pins minors 3.9-3.13, which silently rejects
Python 3.14+ — reproduced locally, where 3.14.0 made observe.sh
no-op again. Matching any "Python 3." keeps the original semantics
while still rejecting the shim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ction

12 tests in tests/test-bom-decay-python.sh:
- BOM (#16): functional — BOM-prefixed index still injects and
  increments occurrences; BOM-prefixed rules file still fires; static
  checks that the strip is present in all 6 core readers and that the
  dashboard reads utf-8-sig.
- Decay: functional — stale confirmed instinct is persisted as draft
  and stale draft is archived out of the index on a no-match tool
  use; fresh instincts cause no spurious rewrite.
- Python detection: static checks on observe.sh and install.sh plus a
  functional run where a Store-shim-like python3 is skipped and a
  Python 3.14 interpreter is accepted and executes the observer.

Verified A/B: 11/12 fail on main (the 12th is a both-sides guard),
12/12 pass with the fixes. All 11 pre-existing suites stay green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… hot path

The candidate loop ran cmd=$(echo ... | awk '{print $1}') per candidate,
two forks inside observe.sh which fires on every PreToolUse/PostToolUse
(process spawn is expensive under Git Bash on Windows). Native
${candidate%% *} expansion extracts the first word with zero forks.

Also adds the v4.6.2 CHANGELOG entry covering the three fixes in this
branch (#16 BOM tolerance, decay persistence on no-match, Windows-safe
Python detection aligned with #24).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The final banner still announced '2 global skills' (Step 7 copies 3:
sinapsis-learning, sinapsis-instincts, skill-router) and '5 hook
scripts' (Step 5 installs 6 plus the dream cycle since v4.5 added
_precompact-guard.sh). Cosmetic drift reported in #17.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Luispitik and others added 3 commits June 10, 2026 12:02
The new tests/test-bom-decay-python.sh suite guarded #16 and the decay
persistence fix only locally — the workflow still ran just the original
5 suites, so a regression would not have been caught on PRs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The workflow only exercised 5 of the 12 suites; dashboard, eod-gather,
install-upgrade, registry-isolation, v4.5 and v4.6 ran only on the
maintainer machine. Wire the remaining 6 in so every PR exercises the
full battery on the three platforms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every hook script was committed with mode 100644, so a fresh clone on
Linux/macOS has no executable bit anywhere — surfaced by running
test-v45-opus47.sh in CI, where T5 asserts -x on _precompact-guard.sh
(it passed on Windows only because Git Bash's -x check is lax).
Runtime never noticed because install.sh chmods on install and the
suites invoke scripts via bash. Set 100755 on the shebang scripts:
core/*.sh, _generate-dashboard.py, install.sh and observe.sh.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

_instinct-activator.sh silently stops updating _instincts-index.json when file has UTF-8 BOM

1 participant