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
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,51 @@ 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.2.0] — 2026-07-05

**Laws layer: de-dup, legibility, post-promotion audit.** Follow-up to the
question "are laws the right mechanism, or noise?" — investigated with measured
data + a 3-agent adversarial counter-analysis that refuted the invasive ideas
(shrinking, demoting gotchas, stopping auto-promotion) and confirmed the
surgical ones. Full rationale in `docs/DESIGN-laws-v4.2.md`.

### Fixed
- **Double injection (measured bug)**: `read-instructions-before-executing`
(41 redundant PreToolUse injections) and `pref-fix-all-lint-test-issues` (17)
existed as both a law AND an active global instinct. `injector-engine.js` now
skips any instinct candidate whose id has a `laws/{id}.txt` twin, and the two
duplicate instincts were archived. Root cause fixed: `manual_swap_promote`
now archives the source instinct on promotion (like `auto_promote_to_law`
already did) — seed/manual paths previously left the duplicate behind.
- **`advisor-escalation` bloat**: trimmed 1505 → 717 chars (kept the 4 triggers
and exclusions, dropped the verbose mechanics). It was 26% of the whole law
budget; v4.1.0's full-text injection had surfaced it.
- **`bash-cat-use-read` regression (v4.1.0)**: the noise-reduction negative
lookahead `(?!\s*(\|<<))` also excluded legitimate targets — `cat file.py |
head`, `tail file.json | grep` — where Read/Grep IS the better tool, breaking
the `test_reflex_matchers` contract (piped source-file cat must still fire).
Reverted the lookahead in seed + live; the reflex fires on any recognised
source extension regardless of pipe, as the contract specifies.

### Added
- **Law presentation split by tier** (`session-start.py` + `core/laws-meta.default.json`):
laws render in two labelled blocks — `[principios]` (behavioural) vs
`[herramienta]` (tool gotchas) — so principles don't get diluted by mechanical
gotchas. Driven by an optional `laws/laws-meta.json` `{id: {tier}}` map;
absent map or entry → single block / `principle` default (backward compatible).
- **`law_audit()` + `law-audit` CLI** (`distill_engine.py`): per-law
`{id, tier, age_days, dup_active_instinct, backing_instinct_noise}`, written to
`.law-audit.json`. Gives periodic legibility to prune laws by data — the honest
model for a curated constitution (measuring "law followed" is unsolvable without
a trigger, per the adversarial pass).

### Notes
- Deliberately NOT done (adversarial refutation): domain-scoping laws (only 2-3
of the "mechanical" laws are project-domain-specific; the rest are universal
tooling — new machinery unjustified for the payoff); demoting the 7 tool
gotchas to instincts (re-exposes them to trigger-miss); hand-curating laws down
to 5-7 (no evidence current content is bad; token cost is <0.5%).

## [4.1.0] — 2026-07-04

**Audit hardening.** Fixes and hygiene from a multi-agent read-only audit of the
Expand Down
21 changes: 21 additions & 0 deletions core/laws-meta.default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": 1,
"_comment": "tier drives the SessionStart presentation split (principle vs tool). Absent id -> defaults to 'principle' (never hidden).",
"laws": {
"advisor-escalation": {"tier": "principle"},
"deep-work-to-docs": {"tier": "principle"},
"read-instructions-before-executing": {"tier": "principle"},
"loop-reorient": {"tier": "principle"},
"pref-fix-all-lint-test-issues": {"tier": "principle"},
"gotcha-ad-por-fase-no-sustituye-e2e": {"tier": "principle"},
"build-output-to-log": {"tier": "tool"},
"cat-pipe-head-claudemd-anti": {"tier": "tool"},
"gotcha-inline-python-readability": {"tier": "tool"},
"macos-downloads-read-tool": {"tier": "tool"},
"gotcha-mcp-server-wrong-config-file": {"tier": "tool"},
"gotcha-webfetch-fallback-to-websearch": {"tier": "tool"},
"gotcha-firecrawl-over-tavily-html": {"tier": "tool"},
"e2e-playwright-selectors": {"tier": "tool"},
"gotcha-api-route-error-message-leak": {"tier": "tool"}
}
}
6 changes: 3 additions & 3 deletions core/reflexes.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
{
"id": "bash-cat-use-read",
"matcher": "Bash",
"condition": "(?:^|[;&|]\\s*)(cat|head|tail)\\s+(-[0-9]+\\s+)?[\\.\\/~]?\\S*\\.(py|js|jsx|ts|tsx|md|json|jsonl|ya?ml|sh|html|css|toml|cfg|ini|conf|sql|env)\\b(?!\\s*(\\||<<))",
"condition": "(?:^|[;&|]\\s*)(cat|head|tail)\\s+(-[0-9]+\\s+)?[\\.\\/~]?\\S*\\.(py|js|jsx|ts|tsx|md|json|jsonl|ya?ml|sh|html|css|toml|cfg|ini|conf|sql|env)\\b",
"action": "Use Read tool (with limit/offset) instead of cat/head/tail for source files.",
"severity": "medium",
"enabled": true,
Expand All @@ -152,7 +152,7 @@
"type": "tool-substitution",
"expected_tool": "Read",
"anti_tool": "Bash",
"anti_pattern": "(?:^|[;&|]\\s*)(cat|head|tail)\\s+(-[0-9]+\\s+)?[\\.\\/~]?\\S*\\.(py|js|jsx|ts|tsx|md|json|jsonl|ya?ml|sh|html|css|toml|cfg|ini|conf|sql|env)\\b(?!\\s*(\\||<<))",
"anti_pattern": "(?:^|[;&|]\\s*)(cat|head|tail)\\s+(-[0-9]+\\s+)?[\\.\\/~]?\\S*\\.(py|js|jsx|ts|tsx|md|json|jsonl|ya?ml|sh|html|css|toml|cfg|ini|conf|sql|env)\\b",
"window": 3
}
},
Expand Down Expand Up @@ -195,4 +195,4 @@
}
}
]
}
}
56 changes: 56 additions & 0 deletions docs/DESIGN-laws-v4.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# DESIGN — v4.2.0 · Laws layer: de-dup, legibility, post-promotion audit

Fecha: 2026-07-05 · Estado: aprobado (Fernando delegó el criterio) · Origen: pregunta de Fer "¿son las laws la mejor forma de hacer esto, o son ruido?"

## Contexto y evidencia (medida, no supuesta)

La capa de leyes son `laws/*.txt` inyectadas SIEMPRE al SessionStart (`session-start.py:load_laws` → `565-568`), sin filtro, ~975 tokens. Investigación de esta sesión + contra-análisis adversarial (3 agentes):

- **Coste real:** 15 leyes, ~975 tokens/sesión = <0.5% de un contexto de 200k. **El coste NO es el problema.**
- **Sin feedback de salida:** la promoción a ley SÍ exige feedback de entrada (DESIGN-V4 §3: conf≥0.95, ≥3 proyectos, ≥10 ocurrencias, 14 días sin ruido). Pero una vez escrita la ley, nada vuelve a medir su valor salvo `_find_least_impactful_law` cuando el cap 15 satura. Feedback congelado post-promoción.
- **Doble inyección (bug confirmado):** `read-instructions-before-executing` (41 inyecciones PreToolUse redundantes medidas en impact.jsonl) y `pref-fix-all-lint-test-issues` (17) existen como ley Y como instinct global activo. `auto_promote_to_law` archiva el instinct fuente, pero las rutas seed + swap manual NO → duplicados.
- **Mezcla de granularidad:** las 15 mezclan principios de conducta (advisor-escalation, deep-work, loop-reorient) con gotchas de herramienta (cat→Read, inline-python) en un solo bloque "follow always", lo que diluye la atención sobre los principios.

## Qué NO se hace (y por qué) — evitar sobre-corrección

El contra-análisis refutó las ideas invasivas:
- **NO encoger a 5-7 curadas a mano:** el contenido actual no es malo; el coste es <0.5%. Sin evidencia que lo justifique.
- **NO demotar los gotchas a instinct:** se promovieron porque son universales Y ya pasaron trigger-gating. Como instinct, un log con formato inesperado no dispara el regex; como ley siempre está. Demotar = perder disparo garantizado.
- **NO parar `auto_promote_to_law`:** ya tiene gates de calidad (no es ciega).
- **NO scoping por dominio:** solo 2-3 leyes son de dominio de proyecto (playwright→testing, api-route→web); el resto de "mecánicas" son universales de tooling. Montar meta-file + detección de dominio en Python para 2 casos que ahorran ~100 tok es abstracción nueva injustificada (viola minimalismo).

## Cambios v4.2.0

### C1 — De-dup + fix de raíz (bug)
- **Data:** archivar `instincts/global/read-instructions-before-executing.yaml` y `pref-fix-all-lint-test-issues.yaml` → `instincts/global/archive/*.dup-of-law-20260705.yaml`.
- **`injector-engine.js`:** al construir candidatos, excluir cualquier instinct cuyo `id` tenga un fichero `laws/{id}.txt` (una ley ya se inyecta al SessionStart; el instinct gemelo es redundante). Leer LAWS_DIR una vez → Set de law-ids → skip. Retrocompatible.
- **`distill_engine.py`:** que `manual_swap_promote` (y cualquier ruta de creación de ley) archive SIEMPRE el instinct fuente si existe, igual que `auto_promote_to_law`.

### C2 — Adelgazar advisor-escalation
- **Data:** `laws/advisor-escalation.txt` de ~1500 chars a ~500 (resumen + los 4 triggers esenciales, sin el detalle expandido). Es un principio válido, solo desproporcionado (26% del presupuesto en una ley; el fix de inyección-completa de v4.1.0 lo destapó).

### C3 — Legibilidad: split de presentación por tier
- **Data:** nuevo `laws/laws-meta.json` (+ seed `core/laws-meta.default.json`) con `{version, laws: {<id>: {tier: "principle"|"tool"}}}`. Clasificación inicial abajo.
- **`session-start.py:load_laws`:** devolver las leyes agrupadas por tier. `565-568` renderiza dos sub-bloques bajo el header:
```
CORTEX LAWS (follow always):
[principios]
- ...
[herramienta]
- ...
```
Retrocompat: ley sin entrada en meta → tier "principle" (nunca se oculta nada). Sin meta-file → comportamiento actual (un bloque).

### C4 — Auditoría post-promoción (legibilidad periódica)
- **`distill_engine.py`:** nueva función `law_audit()` que devuelve por cada ley activa `{id, tier, age_days, dup_active_instinct: bool, backing_instinct_noise: ratio|null}`, más subcomando CLI `law-audit [--json]` (mismo patrón que los `prune-tracking`/`reap-nudges` de v4.1.0). Escribe también `~/.claude/cortex/.law-audit.json` al correr. NO se toca el heredoc de cx-maintain ni `bin/cx-maintain.sh` (tienen WIP ajeno sin commitear); la integración en el digest se hará cuando ese WIP asiente. Da a Fernando legibilidad periódica para podar a mano — el modelo correcto ahora que las leyes son una constitución curada (humano poda con datos, no auto-feedback falso; medir "ley seguida" es irresoluble sin trigger, confirmado por el adversarial).

## Clasificación tier inicial (laws-meta.json)

**principle** (conducta / cómo trabajar): advisor-escalation · deep-work-to-docs · read-instructions-before-executing · loop-reorient · pref-fix-all-lint-test-issues · gotcha-ad-por-fase-no-sustituye-e2e

**tool** (gotcha de herramienta): build-output-to-log · cat-pipe-head-claudemd-anti · gotcha-inline-python-readability · macos-downloads-read-tool · gotcha-mcp-server-wrong-config-file · gotcha-webfetch-fallback-to-websearch · gotcha-firecrawl-over-tavily-html · e2e-playwright-selectors · gotcha-api-route-error-message-leak

## Verificación
- Test pair + `run_all.sh` verde.
- Smoke `echo '{}' | python3 hooks/session-start.py` con los dos bloques.
- Deploy `install.sh` + verificar dedup (0 instincts gemelos), advisor adelgazada, digest con law_audit.
2 changes: 1 addition & 1 deletion docs/FEATURES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# fs-cortex v4.1.0 — Feature Reference
# fs-cortex v4.2.0 — Feature Reference

> Complete inventory of all features, commands, hooks, modules, and capabilities.
> Last updated: 2026-07-02
Expand Down
Loading
Loading