feat: Helm-first unified IaC backend (L1–L3, filesystem and Neo4j) - #2
Merged
Conversation
Order load, detect, parse, resolve, profile and evaluate behind one orchestrator, bound per-artifact parsing and per-profile rendering by --jobs without letting completion order reach any identity, and emit the canonical analysis document after validating it against both the model invariants and the embedded schema. An unusable configuration and, under --strict, any error-severity diagnostic fail the analyzer only after the partial model has been written. Graph input and graph emission report their own deterministic unavailability until the Neo4j boundary lands.
Validate the --config selector and reject input for --emit schema in options, so both are refused before any work and with one mode-neutral message. Publish analysis.json at 0644 rather than the temporary file's 0600, and build the schema payload without writing into the embedded buffer's spare capacity. A profile that cannot be set up for rendering is now that profile's diagnostic on the chart it targets instead of the whole evaluation's failure, matching how the parse phase isolates one artifact. The phase-order test now observes the load event it was asserting on.
Project a validated L3 analysis into the exact node and relationship rows the accepted Neo4j catalog describes. The allowlist of labels, property names and types, ownership and endpoint families is parsed from the embedded schema.neo4j.json rather than derived from Go types, and every projected row is checked against it, so the graph vocabulary can only be the accepted contract's. Artifacts stay one progressively typed node: neutral source, hash, path and format remain unclaimed, an IaC facet adds only its labels and its namespaced iac_*/helm_* properties, and nodes this analyzer creates in full carry unprefixed producer metadata. Structured values become deterministic *_json strings because Neo4j properties cannot nest; scalar arrays stay native lists. Secret data reaches the graph as key names and digests only. Render deterministic Cypher and write directly over Bolt from the same statements, so a script and a live write cannot drift. Nothing from the analysis becomes Cypher syntax: values travel as parameters, and the only spliced names are catalog labels, relationship types and property names, all proven to be bare identifiers when the catalog compiles. Reconciliation defaults to non-destructive upsert. Eager operation gives back only this producer's stale facts for the selected application: owned nodes, IaC facet labels, namespaced properties and IAC_* relationships. Artifact, ConfigKey, Package, Application, HAS_ARTIFACT and DEFINES_CONFIG are refused at the write boundary as well as at planning time. One managed write transaction per generation means a failure leaves the graph as it was. Filesystem input bound for a graph is wrapped so an Artifact the graph holds at a different hash stays raw and diagnosed instead of being enriched from text the graph disagrees with.
Constraint creation ran its statements without consuming their results and discarded the session's close error, so a failure the server raised after acknowledging RUN would have been lost and the generation would then have written without the uniqueness constraints that make MERGE-by-id idempotent. Constraint creation now checks the acknowledgement, consumes the statement, and reports the session close error. guardPlan checked label, property and relationship removals but never node deletion, so a plan naming a shared Artifact ID was applied. Plans now carry the labels each deleted node was observed with, and the write boundary refuses any deletion those labels do not prove is wholly this analyzer's, including one that carries no labels at all. Both secret-plaintext tests searched for a canary the fixture never fed in, so they held against a projector that leaks. The canary is now real analysed text in the template artifact's source and the rendered Secret carries its digest; the tests pin the one property that may contain it and require every derived property, and the Cypher script outside that one literal, to carry the digest instead. Also: helm_roles no longer aliases the model's slice, the fixture render uses a phase the accepted schema declares, and the vocabulary test now requires the removal set to be empty rather than tolerating a desired property in it.
Rendering a chart with a values.schema.json performed outbound HTTP and arbitrary local file reads: chartutil.ToRenderValues unconditionally calls Helm's ValidateAgainstSchema, whose compiler carries http, https and file loaders, and the analyzer's denyExternalSchemaLoader guarded only the L1 diagnostic compiler. The render now skips Helm's validation and runs the same check on the analyzer's own compiler, which refuses every external reference. Render diagnostics no longer embed the SDK's error text. Values-schema and goccy errors quote failing values and source lines, which broke README's absolute claim that nothing derived from a render reproduces template plaintext; a failed render now reports the phase, the profile and the chart, the way the decode path already did. A binary file in a repository was an error-severity IAC_SOURCE_NOT_TEXT, so --strict failed on any real workspace, and the artifact written to the graph with no source and a raw digest came back as IAC_GRAPH_SOURCE_HASH_MISMATCH against a row the analyzer itself wrote. It is a warning in both input modes now, and the graph reads such a row back as the same ineligible raw artifact. One version string is threaded from main through core into the analysis document, stamped by a new `make build` through -ldflags. Also: delete the unwired parseConfig, order relationship projection, set EvaluationInput.TempRoot explicitly and drop its unread Artifacts field, discriminate ingest diagnostics that have no workspace-relative path, and document that --emit cypher does not consult the target graph.
4 tasks
This was referenced Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements codellm-devkit/.github#52 delivery slice:
codeanalyzer-iacHelm-first backend through L3, filesystem and Neo4j inputs, JSON/Cypher/Neo4j projections with exact parity.Closes #1.
What is in this branch
e127901(schema.json,schema.neo4j.jsonbyte-identical to the embedded copies).os.OpenRoot) and paged Neo4j ingestion with SHA-256 verification.--config), isolated Helm 4.2.4 SDK rendering (no network, no cluster, no fetch, no external$ref), Kubernetes decode with hash-only Secrets.--jobsbyte-identical), compact JSON validated against the embedded schema, Cypher script, transactional Neo4j reconciliation with producer-scoped ownership and eager cleanup guard.helm template4.2.4 as an independent oracle; fuzz targets; CI.Gates on
fcc32d6go vet,go test ./...,go test -race ./...,make schema-check,make fuzz-smoke,-j 1vs-j 8byte-identical (JSON and Cypher), acceptedcheck_iac.pyon L3, Neo4j parity/e2e/security withCI=true, reconcile integration,make test-live.Known follow-ups
live.ymlpinscodeanalyzer-schema@e127901, which must be pushed before the remote live lane can run..claude/SCHEMA_DECISIONS.md; deferred minors are listed in the review ledger and will be filed just-in-time.