fix(infra): bundle skills/ in wheel, surface install failures, rename /bicameral:* → /bicameral-*#178
Merged
Merged
Conversation
The installer silently left .claude/skills/ empty because: - pyproject.toml did not force-include skills/, so Hatchling dropped the directory from the wheel (no __init__.py; the existing `artifacts` directive was insufficient on its own — verified empty pre-fix). - setup_wizard._install_skills returned 0 silently when source was missing. - run_setup suppressed the zero count via `if num_skills:`, hiding the failure from the "Done!" summary. Compounding bugs surfaced during audit: - _detect_runner kept a `python -m bicameral_mcp` fallback that produced a non-functional MCP config (no such package; entry is server:cli_main). - A stale `Note: using ... -m bicameral_mcp ...` advisory printed regardless of selected runner. Changes: - pyproject.toml: add [tool.hatch.build.targets.wheel.force-include] with `"skills" = "skills"`; add `build>=1.0.0` to test extras. - setup_wizard.py: add RunnerNotFoundError; drop broken module fallback from _detect_runner; surface RunnerNotFoundError at top of run_setup before any filesystem mutation; replace silent return in _install_skills with a loud WARNING; remove `if num_skills:` gate so the count is always reported; delete stale runner-note block. - tests/test_installer_packaging.py: real `python -m build` + zipfile inspection. Skill members enumerated dynamically from skills/ source so coverage stays correct as skills are added or removed. - tests/test_setup_wizard.py: behavioral coverage of warning, runner selection branches, and a regression guard against re-introducing a non-script `_detect_runner` return. Closes #177 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
7 tasks
Claude Code resolves slash commands by skill folder name. Folders are named bicameral-<verb> (hyphen), so the colon-form /bicameral:<verb> never resolved — it would only work for skills shipped via a Claude Code plugin namespace, which bicameral-mcp does not provide. Functional impact (what was broken before): - SessionEnd hook spawned `claude -p '/bicameral:capture-corrections'` which Claude could not invoke. The hook never ran the intended skill. - PostToolUse reminder text told the agent to "run /bicameral:sync" pointing at a non-resolvable command. - run_setup advertised /bicameral:* names that users would type and see "command not found". - README and DEV_CYCLE documented the broken form. - 11 server.py tool descriptions referenced colon-form aliases. Renames (functional): - setup_wizard.py: SessionEnd hook command, slash-command summary, inline comment. - .claude/settings.json: deployed SessionEnd hook command. - scripts/hooks/post_commit_sync_reminder.py: PostToolUse reminder text and module docstring. - server.py: 11 tool-description "Slash alias:" lines. - 6 skills/*/SKILL.md cross-references and self-references. - assets/git-for-specs-deck.html presentation chip. - README.md slash-commands table (5 rows + skills column). - docs/DEV_CYCLE.md example. Renames (test lockstep): - tests/test_post_commit_sync_hook.py assertion. - tests/test_session_end_hook_drift.py canonical command + docstring. - tests/e2e/_harness_setup.py + run_e2e_flows.py docstring refs. New regression guard: - tests/test_setup_wizard.py::test_session_end_command_uses_hyphen_slash_command fails if the colon form is ever re-introduced into _BICAMERAL_SESSION_END_COMMAND. CHANGELOG.md historical entries left intact (archival). .claude/skills/* dupes left intact (regenerated by `bicameral-mcp setup`). Refs #177
This was referenced May 4, 2026
Closed
Knapp-Kevin
added a commit
that referenced
this pull request
May 4, 2026
The v0 architecture reference previously lived only in a Notion page (mirrored locally at .failsafe/governance/v0 Architecture *.md, gitignored). It had drifted from dev HEAD across every load-bearing dimension and was not editable via PR. This doc: - Is derived from code; cites ledger/schema.py, server.py, events/team_adapter.py, contracts.py as source-of-truth pointers for every numeric claim - Delineates v0 core (6 nodes, 5 edges) from operational infrastructure (symbol/vocab_cache/ledger_sync/graph_proposal/ schema_meta) and v1 CodeGenome scaffolding (subject_identity/ subject_version, has_identity/has_version/identity_supersedes/ locates/depends_on) already present on dev - Documents the 23-tool MCP surface (lifecycle / telemetry / code-locator) and explicitly notes there is no internal/external split at the tool boundary - Documents the JSONL-in-git event substrate and explains why it delivers the Notion page's stated team-sync goal better than the graph-node design described there - Names the known compliance_checked emission gap (#178 open-issues survey) so it can be tracked separately - Lists the 6 v0 invariants with test-as-contract semantics and an honest fat callout - Includes a Reconciliation notes table at the end mapping each drifted Notion claim to current reality Closes #179 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
Knapp-Kevin
added a commit
that referenced
this pull request
May 5, 2026
Closes the e2e flake observed on PR #181 (run 25345393569): Flow 5 (PM history+ratify, MCP-layer) and Flow 3 (commit→sync, agentic) failed because a stale ~/.claude/projects/-tmp-desktop-clone/memory/MEMORY.md from a prior run let the agent answer Flow 5's prompt directly from disk instead of invoking bicameral.history. Flow 3's failure cascaded because its ledger snapshot relies on Flow 5's bicameral call to drain the post-commit JSONL queue via EventMaterializer.replay_new_events (documented at run_e2e_flows.py:342-349). Diagnosis evidence (from /qor-debug Phase 1 four-layer analysis): - PASS run #178 Flow 5.ndjson L5: 'File does not exist' on MEMORY.md → agent runs ToolSearch + bicameral_history. Flow 5 PASS. - FAIL run #181 Flow 5.ndjson L5: full MEMORY.md content shown → agent reads memory + git-log + answers from disk. Zero bicameral calls. - The team-server materializer hunk at events/materializer.py:88-106 is correctly gated (is_team_server_payload requires both source_type AND extraction keys; standard MCP payloads have neither). Dormant in the e2e flow. NOT the regressor. Fix: - New helper clean_claude_memory_for_repo(repo_path) in tests/e2e/_harness_setup.py that purges ~/.claude/projects/<key>/memory/ where <key> is the absolute repo path with / and \ replaced by -. - Wired into tests/e2e/run_e2e_flows.py::main as _clean_claude_memory(), called alongside the existing _clean_ledger / _reset_desktop_repo / _bootstrap_bicameral_dir cleanup chain. - Regression-locking unit tests in tests/test_e2e_harness_memory_purge.py cover: purges-when-present, no-op-when-absent, scoped-to-target-project (does NOT touch other repos' memory on the same runner). 3/3 tests pass; ruff + ruff format + mypy all clean. Refs PR #181, root-cause via /qor-debug session
This was referenced May 5, 2026
Knapp-Kevin
added a commit
to Knapp-Kevin/bicameral-mcp
that referenced
this pull request
May 21, 2026
Closes BicameralAI#187 BREAKING: removes IngestResponse.judgment_payload and IngestResponse.judgment_payloads. Replaces them with a single IngestResponse.brief: BriefEnvelope | None field that carries gap-judge findings inline. Caller no longer has to remember to render two separate sections; the dual-render contract was the most-skipped step in bicameral-ingest per the issue body. Phase 1 — schema + handler: - contracts.py: new BriefEnvelope class (mirrors PreflightResponse's brief surface; includes rubric: GapRubric | None for the migrated rubric reference). Removes judgment_payload + judgment_payloads from IngestResponse. - handlers/ingest.py: populates brief from judgment_payloads (auto-chain output remains the local var; the legacy public fields are gone). brief stays None when no gap-judge findings — silent-on-no-signal matches the PreflightResponse contract. - handlers/gap_judge.py: docstring text update to reference the new IngestResponse.brief.gaps location (was IngestResponse.judgment_payload). Phase 1 — cascade migration of existing tests: - tests/test_v0416_gap_judge.py: 5 assertions on response.judgment_payload migrated to response.brief / response.brief.rubric / response.created_decisions. Test names renamed to reflect the new field. Module docstring updated. - 4 new behavioral tests in tests/test_ingest_brief_unification.py: brief populated when gaps judged, rubric carried through, brief is None on no signal, judgment_payload[s] not in model_fields or model_dump output. Phase 2 — skill render contract: - skills/bicameral-ingest/SKILL.md Step 6: rewrites gap-judge rubric trigger to read response.brief.gaps[] and response.brief.rubric instead of response.judgment_payload. - skills/bicameral-judge-gaps/SKILL.md: trigger description + auto-chain reference updated to brief.gaps. Migration note added. Phase 3 — stale colon mop-up (orthogonal): - skills/bicameral-report-bug/SKILL.md: 4 line edits, /bicameral:report-bug → /bicameral-report-bug. The single residual occurrence of the legacy colon-namespace form across skills/, post-BicameralAI#178's sweep. Verification: - 16/16 tests pass (4 new + 12 v0416 regression incl. 3 migrated) - mypy . clean on changed files (contracts.py, handlers/ingest.py, handlers/gap_judge.py) - ruff check . + ruff format --check . clean - grep -rn "judgment_payload" --include="*.py" . — only intentional hits remain: contracts.py docstrings (migration markers), handlers/ingest.py (local var name for the gap-judge auto-chain loop), and server.py:1119 (standalone bicameral.judge_gaps tool's response shape — non-cascading collision, intentionally untouched per plan-187 §Phase 1). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
End-to-end fix for "skills are not invocable after install" — the cause was three layered bugs:
skills/despite theartifactsdirective (verified empty pre-fix). Claude Code skills require folder copies that the installer reads fromPath(__file__).parent / "skills", so the wheel must contain that path._install_skillsfailed silently when source was missing; the summary suppressed the zero count./bicameral:*slash commands were unresolvable. Claude Code resolves slash commands by skill folder name, and the folders use hyphens (bicameral-ingest, etc.). The colon form would only work for plugin-namespaced skills — and bicameral-mcp does not ship a Claude Code plugin. Renaming everywhere is the right fix; switching to a plugin distribution was considered and rejected as out-of-scope (multi-agent strategy, plugin maturity, separate engine layer)._detect_runnerhad a brokenpython -m bicameral_mcpfallback that produced non-functional MCP configs (no such package; entry isserver:cli_main).Note: using ... -m bicameral_mcp ...advisory printed regardless of selected runner.Linked issues
Closes #177
Plan / Audit / Seal
plan-installer-skills-remediation.md(originally authored againstmain; adapted ondevafter discovering significant divergence and verifying via wheel build thatdevhad the same packaging bug)_BICAMERAL_SESSION_END_COMMANDends with'/bicameral-capture-corrections --auto-ingest')Test plan
pytest tests/test_setup_wizard.py -v— 6 passed (incl. newtest_session_end_command_uses_hyphen_slash_commandregression guard)pytest tests/test_installer_packaging.py -v— 2 passed (realpython -m build+ zipfile inspection; skill members enumerated dynamically from source)pytest tests/test_post_commit_sync_hook.py -v— 11 passed (hook reminder text now references/bicameral-sync)pytest tests/test_session_end_hook_drift.py -v— 5 passed (canonical SessionEnd command + drift checks)python -c "import setup_wizard, server"— clean imports,RunnerNotFoundErrorexportedpip install --force-reinstallit into a fresh venv, runbicameral-mcp setup <some-target-repo>. Expect: all 12 skill folders populated under<repo>/.claude/skills/, summary advertises/bicameral-<name>(hyphen) commands, no-m bicameral_mcptext anywhere.bicameral-mcpscript on PATH, run setup. Expect:RunnerNotFoundErrorwith install hint, not silent broken config.Scope summary
Functional code (must change for the fix to work end-to-end):
pyproject.toml—[tool.hatch.build.targets.wheel.force-include]with"skills" = "skills"setup_wizard.py— RunnerNotFoundError; clean_detect_runner; WARNING in_install_skills; dropif num_skills:gate; surface RunnerNotFoundError inrun_setup; delete stale runner-note; rename advertised slash commands; rename hook command string.claude/settings.json— SessionEnd hook commandscripts/hooks/post_commit_sync_reminder.py— PostToolUse reminder textserver.py— 11 tool-description "Slash alias:" linesskills/*/SKILL.md— 6 skill files with cross-references / self-references / hook examplesUser-facing docs:
README.md— slash-commands table (5 rows + skills column reference)docs/DEV_CYCLE.md— one exampleassets/git-for-specs-deck.html— presentation chipTests (lockstep + regression guard):
tests/test_post_commit_sync_hook.py,tests/test_session_end_hook_drift.py,tests/e2e/_harness_setup.py,tests/e2e/run_e2e_flows.py— references in test text/assertstests/test_setup_wizard.py,tests/test_installer_packaging.py— new (TDD)Intentionally NOT changed:
CHANGELOG.md— historical entries are archival.claude/skills/*— repo-local dupes regenerated bybicameral-mcp setup .pyproject.tomlartifacts = ["skills/**/*.md", ...]— left alongsideforce-include(removing would be tangential cleanup)Notes for reviewer
setup_wizard.pysize (~970 LOC) is over the Razor 250-LOC file limit; not addressed here. Suggest a follow-uprefactorissue./bicameral:ingestcould work natively) was considered and explicitly rejected — the multi-agent surface (Cursor + Codex don't have plugin systems) makes a Python-packaged engine + per-agent installer the right shape. The plugin route would be a several-week migration with no net win.