commonsformat-resolver
A resolver for Commons Format module dependencies. Takes a root module and produces a merged virtual spec by walking the dependency graph and applying the merge algorithm defined by the format.
Premise
The resolver takes a root module and the set of modules
reachable from it via depends_on and
extends, and produces a merged virtual spec
per the merge algorithm defined by the format.
The resolver does not fetch dependencies from disk or network. It operates on already-loaded modules. Fetching is a separate concern handled by the fetcher (a separate spec module). The resolver assumes its input is a set of parsed modules and a description of which depends on which.
The resolver does not generate implementations. The merged virtual spec it produces is the contract a generator consumes; what the generator does with it is the generator's concern.
Interface
A resolver is a function or component that takes:
- A root parsed module
- A function or interface that, given a dependency declaration (spec URL, version constraint, optionally commit), returns the resolved parsed module for that dependency
And returns either:
- A merged virtual spec — a structure containing the unioned intent, constraints, anti-patterns, examples, threat-model content, evals, and properties from all modules in the dependency closure, with the merge algorithm's rules applied
- A resolution error — a structured description of why merging failed (cycle, conflict, unmet constraint, etc.)
The resolver is implementation-language-agnostic. The structure of the merged virtual spec and the resolution error is defined by the format; how a particular implementation represents them is the consumer's choice.
Schema
This module ships a schema.sql declaring the
shape of its data. Per §8
this is a shape commitment, not a storage commitment — the
generated implementation chooses a runtime representation
appropriate to its intent.
-- This DDL describes data shape, not storage. Runtime representation
-- is implementation-defined; choose what is appropriate to the target
-- language and the module's intent. The resolver is a pure function
-- over already-parsed modules; the tables below are the shape of its
-- inputs and outputs, not a persistent store.
CREATE TABLE graph_nodes (
module_id TEXT NOT NULL PRIMARY KEY,
spec_url TEXT NOT NULL,
version TEXT NOT NULL,
resolved_commit_sha TEXT
);
CREATE TABLE graph_edges (
from_module TEXT NOT NULL,
to_module TEXT NOT NULL,
edge_kind TEXT NOT NULL CHECK (edge_kind IN ('depends_on', 'extends')),
PRIMARY KEY (from_module, to_module, edge_kind),
FOREIGN KEY (from_module) REFERENCES graph_nodes (module_id),
FOREIGN KEY (to_module) REFERENCES graph_nodes (module_id)
);
CREATE TABLE merged_specs (
root_module TEXT NOT NULL PRIMARY KEY,
merged_intent TEXT NOT NULL,
merged_avoid TEXT NOT NULL,
merged_threat_model TEXT NOT NULL,
root_interface TEXT NOT NULL,
completed_at_ms INTEGER NOT NULL
);
CREATE TABLE merged_constraints (
root_module TEXT NOT NULL,
constraint_name TEXT NOT NULL,
description TEXT NOT NULL,
contributing_module TEXT NOT NULL,
PRIMARY KEY (root_module, constraint_name),
FOREIGN KEY (root_module) REFERENCES merged_specs (root_module)
);
CREATE TABLE merged_examples (
root_module TEXT NOT NULL,
example_name TEXT NOT NULL,
content TEXT NOT NULL,
contributing_module TEXT NOT NULL,
PRIMARY KEY (root_module, example_name),
FOREIGN KEY (root_module) REFERENCES merged_specs (root_module)
);
CREATE TABLE merged_eval_cases (
root_module TEXT NOT NULL,
case_name TEXT NOT NULL,
case_class TEXT NOT NULL CHECK (case_class IN ('functional', 'adversarial', 'generator_adversary')),
contributing_module TEXT NOT NULL,
PRIMARY KEY (root_module, case_name),
FOREIGN KEY (root_module) REFERENCES merged_specs (root_module)
);
CREATE TABLE merged_properties (
root_module TEXT NOT NULL,
property_name TEXT NOT NULL,
property_value TEXT NOT NULL,
contributing_module TEXT NOT NULL,
PRIMARY KEY (root_module, property_name),
FOREIGN KEY (root_module) REFERENCES merged_specs (root_module)
);
CREATE TABLE applied_overrides (
root_module TEXT NOT NULL,
element_path TEXT NOT NULL,
override_value TEXT NOT NULL,
declaring_module TEXT NOT NULL,
PRIMARY KEY (root_module, element_path),
FOREIGN KEY (root_module) REFERENCES merged_specs (root_module)
);
CREATE TABLE resolution_errors (
root_module TEXT NOT NULL,
ordinal INTEGER NOT NULL,
error_kind TEXT NOT NULL CHECK (error_kind IN ('cycle', 'conflict', 'unmet-version', 'missing-dependency', 'merge-bound-exceeded')),
element_path TEXT,
contributing_modules TEXT,
detail TEXT NOT NULL,
PRIMARY KEY (root_module, ordinal)
);
What the resolver does
walks-dependency-graph— starting from the root, discovers all reachable modules via depends_on and extends declarationsdetects-cycles— a cycle in the dependency graph is detected and reported as a resolution errorresolves-version-constraints— for each dependency declaration, selects the highest version satisfying the constrainttopological-merge-order— contributions to the merged spec are applied in topological order from leaves to rootdetects-conflicts— conflicts in constraints, evals, properties, or examples are detected and reportedapplies-overrides— explicit overrides declared in dependency entries resolve conflicts as specifiedproduces-deterministic-output— the same input graph produces byte-identical merged virtual specs
What the resolver does not do
- Fetching from network or filesystem. The resolver receives modules already loaded; fetching is the fetcher's responsibility.
- Parsing modules. Modules are already parsed when handed to the resolver.
- Running evals. The merged virtual spec contains evals; running them is the eval runner's job.
- Generating implementations. The merged virtual spec is input to a generator; the resolver does not invoke generation.
- Modifying input modules. Modules passed in are read-only inputs; the merged virtual spec is a new structure.
Merge contributions
The merge algorithm is specified by format-spec §11.4. The resolver implements that algorithm; the format is canonical.
The resolver-specific concerns — how conflicts are reported, how overrides are applied, how the merge result is structured for downstream consumption — are described in subsequent sections of this spec. Those are implementation contracts the resolver adds on top of the format's algorithmic specification.
Conflicts and overrides
A conflict arises when two contributing modules declare the same named element with different content. Conflicts are detected during merging and reported with enough information to identify which modules contributed the conflicting elements.
A depending module may declare overrides for a specific dependency:
[[depends_on]] spec = "..." version = "..." [depends_on.overrides] constraints.uses-redis = "ignore" properties.requires_logging = false
Overrides resolve conflicts at merge time. An overridden element is either omitted from the merge ("ignore") or replaced with the override's literal value. Overrides are recorded in the merge result so consumers auditing the contract can see what was suppressed.
Conflicts without overrides cause the resolver to return a resolution error. The merge does not partially complete and proceed with conflicts unaddressed.
Extension semantics
A module declaring extends follows the same
merge logic as depends_on with two
differences:
- The extending module's content takes precedence on conflicts without requiring explicit overrides for every conflicting element
- The extending module's evals are unioned with the parent's; an implementation passing the extended module's evals also semantically passes the parent's evals (since the parent's evals are now part of the extended suite)
A module may extend at most one parent. Extension does
not preclude declaring additional depends_on
dependencies; extension is one edge in the graph,
depends_on declarations are other edges.
Threat model
The resolver operates on potentially adversarial dependency graphs. A malicious module could attempt:
- Pathological cycles (very long, indirect)
- Pathological diamond conflicts that obscure their conflict nature
- Pathological merge expansions where the merged spec is much larger than any individual contributor (decompression-bomb style)
- Override declarations that hide conflicting content from auditing
The resolver mitigates by:
- Detecting cycles regardless of length
- Reporting conflicts with full provenance (which modules contributed what)
- Bounding merge output relative to inputs (refusing to produce merged specs above a configurable size limit)
- Recording overrides explicitly in merge output so they cannot be hidden
A buggy resolver that produces incorrect merges silently is the worst failure mode. Consumers depending on the merged spec to generate implementations would generate against a contract that isn't what they declared. The resolver must be correct or fail loudly; it must not be subtly wrong.
Verification
This module's eval suite tests the resolver on synthetic dependency graphs with known expected merges. Cases cover the merge algorithm itself, conflict detection, override application, cycle detection, and version constraint resolution.
A consumer generating a resolver implementation iterates against this module's evals until conformance. They then have a working resolver that can be composed with their parser and eval runner to form a complete Commons Format toolchain.