Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@ All notable changes to fs-cortex will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [4.3.1] — 2026-07-05

### Fixed
- **Retired laws now cascade to the instinct pool instead of dying in
`laws/archive/`** (operator feedback on v4.3.0). The auto-swap retirement
and `demote_law_to_domain` share the new `_restore_backing_instinct()`:
the victim's backing YAML is restored into `instincts/global/` with
`law_eligible: false` (blocks the re-promotion ping-pong), so the knowledge
keeps injecting via PreToolUse and decays/archives through the normal
instinct lifecycle if it truly stopped mattering. Knowledge-cascade instead
of two-layer loss: promotion had archived the source instinct (v4.2.0
anti-double-injection), so retiring the law used to remove the knowledge
from BOTH layers at once.
- **`demote_law_to_domain` lookup could never find v4-promoted instincts**:
it searched `instincts/archive/<id>.yaml` while every promotion path since
v4.0 archives as `instincts/global/archive/<id>.promoted-to-law-<date>.yaml`
— different name AND different directory. The shared
`_find_backing_instinct_yaml()` checks live global/, the promotion-archive
name (newest wins) and the legacy path.
- Laws with no backing YAML (manually seeded, no trigger) stay archive-only —
a trigger is never invented, an instinct without one would silently never
inject. Logged as `retired-archive-only`. `test_distill_v4` 10→12.

## [4.3.0] — 2026-07-05

**Zero-touch mode.** The learning pipeline no longer waits on a human anywhere:
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- **Observes** every tool call silently via async hooks (0 tokens overhead), capturing real output + error lines with anti-noise guards
- **Injects** matched instincts and reflexes per tool use via PreToolUse (~120 tokens max)
- **Learns** patterns automatically: instincts start as silent `draft`, earn `confirmed` (injectable) at 5+ occurrences across 3+ sessions — no manual analyze/validate step
- **Maintains** itself: `/cx-maintain` (deterministic, cron-able) promotes proven instincts to Laws — auto-swapping the least-impactful law when the cap is full (v4.3.0) — dedups, decays, expires stale proposals at 30 days, rotates storage. Zero questions
- **Maintains** itself: `/cx-maintain` (deterministic, cron-able) promotes proven instincts to Laws — auto-swapping the least-impactful law when the cap is full (v4.3.0); the retired law falls back to the instinct pool instead of dying (v4.3.1) — dedups, decays, expires stale proposals at 30 days, rotates storage. Zero questions
- **Reports** instead of assigning work: the SessionStart badge tells you what the last pass did; `/cx-review` is an optional veto digest — nothing ever waits on you
- **Protects** with deterministic reflex hooks (not probabilistic instructions)

Expand Down Expand Up @@ -202,7 +202,8 @@ pass already did (law swaps, expired proposals, queue):
You work → Cortex captures output+errors (guarded) → instinct born as draft
→ 5+ occurrences across 3+ sessions → status: confirmed → starts injecting
→ /cx-maintain promotes proven instincts to law deterministically
(auto-swapping the least-impactful law when the cap is full, v4.3.0)
(auto-swapping the least-impactful law when the cap is full, v4.3.0;
the retired law falls back to the instinct pool, v4.3.1)
→ stale pending proposals expire on their own after 30 days
→ /cx-review is an optional veto pass — nothing ever waits on you
```
Expand Down
4 changes: 3 additions & 1 deletion commands/cx-maintain.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ ever waits on the user. Idempotent: running it twice in a row is safe (each
sub-step guards its own idempotency: `last_decay_at`, the `.last-auto-distill`
marker, size-gated storage rotation, etc.).
When the law cap is saturated, promotion now auto-swaps under deterministic
guards; stale pending proposals expire after 30 days.
guards; the retired law cascades back to the instinct pool (`law_eligible:
false`, v4.3.1) instead of dying in archive; stale pending proposals expire
after 30 days.

Replaces the manual judgment calls that used to live in `/cx-analyze`,
`/cx-distill`, `/cx-dream`, `/cx-promote`, `/cx-backfill` (deterministic parts
Expand Down
7 changes: 4 additions & 3 deletions docs/FEATURES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# fs-cortex v4.3.0 — Feature Reference
# fs-cortex v4.3.1 — Feature Reference

> Complete inventory of all features, commands, hooks, modules, and capabilities.
> Last updated: 2026-07-05
Expand Down Expand Up @@ -252,7 +252,7 @@ mapping: [`MIGRATION-V4.md`](MIGRATION-V4.md).
| Command | Purpose | Token Cost |
|---|---|---|
| `/cx-status` | Visual glance dashboard: 🟢🟡🔴 semaphore per section (laws, instincts, reflexes, sistema) + a SALUD GLOBAL header box (worst-of), confidence bars, top-5 projects/tracking, domain grouping collapsed to top4+resto. Same collector JSON as v3 (nothing removed, overflow moves to compact secondary lines / on-request detail). `--impact`, `--reflexes`, `--pipeline` flags unchanged from v3 | ~200 |
| `/cx-maintain` | **New in v4, deterministic, cron-able.** Single pass: engine decay/purge/auto-validate/deterministic law-promotion, Jaccard dedup by subtopic, storage rotation, proposals↔instincts reconciliation, health check, writes `.review-digest.json` for `/cx-review`. Resets the learn markers (`.last-learn-count`, `.obs-count`, `.learn-pending`) so the SessionStart "N+ new observations" banner measures since-last-maintenance (v4.2.2). **v4.3.0 zero-touch**: auto-swaps laws at a saturated cap (victim = lowest 14d impact, age >= `LAW_AUTO_SWAP_MIN_AGE_DAYS` 30, max `LAW_AUTO_SWAP_MAX_PER_RUN` 2, only when the cap is the sole failing gate) via the deadlock-safe `_swap_promote_unlocked`; expires human-gated proposals pending > `PROPOSAL_TTL_DAYS` 30 (`expire_stale_proposals`, rejecter `cx-maintain-ttl`, tombstoned); digest carries `swaps_last_run` + `expired_last_run`. Zero questions, idempotent. Also ships as `bin/cx-maintain.sh` — a plain bash mirror of the same steps for cron/launchd, no LLM call, 0 tokens | ~400 / 0 (script) |
| `/cx-maintain` | **New in v4, deterministic, cron-able.** Single pass: engine decay/purge/auto-validate/deterministic law-promotion, Jaccard dedup by subtopic, storage rotation, proposals↔instincts reconciliation, health check, writes `.review-digest.json` for `/cx-review`. Resets the learn markers (`.last-learn-count`, `.obs-count`, `.learn-pending`) so the SessionStart "N+ new observations" banner measures since-last-maintenance (v4.2.2). **v4.3.0 zero-touch**: auto-swaps laws at a saturated cap (victim = lowest 14d impact, age >= `LAW_AUTO_SWAP_MIN_AGE_DAYS` 30, max `LAW_AUTO_SWAP_MAX_PER_RUN` 2, only when the cap is the sole failing gate) via the deadlock-safe `_swap_promote_unlocked`; the retired law cascades to the instinct pool (v4.3.1 — backing YAML restored with `law_eligible: false`, archive-only when no backing/trigger exists); expires human-gated proposals pending > `PROPOSAL_TTL_DAYS` 30 (`expire_stale_proposals`, rejecter `cx-maintain-ttl`, tombstoned); digest carries `swaps_last_run` + `expired_last_run`. Zero questions, idempotent. Also ships as `bin/cx-maintain.sh` — a plain bash mirror of the same steps for cron/launchd, no LLM call, 0 tokens | ~400 / 0 (script) |
| `/cx-review` | **Optional human veto pass (v4.3.0 — was "weekly, only command with judgment" in v4.0-v4.2).** Presents the accumulated digest (what auto-swapped, what expired, what waits) as ONE shorthand list for veto or fast-track. Nothing blocks if never run: proposals expire at 30d, laws auto-swap. The SessionStart badge is informative (`[cx] maintain: ...`, silenced 48h after the pass) | ~600 |
| `/cx-eod` | End-of-day summary. Deterministic gather (`core/_cx-eod-gather.sh`, no LLM); structurally cumulative (rereads the full day on every run); **v4**: per-project `context` field from `context.md`, `[eod-eisenhower]` Q1–Q4 keyword classification of "for tomorrow" bullets on next-day reinjection, review-digest pending count surfaced in `--write` mode | ~300 / 0 (auto) |
| `/cx-gotcha` | Capture error→fix as high-priority instinct (unchanged) | ~200 |
Expand Down Expand Up @@ -420,7 +420,7 @@ Deterministic rules via hooks — not probabilistic instructions. Triggers are r
| `test_hooks_e2e.sh` | 14 | Full pipeline: observe→inject→learn, **token budget reset** |
| `test_uninstall.sh` | 13 | Cleanup, backup creation, data preservation, **safety guard**, CLAUDE.md preservation, **v3.19.0 env removal** (Cortex var removed, user vars preserved, empty env block dropped) |
| `test_integrity.sh` | 14 | observe.py direct, **21 commands** validated (EXPECTED_COMMANDS subset; 24 command files total as of v4.0.0 — `ls commands/*.md` — incl. new `cx-maintain`/`cx-review` not yet in EXPECTED_COMMANDS), core file schemas, **version consistency** |
| `test_distill_v4.sh` | 10 | **(v4.0.0)** `auto_promote_to_law` deterministic gate: conf/projects/occurrences_v4/noise-14d criteria, `law_eligible: false` veto, lazy `occurrences_legacy`/`occurrences_v4` migration; **v4.3.0**: auto-swap at saturated cap (law replaced + victim archived + `swapped_out` marker), age-floor 30d blocks swap, `expire_stale_proposals` TTL (40d-old rejected, fresh kept), dry-run mutates nothing |
| `test_distill_v4.sh` | 12 | **(v4.0.0)** `auto_promote_to_law` deterministic gate: conf/projects/occurrences_v4/noise-14d criteria, `law_eligible: false` veto, lazy `occurrences_legacy`/`occurrences_v4` migration; **v4.3.0**: auto-swap at saturated cap (law replaced + victim archived + `swapped_out` marker), age-floor 30d blocks swap, `expire_stale_proposals` TTL (40d-old rejected, fresh kept), dry-run mutates nothing; **v4.3.1**: retired law cascades to instinct (backing YAML restored, `law_eligible: false`, trigger intact), no-backing victim stays archive-only |
| `test_session_start.sh` | 11 | SessionStart injection block, laws-meta tier split + malformed-meta tolerance (v4.2.1); **v4.3.0**: informative `[cx] maintain:` badge from digest `swaps_last_run`/`expired_last_run`, 48h staleness silences it, old-format digest → no crash |
| `test_injector_v4.sh` | 13 | **(v4.0.0)** `status: draft` never injects, `status: confirmed`/legacy-no-field inject normally, subtopic dedup + domain soft-ceiling at conf>=0.85, JSON-fragment + prompt-injection action guards, degenerate-trigger static validation, live `reflexes.json` polling conditions |
| `test_cx_maintain_runner.sh` | 10 | **(v4.0.0, inventoried v4.2.2)** `bin/cx-maintain.sh` hermetic sandbox: exit 0 on repeated runs, decay exactly-once (same-day idempotency via `last_decay_at`), compat markers, valid `.review-digest.json`, no leftover lock dir, `--dry-run` writes nothing; **v4.2.2**: learn markers reset (`.last-learn-count` snapshot, `.obs-count` zeroed, `.learn-pending` cleared); **v4.3.0**: engine-pass report carries `swapped=`/`expired=` counters |
Expand Down Expand Up @@ -670,6 +670,7 @@ locally (not installed to `~/.claude/`):
| v4.0.0 | 2026-07-02 | **"Signal-first, zero-decision" — full redesign of capture and command set.** `docs/DESIGN-V4.md` + `docs/SPEC-PORT-SINAPSIS.md` (port of Sinapsis v4.6.1, MIT, Luis Salgado). **BREAKING:** 17 of 24 commands deprecated to notice-only stubs (mapping in `docs/MIGRATION-V4.md`); 7 remain active (`cx-status`, `cx-maintain`, `cx-review`, `cx-eod`, `cx-gotcha`, `cx-backup`, `cx-restore`). New `/cx-maintain` (deterministic, cron-able: decay+purge+auto-validate+deterministic law-promotion+Jaccard dedup-by-subtopic+storage rotation+proposals↔instincts reconciliation+health check, writes `.review-digest.json`). New `/cx-review` (the only command with human judgment, weekly, fuses old validate+evolve-confirm+downvote+distill-swap into one shorthand pass). `hooks/observe.py`: per-line capture guards before trusting an error match (`[codex]`/npm-log prefixes, version listings, grep headers, `0 errors`, bare `warning:`), `output` cap 8000→10000, `err_msg` now the first non-guarded matching line. New `status: draft`/`confirmed` instinct lifecycle (`session-learner.js`, `injector-engine.js`) — drafts track silently, auto-promote at `occurrences>=5 AND sessions_seen>=3`; legacy instincts default `confirmed`. Law promotion gate replaced with a fully deterministic one (`conf>=0.95`, `projects_seen>=3`, `occurrences_v4>=10` via lazy `occurrences_legacy` migration, 0 noise 14d) — `auto-distill-candidates.md` buzón removed. Injector: JSON-fragment + prompt-injection guards at load, degenerate-trigger static validation, dedup switched from per-domain to per-subtopic (2 first words of id) with a soft 2-per-domain ceiling at conf>=0.85. `core/_cx-eod-gather.sh`: per-project `context` field, `[eod-eisenhower]` Q1-Q4 keyword classification of "for tomorrow" bullets, review-digest pending count surfaced. New test suites `test_distill_v4.sh` (6/6 PASS), `test_injector_v4.sh` (13/13 PASS). |
| v4.1.0 | 2026-07-04 | **Audit hardening (multi-agent audit + adversarial verify).** `session-start.py` injects full law text (not just line 1). `session-learner.js`: tombstone gate matches by trigger vs `proposals-history.jsonl`, dup-trigger dedup, `$HOME`→`~` paths, head+tail sampling. Reflexes: re-enabled `python3-bypass-write-tool`, hardened `bash-cat-use-read`. `storage-rotation.js`: prunes `cross-day-tracker` past 1.5× threshold, stale `.lock`/`.bak` cleanup, `timeline.jsonl` rotation. `distill_engine.py`: `prune_instinct_tracking()` + `reap_stale_nudge_state()` + CLI subcommands. `injector-engine.js`: session token budget 8000→12000 with a visible suppression warning, optional per-reflex `domains` filter. |
| v4.2.0 | 2026-07-05 | **Laws layer: de-dup, tier legibility, post-promotion audit** (`docs/DESIGN-laws-v4.2.md`; 3-agent adversarial pass). Fixed double injection (`read-instructions` 41×, `pref-fix` 17× redundant PreToolUse) — `injector-engine.js` skips instinct candidates with a `laws/{id}.txt` twin; `manual_swap_promote` archives the source instinct like `auto_promote_to_law`. `advisor-escalation` trimmed 1505→717 chars. Reverted the v4.1.0 `bash-cat-use-read` lookahead regression (broke `test_reflex_matchers`). Added law presentation split by tier (`[principios]`/`[herramienta]`, `core/laws-meta.default.json`) + `law_audit()` + `law-audit` CLI. |
| v4.3.1 | 2026-07-05 | **Retired laws cascade to instincts (operator feedback on v4.3.0).** Auto-swap retirement and `demote_law_to_domain` share `_restore_backing_instinct()`: the victim's backing YAML returns to `instincts/global/` with `law_eligible: false` (no re-promotion ping-pong), keeping the knowledge injecting via PreToolUse and subject to normal decay. Fixes the two-layer loss (promotion archives the instinct; retirement used to archive the law → knowledge gone from both). Also fixes `demote_law_to_domain`'s lookup, blind to the `<id>.promoted-to-law-<date>.yaml` name every v4 promotion writes. No-backing laws stay archive-only (never invent a trigger). `test_distill_v4` 10→12. |
| v4.3.0 | 2026-07-05 | **Zero-touch mode — the pipeline never waits on a human.** Reverts the v4.2 "human prunes" model at the operator's request. (1) `auto_promote_to_law` auto-swaps at a saturated cap: victim = `_find_least_impactful_law` (lowest useful/(1+noise) 14d), age >= 30d (`LAW_AUTO_SWAP_MIN_AGE_DAYS`), max 2 swaps/run, only when the cap is the sole failing gate; executes via `_swap_promote_unlocked` (extracted from `manual_swap_promote` — calling the locked wrapper from inside the maintain lock would deadlock on the shared flock). (2) `expire_stale_proposals()`: pending human-gated proposals older than 30d (`PROPOSAL_TTL_DAYS`) auto-reject as `cx-maintain-ttl` (added to both tombstone whitelists — never resurrect). (3) SessionStart badge informative: `[cx] maintain: +law (jubilada X), N caducadas, M en cola` from digest `swaps_last_run`/`expired_last_run`, silenced 48h after the pass; the imperative `[REVIEW] N items -> /cx-review` badge is gone. (4) `/cx-review` reframed as optional veto pass. Shared `_archive_promoted_instinct_source` helper dedups 3 archive sites. `test_distill_v4` 6→10, `test_session_start` 7→11, `test_session_learner` 60→61, runner report carries `swapped=`/`expired=`. Implementation delegated to Codex GPT-5.3 (spec-driven), reviewed inline. |
| v4.2.2 | 2026-07-05 | **Learn markers reset in `/cx-maintain` Step 7.** Nothing cleared `.learn-pending` after `/cx-analyze` retired in v4, so the SessionStart "N+ new observations, run /cx-maintain" banner nagged forever. Step 7 (both `commands/cx-maintain.md` and `bin/cx-maintain.sh`) now snapshots the obs total into `.last-learn-count`, zeroes `.obs-count` and drops the flag. `test_cx_maintain_runner` 9→10 + suite inventoried in the tests table (gap since v4.0.0). |
| v4.2.1 | 2026-07-05 | **AD follow-up (Codex GPT-5.5 review of v4.1/v4.2).** `load_laws()` (`session-start.py`) now tolerant of a malformed `laws-meta.json` — a non-dict schema no longer crashes the whole SessionStart injection. `impact.archive` retention fixed from count-based `keep=5` (could delete chunks < 90 days old, contradicting the append-only "never deleted" contract) to 90-day age-based per `DESIGN-V4 §7`; the stale "never deleted" comments in `impact_log.py`/`session-learner.js` reconciled. New laws-meta robustness tests; version history caught up. |
Loading
Loading