fpf-thinking-map 1.2.1__tar.gz → 1.3.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.
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/PKG-INFO +4 -4
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/README.md +3 -3
- fpf_thinking_map-1.3.0/fpf_thinking_map/RELATED_WORK_GOFLOW_FPF_SKILL.md +88 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/__init__.py +1 -1
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/state.py +50 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/traversal.py +48 -7
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/verify.py +92 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/PKG-INFO +4 -4
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/SOURCES.txt +1 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/pyproject.toml +1 -1
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/LICENSE +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/FPF_AUDIT_RESPONSE.md +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/FPF_FLOOR_MAP.md +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/FPF_SOURCE_TO_CODE_RELATION_AUDIT.md +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/README.md +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/REJECTED_C32_CANDIDATE_SYNTHESIS.md +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/REJECTED_NQD_OEE_CULTURAL_EVOLUTION.md +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/SOURCES.md +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/WHY_THIS_EXISTS.md +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/examples.py +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/guards.py +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/logic.py +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/primitives.py +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/py.typed +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/dependency_links.txt +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/top_level.txt +0 -0
- {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fpf-thinking-map
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.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
6
|
License: MIT
|
|
@@ -27,7 +27,7 @@ Dynamic: license-file
|
|
|
27
27
|
|
|
28
28
|
# prichindel.com Agentic Thinking Map
|
|
29
29
|
|
|
30
|
-
**v1.
|
|
30
|
+
**v1.3.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
|
|
31
31
|
|
|
32
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.
|
|
33
33
|
|
|
@@ -44,7 +44,7 @@ The model's job is not "what does FPF mean?" — it is: **given this semantic ma
|
|
|
44
44
|
```bash
|
|
45
45
|
# No dependencies. Python 3.12+.
|
|
46
46
|
|
|
47
|
-
# Verify the package (
|
|
47
|
+
# Verify the package (21 checks)
|
|
48
48
|
python -m fpf_thinking_map.verify
|
|
49
49
|
|
|
50
50
|
# Run the deploy decision scenario
|
|
@@ -60,7 +60,7 @@ fpf_thinking_map/
|
|
|
60
60
|
├── guards.py 9 deterministic guards (context, role, gate, evidence, assignment, speech act, readiness)
|
|
61
61
|
├── logic.py 6 logic operators + decision rules + LogicLayer
|
|
62
62
|
├── traversal.py Step engine with 10 lawful outcomes (incl. IDLE, BRIDGE)
|
|
63
|
-
├── verify.py Self-verification harness (
|
|
63
|
+
├── verify.py Self-verification harness (21/21 checks)
|
|
64
64
|
├── examples.py 5 deploy decision scenarios (missing evidence, role conflict, logic glue, truth table)
|
|
65
65
|
├── README.md Full documentation (any-model readable)
|
|
66
66
|
├── SOURCES.md Source attribution (FPF spec + Mitev lectures)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# prichindel.com Agentic Thinking Map
|
|
2
2
|
|
|
3
|
-
**v1.
|
|
3
|
+
**v1.3.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
|
|
4
4
|
|
|
5
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.
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ The model's job is not "what does FPF mean?" — it is: **given this semantic ma
|
|
|
17
17
|
```bash
|
|
18
18
|
# No dependencies. Python 3.12+.
|
|
19
19
|
|
|
20
|
-
# Verify the package (
|
|
20
|
+
# Verify the package (21 checks)
|
|
21
21
|
python -m fpf_thinking_map.verify
|
|
22
22
|
|
|
23
23
|
# Run the deploy decision scenario
|
|
@@ -33,7 +33,7 @@ fpf_thinking_map/
|
|
|
33
33
|
├── guards.py 9 deterministic guards (context, role, gate, evidence, assignment, speech act, readiness)
|
|
34
34
|
├── logic.py 6 logic operators + decision rules + LogicLayer
|
|
35
35
|
├── traversal.py Step engine with 10 lawful outcomes (incl. IDLE, BRIDGE)
|
|
36
|
-
├── verify.py Self-verification harness (
|
|
36
|
+
├── verify.py Self-verification harness (21/21 checks)
|
|
37
37
|
├── examples.py 5 deploy decision scenarios (missing evidence, role conflict, logic glue, truth table)
|
|
38
38
|
├── README.md Full documentation (any-model readable)
|
|
39
39
|
├── SOURCES.md Source attribution (FPF spec + Mitev lectures)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Related work: goflowspace/goflow FPF skill
|
|
2
|
+
|
|
3
|
+
**Status**: Acknowledged — independent prior art, not incorporated.
|
|
4
|
+
**Date**: 2026-06-30 (discovered 2026-06-29)
|
|
5
|
+
**Decision by**: igareosh (prichindel.com)
|
|
6
|
+
**Related repo**: [goflowspace/goflow](https://github.com/goflowspace/goflow/tree/main/.claude/skills/fpf)
|
|
7
|
+
**FPF source repo**: [ailev/FPF](https://github.com/ailev/FPF)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What we found
|
|
12
|
+
|
|
13
|
+
An independent decomposition of Levenchuk's FPF specification, published as a Claude Code skill at `goflowspace/goflow/.claude/skills/fpf/`. Both projects derive from the same source — FPF-Spec.md, ~51,000 lines — with no mutual awareness, and arrived at opposite designs.
|
|
14
|
+
|
|
15
|
+
Full respect for the effort: 189 pattern files across 11 domains, roughly 3.7 MB of structured reference material, plus maintenance tooling (`fpf_tools.py`, `split_fpf_spec.py`, `audit_fpf_patterns.py`) to search and audit the corpus. It is free, public, and a serious piece of decomposition work. This note exists to record what it is, what we evaluated from it, and why none of it was pulled in here — not to diminish it.
|
|
16
|
+
|
|
17
|
+
## What goflow built
|
|
18
|
+
|
|
19
|
+
A **prompt-time reference library**. The model reads pattern markdown at inference time, evaluates its own position against natural-language criteria written into the patterns, and picks the next branch. Progressive disclosure (master index → domain index → pattern file) keeps token cost down. It is a skill, not an engine — there is no code that evaluates state and returns a verdict; the model is the evaluator.
|
|
20
|
+
|
|
21
|
+
## What this package builds
|
|
22
|
+
|
|
23
|
+
A **runtime constraint engine**: 10 primitives, 9 deterministic guards, 6 propositional logic operators, one traversal loop. The model never evaluates its own reasoning state. It receives a state snapshot, the engine checks deterministic facts (evidence present, gate passed, role conflict, freshness), and returns one of eight fixed outcomes. The model navigates a board with walls; it does not judge the quality of its own thinking.
|
|
24
|
+
|
|
25
|
+
## Coverage comparison
|
|
26
|
+
|
|
27
|
+
| Domain | goflow patterns | This package |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| foundations (A.0–A.19) | 20 | 10 primitives |
|
|
30
|
+
| transformation (A.3–A.15, B.4) | 14 | WorkPrimitive, WorkPlanPrimitive |
|
|
31
|
+
| reasoning (B.5–B.7) | 8 | — |
|
|
32
|
+
| trust-evidence (A.10, B.3, C.2) | 12 | EvidencePrimitive + FGR |
|
|
33
|
+
| aggregation (B.1–B.2) | 15 | — |
|
|
34
|
+
| signature (A.6) | 17 | ContextBridge only |
|
|
35
|
+
| architheories (C.1–C.25) | 46 | — |
|
|
36
|
+
| constitution (E.1–E.20) | 30 | — |
|
|
37
|
+
| unification (F.0–F.18) | 20 | — |
|
|
38
|
+
| ethics (D.5) | 2 | — |
|
|
39
|
+
| sota (G.0–G.13) | 16 | — |
|
|
40
|
+
|
|
41
|
+
goflow covers roughly 5x the specification surface. That is the correct outcome for a reference library and the wrong direction for a constraint engine — see `WHY_THIS_EXISTS.md` on the compiled-vs-raw tax this package is built to avoid.
|
|
42
|
+
|
|
43
|
+
## The core design disagreement
|
|
44
|
+
|
|
45
|
+
goflow's skill is a prompt-time reference the model reads, self-evaluates against, and uses to steer its own reasoning. The model asks itself "am I in Explore or Shape?", answers itself, and picks the next branch. The patterns are decision trees the model walks by interpreting natural-language criteria written into the pattern text.
|
|
46
|
+
|
|
47
|
+
This package is the opposite. The model does not self-evaluate. The engine evaluates state and returns a verdict; the model gets an `Outcome` it can act on. The model navigates, the guards constrain. No self-assessment, anywhere in the loop.
|
|
48
|
+
|
|
49
|
+
Two valid designs for different problems. goflow trusts the model to be an honest judge of its own reasoning state. We don't.
|
|
50
|
+
|
|
51
|
+
We also don't think we need to. This package has never had to put a model in the position of reasoning about its own reasoning — and reasoning-about-reasoning is exactly where models hallucinate most confidently. A model asked "what kind of thinking am I doing right now" will answer fluently and wrongly with equal ease; there is no way to check the answer against anything external. The map's job is to give the model a board to walk and walls it cannot walk through — not to ask it to grade the quality of its own thought, which is the one judgment call it cannot be trusted to make honestly.
|
|
52
|
+
|
|
53
|
+
## Patterns evaluated and rejected
|
|
54
|
+
|
|
55
|
+
### B.5.1 — Explore → Shape → Evidence → Operate (ESEO)
|
|
56
|
+
|
|
57
|
+
Considered adding a `ReasoningPhase` enum to `ActiveState` (EXPLORE / SHAPE / EVIDENCE / OPERATE) with a guard enforcing phase order, mirroring goflow's lifecycle pattern.
|
|
58
|
+
|
|
59
|
+
**Rejected.** Phase assignment requires the model to answer "am I in Explore or Shape?" — a self-evaluative claim with no externally checkable answer. A guard that validates this claim is circular: it constrains a fact only the constrained party can report. It also forks the decision into three branches before any guard runs, which is the wrong direction for a package designed to converge to one move per step (same objection as `REJECTED_NQD_OEE_CULTURAL_EVOLUTION.md`).
|
|
60
|
+
|
|
61
|
+
What ESEO actually prescribes — no operating without evidence, no skipping the shaping step — is already enforced by the existing guards:
|
|
62
|
+
|
|
63
|
+
- `commitment_evidence` — blocks proceeding without required evidence
|
|
64
|
+
- `planning_not_enactment` — blocks marking done without work records
|
|
65
|
+
- `gate_pass` — blocks skipping a structural checkpoint
|
|
66
|
+
- `evidence_freshness` — flags stale evidence
|
|
67
|
+
|
|
68
|
+
The lifecycle is implicit in the guard composition already shipped. Making it explicit adds an enum, a state field, a constraint, and a guard — four new moving parts to formalize something the guards already do, with no new failure mode caught.
|
|
69
|
+
|
|
70
|
+
### B.5 — Canonical Reasoning Cycle (Abduction → Deduction → Induction)
|
|
71
|
+
|
|
72
|
+
Considered a reasoning-mode selector mirroring the three-phase cycle.
|
|
73
|
+
|
|
74
|
+
**Rejected.** Whether the model is abducting, deducing, or inducing is a property of its internal process, not of the observable state this engine evaluates (evidence, gates, transitions, roles). There is no state-layer signal to check it against. Adding the selector buys three-way ambiguity per step with no verification path — exactly the failure mode this package exists to avoid (per `WHY_THIS_EXISTS.md`).
|
|
75
|
+
|
|
76
|
+
### A.3.1 / A.3.2 — Method vs. MethodDescription split
|
|
77
|
+
|
|
78
|
+
goflow keeps the design-time recipe (MethodDescription) and the runtime-bound instance (Method) as separate patterns. This package's `WorkPrimitive.method_id` collapses both.
|
|
79
|
+
|
|
80
|
+
**Not rejected, deferred.** Legitimate split — would add one intermediate state and one guard ("no Work without a bound Method"). Held back because no observed failure mode in current usage needs it. First candidate to revisit if method-binding bugs show up.
|
|
81
|
+
|
|
82
|
+
## Conclusion
|
|
83
|
+
|
|
84
|
+
Same source, opposite bets on what a model can be trusted to do with its own introspection. goflow trusts the model to grade its own reasoning state against reference text. This package doesn't, and routes every decision through external, deterministic checks instead. Nothing from goflow's decomposition was pulled into this package. The guard stack already does, implicitly, what the rejected patterns would have made explicit — without asking the model to self-report.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
prichindel.com | 2026-06-30 | v1.2.1
|
|
@@ -12,7 +12,7 @@ Modules:
|
|
|
12
12
|
logic — 6 propositional operators + freshness-aware decision rules
|
|
13
13
|
traversal — step engine with 10 lawful outcomes
|
|
14
14
|
examples — deploy decision scenarios demonstrating the full system
|
|
15
|
-
verify —
|
|
15
|
+
verify — 21-check self-verification harness
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
# --- Primitives: the semantic field ---
|
|
@@ -170,6 +170,7 @@ class MoveTrace:
|
|
|
170
170
|
"""#24: compressed trace — last move only, no narrative accumulation."""
|
|
171
171
|
previous_state: str | None = None
|
|
172
172
|
last_transition_id: str | None = None
|
|
173
|
+
bridge_target: str | None = None
|
|
173
174
|
blockers: list[str] = field(default_factory=list)
|
|
174
175
|
evidence_delta: list[str] = field(default_factory=list)
|
|
175
176
|
|
|
@@ -339,6 +340,55 @@ class ActiveState:
|
|
|
339
340
|
self.current_state = t.to_state
|
|
340
341
|
return True
|
|
341
342
|
|
|
343
|
+
def cross_bridge(self, target_context_id: str, entry_state: str) -> tuple[bool, str]:
|
|
344
|
+
"""#26: validated writeback for a cross-context bridge crossing.
|
|
345
|
+
|
|
346
|
+
FPF A.6.9 gives bridges an explicit fidelity contract — substitution_license
|
|
347
|
+
and translation_loss — but until now that contract was surfaced only as
|
|
348
|
+
advisory metadata in bridge_options(); nothing enforced it before the LLM
|
|
349
|
+
wandered into the target context. This closes that gap: the engine, not
|
|
350
|
+
the model, decides whether a crossing is licensed.
|
|
351
|
+
|
|
352
|
+
An unlicensed bridge (substitution_license=False) is fine for low/normal
|
|
353
|
+
risk moves — the translation_loss is real but tolerable. At high/critical
|
|
354
|
+
risk_level it is refused: a lossy, unlicensed substitution must not carry
|
|
355
|
+
weight it wasn't licensed for.
|
|
356
|
+
|
|
357
|
+
Returns (ok, reason). On ok=True, current_state and active_context_id are
|
|
358
|
+
updated and the crossing is recorded in trace as bridge_target.
|
|
359
|
+
"""
|
|
360
|
+
ctx = self.active_context
|
|
361
|
+
if not ctx:
|
|
362
|
+
return False, "no active context to bridge from"
|
|
363
|
+
|
|
364
|
+
bridge = next(
|
|
365
|
+
(b for b in ctx.bridges_to if b.target_context_id == target_context_id),
|
|
366
|
+
None,
|
|
367
|
+
)
|
|
368
|
+
if bridge is None:
|
|
369
|
+
return False, f"no bridge from '{ctx.context_id}' to '{target_context_id}'"
|
|
370
|
+
|
|
371
|
+
if not self.semantic_map.transitions_for(target_context_id, entry_state):
|
|
372
|
+
return False, (
|
|
373
|
+
f"'{entry_state}' is not a valid entry state in '{target_context_id}' "
|
|
374
|
+
f"(no transitions start there)"
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
if not bridge.substitution_license and self.binding.risk_level in ("high", "critical"):
|
|
378
|
+
return False, (
|
|
379
|
+
f"bridge to '{target_context_id}' is unlicensed for substitution "
|
|
380
|
+
f"(translation_loss: '{bridge.translation_loss}') — risk_level "
|
|
381
|
+
f"'{self.binding.risk_level}' requires a licensed bridge"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
self.trace = MoveTrace(
|
|
385
|
+
previous_state=self.current_state,
|
|
386
|
+
bridge_target=target_context_id,
|
|
387
|
+
)
|
|
388
|
+
self.binding.active_context_id = target_context_id
|
|
389
|
+
self.current_state = entry_state
|
|
390
|
+
return True, f"crossed to '{target_context_id}' at '{entry_state}'"
|
|
391
|
+
|
|
342
392
|
def add_evidence(self, evidence_id: str) -> None:
|
|
343
393
|
if evidence_id not in self.binding.current_evidence:
|
|
344
394
|
self.binding.current_evidence.append(evidence_id)
|
|
@@ -111,10 +111,19 @@ class ThinkingMapTraversal:
|
|
|
111
111
|
logic_ctx: dict,
|
|
112
112
|
transition_id: str | None = None,
|
|
113
113
|
guard_blockers: list[str] | None = None,
|
|
114
|
+
include_full_state: bool = True,
|
|
114
115
|
) -> dict:
|
|
116
|
+
"""#27: include_full_state=False ships the A-RAG "chunk read" alone.
|
|
117
|
+
|
|
118
|
+
A caller that already knows its transition_id doesn't need the whole
|
|
119
|
+
board bolted on — that's the win slice() was built for in the first
|
|
120
|
+
place. Default stays True so every existing caller sees identical
|
|
121
|
+
output; opt out only when you want the lean payload.
|
|
122
|
+
"""
|
|
115
123
|
if transition_id:
|
|
116
124
|
prompt = state.slice(transition_id, guard_blockers=guard_blockers)
|
|
117
|
-
|
|
125
|
+
if include_full_state:
|
|
126
|
+
prompt["full_state"] = state.to_llm_prompt_state()
|
|
118
127
|
else:
|
|
119
128
|
prompt = state.to_llm_prompt_state()
|
|
120
129
|
if logic_ctx:
|
|
@@ -131,6 +140,7 @@ class ThinkingMapTraversal:
|
|
|
131
140
|
state: ActiveState,
|
|
132
141
|
transition_id: str | None = None,
|
|
133
142
|
logic_tags: set[str] | None = None,
|
|
143
|
+
include_full_state: bool = True,
|
|
134
144
|
) -> Outcome:
|
|
135
145
|
"""#6: one traversal step, optionally focused on a specific transition.
|
|
136
146
|
|
|
@@ -138,6 +148,9 @@ class ThinkingMapTraversal:
|
|
|
138
148
|
Without: scans possible transitions from current state.
|
|
139
149
|
|
|
140
150
|
Increments step_count each call — drives TTL evidence decay.
|
|
151
|
+
|
|
152
|
+
include_full_state=False (only meaningful together with transition_id):
|
|
153
|
+
ships the scoped slice without the full board — see #27 in _build_prompt.
|
|
141
154
|
"""
|
|
142
155
|
state.step_count += 1
|
|
143
156
|
|
|
@@ -167,7 +180,7 @@ class ThinkingMapTraversal:
|
|
|
167
180
|
kind=OutcomeKind.ABSTAIN,
|
|
168
181
|
reason=f"Logic contradiction: {consistency.get('contradictions', [])}",
|
|
169
182
|
warnings=consistency.get("contradictions", []),
|
|
170
|
-
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id),
|
|
183
|
+
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id, include_full_state=include_full_state),
|
|
171
184
|
)
|
|
172
185
|
|
|
173
186
|
allowed, guard_results = self.guard_engine.is_action_allowed(state, transition_id)
|
|
@@ -186,7 +199,8 @@ class ThinkingMapTraversal:
|
|
|
186
199
|
missing_evidence=missing,
|
|
187
200
|
warnings=warnings,
|
|
188
201
|
llm_prompt_state=self._build_prompt(
|
|
189
|
-
state, logic_ctx, transition_id,
|
|
202
|
+
state, logic_ctx, transition_id,
|
|
203
|
+
guard_blockers=denials, include_full_state=include_full_state,
|
|
190
204
|
),
|
|
191
205
|
)
|
|
192
206
|
return Outcome(
|
|
@@ -208,7 +222,7 @@ class ThinkingMapTraversal:
|
|
|
208
222
|
reason=f"Evidence gaps: {missing}",
|
|
209
223
|
missing_evidence=missing,
|
|
210
224
|
warnings=warnings,
|
|
211
|
-
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id),
|
|
225
|
+
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id, include_full_state=include_full_state),
|
|
212
226
|
)
|
|
213
227
|
|
|
214
228
|
transitions = state.possible_transitions
|
|
@@ -218,7 +232,7 @@ class ThinkingMapTraversal:
|
|
|
218
232
|
kind=OutcomeKind.CONTINUE,
|
|
219
233
|
reason="No transitions but actions available",
|
|
220
234
|
warnings=warnings,
|
|
221
|
-
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id),
|
|
235
|
+
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id, include_full_state=include_full_state),
|
|
222
236
|
)
|
|
223
237
|
|
|
224
238
|
bridge_opts = self.semantic_map.bridge_options(ctx_id)
|
|
@@ -240,14 +254,14 @@ class ThinkingMapTraversal:
|
|
|
240
254
|
kind=OutcomeKind.IDLE,
|
|
241
255
|
reason="At rest — no transitions, no actions, no bridges",
|
|
242
256
|
warnings=warnings,
|
|
243
|
-
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id),
|
|
257
|
+
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id, include_full_state=include_full_state),
|
|
244
258
|
)
|
|
245
259
|
|
|
246
260
|
return Outcome(
|
|
247
261
|
kind=OutcomeKind.CONTINUE,
|
|
248
262
|
reason="Guards pass, evidence sufficient, transitions available",
|
|
249
263
|
warnings=warnings,
|
|
250
|
-
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id),
|
|
264
|
+
llm_prompt_state=self._build_prompt(state, logic_ctx, transition_id, include_full_state=include_full_state),
|
|
251
265
|
)
|
|
252
266
|
|
|
253
267
|
def attempt_transition(
|
|
@@ -323,6 +337,33 @@ class ThinkingMapTraversal:
|
|
|
323
337
|
reason="Transition failed",
|
|
324
338
|
)
|
|
325
339
|
|
|
340
|
+
def attempt_bridge(
|
|
341
|
+
self,
|
|
342
|
+
state: ActiveState,
|
|
343
|
+
target_context_id: str,
|
|
344
|
+
entry_state: str,
|
|
345
|
+
) -> Outcome:
|
|
346
|
+
"""#26: enact a bridge crossing the BRIDGE outcome only advertised.
|
|
347
|
+
|
|
348
|
+
step() surfaces bridge_options() as advisory metadata; this is the
|
|
349
|
+
enactment half — it enforces the bridge's fidelity contract
|
|
350
|
+
(substitution_license vs. risk_level) before mutating state, then
|
|
351
|
+
performs the crossing. An unlicensed bridge under high/critical risk
|
|
352
|
+
is refused as ESCALATE, not silently allowed.
|
|
353
|
+
"""
|
|
354
|
+
ok, reason = state.cross_bridge(target_context_id, entry_state)
|
|
355
|
+
if not ok:
|
|
356
|
+
kind = OutcomeKind.ESCALATE if "unlicensed" in reason else OutcomeKind.ABSTAIN
|
|
357
|
+
return Outcome(kind=kind, reason=reason)
|
|
358
|
+
|
|
359
|
+
logic_ctx = self._eval_logic(state)
|
|
360
|
+
return Outcome(
|
|
361
|
+
kind=OutcomeKind.CONTINUE,
|
|
362
|
+
reason=reason,
|
|
363
|
+
next_state=state.current_state,
|
|
364
|
+
llm_prompt_state=self._build_prompt(state, logic_ctx),
|
|
365
|
+
)
|
|
366
|
+
|
|
326
367
|
def demo_walk(
|
|
327
368
|
self,
|
|
328
369
|
binding: RuntimeBinding,
|
|
@@ -692,6 +692,96 @@ def check_bridge_outcome():
|
|
|
692
692
|
assert o2.kind == OutcomeKind.IDLE
|
|
693
693
|
|
|
694
694
|
|
|
695
|
+
def check_bridge_crossing():
|
|
696
|
+
"""#26: validated writeback for bridge crossing — enact, not just advertise."""
|
|
697
|
+
def _make_map(license_: bool):
|
|
698
|
+
sm = SemanticMap()
|
|
699
|
+
sm.register_context(ContextPrimitive(
|
|
700
|
+
context_id="ctx_a", label="Context A",
|
|
701
|
+
bridges_to=[ContextBridge(
|
|
702
|
+
target_context_id="ctx_b",
|
|
703
|
+
mapping={"deploy": "release"},
|
|
704
|
+
translation_loss="ops uses 'release' where dev uses 'deploy'",
|
|
705
|
+
substitution_license=license_,
|
|
706
|
+
)],
|
|
707
|
+
))
|
|
708
|
+
sm.register_context(ContextPrimitive(context_id="ctx_b", label="Context B"))
|
|
709
|
+
sm.register_transition(TransitionPrimitive(
|
|
710
|
+
"t_b1", "B entry", "ctx_b", "ready", "active",
|
|
711
|
+
))
|
|
712
|
+
return sm
|
|
713
|
+
|
|
714
|
+
# Unlicensed bridge, normal risk — lossy but tolerable, crossing succeeds
|
|
715
|
+
sm = _make_map(license_=False)
|
|
716
|
+
engine = ThinkingMapTraversal(sm)
|
|
717
|
+
b = RuntimeBinding(active_context_id="ctx_a", risk_level="normal")
|
|
718
|
+
s = engine.build_active_state(b, current_state="done")
|
|
719
|
+
o = engine.attempt_bridge(s, "ctx_b", "ready")
|
|
720
|
+
assert o.kind == OutcomeKind.CONTINUE, f"Expected CONTINUE, got {o.kind}: {o.reason}"
|
|
721
|
+
assert s.binding.active_context_id == "ctx_b"
|
|
722
|
+
assert s.current_state == "ready"
|
|
723
|
+
assert s.trace.bridge_target == "ctx_b"
|
|
724
|
+
|
|
725
|
+
# Unlicensed bridge, high risk — refused, state untouched
|
|
726
|
+
sm2 = _make_map(license_=False)
|
|
727
|
+
engine2 = ThinkingMapTraversal(sm2)
|
|
728
|
+
b2 = RuntimeBinding(active_context_id="ctx_a", risk_level="high")
|
|
729
|
+
s2 = engine2.build_active_state(b2, current_state="done")
|
|
730
|
+
o2 = engine2.attempt_bridge(s2, "ctx_b", "ready")
|
|
731
|
+
assert o2.kind == OutcomeKind.ESCALATE, f"Expected ESCALATE, got {o2.kind}"
|
|
732
|
+
assert "unlicensed" in o2.reason
|
|
733
|
+
assert s2.binding.active_context_id == "ctx_a", "unlicensed+high-risk must not mutate state"
|
|
734
|
+
assert s2.current_state == "done"
|
|
735
|
+
|
|
736
|
+
# Licensed bridge, high risk — allowed despite risk level
|
|
737
|
+
sm3 = _make_map(license_=True)
|
|
738
|
+
engine3 = ThinkingMapTraversal(sm3)
|
|
739
|
+
b3 = RuntimeBinding(active_context_id="ctx_a", risk_level="high")
|
|
740
|
+
s3 = engine3.build_active_state(b3, current_state="done")
|
|
741
|
+
o3 = engine3.attempt_bridge(s3, "ctx_b", "ready")
|
|
742
|
+
assert o3.kind == OutcomeKind.CONTINUE
|
|
743
|
+
assert s3.binding.active_context_id == "ctx_b"
|
|
744
|
+
|
|
745
|
+
# No such bridge target
|
|
746
|
+
sm4 = _make_map(license_=True)
|
|
747
|
+
engine4 = ThinkingMapTraversal(sm4)
|
|
748
|
+
b4 = RuntimeBinding(active_context_id="ctx_a")
|
|
749
|
+
s4 = engine4.build_active_state(b4, current_state="done")
|
|
750
|
+
o4 = engine4.attempt_bridge(s4, "ctx_z", "ready")
|
|
751
|
+
assert o4.kind == OutcomeKind.ABSTAIN
|
|
752
|
+
assert "no bridge" in o4.reason
|
|
753
|
+
|
|
754
|
+
# Invalid entry state — not a real transition start in target context
|
|
755
|
+
sm5 = _make_map(license_=True)
|
|
756
|
+
engine5 = ThinkingMapTraversal(sm5)
|
|
757
|
+
b5 = RuntimeBinding(active_context_id="ctx_a")
|
|
758
|
+
s5 = engine5.build_active_state(b5, current_state="done")
|
|
759
|
+
o5 = engine5.attempt_bridge(s5, "ctx_b", "nowhere")
|
|
760
|
+
assert o5.kind == OutcomeKind.ABSTAIN
|
|
761
|
+
assert "not a valid entry state" in o5.reason
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def check_lean_slice():
|
|
765
|
+
"""#27: include_full_state=False ships the slice without the full board."""
|
|
766
|
+
sm = SemanticMap()
|
|
767
|
+
sm.register_context(ContextPrimitive(context_id="ctx", label="Ctx"))
|
|
768
|
+
sm.register_transition(TransitionPrimitive(
|
|
769
|
+
"t1", "Do it", "ctx", "start", "done",
|
|
770
|
+
))
|
|
771
|
+
engine = ThinkingMapTraversal(sm)
|
|
772
|
+
b = RuntimeBinding(active_context_id="ctx")
|
|
773
|
+
s = engine.build_active_state(b, current_state="start")
|
|
774
|
+
|
|
775
|
+
o_full = engine.step(s, transition_id="t1")
|
|
776
|
+
assert "full_state" in o_full.llm_prompt_state
|
|
777
|
+
assert "move" in o_full.llm_prompt_state
|
|
778
|
+
|
|
779
|
+
s2 = engine.build_active_state(b, current_state="start")
|
|
780
|
+
o_lean = engine.step(s2, transition_id="t1", include_full_state=False)
|
|
781
|
+
assert "full_state" not in o_lean.llm_prompt_state
|
|
782
|
+
assert "move" in o_lean.llm_prompt_state, "lean payload must keep the slice itself"
|
|
783
|
+
|
|
784
|
+
|
|
695
785
|
def check_semantic_floors():
|
|
696
786
|
"""Verify semantic floor TTL computation from FGR trust factors."""
|
|
697
787
|
# Floor constants exist and are correct
|
|
@@ -989,6 +1079,8 @@ def main():
|
|
|
989
1079
|
("TTL evidence decay", check_ttl_decay),
|
|
990
1080
|
("IDLE outcome", check_idle_outcome),
|
|
991
1081
|
("BRIDGE outcome (cross-context)", check_bridge_outcome),
|
|
1082
|
+
("bridge crossing (validated writeback)", check_bridge_crossing),
|
|
1083
|
+
("lean slice (no full_state bolt-on)", check_lean_slice),
|
|
992
1084
|
("slice blockers (HITL)", check_slice_blockers),
|
|
993
1085
|
("semantic floors (FPF vertical)", check_semantic_floors),
|
|
994
1086
|
("EvidenceFresh prop + integration", check_evidence_fresh_prop),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fpf-thinking-map
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.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
6
|
License: MIT
|
|
@@ -27,7 +27,7 @@ Dynamic: license-file
|
|
|
27
27
|
|
|
28
28
|
# prichindel.com Agentic Thinking Map
|
|
29
29
|
|
|
30
|
-
**v1.
|
|
30
|
+
**v1.3.0** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
|
|
31
31
|
|
|
32
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.
|
|
33
33
|
|
|
@@ -44,7 +44,7 @@ The model's job is not "what does FPF mean?" — it is: **given this semantic ma
|
|
|
44
44
|
```bash
|
|
45
45
|
# No dependencies. Python 3.12+.
|
|
46
46
|
|
|
47
|
-
# Verify the package (
|
|
47
|
+
# Verify the package (21 checks)
|
|
48
48
|
python -m fpf_thinking_map.verify
|
|
49
49
|
|
|
50
50
|
# Run the deploy decision scenario
|
|
@@ -60,7 +60,7 @@ fpf_thinking_map/
|
|
|
60
60
|
├── guards.py 9 deterministic guards (context, role, gate, evidence, assignment, speech act, readiness)
|
|
61
61
|
├── logic.py 6 logic operators + decision rules + LogicLayer
|
|
62
62
|
├── traversal.py Step engine with 10 lawful outcomes (incl. IDLE, BRIDGE)
|
|
63
|
-
├── verify.py Self-verification harness (
|
|
63
|
+
├── verify.py Self-verification harness (21/21 checks)
|
|
64
64
|
├── examples.py 5 deploy decision scenarios (missing evidence, role conflict, logic glue, truth table)
|
|
65
65
|
├── README.md Full documentation (any-model readable)
|
|
66
66
|
├── SOURCES.md Source attribution (FPF spec + Mitev lectures)
|
|
@@ -7,6 +7,7 @@ fpf_thinking_map/FPF_SOURCE_TO_CODE_RELATION_AUDIT.md
|
|
|
7
7
|
fpf_thinking_map/README.md
|
|
8
8
|
fpf_thinking_map/REJECTED_C32_CANDIDATE_SYNTHESIS.md
|
|
9
9
|
fpf_thinking_map/REJECTED_NQD_OEE_CULTURAL_EVOLUTION.md
|
|
10
|
+
fpf_thinking_map/RELATED_WORK_GOFLOW_FPF_SKILL.md
|
|
10
11
|
fpf_thinking_map/SOURCES.md
|
|
11
12
|
fpf_thinking_map/WHY_THIS_EXISTS.md
|
|
12
13
|
fpf_thinking_map/__init__.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fpf-thinking-map"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.3.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
6
|
license = {text = "MIT"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|