Skip to content

refactor(mcp): collapse role ladder into independent capability config#3202

Merged
Sinity merged 3 commits into
masterfrom
refactor/mcp/roles-to-config
Jul 20, 2026
Merged

refactor(mcp): collapse role ladder into independent capability config#3202
Sinity merged 3 commits into
masterfrom
refactor/mcp/roles-to-config

Conversation

@Sinity

@Sinity Sinity commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

Collapses the MCP server's role ladder (read < write < review < admin) into
independent, config-only capability opt-ins per polylogue-800m: the server is
read-only by default; write (write/run), judge (judge), and maintenance
(maintenance) are each an explicit, independent boolean opt-in resolved
once at server startup from polylogue.toml [mcp] or
POLYLOGUE_MCP_*_ENABLED env vars. There is no ladder — enabling one
capability never implies another — and no --role CLI flag anywhere in the
stack.

Problem

The operator decided (2026-07-20, polylogue-800m) that the MCP role ladder
is not a product concept worth keeping: --role read/write/review/admin
modeled an ordered tier where each level silently included the previous
one's authority, which is both more machinery than the actual requirement
(independent per-capability opt-in) and a worse security default (enabling
review to unlock judge also silently re-exposed write/run). Per the
repo's no-compat doctrine this is a hard removal, not a deprecation.

Solution

  • polylogue/config.py: three new layered config keys following the
    existing single-resolver (PolylogueConfig) pattern —
    mcp_write_enabled/mcp_judge_enabled/mcp_maintenance_enabled
    ([mcp] TOML table, POLYLOGUE_MCP_*_ENABLED env, all default False).
  • polylogue/mcp/declarations/models.py: new MCPCapabilities frozen
    dataclass (three independent booleans + .allows(required)) replaces
    MCPRole/MCP_ROLE_ORDER/mcp_role_allows. Every declaration's
    minimum_role: MCPRole becomes required_capability: MCPCapabilityFlag | None (None = always-on read tool).
  • polylogue/mcp/declarations/{registry,adapter}.py,
    polylogue/mcp/{server,server_cutover,server_support,cli}.py: the
    10-dispatcher registration/gating (6 read + write/run behind write,
    judge behind judge, maintenance behind maintenance) now reads
    MCPCapabilities instead of a role string. polylogue-mcp
    (mcp/cli.py) drops --role entirely and resolves capabilities from
    load_polylogue_config() at startup — config is the sole authority, not a
    launch argument.
  • polylogue/agent_integration/{spec,manifest,installer}.py,
    polylogue/cli/commands/agent.py: the parallel "role" vocabulary in
    the native-client installer/manifest system (ROLES/ROLE_ORDER,
    polylogue agent manifest/install --role) collapses the same way —
    polylogue agent manifest/install gained
    --enable-write/--enable-judge/--enable-maintenance flags. The
    installer now emits POLYLOGUE_MCP_WRITE_ENABLED=1 etc. into the
    generated MCP client entry's env block instead of args: ["--role", ...] — this is real wiring (the client launches polylogue-mcp with that
    env, which load_polylogue_config() picks up), not just a renamed
    no-op. InstallOptions' on-disk state schema bumped STATE_SCHEMA_VERSION
    1→2 (role→capabilities field shape) so stale state fails closed via the
    existing integrity gate instead of silently exposing a missing field.
  • nix/agent-integration-module.nix: mcpRole enum option replaced by
    three independent mcpEnableWrite/mcpEnableJudge/mcpEnableMaintenance
    booleans (Home Manager module), each off by default.
  • Docs: docs/mcp-reference.md, docs/mcp-integration.md,
    docs/architecture-spine.md (decision log entry), docs/daemon-threat-model.md,
    docs/architecture-hotspots.md — role/--role prose replaced with the
    config-opt-in description. docs/agent-manual.md,
    docs/agent-integration-reference.md, and the packaged
    polylogue/agent_integration/data/*.{md,json} assets regenerated via
    devtools render agent-manual/render mcp-equivalence/render mcp-tool-index.
  • Registration traps (all four): tests/infra/mcp.py::EXPECTED_TOOL_NAMES
    (now derived from declared_tool_names(ALL_CAPABILITIES)), the
    TOOL_CONTRACT envelope tests, docs/generated/mcp-equivalence.json
    (render mcp-equivalence), and docs/mcp-reference.md's generated
    tool-index appendix (render mcp-tool-index) — all regenerated and
    green.
  • Tests: tests/unit/mcp/test_privileged_tools.py's TestRoleGating
    became TestCapabilityGating, asserting the independent-flag behavior
    directly (e.g. judge=True alone must not also expose write/run
    the old ladder-based test asserted the opposite). All other
    build_server(role=...)/declared_tool_names("admin")-style call sites
    across tests/unit/mcp/, tests/unit/agent_integration/,
    tests/unit/{maintenance,storage,cost,archive/query}/,
    tests/integration/test_mcp.py, and devtools/verify_agent_integration.py
    updated to the capability model.

Design note (deferred, not built here): the second half of polylogue-800m
— assertion judgment at agent scale (policy/automation-driven, human review
reserved for escalations, replacing the per-assertion human-review pipeline
that doesn't scale) — is design-only per the task brief. Summary for the
coordinator to bead separately: the judge MCP dispatcher and
judge_assertion_candidates API already support bulk, policy-shaped
decisions (accept/reject/defer/supersede with reason + provenance); what's
missing is an automation actor that calls judge on a schedule/trigger
using declared acceptance policy per AssertionKind, with an explicit
escalation path (a defer+flag that routes specifically to human review)
rather than every candidate defaulting to human attention. That's a new
policy-engine + trigger surface, not a schema or MCP-surface change, and is
out of scope for this PR.

Sequencing with #3190: #3190 (README rewrite, branch docs/readme-rewrite,
currently open) contains an MCP config example using --role read. That
branch was intentionally not touched here per the task brief. Whichever of
#3190 / this PR merges second should rebase and drop the --role flag from
its README example — the flag no longer exists after this PR merges.

Verification

  • devtools render all --check → sync OK, no drift (all generated MCP/agent
    docs and JSON assets committed after regeneration).
  • devtools verify --quick → ruff format/check, mypy --strict, render all,
    topology, layering, closure-matrix, manifests, ci-workflows, doc-commands,
    docs-coverage, test-infra-currency, test-clock-hygiene,
    pytest-timeout-overrides, degrade-loudly — all exit 0.
  • devtools test tests/unit/mcp tests/unit/agent_integration → 214 passed, 1
    pre-existing unrelated failure
    (test_manual_contract.py::test_generated_continuation_token_decodes_to_the_bound_result
    asserts a stale q1. continuation-token prefix against the current q2
    codec; reproduced identically on unmodified master, unrelated to this
    change).
  • devtools test tests/unit/maintenance tests/unit/storage/{test_query_parity,test_retrieval_readiness_laws,test_surface_parity_adapters} tests/unit/cost/test_contract_suite.py tests/unit/archive/query/test_continuation_surface_parity.py tests/integration/test_mcp.py
    → pre-existing unrelated failures only (KeyError: 'search'/'list_sessions'/'list_tags'
    — these tests call individual MCP tool names retired by the earlier
    six-tool cutover, unrelated to the role→capability collapse; same
    failures reproduce on unmodified master).

Ref polylogue-800m

Sinity and others added 2 commits July 20, 2026 11:56
…docs

Problem: the WIP rescue commit landed mid-refactor -- devtools render_agent_manual.py
still imported the retired ROLE_ORDER/ROLES from polylogue.agent_integration.spec,
several devtools/test call sites still passed positional "admin"/"read"/"write" role
strings into declared_tool_names()/build_server()/target_tool_names(), and
docs (mcp-reference.md, mcp-integration.md, architecture-spine.md,
daemon-threat-model.md) still described the retired --role ladder.

What changed:
- devtools/render_agent_manual.py, render_mcp_equivalence.py, render_mcp_tool_index.py,
  verify_agent_integration.py, continuity_replay.py: updated to the MCPCapabilities
  model (independent write/judge/maintenance booleans, no role ladder).
- polylogue/agent_integration/{spec,manifest}.py, cli/commands/agent.py: role/ROLES/
  ROLE_ORDER replaced by required_capability/MCPCapabilities; `polylogue agent
  manifest`/`install` gained --enable-write/--enable-judge/--enable-maintenance
  flags instead of --role.
- Regenerated docs/generated/mcp-equivalence.json, docs/agent-manual.md,
  docs/agent-integration-reference.md, and the packaged
  polylogue/agent_integration/data/*.{md,json} assets via `devtools render agent-manual`
  + `devtools render mcp-equivalence` + `devtools render mcp-tool-index`.
- docs/mcp-reference.md, docs/mcp-integration.md, docs/architecture-spine.md,
  docs/daemon-threat-model.md, docs/architecture-hotspots.md: replaced --role/role-ladder
  prose with the config-opt-in description ([mcp] write_enabled/judge_enabled/
  maintenance_enabled, POLYLOGUE_MCP_*_ENABLED).
- Remaining test call sites (tests/infra/surfaces.py, tests/unit/{storage,maintenance,
  cost,archive/query,agent_integration}/..., tests/integration/test_mcp.py) updated to
  build_server(capabilities=...)/target_tool_names()/declared_tool_names(...).
- devtools/render_mcp_equivalence.py: annotated capability_counts/capability_order as
  plain str to satisfy mypy --strict against the new Literal-keyed Counter.

Verification:
- devtools render all --check -> sync OK, no drift.
- devtools verify --quick -> ruff format/check, mypy --strict, render all, topology,
  layering, closure-matrix, manifests, ci-workflows, doc-commands, docs-coverage,
  test-infra-currency, test-clock-hygiene, pytest-timeout-overrides, degrade-loudly
  all exit 0.
- devtools test tests/unit/mcp tests/unit/agent_integration -> 214 passed, 1
  pre-existing unrelated failure (test_manual_contract.py::...decodes_to_the_bound_result
  asserts a stale q1. token prefix against the current q2 codec; fails identically on
  unmodified master, confirmed via git show).
- devtools test tests/unit/maintenance tests/unit/storage/{test_query_parity,
  test_retrieval_readiness_laws,test_surface_parity_adapters} tests/unit/cost/
  test_contract_suite.py tests/unit/archive/query/test_continuation_surface_parity.py
  tests/unit/core/test_config*.py -> 344 passed, 3 pre-existing unrelated failures
  (KeyError 'search'/'list_sessions'/'list_tags' -- these tests still call retired
  individual MCP tool names that predate the six-tool cutover; unrelated to
  role-to-capability collapse).
- devtools test tests/integration/test_mcp.py -> 4 pre-existing unrelated failures,
  same retired-tool-name cause.

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

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Sinity, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: aecd0a48-a750-43ce-b5a2-3f458c124fb3

📥 Commits

Reviewing files that changed from the base of the PR and between a87c530 and 52f4443.

⛔ Files ignored due to path filters (1)
  • docs/generated/mcp-equivalence.json is excluded by !**/generated/**
📒 Files selected for processing (59)
  • devtools/continuity_replay.py
  • devtools/render_agent_manual.py
  • devtools/render_mcp_equivalence.py
  • devtools/render_mcp_tool_index.py
  • devtools/verify_agent_integration.py
  • docs/agent-integration-reference.md
  • docs/agent-manual.md
  • docs/architecture-hotspots.md
  • docs/architecture-spine.md
  • docs/daemon-threat-model.md
  • docs/mcp-integration.md
  • docs/mcp-reference.md
  • nix/agent-integration-module.nix
  • polylogue/agent_integration/data/deep-reference.md
  • polylogue/agent_integration/data/integration-manifest.json
  • polylogue/agent_integration/data/integration-spec.json
  • polylogue/agent_integration/data/standing-manual.md
  • polylogue/agent_integration/data/tool-contracts.json
  • polylogue/agent_integration/installer.py
  • polylogue/agent_integration/manifest.py
  • polylogue/agent_integration/spec.py
  • polylogue/cli/commands/agent.py
  • polylogue/config.py
  • polylogue/mcp/cli.py
  • polylogue/mcp/declarations/__init__.py
  • polylogue/mcp/declarations/adapter.py
  • polylogue/mcp/declarations/models.py
  • polylogue/mcp/declarations/registry.py
  • polylogue/mcp/server.py
  • polylogue/mcp/server_cutover.py
  • polylogue/mcp/server_resources.py
  • polylogue/mcp/server_support.py
  • tests/infra/mcp.py
  • tests/infra/surfaces.py
  • tests/integration/test_mcp.py
  • tests/unit/agent_integration/test_assets_and_cli.py
  • tests/unit/agent_integration/test_installer.py
  • tests/unit/archive/query/test_continuation_surface_parity.py
  • tests/unit/core/test_config_inventory.py
  • tests/unit/cost/test_contract_suite.py
  • tests/unit/maintenance/test_envelope_contracts.py
  • tests/unit/maintenance/test_scope_filter.py
  • tests/unit/maintenance/test_scope_filter_envelope_contract.py
  • tests/unit/mcp/conftest.py
  • tests/unit/mcp/test_cli.py
  • tests/unit/mcp/test_contract_evidence.py
  • tests/unit/mcp/test_envelope_contracts.py
  • tests/unit/mcp/test_mcp_call_log.py
  • tests/unit/mcp/test_privileged_tools.py
  • tests/unit/mcp/test_prompt_query_parity.py
  • tests/unit/mcp/test_query_gap_projections.py
  • tests/unit/mcp/test_server_runtime.py
  • tests/unit/mcp/test_server_surfaces.py
  • tests/unit/mcp/test_tool_declarations.py
  • tests/unit/mcp/test_tool_discovery.py
  • tests/unit/mcp/test_tool_error_isolation.py
  • tests/unit/storage/test_query_parity.py
  • tests/unit/storage/test_retrieval_readiness_laws.py
  • tests/unit/storage/test_surface_parity_adapters.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/mcp/roles-to-config

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7cf4426919

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread polylogue/config.py Outdated
``POLYLOGUE_MCP_WRITE_ENABLED=1`` to register the mutation
dispatchers.
"""
return bool(self._data.get("mcp_write_enabled"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject malformed capability booleans instead of enabling them

When POLYLOGUE_MCP_WRITE_ENABLED contains a typo such as flase (or TOML supplies a quoted/non-boolean value), _coerce_env_value preserves the nonempty string and this bool(...) converts it to True, registering the write/run mutation dispatchers despite the value not being an accepted opt-in. The same fail-open behavior affects judge and maintenance; these security-boundary properties should accept only an actual True or reject invalid configuration.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 52f4443: _coerce_env_value's boolean branch now raises ConfigError naming the env var/key/offending value for any _BOOL_CONFIG_KEYS entry that isn't a canonical 1/true/yes/on / 0/false/no/off token (case-insensitive), instead of passing the raw string through — this fixes the shared coercion path, not just the three new MCP keys. Also added _require_bool_config_value() (shared by mcp_write_enabled/mcp_judge_enabled/mcp_maintenance_enabled) to close the same hole for a quoted TOML value, which bypasses env coercion entirely. New tests in tests/unit/core/test_config_inventory.py::TestMCPCapabilityBooleanFailsClosed cover: typo raises, false/0/no/off (case-insensitive) disables, true/1/yes/on (case-insensitive) enables, TOML true works, and quoted TOML string raises.

Problem: CodeRabbit flagged (PR #3202) that POLYLOGUE_MCP_WRITE_ENABLED=flase
(or any nonempty non-boolean string, or a quoted TOML value) passed through
_coerce_env_value's bool branch unchanged as a raw string, and the
mcp_write_enabled/mcp_judge_enabled/mcp_maintenance_enabled properties did
bool(self._data.get(...)) -- bool("flase") is True, so a typo silently
opened a capability boundary instead of leaving it closed. Same shape for
every other _BOOL_CONFIG_KEYS entry (not special-cased to the new keys).

What changed:
- _coerce_env_value's boolean branch now raises ConfigError naming the env
  var, offending value, and config key when the string isn't one of the
  canonical 1/true/yes/on / 0/false/no/off tokens (case-insensitive),
  instead of silently passing the raw string through. This applies to every
  _BOOL_CONFIG_KEYS entry via the one shared coercion path, not a
  capability-only special case.
- New _require_bool_config_value() (shared by all three capability
  properties) additionally closes the TOML-layer version of the same gap: a
  quoted TOML value (`write_enabled = "flase"`) bypasses env coercion
  entirely, so the property getter itself now validates the resolved value
  is a genuine bool or a canonical token string, raising ConfigError
  otherwise. Token-parsing logic is shared via _parse_bool_token() between
  the env-coercion path and this property-level guard.

Verification:
- devtools test tests/unit/core/test_config_inventory.py tests/unit/core/test_config.py
  -> 155 passed, including the new TestMCPCapabilityBooleanFailsClosed
  (typo raises, false/0/no/off/case-insensitive disables, true/1/yes/on/
  case-insensitive enables, TOML true works, quoted TOML string raises).
- devtools test tests/unit/mcp/test_cli.py tests/unit/cli/test_config_command.py
  -> 15 passed.
- devtools verify --quick -> ruff format/check, mypy --strict, render all,
  topology, layering, docs-coverage, etc. all exit 0.
- devtools test tests/unit/core -> 2327 passed; 4 failed / 6 errored, all in
  test_sync_surface_runtime.py/test_verification.py/test_models.py/
  test_schema_generation.py over artifact-taxonomy/schema-generation
  naming (agent_transcript vs subagent_session_stream) -- no reference to
  config.py, load_polylogue_config, or boolean coercion in any of them;
  pre-existing drift from the concurrent master rebase (#3196-#3200 landed
  mid-session), unrelated to this fix.

Co-Authored-By: Claude <noreply@anthropic.com>
@Sinity
Sinity merged commit 8b41abe into master Jul 20, 2026
3 checks passed
@Sinity
Sinity deleted the refactor/mcp/roles-to-config branch July 20, 2026 10:19
Sinity added a commit that referenced this pull request Jul 20, 2026
## Summary

Adds the missing automated actor for the MCP `judge` dispatcher's
candidate
queue: a policy engine plus a daemon periodic-loop trigger surface that
decides which `CANDIDATE`-status assertions are auto-judgeable and calls
the
same storage chokepoint the MCP `judge` dispatcher uses, escalating the
residue to an explicit, queryable `handoff` assertion.

## Problem

Per the polylogue-800m design note (PR #3202), the MCP `judge`
dispatcher
lets an operator judge assertion candidates in bulk, but nothing calls
it
automatically. Every agent-authored candidate (pathology findings,
ontology/
transform candidates, findings, ...) defaults to waiting on an explicit
human decision, which the operator has said does not scale — most
agent-authored annotations will never be seen by a human.

## Solution

- **`polylogue/daemon/judgment_automation.py`** (new): a policy engine
  (`evaluate_candidate`) applies per-kind confidence thresholds
(`auto_accept_min_confidence`/`auto_reject_max_confidence`) decoded from
a
  new `[judgment_automation.policies]` config table
  (`parse_judgment_automation_policy`). A candidate is judged
  (`run_judgment_automation_sweep_once`) via the real
  `judge_assertion_candidates` storage chokepoint — the same one the MCP
  `judge` dispatcher calls — when the policy is unambiguous; every other
candidate (no policy for its kind, no `confidence` value, or a
confidence
inside the undecided band) is escalated: a deterministic `handoff`-kind
  assertion pointing at the candidate is upserted so the residue is a
  queryable review queue, not silent limbo.
- **`polylogue/daemon/cli.py`**: wires
`periodic_judgment_automation_sweep`
into the existing `periodic_loops` list next to `_periodic_db_optimize`/
`periodic_embedding_backlog_check`, following the established periodic-
maintenance-loop pattern rather than a `DaemonConverger` stage —
judgment
  candidates are not file/session-scoped, and
`docs/retro/2026-05-24-1498-cascade.md` flags `convergence_stages.py` as
due for a refactor before a 4th stage, so this keeps the bead's
footprint
  out of that hot file entirely.
- **`polylogue/config.py`**: four new layered config keys —
`judgment_automation_enabled` (fail-closed bool, off by default,
following
  the `mcp_judge_enabled` pattern), `judgment_automation_interval_s`,
`judgment_automation_batch_limit`, and the raw
`judgment_automation_policy`
  table (decoded by the owning module, same shape as
  `health_convergence_debt`).
- **Dual capability gate**: the sweep only judges when *both*
`judgment_automation_enabled` AND `mcp_judge_enabled` (polylogue-800m's
independent capability boundary) are true — it exercises the same judge
  write authority as the MCP dispatcher, so its own opt-in must not, by
  itself, grant that authority. Both flags are re-read every tick
  (`reload_behavior="daemon-loop"`), so flipping either off in
`polylogue.toml` takes effect on the next tick without a daemon restart.
- Docs: `docs/configuration.md` (config key table) and `docs/daemon.md`
(new "Judgment Automation" section with a worked `polylogue.toml`
example).

## Verification

- `devtools test tests/unit/daemon/test_judgment_automation.py
tests/unit/core/test_config_inventory.py
tests/unit/daemon/test_daemon_cli.py` → `183 passed`
- `mypy --strict` on `polylogue/config.py polylogue/daemon/cli.py
polylogue/daemon/judgment_automation.py
tests/unit/daemon/test_judgment_automation.py` → `Success: no issues
found in 4 source files`
- `devtools verify --quick` (ruff format/check, mypy, render-all
--check,
topology/layering/closure-matrix/manifests/ci-workflows/doc-commands/docs-coverage/test-infra-currency/test-clock-hygiene/pytest-timeout-overrides/degrade-loudly)
→ `exit_code: 0`
- Not run: full `devtools verify` (testmon-affected) / `--seed-testmon`
— per-repo policy this is the default pre-PR baseline, but it was
explicitly deferred for this lane in favor of the focused set above plus
`--quick`.

## Residual risk

- No real-world confidence-producing detector is wired to a
`[judgment_automation.policies]` entry yet in this PR — the policy table
starts empty by default, so the sweep is a bounded no-op until an
operator
  configures at least one kind. This is intentional (config-gated OFF,
  fail-closed on missing policy) but means the automation has no live
exercise beyond the test fixtures until an operator opts a real detector
  kind in.
- The escalation `handoff` assertion is discoverable only via an
explicit
`kind=handoff` filter today (not in `ASSERTION_CLAIM_KINDS`'s default
set);
  wiring a dedicated MCP/CLI "review queue" surface for it is left as
  follow-up rather than expanding this PR's scope.

Ref polylogue-6qjc

---------

Co-authored-by: Claude <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