FPF Agentic Thinking Map
FPF Agentic Thinking Map is a zero-dependency Python runtime for controlling multi-step agent traversal through explicit states, evidence gates, lawful transitions, and inspectable outcomes.
Use it when an agent must know where it is, what it may do next, what evidence is missing, and when human authorization is required.
For agentic systems that must think freely, but move lawfully.
v1.9.1 · Python 3.12+ · MIT · zero runtime dependencies
Install and 60-second example
pip install fpf-thinking-map
python -m fpf_thinking_map.verify # 26/26 checks against your install
python -m fpf_thinking_map.examples # runnable walkthroughs, incl. Ignition Lock
from fpf_thinking_map import (
SemanticMap, ContextPrimitive, RolePrimitive,
TransitionPrimitive, GatePrimitive, GateCheck,
RuntimeBinding, ThinkingMapTraversal,
)
sm = SemanticMap()
sm.register_context(ContextPrimitive("deploy", "Deploy Context"))
sm.register_role(RolePrimitive("owner", "Owner", "deploy"))
sm.register_gate(GatePrimitive(
"release_gate", "Release Gate", "deploy",
checks=[GateCheck("tests", "Green tests", required_evidence=["test_results"])],
))
sm.register_transition(TransitionPrimitive(
"ship", "Ship release", "deploy", "candidate", "released",
required_evidence=["test_results"],
))
engine = ThinkingMapTraversal(sm)
state = engine.build_active_state(
RuntimeBinding(task="release", actor_role_ids=["owner"],
active_context_id="deploy", current_evidence=["test_results"]),
current_state="candidate",
)
print(engine.step(state).kind) # CONTINUE / COLLECT_EVIDENCE / BRIDGE / IDLE / ESCALATE
candidate → gate checks pass on test_results → ship fires → released.
The engine is domain-agnostic: you define your own contexts, evidence,
gates, and transitions. More runnable walkthroughs, including the
human-authorization path below, live in
fpf_thinking_map/examples.py — run them all with
python -m fpf_thinking_map.examples.
What problem it solves
In long multi-step agent runs, models waste budget on self-management: re-checking what was already checked, re-deriving state from prior prose, re-arguing about their own prior reasoning. That's where drift and context noise come from.
This package moves traversal bookkeeping — context, roles, transitions, evidence freshness, guards, outcome kind — out of the token stream and into code, so the model spends capacity on the task instead of on tracking where it is.
Runtime flow
Application / Agent
│
▼
RuntimeBinding
│
▼
ActiveState
│
├── evidence freshness
├── gate checks
├── transition legality
└── authorization
│
▼
Outcome
CONTINUE | COLLECT_EVIDENCE | BRIDGE | IDLE | ESCALATE
Each step() returns a compact JSON slice: where the agent is, what can
fire, what's blocked, what evidence is missing or stale, and which outcome
applies. The map constrains traversal legality — it does not overwrite user
meaning and does not replace model intelligence.
Core capabilities
- Explicit state — contexts, roles, and the active state are first-class objects, not prose the model has to re-derive each turn.
- Evidence gates — transitions declare
required_evidence; the engine checks freshness before it lets a move fire. - Lawful transitions, enforced in code — most agent setups handle this with a system prompt or a rules file: tokens sitting in context, waiting to be deprioritized or reinterpreted as the conversation grows. Here, legality of the next move is computed by
GatePrimitive/TransitionPrimitiveoutside the token stream, so it can't be silently reinterpreted the way prose instructions can. - Inspectable outcomes — every step resolves to one of a fixed set of outcome kinds, not free text (11 declared, 8 currently reachable — see
ARCHITECTURE.md's "What's declared vs. what's reachable").
Ignition Lock & Abort to Orbit — HITL gating with a reroute on denial
requires_human_authorization lets a transition be fully legal by every
FPF-computed measure (evidence fresh, gate satisfied) and still refuse to
fire without a caller passing authorized=True. safe_alternatives +
ActiveState.deny_pending_authorization(...) mean a denial reroutes to a
declared non-destructive twin instead of dead-ending. Motivated by
destructive/irreversible moves, but the primitives are general — a map
author can gate on cost, scope, or anything else that needs a second
party's say-so.
authorized=True is an ambient boolean — it proves a human said yes, not
that they said yes to this state. fpf_thinking_map.authorization.AuthorizationReceipt
scopes the yes to one transition_id and a hash of the exact state it was
issued against (issue_authorization_receipt(state, transition_id, request_id));
attempt_transition(..., authorization=receipt) rejects it if the transition,
state, expiry, or prior consumption don't check out — closing the
inspect-one-state / fire-into-another gap that a bare boolean can't see.
authorized=True still works for callers who haven't migrated.
ADOPTED_IGNITION_LOCK.md— what shipped, why, how it was testedADVISORIES.md—ADV-08(no persistence surface),ADV-10(ungated-by-default lint),ADV-11(unsoundsafe_alternativeslint)run_scenario_destructive_hitl/run_scenario_denied_reroute— runnable walkthroughsdev_mcp— test your own map's use of this against the live engine
AWAIT — waiting on something outside the map, distinct from being done
IDLE used to mean two different things: "done, nothing left to do" and
"nothing to do right now, but something external is still owed" — the
same conflation pending_authorizations already fixed for human decisions
(see ADV-08), applied here to external dependencies instead.
fpf_thinking_map.pending_input.PendingInput declares one such dependency —
a worker result, a human reply, anything the map itself doesn't produce —
with declared wake_conditions describing what would resolve it. When
nothing else is actionable and an unresolved PendingInput exists,
step() returns AWAIT (carrying pending_input_ids and
wake_conditions) instead of IDLE. A candidate action or a context
bridge elsewhere still wins over AWAIT — waiting never hides an
available move. The map never polls, schedules, or resolves the
dependency; the host owns that lifecycle and sets PendingInput.status
itself.
Maps that never declare pending_inputs see no change — AWAIT never
fires and IDLE's behavior is exactly what it was before.
check_pending_input_await— regression, ordering (CONTINUE/BRIDGEstill win overAWAIT), and projection coverage- Why runtime affordance projection was rejected alongside this →
MoveIntent — a concrete proposed move, distinct from its transition type
TransitionPrimitive names a reusable move type — "publish." Every
concrete attempt at firing it ("publish report-v3 to the public site" vs.
"publish report-v4 to a regulator") used to collapse onto that same bare
transition_id, with nothing distinguishing one proposal from another.
fpf_thinking_map.move_intent.MoveIntent gives one concrete proposal a
stable move_id, optional parent_move_id lineage, and a place for its
own parameters to live — opaque to the core, never read by any gate,
guard, or can_fire check.
ThinkingMapTraversal.inspect_move(state, intent) evaluates one without
firing anything — a thin wrapper over the same no-mutation step() path
slice() already uses, safe to call as many times as the model wants to
revise parameters before deciding. attempt_transition(state, transition_id, intent=intent) fires exactly as before, and additionally
stamps MoveTrace.move_id/parent_move_id on success. An intent naming a
different transition than the one that actually fired isn't stamped —
treated as if none were given, not silently crediting the trace to an
unrelated move.
Deliberately not wired in: MoveIntent.parameters does not reset the
stagnation counter. Two distinct concrete moves sharing a transition_id
and evidence snapshot still count as the same stuck retry — folding
opaque parameters into that comparison is a separate policy decision this
feature doesn't make on its own (same gaming-vector tradeoff
evidence-triggered stagnation reset already documents).
check_move_intent— no-mutation inspection, trace stamping, mismatched-intent handling, and the stagnation-counter boundary, asserted rather than silently changedEXPANDED_MOVE_INTENT.md— what shipped, why, what's still design-only
Provenance — v1.7.0–1.9.1 in one line
AuthorizationReceipt ("Clearance"), PendingInput/AWAIT ("Holding
Pattern"), and MoveIntent ("Tail Number") are three separate
mechanisms with one throughline: each closes a gap 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 the traversal is finished versus merely blocked on something
external — instead of flattening that into a bare boolean, a bare
string, or a single overloaded rest state. That's a correctness property
of the logic and traversal layer itself, independent of which model or
harness is driving it; a well-built caller supplying this structure
correctly is a welcome side effect, not the reason it exists.
Full narrative: EXPANDED_PROVENANCE.md · version-by-version: CHANGELOG.md.
Measurements
Tested on 5 shipped decision points:
- compiled
state.slice()averaged 481.4 tokens per decision - raw FPF exact-section prompt averaged 138977.2 tokens per decision
- that is 288.7x smaller per decision (259.0x on live billed input tokens: 537.4 vs 139194.6)
This measures traversal-context size for these five decision points — compiled runtime state versus injecting the equivalent raw FPF source sections — not general model intelligence or total application cost. Full methodology: TRIPLE_TAX_CALCULUS.md.
Architecture and repository components
- Visual architecture → (core engine)
- dev_mcp visual architecture → (MCP tool layer, testing mode)
| Path | What it is |
|---|---|
fpf_thinking_map/ |
Published runtime library — what PyPI ships |
dev_mcp/ |
Development and compliance-testing harness, not shipped, own test suite (38/38 pass), own 11 integrator advisories — kept distinct on purpose rather than folded into core numbers above |
docs/ |
Architecture, experiments, decisions, and adversarial studies |
- Release history → · CHANGELOG.md — version-by-version detail
- Deep decisions/rejections/adoptions →
- Related projects we've reviewed → (not used by us, not a partnership — just noted so we don't forget)
- Live demo →
Relationship to FPF
Based on ailev/FPF by Anatoly Levenchuk. Acknowledged as inspiration and source material, not as a scope lock.
This package is an independent implementation, MIT-licensed, and open to reuse in other developments. It keeps its own runtime scope and, where needed to preserve that scope, omits or explicitly rejects parts of FPF rather than inheriting the framework as an inseparable whole.
FPF is the broad frame. This package is the compact runtime traversal tool.
Scope and non-goals
It is for:
- bounded, stepwise agent traversal
- clearer failure signals
- lower runtime noise
- inspectable behavior
- Ignition Lock — HITL gating on destructive/irreversible transitions, with declared non-destructive alternatives so a denial routes somewhere instead of dead-ending
- Clearance — approval scoped to one transition and the exact inspected state it was given for, not an ambient boolean any caller can assert
- Holding Pattern — distinguishing "waiting on a declared external input" (
AWAIT) from "done, nothing left to do" (IDLE) - Tail Number — a concrete proposed move with its own identity and parameters, distinct from the reusable transition type that fires it
It is not:
- full semantic ingestion of FPF
- a universal reasoning engine
- a replacement for application logic
- an in-engine memory/retrieval system (no embeddings/vector store inside this engine)
- a tool runner, scheduler, or worker/task supervisor (
PendingInput/MoveIntentcarry identity and status the host sets — the engine never polls, executes, or resolves anything itself)
Compatibility: works with model families that can read structured JSON and follow constraints. No model-specific prompt protocol is required by the engine itself.
Design principles: add structure only when behavior improves; keep per-step payload small; keep legality checks explicit; keep model generation free; optimize for inspectability.
Documentation, provenance, attribution, and licence
- Decisions, rejections, adoptions index — theory, adoption/rejection rationale, analysis provenance
- Testing this package's behavior against the documented integrator advisories (evidence staleness, risk-level filtering, bridge trust, and the rest)?
dev_mcpchecks scenario runs against all 8 automatically and keeps a log of what fired — useful if you're integrating this into your own agent and want to know which sharp edges your scenarios actually touched, not just which ones exist on paper. - Repository-wide SHA-256 fingerprints in SHA256SUMS give a simple integrity proof for the tracked source state that ships with this repository.
Maintained by: igareosh.com · Contact: igareosh@igareosh.com · GitHub / Telegram: @igareosh
Inspiration acknowledged: Anatoly Levenchuk / ailev/FPF
Plain-language attribution and scope boundaries live in NOTICE.
License: MIT. See LICENSE. For ownership, attribution, and scope notes, see NOTICE.
Published as a small community implementation: free to use, open to inspect, and meant to be a practical point of discussion rather than a total framework.
"Pick something. Get good at it. See if you can be the best at it." — Jordan Peterson
"All speech is vain and empty unless it be accompanied by action." — Demosthenes
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fpf_thinking_map-1.9.1.tar.gz.
File metadata
- Download URL: fpf_thinking_map-1.9.1.tar.gz
- Upload date:
- Size: 69.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e201ebf473ce9fda985ac03f949c4681f2f4ce505c0d37a8ed2a7d000227455f
|
|
| MD5 |
01eef17db7ea0e4dfe66857e58f01f1b
|
|
| BLAKE2b-256 |
465f71ffbc2af34272bce1ebb573e48f8243a58ee90b3a49fff2699670aedee0
|
Provenance
The following attestation bundles were made for fpf_thinking_map-1.9.1.tar.gz:
Publisher:
publish.yml on igareosh/fpf-agentic-thinking-map
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fpf_thinking_map-1.9.1.tar.gz -
Subject digest:
e201ebf473ce9fda985ac03f949c4681f2f4ce505c0d37a8ed2a7d000227455f - Sigstore transparency entry: 2225246154
- Sigstore integration time:
-
Permalink:
igareosh/fpf-agentic-thinking-map@fa68332966b1ecfea4078e1da8124ab066a6ecfc -
Branch / Tag:
refs/tags/v1.9.1 - Owner: https://github.com/igareosh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fa68332966b1ecfea4078e1da8124ab066a6ecfc -
Trigger Event:
release
-
Statement type:
File details
Details for the file fpf_thinking_map-1.9.1-py3-none-any.whl.
File metadata
- Download URL: fpf_thinking_map-1.9.1-py3-none-any.whl
- Upload date:
- Size: 70.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
33f5af153a7fac6321b3b4a4fcfe6af0bffc28d1b2496f5b0e047ff48f9e8f25
|
|
| MD5 |
57a4fddb07a1b5821f69fe3607e420f3
|
|
| BLAKE2b-256 |
26b0e0f601ba9b0c280c0d0e232cc075bc59a884ae14b4ef2b97c45018cb2b81
|
Provenance
The following attestation bundles were made for fpf_thinking_map-1.9.1-py3-none-any.whl:
Publisher:
publish.yml on igareosh/fpf-agentic-thinking-map
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fpf_thinking_map-1.9.1-py3-none-any.whl -
Subject digest:
33f5af153a7fac6321b3b4a4fcfe6af0bffc28d1b2496f5b0e047ff48f9e8f25 - Sigstore transparency entry: 2225246801
- Sigstore integration time:
-
Permalink:
igareosh/fpf-agentic-thinking-map@fa68332966b1ecfea4078e1da8124ab066a6ecfc -
Branch / Tag:
refs/tags/v1.9.1 - Owner: https://github.com/igareosh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fa68332966b1ecfea4078e1da8124ab066a6ecfc -
Trigger Event:
release
-
Statement type: