From c46160f719c8f05a0a3f16616342bd38daf02da5 Mon Sep 17 00:00:00 2001 From: talhademirel Date: Wed, 3 Jun 2026 18:10:45 +0300 Subject: [PATCH 1/3] fix: detect bash/bats test suites in test-runner check centaur_has_test_runner now recognizes test.sh/run-tests.sh and *.bats suites, so bash-only projects (Centaur itself included) no longer report a missing test runner. centaur-health routes through the shared helper instead of duplicating the detection, removing the divergence between the two. Found via self-dogfood. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/centaur-health.sh | 8 +------- scripts/lib/common.sh | 4 ++++ tests/bats/check.bats | 11 +++++++++++ tests/bats/health.bats | 8 ++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/scripts/centaur-health.sh b/scripts/centaur-health.sh index adbab81..044fc4a 100755 --- a/scripts/centaur-health.sh +++ b/scripts/centaur-health.sh @@ -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 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/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..e7b34f9 100644 --- a/tests/bats/health.bats +++ b/tests/bats/health.bats @@ -30,6 +30,14 @@ load helpers/setup.bash centaur_assert_contains "$out" "Restore required sections" } +@test "centaur-health detects bats suite as verification command" { + repo="$(centaur_mktemp_repo)" + 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, sea integrations" { repo="$(centaur_mktemp_repo)" mkdir -p "$repo/.claude/knowledge/charter" "$repo/.claude/hooks" "$repo/.sea" From 378094fa49bf0e250fb526bdec00009964cd4ac9 Mon Sep 17 00:00:00 2001 From: talhademirel Date: Wed, 3 Jun 2026 18:20:27 +0300 Subject: [PATCH 2/3] fix: probe engine state at .se/ and correct engine name to software-engineer The engine project is software-engineer (singular, github.com/demwick/ software-engineer), not the plural software-engineer-agents, and its runtime state directory moved from .sea/ to .se/. Update the centaur-init and centaur-health probes, the integration label / signal / metric field (sea -> se), the skill docs, and the tests in lockstep. The validate guardrail now asserts standalone positioning instead of the retired single-product language. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/centaur-health.sh | 8 ++++---- scripts/centaur-init.sh | 10 +++++----- scripts/validate-plugin.sh | 8 ++++---- skills/centaur-health/SKILL.md | 2 +- skills/centaur-init/SKILL.md | 2 +- templates/runtime-readme.md | 2 +- tests/bats/health.bats | 6 +++--- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/scripts/centaur-health.sh b/scripts/centaur-health.sh index 044fc4a..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=() @@ -80,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") @@ -114,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/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/health.bats b/tests/bats/health.bats index e7b34f9..5860e96 100644 --- a/tests/bats/health.bats +++ b/tests/bats/health.bats @@ -38,14 +38,14 @@ load helpers/setup.bash centaur_assert_contains "$out" "verification_command: detected" } -@test "centaur-health detects charter, guardrails, sea integrations" { +@test "centaur-health detects charter, guardrails, se integrations" { repo="$(centaur_mktemp_repo)" - mkdir -p "$repo/.claude/knowledge/charter" "$repo/.claude/hooks" "$repo/.sea" + 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" } From 8b7a4f441e96e557ad4fc4e840aac6e5a900ae85 Mon Sep 17 00:00:00 2001 From: talhademirel Date: Wed, 3 Jun 2026 18:20:33 +0300 Subject: [PATCH 3/3] docs: align ecosystem composition to Detect & Defer model Replace the single-product framing with the three-layer model: software-engineer (engine), claude-charter (constitution), centaur-layer (brake). Document Detect & Defer (each layer standalone, defers to a detected peer, never requires it), and clarify the boundaries Centaur owns: diff-risk scoring and the human-comprehension check fire at accept/commit time (the engine warns forward at planning time); Centaur's guardrail is the pre-commit surface (charter's is PreToolUse); and centaur-check questions the human, not the machine. Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 6 ++++-- README.md | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 10 deletions(-) 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. ---