Skip to content

feat: Docker/Podman containerized setup with GHCR publishing (closes #1) - #2743

Open
slmingol wants to merge 3 commits into
Graphify-Labs:v8from
slmingol:v8
Open

feat: Docker/Podman containerized setup with GHCR publishing (closes #1)#2743
slmingol wants to merge 3 commits into
Graphify-Labs:v8from
slmingol:v8

Conversation

@slmingol

@slmingol slmingol commented Aug 15, 2026

Copy link
Copy Markdown

Summary

  • Add `.github/workflows/docker.yml` — builds `linux/amd64` + `linux/arm64` images via native runners (no QEMU) on push to `v8` and on release; pushes to `ghcr.io/graphify-labs/graphify` with `:latest`, semver, and `:sha-` tags; uses GHA layer cache for fast incremental rebuilds
  • Add `docker-compose.yaml` — two services: `graphify` (CLI indexing, `profile:cli`) and `mcp` (HTTP MCP server); prefers the GHCR image when present, falls back to local `build:`; supports `GRAPHIFY_API_KEY`, `GRAPHIFY_PORT`, `GRAPHIFY_IMAGE` env vars; SELinux `:Z` guidance in comments; TCP socket healthcheck (avoids SSE framing false negatives)
  • Add `Makefile` — auto-detects `docker` vs `podman`; targets: `pull`, `build`, `up`, `down`, `index [SRC=...]`, `logs`; `up` and `index` auto-pull from GHCR, fall back to local build; `make` (no target) prints colorized help with targets, variables, examples, and auto-detected runtime
  • Update `Dockerfile` — restore `graphify-mcp` as default entrypoint (backward compat); CLI accessible via `--entrypoint graphify` or compose `graphify` service; pin `mcp<2.0.0` (2.0.0 removed `mcp.types.AnyUrl` used by `graphify.serve`) and `cryptography<42` (42+ Rust `_openssl` binding crashes with SIGILL on ARM64 container runtimes)
  • Update `README.md` — expand container section with GHCR pull, `make` quick start, compose and bare docker/podman examples, Podman/SELinux notes

Test plan

  • `podman build` succeeds locally (tested on Apple Silicon)
  • `podman compose config` parses `docker-compose.yaml` without errors
  • `podman run --rm graphify --help` and `podman run --rm --entrypoint graphify-mcp graphify --help` both work
  • GHA workflow (`docker.yml`) ran green on push: build + push `linux/amd64` + `linux/arm64` in 3m 48s
  • `make pull && make index && GRAPHIFY_API_KEY=x make up` — end-to-end smoke test (`graphify extract /src --code-only --output /data` + MCP server starts on `:8080`)
  • `make` (no args) prints colorized help output

Notes for maintainers

  • `mcp<2.0.0` pin is a workaround — `graphify.serve` imports `from mcp.types import AnyUrl` which was removed in mcp 2.0.0. The HTTP transport needs updating for mcp 2.x compatibility.
  • `cryptography<42` pin avoids the Rust `_openssl` binding (introduced in 42) that crashes with SIGILL in ARM64 container runtimes (podman on Apple Silicon). Should be revisited when upstream fixes the runtime compatibility.

🤖 Generated with Claude Code

@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. 7 change(s) tested, no difference found (not proven).


Graphify review — findings

This PR adds a new GitHub Actions workflow (.github/workflows/docker.yml) that builds multi-arch (amd64/arm64) Docker images on native runners and pushes them to GHCR as a merged manifest. It also rewrites the ARCHITECTURE.md pipeline/module documentation to describe a simplified, single-function-per-stage API (e.g. build_graph, analyze, render_report, export, start_server) and trims older content. The changed-symbols list further suggests accompanying edits across many test files, extractors, skill/tooling fragments, and the changelog. Note: the diff was truncated, so the full surface area (especially the many listed test and extractor symbols) isn't visible here.

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

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 6784 functions depend on the 5529 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: _find_node() — 13 callers, 3 callees
  • worse: _emit_rescued_import() — 3 callers, 4 callees

Verification — 6784 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: 6760 function(s) in the blast radius were not formally verified this run

Formal verification

No difference found (not proven): No behavior difference found in resolve\_seed (not a proof).

The verifier ran both versions of resolve\_seed 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 \_query\_subgraph\_tokens (not a proof).

The verifier ran both versions of \_query\_subgraph\_tokens 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.

Could not verify: Could not verify \_abs\_identity.

The verifier did not have enough to check \_abs\_identity, 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: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify build.

The verifier did not have enough to check build, 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 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_from\_json.

The verifier did not have enough to check build\_from\_json, 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 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_merge.

The verifier did not have enough to check build\_merge, 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: parameter `graph_path` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify graph\_has\_legacy\_ids.

The verifier did not have enough to check graph\_has\_legacy\_ids, 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: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_infer\_merge\_root.

The verifier did not have enough to check \_infer\_merge\_root, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_load\_existing\_graph.

The verifier did not have enough to check \_load\_existing\_graph, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify merge\_raw\_extraction.

The verifier did not have enough to check merge\_raw\_extraction, 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: parameter `graph_path` is annotated `str | Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_norm\_source\_file (not a proof).

The verifier ran both versions of \_norm\_source\_file 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 \_normalize\_hyperedge\_members (not a proof).

The verifier ran both versions of \_normalize\_hyperedge\_members 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.

Could not verify: Could not verify prefix\_graph\_for\_global.

The verifier did not have enough to check prefix\_graph\_for\_global, 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 115 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).

The verifier ran both versions of \_semantic\_id\_remap 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.

Could not verify: Could not verify cached\_word\_count.

The verifier did not have enough to check cached\_word\_count, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify check\_semantic\_cache.

The verifier did not have enough to check check\_semantic\_cache, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_relativize\_source\_files\_in.

The verifier did not have enough to check \_relativize\_source\_files\_in, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_cached.

The verifier did not have enough to check save\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in generate\_call\_table\_rows (not a proof).

The verifier ran both versions of generate\_call\_table\_rows 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 \_node\_link\_payload (not a proof).

The verifier ran both versions of \_node\_link\_payload 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.

Could not verify: Could not verify write\_callflow\_html.

The verifier did not have enough to check write\_callflow\_html, 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: parameter `project` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

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 \_stamped\_manifest\_files.

The verifier did not have enough to check \_stamped\_manifest\_files, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

· 2 more finding(s) on lines outside this diff (see the check run).

@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. 7 change(s) tested, no difference found (not proven).


Graphify review — findings

This PR appears to focus on Docker packaging, documentation, and a range of extractor/pipeline behavior changes across the graphify library. - Adds a new GitHub Actions workflow (.github/workflows/docker.yml) that builds native linux/amd64 and linux/arm64 images, pushes per-platform digests to GHCR, and merges them into a single multi-arch manifest with tagging logic. - Rewrites ARCHITECTURE.md to describe a simplified single-function-per-stage pipeline and updated module signatures, and touches CHANGELOG.md (including version/changelog entries). - Changes span many library modules (watch, install, cache, extract, callflow HTML, security, dedup, resolution, skill definitions) plus their corresponding test files, indicating adjustments to extraction/resolution behavior, skill generation, and related test expectations. Surface area is broad, covering CI, docs, core extractor logic, and tests; a reviewer should verify the doc/module-signature claims against the actual code since the rewritten architecture doc diverges from some previously documented signatures.

No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 6784 functions depend on the 5529 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: _find_node() — 13 callers, 3 callees
  • worse: _emit_rescued_import() — 3 callers, 4 callees

Verification — 6784 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: 6760 function(s) in the blast radius were not formally verified this run

Formal verification

No difference found (not proven): No behavior difference found in resolve\_seed (not a proof).

The verifier ran both versions of resolve\_seed 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 \_query\_subgraph\_tokens (not a proof).

The verifier ran both versions of \_query\_subgraph\_tokens 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.

Could not verify: Could not verify \_abs\_identity.

The verifier did not have enough to check \_abs\_identity, 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: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify build.

The verifier did not have enough to check build, 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 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_from\_json.

The verifier did not have enough to check build\_from\_json, 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 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_merge.

The verifier did not have enough to check build\_merge, 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: parameter `graph_path` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify graph\_has\_legacy\_ids.

The verifier did not have enough to check graph\_has\_legacy\_ids, 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: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_infer\_merge\_root.

The verifier did not have enough to check \_infer\_merge\_root, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_load\_existing\_graph.

The verifier did not have enough to check \_load\_existing\_graph, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify merge\_raw\_extraction.

The verifier did not have enough to check merge\_raw\_extraction, 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: parameter `graph_path` is annotated `str | Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_norm\_source\_file (not a proof).

The verifier ran both versions of \_norm\_source\_file 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 \_normalize\_hyperedge\_members (not a proof).

The verifier ran both versions of \_normalize\_hyperedge\_members 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.

Could not verify: Could not verify prefix\_graph\_for\_global.

The verifier did not have enough to check prefix\_graph\_for\_global, 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 115 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).

The verifier ran both versions of \_semantic\_id\_remap 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.

Could not verify: Could not verify cached\_word\_count.

The verifier did not have enough to check cached\_word\_count, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify check\_semantic\_cache.

The verifier did not have enough to check check\_semantic\_cache, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_relativize\_source\_files\_in.

The verifier did not have enough to check \_relativize\_source\_files\_in, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_cached.

The verifier did not have enough to check save\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in generate\_call\_table\_rows (not a proof).

The verifier ran both versions of generate\_call\_table\_rows 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 \_node\_link\_payload (not a proof).

The verifier ran both versions of \_node\_link\_payload 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.

Could not verify: Could not verify write\_callflow\_html.

The verifier did not have enough to check write\_callflow\_html, 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: parameter `project` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

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 \_stamped\_manifest\_files.

The verifier did not have enough to check \_stamped\_manifest\_files, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

· 2 more finding(s) on lines outside this diff (see the check run).

slmingol and others added 2 commits August 14, 2026 23:29
 #1)

- docker-compose.yaml: two services — graphify (CLI indexing, profile:cli)
  and mcp (HTTP MCP server); prefers ghcr.io/graphify-labs/graphify:latest
  when present, falls back to local build; GRAPHIFY_API_KEY/PORT/IMAGE env
  vars; SELinux :Z guidance; TCP socket healthcheck (avoids SSE 406 false
  negatives); user:0 for podman rootless bind-mount compat
- Makefile: auto-detects docker vs podman (RUNTIME override); targets:
  pull, build, up, down, index [SRC=...], logs; up/index try GHCR pull
  first, fall back to local build; mkdir -p graphify-out guard
- .github/workflows/docker.yml: native runners (ubuntu-latest for amd64,
  ubuntu-24.04-arm for arm64) — avoids broken QEMU cross-compiled C
  extensions; pushes :latest, semver, and :sha- tags; GHA layer cache

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expand the "Shared HTTP server" section with a new "Running in a
container" subsection covering: GHCR pull (no local Python install),
make quick start, docker/podman compose examples, bare docker/podman
commands, Podman rootless and SELinux notes, GRAPHIFY_IMAGE override.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

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

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.


Graphify review — findings

This PR adds container-based distribution for graphify. It introduces a GitHub Actions workflow that builds and pushes multi-arch (amd64/arm64) Docker images to GHCR on native runners, a Makefile and docker-compose.yaml for running builds/indexing/the MCP server via Docker or Podman, and modifies the Dockerfile (new entrypoint, pinned mcp<2.0.0 and cryptography deps, general-purpose usage). The README is updated with a new "Running in a container" section documenting the make, compose, and bare docker/podman workflows.

Worth a look

  • Docker image entrypoint no longer starts the MCP serverDockerfile:44 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
  • Docker CMD changed from server launch to --help breaks existing run invocationsDockerfile:44 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 27 functions depend on the 27 functions this change touches.

Health — grade A; no new coupling hotspots.

Verification — 27 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: 27 function(s) in the blast radius were not formally verified this run

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

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.


Graphify review — findings

This PR adds container/deployment infrastructure for graphify. It introduces a GitHub Actions workflow (docker.yml) that builds and pushes multi-arch (amd64/arm64) images to GHCR on native runners, adds a Makefile and docker-compose.yaml for building/running via Docker or Podman, and reworks the Dockerfile to be a general-purpose image (changing the entrypoint, adding mcp/cryptography version pins). It also updates the README's container documentation, replacing the previous bare docker build/docker run snippet with a new "Running in a container" section covering make, docker compose/podman compose, and bare Docker/Podman usage. Surface area is limited to CI config, container/build tooling, and README documentation; no application source code changes are included.

Worth a look

  • Docker CMD/ENTRYPOINT change breaks documented docker run contractDockerfile:44 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
  • Docker image no longer starts MCP server by defaultDockerfile:44 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
  • Serve command mounts graph.json read-only at /data but earlier index writes to /data/graphify-out, path mismatchREADME.md · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 27 functions depend on the 27 functions this change touches.

Health — grade A; no new coupling hotspots.

Verification — 27 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: 27 function(s) in the blast radius were not formally verified this run

@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. 7 change(s) tested, no difference found (not proven).


Graphify review — findings

This pull request adds a new GitHub Actions workflow (docker.yml) that builds and pushes multi-arch (amd64/arm64) Docker images to GHCR on native runners, merging platform digests into a single manifest. It also substantially rewrites ARCHITECTURE.md to describe a simplified/renamed pipeline and module API surface, and trims the CHANGELOG.md. The listed symbol changes span many graphify/* modules and their corresponding tests, along with skillgen expected-output fixtures. Note: the truncated diff means the full scope of the source and test symbol changes isn't visible here, so review of those files should be done against the actual code rather than this summary.

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

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 6973 functions depend on the 6090 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 333 callers, 35 callees
  • new: _rebuild_code() — 50 callers, 48 callees
  • new: build_from_json() — 138 callers, 15 callees
  • new: detect() — 80 callers, 13 callees
  • new: deduplicate_entities() — 49 callers, 18 callees
  • new: save_semantic_cache() — 50 callers, 9 callees
  • new: _extract_generic() — 18 callers, 20 callees
  • new: extract_files_direct() — 17 callers, 20 callees
  • …and 175 more

Verification — 6973 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: 6949 function(s) in the blast radius were not formally verified this run

Formal verification

No difference found (not proven): No behavior difference found in format\_affected (not a proof).

The verifier ran both versions of format\_affected 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.

Could not verify: Could not verify resolve\_seed.

The verifier did not have enough to check resolve\_seed, 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: parameter `root` is annotated `Path | None` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_query\_subgraph\_tokens (not a proof).

The verifier ran both versions of \_query\_subgraph\_tokens 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.

Could not verify: Could not verify \_abs\_identity.

The verifier did not have enough to check \_abs\_identity, 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: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify build.

The verifier did not have enough to check build, 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 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_from\_json.

The verifier did not have enough to check build\_from\_json, 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 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_merge.

The verifier did not have enough to check build\_merge, 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: parameter `graph_path` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify graph\_has\_legacy\_ids.

The verifier did not have enough to check graph\_has\_legacy\_ids, 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: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_infer\_merge\_root.

The verifier did not have enough to check \_infer\_merge\_root, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_load\_existing\_graph.

The verifier did not have enough to check \_load\_existing\_graph, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify merge\_raw\_extraction.

The verifier did not have enough to check merge\_raw\_extraction, 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: parameter `graph_path` is annotated `str | Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_norm\_source\_file (not a proof).

The verifier ran both versions of \_norm\_source\_file 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 \_normalize\_hyperedge\_members (not a proof).

The verifier ran both versions of \_normalize\_hyperedge\_members 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.

Could not verify: Could not verify prefix\_graph\_for\_global.

The verifier did not have enough to check prefix\_graph\_for\_global, 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 115 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).

The verifier ran both versions of \_semantic\_id\_remap 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.

Could not verify: Could not verify cached\_word\_count.

The verifier did not have enough to check cached\_word\_count, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify check\_semantic\_cache.

The verifier did not have enough to check check\_semantic\_cache, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_relativize\_source\_files\_in.

The verifier did not have enough to check \_relativize\_source\_files\_in, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_cached.

The verifier did not have enough to check save\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in generate\_call\_table\_rows (not a proof).

The verifier ran both versions of generate\_call\_table\_rows 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 \_node\_link\_payload (not a proof).

The verifier ran both versions of \_node\_link\_payload 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.

Could not verify: Could not verify write\_callflow\_html.

The verifier did not have enough to check write\_callflow\_html, 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: parameter `project` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

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)

· 183 more finding(s) on lines outside this diff (see the check run).

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

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. 7 change(s) tested, no difference found (not proven).


Graphify review — findings

This pull request adds a new GitHub Actions workflow (.github/workflows/docker.yml) that builds and pushes multi-arch Docker images (amd64/arm64) to GHCR using native runners and a digest-merge step. It also rewrites large portions of ARCHITECTURE.md to describe a simplified single-function-per-module pipeline with renamed entry points (e.g., build_graph, analyze, render_report, export, start_server), and trims the CHANGELOG. The changed-symbols list further suggests corresponding renames and edits across the skill generator, extractors, and many test files. Reviewers should focus on the new CI workflow, the documentation/API-naming changes in ARCHITECTURE.md, and the wide surface area of touched test and skillgen files.

Worth a look

  • extract() signature changed from list-of-paths to single path in docsARCHITECTURE.md · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 6973 functions depend on the 6090 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 333 callers, 35 callees
  • new: _rebuild_code() — 50 callers, 48 callees
  • new: build_from_json() — 138 callers, 15 callees
  • new: detect() — 80 callers, 13 callees
  • new: deduplicate_entities() — 49 callers, 18 callees
  • new: save_semantic_cache() — 50 callers, 9 callees
  • new: _extract_generic() — 18 callers, 20 callees
  • new: extract_files_direct() — 17 callers, 20 callees
  • …and 175 more

Verification — 6973 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: 6949 function(s) in the blast radius were not formally verified this run

Formal verification

No difference found (not proven): No behavior difference found in format\_affected (not a proof).

The verifier ran both versions of format\_affected 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.

Could not verify: Could not verify resolve\_seed.

The verifier did not have enough to check resolve\_seed, 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: parameter `root` is annotated `Path | None` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_query\_subgraph\_tokens (not a proof).

The verifier ran both versions of \_query\_subgraph\_tokens 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.

Could not verify: Could not verify \_abs\_identity.

The verifier did not have enough to check \_abs\_identity, 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: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify build.

The verifier did not have enough to check build, 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 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_from\_json.

The verifier did not have enough to check build\_from\_json, 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 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_merge.

The verifier did not have enough to check build\_merge, 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: parameter `graph_path` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify graph\_has\_legacy\_ids.

The verifier did not have enough to check graph\_has\_legacy\_ids, 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: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_infer\_merge\_root.

The verifier did not have enough to check \_infer\_merge\_root, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_load\_existing\_graph.

The verifier did not have enough to check \_load\_existing\_graph, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify merge\_raw\_extraction.

The verifier did not have enough to check merge\_raw\_extraction, 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: parameter `graph_path` is annotated `str | Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_norm\_source\_file (not a proof).

The verifier ran both versions of \_norm\_source\_file 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 \_normalize\_hyperedge\_members (not a proof).

The verifier ran both versions of \_normalize\_hyperedge\_members 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.

Could not verify: Could not verify prefix\_graph\_for\_global.

The verifier did not have enough to check prefix\_graph\_for\_global, 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 115 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).

The verifier ran both versions of \_semantic\_id\_remap 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.

Could not verify: Could not verify cached\_word\_count.

The verifier did not have enough to check cached\_word\_count, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify check\_semantic\_cache.

The verifier did not have enough to check check\_semantic\_cache, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_relativize\_source\_files\_in.

The verifier did not have enough to check \_relativize\_source\_files\_in, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_cached.

The verifier did not have enough to check save\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in generate\_call\_table\_rows (not a proof).

The verifier ran both versions of generate\_call\_table\_rows 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 \_node\_link\_payload (not a proof).

The verifier ran both versions of \_node\_link\_payload 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.

Could not verify: Could not verify write\_callflow\_html.

The verifier did not have enough to check write\_callflow\_html, 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: parameter `project` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

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)

· 183 more finding(s) on lines outside this diff (see the check run).

- Restore graphify-mcp as default entrypoint (backward compat with
  pre-PR bare `docker run` usage); docker-compose.yaml graphify CLI
  service overrides to `graphify` for indexing
- Pin mcp<2.0.0: mcp 2.0.0 removed mcp.types.AnyUrl which
  graphify.serve imports for the HTTP transport (ImportError at startup)
- Pin cryptography<42: cryptography 42+ ships a Rust _openssl binding
  that crashes with SIGILL (exit 132) on ARM64 container runtimes
  (podman on Apple Silicon); 41.x is cffi-based and unaffected
- Add `make help` as default target with colorized output (targets,
  variables, examples, auto-detected runtime)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

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

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. 7 change(s) tested, no difference found (not proven).


Graphify review — findings

This PR adds a new GitHub Actions workflow (.github/workflows/docker.yml) that builds multi-arch Docker images (linux/amd64 and linux/arm64) on native runners and pushes them to GHCR as a merged manifest. It also substantially rewrites ARCHITECTURE.md, simplifying the documented pipeline stages and module signatures (e.g. buildbuild_graph, analyze helpers → a single analyze(), servestart_server) and removing the "calling extract() from your own code" section. The CHANGELOG appears to have a large block of prior release notes removed. The changed-symbols list also spans many extractor, CLI, skillgen, and test modules, so the review surface extends beyond the docs/workflow into the library and test suite. I only see the CI workflow and doc/changelog portions in the provided diff; the code and test changes referenced in the symbol list are truncated, so a reviewer should pull the full diff to assess those.

Worth a look

  • Package-write workflow uses unpinned third-party actions.github/workflows/docker.yml:30 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 6973 functions depend on the 6090 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 333 callers, 35 callees
  • new: _rebuild_code() — 50 callers, 48 callees
  • new: build_from_json() — 138 callers, 15 callees
  • new: detect() — 80 callers, 13 callees
  • new: deduplicate_entities() — 49 callers, 18 callees
  • new: save_semantic_cache() — 50 callers, 9 callees
  • new: _extract_generic() — 18 callers, 20 callees
  • new: extract_files_direct() — 17 callers, 20 callees
  • …and 175 more

Verification — 6973 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: 6949 function(s) in the blast radius were not formally verified this run

Formal verification

No difference found (not proven): No behavior difference found in format\_affected (not a proof).

The verifier ran both versions of format\_affected 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.

Could not verify: Could not verify resolve\_seed.

The verifier did not have enough to check resolve\_seed, 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: parameter `root` is annotated `Path | None` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_query\_subgraph\_tokens (not a proof).

The verifier ran both versions of \_query\_subgraph\_tokens 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.

Could not verify: Could not verify \_abs\_identity.

The verifier did not have enough to check \_abs\_identity, 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: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify build.

The verifier did not have enough to check build, 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 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_from\_json.

The verifier did not have enough to check build\_from\_json, 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 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_merge.

The verifier did not have enough to check build\_merge, 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: parameter `graph_path` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify graph\_has\_legacy\_ids.

The verifier did not have enough to check graph\_has\_legacy\_ids, 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: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_infer\_merge\_root.

The verifier did not have enough to check \_infer\_merge\_root, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_load\_existing\_graph.

The verifier did not have enough to check \_load\_existing\_graph, 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: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify merge\_raw\_extraction.

The verifier did not have enough to check merge\_raw\_extraction, 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: parameter `graph_path` is annotated `str | Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_norm\_source\_file (not a proof).

The verifier ran both versions of \_norm\_source\_file 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 \_normalize\_hyperedge\_members (not a proof).

The verifier ran both versions of \_normalize\_hyperedge\_members 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.

Could not verify: Could not verify prefix\_graph\_for\_global.

The verifier did not have enough to check prefix\_graph\_for\_global, 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 115 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).

The verifier ran both versions of \_semantic\_id\_remap 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.

Could not verify: Could not verify cached\_word\_count.

The verifier did not have enough to check cached\_word\_count, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify check\_semantic\_cache.

The verifier did not have enough to check check\_semantic\_cache, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_relativize\_source\_files\_in.

The verifier did not have enough to check \_relativize\_source\_files\_in, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_cached.

The verifier did not have enough to check save\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in generate\_call\_table\_rows (not a proof).

The verifier ran both versions of generate\_call\_table\_rows 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 \_node\_link\_payload (not a proof).

The verifier ran both versions of \_node\_link\_payload 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.

Could not verify: Could not verify write\_callflow\_html.

The verifier did not have enough to check write\_callflow\_html, 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: parameter `project` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

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)

· 183 more finding(s) on lines outside this diff (see the check run).

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