From 0dbcdc2e963d6295775309fcdc6ca7e79fe15735 Mon Sep 17 00:00:00 2001 From: LantisPrime Date: Sun, 21 Jun 2026 20:50:47 +0800 Subject: [PATCH] docs(rfc-008): P4d S7 phase index + Principle 12 record P4d (closes #403) Refresh RFC-008 phase docs to record P4d (S1-S6 + ESC merged, S7-S8 open); fix stale P2/P3 queued ledger stubs in the RFC body; add a CI guard rejecting stale P4b phase status; note per-project uninstall implemented in Principle 12. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/rfc-validate.yml | 7 +++++++ PRINCIPLES.md | 2 +- ...008-decouple-enforcement-from-substrate.md | 6 +++--- docs/rfcs/RFC-008/P4-enforce-config.md | 21 ++++++++++++++++++- docs/rfcs/RFC-008/README.md | 2 +- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rfc-validate.yml b/.github/workflows/rfc-validate.yml index 6c03dda..426b851 100644 --- a/.github/workflows/rfc-validate.yml +++ b/.github/workflows/rfc-validate.yml @@ -185,3 +185,10 @@ jobs: - name: Run install.mjs BP-1 run.key gitignore tests run: node tests/test-install-bp1-runkey-gitignore.mjs + + - name: Reject stale P4 phase status in RFC-008 phase docs (closes #403) + run: | + if grep -rnE "P4b.*(remains|reduced|cutover)" docs/rfcs/RFC-008*; then + echo "::error::stale P4b phase status in docs/rfcs/RFC-008: refresh the phase index (#403)" + exit 1 + fi diff --git a/PRINCIPLES.md b/PRINCIPLES.md index 6813951..344ac53 100644 --- a/PRINCIPLES.md +++ b/PRINCIPLES.md @@ -127,7 +127,7 @@ Enforcement — hooks, gates, classifiers — is activated and controlled **per **Why:** A hook in global `~/.claude/settings.json` fires in *every* project — Claude Code merges hooks across scopes and a project cannot subtract a global one — so global enforcement reaches into unrelated projects and breaks them (a gate looking for a project-local lib that isn't there denies real work). Enforcement that cannot be scoped or switched off per project defeats the entire point of RFC-008: decoupling enforcement from the substrate. Memory must stay usable everywhere without dragging enforcement along. -**How to apply:** Enforcement adapters install their hook FILES + libs + hook-invoked scripts into the activating project's `/.claude/hooks/` and register them only into `/.claude/settings.json` (never `~/.claude/hooks/` or `~/.claude/settings.json`). Global install deploys ONLY substrate (the `em-*` memory tools, `patterns/`, the skill); it copies zero hook files and writes zero hook registrations. Each project carries its own enable/disable switch — `/.episodic-memory/enforce-config.json` `active`, plus the presence of its hook registrations. Installers expose enforcement as an explicit per-project opt-in (Principle 3) with per-project uninstall (Principle 10); a project that did not opt in runs zero enforcement hooks. Core memory operations never depend on any hook being installed (Principle 9). **Test this:** a mock-project E2E must assert that after any global/core install, `~/.claude/hooks/` contains no enforcement file and `~/.claude/settings.json` contains no enforcement registration; that the enforcement set is installed only under `/.claude/`; and any design/code/test that places an enforcement artifact in global scope is a P12 violation. +**How to apply:** Enforcement adapters install their hook FILES + libs + hook-invoked scripts into the activating project's `/.claude/hooks/` and register them only into `/.claude/settings.json` (never `~/.claude/hooks/` or `~/.claude/settings.json`). Global install deploys ONLY substrate (the `em-*` memory tools, `patterns/`, the skill); it copies zero hook files and writes zero hook registrations. Each project carries its own enable/disable switch — `/.episodic-memory/enforce-config.json` `active`, plus the presence of its hook registrations. Installers expose enforcement as an explicit per-project opt-in (Principle 3) with per-project uninstall (Principle 10); a project that did not opt in runs zero enforcement hooks. Core memory operations never depend on any hook being installed (Principle 9). **Test this:** a mock-project E2E must assert that after any global/core install, `~/.claude/hooks/` contains no enforcement file and `~/.claude/settings.json` contains no enforcement registration; that the enforcement set is installed only under `/.claude/`; and any design/code/test that places an enforcement artifact in global scope is a P12 violation. Per-project uninstall is implemented (P4d S5, #416): `install.mjs --uninstall-enforcement` removes the project's enforcement set while preserving the core set and the global substrate, and the round-trip invariant (core install, then enforce, then uninstall, restores the core-install state) is asserted by `tests/test-uninstall-enforcement.mjs`. --- diff --git a/docs/rfcs/RFC-008-decouple-enforcement-from-substrate.md b/docs/rfcs/RFC-008-decouple-enforcement-from-substrate.md index 97c433c..a0af9e4 100644 --- a/docs/rfcs/RFC-008-decouple-enforcement-from-substrate.md +++ b/docs/rfcs/RFC-008-decouple-enforcement-from-substrate.md @@ -1205,15 +1205,15 @@ Serves R1, R6, R8 · depends on: P0, R0b′ · **DONE (P1a #373, P1b #374, P1c # #### P2 — BP contract instances + contract validators → [RFC-008/P2-bp-contracts.md](RFC-008/P2-bp-contracts.md) -Serves R2, R3, R4 · depends on: P0 · queued (parallelizable with P1). `bp-001..006, bp-008..012.json` (11 contracts; **bp-007 absent**) + `scaffold-bp.mjs` + `validate-bp-contract.mjs` + `validate-schemas.mjs`; lands the shared-negative-corpus drift guard (issue #368). +Serves R2, R3, R4 · depends on: P0 · **DONE (P2a #381, P2b #384, P2c #388).** `bp-001..006, bp-008..012.json` (11 contracts; **bp-007 absent**) + `scaffold-bp.mjs` + `validate-bp-contract.mjs` + `validate-schemas.mjs`; lands the shared-negative-corpus drift guard (issue #368). #### P3 — Thin waist + classifier runtime-sourcing + em-recall purification → [RFC-008/P3-thin-waist.md](RFC-008/P3-thin-waist.md) -Serves R1, R2, R3, R4, R5, R9 (+ F38) · depends on: P0, P2 · queued. `enforce-contract.mjs` + `lib/marker-state.mjs` + classifier runtime-sourcing + **em-recall STRICT DELETION** (F38/F60). The load-bearing phase; where gauntlet steps 5/6 finally run. +Serves R1, R2, R3, R4, R5, R9 (+ F38) · depends on: P0, P2 · **DONE (P3a #389, P3b-1 #391, P3c #392, P3b-2 #393, P3d #395).** `enforce-contract.mjs` + `lib/marker-state.mjs` + classifier runtime-sourcing + **em-recall STRICT DELETION** (F38/F60). The load-bearing phase; where gauntlet steps 5/6 finally run. #### P4 — Per-project `enforce-config.json` → [RFC-008/P4-enforce-config.md](RFC-008/P4-enforce-config.md) -Serves R3, R5 · depends on: P3 · queued *(legacy "Phase 5")*. `effective_tier = min(harness, contract, project_config)` clamps DOWN only; `active: false` makes the classifier silent (R5). +Serves R3, R5 · depends on: P3 · **IN PROGRESS**: P4a #397 + P4c #398 done; **P4d** per-project enforcement re-architecture (Principle 12) S1-S6 + ESC merged, S7-S8 open *(legacy "Phase 5")*. `effective_tier = min(harness, contract, project_config)` clamps DOWN only; `active: false` makes the classifier silent (R5). #### P5–P7 — Per-tool plugins (OpenCode / Codex / Pi Agent) → [RFC-008/P5-P7-tool-plugins.md](RFC-008/P5-P7-tool-plugins.md) diff --git a/docs/rfcs/RFC-008/P4-enforce-config.md b/docs/rfcs/RFC-008/P4-enforce-config.md index 99f51d2..3220dda 100644 --- a/docs/rfcs/RFC-008/P4-enforce-config.md +++ b/docs/rfcs/RFC-008/P4-enforce-config.md @@ -3,7 +3,7 @@ > Part of [RFC-008](../RFC-008-decouple-enforcement-from-substrate.md). Index: > [RFC-008/README.md](README.md). -**Status:** IN PROGRESS — schema landed P3b-2 #393; **P4a #397** (per-project loader → 3 pre_tool_use contract gates) + **P4c #398** (layer-wide `active:false` kill switch across preflight + second-opinion + SessionStart, R5) merged + deployed to global. **P4b** (reduced live SessionStart→Stop E2E + cutover) remains. *(Legacy "Phase 5".)* +**Status:** IN PROGRESS: schema landed P3b-2 #393; **P4a #397** (per-project loader, 3 pre_tool_use contract gates) + **P4c #398** (layer-wide `active:false` kill switch across preflight + second-opinion + SessionStart, R5) merged + deployed. **P4d** (per-project enforcement re-architecture, Principle 12) S1-S6 + ESC merged; S7-S8 open. Original P4b superseded by the P4d re-architecture. *(Legacy "Phase 5".)* **Serves:** R3, R5. **Depends on:** P3. **Estimate:** ~25K. @@ -42,3 +42,22 @@ all plugins makes the classifier silent (R5 — no hook spawn, no token cost). ## Maps to R3, R5. Principle anchor: the project-config leg of the R3 effective-tier formula. + +## P4d: per-project enforcement re-architecture (Principle 12) + +P4d decouples enforcement from the substrate: every enforcement artifact (gate `.sh` +hooks, the enforcement engine, the classifier, hook libs, the SessionEnd/SessionStart hook +scripts) installs under `/.claude/` and registers only in +`/.claude/settings.json`; the memory substrate stays global and hook-free. Each +project owns its own on/off switch (`/.episodic-memory/enforce-config.json` `active`). + +Slice status (phase index; full slice and PR detail live in the workplan episode): + +| Slice | Concern | Status | +|---|---|---| +| S1-S4 | per-project install + scope + the 3 pre_tool_use gates | merged (#400 / #401 / #402) | +| ESC | gates block ONLY repo-source writes (R1-R3) | merged (#409) | +| S5 | `--uninstall-enforcement [--purge-config]` | merged (#416) | +| S6 | install seed matches `loadEnforceConfig` identity (coupling guard) | merged (#417) | +| S7 | this phase-index + Principle 12 docs refresh | in progress (closes #403) | +| S8 | the 3 P12 invariants as a required CI gate | open | diff --git a/docs/rfcs/RFC-008/README.md b/docs/rfcs/RFC-008/README.md index fd0ec26..16fe93e 100644 --- a/docs/rfcs/RFC-008/README.md +++ b/docs/rfcs/RFC-008/README.md @@ -18,7 +18,7 @@ requirement parent. Each phase file carries its own R-anchors and back-links to | **P1** | [P1-plugin-registry.md](P1-plugin-registry.md) | R1, R6, R8 | P0, R0b′ | **DONE** — P1a #373 + P1b #374 + P1c #376 + Follow #378 | | **P2** | [P2-bp-contracts.md](P2-bp-contracts.md) | R2, R3, R4 | P0 | **DONE** — P2a #381 + P2b #384 + P2c #388 | | **P3** | [P3-thin-waist.md](P3-thin-waist.md) | R1, R2, R3, R4, R5, R9 | P0, P2 | **DONE** — P3a #389 + P3b-1 #391 + P3c #392 + P3b-2 #393 (P4 schema folded in) + P3d #395 | -| **P4** | [P4-enforce-config.md](P4-enforce-config.md) | R3, R5 | P3 | **IN PROGRESS** — schema P3b-2 #393 + P4a #397 (per-project loader → 3 pre_tool_use gates) + P4c #398 (layer-wide `active:false` kill switch) DONE; P4b reduced E2E + cutover remains | +| **P4** | [P4-enforce-config.md](P4-enforce-config.md) | R3, R5 | P3 | **IN PROGRESS**: P4a #397 (per-project loader, 3 pre_tool_use gates) + P4c #398 (`active:false` kill switch) DONE; **P4d** per-project enforcement re-architecture (Principle 12: enforcement per-project, substrate global) S1-S6 + ESC merged, S7-S8 open (slice detail in workplan). Original P4b superseded by the P4d re-architecture. | | **P5–P7** | [P5-P7-tool-plugins.md](P5-P7-tool-plugins.md) | R6, R10 | P3 | queued | | **P8** | [P8-cursor-windsurf.md](P8-cursor-windsurf.md) | R6, R10 | P3 | queued | | **P9** | [P9-recall-strategies.md](P9-recall-strategies.md) | R7 | — | **DEFERRED** — own RFC |