Skip to content

Subagent-public floor: the no-entitlement spawn+dispatch fixture + entitlement-freedom guards (#826) - #827

Open
aarontrowbridge wants to merge 2 commits into
mainfrom
826-subagent-public-floor-fixture
Open

Subagent-public floor: the no-entitlement spawn+dispatch fixture + entitlement-freedom guards (#826)#827
aarontrowbridge wants to merge 2 commits into
mainfrom
826-subagent-public-floor-fixture

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

Implements #826.

Acceptance criteria

  • With an empty entitlements config (no amicissimo/premium codes), the amicode_session tool registers and a spawn executes end-to-end (double/mock client, honest counts + metadata stamps) — PASS with zero entitlement presence
  • With the same empty config, a spawned child dispatches through a staged worker base card (implementer) — base cards staged complete, no overlay fields, no missing-target errors (composed with the existing base-alone staging test, not duplicated)
  • A regression guard asserts the spawn/dispatch staging sources (tool registration + spawn policy) contain no entitlement/premium reads — the blocklist-grep discipline, so a future entitlement read in those files goes red with a message naming the boundary ruling
  • The audit record lands in the PR body (below), citing the feat(session): amicode_session tool — spawn sessions as background tabs #641 lineage

What landed

One focused fixture file, packages/extension/test/session_public_floor.test.ts (14 tests), three layers:

  1. The no-entitlement spawn fixture — an empty entitlements.toml (codes = [], present file, zero codes) resolved through the real readLocalEntitlements path; the plugin twin registers amicode_session unconditionally; a spawn executes end-to-end through the mock engine (double pattern from session_spawn_double_create.test.ts, extended to record bodies): honest counts (one create + one prompt per child; fan-out count=3 → exactly three, titled (i/3)), honest {spawned_by, spawned_depth} stamps (child-of-child chains to depth 2; the soft cap refuses at depth 2 without force, zero creates), fork-mode stamps via session.update, and the CORE twin (the one implementation both transports project) spawns identically.
  2. The dispatch-through dimension — with the same empty config and the premium overlay source present, stageModCards stages the implementer worker card complete (byte-identical base, overlay_id: null, empty merged_fields, zero rejections — composing mode_cards_staging.test.ts's base-alone test, which pins the all-cards half; this pins the dispatch half through the real fixture-dir resolution), and a spawned child is cast through it: the create/promptAsync bodies carry agent: "implementer" against the staged base card.
  3. The entitlement-freedom guards — blocklist-grep source tripwires (["entitlement", "premium", "amicissimo"], non-emptiness asserted so the lens can't go vacuous) over the spawn policy (session_spawn.ts, whole file) and each transport's amicode_session section (extracted as the window between amicode_report_fallback: and amicode_veloce: so a conditional wrapper around the registration lands in the lens). Verified to fire: a temporary // entitlement mutation in session_spawn.ts goes RED with "ADR-0004 decision 1 (amicode#826): the subagent surface is pre-pay product, never premium — …an entitlement read on the spawn/dispatch path violates the boundary ruling" (mutation reverted; no production file changed).

No production code touched — the fixture pins the audit, it does not change behavior.

The audit record (ADR-0004 decision 1)

What was audited — the full staging path of the subagent surface: tool registration (opencode-plugin/amicode_tools.ts AmicodeTools — the amicode_session key registers unconditionally, ~line 1869, no entitlement gate anywhere on the registration), the spawn policy (opencode-plugin/session_spawn.ts — pure, no imports, no entitlement coupling), the card staging split (src/mode_cards.ts stageModCards — base cards ALWAYS stage complete; overlays resolve ONLY with the amicissimo premium entitlement, which is the correct split and is pinned, not changed), and the issimo entitlement surface (src/amicode_service/solver_mode.ts applyEntitlementForMode — grants/revokes the HP solver tier, gates solving not subagents; out of scope by design).

What was found clean — everything. The audit's central finding held under fixture construction: zero entitlement residue on the spawn/dispatch path. The plugin registers the tool with no entitlement consult; the spawn policy's caps (count 4, soft depth 2, model precedence) are pure policy; base worker cards stage complete with no entitlement; the MCP-floor refusal (spawn refuses on non-opencode harnesses) is harness coupling, not entitlement gating — already honest, out of scope. No behavioral change was needed or made.

What the fixture pins — the public floor end-to-end: registration + spawn + stamps + caps under zero entitlement; dispatch through a staged worker base card under zero entitlement (even with a premium overlay source sitting present and unreached); and the source-level tripwires so a future entitlement read hung on the spawn/dispatch path goes RED naming the ruling. Sources that are SUPPOSED to read entitlements (mode_cards.ts's overlay gate, solver_mode.ts's HP tier) are deliberately outside the guard — the ruling splits subagents from those.

Lineage — ADR-0004 (amicissimo, decision 1 — the subagent surface is pre-pay product, never premium); #641 (the amicode_session tool's landing + lineage); #761 (the card staging + its base-alone test, composed here); the blocklist/proprietary-strings floor tests (the grep discipline reused); session_spawn.test.ts / session_spawn_double_create.test.ts (the policy suite + double patterns reused). Campaign slice 2 per the fleet-amicissimo-forksplit work order.

Verification

  • pnpm --filter amicode typecheck — clean
  • env -u OPENCODE_DB pnpm --filter amicode test — 148 files passed (1 skipped), 2303 passed / 40 skipped / 0 failed
  • Guard tripwire verified by mutation: RED with the boundary-ruling message, then reverted

@aarontrowbridge
aarontrowbridge marked this pull request as ready for review September 5, 2026 18:40
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: a5791f8d-db3a-4f5b-947a-40cf42e4ba3f


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.

❤️ Share

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

@aarontrowbridge

Copy link
Copy Markdown
Member Author

Foreign-red record for the merge decision: the first fast run failed on mode_block.test.ts > H4 FIRST — the session-API availability fixture — a file this diff does not touch (one new test file added, zero modifications to existing tests). The identical test fails identically on unrelated PRs #828, #819, #818, #815 (foreign diffs, same ~12.2s timeout shape); the sibling campaign recorded the same red on #815. Filed as #829 (the owning-surface chore). Local gates: suite 2303/0, typecheck clean, the new fixture 14/14 director-verified. A re-run of the failed job is in flight; if it greens, the flake theory strengthens — either way #829 owns the fixture.

@aarontrowbridge

Copy link
Copy Markdown
Member Author

Green update (supersedes the foreign-red record above): main absorbed the owner's fix (#830#831, merged) for the H4 teardown race; this branch was updated onto it and all 9 checks are now green, including fast. #829 closed as resolved-by-#831. No caveat remains on this PR — it awaits review/merge on its own merits.

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