Releases: Consolidare-Continuum/fpf-agentic-thinking-map
Release list
v1.9.5 — A.21 gate lattice repair (Ground Stop)
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.
v1.9.4
Fixed
TransitionPrimitive.guard_expressionwas declared but never consulted — not read byguards.py,traversal.py,state.py, orlogic.py.ThinkingMapTraversal.attempt_transition()now treats a non-emptyguard_expressionas aDecisionRule.namereference in the boundLogicLayer: the transition only fires when that rule's current recommendation (action_if_true/action_if_false, whichever the condition resolves to) matches the transition_id being attempted. Closes #10.- A routing-policy mismatch returns
REVISE_PLAN— notESCALATE— with the rule's actual recommendation inOutcome.alternatives, so an agentic caller can retry the correct transition_id in the same turn.REVISE_PLANwas declared inOutcomeKindsince the outcome space was first drawn up but never previously returned by any code path. - No active recommendation (condition false with no
action_if_false, or aHINT/WARNrule's vacuous-implication suppression): silent pass-through, same as not settingguard_expressionat all. - Dangling reference (no
logic_layerbound, or the named rule doesn't exist): silent no-op, matchingrequired_gate_id's existing convention for an unresolved reference — backward compatible with every existing map.
- A routing-policy mismatch returns
check_route_gated_transitionadded topython -m fpf_thinking_map.verify(28/28).
Full Changelog: v1.9.3...v1.9.4
v1.9.3 — discrete reachability analysis
Added
fpf_thinking_map.reachability: discrete reachability analysis over a
SemanticMap's declared transition graph —graph_roots(),
forward_reachable(),unreachable_transitions(). Algorithm 10.1/10.3
from Kochenderfer, Katz, Corso, and Moss, Algorithms for Validation
(MIT Press, 2026), ch.10.check_reachabilityadded topython -m fpf_thinking_map.verify(27/27).
Fixed (by the check catching it, not by changing runtime behavior)
A downstream domain map's requires_human_authorization transition had a
from_state nothing else in the map produced. Looked unreachable; wasn't —
it was an intentional external entry point. The obvious-looking fix (merge
it onto the shared from_state its safe twin used) broke four behavior
tests: step() without an explicit transition_id aggregates
missing_evidence across every transition sharing a from_state.
entry_states is a required argument to unreachable_transitions() so
that distinction is a reviewable line of code, not tribal knowledge.
Runtime behavior of this package is unchanged. Full details:
CHANGELOG.md.
See also: docs/deep/EXPANDED_PROVENANCE.md for the narrative arc.
v1.9.2 — PyPI presentation alignment
Documentation-only patch release aligning PyPI with the current public library presentation.
- Publishes the polished README, seven status badges, important links, and test-backed runtime visual to PyPI.
- Uses absolute image and repository URLs so the same long description works on GitHub and PyPI.
- Refreshes package summary, keywords, and project links for AuthorizationReceipt, PendingInput/AWAIT, and MoveIntent.
- No runtime behavior change from v1.9.1; all 26 deterministic checks pass.
Documentation deployment is bound to this version tag.
v1.9.1 — Provenance (Clearance, Holding Pattern, Tail Number)
Provenance — v1.7.0 → v1.9.1
Three mechanisms, one throughline: closing gaps in what the runtime could say about its own state — which exact state an approval was checked against, which concrete move fired versus its reusable type, whether traversal is finished versus merely blocked on something external. Each is a soundness property of the logic/traversal layer itself, independent of any particular model or harness.
| Name | Mechanism | Version |
|---|---|---|
| Clearance | AuthorizationReceipt — approval scoped to one transition and the exact inspected state it was issued against, not an ambient authorized=True boolean |
v1.7.0 |
| Holding Pattern | PendingInput / OutcomeKind.AWAIT — waiting on a declared external dependency, distinct from IDLE |
v1.8.0 |
| Tail Number | MoveIntent / inspect_move() — a concrete proposed move's own identity, distinct from its reusable transition type |
v1.9.0 |
| — | AuthorizationReceipt expiry rebound to a dedicated _authorization_clock, found via adversarial testing against the live engine, not design review |
v1.9.1 |
Also in this release:
- 3 new runnable example scenarios (
python -m fpf_thinking_map.examples) — one per mechanism, alongside the existing Ignition Lock walkthroughs ARCHITECTURE.mdand README updated to match the current module graph and outcome/check counts- First
CHANGELOG.mdfor the project
Full narrative: docs/deep/EXPANDED_PROVENANCE.md
Version-by-version: CHANGELOG.md
26/26 self-verification checks pass (python -m fpf_thinking_map.verify); dev_mcp's own 38/38 test suite unaffected.
v1.6.0 — Ignition Lock
Ignition Lock
A transition can be fully legal — evidence fresh, gate green, everything the FPF logic computes says fire — and still not be cleared to fire without a human turning the key. That's the new mechanism this release adds, named off vocabulary the library already had (can_fire) rather than borrowed HITL jargon.
TransitionPrimitive.requires_human_authorization — a transition can opt into needing authorized=True to fire, enforced at ActiveState.transition_to() itself so there's no lower-level bypass. The model still sees everything (step()/slice() report evidence, gate status, all of it) — it just can't pull the trigger alone.
ActiveState.pending_authorizations (set[str]) — every currently-unresolved escalated ask, plural on purpose: an earlier single-value design silently lost track of one ask the moment a second, different transition also escalated. Found and fixed via adversarial live testing against two concurrent destructive requests, not by code review.
Abort to Orbit — TransitionPrimitive.safe_alternatives names a transition's non-destructive twins (archive instead of hard delete), surfaced before the model ever attempts the destructive move. ActiveState.deny_pending_authorization(transition_id, reason) records an explicit "no" as a fact, not a void — and doesn't lock the door permanently, a later authorized=True still fires.
Two new integrator advisories (ADV-10, ADV-11, both auto-detected by dev_mcp): a keyword-heuristic lint for destructive-sounding transitions with no gate set, and a soundness check for safe_alternatives declarations (dangling references, an alternative that's itself gated, an alternative already denied). Both explicitly heuristic, explicitly not enforcement — same honesty as the existing advisories.
Fixes: ESCALATE now surfaces missing evidence alongside the auth requirement instead of hiding it; dev_mcp's compliance wrapper forwards authorized= (and future kwargs generally) instead of silently dropping them.
23/23 core verify, 38/38 dev_mcp self-test. All changes purely additive over v1.5.0 — nothing published ever had a field renamed out from under it.
v1.5.0
Stop-point and reference release for the current repo/site state.
Highlights:
- package version bumped to 1.5.0
- README simplified and repositioned around runtime behavior
- package folder cleaned to runnable files only
- deep theory/analysis docs moved under docs/deep/
- dev_mcp docs made more agent-oriented
- ARCHITECTURE wording simplified without changing diagrams
- measured per-decision raw-FPF vs compiled figures added to README and ARCHITECTURE
- MIT-licensed community release for public use, reuse, and discussion
This release should also trigger the configured PyPI publish workflow.
v1.4.25
Docs-only fix: version-number consistency.
Both README.md (PyPI long-description) and the package-bundled
fpf_thinking_map/README.md were stuck displaying v1.4.4/v1.4.5 while the
shipped package was already at 1.4.24. Fixed the badges/footers in
README.md, fpf_thinking_map/README.md, and ARCHITECTURE.md, and added one
consolidated v1.4.4-v1.4.24 changelog entry (confirmed docs/provenance
only — zero fpf_thinking_map/*.py changes in that range) instead of
leaving a 21-version gap.
No runtime code changes. 22/22 verify checks pass.
v1.4.24
Docs-only correction release.
- Removed the unbacked "36 reasoning passes where 6 would suffice" claim from
WHY_THIS_EXISTS.md; the shipped example is a 3-step traversal (2
decision-bearing), not 6, and no code in this repo counts per-layer
reasoning passes. Rewritten as explicitly illustrative math, not a
measured result. - Added a Verification section to WHY_THIS_EXISTS.md, cross-linked with
TRIPLE_TAX_CALCULUS.md, documenting what the triple-tax measurement
harness actually supports vs. what it doesn't. - Removed self-reported pass_count/pass_labels fields from the triple-tax
measurement harness (scripts/triple_tax_calculus.py) — they asked the
model to narrate its own internals with no causal link to actual
behavior. Fixed related crash bugs when live API calls error out.
No changes to fpf_thinking_map/ runtime code. 22/22 verify checks pass.
v1.4.23 -- Results summary, real signed footer
Codex proposed a Good/Bad/Plus-Minus/Conclusions summary in a commit on a stale base (predates the full rewrite, referenced fields that no longer exist). Kept the idea, rebuilt against the current System A/B/C structure with accurate data, generated by build_markdown() so it stays correct on rerun.
Declined Codex proposed signature claiming joint authorship on a commit Claude never touched and calling the correction history a BFF footer. Replaced with a real signed footer naming what each system actually did, both systems mistakes included, not smoothed over.
Retested: two independent runs matched bit-for-bit.
No functional changes.