Repository navigation
feat(spec): offer agent.structuredOutput on the agent form and drop its stale not-enforced-yet ledger row - #21398
Conversation
…stale omit row The reconciliation ledger excused `agent.structuredOutput` as declared but not enforced. The key is enforced now (liveness `live`), and the ledger's own rule hands the offer decision to the enforcement. Studio's composite control carries every child of the block, the free-form JSON Schema record included (raw JSON editor), so the key is offered as a composite beside the other agent blocks and the row goes. Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM Co-authored-by: Claude <noreply@anthropic.com>
…e structuredOutput row `pnpm i18n:extract` adds the agent form's new `structuredOutput` row (label and help text) to the four metadata-form bundles. The zh-CN, ja-JP and es-ES leaves are authored, not left as copies of the English source, and the re-run extract drops their source-hash entries by itself. Changeset: spec minor (a new form offer), platform-objects patch. Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM Co-authored-by: Claude <noreply@anthropic.com>
…he structuredOutput row The per-locale positive control counts the metadata-form labels authored in each of zh-CN, ja-JP and es-ES. The agent form's new row adds one, so the measured count is 660 (was 659) in every locale. Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM Co-authored-by: Claude <noreply@anthropic.com>
The console pin moved on main after the reading; the renderer files the reading cites are unchanged across the move, so the changeset names the renderer rather than one sha. Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 anchor(s) derived from 2 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 447015d16eaca22294645530ba650739e2474492 && git checkout 447015d16eaca22294645530ba650739e2474492
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ecb6ca0258176466767588a6805363387c5777a6 5870ff91d74be07f621380dbfb0c17b9c85c466a && git checkout -B drift-repro ecb6ca0258176466767588a6805363387c5777a6 && git merge --no-ff 5870ff91d74be07f621380dbfb0c17b9c85c466a
node scripts/docs-audit/affected-docs.mjs --json ecb6ca0258176466767588a6805363387c5777a6 |
Contract reviewServed-tier: Inputs: card #21374 (body; comments 5948883894 triage, 5949271559 claim, 5951402104 os-dev-report), PR #21398 (body, 8-file list, net diff against ① Derived judgments
② Semver level
③ Boundary flags
Dev deviations, each answered:
Out-of-scope findings (four, noted not filed), judged:
Check-runs on the head, read 2026-10-02T11:38Z: 23 success, 3 skipped ( Implemented-by: VERDICT: PASS Generated by Claude Code |
…me honours — maxEntries and reflectionInterval are required once long-term memory is enabled, longTerm.store is retired, and the block is live (objectstack-ai#21413) Part of objectstack-ai#20274 Clause-②: yes (narrowing) ## What this does Ruling of record 5950198150 (director batch objectstack-ai#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 objectstack-ai#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 objectstack-ai#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>
Fixes #21374
Clause-②: no
The form reconciliation ledger kept
agent.structuredOutputunoffered on a reason that stopped being true: the key is enforced and gradedlive. This PR deletes that row and decides the offer on a measurement, as triage5948883894directed. The measurement says the Studiocompositecontrol carries the block, including its free-form JSON Schema record, so the key is offered.What changes
packages/spec/src/system/metadata-form-zod-reconciliation.test.tsagent/structuredOutputomitrow (base lines 408 to 414) is deleted. The gate's own rule (lines 307 to 309) gives this decision to the enforcement. No other line changes, and nothing new checks a row'swhytext.packages/spec/src/ai/agent.form.ts{ field: 'structuredOutput', type: 'composite', helpText }. It is spelled likememoryandguardrails, with no hand-writtenfields, so Studio derives the sub-rows from the served JSON Schema.packages/platform-objects/src/apps/translations/*.metadata-forms.generated.tspnpm i18n:extract. Two new leaves per locale: the row's label and help text. Thezh-CN,ja-JPandes-ESleaves are authored, not left as copies of the English source. After the second extract, no locale'ssource-hashes.generated.tsholds an entry for them, so those three files are not in the diff.packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.tsexpected 660 to be 659) before the pin was edited..changeset/21374-agent-structured-output-form-offer.md@objectstack/specminor (a new form offer),@objectstack/platform-objectspatch (two catalog leaves). Neither is breaking.The measurement (triage item 2)
Where it was read: objectui at the
.objectui-shapin31971ff1e28f, from a local clone withgit show.mainmoved the pin to89cad75d5570while this was in flight.SchemaForm.tsxandwidgets.tsxare byte-identical between the two pins (git diff --statis empty), andmerge-base --is-ancestor 31971ff1e28f 89cad75d5570exits 0. So the same reading holds at the pin this branch now carries.What the control is given: the served node, measured with the emitter's own code.
z.toJSONSchemawas run withmarkErasedAuthoringInput, thenstripUnauthorableProperties: the stepstoJsonSchemaSafeinmetadata-protocoltakes.agenttakes the output arm (24 top-level keys), andstructuredOutputis served inline, with no$ref:format,fallbackFormattype: string,enum: [json_object, json_schema]schematype: object,propertyNames: {type: string},additionalProperties: {}, and nopropertiesstrict,retryOnValidationFailuretype: booleanmaxRetriestype: integer,minimum: 0transformPipelinetype: array,items: {type: string, enum: [trim, parse_json, validate]}How a
compositerow renders (packages/app-shell/src/views/metadata-admin/, at the pin):SchemaForm.tsxresolveFieldFace(line 837):fieldSpec.type === 'composite'gives{ kind: 'composite' }.FieldControl(lines 2081 to 2099): the row has nofields, soderivePropertyNames(schema)(line 3376) lists every property of the served node.CompositeField(line 2518) renders oneFieldRowper property, with the child node frompickSubSchema(schema, 'composite', name)(line 2487). A child edit writesonChange({ ...obj, [field]: v }), and an untouched child is never written.resolveFieldWidget, thenresolveFieldFace:schema, the open record:inferWidget(line 519) answersobject-fieldsfortype: object, and no name detector matches.object-fieldsis not a key ofWIDGETS(widgets.tsxline 2934).isObjectFormis false (noproperties), the node is not an object-row array, and the name is not inKNOWN_PASSTHROUGH_WIDGETS(line 230). So the face is{ kind: 'raw-json', hint: 'object-fields' }, which isRawJsonEditor(line 3218). The editor showsJSON.stringify(value, null, 2). Each edit runsJSON.parseand passes the parsed value up. Text that does not parse showsInvalid JSONand passes nothing, and empty text passesundefined.transformPipeline, the enum array:inferWidget(line 510) answersmultiselect, a registered widget, soMultiSelectWidgetrenders it (widgets.tsxline 1396). Its options areitems.enum.toggle()(lines 1428 to 1443) rebuilds the selection asoptions.map(o => o.value).filter(v => set.has(v)): declared enum order, duplicates collapsed, and an empty selection passed up asundefined.formatandfallbackFormatare selects overenum.strictandretryOnValidationFailureare switches, andmaxRetriesis a number input.Round-trip verdicts ("carries" means view it, edit it and save it back unchanged):
schema: carries. The stored record is shown whole as JSON. An untouched value is never re-emitted, so it saves back byte-equal. An edited value saves exactly the JSON that was typed. The parse still refuses an untyped subschema at its path, and that refusal is unchanged.transformPipeline: carries the step set. An untouched value saves back unchanged. A toggle writes the steps in declared enum order (trim,parse_json,validate) with duplicates collapsed, so this control cannot author another order. Every pipeline written in this repo is already in enum order (git grep transformPipeline: the agent tests and the conversion fixtures). This repo cannot read whether the cloud runtime honours another order, so this PR makes no claim about it.action.ai.outputSchemacomes from the sameaiJsonSchemaSlotfactory. It is served with an identical node and is already offered through the action form'sairow, where it reaches the same raw-JSON face. OfferingstructuredOutputadds no new face.Verification
All runs are on this branch. Gates and suites are at the merged head
5870ff91d7(origin/main69a12a0952merged in, with a fullturbo run build --filter='./packages/**'of 71 tasks first).Ablation of the pin. Two legs, both written with
scripts/ablation-replace.mjs(anchor hit, then blob restored equal toHEADandgit diff HEADempty), run at77dedb2c86. The suite waspnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/system/metadata-form-zod-reconciliation.test.ts, which imports the form registry from source, so no build sits between the edit and the test.agent.(root): accepted by the Zod but unauthorable in the form … expected [ 'structuredOutput' ] to deeply equal [].agent.(root).structuredOutput: the form offers it now — drop the ledger entry. The first attempt at leg B was a no-op: the tool refused it because the replacement contained its own anchor, so no test ran. The retry used an anchor the replacement does not contain.HEAD:f37667ab25a0(form) and2350eaed10be(test).Suites:
pnpm --filter @objectstack/platform-objects testTest Files 59 passed (59)·Tests 949 passed (949)pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2(whole package)Test Files 647 passed (647)·Tests 18407 passed | 1 todo (18408)pnpm --filter @objectstack/spec --filter @objectstack/platform-objects run typecheckDonepnpm --filter @objectstack/spec check:generated✓ All 15 generated artifacts are up to dateGates:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 87 commands against merge base69a12a095, with no stale-tree warning. Each was run, and its exit code was recorded before any pipe.--ranreconciliation:87 derived, 87 run, 0 NOT-MEASURED, 0 UNRUN. This includespnpm check:i18n(platform-objects in sync (11 bundle(s))),pnpm check:doc-authoring,pnpm check:nul-bytes, the changeset gates (check-adr-0087-registration: no declared-breaking changeset;check-changeset-no-major: nomajor) andpnpm check:i18n-coverage(621 baselined untranslated string(s), none new, run in addition to the derived list).Semver (A5):
Clause-②: no.AgentSchema's accept set does not move, andcheck:authorable-surfaceandcheck:api-surfaceare green with no regeneration. The form definition ships in@objectstack/spec'sdist, and the catalogs ship in@objectstack/platform-objects'sdist. Both new leaves were found in the builtplatform-objectsdist. So both packages publish a change, and this is a changeset, notskip-changeset.Acceptance notes
Observations only. None meets a filing class here.
schemachild's hint. It shows objectui's announced fallback line,widget object-fields — falling back to JSON until a custom renderer is registered.The action form'sai.outputSchemashows the same line today. Polish on the objectui side.prettify(name), help text from eachdescribe()), so they read in English in every locale.memory's andguardrails' sub-rows read the same way: a schema-derived composite has no catalog key for its children.agent.form.ts.planningnames "strategy, max iterations, replan" (the schema declares onlymaxIterations).memorynames "short-term" (removed, with a refusal and guidance on the key). Nobody owns them yet, so they are noted, not filed.memoryandlifecycleare offered as composites while their describes say[EXPERIMENTAL — not enforced], the reverse of this ledger's not-enforced-yet discipline. That discipline is a reconciliation-ledger convention, not a published contract, and this card does not touch them.Generated by Claude Code