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
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]
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:
- checks each archived file's size and SHA-256 against the record;
- runs the full LP checks below on the full evidence;
- 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;
- checks that every delivered standard with a CASE UUID and every learning component has exactly one slim provenance entry; and
- 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:
passedmust be the exact booleantrue;errorsmust be an empty array;validation_checksmust be a non-empty array of unique non-blank strings; andobject_countsmust 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:
- the observed manifest state is
pending; - the caller did not request read-only validation;
- the loader retained the integrity context needed for a safe transition;
- the package/profile state is verified unchanged after validation; and
- 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:
failedandquarantinedpackages are excluded;- valid
pendingpackages are excluded; - a
passedpackage 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