Skip to content

feat(reason): R3 VERDICT-driven dispatch + feedback close-the-loop (#645) - #665

Merged
github-actions[bot] merged 5 commits into
mainfrom
feat/issue-659-r3-verdict-dispatch
May 11, 2026
Merged

github-actions[bot] merged 5 commits into
mainfrom
feat/issue-659-r3-verdict-dispatch

Conversation

@robotrocketscience

Copy link
Copy Markdown
Owner

Closes #659 (R3 sub-task of wonder/reason umbrella #645).

What ships

The slash-layer behavior that closes the loop on aelf reason's verdict + impasse derivation (R1, #660). Four atomic commits:

  1. feat(reason): dispatch policy + suggested-updates derivation — adds two pure functions to src/aelfrice/reason.py:
    • dispatch_policy(verdict, impasses) -> list[DispatchItem] maps each impasse to a typed DispatchItem whose role is one of Verifier / Gap-filler / Fork-resolver (mapping: CONSTRAINT_FAILURE → Verifier, GAP / NO_CHANGE → Gap-filler, TIE → Fork-resolver).
    • suggested_updates(verdict, impasses, hops) -> list[SuggestedUpdate] emits (belief_id, direction, note) rows with direction +1 (confident hop not on any impasse), ? (impasse locus), or -1 (reserved for R2 fork-path data; never emitted from this surface).
  2. feat(cli): aelf reason --json emits dispatch + suggested_updates — augments the existing R1 --json payload with two additive top-level keys.
  3. feat(slash): /aelf:reason VERDICT-driven dispatch + feedback loop — expands slash_commands/reason.md from a verbatim CLI wrapper into a three-step dispatch script: present chain, fan out one Task subagent per dispatch[i] with role-tagged prompt scaffolds in parallel, then print SUGGESTED UPDATES from suggested_updates. Adds Task to allowed-tools.
  4. docs(changelog): unreleased entry for #659 R3 dispatch + feedback loop.

Dispatch policy

Verdict Dispatch
SUFFICIENT empty — chain already answers
PARTIAL one subagent per impasse
UNCERTAIN one subagent per impasse
INSUFFICIENT one subagent per impasse (typically NO_CHANGE → Gap-filler)
CONTRADICTORY one subagent per impasse (typically TIE → Fork-resolver)

Within each verdict, role is derived from impasse kind, not verdict — so a PARTIAL verdict with both CONSTRAINT_FAILURE and GAP impasses dispatches one Verifier and one Gap-filler.

SUGGESTED UPDATES direction rules

  • +1 — confident hop (alpha + beta ≥ CONFIDENT_TRIALS_MIN) that is not the locus of any impasse. Suppressed entirely under INSUFFICIENT (no chain to vote on).
  • ? — belief appears in any impasse's belief_ids. Surfaced for manual review, never auto-piped.
  • -1 — deferred to a follow-up after R2 (feat(reason): R2 — compound confidence decay + CONTRADICTS fork (#645 sub-task) #658) ships fork-path data; this surface emits zero -1 rows.

If a belief is both confident-on-chain and on an impasse, the ? row wins (impasse evidence takes precedence; the caller's manual review is the right path).

Concurrency with R2 (#658)

R2 adds ConsequencePath with compound_confidence, weakest_link_belief_id, fork_from. None of those fields appear in this PR — R3 consumes R1's surface only. When R2 lands, the slash skill's Fork-resolver prompts can be sharpened to use compound-decay scores instead of raw posterior means; that wiring is a small follow-up that doesn't churn R3's external contract.

Acceptance check

  • slash_commands/reason.md reads the structured payload, branches dispatch on verdict, and emits role-tagged subagent prompts (Task in allowed-tools).
  • SUGGESTED UPDATES section emits (belief_id, direction) triples on every call where the policy yields rows.
  • End-to-end test exercises aelf reason --json → payload contains dispatch and suggested_updates with the right shape and role labels.
  • Pure unit tests cover every verdict × impasse-kind mapping plus the ?-wins-over-+1 tie-breaker.
  • No staging-gate / discretion-grep regressions.

Test plan

  • uv run pytest tests/test_reason_dispatch.py tests/test_reason_classify.py tests/test_cli_reason_wonder.py -v → 50+ passed (R1 untouched, R3 unit + CLI tests).
  • uv run pytest tests/ --ignore=tests/bench_gate -q → 3402 passed, 30 skipped, 0 failed.
  • Discretion grep on the diff vs github/main is clean.
  • CI green on this PR.

Out of scope

  • -1 SUGGESTED UPDATES rows (depends on R2 fork-path data).
  • Updating Fork-resolver prompts to use R2's compound-decay scores (small follow-up after R2 lands).
  • Auto-piping +1 rows into aelf feedback (the slash skill surfaces the rows; the caller / operator decides whether to apply them).

@robotrocketscience robotrocketscience added the author-Leibniz PR authored by Leibniz session (don't self-review) label May 11, 2026
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@robotrocketscience has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 29 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9ea8f293-1f04-4e0c-ac44-645ec6dad337

📥 Commits

Reviewing files that changed from the base of the PR and between 0c619f0 and 03c7d23.

⛔ Files ignored due to path filters (1)
  • CHANGELOG.md is excluded by !**/CHANGELOG.md
📒 Files selected for processing (5)
  • src/aelfrice/cli.py
  • src/aelfrice/reason.py
  • src/aelfrice/slash_commands/reason.md
  • tests/test_cli_reason_wonder.py
  • tests/test_reason_dispatch.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-659-r3-verdict-dispatch

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 and usage tips.

@sourcery-ai sourcery-ai 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.

Sorry @robotrocketscience, you have reached your weekly rate limit of 2500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

  • 480 changed lines (limit: 200)
  • 6 changed files (limit: 3)

Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated attn:merge-conflict cycles (see #602). When practical, split into smaller PRs that each touch a focused surface.

This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the size:override label and this comment will be removed on the next push.

Comment thread tests/test_reason_dispatch.py Fixed
Comment thread tests/test_reason_dispatch.py Fixed
Pure-derivation helpers consumed by /aelf:reason's slash skill:
dispatch_policy(verdict, impasses) maps each impasse to a typed
DispatchItem (Verifier / Gap-filler / Fork-resolver), and
suggested_updates(verdict, impasses, hops) emits the (belief_id,
direction) rows for the SUGGESTED UPDATES section. Direction is
+1 (confident hop on chain), ? (impasse locus), or -1 (deferred
until R2 ships fork-path data; never emitted by this surface).
…R3)

Augments the --json payload from R1 with two additive top-level
keys: `dispatch` (one row per impasse, role-tagged Verifier /
Gap-filler / Fork-resolver) and `suggested_updates` (belief_id,
direction, note rows). The slash skill consumes both from one
payload instead of re-running the policy off-band.
… R3)

Expand the slash markdown from a verbatim CLI wrapper to a
three-step dispatch script: invoke `aelf reason --json`, fan out
one Task subagent per `payload.dispatch[i]` with role-tagged
prompt scaffolds (Verifier / Gap-filler / Fork-resolver), then
emit SUGGESTED UPDATES from `payload.suggested_updates`. Adds
Task to allowed-tools. The Python side (R3 commit 1+2) owns the
contract; the markdown only describes how the host agent acts.
…R3)

- Drop unused 'import pytest' (CodeQL py/unused-import note).
- Iterate list(Verdict) instead of Verdict directly so CodeQL's
  py/non-iterable-in-for-loop heuristic doesn't trip on the
  str-Enum subclass. Behavior is unchanged.
@robotrocketscience
robotrocketscience force-pushed the feat/issue-659-r3-verdict-dispatch branch from 2d8c9ba to 03c7d23 Compare May 11, 2026 18:36
@robotrocketscience

Copy link
Copy Markdown
Owner Author

Rebased on github/main (was BEHIND) and pushed one new commit on top:

  • test(reason): silence CodeQL false positive on Verdict iteration (#659 R3) — drops the unused import pytest (CodeQL py/unused-import note) and changes for v in Verdict: to for v in list(Verdict): so CodeQL's py/non-iterable-in-for-loop heuristic doesn't trip on the (str, Enum) subclass. Behavior unchanged; tests/test_reason_dispatch.py still 14/14 locally.

Pytest failures on the prior run were test_append_telemetry_ring_cap_evicts_oldest (both hook_search_tool + user_prompt_submit variants) timing out at 5s. That looks unrelated to R3 and matches the known telemetry-test flake pattern. Watching the re-run; will reopen if it reproduces against current main.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:pascal:2026-05-11T18:44:53Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

LGTM — implements #659 R3 cleanly. Four signed atomic commits, all CI green, FF-clean against current github/main, discretion-clean (subagent/Task are established public-surface terms in onboard.md already on main).

Acceptance (vs #659):

  • ✅ Pure derivation helpers in reason.py: dispatch_policy (Verdict → list[DispatchItem]) and suggested_updates ((Verdict, impasses, hops) → list[SuggestedUpdate]).
  • ✅ Role mapping deterministic: CONSTRAINT_FAILURE → Verifier, GAP|NO_CHANGE → Gap-filler, TIE → Fork-resolver. SUFFICIENT short-circuits to empty.
  • --json additive top-level keys dispatch + suggested_updates; existing payload shape preserved (R1/R2 untouched in this PR).
  • ✅ Slash skill three-step contract: present chain → fan out one Task subagent per dispatch[i] → emit SUGGESTED UPDATES.
  • ✅ Direction rules: +1 confident+non-impasse, ? impasse locus, -1 reserved for R2. ?-wins-over-+1 tiebreaker pinned in test_belief_on_chain_and_impasse_resolves_to_question_mark.

Design observations:

  • Role is a function of impasse kind, not verdict — so a PARTIAL verdict with both CONSTRAINT_FAILURE and GAP dispatches one Verifier and one Gap-filler. Matches the PR-body table.
  • Determinism preserved: dispatch order = impasse list order (which is itself sorted per classify's contract). test_dispatch_order_matches_impasse_order pins it.
  • The _is_low_evidence gate on +1 rows correctly uses CONFIDENT_TRIALS_MIN from existing reason.py — no new threshold introduced. Aligns with R1.
  • Slash-skill prompts are written so subagents propose aelf commands rather than running them — leaves the operator in control of the feedback loop, matching the "caller decides" pattern.

R2 interaction (#658): R3 correctly avoids consuming ConsequencePath fields — when R2 lands, Fork-resolver prompts can be sharpened to use compound_confidence + weakest_link_belief_id, but that's a follow-up that doesn't churn R3's contract. Good seam.

Test coverage: 14 unit tests (every verdict × impasse mapping, dedup, low-evidence gate, ?-wins, never--1-in-R3) + 1 CLI end-to-end test verifying the JSON payload shape. CodeQL silence (list(Verdict)) is a minor false-positive workaround, well-justified.

FF against main — adding attn:review + ready-to-merge so merge-train picks it up.

@robotrocketscience robotrocketscience added attn:review Needs review (PR open, awaiting reviewer) ready-to-merge Trigger merge-train: FF main to this PR's head labels May 11, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:pascal:2026-05-11T18:46:14Z]

@github-actions
github-actions Bot merged commit 03c7d23 into main May 11, 2026
28 of 30 checks passed
@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label May 11, 2026
@github-actions

Copy link
Copy Markdown

merge-train: merged 03c7d23main via FF push.

@robotrocketscience
robotrocketscience deleted the feat/issue-659-r3-verdict-dispatch branch May 14, 2026 04:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

attn:review Needs review (PR open, awaiting reviewer) author-Leibniz PR authored by Leibniz session (don't self-review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(slash): R3 — VERDICT-driven dispatch + feedback close-the-loop (#645 sub-task)

2 participants