diff --git a/CLAUDE.md b/CLAUDE.md index 9c0083e..154dcb4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,9 @@ This repository contains the Centaur Layer plugin (ships as both a Codex plugin ## Product Direction -Centaur Layer is the single installable product. It may borrow ideas from `claude-charter` and `software-engineer-agents`, but users should not need to install those projects separately. +Centaur Layer is one of three independent, composable layers: `software-engineer` (the engine — how work gets done, runtime state in `.se/`), `claude-charter` (the constitution — rules and trust boundaries) and Centaur Layer (the brake — did the human understand the diff). Each works fully standalone; none requires the others. + +The composition model is **Detect & Defer**: when Centaur detects another layer it defers that concern to it instead of duplicating it, but it never requires the other to be installed. Centaur's role in the ecosystem is to question the human, not the machine. Keep the product centered on one promise: AI can accelerate coding without weakening the developer's reasoning, review discipline, or ownership of risky decisions. @@ -13,7 +15,7 @@ Keep the product centered on one promise: AI can accelerate coding without weake - Keep the MVP small: contract, check, coach, health. - Do not add remote telemetry. - Do not write deliberate defect drills into real project files. -- Treat `.claude/` and `.sea/` as optional integrations. +- Treat `.claude/` and `.se/` as optional integrations. - Prefer clear skill instructions over custom runtime code until the workflow proves it needs scripts. - Every new skill should say when it stops, when it asks, and what evidence it reports. diff --git a/README.md b/README.md index 2a0c4e0..d6af7de 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ bash /path/to/centaur-layer/scripts/centaur-init.sh --with-hooks /path/to/target - `.centaur/metrics.jsonl` — local-only usage log - `.gitignore` entries for runtime files (`metrics.jsonl`, `session.json`) -It also probes for optional integrations (`.claude/knowledge/charter/`, `.sea/`) and reports whether they were detected. Neither is required. +It also probes for optional integrations (`.claude/knowledge/charter/`, `.se/`) and reports whether they were detected. Neither is required. Commit the initialization separately from product changes: @@ -323,16 +323,39 @@ The script never downgrades risk based on optimism — it only ever escalates wh --- -## Relationship To Other Projects +## Ecosystem: Engine, Constitution, Brake -Centaur Layer is the single product you install. It borrows proven ideas from two companion projects without requiring you to install them separately: +Centaur Layer is one of three independent, composable layers. Each works fully standalone; none requires installing the others. -| Source | What Centaur uses | -|---|---| -| `claude-charter` | layered policy, trust boundaries, guardrails, self-audit | -| `software-engineer-agents` | scoped execution, risk gates, verification discipline, atomic work | +| Layer | Project | Role | Answers | +|---|---|---|---| +| Engine | [`software-engineer`](https://github.com/demwick/software-engineer) (`.se/`) | how work gets done | "execute the work, scoped and verified" | +| Constitution | `claude-charter` (`.claude/`) | what rules & boundaries hold | "is the AI allowed to do this?" | +| Brake | `centaur-layer` (`.centaur/`) | did the human understand | "do *you* understand this diff before accepting it?" | + +### Detect & Defer + +Each layer is independent and self-sufficient. When one layer detects another, it **defers** that responsibility to it and does not duplicate it — but it never requires the other to be installed. Centaur runs fully on its own; if the engine or charter is present, Centaur reads and respects their state instead of re-implementing their concerns. + +Centaur's job in this ecosystem is to **question the human, not the machine.** + +### Two moments of risk (no overlap) + +- The **engine** warns *forward*, at planning time — before a change is made ("this change looks risky, here's what to watch"). +- **Centaur** brakes *at acceptance* — scoring the diff and checking human comprehension at the moment of accept/commit. + +Different moments, no collision: the engine warns while planning, Centaur brakes while accepting. Diff-risk scoring and the comprehension check at accept/commit time are Centaur's responsibility. + +### Two guardrail surfaces (no duplication) + +- **Charter** guards the **PreToolUse** surface — before the AI invokes a tool. It watches the *machine's action*. +- **Centaur** guards the **pre-commit** surface — when a human runs `git commit` on a high-risk diff. It watches the *human's commit*. + +If charter is installed, Centaur keeps its own pre-commit hook: a different surface, not a repeat. + +### Verification: machine vs human -If a project already has `.claude/` charter files or `.sea/` state, Centaur reads and respects them — but they are optional integrations, not dependencies. +`centaur-check` questions the **human** ("did you understand this?"), never the machine. Charter defines `/verify` and the engine's verifier runs it autonomously — those validate the *code*. Centaur stays separate in every case because its target is different: the human, not the code. --- diff --git a/scripts/centaur-health.sh b/scripts/centaur-health.sh index adbab81..4b8e6a8 100755 --- a/scripts/centaur-health.sh +++ b/scripts/centaur-health.sh @@ -19,7 +19,7 @@ has_verification_command=0 has_guardrails=0 has_git=0 git_clean=0 -has_sea=0 +has_se=0 missing=() signals=() @@ -65,13 +65,7 @@ if [ -d ".claude/knowledge/context" ] || [ -d "docs" ] || [ -f "README.md" ]; th has_context=1 fi -if [ -f "package.json" ] && grep -q '"test"' package.json 2>/dev/null; then - has_verification_command=1 -elif [ -f "pyproject.toml" ] || [ -f "pytest.ini" ]; then - has_verification_command=1 -elif [ -f "go.mod" ] || [ -f "Cargo.toml" ]; then - has_verification_command=1 -elif [ -f "Makefile" ] && grep -qE '^test:' Makefile 2>/dev/null; then +if centaur_has_test_runner "."; then has_verification_command=1 fi @@ -86,8 +80,8 @@ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then fi fi -if [ -d ".sea" ]; then - has_sea=1 +if [ -d ".se" ]; then + has_se=1 fi [ "$has_contract" -eq 1 ] && signals+=("contract: present") || missing+=("Run centaur-init to create .centaur/contract.md") @@ -120,7 +114,7 @@ else fi [ "$has_git" -eq 1 ] && signals+=("git: repository") || missing+=("Initialize git before AI-assisted changes") [ "$git_clean" -eq 1 ] && signals+=("git_clean: yes") || signals+=("git_clean: no") -[ "$has_sea" -eq 1 ] && signals+=("sea: detected") || signals+=("sea: absent") +[ "$has_se" -eq 1 ] && signals+=("se: detected") || signals+=("se: absent") score=$((has_contract + valid_contract + has_policy_file + has_centaur_policy + has_context + has_verification_command + has_guardrails + has_git)) status="RISKY" diff --git a/scripts/centaur-init.sh b/scripts/centaur-init.sh index 475c37e..a820337 100755 --- a/scripts/centaur-init.sh +++ b/scripts/centaur-init.sh @@ -59,9 +59,9 @@ if [ -d "$TARGET_DIR/.claude/knowledge/charter" ] || [ -d "$TARGET_DIR/.claude" charter="detected" fi -sea="absent" -if [ -d "$TARGET_DIR/.sea" ]; then - sea="detected" +se="absent" +if [ -d "$TARGET_DIR/.se" ]; then + se="detected" fi printf 'CENTAUR INIT: complete\n' @@ -70,7 +70,7 @@ printf 'runtime_readme: %s (%s)\n' "$RUNTIME_README" "$([ "$created_readme" -eq printf 'policy: %s (%s)\n' "$POLICY" "$([ "$created_policy" -eq 1 ] && printf created || printf preserved)" printf 'gitignore: %s\n' "$([ "$updated_gitignore" -eq 1 ] && printf updated || printf unchanged)" printf 'integration.claude_charter: %s\n' "$charter" -printf 'integration.sea: %s\n' "$sea" +printf 'integration.se: %s\n' "$se" hooks_state="absent" if [ "$with_hooks" -eq 1 ]; then if bash "$PLUGIN_ROOT/scripts/centaur-install-hooks.sh" "$TARGET_DIR" >/dev/null 2>&1; then @@ -87,7 +87,7 @@ centaur_emit_metric "$TARGET_DIR" "init" \ "contract_created=$created_contract" \ "policy_created=$created_policy" \ "charter=$charter" \ - "sea=$sea" \ + "se=$se" \ "hooks=$hooks_state" exit 0 diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index d4b2558..96f08dd 100644 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -89,6 +89,10 @@ centaur_has_test_runner() { return 0 elif [ -f "$dir/Makefile" ] && grep -qE '^test:' "$dir/Makefile" 2>/dev/null; then return 0 + elif [ -f "$dir/test.sh" ] || [ -f "$dir/run-tests.sh" ]; then + return 0 + elif find "$dir" -maxdepth 3 -name '*.bats' -type f -print -quit 2>/dev/null | grep -q .; then + return 0 fi return 1 } diff --git a/scripts/validate-plugin.sh b/scripts/validate-plugin.sh index 41f25c3..acd988e 100755 --- a/scripts/validate-plugin.sh +++ b/scripts/validate-plugin.sh @@ -174,8 +174,8 @@ grep -qF "## Required Behavior" templates/claude.md \ || fail "CLAUDE.md template missing Required Behavior" ok "CLAUDE.md template has required sections" -grep -qE "Centaur Layer is the single product (users|you) install" README.md \ - || fail "README must state single-product positioning" +grep -qF "none requires installing the others" README.md \ + || fail "README must state standalone positioning (Detect & Defer independence)" grep -qiE "opt-in synthetic|synthetic drills only|synthetic-only|defect drills should be opt-in" README.md \ || fail "README must keep defect drills opt-in" ok "README product guardrails present" @@ -287,7 +287,7 @@ invalid_out="$(bash scripts/centaur-health.sh "$invalid_contract")" assert_contains "centaur-health detects invalid contract" "$invalid_out" "Restore required sections" integrated="$(mktemp_repo)" -mkdir -p "$integrated/.claude/knowledge/charter" "$integrated/.claude/hooks" "$integrated/.sea" +mkdir -p "$integrated/.claude/knowledge/charter" "$integrated/.claude/hooks" "$integrated/.se" printf '# Policy\n' > "$integrated/.claude/knowledge/charter/principles.md" printf '{"hooks":{}}\n' > "$integrated/.claude/hooks/hooks.json" bash scripts/centaur-init.sh "$integrated" >/dev/null @@ -297,7 +297,7 @@ assert_contains "centaur-init detects real charter integration" "$integration_in assert_contains "centaur-health detects policy file" "$integration_out" "policy_file: present" assert_contains "centaur-health detects charter policy" "$integration_out" "charter_policy: detected" assert_contains "centaur-health detects guardrails" "$integration_out" "guardrails: detected" -assert_contains "centaur-health detects sea" "$integration_out" "sea: detected" +assert_contains "centaur-health detects se" "$integration_out" "se: detected" drill_out="$(bash scripts/centaur-drill.sh boundary)" assert_contains "centaur-drill is synthetic" "$drill_out" "mode: synthetic-only" diff --git a/skills/centaur-health/SKILL.md b/skills/centaur-health/SKILL.md index 25f3e3c..a939c9d 100644 --- a/skills/centaur-health/SKILL.md +++ b/skills/centaur-health/SKILL.md @@ -24,7 +24,7 @@ Then use the script output as the evidence base. The script inspects: 5. Review or verification command signal 6. Dangerous-operation guardrails 7. Git cleanliness -8. Optional SEA state: `.sea/` +8. Optional software-engineer state: `.se/` 9. Centaur policy language in `CLAUDE.md` ## Scoring diff --git a/skills/centaur-init/SKILL.md b/skills/centaur-init/SKILL.md index afe9c6b..fd1cec3 100644 --- a/skills/centaur-init/SKILL.md +++ b/skills/centaur-init/SKILL.md @@ -22,7 +22,7 @@ Initialize Centaur Layer for the current repository. ## Rules - Do not overwrite an existing `.centaur/contract.md` without explicit user approval. -- Do not require `claude-charter` or `software-engineer-agents`; treat them as optional integrations. +- Do not require `claude-charter` or `software-engineer`; treat them as optional integrations. - Keep initialization small. No scaffolding, no dependency installs, no git commits. - If the repository is dirty, preserve all existing work. - Commit `.centaur/contract.md`, `.centaur/README.md`, and `CLAUDE.md`; do not commit runtime metrics. diff --git a/templates/runtime-readme.md b/templates/runtime-readme.md index f013d68..bbca89e 100644 --- a/templates/runtime-readme.md +++ b/templates/runtime-readme.md @@ -21,4 +21,4 @@ Each line of `metrics.jsonl` is a single JSON object: Event types: `init`, `check`, `health`, `drill`. Summarize with `centaur-stats`. -Centaur Layer treats `.claude/` and `.sea/` as optional integrations. This directory is the only required Centaur state. +Centaur Layer treats `.claude/` and `.se/` as optional integrations. This directory is the only required Centaur state. diff --git a/tests/bats/check.bats b/tests/bats/check.bats index 3a9e5f5..e5dbf76 100644 --- a/tests/bats/check.bats +++ b/tests/bats/check.bats @@ -64,6 +64,17 @@ load helpers/setup.bash centaur_assert_contains "$output" "dependency change without test command evidence" } +@test "centaur-check detects bats suite as test runner" { + repo="$(centaur_mktemp_repo)" + mkdir -p "$repo/tests" + printf '@test "ok" { true; }\n' > "$repo/tests/smoke.bats" + printf '# Demo\n' > "$repo/README.md" + centaur_seed_commit "$repo" . + printf '\nMore docs.\n' >> "$repo/README.md" + out="$(centaur_run_script centaur-check.sh "$repo")" + centaur_assert_contains "$out" "test_runner: detected" +} + @test "centaur-check exit code respects CENTAUR_FAIL_ON=none" { repo="$(centaur_mktemp_repo)" printf '{"scripts":{"test":"echo ok"}}\n' > "$repo/package.json" diff --git a/tests/bats/health.bats b/tests/bats/health.bats index 8587c18..5860e96 100644 --- a/tests/bats/health.bats +++ b/tests/bats/health.bats @@ -30,14 +30,22 @@ load helpers/setup.bash centaur_assert_contains "$out" "Restore required sections" } -@test "centaur-health detects charter, guardrails, sea integrations" { +@test "centaur-health detects bats suite as verification command" { repo="$(centaur_mktemp_repo)" - mkdir -p "$repo/.claude/knowledge/charter" "$repo/.claude/hooks" "$repo/.sea" + mkdir -p "$repo/tests" + printf '@test "ok" { true; }\n' > "$repo/tests/smoke.bats" + out="$(centaur_run_script centaur-health.sh "$repo")" + centaur_assert_contains "$out" "verification_command: detected" +} + +@test "centaur-health detects charter, guardrails, se integrations" { + repo="$(centaur_mktemp_repo)" + mkdir -p "$repo/.claude/knowledge/charter" "$repo/.claude/hooks" "$repo/.se" printf '# Policy\n' > "$repo/.claude/knowledge/charter/principles.md" printf '{"hooks":{}}\n' > "$repo/.claude/hooks/hooks.json" centaur_run_script centaur-init.sh "$repo" >/dev/null out="$(centaur_run_script centaur-health.sh "$repo")" centaur_assert_contains "$out" "charter_policy: detected" centaur_assert_contains "$out" "guardrails: detected" - centaur_assert_contains "$out" "sea: detected" + centaur_assert_contains "$out" "se: detected" }