Graphify: bounded query normalization and context packets - #982
Conversation
Read the pinned graph schema directly out of a published generation, answer four bounded deterministic questions, and turn the answer into a revision-bound context packet the existing delivery path already carries. The lifecycle (#913) publishes an immutable generation; context_graph (#876) scores a delivered packet. Between them there was no way to ask the graph a question and no way to turn an answer into evidence a recipient may read. - Pinned schema read strictly from the artifact member, with node/edge kinds, dangling edges, duplicate ids, inverted spans and out-of-checkout paths all refusals rather than best-effort reads. - Symbol-first, relationship-filtered traversals for impact, dependency, symbol and related-test questions, breadth-first over sorted adjacency with explicit depth and node budgets; reaching a budget reports truncation. - Citations validated against the bound commit's tracked census and blobs, not the working tree; an unconfirmable location is dropped and reported. - Packets carry source revision, graph generation, completeness, truncation, omission codes and extracted/inferred/ambiguous confidence. - Required unavailable context blocks dependent work; optional unavailable context degrades to ordinary repository tools. - One approved packet reaches Claude, Codex and Devin through the shared delivery path with no provider tools or credentials in the recipient. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Orchestrator validation of current headHead: The new query suite ran with the repository's supported Python 3.12 environment: 40 tests, 1 failure and 2 errors. Focused Ruff passed.
The implementation checkpoint on #914 also applies: read the pinned provider's actual |
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: The patch can expose packet contents through existing output-file permissions, misstate multi-hop relationships, and silently omit seeds. It also introduces a test that necessarily raises TypeError. Findings:
|
Codex supplemental plan-conformance reviewHead:
Retain the same Claude writer and draft PR. Resolve these findings, the four Code Mower audit findings, and all three observed test failures before requesting a new exact-head review. Keep public validation and PR descriptions free of private local paths. |
CI ran the suite this lane's sandbox could not. Three real defects: - Two tests iterated a QueryResult rather than its relations. - The command test's authorization envelope expired at the fixed NOW the library-level tests use, but the command has no injected clock and authorizes against the real one, so every run was refused as expired. - The new module and doc were missing from the package materializer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Work Order: complete the review-guided repairThe first Code Mower run delivered current head Before another CI-only wait, address every substantive review item in the Code Mower Codex audit and supplemental plan-conformance comment on this PR. In particular, consume the actual pinned Graphify export and prove the executable guided-session context route, including revision/generation checks on reuse and delivery to Claude, Codex, and Devin. Correct packet-output privacy, multi-hop relationship/citation truth, seed overflow/truncation, and any remaining focused failures. Do not claim compatibility from an invented provider schema or from generic packet-format tests alone. Refresh the branch from current |
Addresses the Code Mower Codex audit at 5edb190. [P1] `--packet-out` into an existing world-readable file put the evidence behind that file's permissions: a creation mode binds only a file the open creates. The packet now goes to a freshly created private sibling and is renamed over the destination, which is also atomic. [P2] A second-hop result reported the seed as the relationship's other end, so a two-hop walk from `parse_config` through `load` to `render` asserted "render calls parse_config" and cited two nodes with no edge between them. `Relation.origin` is now the actual other endpoint of the edge that was crossed, the seed travels alongside as `Relation.seed` and reads as a "reached from" clause, and each citation is titled with the node it points at. [P2] The seed bound sliced matching definitions away silently, so a packet could report complete, untruncated evidence while never starting from some of the target's definitions. Seed overflow now sets `truncated`, raises `provider_has_more`, and counts as ambiguity. [P2] The two tests that iterated a `QueryResult` rather than its relations were already fixed at cc66dc3, as was the command test whose envelope expired at the fixed library clock. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude builder fix roundHead SHA: Code Mower Codex audit at
|
Work Order: finish actual Graphify integrationThe previous Claude writer exited normally at The source-access blocker is resolved: verified pinned public upstream files are staged read-only at Complete BOTH remaining acceptance findings: (1) replace the invented input schema with a bounded reader for actual Graphify nodes/links, source locations, relations, confidence and supported optional metadata; (2) wire an executable guided-session prepare/deliver/reuse path with revision and graph-generation checks and identical approved evidence for Claude, Codex and Devin. Add meaningful compatibility and guided-flow tests. This is existing #914 scope and requires no owner decision. Refresh from origin/main (now including #981 at |
`load_graph` required exactly `schema`/`nodes`/`edges` and a `code_mower.contextGraph.v1` declaration. The lifecycle (#913) produces no such document: it archives the pinned provider's own `graph.json`, whose exporter emits a NetworkX `node_link_data` shape -- `nodes`/`links`, Graphify source-location strings, and uppercase confidence labels. So every ordinary generation was rejected by the query command, and the adapter's strictness was strictness about a shape nobody produces. Replace it with a bounded reader for the real format, validating the *provider's* contract rather than one of ours: the required node and edge fields of its validator, its `file_type` and `confidence` vocabularies, its `L<line>` locations, and `built_at_commit` checked against the commit the generation is bound to. What the real export carries and must therefore load: exporter and extractor annotations this module does not read (`community`, `norm_label`, `confidence_score`, `weight`, `metadata`); relations outside the extractor's fixed set, since the provider's validator does not constrain `relation` and its LLM extraction emits more -- grouped as `related` so they never stand in for a `calls` claim, while the packet sentence still states the provider's own word; sourceless cross-file stubs, traversable and never citable; and non-code corpora, dropped with their links pruned as the exporter's own `prune_dangling_edges` does. Node kinds are now derived in `_node_kind` from the shape the pinned extractor emits, and named as a derivation, because the export states no such kind. Citations are one line per node rather than a span: `source_location` records no extent. Reference: Graphify-Labs/graphify at 23f2ffa (release 0.9.58), `graphify/export.py`, `graphify/validate.py`, `graphify/extractors/engine.py`. Not executed locally: no interpreter in this lane's sandbox can run a `requires-python >= 3.12` codebase. CI is the first execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude builder fix roundHead SHA: Done: [P1] Read the real pinned Graphify exportRead the staged reference at The finding was correct and the diagnosis was the important part: the old What is now validated is the provider's contract, not one of ours:
Three things the real export carries are deliberately not refusals, since rejecting them is what broke every ordinary generation:
Node kinds ( Tests in Not done: [P1] Complete the guided-session delivery routeThis is the remaining acceptance finding and it is not in this head. I ran out of wall clock before it could be written and tested honestly, and shipping a half-wired Design I got to, for whoever picks it up (me on the next dispatch, unless reassigned): the right seam is the graph as a second retrieval backend for the same packet store, not a parallel path. ValidationNot run locally. No interpreter in this lane's sandbox can run a So CI on What remains
Re-requesting audit: 🤖 Generated with Claude Code |
Work Order: complete guided Graphify sessionsThe sole remaining implementation gap is guided-session integration. The actual pinned reader at Implement Graphify as a supported local repository connection and retrieval route through the existing packet store, so an operator can use session context prepare/deliver and subsequent reuse. Preserve the same protected packet handle, authorization scope, work item and recipient contract. On every reuse/delivery, validate the requested Git revision and graph generation against current trusted local state; stale or unknown required context blocks, optional absence degrades. No Coworker SDK or Graphify credentials/tools should be needed for graph recipients. Add executable guided-flow tests demonstrating Claude, Codex, and Devin receive identical approved evidence and stale/rebuilt graphs are refused. Continue as the only Claude source writer. Remain draft, do not merge. The runner enforces a 60-minute cap externally; continue until this scoped integration is implemented, or report a concrete blocker/actual elapsed limit. The orchestrator will run canonical tests, so an unavailable local interpreter is not a reason to leave implementation unfinished or wait solely on CI. Read existing context connection/packet/session code and keep organization-context behavior intact. Keep reference staging out of the PR. Limit any docs/sessions.md edits to guided context; another lane owns Start From Any Agent and role eligibility. |
The query module answered one bounded question and minted one packet, but only a standalone command could ask. This registers the same graph as an ordinary context connection so `session context prepare`, `deliver`, and the reuse and attachment that follow reach it through the shared packet store -- same protected handle, same authorization scope, same work item and recipient contract, same delivery path. A local connection has no principal, workspace, or credential. What replaces the credential is the graph itself: authorization is re-derived from current local state on every load and every replay, and the envelope carries the published generation as its `generation`. The freshness rules then fall out of the shared packet contract rather than out of new checks -- a rebuilt graph publishes a new generation and no longer matches a packet bound to the old one, and a moved HEAD makes the published generation stale for that revision so authorization fails outright. Required context that is refused pauses the dependent work; optional context degrades. `--question` is the retrieval source for this connection and the query names the symbol or path, both explicit: guessing a target out of a work item's prose would produce confident evidence about whatever happened to match. No Coworker SDK, credential, or network call takes part, and no recipient needs the provider, its pin, or any Graphify tool to read what it is given. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude builder fix roundHead SHA: Done: [P1] Complete the guided-session delivery routeThis was the sole remaining acceptance finding. The new module is no longer wired only to a standalone
The freshness rules are the shared contract's, not new checks. Authorization is re-derived from current local state on every load and replay — never cached — from
That is deliberate: the interesting property is that a recipient cannot be handed evidence from a graph that no longer describes the code, and the cheapest way to be sure of it is to make it the same check that already refuses a revoked organization authorization. Dispatch, not duplication. Three touch points branch on the saved connection schema, read under the lock that already guards the operation:
Tests
Docs: Not verified locallyI could not execute the test suite in this builder shell — running a Python interpreter is denied by this environment's sandbox ( Remaining
Re-requesting |
|
Code Mower supervising validation at head All 416 focused context tests passed in the canonical Python 3.12 environment. CI across Python 3.12, 3.13, and 3.14 found one unit failure: the package materializer omits the new connection module. The independent Codex review produced two substantive P2 observations, but its structured artifact used invalid zero line numbers and the wrapper returned UNKNOWN. That run confers no valid review verdict; another exact-head audit is required after repair. Bounded Claude fix round, same builder and branch:
Complete these fixes within the existing ownership boundary. Preserve actual Claude builder provenance and the draft PR. Incorporate current main if it advances, resolving only owned changes and preserving concurrent session role admission. Keep the staged pinned upstream references as read-only local inputs, excluded from the PR. Run the focused graph/connection tests, package/release hygiene checks and Ruff if the runner environment permits; the supervisor will run the canonical full suite independently. Do not wait on CI instead of completing the requested fixes. Update the PR description to the final implemented behavior with honest validation, without private paths or provider transcripts. Another independent exact-head Code Mower audit and normal CI/gate are required; do not merge. |
Three repairs from the audit at `c10bba9`, all about a claim the code made without checking it. The packet's binding copied the authorization envelope's `generation` straight through. Every freshness rule in the delivery contract reads that field -- a rebuilt graph is refused precisely because the published generation moved -- so a packet bound to a generation its traversal never read would go on passing all of them. `build_packet` now refuses when the authorized generation and the queried one disagree. It does not rewrite the envelope to match: that is an authorization this module did not mint, and the disagreement is the answer. The guided route is unaffected, since `authorize_locked` and `graph_context` both read current local state; the standalone command path is where an envelope from another moment could arrive. The query tests now authorize against what is published at call time, which is what the connection actually does, rather than a literal fixed at setUp. Both provider vocabulary fields were tested for membership before being tested for type. `file_type` is looked up in a set and `confidence` in a dict, so a JSON array or object in either raised TypeError out of a reader whose callers only catch ContextError -- an opt-in feature crashing instead of reporting the graph unreadable. Both are read as bounded text first, and the graph-context outcomes are covered both ways: required blocks, optional degrades. `context_graph_connection.py` was missing from the package manifest. `context_packets` and `context_delivery` import it, so an installed package would have lost the context routes entirely. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude builder fix roundHead SHA: All three requested P2 fixes are in one commit. [P2] Include the guided graph connection in materialized packages
[P2] Bind the actual graph generation in standalone packets
It refuses rather than reconciles. Rewriting the envelope's generation to match would be this module editing an authorization it did not mint, and the live authorization checks in the guided route are untouched: The query tests were authorizing against a literal [P2] Reject malformed provider vocabulary fields through the context diagnostic
Two new tests: Tests runNone in this shell, and I want to be exact about why rather than imply otherwise: every Python interpreter invocation is refused here, so I could not execute the focused graph/connection tests, the package/release hygiene checks, or Ruff. The changes were made by reading the code and the existing fixtures. The orchestrator's canonical Python 3.12 run is the evidence that matters for this head; the prior head Remaining
Re-requesting the peer audit: needs-codex-audit. |
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: One P2 finding: attachments do not bind the actual consuming checkout. Findings:
|
…6416b1c) reserve_attachment() reconstructed the caller's request with the remote PR head and supplied that same head to authorization as the consuming revision, replacing an unknown or different caller checkout. A standalone or guided caller could therefore reserve and publish repository evidence for the PR head while actually running from a different checkout or a non-Git directory. reserve_attachment() and attach() now take an explicit consuming_revision, separate from the PR head kept as attachment metadata; it is never substituted from head, and a repository connection fails closed when it is missing. The standalone CLI derives it from --repo-path, and guided attachment reservation passes the actual checkout revision for both a fresh reservation and a pending/uncertain retry, so a retry revalidates the current consumer before marking an attachment published. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: One P2 finding: reconnect can reauthorize packets after cleanup fails. Static review only; no product code or tests were executed. Findings:
|
…aeba514) disconnect() marks the connection disconnected before attempting packet cleanup, and only reports failure as needs_attention. connect() then let a reconnect from any disconnected local graph connection write verified state straight back, regardless of whether that cleanup ever actually ran, so a surviving packet and its delivery/attachment bindings could become authorized again once the graph and approved scope matched. Reconnect now retries packet cleanup under the same connection lock as an idempotent step before writing verified state, even when the preceding disconnect reported the cleanup complete. A cleanup failure during reconnect propagates a ContextError and leaves the connection disconnected, so it stays unable to authorize or load any surviving packet.
…a514) read_binding() is a private file lookup with no authorization of its own, so its surviving the failed disconnect/reconnect pair is expected evidence that cleanup is pending, not a security check. Publish the reserved attachment before forcing both cleanup failures so the test replays the strongest path, assert the binding file still exists rather than raising on it, and assert that context_delivery.deliver() -- the actual authorization boundary -- fails for that published binding while the connection stays disconnected.
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: One P2 finding: parallel-edge identity is not preserved. Findings:
|
GraphEdge discarded the provider edge's source_file/source_location, so run_query treated call-site records that share endpoints, relation, kind and confidence as duplicates and silently dropped all but one -- possibly reporting a complete answer that was not. GraphEdge now retains the call site's path and optional line, holds it to the same citation rules as a node's, and folds it into one canonical identity used for both traversal dedup and the depth-frontier already-reported check. load_graph() and _grouped() now sort by that same full identity instead of the incomplete (kind, target, source) key, so a harmless permutation of the provider's own node or edge order can no longer change which relationships a tight budget reports. A resolvable call site is carried into packet citations under its own "call site: <relation>" title, alongside both endpoint citations; an unresolved one is dropped and reported via the existing provider_warning rather than presented as verified. Also fixes _location() accepting Unicode digits (e.g. "L²") that str.isdigit() allows but int() cannot parse, which escaped the bounded ContextError refusal as an unhandled ValueError. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: One P2 finding. Static review only; no product code or tests executed. Findings:
|
A fresh or re-minted attachment reservation that fails before publication now rolls the session back to a refreshable prepared/none state instead of stranding a permanently pending intent that every retry reauthorizes and that `prepare --refresh` alone could never clear. A saved pending/uncertain intent for an old PR head is retired before its evidence is ever reauthorized, and stale-binding cleanup is retry-safe across both of its write boundaries, with the targeted binding always identity-checked and a malformed remote head never treated as authoritative movement.
context_prepare.prepare's earliest attachment-in-progress guard only checked pending/uncertain, so a saved reserving association could reach ordinary prepare or --refresh and have its unpublished binding replaced or discarded instead of preserved for attach's cleanup. context_session .status also checked context_state failure values before the attachment branch, so a failed reservation could report optional/refresh guidance instead of attach reconciliation even though reserving proves no GitHub write ever began. Add reserving to prepare's guard alongside pending/uncertain, and move the reserving check in status ahead of the context_state failure block (pending/uncertain keep their existing failure precedence).
Codex audit (merge-authority lane)Head SHA: Codex Audit: PASS Summary: No merge-blocking regressions were found. Static review only; no code or tests were executed, and candidate bindings could not be independently verified because the workspace was empty. Runtime acceptance and exact-candidate approval are not certified. Findings: none. |
Closes #914. Part of #902.
Adds bounded local Graphify queries and revision-bound context packets to guided sessions. Users can ask symbol, dependency, impact, or related-test questions, then prepare, reuse, and deliver the same approved evidence to Claude, Codex, or Devin. Recipients need no Graphify installation, credentials, or provider access.
The source builder is Claude through Code Mower. Codex supervises validation and provides the independent peer audit. This PR remains draft for serialized roadmap acceptance; it includes no version or release change.
Query and delivery behavior
nodes/edgesdocument produced by pinned Graphify 0.9.58 with--code-only --no-cluster, preserving extractor direction. The separate NetworkX export requires directed evidence.document_limit, truncation, and partial completeness; protected fetch and guided delivery accept that bounded packet.Required lifecycle repairs
Real execution exposed gaps missed by schema fixtures. The launcher now supplies the required
.scan target in the immutable materialized working directory and collects only the expectedgraphify-outartifacts. Competing, foreign, pre-existing, and symlink output roots are refused.On macOS, derive narrowly verified native dependencies from the provider and base interpreter. Validate Mach-O container structure, slice and load-command bounds, and shared-library type before exposing a derived file. Existing ownership and path exclusions, filesystem containment, and network denial remain enforced; unsafe runtime ancestry is refused. Linux behavior is preserved.
Completeness follows the pinned input classifier, including named manifests and extensionless shebang code. Manifest hashes must match bytes supplied before launch and satisfy the pinned post-extraction writer rule. Keep digest and manifest records keyed by exact Git paths; normalization collisions remain partial, while unambiguous single-path macOS spelling differences stay supported. Unsupported code dispatch is counted separately; failures, unknown postconditions, mismatches, and unproven zero-node results remain partial.
Known provider limitations remain explicit: the evaluation's 54-file requeue had stamped manifest rows, and some module-alias cross-file call relationships are absent from extracted evidence.
Attachment recovery hardening
The final review cycle also hardened guided pull-request attachment recovery. Remote heads are validated before cleanup or publication; stale pending, uncertain, and published evidence is retired when the PR head moves; binding removal is identity checked and retry safe. Fresh attachment now records a durable
reservingstate before external reservation, moves topendingonly after reservation succeeds, blocks prepare/refresh while reconciliation is required, and gives cleanup priority in session status. This prevents failed attachment attempts from stranding a session or later publishing stale evidence.Validation
Feature head:
9216db200e636089dc6baba555d7d1d28b9cf3d5, including accepted main0b5fdcf78fe0ec7d76042fbdae5e4d3c5849c514.The Claude builder shell did not run the broker-owned qualification. Code Mower recorded Claude as the source writer; the supervisor and CI produced the test, provider, readiness, and audit evidence above.