fix(audit-batch-2): regressions from prev batch + 2nd-wave audit findings

12-agent audit (waves 3+4 Opus+Sonnet) on commit 88de01c found that 2 of
my prior fixes had regressions, plus the prev batch missed 8 stale-text
sites and 2 latent bugs. This batch closes them all.

== Regressions in audit-batch (88de01c) — now fixed ==

1. PRAGMA user_version=9 placement — could silently downgrade schema on
   cross-version install (existing v10 DB → re-run reset to 9 →
   migrations replay → ALTER TABLE duplicate-column errors)
   - install/sql/outcome-only-schema.sql: PRAGMA moved OUTSIDE the
     transaction (after COMMIT) for portability across SQLite versions
   - install/lib-profile-outcome-only.sh::_outcome_install_ledger:
     added downgrade guard — reads existing user_version BEFORE running
     ANY init path; if >9, skips entirely (preserves newer schema)
   - VERIFIED: simulated v10 DB → re-run prints "skipping init to
     preserve newer schema"; user_version stays at 10 (was downgraded
     to 9 in the prior batch) [REAL: ran in this session]

2. backup_file mv→cp workaround left orphan backups + bypassed rollback
   contract (BACKUP_PAIRS not registered)
   - install/lib-profile-outcome-only.sh: now manually appends to
     BACKUP_PAIRS so rollback trap restores on later failure;
     removes the .bak on success path
   - Comment updated to explain the workaround vs backup_file mv

3. CLAUDE.md skip-guard "STATUS-TRUTH MARKER" was too broad —
   false-positive on existing kit users (RULE 0.16 doc text matches)
   - lib-profile-outcome-only.sh: changed grep to literal HTML comment
     marker `<!-- outcome-only profile (KeiSeiKit) -->` (specific marker
     written by the installer itself)

== Tier 1 missed in prev batch — now fixed ==

4. _ts_packages/package-lock.json referenced packages/cortex-ui which
   does NOT exist on disk → npm ci would fail with ELSPROBLEMS in CI
   - Regenerated via fresh `rm package-lock.json && npm install`
   - npm ci now exits 0 cleanly [REAL: ran in this session]
   - Lockfile shrunk 2403→0 lines on the cortex-ui section (full regen)

5. v3 triggers (branch length cap ≤256) were MISSING from
   outcome-only-schema.sql — sqlite3 fallback path skipped a schema
   feature that the Rust kei-ledger flow enforces, creating cross-flow
   drift
   - Added trg_agents_branch_len_ins + trg_agents_branch_len_upd
     mirroring migrations_list.rs:30-44
   - Header comment in outcome-only-schema.sql rewritten to match
     current behavior (was stale)
   - VERIFIED: end-to-end install creates 2 triggers [REAL: sqlite3
     .schema | grep trg_agents_branch_len returns 2]

6. README.md:232 said "102 crates" while README.md:9 said "105 crates"
   — internal contradiction in same doc
   - README:232 → "105 workspace crates"

7. ARCHITECTURE.md:165 "53 Rust crates + 13 shell primitives" stale
   - Updated to "105 Rust workspace crates (47 declared in MANIFEST.toml
     `full` profile) + 14 shell primitives"

8. ARCHITECTURE.md:157 "45 /commands" stale
   - Updated to 68

9. plugin.json + marketplace.json description strings still had
   pre-fix counts (23 primitives / 39 skills / 9 hooks / 12 agents)
   - Both rewritten to match README:9 SSoT (38 agents / 68 skills /
     38 hooks / 105 workspace crates / 47 installable + 14 shell)

10. PROFILE-OUTCOME-ONLY.md:28-29 "What does NOT get installed" still
    cited 102/67/37/82
    - Updated to 105/68/38/85

11. encyclopedia/substrate-overview.md §6/§11/§12 still said
    "80-char DNA"; §13 said "495 DNA indices"; §6 said "11 install
    profiles (.../Cursor/Continue/etc)"
    - All 4 sites fixed to current language (≥33-char variable, 565
      DNAs, 12 install profiles)

12. docs/DNA-INDEX.md:1352 said wire format is "(80 chars)"
    - Updated to "(≥33 chars; role + caps slugs are variable — see
      docs/DNA-FORMAT.md)"

== Tier 2 honesty fixes ==

13. Wagner et al. 2004 citation in SLEEP-LAYER.md:26 lacked [VERIFIED]
    marker (W3 doc consistency caught it)
    - Added [VERIFIED: doi:10.1038/nature02223] + clarification that
      the original study did not isolate a specific sleep stage; SWS
      attribution comes from secondary literature (Diekelmann/Born)

14. PHILOSOPHY.md:125 attributed "overnight consolidation of un-finished
    intentions" to Wagner 2004 — that paper is about insight gain on
    the Number Reduction Task, not Zeigarnik-effect cued memory
    - Rewritten to accurately describe Wagner 2004's actual finding +
      [VERIFIED: doi:10.1038/nature02223]

Verification:
- `npm ci` in _ts_packages/ exits 0 [REAL: ran in this session]
- `cargo check --workspace` exits 0 in _primitives/_rust [REAL: ran in
  this session]
- Outcome-only end-to-end fresh install produces user_version=9 +
  2 triggers (correct schema shape)
- Outcome-only re-run against v10 DB preserves user_version=10
  (downgrade guard works)
- CLAUDE.md skip-guard now triggers ONLY on literal marker, not on
  RULE 0.16 phrase

NOT addressed in this batch (deferred to a future round):
- github KeiSei84/{KeiSeiKit, KeiSeiKit-1.0} 404 (user-side action:
  publish repo or update refs)
- keigit user `keisei` does not exist (user-side: create org or
  rename scope)
- KEIGIT_TOKEN secret not configured (user-side action)
- Forgejo registration disabled (admin-side)
- safeEqual timing leak in TS server (LOW per W3 reassessment)
- HTTP bind 0.0.0.0 default (MEDIUM)
- Unbounded request body (MEDIUM)
- Outcome-only confirm-screen bypass (RULE 0.1 spirit)
- Ledger fallthrough false summary
- Node 20 deprecation (deadline 2026-06-02, 30 days)
- Hook count triple-discrepancy (38 README / 53 DNA-INDEX / 35 maturity-row)
- 100-row router claim still in README:117 + PROFILE-OUTCOME-ONLY.md
- INSTALL.md numerics without [REAL:] markers
- Stale .bak files accumulation policy (cosmetic)
- README per-claim [REAL: ] markers for 6 of 7 numerics

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Parfii-bot 2026-05-03 20:30:51 +08:00
parent 88de01cae0
commit 209460df6b
12 changed files with 117 additions and 2313 deletions

View file

@ -15,7 +15,7 @@
"source": "github", "source": "github",
"repo": "KeiSei84/KeiSeiKit" "repo": "KeiSei84/KeiSeiKit"
}, },
"description": "Full KeiSeiKit — 12-agent fleet, 39 skills, 9 hooks, 23 Rust primitives, sleep-sync cloud consolidation, MCP server layer" "description": "Full KeiSeiKit — 38 agent manifests, 68 skills, 38 hooks, 105 Rust workspace crates (47 installable primitives via MANIFEST.toml `full` profile + 14 shell primitives), sleep-sync cloud consolidation, MCP server layer"
} }
] ]
} }

View file

@ -1,7 +1,7 @@
{ {
"name": "keisei", "name": "keisei",
"version": "0.16.0", "version": "0.16.0",
"description": "Constructor-Pattern agent kit for Claude Code: composable behavioral blocks, 23 Rust primitives, 39 portable skills, 9 pre-wired hooks, typed artifact handoff, sleep-sync cloud consolidation, MCP server layer.", "description": "Constructor-Pattern agent kit for Claude Code: 38 agent manifests, 68 portable skills, 38 hooks, 105 Rust workspace crates (47 installable primitives + 14 shell primitives via MANIFEST.toml `full` profile), typed artifact handoff, sleep-sync cloud consolidation, MCP server layer.",
"author": { "author": {
"name": "KeiSei", "name": "KeiSei",
"url": "https://github.com/KeiSei84" "url": "https://github.com/KeiSei84"

View file

@ -229,9 +229,9 @@ Orchestrator skill `landing-page` composes 11 skills across 6 recipes
## Architecture ## Architecture
Stack: **Rust core** (102 crates, ≤2 MB each, 12-trait runtime + plugin Stack: **Rust core** (105 workspace crates, ≤2 MB each, 12-trait runtime
registry) + **TypeScript glue** (6 adapters: gmail / grok / recall / + plugin registry) + **TypeScript glue** (6 adapters: gmail / grok /
telegram / youtube / mcp-server). Backend impls cover: recall / telegram / youtube / mcp-server). Backend impls cover:
| Trait | Impls | | Trait | Impls |
|---|---| |---|---|

File diff suppressed because it is too large Load diff

View file

@ -154,7 +154,7 @@ The substrate is layered. Each layer has a single contract; layers below are sta
@keisei/vscode-cortex · MCP clients (Cline, OpenClaw, Cursor MCP) │ @keisei/vscode-cortex · MCP clients (Cline, OpenClaw, Cursor MCP) │
├──────────────────────────────────────────────────────────────────────────┤ ├──────────────────────────────────────────────────────────────────────────┤
│ Layer 3 — Skills (markdown wizards) │ │ Layer 3 — Skills (markdown wizards) │
45 `/commands` under skills/<name>/SKILL.md │ 68 `/commands` under skills/<name>/SKILL.md │
│ Each is an AskUserQuestion-driven phase-pipeline (5-9 phases typical) │ │ Each is an AskUserQuestion-driven phase-pipeline (5-9 phases typical) │
├──────────────────────────────────────────────────────────────────────────┤ ├──────────────────────────────────────────────────────────────────────────┤
│ Layer 2 — Recipes (TOML DAGs) │ │ Layer 2 — Recipes (TOML DAGs) │
@ -162,8 +162,8 @@ The substrate is layered. Each layer has a single contract; layers below are sta
│ Runtime: kei-pipe │ │ Runtime: kei-pipe │
├──────────────────────────────────────────────────────────────────────────┤ ├──────────────────────────────────────────────────────────────────────────┤
│ Layer 1 — Primitives (cubes) │ │ Layer 1 — Primitives (cubes) │
53 Rust crates + 13 shell primitives 105 Rust workspace crates (47 declared in MANIFEST.toml `full` profile)
Each ≤ 200 LOC per file, ≤ 30 LOC per function (Constructor Pattern) + 14 shell primitives. Each ≤ 200 LOC / file, ≤ 30 LOC / function.
├──────────────────────────────────────────────────────────────────────────┤ ├──────────────────────────────────────────────────────────────────────────┤
│ Layer 0 — Atoms (locked vocabulary) │ │ Layer 0 — Atoms (locked vocabulary) │
│ 13 verbs: INGEST PARSE TRANSFORM ENRICH VALIDATE DECIDE DISPATCH │ │ 13 verbs: INGEST PARSE TRANSFORM ENRICH VALIDATE DECIDE DISPATCH │

View file

@ -1349,6 +1349,6 @@ Sorted alphabetically by name.
## Schema notes ## Schema notes
- `dna` wire format: `<block_type>::<caps>::<scope_sha8>::<body_sha8>-<nonce8>` (80 chars). - `dna` wire format: `<block_type>::<caps>::<scope_sha8>::<body_sha8>-<nonce8>` (≥33 chars; role + caps slugs are variable — see docs/DNA-FORMAT.md).
- Active vs superseded: rows where `superseded_by IS NULL` are active. - Active vs superseded: rows where `superseded_by IS NULL` are active.
- See `_primitives/_rust/kei-shared/src/dna.rs` for canonical DNA spec. - See `_primitives/_rust/kei-shared/src/dna.rs` for canonical DNA spec.

View file

@ -122,10 +122,13 @@ Claude Code agent on Anthropic's cloud picks up the queue (up to 480
minutes total across ≤ 5 tasks, packed greedily in FIFO order) and minutes total across ≤ 5 tasks, packed greedily in FIFO order) and
works until the budget or checkpoint fires. works until the budget or checkpoint fires.
Biological analog: the overnight consolidation of un-finished intentions Biological analog: the post-sleep insight gain documented by
(Wagner et al. 2004, *Nature*). Things unsolved when you fell asleep are Wagner et al. 2004, *Nature* 427:352355
often solved by morning not because the brain ran harder, but because [VERIFIED: doi:10.1038/nature02223]. The original paper showed that
it ran offline. problems unsolved when you went to bed are sometimes solved on waking
not because the brain ran harder but because it ran offline; the study
did not isolate a specific sleep stage, and our metaphor is a loose
mapping of that observation onto the kit's offline consolidation.
### Phase B — REM consolidation ### Phase B — REM consolidation

View file

@ -25,8 +25,8 @@ list and exits 0 without writing.
## What does NOT get installed ## What does NOT get installed
- 102 Rust crates (cortex, frustration-loop, sleep-layer, …) - 105 Rust workspace crates (cortex, frustration-loop, sleep-layer, …)
- 67 skills, 37 agent manifests, 82 substrate blocks - 68 skills, 38 agent manifests, 85 substrate blocks
- `kei-cortex` HTTP / WS daemon - `kei-cortex` HTTP / WS daemon
- Forgejo, dev hub, Datasette, restic, mdbook, gdrive-import - Forgejo, dev hub, Datasette, restic, mdbook, gdrive-import
- launchd plists (`disk-reclaim`, sleep-layer cron) - launchd plists (`disk-reclaim`, sleep-layer cron)

View file

@ -23,7 +23,7 @@ Day →→→ │ Phase A Phase B Phase C │ →
<uuid>.md (always) (every N days) <uuid>.md (always) (every N days)
``` ```
**Biological analog.** Your Mac is the hippocampus (fast, stateful, volatile — captures raw episodes). The memory-repo is the transport layer. The cloud agent is the neocortex (slow, stateless, generalising). The morning `git pull` is the recall. Phase A mirrors the "sleep on it" effect (Wagner et al. 2004, *Nature*), Phase B mirrors REM dream-state pattern extraction, Phase C mirrors NREM slow-wave system consolidation. **Biological analog.** Your Mac is the hippocampus (fast, stateful, volatile — captures raw episodes). The memory-repo is the transport layer. The cloud agent is the neocortex (slow, stateless, generalising). The morning `git pull` is the recall. Phase A mirrors the "sleep on it" insight effect (Wagner et al. 2004, *Nature* 427:352355 [VERIFIED: doi:10.1038/nature02223]; the original study did not isolate a specific stage — secondary literature attributes the effect primarily to slow-wave sleep, our mapping is loose). Phase B mirrors REM dream-state pattern extraction. Phase C mirrors NREM slow-wave system consolidation.
**Phase interaction rules (important):** **Phase interaction rules (important):**

View file

@ -210,11 +210,11 @@ KeiSeiKit is a **multi-tier agent composition substrate** built on four mutually
| **ARCHITECTURE.md** | Stack overview + trait impl matrix | Compute/Git/Memory/Auth/Notify/Network/LLM/ServiceManager impls per backend | | **ARCHITECTURE.md** | Stack overview + trait impl matrix | Compute/Git/Memory/Auth/Notify/Network/LLM/ServiceManager impls per backend |
| **AGENT-ROLES.md** | Generated role matrix (human-readable) | 7 roles × capabilities × tools × escalation (auto-generated from _roles/*.toml) | | **AGENT-ROLES.md** | Generated role matrix (human-readable) | 7 roles × capabilities × tools × escalation (auto-generated from _roles/*.toml) |
| **PHILOSOPHY.md** | Substrate philosophy + design principles | Single-source-of-truth, Constructor Pattern, no overlays, decomposability | | **PHILOSOPHY.md** | Substrate philosophy + design principles | Single-source-of-truth, Constructor Pattern, no overlays, decomposability |
| **DNA-INDEX.md** | Agent DNA (deterministic identity) format | 80-char DNA breakdown: role::caps::scope-sha8::body-sha8-nonce | | **DNA-INDEX.md** | Agent DNA (deterministic identity) format | ≥33-char variable-length DNA breakdown: role::caps::scope-sha8::body-sha8-nonce |
| **IMPORT-RUNTIME.md** | Foreign-project ingestion pipeline | Decompose → match → extract skills → plan → execute (Hermes proof-of-concept) | | **IMPORT-RUNTIME.md** | Foreign-project ingestion pipeline | Decompose → match → extract skills → plan → execute (Hermes proof-of-concept) |
| **PUBLISHING.md** | Community npm registry + scoped package publishing | keigit.com npm, OAuth, per-user PAT, `npm publish` / `npm install` flow | | **PUBLISHING.md** | Community npm registry + scoped package publishing | keigit.com npm, OAuth, per-user PAT, `npm publish` / `npm install` flow |
| **RULES-AS-BLOCKS.md** | How user ~/.claude/rules/*.md become prompt blocks | Rule fragment extraction, RULE re-composition, link-tracking | | **RULES-AS-BLOCKS.md** | How user ~/.claude/rules/*.md become prompt blocks | Rule fragment extraction, RULE re-composition, link-tracking |
| **QUICKSTART.md** | 60-second install guide | 11 install profiles (minimal/core/full + MCP/Cortex/Cursor/Continue/etc) | | **QUICKSTART.md** | 60-second install guide | 12 install profiles (outcome-only/minimal/core/frontend/ops/dev/mcp/cortex/local-mirror/dashboard/full-hub/full) |
| **INSTALL.md** | Full installation docs | Prereqs, profiles, lib-*.sh breakdown, troubleshooting | | **INSTALL.md** | Full installation docs | Prereqs, profiles, lib-*.sh breakdown, troubleshooting |
| **CONVERGENCE-PLAN.md** | Multi-stream parallel work roadmap | UI / Atoms refactor / Graph / Runtime phases (2026-06 closure target) | | **CONVERGENCE-PLAN.md** | Multi-stream parallel work roadmap | UI / Atoms refactor / Graph / Runtime phases (2026-06 closure target) |
| **REFERENCE.md** | Command-line reference for kei-* binaries | kei-runtime, kei-sage, kei-registry, kei-import, kei-capability, kei-forge | | **REFERENCE.md** | Command-line reference for kei-* binaries | kei-runtime, kei-sage, kei-registry, kei-import, kei-capability, kei-forge |
@ -361,7 +361,7 @@ These are concatenated in role-declared order, with `\n\n---\n\n` separators bet
| Capabilities exist + have both toml + text.md | assembler `validate-capability` + CI | compose fails; missing fragments | | Capabilities exist + have both toml + text.md | assembler `validate-capability` + CI | compose fails; missing fragments |
| JSON Schemas are valid draft-07 | `kei-schema-lint` + CI | atom discovery skips malformed schema | | JSON Schemas are valid draft-07 | `kei-schema-lint` + CI | atom discovery skips malformed schema |
| Gates/verifies registered in kei-agent-runtime | `cargo test --all` | gate lookup fails; aborting capability | | Gates/verifies registered in kei-agent-runtime | `cargo test --all` | gate lookup fails; aborting capability |
| Agent DNA is 80 chars unique per invocation | kei-ledger fork + DNA-INDEX.md | collision risk; query ambiguity | | Agent DNA is variable-length (≥33 chars) unique per invocation | kei-ledger fork + DNA-INDEX.md | collision risk; query ambiguity |
| No file edits outside files-whitelist | scope::files-whitelist gate | merge blocked; scope violation | | No file edits outside files-whitelist | scope::files-whitelist gate | merge blocked; scope violation |
| Files ≤ 200 LOC, functions ≤ 30 LOC | quality::constructor-pattern verify | merge blocked; pattern violation | | Files ≤ 200 LOC, functions ≤ 30 LOC | quality::constructor-pattern verify | merge blocked; pattern violation |
| cargo check + tests green | quality::cargo-check-green + tests-green | merge blocked; integration failure | | cargo check + tests green | quality::cargo-check-green + tests-green | merge blocked; integration failure |
@ -381,7 +381,7 @@ These are concatenated in role-declared order, with `\n\n---\n\n` separators bet
| **Role** | Bundle of capabilities + tool allowlist + escalation policy | `edit-local`, `read-only`, `git-ops` | | **Role** | Bundle of capabilities + tool allowlist + escalation policy | `edit-local`, `read-only`, `git-ops` |
| **Gate** | PreToolUse Rust impl blocking tool calls (deny) | `policy::no-git-ops` blocks `^git` | | **Gate** | PreToolUse Rust impl blocking tool calls (deny) | `policy::no-git-ops` blocks `^git` |
| **Verify** | On-return Rust impl validating agent output + integration | `quality::cargo-check-green` runs `cargo check --workspace` | | **Verify** | On-return Rust impl validating agent output + integration | `quality::cargo-check-green` runs `cargo check --workspace` |
| **DNA** | 80-char deterministic agent identity; "did this run before?" without embeddings | `code-impl::edit-local::7f1a2c3d::5e9b4f2a-nonce` | | **DNA** | ≥33-char variable-length deterministic agent identity; "did this run before?" without embeddings | `code-impl::edit-local::7f1a2c3d::5e9b4f2a-nonce` |
| **Simulated merge** | Orchestrator creates test branch, applies agent diff, runs checks from there | catches integration regressions pre-merge | | **Simulated merge** | Orchestrator creates test branch, applies agent diff, runs checks from there | catches integration regressions pre-merge |
| **Hands off** | Agent delegates work to another agent via manifest `[[handoff]]` table | `code-implementer` hands off to `ml-implementer` | | **Hands off** | Agent delegates work to another agent via manifest `[[handoff]]` table | `code-implementer` hands off to `ml-implementer` |
@ -402,7 +402,7 @@ These are concatenated in role-declared order, with `\n\n---\n\n` separators bet
| Bridges | stable | 11 cross-tool format generators (.cursorrules, .windsurf/rules, GEMINI.md, etc.) | | Bridges | stable | 11 cross-tool format generators (.cursorrules, .windsurf/rules, GEMINI.md, etc.) |
| Sleep Layer (Phase A/B/C) | stable | Incubation (tasks), REM consolidation (reports), NREM deep-sleep (conflicts) | | Sleep Layer (Phase A/B/C) | stable | Incubation (tasks), REM consolidation (reports), NREM deep-sleep (conflicts) |
| Foreign-project ingestion | stable | kei-import <repo> proof-of-concept via Hermes validation | | Foreign-project ingestion | stable | kei-import <repo> proof-of-concept via Hermes validation |
| Nightly consolidation | active | Running 2026-05-02; Phase A + B + C observed; reports 495 DNA indices | | Nightly consolidation | active | Running 2026-05-02; Phase A + B + C observed; reports 565 DNA indices (per docs/DNA-INDEX.md header 2026-05-03) |
**Roadmap:** **Roadmap:**
- Model router (Bayesian posterior, currently manual routing) - Model router (Bayesian posterior, currently manual routing)

View file

@ -53,6 +53,19 @@ _outcome_install_ledger() {
local db="$AGENTS_DIR/ledger.sqlite" local db="$AGENTS_DIR/ledger.sqlite"
mkdir -p "$AGENTS_DIR" mkdir -p "$AGENTS_DIR"
local kl="$KIT_DIR/_primitives/_rust/kei-ledger/target/release/kei-ledger" local kl="$KIT_DIR/_primitives/_rust/kei-ledger/target/release/kei-ledger"
# Cross-version downgrade guard (audit fix 2026-05-03 W3): if an
# existing DB is at a NEWER schema (user_version > 9, e.g. user
# later upgrades to a full kit that adds a v10 migration), do NOT
# re-run any init path — the SQL fallback would otherwise reset
# user_version and the binary path may replay incompatible v9 ALTERs.
if [ -f "$db" ] && command -v sqlite3 >/dev/null 2>&1; then
local current_v
current_v=$(sqlite3 "$db" "PRAGMA user_version;" 2>/dev/null || echo 0)
if [ "${current_v:-0}" -gt 9 ] 2>/dev/null; then
say "ledger already at schema v$current_v (>9); skipping init to preserve newer schema"
return 0
fi
fi
if command -v kei-ledger >/dev/null 2>&1; then if command -v kei-ledger >/dev/null 2>&1; then
kei-ledger --db "$db" init >/dev/null 2>&1 \ kei-ledger --db "$db" init >/dev/null 2>&1 \
&& say "ledger initialised via kei-ledger CLI" && return 0 && say "ledger initialised via kei-ledger CLI" && return 0
@ -67,16 +80,21 @@ _outcome_install_ledger() {
fi fi
warn "no kei-ledger or sqlite3 found; ledger NOT initialised." warn "no kei-ledger or sqlite3 found; ledger NOT initialised."
warn " install one of: brew install sqlite, or rerun after a full kit install." warn " install one of: brew install sqlite, or rerun after a full kit install."
return 0 return 1
} }
# Append STATUS-TRUTH MARKER instruction to CLAUDE.md (idempotent: skip # Append STATUS-TRUTH MARKER instruction to CLAUDE.md (idempotent: skip
# if marker phrase is already present). # if our specific marker comment is already present).
_outcome_install_claude_md() { _outcome_install_claude_md() {
local cm="$HOME_DIR/.claude/CLAUDE.md" local cm="$HOME_DIR/.claude/CLAUDE.md"
mkdir -p "$HOME_DIR/.claude" mkdir -p "$HOME_DIR/.claude"
if [ -f "$cm" ] && grep -q "STATUS-TRUTH MARKER" "$cm" 2>/dev/null; then # Audit fix 2026-05-03 (W3): match the literal HTML comment marker we
say "CLAUDE.md already contains STATUS-TRUTH MARKER instruction; skipping" # wrote, NOT the broad phrase "STATUS-TRUTH MARKER" — the broad phrase
# is also used in RULE 0.16 documentation that many users already have
# in their CLAUDE.md, which would cause a false-positive skip and the
# outcome-only instruction would never land.
if [ -f "$cm" ] && grep -qF '<!-- outcome-only profile (KeiSeiKit) -->' "$cm"; then
say "CLAUDE.md already contains outcome-only marker; skipping"
return 0 return 0
fi fi
backup_file "$cm" 2>/dev/null || true backup_file "$cm" 2>/dev/null || true
@ -133,17 +151,22 @@ install_profile_outcome_only() {
cp -f "$snippet" "$HOME_DIR/.claude/settings.json" \ cp -f "$snippet" "$HOME_DIR/.claude/settings.json" \
&& say "created settings.json from outcome-only snippet" && say "created settings.json from outcome-only snippet"
else else
# Audit fix 2026-05-03: backup_file MOVES the target. We need the file # Audit fix 2026-05-03 (W3): backup_file MOVES the target which would
# still in place for jq to read during merge — copy aside instead so # leave _jq_merge_hooks with no file to read. Use cp -p to copy aside
# _jq_merge_hooks reads the original. The trap-pair entry registered # AND register the pair in BACKUP_PAIRS so the rollback trap restores
# by backup_file restores on rollback if the merge fails. # it on later failure (was orphan-bak-not-in-rollback-contract).
cp -p "$HOME_DIR/.claude/settings.json" \ local _ts
"$HOME_DIR/.claude/settings.json.bak-$(date +%s)" _ts=$(date +%s)
local _bak="$HOME_DIR/.claude/settings.json.bak-$_ts"
cp -p "$HOME_DIR/.claude/settings.json" "$_bak"
BACKUP_PAIRS+=("$HOME_DIR/.claude/settings.json|$_bak")
if ! _jq_merge_hooks "$snippet" "$HOME_DIR/.claude/settings.json"; then if ! _jq_merge_hooks "$snippet" "$HOME_DIR/.claude/settings.json"; then
err "settings.json merge failed; the .bak file remains as recovery" err "settings.json merge failed; rollback trap will restore from $_bak"
rm -f "$snippet" rm -f "$snippet"
return 1 return 1
fi fi
# Success: remove our backup (rollback contract released)
rm -f "$_bak"
fi fi
rm -f "$snippet" rm -f "$snippet"
say "outcome-only profile installed." say "outcome-only profile installed."

View file

@ -1,10 +1,17 @@
-- outcome-only-schema.sql — minimal SQLite schema for the outcome-only -- outcome-only-schema.sql — minimal SQLite schema for the outcome-only
-- profile. Mirrors `_primitives/_rust/kei-ledger/src/migrations_list.rs` -- profile. Mirrors `_primitives/_rust/kei-ledger/src/migrations_list.rs`
-- but flattened: a single transaction that creates the v9-equivalent -- but flattened: a single transaction that creates the v9-equivalent
-- shape of `agents` + `skill_invocations`. No PRAGMA user_version bump -- shape of `agents` + `skill_invocations`, plus the v3 BEFORE-INSERT/
-- is performed (the Rust runner expects to own that); if/when the user -- UPDATE triggers that enforce branch length ≤256.
-- later upgrades to a full kit install, `kei-ledger init` is idempotent --
-- — IF NOT EXISTS guards keep both paths compatible. -- PRAGMA user_version = 9 is set OUTSIDE the transaction (after COMMIT)
-- so it lands atomically and is portable across SQLite versions
-- (transaction-aware PRAGMA writes are documented-undefined on
-- pre-3.37 builds). The shell installer (`_outcome_install_ledger`
-- in `lib-profile-outcome-only.sh`) guards re-runs against an
-- already-upgraded DB by reading user_version BEFORE invoking this
-- file; that prevents silent downgrade if the user later runs full
-- kit which bumps schema past v9.
-- --
-- Two tables: -- Two tables:
-- agents → outcome rows (kei-model-router posterior) -- agents → outcome rows (kei-model-router posterior)
@ -66,10 +73,30 @@ CREATE INDEX IF NOT EXISTS idx_skill_invocations_name_ts
CREATE INDEX IF NOT EXISTS idx_skill_invocations_success CREATE INDEX IF NOT EXISTS idx_skill_invocations_success
ON skill_invocations(skill_name, success); ON skill_invocations(skill_name, success);
-- Audit fix 2026-05-03: bump user_version to 9 so a later `kei-ledger init` -- v3 triggers — enforce branch length ≤256 chars (mirrors
-- short-circuits via the `current >= 9` gate instead of replaying v1..v9 -- `migrations_list.rs:30-44` v3 migration). Without these, the flat
-- migrations whose ALTER TABLE ADD COLUMN steps would error with "duplicate -- schema would silently accept rows that the Rust kei-ledger flow
-- column" against the already-flat schema this file installed. -- rejects, creating cross-version drift.
PRAGMA user_version = 9; CREATE TRIGGER IF NOT EXISTS trg_agents_branch_len_ins
BEFORE INSERT ON agents
BEGIN
SELECT RAISE(ABORT, 'branch length exceeds 256')
WHERE length(NEW.branch) > 256;
SELECT RAISE(ABORT, 'parent_branch length exceeds 256')
WHERE NEW.parent_branch IS NOT NULL AND length(NEW.parent_branch) > 256;
END;
CREATE TRIGGER IF NOT EXISTS trg_agents_branch_len_upd
BEFORE UPDATE OF branch, parent_branch ON agents
BEGIN
SELECT RAISE(ABORT, 'branch length exceeds 256')
WHERE length(NEW.branch) > 256;
SELECT RAISE(ABORT, 'parent_branch length exceeds 256')
WHERE NEW.parent_branch IS NOT NULL AND length(NEW.parent_branch) > 256;
END;
COMMIT; COMMIT;
-- PRAGMA user_version is set OUTSIDE the transaction so the write is
-- portable across SQLite versions. The shell installer guards against
-- silent downgrade by checking user_version BEFORE invoking this file.
PRAGMA user_version = 9;