Lens: epistemic architecture — Fractal
- Run/result ID:
AAS-2026-08-20-fractal-01 - Lens: epistemic architecture (knowledge production, checking, warrant, authority)
- Invoked method:
kb/instructions/analyse-external-system-epistemic-architecture.md(six required output blocks, steps 1–9) - System: Fractal —
github.com/Trampoline-AI/fractal, PyPIfractal-rlm - Frozen revision:
5954a07d464feeaf6c311a9fa5ca2e54200a6794(authored 2026-06-23); analysis cutoff 2026-08-20 - Source register: this lens cites the shared
SRC-*register defined in the run's evidence packet (trials/fractal/evidence-packet.md§2). No source was reacquired, refreshed, or widened. All reads were targeted reads inside the already-declared boundary. - Execution status: nothing was run. No
observed runand nocausal experimentevidence exists in this run, so no observed candidate state aboveno instance observed/not determinableis available anywhere in block 4. - ID discipline: existing
CMP-*/OBJ-*/RTE-*/BAP-*/CLM-*records are extended by ID. Ledger rows are written as extensions (RTE-nn.En). Genuinely new material records carry provisional placeholders[NEW-n]in the body and are listed for canonical assignment in the final section; no canonical ID is minted here.
Bounded epistemic subquestion: within Fractal's inspected host layer at this revision, which routes produce, check, dispose, retain, or integrate truth-apt content, and what do Fractal's own knowledge-production and warrant claims (CLM-01…CLM-13) actually rest on — given that the inner RLM loop (CMP-12, SRC-8) is uninspected and no run was observed?
1. Source-and-claim boundary
| Field | Value |
|---|---|
| System | Fractal — terminal CLI coding agent; a host layer over predict-rlm ("self-harnessed Recursive Language Model"). |
| Reviewed revision/version | 5954a07d (host layer, SRC-1…SRC-7, SRC-9); predict-rlm 0.7.0 declared but not obtained (SRC-8). |
| Declared scope | The fractal Python package as checked out: CLI/mode select, TUI loop, turn coordinator, PredictRLM adapter, per-turn prompt assembly, session store, host-side action observation, config/provider/credential stack, context meter, shipped instruction artifacts, project doctrine. Actors in scope: human operator, main LM, sub-LM, external calling agent. |
| Excluded components | predict-rlm 0.7.0 (CMP-12) — the RLM iteration/recursion loop, code generation, sub-LM spawning, RunTrace production, RuntimeHook injection, Skill handling, Workspace semantics; sbx/Docker Sandboxes (CMP-13); dspy, litellm, tiktoken, all provider services. Boundary kind is subsystem-only with respect to deployed agentic behavior. |
| Analysis question | See bounded subquestion above. |
| Assessed route families | Per-turn context assembly and content transformation (RTE-06, RTE-07, RTE-09); retained-state write, trim, and serialization (RTE-10, RTE-11, RTE-19); host-side observation of file/command actions (RTE-13); usage derivation from trace (RTE-14); turn-outcome classification and recovery persistence (RTE-05, RTE-16, RTE-04); output production and consumption (RTE-15, RTE-20); operational admission via config/model policy (RTE-17); pre-turn context estimation (host-layer, CMP-09). |
| Unassessed route families | (a) The entire inner RLM loop — iteration, recursion, sub-call spawning, in-loop context management, code generation and its own checking, RunTrace construction, hook emission (CMP-12, SRC-8). (b) Sandbox mount/network enforcement (CMP-13, SRC-8). (c) Action execution against mounted host paths (RTE-12) — the host layer only names the mounts. (d) Test-suite content (SRC-7, listing only) — no conclusion about what Fractal actually verifies about itself. (e) Line-level behavior of providers.py, onboarding.py, credentials.py, config_commands.py, connectivity.py, runtime_lms.py, version_check.py (symbol-level greps only). Omission of (a)–(c) prevents any whole-system conclusion about how Fractal's answers are actually produced, whether the trace faithfully records production, and whether execution is contained. |
| Source register | SRC-1 host source @ 5954a07d, implementation · SRC-2 README.md, doctrine/design · SRC-3 docs/session-management.md, docs/headless.md, doctrine/design · SRC-4 AGENTS.md, doctrine/design · SRC-5 shipped skills, implementation-as-artifact / doctrine-as-content · SRC-6 pyproject.toml, uv.lock, implementation · SRC-7 tests/**, implementation, listing only · SRC-8 predict-rlm 0.7.0, not inspected, no evidence layer · SRC-9 docs/predict-rlm-notes.md, absent within boundary. |
| System knowledge-production / warrant claims | Present and consequential: CLM-01…CLM-13 (SRC-2, SRC-3, SRC-4, SRC-5, and doctrine embedded in SRC-1). The load-bearing ones for this lens are CLM-02 (no context rot), CLM-03 (distilled answer), CLM-05 (host-side truth over model-reported truth), CLM-06 (TurnUsage trustworthy), CLM-08 (self-declared caveat: changed files coerced from model output), CLM-09 (trace fully readable), CLM-11/CLM-13 (summary preserves prior content). |
Missing evidence → conclusion prevented
Consolidated in the closing table (§ "Missing evidence → conclusion prevented"). The single largest gap: SRC-8 is not obtained, so every statement about what happens between FractalAgent.aforward's call and its returned dspy.Prediction is uninspected.
2. Epistemic-object inventory
Rows extend the registered OBJ-* records; the added columns are this lens's. Objects are split where content, checks, producers/consumers, or authority paths differ.
| Object ID | System name and description | Representational form | Source/input and lineage | Producer → consumer | Candidate truth-apt content | Claimed role | Evidence (SRC + anchor) | Gap/limit |
|---|---|---|---|---|---|---|---|---|
OBJ-01 |
user_message — the turn's request |
natural language | external: operator keystrokes, -p argv, or piped stdin |
operator / external agent → CMP-03 → CMP-05 prompt field, CMP-06 retained record |
Yes — an imported directive that may embed truth-apt assertions about the workspace; imported, not produced | overriding directive (BAP-06) |
SRC-1 runtime.py:222, signature.py:87; SRC-1 cli.py:554-569 |
Source warrant is the operator's; Fractal applies no check |
OBJ-02 |
rendered session summary (prompt text) | natural language, baked into the signature docstring | derived from OBJ-03 by render_session_summary |
CMP-06 → CMP-05 docstring → main LM (BAP-03) |
Yes — asserts, per turn, what was asked, what status resulted, what the agent answered, and how many files/commands were touched | "compressed structured trajectory context… preserves prior user messages and compressed agent results" (CLM-11) |
SRC-1 session.py:368-390, signature.py:54-64 |
Never trimmed (RTE-11 caps history only), and the agent response is copied verbatim, not compressed — this channel grows monotonically with session length |
OBJ-03 |
SessionSummary/SummaryTurn/AgentTurn structured record |
typed pydantic → JSON | OBJ-01 verbatim; OBJ-07 verbatim; counts computed from OBJ-08 and OBJ-09; OBJ-06 |
CMP-06 → disk, render_session_summary, /usage |
Yes — status, response, and three counts | durable structured trajectory | SRC-1 session.py:56-74, 221-262 |
files_changed_count counts the model-reported list (session.py:239, 245), not the host-observed one |
OBJ-04 |
session_history: list[SessionHistoryTurn] |
typed pydantic delivered as a REPL variable | verbatim retention of OBJ-01, OBJ-05, OBJ-08, OBJ-09, error strings |
CMP-06 → main LM on the model's own initiative (BAP-04) |
Yes — per-turn file lists, command lists, trace, error | "exact recall" channel | SRC-1 session.py:76-87, 197-201, 278-279, signature.py:90-95 |
Capped at the last 20 turns; files_modified here is model-reported (see OBJ-08) |
OBJ-05 |
RunTrace — REPL reasoning, code, output, sub/predict calls, usage, status |
typed object produced outside the boundary | produced by CMP-12 (SRC-8, uninspected) |
CMP-12 → CMP-03 → CMP-06 (persisted inside OBJ-04) → main LM on request; CMP-09 never |
Yes — asserts what the loop did | "every peek, chunk, sub-call, and verification step is fully readable" (CLM-09) |
SRC-1 session.py:83, 259, runtime.py:301, 326-346 |
Contents and fidelity uninspected. On exception paths the trace is recovered by extract_trace_from_exc and may be None; _validate_run_trace swallows ValueError and returns None silently (runtime.py:342-346) |
OBJ-06 |
TurnUsage — tokens, cost, duration, iterations, live context size |
typed numeric | arithmetic over OBJ-05's usage fields |
CMP-06 → session JSON, /usage, stderr line, OBJ-15 |
Yes — quantitative claims about the turn | "derived from the RunTrace, not from model output, so it stays trustworthy" (CLM-06) |
SRC-1 session.py:38-53, 306-322, 285-303 |
Warrant is inherited wholesale from the uninspected OBJ-05; None whenever no trace is attached; not rendered into OBJ-02 |
OBJ-07 |
agent response — the user-facing deliverable |
natural language, model-produced | produced by CMP-12 from workspace evidence gathered inside the sandbox |
CMP-12 → CMP-04 → CMP-03 → stdout/TUI, session JSON, OBJ-02, OBJ-15 |
Yes — the primary truth-apt product. Audits, traces, root-cause findings, syntheses | "a distilled answer" (CLM-03) |
SRC-1 agent/schema.py:13, service.py:200-217, session.py:58, cli.py:523-526; SRC-5 SKILL.md:36-51 |
Its production process is entirely uninspected; no route in the host layer checks or disposes it (see RTE-15.E3) |
OBJ-08 |
changed_files — model's list of files it changed |
list[str], model-produced |
dspy output field changed_files |
CMP-12 → CMP-04 → persisted as history_turn.files_modified and as files_changed_count; printed to stderr; carried in OBJ-15 |
Yes — asserts which host files were modified | "relative paths of files changed under the workspace path" | SRC-1 signature.py:103-105, service.py:205-208, session.py:239, 245, 257, cli.py:528-532 |
Self-declared mismatch with CLM-05 (CLM-08). The host has an independent measurement of the same fact and discards it — see RTE-13.E2 |
OBJ-09a |
RuntimeEventTracker.files_read and .commands_run — host-recorded |
list[str] |
Python-level runtime hooks on 12 file APIs + 5 subprocess APIs | CMP-07 → CMP-03 → OBJ-03 counts, OBJ-04 lists |
Yes — asserts which paths were read and which commands were launched | host-side truth (CLM-05) |
SRC-1 events.py:87, 89, 102-118, 215-219; runtime.py:233-234, 267-268, 291-292, 306-307, 317-318 |
Records only what crosses the hooked Python APIs; file effects of subprocesses are invisible to the file hooks. Requires CMP-12 hook injection (SRC-8, uninspected) — SRC-3 "Known Limits" says tracking works only when the backend supports runtime hook events. Adapter exceptions are swallowed (runtime.py:202-206) so under-recording is silent. files_read is recorded at hook after phase for path-bearing calls, i.e. "opened/read", not "content consumed" |
OBJ-09b |
RuntimeEventTracker.files_modified — host-recorded write targets |
list[str] |
same hook stream, write-mode branch | CMP-07 → no consumer |
Yes — asserts which paths were opened for writing | (none declared) | SRC-1 events.py:88, 219; absence confirmed by exhaustive grep of src/** for files_modified |
Computed and discarded. No call site outside events.py reads it. Also means "opened for write", not "content differs" |
OBJ-10 |
workspace files (+ --include dirs) |
arbitrary files, mounted read/write | mutated by generated code inside CMP-13 |
CMP-12/CMP-13 → host filesystem |
Yes — but no host-layer route produces or changes them | the actual work product | SRC-1 agent/service.py:186-197; SRC-2 "How it works" |
Mutation path is RTE-12, entirely outside the boundary. .fractal is excluded from the workspace (service.py:79-80) |
OBJ-11 |
workspace AGENTS.md |
natural language, user-authored | read fresh from disk each turn, truncated at 20 000 chars | CMP-04 → CMP-05 prompt section → main LM (BAP-02) |
Mixed: directive plus truth-apt project assertions; imported | project guidance, subordinate to user_message |
SRC-1 agent/service.py:28-43, signature.py:42-50 |
Truncation is silent to the model except for an appended marker; source warrant is the workspace author's and is not checked |
OBJ-12 |
shipped skill instruction texts (filesystem-coding, spreadsheet, pdf, docx) |
natural language, static constants | authored in-repo / shipped by predict-rlm |
CMP-10/CMP-12 → main LM (BAP-05) |
Predominantly technique directives; incidental truth-apt claims about Python/rg APIs |
prescriptive technique guidance | SRC-1 agent/skills.py:1-315, service.py:96 |
Static shipped material — retained state, not memory read-back. No route produces, checks, or updates it at run time |
OBJ-13 |
session JSON file, schema_version: 1 |
JSON on disk | serialization of OBJ-03 + OBJ-04 |
CMP-06 ↔ disk |
Yes, as a container of the above | durable session state | SRC-1 session.py:187-192, 435-471 |
Path is the global state dir, contradicting three doctrine artifacts (CONF-1) |
OBJ-14 |
effective config + credentials | TOML + env + keyring/env refs | layered global < project < env < flags | CMP-08 → CMP-03, CMP-04 |
No — policy/configuration, not truth-apt | operational selection | SRC-1 config.py:256-310, providers.py:107-113, 657-660 |
Line-level behavior only grepped |
OBJ-15 |
HeadlessResult JSON envelope |
JSON on stdout | reshaping of OBJ-07, OBJ-08, OBJ-06, plus a status recomputed from OBJ-05 |
CMP-01 → external calling agent / script (BAP-11) |
Yes — inherits OBJ-07/OBJ-08 truth-aptness |
"the public CLI output contract" | SRC-1 session.py:325-365, cli.py:512-522 |
Carries the model-reported changed_files by design (session.py:330-332); usage is None when no trace |
OBJ-16 |
FractalRuntimeEvent live stream |
operator-facing status lines | derived from the same hook stream as OBJ-09a |
CMP-07 → operator (stderr/TUI) |
Yes — asserts an action is starting/failing | live progress display | SRC-1 events.py:61-69, 190-234; cli.py:466 |
Ephemeral, unretained; after-phase file/command events are suppressed from display (events.py:196-197, 220-221) |
OBJ-17 |
pre-turn context-token estimate | integer (or None) |
reconstruction of the next call's messages via PredictRLM private APIs, then litellm/tiktoken counting |
CMP-09 → TUI toolbar only |
Yes — predicts the next turn's prompt size | operator-facing context meter | SRC-1 context_meter.py:56-58, 61-121, 124-168; consumers: tui/app.py:434, 442 only |
Fidelity to the real call is not established: private _prepare_file_io/_build_variables belong to CMP-12 (SRC-8); user_message is deliberately empty; tokenizer silently falls back through three encodings and may return None. Never computed in headless mode, and no route consumes it to change behavior |
Omitted route classes and what their omission prevents: version-check networking (CMP-14) and onboarding/credential UX are excluded as non-material to truth-apt content; their omission prevents no conclusion in scope. SRC-7 test bodies are unread, which prevents any conclusion about self-verification. RTE-12/CMP-13 omission prevents any conclusion about whether OBJ-10 mutations are contained or correct.
3. Authority-route ledger
One functional kind per row. Rows are extensions of registered RTE-* IDs; [NEW-n] marks a provisional placeholder listed in the final section.
3a. Content transformation
| Route ID | Function | Architectural status | Object/candidate | Content/update relation | Transition/check target | Evaluator/condition + domain | Activation and timing | Possible result | Implemented force | Epistemic authority + scope | Operational authority | Behavioral-authority path (consumer, channel, force, horizon) | Evidence | Claim IDs | Mismatch | Gap/limit |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
RTE-06.E1 |
content transformation | implemented |
OBJ-03 → OBJ-02 |
non-ampliative reshaping (lossy selection + reformat) — user message verbatim, status verbatim, response verbatim, three lists replaced by counts, TurnUsage and RunTrace dropped entirely, turn ordering preserved |
n/a — no check | none | every turn, before the RLM call | rendered text or "No prior Fractal session context." |
n/a | Licenses reliance only on: the turn sequence, the exact prior user messages, the prior response strings, and three integers. Does not preserve which files/commands, nor usage |
none directly; sets what the main LM sees by default | main LM · signature docstring · informative, unenforced (BAP-03) · the turn, content spanning the whole session |
SRC-1 session.py:368-390 |
CLM-11, CLM-13 |
partial mismatch with CLM-11 — "compressed agent results" holds for file/command facts (list→count) but the response is copied verbatim and is not compressed |
Summary is never trimmed, so this channel grows without bound; the verbatim response re-enters the prompt unchecked each turn |
RTE-06.E2 |
content transformation | implemented |
OBJ-11 → prompt section |
acquisition/import; source warrant = workspace author's, preserved as-is, unchecked; plus a truncation reshaping at 20 000 chars | n/a | none | every turn, fresh read | text, "" on OSError/UnicodeDecodeError |
n/a | Licenses nothing about the file's correctness; only that the workspace root contained this text at read time | none | main LM · prompt section · directive, explicitly subordinate to user_message, unenforced (BAP-02) · the turn |
SRC-1 agent/service.py:28-43, signature.py:42-50 |
— | none | Read failure is silent (empty string); truncation marker is the only signal |
RTE-11.E1 |
content transformation | implemented |
OBJ-04 |
non-ampliative reshaping (deletion) — keep the last 20 turns | n/a | none | on save, on payload build, on append | trimmed list | n/a | After 20 turns, exact-recall claims about earlier turns are no longer available from this object | limits what the model can retrieve on its own initiative | main LM · REPL variable · informative, opt-in (BAP-04) · the turn |
SRC-1 session.py:23, 197-201, 218, 262, 278-279 |
— | none | Trace, file lists, and command lists for older turns are irrecoverably dropped; only counts survive in OBJ-03 |
RTE-14.E1 |
content transformation | implemented (host arithmetic) over an uninspected input |
OBJ-05 → OBJ-06 |
entailed derivation (sums of main+sub tokens/cost; direct copies of duration_ms, iterations; context_tokens = last step with non-zero main input_tokens) — from imported premises whose warrant is uninspected |
n/a | none | on every add_agent_turn and in headless output |
TurnUsage or None |
n/a | The arithmetic is warranted; the numbers are warranted only to the extent OBJ-05 is, which is not established |
none | operator · stderr//usage/toolbar · informative · session |
SRC-1 session.py:306-322 |
CLM-06 |
none in the host arithmetic | Yields None whenever no trace was attached, including on many failed/interrupted turns |
RTE-14.E2 |
content transformation | implemented |
OBJ-03 → session usage totals |
entailed derivation for the four summed fields; selection, not aggregation, for context_tokens (last non-zero in turn order) |
n/a | none | /usage, session close |
TurnUsage |
n/a | Session totals are warranted arithmetic over per-turn records; context_tokens warrants only "the most recent turn that reported one" |
none | operator · /usage · informative · session |
SRC-1 session.py:285-303 |
CLM-06 |
none | Docstring's rationale ("the RLM re-summarizes between turns") is a claim about CMP-12, uninspected |
RTE-15.E2 |
content transformation | implemented |
OBJ-07+OBJ-08+OBJ-06+status → OBJ-15 |
non-ampliative reshaping (envelope construction; no content altered) | n/a | none | headless --json only |
JSON object | n/a | Licenses only that these were the turn's returned values | n/a | external caller · stdout JSON · enforcing on the caller's control flow (BAP-11) · the invocation |
SRC-1 session.py:346-365, cli.py:512-522 |
CLM-03 |
none | Deliberately carries model-reported changed_files (session.py:330-332) |
[NEW-1] |
content transformation | implemented (host reconstruction) over uninspected internals |
→ OBJ-17 |
indeterminate — a reconstruction of a future prompt plus a token count; whether it preserves the real call's message set cannot be established without SRC-8 |
n/a | none | TUI toolbar refresh only, cache-keyed on workspace/includes/session digest/model/max_iterations/AGENTS.md stat |
integer or None |
n/a | Licenses an estimate of the next prompt size, not the actual size | none — no route consumes it to trim, warn-and-block, or compact | operator · TUI toolbar · advisory · until the next refresh | SRC-1 context_meter.py:30-121, 124-168; consumers tui/app.py:434, 442 |
CLM-02 |
none asserted, but see RTE-06.E1: the meter measures a growth the system does not bound |
Calls CMP-12 private methods; empty user_message by design; three-level tokenizer fallback; absent in headless mode |
3b. Check / evidence production
| Route ID | Function | Architectural status | Object/candidate | Content/update relation | Check target | Evaluator/condition + domain | Activation and timing | Possible result | Implemented force | Epistemic authority + scope | Operational authority | Behavioral-authority path | Evidence | Claim IDs | Mismatch | Gap/limit |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
RTE-13.E1 |
check/evidence production | implemented in the host reducer; the hook-emission half is uninspected (CMP-12) |
→ OBJ-09a, OBJ-16 |
truth-apt transformation: acquisition/import (measurement of an environment fact via interposition) + non-ampliative normalization (dedupe, fd→path mapping, compound-call collapse, command quoting) | Target: which host paths were opened/read and which subprocess commands were launched during this turn | Evaluator: the Python-level interposition itself — a mechanical observer, domain = calls crossing the 12 file / 5 subprocess hooked targets inside the interpreter | continuously during the turn, event-driven | appended path/command strings; a display event; or None |
Consumed by RTE-10.E2 into OBJ-03 counts and OBJ-04 lists; displayed live |
Licenses "these hooked calls occurred". Does not license "these are all the files read", "the file content was actually used", or "the command succeeded" | none — nothing is blocked or gated on these facts | operator · live stderr/TUI lines · informative · the turn · | main LM · session_history REPL variable · informative, opt-in · later turns |
SRC-1 events.py:94-234, 277-292; runtime.py:200-212; SRC-3 "Known Limits" |
CLM-05 |
none for files_read/commands_run |
RTE-13.E2 |
check/evidence production — evidenced discard | implemented (production) + no route found within boundary (consumption) |
OBJ-09b |
acquisition/import, then not consumed | Target: which host paths were opened for writing | same mechanical interposition | during the turn | files_modified list is populated |
no implemented force — a recorded result with no consequential consumer | Licenses nothing downstream: nothing reads it | none | none | SRC-1 events.py:88, 219; exhaustive rg over src/** for files_modified shows the only other occurrences are session.py's field, which is assigned from changed_files (session.py:239, 257) |
CLM-05, CLM-08 |
Mismatch (implemented, self-declared). The host-side measurement of "files changed" exists, is computed every turn, and is thrown away; the persisted and reported field is the model's own report (OBJ-08) |
files_modified from hooks would mean "opened for write", not "content differs" — so even if consumed it would not be a content-change check |
RTE-16.E1 |
check/evidence production | implemented |
OBJ-05 → turn status |
non-truth-apt policy/content update: assigns one of four turn-status labels | Target: did the RLM loop exhaust its iteration budget? | Evaluator: result.trace is not None and result.trace.status == "max_iterations" — a single equality test on a field produced by CMP-12; domain = budget exhaustion only |
immediately after aforward returns normally |
max_iterations (with MAX_ITERATIONS_ERROR attached) or succeeded |
Sets the persisted status, the OBJ-02 line, exit code 2, and a stderr warning |
Licenses "the loop did/did not report budget exhaustion". Does not license "the answer is correct", "the task was completed", or "the edits are sound" | changes the caller's control flow via exit code; changes what the next turn reads in OBJ-02 |
external caller · exit code 0/2 · enforcing on the caller's control flow (BAP-11) · the invocation · |
main LM · summary line · informative · later turns | SRC-1 runtime.py:298-321; session.py:355-357; cli.py:545-547 |
CLM-05 |
none |
RTE-16.E2 |
check/evidence production | implemented |
exception → turn status | non-truth-apt policy/content update: interrupted vs failed |
Target: was this turn ended by the operator or by an error? | Evaluator: interrupt_requested() callback plus exception type (asyncio.CancelledError) |
on any exception from aforward |
interrupted (+INTERRUPTED_ERROR) or failed (+user_facing_error(exc)) |
Persisted with whatever trace could be extracted; re-raised | Licenses "the turn ended this way". Nothing about content | exit code 1 or 130; the interpreter is deliberately retained, not rebuilt | external caller · exit code · enforcing · the invocation · | main LM · summary Error: line · informative · later turns |
SRC-1 runtime.py:250-296; session.py:24-27 |
CLM-05 |
none |
[NEW-2] |
check/evidence production | implemented |
OBJ-07, OBJ-08 |
no content change | Target: are the RLM's returned fields of the declared types? | Evaluator: isinstance checks in _prediction_to_result/_require_string_list; domain = Python type conformance only |
on every returned prediction | pass, or TypeError → the turn becomes failed via RTE-16.E2 |
Enforcing: a type violation aborts the turn | Licenses "response is a str and changed_files is a list[str]". Says nothing about truth, relevance, or completeness |
blocks persistence and output of a malformed result | external caller · exit code 1 · enforcing · the invocation | SRC-1 agent/service.py:200-223; session.py:497-502 |
CLM-05 |
none | This is the only implemented gate anywhere on the path of OBJ-07, and it is a form check, not a content check |
RTE-19.E1 |
check/evidence production → linked disposition (see RTE-19.E1d) |
implemented |
OBJ-13 |
no content change | Target: is this on-disk file a well-formed Fractal session with the requested identity? | Evaluator: JSON parse + schema_version == 1 + pydantic SessionState.model_validate + state.session_id == session_id; domain = file integrity and identity, not content truth |
on --resume / /resume only |
readable+matching, or one of four rejection paths | Each rejection emits a RuntimeWarning and yields a fresh empty session |
Licenses "this file parses as schema v1 and claims the requested id". Not that its contents are accurate | admits or refuses prior state into the run | operator · RuntimeWarning on stderr · advisory · the run |
SRC-1 session.py:120-192, 474-494 |
— | none | An id mismatch is treated identically to corruption: prior state is silently replaced by an empty session |
3c. Disposition / acceptance
| Route ID | Function | Architectural status | Object/candidate | Content/update relation | Transition target | Evaluator/criterion + domain | Activation | Result | Force | Epistemic authority | Operational authority | Behavioral-authority path | Evidence | Claim IDs | Mismatch | Gap/limit |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
RTE-19.E1d |
disposition/acceptance | implemented |
OBJ-13 |
no content change | admit or refuse a stored session into the current run | criterion: the RTE-19.E1 integrity+identity result |
on resume | admitted (state loaded) / refused (fresh session + backup copy at *.bad-<stamp>) |
enforcing on what the run starts from | Scope: artifact integrity and identity only. This is not acceptance of any claim the session contains | determines the prompt content of every subsequent turn | operator · warning + observable behavior · enforcing · the run | SRC-1 session.py:130-192, 483-494 |
— | none | Refusal is recoverable only manually, from the backup |
RTE-17.E1 |
operational admission/selection | implemented |
OBJ-14 |
non-truth-apt policy update: select provider, main model, sub-model | reject model ids outside a provider's restricted_models allowlist |
criterion: membership in an explicit tuple; domain = model identifiers | at config resolution and on /model, /provider |
accepted or rejected with a "supported: …" message | enforcing (BAP-10) |
none epistemic | blocks a run with a disallowed model | operator · CLI error · enforcing · the run | SRC-1 providers.py:107-113, 657-660; config_commands.py:507-512 |
— | none | Line-level behavior of providers.py only grepped (SRC-1 access gap) |
RTE-15.E3 |
disposition/acceptance of OBJ-07 |
no route found within boundary |
OBJ-07 (and OBJ-08) |
n/a | would be: accept/reject/revise the answer against a named criterion | none found | n/a | n/a | none | No epistemic authority is granted or withheld anywhere in the host layer for the deliverable. It is produced, retained, re-injected, and served unchecked | the answer reaches stdout, the session summary, and the external caller with no gate but [NEW-2]'s type check |
operator/external agent · stdout, TUI, OBJ-15 · the answer is the product · the caller's session |
Evidenced absence: rg -i -e approve -e '\baccept' -e reject -e retry -e rollback -e 'diff review' over src/** returns only unrelated matches (Accept: HTTP header, reject_raw_secret_fields, provider-credential rejection). Confirmed against runtime.py:298-323, cli.py:504-551, tui/app.py:60-69 (slash-command table has no approval/retry/accept command) |
CLM-03, CLM-05, CLM-12 |
Not a mismatch with Fractal's doctrine — CLM-12 explicitly says there is no approval, no diff review, no rollback. It is a limit on CLM-03 |
Scope of this absence: src/fractal/** at 5954a07d. It does not establish that no check occurs inside CMP-12 (SRC-8), nor that operators do not check by hand |
3d. Retention
| Route ID | Function | Architectural status | Object | Content/update relation | Target | Condition | Activation/timing | Result | Force | Epistemic authority | Operational authority | Behavioral-authority path | Evidence | Claims | Mismatch | Gap/limit |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
RTE-10.E1 |
retention | implemented |
OBJ-01 → OBJ-03, OBJ-04, OBJ-13 |
non-ampliative (verbatim) | persist a pending turn before the RLM call |
unconditional | pre-call | turn appended with status: pending and written to disk |
n/a | Retention is not acceptance | ensures a crashed process still leaves the request recorded | main LM (next turn) · summary "Agent status: pending" · informative · later turns | SRC-1 runtime.py:222-223; session.py:203-219 |
— | none | — |
RTE-10.E2 |
retention | implemented |
OBJ-07, OBJ-08, OBJ-09a, OBJ-05, OBJ-06 → OBJ-03, OBJ-04, OBJ-13 |
non-ampliative (verbatim for history; count-reduction for summary) | persist the completed turn after the call | on every terminal path incl. failure and interrupt | post-call | record updated; file rewritten | n/a | Retention is not acceptance; nothing here evaluates the retained content | makes the content available to the next turn and to --resume |
main LM · OBJ-02 and OBJ-04 · informative · all later turns of the session |
SRC-1 runtime.py:229-322; session.py:221-262, 187-192 |
CLM-07 |
none | history_turn.files_modified is filled from changed_files (session.py:239, 257) — the retained "host fact" field carries model-reported content |
RTE-04.E1 |
lineage/freshness/recovery | implemented |
interrupted-turn record | non-ampliative | preserve a user-cancelled turn distinctly from a failure | interrupt_requested() true |
on SIGINT | interrupted record with INTERRUPTED_ERROR and any extractable trace; interpreter deliberately retained |
n/a | Licenses "this turn was user-cancelled" | the next turn reuses the same live interpreter | main LM · summary status+error · informative · later turns | SRC-1 runtime.py:250-284 |
CLM-05 |
none | Interpreter-reuse safety rests on uninspected upstream behavior (SRC-8) |
RTE-19.E2 |
lineage/freshness/recovery | implemented |
OBJ-13 set |
no content change | list resumable sessions | files under the workspace sessions dir | /sessions, --list-sessions |
ordered list; unreadable/foreign files silently skipped | advisory | Self-declared: "listing is a navigation aid, not a validation pass" | none | operator · session list · advisory · the selection | SRC-1 session.py:402-432 |
— | none | Silent skipping means a corrupt session is invisible until resume |
3e. Lifecycle integration
| Route ID | Function | Architectural status | Notes |
|---|---|---|---|
| — | lifecycle integration | no route found within boundary |
Lifecycle integration requires a prior acceptance. RTE-15.E3 establishes that no acceptance route for OBJ-07/OBJ-08 exists within src/fractal/** at 5954a07d. Re-injection of OBJ-02 into the next prompt (RTE-06.E1) and REPL exposure of OBJ-04 (RTE-07.E1) are pre-acceptance retention and use, recorded above as retention/consumption rows, and are explicitly not lifecycle integration. |
3f. Operational admission / selection / consumption
| Route ID | Function | Architectural status | Object | Content relation | Consumer | Force | Horizon | Evidence | Claims | Gap/limit |
|---|---|---|---|---|---|---|---|---|---|---|
RTE-06.E3 |
operational admission/selection/consumption | implemented |
assembled signature docstring (base instructions + OBJ-11 section + OBJ-02) |
no content change (assembly only) | main LM (BAP-01, BAP-02, BAP-03) |
directive, unenforced | the turn | SRC-1 signature.py:8-71 |
CLM-11, CLM-13 |
Declared ordering rationale (stable cacheable prefix; summary must precede variable inspection) is a design claim about CMP-12's prompt handling, uninspected. Base instructions say "verify important edits" — a directive with no implemented verifier |
RTE-07.E1 |
operational admission/selection/consumption | implemented (delivery) / uninspected (whether the model ever inspects it) |
OBJ-04 |
no content change | main LM, on its own initiative (BAP-04) |
informative, opt-in | the turn | SRC-1 signature.py:90-95; session.py:197-201 |
CLM-09 |
Whether session_history is actually surfaced as a REPL variable, and whether the model reads it, is CMP-12 behavior (SRC-8) and unobserved |
RTE-09.E1 |
operational admission/selection/consumption | implemented (injection) / uninspected (effect) |
OBJ-12 |
no content change | main LM (BAP-05) |
prescriptive, unenforced | the turn | SRC-1 agent/service.py:96; agent/skills.py |
— | filesystem_coding_skill is a technique cheatsheet; grep for verif|confirm|assert|correct finds no verification obligation in it |
RTE-15.E1 |
operational admission/selection/consumption | implemented |
OBJ-07 |
no content change | operator (TUI/stdout) | the answer is the product | the session | SRC-1 cli.py:523-526; tui/app.py:291-292 |
CLM-03 |
Served with no gate other than [NEW-2] |
RTE-20.E1 |
operational admission/selection/consumption | implemented (host side) / doctrine only (the calling agent's behavior) |
OBJ-07 / OBJ-15 |
no content change | external calling agent | SRC-5 instructs the caller to treat stdout as "the agent's final response text, nothing else" and to delegate audits/root-cause/synthesis to it |
that agent's session (BAP-07), plus enforcing exit-code semantics (BAP-11) |
SRC-5 SKILL.md:36-51, 58-60; SRC-1 cli.py:512-551 |
CLM-03 |
The external agent imports an unchecked ampliative claim as an answer. Nothing in either artifact instructs the caller to verify it; nothing in Fractal supports verification |
RTE-12.E1 |
operational admission/selection/consumption | uninspected |
OBJ-10 |
at the host layer: no content change (mount construction only) | generated code inside CMP-13 |
enforcing (operational), external (BAP-09) |
sandbox lifetime | SRC-1 agent/service.py:158-197; SRC-2 "How it works" |
CLM-10 |
Fractal constructs DirectWorkspaceMount(host_path == sandbox_path) and a deterministic sandbox name; all containment is CMP-13/SRC-8 |
RTE-18.E1 |
operational admission/selection/consumption | implemented (naming/teardown) / uninspected (reuse semantics) |
sandbox instance | no content change | CMP-12/CMP-13 |
enforcing on execution locality | sandbox lifetime | SRC-1 agent/service.py:140-183 |
CLM-10 |
Hot reuse means state may persist across turns and across sessions for the same (workspace + include-set) key; what persists is uninspected |
4. Per-object lifecycle disposition
Ampliative truth-apt candidates
OBJ-07 — agent response
| Field | Record |
|---|---|
| Candidate object ID | OBJ-07 |
| Relevant route IDs | RTE-05 (production, external), [NEW-2] (type gate), RTE-16.E1/RTE-16.E2 (turn status), RTE-10.E2 (retention), RTE-06.E1 (re-injection), RTE-15.E1/RTE-15.E2/RTE-20.E1 (consumption), RTE-15.E3 (acceptance — absent) |
| Transformation | ampliative conjecture — a natural-language claim about the workspace that does not follow from the host-layer inputs (OBJ-01, OBJ-02, OBJ-04, OBJ-11, OBJ-12). Novelty, fluency, and plausibility establish candidate generation only |
| Observation/anomaly | routes: RTE-13.E1 (host-side), RTE-05/RTE-12 (in-loop, external) · architectural status: implemented for the host reducer, uninspected for whatever evidence-gathering the loop performs · observed candidate state: no instance observed · evidence: SRC-1 events.py; SRC-8 not obtained |
| Conjecture | routes: RTE-05 · architectural status: uninspected (CMP-12) · observed candidate state: no instance observed · evidence: SRC-8 |
| Derived consequence | routes: none identified · architectural status: no route found within boundary (host layer derives nothing from the response) · observed candidate state: no instance observed · evidence: SRC-1, exhaustive read of runtime.py, session.py, cli.py |
| Test/evidence | routes: [NEW-2] only · architectural status: implemented, but its domain is Python type conformance, not the content's truth · observed candidate state: no instance observed · evidence: SRC-1 agent/service.py:200-223. The base instructions ask the model to "verify important edits" (SRC-1 signature.py:26) — doctrine only, with no implemented verifier and no place to record a verification result |
| Acceptance | routes: none · evaluator: none found · criterion: none found · intended use: n/a · architectural status: no route found within boundary (search boundary: src/fractal/** at 5954a07d; see RTE-15.E3 for the exact search) · observed candidate state: no instance observed · accepted scope: none · evidence: evidenced absence per RTE-15.E3; corroborated by CLM-12 (SRC-4 "Current Caveats"), which declares the absence of approval, diff review, and rollback |
| Lifecycle integration | routes: n/a · post-acceptance change/consumer: n/a · architectural status: no route found within boundary · observed candidate state: no instance observed · evidence: integration requires acceptance, which does not occur. RTE-06.E1 re-injects the response into the next prompt and RTE-20.E1 hands it to an external agent — both are pre-acceptance use, recorded as separate ledger rows |
| Missing phase/evidence | The whole production side (observation → conjecture) is inside CMP-12/CMP-13 (SRC-8). No test, acceptance, or integration phase exists in the host layer. No candidate artifact was observed at all, because nothing was run |
OBJ-08 — changed_files
| Field | Record |
|---|---|
| Candidate object ID | OBJ-08 |
| Relevant route IDs | RTE-05 (production, external), [NEW-2], RTE-10.E2, RTE-06.E1 (as a count), RTE-15.E1/RTE-15.E2, RTE-13.E2 (the discarded independent measurement) |
| Transformation | ampliative conjecture — a model-authored claim about which host files were modified. It is not entailed by anything the host supplies, and Fractal says so itself (CLM-08) |
| Observation/anomaly | routes: RTE-13.E2 produces an independent host-side observation of the same fact · architectural status: implemented (production) but no consumer · observed candidate state: no instance observed · evidence: SRC-1 events.py:88, 219; no read site in src/** |
| Conjecture | routes: RTE-05 · architectural status: uninspected · observed candidate state: no instance observed · evidence: SRC-8 |
| Derived consequence | routes: RTE-06.E1, RTE-10.E2 derive files_changed_count = len(changed_files) · architectural status: implemented · observed candidate state: no instance observed · evidence: SRC-1 session.py:239, 245. This is an entailed derivation from an unwarranted premise: the count is exactly as reliable as the model's list |
| Test/evidence | routes: none — the one available cross-check (RTE-13.E2 vs OBJ-08) is not implemented; the two lists are never compared · architectural status: no route found within boundary · observed candidate state: no instance observed · evidence: SRC-1 runtime.py:305-318 passes changed_files=result.changed_files and files_read/commands_run from the tracker, never runtime_events.files_modified |
| Acceptance | routes: none · architectural status: no route found within boundary · observed candidate state: no instance observed · accepted scope: none · evidence: as RTE-15.E3 |
| Lifecycle integration | no route found within boundary / no instance observed — the list is retained (OBJ-04.files_modified), counted (OBJ-03), printed to stderr, and carried in OBJ-15, all without acceptance |
| Missing phase/evidence | The decisive missing artifact is a comparison between OBJ-08 and OBJ-09b. Both exist in the same process at the same moment; nothing joins them |
Non-ampliative truth-apt content
| Candidate object ID | Relevant route IDs | Transformation | Discovery lifecycle | Applicable acquisition/lineage/derivation/update route and warrant | Missing evidence/limit |
|---|---|---|---|---|---|
OBJ-01 |
RTE-10.E1, RTE-06.E3 |
acquisition/import | not applicable | Imported verbatim from the operator or calling agent; source warrant preserved as the operator's own, never checked, never re-derived. Retained verbatim and unbounded in OBJ-03 |
Fractal grants it overriding directive force (BAP-06) without any authentication of the caller beyond process ownership |
OBJ-02 |
RTE-06.E1, RTE-06.E3 |
non-ampliative reshaping (lossy selection + reformat) | not applicable | Warrant is exactly the warrant of OBJ-03's fields, no more. Preserved: turn order, user messages, statuses, response text, error strings. Dropped: file/command names (→ counts), TurnUsage, RunTrace. Note: it re-injects OBJ-07 verbatim, so unchecked ampliative content re-enters the prompt each turn with no marking that it was never verified |
Never trimmed: this always-visible channel grows monotonically. No implemented budget, threshold, or compaction anywhere in the host layer |
OBJ-03 |
RTE-10.E1, RTE-10.E2, RTE-14.E2 |
non-ampliative retention + entailed derivation of three counts | not applicable | files_read_count and commands_run_count carry host-measurement warrant (bounded by RTE-13.E1's limits). files_changed_count carries model-report warrant only |
The summary's uniform presentation gives all three counts the same apparent standing; nothing in OBJ-02 marks the provenance difference |
OBJ-04 |
RTE-10.E2, RTE-11.E1, RTE-07.E1 |
non-ampliative retention + deletion (trim to 20) | not applicable | Verbatim retention. The files_modified field is mislabelled relative to its lineage: it holds OBJ-08, not host observation |
Whether the model ever reads it is CMP-12 behavior and unobserved |
OBJ-05 |
RTE-10.E2, RTE-07.E1, RTE-14.E1, RTE-16.E1 |
acquisition/import from an external producer | not applicable | Warrant is unknown: CMP-12 produces it and was not inspected (SRC-8). Fractal preserves it verbatim inside OBJ-04 for the last 20 turns and derives OBJ-06 and the turn status from it |
Whether it truly records "every peek, chunk, sub-call, and verification step" (CLM-09) cannot be assessed. _validate_run_trace returns None on ValueError without warning (runtime.py:342-346), so a malformed trace degrades to "no trace" silently |
OBJ-06 |
RTE-14.E1, RTE-14.E2 |
entailed derivation | not applicable | The arithmetic is warranted within its domain. The premises are OBJ-05's numbers, whose warrant is unknown. CLM-06's provenance half ("not from model output") is implemented; its trustworthiness half rests on the uninspected producer |
None whenever no trace is attached — including on failure paths, which is precisely where CLM-06 claims it "stays trustworthy" |
OBJ-09a |
RTE-13.E1, RTE-10.E2 |
acquisition/import (measurement by interposition) | not applicable | Warrant: "these hooked Python calls occurred inside the interpreter during this turn". Genuinely host-side, and the strongest warrant in the system | Bounded by: subprocess-mediated file effects invisible to the file hooks; dependence on CMP-12 injecting hooks (SRC-3 "Known Limits"); silent swallowing of adapter errors; "read" means "opened/read", not "used" |
OBJ-09b |
RTE-13.E2 |
acquisition/import, unconsumed | not applicable | Same measurement warrant as OBJ-09a, but no consumer, therefore no force |
Even consumed, it would warrant "opened for writing", not "content changed" |
OBJ-11 |
RTE-06.E2 |
acquisition/import + truncation | not applicable | Source warrant is the workspace author's, preserved unchecked | Silent empty-string on read failure; truncation at 20 000 chars |
OBJ-12 |
RTE-09.E1 |
static shipped material — no production route | not applicable | Authored once, in-repo. Retained state, not memory read-back. No route produces, checks, or updates it during a run | Its technical claims about Python/rg behavior are never validated by the system |
OBJ-13 |
RTE-10.E2, RTE-19.E1, RTE-19.E1d, RTE-19.E2 |
non-ampliative serialization | not applicable | Integrity and identity are checked on resume (RTE-19.E1) and disposed (RTE-19.E1d). Content truth is never checked |
CONF-1: three doctrine artifacts state the wrong storage path |
OBJ-15 |
RTE-15.E2, RTE-20.E1 |
non-ampliative reshaping | not applicable | Inherits the warrant of its fields: response unchecked ampliative, changed_files model-reported, usage derived from an uninspected trace, status from a single-field equality test |
The envelope's typed, machine-readable shape gives no signal of the heterogeneous provenance inside it |
OBJ-16 |
RTE-13.E1 |
non-ampliative reshaping of the hook stream for display | not applicable | Same warrant as OBJ-09a; ephemeral, unretained |
after-phase file and command events are suppressed from display, so the operator sees starts, not completions or outcomes |
Indeterminate
| Candidate object ID | Relevant route IDs | Transformation | Classifications still possible | Preserved lineage | Implemented checks, retention, or use | Current warrant limit | Evidence needed to decide |
|---|---|---|---|---|---|---|---|
OBJ-17 |
[NEW-1] |
indeterminate | entailed derivation (if the reconstruction provably reproduces the real call's message set and the tokenizer matches the provider's) or ampliative conjecture (if it is an approximation of a different object than the one actually sent) |
Lineage is explicit and inspectable on the host side: OBJ-03+OBJ-04+OBJ-11+OBJ-12 → PredictRLM._prepare_file_io/_build_variables → ChatAdapter().format → token count |
No check. Not retained. Displayed in the TUI toolbar only; never computed in headless mode; no consumer changes behavior on it | Warrants "an estimate produced by this reconstruction", not "the next prompt will be N tokens". Three silent degradations: private-API drift, deliberately empty user_message, tokenizer fallback chain ending in None |
SRC-8 (predict-rlm 0.7.0 internals) plus an observed run comparing the estimate against the actual context_tokens recorded in OBJ-06 for the same turn |
Objects with no candidate truth-apt output
- No lifecycle record for
OBJ-10: within the inspected boundary the host layer only constructs mount descriptors; every content change to workspace files occurs inRTE-12/CMP-13(SRC-8, uninspected). Relevant direct-adaptation or update routes:RTE-12.E1,RTE-18.E1. - No lifecycle record for
OBJ-14: no candidate truth-apt output for this object; it is a non-truth-apt policy/configuration object. Relevant direct-adaptation or update routes:RTE-17.E1.
5. System-claim versus route comparison
| Claim ID | Claimed operation or warrant | Claim source + evidence layer | Doctrine/design support | Implemented route IDs | Observed-run support | Causal support and design limits | Supported conclusion | Mismatch/unknown |
|---|---|---|---|---|---|---|---|---|
CLM-01 |
"Fractal's loop is the model"; no orchestration to assemble | SRC-2 §"What is Fractal?" — doctrine |
Consistent with the code's shape | RTE-02, RTE-03: one submitted message → exactly one PredictRLM.acall (SRC-1 runtime.py:241-249, agent/service.py:111-128) |
none | none | Supported at the host layer only: Fractal's host code contains no iteration, tool-dispatch, or planning loop; the whole loop is delegated to CMP-12. This confirms where the loop is, not what it does |
The loop's actual behavior is uninspected (SRC-8) |
CLM-02 |
The RLM reasons over context programmatically "without context rot" | SRC-2; SRC-5 frontmatter — doctrine |
asserted only | none inside the boundary; the RLM's own context handling is CMP-12 |
none | none | Unsupported within this run for the inner loop. Separately, a host-layer finding bears on it: Fractal's own always-visible channel OBJ-02 has no trimming, budget, or compaction (RTE-06.E1, contrast RTE-11.E1 which caps history only), and copies each response verbatim, so it grows monotonically with session length. CMP-09 measures that growth (OBJ-17) but no route acts on the measurement |
Unknown for CMP-12. Tension at the host layer: the one context channel Fractal itself owns is unbounded |
CLM-03 |
A calling agent "gets back a distilled answer" for audits, tracing, root-cause, synthesis | SRC-2; SRC-5 SKILL.md:36-51 — doctrine |
The delivery mechanism is fully specified (stdout contract, --json, exit codes) |
RTE-15.E1, RTE-15.E2, RTE-20.E1, BAP-11 |
none | none | Delivery is implemented; warrant is not. OBJ-07 reaches the caller as an unchecked ampliative claim: the only gate on its path is a Python type check ([NEW-2]), and no acceptance route exists (RTE-15.E3). For audit and root-cause tasks the answer is the epistemic product, and nothing in Fractal or in SRC-5 tells the caller to verify it |
Mismatch between the claim's epistemic register and the implemented route: "distilled answer" implies a warranted output; the route implements retrieval-and-return of a model assertion |
CLM-04 |
Capability scales with the model, not with harness engineering | SRC-2 — doctrine |
asserted only | none — this is a comparative empirical claim | none | none | Unsupported. No comparison, no benchmark, no varied-model evidence exists in this run | Unknown; would require a causal design varying the model with the harness fixed |
CLM-05 |
"Prefer host-side truth over model-reported truth for state, files changed, commands run, verification status, and errors" | SRC-4 §"Engineering Guidelines" — doctrine (contributor policy) |
An explicit provenance policy | Implemented for: commands run (RTE-13.E1 → OBJ-09a), files read (same), usage/state (RTE-14.E1), errors/turn status (RTE-16.E1, RTE-16.E2) |
none | none | Partially implemented. Three of five categories follow the policy. Files changed does not (RTE-13.E2): the host-side measurement is computed every turn and discarded, and the model's list is what gets persisted, counted, printed, and returned. Verification status is vacuous: no route produces one, host-side or otherwise ("verify important edits" at signature.py:26 is an unenforced directive) |
Mismatch, implemented and self-declared. See CLM-08. The discard is the sharper form of the finding: this is not a missing capability but an unwired one |
CLM-06 |
TurnUsage is derived from the RunTrace, not from model output, "so it stays trustworthy across failed and interrupted turns" |
SRC-1 session.py:38-44 — doctrine embedded in implementation |
asserted in the type's docstring | RTE-14.E1, RTE-14.E2; runtime.py:266, 278, 290 do attach extracted traces on failure/interrupt paths |
none | none | The provenance half is implemented and true within the host layer: no field of TurnUsage is read from model output. The trustworthiness half does not follow — it transfers warrant from OBJ-05, produced by the uninspected CMP-12. The "across failed and interrupted turns" half is conditional: turn_usage_from_trace(None) returns None, and _extract_run_trace returns None when no trace is attached or when validation raises ValueError (runtime.py:326-346) |
Partial mismatch: the claim states an unconditional property; the implementation yields no usage at all whenever a trace cannot be recovered — exactly the failure cases the claim highlights |
CLM-07 |
"Fractal adds exactly one thing on top: session management" | SRC-2 — doctrine |
marketing simplification | Contradicted in scope by RTE-13.E1 (host-side action observation), RTE-17.E1 (provider/model admission), RTE-16.E1/E2 (turn-outcome classification), [NEW-1] (context estimation), RTE-18.E1 (sandbox naming/reuse) |
none | none | Understates the host layer. Fractal also implements observation, outcome classification, operational admission, sandbox lifecycle, and an output contract. Several of these are the only epistemically consequential host routes that exist | Mismatch (understatement, not overclaim); harmless for adoption, misleading for an epistemic reading |
CLM-08 |
"Changed files are currently coerced from model output" | SRC-4 §"Current Caveats" — doctrine |
self-declared caveat | Confirmed: RTE-13.E2, SRC-1 runtime.py:305, 316; session.py:239, 257; agent/service.py:205-208 |
none | none | Confirmed and extended. The caveat is accurate, and inspection adds a fact the caveat does not state: a host-side files_modified list already exists (events.py:88, 219) and is silently unused. The gap is a wiring gap, not a measurement gap |
none between claim and code; the claim is honest and understated |
CLM-09 |
"Every peek, chunk, sub-call, and verification step is fully readable in the trace" | SRC-2 §"How it works" — doctrine |
asserted | Retention of whatever CMP-12 returns: RTE-10.E2 (session.py:259), exposure via RTE-07.E1 |
none | none | The retention path is implemented; the completeness claim is uninspected. What the trace contains is CMP-12 behavior (SRC-8). Within the host layer three bounds apply regardless: traces survive only for the last 20 turns (RTE-11.E1); OBJ-02 carries no trace content; and on exception paths the trace may be None (silently, via _validate_run_trace). Also, "verification step" presupposes verification the host layer never requires or records |
Unknown for completeness; bounded for retention. SRC-2 itself labels its example trace as non-real |
CLM-10 |
Every turn runs in a Docker Sandbox, isolated, no network by default; workspace mounted so edits land on the host immediately | SRC-2 §"How it works" — doctrine |
asserted | Fractal implements only the request: create_sbx_interpreter, SbxConfig(name=…, reuse=True), DirectWorkspaceMount(host_path == sandbox_path) (SRC-1 agent/service.py:158-197). All enforcement is CMP-13 |
none | none | Not assessable. This is an operational-authority claim whose implementation lies entirely outside the boundary (BAP-09, SRC-8). The host code is consistent with it and confirms the "edits land on the host immediately" half by construction — the mount is the host path itself |
Unknown for isolation and network policy. Note in scope: reuse=True by default means a sandbox persists across turns and sessions per (workspace + include-set); what persists inside it is uninspected |
CLM-11 |
The summary is "compressed structured trajectory context… preserves prior user messages and compressed agent results" | SRC-1 signature.py:61-64; SRC-3 — doctrine embedded in implementation |
asserted in the prompt text the model reads | RTE-06.E1 (session.py:368-390) |
none | none | Partially supported, precisely. Preserves prior user messages: true and verbatim, for all turns, never trimmed. Compressed agent results: true for file/command facts (three lists → three integers) and for the dropped RunTrace and TurnUsage; false for the response, which is copied verbatim under Agent response:. So the transformation is non-ampliative lossy reshaping, and what it drops is exactly the host-observed evidence, while what it keeps verbatim is exactly the unchecked model assertion |
Mismatch in kind, not in honesty: "compressed" is asserted of the whole record but holds only for its evidential fields. The claim is stated to the model, which may therefore over-trust the summary as a complete record |
CLM-12 |
No robust approval/sandbox policy yet; no host command execution tool; no git checkpoint, diff review, or rollback | SRC-4 §"Current Caveats" — doctrine |
self-declared caveat | Confirmed by evidenced absence: RTE-15.E3 |
none | none | Confirmed. The absence of any approval, review, or rollback route in src/fractal/** at 5954a07d matches the caveat exactly. This is the doctrinal counterpart to the lens's central finding: Fractal does not claim to check its output, and does not |
none — claim and code agree |
CLM-13 |
The summary must be always visible, so it is embedded in prompt text rather than declared as an input field, because PredictRLM exposes input fields "primarily as REPL variables with prompt previews" | SRC-1 signature.py:66-71; SRC-3 — doctrine embedded in implementation |
asserted with a stated rationale | The mechanism is implemented: the summary is interpolated into __doc__ (signature.py:55-65, 72-73), not declared via dspy.InputField |
none | none | The mechanism is implemented; the rationale is uninspected. Whether PredictRLM in fact exposes input fields "primarily as REPL variables with prompt previews" is a claim about CMP-12 (SRC-8). SRC-3 "Known Limits" repeats it as a limitation |
Unknown for the rationale. Consequence in scope: because the summary is prompt text rather than a typed field, it is also outside any field-level budgeting CMP-12 might apply — reinforcing the unbounded-growth finding under CLM-02 |
6. Bounded conclusion
Findings are stated by route and scoped to src/fractal/** at 5954a07d, host layer only. No system-wide epistemic grade is given, and none of Fractal's claims is accepted here.
What the system retains, retrieves, reshapes, and uses. Fractal retains, per session, a never-trimmed structured summary (OBJ-03 → OBJ-02) and a 20-turn exact-recall history (OBJ-04), both serialized to a workspace-keyed JSON file in a global state directory (OBJ-13; note CONF-1, where three doctrine artifacts give the wrong path). Retention happens twice per turn — pending before the call, completed after (RTE-10.E1, RTE-10.E2) — on every terminal path including failure and interrupt. Retrieval is by explicit --resume//resume only; default is a fresh session (RTE-19). The one reshaping route that matters epistemically is RTE-06.E1: it preserves user messages and the agent's response verbatim while collapsing the host-observed file and command lists into three integers and dropping RunTrace and TurnUsage entirely. Retention here is not acceptance, and re-injection of OBJ-02 into the next prompt is pre-acceptance use, not lifecycle integration.
What it acquires, and what happens to source warrant. Four import routes: the user message (RTE-10.E1, warrant = the operator's, unchecked, granted overriding directive force via BAP-06); workspace AGENTS.md (RTE-06.E2, warrant = the workspace author's, unchecked, truncated at 20 000 chars, silently empty on read failure); the RunTrace from predict-rlm (RTE-10.E2, warrant unknown — CMP-12 was not inspected, and malformed traces degrade to None without a warning at runtime.py:342-346); and host-side action facts (RTE-13.E1). The last is the only import whose warrant Fractal itself establishes.
What it derives, from which premises. OBJ-06 (RTE-14.E1, RTE-14.E2): correct arithmetic over the trace's usage fields — warranted as arithmetic, and no further than OBJ-05's unknown warrant. files_read_count and commands_run_count: entailed from host measurement, warranted within RTE-13.E1's bounds. files_changed_count: entailed from OBJ-08, i.e. entailed from an unwarranted premise, and presented in OBJ-02 in the same uniform format as the other two counts with nothing marking the provenance difference.
What it conjectures, tests, accepts, and integrates. Two ampliative candidates are produced per turn — the response (OBJ-07) and changed_files (OBJ-08) — both by the uninspected CMP-12. Neither is tested and neither is accepted. The only implemented gate on their path is a Python isinstance check ([NEW-2], agent/service.py:200-223), whose domain is type conformance and which establishes nothing about truth, relevance, or completeness. Searching src/fractal/** at this revision for approval, acceptance, rejection, retry, rollback, or diff-review routes returns nothing (RTE-15.E3); the slash-command table (tui/app.py:60-69) offers no such command. This absence is scoped to the inspected host layer and does not establish that no check occurs inside CMP-12, nor that operators do not check by hand. Because there is no acceptance, there is no lifecycle integration anywhere in the boundary.
The sharpest implemented finding. Fractal's contributor policy CLM-05 says to prefer host-side truth over model-reported truth for "files changed". RuntimeEventTracker.files_modified is a host-side measurement of exactly that fact, populated on every write-mode hook event (events.py:88, 219) — and no code outside events.py reads it. runtime.py:305-318 passes the tracker's files_read and commands_run through, and passes the model's changed_files for the modified list; session.py:239, 257 stores it into a field named files_modified. So the host holds two independent claims about the same fact in the same process at the same moment, never compares them, and keeps the model's. CLM-08 honestly declares the coercion; inspection adds that the alternative is already computed. Even if wired, the hook-derived list would warrant "opened for writing", not "content differs".
Material acceptance criteria, intended use, scope, operational authority, behavioral-authority paths. The only implemented dispositions in the boundary judge artifact integrity and identity, not content truth: RTE-19.E1d admits or refuses a stored session on JSON parse + schema_version == 1 + pydantic validation + session-id match, with refusal yielding a fresh empty session plus a *.bad-<stamp> backup and a RuntimeWarning; RTE-17.E1 rejects model ids outside a provider allowlist. Turn-outcome classification (RTE-16.E1, RTE-16.E2) is check/evidence production about the run, not disposition of content: its evaluator is a single equality test, result.trace.status == "max_iterations", and trace is None falls through to succeeded. Its results are nonetheless operationally consequential — exit codes 0/1/2/130 that a calling script branches on (BAP-11) — and epistemically thin: they license "the loop did or did not report budget exhaustion", nothing about the answer. Behavioral-authority paths differ by consumer and must not be merged: the operator gets advisory live event lines, warnings, and a toolbar estimate; the main LM gets an unenforced directive stack (BAP-01, BAP-02, BAP-05, BAP-06) plus an always-visible informative summary (BAP-03) and an opt-in history variable (BAP-04); the sub-LM is reachable only through CMP-12 and is invisible at this boundary; the external calling agent consumes stdout as a finished answer under SRC-5's routing instruction (BAP-07) and branches on exit codes (BAP-11) — the path on which an unchecked ampliative claim leaves Fractal and enters another agent's reasoning as an "audit" or "root cause" result.
Direct behavior or policy adaptation without a truth-apt route. Config layering and model admission (RTE-17.E1) change what runs without producing truth-apt content. Sandbox naming and hot reuse (RTE-18.E1) change execution locality and, by defaulting reuse=True per (workspace + include-set), let sandbox state persist across turns and sessions; what persists is uninspected. The interrupt path deliberately retains the live interpreter (RTE-04.E1) on the strength of an uninspected upstream behavior.
One measurement produced and not acted on. CMP-09 reconstructs the next turn's prompt and counts its tokens (OBJ-17, [NEW-1]). It is displayed in the TUI toolbar, never computed in headless mode, and consumed by no route that trims, compacts, warns-and-blocks, or otherwise changes behavior. Its own fidelity is indeterminate: it calls PredictRLM private methods, deliberately passes an empty user_message, and falls through a three-level tokenizer chain that can return None. Read alongside RTE-06.E1 — where the always-visible summary is never trimmed and copies each response verbatim — the system measures a growth in the one context channel it fully owns and does not bound it.
Claims that remain unsupported, and why. CLM-02 (no context rot) and CLM-04 (capability scales with the model) have no implemented route inside the boundary and no run or causal evidence; CLM-04 is a comparative empirical claim for which no contrast exists here. CLM-09 (trace completeness) and CLM-13 (the PredictRLM input-field rationale) rest on CMP-12 behavior that SRC-8 would be needed to inspect; only their host-side retention and mechanism halves are implemented. CLM-10 (sandbox isolation, no network) is an operational-authority claim implemented entirely outside the boundary — Fractal implements the request, CMP-13 implements the enforcement. CLM-03 (distilled answer) has a fully implemented delivery route and no warrant route. CLM-06 is implemented in its provenance half and unsupported in its trustworthiness half, and is conditional in exactly the failure cases it advertises. CLM-05 is implemented for commands, reads, usage, and errors, unimplemented for files changed, and vacuous for "verification status" — no route in the boundary produces one. CLM-07 understates the host layer. CLM-08 and CLM-12 are accurate self-declared caveats and are confirmed. CLM-11 is accurate about user messages and about compressing evidential fields, and inaccurate about the response, which is preserved verbatim — a claim made to the model in the prompt it reads.
What no evidence in this run could establish. Nothing was executed. Every observed-candidate-state field above is no instance observed or not determinable. No statement here upgrades doctrine to implementation, implementation to operation, observation to causality, or operational continuation to warrant.
NEW RECORDS REQUESTED (orchestrator must assign canonical IDs)
Placeholders used in the body are provisional and local to this lens file. Each is a genuinely new material record with no existing registered ID.
| Placeholder | Proposed record kind | Proposed content | Evidence anchor | Why it is new |
|---|---|---|---|---|
[NEW-1] |
RTE-* (route) |
Pre-turn context estimation. CMP-09 reconstructs the next turn's initial action-LM messages by mirroring FractalAgent.aforward's setup through PredictRLM._prepare_file_io, _build_signatures_with_files, and _build_variables, formats them with ChatAdapter, and counts tokens via litellm.token_counter with a tiktoken fallback chain. Cache-keyed on workspace, include-set, session digest, model label, max_iterations, and AGENTS.md stat. Consumed only by the TUI toolbar; absent in headless mode |
SRC-1 context_meter.py:19-121, 124-168; consumers tui/app.py:33-36, 434, 442 |
CMP-09 and OBJ-17 are registered, but no RTE-* covers the estimation route itself. RTE-06 covers real prompt assembly, not this parallel reconstruction |
[NEW-2] |
RTE-* (route) |
Model-output type gate. _prediction_to_result raises TypeError unless response is a str, changed_files is a list[str], and trace is RunTrace or None; the raised error is caught by FractalRuntime.submit and recorded as a failed turn. This is the only implemented gate on the path of OBJ-07/OBJ-08 |
SRC-1 agent/service.py:200-223; session.py:497-502; runtime.py:273-296 |
No registered RTE-* covers a check on the returned prediction. It is materially consequential (it can abort a turn) and is load-bearing for the CLM-03/CLM-05 assessment |
[NEW-3] |
OBJ-* split |
Split OBJ-09 into OBJ-09a and OBJ-09b. The registered OBJ-09 groups files_read / files_modified / commands_run as "host-recorded from runtime hooks", and cites session.py:255-258. Inspection shows the persisted files_modified is assigned from the model-produced changed_files (session.py:239, 257), while the host-recorded files_modified (events.py:88, 219) has no consumer. The three fields therefore differ in producer, lineage, and warrant and must be split under the method's rule |
SRC-1 events.py:87-89, 215-219; runtime.py:305-318; session.py:238-258; exhaustive rg for files_modified over src/** |
Not a rename or re-inventory: it is a heterogeneity split forced by evidence, plus a correction to OBJ-09's cited lineage |
[NEW-4] |
CONF-* (evidence conflict) |
Persisted files_modified contradicts its own field name and CLM-05. SessionHistoryTurn.files_modified sits beside files_read and commands_run — both genuinely host-recorded — but is filled from model output, while an unused host-recorded list of the same name exists in RuntimeEventTracker. A reader of the session JSON cannot distinguish the provenances |
impl: SRC-1 session.py:80-82, 239, 257, events.py:88, 219, runtime.py:305-318; doctrine: SRC-4 §"Engineering Guidelines" (CLM-05), §"Current Caveats" (CLM-08) |
Distinct from CLM-08, which states the coercion but not the field-name collision or the discarded measurement |
Missing evidence → conclusion prevented
| Missing evidence | Conclusion it prevents |
|---|---|
SRC-8 — predict-rlm 0.7.0 source not obtained (CMP-12) |
Any conclusion about how OBJ-07 and OBJ-08 are actually produced; whether the loop performs its own checking, verification, or self-correction; whether OBJ-05 faithfully records production (CLM-09); whether recursion or sub-LM delegation manages context as claimed (CLM-02); whether session_history is exposed as a REPL variable and whether input fields behave as CLM-13 asserts; whether runtime hooks are injected at all, which bounds every warrant resting on OBJ-09a |
SRC-8 — sbx/Docker Sandbox implementation not obtained (CMP-13) |
Any conclusion about CLM-10 (isolation, no network by default), about what persists inside a reused sandbox across turns and sessions, and about whether RTE-12 execution is contained |
No execution environment — no sbx, no provider credentials, no virtualenv; checkout must not be mutated |
Any observed candidate state above no instance observed in block 4; any evidence about what a real OBJ-05 contains; any comparison of OBJ-17's estimate against the context_tokens actually recorded in OBJ-06; any measurement of how fast OBJ-02 grows in practice |
| No causal design — no interventional comparison of any kind | Any attribution of an outcome to the RLM architecture, to a model choice (CLM-04), to the summary format, or to any individual component |
SRC-7 — test bodies unread (file listing only) |
Any conclusion about what Fractal verifies about itself, and therefore any claim that the implemented routes behave as read. In particular, tests/test_runtime_interrupt_recovery.py is cited by a code comment as justifying interpreter retention; its content is unassessed |
SRC-1 access gaps — providers.py, onboarding.py, credentials.py, config_commands.py, connectivity.py, runtime_lms.py, version_check.py read by symbol-level grep only |
Any fine-grained conclusion about RTE-17.E1's admission behavior beyond the restricted_models allowlist, about credential handling, or about CMP-14's network behavior |
SRC-9 — docs/predict-rlm-notes.md absent (CONF-3) |
Any use of the project's own recorded notes about upstream predict-rlm issues, including the predict-rlm#42 cancellation behavior that runtime.py:255-262 relies on |
No OBJ-08 vs OBJ-09b comparison exists in the system, and none could be computed here without a run |
Any quantification of how far model-reported changed_files diverges from host-observed write targets — i.e. how consequential the CLM-05/CLM-08 gap actually is in operation |