fpf-thinking-map 1.3.0__tar.gz → 1.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/PKG-INFO +112 -12
  2. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/README.md +110 -9
  3. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/README.md +15 -6
  4. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/WHY_THIS_EXISTS.md +7 -1
  5. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/state.py +65 -0
  6. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/traversal.py +2 -0
  7. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/verify.py +59 -0
  8. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map.egg-info/PKG-INFO +112 -12
  9. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/pyproject.toml +2 -3
  10. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/LICENSE +0 -0
  11. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/FPF_AUDIT_RESPONSE.md +0 -0
  12. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/FPF_FLOOR_MAP.md +0 -0
  13. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/FPF_SOURCE_TO_CODE_RELATION_AUDIT.md +0 -0
  14. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/REJECTED_C32_CANDIDATE_SYNTHESIS.md +0 -0
  15. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/REJECTED_NQD_OEE_CULTURAL_EVOLUTION.md +0 -0
  16. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/RELATED_WORK_GOFLOW_FPF_SKILL.md +0 -0
  17. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/SOURCES.md +0 -0
  18. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/__init__.py +0 -0
  19. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/examples.py +0 -0
  20. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/guards.py +0 -0
  21. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/logic.py +0 -0
  22. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/primitives.py +0 -0
  23. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map/py.typed +0 -0
  24. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map.egg-info/SOURCES.txt +0 -0
  25. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map.egg-info/dependency_links.txt +0 -0
  26. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/fpf_thinking_map.egg-info/top_level.txt +0 -0
  27. {fpf_thinking_map-1.3.0 → fpf_thinking_map-1.4.0}/setup.cfg +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fpf-thinking-map
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: Agentic thinking map — structured reasoning constraints for LLMs with TTL evidence decay, deterministic guards, and propositional logic glue. Compiled from FPF (First Principles Framework).
5
5
  Author-email: "prichindel.com" <igareosh@igareosh.com>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/igareosh/prichindel.com-agentic-thinking-map
8
8
  Project-URL: Repository, https://github.com/igareosh/prichindel.com-agentic-thinking-map
9
9
  Project-URL: Documentation, https://github.com/igareosh/prichindel.com-agentic-thinking-map#readme
@@ -12,7 +12,6 @@ Keywords: agentic,thinking-map,llm,reasoning,propositional-logic,evidence-decay,
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Intended Audience :: Science/Research
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Operating System :: OS Independent
17
16
  Classifier: Programming Language :: Python :: 3
18
17
  Classifier: Programming Language :: Python :: 3.12
@@ -27,24 +26,53 @@ Dynamic: license-file
27
26
 
28
27
  # prichindel.com Agentic Thinking Map
29
28
 
30
- **v1.3.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
29
+ **v1.4.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF), compiled into a small traversal map for LLM agents.
31
30
 
32
- A Python package that gives an AI model a small, structured board to reason on one move at a time. Instead of freeform text generation, the model navigates a pre-shaped semantic field with deterministic guards and propositional logic constraints.
31
+ A Python package that gives a model a bounded move board instead of a giant framework to digest at runtime. Instead of rereading a sprawling semantic corpus and improvising from it, the model gets a small JSON slice: what context it is in, what move is open, what evidence is missing, what is risky, and what outcome class applies.
33
32
 
34
33
  **[Visual architecture →](ARCHITECTURE.md)** — module graph, step flowchart, floor map, evidence lifecycle, slice structure, deploy sequence diagram.
35
34
 
35
+ ## Why this exists
36
+
37
+ FPF is valuable, but it is large, clumsy at runtime, and poorly shaped for direct model consumption. A human can read a 51k-line framework, understand its distinctions, and apply them carefully. A model usually does something else: it absorbs the vocabulary, performs the tone of the framework, and still drifts on the actual task.
38
+
39
+ This package exists because the useful part is not making the model sound more scientific. The useful part is compiling the parts of FPF that help operational decisions into something small, explicit, and inspectable. We took what mattered for bounded traversal, left out what would bloat or bias the model, and evolved the result as publishers of a practical library rather than trying to reproduce the whole upstream framework inside another AI mega-system.
40
+
41
+ The goal is simple: give the model enough structure to behave understandably, without building a cage so elaborate that it becomes another source of drift.
42
+
43
+ We do not compete with FPF and we do not need to. FPF stays FPF: broad, ambitious, and useful as a frame. This library is the smaller usable tool for people who want the frame without carrying the whole corpus into every project.
44
+
36
45
  ## What it does
37
46
 
38
- You define a domain as a semantic map (contexts, roles, gates, evidence, transitions). The model gets a per-move slice — just the current transition, its gate, its evidence, and whether it can fire. Deterministic guards enforce hard constraints the model cannot override. Propositional logic rules (NOT, AND, OR, XOR, IMPLIES, IFF) provide decision glue between the semantic primitives and the model's reasoning.
47
+ You define a domain as a semantic map: contexts, roles, gates, evidence, transitions. The model gets a per-move slice, not a theory dump. Deterministic guards handle the hard checks. Propositional logic rules (NOT, AND, OR, XOR, IMPLIES, IFF) provide explicit decision glue between the semantic primitives and the current state.
48
+
49
+ The model's job is not "what does FPF mean?" It is: **given this map and this state, what is the best lawful next move?**
50
+
51
+ In practice this does two useful things:
52
+
53
+ - it reduces unexplained drift by moving state tracking and hard checks out of freeform model reasoning
54
+ - it gives the model simple, understandable outcomes such as `CONTINUE`, `COLLECT_EVIDENCE`, `BRIDGE`, `IDLE`, `ESCALATE`, instead of making it reconstruct its own epistemic condition from scratch
55
+
56
+ This is not a panacea. Models still miss information. They still drift. But with a bounded traversal map, the failure mode becomes much easier to explain: missing evidence, wrong context, blocked move, stale basis, unlicensed bridge. The weirdness gets smaller because the state is smaller.
57
+
58
+ ## Provable practical gains
39
59
 
40
- The model's job is not "what does FPF mean?" — it is: **given this semantic map and this state, what is the best lawful next move?**
60
+ The improvements here are intentionally simple and operational:
61
+
62
+ - less random drift, because the agent reads explicit state instead of reconstructing state from prose
63
+ - fewer unlawful moves, because guards and scoped transitions check them before the model improvises
64
+ - clearer failure, because "why blocked" is surfaced directly
65
+ - smaller runtime payload, because the model gets a slice, not a corpus
66
+ - easier debugging, because outcomes are discrete and inspectable
67
+
68
+ That is the whole point: practical gain, not intellectual theater.
41
69
 
42
70
  ## Quick start
43
71
 
44
72
  ```bash
45
73
  # No dependencies. Python 3.12+.
46
74
 
47
- # Verify the package (21 checks)
75
+ # Verify the package (22 checks)
48
76
  python -m fpf_thinking_map.verify
49
77
 
50
78
  # Run the deploy decision scenario
@@ -60,7 +88,7 @@ fpf_thinking_map/
60
88
  ├── guards.py 9 deterministic guards (context, role, gate, evidence, assignment, speech act, readiness)
61
89
  ├── logic.py 6 logic operators + decision rules + LogicLayer
62
90
  ├── traversal.py Step engine with 10 lawful outcomes (incl. IDLE, BRIDGE)
63
- ├── verify.py Self-verification harness (21/21 checks)
91
+ ├── verify.py Self-verification harness (22/22 checks)
64
92
  ├── examples.py 5 deploy decision scenarios (missing evidence, role conflict, logic glue, truth table)
65
93
  ├── README.md Full documentation (any-model readable)
66
94
  ├── SOURCES.md Source attribution (FPF spec + Mitev lectures)
@@ -76,6 +104,8 @@ fpf_thinking_map/
76
104
 
77
105
  This package is built on [ailev/FPF](https://github.com/ailev/FPF) by Anatoly Levenchuk. It is an independent implementation — our own research and code, MIT-licensed, with further development rights.
78
106
 
107
+ It is not an attempt to "finish" FPF, replace FPF, or repackage the whole corpus for LLM ingestion. It is a selective compilation for agentic traversal.
108
+
79
109
  ### What we reviewed
80
110
 
81
111
  We cross-checked the following FPF commits (June 2026 precision restoration cluster) against our code:
@@ -104,6 +134,33 @@ Two FPF pattern families rejected for activation bias — they amplify existing
104
134
 
105
135
  **Design rule**: the map evaluates and constrains moves. It does not propose them. Generative, branch-friendly, candidate-multiplying patterns are the opposite of what a per-move guard-constrained thinking map should contain.
106
136
 
137
+ ## Scope and non-goals
138
+
139
+ This library is intentionally small in scope.
140
+
141
+ - It is for bounded, stepwise agent traversal in a defined domain.
142
+ - It is for making model behavior more understandable, not more magical.
143
+ - It is for simple +/- operational decisions in context, not for open-ended idea generation.
144
+ - It is for constraining and instrumenting an agent run, not replacing the model.
145
+
146
+ It is not:
147
+
148
+ - a full semantic ingestion layer for all of FPF
149
+ - a universal reasoning engine
150
+ - a replacement for ordinary application logic or policy code
151
+ - a proof that an LLM will behave correctly just because the map exists
152
+ - Memory-Augmented Generation (MAG) — no embeddings, no vector store, no cross-session retrieval, ever, inside this engine. Rejected deliberately and permanently, not by omission. See below.
153
+
154
+ ## What this actually guarantees
155
+
156
+ fpf does not ship better reasoning. It ships **inspectability**: did the agent follow the lawful path, and was evidence honestly reported at the one place that matters — the wire between what the agent actually did and what this library was told.
157
+
158
+ Take the stagnation counter as the concrete case. It bounds repetition — but only if the integration maps one real attempt to one `step()` call, and only adds evidence when something genuinely new was found. Whether that actually catches a real LLM thrashing depends entirely on whether the calling harness wires evidence honestly, not just "a tool returned something." That wiring discipline lives outside this library, in whatever integrates it — an agent loop, an MCP server, application code. fpf can't verify that discipline was followed; it can only guarantee that if it was, repetition is bounded.
159
+
160
+ That boundary is the correct seam, not a gap to apologize for. Judging whether a piece of evidence is actually meaningful is a semantic question, and semantic questions need prompting, an LLM judge, or domain logic to answer — which is exactly where that belongs: outside fpf, at the evidence-wiring layer, not folded into a library whose entire value is staying small and deterministic. If you want semantics and clever prompting, that's the plug-in point.
161
+
162
+ **This is also why Memory-Augmented Generation (MAG) is rejected as fpf's own architecture, deliberately and permanently.** MAG — retrieval, embeddings, cross-session recall — solves a different problem (what did we discuss three sessions ago) than fpf solves (is this move lawful right now), and it needs exactly the dependency profile this library rules out by design. That is a scope boundary, not a verdict on MAG as a technique: a MAG system is a legitimate thing to run *upstream* of fpf. Whatever it retrieves becomes an evidence_id in `RuntimeBinding.current_evidence` like any other — fpf doesn't care how evidence was produced, only whether it's present, fresh, and licensed. Memory belongs at the evidence-wiring layer, alongside every other semantic judgment this library deliberately doesn't make.
163
+
107
164
  ## Sources
108
165
 
109
166
  - **[FPF (First Principles Framework)](https://github.com/ailev/FPF)** by Anatoly Levenchuk — transdisciplinary specification (~51k lines). We extracted 10 semantic primitives and 9 guard rules. This is a compiled distillation, not a port.
@@ -111,6 +168,22 @@ Two FPF pattern families rejected for activation bias — they amplify existing
111
168
 
112
169
  Full attribution in [SOURCES.md](fpf_thinking_map/SOURCES.md).
113
170
 
171
+ ## Why it works
172
+
173
+ The core advantage is not "more theory." It is less runtime burden.
174
+
175
+ Without a compiled map, the model keeps re-addressing its own run: am I allowed to move, did I already satisfy the gate, am I missing evidence, am I in the wrong context, am I done or blocked? That self-management loop is where a lot of bad agent behavior comes from.
176
+
177
+ This package turns that loop into a small stateful instrument panel. The model sees what can fire, what cannot, and why. That is enough of an operating surface for many practical agent tasks. Not a panacea, not a grand theory of intelligence, just enough window and file-handles for the model to open the right thing without smashing the house.
178
+
179
+ The bottleneck was never raw model capability — it's self-management overhead eating the capability that's already there. Most orgs running these models, at any scale, hit that ceiling: infra and process that never actually asked the model for full capacity, so upgrading the model changes nothing. This library doesn't make the model smarter. It shrinks what the model has to hold in its head at any one step, so whatever capability is actually there gets spent on the real problem instead of on self-management overhead.
180
+
181
+ Clean product truth:
182
+
183
+ - FPF is the frame.
184
+ - This library is the usable tool.
185
+ - It keeps the frame where it helps, removes the bloat where it hurts, and gives agents a small operational board that produces simpler, more provable behavior.
186
+
114
187
  ## Why v1.1 exists — reasoning about reasoning is the bug
115
188
 
116
189
  When an LLM navigates a multi-step decision, it faces the same sub-questions at every step: "Is my evidence still valid? Am I going in circles? Is there another path? Why am I blocked?" Without structure, the model re-derives these answers from scratch each time — re-reasoning on its own prior reasoning. Each pass costs tokens, drifts from the original question, and eventually the context fills up with the model arguing with itself about whether it already checked something it checked three steps ago.
@@ -131,6 +204,18 @@ The model's job shrinks from "figure out the entire epistemic state of your own
131
204
  - **Evidence status in prompt** — the LLM prompt state now includes per-evidence freshness and TTL remaining. The model sees "test_results: 3 steps left" instead of just "test_results: exists." Decisions informed by countdown, not by guessing.
132
205
  - **Response contract** — every slice now ends with a `response_contract`: the structured template the model must fill when responding. Pre-filled fields (scope, basis with freshness/TTL, allowed use, not allowed use, modality, canonical terms, audience) come from the computed state. Empty fields (claim, risky aliases) are for the model. This is why all the code exists — so the contract has precomputed, validated values instead of being re-derived by the model from scratch.
133
206
 
207
+ ## v1.3.0 changes
208
+
209
+ - **Bridge crossing is enforced, not just advertised** — `ActiveState.cross_bridge()` / `ThinkingMapTraversal.attempt_bridge()` actually perform a cross-context hop and check `substitution_license` against `risk_level` before mutating state. An unlicensed bridge under `high`/`critical` risk is refused (`ESCALATE`), not silently allowed. Before this, `bridge_options()` was advisory metadata only — the model decided for itself whether a lossy translation was acceptable.
210
+ - **`include_full_state=False`** — `step()` can now ship the scoped `slice()` alone, without the whole board bolted on. Default stays `True`; opt in when the caller already knows its `transition_id` and wants the lean payload.
211
+ - 21/21 self-verification checks (`python -m fpf_thinking_map.verify`), two new: bridge crossing and lean-slice payload shape.
212
+
213
+ ## v1.4.0 changes
214
+
215
+ - **Stagnation counter** — `ActiveState.register_visit()` / `visit_count` / `visits_remaining` / `is_stagnant` count consecutive `step()`s at the same `(context, state)` pair with no new evidence gathered; the counter resets the moment the evidence set changes. `visits_remaining` mirrors `ttl_remaining`'s exact shape — a countdown, not a boolean. Surfaced in both `slice()` and `to_llm_prompt_state()` as a `stagnation` block. Pure signal: no new outcome kind, nothing blocks, no restraint added to the model's options. Closes a real blind spot — `MoveTrace` is deliberately last-move-only (no narrative accumulation), so the engine previously had no way to notice "I've revisited this exact state N times with nothing new to show for it."
216
+ - The guarantee is conditional, and the docs say so plainly: this tracks evidence *set membership*, not evidence *meaning*. It bounds repetition only if the integration maps one real attempt to one `step()` call and only adds evidence that's genuinely new — see [What this actually guarantees](#what-this-actually-guarantees).
217
+ - 22/22 self-verification checks, one new: `check_stagnation_counter`.
218
+
134
219
  ## Design principles
135
220
 
136
221
  - **Only add structure when it changes agentic behavior** — not for source fidelity alone
@@ -140,10 +225,10 @@ The model's job shrinks from "figure out the entire epistemic state of your own
140
225
 
141
226
  ## Using as a dependency
142
227
 
143
- This package is the reasoning engine. Your domain maps run on top of it.
228
+ This package is the compiled map, not a thinking replacement for the model. Denying unlawful moves is only half the job. The bigger half is returning the lawful surface as a small, plain-JSON slice: what can fire, what is missing, what is risky, why a move is blocked, what response shape is expected. That lets the model self-address its own run with simple signals such as `can_fire`, `blockers`, and `outcome.kind` instead of re-reasoning about its state from prose.
144
229
 
145
230
  ```bash
146
- pip install git+https://github.com/igareosh/prichindel.com-agentic-thinking-map.git
231
+ pip install fpf-thinking-map
147
232
  ```
148
233
 
149
234
  ```python
@@ -207,10 +292,25 @@ Built with Claude Code (Anthropic claude-sonnet-4-6). Tested and verified to wor
207
292
 
208
293
  This is not a compliance seal. It means: we used these models against this package and they produced correct, usable results. The per-move slice is small enough for mid-tier models. The logic and guard outputs are plain JSON — no special tokenization or prompt format required.
209
294
 
295
+ ## Why release this
296
+
297
+ We release this because it is useful beyond our own stack.
298
+
299
+ - It captures a practical subset of FPF in a form models can actually use.
300
+ - It gives agent builders a small constraint surface instead of another giant AI framework.
301
+ - It helps turn model failure from mystical drift into inspectable state.
302
+ - It stays intentionally narrow, so it can remain legible instead of becoming another overbuilt "agent platform."
303
+
304
+ ## Credits
305
+
306
+ - **igareosh** — project direction, scope discipline, practical use-case pressure, and product truth
307
+ - **Claude Code (Anthropic)** — implementation support, library iteration, and earlier release preparation
308
+ - **Codex / OpenAI** — product framing polish, packaging cleanup, verification, and release-readiness work
309
+
210
310
  ## License
211
311
 
212
312
  MIT. See [LICENSE](LICENSE).
213
313
 
214
314
  ---
215
315
 
216
- **prichindel.com** — v1.2.1 — 2026-06-26
316
+ **prichindel.com** — v1.4.0 — 2026-07-07
@@ -1,23 +1,52 @@
1
1
  # prichindel.com Agentic Thinking Map
2
2
 
3
- **v1.3.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
3
+ **v1.4.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF), compiled into a small traversal map for LLM agents.
4
4
 
5
- A Python package that gives an AI model a small, structured board to reason on one move at a time. Instead of freeform text generation, the model navigates a pre-shaped semantic field with deterministic guards and propositional logic constraints.
5
+ A Python package that gives a model a bounded move board instead of a giant framework to digest at runtime. Instead of rereading a sprawling semantic corpus and improvising from it, the model gets a small JSON slice: what context it is in, what move is open, what evidence is missing, what is risky, and what outcome class applies.
6
6
 
7
7
  **[Visual architecture →](ARCHITECTURE.md)** — module graph, step flowchart, floor map, evidence lifecycle, slice structure, deploy sequence diagram.
8
8
 
9
+ ## Why this exists
10
+
11
+ FPF is valuable, but it is large, clumsy at runtime, and poorly shaped for direct model consumption. A human can read a 51k-line framework, understand its distinctions, and apply them carefully. A model usually does something else: it absorbs the vocabulary, performs the tone of the framework, and still drifts on the actual task.
12
+
13
+ This package exists because the useful part is not making the model sound more scientific. The useful part is compiling the parts of FPF that help operational decisions into something small, explicit, and inspectable. We took what mattered for bounded traversal, left out what would bloat or bias the model, and evolved the result as publishers of a practical library rather than trying to reproduce the whole upstream framework inside another AI mega-system.
14
+
15
+ The goal is simple: give the model enough structure to behave understandably, without building a cage so elaborate that it becomes another source of drift.
16
+
17
+ We do not compete with FPF and we do not need to. FPF stays FPF: broad, ambitious, and useful as a frame. This library is the smaller usable tool for people who want the frame without carrying the whole corpus into every project.
18
+
9
19
  ## What it does
10
20
 
11
- You define a domain as a semantic map (contexts, roles, gates, evidence, transitions). The model gets a per-move slice — just the current transition, its gate, its evidence, and whether it can fire. Deterministic guards enforce hard constraints the model cannot override. Propositional logic rules (NOT, AND, OR, XOR, IMPLIES, IFF) provide decision glue between the semantic primitives and the model's reasoning.
21
+ You define a domain as a semantic map: contexts, roles, gates, evidence, transitions. The model gets a per-move slice, not a theory dump. Deterministic guards handle the hard checks. Propositional logic rules (NOT, AND, OR, XOR, IMPLIES, IFF) provide explicit decision glue between the semantic primitives and the current state.
22
+
23
+ The model's job is not "what does FPF mean?" It is: **given this map and this state, what is the best lawful next move?**
24
+
25
+ In practice this does two useful things:
26
+
27
+ - it reduces unexplained drift by moving state tracking and hard checks out of freeform model reasoning
28
+ - it gives the model simple, understandable outcomes such as `CONTINUE`, `COLLECT_EVIDENCE`, `BRIDGE`, `IDLE`, `ESCALATE`, instead of making it reconstruct its own epistemic condition from scratch
29
+
30
+ This is not a panacea. Models still miss information. They still drift. But with a bounded traversal map, the failure mode becomes much easier to explain: missing evidence, wrong context, blocked move, stale basis, unlicensed bridge. The weirdness gets smaller because the state is smaller.
31
+
32
+ ## Provable practical gains
12
33
 
13
- The model's job is not "what does FPF mean?" — it is: **given this semantic map and this state, what is the best lawful next move?**
34
+ The improvements here are intentionally simple and operational:
35
+
36
+ - less random drift, because the agent reads explicit state instead of reconstructing state from prose
37
+ - fewer unlawful moves, because guards and scoped transitions check them before the model improvises
38
+ - clearer failure, because "why blocked" is surfaced directly
39
+ - smaller runtime payload, because the model gets a slice, not a corpus
40
+ - easier debugging, because outcomes are discrete and inspectable
41
+
42
+ That is the whole point: practical gain, not intellectual theater.
14
43
 
15
44
  ## Quick start
16
45
 
17
46
  ```bash
18
47
  # No dependencies. Python 3.12+.
19
48
 
20
- # Verify the package (21 checks)
49
+ # Verify the package (22 checks)
21
50
  python -m fpf_thinking_map.verify
22
51
 
23
52
  # Run the deploy decision scenario
@@ -33,7 +62,7 @@ fpf_thinking_map/
33
62
  ├── guards.py 9 deterministic guards (context, role, gate, evidence, assignment, speech act, readiness)
34
63
  ├── logic.py 6 logic operators + decision rules + LogicLayer
35
64
  ├── traversal.py Step engine with 10 lawful outcomes (incl. IDLE, BRIDGE)
36
- ├── verify.py Self-verification harness (21/21 checks)
65
+ ├── verify.py Self-verification harness (22/22 checks)
37
66
  ├── examples.py 5 deploy decision scenarios (missing evidence, role conflict, logic glue, truth table)
38
67
  ├── README.md Full documentation (any-model readable)
39
68
  ├── SOURCES.md Source attribution (FPF spec + Mitev lectures)
@@ -49,6 +78,8 @@ fpf_thinking_map/
49
78
 
50
79
  This package is built on [ailev/FPF](https://github.com/ailev/FPF) by Anatoly Levenchuk. It is an independent implementation — our own research and code, MIT-licensed, with further development rights.
51
80
 
81
+ It is not an attempt to "finish" FPF, replace FPF, or repackage the whole corpus for LLM ingestion. It is a selective compilation for agentic traversal.
82
+
52
83
  ### What we reviewed
53
84
 
54
85
  We cross-checked the following FPF commits (June 2026 precision restoration cluster) against our code:
@@ -77,6 +108,33 @@ Two FPF pattern families rejected for activation bias — they amplify existing
77
108
 
78
109
  **Design rule**: the map evaluates and constrains moves. It does not propose them. Generative, branch-friendly, candidate-multiplying patterns are the opposite of what a per-move guard-constrained thinking map should contain.
79
110
 
111
+ ## Scope and non-goals
112
+
113
+ This library is intentionally small in scope.
114
+
115
+ - It is for bounded, stepwise agent traversal in a defined domain.
116
+ - It is for making model behavior more understandable, not more magical.
117
+ - It is for simple +/- operational decisions in context, not for open-ended idea generation.
118
+ - It is for constraining and instrumenting an agent run, not replacing the model.
119
+
120
+ It is not:
121
+
122
+ - a full semantic ingestion layer for all of FPF
123
+ - a universal reasoning engine
124
+ - a replacement for ordinary application logic or policy code
125
+ - a proof that an LLM will behave correctly just because the map exists
126
+ - Memory-Augmented Generation (MAG) — no embeddings, no vector store, no cross-session retrieval, ever, inside this engine. Rejected deliberately and permanently, not by omission. See below.
127
+
128
+ ## What this actually guarantees
129
+
130
+ fpf does not ship better reasoning. It ships **inspectability**: did the agent follow the lawful path, and was evidence honestly reported at the one place that matters — the wire between what the agent actually did and what this library was told.
131
+
132
+ Take the stagnation counter as the concrete case. It bounds repetition — but only if the integration maps one real attempt to one `step()` call, and only adds evidence when something genuinely new was found. Whether that actually catches a real LLM thrashing depends entirely on whether the calling harness wires evidence honestly, not just "a tool returned something." That wiring discipline lives outside this library, in whatever integrates it — an agent loop, an MCP server, application code. fpf can't verify that discipline was followed; it can only guarantee that if it was, repetition is bounded.
133
+
134
+ That boundary is the correct seam, not a gap to apologize for. Judging whether a piece of evidence is actually meaningful is a semantic question, and semantic questions need prompting, an LLM judge, or domain logic to answer — which is exactly where that belongs: outside fpf, at the evidence-wiring layer, not folded into a library whose entire value is staying small and deterministic. If you want semantics and clever prompting, that's the plug-in point.
135
+
136
+ **This is also why Memory-Augmented Generation (MAG) is rejected as fpf's own architecture, deliberately and permanently.** MAG — retrieval, embeddings, cross-session recall — solves a different problem (what did we discuss three sessions ago) than fpf solves (is this move lawful right now), and it needs exactly the dependency profile this library rules out by design. That is a scope boundary, not a verdict on MAG as a technique: a MAG system is a legitimate thing to run *upstream* of fpf. Whatever it retrieves becomes an evidence_id in `RuntimeBinding.current_evidence` like any other — fpf doesn't care how evidence was produced, only whether it's present, fresh, and licensed. Memory belongs at the evidence-wiring layer, alongside every other semantic judgment this library deliberately doesn't make.
137
+
80
138
  ## Sources
81
139
 
82
140
  - **[FPF (First Principles Framework)](https://github.com/ailev/FPF)** by Anatoly Levenchuk — transdisciplinary specification (~51k lines). We extracted 10 semantic primitives and 9 guard rules. This is a compiled distillation, not a port.
@@ -84,6 +142,22 @@ Two FPF pattern families rejected for activation bias — they amplify existing
84
142
 
85
143
  Full attribution in [SOURCES.md](fpf_thinking_map/SOURCES.md).
86
144
 
145
+ ## Why it works
146
+
147
+ The core advantage is not "more theory." It is less runtime burden.
148
+
149
+ Without a compiled map, the model keeps re-addressing its own run: am I allowed to move, did I already satisfy the gate, am I missing evidence, am I in the wrong context, am I done or blocked? That self-management loop is where a lot of bad agent behavior comes from.
150
+
151
+ This package turns that loop into a small stateful instrument panel. The model sees what can fire, what cannot, and why. That is enough of an operating surface for many practical agent tasks. Not a panacea, not a grand theory of intelligence, just enough window and file-handles for the model to open the right thing without smashing the house.
152
+
153
+ The bottleneck was never raw model capability — it's self-management overhead eating the capability that's already there. Most orgs running these models, at any scale, hit that ceiling: infra and process that never actually asked the model for full capacity, so upgrading the model changes nothing. This library doesn't make the model smarter. It shrinks what the model has to hold in its head at any one step, so whatever capability is actually there gets spent on the real problem instead of on self-management overhead.
154
+
155
+ Clean product truth:
156
+
157
+ - FPF is the frame.
158
+ - This library is the usable tool.
159
+ - It keeps the frame where it helps, removes the bloat where it hurts, and gives agents a small operational board that produces simpler, more provable behavior.
160
+
87
161
  ## Why v1.1 exists — reasoning about reasoning is the bug
88
162
 
89
163
  When an LLM navigates a multi-step decision, it faces the same sub-questions at every step: "Is my evidence still valid? Am I going in circles? Is there another path? Why am I blocked?" Without structure, the model re-derives these answers from scratch each time — re-reasoning on its own prior reasoning. Each pass costs tokens, drifts from the original question, and eventually the context fills up with the model arguing with itself about whether it already checked something it checked three steps ago.
@@ -104,6 +178,18 @@ The model's job shrinks from "figure out the entire epistemic state of your own
104
178
  - **Evidence status in prompt** — the LLM prompt state now includes per-evidence freshness and TTL remaining. The model sees "test_results: 3 steps left" instead of just "test_results: exists." Decisions informed by countdown, not by guessing.
105
179
  - **Response contract** — every slice now ends with a `response_contract`: the structured template the model must fill when responding. Pre-filled fields (scope, basis with freshness/TTL, allowed use, not allowed use, modality, canonical terms, audience) come from the computed state. Empty fields (claim, risky aliases) are for the model. This is why all the code exists — so the contract has precomputed, validated values instead of being re-derived by the model from scratch.
106
180
 
181
+ ## v1.3.0 changes
182
+
183
+ - **Bridge crossing is enforced, not just advertised** — `ActiveState.cross_bridge()` / `ThinkingMapTraversal.attempt_bridge()` actually perform a cross-context hop and check `substitution_license` against `risk_level` before mutating state. An unlicensed bridge under `high`/`critical` risk is refused (`ESCALATE`), not silently allowed. Before this, `bridge_options()` was advisory metadata only — the model decided for itself whether a lossy translation was acceptable.
184
+ - **`include_full_state=False`** — `step()` can now ship the scoped `slice()` alone, without the whole board bolted on. Default stays `True`; opt in when the caller already knows its `transition_id` and wants the lean payload.
185
+ - 21/21 self-verification checks (`python -m fpf_thinking_map.verify`), two new: bridge crossing and lean-slice payload shape.
186
+
187
+ ## v1.4.0 changes
188
+
189
+ - **Stagnation counter** — `ActiveState.register_visit()` / `visit_count` / `visits_remaining` / `is_stagnant` count consecutive `step()`s at the same `(context, state)` pair with no new evidence gathered; the counter resets the moment the evidence set changes. `visits_remaining` mirrors `ttl_remaining`'s exact shape — a countdown, not a boolean. Surfaced in both `slice()` and `to_llm_prompt_state()` as a `stagnation` block. Pure signal: no new outcome kind, nothing blocks, no restraint added to the model's options. Closes a real blind spot — `MoveTrace` is deliberately last-move-only (no narrative accumulation), so the engine previously had no way to notice "I've revisited this exact state N times with nothing new to show for it."
190
+ - The guarantee is conditional, and the docs say so plainly: this tracks evidence *set membership*, not evidence *meaning*. It bounds repetition only if the integration maps one real attempt to one `step()` call and only adds evidence that's genuinely new — see [What this actually guarantees](#what-this-actually-guarantees).
191
+ - 22/22 self-verification checks, one new: `check_stagnation_counter`.
192
+
107
193
  ## Design principles
108
194
 
109
195
  - **Only add structure when it changes agentic behavior** — not for source fidelity alone
@@ -113,10 +199,10 @@ The model's job shrinks from "figure out the entire epistemic state of your own
113
199
 
114
200
  ## Using as a dependency
115
201
 
116
- This package is the reasoning engine. Your domain maps run on top of it.
202
+ This package is the compiled map, not a thinking replacement for the model. Denying unlawful moves is only half the job. The bigger half is returning the lawful surface as a small, plain-JSON slice: what can fire, what is missing, what is risky, why a move is blocked, what response shape is expected. That lets the model self-address its own run with simple signals such as `can_fire`, `blockers`, and `outcome.kind` instead of re-reasoning about its state from prose.
117
203
 
118
204
  ```bash
119
- pip install git+https://github.com/igareosh/prichindel.com-agentic-thinking-map.git
205
+ pip install fpf-thinking-map
120
206
  ```
121
207
 
122
208
  ```python
@@ -180,10 +266,25 @@ Built with Claude Code (Anthropic claude-sonnet-4-6). Tested and verified to wor
180
266
 
181
267
  This is not a compliance seal. It means: we used these models against this package and they produced correct, usable results. The per-move slice is small enough for mid-tier models. The logic and guard outputs are plain JSON — no special tokenization or prompt format required.
182
268
 
269
+ ## Why release this
270
+
271
+ We release this because it is useful beyond our own stack.
272
+
273
+ - It captures a practical subset of FPF in a form models can actually use.
274
+ - It gives agent builders a small constraint surface instead of another giant AI framework.
275
+ - It helps turn model failure from mystical drift into inspectable state.
276
+ - It stays intentionally narrow, so it can remain legible instead of becoming another overbuilt "agent platform."
277
+
278
+ ## Credits
279
+
280
+ - **igareosh** — project direction, scope discipline, practical use-case pressure, and product truth
281
+ - **Claude Code (Anthropic)** — implementation support, library iteration, and earlier release preparation
282
+ - **Codex / OpenAI** — product framing polish, packaging cleanup, verification, and release-readiness work
283
+
183
284
  ## License
184
285
 
185
286
  MIT. See [LICENSE](LICENSE).
186
287
 
187
288
  ---
188
289
 
189
- **prichindel.com** — v1.2.1 — 2026-06-26
290
+ **prichindel.com** — v1.4.0 — 2026-07-07
@@ -1,6 +1,6 @@
1
1
  # FPF Thinking Map
2
2
 
3
- This folder contains a Python package that helps an AI model make decisions step by step. It does this by combining two things: a structured framework for reasoning (called FPF) and basic logic operations (AND, OR, NOT, etc.) from computer science.
3
+ This folder contains a Python package that helps an AI model make bounded decisions step by step. It combines a compiled subset of FPF with basic logic operations from computer science so the model can read a small move board instead of digesting a giant framework at runtime.
4
4
 
5
5
  No external dependencies. Pure Python 3.12+. No pip install needed.
6
6
 
@@ -15,6 +15,14 @@ This package gives the model a small, structured board to reason on. The board h
15
15
 
16
16
  The model reads the board, then picks from a fixed set of moves: continue, ask, abstain, escalate, collect evidence, etc.
17
17
 
18
+ The point is not to make the model "more intelligent." The point is to make its behavior smaller, more understandable, and less likely to drift for stupid reasons.
19
+
20
+ ## Why this package exists next to FPF
21
+
22
+ FPF is strong material for humans, but too big and too clumsy to hand to a model raw and expect clean operational behavior. A model tends to absorb the vocabulary, imitate the posture of rigor, and still miss the simple thing it needed to do.
23
+
24
+ So we did not port the whole framework. We extracted what was useful for bounded traversal, made it executable, and left out the parts that would inflate runtime payload or trigger open-ended academic-pattern generation. This package is not "FPF but more." It is "the part of FPF that helps agents behave better, compiled into something they can actually use."
25
+
18
26
  ## Where this comes from
19
27
 
20
28
  Two sources, both real academic material:
@@ -42,7 +50,7 @@ fpf_thinking_map/
42
50
  ├── guards.py 9 deterministic guards the model cannot break
43
51
  ├── logic.py 6 logic operators + EvidenceFresh + decision rules
44
52
  ├── traversal.py Step engine with 10 lawful outcomes (incl. IDLE, BRIDGE)
45
- ├── verify.py Self-test: run it, if 18/18 pass, package works
53
+ ├── verify.py Self-test: run it, if 21/21 pass, package works
46
54
 
47
55
  ├── examples.py 5 deploy decision scenarios (all features in action)
48
56
 
@@ -135,7 +143,7 @@ Output: an Outcome with:
135
143
  - llm_prompt_state: the JSON the model reads to decide its next move
136
144
  ```
137
145
 
138
- When `transition_id` is given, the model gets a `slice()` — a tiny dict with just the move, its gate, its evidence, and whether it can fire. That is the per-move maneuver board.
146
+ When `transition_id` is given, the model gets a `slice()` — a tiny dict with just the move, its gate, its evidence, and whether it can fire. That is the per-move maneuver board. If the caller wants it, the package can now return that lean slice without bolting the full state back on.
139
147
 
140
148
  ## How state works (state.py)
141
149
 
@@ -158,7 +166,7 @@ Three objects:
158
166
  - `slice(transition_id)` — tiny dict for one move
159
167
  - `transition_to(transition_id)` — execute a transition (checks context, evidence, and gates)
160
168
 
161
- **MoveTrace** — compressed history. Only stores last move, not full history: previous_state, last_transition_id, blockers, evidence_delta.
169
+ **MoveTrace** — compressed history. Only stores last move, not full history: previous_state, last_transition_id, bridge_target, blockers, evidence_delta.
162
170
 
163
171
  ## Boundary rules (enforced, not advisory)
164
172
 
@@ -174,7 +182,7 @@ These constraints are checked at execution time, not just in the display layer:
174
182
  ```bash
175
183
  # From the repo root:
176
184
 
177
- # Verify the package works (18 checks)
185
+ # Verify the package works (21 checks)
178
186
  python -m fpf_thinking_map.verify
179
187
 
180
188
  # Run the deploy decision scenario
@@ -253,9 +261,10 @@ outcome = engine.step(state, transition_id="start_to_reviewed")
253
261
 
254
262
  - Not a prompt template or system prompt
255
263
  - Not a retrieval/RAG system for FPF text
256
- - Not a replacement for the full FPF spec (51k lines — we extracted 8 objects)
264
+ - Not a replacement for the full FPF spec
257
265
  - Not a symbolic AI / expert system (the LLM interprets; logic + guards constrain)
258
266
  - Not a framework to build on top of — it is a small self-contained package
267
+ - Not a panacea for agent drift; it reduces and explains drift, it does not eliminate it
259
268
 
260
269
  ## Known design decisions
261
270
 
@@ -4,6 +4,8 @@ This document explains why this package exists adjacent to the original FPF spec
4
4
 
5
5
  This is not a critique of FPF itself. As a human-readable transdisciplinary framework, FPF is strong and well-structured. The problem is not the framework — it is the assumption that a language model can absorb a 51,000-line specification and then apply it the way a human reader would.
6
6
 
7
+ We are not trying to turn FPF into another giant AI stack. We took what was useful for bounded traversal, compiled it, and kept the result intentionally small. That is the product choice here: publish a practical instrument, not an empire.
8
+
7
9
  ## The triple tax: what happens when a model reads raw FPF
8
10
 
9
11
  When you give a language model the FPF specification text and ask it to make a decision, three things happen in sequence. Each one costs tokens, time, and accuracy.
@@ -52,6 +54,8 @@ One pass. No translation. No re-reasoning. The code already did the FPF thinking
52
54
 
53
55
  The model's job shrinks from "understand this 51k-line philosophy of reasoning and then apply it" to "read this small JSON, fill in the claim field, pick the next move."
54
56
 
57
+ That smallness matters. It gives the model a simple operating surface for self-addressing its own run: what can fire, what cannot, what is missing, what is risky, whether it should continue, bridge, idle, or escalate. Not mystical agency. Just enough instrumentation to behave properly.
58
+
55
59
  ## Three problems with feeding raw FPF to a model
56
60
 
57
61
  ### 1. It makes the model re-reason N times
@@ -87,6 +91,8 @@ FPF as a human-readable specification: strong.
87
91
  FPF as raw input to a language model: triple tax.
88
92
  FPF compiled into a thinking map with precomputed state: what this package does.
89
93
 
94
+ This is also why we release it publicly. The value is not private magic. The value is that other people building agents can stop overfeeding models with giant conceptual blobs and start giving them smaller, inspectable decision surfaces instead.
95
+
90
96
  ---
91
97
 
92
- prichindel.com | 2026-06-27 | v1.1.3
98
+ prichindel.com | 2026-07-06 | v1.3.1
@@ -187,7 +187,10 @@ class ActiveState:
187
187
  current_state: str = "initial"
188
188
  trace: MoveTrace = field(default_factory=MoveTrace)
189
189
  step_count: int = 0
190
+ stagnation_threshold: int = 3
190
191
  _evidence_added_at: dict[str, int] = field(default_factory=dict, init=False, repr=False)
192
+ _state_visits: dict[str, int] = field(default_factory=dict, init=False, repr=False)
193
+ _state_visit_evidence: dict[str, frozenset[str]] = field(default_factory=dict, init=False, repr=False)
191
194
 
192
195
  def __post_init__(self) -> None:
193
196
  for eid in self.binding.current_evidence:
@@ -276,6 +279,56 @@ class ActiveState:
276
279
  added_at = self._evidence_added_at.get(evidence_id, 0)
277
280
  return max(0, ttl - (self.step_count - added_at))
278
281
 
282
+ def _visit_key(self) -> str:
283
+ return f"{self.binding.active_context_id}:{self.current_state}"
284
+
285
+ def register_visit(self) -> int:
286
+ """#28: count consecutive step()s at this (context, state) pair.
287
+
288
+ A revisit with the same evidence set is not progress — it's the
289
+ countable, deterministic version of "the agent is stuck," instead
290
+ of asking the model to introspect about its own psychology. New
291
+ evidence resets the counter: different evidence means there's
292
+ something new to try, not a loop.
293
+
294
+ Called once per step() — see traversal.py. Returns the new count.
295
+
296
+ Syntactic, not semantic: this tracks set membership on evidence_ids,
297
+ not whether anything meaningful actually happened. Two consequences
298
+ a caller should know about. (1) A harness that adds a new evidence_id
299
+ on every attempt — even a low-value one — resets the counter every
300
+ time and this will never fire, no matter how unproductive the loop
301
+ actually is. (2) This only sees what reaches step(); flailing that
302
+ happens inside a single turn and never gets reported back as a step()
303
+ call is invisible to it. The guarantee this method gives is
304
+ conditional: if one real attempt maps to one step() call, and
305
+ evidence is only added when it's genuinely new, revisits are bounded
306
+ at stagnation_threshold. It cannot judge whether an attempt was real
307
+ progress — that's a semantic question this library doesn't answer.
308
+ """
309
+ key = self._visit_key()
310
+ snapshot = frozenset(self.available_evidence_ids)
311
+ if self._state_visit_evidence.get(key) != snapshot:
312
+ self._state_visits[key] = 0
313
+ self._state_visit_evidence[key] = snapshot
314
+ self._state_visits[key] = self._state_visits.get(key, 0) + 1
315
+ return self._state_visits[key]
316
+
317
+ @property
318
+ def visit_count(self) -> int:
319
+ """How many consecutive step()s have landed on this (context, state)
320
+ with the current evidence set, without advancing."""
321
+ return self._state_visits.get(self._visit_key(), 0)
322
+
323
+ @property
324
+ def visits_remaining(self) -> int:
325
+ """Countdown to stagnation_threshold — mirrors ttl_remaining's shape."""
326
+ return max(0, self.stagnation_threshold - self.visit_count)
327
+
328
+ @property
329
+ def is_stagnant(self) -> bool:
330
+ return self.visit_count >= self.stagnation_threshold
331
+
279
332
  @property
280
333
  def possible_transitions(self) -> list[TransitionPrimitive]:
281
334
  """#1: scoped to active context + current state."""
@@ -524,6 +577,12 @@ class ActiveState:
524
577
  ],
525
578
  "can_fire": can_fire,
526
579
  "blockers": blockers,
580
+ "stagnation": {
581
+ "visits": self.visit_count,
582
+ "remaining": self.visits_remaining,
583
+ "threshold": self.stagnation_threshold,
584
+ "is_stagnant": self.is_stagnant,
585
+ },
527
586
  "response_contract": self.response_contract(transition_id),
528
587
  }
529
588
 
@@ -577,6 +636,12 @@ class ActiveState:
577
636
  "available_tools": self.binding.available_tools,
578
637
  "audience": self.binding.audience,
579
638
  "step_count": self.step_count,
639
+ "stagnation": {
640
+ "visits": self.visit_count,
641
+ "remaining": self.visits_remaining,
642
+ "threshold": self.stagnation_threshold,
643
+ "is_stagnant": self.is_stagnant,
644
+ },
580
645
  "trace": {
581
646
  "previous_state": self.trace.previous_state,
582
647
  "last_transition": self.trace.last_transition_id,
@@ -161,6 +161,8 @@ class ThinkingMapTraversal:
161
161
  llm_prompt_state=state.to_llm_prompt_state(),
162
162
  )
163
163
 
164
+ state.register_visit()
165
+
164
166
  ctx_id = state.binding.active_context_id or ""
165
167
 
166
168
  if transition_id:
@@ -596,6 +596,64 @@ def check_ttl_decay():
596
596
  assert s3.effective_freshness("fast_ev") == Freshness.STALE
597
597
 
598
598
 
599
+ def check_stagnation_counter():
600
+ """#28: visit counter + countdown for repeated (context, state) with no new evidence."""
601
+ sm = SemanticMap()
602
+ sm.register_context(ContextPrimitive("ctx", "Test"))
603
+ sm.register_transition(TransitionPrimitive(
604
+ "t1", "Go", "ctx", "stuck", "done", required_evidence=["missing_ev"],
605
+ ))
606
+
607
+ b = RuntimeBinding(active_context_id="ctx")
608
+ engine = ThinkingMapTraversal(sm)
609
+ s = engine.build_active_state(b, current_state="stuck")
610
+
611
+ # First visit: 1 visit registered, threshold default 3, 2 remaining, not stagnant
612
+ o1 = engine.step(s)
613
+ st1 = o1.llm_prompt_state["stagnation"]
614
+ assert st1["visits"] == 1, st1
615
+ assert st1["remaining"] == 2, st1
616
+ assert st1["threshold"] == 3
617
+ assert st1["is_stagnant"] is False
618
+
619
+ # Same state, same (empty) evidence, no progress — counter climbs
620
+ o2 = engine.step(s)
621
+ o3 = engine.step(s)
622
+ st3 = o3.llm_prompt_state["stagnation"]
623
+ assert st3["visits"] == 3, st3
624
+ assert st3["remaining"] == 0, st3
625
+ assert st3["is_stagnant"] is True
626
+
627
+ # New evidence arrives — counter resets, this is not a loop anymore
628
+ s.add_evidence("missing_ev")
629
+ o4 = engine.step(s)
630
+ st4 = o4.llm_prompt_state["stagnation"]
631
+ assert st4["visits"] == 1, "new evidence must reset the stagnation counter"
632
+ assert st4["is_stagnant"] is False
633
+
634
+ # slice() carries the same signal for a transition-focused call
635
+ s2 = engine.build_active_state(RuntimeBinding(active_context_id="ctx"), current_state="stuck")
636
+ o5 = engine.step(s2, transition_id="t1")
637
+ assert "stagnation" in o5.llm_prompt_state
638
+ assert o5.llm_prompt_state["stagnation"]["visits"] == 1
639
+
640
+ # Custom threshold is honored
641
+ s3 = ActiveState(sm, RuntimeBinding(active_context_id="ctx"), current_state="stuck", stagnation_threshold=1)
642
+ s3.register_visit()
643
+ assert s3.is_stagnant is True
644
+ assert s3.visits_remaining == 0
645
+
646
+ # attempt_transition (a real move) does not itself register a visit —
647
+ # only step() (the "what can I do" scan) counts as one
648
+ sm2 = SemanticMap()
649
+ sm2.register_context(ContextPrimitive("ctx2", "Test2"))
650
+ sm2.register_transition(TransitionPrimitive("t2", "Go", "ctx2", "a", "b"))
651
+ engine2 = ThinkingMapTraversal(sm2)
652
+ s4 = engine2.build_active_state(RuntimeBinding(active_context_id="ctx2"), current_state="a")
653
+ engine2.attempt_transition(s4, "t2")
654
+ assert s4.visit_count == 0
655
+
656
+
599
657
  def check_idle_outcome():
600
658
  """Verify IDLE outcome for clean terminal states."""
601
659
  sm = SemanticMap()
@@ -1077,6 +1135,7 @@ def main():
1077
1135
  ("end-to-end logic scenario", check_logic_end_to_end),
1078
1136
  ("horizontal properties (25 items)", check_horizontal_properties),
1079
1137
  ("TTL evidence decay", check_ttl_decay),
1138
+ ("stagnation counter (visit countdown)", check_stagnation_counter),
1080
1139
  ("IDLE outcome", check_idle_outcome),
1081
1140
  ("BRIDGE outcome (cross-context)", check_bridge_outcome),
1082
1141
  ("bridge crossing (validated writeback)", check_bridge_crossing),
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fpf-thinking-map
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: Agentic thinking map — structured reasoning constraints for LLMs with TTL evidence decay, deterministic guards, and propositional logic glue. Compiled from FPF (First Principles Framework).
5
5
  Author-email: "prichindel.com" <igareosh@igareosh.com>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/igareosh/prichindel.com-agentic-thinking-map
8
8
  Project-URL: Repository, https://github.com/igareosh/prichindel.com-agentic-thinking-map
9
9
  Project-URL: Documentation, https://github.com/igareosh/prichindel.com-agentic-thinking-map#readme
@@ -12,7 +12,6 @@ Keywords: agentic,thinking-map,llm,reasoning,propositional-logic,evidence-decay,
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Intended Audience :: Science/Research
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Operating System :: OS Independent
17
16
  Classifier: Programming Language :: Python :: 3
18
17
  Classifier: Programming Language :: Python :: 3.12
@@ -27,24 +26,53 @@ Dynamic: license-file
27
26
 
28
27
  # prichindel.com Agentic Thinking Map
29
28
 
30
- **v1.3.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
29
+ **v1.4.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF), compiled into a small traversal map for LLM agents.
31
30
 
32
- A Python package that gives an AI model a small, structured board to reason on one move at a time. Instead of freeform text generation, the model navigates a pre-shaped semantic field with deterministic guards and propositional logic constraints.
31
+ A Python package that gives a model a bounded move board instead of a giant framework to digest at runtime. Instead of rereading a sprawling semantic corpus and improvising from it, the model gets a small JSON slice: what context it is in, what move is open, what evidence is missing, what is risky, and what outcome class applies.
33
32
 
34
33
  **[Visual architecture →](ARCHITECTURE.md)** — module graph, step flowchart, floor map, evidence lifecycle, slice structure, deploy sequence diagram.
35
34
 
35
+ ## Why this exists
36
+
37
+ FPF is valuable, but it is large, clumsy at runtime, and poorly shaped for direct model consumption. A human can read a 51k-line framework, understand its distinctions, and apply them carefully. A model usually does something else: it absorbs the vocabulary, performs the tone of the framework, and still drifts on the actual task.
38
+
39
+ This package exists because the useful part is not making the model sound more scientific. The useful part is compiling the parts of FPF that help operational decisions into something small, explicit, and inspectable. We took what mattered for bounded traversal, left out what would bloat or bias the model, and evolved the result as publishers of a practical library rather than trying to reproduce the whole upstream framework inside another AI mega-system.
40
+
41
+ The goal is simple: give the model enough structure to behave understandably, without building a cage so elaborate that it becomes another source of drift.
42
+
43
+ We do not compete with FPF and we do not need to. FPF stays FPF: broad, ambitious, and useful as a frame. This library is the smaller usable tool for people who want the frame without carrying the whole corpus into every project.
44
+
36
45
  ## What it does
37
46
 
38
- You define a domain as a semantic map (contexts, roles, gates, evidence, transitions). The model gets a per-move slice — just the current transition, its gate, its evidence, and whether it can fire. Deterministic guards enforce hard constraints the model cannot override. Propositional logic rules (NOT, AND, OR, XOR, IMPLIES, IFF) provide decision glue between the semantic primitives and the model's reasoning.
47
+ You define a domain as a semantic map: contexts, roles, gates, evidence, transitions. The model gets a per-move slice, not a theory dump. Deterministic guards handle the hard checks. Propositional logic rules (NOT, AND, OR, XOR, IMPLIES, IFF) provide explicit decision glue between the semantic primitives and the current state.
48
+
49
+ The model's job is not "what does FPF mean?" It is: **given this map and this state, what is the best lawful next move?**
50
+
51
+ In practice this does two useful things:
52
+
53
+ - it reduces unexplained drift by moving state tracking and hard checks out of freeform model reasoning
54
+ - it gives the model simple, understandable outcomes such as `CONTINUE`, `COLLECT_EVIDENCE`, `BRIDGE`, `IDLE`, `ESCALATE`, instead of making it reconstruct its own epistemic condition from scratch
55
+
56
+ This is not a panacea. Models still miss information. They still drift. But with a bounded traversal map, the failure mode becomes much easier to explain: missing evidence, wrong context, blocked move, stale basis, unlicensed bridge. The weirdness gets smaller because the state is smaller.
57
+
58
+ ## Provable practical gains
39
59
 
40
- The model's job is not "what does FPF mean?" — it is: **given this semantic map and this state, what is the best lawful next move?**
60
+ The improvements here are intentionally simple and operational:
61
+
62
+ - less random drift, because the agent reads explicit state instead of reconstructing state from prose
63
+ - fewer unlawful moves, because guards and scoped transitions check them before the model improvises
64
+ - clearer failure, because "why blocked" is surfaced directly
65
+ - smaller runtime payload, because the model gets a slice, not a corpus
66
+ - easier debugging, because outcomes are discrete and inspectable
67
+
68
+ That is the whole point: practical gain, not intellectual theater.
41
69
 
42
70
  ## Quick start
43
71
 
44
72
  ```bash
45
73
  # No dependencies. Python 3.12+.
46
74
 
47
- # Verify the package (21 checks)
75
+ # Verify the package (22 checks)
48
76
  python -m fpf_thinking_map.verify
49
77
 
50
78
  # Run the deploy decision scenario
@@ -60,7 +88,7 @@ fpf_thinking_map/
60
88
  ├── guards.py 9 deterministic guards (context, role, gate, evidence, assignment, speech act, readiness)
61
89
  ├── logic.py 6 logic operators + decision rules + LogicLayer
62
90
  ├── traversal.py Step engine with 10 lawful outcomes (incl. IDLE, BRIDGE)
63
- ├── verify.py Self-verification harness (21/21 checks)
91
+ ├── verify.py Self-verification harness (22/22 checks)
64
92
  ├── examples.py 5 deploy decision scenarios (missing evidence, role conflict, logic glue, truth table)
65
93
  ├── README.md Full documentation (any-model readable)
66
94
  ├── SOURCES.md Source attribution (FPF spec + Mitev lectures)
@@ -76,6 +104,8 @@ fpf_thinking_map/
76
104
 
77
105
  This package is built on [ailev/FPF](https://github.com/ailev/FPF) by Anatoly Levenchuk. It is an independent implementation — our own research and code, MIT-licensed, with further development rights.
78
106
 
107
+ It is not an attempt to "finish" FPF, replace FPF, or repackage the whole corpus for LLM ingestion. It is a selective compilation for agentic traversal.
108
+
79
109
  ### What we reviewed
80
110
 
81
111
  We cross-checked the following FPF commits (June 2026 precision restoration cluster) against our code:
@@ -104,6 +134,33 @@ Two FPF pattern families rejected for activation bias — they amplify existing
104
134
 
105
135
  **Design rule**: the map evaluates and constrains moves. It does not propose them. Generative, branch-friendly, candidate-multiplying patterns are the opposite of what a per-move guard-constrained thinking map should contain.
106
136
 
137
+ ## Scope and non-goals
138
+
139
+ This library is intentionally small in scope.
140
+
141
+ - It is for bounded, stepwise agent traversal in a defined domain.
142
+ - It is for making model behavior more understandable, not more magical.
143
+ - It is for simple +/- operational decisions in context, not for open-ended idea generation.
144
+ - It is for constraining and instrumenting an agent run, not replacing the model.
145
+
146
+ It is not:
147
+
148
+ - a full semantic ingestion layer for all of FPF
149
+ - a universal reasoning engine
150
+ - a replacement for ordinary application logic or policy code
151
+ - a proof that an LLM will behave correctly just because the map exists
152
+ - Memory-Augmented Generation (MAG) — no embeddings, no vector store, no cross-session retrieval, ever, inside this engine. Rejected deliberately and permanently, not by omission. See below.
153
+
154
+ ## What this actually guarantees
155
+
156
+ fpf does not ship better reasoning. It ships **inspectability**: did the agent follow the lawful path, and was evidence honestly reported at the one place that matters — the wire between what the agent actually did and what this library was told.
157
+
158
+ Take the stagnation counter as the concrete case. It bounds repetition — but only if the integration maps one real attempt to one `step()` call, and only adds evidence when something genuinely new was found. Whether that actually catches a real LLM thrashing depends entirely on whether the calling harness wires evidence honestly, not just "a tool returned something." That wiring discipline lives outside this library, in whatever integrates it — an agent loop, an MCP server, application code. fpf can't verify that discipline was followed; it can only guarantee that if it was, repetition is bounded.
159
+
160
+ That boundary is the correct seam, not a gap to apologize for. Judging whether a piece of evidence is actually meaningful is a semantic question, and semantic questions need prompting, an LLM judge, or domain logic to answer — which is exactly where that belongs: outside fpf, at the evidence-wiring layer, not folded into a library whose entire value is staying small and deterministic. If you want semantics and clever prompting, that's the plug-in point.
161
+
162
+ **This is also why Memory-Augmented Generation (MAG) is rejected as fpf's own architecture, deliberately and permanently.** MAG — retrieval, embeddings, cross-session recall — solves a different problem (what did we discuss three sessions ago) than fpf solves (is this move lawful right now), and it needs exactly the dependency profile this library rules out by design. That is a scope boundary, not a verdict on MAG as a technique: a MAG system is a legitimate thing to run *upstream* of fpf. Whatever it retrieves becomes an evidence_id in `RuntimeBinding.current_evidence` like any other — fpf doesn't care how evidence was produced, only whether it's present, fresh, and licensed. Memory belongs at the evidence-wiring layer, alongside every other semantic judgment this library deliberately doesn't make.
163
+
107
164
  ## Sources
108
165
 
109
166
  - **[FPF (First Principles Framework)](https://github.com/ailev/FPF)** by Anatoly Levenchuk — transdisciplinary specification (~51k lines). We extracted 10 semantic primitives and 9 guard rules. This is a compiled distillation, not a port.
@@ -111,6 +168,22 @@ Two FPF pattern families rejected for activation bias — they amplify existing
111
168
 
112
169
  Full attribution in [SOURCES.md](fpf_thinking_map/SOURCES.md).
113
170
 
171
+ ## Why it works
172
+
173
+ The core advantage is not "more theory." It is less runtime burden.
174
+
175
+ Without a compiled map, the model keeps re-addressing its own run: am I allowed to move, did I already satisfy the gate, am I missing evidence, am I in the wrong context, am I done or blocked? That self-management loop is where a lot of bad agent behavior comes from.
176
+
177
+ This package turns that loop into a small stateful instrument panel. The model sees what can fire, what cannot, and why. That is enough of an operating surface for many practical agent tasks. Not a panacea, not a grand theory of intelligence, just enough window and file-handles for the model to open the right thing without smashing the house.
178
+
179
+ The bottleneck was never raw model capability — it's self-management overhead eating the capability that's already there. Most orgs running these models, at any scale, hit that ceiling: infra and process that never actually asked the model for full capacity, so upgrading the model changes nothing. This library doesn't make the model smarter. It shrinks what the model has to hold in its head at any one step, so whatever capability is actually there gets spent on the real problem instead of on self-management overhead.
180
+
181
+ Clean product truth:
182
+
183
+ - FPF is the frame.
184
+ - This library is the usable tool.
185
+ - It keeps the frame where it helps, removes the bloat where it hurts, and gives agents a small operational board that produces simpler, more provable behavior.
186
+
114
187
  ## Why v1.1 exists — reasoning about reasoning is the bug
115
188
 
116
189
  When an LLM navigates a multi-step decision, it faces the same sub-questions at every step: "Is my evidence still valid? Am I going in circles? Is there another path? Why am I blocked?" Without structure, the model re-derives these answers from scratch each time — re-reasoning on its own prior reasoning. Each pass costs tokens, drifts from the original question, and eventually the context fills up with the model arguing with itself about whether it already checked something it checked three steps ago.
@@ -131,6 +204,18 @@ The model's job shrinks from "figure out the entire epistemic state of your own
131
204
  - **Evidence status in prompt** — the LLM prompt state now includes per-evidence freshness and TTL remaining. The model sees "test_results: 3 steps left" instead of just "test_results: exists." Decisions informed by countdown, not by guessing.
132
205
  - **Response contract** — every slice now ends with a `response_contract`: the structured template the model must fill when responding. Pre-filled fields (scope, basis with freshness/TTL, allowed use, not allowed use, modality, canonical terms, audience) come from the computed state. Empty fields (claim, risky aliases) are for the model. This is why all the code exists — so the contract has precomputed, validated values instead of being re-derived by the model from scratch.
133
206
 
207
+ ## v1.3.0 changes
208
+
209
+ - **Bridge crossing is enforced, not just advertised** — `ActiveState.cross_bridge()` / `ThinkingMapTraversal.attempt_bridge()` actually perform a cross-context hop and check `substitution_license` against `risk_level` before mutating state. An unlicensed bridge under `high`/`critical` risk is refused (`ESCALATE`), not silently allowed. Before this, `bridge_options()` was advisory metadata only — the model decided for itself whether a lossy translation was acceptable.
210
+ - **`include_full_state=False`** — `step()` can now ship the scoped `slice()` alone, without the whole board bolted on. Default stays `True`; opt in when the caller already knows its `transition_id` and wants the lean payload.
211
+ - 21/21 self-verification checks (`python -m fpf_thinking_map.verify`), two new: bridge crossing and lean-slice payload shape.
212
+
213
+ ## v1.4.0 changes
214
+
215
+ - **Stagnation counter** — `ActiveState.register_visit()` / `visit_count` / `visits_remaining` / `is_stagnant` count consecutive `step()`s at the same `(context, state)` pair with no new evidence gathered; the counter resets the moment the evidence set changes. `visits_remaining` mirrors `ttl_remaining`'s exact shape — a countdown, not a boolean. Surfaced in both `slice()` and `to_llm_prompt_state()` as a `stagnation` block. Pure signal: no new outcome kind, nothing blocks, no restraint added to the model's options. Closes a real blind spot — `MoveTrace` is deliberately last-move-only (no narrative accumulation), so the engine previously had no way to notice "I've revisited this exact state N times with nothing new to show for it."
216
+ - The guarantee is conditional, and the docs say so plainly: this tracks evidence *set membership*, not evidence *meaning*. It bounds repetition only if the integration maps one real attempt to one `step()` call and only adds evidence that's genuinely new — see [What this actually guarantees](#what-this-actually-guarantees).
217
+ - 22/22 self-verification checks, one new: `check_stagnation_counter`.
218
+
134
219
  ## Design principles
135
220
 
136
221
  - **Only add structure when it changes agentic behavior** — not for source fidelity alone
@@ -140,10 +225,10 @@ The model's job shrinks from "figure out the entire epistemic state of your own
140
225
 
141
226
  ## Using as a dependency
142
227
 
143
- This package is the reasoning engine. Your domain maps run on top of it.
228
+ This package is the compiled map, not a thinking replacement for the model. Denying unlawful moves is only half the job. The bigger half is returning the lawful surface as a small, plain-JSON slice: what can fire, what is missing, what is risky, why a move is blocked, what response shape is expected. That lets the model self-address its own run with simple signals such as `can_fire`, `blockers`, and `outcome.kind` instead of re-reasoning about its state from prose.
144
229
 
145
230
  ```bash
146
- pip install git+https://github.com/igareosh/prichindel.com-agentic-thinking-map.git
231
+ pip install fpf-thinking-map
147
232
  ```
148
233
 
149
234
  ```python
@@ -207,10 +292,25 @@ Built with Claude Code (Anthropic claude-sonnet-4-6). Tested and verified to wor
207
292
 
208
293
  This is not a compliance seal. It means: we used these models against this package and they produced correct, usable results. The per-move slice is small enough for mid-tier models. The logic and guard outputs are plain JSON — no special tokenization or prompt format required.
209
294
 
295
+ ## Why release this
296
+
297
+ We release this because it is useful beyond our own stack.
298
+
299
+ - It captures a practical subset of FPF in a form models can actually use.
300
+ - It gives agent builders a small constraint surface instead of another giant AI framework.
301
+ - It helps turn model failure from mystical drift into inspectable state.
302
+ - It stays intentionally narrow, so it can remain legible instead of becoming another overbuilt "agent platform."
303
+
304
+ ## Credits
305
+
306
+ - **igareosh** — project direction, scope discipline, practical use-case pressure, and product truth
307
+ - **Claude Code (Anthropic)** — implementation support, library iteration, and earlier release preparation
308
+ - **Codex / OpenAI** — product framing polish, packaging cleanup, verification, and release-readiness work
309
+
210
310
  ## License
211
311
 
212
312
  MIT. See [LICENSE](LICENSE).
213
313
 
214
314
  ---
215
315
 
216
- **prichindel.com** — v1.2.1 — 2026-06-26
316
+ **prichindel.com** — v1.4.0 — 2026-07-07
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "fpf-thinking-map"
3
- version = "1.3.0"
3
+ version = "1.4.0"
4
4
  description = "Agentic thinking map — structured reasoning constraints for LLMs with TTL evidence decay, deterministic guards, and propositional logic glue. Compiled from FPF (First Principles Framework)."
5
5
  readme = "README.md"
6
- license = {text = "MIT"}
6
+ license = "MIT"
7
7
  requires-python = ">=3.12"
8
8
  authors = [
9
9
  {name = "prichindel.com", email = "igareosh@igareosh.com"},
@@ -17,7 +17,6 @@ classifiers = [
17
17
  "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: Developers",
19
19
  "Intended Audience :: Science/Research",
20
- "License :: OSI Approved :: MIT License",
21
20
  "Operating System :: OS Independent",
22
21
  "Programming Language :: Python :: 3",
23
22
  "Programming Language :: Python :: 3.12",