Skip to content

[luv-299] fix: enforce require-*-before-stop on GitHub Copilot CLI#299

Merged
NiveditJain merged 4 commits intomainfrom
luv-299
May 5, 2026
Merged

[luv-299] fix: enforce require-*-before-stop on GitHub Copilot CLI#299
NiveditJain merged 4 commits intomainfrom
luv-299

Conversation

@NiveditJain
Copy link
Copy Markdown
Member

@NiveditJain NiveditJain commented May 5, 2026

Summary

  • Empirically verified (Copilot CLI 1.0.41, via ~/.copilot/logs/process-*.log + session-state events.jsonl): exit-2 + stderr from a Stop hook on Copilot's agentStop is logged as [WARNING] Hook warning: … and surfaced to the user, but the agent stops cleanly without retrying — same observation-only failure mode as Cursor/OpenCode/Pi, except Copilot at least shows the message.
  • Per Copilot's documented agentStop semantics, the retry shape is {decision: "block", reason} JSON on stdout (exit 0); reason becomes the next-turn prompt. New cli === "copilot" branches in src/hooks/policy-evaluator.ts emit that shape on both the deny path (line ~267) and the instruct path (line ~457), mirroring the existing Gemini AfterAgent branches. Without these branches, all five require-*-before-stop builtins (commit / push / PR / no-conflicts / CI-green) were silently observation-only on Copilot.
  • Adds SubagentStop to COPILOT_HOOK_EVENT_TYPES so custom policies subscribing to that event are reachable from Copilot subagent boundaries (Copilot's subagentStop aliases to PascalCase via the same VS Code-compat alias that maps agentStopStop).
  • Both cli === "copilot" branches (deny + instruct) match Stop and SubagentStop so those custom subagent policies get the correct retry shape too. The 5 require-*-before-stop builtins still match Stop only by design — they are session-completion gates (commit/push/PR/conflicts/CI), not subagent-return gates — so the SubagentStop widening does not change builtin behavior.
  • Updates CLAUDE.md per-CLI capability matrix and the Copilot section in src/hooks/types.ts with the verified Stop block semantics.

Test plan

  • bun run test:run — 1524 passing (was 1517 before; +7 from new Copilot Stop deny, Copilot SubagentStop deny, Claude SubagentStop deny, SubagentStop config assertion, plus the three new instruct-path tests added by the latest commit: Claude SubagentStop instruct, Copilot Stop instruct, Copilot SubagentStop instruct)
  • bun run test:e2e — 292 passing (was 291; +1 from new Copilot Stop deny e2e regression)
  • bun run lint — clean (only pre-existing unrelated @next/next/no-img-element warning)
  • bunx tsc --noEmit — clean
  • Empirical verification against real Copilot CLI 1.0.41 in a sandbox: confirmed exit-2 path produces [WARNING] Hook warning: … with no retry; confirmed agentStop and Stop PascalCase aliasing works for user-scope hooks
  • CodeRabbit review: addressed (1) the deny-branch SubagentStop widening on the first review pass and (2) the parallel instruct-branch fix on the second pass — the deny path had a Copilot arm but the instruct path's eventType === "Stop" arm was missing both the SubagentStop widening AND the Copilot arm, so instruct verdicts on Copilot still fell through to exit-2

Open follow-ups (not blocking this PR)

  • .github/hooks/failproofai.json project-scope appears NOT to be loaded by Copilot CLI 1.0.41 in my sandbox tests — only user-scope ~/.copilot/hooks/ was dispatched. I tried multiple schema variants (PascalCase + matcher-wrap, flat camelCase) and filenames (failproofai.json, canonical hooks.json), with files committed to git. None fired. Worth filing a separate investigation issue.
  • The Cursor followup_message lift (policy-evaluator.ts:147 Stop+deny → {followup_message} instead of {permission:"deny"}) is still pending. Lower stakes; covered in the PR [luv-299] fix: enforce require-*-before-stop on GitHub Copilot CLI #299 review thread.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Copilot stop now surfaces mandatory-action block messages and supports SubagentStop parity.
    • Per-session download/export expanded (including OpenCode export format) and transcripts populate across activity dashboard harnesses.
  • Bug Fixes

    • Improved transcript path resolution, tool-name canonicalization, and session-log rendering stability.
    • Better install/launch error messaging and PATH shadowing diagnostics/path protection.
  • Documentation

    • Added Copilot Stop semantics.
  • Tests

    • Expanded integration and end-to-end coverage for Copilot/Stop/SubagentStop and OpenCode exports.

Empirically verified against Copilot CLI 1.0.41 (~/.copilot/logs/process-*.log
+ session-state events.jsonl): exit-2 + stderr from a Stop hook on Copilot's
agentStop is logged as `[WARNING] Hook warning: ...` and surfaced to the user,
but the agent stops cleanly without retrying — same observation-only failure
mode as Cursor/OpenCode/Pi, except Copilot at least shows the message.

The documented retry shape (cli-hooks-reference) is `{decision: "block",
reason}` JSON on stdout (exit 0); reason becomes the next-turn prompt. New
`cli === "copilot"` branch in policy-evaluator.ts:267 emits that shape on Stop
deny, mirroring the existing Gemini AfterAgent branch at :188. Without this
branch, all 5 require-*-before-stop builtins (commit / push / PR / no-conflicts
/ CI-green) were observation-only on Copilot.

Also adds SubagentStop to COPILOT_HOOK_EVENT_TYPES so the same policies fire
when Copilot subagents finish (Copilot's `subagentStop` aliases to PascalCase
via the same VS Code-compat alias that maps `agentStop` ↔ `Stop`).

Tests:
- New unit test (policy-evaluator.test.ts) pinning the Copilot Stop JSON shape
- New e2e regression (copilot-integration.e2e.test.ts) round-tripping Stop deny
- Existing install/uninstall test extended to assert SubagentStop is written
- New assertCopilotStopBlock helper in hook-runner.ts

Docs: CLAUDE.md per-CLI capability matrix + types.ts comment block updated
with the verified Stop block semantics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

📝 Walkthrough

Walkthrough

The PR adds Copilot-aware Stop handling: Stop and SubagentStop now emit a Copilot-specific JSON "block" payload on stdout with exitCode 0 when the session CLI is "copilot". It extends Copilot event types to include "SubagentStop", updates docs, and adds tests and a test helper to validate the new behavior.

Changes

Copilot Stop Event Handling & SubagentStop Support

Layer / File(s) Summary
Type Definitions & Documentation
src/hooks/types.ts, CLAUDE.md
COPILOT_HOOK_EVENT_TYPES now includes "SubagentStop". Added docs describing Copilot Stop block semantics and the stdout JSON block shape used to enforce denials.
Core Policy Evaluator Logic
src/hooks/policy-evaluator.ts
Stop and SubagentStop handling now branch on session.cli === "copilot": for Copilot return exitCode: 0 with stdout JSON { decision: "block", reason: ... } (and empty stderr); non-Copilot behavior continues to return exitCode: 2 with reason on stderr. This behavior applies to both deny and instruct paths.
Test Helpers
__tests__/e2e/helpers/hook-runner.ts
Added assertCopilotStopBlock(result: HookRunResult) asserting exitCode === 0, parsed decision === "block", and reason contains MANDATORY ACTION REQUIRED.
Tests & Validation
__tests__/e2e/hooks/copilot-integration.e2e.test.ts, __tests__/hooks/policy-evaluator.test.ts, __tests__/hooks/integrations.test.ts
New and updated tests validate Copilot Stop JSON output, SubagentStop inclusion in event types, and Copilot-specific deny/instruct flows producing the stdout JSON with expected reason prefix; e2e and unit expectations updated accordingly.
Changelog
CHANGELOG.md
Unreleased notes updated to document Copilot require-*-before-stop policy enforcement, SubagentStop parity, transcript/export and download-session mentions, and related test/doc updates.

Sequence Diagram

sequenceDiagram
  participant Copilot as Copilot CLI
  participant HookRunner as Hook Runner
  participant Policy as Policy Evaluator
  participant Std as Stdout/Stderr

  Copilot->>HookRunner: invoke Stop/SubagentStop hook (session.cli="copilot")
  HookRunner->>Policy: evaluatePolicies(session, eventType)
  Policy-->>HookRunner: { decision: "instruct/deny", reasonText }
  alt session.cli == "copilot" and eventType is Stop/SubagentStop
    HookRunner->>Std: write stdout JSON { decision: "block", reason: reasonText }
    HookRunner-->>Copilot: exitCode 0
  else non-Copilot Stop/SubagentStop
    HookRunner->>Std: write stderr reasonText
    HookRunner-->>Copilot: exitCode 2
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • exospherehost/failproofai#63: Modifies evaluatePolicies in src/hooks/policy-evaluator.ts and related stdout JSON behavior; touches the same function and output conventions.
  • exospherehost/failproofai#109: Related Stop-event handling changes that introduce or standardize the "MANDATORY ACTION REQUIRED" framing used in deny responses.
  • exospherehost/failproofai#64: Also modifies Stop behavior in src/hooks/policy-evaluator.ts; related to where Stop denies write their reason and output semantics.

Poem

🐰 I hopped through hooks with careful cheer,
Copilot stops now speak plain and clear,
Subagent joins, a JSON block so bright,
Tests nod, docs glow in the springtime light,
Hop, patch, assert — the rabbit claps in delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: enforcing require-*-before-stop policies on Copilot CLI via documented Stop block semantics.
Description check ✅ Passed The description provides comprehensive context including empirical verification, technical rationale, test results, and checklist completion, well exceeding template requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/hooks/policy-evaluator.ts`:
- Around line 267-287: Builtin policies registered with match: { events:
["Stop"] } and the runtime check if (eventType === "Stop") both miss
SubagentStop, so Copilot subagent denials fall through to exitCode 2 instead of
returning the Copilot block payload; update the policy registrations that
currently use match: { events: ["Stop"] } to include "SubagentStop" as well, and
change the runtime condition in policy-evaluator.ts from if (eventType ===
"Stop") to check both Stop and SubagentStop (e.g., eventType === "Stop" ||
eventType === "SubagentStop") so the Copilot branch that returns
JSON.stringify({ decision: "block", reason: reasonText }) (and the surrounding
session?.cli === "copilot" handling, policy.name, reason, decision: "deny"
fields) is executed for both event types.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2e1207c2-5f3e-4f64-a2cd-97b6540ffdbb

📥 Commits

Reviewing files that changed from the base of the PR and between e5f3eb7 and 5959e6d.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • CLAUDE.md
  • __tests__/e2e/helpers/hook-runner.ts
  • __tests__/e2e/hooks/copilot-integration.e2e.test.ts
  • __tests__/hooks/integrations.test.ts
  • __tests__/hooks/policy-evaluator.test.ts
  • src/hooks/policy-evaluator.ts
  • src/hooks/types.ts

Comment thread src/hooks/policy-evaluator.ts Outdated
NiveditJain and others added 2 commits May 5, 2026 16:11
Address CodeRabbit on PR #299: the cli==="copilot" branch initially only
matched eventType==="Stop", so SubagentStop denies fell through to exit-2
— which Copilot ignores for stop-style events, leaving any custom policy
matching SubagentStop as observation-only on Copilot subagent boundaries.

Widen the condition to `eventType === "Stop" || eventType === "SubagentStop"`
so both stop-style events emit the {decision:"block", reason} JSON shape on
Copilot. Per the cli-hooks-reference, both `agentStop` and `subagentStop`
honor the same retry shape.

Skip the second half of CodeRabbit's suggestion (extending the 5
require-*-before-stop builtin policies to also match SubagentStop): those
policies are session-completion gates (commit/push/PR/conflicts/CI) and
firing them on every subagent return is conceptually mismatched and
noisy. SubagentStop subscription remains for custom policies that
genuinely care about subagent boundaries.

Side effect: Claude SubagentStop denies now use the MANDATORY ACTION
REQUIRED wrapper too (was previously falling through to bare-reason in
the "other events" fallback). Consistent with Stop and an improvement.

Tests: +2 cases — Copilot SubagentStop emits JSON-block; Claude
SubagentStop preserves exit-2+wrapped-stderr.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…p widening

Existing entry only mentioned the SubagentStop *subscription* in
COPILOT_HOOK_EVENT_TYPES. Add the runtime-side change too: the
cli==="copilot" Stop branch in policy-evaluator.ts now matches both
Stop and SubagentStop so custom policies subscribing to subagent
boundaries get the correct retry shape on Copilot. Also calls out the
explicit design decision to NOT extend the 5 require-*-before-stop
builtin policies to SubagentStop (they're session-completion gates).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Line 9: Update the CHANGELOG language to avoid implying the five builtins are
affected: clarify that adding SubagentStop to COPILOT_HOOK_EVENT_TYPES and
widening the cli === "copilot" branch in policy-evaluator.ts to handle both Stop
and SubagentStop enables custom policies to fire on Copilot subagent boundaries,
while the existing require-*-before-stop builtins still intentionally match only
Stop; reword the earlier sentence to explicitly state this distinction
(reference symbols: SubagentStop, COPILOT_HOOK_EVENT_TYPES,
require-*-before-stop, policy-evaluator.ts cli === "copilot" branch).

In `@src/hooks/policy-evaluator.ts`:
- Around line 267-288: The instruct-path for policy enforcement misses the
Copilot-specific `{decision: "block", reason}` response for both "Stop" and
"SubagentStop" events; update the instruct branch that currently only checks
`eventType === "Stop"` to also handle `eventType === "SubagentStop"` and add a
`session?.cli === "copilot"` branch that returns the same shape as the deny-path
(exitCode: 0, stdout: JSON.stringify({decision: "block", reason: reasonText}),
stderr: "", policyName: policy.name, reason, decision: "deny") before falling
through to the generic Exit-2 + stderr handler so Copilot retries on both Stop
and SubagentStop for instruct policies.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f7e44b9e-f852-4e73-bd26-b80ebec943f4

📥 Commits

Reviewing files that changed from the base of the PR and between 5959e6d and e80d8ce.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • __tests__/hooks/policy-evaluator.test.ts
  • src/hooks/policy-evaluator.ts

Comment thread CHANGELOG.md Outdated
Comment thread src/hooks/policy-evaluator.ts
CodeRabbit catch on PR #299: the instruct branch in policy-evaluator.ts
only matched eventType === "Stop" and lacked any cli === "copilot" arm.
The deny branch above was already widened, but the instruct path was
missed — instruct verdicts on Copilot (Stop or SubagentStop) fell
through to exit-2, which Copilot logs as a hook warning but does NOT
honor for retry. Mirror the deny-path fix:

- Widen the condition to (eventType === "Stop" || eventType === "SubagentStop")
- Add a session?.cli === "copilot" branch returning {decision: "block",
  reason} JSON on stdout (exit 0), same shape as the deny path

Builtin require-*-before-stop policies still match Stop only by design;
this fix is what custom subagent-instruct policies need to actually
enforce on Copilot.

Also reword the CHANGELOG sentence so "the same policies fire when
Copilot subagents finish" no longer implies the 5 builtins are subagent-
scoped (they are session-completion gates and intentionally still match
Stop only).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
CHANGELOG.md (1)

9-9: ⚡ Quick win

Condense this changelog item to the repo’s single-line format.

This reads like release notes rather than an Unreleased changelog bullet. Please trim it to a short one-line summary with the PR reference.

As per coding guidelines, CHANGELOG.md: “Add every PR update under the ## Unreleased section with subsections for Features, Fixes, Docs, and Dependencies; format as single-line entries like - Add foo support (#123).”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` at line 9, Replace the long multi-paragraph entry in
CHANGELOG.md with a single-line Unreleased bullet in the repo's required format:
condense the content to one short sentence like "- Enforce require-*-before-stop
policies and add Copilot SubagentStop parity (`#PR`)" (use the actual PR number in
place of `#PR`), place it under the appropriate Unreleased subsection (Features or
Fixes), and remove the verbose release-note style paragraph entirely.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@CHANGELOG.md`:
- Line 9: Replace the long multi-paragraph entry in CHANGELOG.md with a
single-line Unreleased bullet in the repo's required format: condense the
content to one short sentence like "- Enforce require-*-before-stop policies and
add Copilot SubagentStop parity (`#PR`)" (use the actual PR number in place of
`#PR`), place it under the appropriate Unreleased subsection (Features or Fixes),
and remove the verbose release-note style paragraph entirely.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4fa93ef5-6ae9-4cc1-8175-ac083735f38c

📥 Commits

Reviewing files that changed from the base of the PR and between e80d8ce and de88fea.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • __tests__/hooks/policy-evaluator.test.ts
  • src/hooks/policy-evaluator.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/hooks/policy-evaluator.test.ts

@NiveditJain NiveditJain merged commit 04abaf4 into main May 5, 2026
9 checks passed
NiveditJain added a commit that referenced this pull request May 8, 2026
…a.6 (#318)

* [luv-319] fix: enforce Stop hook on Cursor Agent CLI (followup_message + SubagentStop parity)

Cursor's `stop` hook ignores the flat `{permission: "deny"}` shape — that's
honored on tool events only. The only force-retry channel for Stop is
`{followup_message}` on stdout (exit 0), per https://cursor.com/docs/hooks.
The instruct branch already used this shape correctly since #245; the deny
path needed the same treatment, mirroring Copilot's #299 fix.

Without this, the 5 require-*-before-stop builtins were observation-only on
Cursor — the deny was logged but the agent stopped cleanly. User repro:
session 1b510ad4-906c-4f30-9467-ff2e6c581cce at /home/nivedit/dev-purge.

Also subscribes to `subagentStop` (CURSOR_HOOK_EVENT_TYPES + CURSOR_EVENT_MAP)
and widens both deny and instruct branches to match it, for parity with the
Copilot SubagentStop widening from #299.

Cloud Agents caveat: Cursor Cloud Agent VMs do NOT run stop/subagentStop
hooks at all, so this fix only covers local Cursor sessions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: cut 0.0.10-beta.6 release in CHANGELOG

Promote the six entries accumulated under `## Unreleased` to a versioned
heading `## 0.0.10-beta.6 — 2026-05-08`. Add a fresh `## Unreleased`
heading at the top for the next development cycle.

package.json was already at 0.0.10-beta.6 (pre-bumped); no version edit
needed here. The CHANGELOG cut completes the release-prep handshake.

Entries promoted:
- Cursor Stop hook enforcement fix (this PR)
- 5 scripts/translate-docs fixes from #305, #306, #307, #312, #313

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
NiveditJain added a commit that referenced this pull request May 9, 2026
* [luv-324] fix: enforce Stop hook on OpenCode

Stop hooks fired on OpenCode (visible in dashboard activity feed) but
the agent stopped without retry — same failure mode Cursor had pre-#318
and Copilot had pre-#299. Root cause: no `cli === "opencode"` branch in
policy-evaluator's Stop / SubagentStop handling, so OpenCode fell into
the generic exit-2 path. The plugin shim's applyDecision turns exit-2
into `throw new Error(reason)`, but throwing from the `session.idle`
event callback is a no-op — OpenCode is already idle by the time the
event fires.

Fix: emit `{hookSpecificOutput: {additionalContext: <MANDATORY ACTION
reasonText>}}` for opencode Stop / SubagentStop in both deny and
instruct paths. The shim already routes `additionalContext` through
`client.session.prompt(...)` which submits a new user message that
re-triggers the agent loop — same model as Cursor's `followup_message`
and Copilot's `{decision: "block", reason}`. Promote applyDecision to
async and `await client.session.prompt` for Stop/SubagentStop events
so the SDK round-trip completes before the plugin context tears down;
keep fire-and-forget for tool events to avoid hot-path latency.

Sister CLIs verified while in here:
- Gemini AfterAgent (canonical Stop) was already correctly emitting
  `{decision: "block", reason}`; new unit tests pin both deny and
  instruct shapes to prevent regression.
- Pi `agent_end` is observation-only by upstream design — Pi's agent
  loop has already exited and `AgentEndEventResult` exposes no `block`
  field. CLAUDE.md already documents this; no code change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* [luv-324] docs: clarify OpenCode plugin shim Stop semantics

Update configuration.mdx to reflect the new Stop / SubagentStop force-
retry channel: deny on Stop now routes through `client.session.prompt`
just like instruct, since `session.idle` is notification-only and
throwing from it is silently dropped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* [luv-324] fix: address CodeRabbit feedback + cut 0.0.10-beta.8

Address PR #323 review:
- CHANGELOG.md: append (#323) to the Unreleased entry per repo convention
  (every entry ends with the PR number).
- docs/configuration.mdx:199: "Unlike the other four CLIs" → "Unlike the
  other six CLIs" — the page now lists six other integrations
  (Claude Code, Codex, Copilot, Cursor, Pi, Gemini) so the count was
  stale.

Release prep: promote the Unreleased entry to a versioned heading
`## 0.0.10-beta.8 — 2026-05-08`. Add a fresh `## Unreleased` heading
at the top for the next development cycle. package.json is already at
0.0.10-beta.8 (pre-bumped by chore commit a146ae6 after beta.7 release).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant