# Lazy state through progressive `read_state`

This document describes the implemented lazy-state contract. [BACKLOG.md](../BACKLOG.md) owns release readiness; [temporal acceptance](temporal-acceptance.md) maps behavior to executable evidence.

## Thesis

State Flow provides `lazy` as an object-root semantic plane in every scope's runtime view.

- It may be absent from stored state. Reads then use `{}` or values inherited through the effective overlay, without rewriting storage.
- Nested lazy values are ordinary JSON. They are durable and versioned with the same causal lineage as hot state, but excluded from ordinary baseline hydration.

The model-facing surface remains small:

- `read_state` reads one path or an ordered list of paths using one explicit projection.
- `value` and `patch` return only the requested semantic snapshot or patch; `keys` returns narrowly bounded structural `meta` followed by `keys`.
- `patch_state` remains a recursive semantic patch, not an edit-command language.
- Array index selectors extend ordinary patch addressing; there are no `insert`, `remove`, `move`, or generalized query operations.

> Retention is not activation. Hot state carries what must matter now; lazy state preserves what may matter later; an explicit read activates only the current trajectory.

## Goals

- Preserve large or infrequently needed semantic state without linearly growing baseline context.
- Keep values domain-native: strings remain strings, arrays of strings remain arrays of strings, and objects exist only when the domain needs objects.
- Navigate current, scoped, effective, and retained historical state through one read protocol.
- Make every successful read exact: return everything requested or fail, never silently truncate.
- Keep runtime revision, CAS, and publication mechanics out of model-facing results.
- Preserve one lock/CAS publication cohort and one causal lineage across hot and lazy mutations.

## Non-goals

- Mandatory entry objects, stable IDs, timestamps, or provenance fields inside lazy values.
- A generalized typed query language, free-text search, tags, ranking, or a separate memory subsystem.
- Product-level element limits, pagination, cursors, or partial successful range reads.
- A separate array-edit algebra or mutation tool.
- Automatic promotion of retrieved values into future baseline context.

## Semantic model

Runtime views select present documented semantic planes in intent-first order, omitting absent fields, empty responses and unknown fields:

```text
global | CWD | session
├── intents
├── contract
├── working
├── artifacts
├── response (session-owned; empty structural slot in Global/CWD)
└── lazy
```

Hot planes:

- `intents`, `contract`, `working` and `artifacts` stay hot in every scope.
- Session-owned `response` is also hot. New Global and CWD states use an empty structural slot, and stored scopes may omit it. Effective uses the highest-priority present response, and each newly accepted answer is written only in Session.
- `intents` may keep compact active direction while referring to large supporting detail in `lazy`.

`lazy` differs only in projection policy:

- It is canonical semantic JSON, validated and versioned with its owning scope.
- Its bodies are excluded from automatic state and recent-transition projections, including lazy writes, replacements and deletions. Empty visible patches/transitions disappear without renumbering history; hot changes remain visible.
- Bounded baseline navigation exposes presence, path and structure without bodies. Explicit current/historical `read_state` values and patches may contain lazy bodies; already communicated native/user/tool/response text is not redacted.
- Reading it does not mutate state, freshness, usage metadata, history, or future context.

### Semantic references

A reference is semantic content, not a runtime type. There are two forms:

- **Structured:** the optional `{"$ref":"cwd.lazy.plan"}` object.
- **Textual:** inside any ordinary string or paragraph, `$` immediately followed by one valid `read_state` path, for example `$effective.lazy.memory[7]`. The prefix separates a deliberate reference from incidental path-like text, and leaves a deterministic seam if code-based parsing is ever justified.

File paths, document sections, URIs, artifact locators, Skill identities and agent identities keep their native syntax.

**What State Flow does with references.** It preserves all forms exactly as ordinary JSON. It does not scan prose, index targets, validate existence, rewrite relative locators, or infer authority, dependency, hydration, execution or completion. The single exception is [intent ownership](#intent-ownership).

When a reference matters, the agent resolves it explicitly: with `read_state` for semantic paths, or with the appropriate external read/tool for other resources. A locator helps retrieval but does not replace content required for the current decision.

**Reference repair is reactive, not a maintenance scan.** The agent does not enumerate, audit or resolve references merely to test them. Only after one requested `read_state` value path is missing does State Flow perform one bounded reverse lookup over current model-patchable semantic planes for exact structured `$ref` and `$path` matches. The reverse lookup searches current reference owners only, never history. If it finds matches, the tool returns `{value:null, hint:[{type:"dangling-reference", message, paths}]}`:

- `hint` is explicit top-level metadata, not state data. Its conditional message describes unavailability.
- Its path array contains at most three runtime-verified current reference owners, not verified new locations of the target.
- The hint contains no lazy bodies and proves neither prior existence, retention nor relocation.
- The null sentinel is never returned alone for this case.

Without a durable match, the ordinary missing-path error remains, and it does not prove the agent invented the path. Keys, patch and multi-path reads keep ordinary all-or-error semantics.

After a hint, the agent may reconcile a proven stale owning value while preserving its surrounding meaning; this applies equally to `$ref` objects and contextual references in prose. None of these prove a durable reference is broken:

- effective-state absence alone (it does not identify the owner);
- unavailable history;
- inaccessible external resources;
- a transient read failure.

**Historical search is task-driven.** Missing paths or runtime hints alone do not require historical search. The agent may choose a targeted historical read when a previous value is useful to the current task, without separate user permission; otherwise it continues without searching. Found values are historical evidence, not automatically current memory. Never automatically restore deleted data, scan all offsets, hydrate bodies or trigger repair inference. A proven stale reference can be repaired within touched work without resurrecting its target.

When present, the `lazy` root must be an object. Its nested values may include arrays, objects, and scalars, for example:

```json
["important thought", "next thought"]
```

```json
{
  "decisions": ["keep one publication barrier"],
  "openQuestions": ["large-value storage layout"]
}
```

```json
42
```

State Flow does not inject IDs, provenance, revisions, range descriptors, or truncation fields into those values.

### Intent ownership

Intents can own the memory they create:

- A structured `{"$ref"}` anywhere inside an `intents` entry means "delete with me" for an existing object key under `working` or `lazy` of the same scope, such as `{"$ref":"cwd.lazy.plan"}` inside `cwd.intents.release`.
- A textual `$cwd.lazy.plan` mention means "I use this" and never owns.

**What happens when a patch deletes an intent key.** State Flow first applies the authored operations, then deletes each owned target that no remaining intent of that scope references, directly, through an ancestor or through a descendant. Everything happens in one atomic cohort, with one revision per changed scope.

- **Supersession works in one patch:** delete the old intent and reference the same targets from its replacement.
- **Writes in the deleting patch do not save a target.** Updates, nested additions and keys created by that same patch are deleted silently along with it, and the accepted record stores only the net deletion. Save survivors to an unowned path instead.
- **Editing is not deleting.** Editing an intent to drop a reference leaves the target as ordinary unowned state. Unowned entries remain legal.

**What is never deleted.** The cascade reads only the `intents` plane of the intent's own scope. These targets are skipped silently, and no patch is rejected, warned about or delayed:

- other scopes;
- plane roots and array elements;
- `contract`/`artifacts`/`response`/`intents` targets;
- unscoped or `effective` paths;
- missing targets;
- keys outside the `read_state` key grammar `[A-Za-z_$][A-Za-z0-9_$-]*`, for example keys with spaces, dots or non-Latin letters.

**Receipt.** `state_updates.cascaded` lists all owner-scoped targets removed by intent deletion, for example `["session.lazy.plans.x", "session.working.draft"]`. Paths only, never lazy bodies or deleted values. The list is deterministic (Global, CWD, Session, then each scope's cascade order), absent when no target cascaded, and never omitted as a predictable write. Nested lazy deletions are reported even when the top-level `lazy_navigation` catalog is unchanged.

**Edge cases:**

- Deletion is scope-local, so an effective read may afterwards show a same-path value inherited from a broader scope. The receipt's `effective` entry then reports that value rather than `deleted: true`; `cascaded` still identifies the deleted owner path.
- Concurrent shared writers keep last-accepted-wins behaviour: a later write into an intent another session already deleted simply recreates a partial intent.

**History and limits.** The accepted patch record stores cascaded keys as explicit deletions, so replay never re-derives them, and nothing is archived beyond ordinary retained history. Ownership adds no validation, unresolved-reference warning, cross-scope cascade, age-based cleanup, size budget, growth notice, archive of deleted entries or automatic hydration.

### Effective lazy overlay

The explicit `effective.lazy` path recursively overlays `global.lazy → cwd.lazy → session.lazy` using the existing scope precedence and conflict semantics. It is read-only as an effective view and is not inserted into ordinary hot baseline state.

Explicit paths such as `global.lazy`, `cwd.lazy`, and `session.lazy` preserve direct ownership access. Deleting a key from an upper scope reveals a lower-scope value exactly as ordinary scope-local deletion does. Hiding an inherited value without changing its owner is not a separate lazy feature.

Active obligations, current constraints, unresolved next actions, and facts required for the next correct action remain hot.

## Progressive `read_state`

### Request shape

`read_state` accepts either one path or an ordered path list:

```json
{
  "path": "cwd.lazy.memory[0..10]",
  "projection": "value"
}
```

```json
{
  "paths": [
    "effective.lazy.rules",
    "cwd.lazy.memory[0..10]",
    "session.working.nextAction"
  ],
  "projection": "value"
}
```

Rules:

- `projection` defaults to `value`.
- A batch uses one projection for every path, evaluates every path against one captured state view, and returns results in request order. Duplicate paths stay duplicate results.
- An absent documented top-level field has value `null`, including an empty or absent `response`; the root view simply omits it.
- If any other path is invalid, the whole read fails; there is no mixed partial result.

The single `path` form is first-class. Top-level `offset` and `scope` inputs are rejected; history and ownership belong in the semantic path itself, such as `cwd[1].lazy.memory`.

### Response shape

There are three projections:

| Projection | Response shape | Meaning |
| --- | --- | --- |
| `value` | `{ "value": ... }` | Exact selected semantic snapshot |
| `keys` | `{ "meta": ..., "keys": ... }` | Minimal structural facts followed by immediate keys |
| `patch` | `{ "patch": ... }` | Historical semantic patch at the selected boundary |

A single-path request returns one payload. A multi-path request returns positionally aligned arrays under the same projection fields.

One missing value path with exact current durable references returns this sentinel instead; it is diagnostic metadata, not semantic state:

```json
{ "value": null, "hint": [{ "type": "dangling-reference", "message": "The requested value is unavailable in the selected state. These current values reference that path, not a verified new location. Use this evidence if relevant to the task.", "paths": ["cwd.working.note"] }] }
```

What each projection contains:

- `value` deliberately mirrors the effective-state snapshot injected at iteration start: semantic state without revision, provenance, range, transport or storage fields.
- `patch` likewise contains only the selected semantic patch.
- Only structural discovery earns `meta`, and `keys` stays the final and most valuable field in that response.

The response repeats neither the requested path nor the projection, and returns no internal revision. Runtime owns revision selection, locking, CAS and publication; echoing that machinery would not help the model be correct.

Errors use the normal tool-error channel, never successful JSON with an `error` field. The dangling-reference sentinel above is the only missing-path exception; keys, patch, multi-path and unmatched value reads still fail.

### Path and range model

Unscoped semantic paths alias current effective state. Explicit roots and selectors address:

- `effective` for the current composed overlay or an indexed historical effective root.
- `global`, `cwd`, and `session` for explicit current or historical scopes.
- `lazy` beneath `effective` or an explicit scope.
- Existing retained temporal selectors using accepted-transition semantics.
- Object members and array indices.
- Canonical half-open array ranges `[start:end]`, selecting indices `start <= i < end`; `[start..end]` is an accepted fallback spelling.

Examples:

```text
effective.lazy
effective.working.nextAction
cwd.lazy.memory
cwd.lazy.memory[4]
cwd.lazy.memory[10:20]
session[3].lazy.investigation
```

Indices are zero-based. Negative indices, open-ended ranges, steps, predicates, wildcards, unions, and cross-array expressions are rejected.

A range must fit entirely within the current array. If an array has length 10, `[0:10]` and `[10:10]` are valid, while `[0:15]` and `[11:11]` fail. The fallback `..` spelling has identical semantics. A successful result always contains exactly the requested range. State Flow never returns a shorter successful range with truncation metadata.

All projections use the same strict member grammar; no separate lazy path language exists.

## Projections

### `value`

`value` returns the exact selected scalar, object, array, item, or range:

```json
{
  "path": "cwd.lazy.memory[0..3]"
}
```

```json
{
  "value": [
    "first thought",
    "second thought",
    "third thought"
  ]
}
```

A whole-value request returns the whole value. State Flow imposes no semantic element-count limit, pagination, cursor, or silent truncation. Host, model-context, and transport ceilings remain external operational constraints and surface as ordinary failures rather than partial semantic success.

A multi-path value response is positional:

```json
{
  "value": [
    {"ranges": "Ranges are half-open."},
    ["first thought", "second thought"],
    "Implement progressive reads."
  ]
}
```

### `keys`

`keys` combines the minimum structural facts needed to navigate with the complete immediate named keys and child kinds:

```json
{
  "path": "effective.lazy",
  "projection": "keys"
}
```

```json
{
  "meta": {
    "type": "object",
    "size": 3,
    "sources": ["global", "cwd"]
  },
  "keys": {
    "memory": "array",
    "rules": "object",
    "soul": "array"
  }
}
```

For an explicitly scoped object, redundant ownership is omitted:

```json
{
  "meta": {
    "type": "object",
    "size": 2
  },
  "keys": {
    "ranges": "string",
    "publication": "object"
  }
}
```

Arrays and scalars have no named child keys. Their `keys` result stays structurally uniform while `meta` supplies the only useful navigation fact:

```json
{
  "meta": {
    "type": "array",
    "length": 10000
  },
  "keys": []
}
```

```json
{
  "meta": {
    "type": "string",
    "length": 18420
  },
  "keys": []
}
```

```json
{
  "meta": {
    "type": "number"
  },
  "keys": []
}
```

Progressive discovery comes from selecting a deeper path, not from key pagination or enumerating every array index. State Flow does not add `limit`, `cursor`, or `truncated` fields.

The read-level `meta` object is intentionally narrow and unrelated to persisted scope `meta.json` except for the generic word “metadata.” Its closed initial schema contains only:

- `type` always.
- `size` for objects, meaning immediate named-key count.
- `length` for arrays and strings.
- `sources` only when an effective view actually combines or resolves scope ownership.

It never exposes temporal boundaries, runtime identity, revisions, publication state, provenance registries, timestamps, encoded sizes, cache/index details, diagnostics, or arbitrary copied fields from `meta.json`. Adding a metadata field requires evidence that it changes the agent's next read decision; diagnostic convenience alone is insufficient.

A multi-path `keys` response keeps both arrays positional and places `keys` last:

```json
{
  "meta": [
    {"type": "array", "length": 10000},
    {"type": "object", "size": 2}
  ],
  "keys": [
    [],
    {"ranges": "string", "publication": "object"}
  ]
}
```

### `patch`

`patch` explains change rather than materialized state:

```json
{
  "path": "cwd[3].lazy.memory",
  "projection": "patch"
}
```

An indexed change:

```json
{
  "patch": {
    "[1]": "corrected thought"
  }
}
```

A whole-array replacement:

```json
{
  "patch": [
    "new first thought",
    "new second thought"
  ]
}
```

No change to the selected path at that boundary:

```json
{
  "patch": {}
}
```

Deletion of the selected object key:

```json
{
  "patch": null
}
```

This is unambiguous because empty supplied semantic patches are invalid no-ops, while `null` is patch deletion syntax and cannot be retained semantic state. History before the active origin or outside the retained hot window fails explicitly.

## Baseline lazy hint

Baseline inference receives ordinary hot effective state plus a fixed-shape lazy availability hint. The hint contains navigation only, not lazy bodies, a catalog, pagination state, or per-value metadata.

It may identify:

- Whether effective lazy state exists.
- Its immediate top-level keys and kinds.
- The exact `read_state` path for deeper inspection.

The hint is emitted as `lazy_navigation` with `available` and the exact root `path`. For an object root it includes the complete immediate `keys` → child-kind map only when there are at most 32 keys and its canonical JSON is at most 1,024 characters. Otherwise it omits `keys` entirely rather than presenting a partial catalog. This keeps the hint bounded while `read_state` itself returns every explicitly requested value or key set; omission from the hint never makes lazy state unreachable.

## Activation and promotion

A lazy read enriches only the current tool-result trajectory. It does not:

- Enter ordinary effective baseline state.
- Persist in later baseline inference automatically.
- Become authoritative merely because it was retrieved.
- Update semantic freshness, usage counters, or history.

When a retrieved fact becomes necessary for future correctness, the agent promotes a distilled consequence through an ordinary hot `patch_state` mutation:

- Durable requirement or decision → `contract`.
- Current fact, uncertainty, or continuation → `working`.
- Source-addressed reusable compilation → `artifacts`.
- Historical support with no current consequence → remain under `lazy`.

## `patch_state` and arrays

`patch_state` remains the sole semantic mutation and publication barrier. There is no `edits` array and no operation vocabulary such as `replace`, `insert`, `remove`, or `move`.

Existing recursive semantics continue:

- An object recursively patches an object.
- A scalar replaces the selected scalar/value.
- An array replaces the selected array as a whole.
- `null` deletes an object key and remains invalid as retained semantic state.

Array index selectors extend recursive addressing:

```json
{
  "cwd": {
    "lazy": {
      "memory": {
        "[1]": "corrected second thought",
        "[4]": "corrected fifth thought"
      }
    }
  }
}
```

Normative index behavior:

- `"[N]"` addresses an existing zero-based element of the retained array.
- Every addressed index must exist against the one captured publication basis.
- If any index is invalid, the entire `patch_state` call fails and publishes nothing.
- An indexed scalar or array value is replaced.
- An indexed object receives the ordinary recursive object patch; the materialized element is the resulting replacement value at that index.
- Multiple indexed and object changes in one call share one validation and one lock/CAS publication cohort.
- Index syntax is reserved and distinct from an ordinary object key such as `"1"`.

Nested addressing remains ordinary patch structure:

```json
{
  "cwd": {
    "lazy": {
      "groups": {
        "[0]": {
          "notes": {
            "[1]": "corrected note"
          }
        }
      }
    }
  }
}
```

Local insertion, removal with shifting, movement, predicates, and ID addressing are intentionally absent. A caller that needs structural array changes reads the array, constructs the desired ordinary JSON value, and replaces the array. A richer mutation language is considered only if measured real workloads prove whole-array replacement inadequate.

## Publication and storage

Lazy mutations inherit existing guarantees:

- One accepted transition identity across all changed scopes and planes.
- One lock/CAS publication cohort.
- Atomic hot-plus-lazy multi-scope changes.
- Scope-local deletion and effective revelation semantics.
- Exact retained-boundary selection on restore and branch navigation.
- Read-only discovery with no commit, timestamp update, or transition.

Lazy trees are co-located in canonical scope checkpoints/tails and use the same bounded lineage as hot state. The [performance guide](performance.md) owns current synthetic workloads and measurement limits. No separate lazy index or sharded authority exists; any future layout change requires measured need and must preserve canonical semantics, ownership, and one causal lineage.

## Failure semantics

- A nonexistent path, wrong target kind, malformed selector, or out-of-bounds index/range is a tool error.
- One invalid member of a path batch fails the entire read before returning partial success.
- One invalid indexed patch fails the entire mutation before publication.
- Malformed canonical lazy data fails the dependent scope/runtime load without rewriting retained bytes; it is not silently discarded to manufacture valid hot state.
- Invalid reads and rejected patches create no semantic transition and preserve accepted hot state.
- Independent scopes remain usable only where the ordinary filesystem/recovery contract proves their authority.

## Normative invariants

1. **Object root, ordinary JSON children**: Every runtime scope view has a lazy object, defaulting to `{}` when absent from stored semantics. Its nested values contain domain semantics, never mandatory State Flow record wrappers.
2. **Semantic snapshots**: `value` contains only the selected state snapshot and `patch` only the selected semantic patch; `keys` alone adds closed structural `meta` before `keys`.
3. **Exact success**: A successful read returns everything requested; it never truncates or paginates silently.
4. **Runtime-owned concurrency**: Revisions, locks, and CAS remain internal unless explicitly needed for diagnostics.
5. **Hot-state safety**: Anything required for the next correct action remains hot.
6. **Retention/activation independence**: Growing lazy state does not hydrate its body into baseline context.
7. **One read protocol**: Current values, effective lazy values, structure, metadata, and retained patches share one address model.
8. **No activation by side effect**: Reads cannot change future inference context.
9. **Explicit frontier crossing**: Only a visible hot-state patch promotes a lazy consequence.
10. **Patch remains patch**: Array indices extend recursive addressing without introducing an edit-command language.
11. **Index safety**: Array indices are interpreted only against one captured basis under lock/CAS.
12. **Failure preservation**: Invalid reads or patches preserve accepted bytes; canonical corruption fails closed rather than granting partial authority.

## Validation contract

Before release, implementation evidence must prove:

- Lazy bodies do not enter ordinary baseline effective state.
- Baseline lazy navigation remains bounded as lazy body size grows.
- Single and ordered multi-path reads preserve exact request order and one captured state view.
- A failing path produces no partial batch result.
- Every projection returns exactly its matching top-level key.
- `value` returns complete selected values and exact valid ranges without product pagination or truncation.
- Out-of-bounds indices and ranges fail, including batch and boundary cases.
- `keys` returns minimal closed-schema `meta` followed by complete immediate named-key structure without descendant values or array-index enumeration.
- Read-level `meta` cannot leak or copy persisted `meta.json`, runtime revision, temporal, publication, provenance, cache, or diagnostic fields.
- `patch` selects the same causal boundary as existing historical reads and distinguishes no-change from deletion.
- `effective.lazy` follows global → CWD → session overlay while explicit scope paths preserve ownership.
- Reads create no semantic transition, Git commit, publication, freshness update, or future activation.
- Whole-array replacement and indexed scalar, array, object, nested, multi-index, and stale-basis patches remain atomic.
- Restore and fork select lazy state from the same retained canonical boundary as the rest of the owning session scope.
- Malformed canonical lazy data fails closed without rewriting the store; rejected queries and patches preserve accepted hot state.
- Unsupported store formats and `read_state` inputs fail actionably without rewriting retained bytes.

## Limits and change authority

Retained hot history is the entire semantic history available to these readers; cold Git-history access is unsupported. Names must fit the shared path grammar. Generalized querying, sharding, mandatory record objects, metadata envelopes, pagination, and a second mutation language are not implied future work and require a separate evidence-backed design decision.

The [canonical backlog](../BACKLOG.md) owns remaining implementation and release gates. This contract is not a parallel delivery plan.

The stopping rule is conceptual economy: ordinary JSON, one effective lazy overlay, pure state and patch snapshots, one narrow structural `meta` + `keys` projection, recursive patches with indexed array addressing, and one mutation/publication barrier.
