Skip to content

fix(service-settings)!: a json setting persists a JSON object or array, and the console sends one - #31279

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/project-thread-4ntp5s
Nov 8, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/project-thread-4ntp5s

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread

Fixes #31241

Clause-②: yes (narrowing) — the settings save door and the env door start refusing a json value that is not a JSON object or array, which they accept today; ADR-0007:123 already declares json persists an object, so the narrowing enforces the declared contract and widens nothing
Metadata-protocol: none
Skills-docs: updated here — content/docs/protocol/kernel/config-resolution.mdx (the env coercion paragraph said an unparseable value is used as the raw string; a json key now rejects it). skills/** names the three lifecycle keys but never spells how a json value is sent, so nothing it teaches changes.

BREAKING accept-set narrowing at the settings doors (@objectstack/service-settings). PUT /api/settings/:namespace refuses a string, number or boolean under a json key with 400 SETTINGS_VALIDATION and one invalid_type field error, and stores nothing; an OS_* override for a json key whose text does not parse to an object or array is not in force (one error line, the key resolves from the next layer). Migration: send the parsed value, not its text: { "quotas": { "sys_audit_log": 1000 } }, not { "quotas": "{\"sys_audit_log\": 1000}" }. Values already stored as JSON text: quota text is read as the object it holds, with one warning; retention-override text is not applied until an administrator saves the Data Lifecycle page once: the page opens with that text already marked as an unsaved change and sends it as an object, so one Save with no edit puts it in force (one error names the key and the fix until then). ADR-0087 disposition: not-required (no-migration-prescription), one marker, in .changeset/31241-json-setting-persists-object.md.

维护者速读

  • 改了什么: 「数据生命周期」设置页的三个 JSON 框(保留期覆盖、行数配额、按类默认配额)以前保存的是一段文字,平台读不懂,配额和覆盖全部静默失效。现在控制台保存时先把文字解析成对象再发送,JSON 写错会在该行提示并拦下保存;服务端保存口和环境变量口也不再接受文字。以前存成文字的 JSON 值,打开设置页时会自动标成 1 处未保存并在该行提示,管理员直接点保存就改存为对象。
  • 为什么改: 管理员看到「已保存」,以为平台在执行配额和保留期,实际上什么都没执行,也没有任何日志,直到存储或合规出问题才发现(settings: a json setting saved from the console is stored as a string, so retention overrides and row quotas silently do nothing #31241,p2)。
  • 风险与回滚: 以前存成文字的配额升级后开始告警,每条打一次 warn;保留期覆盖的旧文字不会生效,只报一次 error,需要管理员打开「数据生命周期」页点一次保存才生效(页面打开就显示未保存,不用改任何内容),所以不会因为旧值意外删数据。打开含旧文字的设置页时离开页面会先确认,因为它被算作未保存的更改。直接调 API 把 JSON 当文字发送的脚本会收到 400。回滚方式是 revert。
  • 先看: packages/objectql/src/lifecycle/lifecycle-service.ts 的 governanceMap(旧文字的读取与告警);packages/services/service-settings/src/settings-service.ts 的 jsonShapeMiss 与它在 validatePatch、effectiveEnvOverride 的两处调用;ui/apps/console/src/pages/settings/SettingsView.tsx 的 outgoingSettingsPatch 与 storedJsonTextDraft(加载时把已存文字放进 draft)。
  • 两分钟验证: cd packages/services/service-settings && pnpm exec vitest run src/settings-json-shape-31241.test.ts,预期 8 个全过;其中第一个用控制台的旧请求体,得到 400 和 invalid_type,什么都不存。
  • 需要你做的: 无,席位落地。

What changes

  • @objectstack/service-settings: one shape judgment for both doors (jsonShapeMiss). A json key takes a plain JSON object or an array; null is a clear. validatePatch refuses anything else (a string, a blank string included, a number, a boolean) with one FieldError per key: code: 'invalid_type', constraint: { type: 'json' }, a message that names what to send and never the value. It judges only the keys the save touches, like every other family, so a string already stored under another key does not block an unrelated save. effectiveEnvOverride rejects an override that did not parse to an object or array through the existing reportRejectedEnvOverride (one error line per variable and value, carrying the value's shape, never its content).
  • @objectstack/objectql: values already stored as text (LifecycleService.governanceMap). loadGovernance reads retention_overrides, quotas and quota_defaults through it, at the global read and at each organization's read. For quotas and quota_defaults, text that parses to a plain object is applied as that object, with one warn per stored value naming the key (and the organization, for a tenant-scope row) and the fix: save the page again. They only raise alerts and never delete. For retention_overrides (LEGACY_TEXT_NOT_APPLIED_KEYS), stored text is not applied: the declared windows stay in force and one error per stored value names the key (and the organization) and the fix, open Setup → Data Lifecycle, check the value and save the page once. The reaper deletes on these windows, so a window that was never in force must not start deleting rows on upgrade without an administrator acting (seat review 6713609079). Anything else that is not a plain object applies nothing, as before, and is now said once at error. Choice: a reader repair, not a migration. These three keys are the only json specifiers in packages/ and LifecycleService is their only reader, so the repair needs no rewrite of sys_setting / sys_platform_setting rows, and the settings read API keeps returning what is stored. Saving the page once stores the object, and the warning or error stops.
  • @object-ui/console: the field sends the value, never the text, and offers a value stored as text for one save (SettingsView). At Save, each json key in the draft is parsed: valid JSON is sent as the object or array, blank text as null. Text that does not parse blocks Save on that row through the existing pre-save path (no request, the row marked, focus on it) with the widget's own translated sentence fields.object.invalidJson. Only a key in the draft is judged. At load, storedJsonTextDraft puts every json key whose stored value is a string, and is not locked, into the draft as it stands, so the page opens with the save bar (one unsaved change per such key) and the row carries a note, console.settingsView.jsonStoredAsText (new key, all 11 packs; zh-Hant regenerated from zh-Hans). One Save with no edit sends the parsed object. Stored text that does not parse is seeded too and stops Save on its row until it is fixed or cleared. A locked row (env override) is neither seeded nor noted. Seeding also arms the existing leave guard, since the page holds an unsaved change.
  • Docs: config-resolution.mdx states the json exception to "the raw string is used".
  • Changeset: .changeset/31241-json-setting-persists-object.md, patch for all three packages, with both declaration lines, the BREAKING section, its migration and the ADR-0087 marker.

Tests

  • packages/services/service-settings/src/settings-json-shape-31241.test.ts (8), over real ObjectQL, the platform setting objects, the real route and the shipped lifecycleSettingsManifest: the console's old body is refused (400, invalid_type on both keys, nothing stored); a number, a boolean and a blank string are refused; an object and an array are stored as themselves; a clear falls back to the default; a stored string does not block an unrelated save; env JSON text of an object is in force, while unparseable text and a scalar are not, each with one error line.
  • packages/objectql/src/lifecycle/lifecycle-service.governance-map-text.test.ts (6): an object control alerts with no log; quotas stored as text alert quota-exceeded and warn once over two sweeps; quota_defaults as text applies; a global retention_overrides as text moves the reap cutoff to 90d; an organization's retention_overrides as text runs that organization's pass on its 2y window and the global pass excludes it; text that is not a JSON object applies nothing and errors once.
  • ui/apps/console/src/pages/settings/__tests__/SettingsView.jsonSendsObject-31241.test.tsx (7): the patch carries the object, an array and a clear; text that does not parse blocks Save; a locked stored string is not seeded and does not block an unrelated save; a stored string shows the save bar and the note on load; Save with no edit PUTs { quotas: { sys_audit_log: 1000 } } and the bar and note go; stored text that does not parse is seeded and stops Save on its row.

Acceptance notes

  • Measured retention_overrides this time. Global and tenant scope both, through a sweep (the lifecycle test above), not code-read.
  • A JSON value that parses but is not an object or array (for example 42) is not blocked by the console. It is sent parsed and the server refuses it inline with its own sentence, which is English. The server already refuses it, so the page does not add a second refusal.
  • The claim's file surface did not name content/docs/protocol/kernel/config-resolution.mdx (the env paragraph became false with this change) or the 11 locale packs ui/packages/i18n/src/locales/*.ts (the new note key, round 2), so both are changed here; the seat amends the claim.
  • The changeset carries the ADR-0087 marker. This body states the disposition in prose, because a body sent to GitHub carries no HTML comment other than the attribution line.

Verification

Ablations, each through scripts/ablation-replace.mjs (mutation landed, test red, restored blob equal to HEAD and git diff HEAD empty), on head 4246cad1 (the code under them is unchanged since, except the A3/A4 file's error line now calls its logger channel directly):

# Mutation Red
A1 validatePatch: jsonShapeMiss(type, value) → null 2/8: the old console body answered expected 200 to be 400; the scalar case too
A2 env door: jsonShapeMiss(...) → null 2/8: expected '{"sys_audit_log": 1000' to deeply equal {}, expected 5 to deeply equal {}
A3 governanceMap: isPlainRecord(parsed) → false && … 4/6: both quota cases expected [] to deeply equal [quota-exceeded], both retention cases
A4 loadGovernance: the map keys bypass governanceMap 5/6, the error case included (called 0 times)
A5 console: outgoingSettingsPatch(...) → draft 3/4: expected { quotas: '[1, 2]', … } and the text cases
A6 console: the parse refusal disabled 1/4: expected null to be truthy (no alert on the row)
A7 console: load seeds {} instead of storedJsonTextDraft(p) 3/7: the three stored-text cases (no save bar, no request, no row alert)
A8 console: !resolved.locked dropped from isStoredJsonText 1/7: the locked case (save bar drawn on load, expected … to be null)
A9 console: the note condition set to false 1/7: the load case (no note on the row)

Local runs (shared container, under os-verify-lock; package suites before the origin/main merge, the three new files and src/lifecycle/ re-run on the final head e4d9a02a, green): @objectstack/service-settings test 72 files / 1379 tests and typecheck, exit 0; @objectstack/objectql test 565 files / 11431 tests, exit 0, typecheck and src/lifecycle/ 6 files / 150 tests, exit 0; console pages/settings/ 39 files / 317 tests, exit 0. Console type-check NOT MEASURED locally (it needs every ui/ package built; CI's Type Check UI carries it); tsc --noEmit on the console shows no error in the two changed files beyond the unbuilt-module family. Round 2 on 29159a9b: console pages/settings/ 39 files / 320 tests, ui/packages/i18n 1804 tests, the seven check:i18n* scripts, all exit 0.

Gates, on head 29159a9b: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 101 commands; each ran with stdin from /dev/null, all :: exit 0. --ran: "101 derived, 101 run, 0 NOT-MEASURED". Round 1 on e4d9a02a: 100 derived, 100 run, 0 NOT-MEASURED, 0 UNRUN. The first round (before the merge) caught one real red, check:logger-receiver-detach on the new error line in governanceMap (logger.error ?? logger.warn read into a local), fixed in 748e6b27; its check:dts-closure and check:dual-build-cjs-loads reds were a closure build running alongside and a missing dist/ (exit 3), both green on the final head. pnpm lint: exit 0. ui eslint on the two changed console files: 0 errors (4 warnings, all on lines this diff does not touch).

Dogfood (narrowing arm), full battery on the final head e4d9a02a: closure built with pnpm --filter '@objectstack/dogfood^...' build, then pnpm --filter @objectstack/dogfood test under the lock, no shard and no path: Test Files 435 passed | 3 skipped (438), Tests 3609 passed | 41 skipped (3650), VERDICT command-exit 0. Not re-run for round 2: it changes only the console, the locale packs and the changeset, none of which the battery loads.

🤖 Generated with Claude Code

…y, and the console sends one

The console's json field sent the textarea's text and the settings save
door stored it, so Data Lifecycle quotas and retention overrides were
saved as strings and the lifecycle sweep read them as absent.

- service-settings: validatePatch refuses a json value that is not a
  JSON object or array (invalid_type), and the env door rejects such an
  OS_* override; one helper (jsonShapeMiss) for both doors.
- objectql: LifecycleService reads a map setting stored as JSON text as
  the object it holds, with one warning per stored value; a value that
  is not an object is reported once at error.
- console: the json field's text is parsed at Save; text that does not
  parse blocks Save on that row.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2dxcqiQvzKhr5odTPe
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 7, 2026
@github-actions

github-actions Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/objectql, @objectstack/service-settings, touching 14 documentable anchor(s).

12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/error-catalog.mdx (via invalid_type (literal, a string literal in validatePatch))
  • content/docs/api/error-handling-server.mdx (via invalid_type (literal, a string literal in validatePatch))
  • content/docs/automation/flows.mdx (via invalid_type (literal, a string literal in validatePatch))
  • content/docs/data-modeling/drivers.mdx (via LifecycleService (symbol, a top-level class))
  • content/docs/data-modeling/objects.mdx (via LifecycleService (symbol, a top-level class), retention_overrides (literal, a string literal in GOVERNANCE_MAP_KEYS; a string literal in LEGACY_TEXT_NOT_APPLIED_KEYS))
  • content/docs/deployment/cli.mdx (via invalid_type (literal, a string literal in validatePatch))
  • content/docs/frontend/cli.mdx (via invalid_type (literal, a string literal in validatePatch))
  • content/docs/kernel/services.mdx (via LifecycleService (symbol, a top-level class))
  • content/docs/permissions/attachments-access.mdx (via LifecycleService (symbol, a top-level class))
  • content/docs/protocol/kernel/config-resolution.mdx (via invalid_type (literal, a string literal in validatePatch))
  • content/docs/protocol/knowledge.mdx (via LifecycleService (symbol, a top-level class))
  • content/docs/protocol/objectui/concept.mdx (via invalid_type (literal, a string literal in validatePatch))

⛔ 5 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via LifecycleService (symbol, a top-level class), retention_overrides (literal, a string literal in GOVERNANCE_MAP_KEYS; a string literal in LEGACY_TEXT_NOT_APPLIED_KEYS))
  • content/docs/releases/v15.mdx (via LifecycleService (symbol, a top-level class))
  • content/docs/releases/v17/17-0.mdx (via invalid_type (literal, a string literal in validatePatch))
  • content/docs/releases/v17/17-5.mdx (via retention_overrides (literal, a string literal in GOVERNANCE_MAP_KEYS; a string literal in LEGACY_TEXT_NOT_APPLIED_KEYS))
  • content/docs/releases/v17/17-6.mdx (via invalid_type (literal, a string literal in validatePatch))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 63 of 226 client-bound route-ledger rows — the other 163 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 163: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 104 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 27 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json cba7d8ae719f22e80ae7b336e4ee3a15d5c3057b → packageMentionDocs.

Which tree this was computed on

This run read content/docs from cb589e93abba7f8f61cfb9692daecbe6235ed46f — the merge of head fcafa11dda483eae6554253a1c86bbe0e80d4966 into base cba7d8ae719f22e80ae7b336e4ee3a15d5c3057b, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cb589e93abba7f8f61cfb9692daecbe6235ed46f && git checkout cb589e93abba7f8f61cfb9692daecbe6235ed46f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cba7d8ae719f22e80ae7b336e4ee3a15d5c3057b fcafa11dda483eae6554253a1c86bbe0e80d4966 && git checkout -B drift-repro cba7d8ae719f22e80ae7b336e4ee3a15d5c3057b && git merge --no-ff fcafa11dda483eae6554253a1c86bbe0e80d4966

node scripts/docs-audit/affected-docs.mjs --json cba7d8ae719f22e80ae7b336e4ee3a15d5c3057b

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs cba7d8ae719f22e80ae7b336e4ee3a15d5c3057b → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

Contract review

Head-sha: e4d9a02a4d

CHANGES · in-seat review (no spec / skills / ADR hunk, so no Fable round) · 2026-11-07T22:03Z

I read the whole diff: settings-service.ts (jsonShapeMiss at both doors), lifecycle-service.ts (governanceMap), SettingsView.tsx (parseJsonDraft / outgoingSettingsPatch / the json pre-save block), the changeset, the config-resolution.mdx paragraph, and the three new test files. The door and the console halves meet the card.

should-fix — legacy text under retention_overrides must not start deleting rows on upgrade without an admin acting. governanceMap applies every stored string that parses to an object, for all three keys. For quotas / quota_defaults that is safe: they only raise quota-exceeded alerts and never delete (lifecycle-service.ts header, "quotas/growth never delete data"). retention_overrides drives the reaper, which batch-deletes rows past maxAge / expireAfter. Today a deployment holds such text dormant, and the default windows are in force. With this PR, the first sweep after the upgrade would start deleting on a window nobody has looked at since the old console swallowed it. The changeset's "check them before you upgrade" asks every operator to read a release note before an irreversible effect, and the seat does not accept that trade.

Fix: keep the read-side repair for quotas and quota_defaults (warn once, apply). For retention_overrides, a stored string is not applied. Log it once at error, naming the key and the one-step fix: open Setup → Data Lifecycle and Save. The console already shows the stored text in the textarea, and a Save now sends the parsed object, so the admin's Save is the explicit consent. Pin it: a legacy string under retention_overrides deletes nothing on a sweep, and the same value saved as an object does. Ablate the guard. Rewrite the changeset's "Values saved as text before this release" paragraph to match.

Accepted as reported:

  1. content/docs/protocol/kernel/config-resolution.mdx is added to the file surface. The paragraph it amends is falsified by this PR for json keys.
  2. The ADR-0087 marker sits in the changeset, and the PR body states the disposition in prose. This is accepted, provided the PR-body guards are green on the next head.
  3. The PR body's line 2 uses the card thread's own link.
  4. Console type-check is left to CI's Type Check UI. The ACCEPT waits for it to be green.
  5. Text that parses to a non-object (42) passes the console and is refused by the server's invalid_type on that row. That is acceptable.

Gates as reported: 100/100 derived exit 0 with --ran reconciled; pnpm lint 0; full dogfood 435 passed / 3 skipped, VERDICT 0; 6 negative pins ablated. The next head re-runs the derived gates. The narrowing is unchanged by the fix, so the dogfood run is not owed again unless the diff grows beyond lifecycle-service.ts, its test and the changeset.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

Contract review

Head-sha: c4ad13974762e3b1622c0d40a1b623e9449c8709

CHANGES · in-seat delta r2 · 2026-11-07T22:35Z

Round 1's should-fix is resolved in code. LEGACY_TEXT_NOT_APPLIED_KEYS = {retention_overrides}: governanceMap returns undefined for that key's legacy text with one error, while quota text still applies with one warn. The global pair and the organization pair are pinned (no 1d reap and no organization pass from text; both appear from the object), and the guard ablation reds 2/8. The changeset and the PR body (rewritten by the seat at the dev's request) match.

should-fix — the remedy the logs print cannot be performed as written. Both the error (retention) and the warn (quotas) tell the admin to open Setup → Data Lifecycle and "save the page once". The console only offers Save when draft has a key (SettingsView.tsx dirtyKeys.length > 0 gates the save bar, and onSave returns on an empty draft). Opening the page and saving therefore sends nothing, so the stored text stays text, and the retention window never applies.

Fix: make the printed remedy true and pin it. The seat prefers the console half: when a json key's resolved value is a string, not locked, the page treats it as needing a save. It seeds that key's draft with the stored text, so the save bar shows one unsaved change, and a short inline note says the value was stored as text and will be saved as an object. A Save then sends the parsed object through outgoingSettingsPatch. Pin it: a stored string under a json key shows the save bar on load, and Save PUTs the object. The fallback, if that is not workable, is wording in both log lines and the changeset that names an edit to the value before saving. Either way, send me the PR-body lines to change and the seat rewrites them.

A json key whose stored value is text (saved by the console before this
fix) and is not locked is put in the draft at load, so the page opens
with the save bar and a note on the row; Save sends the parsed object.
Without it the "save the page once" remedy found no Save button.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2dxcqiQvzKhr5odTPe
@github-actions github-actions Bot added size/xl and removed size/l labels Nov 7, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

席位判读 — Test Core (6/8) 判为 flake,重跑一次 · 2026-11-07T23:28Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

Contract review — ACCEPT

Head-sha: 0e3561f06abac063461b1f88f29ba726d5a5a516

ACCEPT · in-seat (no spec / skills / ADR hunk) · landing held on Test Core · 2026-11-07T23:49Z

  • Round 2's should-fix is resolved (29159a9b). storedJsonTextDraft seeds every unlocked json key whose stored value is a string into the draft at load, so the save bar shows and the row carries console.settingsView.jsonStoredAsText. One Save with no edit PUTs the parsed object through outgoingSettingsPatch. Unparseable stored text is seeded and blocked on its row, and locked rows are neither seeded nor noted. Seven console pins are in place, with ablations A7, A8 and A9 red then restored. The new key ships in all 11 packs and the check:i18n* family is green. The printed remedy ("save the page once") is now true.
  • Round 1's should-fix stands. Legacy retention_overrides text is not applied (LEGACY_TEXT_NOT_APPLIED_KEYS), with one error per stored value. Quota text applies with a warning.
  • 0e3561f06 is a plain merge of origin/main. It brings the test(scenarios): give the external-driver crm run a measured 30s budget and a lighter stand-in #31284 fix, and no file of this PR changed. content/docs/data-modeling/objects.mdx was checked against the docs-drift list and stays accurate. The claim is amended (6714309912), and the PR body is rewritten by the seat at the dev's request.
  • Gates: 101 derived, all exit 0, --ran 0 NOT-MEASURED (on 29159a9b). Full dogfood ran on e4d9a02a (435 passed / 3 skipped, VERDICT 0); rounds 2 and 3 touch nothing the battery loads. On this head every check is green except Test Core (6/8).

Test Core (6/8) is red twice and is not this PR's. Both attempts time out at the 5 s default in examples/app-todo/test/loop-body-iteration.test.ts (ESCALATION: the run reaches notify_owner …), with no assertion failure. The same file is the queue-flake anchor #31294: it ejected #31249 and #31258 from the merge queue in the last 24 h, and it reddened #31243 tonight. This PR does not touch the flow engine or examples/app-todo. No fix exists on main yet; #31284 gave external-driver.test.ts the same kind of measured budget, and that is the likely shape. The seat does not re-run a second time. Landing waits for a fix to #31294 on main; then the dev merges main and the seat enables auto-merge on a green head.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 8, 2026 01:33
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 8, 2026 01:33
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

Landing hold lifted: ready, auto-merge on · 2026-11-08T01:33Z

Head-sha: fcafa11dda

This head is a plain merge of origin/main (0e3561f06 → fcafa11dda), which brings #31312 (cba7d8ae71), the fix for the loop-body-iteration timeout that held this PR. The thread checked that none of the 109 files the merge brings in overlap this PR's 19 files. The ACCEPT on 0e3561f06 stands. check-governed-merges: NOT governed, 1039 lines. The seat has marked the PR ready and enabled auto-merge; it enters the queue once the required checks are green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

settings: a json setting saved from the console is stored as a string, so retention overrides and row quotas silently do nothing

1 participant