core/docs/analysis/afferent-field-ingest-scope-2026-06-15.md
Shay efd280d45f
feat(l10): autonomous idle frontier-contemplation — the always-on life learns on its own (#758)
* feat(l10): autonomous idle frontier-contemplation — the always-on life learns on its own

Frontier survey move #2, path (c): give the always-on idle life a stream of experience via
SYMBOLIC intake (not the afferent→field seam, which is deferred pending a commensurability
ruling — docs/analysis/afferent-field-ingest-scope-2026-06-15.md). The idle heartbeat
converges (idle_tick drains a finite backlog + saturates is-a closure, then does nothing
forever); this makes it autonomously MINE its frontier with no user turn.

Reuse-heavy (the mechanism largely existed): a gated idle pass wires ADR-0080 contemplation
into the always-on loop.

- core/config.py: contemplate_frontier_during_idle (default off → no behavior change).
- chat/runtime.py: idle_tick pass 2b — when on, runs core.contemplation.run_contemplation()
  (mines the checked-in frontier-compare reports) and persists a reviewable ContemplationRun
  to <engine_state>/contemplation_runs/idle_<substrate_hash>.json. IDEMPOTENT per frontier
  (an already-mined frontier is not re-persisted → the life converges, no churn) and MEMOIZED
  per session (the static frontier is mined once, not re-read every beat). SPECULATIVE-only
  (ADR-0080: ContemplationFinding.__post_init__ RAISES if not SPECULATIVE → wrong=0 by
  construction; never ratified here — the HITL path is untouched). IdleTickResult gains
  frontier_findings.
- chat/always_on.py: run_continuous did_work + HeartbeatRecord count frontier work, so the
  heartbeat/soak convergence (H3) stays honest.
- core/cli.py: `core always-on --contemplate-frontier` (explicit learning-daemon mode) +
  honest per-beat log ("+N findings").

Adversarial 3-lens review (wrong=0/convergence, determinism/trust-boundary, test-vacuity)
found 7; fixed the real ones: the MEDIUM torn-write (write_contemplation_run is now atomic
temp+os.replace — the indefinite-uptime daemon expects SIGKILL mid-write, and the skip-guard
would never repair a torn canonical file), the gitignore breadth (contemplation_runs/ at any
engine-state root), the 64-bit filename truncation (full substrate_hash), and the
re-mine-every-beat cost (session memoization). Deferred + documented: the shared
contemplation miner embeds absolute report paths in finding CONTENT — a portability wrinkle
that does NOT affect wrong=0/convergence/soak determinism (those key on the content-only
substrate_hash) and touches shared code + its test surface; a follow-up.

Tests (non-vacuous): 7 — off-by-default (no behavior change), mines-when-enabled,
findings-SPECULATIVE (wrong=0), converges-idempotent, heartbeat-mines-then-converges,
mines-once-per-session (memoization), atomic-no-orphan. 55 idle/contemplation/always-on +
96 invariants/contemplation + 28 smoke green; `core always-on --contemplate-frontier` mines
end-to-end. engine_state contemplation_runs are per-life runtime state (gitignored).

* fix(l10): isolate idle frontier pass + persist frontier_findings (#758)

Three review blockers on the autonomous idle frontier-contemplation pass:

1. Daemon isolation. idle_tick's pass 2b called run_contemplation /
   write_contemplation_run bare inside the always-on loop (run_continuous
   wraps idle_tick in nothing but an exit-checkpoint finally), so a malformed
   frontier report or a transient FS fault would propagate out and KILL the
   continuous life. Wrap the optional pass best-effort (the exit-checkpoint
   boundary idiom): degrade to 0 findings + RuntimeWarning, leave did_work
   untouched, let a later beat retry.

2. Durable did_work explanation. HeartbeatRecord carried frontier_findings
   but serialize_report omitted it and AlwaysOnReport had no run total, so
   lived_life.json could show did_work=true with facts=0/proposals=0 and no
   persisted cause. Persist frontier_findings per-record + total_frontier_findings.

3. Honest HITL claim. The docstring/CLI/config said findings are "reviewable
   via the HITL path", but the existing HITL queue + workbench reader scan
   <repo>/contemplation/runs while the pass writes <engine_state>/contemplation_runs/.
   Narrow the claim to "persisted reviewable artifact, not yet in the HITL
   queue" (wiring that discovery is a follow-up).

Tests: 4 new non-vacuous regressions (mining-failure degrade, write-failure
degrade, always-on loop survives frontier failure, lived_life persists
frontier_findings) — all fail against the unpatched pass. 38 L10/always-on/
workbench tests green.
2026-06-14 23:59:49 -07:00

100 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Afferent → Field Ingest Seam — Scope & Decision Brief
**Status:** scope (decision-gated; field-touching, CLAUDE.md sanctioned-site implication) ·
**Context:** frontier survey move #2, after the always-on process (#747/#748/#752) + its
long-horizon soak (#757) closed the *idle* lived-spine. This is the proposed next edge:
give the always-on life a **stream of experience** by folding compiled afferent
observations into the live cognitive field.
## The gap it would close
The always-on heartbeat lives, holds closure, and resumes — but it is a continuously
**idling** life: `idle_tick` drains a finite backlog + saturates deductive closure, then
does no work forever (proven by the convergence gate H3 in `evals/l10_always_on`). There is
**no afferent input channel** into the idle loop. Separately, the sensorium is fully built
as a compiler/frame/merge substrate but is **never wired to the field** — ADR-0208's own
pipeline arrow (`ObservationFrame → … → field / recall / cognition`) stops at the merge.
This seam is the missing edge in `listen → comprehend → … → replay`.
## What is confirmed (geometric feasibility)
- **Units carry a field-shaped target.** `CompilationUnitLike` (sensorium/compiler/protocol.py:28)
exposes `versor: np.ndarray` — a real CL(4,1) multivector — alongside `versor_condition`.
A live `ObservationFrame`'s units therefore expose targets in the field's algebra.
- **The sanctioned pull pattern applies directly, closure-preserving BY CONSTRUCTION.** The
exemplar is `session/context.py::_session_anchor_pull`:
```
R = word_transition_rotor(field_state.F, target) # rotor toward target
R_step = rotor_power(R, α) # fractional, stays on Spin manifold
pulled = versor_apply(R_step, field_state.F) # apply — no post-hoc unitize
```
It explicitly *replaced* the rejected `_slerp_toward` (which broke closure and needed a
forbidden post-hoc `unitize_versor`). An afferent pull toward `unit.versor` is structurally
identical → preserves `versor_condition < 1e-6` by construction, adds no normalization site.
- **Home:** `session/context.py` (the already-sanctioned site that owns the field), a new
opt-in `observe(frame)` that pulls the field toward each unit's versor by a fractional
step. Default off → serving + existing behavior byte-unchanged.
## The load-bearing wrinkle (the decision)
**Closure-preservation is necessary but NOT sufficient.** The sanctioned anchor-pull moves
the field toward a **session concept-attractor** — a target in the *cognitive* semantic
frame, carrying meaning ("the field anchors toward the session's concept"). An afferent
unit's versor is in the *same algebra* (CL(4,1)) but:
> **Is the sensorium's CGA embedding semantically COMMENSURABLE with the cognitive field —
> or merely same-algebra, different meaning?**
- If **commensurable**: pulling the field toward `unit.versor` is genuine perception →
cognitive state (decoding doctrine: the observation is real, the field moves toward it).
The seam is clean and meaningful.
- If **not** (same algebra, unrelated embedding): the pull is geometrically valid but
**semantically empty** — the field drifts toward an arbitrary point. That is exactly the
*degenerate fit* the field-as-standing-hand doctrine forbids ("geometrically-realizable
structure only; never force a degenerate fit").
This question is unestablished in the code: the sensorium compiles units into CL(4,1) but
nothing today asserts that embedding shares the cognitive field's semantic frame (they were
built as **disjoint** tracks — CLAUDE.md: the afferent arc "is disjoint from the GSM8K
serving path"). Wiring afferent → field is the *first* connection of the two.
## Decisions for ratification (Shay reviews the design)
1. **Commensurability ruling (the crux).** Is the sensorium CGA embedding intended to be
commensurable with the cognitive field? Three honest paths:
- **(a) Commensurable by construction** — if the modality packs project into the *same*
concept manifold the cognition field uses, the pull is meaningful → build the seam.
(Needs: evidence/assertion that the projection shares the frame, e.g. a shared
concept-attractor basis, before any pull is wired.)
- **(b) Bridge required** — define an explicit, falsifiable map from a unit's versor to a
cognitive-field target (a grounded embedding), so the pull is toward a *meant* point.
Larger; an ADR + a commensurability gate.
- **(c) Not yet** — keep the tracks disjoint; the always-on life stays idle-converging for
now; the stream-of-experience comes from a different intake (the autonomous
determination-frontier — roadmap Step 5 — which feeds *symbolic* new questions, not
afferent versors). Lower geometric risk, advances the telos via comprehension.
2. **Sanctioned-site clause.** If (a)/(b): extend `session/context.py`'s CLAUDE.md clause to
cover afferent-observation pull (closure-preserving by construction AND semantic =
perception→field), with the same bright-line justification. A CLAUDE.md amendment.
3. **ADR.** Completing ADR-0208's arrow (`ObservationFrame → field`) is the first
afferent→cognitive-core connection; it likely warrants an ADR (extend 0208 or new),
given the tracks were deliberately disjoint.
4. **Falsifiable gate (regardless of path).** Any pull that "preserves closure" is only
proven if a test fails when it doesn't: a soak guard (sibling to `evals/l10_always_on`)
asserting (i) `versor_condition < 1e-6` every observed beat, (ii) the same frame sequence
→ byte-identical field digest (determinism), (iii) a *commensurability* assertion — the
field moves **toward** the observation (`dist(F, target)` decreases), not arbitrarily.
## Recommendation
The geometry is clean; the **semantics are the gate**. Recommend **ruling on decision 1
first** — do not wire a field pull until the commensurability question is answered, or the
seam risks a closure-valid-but-meaningless fit. If commensurability isn't yet established
(likely), the higher-leverage telos move is path **(c)**: the autonomous
determination-frontier intake (feed the idle heartbeat *symbolic* new questions it can
`determine` + commit wrong=0 / propose HITL) — which needs no field-embedding ruling and
directly converts the idle life into a learning one. The afferent→field seam then follows
once the embedding frame is established.