From 68d0b1abf238d0526189c90a23572b1d6e1a43ce Mon Sep 17 00:00:00 2001 From: kmajdoub Date: Thu, 28 May 2026 20:26:00 +0200 Subject: [PATCH] docs(manifesto): add no-stringly-typed-discriminators rule (feedback loop) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .forge/quality-manifesto.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.forge/quality-manifesto.md b/.forge/quality-manifesto.md index f533a06..0cbfa39 100644 --- a/.forge/quality-manifesto.md +++ b/.forge/quality-manifesto.md @@ -65,6 +65,38 @@ SDK, #105 GhClient). (#128), eat structured errors, and make tests painful (you end up mocking argv strings instead of method calls). +## Rule: No stringly-typed cross-module event boundaries + +**Rule.** If module A emits an event consumed by module B, the event KIND +(or state name, or outcome label, or any cross-module discriminator) must +be an enum (e.g. ``class FooKind(str, Enum)``) imported from a shared +module. String literal comparisons on dynamic dict values are sev1. + +**Rationale.** A 4-PR train of bugs in this codebase had the SAME shape: +a discriminator was a string literal on one side and a different string +literal on the other side, the type checker had nothing to say, a typo +silently broke production. + +- ``#147`` — ``_critic_sdk`` checked ``event["type"] == "result"`` but + ``_worker_sdk`` emits ``event["kind"] == "final_result"``. Two- + field-name mismatch. Symptom: brainstormer/critic/PO returned empty + output for an unknown duration. +- ``#128`` — iteration state ``"PUSHED_NO_PR"`` returned via a + fallthrough default instead of an explicit edge. +- ``#120`` — PR state ``"CLOSED"`` not handled because the dispatcher + matched ``"MERGED"`` only. +- ``#97`` — worker outcome ``"failed"`` vs ``"no_pr"`` distinguished by + substring matching. + +Any of these would have been a 1-line type error with proper enums. + +**How to apply.** When adding a new event KIND, state name, outcome +label, brief KIND, severity, or any cross-module discriminator: declare +it as a ``str`` Enum in a shared module; import the enum on both sides; +compare with ``is`` not ``==``. The critic flags string-literal +comparisons of dynamic dict values as sev1 — fix by extracting the +discriminator into an enum + updating both call sites in the same PR. + ## How to apply this manifesto * When reviewing a forge-loop PR, scan the diff for each rule. A