Skip to content

feat: RE-3/RE-4 — Anthropic REST backend + emission fixes - #384

Merged
briansrls merged 1 commit into
mainfrom
dark-koi-448
Apr 11, 2026
Merged

briansrls merged 1 commit into
mainfrom
dark-koi-448

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Summary

  • review.dag now calls the Anthropic Messages API directly via REST instead of shelling out to CLI tools. Default backend is anthropic with claude-sonnet-4-6.
  • anthropic.dag wired with auth_source: EnvVar { name: "ANTHROPIC_API_KEY" }, anthropic-version header, and service config endpoint.
  • 7 emitter fixes in 05_emit_rust.dag:
    • self.auth_token.clone() for Header auth with auth_source
    • Dry-run mock tuple extraction for multi-field outputs
    • Optional field Some() wrapping in mock extraction
    • Service config endpoint resolution (was always localhost)
    • User-Agent header on REST client (Client::builder().user_agent())
    • #[serde(rename)] for reserved keyword fields (ref, type)
    • response.text() for String response types (was response.json())

Live test status

Compiles, builds, dry-runs. Live execution reaches GitHub API (Get + ListReviews deserialize). Remaining: trace which later operation fails deserialization.

Test plan

  • 388/388 non-ignored tests pass
  • Clippy clean
  • Stage0 freshness verified (fixed point)
  • L1 ratchet holds (0)
  • Diagnostic ratchet holds (424)
  • review_dag_compiles_to_rust — 0 cargo check errors
  • review_dag_builds_and_runs_dry_run — passes
  • 3 new RE-4 Anthropic emission tests pass

🤖 Generated with Claude Code

@briansrls

briansrls commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

I reviewed the PR diff.

Overall: good RE-3/RE-4 progress, but I would not merge this as-is. I see two blocking issues and a few medium concerns.

  1. Blocking — extdeps modeling regresses in dsl/extdeps/github/github.dag.

In diff lines 5–12, GitHubUser.type is widened from UserType to raw String to work around serde rename gaps. That is the wrong layer. The modeling guide says closed sets should be enums, not strings, and extdeps models should stay structurally faithful rather than absorb emitter limitations. The invariants also say to fix missing facts at the source/serialization boundary, not by degrading the model. This change moves opposite to the current roadmap direction for extdeps fidelity. I would keep a coproduct here and fix serde/emit rename support instead. chatgpt-review-54575df2-e175-45…

chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-3c5f1390-4fe5-47…

  1. Blocking — the new dry-run tuple branch is broader than its own comment.

In emit_dry_run_branch_from_props (diff lines 228–268), the comment says tuple extraction is only for anonymous conjunctions, but the actual condition is only inferred.connective == Conj && children > 1. There is no anonymity / named-struct guard. So if a named multi-field conjunction reaches this path, dry-run can emit Ok((...)) instead of deserializing the named struct, diverging from the real path. That is exactly the kind of emit-side re-derivation the invariants say to avoid. At minimum, add the missing guard; better, carry the materialization shape structurally into emit. chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-c5aa3312-d5b5-4f…

  1. Medium — Anthropic API versioning is now duplicated.

The PR hardcodes headers: { "anthropic-version": "2023-06-01" } in anthropic.dag (diff lines 37–42), but the existing extdeps model already has current_api_version { version: "2023-06-01", header: "anthropic-version" } in extdeps.dag. That creates two authorities for one fact. The invariants/modeling guidance both push toward single-authority data, not copied literals. chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-54575df2-e175-45…

  1. Medium — the new string-response path is still an emitter heuristic.

In diff lines 294–308, emit_response_arm chooses .text() vs .json() by checking rn.name and then falling back to resp_value.name. That helps the direct 200 => String case, but it is still name-based emit logic. Aliases/wrappers are still at risk whenever the resolved type is not fully surfaced. The invariants explicitly say emission should not branch on type names and missing facts should be pushed upstream or into LanguageSpec. chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-c5aa3312-d5b5-4f…

  1. Low — the RE-2 acceptance test lost one observable assertion.

In review_dag_builds_and_runs_dry_run, after changing the output check from object-shaped JSON to a 2-element array, the test still checks length and reviewed == true, but it no longer verifies the second element is the comment URL. So a broken/missing second return value would now pass. chatgpt-review-c5aa3312-d5b5-4f…

A couple of modeling notes on the .dag changes:

  • Good: auth_source: EnvVar { name: "ANTHROPIC_API_KEY" } is the right layer and matches the pattern already documented in std-library.dag.
  • Mixed: moving review.dag from shell CLI invocation to llm.Anthropic.Messages is directionally right for RE-3/RE-4, but the call site still sends messages as a raw JSON string and puts the real task payload into system. That is workable under the current service signature, but it does not improve the underlying message model and keeps this path weakly structured. The roadmap still calls out richer structural LLM content modeling as open debt. chatgpt-review-3c5f1390-4fe5-47…

On roadmap alignment:

  • ROADMAP.md is not modified, so there is no roadmap drift in the patch itself.
  • Directionally, this is clearly Track 5 work. But I would not advance statuses from this PR alone. The roadmap still has RE-3 as partial and RE-4 short of end-to-end, and the invariants’ Tier 3 bar is live-service integration, not just emission/dry-run/source-audit coverage. chatgpt-review-3c5f1390-4fe5-47…

chatgpt-review-c5aa3312-d5b5-4f…

My merge bar here would be: fix #1 and #2 before merge, and ideally clean up #3 in the same PR since it is a straightforward single-authority correction.


View conversation

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 82ef1e310f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread dsl/extdeps/github/github.dag Outdated
type: UserType
// Raw string: GitHub returns "User", "Bot", "Organization".
// Enum deserialization needs serde variant renames (future emitter work).
type: String

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep GitHubUser.type aligned with pull mock payload shape

Changing GitHubUser.type to String makes the current PR mock responses invalid, because extdeps/github/pulls.dag still emits type: { _variant: "HumanUser" } (e.g., in List and Get mocks). In dry-run mode those mocks are deserialized via serde_json::from_value(...), so any path that materializes PullRequest.user.type will now fail at runtime with a map-vs-string type error instead of returning mock data.

Useful? React with 👍 / 👎.

command = backend_command(backend: backend, model: model, prompt_file: prompt_file, cwd: "/Users/briansrls/gunbc")
llm_result = shell.Exec.Run(
script: "mkdir -p /Users/briansrls/.local/share/gunbc-review && printf '%s' '{prompt}' > {prompt_file} && {command}"
llm_response = llm.Anthropic.Messages(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Respect selected backend when invoking the review model

review_pr still accepts a backend parameter (and review_cycle passes it through), but this code unconditionally calls llm.Anthropic.Messages. That means callers who set backend to another value now get silently routed to Anthropic and can fail due to missing ANTHROPIC_API_KEY, which is a behavioral regression from the prior backend-switching flow.

Useful? React with 👍 / 👎.

@briansrls

briansrls commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

I reviewed the PR diff.

Overall: good RE-3/RE-4 progress, but I would not merge this as-is. I see two blocking issues and a few medium concerns.

  1. Blocking — extdeps modeling regresses in dsl/extdeps/github/github.dag.

In diff lines 5–12, GitHubUser.type is widened from UserType to raw String to work around serde rename gaps. That is the wrong layer. The modeling guide says closed sets should be enums, not strings, and extdeps models should stay structurally faithful rather than absorb emitter limitations. The invariants also say to fix missing facts at the source/serialization boundary, not by degrading the model. This change moves opposite to the current roadmap direction for extdeps fidelity. I would keep a coproduct here and fix serde/emit rename support instead. chatgpt-review-54575df2-e175-45…

chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-3c5f1390-4fe5-47…

  1. Blocking — the new dry-run tuple branch is broader than its own comment.

In emit_dry_run_branch_from_props (diff lines 228–268), the comment says tuple extraction is only for anonymous conjunctions, but the actual condition is only inferred.connective == Conj && children > 1. There is no anonymity / named-struct guard. So if a named multi-field conjunction reaches this path, dry-run can emit Ok((...)) instead of deserializing the named struct, diverging from the real path. That is exactly the kind of emit-side re-derivation the invariants say to avoid. At minimum, add the missing guard; better, carry the materialization shape structurally into emit. chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-c5aa3312-d5b5-4f…

  1. Medium — Anthropic API versioning is now duplicated.

The PR hardcodes headers: { "anthropic-version": "2023-06-01" } in anthropic.dag (diff lines 37–42), but the existing extdeps model already has current_api_version { version: "2023-06-01", header: "anthropic-version" } in extdeps.dag. That creates two authorities for one fact. The invariants/modeling guidance both push toward single-authority data, not copied literals. chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-54575df2-e175-45…

  1. Medium — the new string-response path is still an emitter heuristic.

In diff lines 294–308, emit_response_arm chooses .text() vs .json() by checking rn.name and then falling back to resp_value.name. That helps the direct 200 => String case, but it is still name-based emit logic. Aliases/wrappers are still at risk whenever the resolved type is not fully surfaced. The invariants explicitly say emission should not branch on type names and missing facts should be pushed upstream or into LanguageSpec. chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-c5aa3312-d5b5-4f…

  1. Low — the RE-2 acceptance test lost one observable assertion.

In review_dag_builds_and_runs_dry_run, after changing the output check from object-shaped JSON to a 2-element array, the test still checks length and reviewed == true, but it no longer verifies the second element is the comment URL. So a broken/missing second return value would now pass. chatgpt-review-c5aa3312-d5b5-4f…

A couple of modeling notes on the .dag changes:

  • Good: auth_source: EnvVar { name: "ANTHROPIC_API_KEY" } is the right layer and matches the pattern already documented in std-library.dag.
  • Mixed: moving review.dag from shell CLI invocation to llm.Anthropic.Messages is directionally right for RE-3/RE-4, but the call site still sends messages as a raw JSON string and puts the real task payload into system. That is workable under the current service signature, but it does not improve the underlying message model and keeps this path weakly structured. The roadmap still calls out richer structural LLM content modeling as open debt. chatgpt-review-3c5f1390-4fe5-47…

On roadmap alignment:

  • ROADMAP.md is not modified, so there is no roadmap drift in the patch itself.
  • Directionally, this is clearly Track 5 work. But I would not advance statuses from this PR alone. The roadmap still has RE-3 as partial and RE-4 short of end-to-end, and the invariants’ Tier 3 bar is live-service integration, not just emission/dry-run/source-audit coverage. chatgpt-review-3c5f1390-4fe5-47…

chatgpt-review-c5aa3312-d5b5-4f…

My merge bar here would be: fix #1 and #2 before merge, and ideally clean up #3 in the same PR since it is a straightforward single-authority correction.


View conversation

@briansrls

briansrls commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

I reviewed the PR diff and kept the review scoped to the diff itself, using the attached docs only as authority for invariants/patterns.

Overall: good direction for RE-3/RE-4, but I would not merge this as-is. I see two clear blockers and a few medium concerns.

  1. Blocking: the new wire_contract path is internally inconsistent and too broad.

In the diff, std/serialization.dag introduces WireContract { variant_encoding: VariantEncoding } and even documents data wire_contract: WireContract = ... (diff lines 286–290). But the changed modules do not use that wrapper; they declare data wire_contract: VariantEncoding = ... directly in github.dag, anthropic.dag, llm.dag, and openai.dag (diff lines 12–13, 57, 91, 108). Then the emitter explicitly assumes the body is raw VariantEncoding rather than WireContract (diff lines 308–337). So the new std API, the module declarations, and the emitter implementation do not actually agree.

There is a second problem in the same seam: emit_module_full computes one serde_enum_tag for the whole module and emit_enum_from_children applies it to every enum in that module (diff lines 347–356 and 655–667). That is probably fine for closed string enums like UserType, Role, or StopReason, but it is too coarse for modules like Anthropic/OpenAI that also contain tagged unions such as ContentBlock, ResponseFormat, and ToolChoice. The modeling docs explicitly call out ContentBlock/ResponseFormat/ToolChoice as richer tagged unions, and Track 10 already flags stringly modeling in this area as open debt. This change is pushing a module-wide blanket rule through the emitter instead of surfacing a typed per-type fact. That cuts directly against the Root-Cause Depth invariant and the repo’s “single authority / structural dispatch” guidance. chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-b64ee721-da56-42…

chatgpt-review-7f0b5430-d295-43…

chatgpt-review-54575df2-e175-45…

  1. Blocking: the dry-run tuple branch is broader than its own comment, and the test now accepts a likely regression.

review_pr still declares a field-named return type: -> { reviewed: Bool, comment_url: String } (diff line 188). But in emit_dry_run_branch_from_props, the comment says tuple extraction is only for anonymous conjunctions, while the actual condition is just inferred.connective == Conj && inferred.children |> count > 1 (diff lines 462–468). There is no guard for “anonymous vs named/record-shaped”. Then the acceptance test is changed to expect a JSON array and even comments that review_pr returns (Bool, String) (diff lines 832–848), which is not what the DSL signature says.

That looks like structure loss in emit being papered over in tests instead of fixed at the source. The invariants explicitly warn against downstream semantic repair and fabricated fallbacks, and the modeling guide’s preferred direction for anonymous products is “name them before emission”, not “quietly collapse them into tuples/arrays”. chatgpt-review-f9af4385-6df3-43…

chatgpt-review-c5aa3312-d5b5-4f…

chatgpt-review-8e90beda-f7a3-44…

  1. Medium: review.dag is off the CLI now, but the new Anthropic call is still weakly structured.

The switch from shell/CLI to llm.Anthropic.Messages is directionally right. But the actual call sends messages as a raw JSON string literal and stuffs the real payload into one interpolated system string (diff lines 217–223). That works as a bridge, but compositionally it is not much of an improvement over the old shell boundary: the message list is still not modeled as structured content in the DAG. Track 10 already calls out LlmMessage.content: String and ThinkingConfig.type: String as fidelity debt, and the modeling notes specifically mark llm/llm.dag and llm/anthropic.dag as still too stringly in those spots. chatgpt-review-7f0b5430-d295-43…

chatgpt-review-54575df2-e175-45…

There is also a smaller smell here: anthropic.dag now adds auth_source: EnvVar { name: "ANTHROPIC_API_KEY" } (good), but still keeps auth_input: api_key, so the call site passes api_key: "" just to satisfy the signature. That suggests credential provenance is still split between two authorities instead of fully modeled in one place.

  1. Medium: Anthropic API versioning is duplicated again.

The PR hardcodes headers: { "anthropic-version": "2023-06-01" } in dsl/extdeps/llm/anthropic.dag (diff lines 73–76). The attached extdeps reference already models the same fact as current_api_version = { version: "2023-06-01", header: "anthropic-version" } in extdeps.dag. I’m not flagging the reference file itself, but I am flagging the pattern the PR introduces: two representations for one fact is exactly the drift shape the invariants warn about. chatgpt-review-b64ee721-da56-42…

  1. Medium: emit_response_arm is still deciding based on names.

The new string-response path is an improvement over blindly using .json(), but it still checks rn.name / resp_value.name to decide between .text() and .json() (diff lines 528–542). That is still emitter-side guessing from names rather than a carried structural fact. Track 4 already describes this general class as “codegen decisions scattered across emitter heuristics instead of derived from structural authorities.” chatgpt-review-7f0b5430-d295-43…

chatgpt-review-c5aa3312-d5b5-4f…

  1. Roadmap alignment: no direct issue, because ROADMAP.md is not modified.

Directionally this is clearly Track 5 work, and the choice not to edit roadmap status is the right one. The attached roadmap still says RE-2 is done, RE-3 is partial, and RE-4 is not started. The new tests here do not change that: they are ignored source-audit/emit-smoke tests, not live integration, and the invariant docs still say Tier 3 full-real integration is not yet implemented. So this PR helps Track 5, but it does not justify a status bump by itself. chatgpt-review-7f0b5430-d295-43…

chatgpt-review-f9af4385-6df3-43…

A few positives:

  • auth_source: EnvVar { name: "ANTHROPIC_API_KEY" } is the right kind of modeling move and matches the existing std.credentials service pattern in std-library.dag.
  • Renaming GitHub’s user variant to User | Bot | Organization and updating the PR mocks from { _variant: "HumanUser" } to "User" is a good wire-faithful closed-set fix.
  • The reserved-keyword serde rename in emit_struct_field_from_child looks good and addresses a real Rust edge case.

My merge bar would be: fix the wire_contract design/implementation mismatch and the dry-run named-record/tuple regression before merge. The rest I’d classify as follow-up debt unless you want this PR to claim more than “directionally correct RE-3/RE-4 progress.”


View conversation

@briansrls

briansrls commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

I reviewed the diff itself and treated src/v2/stage0/src/v2_compiler_emit_rust.rs as the generated mirror of src/v2/05_emit_rust.dag, not as a separate design surface.

I would not merge this as-is. Two issues look blocking, and a few others are meaningful follow-ups.

1. Blocker — wire_contract is modeled at the wrong granularity, and the emitter gives it the wrong semantics.

The new std.serialization model says a module can declare one wire_contract that “applies to all types within,” and StringVariant means enums serialize as plain strings. In the emitter, emit_module_full() finds a single module-level wire_contract, resolve_wire_serde_tag() collapses it to one Rust serde attribute, and that same serde_enum_tag is applied to every enum in the module. In this PR that affects, at minimum, extdeps.github.github, extdeps.llm.llm, extdeps.llm.openai, and extdeps.llm.anthropic (see the new wire_contract data declarations and the resolve_wire_serde_tag / emit_enum_from_children plumbing in the diff). The problem is that “string enum on the wire” is not a module-wide fact; it is a per-type fact, and it only cleanly describes unit enums. For data-carrying enums, the current Rust serde behavior here is not “plain string” at all.

extdeps.llm.anthropic is the clearest counterexample. That module already contains ContentBlock, which is a tagged union with payload-bearing variants. The modeling guidance explicitly calls Anthropic’s ContentBlock tagged union out as a strong, faithful model. A module-wide StringVariant { naming: SnakeCase } now cuts across that and asks the emitter to apply one serialization policy to both unit enums and data-carrying enums, which is not compositional. Compared to the existing std-library.dag pattern, where serialization lives under LanguageSpec.serialization / SerializationFramework, this PR is still encoding serialization behavior in Rust emitter logic rather than in sufficiently precise typed facts. That conflicts with the invariant that emission should translate annotated structure, not make semantic decisions, and with the modeling rules around structural, spec-grounded closed sets. INVARIANTS

chatgpt-review-ad2fe2f7-46aa-4d…

chatgpt-review-ad2fe2f7-46aa-4d…

The fix I’d want is either per-type/per-enum wire contracts, or a richer wire model that distinguishes at least:

  • unit enums that are plain strings,
  • tagged object unions,
  • any rename convention attached to those shapes.

Right now the model is too coarse, and the emitter is compensating.

2. Blocker — the new shell multi-field emission path fabricates values by field-name heuristic.

In emit_shell_return, multi-field shell outputs no longer go through structured decoding. Instead, the emitter now synthesizes a tuple by guessing:

  • “bool-like” field → output.status.success()
  • field literally named stderr → stderr
  • everything else → stdout

That happens in the new branch added around the shell-return hunk in 05_emit_rust.dag. This is only accidentally correct for shapes like shell.Exec.Run { success, stdout, stderr }. It is already dubious for shell.Which.Check { exists: Bool, path: FilePath? }, where path becomes Some(stdout.clone()) with no explicit modeling that the path comes from stdout and no trimming in this tuple path. Any future shell op with fields like code: Int, path: FilePath, message: String, or multiple non-bool channels will silently miscompile.

That is exactly the class of problem the invariants warn about: if emit needs the fact “which shell channel populates which output field,” that fact belongs upstream in the model/IR, not as name-based guessing in Rust emission. The current branch is a fabrication fallback disguised as convenience. chatgpt-review-e569fb71-e239-43…

INVARIANTS

I’d push this back into modeled transport semantics, for example with explicit field sources like from_stdout, from_stderr, from_exit_status, or by reusing a generalized extraction mechanism analogous to the REST from_key path model.

3. Medium — WireContract is dead scaffolding right now.

dsl/std/serialization.dag introduces:

dagtype WireContract {  variant_encoding: VariantEncoding}

and the file comment says modules declare:

dagdata wire_contract: WireContract = { variant_encoding: ... }

But none of the changed extdep modules do that. They all declare data wire_contract: VariantEncoding = ... directly, and resolve_wire_serde_tag() explicitly comments that the body is a VariantEncoding value directly. So the new wrapper type is not part of the live path at all; the docs/model and the implementation disagree on day one.

That is a straightforward single-authority problem. Either use WireContract end-to-end and make the emitter read wire_contract.variant_encoding, or delete it for now. Landing both shapes at once creates a stale parallel representation. The invariants explicitly call unused or lossy fact tables/speculative metadata out as violations. INVARIANTS

chatgpt-review-ad2fe2f7-46aa-4d…

4. Medium — response.text() vs response.json() is still an emitter-side heuristic.

The new emit_response_arm() checks whether the success payload “looks string-like” and then chooses response.text().await? instead of response.json().await?. That is a pragmatic fix for raw-text endpoints, but it is still the emitter inferring transport semantics from type names. It will mis-handle endpoints that legitimately return a JSON string body ("foo"), because text() will preserve the JSON quotes while json() would deserialize to foo.

I would want response-body format to be explicit in the transport/response model, not inferred in 05_emit_rust.dag. The invariant here is the same: emission should not decide semantics from target-language or type-name heuristics. INVARIANTS

5. Medium — the RE-2 CLI output contract appears to regress from named JSON to positional JSON.

In pipeline.rs, the dry-run test used to assert reviewed and comment_url fields in a JSON object. This PR changes the assertion to expect a 2-element array [true, "..."], with a comment explaining that review_pr now serializes as (Bool, String).

That may be acceptable internally, but it is a user-visible contract change. If anything consumes review-pr --dry-run programmatically, positional JSON is much more brittle than named fields. At minimum I’d call this out explicitly; ideally I’d preserve named JSON by returning a named type instead of relying on anonymous tuple serialization.

6. Testing / roadmap alignment — this is RE-4 plumbing, not RE-4 completion.

ROADMAP.md is not modified in this PR, so there is no roadmap drift to review directly. That said, the current roadmap still says Track 5 has RE-1 done, RE-2 done, RE-3 partial, and RE-4 not started. I would not change those statuses based on this diff alone. The new coverage is mostly:

  • dry-run execution,
  • emitted-source assertions (contains("ANTHROPIC_API_KEY"), contains("anthropic-version"), contains("snake_case")),
  • compile/build checks.

That is useful, but it is not Tier 3 full-real integration. The invariants’ testing model explicitly distinguishes dry-run/structural checks from full-real live-service execution, and says Tier 3 full-real integration is not yet implemented. So this PR clearly advances RE-4 infrastructure, but it does not prove “Anthropic REST API end-to-end” yet. chatgpt-review-2d3c421a-7f02-4c…

chatgpt-review-e569fb71-e239-43…

A few things I do like in the diff:

  • VariantEncoding / VariantNaming themselves are a good direction. Modeling the closed set as a sum type is better than raw strings.
  • Renaming GitHub’s user variant to User and updating the mock payload from { _variant: "HumanUser" } to "User" is more faithful to the actual wire shape.
  • Preferring service_config_endpoint() over transport fallback in emit_service_new_method() is a good single-authority move.
  • The reserved-word serde(rename = "...") fix for Rust field names is a good interop patch.

The smallest acceptable merge shape for me would be:

  • fix the shell fabrication,
  • either delete WireContract or use it end-to-end,
  • and narrow wire_contract so it cannot silently misdescribe data-carrying enums.

Everything else I’d mark as follow-up rather than hard stop.


View conversation

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codex · gpt-5.4 · b576a97e

Review (INVARIANTS: 4, MODELING: 5+/5-, ROADMAP: 1✓/2!)

INVARIANTS — Violations (4)

ROOT CAUSE ANALYSIS

  • dsl/std/serialization.dag Missing upstream wire-shape authority (M1/M3/M7/M9): std.serialization introduces both WireContract and a direct VariantEncoding path, then the emitter collapses the surviving module-level fact to one string attribute for all enums; replace that with one structural wire-form authority on each relevant type or request/response shape, such as unit-string enum vs tagged object union, and have the emitter read that plus LanguageSpec.serialization mechanically.
  • src/v2/05_emit_rust.dag Missing upstream response-body-format fact (M1/M3/M7): service responses currently say only status-to-type, so emit falls back to a type-name heuristic to decide transport decoding; add an explicit response body kind in the service model, for example raw text versus JSON-decoded value, so emission becomes a lookup instead of a branch on string-ness.
  • src/v2/05_emit_rust.dag Missing upstream shell extraction model (M1/M5/M8): shell outputs do not carry structural facts about which field comes from stdout, stderr, or exit status, so emit guesses by name and coproduct shape; introduce typed shell field sources, analogous to REST from_key extraction, so the graph says from_stdout or from_exit_status and the emitter only renders declared structure.

MODELING — Strengths

  • dsl/gunbc/tools/review.dag Replacing the CLI shell-out with a direct import of extdeps.llm.anthropic is the right L5 tool composition over an L3 provider service.
  • dsl/extdeps/github/github.dag UserType = User | Bot | Organization is a more faithful closed set than the invented HumanUser label.
  • dsl/extdeps/llm/llm.dag Role = System | User | Assistant | Tool is a clean shared-provider L2 closed set and composes better than provider-prefixed variants.
  • dsl/extdeps/llm/anthropic.dag auth_source: EnvVar plus the explicit anthropic-version header captures real API contract facts at the provider-specific service layer.
  • dsl/std/serialization.dag VariantNaming and VariantEncoding use closed coproducts instead of string flags, which is the right M1/M4 shape for wire-format facts.

MODELING — Improvements

  • dsl/gunbc/tools/review.dag The Anthropic request is still assembled as raw JSON and prompt strings; compose it from shared message/content types so backend behavior stays structural rather than stringly (M1/M8, Track 10).
  • dsl/extdeps/github/github.dag Keep wire-shape facts on UserType itself instead of a module-wide wire_contract, because the same file also defines other enums with different wire meanings (M3/M4).
  • dsl/extdeps/llm/llm.dag If this file owns provider-shared wire facts, LlmMessage should also move toward provider-shared content blocks rather than String; otherwise keep serialization contracts in L3 provider modules (M1/M3, Track 10).
  • dsl/extdeps/llm/anthropic.dag A module-wide SnakeCase enum contract is not faithful for mixed Anthropic types such as ContentBlock and AnthropicModel; model each wire form on the relevant type or request/response boundary instead (M1/M3).
  • dsl/std/serialization.dag Either make WireContract the single consumed authority or delete it; as added here it is parallel scaffolding at the wrong granularity (M2/M7/M9).

ROADMAP — Verified

  • RE-2: review.dag compiles (dry-run): The diff still supports RE-2 by building and dry-running review-pr with an Anthropic env placeholder in the pipeline test.

ROADMAP — Incomplete

  • RE-3: review.dag passes live integration: The runtime path changed substantially, but the diff adds no non-ignored full-real review-cycle test proving the tool completes against live services.
  • RE-4: Anthropic REST API end-to-end: The added RE-4 tests are ignored compile/emission checks and dry-run never exercises the live Anthropic API, so end-to-end integration is still not demonstrated.

ROADMAP — Unclaimed

  • RE-4: Anthropic REST API end-to-end: This PR clearly starts RE-4 by adding Anthropic auth, endpoint/header plumbing, and targeted emission coverage, but ROADMAP.md still says Not started.

The PR makes real RE-4 progress and includes some strong modeling fixes, but the new wire-contract and emitter heuristics still violate core single-authority and no-fabrication invariants.

Comment thread src/v2/05_emit_rust.dag
// TaggedVariant → #[serde(tag = "_variant")] (default)
fn resolve_wire_serde_tag(wire_item: Node) -> String {
// wire_item body is a VariantEncoding value directly (StringVariant or TaggedVariant)
match wire_item.body {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: resolve_wire_serde_tag reads wire_contract as bare VariantEncoding, so the new WireContract boundary never becomes authority, violating the single-authority metadata and end-to-end semantic-boundary invariants.

Comment thread src/v2/05_emit_rust.dag Outdated
// Resolve module-level wire contract for enum serde attributes.
// Modules with wire_contract: WireContract get StringVariant or TaggedVariant;
// modules without get the default _variant tagging.
let serde_enum_tag = match typed_module.items |> filter(i => i.name == "wire_contract") |> first {

This comment was marked as resolved.

Comment thread src/v2/05_emit_rust.dag Outdated
let resp_value = field_init_node_value(n: prop)
// Response type reference may not be fully resolved — check both
// inferred type and the source-level name via coercion data.
let is_string_response = match resp_value.inferred {

This comment was marked as resolved.

Comment thread src/v2/05_emit_rust.dag Outdated
concat("let parsed: serde_json::Value = serde_json::from_str(&stdout)?;\nOk(serde_json::from_value(parsed)?)")
// Shell multi-field: construct tuple from process output channels.
// Map each field by name: success→exit status, stdout→stdout, stderr→stderr.
let field_exprs = effective.children |> map(ch =>

This comment was marked as resolved.

@briansrls

Copy link
Copy Markdown
Contributor Author

codex · gpt-5.4 · b576a97e

Review (INVARIANTS: 4, MODELING: 5+/5-, ROADMAP: 1✓/2!)

INVARIANTS — Violations (4)

ROOT CAUSE ANALYSIS

  • dsl/std/serialization.dag Missing upstream wire-shape authority (M1/M3/M7/M9): std.serialization introduces both WireContract and a direct VariantEncoding path, then the emitter collapses the surviving module-level fact to one string attribute for all enums; replace that with one structural wire-form authority on each relevant type or request/response shape, such as unit-string enum vs tagged object union, and have the emitter read that plus LanguageSpec.serialization mechanically.
  • src/v2/05_emit_rust.dag Missing upstream response-body-format fact (M1/M3/M7): service responses currently say only status-to-type, so emit falls back to a type-name heuristic to decide transport decoding; add an explicit response body kind in the service model, for example raw text versus JSON-decoded value, so emission becomes a lookup instead of a branch on string-ness.
  • src/v2/05_emit_rust.dag Missing upstream shell extraction model (M1/M5/M8): shell outputs do not carry structural facts about which field comes from stdout, stderr, or exit status, so emit guesses by name and coproduct shape; introduce typed shell field sources, analogous to REST from_key extraction, so the graph says from_stdout or from_exit_status and the emitter only renders declared structure.

MODELING — Strengths

  • dsl/gunbc/tools/review.dag Replacing the CLI shell-out with a direct import of extdeps.llm.anthropic is the right L5 tool composition over an L3 provider service.
  • dsl/extdeps/github/github.dag UserType = User | Bot | Organization is a more faithful closed set than the invented HumanUser label.
  • dsl/extdeps/llm/llm.dag Role = System | User | Assistant | Tool is a clean shared-provider L2 closed set and composes better than provider-prefixed variants.
  • dsl/extdeps/llm/anthropic.dag auth_source: EnvVar plus the explicit anthropic-version header captures real API contract facts at the provider-specific service layer.
  • dsl/std/serialization.dag VariantNaming and VariantEncoding use closed coproducts instead of string flags, which is the right M1/M4 shape for wire-format facts.

MODELING — Improvements

  • dsl/gunbc/tools/review.dag The Anthropic request is still assembled as raw JSON and prompt strings; compose it from shared message/content types so backend behavior stays structural rather than stringly (M1/M8, Track 10).
  • dsl/extdeps/github/github.dag Keep wire-shape facts on UserType itself instead of a module-wide wire_contract, because the same file also defines other enums with different wire meanings (M3/M4).
  • dsl/extdeps/llm/llm.dag If this file owns provider-shared wire facts, LlmMessage should also move toward provider-shared content blocks rather than String; otherwise keep serialization contracts in L3 provider modules (M1/M3, Track 10).
  • dsl/extdeps/llm/anthropic.dag A module-wide SnakeCase enum contract is not faithful for mixed Anthropic types such as ContentBlock and AnthropicModel; model each wire form on the relevant type or request/response boundary instead (M1/M3).
  • dsl/std/serialization.dag Either make WireContract the single consumed authority or delete it; as added here it is parallel scaffolding at the wrong granularity (M2/M7/M9).

ROADMAP — Verified

  • RE-2: review.dag compiles (dry-run): The diff still supports RE-2 by building and dry-running review-pr with an Anthropic env placeholder in the pipeline test.

ROADMAP — Incomplete

  • RE-3: review.dag passes live integration: The runtime path changed substantially, but the diff adds no non-ignored full-real review-cycle test proving the tool completes against live services.
  • RE-4: Anthropic REST API end-to-end: The added RE-4 tests are ignored compile/emission checks and dry-run never exercises the live Anthropic API, so end-to-end integration is still not demonstrated.

ROADMAP — Unclaimed

  • RE-4: Anthropic REST API end-to-end: This PR clearly starts RE-4 by adding Anthropic auth, endpoint/header plumbing, and targeted emission coverage, but ROADMAP.md still says Not started.

The PR makes real RE-4 progress and includes some strong modeling fixes, but the new wire-contract and emitter heuristics still violate core single-authority and no-fabrication invariants.

briansrls added a commit that referenced this pull request Apr 11, 2026
Addresses invariant violation feedback on PR #384:

1. Wire contract: no longer collapses to module-wide string. Passes
   wire_contract_item (Node?) per-enum for per-type resolution.

2. Response format (text vs json): reverted is_string_like heuristic.
   Needs WireFormat fact on response declarations in std/.

3. Shell multi-field return: reverted name-based channel fabrication.
   Needs channel→field mapping in transport definition.

Both #2 and #3 now emit compile_error! on the unmodeled cases,
making the gap explicit rather than silently fabricating.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@briansrls

briansrls commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

I reviewed the diff itself and treated src/v2/stage0/src/v2_compiler_emit_rust.rs as the generated mirror of src/v2/05_emit_rust.dag, not as a separate design surface.

My take: not merge-ready. I see two blockers and a few meaningful follow-ups.

1. Invariant violations

Blocker: wire_contract lands as two different representations, and the emitter compensates for that mismatch

The new dsl/std/serialization.dag introduces a wrapper type:

dagtype WireContract {  variant_encoding: VariantEncoding}

and its comment says modules should declare data wire_contract: WireContract = { ... }. But the changed extdep modules do not do that; they declare data wire_contract: VariantEncoding = ... directly. Then resolve_wire_serde_tag() in 05_emit_rust.dag explicitly assumes the body is a direct VariantEncoding value.

That is a classic single-authority failure: the PR adds a new semantic boundary, but does not land it end-to-end. The invariants explicitly call out unused/lossy fact tables and partial boundary layers as violations, and they also say emission should read facts, not invent or reinterpret them downstream. chatgpt-review-e569fb71-e239-43…

chatgpt-review-aa1bb2dd-aa8c-4d…

I would either:

  • use WireContract end-to-end immediately, or
  • delete the wrapper for now and keep only VariantEncoding.

Right now the model, comments, and emitter disagree on what wire_contract actually is.

Blocker: module-level wire_contract is too coarse, and the emitter gives it semantics it cannot safely have

In the diff, emit_module_full() finds one module-level wire_contract, emit_type_def_from_connective() resolves it once, and emit_enum_from_children() applies that same serde policy to every enum in the module.

That is not compositional enough for the extdep models being changed. llm/llm.dag does not just contain string-like enums such as Role; it also contains richer enums like StreamEvent. anthropic.dag contains ContentBlock, which the modeling notes explicitly call out as a good tagged union. Applying one module-wide StringVariant policy to both unit enums and payload-bearing tagged unions is not a faithful wire model. The modeling guidelines emphasize that extdeps must be grounded in actual API facts, and the invariants say emission should not recover or guess semantics that the model did not express precisely. chatgpt-review-ad2fe2f7-46aa-4d…

chatgpt-review-ad2fe2f7-46aa-4d…

chatgpt-review-e569fb71-e239-43…

There is also a concrete semantic bug in the new comments: std/serialization.dag says StringVariant means “enums serialize as plain strings.” That is only true for unit enums. For payload-bearing Rust enums, the new emitter path is not producing “plain strings”; it is just changing serde attributes.

I would push this upstream into a more precise model:

  • per-enum or per-type wire contracts, not module-wide,
  • or a richer wire-shape type that distinguishes unit-string enums from tagged object unions.

High concern: emit_shell_return now fail-louds without the upstream model that would make the failure actionable

The new emit_shell_return() branch replaces JSON-based multi-field shell decoding with:

Rustcompile_error!("shell multi-field output needs channel mapping in transport definition")

The TODO is directionally correct. The invariants do prefer fail-loud over silent fabrication. But this change lands without the corresponding transport/channel model, even though existing shell patterns already use multi-field outputs. The same invariants document says new semantic boundaries must land end-to-end in one change, and that if a downstream stage needs a fact, the fix is to surface that fact upstream rather than leave the consumer uncompilable. chatgpt-review-e569fb71-e239-43…

chatgpt-review-aa1bb2dd-aa8c-4d…

So the diagnosis is right, but the landing shape is incomplete. Unless some upstream wrapper prevents this branch from being reached for the current shell services, this looks like a regression vector.

2. Compositional modeling quality of the .dag changes

There is some good work here.

VariantEncoding / VariantNaming as closed sums is the right general direction. That follows the modeling rule that closed sets should be modeled structurally rather than as strings. auth_source: EnvVar { name: "ANTHROPIC_API_KEY" } is also a good, grounded improvement. The GitHub rename from HumanUser to User is more faithful to the likely wire/domain shape. chatgpt-review-ad2fe2f7-46aa-4d…

The main modeling problem is still the granularity of wire_contract.

The existing std-library pattern already puts serialization facts in language/spec data, not in ad hoc emitter logic. The attached roadmap and invariants both point in the same direction: codegen decisions should dissolve into LanguageSpec or other structural facts, not be introduced as new shared-emitter conditionals. ROADMAP

chatgpt-review-2d3c421a-7f02-4c…

I would score the .dag side like this:

  • Good: closed-sum wire vocabulary, env-based auth source, GitHub/User rename.
  • Needs rework: module-scoped wire_contract, because it cannot faithfully describe mixed wire shapes inside one module.
  • Still duplicated: Anthropic versioning. The extdep model already had AnthropicApiVersion / current_api_version; the diff hardcodes "anthropic-version" and "2023-06-01" again in the transport block instead of deriving from the existing authority. That is exactly the “single-authority” class the modeling rules warn about. chatgpt-review-ad2fe2f7-46aa-4d…

3. Roadmap alignment

ROADMAP.md is not modified in this PR, so there is no roadmap text drift to review directly.

I also would not infer a roadmap status bump from this diff. The attached roadmap still says:

  • RE-1 done,
  • RE-2 done,
  • RE-3 partial,
  • RE-4 not started. chatgpt-review-2d3c421a-7f02-4c…

That still seems right. This PR adds Anthropic plumbing and emitter support, but it does not demonstrate full real end-to-end integration. The testing invariants document says Tier 3 “Full Real” integration against live services is not yet implemented. chatgpt-review-e569fb71-e239-43…

So I would describe this PR as RE-4 infrastructure, not “RE-4 complete.”

4. General code review concerns

  • The RE-2 dry-run output contract appears to regress from a named JSON object to a positional JSON array. The attached current test suite expected reviewed and comment_url as named fields in the output JSON; this diff changes the test to accept [true, "..."] instead. That is a weaker public contract and feels like accepting tuple serialization rather than fixing it. chatgpt-review-f51035dc-6ba1-44…
  • reqwest::Client::builder().user_agent("gunbc/1.0") is a new emitter-side behavioral decision. It is probably harmless, but it is still policy inserted in codegen rather than modeled in language/service data. Given the repo’s “emission is translation” stance, I would keep an eye on that. chatgpt-review-e569fb71-e239-43…
  • review.dag now relies on auth_source, but still passes api_key: "" to llm.Anthropic.Messages(...). The emitter comment says this is a known follow-up around parameter pruning. That is okay as a temporary bridge, but it is another sign the boundary is not fully closed yet.
  • build_review_prompt() is now effectively dead wrapper code after the split into build_system_prompt() and build_user_message(). Not harmful, but it is cleanup debt inside the diff.

Bottom line

I would block on:

  1. wire_contract shape/semantics: make it single-authority and more precise than module-wide.
  2. shell multi-field emission: do not land the new fail-loud branch without the upstream channel mapping model in the same PR.

Everything else I’d mark as follow-up:

  • restore a named JSON contract for review_pr,
  • derive Anthropic version header from existing model data,
  • decide whether the new global user-agent belongs in modeled service/language config,
  • remove the dead prompt wrapper once the Anthropic path is settled.

View conversation

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codex · gpt-5.4 · 75fbf4c0

Review (INVARIANTS: 3, MODELING: 2+/2-, ROADMAP: 0✓/1!)

INVARIANTS — Violations (3)

ROOT CAUSE ANALYSIS

  • dsl/extdeps/llm/anthropic.dag The upstream gap is that llm.Anthropic.Messages exposes messages as generic Json instead of a structural request type, so review.dag compensates by hand-encoding JSON text and that text flows unchanged into the transport body; define AnthropicMessage or AnthropicRequest at L3, reuse LlmMessage where valid, and make callers pass data rather than encoded strings per M1, M8, and M9.
  • dsl/extdeps/llm/anthropic.dag The upstream gap is that service transport declarations cannot yet consume version authority as structure, so the version fact is declared once as data and again as raw header strings; add a structural api_version field or allow header expressions to reference current_api_version, then derive the emitted header from that single authority per M7 and M9.
  • src/v2/05_emit_rust.dag The upstream gap is that shell transports still have no structural channel-to-field mapping, so emit cannot know whether each product field comes from stdout, stderr, exit status, or parsed JSON and compensates with a generated compile_error!; model shell output channels in the transport or type layer and make lowering or inference carry that mapping so emit only translates it, aligned with M1, M8, and M9.

MODELING — Strengths

  • dsl/std/serialization.dag VariantNaming, VariantEncoding, and WireContract put enum wire-shape facts in a reusable L0 vocabulary instead of scattering target-specific cases through extdeps modules and emitters.
  • dsl/extdeps/github/github.dag Renaming UserType to User, Bot, and Organization matches GitHub's actual wire values and improves extdeps fidelity without inventing a new abstraction.

MODELING — Improvements

  • dsl/extdeps/llm/anthropic.dag Messages.input.messages: Json is too weak for the Anthropic spec; introduce a structural Anthropic request message type so tool code composes message arrays from data rather than encoded text.
  • dsl/extdeps/llm/llm.dag A module-level snake_case wire_contract in the provider-agnostic LLM vocabulary overstates cross-provider commonality; keep the shared conversation types neutral at L2 and let Anthropic and OpenAI declare their concrete wire mappings at L3.

ROADMAP — Incomplete

  • RE-3: review.dag passes live integration: The new live path still constructs the Anthropic messages field as encoded text, so the review agent is not at a reliable end-to-end integration point yet.

ROADMAP — Unclaimed

  • RE-4: Anthropic REST API end-to-end: The diff clearly advances RE-4 with a provider module, auth_source, header emission, review-tool integration, and tests, but ROADMAP.md still says Not started and the current request-modeling bug means only Partial is justified.

The PR makes real RE-4 progress and removes one prior shell-emission fabrication, but it still has a malformed Anthropic request body, a duplicated version authority, and a new emit-stage compile_error fallback.

Comment thread dsl/gunbc/tools/review.dag Outdated
llm_response = llm.Anthropic.Messages(
api_key: "",
model: model,
messages: "[{\"role\": \"user\", \"content\": \"Review the PR described in the system prompt.\"}]",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: messages is passed as a JSON-encoded string literal, so the Anthropic request body sends a string where the API requires a JSON array, violating the Modeling Faithfulness and Explicit boundary contracts invariants.

Comment thread dsl/extdeps/llm/anthropic.dag Outdated
path: "/v1/messages",
body: { model: model, messages: messages, max_tokens: max_tokens, temperature: temperature, system: system }
body: { model: model, messages: messages, max_tokens: max_tokens, temperature: temperature, system: system },
headers: { "anthropic-version": "2023-06-01" }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: The transport repeats anthropic-version and 2023-06-01 as header literals even though current_api_version already declares that fact, violating Single-authority metadata and MODELING.md M7.

Comment thread src/v2/05_emit_rust.dag Outdated
// fact in the transport definition (e.g., stdout from stdout, stderr from stderr,
// success from exit_code). Currently emits compile_error — model the
// channel→field mapping in std/ to dissolve this.
"compile_error!(\"shell multi-field output needs channel mapping in transport definition\")"

This comment was marked as resolved.

@briansrls

Copy link
Copy Markdown
Contributor Author

codex · gpt-5.4 · 75fbf4c0

Review (INVARIANTS: 3, MODELING: 2+/2-, ROADMAP: 0✓/1!)

INVARIANTS — Violations (3)

ROOT CAUSE ANALYSIS

  • dsl/extdeps/llm/anthropic.dag The upstream gap is that llm.Anthropic.Messages exposes messages as generic Json instead of a structural request type, so review.dag compensates by hand-encoding JSON text and that text flows unchanged into the transport body; define AnthropicMessage or AnthropicRequest at L3, reuse LlmMessage where valid, and make callers pass data rather than encoded strings per M1, M8, and M9.
  • dsl/extdeps/llm/anthropic.dag The upstream gap is that service transport declarations cannot yet consume version authority as structure, so the version fact is declared once as data and again as raw header strings; add a structural api_version field or allow header expressions to reference current_api_version, then derive the emitted header from that single authority per M7 and M9.
  • src/v2/05_emit_rust.dag The upstream gap is that shell transports still have no structural channel-to-field mapping, so emit cannot know whether each product field comes from stdout, stderr, exit status, or parsed JSON and compensates with a generated compile_error!; model shell output channels in the transport or type layer and make lowering or inference carry that mapping so emit only translates it, aligned with M1, M8, and M9.

MODELING — Strengths

  • dsl/std/serialization.dag VariantNaming, VariantEncoding, and WireContract put enum wire-shape facts in a reusable L0 vocabulary instead of scattering target-specific cases through extdeps modules and emitters.
  • dsl/extdeps/github/github.dag Renaming UserType to User, Bot, and Organization matches GitHub's actual wire values and improves extdeps fidelity without inventing a new abstraction.

MODELING — Improvements

  • dsl/extdeps/llm/anthropic.dag Messages.input.messages: Json is too weak for the Anthropic spec; introduce a structural Anthropic request message type so tool code composes message arrays from data rather than encoded text.
  • dsl/extdeps/llm/llm.dag A module-level snake_case wire_contract in the provider-agnostic LLM vocabulary overstates cross-provider commonality; keep the shared conversation types neutral at L2 and let Anthropic and OpenAI declare their concrete wire mappings at L3.

ROADMAP — Incomplete

  • RE-3: review.dag passes live integration: The new live path still constructs the Anthropic messages field as encoded text, so the review agent is not at a reliable end-to-end integration point yet.

ROADMAP — Unclaimed

  • RE-4: Anthropic REST API end-to-end: The diff clearly advances RE-4 with a provider module, auth_source, header emission, review-tool integration, and tests, but ROADMAP.md still says Not started and the current request-modeling bug means only Partial is justified.

The PR makes real RE-4 progress and removes one prior shell-emission fabrication, but it still has a malformed Anthropic request body, a duplicated version authority, and a new emit-stage compile_error fallback.

@briansrls

briansrls commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

I reviewed the diff itself and treated src/v2/stage0/src/v2_compiler_emit_rust.rs as the generated mirror of src/v2/05_emit_rust.dag, not as a separate design surface.

My merge call: not ready as-is. I would block on two issues.

1. Invariant violations

Blocker: wire_contract is both single-authority-inconsistent and modeled at the wrong scope

std/serialization.dag introduces:

dagtype WireContract {  variant_encoding: VariantEncoding}

but the changed extdep modules do not use that shape. They declare data wire_contract: VariantEncoding = ... directly, and resolve_wire_serde_tag() in 05_emit_rust.dag is written to read that direct value. So the PR lands two competing representations for the same fact on day one. That conflicts with the repo’s single-authority / “surface the fact once” rules. MODELING.md is explicit that facts should not exist both as data and as parallel derived logic, and INVARIANTS.md is explicit that downstream consumers should not compensate for missing or inconsistent upstream structure. chatgpt-review-ad2fe2f7-46aa-4d…

chatgpt-review-ffac49f4-d067-4f…

There is a second, deeper problem: the new wire_contract is module-wide, and emit_module_full() resolves it once and passes the same serde policy to every enum in the module via emit_enum_from_children(). That is not compositional enough for the changed models. It is fine for unit enums like github.UserType or llm.Role, but it is not faithful for payload-carrying unions. The attached modeling notes explicitly praise llm/anthropic.dag’s ContentBlock as a good tagged union; under this PR, the module-level StringVariant path would remove the repo’s default _variant tagging for every enum in that module, which changes the wire shape of data-carrying enums, not just unit enums. That is exactly the sort of emitter-side semantic repair the invariants warn about. chatgpt-review-ad2fe2f7-46aa-4d…

chatgpt-review-aa1bb2dd-aa8c-4d…

The doc comment in std/serialization.dag also overclaims: “StringVariant means enums serialize as plain strings” is only true for unit enums. For payload-bearing enums, the current emitter path does not mean “plain string on the wire.”

Compared with the existing std-library pattern, this is the wrong layer too. std-library.dag already has serialization modeled in LanguageSpec.serialization / SerializationFramework; this PR adds a second policy path in 05_emit_rust.dag instead of adding a precise structural wire-shape fact.

I would want one of these before merge:

  • make wire_contract a real WireContract end-to-end, and
  • narrow it to per-type / per-union scope, or
  • model wire shape more precisely than one module-global switch.

Blocker: shell multi-field output is still guessed in emit

The new shell path is better than silently fabricating values, but it is still fixing a missing upstream fact in the emitter. emit_shell_return() now assumes that any multi-field shell output is JSON on stdout and can be deserialized directly into the return type. emit_dry_run_branch_from_props() separately reconstructs tuple-like multi-field outputs from mock JSON. Those are transport semantics, not Rust-emission facts.

INVARIANTS.md is very direct here: when a downstream stage needs a fact, the fix is to surface that fact at its origin rather than guess it from shapes or names later. The “heuristics indicate lost structure” rule fits this exactly. If an operation needs “this field comes from stdout”, “this one from stderr”, or “this one from exit status”, that belongs in the transport model. chatgpt-review-ffac49f4-d067-4f…

chatgpt-review-ffac49f4-d067-4f…

So I would block until shell channel mapping is modeled, or the PR is narrowed so this path cannot silently redefine shell semantics.

2. Compositional modeling quality

There is good work here.

VariantEncoding / VariantNaming as closed sums are a good direction. auth_source: EnvVar { name: "ANTHROPIC_API_KEY" } is grounded and useful. Renaming GitHub’s user variant from HumanUser to User plus updating the mock payloads is also a fidelity improvement.

The compositional miss is the granularity of wire_contract. MODELING.md says closed sets should be enums, data tables should have one authority, and dispatch should be structural rather than string- or shape-guessing. A module-wide “all enums in here serialize like X” switch is too blunt for mixed modules, especially Anthropic and shared LLM types. chatgpt-review-ad2fe2f7-46aa-4d…

I would also call out the review_messages bridge in review.dag. The diff comment is honest: messages should be modeled as real list/record structure, but the app is currently forced to JSON-encode them into a String because the emitter cannot render list/record literals in transport bodies yet. That is useful as an explicit bridge, but it is still app-layer compensation for a missing structural capability in emission.

3. Roadmap alignment

ROADMAP.md is not modified in this PR, so there is no direct roadmap-text drift to review.

I also would not infer a status bump from the diff. The attached roadmap still says:

  • RE-1 done,
  • RE-2 done,
  • RE-3 partial,
  • RE-4 not started. chatgpt-review-6f33a61f-156c-44…

That still feels right. This PR clearly advances Anthropic plumbing and RE-4 infrastructure, but it does not demonstrate end-to-end Anthropic execution. The invariants/testing doc also says Tier 3 “Full Real” integration against live services is not yet implemented. chatgpt-review-aa1bb2dd-aa8c-4d…

So I’d describe this as RE-4 scaffolding, not RE-4 completion.

4. General code review concerns

The RE-2 dry-run output contract appears to regress from a named JSON object to a positional JSON array. The attached current compiler test expected reviewed and comment_url as named fields; this PR changes the acceptance test to allow tuple/array output instead. That is a weaker CLI contract and much more brittle for downstream consumers. I would keep the named result shape. chatgpt-review-aca2745e-d462-41…

The review-agent path is still only half-closed structurally. auth_source is a good model improvement, but review.dag still calls llm.Anthropic.Messages(api_key: ""), which leaves dead plumbing in the interface. I would clean that up once parameter pruning / auth-source-only binding is landed.

The added Anthropic tests are worthwhile smoke tests, but they only prove emitted-source properties like “contains ANTHROPIC_API_KEY”, “contains anthropic-version”, and “contains snake_case”. They do not prove end-to-end wire behavior.

What I liked

auth_source: EnvVar is the right kind of change: it moves credential sourcing into explicit structure instead of threading raw secrets around.

Preferring service_config_endpoint() over transport fallback is a good single-authority cleanup.

The reserved-word serde(rename = "...") fix for Rust field names is a good interop patch.

The GitHub HumanUser → User rename and the matching mock update look like fidelity improvements rather than churn.

Bottom line

I would block on:

  1. making wire_contract single-authority and narrowing it away from module-wide “all enums serialize like this”, and
  2. moving shell multi-field semantics out of 05_emit_rust.dag and into transport facts.

Everything else I would treat as follow-up, with the RE-2 JSON-contract regression the next thing I’d fix.


View conversation

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codex · gpt-5.4 · 61c2314d

Review (INVARIANTS: 3, MODELING: 3+/3-, ROADMAP: 0✓/2!)

INVARIANTS — Violations (3)

ROOT CAUSE ANALYSIS

  • dsl/extdeps/llm/anthropic.dag The Anthropic boundary is still shaped around raw Strings and duplicated auth/model facts, so Layer 5 code must repeat provider ids and the service config competes with per-call inputs; make model selection a typed AnthropicModel/AnthropicModelSpec reference and let auth_source rewrite the callable signature so each fact has one structural authority end-to-end (M7, M9).
  • src/v2/05_emit_rust.dag The output projection carries cardinality on the field but emit_json_value_extract only models required extraction, so dry-run emission compensates downstream with a Some wrapper; introduce one structural projection/extractor that consumes path plus cardinality plus type and reuse it for both live and dry-run paths (M1, M8).

MODELING — Strengths

  • dsl/std/serialization.dag VariantEncoding and VariantNaming are a clean coproduct-based model of enum wire shape at the std layer, so provider modules can state wire facts without inventing provider-specific emitter logic.
  • dsl/extdeps/github/github.dag Renaming HumanUser to User and attaching AsAuthored keeps the model faithful while staying at the provider-facts layer.
  • dsl/extdeps/llm/llm.dag Moving role casing into a module-level wire contract keeps the shared LLM ontology provider-agnostic while still faithful to wire strings.

MODELING — Improvements

  • dsl/std/serialization.dag Land WireContract as the actual consumed authority and extend it to cover body format and field naming, otherwise emit must keep reconstructing wire facts from raw nodes instead of reading a closed contract.
  • dsl/extdeps/llm/anthropic.dag Promote Messages.model and messages from String and Json to provider-grounded types so the Anthropic service composes from AnthropicModel and message algebras instead of string bridges.
  • dsl/gunbc/tools/review.dag When transport body literals land, represent review_messages as typed message data and keep PR-specific context in the user-message channel rather than tunneling it through a JSON string at Layer 5.

ROADMAP — Incomplete

  • RE-3: review.dag passes live integration: review_pr still computes already_done without branching on it and the Anthropic request payload still depends on the JSON-string message bridge, so the live workflow remains partial.
  • RE-4: Anthropic REST API end-to-end: The PR integrates Anthropic REST, but model ids, credentials, and message bodies are still duplicated or bridged through Strings rather than flowing from structural authorities end-to-end.

ROADMAP — Unclaimed

  • RE-4: Anthropic REST API end-to-end: This diff clearly advances RE-4 beyond Not started, but ROADMAP.md was not updated to reflect that the item is at least partial.

The PR fixes several earlier emitter regressions and materially advances RE-4, but it still adds new duplicate authorities and a cardinality leak, so Track 5 remains partial.


data default_backend: String = "codex"
data default_model: String = "gpt-5.4"
data default_model: String = "claude-sonnet-4-6-20250929"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: default_model re-literals Anthropic's model id instead of reusing the provider authority already declared in anthropic.dag, violating Single-authority metadata and MODELING.md M7.

endpoint: "https://api.anthropic.com"
auth: Header("x-api-key")
auth_input: api_key
auth_source: EnvVar { name: "ANTHROPIC_API_KEY" }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: auth_source adds env-based credential authority while Messages still exposes api_key as an operation input, violating Single-authority metadata and Explicit boundary contracts.

Comment thread src/v2/05_emit_rust.dag
let raw = match ch.inferred {
Some { value: Resolved { node: tn } } =>
emit_json_value_extract(var_name: field_name, from_path: from_path, dag_type_name: tn.name)
_ =>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: The new dry-run optional-field branch extracts the field as required and only then wraps it in Some, so absent optional fields still error instead of yielding None, violating Explicit boundary contracts and Emission is translation, not decision-making.

@briansrls

Copy link
Copy Markdown
Contributor Author

codex · gpt-5.4 · 61c2314d

Review (INVARIANTS: 3, MODELING: 3+/3-, ROADMAP: 0✓/2!)

INVARIANTS — Violations (3)

ROOT CAUSE ANALYSIS

  • dsl/extdeps/llm/anthropic.dag The Anthropic boundary is still shaped around raw Strings and duplicated auth/model facts, so Layer 5 code must repeat provider ids and the service config competes with per-call inputs; make model selection a typed AnthropicModel/AnthropicModelSpec reference and let auth_source rewrite the callable signature so each fact has one structural authority end-to-end (M7, M9).
  • src/v2/05_emit_rust.dag The output projection carries cardinality on the field but emit_json_value_extract only models required extraction, so dry-run emission compensates downstream with a Some wrapper; introduce one structural projection/extractor that consumes path plus cardinality plus type and reuse it for both live and dry-run paths (M1, M8).

MODELING — Strengths

  • dsl/std/serialization.dag VariantEncoding and VariantNaming are a clean coproduct-based model of enum wire shape at the std layer, so provider modules can state wire facts without inventing provider-specific emitter logic.
  • dsl/extdeps/github/github.dag Renaming HumanUser to User and attaching AsAuthored keeps the model faithful while staying at the provider-facts layer.
  • dsl/extdeps/llm/llm.dag Moving role casing into a module-level wire contract keeps the shared LLM ontology provider-agnostic while still faithful to wire strings.

MODELING — Improvements

  • dsl/std/serialization.dag Land WireContract as the actual consumed authority and extend it to cover body format and field naming, otherwise emit must keep reconstructing wire facts from raw nodes instead of reading a closed contract.
  • dsl/extdeps/llm/anthropic.dag Promote Messages.model and messages from String and Json to provider-grounded types so the Anthropic service composes from AnthropicModel and message algebras instead of string bridges.
  • dsl/gunbc/tools/review.dag When transport body literals land, represent review_messages as typed message data and keep PR-specific context in the user-message channel rather than tunneling it through a JSON string at Layer 5.

ROADMAP — Incomplete

  • RE-3: review.dag passes live integration: review_pr still computes already_done without branching on it and the Anthropic request payload still depends on the JSON-string message bridge, so the live workflow remains partial.
  • RE-4: Anthropic REST API end-to-end: The PR integrates Anthropic REST, but model ids, credentials, and message bodies are still duplicated or bridged through Strings rather than flowing from structural authorities end-to-end.

ROADMAP — Unclaimed

  • RE-4: Anthropic REST API end-to-end: This diff clearly advances RE-4 beyond Not started, but ROADMAP.md was not updated to reflect that the item is at least partial.

The PR fixes several earlier emitter regressions and materially advances RE-4, but it still adds new duplicate authorities and a cardinality leak, so Track 5 remains partial.

review.dag: Anthropic Messages REST API replaces CLI shell-out.
Default model claude-sonnet-4-6. Prompt split into system + user message.
BRIDGE: messages as JSON-encoded string (needs emit_simple_expr list literals).

anthropic.dag: auth_source EnvVar, anthropic-version from current_api_version,
service config endpoint.

std/serialization.dag: VariantEncoding (StringVariant | TaggedVariant),
VariantNaming (AsAuthored | SnakeCase), WireContract. Module-level
wire_contract data declarations in github + llm extdeps.

Emitter (05_emit_rust.dag):
- Wire contract: per-enum serde tag from module wire_contract item
- auth_token.clone() for Header auth with auth_source
- Service config endpoint resolution
- User-Agent on REST client
- Serde rename for reserved keyword fields (ref, type)
- Dry-run mock: multi-field tuple extraction + optional wrapping
- Shell multi-field: JSON-parse bridge (needs channel mapping)

BRIDGE items pending dissolution:
- review_messages: string encoding of JSON array (emit_simple_expr)
- Shell multi-field: stdout JSON parse (transport channel mapping)
- Response format: all success responses use json() (needs WireFormat)
- api_key param: still in operation signature with auth_source

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@briansrls

briansrls commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

Reviewed against the diff plus the attached invariants/modeling/roadmap docs. I’d block on 4 items.

  1. wire_contract is too coarse at module scope.
  • dsl/std/serialization.dag says the wire contract applies to all types in a module, and emit_module_full now finds one wire_contract item and applies it to every enum emitted from that module.
  • That is okay for simple unit enums like Role / StopReason, but it is too broad for modules that also contain data-carrying tagged unions. The modeling notes explicitly call out Anthropic’s ContentBlock and OpenAI’s ResponseFormat / ToolChoice as tagged unions, while also noting the shared LLM layer is still under-modeled because LlmMessage.content is only String. This PR makes the whole module “string-variant” instead of making those differences structural. chatgpt-review-39bdc789-f163-42…

chatgpt-review-c8ae25dc-0efe-4a…

  • Diff refs: dsl/std/serialization.dag:273-301, dsl/extdeps/llm/anthropic.dag:56-57, dsl/extdeps/llm/llm.dag:90-98, dsl/extdeps/llm/openai.dag:113-114, src/v2/05_emit_rust.dag:358-363, 386-400, 425-434.

Why I think this is a real invariant violation:

  • The repo’s rule is to carry the needed fact structurally, not make emit infer/apply a coarse rendering policy downstream. chatgpt-review-b2d42255-adc0-48…

chatgpt-review-b2d42255-adc0-48…

  1. The new serialization model and the emitter already disagree.
  • std/serialization.dag documents data wire_contract: WireContract = { variant_encoding: ... }.
  • But the changed extdeps declare data wire_contract: VariantEncoding = ..., and resolve_wire_serde_tag() explicitly assumes the body is a VariantEncoding directly.
  • So the documented authority and the consumer are already out of sync on day one. A future module author following the std comment literally will get the wrong emission.

Diff refs:

  • dsl/std/serialization.dag:297-301
  • src/v2/05_emit_rust.dag:319-349

This is exactly the kind of “new boundary must land end-to-end” problem the invariants call out. chatgpt-review-b2d42255-adc0-48…

  1. review.dag adds an admitted stringified-JSON bridge instead of fixing the upstream structural gap.
  • The new review_messages constant is a JSON string.
  • The comment explicitly says this exists because emit cannot render list/record literals in transport bodies, so the value is passed as a string and coerced later with serde_json::from_str.
  • That is the repo’s “consumer compensates for producer loss” anti-pattern almost word-for-word.

Diff refs:

  • dsl/gunbc/tools/review.dag:137-141
  • dsl/gunbc/tools/review.dag:228-235

Why I’d block on it:

  • Root-cause depth says fix the deepest broken ancestor, not the downstream compensation. chatgpt-review-b2d42255-adc0-48…
  • Explicit boundary contracts / emission-as-translation say emit should not have to recover structure from strings. chatgpt-review-b2d42255-adc0-48…

chatgpt-review-b2d42255-adc0-48…

  • It also leans further into known Track 10 extdeps debt around LlmMessage.content being too weakly modeled. chatgpt-review-c8ae25dc-0efe-4a…

chatgpt-review-c8ae25dc-0efe-4a…

  1. Dry-run tuple extraction fabricates Some(...) for optional outputs.
  • In emit_dry_run_branch_from_props, the new multi-field conjunction path extracts tuple fields from mock JSON.
  • For optional fields, it then unconditionally does let field = Some(field);.
  • That means dry-run cannot represent None for optional outputs. If the field is absent/null, this either fails before wrapping or fabricates presence; either way it diverges from real deserialization semantics.

Diff refs:

  • src/v2/05_emit_rust.dag:480-514
  • especially 498-501

This is a straightforward “no fallbacks that fabricate” issue. Dry-run is supposed to preserve shape, not force presence. chatgpt-review-b2d42255-adc0-48…

Non-blocking notes

  • Good change: github.UserType = User | Bot | Organization plus StringVariant { naming: AsAuthored } is a real improvement over the old HumanUser mismatch. That moves the model closer to the wire without inventing a new taxonomy.
  • Test gap: the new tests mostly assert emitted strings or a dry-run happy path. They do not pin the risky part of this PR: serde round-trip for the new wire contract on data-carrying enums. Given the modeling docs frame serialization round-trips as structural verification territory, I’d want at least one focused test here. chatgpt-review-c8ae25dc-0efe-4a…

Roadmap

  • ROADMAP.md is not modified, so there is no roadmap-doc drift item.
  • Substantively this is clearly Track 5 work (RE-3/RE-4), but the implementation pushes more logic into emit in an area the roadmap still lists as extdeps modeling fidelity debt. chatgpt-review-c8ae25dc-0efe-4a…

chatgpt-review-c8ae25dc-0efe-4a…

Overall

I would not approve as-is. The biggest blocker is the new module-level wire_contract abstraction: it is too coarse for the LLM modules it is applied to, and the std model / emitter consumer already disagree on what wire_contract even is.


View conversation

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codex · gpt-5.4 · 9ce7288e

Review (INVARIANTS: 3, MODELING: 3+/1-, ROADMAP: 0✓/1!)

INVARIANTS — Violations (3)

ROOT CAUSE ANALYSIS

  • src/v2/05_emit_rust.dag The resolved output type already carries the upstream shape fact that distinguishes a named product from an anonymous conjunction, but this branch re-derives shape from connective plus arity and diverges from the same authority used by render_rust_type; factor one structural output-shape classifier from the resolved Node identity and make both dry-run and real response extraction consume it (MODELING.md M8).
  • src/v2/05_emit_rust.dag Service endpoint authority reaches emit as a real expression, but the service-constructor path only accepts raw strings, so non-literal endpoints are discarded and the legacy localhost fallback masks the loss downstream; move endpoint into a structural service-constructor or LanguageSpec input that can translate the declared expression end-to-end, and fail closed when that authority is missing (MODELING.md M8, Track 2).
  • src/v2/05_emit_rust.dag The emitter is mixing target-identifier normalization with wire-key authority: it already has exact authored_name and from_key facts, but re-derives keys from Rust naming rules because field-key serialization is not a single structural authority; use exact authored/from_key keys end-to-end, or extend std.serialization so field-key mapping is modeled once and consumed structurally by emit (MODELING.md M7-M8).

MODELING — Strengths

  • dsl/std/serialization.dag VariantEncoding and VariantNaming put wire-shape facts in a provider-agnostic std module instead of burying serde policy in Rust-specific emission logic.
  • dsl/extdeps/llm/llm.dag Recasting Role as plain System, User, Assistant, and Tool under a shared snake_case wire contract is a faithful L2 model that composes cleanly into Anthropic and OpenAI.
  • dsl/extdeps/github/github.dag Renaming HumanUser to User and declaring an AsAuthored string-enum contract matches GitHub’s actual wire values more faithfully at the provider-core layer.

MODELING — Improvements

  • dsl/std/serialization.dag WireContract should grow into the single structural authority for enum form, field keys, and response or body formats so Track 2 can delete the remaining rename and text-versus-json emitter heuristics.

ROADMAP — Incomplete

  • RE-4: Anthropic REST API end-to-end: The diff starts the REST backend, but auth authority, request-body typing, and wire-format authority are still incomplete, so RE-4 is not done.

ROADMAP — Unclaimed

  • RE-4: Anthropic REST API end-to-end: Because the diff clearly starts RE-4 with Anthropic provider wiring, review.dag integration, and RE-4 tests, ROADMAP should no longer leave this item at Not started.

The PR materially advances RE-4 and resolves several earlier emitter heuristics, but it still introduces new emit-side structure loss and leaves key Anthropic and wire-contract authorities incomplete.

Comment thread src/v2/05_emit_rust.dag
// serde_json::from_value can't deserialize JSON objects into tuples,
// so we extract fields individually and construct the tuple.
// Single-field conjunctions (return type = field type) use from_value.
let is_multi_field_conj = inferred.connective == Conj && inferred.children |> count > 1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: emit_dry_run_branch_from_props treats every multi-field conjunction as tuple-shaped dry-run output, so named struct results are rebuilt as tuples, violating Explicit boundary contracts and Heuristics indicate lost structure.

Comment thread src/v2/05_emit_rust.dag
let base_url_default = if fs.has_rest {
// Find base_url: prefer service config endpoint, then transport base_url
let from_config = match service_config_endpoint(n: service_item, source_index: source_index) {
Some { value: ep } => match ep.expr_data {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: emit_service_new_method drops any non-literal config.endpoint expression to an empty string and then falls through to http://localhost, violating No fallbacks that fabricate and Emission is translation, not decision-making.

Comment thread src/v2/05_emit_rust.dag
// Reserved keywords: emit_ident adds r# prefix, but serde needs a rename
// to match the original JSON key (e.g., "ref" → r#ref needs #[serde(rename = "ref")])
let authored = authored_name(env: env, node: child)
let snake = to_snake(name: authored)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: emit_struct_field_from_child derives reserved-field serde rename keys with to_snake(authored) instead of the declared wire key, so Rust keyword escaping can silently rewrite the JSON contract, violating Explicit boundary contracts and Emission is translation, not decision-making.

@briansrls

Copy link
Copy Markdown
Contributor Author

codex · gpt-5.4 · 9ce7288e

Review (INVARIANTS: 3, MODELING: 3+/1-, ROADMAP: 0✓/1!)

INVARIANTS — Violations (3)

ROOT CAUSE ANALYSIS

  • src/v2/05_emit_rust.dag The resolved output type already carries the upstream shape fact that distinguishes a named product from an anonymous conjunction, but this branch re-derives shape from connective plus arity and diverges from the same authority used by render_rust_type; factor one structural output-shape classifier from the resolved Node identity and make both dry-run and real response extraction consume it (MODELING.md M8).
  • src/v2/05_emit_rust.dag Service endpoint authority reaches emit as a real expression, but the service-constructor path only accepts raw strings, so non-literal endpoints are discarded and the legacy localhost fallback masks the loss downstream; move endpoint into a structural service-constructor or LanguageSpec input that can translate the declared expression end-to-end, and fail closed when that authority is missing (MODELING.md M8, Track 2).
  • src/v2/05_emit_rust.dag The emitter is mixing target-identifier normalization with wire-key authority: it already has exact authored_name and from_key facts, but re-derives keys from Rust naming rules because field-key serialization is not a single structural authority; use exact authored/from_key keys end-to-end, or extend std.serialization so field-key mapping is modeled once and consumed structurally by emit (MODELING.md M7-M8).

MODELING — Strengths

  • dsl/std/serialization.dag VariantEncoding and VariantNaming put wire-shape facts in a provider-agnostic std module instead of burying serde policy in Rust-specific emission logic.
  • dsl/extdeps/llm/llm.dag Recasting Role as plain System, User, Assistant, and Tool under a shared snake_case wire contract is a faithful L2 model that composes cleanly into Anthropic and OpenAI.
  • dsl/extdeps/github/github.dag Renaming HumanUser to User and declaring an AsAuthored string-enum contract matches GitHub’s actual wire values more faithfully at the provider-core layer.

MODELING — Improvements

  • dsl/std/serialization.dag WireContract should grow into the single structural authority for enum form, field keys, and response or body formats so Track 2 can delete the remaining rename and text-versus-json emitter heuristics.

ROADMAP — Incomplete

  • RE-4: Anthropic REST API end-to-end: The diff starts the REST backend, but auth authority, request-body typing, and wire-format authority are still incomplete, so RE-4 is not done.

ROADMAP — Unclaimed

  • RE-4: Anthropic REST API end-to-end: Because the diff clearly starts RE-4 with Anthropic provider wiring, review.dag integration, and RE-4 tests, ROADMAP should no longer leave this item at Not started.

The PR materially advances RE-4 and resolves several earlier emitter heuristics, but it still introduces new emit-side structure loss and leaves key Anthropic and wire-contract authorities incomplete.

@briansrls
briansrls merged commit d83b215 into main Apr 11, 2026
1 check passed
briansrls pushed a commit that referenced this pull request Apr 11, 2026
S1-S6 all done (were listed as "S4 in progress"). C1-C2 done.
Track 6 done (AlgebraFieldKind coproduct, PR #387). RE-4 done
(PR #384). Stream B Layer 2 done, Layer 1 reverted (blocked on
Track 3). Track 7/10 items dissolved by PR #377/#387.

Updated CI gates (390 tests, 421 violations), active branches
table (quick-owl-889, PRs #388-390), clone count (23,733→13,820),
and "what to start now" section to reflect C3-C6 as next unblocked
work.

https://claude.ai/code/session_01LtFw3MQ9qUq9mSwN7VLZMR
@briansrls
briansrls deleted the dark-koi-448 branch June 1, 2026 18:41
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