DGR-018 evidence — canonical Ralph and Gitea metadata schema
Completed: 2026-07-16
Branch: ralph/distributed-gguf-runtime
Authority: .scratch/distributed-gguf-runtime/prd.json
Dependency: DGR-017 (evidence/DGR-017/README.md) — cleaned backlog reconciled to origin/master; no old pass state transferred.
Objective
Make prd.json the validated source from which Markdown (and, later, Gitea) issues
can be generated losslessly, per
.scratch/distributed-gguf-runtime/issues/018-define-canonical-ralph-and-gitea-metadata-schema.md.
Pre-existing state found (not caused by this story)
Before any change in this session, git status showed .scratch/distributed-gguf-runtime/prd.json
already modified in the working tree relative to HEAD (commit 369b207), with no corresponding
progress-log entry. Diffing against HEAD showed the working copy had dropped prd.json's
top-level sourceOfTruth, qualityGates, metadataSchema, milestones, and supersededStories
objects, while userStories itself was byte-identical to HEAD. This looked like an abandoned,
uncommitted partial edit from a prior session, not intentional current work — those fields are
exactly the schema/quality-gate/audit-provenance content this story depends on, and their loss
wasn't explained by any acceptance criterion. They were restored (see "Changes" below) rather than
silently accepted or discarded, per the instruction to investigate unexplained working-tree state
before building on top of it.
Changes
scripts/ralph_prd_schema.py (new)
Single module providing:
- Parse:
load_prd(path)— JSON load with clearPrdValidationErrors for missing file / invalid JSON / non-object document. - Canonical schema registry:
STORY_FIELDS(name → required/type),EXECUTION_MODES,EVIDENCE_CLASSES,HARDWARE_FLAGS,UPSTREAM_FLAGS,TRIAGE_VALUES. Covers every field named in the acceptance criteria: stableid/title,labels,milestone, derivedtype(derive_type),dependsOn, derivedblocks,triage,evidenceClass, andhardware/model/upstreamflags. - Structural validation:
validate_schema(data)— required fields, types, enum membership, ID convention,type:/priority:label cardinality, non-emptyacceptanceCriteria. - Semantic validation:
validate_semantics(data)— unique IDs, unique titles,dependsOnresolves to known stories (no self-dependency), dependency graph is acyclic (with a reported cycle path on failure),blocksmatches the dependency graph exactly (sorted set equality, not superset), andevidencePathmatches the per-story convention. - Fresh vs. in-progress backlog:
validate_fresh_backlog(data)additionally requires every story to startpasses: false(for a backlog that hasn't started execution yet);validate_backlog(data)is the composed check for a real, in-flight backlog where some stories have legitimately completed. - Self-consistency check:
validate_metadata_schema_consistency(data)— when prd.json declares its ownmetadataSchema/qualityGates(as this one now does), verifies that self-documentation hasn't drifted from what the validator actually enforces (enum sets, required/optional field lists, presence ofqualityGatesandgeneratedArtifactDisclaimer). This is a no-op for minimal fixture PRDs that don't carry that documentation. - Generation (one-directional, prd.json → artifact):
render_issue_markdown(story, data)renders the exact Markdown convention already used by.scratch/distributed-gguf-runtime/issues/*.md, sourcing the "Shared quality gates" bullets fromdata["qualityGates"]and the leading disclaimer fromdata["metadataSchema"]["generatedArtifactDisclaimer"](falling back to a module default only whendataomits them) — not from a duplicated Python string literal.to_gitea_issue_payload(story, data)wraps the same body into a Gitea create-issue-shaped payload (title,body,labels,milestone). - Authority guard:
check_generated_markdown_authority(text, disclaimer=...)rejects generated Markdown that's missing the disclaimer or that contains a conflicting authority claim (e.g. "this file is authoritative"). There is deliberately no Markdown → prd.json parser, so a generated artifact structurally cannot feedpasses(or anything else) back into the authoritative source. - CLI:
python scripts/ralph_prd_schema.py validate <prd.json> [--fresh]and... render <prd.json> <STORY-ID>.
.scratch/distributed-gguf-runtime/prd.json
- Restored the top-level
sourceOfTruth,qualityGates,milestones, andsupersededStoriesobjects to theirHEADcontent (see "Pre-existing state" above);userStorieswas already identical toHEADand is unchanged in content. - Extended
metadataSchema(previously incomplete for this story's own acceptance criteria) with:requiredStoryFieldsnow also listsnotesandblocks(present on all 55 stories); newoptionalStoryFields: ["completionNotes"]; newhardwareValues/upstreamValuesenums (modelis documented as an open convention, not a closed enum, since quantization/model targets are dynamic recipe inputs perRALPH-CONTEXT.md); newtypeDerivationandlabelConventions(reserved prefixes, cardinality); newgeneratedArtifactDisclaimer(the exact string generated artifacts must start with); extendeddependencyRules/authorityRuleprose to match what the validator enforces. - Reworded
sourceOfTruth's stale "All stories are unimplemented ... passes=false" clause, which was no longer accurate once DGR-017 completed. - Marked
DGR-018.passes = truewithcompletionNotesrecording this story's outcome.
.scratch/distributed-gguf-runtime/issues/018-define-canonical-ralph-and-gitea-metadata-schema.md
Regenerated via render_issue_markdown to reflect passes: true (checked acceptance criteria,
"completed" status line, "Verified evidence" handoff line) — matching the same convention DGR-017's
issue file already used for a completed story.
tests/test_ralph_prd_schema.py (new)
108 deterministic, model-download-free, GPU-free tests:
- Parse (4 tests): real backlog parses to 55 stories; missing file, invalid JSON, and
non-object documents raise
PrdValidationError. - Structural/semantic validation against the real backlog (7 tests): passes
validate_schema,validate_semantics, and the composedvalidate_backlog; unique IDs/titles; alldependsOnresolve;blocksmatches the derived dependency graph for all 55 stories; no cycle; everypasses: truestory carriescompletionNotesand an existing evidence README (a durable invariant, not a hardcoded list of which stories have completed — that list will keep growing). - Structural/semantic failure-mode fixtures (13 tests): missing required field, bad enum, wrong
type, empty
acceptanceCriteria, multipletype:labels, duplicate ID, duplicate title, unknown dependency, self-dependency, dependency cycle, mismatchedblocks, badevidencePath. - Fresh-backlog invariant (3 tests): accepts all-
false, rejects a prematurepasses: true, and confirmsvalidate_backlog(the in-progress variant) permits completed stories. - prd.json-as-source-of-truth for boilerplate (9 tests):
qualityGates/metadataSchemaself-consistency checks (no-op without them, catches a drifted enum, catches a missingqualityGates),quality_gate_bulletsflattening order,authority_disclaimerprecedence and fallback, and 3 tests asserting the real backlog's declared schema matches the code, its 7 quality-gate bullets are intact, and its disclaimer matches the module default. derive_type(4 tests): label-derived type, release-gate synthetic type for HITL gate stories,Nonewhen absent, and confirmation that the real backlog's two release-gate stories (DGR-054,DGR-070) deriverelease-gate.- Markdown generation round trips (55 parametrized + 6 tests):
render_issue_markdownfor every storyDGR-017..DGR-071is byte-for-byte identical to the corresponding file already in.scratch/distributed-gguf-runtime/issues/; determinism; leading disclaimer;Blocks (derived)rendering (Nonevs. listed); checkbox reflectspasses; filename convention. - Authority-claim rejection (4 tests): accepts real generated text, rejects a missing
disclaimer, rejects an overriding claim, and confirms every committed issue file in
.scratch/distributed-gguf-runtime/issues/passes the check. - Gitea payload generation (3 tests): payload shape, body carries no information beyond what's in prd.json, and every real story's payload is well-formed and authority-clean.
Commands and results
python3 -m pytest -q tests/test_ralph_prd_schema.py
108 passed in 0.16s
python3 -m compileall -q packages tests
Exit code 0, no output (all files compile).
git diff --check
Exit code 0 (no whitespace errors).
python3 scripts/ralph_prd_schema.py validate .scratch/distributed-gguf-runtime/prd.json
OK: 55 stories validated.
python3 scripts/ralph_prd_schema.py validate .scratch/distributed-gguf-runtime/prd.json --fresh
ERROR: DGR-017: fresh backlog requires passes=false, got True
ERROR: DGR-018: fresh backlog requires passes=false, got True
2 validation error(s).
Expected: --fresh is the invariant for a backlog that hasn't started execution; this backlog has
legitimately completed two stories, so it correctly fails that stricter check while passing the
plain (in-progress) validate command above.
Baseline: full repository suite (ad hoc python3, not a project venv)
python3 -m pytest -q
20 failed, 776 passed, 13 skipped, 2 warnings in 244.17s (0:04:04)
None of the failures touch scripts/ralph_prd_schema.py or tests/test_ralph_prd_schema.py
(neither file existed before this story; this story adds no changes to packages/). Four of the
20 failures reproduce exactly the pre-existing baseline defects DGR-017's evidence already recorded
(test_tracker_models_endpoint_lists_registered_hf_repo_and_short_name_alias,
test_torch_node_applies_tracker_load_shard_directive,
test_shard_heal_cycle_surviving_node_covers_dead_peers_gap,
test_a_node_with_an_unusable_precision_covers_no_layers). The remaining 16 (activation
compression, dynamic routing, gossip/relay, manual route benchmark, openai gateway, TOPLoC
calibration dispatch, tracker control plane) include a ModuleNotFoundError: langchain failure,
indicating this ad hoc python3 lacks the project's dev extras (langchain-openai, etc.) rather
than a real regression; this environment has no project virtualenv (e.g. no .venv-rocm) to run
against instead. Not investigated further as out of scope for this story.
Limitations
- No real Gitea instance or API integration exists;
to_gitea_issue_payloaddefines the payload shape (title/body/labels/milestone) only. Creating issues against a live Gitea server is future work, not claimed here. modelis intentionally validated as an open string, not a closed enum, perRALPH-CONTEXT.md's "Quantization and placement are dynamic recipe inputs" constraint; the schema documents (metadataSchema.modelConvention) but does not restrict its value set.- Validation and generation were exercised only against this feature's
prd.json(.scratch/distributed-gguf-runtime/prd.json);docs/prd.jsonand other.scratch/*/prd.jsonfiles in this repo use a materially different (simpler) shape and are out of scope.
Dependency handoff
DGR-021 and DGR-025 (this story's derived blocks) may treat prd.json's metadataSchema,
qualityGates, and this validator/generator as stable. Any future field addition to a story shape
must extend STORY_FIELDS in scripts/ralph_prd_schema.py and the corresponding
metadataSchema.requiredStoryFields/optionalStoryFields in prd.json together —
validate_metadata_schema_consistency fails closed if they drift apart.