Skip to main content

FPF Agentic Thinking Map — Agent freedom. Explicit movement rules.

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

PyPI version Python versions License Zero dependencies Verify Live demo Downloads (honest)


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_resultsship 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 / TransitionPrimitive outside 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.


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.


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 changed
  • EXPANDED_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

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

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/MoveIntent carry 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_mcp checks 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

fpf_thinking_map-1.9.1.tar.gz (69.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fpf_thinking_map-1.9.1-py3-none-any.whl (70.2 kB view details)

Uploaded Python 3

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

Hashes for fpf_thinking_map-1.9.1.tar.gz
Algorithm Hash digest
SHA256 e201ebf473ce9fda985ac03f949c4681f2f4ce505c0d37a8ed2a7d000227455f
MD5 01eef17db7ea0e4dfe66857e58f01f1b
BLAKE2b-256 465f71ffbc2af34272bce1ebb573e48f8243a58ee90b3a49fff2699670aedee0

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fpf_thinking_map-1.9.1-py3-none-any.whl.

File metadata

File hashes

Hashes for fpf_thinking_map-1.9.1-py3-none-any.whl
Algorithm Hash digest
SHA256 33f5af153a7fac6321b3b4a4fcfe6af0bffc28d1b2496f5b0e047ff48f9e8f25
MD5 57a4fddb07a1b5821f69fe3607e420f3
BLAKE2b-256 26b0e0f601ba9b0c280c0d0e232cc075bc59a884ae14b4ef2b97c45018cb2b81

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.9.5

2 files

1.9.4

2 files

1.9.3

2 files

1.9.2

2 files

This release

1.9.1 This release

2 files

1.6.0

2 files

1.5.0

2 files

1.4.25

2 files

1.4.24

2 files

1.4.23

2 files

1.4.22

2 files

1.4.21

2 files

1.4.20

2 files

1.4.19

2 files

1.4.18

2 files

1.4.17

2 files

1.4.16

2 files

1.4.15

2 files

1.4.14

2 files

1.4.13

2 files

1.4.12

2 files

1.4.11

2 files

1.4.10

2 files

1.4.9

2 files

1.4.8

2 files

1.4.7

2 files

1.4.6

2 files

1.4.5

2 files

1.4.4

2 files

1.4.3

2 files

1.4.2

2 files

1.4.1

2 files

1.4.0

2 files

1.3.1

2 files

1.3.0

2 files

1.2.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page