Ground Stop — v1.9.4 → v1.9.5
A hard denial (GateDecision.BLOCK) was structurally unreachable and, once reachable, would have been silently discarded by the gate-aggregation join — confirmed by direct testing, not assumed. This release makes the distinction real end to end: a genuine hard stop is now distinguishable from a soft, resolvable hold at every layer, the same distinction the FAA draws between a Ground Stop (nothing lifts it but an explicit all-clear) and a Ground Delay Program (resolves once conditions improve).
| Fix | What was wrong |
|---|---|
| A.21 gate lattice join | GatePrimitive.evaluate() checked ABSTAIN before DEGRADE, contradicting its own declared lattice order. A mixed [PASS, BLOCK] result silently returned PASS. Fixed with an explicit GateDecision.lattice_rank and a proper max() join. |
GateDecision.BLOCK reachability |
No built-in check could ever produce it. GateCheck.failure_decision is now an opt-in field — every existing check keeps its exact prior behavior since it defaults to unset. |
GateBlocked |
Tested ABSTAIN, not BLOCK — the name lied about what it checked. Fixed; new GateAbstained preserves the old behavior verbatim for anything that relied on it. |
Unknown transition_id |
step()/attempt_transition() silently returned an unfiltered result for a nonexistent ID. Now a typed ABSTAIN. |
Also in this release, all opt-in, verified not to change behavior for any map that doesn't declare the new primitives:
Outcome.cause— typed reason (gate_block,unknown_transition,context_mismatch, ...) alongside the unchangedOutcomeKindThinkingMapTraversal.validate_map()— opt-in fail-closed preflight for danglingrequired_gate_id/guard_expressionreferences- F.6 work attribution (
WorkPrimitive.performed_under, opt-in onRoleAssignmentpresence), E.16 autonomy budgets (opt-in viarequires_autonomy_budget_id), C.24 call-plan closure, typedClaimScope/FormalityLevel/ReliabilityPathalongside the existing scalar F-G-R
Compatibility note: a gate built from several single-evidence checks where one check individually abstains and another individually degrades now aggregates to DEGRADE (allowed, with warning) rather than ABSTAIN (denied) — this package's own example gates can't hit that mix. If your map needs "any missing evidence anywhere is a hard stop," set failure_decision=GateDecision.BLOCK on that check.
Full detail: CHANGELOG.md
35/35 self-verification checks pass (python -m fpf_thinking_map.verify); dev_mcp's own 38/38 test suite unaffected. Independently re-verified against the live installed package and against a 20-case adversarial scenario suite exercising every fix above, both before and after this release was tagged.