feat(decision-parser): port extractDecisionBlock from mini-agent (parser half of #452)#14
Open
kuro-agent wants to merge 2 commits into
Open
Conversation
agent-middleware spawns Claude Code as subprocess but never parses
`## Decision` blocks from its output, so falsifiers from middleware-
driven Kuro cycles silently no-op (38+ cycles since 2026-05-07
isolation cutover). Diagnosis: mini-agent#452 comment by kuro-agent.
This commit ships the parser half only:
- `extractDecisionBlock(response)` — soft extractor for Decision header
- `synthesizeDecisionFromProse(response)` — fallback when header absent
- 12 unit tests (canonical / bold-bullet / ttl-clamp / next-section /
h3 header / synthesis safety / falsifier+ttl extraction)
- Logic ported verbatim from mini-agent/src/dispatcher.ts:958-1060
to keep behavioural parity; falsifier patterns match end-to-end.
Wiring into sdk-provider.ts:148 (where final `result` is captured) and
ledger persistence (writeCommitment + counterparty=self schema) are
intentionally separated — the agent-middleware ledger surface is delegate-
ack only today, and porting writer needs a design call (Option A: reuse
mini-agent-memory/memory/state/commitments.jsonl path; Option B: define
middleware-local ledger with header rendering update).
Falsifier for next step:
grep:/Users/user/Workspace/agent-middleware/src/sdk-provider.ts \
"extractDecisionBlock" >=1
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The ported FIELD_TAIL regex used `\s*:\s*(.+)$` after the field label. `\s` matches `\n`, so when a field had an empty value (e.g. `chose: `), the regex spanned across the newline and captured the *next* field's content as the value — making `extractDecisionBlock` silently mis-attribute the falsifier line as the chose value. Discovered by adding 4 edge-case tests; one (`empty chose value`) failed with `actual='falsifier: file_exists:/a'` instead of `undefined`. Fix: replace `\s*:\s*` with `\s*:[ \t]*` in all 4 field regexes (serving/chose/falsifier-or-falsify/ttl × extract+synth). Same bug shape exists in mini-agent/src/dispatcher.ts:958-1060 — file follow-up. Tests: 16/16 pass. tsc --noEmit clean. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Author
Stale "Out of scope" claim — parity follow-up already doneThe PR body says:
This is stale. The mini-agent fix already shipped:
So:
No code change needed on this PR. Flagging so the merge reviewer doesn't chase a non-existent todo. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports
extractDecisionBlock+synthesizeDecisionFromProsefrommini-agent/src/dispatcher.ts:958-1060into agent-middleware (src/decision-parser.ts) so middleware-driven Kuro instances can have their## Decisionblocks parsed at the place final results are captured.Context: middleware spawns Claude Code as subprocess but never parses Decision blocks from its output. Combined with miles990/mini-agent#452 (writeCommitment gate misses middleware-driven instances), this is why 40+ Kuro cycles since 2026-05-07 have written falsifiers that silently no-op.
This PR is the parser half only. Wiring into
sdk-provider.ts:148(where finalresultis captured) and ledger persistence (writeCommitment+counterparty=selfschema) are intentionally separated — those need a design call:mini-agent-memory/memory/state/commitments.jsonl(shared ledger)Shipping the parser first lets us verify behavioural parity with mini-agent before committing to a wiring choice.
Changes
src/decision-parser.ts(new, 84 lines) —extractDecisionBlock(response),synthesizeDecisionFromProse(response), regex-bounded field parsingtests/decision-parser.test.ts(new, 132 lines) — 16 cases: canonical/bold-bullet/ttl-clamp/next-section/h3 header/synthesis safety/falsifier+ttl extraction + 4 edge cases (CRLF, empty input, truncated stream, malformed empty value)Includes a follow-up fix (
da96fe7) for a regex bug discovered by the new edge-case tests:\s*:\s*was matching\nand silently capturing the next field's content as the value when a field had an empty value (e.g.chose:). Fix:\s*:[ \t]*in all 4 field regexes. Same bug shape exists inmini-agent/src/dispatcher.ts:958-1060— file follow-up there too.Verification
$ npx tsx --test tests/decision-parser.test.ts ℹ tests 16 ℹ suites 3 ℹ pass 16 ℹ fail 0 ℹ duration_ms 152.724 $ pnpm typecheck # clean (tsc --noEmit, 0 errors)Behavioural parity with mini-agent's parser confirmed by porting the canonical/bold-bullet/ttl-clamp/h3-header test cases verbatim and getting identical extraction results.
Falsifier (auto-graded)
After this merges + a follow-up wiring PR:
grep:/Users/user/Workspace/agent-middleware/src/sdk-provider.ts "extractDecisionBlock" >=1Out of scope (explicit non-goals)
sdk-provider.ts(separate PR after design call)commitments.jsonlschema (separate PR; depends on Option A vs B)\s*:\s*regex bug in mini-agent's dispatcher (file follow-up there)🤖 Generated with Claude Code