Skip to content

hot-fix(critic-sdk): event capture field-name mismatch broke brainstormer#147

Merged
hadamrd merged 1 commit into
trunkfrom
fix/critic-sdk-event-capture-mismatch
May 28, 2026
Merged

hot-fix(critic-sdk): event capture field-name mismatch broke brainstormer#147
hadamrd merged 1 commit into
trunkfrom
fix/critic-sdk-event-capture-mismatch

Conversation

@hadamrd
Copy link
Copy Markdown
Owner

@hadamrd hadamrd commented May 28, 2026

Dogfood-caught running forge-loop brainstorm against Titan: every invocation returned empty SDK output. The capture looked for event['type'] == 'result' but worker SDK emits event['kind'] == 'final_result'. Two-field-name mismatch. Same bug had been silently breaking critic + PO SDK paths since #103/#123. Three regression tests pin the canonical + fallback + legacy paths.

…rmer

Dogfood-caught running ``forge-loop brainstorm`` against Titan:
``empty SDK output`` on every invocation. Root cause: a two-field
mismatch between _critic_sdk and _worker_sdk.

forge_loop._worker_sdk emits events with key ``kind`` (NOT ``type``)
and the session-end kind is ``final_result`` (NOT ``result``).
_critic_sdk's on_event capture looked for ``event["type"] == "result"``
and silently never matched, so every assistant text was discarded.

Also, the SDKRunResult fallback at ``getattr(result, "last_message", "")``
returned "" because the field is actually ``final_result_text``.

Result: critic, PO, and brainstormer SDK paths ALL returned empty
``last_message`` in production — which previously surfaced as a parse
error after the empty text. The bundled CLI is healthy (verified with a
direct call); only the field-name plumbing was broken.

Fix:
- _critic_sdk._capture now reads ``event["kind"]`` first, falls back to
  ``event["type"]`` for forward/backward compat. Matches kind
  ``final_result`` (canonical) and ``assistant_text``.
- The result fallback now checks ``final_result_text`` first, then
  legacy ``last_message``.

Tests: tests/test_critic_sdk_event_capture.py — 3 new
- Headline regression pin: ``event["kind"] == "final_result"``
  populates ``result.last_message``.
- Fallback: ``final_result_text`` on the SDKRunResult is the second
  source of truth.
- Defensive: legacy ``event["type"]`` field still tolerated.

Full suite: 927 passed (was 783; net +144 from loop's recent work + 3 from me).
@hadamrd hadamrd merged commit 22fd8b0 into trunk May 28, 2026
2 checks passed
@hadamrd hadamrd deleted the fix/critic-sdk-event-capture-mismatch branch May 28, 2026 18:21
hadamrd added a commit that referenced this pull request May 28, 2026
…loop) (#149)

Closes the feedback loop the CTO described: every bug we fix becomes a
permanent gate. Today's PR #147 (critic SDK event-capture mismatch)
exposed a 4-PR train of bugs with the same shape — #97, #120, #128,
#147 — all driven by string-literal discriminators that didn't match
across module boundaries.

The critic (PR #141) reads the quality manifesto + flags sev1
violations. This rule + the critic infrastructure together mean the
next worker that writes ``event["type"] == "result"`` (or similar
cross-module string-comparison) gets the PR auto-blocked with the
manifesto rationale.
hadamrd added a commit that referenced this pull request May 28, 2026
Adds the customer-facing documentation for the manifestos + brainstormer
feature that closed the cosmetic-tickets gap. Real customers consuming
this OSS need to know:

1. The four files they own (.forge/product-vision.md, axes.yaml,
   quality-manifesto.md, testing-manifesto.md).
2. The brainstormer dry-run + --apply workflow.
3. The feedback loop (`forge-loop manifesto suggest --from-pr <N>`)
   where every bug becomes a permanent gate.
4. What the worker + critic see (manifestos injected into briefs;
   sev1 violations block auto-merge).

README: new section "Manifestos & the brainstormer (axis-aligned
tickets)" between Briefs and CLI reference. CLI reference table gains
`brainstorm`, `brainstorm --apply`, `manifesto suggest --from-pr`.

GUIDE: new section 4 "Manifestos: drive what gets built (not just how)"
between "discipline matters" and "the brief is your contract" — with
the real Titan brainstormer output as the worked example. Sections
5-10 renumbered accordingly.

Both docs cite PR #147 as the canonical feedback-loop example: a
stringly-typed event-boundary bug that surfaced after #97/#120/#128
all had the same shape; the fix landed the manifesto rule that the
critic now enforces.
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