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.
Files changed (27) hide show
  1. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/PKG-INFO +4 -4
  2. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/README.md +3 -3
  3. fpf_thinking_map-1.3.0/fpf_thinking_map/RELATED_WORK_GOFLOW_FPF_SKILL.md +88 -0
  4. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/__init__.py +1 -1
  5. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/state.py +50 -0
  6. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/traversal.py +48 -7
  7. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/verify.py +92 -0
  8. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/PKG-INFO +4 -4
  9. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/SOURCES.txt +1 -0
  10. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/pyproject.toml +1 -1
  11. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/LICENSE +0 -0
  12. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/FPF_AUDIT_RESPONSE.md +0 -0
  13. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/FPF_FLOOR_MAP.md +0 -0
  14. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/FPF_SOURCE_TO_CODE_RELATION_AUDIT.md +0 -0
  15. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/README.md +0 -0
  16. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/REJECTED_C32_CANDIDATE_SYNTHESIS.md +0 -0
  17. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/REJECTED_NQD_OEE_CULTURAL_EVOLUTION.md +0 -0
  18. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/SOURCES.md +0 -0
  19. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/WHY_THIS_EXISTS.md +0 -0
  20. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/examples.py +0 -0
  21. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/guards.py +0 -0
  22. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/logic.py +0 -0
  23. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/primitives.py +0 -0
  24. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map/py.typed +0 -0
  25. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/dependency_links.txt +0 -0
  26. {fpf_thinking_map-1.2.1 → fpf_thinking_map-1.3.0}/fpf_thinking_map.egg-info/top_level.txt +0 -0
  27. {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.2.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.2.1** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
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 (18 checks)
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 (19/19 checks)
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.2.1** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
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 (18 checks)
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 (19/19 checks)
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 — 18-check self-verification harness
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
- prompt["full_state"] = state.to_llm_prompt_state()
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, guard_blockers=denials,
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.2.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.2.1** — [FPF (First Principles Framework)](https://github.com/ailev/FPF) compiled into a semi-formal thinking map for agentic AI guidance.
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 (18 checks)
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 (19/19 checks)
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.2.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"}