Repository navigation
0.1a — Establish the shared SchemaPackage as the single source of truth for all five config/artifact schemas #15
Description
Note
🔒 Decision update — 2026-06-27
Critical-path root of Phase 0′. Land to main fast — this blocks #16, #17, #18, which branch from main once it merges (do not rebuild a deep stack like the β rollout).
Owner: @Rchari1 · Reviewer: @jack-champagne
Locked (already reflected below): five schemas incl. SolveSpec (owned here, assembled in amico-run); manifest.toml/result.toml reverse-engineered from β's actual emissions — formalize, don't fork.
Added — execution-environment contract (from the #24 Piccolissimo binary recheck). The schemas must make the runtime contract machine-checkable, not only the config/artifact shapes. On the shipped Piccolissimo binary, #24's template is non-functional — using Piccolo/Piccolissimo/CairoMakie all fail and JLD2.save crashes the run — yet it "passed" against a source Pkg.develop env, because nothing declares what the lab runtime provides vs what the solve requires. So v0 of the frozen contract must carry that dimension:
- SolveSpec declares what the solve requires — packages/symbols, load mechanism, julia patch.
- lab.toml declares what the lab runtime provides — image/depot ref, julia version, loadable packages, license presence.
validate()cross-checksrequires ⊆ provides, so a DRAFT feat(template): verified Piccolissimo solve template (beta target) + integration contract #24-class mismatch fails at validate-time, before any run.- The Julia round-trip AC is exercised against a real binary-backed runtime (the shipped pkgimage), not only a source env — that source-only blind spot is exactly what let DRAFT feat(template): verified Piccolissimo solve template (beta target) + integration contract #24 self-report "verified."
Important
Problem
The run-dir contract and every config artifact (lab.toml, SolveSpec, manifest.toml, result.toml, catalog-entry) is the universal interface every surface reads, but its shape lives only as prose plus β.1's emitter code. β has no validation layer at all (plan §2), so the contract is unenforced: the extension, the CLI, CI, and the Julia emitter can each interpret it differently and drift independently. There is no machine-checkable definition and no single schema_version carrier, so a contract bump silently breaks old runs.
Approach
Stand up the shared workspace SchemaPackage as the single source of truth: JSON Schemas for all five artifacts — lab.toml, SolveSpec, manifest.toml, result.toml, catalog-entry — each carrying schema_version, behind one validate() consumed by the extension, the amico-run CLI, and CI, plus a Julia round-trip that validates emitted artifacts against the same schemas. This formalizes the run-dir contract β.1 currently emits as prose + emitter-code into a versioned, machine-checkable schema set. The FINISHED terminal sentinel ({status, exitCode}) is captured as a sub-shape of the run-dir contract, not as a sixth peer schema with its own schema_version.
Approaches Considered
- Shared workspace package, JSON Schema as source of truth, one
validate()for TS + a Julia round-trip check (chosen — kills cross-language drift at one seam) - No shared layer, keep the contract as prose + β.1 emitter code — rejected: that is the β status quo with no machine check and no version gate
- TS-types-only with
zod/io-ts— rejected: not consumable by the Julia emitter, so the cross-language drift risk (the whole point) survives
Scope
In: JSON Schemas for lab.toml, SolveSpec, manifest.toml, result.toml, catalog-entry; schema_version on each; validate(artifact, kind); the FINISHED {status, exitCode} sub-shape of the run-dir contract; package wired as a workspace dep for extension + CLI; CI fast-tier validation of committed fixtures + a Julia round-trip check of emitted manifest.toml/result.toml.
Out:
- The
lab.tomlon-load field-precise-error UX (S17) — sibling slice 0.1b owns that distinct on-load behavior; thelab.tomlschema itself is defined here. - CatalogStore / any catalog-entry consumer — Phase 3 (3.1); the entry SCHEMA is defined here, the STORE is not.
- HDF5 export schema/shape beyond what the catalog-entry references — Phase 3 (3.2) owns the export API.
- Schema-version migration transforms / upgrade maps — only the
schema_versionfield lands now; maps land when the first bump happens.
Assumptions / Open Qs
- β.1's emitted
manifest.toml/FINISHED/result.tomlare the authoritative shapes to encode (this slice formalizes the contract β.1 emits; it does not redesign it). - SolveSpec's schema is owned by SchemaPackage as the single source of truth; amico-run assembles a SolveSpec and calls
validate()on it — it does not own the schema. - JSON Schema (not a TS-only validator) so the same files drive the Julia round-trip — resolves Q86's cross-language home.
Acceptance Criteria
- A well-formed
lab.toml, SolveSpec,manifest.toml,result.toml, and catalog-entry each validate against their schema via the package's singlevalidate()entrypoint. - A malformed artifact (missing required key, wrong type, out-of-range value, unknown key) fails validation with an error that names the offending key and its path.
- Every schema carries a
schema_version; an artifact whoseschema_versionis absent or unrecognized is rejected (not silently accepted as current). - A
manifest.tomlwhoseFINISHEDfield carries a{status, exitCode}outside the allowed terminal-status set fails validation against the run-dir-contract shape. - The CI fast tier validates committed fixtures for all five artifacts against the package and fails the PR on any nonconformance.
- A Julia-emitted
result.tomlandmanifest.tomlround-trip: emitted by the solver path, then validated against the SAME schema files in CI (no second, transcribed copy). - SolveSpec's
requires(packages + symbols, load mechanism, julia patch) and lab.toml'sprovides(image/depot, julia version, loadable packages, license) are schema-validated;validate()rejects a SolveSpec whoserequiresare not satisfied by the target lab'sprovides— e.g. the DRAFT feat(template): verified Piccolissimo solve template (beta target) + integration contract #24 case (requiresusing-loadable Piccolissimo + CairoMakie; lab provides an image-only depot without CairoMakie) fails before any run. - The Julia round-trip is exercised against a real binary-backed runtime (the shipped pkgimage), not only a source
Pkg.developenv, so an artifact that validates only under source is caught. - The extension and amico-run both obtain validation only from this package's
validate(); a consumer that reintroduces its own schema definition is a regression. - The catalog-entry schema validates a representative promote-shaped record even though no store consumes it yet.
Key Decisions
- JSON Schema is the single source of truth; one
validate()shared across CLI, extension, CI, and a Julia round-trip (PRD Schema-package decision; resolves Q86). - All five schemas (
lab.toml, SolveSpec,manifest.toml,result.toml, catalog-entry) live in SchemaPackage; SolveSpec is validated here even though it is assembled in amico-run. schema_versionlives on every artifact;manifest.tomlis the per-run version + provenance carrier.FINISHEDis the run-dir's terminal sentinel ({status, exitCode}), modeled as a sub-shape of the run-dir contract, not a peer schema with its ownschema_version.- This package formalizes β.1's prose + emitter-code run-dir contract into versioned machine-checkable schemas; it supersedes the informal contract, not any pre-existing validators (β has none).
- catalog-entry schema is in-scope here; its store/consumer is Phase 3.
Constraints & Invariants
- The schemas must encode β.1's emitted run-dir contract exactly — formalize, do not fork it.
- One validator path only: a consumer adding its own schema definition is a regression.
- The Julia round-trip must use the same schema files as the TS consumers, not a transcription.
Prior Art / Patterns
SchemaPackage (the deep module rooted at the top of the dependency order). The run-dir contract artifacts: manifest.toml, result.toml, the FINISHED terminal sentinel; plus the lab.toml, SolveSpec, and catalog-entry config schemas. The amico-run SolveSpec builder (assembles a SolveSpec, then calls the shared validate()). The RunsManager / RunInspector / CatalogStore as downstream consumers of these shapes. The Julia round-trip validator pattern (emit-then-validate against the shared schemas in CI).Source
Phase 0-prime task 0.1 (SchemaPackage, 6 pd), sliced to 0.1a (the full five-schema SET + shared validate() + CI fast-tier conformance + Julia round-trip); plan-20260603 §3 Phase 0'. Realizes S14 (versioned schema per config/artifact), S15 (machine-checkable validate()), S16 (CI conformance + Julia round-trip of emitted artifacts). Resolves Q86 (schema home / cross-language sharing). Partially resolves Q87 (per-artifact schema_version on all five artifacts; migration maps deferred to the first bump). Related: #1 (β.1 emits the contract this formalizes), sibling 0.1b (the S17 lab.toml on-load field-precise error UX), 3.1 CatalogStore (the catalog-entry consumer, Phase 3).
Notes
This slice is the dependency-graph root for Phase 0-prime — its consumers (lab.toml on-load UX 0.1b, CatalogStore 3.1) gate on it. The plan treats 0.1 SchemaPackage as one 6-pd task; this split keeps the full schema SET (all five, incl. SolveSpec) + shared validate() + CI conformance + Julia round-trip together in 0.1a as one coherent vertical slice, and carves out ONLY the distinct S17 lab.toml on-load field-precise-error behavior as 0.1b. Do not merge them and do not pull the catalog STORE or HDF5 export forward.
Testing
TDD applies (RED->GREEN): write failing validation cases first. Extend the CI fast tier's schema-validation suite (committed-fixture conformance across all five artifacts + the Julia round-trip emit-then-validate); add the per-field/per-constraint negative matrix asserting the error names the offending field. No prescribed test names — the tdd loop decides structure.
Activity
fix(schema): 0.1a review — rename run-dir header manifest.toml → run.…
Merge pull request #28 from harmoniqs/rchari/0.1a-schema-package