Skip to content

feat: add governed gateway routing and production graph queries - #2735

Open
Alter-Igor wants to merge 4 commits into
Graphify-Labs:v8from
Alterspective-IO:feat/synapse-auto-routing
Open

feat: add governed gateway routing and production graph queries#2735
Alter-Igor wants to merge 4 commits into
Graphify-Labs:v8from
Alterspective-IO:feat/synapse-auto-routing

Conversation

@Alter-Igor

@Alter-Igor Alter-Igor commented Aug 14, 2026

Copy link
Copy Markdown

Outcome

Adds governed OpenAI-compatible gateway metadata and improves Graphify's structural CLI evidence without changing its default model selection.

Changes

  • accepts validated, non-credential default headers through GRAPHIFY_OPENAI_HEADERS_JSON
  • applies the headers to extraction, lightweight requests, and PR triage
  • keeps the model configuration-driven, including passing auto through unchanged
  • adds affected --production-only with fail-closed path and edge filtering
  • adds deterministic source proof to path output
  • rejects unknown affected flags and supports command help plus option-first ordering

The default behavior remains compatible when the new environment variable and flag are absent.

Validation

  • Linux full suite: 4,404 passed, 44 skipped
  • Ruff: all checks passed
  • skillgen check, coverage audit, schema singleton, monolith roundtrip, and always-on roundtrip: passed
  • focused Pyright for affected logic and new tests: 0 errors
  • required graphify update .: completed
  • install and CLI smoke test in a disposable home: passed

No lockfile change is included.

@graphify-labs graphify-labs 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.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. 2 change(s) alter behavior, breaking input(s) attached.

Behavior changes: format\_affected changes behavior, here is the input that shows it.

The verifier found a concrete input on which format\_affected behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","query":"'\(\)'"\}, the old code produced 'No unique node match for \(\)' but the new code produces raises KeyError. Paste that input straight into a regression test.

Behavior changes: resolve\_seed changes behavior, here is the input that shows it.

The verifier found a concrete input on which resolve\_seed behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","query":"'\(\)'"\}, the old code produced None but the new code produces raises KeyError. Paste that input straight into a regression test.


Graphify review — findings

This pull request adds an optional "production-only" mode to the affected command's reverse dependency traversal, which excludes test, eval, and docs nodes. It introduces a new --production-only CLI flag, path classification helpers (e.g. _is_production_source, _is_production_node), and refactors seed resolution and edge-traversal logic in affected.py into smaller helper functions. Separately, it adds support for a GRAPHIFY_OPENAI_HEADERS_JSON environment variable that lets users supply extra non-credential headers for OpenAI-compatible gateways, with credential/transport headers rejected. The README documents both changes and affected is added to the free-text command set so its arguments bypass the generic help handler. The touched surface spans README.md, __main__.py, affected.py, LLM/OpenAI header handling, and associated test files.

No blocking issues surfaced. 3 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1013 functions depend on the 373 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: dispatch_command() — 2 callers, 118 callees
  • worse: _call_llm() — 12 callers, 18 callees
  • worse: _call_openai_compat() — 24 callers, 9 callees
  • worse: affected_nodes() — 28 callers, 6 callees
  • worse: resolve_seed() — 18 callers, 4 callees
  • worse: triage_with_opus() — 3 callers, 6 callees
  • worse: format_affected() — 4 callers, 4 callees
  • worse: _resolve_triage_backend() — 4 callers, 3 callees

Verification — 1013 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 933 function(s) in the blast radius were not formally verified this run

Formal verification

Behavior changes: format\_affected changes behavior, here is the input that shows it.

The verifier found a concrete input on which format\_affected behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","query":"'\(\)'"\}, the old code produced 'No unique node match for \(\)' but the new code produces raises KeyError. Paste that input straight into a regression test.

Behavior changes: resolve\_seed changes behavior, here is the input that shows it.

The verifier found a concrete input on which resolve\_seed behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","query":"'\(\)'"\}, the old code produced None but the new code produces raises KeyError. Paste that input straight into a regression test.

Verification did not run: Verification did not run for affected\_nodes.

The verification could not execute (an environment/toolchain issue, not a statement about the code).

Guarantee: No guarantee, the check itself did not complete.

Note: Detail: exec: NameError

Could not verify: Could not verify dispatch\_command.

The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_call\_llm.

The verifier did not have enough to check \_call\_llm, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

Verification did not run: Verification did not run for \_call\_openai\_compat.

The verification could not execute (an environment/toolchain issue, not a statement about the code).

Guarantee: No guarantee, the check itself did not complete.

Note: Detail: harness produced no verdict (rc=124): timeout after 30s

No difference found (not proven): No behavior difference found in \_run\_cli (not a proof).

The verifier ran both versions of \_run\_cli on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in \_resolve\_triage\_backend (not a proof).

The verifier ran both versions of \_resolve\_triage\_backend on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in triage\_with\_opus (not a proof).

The verifier ran both versions of triage\_with\_opus on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

· 3 grounded finding(s) anchored inline below; 5 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/affected.py
]
if len(matches) == 1:
return str(matches[0])
return _prefer_file_node(graph, matches, repo_relative_query) if matches else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionresolve_seed()

18 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/affected.py
if production_only and not _is_production_node(graph, member_id):
continue
seen.add(member_id)
queue.append((member_id, 0))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionaffected_nodes()

fans out to 6 callees (efferent coupling); 28 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/prs.py
return _TRIAGE_MODEL_DEFAULTS.get(backend) or _default_model_for_backend(backend)


def _resolve_triage_backend() -> tuple[str, str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regression_resolve_triage_backend()

high coupling complexity (Ca·Ce = 12).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@graphify-labs graphify-labs 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.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. 3 change(s) alter behavior, breaking input(s) attached.

Behavior changes: format\_affected changes behavior, here is the input that shows it.

The verifier found a concrete input on which format\_affected behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","query":"''"\}, the old code produced raises KeyError but the new code produces 'No unique node match for '. Paste that input straight into a regression test.

Behavior changes: \_prefer\_file\_node changes behavior, here is the input that shows it.

The verifier found a concrete input on which \_prefer\_file\_node behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(0, \{\}\), \(1, \{\}\), \(2, \{\}\)\]\), \_g\.add\_edges\_from\(\[\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","node\_ids":"\[1\]","query":"'/'"\}, the old code produced 1 but the new code produces '1'. Paste that input straight into a regression test.

Behavior changes: resolve\_seed changes behavior, here is the input that shows it.

The verifier found a concrete input on which resolve\_seed behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","query":"''"\}, the old code produced raises KeyError but the new code produces None. Paste that input straight into a regression test.


Graphify review — findings

This PR adds a --production-only option to the affected command, which excludes test, eval, and docs nodes during reverse dependency traversal. It introduces path-classification helpers (_is_production_source, _is_production_node) and refactors the resolve_seed logic into smaller helpers (_unique_or_production_match, _label_matches, _resolve_source_match, _seed_members), while also adding affected to the free-text command exemption list in the CLI help handling. The changes also touch OpenAI backend header handling (documented via a new GRAPHIFY_OPENAI_HEADERS_JSON env var in the README) and adjust several LLM-related symbols and tests around synapse routing, triage models, and path/source evidence.

No blocking issues surfaced. 4 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1014 functions depend on the 374 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: dispatch_command() — 2 callers, 118 callees
  • worse: _call_llm() — 12 callers, 18 callees
  • worse: _call_openai_compat() — 24 callers, 9 callees
  • worse: affected_nodes() — 28 callers, 6 callees
  • worse: resolve_seed() — 19 callers, 4 callees
  • worse: format_affected() — 5 callers, 4 callees
  • worse: triage_with_opus() — 3 callers, 6 callees
  • worse: _resolve_triage_backend() — 4 callers, 3 callees

Verification — 1014 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 934 function(s) in the blast radius were not formally verified this run

Formal verification

Behavior changes: format\_affected changes behavior, here is the input that shows it.

The verifier found a concrete input on which format\_affected behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","query":"''"\}, the old code produced raises KeyError but the new code produces 'No unique node match for '. Paste that input straight into a regression test.

Behavior changes: \_prefer\_file\_node changes behavior, here is the input that shows it.

The verifier found a concrete input on which \_prefer\_file\_node behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(0, \{\}\), \(1, \{\}\), \(2, \{\}\)\]\), \_g\.add\_edges\_from\(\[\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","node\_ids":"\[1\]","query":"'/'"\}, the old code produced 1 but the new code produces '1'. Paste that input straight into a regression test.

Behavior changes: resolve\_seed changes behavior, here is the input that shows it.

The verifier found a concrete input on which resolve\_seed behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","query":"''"\}, the old code produced raises KeyError but the new code produces None. Paste that input straight into a regression test.

Verification did not run: Verification did not run for affected\_nodes.

The verification could not execute (an environment/toolchain issue, not a statement about the code).

Guarantee: No guarantee, the check itself did not complete.

Note: Detail: exec: NameError

Could not verify: Could not verify dispatch\_command.

The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_call\_llm.

The verifier did not have enough to check \_call\_llm, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

Verification did not run: Verification did not run for \_call\_openai\_compat.

The verification could not execute (an environment/toolchain issue, not a statement about the code).

Guarantee: No guarantee, the check itself did not complete.

Note: Detail: harness produced no verdict (rc=124): timeout after 30s

No difference found (not proven): No behavior difference found in \_run\_cli (not a proof).

The verifier ran both versions of \_run\_cli on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in \_resolve\_triage\_backend (not a proof).

The verifier ran both versions of \_resolve\_triage\_backend on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in triage\_with\_opus (not a proof).

The verifier ran both versions of triage\_with\_opus on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

· 3 grounded finding(s) anchored inline below; 5 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/affected.py
return _prefer_file_node(graph, matches, repo_relative_query) if matches else None


def resolve_seed(graph: nx.Graph, query: str) -> str | None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionresolve_seed()

19 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/affected.py
queue.append((member_id, 0))


def affected_nodes(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionaffected_nodes()

fans out to 6 callees (efferent coupling); 28 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/prs.py
return _TRIAGE_MODEL_DEFAULTS.get(backend) or _default_model_for_backend(backend)


def _resolve_triage_backend() -> tuple[str, str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regression_resolve_triage_backend()

high coupling complexity (Ca·Ce = 12).

Grounded coupling-delta finding (deterministic), not an LLM guess.

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