* chore(ci): dev metrics + composition mass ratchet gate
Adds a three-tier development-metrics tool and a guardrail that stops the
ironclaw_reborn_composition crate from accreting more of the codebase.
scripts/dev_metrics.py — three tiers from git + GitHub + working tree:
- Tier 1 flow/speed: PR lead time, size distribution, merge cadence
- Tier 2 quality/stability: change-failure proxy, rework, test share
- Tier 3 codebase health: composition mass, v1 src burndown, file sprawl,
abstraction density, boundary-test coverage
Composition mass ratchet — the dependency-boundary tests police edges
*between* crates but are blind to mass piling up *inside* one crate.
ironclaw_reborn_composition is charter-bound to assembly-only wiring yet is
now ~26.7% of all production crate code. This gate is that missing guard:
- scripts/ci/composition-budget.toml — committed ceiling (enforce +
tolerance), modeled on the existing coverage-floor ratchet
- scripts/ci/check-composition-budget.sh — pure-bash gate; one-directional
(fails only on growth past the ceiling), emits a down-ratchet nudge as
carve-outs free up slack
- scripts/ci/test-check-composition-budget.sh — 22 assertions / 10 fixture
cases incl. a guard that the real tree passes the committed budget
Wiring:
- CI: new composition-budget job in code_style.yml (runs the gate + self-
tests it, registered in the aggregating code-style gate)
- Local: pre-commit-safety.sh runs the gate when composition or the gate
itself is staged; dev-setup.sh install message updated
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ci): address review — production-only metric, script hardening, dev-metrics tests
Review feedback on #6167 (gemini, ironloopai, coderabbit):
Blocking — gate counted test-only code despite its documented "tests
excluded" contract. Exclude test-only FILES (tests.rs/test_*.rs/*_tests.rs
and /tests/ dirs) from both numerator and denominator; rebaseline the
ceiling 2670 -> 2398 bp (26.70% -> 23.98%). Inline #[cfg(test)] modules
remain a documented, symmetric residual (a line-counter can't parse them).
Added a regression case proving test files are excluded.
check-composition-budget.sh: toml_get no longer aborts under set -e +
pipefail when a key is missing (|| true) so schema validation is reached;
added a missing-key regression case.
test-check-composition-budget.sh: set -euo pipefail (repo invariant);
SIGPIPE-safe capture + fixture generation; pure-bash asserts (no pipes).
dev_metrics.py: bound `gh` with a 30s timeout and treat non-JSON output as
unavailable; fix the trait-impl density regex to count `impl<T> ... for`
generics; harden find/grep/wc probes with pipefail + rc checks (no more
false-zero metrics); UTF-8 file writes; surface the gate-aligned production
share as the ratchet metric and relabel the byte-based trend as a distinct,
coarser measurement; extract a pure classify_commit helper.
New scripts/test_dev_metrics.py — caller-level unit tests for
classification, percentiles, change-failure bucketing, rendering, and the
test-file/impl regexes; wired into the composition-budget CI job.
pre-commit hook: trigger on any staged crates/**.rs change (the metric is a
ratio) and document the working-tree/CI-authoritative limitation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ci): harden PR classifier against transient GitHub API flakes
The classify job (#6167 CI) failed with `invalid character '<' looking
for beginning of value`: a transient API error returned an HTML page,
`gh --jq` aborted, and under `set -e` the whole labels-only job failed
and blocked the PR.
pr-labeler.sh now:
- routes every gh call through a `gh_retry` wrapper (retry + linear
backoff), and
- treats each classifier as best-effort — a step that still can't fetch
after retries only emits a `::warning::` and the script exits 0, so
labeling never gates a merge.
Two bash traps fixed along the way, both caught by the new test:
- a bare `if cmd; then …; fi` resets `$?` to 0 after `fi`, so gh_retry's
give-up looked like success — capture rc in the `else`;
- `set -e` is suppressed inside a function on the left of `||`, so the
classifiers check their own fetches explicitly instead of relying on
errexit.
Regression test: .github/scripts/test-pr-labeler.sh (retry/backoff,
give-up, and end-to-end non-fatal + happy-path via a fake `gh`), wired
into the code_style "Static-check self-tests" step and the has_code
path filter so it runs when the labeler or its test changes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(ci): add dispatch (Arc<dyn>) ratchet + dev-metrics dispatch signals
Companion to the mass ratchet for the "reduce traits & dispatch" goal
(#6168 / runtime-decomposition plan #4471).
check-composition-budget.sh now enforces TWO metrics: composition's share of
production crate code (existing) AND its Arc<dyn> dispatch count. The dispatch
count is scoped to composition production files EXCLUDING src/slack and
src/extension_host — those are owned by the separate channel/extension
refactor, so this gate must not govern or trip on their work. One-directional
like the mass ratchet: only trips on growth; nudges when slack accrues.
composition-budget.toml: arc_dyn_ceiling = 1093 (current governed count),
tolerance 15.
test harness: +6 dispatch cases (within / breach / dry-run / slack+extension
exclusion / missing-key schema error); budget() helper carries the dispatch
keys; count_arc_dyn tolerates no-match under set -e + pipefail. 36 cases pass.
dev_metrics.py: Tier-3 reports governed Arc<dyn> count and distinct dyn-trait
count (the dispatch-breadth trend), matching the ratchet scope.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(agents): wire codebase knowledge graph + OpenWiki for agent code discovery
Give every agent (fresh session, teammate, cloud, CI) first-class code discovery
so they stop rebuilding the map by grepping 1.27M lines. Two complementary layers,
both auto-maintained.
Graph (structural, precise -- codebase-memory MCP):
- .mcp.json: declare the codebase-memory-mcp server at project scope so any Claude
Code session in this repo auto-connects (it was only in personal global config,
so only one machine had access)
- scripts/dev-setup.sh: install the single static binary (no deps, no keys, local)
- .gitignore: treat .codebase-memory/ as a build artifact (it was neither tracked
nor ignored -- one `git add .` from a 234MB accidental commit)
- scripts/codebase-graph.sh: freshness helper (indexed commit vs HEAD)
OpenWiki (narrative, prose -- auto-generated, read-only):
- .github/workflows/openwiki-update.yml: weekly + manual regen of openwiki/,
Anthropic provider (ANTHROPIC_API_KEY), auto-merged PR opened via the
GH_RELEASES_MANAGER app so required checks trigger and it lands on main hands-off
Docs:
- CLAUDE.md / AGENTS.md: "Code Discovery" section -- query the graph first (with
recipes: search_graph / trace_path calls|data_flow|cross_service), read openwiki/
for narrative orientation
Behavior: no product code touched. dev-setup/CI gain an install step; agents gain
tools they call, not code that runs in the app.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(agents): steer new work to the Reborn stack, not the v1 src/ monolith
Add a "Reborn-first" directive to AGENTS.md + CLAUDE.md: new features go in
crates/ (product_workflow -> composition -> webui_v2 -> runtime/serve -> frontend,
entry reborn_cli), not src/main.rs. src/ is v1 in retirement -- maintain existing
behavior, don't add new features. Fills the gap where the docs' "Where to Work"
pointed almost entirely at src/, steering agents into v1 by default.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci(openwiki): open a PR for human review, not auto-merge (SOC 2)
Auto-merging the generated wiki to main bypasses human change review, which
SOC 2 change management (CC8.1 / separation of duties) does not allow. Drop the
enable-auto-merge step; the bot only authors the PR and a human reviews + merges.
Keep the GitHub App token so required checks still trigger on the bot PR (a
GITHUB_TOKEN-opened PR triggers no workflows, leaving the reviewer no checks).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: add pre-push git hook with delta lint mode
Add pre-push hook and CI quality gate scripts:
- .githooks/pre-push: runs quality gate before push
- scripts/ci/quality_gate.sh: baseline fmt + clippy correctness + tests
- scripts/ci/delta_lint.sh: clippy warnings filtered to changed lines only
- Updated dev-setup.sh to install pre-push hook
Supports environment-gated modes:
- IRONCLAW_STRICT_LINT=1: deny all clippy warnings
- IRONCLAW_STRICT_DELTA_LINT=1: deny warnings only on changed lines
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use git rev-parse for SCRIPT_DIR, add python3 check
- Fix SCRIPT_DIR resolution in pre-push hook to work correctly
with symlinks by using git rev-parse --show-toplevel
- Add python3 availability check in delta_lint.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: delta lint stderr handling, --locked flag, path normalization
- Stop suppressing clippy stderr; capture it and show compilation
errors if clippy produces no JSON output
- Add --locked flag to clippy for lockfile consistency
- Use repo root (via git rev-parse) for path normalization instead
of os.getcwd() which may differ from repo root
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: dynamically detect upstream base branch in delta_lint.sh
Instead of hard-coding `origin/main`, derive the base ref by checking
`refs/remotes/origin/HEAD`, then falling back to `origin/main` and
`origin/master`. If none can be resolved, skip delta lint gracefully
with a warning and exit 0.
Addresses PR #833 review feedback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: re-trigger CI after adding skip-regression-check label
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR #833 review feedback for delta lint
- Pass remote name ($1) from pre-push hook to delta_lint.sh
- Accept optional remote name arg, fall back to dynamic detection
- Treat error-level diagnostics as always blocking
- Check span overlap [line_start, line_end] vs changed ranges
- Handle +++ /dev/null (file deletions) in parse_diff
- Catch git merge-base failure with graceful skip
- Add CLIPPY_STDERR to EXIT trap cleanup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: drop -D warnings from delta lint, scope pre-push tests to --lib
1. Remove `-D warnings` from the clippy invocation in delta_lint.sh.
With -D warnings, all warnings are promoted to error level in JSON
output, which bypasses the delta filter entirely (errors are always
blocking). The Python filter already handles the blocking decision
for warnings based on changed-line overlap.
2. Scope pre-push tests to `cargo test --lib` (unit tests only) instead
of the full test suite. Full integration tests can take minutes and
will train developers to use --no-verify. The full suite runs in CI.
Skip tests entirely with IRONCLAW_PREPUSH_TEST=0.
Addresses zmanian's review feedback on PR #833.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: add reviewer-feedback guardrails (CLAUDE.md, pre-commit hook, skill)
Analysis of ~50 PRs from the past week identified 10 recurring themes
in Copilot and Gemini code review comments. This change addresses them
at development time through three layers:
1. CLAUDE.md additions (7 new rules):
- Transaction safety for multi-step DB operations
- UTF-8 string safety (no byte-index slicing)
- Case-insensitive comparisons for paths/media types
- Decorator/wrapper trait method delegation
- Sensitive data redaction in logs/SSE
- tempfile crate for test temporary files
- Trust boundaries for worker container data
2. Pre-commit hook (scripts/pre-commit-safety.sh):
Mechanical checks for unsafe byte slicing, case-sensitive
extension comparisons, hardcoded /tmp paths, unredacted
tool parameter logging, and non-transactional DB operations.
Installed via dev-setup.sh alongside existing commit-msg hook.
3. Review checklist skill (skills/review-checklist/SKILL.md):
Activates on "review"/"merge" keywords. Covers the judgment-based
items that can't be linted: transaction safety, SSRF validation,
approval checks, decorator delegation, test quality, and doc accuracy.
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback on pre-commit-safety.sh
- Cache diff output in variable to avoid ~10 redundant git diff calls (Gemini)
- Add early exit when no .rs files are changed (Gemini)
- Fix header comment: list all 5 checks, not just 4 (Copilot)
- Fix check 2 comment: only mentions file extensions, not media types (Copilot)
- Add resolve_base_ref() with fallback candidates instead of hardcoded
origin/main for standalone mode (Copilot)
- TX check: use -W (function context) to reduce false positives, honor
// safety: suppression, print triggering lines (Copilot)
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* ci: enforce regression tests for fix commits
Add a commit-msg hook and CI workflow that require test changes
alongside bug fix commits, ensuring every fix includes a regression
test that would have caught the bug.
- scripts/commit-msg-regression.sh: local git hook (blocks fix commits
without test changes; exempts static/docs-only; bypass via
[skip-regression-check] marker)
- .github/workflows/regression-test-check.yml: CI mirror on PRs
(checks title + commit messages; skip via label)
- scripts/dev-setup.sh: install hook in step 6
- .github/scripts/create-labels.sh: add skip-regression-check label
- CLAUDE.md: document regression test policy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback on regression test enforcement
- Use here-strings instead of echo|grep to avoid misinterpreting
special characters in variables
- Use git diff -W (whole-function context) to detect edits inside
existing test functions, not just new #[test] attributes
- Honor [skip-regression-check] in commit messages in CI (not just
the PR label)
- Use git rev-parse --git-path hooks for worktree-safe hook install
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Update .github/workflows/regression-test-check.yml
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* refactor: split large files and consolidate test stubs for contributor velocity
- Extract 7 Database sub-traits (ConversationStore, JobStore, SandboxStore,
RoutineStore, ToolFailureStore, SettingsStore, WorkspaceStore) with Database
as a supertrait combining them all
- Split libsql_backend.rs (2769 lines) into src/db/libsql/ directory with
one file per sub-trait implementation
- Split config.rs (1753 lines) into src/config/ directory with 16 domain files
- Consolidate 3 duplicate test LLM stubs into shared StubLlm in src/testing.rs
- Split server.rs handlers into src/channels/web/handlers/ directory
- Extract main.rs init phases into AppBuilder (src/app.rs)
- Add developer setup script (scripts/dev-setup.sh)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move heartbeat test from examples/ to tests/
Convert standalone example binary into a proper #[ignore] integration
test, matching the convention of the other integration tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* style: fix rustfmt formatting for CI
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review comments from Copilot
- tunnel.rs: replace .ok().flatten() with ? to propagate env var errors
- secrets.rs: remove misleading "process-wide cache" comment
- database.rs: use uppercase "DATABASE_URL" in error key
- testing.rs: gate harness tests with #[cfg(feature = "libsql")]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Illia Polosukhin <ilblacdragon@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>