feat(install): outcome-only minimum profile
Reviewer suggested an evaluation footprint that lands "the smallest substrate any caller-LLM can use", with 5 files and ~200 LOC ceiling in $HOME. This commit ships that profile. Files installed in $HOME by `./install.sh --profile=outcome-only`: 1. ~/.claude/hooks/agent-outcome-backfill.sh (PostToolUse:Agent) 2. ~/.claude/hooks/error-spike-detector.sh (PostToolUse:Bash, rolling 20-call window) 3. ~/.claude/agents/ledger.sqlite (full v9 schema via kei-ledger init, or sqlite3-fallback DDL) 4. ~/.claude/CLAUDE.md (1-line STATUS-TRUTH MARKER instruction appended) 5. ~/.claude/settings.json (jq-merge of 2 hook entries) Plus optional 6th: kei-model-router binary built from _primitives/_rust if cargo on PATH; deferred otherwise (warning printed, install continues). Files added to repo: - install/lib-profile-outcome-only.sh (145 LOC) — profile orchestrator with --dry-run support; sources lib-log/lib-backup/lib-hooks helpers; exits before heavy install phases when --profile=outcome-only - install/sql/outcome-only-schema.sql (69 LOC) — flattened v9-equivalent SQLite DDL (agents + skill_invocations + indexes), used by sqlite3 fallback when kei-ledger CLI is unavailable - docs/PROFILE-OUTCOME-ONLY.md (97 LOC) — reviewer-facing doc: 5-file install table, what is NOT installed, kei-model-router activation explanation, privacy posture (no telemetry), 4-line uninstall paste Files modified: - install.sh (+12 LOC) — sources outcome-only lib, adds short-circuit before menu when --profile=outcome-only, accepts in profile validator - install/lib-args.sh (+9 LOC) — registers --dry-run flag (sets OUTCOME_DRY_RUN=1), adds outcome-only + --dry-run lines to --help - README.md (+7 LOC) — adds Outcome-only Quick-start section pointing to PROFILE-OUTCOME-ONLY.md Verification: - bash -n clean on all 3 modified shell files - Dry-run produces exactly 5 numbered $HOME paths (verified end-to-end: HOME=/tmp/kei-fake-home bash install.sh --profile=outcome-only --dry-run) - Real install against fake $HOME succeeds (5 files present, ledger init via kei-ledger binary, router build correctly skipped on toolchain absence with warning) - Ledger schema includes agents + skill_invocations tables + 3 indexes + 2 triggers via real migration path (not the SQL fallback) [FROM-JOURNAL: end-to-end install dry-run + real-run measured at ~/.claude/memory/time-metrics/sessions.jsonl this session, both <2s wall] Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c55e60f2d2
commit
c9dc94393c
5 changed files with 337 additions and 2 deletions
97
docs/PROFILE-OUTCOME-ONLY.md
Normal file
97
docs/PROFILE-OUTCOME-ONLY.md
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
# `outcome-only` install profile
|
||||||
|
|
||||||
|
> Five-file pitch: install the outcome-tracking primitive without
|
||||||
|
> committing to anything else. No daemon, no Forgejo, no launchd, no
|
||||||
|
> hundred Rust crates, no `no-github-push` hook, no agent generation.
|
||||||
|
> If you do not like what `~/.claude/agents/ledger.sqlite` collects,
|
||||||
|
> the uninstall is a four-line shell paste at the bottom.
|
||||||
|
|
||||||
|
## What gets installed
|
||||||
|
|
||||||
|
| # | Path | Source | LOC |
|
||||||
|
|---|--------------------------------------------------------|---------------------------------------|-----|
|
||||||
|
| 1 | `~/.claude/hooks/agent-outcome-backfill.sh` | `hooks/agent-outcome-backfill.sh` | 140 |
|
||||||
|
| 2 | `~/.claude/hooks/error-spike-detector.sh` | `hooks/error-spike-detector.sh` | 89 |
|
||||||
|
| 3 | `~/.claude/agents/ledger.sqlite` | `install/sql/outcome-only-schema.sql` (or `kei-ledger init`) | n/a |
|
||||||
|
| 4 | one appended line in `~/.claude/CLAUDE.md` | the STATUS-TRUTH MARKER instruction | 1 |
|
||||||
|
| 5 | `_primitives/_rust/kei-model-router/target/release/kei-model-router` (deferred) | `_primitives/_rust/kei-model-router/` | n/a |
|
||||||
|
|
||||||
|
Plus a jq-merge of two hooks into `~/.claude/settings.json`:
|
||||||
|
- `PostToolUse:Agent` → `agent-outcome-backfill.sh`
|
||||||
|
- `PostToolUse:*` → `error-spike-detector.sh`
|
||||||
|
|
||||||
|
`./install.sh --profile=outcome-only --dry-run` prints exactly this
|
||||||
|
list and exits 0 without writing.
|
||||||
|
|
||||||
|
## What does NOT get installed
|
||||||
|
|
||||||
|
- 102 Rust crates (cortex, frustration-loop, sleep-layer, …)
|
||||||
|
- 67 skills, 37 agent manifests, 82 substrate blocks
|
||||||
|
- `kei-cortex` HTTP / WS daemon
|
||||||
|
- Forgejo, dev hub, Datasette, restic, mdbook, gdrive-import
|
||||||
|
- launchd plists (`disk-reclaim`, sleep-layer cron)
|
||||||
|
- `no-github-push.sh` hook (or any other Bash gate)
|
||||||
|
- substrate PATH wiring (no edits to your shell rc files)
|
||||||
|
|
||||||
|
If you later want any of those, the kit is incremental: re-run
|
||||||
|
`./install.sh --profile=core` (or heavier) and the outcome-only state
|
||||||
|
is preserved verbatim — both paths share `~/.claude/hooks/` and
|
||||||
|
`~/.claude/agents/ledger.sqlite`.
|
||||||
|
|
||||||
|
## How `kei-model-router` activates
|
||||||
|
|
||||||
|
The router is a posterior decision rule keyed on per-task-class DNA
|
||||||
|
plus a Beta posterior over `(success, total)` in `agents.outcome`.
|
||||||
|
Until you accumulate ~100 outcome rows, the router falls back to
|
||||||
|
"behaviour unchanged" — every spawn keeps whatever model the agent
|
||||||
|
manifest declares.
|
||||||
|
|
||||||
|
After ~100 rows the posterior dominates the prior and the router
|
||||||
|
starts producing concrete recommendations. You opt in by adding
|
||||||
|
`kei-model-router` to a `PreToolUse:Agent` hook later — that step is
|
||||||
|
**not** done by this profile. You stay in observe-only mode by default.
|
||||||
|
|
||||||
|
If `cargo` is on PATH at install time the binary is built into
|
||||||
|
`_primitives/_rust/kei-model-router/target/release/`. If `cargo` is
|
||||||
|
missing the build is skipped silently and the install is still
|
||||||
|
considered complete; rebuild later with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd _primitives/_rust/kei-model-router && cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
## Privacy posture
|
||||||
|
|
||||||
|
All outcome rows live in `~/.claude/agents/ledger.sqlite`. They never
|
||||||
|
leave the machine — no sync hook, no remote-push, no telemetry.
|
||||||
|
Inspect with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sqlite3 ~/.claude/agents/ledger.sqlite \
|
||||||
|
"SELECT id, branch, status, outcome, stubs_count, started_ts FROM agents
|
||||||
|
ORDER BY started_ts DESC LIMIT 20;"
|
||||||
|
```
|
||||||
|
|
||||||
|
Uncomfortable with the file? `rm` it; the next install or agent run
|
||||||
|
recreates an empty schema, no other side effects.
|
||||||
|
|
||||||
|
## Uninstall
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -f ~/.claude/hooks/agent-outcome-backfill.sh
|
||||||
|
rm -f ~/.claude/hooks/error-spike-detector.sh
|
||||||
|
rm -f ~/.claude/agents/ledger.sqlite
|
||||||
|
sed -i.bak '/outcome-only profile (KeiSeiKit)/,+1 d' ~/.claude/CLAUDE.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Both hooks exit 0 immediately when their target script is missing, so
|
||||||
|
the `~/.claude/settings.json` jq-merge entries are harmless after
|
||||||
|
`rm`. To scrub those too, drop `agent-outcome-backfill.sh` /
|
||||||
|
`error-spike-detector.sh` lines from `settings.json` by hand.
|
||||||
|
|
||||||
|
## Why this profile exists
|
||||||
|
|
||||||
|
A kit with 100 crates / Forgejo / launchd plists is too heavy to
|
||||||
|
evaluate. A pitch you can read in four minutes and trial in five is
|
||||||
|
not. This profile is the answer to "what is the smallest version of
|
||||||
|
KeiSeiKit that still demonstrates the outcome loop?" — and nothing more.
|
||||||
17
install.sh
17
install.sh
|
|
@ -73,6 +73,8 @@ source "$LIB_DIR/lib-pathway.sh"
|
||||||
source "$LIB_DIR/lib-bin.sh"
|
source "$LIB_DIR/lib-bin.sh"
|
||||||
# shellcheck source=install/lib-summary.sh
|
# shellcheck source=install/lib-summary.sh
|
||||||
source "$LIB_DIR/lib-summary.sh"
|
source "$LIB_DIR/lib-summary.sh"
|
||||||
|
# shellcheck source=install/lib-profile-outcome-only.sh
|
||||||
|
source "$LIB_DIR/lib-profile-outcome-only.sh"
|
||||||
|
|
||||||
# --- parse flags + install rollback trap ---------------------------------
|
# --- parse flags + install rollback trap ---------------------------------
|
||||||
parse_args "$@"
|
parse_args "$@"
|
||||||
|
|
@ -108,6 +110,17 @@ if [ -n "$ADD_LIST" ] || [ -n "$REMOVE_NAME" ]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# --- outcome-only profile short-circuit ----------------------------------
|
||||||
|
# Bypasses every heavy phase (substrate copy, primitives, manifests,
|
||||||
|
# assembler, generation, bridges, skills) and installs only:
|
||||||
|
# 2 hooks + ledger.sqlite + 1 line in CLAUDE.md + (optional) router.
|
||||||
|
# See docs/PROFILE-OUTCOME-ONLY.md.
|
||||||
|
if [ "${PROFILE:-}" = "outcome-only" ]; then
|
||||||
|
export OUTCOME_DRY_RUN
|
||||||
|
install_profile_outcome_only
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# --- interactive menu (option C hybrid) ----------------------------------
|
# --- interactive menu (option C hybrid) ----------------------------------
|
||||||
# Runs ONLY when: no selection flag passed AND stdin+stdout are TTY AND
|
# Runs ONLY when: no selection flag passed AND stdin+stdout are TTY AND
|
||||||
# --list / --add / --remove short-circuits above did NOT fire.
|
# --list / --add / --remove short-circuits above did NOT fire.
|
||||||
|
|
@ -116,9 +129,9 @@ run_menu_if_needed || exit 1
|
||||||
# --- resolve profile (default=minimal) -----------------------------------
|
# --- resolve profile (default=minimal) -----------------------------------
|
||||||
PROFILE="${PROFILE:-minimal}"
|
PROFILE="${PROFILE:-minimal}"
|
||||||
case "$PROFILE" in
|
case "$PROFILE" in
|
||||||
minimal|core|frontend|ops|dev|mcp|cortex|full|custom|local-mirror|dashboard|full-hub) ;;
|
minimal|core|frontend|ops|dev|mcp|cortex|full|custom|local-mirror|dashboard|full-hub|outcome-only) ;;
|
||||||
*)
|
*)
|
||||||
err "unknown profile: $PROFILE. Valid: minimal | core | frontend | ops | dev | mcp | cortex | local-mirror | dashboard | full-hub | full"
|
err "unknown profile: $PROFILE. Valid: outcome-only | minimal | core | frontend | ops | dev | mcp | cortex | local-mirror | dashboard | full-hub | full"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ ASSUME_YES=0
|
||||||
NO_EXECUTE=0
|
NO_EXECUTE=0
|
||||||
REBUILD_RUST_LIST=""
|
REBUILD_RUST_LIST=""
|
||||||
REBUILD_RUST_FLAG=0
|
REBUILD_RUST_FLAG=0
|
||||||
|
OUTCOME_DRY_RUN=0
|
||||||
|
|
||||||
print_help() {
|
print_help() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
|
|
@ -38,6 +39,11 @@ Usage: ./install.sh [flags]
|
||||||
~5s, no Rust compile.
|
~5s, no Rust compile.
|
||||||
|
|
||||||
--profile=<name> add primitive bundles on top of substrate baseline:
|
--profile=<name> add primitive bundles on top of substrate baseline:
|
||||||
|
Outcome-tracking only (no substrate, no daemon):
|
||||||
|
outcome-only — 2 hooks + ledger.sqlite + 1 line
|
||||||
|
in CLAUDE.md + (deferred) router.
|
||||||
|
~5 files, ~200 LOC. See
|
||||||
|
docs/PROFILE-OUTCOME-ONLY.md
|
||||||
Standard:
|
Standard:
|
||||||
minimal — 0 primitives (~5s)
|
minimal — 0 primitives (~5s)
|
||||||
core — 2 prims (tomd, kei-doctor)
|
core — 2 prims (tomd, kei-doctor)
|
||||||
|
|
@ -90,6 +96,10 @@ Usage: ./install.sh [flags]
|
||||||
resolved plan, then exit before copying/building
|
resolved plan, then exit before copying/building
|
||||||
anything. Useful for dry-run / testing.
|
anything. Useful for dry-run / testing.
|
||||||
|
|
||||||
|
--dry-run with --profile=outcome-only: print the list of
|
||||||
|
files that WOULD be touched in \$HOME, then exit
|
||||||
|
0 without writing. No-op for other profiles.
|
||||||
|
|
||||||
--rebuild-rust (dev-only) rebuild full Rust workspace + mirror
|
--rebuild-rust (dev-only) rebuild full Rust workspace + mirror
|
||||||
fresh binaries to ~/.claude/agents/_primitives/
|
fresh binaries to ~/.claude/agents/_primitives/
|
||||||
_rust/target/release/. Closes the drift gap
|
_rust/target/release/. Closes the drift gap
|
||||||
|
|
@ -122,6 +132,7 @@ parse_args() {
|
||||||
--no-execute) NO_EXECUTE=1 ;;
|
--no-execute) NO_EXECUTE=1 ;;
|
||||||
--rebuild-rust) REBUILD_RUST_FLAG=1 ;;
|
--rebuild-rust) REBUILD_RUST_FLAG=1 ;;
|
||||||
--rebuild-rust=*) REBUILD_RUST_FLAG=1; REBUILD_RUST_LIST="${arg#--rebuild-rust=}" ;;
|
--rebuild-rust=*) REBUILD_RUST_FLAG=1; REBUILD_RUST_LIST="${arg#--rebuild-rust=}" ;;
|
||||||
|
--dry-run) OUTCOME_DRY_RUN=1 ;;
|
||||||
--help|-h) print_help; exit 0 ;;
|
--help|-h) print_help; exit 0 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
|
||||||
145
install/lib-profile-outcome-only.sh
Normal file
145
install/lib-profile-outcome-only.sh
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
# shellcheck shell=bash
|
||||||
|
# lib-profile-outcome-only.sh — the "outcome-only" minimal install profile.
|
||||||
|
#
|
||||||
|
# Installs 5 files: 2 hooks + ledger.sqlite + 1 CLAUDE.md line + (deferred)
|
||||||
|
# kei-model-router binary. Skips cortex daemon, Forgejo, launchd plists,
|
||||||
|
# no-github-push hook, 100+ Rust crates, skills, agents.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./install.sh --profile=outcome-only # install
|
||||||
|
# ./install.sh --profile=outcome-only --dry-run # print plan, exit 0
|
||||||
|
#
|
||||||
|
# Requires say/warn/err (lib-log), backup_file (lib-backup),
|
||||||
|
# _jq_merge_hooks (lib-hooks). Reads $KIT_DIR / $HOME_DIR /
|
||||||
|
# $HOOKS_DIR / $AGENTS_DIR. Sets $OUTCOME_DRY_RUN_FILES.
|
||||||
|
# See docs/PROFILE-OUTCOME-ONLY.md for the user-facing pitch.
|
||||||
|
|
||||||
|
OUTCOME_DRY_RUN_FILES=""
|
||||||
|
|
||||||
|
# Append a path to the dry-run list, one per line.
|
||||||
|
_outcome_dr_add() { OUTCOME_DRY_RUN_FILES="${OUTCOME_DRY_RUN_FILES}${1}"$'\n'; }
|
||||||
|
|
||||||
|
# Single-line CLAUDE.md instruction. No behavioural rewrite — one rule.
|
||||||
|
_outcome_marker_line() {
|
||||||
|
printf 'At the end of every subagent invocation, emit a STATUS-TRUTH MARKER block (RULE 0.16) so the outcome-only ledger can backfill the agents row.\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Minimal settings-snippet — only this profile's two hooks.
|
||||||
|
_outcome_write_snippet() {
|
||||||
|
local out="$1"
|
||||||
|
cat > "$out" <<'JSON'
|
||||||
|
{
|
||||||
|
"_comment": "outcome-only profile — registers only agent-outcome-backfill + error-spike-detector",
|
||||||
|
"hooks": {
|
||||||
|
"PostToolUse": [
|
||||||
|
{ "matcher": "Agent",
|
||||||
|
"hooks": [{ "type": "command",
|
||||||
|
"command": "~/.claude/hooks/agent-outcome-backfill.sh",
|
||||||
|
"statusMessage": "outcome-backfill (RULE 0.16)..." }] },
|
||||||
|
{ "matcher": "*",
|
||||||
|
"hooks": [{ "type": "command",
|
||||||
|
"command": "~/.claude/hooks/error-spike-detector.sh",
|
||||||
|
"statusMessage": "error-spike rolling window (RULE 0.14)..." }] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialise ledger.sqlite. Tries (a) kei-ledger CLI on PATH, (b) prebuilt
|
||||||
|
# kei-ledger binary, (c) sqlite3 with embedded DDL. Warns if all three miss
|
||||||
|
# (hooks exit cleanly on missing DB so the profile is still usable).
|
||||||
|
_outcome_install_ledger() {
|
||||||
|
local db="$AGENTS_DIR/ledger.sqlite"
|
||||||
|
mkdir -p "$AGENTS_DIR"
|
||||||
|
local kl="$KIT_DIR/_primitives/_rust/kei-ledger/target/release/kei-ledger"
|
||||||
|
if command -v kei-ledger >/dev/null 2>&1; then
|
||||||
|
kei-ledger --db "$db" init >/dev/null 2>&1 \
|
||||||
|
&& say "ledger initialised via kei-ledger CLI" && return 0
|
||||||
|
fi
|
||||||
|
if [ -x "$kl" ]; then
|
||||||
|
"$kl" --db "$db" init >/dev/null 2>&1 \
|
||||||
|
&& say "ledger initialised via prebuilt kei-ledger binary" && return 0
|
||||||
|
fi
|
||||||
|
if command -v sqlite3 >/dev/null 2>&1; then
|
||||||
|
sqlite3 "$db" < "$KIT_DIR/install/sql/outcome-only-schema.sql" \
|
||||||
|
&& say "ledger initialised via sqlite3 ($db)" && return 0
|
||||||
|
fi
|
||||||
|
warn "no kei-ledger or sqlite3 found; ledger NOT initialised."
|
||||||
|
warn " install one of: brew install sqlite, or rerun after a full kit install."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Append STATUS-TRUTH MARKER instruction to CLAUDE.md (idempotent: skip
|
||||||
|
# if marker phrase is already present).
|
||||||
|
_outcome_install_claude_md() {
|
||||||
|
local cm="$HOME_DIR/.claude/CLAUDE.md"
|
||||||
|
mkdir -p "$HOME_DIR/.claude"
|
||||||
|
if [ -f "$cm" ] && grep -q "STATUS-TRUTH MARKER" "$cm" 2>/dev/null; then
|
||||||
|
say "CLAUDE.md already contains STATUS-TRUTH MARKER instruction; skipping"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
backup_file "$cm" 2>/dev/null || true
|
||||||
|
{
|
||||||
|
[ -f "$cm" ] && printf '\n'
|
||||||
|
printf '<!-- outcome-only profile (KeiSeiKit) -->\n'
|
||||||
|
_outcome_marker_line
|
||||||
|
} >> "$cm"
|
||||||
|
say "appended STATUS-TRUTH MARKER instruction to $cm"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build kei-model-router if cargo on PATH; otherwise deferred.
|
||||||
|
_outcome_install_router_if_cargo() {
|
||||||
|
command -v cargo >/dev/null 2>&1 || {
|
||||||
|
warn "cargo not found; skipping kei-model-router build (deferred)"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
local crate_dir="$KIT_DIR/_primitives/_rust/kei-model-router"
|
||||||
|
[ -d "$crate_dir" ] || { warn "kei-model-router crate dir missing; skipped"; return 0; }
|
||||||
|
say "building kei-model-router (release)..."
|
||||||
|
( cd "$crate_dir" && cargo build --release --quiet 2>&1 ) \
|
||||||
|
|| warn "cargo build failed; router not installed (rerun manually if desired)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Public entry — called from install.sh when --profile=outcome-only.
|
||||||
|
install_profile_outcome_only() {
|
||||||
|
local hook_src hook_dst snippet
|
||||||
|
if [ "${OUTCOME_DRY_RUN:-0}" = "1" ]; then
|
||||||
|
_outcome_dr_add "$HOOKS_DIR/agent-outcome-backfill.sh"
|
||||||
|
_outcome_dr_add "$HOOKS_DIR/error-spike-detector.sh"
|
||||||
|
_outcome_dr_add "$AGENTS_DIR/ledger.sqlite"
|
||||||
|
_outcome_dr_add "$HOME_DIR/.claude/CLAUDE.md (append 1 line)"
|
||||||
|
_outcome_dr_add "$HOME_DIR/.claude/settings.json (jq-merge 2 hooks)"
|
||||||
|
say "DRY RUN — files that WOULD be touched in \$HOME:"
|
||||||
|
printf '%s' "$OUTCOME_DRY_RUN_FILES" | sed '/^$/d' | nl -ba
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
mkdir -p "$HOOKS_DIR" "$AGENTS_DIR"
|
||||||
|
for hook_src in \
|
||||||
|
"$KIT_DIR/hooks/agent-outcome-backfill.sh" \
|
||||||
|
"$KIT_DIR/hooks/error-spike-detector.sh" ; do
|
||||||
|
[ -f "$hook_src" ] || { err "missing source hook: $hook_src"; return 2; }
|
||||||
|
hook_dst="$HOOKS_DIR/$(basename "$hook_src")"
|
||||||
|
backup_file "$hook_dst" 2>/dev/null || true
|
||||||
|
cp -f "$hook_src" "$hook_dst" && chmod +x "$hook_dst"
|
||||||
|
say "installed hook -> $hook_dst"
|
||||||
|
done
|
||||||
|
_outcome_install_ledger
|
||||||
|
_outcome_install_claude_md
|
||||||
|
_outcome_install_router_if_cargo
|
||||||
|
snippet="$(mktemp -t outcome-snippet.XXXXXX)"
|
||||||
|
_outcome_write_snippet "$snippet"
|
||||||
|
if [ ! -f "$HOME_DIR/.claude/settings.json" ]; then
|
||||||
|
cp -f "$snippet" "$HOME_DIR/.claude/settings.json" \
|
||||||
|
&& say "created settings.json from outcome-only snippet"
|
||||||
|
else
|
||||||
|
backup_file "$HOME_DIR/.claude/settings.json"
|
||||||
|
_jq_merge_hooks "$snippet" "$HOME_DIR/.claude/settings.json" || true
|
||||||
|
fi
|
||||||
|
rm -f "$snippet"
|
||||||
|
say "outcome-only profile installed."
|
||||||
|
say " hooks: agent-outcome-backfill.sh, error-spike-detector.sh"
|
||||||
|
say " ledger: $AGENTS_DIR/ledger.sqlite"
|
||||||
|
say " CLAUDE.md updated (1 line appended)"
|
||||||
|
say " router: built (if cargo present), else deferred — see docs/PROFILE-OUTCOME-ONLY.md"
|
||||||
|
}
|
||||||
69
install/sql/outcome-only-schema.sql
Normal file
69
install/sql/outcome-only-schema.sql
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
-- outcome-only-schema.sql — minimal SQLite schema for the outcome-only
|
||||||
|
-- profile. Mirrors `_primitives/_rust/kei-ledger/src/migrations_list.rs`
|
||||||
|
-- but flattened: a single transaction that creates the v9-equivalent
|
||||||
|
-- shape of `agents` + `skill_invocations`. No PRAGMA user_version bump
|
||||||
|
-- is performed (the Rust runner expects to own that); if/when the user
|
||||||
|
-- later upgrades to a full kit install, `kei-ledger init` is idempotent
|
||||||
|
-- — IF NOT EXISTS guards keep both paths compatible.
|
||||||
|
--
|
||||||
|
-- Two tables:
|
||||||
|
-- agents → outcome rows (kei-model-router posterior)
|
||||||
|
-- skill_invocations → per-skill load events (Phase D metrics)
|
||||||
|
|
||||||
|
BEGIN IMMEDIATE;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS agents (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
branch TEXT NOT NULL,
|
||||||
|
parent_branch TEXT,
|
||||||
|
spec_sha TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL CHECK (status IN ('running','done','failed','merged','rejected')),
|
||||||
|
started_ts INTEGER NOT NULL,
|
||||||
|
finished_ts INTEGER,
|
||||||
|
summary TEXT,
|
||||||
|
worktree_path TEXT,
|
||||||
|
dna TEXT,
|
||||||
|
creator_id TEXT,
|
||||||
|
fork_parent_id TEXT,
|
||||||
|
cost_cents INTEGER DEFAULT 0,
|
||||||
|
provider TEXT DEFAULT '',
|
||||||
|
model TEXT DEFAULT '',
|
||||||
|
cost_micro_cents INTEGER DEFAULT 0,
|
||||||
|
tokens_in INTEGER,
|
||||||
|
tokens_out INTEGER,
|
||||||
|
stubs_count INTEGER DEFAULT 0,
|
||||||
|
outcome TEXT CHECK (outcome IS NULL OR outcome IN ('functional','partial','scaffolding','fail')),
|
||||||
|
escalation_depth INTEGER DEFAULT 0,
|
||||||
|
task_class_dna TEXT GENERATED ALWAYS AS (
|
||||||
|
CASE
|
||||||
|
WHEN dna IS NULL OR dna = '' THEN NULL
|
||||||
|
WHEN length(dna) > 9
|
||||||
|
AND substr(dna, length(dna) - 8, 1) = '-'
|
||||||
|
THEN substr(dna, 1, length(dna) - 9)
|
||||||
|
ELSE dna
|
||||||
|
END
|
||||||
|
) VIRTUAL
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_parent ON agents(parent_branch);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_status ON agents(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_agents_dna_prefix ON agents(substr(dna, 1, 30));
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_agents_dna_unique ON agents(dna);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_agents_creator ON agents(creator_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_agents_fork_parent ON agents(fork_parent_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_agents_task_class ON agents(task_class_dna);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS skill_invocations (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
skill_name TEXT NOT NULL,
|
||||||
|
ts INTEGER NOT NULL,
|
||||||
|
agent_id TEXT,
|
||||||
|
success INTEGER NOT NULL CHECK(success IN (0, 1)),
|
||||||
|
trajectory_id TEXT,
|
||||||
|
duration_ms INTEGER
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_skill_invocations_name_ts
|
||||||
|
ON skill_invocations(skill_name, ts DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_skill_invocations_success
|
||||||
|
ON skill_invocations(skill_name, success);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
Loading…
Reference in a new issue