Ticket graphs

Every ticket as the root of its provenance graph — goal, patch, approval, runs and (as the ticket-class build lands) mandate, manifestations, guarantees, journey, artifacts and children. Rendered through the <<<TicketGraphList>>> tag.

← all ticket-graphs

DONE#468 congruency

THE COUPLER: senior emits signatures (lower_functions = the mock senior), junior fills ONE body at a time, critic gates AST-equivalence. Reuses code_wrapper.py's lower/critic/judge/render. Python only (89% of unique forge code). Includes the blank-and-refill benchmark.

goal

Build the COUPLER: the loop that sits between codebean and the coding agent. The senior hands the junior ONE function signature; the junior returns ONLY that function's body; codebean holds the skeleton and the machine applies the decorators. The LLM never sees a path, a tool, or a repo. WHY THIS SHAPE (operator's design, do not re-litigate it): * A worker told "rewrite this module" returns 1791 lines for a 65-line decoy. A worker told "here is `def call_tool(name, args):` — return the body" CANNOT. The skeleton is not the LLM's to invent, so size, paths, tool schemas and cross-module wandering all become UNREPRESENTABLE rather than merely discouraged. * MEASURED failure modes this removes (from the GLM canaries #414/#416): wandered into an unrelated module for ~18 steps; wrote to a plausible-but-wrong nested path; emitted Bash `command` as a list; needed ~33 steps vs Claude's ~12. WHAT ALREADY EXISTS — REUSE IT, BUILD NOTHING NEW: codebean_mcp/prototype/code_wrapper.py already provides the whole spine: lower_functions(src_path) -> list[Function] # source -> model (names, args, docstrings) lower_node(node) -> Function _ast_norm(src) # the SAME identity the ticketer uses make_candidates(fn) -> list[Candidate] # per-function candidates (best-of-N) critic(cand, original_src) -> bool # _ast_norm(rendered) == _ast_norm(original) judge(cand) -> (bool, [reasons]) # decorator present, imports, ... render(instrument=True|False) # born-instrumented -> frozen/mature The codebean MCP surface is the contract: new_module / add_class / add_function / set_body / render / save. add_function IS the senior's output; set_body IS the junior's. DELIVERABLE: checkouts/current/congruency/tools/coupler.py — standalone; imports code_wrapper. 1. MOCK SENIOR (no LLM). `lower_functions(original)` IS a deterministic senior for the refactor case: it yields every signature + docstring mechanically. The ticket's embedded ===ORIGINAL SOURCE=== is the input. No senior agent is built here. 2. STRUCTURE FIRST. Lay down EVERY signature via add_function BEFORE any body is filled, so a body may call any sibling regardless of fill order. No topological sort needed. 3. THE JUNIOR LOOP — one function at a time. Hand the junior EXACTLY: its own signature + docstring, the sibling signatures (names and args ONLY, no bodies), the module's imports and constants. Nothing else. No filesystem, no tools, no repo. It returns ONLY the body text. (Signature alone is not enough: a body calls siblings and imports; without them it hallucinates helpers. This is the minimum viable context and it is still text-in/text-out.) 4. THE GATE, per function, cheapest first: parse -> then `critic` (AST-normalized equivalence to the original body) for PRESERVE functions. critic is a MECHANICAL PROOF of behaviour preservation — not an auditor's prose opinion. 5. RETRY + ESCALATE PER FUNCTION, not per module. Gate fails -> re-prompt that ONE function with the exact error (SyntaxError line N / the _ast_norm mismatch). Fails N times -> escalate THAT ONE BLANK to a stronger model. A bad body costs one re-prompt, never a module. 6. THE BLANK-AND-REFILL BENCHMARK (`--benchmark`) — this is how the tool proves itself. Take a real module, BLANK each body, have the junior refill it from signature+docstring, and compare against the original you kept. Report per function: `_ast_norm`-equal (reproduced it) AND parses/passes (works). Ground truth is free; 262 forge repos of real code are the corpus. This answers #173 ("benchmark on ACTUAL agent tasks, not generic evals") with numbers instead of vibes. 7. --engine passthrough so the junior can be claude | cerebras (GLM 4.7) | ollama via the existing ollama_agent.py engines. Default claude. 8. --selftest on a THROWAWAY synthetic module: structure-first (all signatures land before bodies), a body that fails parse is re-prompted, a PRESERVE function that differs semantically FAILS critic, one that differs only in whitespace/comments PASSES, and escalation fires after N. No network, no live db, no writes outside the temp dir. SCOPE: PYTHON ONLY. Measured: by unique blob the forge is 89% Python / 6% PHP (5,119 of 5,736 unique code files). codebean is ast-based and cannot lower PHP. A PHP bean is a separate ticket (357 unique files, 0 parse failures, 0 generators, 0 by-ref params — 99.8% wrapper-safe; the rename+wrapper keeps critic intact and was PROVEN against the shipped php binary). ACCEPTANCE: --selftest green; `--benchmark` on a real forge module prints per-function reproduced/ works rates; a PRESERVE run on a module yields code that passes critic against the original; the junior is never handed a path or a tool (assert it — the prompt contains only signature/docstring/ sibling-signatures/imports). RUN ON CLAUDE.

patch

none

approval

unapproved

runs

no runs recorded

mandate (clauses)

not yet recorded — lands with the ticket-class build

manifestations

not yet recorded — lands with the ticket-class build

guarantees

not yet recorded — lands with the ticket-class build

journey (blunders & successes)

not yet recorded — lands with the ticket-class build

artifacts (forge)

checkouts/current/congruency/tools/coupler.py:56952fa270 forge-source · forge 9556ae09c171
checkouts/current/congruency/tools/coupler.py:0fe0b9b1ca forge-source · forge 030dc44d1794
checkouts/current/congruency/tools/coupler.py:e216343d6a forge-source · forge 5f2c24066b20

source (forge tree)

browse congruency source (forge-parked tree)

children

not yet recorded — lands with the ticket-class build