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>
145 lines
5.8 KiB
Bash
145 lines
5.8 KiB
Bash
# 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"
|
|
}
|