fable-engine 1.3.1__py3-none-any.whl
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.
- fable_compressor.py +356 -0
- fable_engine/__init__.py +1 -0
- fable_engine/actions/__init__.py +291 -0
- fable_engine/actions/cas.py +182 -0
- fable_engine/actions/deliberation.py +523 -0
- fable_engine/actions/fleet.py +807 -0
- fable_engine/actions/lifecycle.py +298 -0
- fable_engine/actions/scrapers.py +116 -0
- fable_engine/actions/system3.py +815 -0
- fable_engine/browser.py +824 -0
- fable_engine/cas.py +974 -0
- fable_engine/fable_session.json +510 -0
- fable_engine/guards.py +283 -0
- fable_engine/schema.py +714 -0
- fable_engine/scrapers/__init__.py +32 -0
- fable_engine/scrapers/arxiv.py +115 -0
- fable_engine/scrapers/base.py +386 -0
- fable_engine/scrapers/github.py +129 -0
- fable_engine/scrapers/reddit.py +154 -0
- fable_engine/scrapers/web.py +120 -0
- fable_engine/scrapers/x.py +125 -0
- fable_engine/scrapers/youtube.py +132 -0
- fable_engine/server.py +414 -0
- fable_engine/session.py +1819 -0
- fable_engine/test_server.py +1362 -0
- fable_engine/updater.py +541 -0
- fable_engine-1.3.1.dist-info/LICENSE +22 -0
- fable_engine-1.3.1.dist-info/METADATA +173 -0
- fable_engine-1.3.1.dist-info/RECORD +104 -0
- fable_engine-1.3.1.dist-info/WHEEL +5 -0
- fable_engine-1.3.1.dist-info/entry_points.txt +5 -0
- fable_engine-1.3.1.dist-info/top_level.txt +6 -0
- fable_mode/__init__.py +3 -0
- fable_mode/__main__.py +4 -0
- fable_mode/adapters.py +1014 -0
- fable_mode/installer.py +553 -0
- fable_mode/launcher.py +437 -0
- fable_mode/manifest.py +142 -0
- fable_mode/resources.json +114 -0
- fable_mode/safety.py +103 -0
- fable_mode_entry.py +10 -0
- fable_v2/__init__.py +146 -0
- fable_v2/adapters.py +151 -0
- fable_v2/coder_fleet/__init__.py +100 -0
- fable_v2/coder_fleet/ast_tools.py +158 -0
- fable_v2/coder_fleet/compute.py +199 -0
- fable_v2/coder_fleet/design_engine.py +1316 -0
- fable_v2/coder_fleet/diagnostics.py +293 -0
- fable_v2/coder_fleet/fleet_dispatcher.py +214 -0
- fable_v2/coder_fleet/mock_auditor.py +306 -0
- fable_v2/coder_fleet/mutation.py +216 -0
- fable_v2/coder_fleet/property_oracle.py +260 -0
- fable_v2/coder_fleet/receipt_attestor.py +122 -0
- fable_v2/coder_fleet/red_team_swarm.py +908 -0
- fable_v2/coder_fleet/test_harness.py +198 -0
- fable_v2/coder_fleet/vector_engine.py +1287 -0
- fable_v2/coder_fleet/visual.py +357 -0
- fable_v2/coder_fleet/workspace.py +153 -0
- fable_v2/cortical/__init__.py +20 -0
- fable_v2/cortical/plasticity_engine.py +992 -0
- fable_v2/execution_broker.py +811 -0
- fable_v2/proof_engine.py +1141 -0
- fable_v2/protocol.py +485 -0
- fable_v2/runtime.py +1010 -0
- fable_v2/system3/__init__.py +204 -0
- fable_v2/system3/causal.py +558 -0
- fable_v2/system3/dialectical.py +577 -0
- fable_v2/system3/evolution.py +503 -0
- fable_v2/system3/executive.py +338 -0
- fable_v2/system3/free_energy.py +479 -0
- fable_v2/system3/hyperbolic.py +555 -0
- fable_v2/system3/induction.py +336 -0
- fable_v2/system3/kripke.py +548 -0
- fable_v2/system3/oracle.py +745 -0
- fable_v2/verifiers.py +72 -0
- tests/__init__.py +1 -0
- tests/test_anti_loop_circuit_breaker.py +64 -0
- tests/test_auto_updater.py +407 -0
- tests/test_coder_fleet.py +535 -0
- tests/test_delegation_compiler.py +54 -0
- tests/test_descriptor_boundaries.py +126 -0
- tests/test_design_engine.py +603 -0
- tests/test_epistemic_evidence_validator.py +66 -0
- tests/test_execution_broker.py +233 -0
- tests/test_fable_v2.py +406 -0
- tests/test_fleet_transitions.py +116 -0
- tests/test_fsm_redteam_evolution.py +406 -0
- tests/test_goal_rubric_and_pipeline.py +367 -0
- tests/test_hebbian_plasticity.py +585 -0
- tests/test_packaging_runtime.py +194 -0
- tests/test_proof_engine.py +259 -0
- tests/test_red_team_swarm.py +645 -0
- tests/test_redteam_remediation.py +169 -0
- tests/test_registration_transaction.py +375 -0
- tests/test_requested_regressions.py +467 -0
- tests/test_scrapers.py +370 -0
- tests/test_server_actions.py +93 -0
- tests/test_server_frontier_actions.py +269 -0
- tests/test_server_protocol.py +88 -0
- tests/test_stealth_browser.py +970 -0
- tests/test_system3.py +381 -0
- tests/test_system3_deep_integration.py +385 -0
- tests/test_system3_frontier.py +436 -0
- tests/test_vector_engine.py +608 -0
|
@@ -0,0 +1,1362 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Comprehensive Unit and Integration Test Suite for Fable-Engine MCP Server.
|
|
4
|
+
Tests:
|
|
5
|
+
- Core session data structures & invariants
|
|
6
|
+
- Anti-rush execution lockout logic & cognitive gating validation
|
|
7
|
+
- Immutable authority time-lock enforcement & host-only emergency overrides
|
|
8
|
+
- Continuous rethink-refine cycle tracking & telemetry
|
|
9
|
+
- Epistemic ledger tracking (PROVEN, HYPOTHESIS, UNKNOWN)
|
|
10
|
+
- Formal invariant modeling
|
|
11
|
+
- Immutable authority deadlines and agent-only pacing telemetry
|
|
12
|
+
- Persistence, atomic checkpointing, and restoration
|
|
13
|
+
- Dispatch handler operations and edge-case handling
|
|
14
|
+
- Full JSON-RPC 2.0 stdio MCP server protocol over subprocess
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
import os
|
|
21
|
+
import json
|
|
22
|
+
import time
|
|
23
|
+
import subprocess
|
|
24
|
+
import threading
|
|
25
|
+
import unittest
|
|
26
|
+
import tempfile
|
|
27
|
+
import shutil
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
class FakeClock:
|
|
31
|
+
"""Deterministic wall and monotonic clocks for lock tests."""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
self.wall = time.time()
|
|
35
|
+
self.mono = time.monotonic()
|
|
36
|
+
|
|
37
|
+
def time(self):
|
|
38
|
+
return self.wall
|
|
39
|
+
|
|
40
|
+
def monotonic(self):
|
|
41
|
+
return self.mono
|
|
42
|
+
|
|
43
|
+
def advance(self, seconds):
|
|
44
|
+
self.wall += seconds
|
|
45
|
+
self.mono += seconds
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Add current directory to path
|
|
49
|
+
CURRENT_DIR = Path(__file__).resolve().parent
|
|
50
|
+
if str(CURRENT_DIR) not in sys.path:
|
|
51
|
+
sys.path.insert(0, str(CURRENT_DIR))
|
|
52
|
+
|
|
53
|
+
from server import (
|
|
54
|
+
FableSession,
|
|
55
|
+
ACTIVE_SESSIONS,
|
|
56
|
+
SESSIONS_DIR,
|
|
57
|
+
PHASES,
|
|
58
|
+
PHASE_INDEX_MAP,
|
|
59
|
+
handle_fable_session,
|
|
60
|
+
TOOL_SCHEMA,
|
|
61
|
+
FableCASError,
|
|
62
|
+
IntegrityError,
|
|
63
|
+
CASNotFoundError,
|
|
64
|
+
ThreadSafeLRUCache,
|
|
65
|
+
FableCASStore,
|
|
66
|
+
CompositeFrame,
|
|
67
|
+
AdaptiveChunkAccumulator,
|
|
68
|
+
FableGrammar333,
|
|
69
|
+
CASSliceViewer,
|
|
70
|
+
FableCompress,
|
|
71
|
+
CAS_ENGINE
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TestFableSessionCore(unittest.TestCase):
|
|
76
|
+
"""Tests core FableSession lifecycle, telemetry, refinement cycles, and anti-rush gates."""
|
|
77
|
+
|
|
78
|
+
def setUp(self):
|
|
79
|
+
self.clock = FakeClock()
|
|
80
|
+
self.session_name = f"test_unit_{int(time.time() * 1000)}"
|
|
81
|
+
self.session = FableSession(
|
|
82
|
+
session_name=self.session_name,
|
|
83
|
+
objective="Architect a lock-free multi-producer ring buffer",
|
|
84
|
+
time_budget_minutes=45.0,
|
|
85
|
+
wall_clock=self.clock.time,
|
|
86
|
+
monotonic_clock=self.clock.monotonic
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def tearDown(self):
|
|
90
|
+
# Cleanup test session files if any
|
|
91
|
+
target_file = SESSIONS_DIR / f"{self.session_name}.json"
|
|
92
|
+
if target_file.exists():
|
|
93
|
+
try:
|
|
94
|
+
target_file.unlink()
|
|
95
|
+
except Exception:
|
|
96
|
+
pass
|
|
97
|
+
if self.session_name in ACTIVE_SESSIONS:
|
|
98
|
+
del ACTIVE_SESSIONS[self.session_name]
|
|
99
|
+
|
|
100
|
+
def test_initialization_defaults(self):
|
|
101
|
+
"""Verifies session initializes in locked state with proper phase, pacing, and refinement structures."""
|
|
102
|
+
self.assertEqual(self.session.session_name, self.session_name)
|
|
103
|
+
self.assertTrue(self.session.execution_locked)
|
|
104
|
+
self.assertFalse(self.session.can_execute_code)
|
|
105
|
+
self.assertEqual(self.session.active_phase, "Phase 1: Epistemic Grounding & Live Research")
|
|
106
|
+
self.assertEqual(self.session.time_budget_minutes, 45.0)
|
|
107
|
+
self.assertEqual(self.session.time_budget_seconds, 2700.0)
|
|
108
|
+
self.assertEqual(len(self.session.epistemic_ledger), 0)
|
|
109
|
+
self.assertEqual(len(self.session.invariants), 0)
|
|
110
|
+
self.assertEqual(len(self.session.refinement_cycles), 0)
|
|
111
|
+
self.assertEqual(len(self.session.phase_history), 1)
|
|
112
|
+
|
|
113
|
+
tel = self.session.get_telemetry()
|
|
114
|
+
self.assertEqual(tel["refinement_count"], 0)
|
|
115
|
+
self.assertEqual(tel["refinement_cycles"], [])
|
|
116
|
+
|
|
117
|
+
def test_timer_adjustment_is_pacing_only(self):
|
|
118
|
+
"""An agent sub-timer cannot shorten the authority deadline."""
|
|
119
|
+
authority_deadline = self.session.deadline_time
|
|
120
|
+
tel = self.session.set_timer(20.0)
|
|
121
|
+
self.assertEqual(self.session.time_budget_minutes, 45.0)
|
|
122
|
+
self.assertEqual(self.session.time_budget_seconds, 2700.0)
|
|
123
|
+
self.assertEqual(self.session.pacing_budget_minutes, 20.0)
|
|
124
|
+
self.assertEqual(self.session.deadline_time, authority_deadline)
|
|
125
|
+
self.assertLess(self.session.pacing_deadline_time, authority_deadline)
|
|
126
|
+
self.assertIn("authority_remaining_formatted", tel)
|
|
127
|
+
self.assertIn("pacing_remaining_formatted", tel)
|
|
128
|
+
self.assertIn("cognitive_gates", tel)
|
|
129
|
+
|
|
130
|
+
def test_epistemic_ledger_logging(self):
|
|
131
|
+
"""Verifies logging fact, hypothesis, and unknown items."""
|
|
132
|
+
item1 = self.session.log_epistemic_item("PROVEN", "Ring buffer power of 2 size allows bitwise mask", f"{__file__}:L42")
|
|
133
|
+
self.assertEqual(item1["id"], "epi_001")
|
|
134
|
+
self.assertEqual(item1["tag"], "PROVEN")
|
|
135
|
+
self.assertEqual(item1["evidence"], f"{__file__}:L42")
|
|
136
|
+
|
|
137
|
+
item2 = self.session.log_epistemic_item("HYPOTHESIS", "Acquire-Release fences suffice for x86 TSO")
|
|
138
|
+
self.assertEqual(item2["id"], "epi_002")
|
|
139
|
+
self.assertEqual(item2["tag"], "HYPOTHESIS")
|
|
140
|
+
|
|
141
|
+
item3 = self.session.log_epistemic_item("UNKNOWN", "Cache line size on target ARM64 architecture")
|
|
142
|
+
self.assertEqual(item3["id"], "epi_003")
|
|
143
|
+
self.assertEqual(item3["tag"], "UNKNOWN")
|
|
144
|
+
|
|
145
|
+
# Invalid tag test
|
|
146
|
+
with self.assertRaises(ValueError):
|
|
147
|
+
self.session.log_epistemic_item("INVALID_TAG", "Some statement")
|
|
148
|
+
|
|
149
|
+
# Empty claim test
|
|
150
|
+
with self.assertRaises(ValueError):
|
|
151
|
+
self.session.log_epistemic_item("PROVEN", " ")
|
|
152
|
+
|
|
153
|
+
tel = self.session.get_telemetry()
|
|
154
|
+
self.assertEqual(tel["epistemic_counts"]["proven"], 1)
|
|
155
|
+
self.assertEqual(tel["epistemic_counts"]["hypothesis"], 1)
|
|
156
|
+
self.assertEqual(tel["epistemic_counts"]["unknown"], 1)
|
|
157
|
+
self.assertEqual(tel["epistemic_counts"]["total"], 3)
|
|
158
|
+
|
|
159
|
+
def test_invariant_recording(self):
|
|
160
|
+
"""Verifies formal invariant recording across domains."""
|
|
161
|
+
inv = self.session.record_invariant(
|
|
162
|
+
invariant_name="INV-01: Head-Tail Boundedness",
|
|
163
|
+
formal_statement="0 <= (head - tail) <= BUFFER_CAPACITY at all times",
|
|
164
|
+
proof_or_rationale="Enforced via CAS atomic operations with modulo mask",
|
|
165
|
+
domain="architecture"
|
|
166
|
+
)
|
|
167
|
+
self.assertEqual(inv["id"], "inv_001")
|
|
168
|
+
self.assertEqual(inv["name"], "INV-01: Head-Tail Boundedness")
|
|
169
|
+
self.assertEqual(inv["domain"], "architecture")
|
|
170
|
+
|
|
171
|
+
# Empty fields validation
|
|
172
|
+
with self.assertRaises(ValueError):
|
|
173
|
+
self.session.record_invariant("", "statement", "rationale")
|
|
174
|
+
with self.assertRaises(ValueError):
|
|
175
|
+
self.session.record_invariant("name", "", "rationale")
|
|
176
|
+
|
|
177
|
+
def test_refinement_cycle_logging(self):
|
|
178
|
+
"""Verifies logging structured rethink-refine cycles with probes and artifacts."""
|
|
179
|
+
cycle1 = self.session.log_refinement_cycle(
|
|
180
|
+
refinement_type="archetype_exploration",
|
|
181
|
+
focus_area="Memory Partitioning",
|
|
182
|
+
critique_or_bottleneck="Global CAS on tail causes false sharing on multi-socket NUMA",
|
|
183
|
+
architectural_refinement="Partitioned ring buffer into per-core L1 chunks with batch migration",
|
|
184
|
+
terminal_probe_results="10M ops benchmark: 82ns down to 14ns latency",
|
|
185
|
+
artifact_path="C:/Users/test/artifacts/partitioned_buffer.md"
|
|
186
|
+
)
|
|
187
|
+
self.assertEqual(cycle1["cycle_number"], 1)
|
|
188
|
+
self.assertEqual(cycle1["refinement_type"], "archetype_exploration")
|
|
189
|
+
self.assertEqual(cycle1["focus_area"], "Memory Partitioning")
|
|
190
|
+
self.assertIn("Global CAS", cycle1["critique_or_bottleneck"])
|
|
191
|
+
self.assertIn("Partitioned", cycle1["architectural_refinement"])
|
|
192
|
+
self.assertEqual(cycle1["terminal_probe_results"], "10M ops benchmark: 82ns down to 14ns latency")
|
|
193
|
+
self.assertEqual(cycle1["artifact_path"], "C:/Users/test/artifacts/partitioned_buffer.md")
|
|
194
|
+
self.assertEqual(cycle1["phase"], self.session.active_phase)
|
|
195
|
+
|
|
196
|
+
cycle2 = self.session.log_refinement_cycle(
|
|
197
|
+
refinement_type="triz_contradiction_resolution",
|
|
198
|
+
focus_area="Queue Full Backpressure",
|
|
199
|
+
critique_or_bottleneck="Spinning burns CPU while yielding increases p99 latency",
|
|
200
|
+
architectural_refinement="Adaptive exponential backoff with futex sleep fallback"
|
|
201
|
+
)
|
|
202
|
+
self.assertEqual(cycle2["cycle_number"], 2)
|
|
203
|
+
self.assertIsNone(cycle2["terminal_probe_results"])
|
|
204
|
+
self.assertIsNone(cycle2["artifact_path"])
|
|
205
|
+
|
|
206
|
+
# Validate required field errors
|
|
207
|
+
with self.assertRaises(ValueError):
|
|
208
|
+
self.session.log_refinement_cycle("", "Focus", "Critique", "Refinement")
|
|
209
|
+
with self.assertRaises(ValueError):
|
|
210
|
+
self.session.log_refinement_cycle("Type", "", "Critique", "Refinement")
|
|
211
|
+
with self.assertRaises(ValueError):
|
|
212
|
+
self.session.log_refinement_cycle("Type", "Focus", "", "Refinement")
|
|
213
|
+
with self.assertRaises(ValueError):
|
|
214
|
+
self.session.log_refinement_cycle("Type", "Focus", "Critique", "")
|
|
215
|
+
|
|
216
|
+
tel = self.session.get_telemetry()
|
|
217
|
+
self.assertEqual(tel["refinement_count"], 2)
|
|
218
|
+
self.assertEqual(len(tel["refinement_cycles"]), 2)
|
|
219
|
+
|
|
220
|
+
def test_phase_transitions(self):
|
|
221
|
+
"""Verifies phase advancement and invalid phase handling."""
|
|
222
|
+
self.session.advance_phase(
|
|
223
|
+
"Phase 2: Invariant Specification & Blueprint",
|
|
224
|
+
"Completed grounding and verified CPU memory model"
|
|
225
|
+
)
|
|
226
|
+
self.assertEqual(self.session.active_phase, "Phase 2: Invariant Specification & Blueprint")
|
|
227
|
+
self.assertEqual(len(self.session.phase_history), 2)
|
|
228
|
+
|
|
229
|
+
# Invalid phase test
|
|
230
|
+
with self.assertRaises(ValueError):
|
|
231
|
+
self.session.advance_phase("Phase 99: Non-existent", "Should fail")
|
|
232
|
+
|
|
233
|
+
def test_hard_time_lock_enforcement(self):
|
|
234
|
+
"""
|
|
235
|
+
Verifies Hard Mechanical Time-Lock:
|
|
236
|
+
1. Fails with PermissionError when the immutable authority deadline is active.
|
|
237
|
+
2. A public hard-coded token cannot bypass the lock.
|
|
238
|
+
3. An explicitly configured out-of-band secret can be used for emergency override.
|
|
239
|
+
4. Succeeds normally when the authority deadline has elapsed.
|
|
240
|
+
"""
|
|
241
|
+
# Set up satisfying cognitive gates
|
|
242
|
+
self.session.log_epistemic_item("PROVEN", "Fact 1: Lock-free atomic swap verified", f"{__file__}:L201")
|
|
243
|
+
self.session.log_epistemic_item("PROVEN", "Fact 2: Modulo arithmetic holds", f"{__file__}:L202")
|
|
244
|
+
self.session.record_invariant("INV-01", "tail <= head", "CAS inductive proof")
|
|
245
|
+
for i in range(9):
|
|
246
|
+
self.session.log_refinement_cycle(
|
|
247
|
+
refinement_type="triz_refinement",
|
|
248
|
+
focus_area=f"Contention Area {i}",
|
|
249
|
+
critique_or_bottleneck=f"Bottleneck {i}",
|
|
250
|
+
architectural_refinement=f"Refinement {i}"
|
|
251
|
+
)
|
|
252
|
+
self.session.advance_phase("Phase 2: Invariant Specification & Blueprint", "Advanced to Phase 2")
|
|
253
|
+
self.session.advance_phase("Phase 3: Adversarial Red-Teaming & Falsification", "Advanced to Phase 3")
|
|
254
|
+
|
|
255
|
+
# 1. Timer has not elapsed -> must throw Hard Time-Lock Violation
|
|
256
|
+
with self.assertRaises(PermissionError) as ctx:
|
|
257
|
+
self.session.unlock_execution("Premature attempt before timer finishes")
|
|
258
|
+
self.assertIn("🛑 HARD TIME-LOCK VIOLATION", str(ctx.exception))
|
|
259
|
+
self.assertIn("immutable 45.0m authority budget has not elapsed yet", str(ctx.exception))
|
|
260
|
+
self.assertIn("internal pacing timer cannot unlock execution", str(ctx.exception))
|
|
261
|
+
self.assertIn("Rethink-Refine Cognitive Loop", str(ctx.exception))
|
|
262
|
+
|
|
263
|
+
# 2. The old public hard-coded token must not bypass the lock.
|
|
264
|
+
with self.assertRaises(PermissionError):
|
|
265
|
+
self.session.unlock_execution(
|
|
266
|
+
"Attempted emergency unlock",
|
|
267
|
+
force_override_token="USER_OVERRIDE_FORCE_UNLOCK"
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# 3. An administrative override works only through the direct host API.
|
|
271
|
+
os.environ["FABLE_FORCE_UNLOCK_TOKEN"] = "test-secret"
|
|
272
|
+
try:
|
|
273
|
+
res = self.session.unlock_execution(
|
|
274
|
+
"Emergency unlock ordered by host administrator",
|
|
275
|
+
force_override_token="test-secret"
|
|
276
|
+
)
|
|
277
|
+
finally:
|
|
278
|
+
os.environ.pop("FABLE_FORCE_UNLOCK_TOKEN", None)
|
|
279
|
+
self.assertEqual(res["status"], "UNLOCKED")
|
|
280
|
+
self.assertTrue(self.session.can_execute_code)
|
|
281
|
+
self.assertTrue(self.session.unlock_details["force_override_used"])
|
|
282
|
+
|
|
283
|
+
# 3. Test elapsed deadline_time without override token
|
|
284
|
+
new_session = FableSession(
|
|
285
|
+
session_name=f"test_elapsed_{int(time.time() * 1000)}",
|
|
286
|
+
objective="Test elapsed clock",
|
|
287
|
+
time_budget_minutes=10.0,
|
|
288
|
+
wall_clock=self.clock.time,
|
|
289
|
+
monotonic_clock=self.clock.monotonic
|
|
290
|
+
)
|
|
291
|
+
new_session.log_epistemic_item("PROVEN", "Fact 1: Lock-free atomic swap verified", f"{__file__}:L229")
|
|
292
|
+
new_session.log_epistemic_item("PROVEN", "Fact 2: Modulo arithmetic holds", f"{__file__}:L230")
|
|
293
|
+
new_session.record_invariant("INV-01", "tail <= head", "CAS inductive proof")
|
|
294
|
+
for i in range(2):
|
|
295
|
+
new_session.log_refinement_cycle(
|
|
296
|
+
refinement_type="triz_refinement",
|
|
297
|
+
focus_area=f"Area {i}",
|
|
298
|
+
critique_or_bottleneck=f"Critique {i}",
|
|
299
|
+
architectural_refinement=f"Refinement {i}"
|
|
300
|
+
)
|
|
301
|
+
new_session.advance_phase("Phase 2: Invariant Specification & Blueprint", "Phase 2 ready")
|
|
302
|
+
new_session.advance_phase("Phase 3: Adversarial Red-Teaming & Falsification", "Phase 3 ready")
|
|
303
|
+
|
|
304
|
+
# Simulate time elapsed past deadline
|
|
305
|
+
self.clock.advance(601.0)
|
|
306
|
+
res = new_session.unlock_execution("Timer has naturally elapsed")
|
|
307
|
+
self.assertEqual(res["status"], "UNLOCKED")
|
|
308
|
+
self.assertTrue(new_session.can_execute_code)
|
|
309
|
+
self.assertFalse(new_session.unlock_details["force_override_used"])
|
|
310
|
+
|
|
311
|
+
def test_anti_rush_lockout_enforcement(self):
|
|
312
|
+
"""
|
|
313
|
+
Rigorous test of Anti-Rush Cognitive Lockout Gates:
|
|
314
|
+
Requires:
|
|
315
|
+
1. >= 2 PROVEN epistemic items
|
|
316
|
+
2. >= 1 formal invariant
|
|
317
|
+
3. Active phase >= Phase 3
|
|
318
|
+
"""
|
|
319
|
+
# Simulate an elapsed authority deadline so this test isolates cognitive gates.
|
|
320
|
+
self.clock.advance(2701.0)
|
|
321
|
+
|
|
322
|
+
# Attempt 1: In Phase 1 with 0 proven, 0 invariants -> Must fail cognitive gate
|
|
323
|
+
with self.assertRaises(PermissionError) as ctx:
|
|
324
|
+
self.session.unlock_execution("Let me code")
|
|
325
|
+
self.assertIn("Anti-Rush Lockout Active", str(ctx.exception))
|
|
326
|
+
self.assertIn("at least 2 [PROVEN]", str(ctx.exception))
|
|
327
|
+
|
|
328
|
+
# Attempt 2: Add 1 PROVEN item -> Must still fail
|
|
329
|
+
self.session.log_epistemic_item("PROVEN", "Fact 1: Single producer queue is wait-free", f"{__file__}:L259")
|
|
330
|
+
with self.assertRaises(PermissionError) as ctx:
|
|
331
|
+
self.session.unlock_execution("Let me code")
|
|
332
|
+
self.assertIn("at least 2 [PROVEN]", str(ctx.exception))
|
|
333
|
+
|
|
334
|
+
# Attempt 3: Add 2nd PROVEN item, but 0 invariants -> Must still fail
|
|
335
|
+
self.session.log_epistemic_item("PROVEN", "Fact 2: Atomic CAS is supported on target CPU", f"{__file__}:L265")
|
|
336
|
+
with self.assertRaises(PermissionError) as ctx:
|
|
337
|
+
self.session.unlock_execution("Let me code")
|
|
338
|
+
self.assertIn("at least 1 formal Invariant", str(ctx.exception))
|
|
339
|
+
|
|
340
|
+
# Attempt 4: Add invariant, but active phase is Phase 1 -> Must still fail
|
|
341
|
+
self.session.record_invariant(
|
|
342
|
+
"INV-01",
|
|
343
|
+
"tail <= head",
|
|
344
|
+
"Proof by induction",
|
|
345
|
+
"coding"
|
|
346
|
+
)
|
|
347
|
+
with self.assertRaises(PermissionError) as ctx:
|
|
348
|
+
self.session.unlock_execution("Let me code")
|
|
349
|
+
self.assertIn("at least Phase 3", str(ctx.exception))
|
|
350
|
+
|
|
351
|
+
# Attempt 5: Advance to Phase 2 -> Must still fail
|
|
352
|
+
self.session.advance_phase("Phase 2: Invariant Specification & Blueprint", "Drafted blueprints")
|
|
353
|
+
with self.assertRaises(PermissionError) as ctx:
|
|
354
|
+
self.session.unlock_execution("Let me code")
|
|
355
|
+
self.assertIn("at least Phase 3", str(ctx.exception))
|
|
356
|
+
|
|
357
|
+
# Log required refinement cycles (9 required for 45m budget)
|
|
358
|
+
for i in range(9):
|
|
359
|
+
self.session.log_refinement_cycle(
|
|
360
|
+
refinement_type="archetype_exploration",
|
|
361
|
+
focus_area=f"Area {i}",
|
|
362
|
+
critique_or_bottleneck=f"Critique {i}",
|
|
363
|
+
architectural_refinement=f"Refinement {i}"
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# Attempt 6: Advance to Phase 3 -> Satisfies ALL gates! Must succeed!
|
|
367
|
+
self.session.advance_phase("Phase 3: Adversarial Red-Teaming & Falsification", "Passed red team fuzzing")
|
|
368
|
+
res = self.session.unlock_execution(
|
|
369
|
+
"All invariants proved and red-team checks passed."
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
self.assertEqual(res["status"], "UNLOCKED")
|
|
373
|
+
self.assertFalse(self.session.execution_locked)
|
|
374
|
+
self.assertTrue(self.session.can_execute_code)
|
|
375
|
+
self.assertIsNotNone(self.session.unlock_details)
|
|
376
|
+
self.assertEqual(self.session.unlock_details["proven_count"], 2)
|
|
377
|
+
self.assertEqual(self.session.unlock_details["invariants_count"], 1)
|
|
378
|
+
|
|
379
|
+
def test_pacing_expiry_cannot_unlock(self):
|
|
380
|
+
"""A completed internal timer never satisfies the authority lock."""
|
|
381
|
+
self.session.set_timer(2.0)
|
|
382
|
+
self.clock.advance(1.0)
|
|
383
|
+
self.session.log_epistemic_item("PROVEN", "Fact 1: Verified property", f"{__file__}:L1")
|
|
384
|
+
self.session.log_epistemic_item("PROVEN", "Fact 2: Validated cache line", f"{__file__}:L2")
|
|
385
|
+
self.session.record_invariant("INV-01", "tail <= head", "CAS monotonic counter guarantee")
|
|
386
|
+
self.session.advance_phase("Phase 2: Invariant Specification & Blueprint", "Phase 2")
|
|
387
|
+
self.session.advance_phase("Phase 3: Adversarial Red-Teaming & Falsification", "Phase 3")
|
|
388
|
+
with self.assertRaises(PermissionError):
|
|
389
|
+
self.session.unlock_execution("Pacing timer expired")
|
|
390
|
+
|
|
391
|
+
def test_proven_claims_require_evidence(self):
|
|
392
|
+
"""A truth label without an evidence pointer is not an admissible gate input."""
|
|
393
|
+
with self.assertRaises(ValueError):
|
|
394
|
+
self.session.log_epistemic_item("PROVEN", "An unreferenced assertion")
|
|
395
|
+
|
|
396
|
+
item = self.session.log_epistemic_item(
|
|
397
|
+
"PROVEN", "A referenced assertion", f"{__file__}:L1"
|
|
398
|
+
)
|
|
399
|
+
self.assertEqual(item["tag"], "PROVEN")
|
|
400
|
+
|
|
401
|
+
def test_invalid_budget_and_session_name_are_rejected(self):
|
|
402
|
+
with self.assertRaises(ValueError):
|
|
403
|
+
FableSession("invalid/escape", "test", 10)
|
|
404
|
+
with self.assertRaises(ValueError):
|
|
405
|
+
FableSession("bad_budget", "test", float("nan"))
|
|
406
|
+
with self.assertRaises(ValueError):
|
|
407
|
+
self.session.set_timer(-1)
|
|
408
|
+
|
|
409
|
+
def test_serialization_and_atomic_save(self):
|
|
410
|
+
"""Verifies session dictionary serialization, deserialization, and disk saving including refinement cycles."""
|
|
411
|
+
self.session.log_epistemic_item("PROVEN", "Proven item 1", f"{__file__}:L303")
|
|
412
|
+
self.session.record_invariant("INV-01", "Statement", "Rationale")
|
|
413
|
+
self.session.log_refinement_cycle(
|
|
414
|
+
refinement_type="archetype_exploration",
|
|
415
|
+
focus_area="Cache coherency",
|
|
416
|
+
critique_or_bottleneck="False sharing",
|
|
417
|
+
architectural_refinement="Padding to 64 bytes"
|
|
418
|
+
)
|
|
419
|
+
saved_path = self.session.save()
|
|
420
|
+
self.assertTrue(saved_path.exists())
|
|
421
|
+
|
|
422
|
+
with open(saved_path, "r", encoding="utf-8") as f:
|
|
423
|
+
data = json.load(f)
|
|
424
|
+
|
|
425
|
+
restored = FableSession.from_dict(data)
|
|
426
|
+
self.assertEqual(restored.session_name, self.session.session_name)
|
|
427
|
+
self.assertEqual(len(restored.epistemic_ledger), 1)
|
|
428
|
+
self.assertEqual(len(restored.invariants), 1)
|
|
429
|
+
self.assertEqual(len(restored.refinement_cycles), 1)
|
|
430
|
+
self.assertEqual(restored.refinement_cycles[0]["focus_area"], "Cache coherency")
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
class TestFableHandlerDispatch(unittest.TestCase):
|
|
434
|
+
"""Tests high-level handle_fable_session tool dispatcher."""
|
|
435
|
+
|
|
436
|
+
def setUp(self):
|
|
437
|
+
self.session_name = f"test_dispatch_{int(time.time() * 1000)}"
|
|
438
|
+
|
|
439
|
+
def tearDown(self):
|
|
440
|
+
target_file = SESSIONS_DIR / f"{self.session_name}.json"
|
|
441
|
+
if target_file.exists():
|
|
442
|
+
try:
|
|
443
|
+
target_file.unlink()
|
|
444
|
+
except Exception:
|
|
445
|
+
pass
|
|
446
|
+
if self.session_name in ACTIVE_SESSIONS:
|
|
447
|
+
del ACTIVE_SESSIONS[self.session_name]
|
|
448
|
+
|
|
449
|
+
def test_refinement_cycle_dispatch(self):
|
|
450
|
+
"""Verifies dispatching log_refinement_cycle and aliases."""
|
|
451
|
+
handle_fable_session({
|
|
452
|
+
"action": "create_session",
|
|
453
|
+
"session_name": self.session_name,
|
|
454
|
+
"objective": "Design lock-free queue",
|
|
455
|
+
"time_budget_minutes": 30
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
# Call log_refinement_cycle
|
|
459
|
+
res = handle_fable_session({
|
|
460
|
+
"action": "log_refinement_cycle",
|
|
461
|
+
"session_name": self.session_name,
|
|
462
|
+
"refinement_type": "archetype_exploration",
|
|
463
|
+
"focus_area": "Atomic memory ordering",
|
|
464
|
+
"critique_or_bottleneck": "SeqCst is unnecessarily heavy on ARM64",
|
|
465
|
+
"architectural_refinement": "Relaxed loads with Acquire-Release fences",
|
|
466
|
+
"terminal_probe_results": "Benchmark: 4.2x speedup on aarch64",
|
|
467
|
+
"artifact_path": "C:/docs/arch_refine.md"
|
|
468
|
+
})
|
|
469
|
+
self.assertIn("Rethink-Refine Cycle #1 Logged", res)
|
|
470
|
+
self.assertIn("ARCHETYPE_EXPLORATION", res)
|
|
471
|
+
self.assertIn("Atomic memory ordering", res)
|
|
472
|
+
self.assertIn("Terminal Probes / Benchmarks", res)
|
|
473
|
+
self.assertIn("Artifact Blueprint", res)
|
|
474
|
+
self.assertIn("Total Refinement Cycles", res)
|
|
475
|
+
|
|
476
|
+
# Call alias refine
|
|
477
|
+
res_alias = handle_fable_session({
|
|
478
|
+
"action": "refine",
|
|
479
|
+
"session_name": self.session_name,
|
|
480
|
+
"refinement_type": "adversarial_falsification",
|
|
481
|
+
"focus_area": "ABA Problem",
|
|
482
|
+
"critique_or_bottleneck": "Pointer recycling can fool standard CAS",
|
|
483
|
+
"architectural_refinement": "Tagged pointer with generation counter"
|
|
484
|
+
})
|
|
485
|
+
self.assertIn("Rethink-Refine Cycle #2 Logged", res_alias)
|
|
486
|
+
self.assertIn("ADVERSARIAL_FALSIFICATION", res_alias)
|
|
487
|
+
|
|
488
|
+
# Missing required parameter tests
|
|
489
|
+
res_err = handle_fable_session({
|
|
490
|
+
"action": "log_refinement_cycle",
|
|
491
|
+
"session_name": self.session_name,
|
|
492
|
+
"refinement_type": "test",
|
|
493
|
+
"focus_area": "test",
|
|
494
|
+
"critique_or_bottleneck": ""
|
|
495
|
+
})
|
|
496
|
+
self.assertIn("Error: 'critique_or_bottleneck' is required", res_err)
|
|
497
|
+
|
|
498
|
+
def test_full_workflow_via_handler(self):
|
|
499
|
+
"""Executes full lifecycle through the dispatcher tool interface."""
|
|
500
|
+
# 1. Create session
|
|
501
|
+
res = handle_fable_session({
|
|
502
|
+
"action": "create_session",
|
|
503
|
+
"session_name": self.session_name,
|
|
504
|
+
"objective": "Build zero-copy parser",
|
|
505
|
+
"time_budget_minutes": 2.0
|
|
506
|
+
})
|
|
507
|
+
self.assertIn("Fable Cognitive Session Initialized", res)
|
|
508
|
+
self.assertIn("Anti-Rush Lockout is ACTIVE", res)
|
|
509
|
+
|
|
510
|
+
# 2. Get status / telemetry
|
|
511
|
+
res = handle_fable_session({
|
|
512
|
+
"action": "get_status",
|
|
513
|
+
"session_name": self.session_name
|
|
514
|
+
})
|
|
515
|
+
self.assertIn("Fable Session Status & Telemetry", res)
|
|
516
|
+
self.assertIn("LOCKED", res)
|
|
517
|
+
|
|
518
|
+
# 3. Log epistemic items
|
|
519
|
+
res = handle_fable_session({
|
|
520
|
+
"action": "log_epistemic_item",
|
|
521
|
+
"session_name": self.session_name,
|
|
522
|
+
"tag": "PROVEN",
|
|
523
|
+
"claim": "Zero-copy slices borrow from underlying buffer without allocation",
|
|
524
|
+
"evidence": f"{__file__}:L10"
|
|
525
|
+
})
|
|
526
|
+
self.assertIn("Epistemic Item Logged", res)
|
|
527
|
+
self.assertIn("[PROVEN]", res)
|
|
528
|
+
|
|
529
|
+
res = handle_fable_session({
|
|
530
|
+
"action": "log_epistemic_item",
|
|
531
|
+
"session_name": self.session_name,
|
|
532
|
+
"tag": "PROVEN",
|
|
533
|
+
"claim": "Lifetime bounds prevent use-after-free",
|
|
534
|
+
"evidence": "cargo test stdout: rustc verification passed"
|
|
535
|
+
})
|
|
536
|
+
self.assertIn("2 PROVEN", res)
|
|
537
|
+
|
|
538
|
+
# 4. Record invariant
|
|
539
|
+
res = handle_fable_session({
|
|
540
|
+
"action": "record_invariant",
|
|
541
|
+
"session_name": self.session_name,
|
|
542
|
+
"invariant_name": "INV-LIFETIME",
|
|
543
|
+
"formal_statement": "SliceLifetime <= BufferLifetime",
|
|
544
|
+
"proof_or_rationale": "Static borrow checker guarantee",
|
|
545
|
+
"domain": "design"
|
|
546
|
+
})
|
|
547
|
+
self.assertIn("Formal Invariant Recorded", res)
|
|
548
|
+
self.assertIn("INV-LIFETIME", res)
|
|
549
|
+
|
|
550
|
+
# 5. Log refinement cycles (2 required for 2.0m budget)
|
|
551
|
+
res = handle_fable_session({
|
|
552
|
+
"action": "log_refinement_cycle",
|
|
553
|
+
"session_name": self.session_name,
|
|
554
|
+
"refinement_type": "benchmark_probe",
|
|
555
|
+
"focus_area": "SIMD tokenization",
|
|
556
|
+
"critique_or_bottleneck": "Scalar byte scanning is memory-bound",
|
|
557
|
+
"architectural_refinement": "AVX-512 vectorized delimiter detection",
|
|
558
|
+
"terminal_probe_results": "Throughput increased from 1.2 GB/s to 9.8 GB/s"
|
|
559
|
+
})
|
|
560
|
+
self.assertIn("Rethink-Refine Cycle #1 Logged", res)
|
|
561
|
+
|
|
562
|
+
res2 = handle_fable_session({
|
|
563
|
+
"action": "log_refinement_cycle",
|
|
564
|
+
"session_name": self.session_name,
|
|
565
|
+
"refinement_type": "adversarial_falsification",
|
|
566
|
+
"focus_area": "Boundary conditions",
|
|
567
|
+
"critique_or_bottleneck": "Unchecked pointer arithmetic on overflow",
|
|
568
|
+
"architectural_refinement": "Saturating arithmetic bounds with compile-time assertions"
|
|
569
|
+
})
|
|
570
|
+
self.assertIn("Rethink-Refine Cycle #2 Logged", res2)
|
|
571
|
+
|
|
572
|
+
# 6. Try premature unlock (Phase 1) -> Must fail Hard Time-Lock (or cognitive gate)
|
|
573
|
+
res = handle_fable_session({
|
|
574
|
+
"action": "unlock_execution",
|
|
575
|
+
"session_name": self.session_name,
|
|
576
|
+
"rationale": "Premature attempt"
|
|
577
|
+
})
|
|
578
|
+
self.assertIn("HARD TIME-LOCK VIOLATION", res)
|
|
579
|
+
|
|
580
|
+
# 7. Advance to Phase 2 then Phase 3
|
|
581
|
+
handle_fable_session({
|
|
582
|
+
"action": "advance_phase",
|
|
583
|
+
"session_name": self.session_name,
|
|
584
|
+
"next_phase": "Phase 2: Invariant Specification & Blueprint",
|
|
585
|
+
"phase_summary": "Blueprints created"
|
|
586
|
+
})
|
|
587
|
+
handle_fable_session({
|
|
588
|
+
"action": "advance_phase",
|
|
589
|
+
"session_name": self.session_name,
|
|
590
|
+
"next_phase": "Phase 3: Adversarial Red-Teaming & Falsification",
|
|
591
|
+
"phase_summary": "Passed red-teaming checks"
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
# 8. Attempt unlock without elapsed time -> Still hits Hard Time-Lock
|
|
595
|
+
res = handle_fable_session({
|
|
596
|
+
"action": "unlock_execution",
|
|
597
|
+
"session_name": self.session_name,
|
|
598
|
+
"rationale": "Cognitive gates satisfied but timer active"
|
|
599
|
+
})
|
|
600
|
+
self.assertIn("HARD TIME-LOCK VIOLATION", res)
|
|
601
|
+
|
|
602
|
+
# 9. Simulate elapsed monotonic time for testing unlock
|
|
603
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
604
|
+
session._authority_deadline_monotonic = time.monotonic() - 1.0
|
|
605
|
+
session._authority_deadline_wall = time.time() - 1.0
|
|
606
|
+
|
|
607
|
+
res = handle_fable_session({
|
|
608
|
+
"action": "unlock_execution",
|
|
609
|
+
"session_name": self.session_name,
|
|
610
|
+
"rationale": "Cognitive gates satisfied with 2 proven facts, 1 invariant, and 2 refinement cycles"
|
|
611
|
+
})
|
|
612
|
+
self.assertIn("Execution Lock Lifted Successfully", res)
|
|
613
|
+
self.assertIn("🟢 UNLOCKED", res)
|
|
614
|
+
|
|
615
|
+
# 10. Adjust internal pacing timer without changing authority budget.
|
|
616
|
+
res = handle_fable_session({
|
|
617
|
+
"action": "set_timer",
|
|
618
|
+
"session_name": self.session_name,
|
|
619
|
+
"time_budget_minutes": 60
|
|
620
|
+
})
|
|
621
|
+
self.assertIn("Pacing Timer", res)
|
|
622
|
+
self.assertIn("Authority Budget", res)
|
|
623
|
+
self.assertIn("2.0", res)
|
|
624
|
+
|
|
625
|
+
# 11. Checkpoint and restore
|
|
626
|
+
res = handle_fable_session({
|
|
627
|
+
"action": "checkpoint_session",
|
|
628
|
+
"session_name": self.session_name
|
|
629
|
+
})
|
|
630
|
+
self.assertIn("Fable Session Checkpointed", res)
|
|
631
|
+
self.assertIn("Refinement Cycles", res)
|
|
632
|
+
|
|
633
|
+
res = handle_fable_session({
|
|
634
|
+
"action": "restore_session",
|
|
635
|
+
"session_name": self.session_name
|
|
636
|
+
})
|
|
637
|
+
self.assertIn("Fable Session Restored", res)
|
|
638
|
+
self.assertIn("Refinement Cycles", res)
|
|
639
|
+
|
|
640
|
+
# 12. List sessions
|
|
641
|
+
res = handle_fable_session({
|
|
642
|
+
"action": "list_sessions"
|
|
643
|
+
})
|
|
644
|
+
self.assertIn(self.session_name, res)
|
|
645
|
+
|
|
646
|
+
def test_cortical_actions_dispatch(self):
|
|
647
|
+
"""Tests handle_fable_session routing for cortical_define_lobe and cortical_list_lobes."""
|
|
648
|
+
cortex_dir = Path(__file__).resolve().parents[1] / "skills" / "fable-mode" / "cortex"
|
|
649
|
+
test_file = cortex_dir / "test_distributed_raft.md"
|
|
650
|
+
try:
|
|
651
|
+
# 1. Sprout custom lobe
|
|
652
|
+
res_sprout = handle_fable_session({
|
|
653
|
+
"action": "cortical_define_lobe",
|
|
654
|
+
"name": "test_distributed_raft",
|
|
655
|
+
"description": "Raft consensus, leader election, and term invariants",
|
|
656
|
+
"initial_heuristics": ["Followers only vote once per term"],
|
|
657
|
+
"initial_synaptic_weights": {"quorum": 0.95},
|
|
658
|
+
})
|
|
659
|
+
self.assertIn("Cortical Lobe Sprouted: `test_distributed_raft`", res_sprout)
|
|
660
|
+
self.assertIn("Raft consensus", res_sprout)
|
|
661
|
+
|
|
662
|
+
# 2. List lobes
|
|
663
|
+
res_list = handle_fable_session({
|
|
664
|
+
"action": "cortical_list_lobes",
|
|
665
|
+
})
|
|
666
|
+
self.assertIn("Available Cortical Lobes", res_list)
|
|
667
|
+
self.assertIn("test_distributed_raft", res_list)
|
|
668
|
+
|
|
669
|
+
# 3. Missing name error
|
|
670
|
+
res_err = handle_fable_session({
|
|
671
|
+
"action": "cortical_define_lobe",
|
|
672
|
+
})
|
|
673
|
+
self.assertIn("Error: 'name' or 'lobe_name' is required", res_err)
|
|
674
|
+
finally:
|
|
675
|
+
if test_file.exists():
|
|
676
|
+
try:
|
|
677
|
+
test_file.unlink()
|
|
678
|
+
except Exception:
|
|
679
|
+
pass
|
|
680
|
+
|
|
681
|
+
def test_edge_cases_and_error_handling(self):
|
|
682
|
+
"""Tests dispatcher error handling on invalid inputs."""
|
|
683
|
+
# Missing action
|
|
684
|
+
res = handle_fable_session({})
|
|
685
|
+
self.assertIn("Error: Missing required parameter 'action'", res)
|
|
686
|
+
|
|
687
|
+
# Unknown action
|
|
688
|
+
res = handle_fable_session({"action": "fly_to_moon"})
|
|
689
|
+
self.assertIn("Error: Unknown action 'fly_to_moon'", res)
|
|
690
|
+
|
|
691
|
+
# Missing session_name
|
|
692
|
+
res = handle_fable_session({"action": "get_status"})
|
|
693
|
+
self.assertIn("Error: 'session_name' is required", res)
|
|
694
|
+
|
|
695
|
+
# Non-existent session
|
|
696
|
+
res = handle_fable_session({"action": "get_status", "session_name": "ghost_session_xyz"})
|
|
697
|
+
self.assertIn("does not exist", res)
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
class TestFableMCPStdioServer(unittest.TestCase):
|
|
701
|
+
"""End-to-end integration test of the JSON-RPC 2.0 stdio server process."""
|
|
702
|
+
|
|
703
|
+
def setUp(self):
|
|
704
|
+
self.python_exe = sys.executable
|
|
705
|
+
self.server_script = str(CURRENT_DIR / "server.py")
|
|
706
|
+
self.proc = subprocess.Popen(
|
|
707
|
+
[self.python_exe, self.server_script],
|
|
708
|
+
stdin=subprocess.PIPE,
|
|
709
|
+
stdout=subprocess.PIPE,
|
|
710
|
+
stderr=subprocess.PIPE,
|
|
711
|
+
text=True,
|
|
712
|
+
encoding="utf-8"
|
|
713
|
+
)
|
|
714
|
+
# Drain diagnostics continuously: a long-running interactive server
|
|
715
|
+
# must not block on a full stderr pipe while the test reads stdout.
|
|
716
|
+
self._stderr_lines = []
|
|
717
|
+
self._stderr_thread = threading.Thread(target=self._drain_stderr, daemon=True)
|
|
718
|
+
self._stderr_thread.start()
|
|
719
|
+
self.req_id = 0
|
|
720
|
+
|
|
721
|
+
def _drain_stderr(self):
|
|
722
|
+
if self.proc.stderr is None:
|
|
723
|
+
return
|
|
724
|
+
try:
|
|
725
|
+
for line in self.proc.stderr:
|
|
726
|
+
if len(self._stderr_lines) < 256:
|
|
727
|
+
self._stderr_lines.append(line)
|
|
728
|
+
except (OSError, ValueError):
|
|
729
|
+
pass
|
|
730
|
+
|
|
731
|
+
def tearDown(self):
|
|
732
|
+
if self.proc.stdin:
|
|
733
|
+
try:
|
|
734
|
+
self.proc.stdin.close()
|
|
735
|
+
except Exception:
|
|
736
|
+
pass
|
|
737
|
+
if self.proc.stdout:
|
|
738
|
+
try:
|
|
739
|
+
self.proc.stdout.close()
|
|
740
|
+
except Exception:
|
|
741
|
+
pass
|
|
742
|
+
if self.proc.stderr:
|
|
743
|
+
try:
|
|
744
|
+
self.proc.stderr.close()
|
|
745
|
+
except Exception:
|
|
746
|
+
pass
|
|
747
|
+
if self.proc.poll() is None:
|
|
748
|
+
self.proc.terminate()
|
|
749
|
+
try:
|
|
750
|
+
self.proc.wait(timeout=2)
|
|
751
|
+
except subprocess.TimeoutExpired:
|
|
752
|
+
self.proc.kill()
|
|
753
|
+
|
|
754
|
+
def _rpc_call(self, method: str, params: dict = None) -> dict:
|
|
755
|
+
self.req_id += 1
|
|
756
|
+
req = {
|
|
757
|
+
"jsonrpc": "2.0",
|
|
758
|
+
"id": self.req_id,
|
|
759
|
+
"method": method
|
|
760
|
+
}
|
|
761
|
+
if params is not None:
|
|
762
|
+
req["params"] = params
|
|
763
|
+
|
|
764
|
+
raw_req = json.dumps(req) + "\n"
|
|
765
|
+
self.proc.stdin.write(raw_req)
|
|
766
|
+
self.proc.stdin.flush()
|
|
767
|
+
|
|
768
|
+
line = self.proc.stdout.readline()
|
|
769
|
+
self.assertTrue(line, "Server returned empty output")
|
|
770
|
+
return json.loads(line)
|
|
771
|
+
|
|
772
|
+
def test_mcp_handshake_and_tool_call(self):
|
|
773
|
+
"""Verifies initialize, tools/list, and tools/call over stdio JSON-RPC."""
|
|
774
|
+
# 1. Initialize
|
|
775
|
+
resp = self._rpc_call("initialize")
|
|
776
|
+
self.assertEqual(resp.get("jsonrpc"), "2.0")
|
|
777
|
+
self.assertEqual(resp.get("id"), 1)
|
|
778
|
+
self.assertEqual(resp["result"]["serverInfo"]["name"], "fable-engine")
|
|
779
|
+
|
|
780
|
+
# 2. Tools List
|
|
781
|
+
resp = self._rpc_call("tools/list")
|
|
782
|
+
tools = resp["result"]["tools"]
|
|
783
|
+
self.assertEqual(len(tools), 14)
|
|
784
|
+
tool_names = [t["name"] for t in tools]
|
|
785
|
+
self.assertIn("fable_session", tool_names)
|
|
786
|
+
self.assertIn("browser_open", tool_names)
|
|
787
|
+
self.assertIn("log_refinement_cycle", tools[0]["inputSchema"]["properties"]["action"]["enum"])
|
|
788
|
+
|
|
789
|
+
# 3. Call fable_session: create_session
|
|
790
|
+
session_name = f"mcp_stdio_test_{int(time.time() * 1000)}"
|
|
791
|
+
resp = self._rpc_call("tools/call", {
|
|
792
|
+
"name": "fable_session",
|
|
793
|
+
"arguments": {
|
|
794
|
+
"action": "create_session",
|
|
795
|
+
"session_name": session_name,
|
|
796
|
+
"objective": "Verify MCP stdio protocol loop",
|
|
797
|
+
"time_budget_minutes": 15
|
|
798
|
+
}
|
|
799
|
+
})
|
|
800
|
+
self.assertFalse(resp["result"].get("isError", True))
|
|
801
|
+
content = resp["result"]["content"][0]["text"]
|
|
802
|
+
self.assertIn("Fable Cognitive Session Initialized", content)
|
|
803
|
+
|
|
804
|
+
# 4. Call fable_session: log_refinement_cycle
|
|
805
|
+
resp = self._rpc_call("tools/call", {
|
|
806
|
+
"name": "fable_session",
|
|
807
|
+
"arguments": {
|
|
808
|
+
"action": "log_refinement_cycle",
|
|
809
|
+
"session_name": session_name,
|
|
810
|
+
"refinement_type": "benchmark_probe",
|
|
811
|
+
"focus_area": "Zero-copy buffering",
|
|
812
|
+
"critique_or_bottleneck": "Memory allocation inside hot loop",
|
|
813
|
+
"architectural_refinement": "Preallocated memory arena slab allocator",
|
|
814
|
+
"terminal_probe_results": "0 allocs/op achieved"
|
|
815
|
+
}
|
|
816
|
+
})
|
|
817
|
+
self.assertFalse(resp["result"].get("isError", True))
|
|
818
|
+
content = resp["result"]["content"][0]["text"]
|
|
819
|
+
self.assertIn("Rethink-Refine Cycle #1 Logged", content)
|
|
820
|
+
|
|
821
|
+
# 5. Call fable_session: compress_payload & view_slice over stdio
|
|
822
|
+
resp = self._rpc_call("tools/call", {
|
|
823
|
+
"name": "fable_session",
|
|
824
|
+
"arguments": {
|
|
825
|
+
"action": "compress_payload",
|
|
826
|
+
"content": "Line 1: Sample MCP payload\nLine 2: Another line\nLine 3: Third line\n",
|
|
827
|
+
"label": "stdio_test_payload"
|
|
828
|
+
}
|
|
829
|
+
})
|
|
830
|
+
self.assertFalse(resp["result"].get("isError", True))
|
|
831
|
+
content = resp["result"]["content"][0]["text"]
|
|
832
|
+
self.assertIn("Fable CAS Payload Compressed", content)
|
|
833
|
+
|
|
834
|
+
# 6. Ping
|
|
835
|
+
resp = self._rpc_call("ping")
|
|
836
|
+
self.assertEqual(resp.get("result"), {})
|
|
837
|
+
|
|
838
|
+
# Clean up session file
|
|
839
|
+
sfile = SESSIONS_DIR / f"{session_name}.json"
|
|
840
|
+
if sfile.exists():
|
|
841
|
+
try:
|
|
842
|
+
sfile.unlink()
|
|
843
|
+
except Exception:
|
|
844
|
+
pass
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
class TestFableTokenCompression(unittest.TestCase):
|
|
848
|
+
"""Direct subsystem tests for FableCASStore, AdaptiveChunkAccumulator, FableGrammar333, CASSliceViewer, and FableCompress."""
|
|
849
|
+
|
|
850
|
+
def setUp(self):
|
|
851
|
+
self.test_dir = Path(tempfile.mkdtemp(prefix="fable_test_cas_"))
|
|
852
|
+
self.compressor = FableCompress(root_dir=self.test_dir)
|
|
853
|
+
|
|
854
|
+
def tearDown(self):
|
|
855
|
+
if self.test_dir.exists():
|
|
856
|
+
shutil.rmtree(self.test_dir, ignore_errors=True)
|
|
857
|
+
|
|
858
|
+
def test_cas_store_atomic_put_get(self):
|
|
859
|
+
"""Verify atomic writes, SHA-256 addresses, and byte-exact retrieval."""
|
|
860
|
+
store = self.compressor.cas_store
|
|
861
|
+
sample = "Fable-Mode Token Compression Subsystem verification payload."
|
|
862
|
+
uri = store.put(sample)
|
|
863
|
+
self.assertTrue(uri.startswith("cas://"))
|
|
864
|
+
self.assertEqual(len(store.normalize_ref(uri)), 64)
|
|
865
|
+
self.assertEqual(store.get_text(uri), sample)
|
|
866
|
+
self.assertEqual(store.get_bytes(uri), sample.encode("utf-8"))
|
|
867
|
+
self.assertTrue(store.verify_integrity(uri))
|
|
868
|
+
|
|
869
|
+
def test_cas_corruption_detection(self):
|
|
870
|
+
"""Verify integrity verification catches file tampering on disk."""
|
|
871
|
+
store = self.compressor.cas_store
|
|
872
|
+
sample = "Pristine data payload"
|
|
873
|
+
uri = store.put(sample)
|
|
874
|
+
file_path = store.get_file_path(uri)
|
|
875
|
+
store.cache.clear()
|
|
876
|
+
|
|
877
|
+
with open(file_path, "r+b") as f:
|
|
878
|
+
f.seek(0)
|
|
879
|
+
f.write(b"Z")
|
|
880
|
+
|
|
881
|
+
with self.assertRaises(IntegrityError):
|
|
882
|
+
store.get_bytes(uri, verify=True)
|
|
883
|
+
self.assertFalse(store.verify_integrity(uri))
|
|
884
|
+
|
|
885
|
+
def test_lru_cache_bounds_and_eviction(self):
|
|
886
|
+
"""Verify LRU cache capacity limits and disk fallback."""
|
|
887
|
+
small_store = FableCASStore(root_dir=self.test_dir / "lru", cache_capacity=2)
|
|
888
|
+
u1 = small_store.put("entry_1")
|
|
889
|
+
u2 = small_store.put("entry_2")
|
|
890
|
+
u3 = small_store.put("entry_3")
|
|
891
|
+
|
|
892
|
+
self.assertEqual(len(small_store.cache), 2)
|
|
893
|
+
self.assertEqual(small_store.get_text(u1), "entry_1")
|
|
894
|
+
|
|
895
|
+
def test_adaptive_chunk_accumulator_coalescing(self):
|
|
896
|
+
"""Verify sub-1000 character micro-payload batching into composite frames."""
|
|
897
|
+
acc = self.compressor.accumulator
|
|
898
|
+
payloads = [f"Log trace entry #{i:03d} for subagent execution step." for i in range(30)]
|
|
899
|
+
|
|
900
|
+
flushed_uris = []
|
|
901
|
+
for p in payloads:
|
|
902
|
+
flushed_uris.extend(acc.add(p, metadata={"step": "trace"}))
|
|
903
|
+
flushed_uris.extend(acc.flush())
|
|
904
|
+
|
|
905
|
+
self.assertGreater(len(flushed_uris), 0)
|
|
906
|
+
extracted = []
|
|
907
|
+
for uri in flushed_uris:
|
|
908
|
+
frame_json = self.compressor.cas_store.get_text(uri)
|
|
909
|
+
frame = CompositeFrame.deserialize_json(frame_json)
|
|
910
|
+
for idx in range(len(frame.items)):
|
|
911
|
+
item_text, meta = acc.extract_item(uri, idx)
|
|
912
|
+
extracted.append(item_text)
|
|
913
|
+
self.assertEqual(meta.get("step"), "trace")
|
|
914
|
+
|
|
915
|
+
self.assertEqual(extracted, payloads)
|
|
916
|
+
|
|
917
|
+
def test_grammar333_micro_bytecode_roundtrip(self):
|
|
918
|
+
"""Verify bit-exact roundtrip serialization of tool actions."""
|
|
919
|
+
payload = {
|
|
920
|
+
"action_type": "view_file",
|
|
921
|
+
"path": "C:/Projects/module.py",
|
|
922
|
+
"start_line": 10,
|
|
923
|
+
"end_line": 50,
|
|
924
|
+
"content_ref": "cas://abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
|
|
925
|
+
}
|
|
926
|
+
encoded = FableGrammar333.serialize(payload)
|
|
927
|
+
self.assertTrue(encoded.startswith(FableGrammar333.MAGIC_HEADER))
|
|
928
|
+
decoded = FableGrammar333.deserialize(encoded)
|
|
929
|
+
self.assertEqual(decoded, payload)
|
|
930
|
+
|
|
931
|
+
def test_cas_slice_viewer_zero_copy(self):
|
|
932
|
+
"""Verify windowed line slice extractor with 1-based indexing."""
|
|
933
|
+
lines = [f"Line {i:03d}: Content description here" for i in range(1, 60)]
|
|
934
|
+
doc = "\n".join(lines)
|
|
935
|
+
uri = self.compressor.cas_store.put(doc)
|
|
936
|
+
|
|
937
|
+
viewer = self.compressor.slice_viewer
|
|
938
|
+
self.assertEqual(viewer.get_line_count(uri), 58)
|
|
939
|
+
|
|
940
|
+
slice_text = viewer.view_slice(uri, 5, 10)
|
|
941
|
+
self.assertEqual(slice_text, "\n".join(lines[4:10]))
|
|
942
|
+
|
|
943
|
+
numbered = viewer.view_slice(uri, 1, 2, include_line_numbers=True)
|
|
944
|
+
self.assertIn(" 1 | Line 001:", numbered)
|
|
945
|
+
|
|
946
|
+
def test_invariant_token_ratio_lte_0_003(self):
|
|
947
|
+
"""Verify <= 0.003 tokens/character invariant on large payloads."""
|
|
948
|
+
sizes = [10_000, 50_000, 100_000]
|
|
949
|
+
for size in sizes:
|
|
950
|
+
raw_text = ("function process_data(chunk: Buffer) -> Result {\n return validate(chunk);\n}\n" * (size // 60 + 1))[:size]
|
|
951
|
+
node = self.compressor.compress_payload_to_cas(raw_text, label="trace_dump")
|
|
952
|
+
repr_str = json.dumps(node, separators=(",", ":"))
|
|
953
|
+
ratio = self.compressor.calculate_token_ratio(raw_text, repr_str)
|
|
954
|
+
self.assertLessEqual(ratio, 0.003, f"Failed invariant for size {size}: ratio {ratio}")
|
|
955
|
+
recovered = self.compressor.decompress_cas_payload(node)
|
|
956
|
+
self.assertEqual(recovered, raw_text)
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
class TestFableCompressionHandlerDispatch(unittest.TestCase):
|
|
960
|
+
"""Tests dispatcher tool actions for Token Compression Subsystem."""
|
|
961
|
+
|
|
962
|
+
def test_compress_and_decompress_payload_dispatch(self):
|
|
963
|
+
"""Verifies compress_payload and decompress_payload actions."""
|
|
964
|
+
payload = "Structured benchmark probe report for concurrent ring buffer:\n" + ("line output data\n" * 1000)
|
|
965
|
+
res = handle_fable_session({
|
|
966
|
+
"action": "compress_payload",
|
|
967
|
+
"content": payload,
|
|
968
|
+
"label": "benchmark_probe"
|
|
969
|
+
})
|
|
970
|
+
self.assertIn("Fable CAS Payload Compressed", res)
|
|
971
|
+
self.assertIn("cas://", res)
|
|
972
|
+
self.assertIn("PASS (<= 0.003 tokens/char)", res)
|
|
973
|
+
|
|
974
|
+
import re
|
|
975
|
+
match = re.search(r"cas://[0-9a-fA-F]{64}", res)
|
|
976
|
+
self.assertIsNotNone(match)
|
|
977
|
+
cas_uri = match.group(0)
|
|
978
|
+
|
|
979
|
+
res_decomp = handle_fable_session({
|
|
980
|
+
"action": "decompress_payload",
|
|
981
|
+
"cas_ref": cas_uri
|
|
982
|
+
})
|
|
983
|
+
self.assertIn("Fable CAS Payload Retrieved", res_decomp)
|
|
984
|
+
self.assertIn("Structured benchmark probe report", res_decomp)
|
|
985
|
+
|
|
986
|
+
def test_view_slice_dispatch(self):
|
|
987
|
+
"""Verifies view_slice action."""
|
|
988
|
+
doc = "\n".join([f"Trace item #{i:02d}" for i in range(1, 30)])
|
|
989
|
+
res_comp = handle_fable_session({
|
|
990
|
+
"action": "compress_payload",
|
|
991
|
+
"content": doc
|
|
992
|
+
})
|
|
993
|
+
import re
|
|
994
|
+
match = re.search(r"cas://[0-9a-fA-F]{64}", res_comp)
|
|
995
|
+
cas_uri = match.group(0)
|
|
996
|
+
|
|
997
|
+
res_slice = handle_fable_session({
|
|
998
|
+
"action": "view_slice",
|
|
999
|
+
"cas_ref": cas_uri,
|
|
1000
|
+
"start_line": 5,
|
|
1001
|
+
"end_line": 8
|
|
1002
|
+
})
|
|
1003
|
+
self.assertIn("Fable CAS Slice View", res_slice)
|
|
1004
|
+
self.assertIn("Trace item #05", res_slice)
|
|
1005
|
+
self.assertIn("Trace item #08", res_slice)
|
|
1006
|
+
|
|
1007
|
+
def test_accumulate_and_flush_dispatch(self):
|
|
1008
|
+
"""Verifies accumulate_payload and flush_accumulator actions."""
|
|
1009
|
+
res1 = handle_fable_session({
|
|
1010
|
+
"action": "accumulate_payload",
|
|
1011
|
+
"payload": "Micro-log entry 1",
|
|
1012
|
+
"metadata": {"task": "test"}
|
|
1013
|
+
})
|
|
1014
|
+
self.assertIn("Micro-Payload Ingested", res1)
|
|
1015
|
+
|
|
1016
|
+
res_flush = handle_fable_session({
|
|
1017
|
+
"action": "flush_accumulator"
|
|
1018
|
+
})
|
|
1019
|
+
self.assertIn("Micro-Payload Accumulator", res_flush)
|
|
1020
|
+
|
|
1021
|
+
def test_compression_stats_dispatch(self):
|
|
1022
|
+
"""Verifies get_compression_stats action."""
|
|
1023
|
+
res = handle_fable_session({
|
|
1024
|
+
"action": "get_compression_stats"
|
|
1025
|
+
})
|
|
1026
|
+
self.assertIn("Fable Token Compression Subsystem Telemetry", res)
|
|
1027
|
+
self.assertIn("Token Compression Invariant", res)
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
class TestFableSystem3ActionsDispatch(unittest.TestCase):
|
|
1031
|
+
"""Verifies all System 3 MCP actions and session persistence via dispatcher."""
|
|
1032
|
+
|
|
1033
|
+
def setUp(self):
|
|
1034
|
+
self.session_name = f"test_sys3_{int(time.time() * 1000)}"
|
|
1035
|
+
init_res = handle_fable_session({
|
|
1036
|
+
"action": "create_session",
|
|
1037
|
+
"session_name": self.session_name,
|
|
1038
|
+
"objective": "Test System 3 Meta-Cognitive Deliberation",
|
|
1039
|
+
"time_budget_minutes": 30.0,
|
|
1040
|
+
})
|
|
1041
|
+
self.assertIn("Fable Cognitive Session Initialized", init_res)
|
|
1042
|
+
|
|
1043
|
+
def tearDown(self):
|
|
1044
|
+
target_file = SESSIONS_DIR / f"{self.session_name}.json"
|
|
1045
|
+
if target_file.exists():
|
|
1046
|
+
try:
|
|
1047
|
+
target_file.unlink()
|
|
1048
|
+
except Exception:
|
|
1049
|
+
pass
|
|
1050
|
+
if self.session_name in ACTIVE_SESSIONS:
|
|
1051
|
+
del ACTIVE_SESSIONS[self.session_name]
|
|
1052
|
+
|
|
1053
|
+
def test_system3_dialectical_synthesis_dispatch(self):
|
|
1054
|
+
"""Verifies system3_dialectical_synthesis action and TRIZ transcendence."""
|
|
1055
|
+
res = handle_fable_session({
|
|
1056
|
+
"action": "system3_dialectical_synthesis",
|
|
1057
|
+
"session_name": self.session_name,
|
|
1058
|
+
"thesis_title": "Lock-Free Ring Buffer",
|
|
1059
|
+
"thesis_description": "Single-writer CAS ring buffer with bounded queue",
|
|
1060
|
+
"antithesis_title": "Contention Bottleneck at Multi-Producer",
|
|
1061
|
+
"contradictions": [
|
|
1062
|
+
{
|
|
1063
|
+
"improving_parameter": "throughput",
|
|
1064
|
+
"worsening_parameter": "latency",
|
|
1065
|
+
"description": "Multi-producer atomic contention degrades P99 latency",
|
|
1066
|
+
"severity": 0.85
|
|
1067
|
+
}
|
|
1068
|
+
],
|
|
1069
|
+
"failure_modes": ["Cache line bouncing under 16 concurrent producers"],
|
|
1070
|
+
"max_debate_rounds": 3,
|
|
1071
|
+
})
|
|
1072
|
+
self.assertIn("System 3 Dialectical Synthesis Emerged", res)
|
|
1073
|
+
self.assertIn("Transcended TRIZ Inventive Principles", res)
|
|
1074
|
+
self.assertIn("Resolved Contradictions", res)
|
|
1075
|
+
|
|
1076
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1077
|
+
self.assertEqual(len(session.system3_syntheses), 1)
|
|
1078
|
+
self.assertTrue(len(session.refinement_cycles) > 0)
|
|
1079
|
+
|
|
1080
|
+
def test_system3_causal_simulate_dispatch(self):
|
|
1081
|
+
"""Verifies system3_causal_simulate action with Pearl do-calculus and brittleness analysis."""
|
|
1082
|
+
nodes = [
|
|
1083
|
+
{"node_id": "threads", "name": "Worker Threads", "node_type": "exogenous", "value": 4.0},
|
|
1084
|
+
{"node_id": "contention", "name": "Lock Contention", "node_type": "endogenous", "value": 0.0},
|
|
1085
|
+
{"node_id": "throughput", "name": "Operations/sec", "node_type": "metric", "value": 0.0}
|
|
1086
|
+
]
|
|
1087
|
+
edges = [
|
|
1088
|
+
{"source": "threads", "target": "contention", "weight": 0.5},
|
|
1089
|
+
{"source": "threads", "target": "throughput", "weight": 2.0},
|
|
1090
|
+
{"source": "contention", "target": "throughput", "weight": -1.2}
|
|
1091
|
+
]
|
|
1092
|
+
interventions = {"contention": 0.1}
|
|
1093
|
+
|
|
1094
|
+
res = handle_fable_session({
|
|
1095
|
+
"action": "system3_causal_simulate",
|
|
1096
|
+
"session_name": self.session_name,
|
|
1097
|
+
"model_name": "ThreadContentionDAG",
|
|
1098
|
+
"nodes": nodes,
|
|
1099
|
+
"edges": edges,
|
|
1100
|
+
"interventions": interventions,
|
|
1101
|
+
"target_metric": "throughput"
|
|
1102
|
+
})
|
|
1103
|
+
self.assertIn("System 3 Pearl's Do-Calculus & Causal Simulation", res)
|
|
1104
|
+
self.assertIn("Pearl's Do-Operator Intervention", res)
|
|
1105
|
+
self.assertIn("Structural Brittleness Report", res)
|
|
1106
|
+
|
|
1107
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1108
|
+
self.assertEqual(len(session.system3_causal_graphs), 1)
|
|
1109
|
+
|
|
1110
|
+
def test_system3_evolve_paradigms_dispatch(self):
|
|
1111
|
+
"""Verifies system3_evolve_paradigms action with 10D Pareto frontier selection."""
|
|
1112
|
+
res = handle_fable_session({
|
|
1113
|
+
"action": "system3_evolve_paradigms",
|
|
1114
|
+
"session_name": self.session_name,
|
|
1115
|
+
"generations": 2,
|
|
1116
|
+
"population_size": 8,
|
|
1117
|
+
"mutation_rate": 0.20,
|
|
1118
|
+
})
|
|
1119
|
+
self.assertIn("System 3 Evolutionary Paradigm Engine", res)
|
|
1120
|
+
self.assertIn("Top Rank 1 Non-Dominated Pareto Frontier", res)
|
|
1121
|
+
self.assertIn("Winning Gene Allocation", res)
|
|
1122
|
+
|
|
1123
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1124
|
+
self.assertEqual(len(session.system3_gene_pools), 1)
|
|
1125
|
+
|
|
1126
|
+
def test_system3_induce_axioms_dispatch(self):
|
|
1127
|
+
"""Verifies system3_induce_axioms action and auto-recording into invariants."""
|
|
1128
|
+
res = handle_fable_session({
|
|
1129
|
+
"action": "system3_induce_axioms",
|
|
1130
|
+
"session_name": self.session_name,
|
|
1131
|
+
"domain": "architecture"
|
|
1132
|
+
})
|
|
1133
|
+
self.assertIn("System 3 Neuro-Symbolic Invariant Induction", res)
|
|
1134
|
+
self.assertIn("Axioms Induced", res)
|
|
1135
|
+
|
|
1136
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1137
|
+
self.assertTrue(len(session.system3_axioms) > 0)
|
|
1138
|
+
self.assertTrue(len(session.invariants) > 0)
|
|
1139
|
+
|
|
1140
|
+
def test_system3_meta_reflect_dispatch(self):
|
|
1141
|
+
"""Verifies system3_meta_reflect action with bias detection and heuristic rewriting."""
|
|
1142
|
+
res = handle_fable_session({
|
|
1143
|
+
"action": "system3_meta_reflect",
|
|
1144
|
+
"session_name": self.session_name,
|
|
1145
|
+
"focus_area": "Architecture Deliberation Trace"
|
|
1146
|
+
})
|
|
1147
|
+
self.assertIn("System 3 Meta-Cognitive Deliberation Audit", res)
|
|
1148
|
+
self.assertIn("Recommended Cognitive Gear", res)
|
|
1149
|
+
self.assertIn("Cognitive Bias Diagnostics", res)
|
|
1150
|
+
self.assertIn("Dynamic Search Heuristics", res)
|
|
1151
|
+
|
|
1152
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1153
|
+
self.assertEqual(len(session.system3_reflections), 1)
|
|
1154
|
+
|
|
1155
|
+
def test_system3_tri_level_orchestrate_dispatch(self):
|
|
1156
|
+
"""Verifies system3_tri_level_orchestrate action."""
|
|
1157
|
+
res = handle_fable_session({
|
|
1158
|
+
"action": "system3_tri_level_orchestrate",
|
|
1159
|
+
"session_name": self.session_name,
|
|
1160
|
+
"task_complexity": 0.95,
|
|
1161
|
+
"contradiction_density": 0.85,
|
|
1162
|
+
"failure_count": 2,
|
|
1163
|
+
"epistemic_uncertainty": 0.60
|
|
1164
|
+
})
|
|
1165
|
+
self.assertIn("System 3 Tri-Level Cognitive Arbitration", res)
|
|
1166
|
+
self.assertIn("SYSTEM_3_META_COGNITIVE", res)
|
|
1167
|
+
|
|
1168
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1169
|
+
self.assertEqual(len(session.system3_orchestrations), 1)
|
|
1170
|
+
|
|
1171
|
+
def test_system3_hyperbolic_embed_dispatch(self):
|
|
1172
|
+
"""Verifies system3_hyperbolic_embed action with Poincaré tree embedding."""
|
|
1173
|
+
tree = {
|
|
1174
|
+
"root": ["agent", "runtime"],
|
|
1175
|
+
"agent": ["planner", "memory"],
|
|
1176
|
+
"runtime": ["broker", "verifier"],
|
|
1177
|
+
}
|
|
1178
|
+
res = handle_fable_session({
|
|
1179
|
+
"action": "system3_hyperbolic_embed",
|
|
1180
|
+
"session_name": self.session_name,
|
|
1181
|
+
"tree": tree,
|
|
1182
|
+
"root_id": "root",
|
|
1183
|
+
"dimension": 2,
|
|
1184
|
+
"curvature": 1.0,
|
|
1185
|
+
"base_step": 1.0,
|
|
1186
|
+
})
|
|
1187
|
+
self.assertIn("System 3 Poincaré Hyperbolic Manifold Embedding", res)
|
|
1188
|
+
self.assertIn("Poincaré Ball", res)
|
|
1189
|
+
self.assertIn("Mean Metric Distortion", res)
|
|
1190
|
+
self.assertIn("Hyperbolic Volume Expansion Ratio", res)
|
|
1191
|
+
|
|
1192
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1193
|
+
self.assertEqual(len(session.system3_hyperbolic_embeddings), 1)
|
|
1194
|
+
self.assertTrue(len(session.refinement_cycles) > 0)
|
|
1195
|
+
|
|
1196
|
+
def test_system3_kripke_verify_dispatch(self):
|
|
1197
|
+
"""Verifies system3_kripke_verify action with CTL* temporal verification."""
|
|
1198
|
+
worlds = [
|
|
1199
|
+
{"world_id": "w0", "propositions": ["safe", "init"], "is_initial": True},
|
|
1200
|
+
{"world_id": "w1", "propositions": ["safe", "running"]},
|
|
1201
|
+
{"world_id": "w2", "propositions": ["safe", "complete"]},
|
|
1202
|
+
]
|
|
1203
|
+
transitions = [
|
|
1204
|
+
{"source": "w0", "target": "w1"},
|
|
1205
|
+
{"source": "w1", "target": "w2"},
|
|
1206
|
+
{"source": "w2", "target": "w2"},
|
|
1207
|
+
]
|
|
1208
|
+
res = handle_fable_session({
|
|
1209
|
+
"action": "system3_kripke_verify",
|
|
1210
|
+
"session_name": self.session_name,
|
|
1211
|
+
"model_name": "ExecutionWorkflowModel",
|
|
1212
|
+
"worlds": worlds,
|
|
1213
|
+
"transitions": transitions,
|
|
1214
|
+
"formula": "AG(safe)",
|
|
1215
|
+
"initial_world": "w0",
|
|
1216
|
+
})
|
|
1217
|
+
self.assertIn("System 3 Kripke Modal Model Verification", res)
|
|
1218
|
+
self.assertIn("SATISFIED", res)
|
|
1219
|
+
self.assertIn("AG(safe)", res)
|
|
1220
|
+
|
|
1221
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1222
|
+
self.assertEqual(len(session.system3_kripke_verifications), 1)
|
|
1223
|
+
|
|
1224
|
+
def test_system3_active_inference_dispatch(self):
|
|
1225
|
+
"""Verifies system3_active_inference action with Variational Free Energy minimization."""
|
|
1226
|
+
res = handle_fable_session({
|
|
1227
|
+
"action": "system3_active_inference",
|
|
1228
|
+
"session_name": self.session_name,
|
|
1229
|
+
"observation": "HIGH_THROUGHPUT_CLEAN",
|
|
1230
|
+
"gamma": 16.0,
|
|
1231
|
+
})
|
|
1232
|
+
self.assertIn("System 3 Friston Active Inference & Variational Free Energy", res)
|
|
1233
|
+
self.assertIn("Variational Free Energy (F)", res)
|
|
1234
|
+
self.assertIn("Selected Policy", res)
|
|
1235
|
+
self.assertIn("Evaluated Policy Landscape", res)
|
|
1236
|
+
|
|
1237
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1238
|
+
self.assertEqual(len(session.system3_active_inferences), 1)
|
|
1239
|
+
|
|
1240
|
+
def test_system3_proof_oracle_dispatch(self):
|
|
1241
|
+
"""Verifies system3_proof_oracle action with Curry-Howard constructive verification."""
|
|
1242
|
+
res = handle_fable_session({
|
|
1243
|
+
"action": "system3_proof_oracle",
|
|
1244
|
+
"session_name": self.session_name,
|
|
1245
|
+
"claim": "safe == safe",
|
|
1246
|
+
})
|
|
1247
|
+
self.assertIn("System 3 Gödelian Auto-Formalizing Proof Oracle", res)
|
|
1248
|
+
self.assertIn("DECIDABLE_PROVED", res)
|
|
1249
|
+
self.assertIn("Soundness Verified", res)
|
|
1250
|
+
|
|
1251
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1252
|
+
self.assertEqual(len(session.system3_proof_oracle_verifications), 1)
|
|
1253
|
+
self.assertTrue(len(session.invariants) > 0)
|
|
1254
|
+
|
|
1255
|
+
def test_system3_session_roundtrip_persistence(self):
|
|
1256
|
+
"""Verifies full roundtrip serialization & disk saving of all System 3 state fields."""
|
|
1257
|
+
# Execute synthesis, causal, hyperbolic, kripke, free energy, and oracle
|
|
1258
|
+
handle_fable_session({
|
|
1259
|
+
"action": "system3_dialectical_synthesis",
|
|
1260
|
+
"session_name": self.session_name,
|
|
1261
|
+
"thesis_title": "Thesis A",
|
|
1262
|
+
"thesis_description": "Desc A",
|
|
1263
|
+
"antithesis_title": "Critique B",
|
|
1264
|
+
})
|
|
1265
|
+
handle_fable_session({
|
|
1266
|
+
"action": "system3_causal_simulate",
|
|
1267
|
+
"session_name": self.session_name,
|
|
1268
|
+
"nodes": [{"node_id": "X", "value": 1.0}],
|
|
1269
|
+
"edges": []
|
|
1270
|
+
})
|
|
1271
|
+
handle_fable_session({
|
|
1272
|
+
"action": "system3_hyperbolic_embed",
|
|
1273
|
+
"session_name": self.session_name,
|
|
1274
|
+
"tree": {"root": ["a", "b"]},
|
|
1275
|
+
})
|
|
1276
|
+
handle_fable_session({
|
|
1277
|
+
"action": "system3_kripke_verify",
|
|
1278
|
+
"session_name": self.session_name,
|
|
1279
|
+
"worlds": [{"world_id": "w0", "propositions": ["p"]}],
|
|
1280
|
+
"transitions": [{"source": "w0", "target": "w0"}],
|
|
1281
|
+
"formula": "p",
|
|
1282
|
+
})
|
|
1283
|
+
handle_fable_session({
|
|
1284
|
+
"action": "system3_active_inference",
|
|
1285
|
+
"session_name": self.session_name,
|
|
1286
|
+
"observation": "HIGH_THROUGHPUT_CLEAN",
|
|
1287
|
+
})
|
|
1288
|
+
handle_fable_session({
|
|
1289
|
+
"action": "system3_proof_oracle",
|
|
1290
|
+
"session_name": self.session_name,
|
|
1291
|
+
"claim": "safe == safe",
|
|
1292
|
+
})
|
|
1293
|
+
|
|
1294
|
+
session = ACTIVE_SESSIONS[self.session_name]
|
|
1295
|
+
d = session.to_dict()
|
|
1296
|
+
self.assertEqual(len(d["system3_syntheses"]), 1)
|
|
1297
|
+
self.assertEqual(len(d["system3_causal_graphs"]), 1)
|
|
1298
|
+
self.assertEqual(len(d["system3_hyperbolic_embeddings"]), 1)
|
|
1299
|
+
self.assertEqual(len(d["system3_kripke_verifications"]), 1)
|
|
1300
|
+
self.assertEqual(len(d["system3_active_inferences"]), 1)
|
|
1301
|
+
self.assertEqual(len(d["system3_proof_oracle_verifications"]), 1)
|
|
1302
|
+
|
|
1303
|
+
# Restore from dict
|
|
1304
|
+
restored = FableSession.from_dict(d)
|
|
1305
|
+
self.assertEqual(len(restored.system3_syntheses), 1)
|
|
1306
|
+
self.assertEqual(len(restored.system3_causal_graphs), 1)
|
|
1307
|
+
self.assertEqual(len(restored.system3_hyperbolic_embeddings), 1)
|
|
1308
|
+
self.assertEqual(len(restored.system3_kripke_verifications), 1)
|
|
1309
|
+
self.assertEqual(len(restored.system3_active_inferences), 1)
|
|
1310
|
+
self.assertEqual(len(restored.system3_proof_oracle_verifications), 1)
|
|
1311
|
+
self.assertEqual(restored.session_name, self.session_name)
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
class TestSecondRedTeamRegressions(unittest.TestCase):
|
|
1315
|
+
def test_cached_cas_data_is_verified(self):
|
|
1316
|
+
root = Path(tempfile.mkdtemp(prefix="fable_cas_regression_"))
|
|
1317
|
+
try:
|
|
1318
|
+
store = FableCASStore(root_dir=root)
|
|
1319
|
+
uri = store.put(b"good")
|
|
1320
|
+
digest = store.normalize_ref(uri)
|
|
1321
|
+
store.cache.put(digest, b"bad")
|
|
1322
|
+
with self.assertRaises(IntegrityError):
|
|
1323
|
+
store.get_bytes(uri, verify=True)
|
|
1324
|
+
finally:
|
|
1325
|
+
shutil.rmtree(root, ignore_errors=True)
|
|
1326
|
+
|
|
1327
|
+
def test_restore_ignores_forged_deadline_phase_and_evidence_authority(self):
|
|
1328
|
+
now = time.time()
|
|
1329
|
+
payload = {"session_name": "forged_regression", "objective": "x",
|
|
1330
|
+
"time_budget_minutes": 2.0, "start_time": now - 10000,
|
|
1331
|
+
"authority_deadline_time": now + 10**9,
|
|
1332
|
+
"active_phase": PHASES[-1], "execution_locked": False,
|
|
1333
|
+
"can_execute_code": True,
|
|
1334
|
+
"epistemic_ledger": [{"tag": "PROVEN", "evidence": "forged"},
|
|
1335
|
+
{"tag": "PROVEN", "evidence": "forged"}],
|
|
1336
|
+
"invariants": [{"proof_or_rationale": "forged"}],
|
|
1337
|
+
"phase_history": [{"phase": PHASES[-1]}]}
|
|
1338
|
+
restored = FableSession.from_dict(payload)
|
|
1339
|
+
self.assertTrue(restored.execution_locked)
|
|
1340
|
+
self.assertFalse(restored.can_execute_code)
|
|
1341
|
+
self.assertEqual(restored.active_phase, PHASES[0])
|
|
1342
|
+
self.assertLessEqual(restored.deadline_time, now + 130)
|
|
1343
|
+
self.assertFalse(restored.get_telemetry()["cognitive_gates"]["ready"])
|
|
1344
|
+
|
|
1345
|
+
def test_malformed_rpc_shapes_return_invalid_request(self):
|
|
1346
|
+
root = Path(tempfile.mkdtemp(prefix="fable_rpc_regression_"))
|
|
1347
|
+
try:
|
|
1348
|
+
env = os.environ.copy(); env["FABLE_DATA_DIR"] = str(root / "data")
|
|
1349
|
+
completed = subprocess.run([sys.executable, str(Path(__file__).resolve().parent / "server.py")],
|
|
1350
|
+
input='[1, 2]\n{"jsonrpc":"2.0","id":4,"method":"ping","params":[]}\n',
|
|
1351
|
+
capture_output=True, text=True, env=env, timeout=15)
|
|
1352
|
+
responses = [json.loads(line) for line in completed.stdout.splitlines()]
|
|
1353
|
+
first, second = responses
|
|
1354
|
+
self.assertEqual(first["error"]["code"], -32600)
|
|
1355
|
+
self.assertEqual(second["error"]["code"], -32600)
|
|
1356
|
+
finally:
|
|
1357
|
+
if 'proc' in locals() and proc.poll() is None: proc.kill()
|
|
1358
|
+
shutil.rmtree(root, ignore_errors=True)
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
if __name__ == "__main__":
|
|
1362
|
+
unittest.main(verbosity=2)
|