diff --git a/.codearbiter/.provenance/release-targets.json b/.codearbiter/.provenance/release-targets.json index 8c369660..c08a874c 100644 --- a/.codearbiter/.provenance/release-targets.json +++ b/.codearbiter/.provenance/release-targets.json @@ -4,7 +4,7 @@ "entries": [ { "drift_trigger": true, - "hash": "a39e18a12f1a3aefaebd5ac80b5519711d6aba84", + "hash": "7933577b4e8985942aed6caf2f859c121c57f7d0", "path": "CHANGELOG.md" }, { @@ -64,7 +64,7 @@ }, { "drift_trigger": true, - "hash": "8c98f20f9e4de1cdec10d6f99922e520347a3ba9", + "hash": "f963449c0e51f52a78ee77afa03315471dae0be7", "path": "plugins/ca/.claude-plugin/plugin.json" }, { diff --git a/.github/scripts/test_release_trace.py b/.github/scripts/test_release_trace.py index d967ffe0..5cfa6da6 100644 --- a/.github/scripts/test_release_trace.py +++ b/.github/scripts/test_release_trace.py @@ -27,14 +27,14 @@ two-manifest ca-pi shape, the three behaviors most likely to drift in a migration. -Pre-change SHA (T-27b): `6a903e6` — the parent of `0664506` (the commit that -performed the mechanism split), i.e. the last commit at which -`.github/scripts/_releaselib.py` was still the ORIGINAL self-contained -module. Chosen over "origin/main's copy" because origin/main moves and a -SHA does not; pinning to the immediate pre-split commit is the tightest, -least-ambiguous "the module as it stood the moment before this migration -began" reference the spec's `git show :...` instruction -asks for. +Pre-change SHA (T-27b): `469c2fb8` — the parent of `8ee5fe11` (the squash +merge that performed the mechanism split), i.e. the last reachable main +commit at which `.github/scripts/_releaselib.py` was still the ORIGINAL +self-contained module. Its module blob is byte-identical to the abandoned +feature-branch parent originally used here. Chosen over "origin/main's copy" +because origin/main moves and a SHA does not; pinning to the reachable +pre-merge commit is the tightest fresh-clone-safe reference the spec's +`git show :...` instruction asks for. Honest limit (stated once here, per the spec's own wording, rather than repeated at every call site): `.github/scripts/_releaselib.py` at the @@ -151,9 +151,9 @@ REPO_ROOT = os.path.dirname(os.path.dirname(HERE)) FIXTURE_DIR = os.path.join(HERE, "fixtures", "release-trace") -# T-27b: the parent of 0664506 (the mechanism-split commit) — see module -# docstring for why this SHA rather than "origin/main's copy" was chosen. -PRE_CHANGE_SHA = "6a903e6" +# T-27b: the parent of 8ee5fe11 (the mechanism-split squash merge) — see +# the module docstring for why this reachable SHA was chosen. +PRE_CHANGE_SHA = "469c2fb8" CORE_RELEASELIB_PATH = os.path.join(REPO_ROOT, "core", "pysrc", "_releaselib.py") SHIM_RELEASELIB_PATH = os.path.join(REPO_ROOT, ".github", "scripts", "_releaselib.py") @@ -182,7 +182,7 @@ def setUpModule(): if not has_commit or not has_tags: missing = [] if not has_commit: - missing.append(f"commit {PRE_CHANGE_SHA} is unreachable (shallow history)") + missing.append(f"commit {PRE_CHANGE_SHA} is unreachable from this checkout") if not has_tags: missing.append("no tags are present") raise RuntimeError( @@ -429,6 +429,15 @@ def test_old_lane_loads_the_pinned_pre_change_module(self): "ca-sandbox": "ca-sandbox-v", "ca-pi": "ca-pi-v"}) self.assertTrue(callable(self.old_lane.last_tag_select)) + def test_pinned_pre_change_commit_is_reachable_from_head(self): + result = subprocess.run( + ["git", "merge-base", "--is-ancestor", PRE_CHANGE_SHA, "HEAD"], + cwd=REPO_ROOT, capture_output=True, text=True, timeout=30) + self.assertEqual( + result.returncode, 0, + f"{PRE_CHANGE_SHA} is not reachable from HEAD; a fresh full-history " + "checkout cannot load the pinned pre-change module") + def test_old_lane_loads_a_genuinely_historical_snapshot(self): # Proves this is really pinned to git history and not accidentally # reading the live (now-converted) shim: the pre-change source must diff --git a/CHANGELOG.md b/CHANGELOG.md index a39e18a1..7933577b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,18 @@ predate the plugin rewrite and are grouped by date. ## [Unreleased] +## [2.11.1] — 2026-08-03 + +### Fixed + +- Restored fresh-clone release-trace verification by pinning its historical + reference to the reachable pre-migration commit with the same release-library + bytes. +- Stabilized repeated statusline-render tests by isolating their unrelated + repository-dirty probes. +- Kept the landing page's guarantee cards aligned by resetting inherited + sibling-flow spacing at the card boundary. + ## [2.11.0] — 2026-07-31 ### Added diff --git a/README.md b/README.md index f565045b..8ea04715 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ project context. You decide. codeArbiter enforces. Claude Code plugin Codex plugin Pi Feature Forge preview -version 2.11.0 +version 2.11.1 commands skills agents diff --git a/plugins/ca/.claude-plugin/plugin.json b/plugins/ca/.claude-plugin/plugin.json index 8c98f20f..f963449c 100644 --- a/plugins/ca/.claude-plugin/plugin.json +++ b/plugins/ca/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "ca", "displayName": "codeArbiter", "description": "Orchestration layer for Claude Code. Routes every intent through gated skills and reviewer agents, drives spec-driven TDD, mechanically enforces the commit and audit-trail gates, decides via SMARTS, and keeps an append-only audit trail. Requires Python 3 on PATH. Dormant until you opt a repo in; run /ca:init to activate.", - "version": "2.11.0", + "version": "2.11.1", "author": { "name": "arbiterForge" }, "license": "AGPL-3.0-only", "homepage": "https://github.com/arbiterForge/codeArbiter", diff --git a/plugins/ca/hooks/tests/test_colorlib.py b/plugins/ca/hooks/tests/test_colorlib.py index 009590f0..8b0765c1 100644 --- a/plugins/ca/hooks/tests/test_colorlib.py +++ b/plugins/ca/hooks/tests/test_colorlib.py @@ -241,18 +241,19 @@ def test_failed_custom_theme_projects_effective_violet_and_legacy_bytes(self): with open(cases["oversized"], "wb") as handle: handle.write(b" " * (_colorlib.MAX_THEME_BYTES + 1)) - with mock.patch.dict(os.environ, {"CODEARBITER_THEME": "violet"}): - violet = sl.render(payload) - - for label, path in cases.items(): - with self.subTest(case=label), mock.patch.dict( - os.environ, {"CODEARBITER_THEME": "custom", - "CODEARBITER_THEME_FILE": path}): - rendered = sl.render(payload) - self.assertEqual(rendered, violet) - self.assertEqual(sl._colorlib.ACTIVE_THEME_NAME, "violet") - self.assertIn(sl.fg(90, 60, 150), rendered) - self.assertIn(sl.fg(170, 110, 240), rendered) + with mock.patch.object(sl, "git_dirty", return_value=True): + with mock.patch.dict(os.environ, {"CODEARBITER_THEME": "violet"}): + violet = sl.render(payload) + + for label, path in cases.items(): + with self.subTest(case=label), mock.patch.dict( + os.environ, {"CODEARBITER_THEME": "custom", + "CODEARBITER_THEME_FILE": path}): + rendered = sl.render(payload) + self.assertEqual(rendered, violet) + self.assertEqual(sl._colorlib.ACTIVE_THEME_NAME, "violet") + self.assertIn(sl.fg(90, 60, 150), rendered) + self.assertIn(sl.fg(170, 110, 240), rendered) def test_valid_partial_custom_keeps_custom_as_effective_theme(self): with tempfile.TemporaryDirectory() as tmp: @@ -322,6 +323,7 @@ def counted_read(path): reads += 1 return real_read(path) sl._colorlib._read_custom = counted_read +sl.git_dirty = lambda _root: True out = sl.render(payload) again = sl.render(payload) if reads != 2: diff --git a/plugins/ca/hooks/tests/test_statusline.py b/plugins/ca/hooks/tests/test_statusline.py index c9af1d73..61ce561b 100644 --- a/plugins/ca/hooks/tests/test_statusline.py +++ b/plugins/ca/hooks/tests/test_statusline.py @@ -825,8 +825,9 @@ def test_render_is_deterministic_for_same_input(self): "context_window_size": 200000}, "cost": {"total_cost_usd": 1.23}, }) - a = sl.render(payload) - b = sl.render(payload) + with mock.patch.object(sl, "git_dirty", return_value=True): + a = sl.render(payload) + b = sl.render(payload) self.assertEqual(a, b) def test_ledger_functions_reachable_via_statusline(self): diff --git a/site/src/styles/landing.css b/site/src/styles/landing.css index eac99172..60fa53ea 100644 --- a/site/src/styles/landing.css +++ b/site/src/styles/landing.css @@ -492,6 +492,7 @@ ca-splash-docs { .ca-feature { position: relative; + margin-block-start: 0; padding: var(--ca-space-6); } diff --git a/site/test/landing/landing-page.test.ts b/site/test/landing/landing-page.test.ts index a2242daf..49f40f0a 100644 --- a/site/test/landing/landing-page.test.ts +++ b/site/test/landing/landing-page.test.ts @@ -183,6 +183,12 @@ describe("first-class product splash", () => { expect(indexMdx).toContain("./guides/uninstalling/"); }); + it("keeps every guarantee card aligned despite Starlight's sibling flow spacing", () => { + expect(landingCss).toMatch( + /\.ca-feature\s*\{[^}]*margin-block-start:\s*0;/s, + ); + }); + it("uses page-relative internal links instead of a hardcoded deployment base", () => { expect(indexMdx).not.toMatch(/href="\/codeArbiter/); expect(indexMdx).toMatch(/href="\.\/getting-started\/install\//);