Repository navigation
Commit 22c2d6f
feat(spec)!: an agent's memory contract states exactly what the runtime honours — maxEntries and reflectionInterval are required once long-term memory is enabled, longTerm.store is retired, and the block is live (#21413)
Part of #20274
Clause-②: yes (narrowing)
## What this does
Ruling of record 5950198150 (director batch #268 item 4, letter A′,
maintainer 「同意」): **the `agent.memory` contract states exactly what the
runtime honours.** One PR, as the ruling's execution parameters say: the
refinement, the `store` retirement, the row flip, the describes. The
claim amendment 5951618837 adds the two agent-form help texts, made
after PR #21398 landed on `main`.
- **The contract check.** `checkAgentMemoryContract` in
`packages/spec/src/ai/agent.zod.ts` is a refinement on `memory`, because
`reflectionInterval` is `longTerm`'s sibling. It raises one `custom`
issue at the key's own path in each of three cases, and each message
names the key, its path and the remedy:
- `longTerm.enabled: true` without `longTerm.maxEntries` is refused at
`memory.longTerm.maxEntries`.
- `longTerm.enabled: true` without `reflectionInterval` is refused at
`memory.reflectionInterval`. This is the most natural declaration, `{
enabled: true, maxEntries: 5 }`, which cloud refused at turn time.
- `reflectionInterval` while `longTerm` is absent or not enabled is
refused at `memory.reflectionInterval`.
- No default is declared for either number. The check runs only on a
body the shape already accepted, so an unknown key, a wrong type or the
`store` tombstone gets its own complaint and nothing on top of it.
- **`longTerm.store` retires as a whole key,** by the
`spec-property-retirement` playbook:
- **Tombstone.** `retiredKey()` carries the prescription: "the memory
store is platform infrastructure, not agent metadata … Delete the key",
closing with the house `os migrate meta --from 17` sentence. `tsc`
refuses the key as well.
- **Aliases.** The `backend` / `storage` / `provider` aliases pointed at
`store`. They moved to `guidance` and carry the same answer, because an
alias may not target a tombstone (`alias-integrity.test.ts`).
- **D2 conversion `agent-memory-long-term-store-removed`.** Step 18,
`retiredFromLoadPath`, `retiredAfter` 17.6.0, order 56. It deletes the
key from `agents[].memory.longTerm`, losslessly, with one notice per
agent. It supplies neither number. The fixture covers `database`, the
materialized `vector` default and `redis`, for 3 notices, plus two
untouched controls.
- **Registration.** `RETIRED_KEYS_BY_MAJOR[18]` gains the nested
`ai/Agent:memory.longTerm.store`. The entry file is
`18.ai__Agent__memory.longTerm.store.ts`, and the generated region was
written by `gen:migration-registry`.
- **D3 entry `agent-memory-store-retired-and-limits-required`.** Its
`conversionIds` names the D2. It carries the judgement no conversion can
make: the two numbers an enabled `longTerm` now requires. Its
`STEP18_RATIONALE` fragment is at order 61.
- **`acceptRetiredDefaultResidue` is deliberately not adopted** for the
old `vector` default. The measured producer census is zero, and the
ruling's ② names the prescription as the backstop for the unmeasured
tenant population. The replayed D2 heals stored rows and built
artifacts. The reasoning is in the retired-key entry file.
- **Ledger.** `agent.memory` moves `experimental` → `live`, with
`evidenceScope: cross-repo` and `verifiedAt` 2026-10-02.
- The evidence is quoted from the `repo:cloud#1` seat's reading
5946891697 of cloud `ef5a4344`: the reader
`agent-runtime.ts#compileAgentMemory` (via
`AgentRuntime.resolveTurnGuardrails`), the enforcement in
`ai-service.ts`, and the store `agent-memory.ts#AgentMemoryStore`.
- The note attributes all of it to that reading. No seat in this session
read cloud.
- **The window, stated in the note.** At `ef5a4344` the reader still
reads `store`: it honours `database` only and refuses `vector` and
`redis`. Per the ruling, cloud drops `store` in that one reader once
this release reaches its pin, and no earlier.
- `state-counts/agent.md` was regenerated by `gen:liveness-counts`: live
22 → 23, experimental 2 → 1.
- **Describes.**
- `memory` drops `[EXPERIMENTAL — not enforced]`, in the `guardrails` /
`structuredOutput` wording: enforced by the cloud AI runtime, and the
open framework edition does not run agents.
- `longTerm`, `enabled`, `maxEntries` and `reflectionInterval` each
state what the runtime does with them: the newest N notes are recalled,
a note is written every N delivered interactions, and notes beyond N are
evicted.
- The NOTE comment's "forward-looking" is replaced by what is enforced.
- **The agent form.**
- The `memory` row's help text no longer names short-term memory, which
is refused. It now states what memory does and that both numbers are
required once long-term memory is enabled.
- The `planning` row's help text names only `maxIterations`.
- `pnpm i18n:extract` regenerated the `en` leaves, run against a spec
rebuilt from this tree. The `zh-CN`, `ja-JP` and `es-ES` leaves are
authored, not copied.
- **The published JSON Schema.** A value-conditioned requirement is not
in the finite projection list: `dependent-required` is presence-only,
and `refinement-projection.ts` says a value rule stays dropped. So the
new site is annotated as dropped. `dropped-refinements.baseline.json`
gains `memory` under `ai/Agent` and under the four installed-package
schemas that embed agents, which is 5 sites (635 → 640). This is the
corrected entry the build printed.
- **Regenerated:** `content/docs/references/ai/agent.mdx`.
- **Changeset:** `.changeset/20274-agent-memory-contract.md`.
- The levels are `@objectstack/spec` minor and
`@objectstack/platform-objects` patch (for the catalog leaves).
- It carries the BREAKING header, the FROM → TO table and the one-line
remedy: "declare `maxEntries` and `reflectionInterval` when
`longTerm.enabled`; delete `store`".
- It declares `Clause-②: yes (narrowing)` and the ADR-0087 marker
`registered agent-memory-long-term-store-removed,
agent-memory-store-retired-and-limits-required`.
The four surface ratchets (`api-surface`, `authorable-surface`,
`json-schema.manifest`, `api-surface-signatures`) are byte-identical, as
expected for a NESTED key of an inline block: it has no
`authorable-surface/` line of its own. `spec-changes.json` and the
upgrade guide are unchanged, because step 18 is not yet projected;
`PROTOCOL_MAJOR` is 17, the same state as the structured-output
precedent.
## Verification (at `39412feb9e`, after merging `origin/main` at
`ca0dfb658a`)
- `@objectstack/spec` build + `check:generated`: "All 15 generated
artifacts are up to date".
- `@objectstack/spec` `vitest --project local`: 599 files, 17583 passed,
1 todo.
- `@objectstack/spec` `vitest --project repo`: 43 of 49 files, 705
passed. The other 6 are NOT MEASURED; see below.
- `@objectstack/spec` `typecheck` (`tsc --noEmit`, the scripts program
and the test layer): OK. "52 file(s) / 246 error(s)" is exactly the
pinned debt. `tsc --listFiles` over `tsconfig.test.json` lists both
`src/ai/agent.test.ts` and
`src/ai/agent-memory-store-retirement.test.ts`, and none of the 246
errors is in either file. So the `@ts-expect-error` on `store` is live.
- Consumers:
- `@objectstack/lint` reads `liveness/agent.json`: `test` 119 files,
5575 passed; `typecheck` OK.
- `@objectstack/platform-objects` holds the catalogs: `test` 59 files,
949 passed; `typecheck` OK.
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--ran`: "117 derived famil(ies) accounted for — 117 run, 0 NOT-MEASURED
(a DERIVED zero — all 117 recorded an exit code and none of them is 3)".
Each exit code was written to disk before any pipe. The list includes:
- `check:adr-0087-registration`: "1 declared-breaking changeset(s), each
carrying an ADR-0087 disposition … [BREAKING+bang+clause-②-narrowing]
registered agent-memory-long-term-store-removed,
agent-memory-store-retired-and-limits-required";
- `check-changeset-no-major`: "This diff introduces no `major` bump";
- `check:empty-changeset`, `check:liveness`, `check:doc-authoring`,
`check:nul-bytes`, `check:migration-registry`,
`check:cross-package-test-inputs`, `check:i18n`, `check:i18n-stale-fill`
and `check:type-check-debt`, which re-measured in 116.6 s.
- On first runs, before the build closures existed, a few gates exited 3
(PREREQUISITE NOT MET). All were re-run green after building the lint,
client and CLI closures.
- One `check:dts-closure` red was a race with this worktree's own
concurrent spec rebuild. It then named three unrelated packages
(`organizations`, `plugin-approvals`, `verify`) whose `dist` had no
`.d.ts`. They were rebuilt, and it is green.
- `check:i18n-coverage` and `check:i18n-walk-parity` also ran green.
- **ESLint, as a proven narrowing** (the repo-wide `pnpm lint` belongs
to CI):
- The run covered the 13 touched `.ts` files with `--no-inline-config
--format json`: 13 files linted, 0 errors, 0 warnings.
- All 13 are inside the config's population: `ESLint.isPathIgnored` is
false for each.
- The narrowing cannot have excluded anything. `eslint.config.mjs`
enables no type-aware linting (no `parserOptions.project`; its own
comment at `:328` says so), so this diff cannot move the verdict on any
untouched file.
- **Ablation** (committed tree `8965af164d`;
`scripts/ablation-replace.mjs`; each mutation proven on disk by anchor
count and blob hash, and each restore proven by blob equal to HEAD and
an empty `git diff HEAD`). Both test files import `./agent.zod`
relatively, so no `dist` rebuild is involved.
- **The contract check.** The anchor `const enabled =
memory.longTerm?.enabled === true;` was replaced by an early return: 6
failed, 67 passed. The six are the five refusal pins and the D2 lit
control. The two acceptance pins stayed green.
- A first attempt, whose replacement contained its own anchor, was a
no-op. The tool refused it (anchor count 1 → 1), so no test ran.
- **The tombstone.** `store: retiredKey(…)` was replaced by the old
three-value enum: 5 failed, 68 passed. The five are the every-value
refusal, the did-you-mean pin, the tsc/parse pin, the `defineStack`
envelope pin and the artifact boot-door pin.
- The direction is red in both runs, as expected.
**NOT MEASURED**
- Six `@objectstack/spec` repo-project files:
`publish-smoke-boot-failure`, `publish-smoke-port-collision`,
`dist-freshness`, `dist-freshness-adoption`, `build-schemas-check-mode`
and `schema-tree-freshness`. Reason: they drive whole builds and
exercise build tooling this diff does not touch; the precedent PR saw
them cap-killed at the foreground ceiling. They are declared to CI.
- `@objectstack/cli` integration tier. Reason: declared to CI; no CLI
file is touched.
## Fixture and producer census
- **Producers.** Measured at merge base `6d487d2094`, outside
`packages/spec`, in `examples/**` and `packages/**`:
- `longTerm`: 0 non-test files and 0 test files. `reflectionInterval`: 0
and 0.
- The lit control `guardrails` hits 4 non-test files, the four
metadata-form catalogs.
- So no producer exists in this repo.
- Cloud's built-in agents also have none, per the ruling record's own
census. Tenant-authored agents are NOT MEASURED.
- **objectui**, at the dispatch pin `31971ff1e28f` and at the current
pin `89cad75d55` (after PR #21380):
- `longTerm`: 1 file,
`packages/app-shell/src/views/metadata-admin/previews/AgentPreview.tsx`,
a display-only preview that reads `memory.longTerm.store` through an `as
any`. No build break follows. See Acceptance notes.
- `reflectionInterval`: 0. The control `AgentSchema` hits 3 files.
- **Fixtures that pinned the old acceptance, flipped:**
- `packages/spec/src/ai/agent.test.ts` had two. "should accept agent
with memory configuration" wrote `store: 'vector'` and expected it back;
it now writes both numbers and asserts that no `store` materializes.
"should accept all memory store backends" is replaced by acceptance of
memory without enabled long-term memory.
- `packages/lint/src/lint-liveness-properties.test.ts` had three cases
using `agent.memory` as the live `experimental` ledger witness. They
were repointed to `tool.outputSchema`: `memory` is no longer
experimental, and `tool.outputSchema` is the remaining experimental row
with no retirement queued. `agent.lifecycle` has one, the spec half
tracked separately.
- **New pins.**
- `agent.test.ts` gains 8 contract cases.
- The new `src/ai/agent-memory-store-retirement.test.ts`, registered in
`vitest.repo-tests.json`, holds 14 cases: the tombstone at every door,
the D2 conversion, the registration, and the tree-scoped absence walk.
- The walk covers the five roots `@objectstack/spec#test:repo` already
declares in `scripts/cross-package-test-inputs.mjs`. Its matcher judges
a `longTerm` block that writes `store` / `backend` / `storage` /
`provider`, has an anti-vacuity case, and excludes the conversions
fixture, release notes and gitignored output.
- **Other docs.** `content/docs/**` other than the regenerated reference
page: 0 hits.
## Acceptance notes
- **Published skill text that is now false (`skills/**`, Tier H, not
edited here).** `skills/objectstack-ai/SKILL.md:311-313` says "`memory`
is declared only — no runtime reads it". No `skills/**` text names
`longTerm` or `store`. Carrier: the skills seat that took the
`guardrails` half of the same sentence.
- **objectui `AgentPreview.tsx:205-216`** renders `short.maxMessages`
(retired by ADR-0013 D3) and `long.store` (retired here). Both now
render nothing. It is display-only and read through `as any`, so the
Console Pin Gate is unaffected. No carrier is named.
- **The release number in the prescriptions.** They say "removed in
@objectstack/spec 17.7.0", which assumes the next release is a minor
(the label is 17.6.0, published). That is the same assumption the
structured-output prescriptions make.
- **`dropped-refinements.baseline.json`
`measured.refinementSitesThatDidProject`** reads 369, while this tree's
build prints 436. That counter was stale before this PR, and only
`droppedRefinementSites` was moved (+5). No carrier is named.
- **The liveness README's `agent` row** still says "autonomy tier
experimental". After this flip, `lifecycle` is the only experimental
agent row, and its retirement is queued. The precedent flips left the
row as written, and so does this one.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent ceb4a93 commit 22c2d6f
19 files changed
Lines changed: 1066 additions & 56 deletions
File tree
- .changeset
- content/docs/references/ai
- packages
- lint/src
- platform-objects/src/apps/translations
- spec
- liveness
- state-counts
- src
- ai
- conversions
- migrations
- entries
- retired-keys
- semantic
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
| 61 | + | |
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | | - | |
105 | | - | |
| 104 | + | |
| 105 | + | |
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
122 | | - | |
| 122 | + | |
123 | 123 | | |
124 | 124 | | |
125 | | - | |
126 | | - | |
127 | | - | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
128 | 129 | | |
129 | 130 | | |
130 | 131 | | |
| |||
930 | 931 | | |
931 | 932 | | |
932 | 933 | | |
933 | | - | |
| 934 | + | |
934 | 935 | | |
935 | 936 | | |
936 | | - | |
| 937 | + | |
937 | 938 | | |
938 | | - | |
| 939 | + | |
939 | 940 | | |
940 | 941 | | |
941 | 942 | | |
| |||
1596 | 1597 | | |
1597 | 1598 | | |
1598 | 1599 | | |
1599 | | - | |
1600 | | - | |
1601 | | - | |
| 1600 | + | |
| 1601 | + | |
| 1602 | + | |
1602 | 1603 | | |
1603 | 1604 | | |
1604 | | - | |
| 1605 | + | |
1605 | 1606 | | |
1606 | 1607 | | |
1607 | 1608 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2394 | 2394 | | |
2395 | 2395 | | |
2396 | 2396 | | |
2397 | | - | |
| 2397 | + | |
2398 | 2398 | | |
2399 | 2399 | | |
2400 | 2400 | | |
2401 | | - | |
| 2401 | + | |
2402 | 2402 | | |
2403 | 2403 | | |
2404 | 2404 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2394 | 2394 | | |
2395 | 2395 | | |
2396 | 2396 | | |
2397 | | - | |
| 2397 | + | |
2398 | 2398 | | |
2399 | 2399 | | |
2400 | 2400 | | |
2401 | | - | |
| 2401 | + | |
2402 | 2402 | | |
2403 | 2403 | | |
2404 | 2404 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2394 | 2394 | | |
2395 | 2395 | | |
2396 | 2396 | | |
2397 | | - | |
| 2397 | + | |
2398 | 2398 | | |
2399 | 2399 | | |
2400 | 2400 | | |
2401 | | - | |
| 2401 | + | |
2402 | 2402 | | |
2403 | 2403 | | |
2404 | 2404 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2394 | 2394 | | |
2395 | 2395 | | |
2396 | 2396 | | |
2397 | | - | |
| 2397 | + | |
2398 | 2398 | | |
2399 | 2399 | | |
2400 | 2400 | | |
2401 | | - | |
| 2401 | + | |
2402 | 2402 | | |
2403 | 2403 | | |
2404 | 2404 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
63 | 64 | | |
64 | 65 | | |
65 | 66 | | |
| 67 | + | |
66 | 68 | | |
67 | 69 | | |
68 | 70 | | |
| |||
168 | 170 | | |
169 | 171 | | |
170 | 172 | | |
| 173 | + | |
171 | 174 | | |
172 | 175 | | |
173 | 176 | | |
| |||
258 | 261 | | |
259 | 262 | | |
260 | 263 | | |
| 264 | + | |
261 | 265 | | |
262 | 266 | | |
263 | 267 | | |
| |||
308 | 312 | | |
309 | 313 | | |
310 | 314 | | |
| 315 | + | |
311 | 316 | | |
312 | 317 | | |
313 | 318 | | |
| |||
0 commit comments