Cookpit v3.2 — Validation Criteria
The criteria a candidate v3.2 JSON-LD file must pass to be considered conformant. Each criterion references a rule from
rules.md(or the v3.2 JSON Schema), declares severity, and describes the check.The bundle accepts output from any LLM. Validation is the gate that tempers their enthusiasm: a file is accepted if and only if it passes the hard criteria below, regardless of which model produced it.
A file is valid v3.2 if and only if every criterion at severity
hardpasses.softcriteria are advisory: they surface in the validator report so the operator can decide whether to refine the prompt or accept the output. There is no third state.
How validation runs
Validation is stage 2 of the v3.2 lifecycle (see rules.md A0). The
candidate file at the input is the AI Chef's stage-1 output: an
unauthenticated (U) file carrying cookpit.attestation.status: "unauthenticated". Validation produces a verdict, not a transformed
file. When the verdict is pass, the validator's stage-3 attestation step
(see rules.md R) consumes the same file to produce the authenticated
(A) form. Validation and attestation are conceptually distinct: the
former checks, the latter certifies. A stage-1 input that already
claims status: "authenticated" is malformed and is rejected at
V-LIFECYCLE-AI-EMITS-U.
The validator executes the following phases in order. The first failure in phases 1–5 terminates the run and reports the failing criterion. Phase 6 collects soft findings without terminating.
- Parse. The candidate output is parsed as JSON.
- Schema. The parsed object is validated against
schema/cookpit-cooking-file-v3.2.json. - Lifecycle gating. The candidate's
cookpit.attestationblock is inspected. The block must be the unauthenticated form perrules.mdR2. The filename's<status>segment must beUand must agree with the internalstatus(V-LIFECYCLE-AI-EMITS-U, V-ATTESTATION-CONSISTENCY). - Hard rule checks. Every criterion of severity
hardbelow is run. - Source-faithfulness. The strict quantitative fingerprint is computed
from the source recipe and compared against the file's
cookpit.quantitativeFingerprint(V-FINGERPRINT-B). This is the stage-1 → stage-2 integrity check across the source-faithfulness boundary. - Soft findings. Advisory checks run and are collected into the report.
The validator never modifies the file during validation. A repair loop may
take the validator's report and re-prompt the AI Chef with "Fix only the following criteria: …", but repair is a separate concern from validation.
When every hard criterion in phases 1–5 passes, the validator's stage-3 attestation step runs:
- Canonicalise the file body with
cookpit.attestation.signaturecleared perrules.mdR5. - Compute the lowercase 64-hex SHA-256 file fingerprint.
- Replace
cookpit.attestationwith the authenticated form (status, issuer, validatorVersion, issuedAt, canonicalization, keyId, fileFingerprint, audit summary, signature). - Sign the canonical bytes with the validator's private key.
- Rename the file's
<status>segment fromUtoA. - Return the authenticated file alongside the validation report.
Stage 3 is governed by the V-FILE-FINGERPRINT and V-SIGNATURE
self-checks below: before returning, the validator re-verifies that the
file it is about to emit verifies under R6. This guards against bugs in
the stamping pipeline.
Severity
| Severity | Meaning |
|---|---|
hard | Must pass for the file to be conformant v3.2. |
soft | Advisory; the file is still conformant if it fails, but the operator should review. |
Statuses
Every criterion produces one of the following statuses. Hard
criteria use the first five; soft criteria use pass, warn and
info.
| Status | Severity (where used) | Meaning |
|---|---|---|
pass | hard, soft | Criterion ran and passed. |
fail | hard | Criterion ran and failed. Verdict → FAIL. |
warn | soft | Soft criterion produced a warning. Verdict unchanged. |
info | soft | Soft criterion produced informational data. Verdict unchanged. |
deferred | hard | Criterion's productised implementation is not yet shipped. The validator cannot perform the check today; a future PR will. Distinct from skipped. |
skipped | hard | Criterion needs an input the user did not supply (e.g. --source <pdf> for V-FINGERPRINT-B; --pub-key for V-SIGNATURE in default mode). Re-run with the input to clear. |
n/a | hard | Criterion does not apply to this file at this lifecycle stage (e.g. V-FILE-FINGERPRINT on a U file; V-INGREDIENT-ALTERNATIVE when no alternatives are declared). |
The verdict line is PASS when there are zero fail statuses and
FAIL otherwise. deferred and skipped statuses do NOT change the
verdict, but they are surfaced in the report's footer so a reader can
tell at a glance how many hard checks ran versus did not. A "PASS
verdict with N hard checks deferred or skipped" is structurally
different from "PASS verdict with all hard checks run and passed";
the validator's footer makes the distinction unambiguous.
Criteria
V-PARSE — JSON parse (hard)
The candidate output parses as a single JSON object. Multiple top-level values, trailing commentary, markdown code fences, or non-JSON output all fail this check.
V-SCHEMA — JSON Schema (hard)
The parsed object validates against schema/cookpit-cooking-file-v3.2.json.
Schema errors are reported with their JSON pointer.
V-LIFECYCLE-AI-EMITS-U — stage-1 input shape (hard) — rules A0.1, R2, R4
The candidate file presented for validation MUST be a stage-1 AI Chef output:
cookpit.attestationis present.cookpit.attestation.statusequals"unauthenticated".cookpit.attestationdoes NOT carrysignature,fileFingerprint,issuer,keyId,validatorVersion,issuedAtorcanonicalization.- The filename's
<status>segment, when present, equalsU.
A file that already claims status: "authenticated", or that carries any
of the authenticated-only fields, is malformed. The validator refuses to
re-validate an authenticated file; the user must strip the attestation
block back to the unauthenticated form and resubmit (rules.md R8).
V-ATTESTATION-SHAPE — attestation block shape (hard) — rules R1, R2, R3
cookpit.attestation.status is one of "unauthenticated" or
"authenticated". When status is "unauthenticated", the block carries
no authenticated-only fields (per V-LIFECYCLE-AI-EMITS-U). When status
is "authenticated", the block carries every required field listed in
rules.md R3. Inconsistent or partial blocks are a hard failure.
V-ATTESTATION-CONSISTENCY — filename / status agreement (hard) — rules O1.2, R10
When the candidate is presented to the validator with a filename, the
filename's <status> segment matches cookpit.attestation.status:
…cpt.U.jsonld↔status: "unauthenticated".…cpt.A.jsonld↔status: "authenticated".
Disagreement is a hard failure. The filename remains decorative; the internal status remains the authoritative claim. The agreement check catches accidental renames and tampering attempts that flip the filename without producing a matching internal block.
V-IDENTITY-A — @type and version (hard) — rules B1, B2
@type includes both Recipe and cookpit:CookingFile, and
cookpit.version equals 3.2.0.
V-IDENTITY-B — file id (hard) — rules B4, G1, G2
cookpit.id matches ^f[0-9a-f]{10}$.
V-IDENTITY-C — courses and difficulty (hard) — rules B5, B6
cookpit.courses is non-empty, has no duplicates, has at most three
entries, and is a subset of [starter, main, dessert].
cookpit.difficulty is one of easy, medium, hard, expert.
V-DURATION-A — phase nominal durations (hard) — rules C2, C4, Q1, Q3
For every declared phase (cookpit.prepCook, cookpit.preCook,
cookpit.liveCook), the phase's nominalDuration matches
^[0-9]{2}:[0-9]{2}:[0-9]{2}$. cookpit.liveCook is required;
prepCook and preCook are optional.
cookpit.orchestration.timingBasis is cookTime. prepHandling is
preStartChecklist. runtimeOverruns is appOwned.
V-DURATION-B — phase-sum vs source (soft) — rule C2
When cookpit.sourceTiming.cookTime is present as ISO 8601 (PT…),
the ISO duration parses to the same HH:MM:SS as the SUM of all
declared phases' nominalDuration (or, for ranges in
cookTimeText, to the chosen composition per C3). A mismatch
suggests the phase decomposition was not source-derived.
V-LANE-MODEL — fixed lane model (hard) — rule D1
cookpit.laneModel is the fixed primary/secondary/tertiary block defined in
the v3.2 spec. The block is compared by structural equivalence (lanes,
seconds, scopes, roles, default sounds), not by JSON byte equality.
V-LANE-SCOPE — lane scope per course (hard) — rules D2, D3, D4
Every task on A0 has kind: "alarm". Every task on S1/S2/S3 has
course: "starter". Every task on M1/M2/M3 has course: "main". Every
task on D1/D2/D3 has course: "dessert". Tasks on secondary or tertiary
lanes (S2/S3/M2/M3/D2/D3) appear only when at least one other task on the
same course has a different lane in the same minute (i.e. there is a real
parallel workstream).
V-LANE-SECONDS — seconds match lane (hard) — rule D5
For every task, the seconds component of time equals the canonical second
of its lane: A0=:00, S1=:15, S2=:20, S3=:25, M1=:30, M2=:35,
M3=:40, D1=:45, D2=:50, D3=:55.
V-ALARMS — required global alarms per phase (hard) — rules E1, E2, E3, E5
For each declared phase, that phase's tasks[] contains exactly one
alarm at 00:00:00.A0 and exactly one alarm at the phase's
nominalDuration on A0. When the phase's nominalDuration is at
least 00:10:00, exactly one alarm at
(nominalDuration − 10 minutes).A0 exists in that phase. Every A0
task has kind: "alarm". A0 alarms do not require timingBasis.
V-ALARM-OPTIONAL — optional 5-minute alarm (soft) — rule E4
Within a phase, at most one (nominalDuration − 5 minutes).A0 alarm
exists; if present it has kind: "alarm". Multiple 5-minute alarms
in a single phase are a soft fail.
V-PREP — prep is untimed (hard) — rules F1, F2, F3
No task in tasks describes prep that the source labels as prep time.
Pre-start preparation appears in cookpit.prerequisites. Live "during cook"
prep that the source assigns to the cook window is allowed as a normal
timed task. cookpit.orchestration.startEnabledBy is
allPrerequisitesConfirmed.
V-IDS-FORMAT — id pattern (hard) — rules G1, G2
Every id in the file matches ^[a-z][0-9a-f]{10}$. The leading prefix
matches the entity type per G2.
V-IDS-DETERMINISTIC — id derivation (hard) — rule G3
For every id in the file, the validator recomputes the deterministic id from
(entityType, canonicalContent, canonicalPosition) per the canonical
generation profile and confirms it matches the id in the file. Mismatches
indicate non-deterministic generation and are a hard fail.
V-IDS-UNIQUE — id uniqueness (hard) — rule G4
No two entities share the same id within the file.
V-REFS-CLOSED — resource closure (hard) — rules H1–H7, A2, Q5
Every cross-reference resolves to a declared entity of the matching type, with phase-scoping where applicable:
ingredientRefs→cookpit.ingredients[].id(must start withi);equipmentRefs→cookpit.equipment[].id(must start withe);utensilRefs→cookpit.utensils[].id(must start withu);sundryRefs→cookpit.sundries[].id(must start withs);- task
processRefs→ aprocesses[].iddeclared IN THE SAME PHASE (must start withp); cross-phaseprocessRefsare forbidden; - process
startTaskandendTask→ atasks[].iddeclared IN THE SAME PHASE (must start witht); cross-phase boundary tasks are forbidden; - hotspot
taskRefs→ any declared phase'stasks[].id(must start witht); hotspots are file-level metadata and may target any phase.
A reference whose target is missing, whose prefix mismatches the entity type, or which crosses a phase boundary against the rule above is a hard failure.
V-REFS-PHANTOM — no undeclared resources in actions (soft) — rule H8
Heuristic check: task action strings are scanned for mentions of common
tools (thermometer, blender, scales, …) and the validator reports any
mention whose corresponding declared resource list does not contain a
plausible match. Heuristic only; the operator decides whether to act.
V-REFS-COVERAGE — declared resources are used (soft) — rule H9
Every declared resource is referenced by at least one task, process or prerequisite. Resources that are listed but never referenced are reported informationally.
V-TASKS-SHAPE — task required fields (hard) — rules I1, I2
Every task has id, time, kind, action. Every task whose kind is
not alarm additionally has a non-empty timingBasis. kind is one of
alarm, alert, update. Only alarm tasks appear on A0; alert and
update tasks appear only on course lanes.
V-TASKS-TIME — task time format (hard) — rule I3
Every task time matches
^([0-9]{2}):([0-9]{2}):([0-9]{2})\.(A0|S[1-3]|M[1-3]|D[1-3])$.
V-TASKS-WITHIN — tasks fit within phase duration (hard) — rule I5
For every task, HH:MM:SS is less than or equal to its phase's
nominalDuration. The single exception is the phase's time-up
alarm, which equals the phase's nominalDuration exactly.
V-TASKS-ORDER — task ordering per phase (hard) — rule I6
Within each phase's tasks[], the array is sorted by time, ties
broken by lane then id. Cross-phase ordering is not meaningful
because each phase has its own clock.
V-TASKS-LANGUAGE — culinary language (soft) — rule I7
Task action strings do not contain UI verbs (tap, swipe, confirm,
press, done, next, continue). Heuristic check; reported as soft so
the operator can confirm wording quality without rejecting the file outright.
V-TIMING-BASIS — non-alarm tasks have a basis (hard) — rules I1, I8
Every non-alarm task has a timingBasis whose basis is one of
sourceExactDuration, sourceRangeMinimum, sourceRangeTarget,
sourceCookTimeEndpoint, sourceOrder, sourceMeanwhile,
sourceOutcomeCue, sourceImpliedDeadline, canonicalProcessEstimate.
The source field is a non-empty string. When basis is
sourceImpliedDeadline, offsetFrom references an existing task id and
offset is a negative ISO 8601 duration recording the prep duration that
was deduced from the deadline.
V-TIMING-NONRANDOM — no cosmetic spacing (soft) — rules A1, I9
Heuristic check: task gaps are scanned for suspiciously regular spacing (e.g. every task placed exactly 30 seconds apart with no source-derived explanation). Suspicious patterns are reported soft so the operator can review the prompt's effectiveness.
V-PROCESSES — process consistency (hard) — rules J1, J2, J3, J4
Every process's startTask and endTask exist in the SAME phase's
tasks[]. The interval between their time values, ignoring lane
seconds, equals the process's duration.target parsed as ISO 8601.
completion.type is one of timed, sensory, temperature,
compound. Within a phase, processes are listed in the order their
startTask occurs.
V-PHASES-PRESENT — phase declaration (hard) — rule Q1
cookpit.liveCook is required and is a phase block with label,
nominalDuration, tasks[] and optional processes[] and
completion. cookpit.prepCook and cookpit.preCook are optional;
when present they have the same shape. No other phase keys are
permitted.
V-PHASE-IDS — phase identity (hard) — rule Q2
cookpit.prepCook.id, when present, matches ^y[0-9a-f]{10}$.
cookpit.preCook.id, when present, matches ^z[0-9a-f]{10}$.
cookpit.liveCook does NOT carry an id field; its identity is
the file's cookpit.id.
V-PHASE-CONTINUITY — phase shape (hard) — rule Q3
For every declared phase: label is a non-empty string,
nominalDuration is HH:MM:SS, tasks[] is non-empty.
processes[] (when present) lives inside the phase block — there
are no top-level processes outside any phase.
V-PHASE-ORDER — phase document order (soft) — rule Q4
When more than one phase is declared, their JSON-document order in
cookpit follows the fixed canonical layout: prepCook,
preCook, liveCook. This is a human-readability convention,
not a runtime sequencing claim — at runtime prepCook and
preCook are independent (they may run concurrently or
sequentially; the file does not encode that choice), and
liveCook is strictly downstream of both. Out-of-order document
layout is a soft warning so the file remains visually consistent
with the rest of the corpus.
V-METHOD-ORDER — method order preservation (hard) — rule A1, A2
Tasks on each course lane appear in an order that is a topological extension of the source method order. The validator extracts numbered or sequential method steps from the source and confirms that the corresponding tasks in the same course do not invert the source order. Violations are hard failures.
V-SOURCE-COVERAGE — source durations covered (hard) — rule K1
Every numeric duration stated in the source method (e.g. "simmer for 20 minutes", "bake 25–30 minutes") is reflected in the plan by a process or by a task pair whose interval matches the stated duration (or its chosen range minimum per C3). Missing durations are hard failures.
V-SOURCE-TEMPS — temperatures and gas marks present (hard) — rule K1
Every temperature, oven setting, gas mark or numeric heat instruction in the
source is present somewhere in the file (typically in a task action or in
the relevant ingredient note). Omissions are hard failures.
V-FINGERPRINT-A — fingerprint shape (hard) — rules K3, K4, K5
cookpit.quantitativeFingerprint is present, has type: "strict",
basis: "ingredients-and-method-active-numbers",
normalization: "cookpit-active-number-sequence-v3.2.0",
a sequence matching ^[0-9]+(-[0-9]+)*$, and a hash with
algorithm: "sha256" and value matching ^[0-9a-f]{64}$.
V-FINGERPRINT-B — fingerprint matches source (hard) — rules K3–K5
The validator independently extracts the active-number sequence from the
source recipe per the canonical normalization rules
(canonical-fingerprint-normalisation.md, executable embodiment at
scripts/lib/source_tokeniser.py) and confirms:
- the file's
sequenceequals the validator's computed sequence; and - the file's
hash.valueequals the SHA-256 hex digest of that sequence.
A mismatch on either is a hard failure (STATUS_FAIL). Without the
--source <pdf|text> flag the criterion reports STATUS_SKIPPED —
the validator cannot verify source faithfulness without the source.
For an image-only PDF whose extracted text is empty, the criterion
also reports STATUS_SKIPPED with detail explaining why.
V-NO-RUNTIME — forbidden runtime fields absent (hard) — rules L1, L2
The validator walks the entire JSON tree and confirms no field name from
the forbidden list appears: urgency, priority, progress,
completionState, checkboxState, actualStartTime, actualFinishTime,
overdue, overdueState, focusTask, bannerColor, timerColor,
overrunState, queuePosition, scrollPosition, plus any field whose
name matches the heuristic pattern of runtime state (/(actual|current|live|overdue|focus|banner|timer|overrun|queue|scroll)[A-Z]/).
V-GENERATION — generation metadata (hard) — rules M1–M5
cookpit.generation is present with profile set, idPolicy =
deterministic-type-prefixed-10-hex, timingPolicy =
source-derived-deterministic-optimal, resourcePolicy =
closed-world-declared-resources, and randomTimingAllowed = false.
V-OPTIONAL-COVERAGE — optional ingredients exempt from coverage (info) — rule N1
The soft resource-coverage check (V-REFS-COVERAGE) ignores ingredients
marked optional: true. Optional ingredients with no task or process
reference are reported informationally only.
V-EQUIPMENT-POWER — equipment power enum (hard) — rule N2
When present, equipment[].power is one of electric, gas, induction,
none. Other values are a hard failure.
V-TASK-SOUND — task sound enum (hard) — rule N3
When present, task.sound is one of bell, klaxon, chime, tick.
V-PREREQ-LEADTIME — prerequisite leadTime is ISO 8601 (hard) — rule N4
When present, prerequisites.*[].leadTime matches an ISO 8601 duration
(^P…).
V-PREREQ-INGREDIENT-REFS — prerequisite ingredient closure (hard) — rule N5
When prerequisites.*[].ingredientRefs is present, every id starts with
i and exists in cookpit.ingredients[].id. Wrong-type or dangling refs
are hard failures (same enforcement as V-REFS-CLOSED).
V-INGREDIENT-ALTERNATIVE — alternative shape (hard) — rule N6
When present, ingredient.alternative carries a non-empty text string
and may carry quantity, unit, metricQuantity, metricUnit matching
the same shapes used on the primary ingredient.
V-LEX-FORBIDDEN — forbidden lexicon terms (soft) — rule P8
Heuristic scan of every field in lexicon scope (per rule P2) for forbidden
tokens drawn from lexicon.md §7.1 (hedgers), §7.2 (warmth and marketing),
§7.3 (filler) and §7.4 (vague outcomes without sensory companion). Each
hit is reported as a soft warning with the field path and the offending
token. The validator does not strip or rewrite text.
V-LEX-IMPERATIVE — imperative form on actions (soft) — rules P3, I7
Heuristic check that every tasks[].action string and every prerequisite
item carrying an active cooking action begins with an imperative verb.
Strings beginning with second-person pronouns (you, your, you'll,
you're) or with hedger constructions (make sure to, you'll want to,
feel free to) are flagged as soft warnings.
V-LEX-SENSORY — sensory completion cues (soft) — rule P7
For every completion whose type is not timed, the cue (or each
condition's cue for compound) is scanned for at least one sensory
token from lexicon.md §6 or a clear synonym. Cues that contain only
abstract outcomes (done, ready, right) without a sensory anchor are
flagged as soft warnings.
V-LEX-PROCESS-LABEL — process-label grammar (soft) — rule P11
Heuristic check that every processes[].label reads as a short
present-continuous noun phrase (e.g. ends in ing and is three to five
words long, or is a recognised noun-phrase pattern). Imperative-form
labels (Reduce the sauce) are flagged as soft warnings.
V-LEX-PERSONA-DRIFT — persona drift (soft) — rules P1, P8
Heuristic scan for register drift away from the rebel chef persona:
brigade-formal language patterns (passive voice on cooking actions, "take
care to", "in order to"), recipe-blog warmth (multiple warmth tokens in a
single field), and headmaster register (make sure, you'd better,
if you don't). Findings are reported as soft warnings to drive prompt
refinement.
V-FILENAME — filename methodology consistency (soft) — rules O1–O8
When the candidate file is presented to the validator with a filename, the validator checks:
- the filename matches the pattern
<slug>.v3.2.cpt.<A|U>.jsonld; - the
<slug>agrees with the slug derived fromnameper O2; - the
<status>segment isAorU; - the
<status>segment agrees withcookpit.attestation.status(this overlaps V-ATTESTATION-CONSISTENCY at hard severity; here it is reported as a soft warning when filename and content are absent or ambiguous); cookpit.versionstarts with3.2..
A filename without the <status> segment (legacy …cpt.jsonld form per
O8) is reported as a soft warning recommending the explicit U flag,
but does not prevent conformance. When no filename is available the
criterion produces an info status only.
V-FILE-FINGERPRINT — file fingerprint consistency (hard, stage 3) — rule R5
For a file the validator is about to emit at stage 3, the
cookpit.attestation.fileFingerprint MUST equal the SHA-256 digest of
the canonicalised file body computed with both
cookpit.attestation.signature AND
cookpit.attestation.fileFingerprint cleared, using the canonicalisation
profile named in cookpit.attestation.canonicalization.
This criterion is checked twice:
- Pre-emit (validator self-check). Before returning a stage-3 authenticated file, the validator recomputes the fingerprint and confirms it matches the value it is about to embed. A mismatch indicates a bug in the stamping pipeline; the validator MUST refuse to emit.
- Post-load (consumer-side). A stage-4 consumer recomputes the fingerprint on every load (R6 step 4). A mismatch indicates the file has been altered since stamping; the consumer MUST treat the file as untrusted (rules.md R7).
V-SIGNATURE — signature verification (hard, stage 3 / stage 4) — rules R3, R6
For a file claiming cookpit.attestation.status: "authenticated", the
signature MUST verify under the public key identified by
cookpit.attestation.keyId, over the canonical bytes produced by R5
(canonicalised file body with signature cleared).
This criterion is checked twice:
- Pre-emit (validator self-check). Before returning a stage-3 authenticated file, the validator verifies its own signature over the canonical payload. Failure indicates a bug in the signing pipeline; the validator MUST refuse to emit.
- Post-load (consumer-side). A stage-4 consumer verifies the
signature on every load (R6 step 5) using its pinned public-key set.
Verification failure is a hard rejection; the consumer MUST NOT
silently downgrade the file to
Usemantics.
Stripping or substituting issuer, validatorVersion, issuedAt,
canonicalization, keyId, fileFingerprint, or any field elsewhere in
the file changes the canonical bytes and invalidates the signature.
Reporting
Each criterion produces one of:
passfail(with a JSON Pointer to the offending location and a one-line message)warn(soft criteria only)info(advisory observations the validator surfaces without judgement)
The full report is shaped as:
{
"version": "3.2.0",
"verdict": "pass | fail",
"summary": { "hardFailures": 0, "softWarnings": 0, "infos": 0 },
"criteria": [
{
"id": "V-LANE-SECONDS",
"rule": "D5",
"severity": "hard",
"status": "pass",
"details": []
}
]
}
verdict is pass if and only if every hard criterion is pass and the
JSON Schema validation succeeded.
Determinism check (optional)
A determinism check is run separately from validation. It re-runs the
generator with the same prompt, rules, schema and source recipe at low
temperature n times (default 3) and compares the resulting files using a
canonical diff. Stable runs produce byte-identical canonical output. Drift
on any field other than free-form action wording is a soft warning; drift
on times, ids, lanes, references, or the fingerprint is a hard failure of
the determinism check (separate from per-file validation).
Conformance and authentication
A file is conformant v3.2 if and only if every hard criterion above
is pass and the JSON Schema validates. soft warnings do not prevent
conformance; they exist to drive prompt refinement and operator review.
A file is authenticated v3.2 if, in addition, it has been processed
through stage-3 attestation (per rules.md R) by the canonical validator,
verifies under V-FILE-FINGERPRINT and V-SIGNATURE against the published
public key, carries cookpit.attestation.status: "authenticated", and
uses the A filename flag. Authentication is a stronger property than
conformance: every authenticated file is conformant, but a stage-1 AI
output may be conformant (passes every hard criterion) without ever
being authenticated, simply because it has not been put through the
canonical validator's stage-3 stamp.