# 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 a required object-root semantic plane in every scope. Nested lazy values are ordinary JSON: 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

Each scope contains six semantic planes in intent-first presentation order:

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

`intents`, `contract`, `working`, and `artifacts` remain hot in every scope. Session-owned `response` is also hot; Global and CWD keep only its required empty structural slot, and Effective inherits the Session value. `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. The optional `{"$ref":"cwd.lazy.plan"}` object provides the structured state-reference form. Inside any ordinary string or paragraph, a semantic-state reference uses `$` 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 provides a deterministic seam if code-based parsing is ever justified. File paths, document sections, URIs, artifact locators, Skill identities, and agent identities retain their native syntax.

State Flow 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. When a reference matters, the agent resolves it explicitly with `read_state` for semantic paths or the appropriate external read/tool for other resources. A locator supports 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. If found, the tool returns `{value:null, hint:[{type:"dangling-reference", message, paths}]}`. `hint` is explicit top-level metadata rather than state data; its conditional message describes unavailability, and 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. Keys, patch, and multi-path reads keep ordinary all-or-error semantics; no durable match retains the missing-path error and does not prove the agent invented the path. The agent may then reconcile a proven stale owning value while preserving surrounding meaning. This applies equally to `$ref` objects and contextual references in prose. Effective-state absence alone does not identify the owner, and unavailable history, inaccessible external resources, or transient read failure do not prove that a durable reference is broken.

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 continue 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. The reverse lookup searches current reference owners only, never history. A proven stale reference can be repaired within touched work without resurrecting its target.

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.

### 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"
}
```

`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 remain duplicate results. If any path is invalid, the whole read fails; there is no mixed partial result.

The single `path` form is first-class. The retired 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 instead returns `{ "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"] }] }`; this explicit sentinel is diagnostic metadata, not semantic state.

`value` otherwise deliberately mirrors the effective-state snapshot injected at iteration start: it is 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` remains the final and most valuable field in that response.

The response does not repeat the requested path or projection and does not return an internal revision. Runtime owns revision selection, locking, CAS, and publication; the model cannot improve correctness by echoing that machinery.

Errors use the normal tool-error channel rather than successful JSON containing an `error` field. The explicit dangling-reference sentinel above is the sole 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. Git-era publication and cold-restoration measurements do not describe this implementation. 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 scope has a lazy object whose 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 predecessor stores and retired `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.
