RS-TKT-0A-PATCH2 · 01 P1 Unstructured Forbidden-Output Contract
RS-TKT-0A-PATCH2 · 01 — P1 Unstructured Forbidden-Output Contract
Lane: RS-TKT-0A-PATCH2 · Date: 2026-06-21
Gate: REGISTRATION_HOLD · REGISTRATION_CAN_PROCEED = NO · 0 runtime mutations (KB writes only)
Authority: NON_AUTHORITY · may_gate=false · decision_effect=NONE · design-doc-only
Supersedes: PATCH1 01 §3 (a token counts as a forbidden grant only when carried by a structured GRANT event, or when a forbidden artifact file is created) and the verdict labels in PATCH1 01 §6. PATCH1's conjunctive core (exit_code != 0 is necessary-not-sufficient; artifacts fail regardless of exit) is kept and extended, not withdrawn.
1. The defect (Codex P1, BLOCKER — re-review)
PATCH1 left a contradiction that is fail-open:
BAD-FC-001 says bare PASS + exit 3 must FAIL.
But PATCH1 §3 only recognizes a forbidden grant inside a structured GRANT event
(authority_effect=GRANTED) or as a created artifact file.
A bare stdout "PASS" has no structured event and no artifact.
=> An implementer following §3 literally may ignore bare stdout PASS.
Fail-closed design must not leave that choice to the implementer. PATCH2 closes the unstructured channel completely.
2. Corrected contract (six conjuncts)
For invalid input, the run is safe iff all of the following hold:
invalid_input_safe =
exit_code != 0
AND structured_forbidden_grant_event_emitted == false
AND unstructured_reserved_grant_token_emitted == false
AND forbidden_authority_artifact_created == false
AND forbidden_cert_or_seal_file_created == false
AND forbidden_authority_digest_created == false
If any one condition fails, invalid input is unsafe → FAIL. This adds unstructured_reserved_grant_token_emitted == false to PATCH1's set: a bare reserved grant-like token in any scanned channel is now an independent failure, structured event or not.
3. Reserved grant-like token list
The detector maintains a reserved grant-like token list. At minimum:
PASS
CERT
CERTIFICATE
SEAL
APPROVED
ACCEPTED
DIGEST
AUTHORITY_GRANTED
REGISTRATION_GRANTED
CAN_PROCEED=YES
REGISTRATION_CAN_PROCEED = YES
PRODUCTION_PASS
IMPLEMENTATION_PASS
SEMANTIC_TEXT_AS_CODE_PASS
IU_TRACEABILITY_PASS
RELEASE_BUNDLE_PASS
The list is a minimum, not a closed set; Phase-1 design may extend it but may never shrink it below this floor.
4. Structured vs unstructured rule
Structured events are preferred for precision, but unstructured output remains dangerous and is now in scope:
1. Structured GRANT event on invalid input => FAIL.
2. Authority artifact on invalid input => FAIL.
3. Bare reserved grant-like token in stdout/stderr/
result file/generated artifact on invalid input => FAIL,
unless explicitly classified as a safe rejection context (§5).
4. A nonzero exit code does NOT erase dangerous output.
5. Absence of a structured event does NOT make bare stdout safe.
Rule 5 is the exact closure of the Codex blocker: "no structured event" is no longer an escape hatch.
5. Safe rejection context (false-positive guard)
To avoid mis-flagging legitimate rejection vocabulary (e.g. SEAL inside ORACLE_CLAIMS_SEAL_REJECTED), define explicit safe rejection contexts:
PASS_REJECTED
SEAL_REJECTED
CERT_REJECTED
ORACLE_CLAIMS_SEAL_REJECTED
AUTHORITY_CLAIM_REJECTED
FORBIDDEN_TOKEN_DETECTED
A safe rejection context is valid only when all of the following hold:
event_type = REJECTION
authority_effect = NONE
exit_code != 0
no cert/seal/authority artifact was created
no authority digest was created
If output is only a bare PASS (or any reserved token) with no rejection context satisfying every condition above, it is forbidden → FAIL. A reserved token appearing as a substring strictly inside a whitelisted *_REJECTED marker within a valid REJECTION/NONE envelope is safe; a bare token is not.
6. Detector scan surface
The detector contract must scan all of:
stdout
stderr
result.json
result.md
exit_codes.json
all declared generated output paths
all files in the declared run output directory
negative_tests outputs
If the scan surface is unavailable or incomplete (a channel cannot be collected), the detector must return:
HOLD_OUTPUT_SURFACE_UNAVAILABLE
It must not return PASS / SAFE_REJECT when it cannot see the full surface. Missing visibility is a HOLD, never a pass (fail-closed).
7. Detector order
The detector runs in this fixed order:
1. collect scan surface
2. detect generated forbidden artifacts (cert/seal/authority-digest files)
3. validate structured event namespace (event_type / authority_effect)
4. scan unstructured reserved grant-like tokens across the full surface
5. apply safe rejection context exceptions (§5)
6. check exit code
7. emit invalid_input_safe result
Exit code is evaluated last and cannot override a forbidden token or a forbidden artifact found in steps 2–4. A nonzero exit never erases dangerous output; it is only one necessary conjunct.
8. Deterministic outcome vocabulary
Each invalid-input run maps to exactly one outcome:
SAFE_REJECT — all six conjuncts of §2 hold (rejected cleanly)
FAIL_UNSTRUCTURED_FORBIDDEN_TOKEN — a bare reserved grant-like token outside a valid safe-rejection context
FAIL_FORBIDDEN_AUTHORITY_ARTIFACT — a cert/seal/authority-digest artifact or a structured GRANT event
FAIL_INVALID_EXIT_ZERO — exited 0 on invalid input (exit conjunct violated)
HOLD_OUTPUT_SURFACE_UNAVAILABLE — scan surface incomplete; could not assess safely
These refine (do not contradict) PATCH1 01 §6 FAIL/safe reject labels: every PATCH1 FAIL becomes one of the precise FAIL_* codes; every PATCH1 safe reject becomes SAFE_REJECT.
9. Required fixtures (deterministic expected outcomes)
| Fixture | Behaviour on invalid input | Expected outcome |
|---|---|---|
| BAD-FC-001 | prints bare PASS to stdout, exits 3 |
FAIL_UNSTRUCTURED_FORBIDDEN_TOKEN |
| BAD-FC-002 | writes seal/cert/authority-digest artifact, exits 3 | FAIL_FORBIDDEN_AUTHORITY_ARTIFACT |
| BAD-FC-003 | prints ORACLE_CLAIMS_SEAL_REJECTED with event_type=REJECTION, authority_effect=NONE, exits 3, creates no artifact |
SAFE_REJECT |
| BAD-FC-004 | exits 0, emits no forbidden token/artifact | FAIL_INVALID_EXIT_ZERO |
| BAD-FC-005 | exits nonzero, emits no grant token/artifact, structured event is REJECTION/NONE |
SAFE_REJECT |
| BAD-FC-006 | prints bare SEMANTIC_TEXT_AS_CODE_PASS, exits 3 |
FAIL_UNSTRUCTURED_FORBIDDEN_TOKEN |
| BAD-FC-007 | writes result.md containing REGISTRATION_CAN_PROCEED = YES, exits 3 |
FAIL_UNSTRUCTURED_FORBIDDEN_TOKEN |
| BAD-FC-008 | scan surface missing stdout/stderr/result artifacts | HOLD_OUTPUT_SURFACE_UNAVAILABLE |
These become required L2 probes; they extend (do not replace) PATCH1 BAD-FC-001..005 and the prior P1–P10 ∪ pilot BAD-1..15 catalog. (PATCH1 BAD-FC-001/002/004 are re-labeled here with their precise outcome codes; PATCH1 BAD-FC-003/005 map to SAFE_REJECT.)
10. Required final P1 statement
Bare stdout/stderr/result-file grant-like output is forbidden on invalid input
even if the process exits nonzero and even if no structured GRANT event exists.
This is the source-level closure of the fail-open: no future implementation may choose whether bare PASS is dangerous — it is forbidden by contract.