Skip to content

Validation and lifecycle

Package validation is the trust boundary between files present in the repository and packages that the MCP server is willing to catalog and serve.

A directory is not accepted merely because it contains syntactically valid JSON. The loader and validator establish filesystem integrity, manifest/artifact integrity, profile binding, delivery decoding, semantic consistency, topology, counts, capabilities, rights metadata, and terminal validation state.

Package states

%%{init: {"themeVariables": {"fontSize": "18px"}}}%%
stateDiagram-v2
    [*] --> pending: newly built package
    pending --> passed: valid + persisted validation
    pending --> failed: invalid + fail policy
    pending --> quarantined: invalid + quarantine policy
    passed --> passed: catalog read-only revalidation
    failed --> failed: terminal and excluded
    quarantined --> quarantined: terminal and excluded
Hold "Alt" / "Option" to enable pan & zoom

Supported validation.status values are:

State Meaning for catalog startup
passed Eligible only if current read-only revalidation also succeeds
pending Not queryable; valid pending packages remain excluded until a terminal transition is persisted
failed Terminal and excluded
quarantined Terminal and excluded

A terminal status requires validatedAt; pending may not declare it.

Validation pipeline

%%{init: {"themeVariables": {"fontSize": "18px"}}}%%
flowchart TB
    A[Safely discover framework/snapshot directory] --> B[Read + validate package_manifest.json]
    B --> C[Verify exact package tree]
    C --> D[Load exact profile + verify profile SHA-256]
    D --> E[Verify all declared artifact checksums]
    E --> F[Validate detailed validation report]
    F --> G[Decode delivery JSONL]
    G --> H[Validate graph semantics + topology]
    H --> I[Reconcile counts + capabilities + report]
    I --> J{Any error finding?}
    J -- No --> K[Valid outcome]
    J -- Yes --> L[Invalid outcome]
Hold "Alt" / "Option" to enable pan & zoom

The stages are deliberately separate so the runtime never relies on one upstream validation flag as a substitute for current integrity checks.

Two validation tiers

A package carries only runtime files, so validation runs in two tiers.

Tier Runs in Reads
Runtime Server startup, kgfegmcp-validate-packages --runtime-only, and first in every validation Only the declared package files
Evidence kgfegmcp-validate-packages without --runtime-only, after the runtime tier passes The package plus the archived full evidence restored under data/input_artifacts/

The runtime tier runs the pipeline above. It also checks that:

  • the evidence archive record parses and agrees with the declared files it marks runtime: true;
  • the LP normalization receipt binds every other file in the record by hash;
  • the slim LP index binds each slim shard's path, entry count and SHA-256, and names the same original map and original index hashes as the record;
  • every slim shard parses, each edge sits in its correct shard, no edge repeats, the shard edges equal the package's LP delivery edges, and each record's identity, endpoints and decision match its delivery edge; and
  • the per-edge judgment projections and coverage counts that progression tools use can be built.

Slim AS and LC entity provenance files are checked by checksum only. The runtime tier never opens a file under data/input_artifacts/, so a fresh clone starts and serves all packages without the archive.

The evidence tier proves that the runtime files are a faithful cut of the full evidence. It:

  1. checks each archived file's size and SHA-256 against the record;
  2. runs the full LP checks below on the full evidence;
  3. derives the slim index, slim shards, slim AS and LC files and the record again, and requires them to match the package files byte for byte;
  4. checks that every delivered standard with a CASE UUID and every learning component has exactly one slim provenance entry; and
  5. requires the judgment projections and coverage built from full evidence to equal those built from the runtime files.

Only then can a pending package become passed.

Outcome Finding or error Effect
An archived file is absent or unreadable evidence_archive_unavailable (exit 2) Nothing is persisted; the package stays pending
An archived file's size or hash differs evidence_archive_checksum_mismatch Blocking finding; when persisted, a pending package becomes failed or quarantined under the policy
A full LP check fails The LP check's own finding Same
A slim file differs from its derivation slim_derivation_mismatch Same
Slim entity provenance misses or adds an entry slim_entity_coverage_mismatch Same
Runtime and full-evidence projections differ slim_projection_mismatch Same

Use --runtime-only to run the same checks as startup when the archive is not restored. It never changes validation status. See CLI commands.

Filesystem discovery and tree integrity

Before graph content is trusted, repository/package discovery checks:

  • framework and snapshot directory names;
  • snapshot namespace agreement with framework ID;
  • directory containment beneath the configured root;
  • real directories/files rather than symbolic links;
  • case-insensitive name collisions; and
  • safe manifest and artifact paths.

The package tree must then match package_manifest.json exactly. Missing declared files, undeclared files, and undeclared directories are findings.

Manifest and artifact integrity

The loader validates the strict manifest contract and then verifies every declared artifact:

  • safe package-local path;
  • regular-file status;
  • exact SHA-256 checksum;
  • exact declaration/checksum closure; and
  • stable integrity observations used to detect changes before persistence.

The manifest itself is parsed as one UTF-8 JSON object with duplicate-key rejection.

Profile integrity

The loader resolves the exact profileId and profileVersion from the central profile repository and checks the exact-byte profile SHA-256 against the manifest.

A package therefore cannot silently start using newly edited profile semantics while retaining the old accepted manifest identity.

Detailed validation report

When declared, as_validation_report.json must satisfy the supported acceptance contract. In particular:

  • passed must be the exact boolean true;
  • errors must be an empty array;
  • validation_checks must be a non-empty array of unique non-blank strings; and
  • object_counts must include the required Learning Commons delivery graph counts.

Required report count names are:

learning_commons_framework_nodes
learning_commons_item_nodes
learning_commons_relationships
learning_commons_unresolved_fallback_relationships

The server later compares report counts with independently observed delivery facts and manifest counts.

Delivery decoding

The decoder reads one physical JSONL line at a time and validates strict node or relationship envelopes. It then decodes known string encodings such as exact boolean strings and embedded JSON arrays.

Malformed UTF-8, invalid JSON, invalid envelopes, or invalid encoded properties produce typed findings. The decoder does not perform source repair.

Graph and semantic validation

After successful decoding, validation checks include:

  • unique and consistent node identifiers/labels;
  • framework and profile semantics;
  • item statement types and normalized classifications;
  • grade/stage mappings;
  • statement-code policy;
  • relationship identity and endpoint agreement;
  • relationship endpoint existence;
  • hierarchy acyclicity;
  • root reachability;
  • root-parent rules;
  • ordinary parent type/cardinality policy;
  • multi-parent policy;
  • unresolved/fallback-parent representation; and
  • consistency of counts and capabilities.

A tree-shaped profile and a multi-parent DAG are validated against different profile rules rather than against one hard-coded hierarchy assumption.

Learning progression validation

Both tiers check stored ID/type/endpoint agreement, standard-only CASE endpoints, no self edges, duplicate IDs/typed edges or conflicting pair judgments, canonical relates ordering, per-type counts and separate buildsTowards cycle detection. They do not use relatesTo as ancestry. AS/LC report counts are compared to the AS/LC subgraph.

The evidence tier adds the full LP checks: the archived split JSONL, final claims, original provenance map, original 64-shard index and their union, report, unresolved data and normalization receipt must agree with the delivered edges and with each other's hashes, and LP report counts are compared to LP edges. Mandatory missing evidence is an error, not a fallback.

Warnings, eligibility limits and unresolved/needs-review claims remain inspectable but are not accepted edges. A valid zero-edge declared LP package is available with empty results; an undeclared LP capability is unavailable. Passed structural/process validation does not certify pedagogy or publisher endorsement.

Count and capability reconciliation

The validator derives graph facts independently:

  • coded item count;
  • multi-parent target count;
  • text-item count; and
  • unresolved relationship count.

Those facts are reconciled with manifest counts, package capabilities, profile code policy, and the detailed validation report. A manifest cannot declare a capability that conflicts with the accepted graph/profile facts.

Persisted validation transitions

The validator can persist a status transition only when all of the following are true:

  1. the observed manifest state is pending;
  2. the caller did not request read-only validation;
  3. the loader retained the integrity context needed for a safe transition;
  4. the package/profile state is verified unchanged after validation; and
  5. no blocking condition suppresses persistence.

Before replacement, the repository checks that the manifest bytes are still the same. The terminal update is atomic and is required to change only the validation state and timestamp, not package-defining fields.

Terminal manifests are revalidated read-only; they are not rewritten by catalog startup.

Invalid-package policy

KGFEGMCP_INVALID_PACKAGE_POLICY controls the target status for an invalid pending package and catalog behavior for that invalid candidate:

Policy Validation target Catalog behavior
fail failed Invalid pending package causes catalog construction to fail
quarantine quarantined Invalid pending package is excluded from the catalog

This policy never makes an invalid package queryable.

A valid pending package is also not queryable until it has a persisted terminal passed state.

Catalog acceptance is stricter than terminal status alone

Catalog construction validates every discovered candidate with persistence disabled. Only a package whose observed status is passed and whose current validation outcome is still valid becomes a CatalogPackageRuntime.

Important consequences:

  • failed and quarantined packages are excluded;
  • valid pending packages are excluded;
  • a passed package that now fails revalidation is a catalog integrity error and stops startup;
  • malformed candidates whose status cannot be trusted stop safe catalog construction; and
  • if no terminal passed packages remain, startup fails instead of publishing an empty catalog.

Passed means accepted bytes plus current revalidation

Do not interpret validation.status == "passed" as a permanent bypass of integrity checks. The server revalidates terminal passed packages read-only every time it constructs the catalog.

See CLI commands for the validation command workflow.


Next: Rights and provenance