- Cross-OS project de-dup in the registry (
_session-learner.sh): the per-project id issha256(remote || root), so a project with no git remote gets a different id on each OS (its root differs —/Users/me/ProjvsC:/Users/Me/Proj). When the registry is shared between machines (e.g. a synced folder), the same project surfaced as two entries — duplicating it in/projects,/eodand cross-project instinct search. The learner now matches on a cross-OS-stable key (the git remote when present, otherwise the project name), so the two sightings collapse into one entry: each per-OS id is preserved inaliases[], and each observed root is stored underroots.{posix,windows}. The name key is restricted to the genuine cross-OS scenario: it only matches an entry whose root belongs to a different OS family and that has no root of the candidate's family recorded, so two same-named projects on the same machine (~/work/appvs~/personal/app) always stay separate. Projects with a remote already shared one id and are unaffected; the remote key takes precedence over the name. Purely additive — the newaliases,rootsandcrossKeyfields are optional and ignored by readers that don't use them. - Migration pass for pre-existing duplicates: registries that already contain both per-OS entries (written by pre-feature learners and synced since) are cured on the next Stop event — entries with the same cross-OS key merge under the same family guard as the lookup, with union of
aliases/roots, the oldestcreatedand the newestlast_seen;crossKeyis backfilled for legacy entries. - Alias-aware consumers:
_eod-gather.shresolves observations recorded under an aliased per-OS id to the merged project, and/projectscounts observations from each alias'shomunculus/projects/{alias}/directory. - Name-key fusions are stamped and logged. The name fallback cannot rule out two unrelated projects that merely share a folder name (
app,api,docs) seen from a Windows and a posix machine — with no remote there is nothing left to tell them apart. Guarding it away would cost more than it saves: requiring a matching parent directory breaks the legitimate case, where one project lives at a different path on each OS. So the limit is accepted and made visible instead — every name-based fusion setsname_mergedon the entry, records the offending root pair inname_merge_log, and appends a line to_session-learner.log. A wrong fusion becomes an auditable event rather than silent registry drift.
- Session-learner false positives eliminated (#29, @juanparisma):
observe_v3.pysetis_errorwhenever the output contained the substringerror/failed— including the content of files read successfully, so aReadof source code containingthrow new Errorwas recorded as a tool failure. One real session produced 190 proposals of which 189 were junk. Detection is now (a) structural — harness-reported failure shapes, every tool; (b) hard failure markers over execution output (Bash,PowerShell), which is a verdict rather than arbitrary content; (c) hard markers over bare, short output from other tools.user_correctionno longer counts files re-edited by design (journals, control panels, daily summaries), andworkflow_chainrequires 5+ repetitions with at least two distinct tools and one non-generic tool instead of proposing everyBash>Bash>Bashtrigram. Measured over a real 8,000-observation window: 1,074 flagged observations drop to 22,error_resolutionproposals from 11 to 3,workflow_chainfrom 507 to 51. - Genuine non-Bash tool failures are no longer discarded. Restricting hard markers to
Bashleft realEditandReadfailures (String to replace not found,File does not exist) flagged by nobody — they are not harness-shaped, so the structural branch missed them too. Detection for these tools keys on shape: a failure returns a bare verdict string, a success returns a payload that serialises starting with{or[, and payloads are never scanned for marker words — that scan is the very bug this release fixes. Verified against 7,931 real non-Bash observations: zero false positives.isRealError()in the learner, which re-validates flags written by older observers still sitting in the observation window, stays deliberately tool-agnostic so PowerShell and MCP failures survive re-validation. - The session learner no longer discards its own diagnostics. The node block ended in
2>/dev/null, swallowing every message it emits — registry lock contention, failed upserts — and overriding theSINAPSIS_DEBUG=1redirect, so the debug switch produced nothing for the 95% of the script that runs inside that block. Diagnostics now append to_session-learner.log. - The cross-OS test suite could not run under Git Bash.
MSYS_NO_PATHCONV=1is needed suite-wide so posix fixtures survive argv conversion, butnewhome()still returned a posixmktemp -dpath, and node — a native Windows binary — resolves/tmp/...to a nonexistentC: mp.... Every fixture write died withENOENT: 0 of 10 tests actually ran on Windows while reporting green elsewhere. The suite now hands node a native path.
- New
tests/test-crossos-registry.sh— 13 hermetic tests (real hook driven via a sandboxHOME,MSYS_NO_PATHCONVexported for Git Bash): two-OS no-remote sighting collapses to one entry, bothroots.{posix,windows}recorded, the second per-OS id registered as an alias, remote key beats name for same-named projects, idempotency on re-run, same-named same-family projects stay separate (anti-regression, both roots intact), pre-existing duplicate migration (merge + union semantics), Git Bash/c/roots classifying as the windows family, and — for the accepted name-key limit — that a name fusion is stampedname_merged, that both roots reachname_merge_logfor audit, and that a remote fusion is not stamped, so the stamp means something. Registered in.github/workflows/tests.yml. Existing suites re-run clean:test-install-upgrade21/21,test-registry-isolation,test-eod-gather,test-bom-decay-python,test-v45-opus47,test-v46-opus48. - Secret scrubbing is verified for the first time. Test Group 4 of
test-security.shcalledimportlib.exec_module()onobserve_v3.pyand then looked for a function namedscrub_secrets. Both were wrong — the observer is a script (importing it runsmain()and blocks on stdin) and the real function isscrub, nested insidemainand unreachable from outside. Every assertion returnedLOAD_FAILand was reported as a SKIP, so a headline of "11/11 passed" covered scrubbing that had never been exercised. The group now drives the hook through the front door — aPostToolUsepayload carrying a real secret — and asserts the secret is absent from what reached disk, across JWT, GitHub, AWS, Stripe and Slack formats, plus a control asserting non-secret output survives so an over-broad scrubber cannot pass by destroying everything. - New
tests/test-error-detection.sh(10 tests): successfulRead/Bash/Grepwhose content mentions errors stay unflagged, realBash/Edit/Readfailures are flagged, long payloads carrying a marker inside their content stay clean, andisRealError()remains tool-agnostic. Note for anyone extending it: the observer resolves its config dir withexpanduser("~"), which on Windows readsUSERPROFILE, notHOME— both must be redirected or the test writes into real learning data. - Suite: 217 tests across 16 files, green on ubuntu, macos and windows.
-
Installers now wire hooks into an EXISTING
settings.json(external audit — iAmasters OS, finding #1): both installers used to print "merge hooks manually" and register nothing when the file already existed — which it almost always does — leaving a ghost install: files on disk, learning pipeline inert, no warning anywhere. Newcore/_merge-hooks.js(shared by install.sh and install.bat) creates the file when missing and otherwise deep-merges: only Sinapsis hooks whosecommandis not yet registered for that event are appended (dedup by trimmed command string), every existing entry — custom hooks, custom events, other plugins — is preserved untouched, a UTF-8 BOM is stripped (#16 family), a timestamped backup is written next to the file before modifying, and the write is atomic. A malformedsettings.jsonaborts the merge with a warning and is left untouched; the install still completes. -
Legacy cleanup archives instead of deleting (finding #2, install.sh — install.bat never had a legacy-cleanup step): Step 5b did
rm -rfover a hardcoded name list that can include a LIVE user directory (e.g. a still-activesynapis-learninginstall). Legacy entries are now moved toskills/_archived/legacy-<timestamp>/, and only successful moves are counted/reported (anmvblocked by open handles warns and leaves the entry in place instead of claiming it was archived). -
install.batactually installs skill subdirectories (finding #3): the skill copy loop usedxcopywithout/E, sohooks/observe.sh+observe_v3.pynever reached disk on Windows — the observer recorded nothing even with hooks correctly wired. Also fixed: stale%errorlevel%reads inside parenthesised blocks (thepythonfallback always reported "Python 3 not found" on machines withpythonbut nopython3; the settings success message could report stale status) now use!errorlevel!, and the backup timestamp no longer depends onwmic(deprecated, removed in Windows 11 24H2+) — it uses PowerShellGet-Date. -
Pre-merge adversarial review round (2026-07-18) caught and fixed three more before release: (a) the
::comments this PR initially added INSIDEinstall.batparenthesised blocks aborted the whole batch at parse time on cmd.exe (exit 255 — Steps 1-6 ran, so hooks got wired to scripts that never reached disk; comments moved out of blocks and a new static assert forbids indented::); (b)_merge-hooks.jswith"hooks": [](valid JSON, truthy) reported "merged" whileJSON.stringifysilently dropped the named properties set on the array — an empty array is now normalised to an object, any other non-objecthooksis refused with the file left untouched; (c)install.batpython detection trustedwhere python3, which the Microsoft Store shim satisfies — it now mirrors install.sh (py -3first, thenpython, accepting only a--versionthat reports "Python 3.").
- Version-consistency pass (finding #5): README title updated to v4.8; banners no longer pinned to stale versions ("v4.4 hooks" / "v4.5 hooks");
/cloneno longer advertised by either installer (Step 5b archives it as legacy);docs/quickstart.mdcleaned of thesynapis-*era (skills listing,/clonesection,[SYNAPIS]labels, uninstall paths).
- New
tests/test-installer-hardening.sh— 21 tests:_merge-hooks.jsunit (create from template, merge into hook-less file, custom hooks/events preserved, idempotency, partial-wiring dedup, BOM strip, malformed file untouched, empty/non-emptyhooksarray), install.sh end-to-end over a pre-existingsettings.json(wires all 7 hooks, double-install dedup, malformed file survives), legacy archiving (content intact under_archived/legacy-*), static installer asserts (xcopy/E, nowmic,!errorlevel!in block reads, no indented::), a version-consistency gate between CHANGELOG, installers and README, and — on Windows hosts — a smoke test that EXECUTESinstall.batin a sandboxedUSERPROFILEand asserts exit 0,observe.shon disk, 7 hooks wired and commands installed (static greps cannot catch cmd.exe parse-time aborts). Registered in CI.
- Sinapsis returns to individual-only autonomous learning. The team layer (Plexus:
shared knowledge over a private git repo, PM directives, metadata-only traceability,
quarantine trust model) shipped here as v4.7.0 and now lives in the private team edition,
where team-oriented development continues. Same clean-extraction pattern as the gstack
separation in v4.3.2:
core/_plexus-sync.sh,commands/plexus.md,docs/PLEXUS.mdandtests/test-plexus.shremoved; installers and CI updated. v4.7.0 remains in the git history under MIT. - The public installer neither installs nor removes
_plexus-sync.sh/plexus.md: those files may belong to a team-edition install layered on top of this one, so the v4.8 upgrade never touches them (deliberately NOT added to the legacy-cleanup list). - New
tests/test-plexus-separation.shguards the boundary: no plexus references in live code paths (core/, commands/, installers, CI); history and changelog references stay.
A plexus is the anatomical level above the synapse: a network of nerves from multiple origins interweaving and redistributing signal with no center. Wire your team's synapses into one nervous system — peer-to-peer over git, no server, trust earned by your own use.
- Team knowledge sharing over plain git (
core/_plexus-sync.sh+/plexus): a development team pools the instincts and project context each member's Sinapsis learned autonomously, through a private per-team git repo.init/join/pull/share/review/directive/ log/context/status/leavesubcommands, all deterministic bash + node — no LLM, no server, no accounts. Decision and trust model documented indocs/PLEXUS.md(same repo as an optional module, NOT a fork: the layer reuses the existing pipeline and touches zero hooks). - Trust rules: shares require
confirmed/permanent(your usage must validate what you publish); imports enter asdraft(quarantine — never injected until the importer's own usage validates them via the existing occurrence tracking/auto-promote, or/promote);permanentis never importable; id collisions with personal instincts are skipped (personal wins); teammate revisions re-enter quarantine./plexus reviewmakes the quarantine actionable: pending imports, who shared them, distance to auto-promote. - PM directives (
/plexus directive add|list|supersede): project guidelines live as versioned frontmatter files indirectives/in the team repo — human-readable, deterministically parseable, git history as audit trail. Deliberately no code path imports a directive into the instincts index: top-down curated content injected as instincts is the rejected seeds model (#8). - Traceability, not surveillance (
/plexus log):activity/<member>.ndjsonrecords metadata of knowledge contributions only (author, action, id, revision) — never session text, never consumption. Written by share/directive/context-push inside the same commit. - Schema contract:
sinapsis-plexus.jsoncarriesschema_versionwith an additive-only policy, so external consumers (dashboards, audit engines) can build on the team repo without coupling to Sinapsis's release cadence. - Safety: share, pull, directives and context push scrub with the same 8 secret patterns
as
observe_v3.py(defense in depth); imports validate id shape (path-traversal safe), reject ReDoS-prone triggers (same nested-quantifier guard as the passive activator) and cap inject length. An import ledger per team makes pull idempotent and prevents resurrection of instincts the operator deleted or downvoted. - Per-project agent context:
/plexus context pushpublishes the current project'scontext.md(scrubbed) keyed by git remote — the cross-machine-stable key; teammates get it on/plexus pullbefore their first session in that repo.
- New
tests/test-plexus.sh— 28 hermetic tests driving the real script against a local bare git remote with two sandboxed members: init/join/bootstrap, share gate + scrubbing + attribution + push, draft quarantine, origin provenance, idempotent pull, no-resurrection, personal-wins collision, permanent cap, hostile-payload rejection (ReDoS + traversal id), revision re-quarantine, directives (create/push/list/supersede/path-safety/never-imported), metadata-only activity ledger, log timeline, review queue, schema_version, leave--purge. Registered in CI.
- A UTF-8 BOM silently disabled the whole pipeline (#16, reported by @juanparisma):
JSON.parsethrows on a UTF-8 BOM, and every JSON reader wrapped it in a catch that exits 0 — so an_instincts-index.json(or registry/rules/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 readers now strip the BOM before parsing:_instinct-activator.sh,_session-learner.sh(5 read sites via areadJsonhelper),_dream.sh,_eod-gather.sh,_passive-activator.sh,_project-context.sh;_generate-dashboard.pyreads withencoding='utf-8-sig'. Writers never emit a BOM, so the first atomic write after a read self-heals the file. - Confidence-decay demotions were discarded on the no-match path (
_instinct-activator.sh): the v4.4 decay pass demotes stale instincts (confirmed 60d inactive → draft, draft 90d inactive → 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 thrown away. In practice stale instincts never decayed unless some other instinct happened to match in the same invocation. The atomic write is now extracted intopersistIndex()(dream-lock check and archived filtering preserved) and runs before the early-exit when decay demoted anything, logging the demotions to_instinct.logas the matched path already did. - Microsoft Store python3 shim aborted install.sh and silenced observe.sh (aligned with #24, credit: @juanparisma): on Windows,
python3commonly resolves to the Store alias shim — it answerscommand -vbut does not execute.install.shaborted underset -eatPYTHON_VER=$(python3 --version), andobserve.shpiped every observation into the shim, recording nothing. Both now iterate candidates (py -3first, the real Windows launcher) and only accept a command whose--versionoutput reportsPython 3.— any minor, deliberately not pinned to 3.9-3.13 as #24 proposed, which would silently reject Python 3.14+ (reproduced locally: 3.14.0 made observe.sh a no-op again). First word extracted with native expansion (no awk fork in the per-tool-use hot path). No behaviour change on macOS/Linux, wherepydoes not exist and the loop falls through to the realpython3.
- New
tests/test-bom-decay-python.sh— 12 tests: A/B BOM repros for the activator (injection + occurrence persistence), BOM-strip presence in all 6 core readers,utf-8-sigin the dashboard generator, a functional BOM-prefixed passive-rules run, decay demotion/archival on the no-match path, no spurious rewrites for fresh indexes, and Python detection (candidate loop asserted in both installers; functional run with a fake Store shim that answerscommand -vbut fails--version, asserting the real interpreter is selected).
- Registry filename collision with skill-router / external launchers (
_session-learner.sh,_eod-gather.sh,_generate-dashboard.py, commands, installers): Sinapsis used~/.claude/skills/_projects.jsonas its canonical project registry, but that filename is also used by the bundledskill-routerskill (and other launchers) with a different schema. On a machine where a launcher owns_projects.json, the session-learner upsert (added in v4.3.3) would append Sinapsis hash-entries into the launcher's registry on every Stop event, mixing two schemas in one file. Sinapsis now owns a dedicated~/.claude/skills/_sinapsis-projects.json;_projects.jsonis left entirely to skill-router. The learner re-populates the new registry automatically on Stop events (no migration needed), and_eod-gather.shreads it with the legacyhomunculus/projects.jsonfallback unchanged. - Template
core/_projects.jsonrenamed tocore/_sinapsis-projects.json; the installers seed/chmod/preserve the new name and no longer create or touch_projects.json.
- New
tests/test-registry-isolation.sh: asserts the learner and gather target_sinapsis-projects.jsonand that nocore/file references the launcher's_projects.json.tests/test-install-upgrade.shandtests/test-eod-gather.shupdated to the new filename.
- Caps re-tuned for Claude Opus 4.8 (
_instinct-activator.sh,_session-learner.sh):MAX_INSTINCTS_INJECTED6 → 8,TOKEN_BUDGET4000 → 6000, learner observation window 5000 → 8000 lines. Opus 4.8 keeps long context on-task with fewer compactions and better compaction recovery, so a richer per-turn instinct injection and a longer cross-session window for the learner carry no quality regression. The 1M context window is unchanged from Opus 4.7. - Prompt-cache fit improved, no code change. Opus 4.8 lowers the minimum cacheable prompt to 1,024 tokens and adds mid-conversation
role: "system"messages — the exact shape of Sinapsis's per-turnsystemMessageinjection — so the byte-stable instinct block introduced in v4.5 caches more readily (~90% read discount once warm). - Hot path remains model-free. The activator and learner are still pure bash/node. Opus 4.8's
effortparameter defaults tohighin Claude Code; Sinapsis needs no change because it never calls the model directly. - RFC
docs/rfc-v5-adaptive-thinking.mdretargeted toclaude-opus-4-8: the opt-in/analyze-sessionSDK path now uses adaptive thinking with theeffortparameter (budget_tokensis rejected on Opus 4.7+). Multi-agent blueprint Architect tier moved Opus 4.7 → 4.8.
- New
tests/test-v46-opus48.sh: asserts the re-tuned caps (TOKEN_BUDGET>= 6000,MAX_INSTINCTS_INJECTED= 8, learner window >= 8000) and that no staleclaude-opus-4-7model ID remains indocs/orcore/. - Existing suites re-run clean, including
test-v45-opus47(cap assertions use>=, so they still pass).
/eodreported 0 projects for non-git folders (core/_eod-gather.sh):observe_v3.pywrites observations for a non-gitcwdto the roothomunculus/observations.jsonlwithproject_id: "global"(theproject_nameis still correct), but the gather only walkedhomunculus/projects/<hash>/and never read the root file. The writer and reader disagreed on where non-git projects live, so a full day of activity in any non-git folder surfaced as 0 in/eod. The gather now also reads the root file, grouping its observations byproject_name. Reported by @NestorPVsf.- Cross-OS gather robustness (
core/_eod-gather.sh): for users syncingobservations.jsonlbetween macOS and Windows (e.g. via Nextcloud), the file mixesC:\…and/Users/…paths. Node'spath.basenameis platform-specific (the POSIX build ignores\), so "files touched" came out mangled on the foreign OS, and the gather could try togitagainst the other machine's path. Added abaseName()that splits on both/and\; roots that don't exist on the current machine are skipped before anygitcall;HOME || USERPROFILEis resolved; and projects are merged byproject_nameso the same project from two machines collapses into one entry. Reported by @NestorPVsf.
- New suite
tests/test-eod-gather.sh— 8 hermetic tests (viaSINAPSIS_HOMUNCULUS/SINAPSIS_SKILLSoverrides) covering root-file detection, name grouping, cross-OS basename, subdir+root merge, today-only filtering, empty-dir graceful exit, the canonical_projects.jsonloader, and output shape. - Existing suites re-run clean:
test-security11/11,test-gstack-separation18/18.
- Cache-stable instinct ordering (
_instinct-activator.sh): added alphabeticalid.localeComparetiebreaker after the priority + occurrences sort. The injectedsystemMessageprefix is now byte-stable across consecutive tool uses with the same match set, which is the prerequisite for prompt-cache hits on Opus 4.7's cached system block (~90% discount on input tokens once the cache warms). PreCompacthook (core/_precompact-guard.sh, new): fires right before Claude Code compacts the context in long-running sessions and re-invokes the session-learner so fresh observations are flushed to proposals before the transcript is rewritten. Usestimeout 8and a fire-and-forget pattern to never block the harness; relies on the existing advisory lock inside_session-learner.shfor parallel safety.settings.template.jsonnow declares the new PreCompact hook (hooks 6 → 7).install.shcopies and chmods_precompact-guard.sh.- RFC
docs/rfc-v5-adaptive-thinking.md: design for an opt-inSINAPSIS_LLM_ANALYZE=1branch in/analyze-sessionthat uses Opus 4.7 adaptive thinking via the Anthropic SDK. Not implemented in this release — ships as a design doc so the core stays fully deterministic until the approach is validated.
_instinct-activator.sh:TOKEN_BUDGET1500 → 4000, top-N per tool use 3 → 6 (MAX_INSTINCTS_INJECTED). With Opus 4.7's 1M window and prompt caching the cost of the extra injection is amortised, so we can surface more instincts per turn._session-learner.sh: observation window 1000 → 5000 lines. Cross-session detectors (repetitions, agent patterns) now see a longer history without paging._operator-state.jsond017: Scout/Analyst blueprint switched from Haiku to Sonnet 4.6 per operator preference; Architect stays on Opus (now 4.7).
- New suite
tests/test-v45-opus47.sh— 11 TDD tests covering deterministic ordering (shuffled-index byte-identical output, alphabetical tiebreaker), PreCompact hook (file present, executable, wired in settings and install.sh), and raised caps. - All existing suites re-run clean:
test-install-upgrade21/21,test-dashboard12/12,test-dream25/25,test-gstack-separation18/18,test-security11/11,test-v433-hardening14/14.
Opus 4.7 brings three things Sinapsis can actually use: a stable 1-hour cache TTL that rewards byte-stable prefixes, a 1M context that removes pressure on per-turn caps, and the PreCompact hook Anthropic now ships in Claude Code. None of the "flashy" features (memory tool, context editing) are a natural fit: Sinapsis already is a memory system and the inject happens in a stable systemMessage. The v4.5 changes are purely about making the existing design richer and cheaper to run on top of Opus 4.7, without introducing a new LLM dependency in the hot path.
_generate-dashboard.pycrashed on_catalog.jsondict schema (regression from v4.4.0):collect_skills()iteratedcatassuming a flat list, but the canonical catalog is{globalSkills: [...], librarySkills: [...]}. On any fresh v4.4.0/v4.4.1 install, the very first/dashboard-sinapsisrun raisedAttributeError: 'str' object has no attribute 'get'. Fix: detect dict vs list shape, concatenateglobalSkills + librarySkills, derive real global count from the dict instead of hardcoding 5, and guard all.get()calls withisinstance(s, dict)so mixed content cannot crash. Reported in #6 by @fvayas, fixed in #7 by @NestorPVsf.
_session-learner.shline 277 — bash quoting bug (regression): the regex["']?insidenode -e '...'closed the bash single-quoted string prematurely, causing every Stop event to crash withsyntax error near unexpected token (. Pattern 4 (repetitions) and Pattern 5 (agent-patterns) never ran. Replaced literal'in the regex char class with the JS unicode escape\u0027. Added regression test (bash -nof allcore/*.sh)._projects.jsonwas never populated: every reader (/projects,/eod,/instinct-status,/evolve,/backup,_session-learner.sh,_eod-gather.sh) consulted_projects.jsonorhomunculus/projects.jsonbut no hook ever wrote to either. The registry stayed empty forever, so_eod-gather.shcould not resolvehash → name(showed raw 12-char hashes),/projectswas always blank, and cross-project instinct search returned nothing._session-learner.shnow upserts the canonical~/.claude/skills/_projects.json(array schema) on every Stop event with{id, name, root, remote, created, last_seen}. Project name is sourced from observationproject_name(already written byobserve.sh) with legacyhomunculus/projects.jsonfallback. Atomic write via tmp + rename. Advisory lock file (_projects.json.lock) withO_EXCL+ backoff + stale detection prevents lost updates when parallel Stop hooks fire concurrently. Idempotent._eod-gather.shregistry path: switched primary source to canonical~/.claude/skills/_projects.json(array schema) so/eodresolves names correctly. Legacyhomunculus/projects.json(map schema) kept as fallback for back-compat._catalog.jsontrailing comma: invalid JSON. Pythonjson.load()failed; Node tolerated but it is fragile. Removed the comma._session-learner.shderivesroot/remotefrom observationcwd: derive them viagit rev-parse --show-toplevel+git remote get-url originagainst the most recent observationcwd. POSIX/c/foopaths are normalized toC:/fooon Windows so nativegit.exeaccepts them. Without this,_projects.jsonentries had blankroot/remoteeven when upsert succeeded.observe_v3.pynow writescwdinto every observation: the session-learner readslines[i].cwdto rungit rev-parse, but the hook never wrote that field — so root/remote stayed empty on fresh installs. Addedcwdto the observation dict.
- 4 new regression tests in
tests/test-install-upgrade.sh(Test Group 6): bash syntax of allcore/*.sh,_projects.jsonupsert detectsname, idempotency on repeat run, and an end-to-end TEST 14 that pipes a real payload throughobserve.shinto a real git sandbox and verifies session-learner derivesroot/remotefrom observationcwdviagit rev-parse.
/dashboard-sinapsiscommand: regenerates~/.claude/skills/_dashboard.html— a self-contained visual dashboard with real data parsed from all pipeline files. Editorial design (Instrument Serif + warm accents on deep ink).core/_generate-dashboard.py: deterministic Python generator. Parses_instincts-index.json,_passive-rules.json,_passive.log,_instinct-proposals.json,_instinct.log,_catalog.json,_projects.json,_operator-state.jsonandhomunculus/projects/*/observations.jsonl. Computes hero KPIs, velocity (new instincts per week), hour-of-day distribution, 21-day activity heatmap, maturation averages (add→first_triggered), funnel metrics, top-10 leaderboards and dead-instincts list. Portable: honors$SINAPSIS_HOMEenv var or falls back to~/.claude/.core/_dashboard-template.html: HTML template with/*__SINAPSIS_DATA__*/nullinjection marker. Chart.js + Google Fonts via CDN. Dark editorial theme with serif display + Inter + JetBrains Mono. Responsive.- 12 TDD tests (
tests/test-dashboard.sh): portability, template substitution, metric computation, dead detection, level counting, domain aggregation, empty-state graceful handling.
install.sh: +2 files installed (_generate-dashboard.py,_dashboard-template.html)- Test badge: 83 → 95 passing
Sinapsis already had /instinct-status, /passive-status and /system-status for terminal inspection. None gave a holistic, at-a-glance view of the learning system's health, velocity or maturation timings. The dashboard surfaces what the existing commands couldn't: how fast you're learning, when the system fires, and where the dead weight is.
/downvotecommand: demote or archive instincts that give bad advice. Closes the feedback loop.- 3 extra scrubbing patterns in
observe_v3.py: Stripe (sk_live/sk_test), Slack (xoxb/xoxp), SendGrid (SG.*). Now 8 patterns total (was 5). - Path traversal protection in
_instinct-activator.sh: blocks inject content containing../,~/,/etc/,/proc/. - Token budget cap (
TOKEN_BUDGET=1500): limits total chars injected per tool use. Prevents instinct loops. - Multi-session auto-promote: drafts now require 5+ occurrences AND 3+ distinct sessions to promote. Tracks
sessions_seen[]per instinct. (Was: 5+ occurrences in any number of sessions.) - 2 new pattern detectors in
_session-learner.sh: repetitions (same error in 3+ sessions) and agent patterns (subagent error capture). Now 5 detectors total (was 3). - GitHub Actions CI: test suite runs on push/PR across Ubuntu, macOS, Windows.
- Pre-push hook:
.githooks/pre-pushblocks push if any test suite fails. Enable:git config core.hooksPath .githooks - Legacy file cleanup in
install.sh: removes obsolete files from v3.2/v4.4 on upgrade (gstack skills, old skill names, clone.md).
observe_v3.py: 5 → 8 scrubbing patterns_instinct-activator.sh: path traversal check, budget cap, multi-session tracking_session-learner.sh: 3 → 5 pattern detectors (+ repetitions + agent patterns)install.sh: legacy cleanup step added
/backup [path]command: export full Sinapsis state to a portable folder for sync or migration between machines. Exports instincts, rules, operator state, commands, settings, CLAUDE.md + manifest./restore [path]command: import Sinapsis state from a backup folder with intelligent merge (by ID, keeps local occurrence data, asks before overwriting machine-specific files)./cleanupcommand: clean homunculus directory — removes v1 legacy files (config.json, identity.json, instincts/, evolved/, exports/, root observations), orphan projects (30+ days inactive), and old archives (60+ days).
- 14 new TDD tests (
tests/test-v433-hardening.sh)
/review-army,/cso-audit,/investigate-proskills moved out (engineering tools, not learning)/retro-semanalcommand moved out (reporting, not learning)_timeline-log.shhelper moved out (infrastructure for removed skills)__pycache__/observe_v3.cpython-314.pycremoved from git tracking- All 5 components archived to
~/.claude/skills/_archived/sinapsis-gstack/with recovery guide - Version badges and references cleaned back to v4.3
- Confidence decay in
_instinct-activator.sh(learning hygiene — confirmed 60d→draft, draft 90d→archived) - Cross-project search in
/instinct-status --cross-project(learning infrastructure)
- Confidence decay in
_instinct-activator.sh: confirmed(60d inactive) -> draft, draft(90d inactive) -> archived. Permanent never decays. Credit: garrytan/gstack learnings confidence decay. /review-armyskill: 5 specialist parallel code review (security, nextjs, supabase, performance, testing). Fix-First workflow, quality scoring. Tested live on mission-control (8.5/10, 3 findings, 0 false positives)./cso-auditskill: OWASP Top 10 + STRIDE + supply chain + LLM security audit. Daily mode (8/10 gate) and comprehensive mode (2/10 gate)./investigate-proskill: 4-phase systematic debugging (investigate -> analyze -> hypothesize -> implement). Iron Law: no fix without confirmed root cause. Scope freeze via hooks.- Session timeline (
_session-timeline.jsonl): JSONL event log for skill usage tracking, context recovery, and retrospectives. Helper:_timeline-log.sh. /retro-semanalcommand: Weekly metrics across all projects — commits, skills used, instincts activated, health score trend, recommendations.- Cross-project instinct search in
/instinct-status --cross-project: search instincts across all registered projects in_projects.jsonwithout promoting.
_catalog.json: +3 skills (review-army, cso-audit, investigate-pro)/instinct-status: rewritten for v4.4 data model (draft/confirmed/permanent levels, occurrence tracking, cross-project search)
- garrytan/gstack (23 YC engineering skills): confidence decay, review army, CSO audit, investigate, retro, session timeline, cross-project search
- Full analysis:
gstack-integration-analysis.md
- #1-3: install.sh preserves user data on upgrade (instincts, rules, projects, operator state)
- #4/5A: execFileSync replaces execSync (command injection prevention)
- #5: Auto-promote works correctly (drafts track occurrences without injecting)
- #6: Race condition fix (dream lock check before index write)
- #7/5E: fcntl.flock on JSONL writes
- #8: Token catalog corrected (9,995 → 6,915 after cleanup)
- #9: install.bat synced to v4.3.1
- #10-11: Command schemas match reality
- #12/5C: ReDoS protection on trigger patterns
- #13: Jaccard Unicode support
- #14: Contradiction false positive reduction
- #15: session-end/eod documented
- #16: tmpdir cleanup
- #17: session-learner selects by recency not hash
- #18: operator-state schema flag
- #19: KNOWLEDGE_FILE dead code removed
- #20: synapis → sinapsis rename
- #22: SINAPSIS_DEBUG mode
- 5B: +4 secret patterns (GitHub, JWT, AWS, Stripe)
- 5D: chmod 600 on data files
- 5F: Inject sanitization (500 char limit + blocked patterns)
- Removed:
skills/sinapsis-researcher/(contradicts d011 — moved to on-demand) - Removed:
skills/sinapsis-optimizer/(90% duplicated bycommands/skill-audit.md) - Removed:
commands/clone.md(100% duplicated by skill-router Section 4) - Removed:
docs/synapis-technical-docs.docx(typo + obsolete v3.2 content) - Fixed: Portable find in
_session-learner.sh(stat fallback for macOS) - Fixed: fcntl Windows compatibility in
observe_v3.py(try/except fallback) - Fixed: install.bat now creates
.last-learnmarker - Fixed:
_catalog.jsonreduced to 3 global skills (was 5) - Fixed:
.gitignoreexpanded from 1 line to 12 patterns - Token savings: ~4,080 tokens/session (~41% reduction)
- 52/52 GREEN (25 dream + 11 security + 16 orchestrator)
- Dream Cycle (
core/_dream.sh): 5-module index hygiene system inspired by Anthropic's AutoDream- Module 1: Duplicate detection (Jaccard word tokens, threshold 0.80)
- Module 2: Contradiction detection (7 opposing keyword pairs, EN+ES)
- Module 3: Staleness scoring (fresh/stale/archive_candidate/never_activated)
- Module 4: Trigger pattern validation (regex validity, overly broad, cross-domain overlap)
- Module 5: Index health metrics and score (0-100)
/dreamcommand (commands/dream.md): Interactive dream cycle with merge/archive actions- Auto-archive: drafts with 0 occurrences and >90 days old
archivedarray in_instincts-index.jsonfor non-destructive archival_dream-report.md: Human-readable report with executive summary and findings_dream.log: Audit trail for dream cycle actions- Lock file (
_dream.lock) with 1-hour stale detection
- 25 TDD unit tests (
tests/test-dream.sh) - 15 E2E integration tests (
tests/test-e2e-dream.sh) - Total: 40 new tests (was 78, now 118)
- Health score formula now penalizes
never_activatedinstincts (-5 each) - Empty index generates minimal report instead of silently exiting
- Multi-project /eod:
_eod-gather.shdeterministic script scans ALL projects worked today via homunculus, aggregates git data per project root, outputs structured JSON for consolidated EOD summary _eod-gather.sh: new helper script incore/— reads homunculus/projects/ for today's observations, cross-references projects.json for names/roots, runs git log/status/branch per project/session-endcommand: added tocommands/— was missing from installer, users couldn't see the command- E2E pipeline test: 25 tests across 6 stages (observe → learn → activate → gather → bridge → integrity) in isolated sandbox
- 12 TDD tests for
_eod-gather.sh: multi-project detection, stale skip, hash fallback, observation counts, schema validation
projectNamescope bug in_session-learner.sh: variable was declared inside JOB 1 try/catch but used in JOB 2 outside it →ReferenceErrorsilenced by2>/dev/null— proposals were never written since v4.2.0. Discovered by E2E test.eod.mdsingle-project limitation: now uses_eod-gather.shinstead of running git commands against current directory only
- Test count: 37 → 78 (21 unit + 12 TDD + 25 E2E + 20 security)
install.shversion bumped to v4.2.2, now copies_eod-gather.sh
- Occurrences tiebreaker in domain dedup: when two instincts share the same domain and level, the one with more occurrences wins (inspired by fs-cortex confidence granularity — credit: Fernando Montero)
- Domain pre-filter by project stack: reads
context.mdto detect project tech, skips instincts from irrelevant domains before regex matching
- Instinct activator sort: level priority preserved, occurrences used as secondary sort key
- Domain dedup:
ALWAYS_DOMAINSset (general, git, security, operations, quality) always passes pre-filter
- 3 pattern detectors in
_session-learner.sh: error-fix (improved), user-corrections, workflow-chains - Occurrence tracking in
_instinct-activator.sh: each instinct match incrementsoccurrences,first_triggered,last_triggered - Auto-promote: draft instincts with 5+ occurrences automatically promoted to confirmed
- Atomic writes: instinct-activator uses tmp + rename to prevent index corruption
- Enriched proposals:
project_name,sample_input,sample_outputin every proposal - 13 TDD tests covering all 3 patterns + occurrence tracking + auto-promote + atomic writes
- Session learner window: 100 → 1000 lines (covers parallel sessions)
- Instincts index schema v4.2: added
occurrences,first_triggered,last_triggeredfields
- 97% of observations were silently discarded per session (100/~3000+)
- Proposals were generic — now include project context and samples
_project-context.sh had a stray break (line 57) outside the conditional block. If today's EOD summary didn't exist, the loop would exit immediately without checking yesterday's file. The flagship auto-resume feature was completely non-functional.
README, CHANGELOG, install output, and multiple SKILL.md files all referenced /analyze-session, but the actual command file was named analyze-observations.md. Renamed to analyze-session.md and rewrote content for v4.1 proposals workflow.
- Added
_daily-summariesdirectory creation (missing —/eodwould fail on Windows) - Added Python 3 detection with warning (was silent)
- Fixed Node.js path quoting using
process.argv(paths with spaces would break)
_session-learner.sh uses find -newer .last-learn which would fail noisily on first run. Installer now creates the marker file.
_instincts.json→_instincts-index.json(8 files)_observations.json→~/.claude/homunculus/projects/{hash}/observations.jsonl(3 files)- Fixed
skills/homunculuspath →homunculus(noskills/prefix) - Fixed
lastSeenfield reference → v4.1 schema fields
- Bumped version 3.2 → 4.1 in
_catalog.json,_projects.json,_operator-state.template.json - Renamed "Synapis" → "Sinapsis" across all
.mdand.jsonfiles - Skill Router header: v3.0 → v4.1
settings.template.json: corrected hook count 7/Stop(2) → 6/Stop(1)
- Rewrote
synapis-instincts/SKILL.md: replaced 0.0-1.0 lifecycle model with draft/confirmed/permanent - Rewrote
instinct-status.md: dashboard now shows levels and domain dedup - Rewrote
promote.md: promotes confirmed → permanent (not project → global) - Updated
evolve.md: filter criteria uses levels, not confidence decimals
Replaced substring matching ("error" in output) with word-boundary regex patterns. Prevents false positives like "0 errors found" from being flagged as errors.
Removed creation of unused directories (instincts/personal, evolved/skills, etc.) per project. Only creates the project directory itself.
The observation→learning→injection pipeline is now fully connected end-to-end:
observe.sh(PreToolUse + PostToolUse): writesobservations.jsonlper project_session-learner.sh(Stop hook): reads observations, detects error patterns, writes_instinct-proposals.json/analyze-session: review proposals, accept → add to_instincts-index.json_instinct-activator.sh(PreToolUse): reads index, injects matched instincts assystemMessage
_session-learner.sh writes context.md per project at session end (project name, last session date, files touched, gotcha count hint).
_project-context.sh reads it at the first PreToolUse of the next session — fires once per session via session_id flag.
_instinct-activator.sh groups instincts by domain. One instinct per domain is injected, max 3 total.
Prevents multiple contradictory instincts from the same area firing simultaneously.
Priority: permanent > confirmed.
Replaces the 0.0–1.0 decimal scoring with 3 explicit levels:
draft: proposed by session-learner, not injected. Review with/analyze-session.confirmed: validated by user. Injected silently when trigger matches.permanent: explicitly promoted via/promote. Highest priority in domain dedup.
Central instinct registry. Replaces scattered YAML files.
Fields: id, domain, level, trigger_pattern, inject, origin, added.
Origin values: manual (curated) or learned (from session-learner).
Documents the 6-hook architecture with comments. Copy/merge into ~/.claude/settings.json.
v3.2 claimed Sinapsis "observes passively in real-time." This was inaccurate.
v4.1 is explicit: hooks are deterministic bash scripts. Claude does NOT analyze observations during a session.
Analysis happens at Stop (deterministic) or on demand (/analyze-session).
- 2 global skills always active (was 5): skill-router + sinapsis-learning
- Instinct injection: ~50–200 tokens per matching tool use (only matched instincts)
- Passive rules: ~20–80 tokens per matching tool use (only matched rules)
- Full
_instincts-index.jsonand_passive-rules.jsonread by hooks, not loaded into LLM context
v3.2 session-learner generated 80+ noise proposals per day (workflow sequences, tool preferences).
v4.1 only detects error_resolution patterns (error → same tool success within 5 events), with dedup per tool per day.
Skills on Demand architecture. Passive rules, skill router, operator state, 5 global always-on skills.