Repository navigation
Description
Part of #14478 — follow-on card 5 of 6, filed by the domain:spec seat (session_01M59rPZZFzqhfMUPFqqZTkf, 2026-09-05T02:55Z) under the maintainer ruling B on #14478 (5548763981, batch #43, 「同意」). The ruling names data/ and ui/ as their own cards; ai/ (1 key) and integration/ (2 keys) are folded here because four tiny cards would each pay the stack's full serial cost for one or two renames. Dispatched after stack cards 1/6–4/6 have PRs to stack on.
Ruling text this card executes (verbatim)
Convert the ~30 authored config durations and the ~16 runtime-emitted measurements […].
(1) the gate's own predicate is the population's definition […]; write the predicate into the follow-on cards so nobody recounts by another rule.
The predicate
Run pnpm --filter @objectstack/spec check:duration-unit-keys on the stacked head; a property whose value is a z.number() / z.int() / z.coerce.number() chain and whose .describe() names a time unit must carry that unit as a token of its key name (Ms / Seconds / Minutes / Hours / Days, plus Millis), and the token must agree with the prose. ⛔ The list below is the gate's own output on e68ae2b58, filtered to src/data/**, src/ui/**, src/ai/**, src/integration/** minus card 1/6's marked key (data/driver/postgres.zod.ts:265 statementTimeout); re-derive on the stacked head before editing.
site (e68ae2b58) |
key | describe unit | gate's suggested name | class |
|---|---|---|---|---|
src/data/driver-nosql.zod.ts:310 |
timeout |
ms | timeoutMs |
authored |
src/data/driver/memory.zod.ts:112 |
autoSaveInterval |
ms | autoSaveIntervalMs |
authored |
src/data/driver/turso.zod.ts:215 |
timeout |
ms | timeoutMs |
authored — packages/drivers/driver-turso/src/spec/turso.zod.ts:104 (the driver's own schema, outside packages/spec, handled by card 6/6) |
src/ui/dashboard.zod.ts:906 |
refreshInterval |
seconds | refreshIntervalSeconds |
authored — |
src/ai/conversation.zod.ts:308 |
duration |
seconds | durationSeconds |
emitted |
src/integration/connector.zod.ts:541 |
monitoringWindow |
ms | monitoringWindowMs |
authored |
src/integration/connector.zod.ts:675 |
interval |
seconds | intervalSeconds |
authored |
Execution notes
- Per key: authored ⇒ D2 conversion with a
retiredKey()tombstone and aRETIRED_KEYS_BY_MAJORrow where on the authorable surface; emitted ⇒ semantic ADR-0087 entry, emitter and every reader renamed in the same PR at the same magnitude. Readers by TYPE across every workspace package (drivers, objectui-facing forms, examples); liveness ledgers, forms, i18n bundles, docs andskills/**prose where they name a key (askills/**hit makes the PR governed: keep it). - Stacked on card 4/6's head; draft PR, base = that branch; body first line
Part of #14478, this card named without a closing keyword. ⛔ NeverFixes #14478. Clause-②: yes⇒needs:contract-reviewon PR and card at creation. Changeset:@objectstack/specminor, BREAKING banner naming every renamed key,adr-0087: registeredwith the ids; readers' packages as derived.packages/spec/src/migrations/registry.tsis written by this stack only; serial order 2 → 3 → 4 → 5 → 6.
Related: #14478 · PR #15626 · stack 1/6 · #15642 (card 6/6).
Activity
ttlkeys with different units in one block, baretimeoutkeys, unit-less tenant timeouts #14478os-zhuang commentedon Sep 5, 2026
分诊 ·
pm:blocked/domain:spec/priority:p2/enhancement⛔ 本席位只分诊:不认领、不派单、不写码、不合并、不裁决 decision-box 卡。方向已由维护者裁决 B(#14478
5548763981,batch #43)定下。Blocked-by: #15676(栈 1/6)
Restart-when: 1/6 的 PR 存在(分支已推、base 指向 #15626)—— ⛔ 不是「1/6 已合并」。整条栈一起落地。
⭐ 人口切分已交叉验证(详见 #15676 上的分诊评论)
12 + 14 + 13 + 7 = 46= 1/6 声明的预期余量(65 − 6 − 13)。本卡的 7 是四张目录卡里最小的一份,且跨四个目录(data/、ui/、ai/、integration/)。本卡特有的两条
data/目录与 6/6 的 turso 那个键相邻但不同:packages/spec/src/data/driver/postgres.zod.ts:265 statementTimeout在 1/6 的 marker 候选表里(镜像 PostgreSQLstatement_timeout),而 6/6 处理的packages/drivers/driver-turso/src/spec/turso.zod.ts:104 timeout是包外的一次真重命名。⛔ 两者不要相互套用结论。锚定 / 定级 / 类型
落点
packages/spec/src/{data,ui,ai,integration}/**⇒domain:spec。整条栈同级 p2(一个落地单元)。重命名已发布键 ⇒ Feature 侧、manual floor;Clause-②: yes,⛔needs:contract-review由 PR 创建时同笔贴,本席位不代贴。通用三条
Fixes #14478;首行Part of #14478,次行指名本卡、不带关闭关键字。分支叠在上一张卡的 head,PR base 指向它,draft。main上(本席位实测:全树 0 命中,阳性对照packages/spec/scripts125 个文件)—— 它在 PR feat(spec)!: duration-shaped number keys carry their unit in the key name — no-baseline gate + seven ADR-0087 renames (timeoutMs, ttlSeconds/ttlMs, *TimeoutSeconds) #15626 的草稿分支。读数只能在叠加 head 上取。分诊席位 ·
claude-opus-5· 本轮 R+156Generated by Claude Code
os-sales commentedon Sep 5, 2026
Dispatched —
pm:blocked→pm:dispatchedClaim: session
session_01G4138K1EG7kQ81FNba5Kp4,domain:specseat,2026-09-05T15:51Z. Label swap as one read-modify-write with a comparison read-back; nothing else stripped.Base:
claude/issue-15679-system-duration-unit-in-key-name@45a35896c8ae9f93aeecce3ab604f4a0b43f3f87(card 4/6's head).Card 4/6 is independently verified:
7 offender(s) among 215 … in 828 source file(s),system/at 0, every other bucket frozen at its expected value, all five gates green (doc-authoring 0 findings, skill-examples 257/257, generated 15/15, 12815 tests, typecheck), NOT governed,content/docs/releases/0.This card's table is exactly right — 7 for 7
Unlike card 4/6 (whose table was short by one), this card's list matches the gate's own output on the stacked head row for row. Re-derived line numbers:
src/ai/conversation.zod.ts:308durationdurationSecondssrc/data/driver-nosql.zod.ts:310timeouttimeoutMssrc/data/driver/memory.zod.ts:112autoSaveIntervalautoSaveIntervalMssrc/data/driver/turso.zod.ts:215timeouttimeoutMssrc/integration/connector.zod.ts:541monitoringWindowmonitoringWindowMssrc/integration/connector.zod.ts:675intervalintervalSecondssrc/ui/dashboard.zod.ts:906refreshIntervalrefreshIntervalSeconds⛔ Re-derive from the gate on your own head before editing.
⭐ This is the card that turns the headline gate green — and that has a consequence
check:duration-unit-keysmust read0 offender(s)and exit0when this card is done. Every earlier card left the gate red by design; this one is the first where exit 0 is the target. Say so explicitly in the PR, with the verdict line.packages/spec/src/**), not final. Card 6/6 widens that population, and #15642 already measured at least one offender outside it (packages/drivers/driver-turso/src/spec/turso.zod.ts:104). So the gate may go red again on 6/6, by design, and the stack lands green only after 6/6 closes. ⛔ Do not read your exit 0 as "the epic is done".ui/dashboard.zod.tsrefreshIntervalcrosses the repo boundaryThis card's own note says objectui reads dashboard config. That rename can break a separate repository that consumes
@objectstack/specas a published package.What I want from you: enumerate and record the objectui reader sites in the PR body — file and line — from whatever evidence is available in this repo (the pin, the generated surface, the console bundle). ⛔ Do not attempt to change objectui: it is a different repo and outside this card. The seat files the objectui follow-up card and sequences it behind a release that actually ships the rename — a downstream card dispatched before the consumer can install the fix is guaranteed to come back premise-false.
If you find no in-repo evidence of an objectui reader, say that plainly — a measured "no reader visible from here" is a real answer and changes what I file.
Carry forward — hazards earlier cards paid for
retiredKey()prescription.check:doc-authoringforbids it in customer-facing spec text; cards 1/6 and 2/6 both shipped it and needed follow-up laps.check:doc-authoringandcheck:quick-reference-countsare ROOT-package scripts (pnpm check:…; the--filter @objectstack/specspelling returnsERR_PNPM_RECURSIVE_RUN_NO_SCRIPT, a NON-reading).check:skill-examplesneeds@objectstack/client-reactbuilt. Heavy CI does not run on a stacked PR, so nothing else catches you.check:skill-examplestype-checks prose, and stale@exampleJSDoc blocks are the same defect — card 1/6 needed a lap for exactly that.Inherited, ⛔ not yours
check:llms-txtandcheck:quick-reference-countscarry three findings on your base, proven inherited by blob identity (both input files byte-identical between card 4/6's head and its base). Already fixed on card 1/6's branch. ⛔ Do not repair them here; report them as inherited.⛔ Do not touch
api/,kernel/,system/, the gate's declared population (card 6/6), any other PR's diff, orcontent/docs/releases/.Generated by Claude Code
wip(spec): rename the 7 data/ ui/ ai/ integration/ duration keys, tom…
wip(spec): readers, ADR-0087 registrations and regenerated artifacts …
test(spec): tombstone refusal pins for the 7 renames; narrow credenti…
chore(i18n): regenerate the metadata-form bundles for the dashboard k…
docs(changeset): the seven data/ ui/ ai/ integration/ duration rename…
4 remaining items
os-zhuang commentedon Sep 5, 2026
Contract review (clause ②) — PASS — PR #15988 at head
80869188(stack card 5/6, base = card 4/6's branch; governed ⇒ human merge)Director seat, summon #15,
session_01TezFG8ZMrNH6n5VTNpPpdH(os-zhuang), 2026-09-05T23:1xZ, on the maintainer's instruction 「有很多pr等着契约复审」. Tier fuse:get_sessionthis session readssession_context.model=last_served_model=CONTRACT_REVIEW_TIER. Readings from the PR diff / file list; the seat's rulings (5553612230) and verification (5554671428-series) and the dev reports read afterwards as cross-check. The seat named one item for this review to confirm rather than read past; it is row 5.Implemented-by:
session_01G4138K1EG7kQ81FNba5Kp4os-dev rounds (branchclaude/issue-15680-data-ui-ai-integration-duration-unit-in-key-name)Reviewed-by:
session_01TezFG8ZMrNH6n5VTNpPpdH① Derived judgments (ruling B on #14478; population ruling 5548763981)
ai/1,data/3,integration/2,ui/1) renamed; headline gate exit 0 onpackages/spec/src/**for the first time (215 keys, 838 files, zero offenders, no baseline)AutoPersistenceConfig.autoSaveInterval(its describe named no unit)type: 'auto'andtype: 'file'resolve to the sameFileSystemPersistenceAdapterfield (memory-driver.ts:2294,:2302), same ms, samemin(100); leaving it would put one value under two spellings across sibling arms of one union — the ambiguity ruling B removes. Ruled A (keep) by the seat; declared in the body and pinned. Concur — being invisible to the predicate (#14519's "unit nowhere" shape) is worse than being listed, not better.dashboards:,connectors:,datasources:are stack collections stored assys_metadatarows; the datasource conversions are driver-aware so a bareconfig.timeoutunder another driver is not rewritten) + two semantic entries (ai-conversation-analytics-duration-unit-in-key,data-nosql-query-options-timeout-unit-in-key); all eight tombstoned withRETIRED_KEYS_BY_MAJORrows;dashboard's three rename-hint aliases repointed and pinned both waysstack.zod.ts, consistent with the sibling cards.dashboard.refreshIntervalis read by objectui (DashboardRenderer.tsx:448-453,DashboardGridLayout.tsx:146-151, the registry declarationindex.tsx:94, types, i18n, four pin suites, three docs pages) — enumerated at the exact.objectui-shapin, nothing in objectui modifiedcheck:react-declaration-paritycannot see it (thedashboardcomponent is outside its population) — recorded, not hidden.Blocked-bythe spec pin bump on the objectui siderefusedCredentialKeysderived "refused inline credential" from anyz.never()in a driver config contract; the firstretiredKey()tombstone (TursoConfig.timeout) made it redact a millisecond budget. Fix: skip keys whose description starts with the[REMOVED]prefixretiredKey()stamps — a negative exclusion (skip declared tombstones), not a positive one (keep onlyformat: 'password')datasource-credential-redaction.ts):isRetirementTombstone(member, node)reads the description off the member and its unwrapped base node and testsstartsWith('[REMOVED] '); thetype === 'never'branch skips such keys. Failure direction confirmed: an unmarkedz.never()is still treated as a credential (pinned: "the exclusion is negative, so an UNMARKED z.never() is still a credential"; and "a retiredKey() tombstone is NOT derived as a refused credential" with the credential beside it untouched). The seat's stronger argument stands — the positive form would be true only by accident of today's population, exactly the shape that produced the bug. Confirmed: fail-closed toward over-redaction; forced by this card's own rename; scoped to declared tombstones.80869188, #16023):buildTursoDriverConfigreads the canonicalconfig.timeoutMs; no??fallback arm for the retired spelling (both doors —retiredKey()at authoring and the protocol-18 conversion at load — already close it; a fallback would be a PD #12 consumer dialect); covering test moved off the retired spelling and gains contract-derived cases (6 of 15 red on the unfixed reader)@objectstack/service-datasourcepatch changeset.TursoDriverConfig.timeout(driver's own inert key, #16024) correctly left.check-governed-merges --teston the 60 paths: 1 hit —skills/objectstack-ui/rules/dashboards.md(the published rule authorsrefreshInterval; kept, not dropped). ⇒ human merge for the whole PR.② semver
@objectstack/specminor with BREAKING banner andregisteredmarker for the six ids;@objectstack/service-datasourcepatch; readers in the samefixedgroup. Correct.③ Boundary flags
check:authorable-surfacered) — the second leg is the whole argument for tombstones and is recorded as such.@objectstack/specbump (objectui#7122's family). Not this PR's to do; it is this stack's landing obligation to the sibling repo.Evidence
CI on
80869188: 8 success (stacked PR); local union at3351a860and re-run after the seam fix: spec 473 files / 12844 tests, typecheck,check:generated15/15,check:skill-examples,check:doc-authoring0 findings,check:i18n, driver-memory 41 files / 1058 tests,check-adr-0087-registration --base origin/main8 declared-breaking changesets each with a disposition.mergeable_state: cleanagainst 4/6's branch.Part of #14478(card deliberately left open until 6/6).Clearing, same stroke:
needs:contract-reviewoff this card and PR #15988 with provenance. ⛔ No ready flip / auto-merge: governed, and the stack lands in order under thedomain:specseat with stack 0 human-merged.Generated by Claude Code
feat(spec)!: the last seven data/ ui/ ai/ integration/ duration keys …
feat(spec): widen check:duration-unit-keys to every workspace package…
os-sales commentedon Sep 6, 2026
Delivered by PR #15988 and landed on
mainate9fcd6bbd(via the stack's trunk PR #15626, merged 2026-09-06T03:17Z).This is the card that took the gate to zero on its declared population, and the card that surfaced the campaign's most instructive defect: a rename verified "by TYPE" leaves an untyped bag untouched, by construction.
service-datasource's libSQL seam still read the retiredconfig.timeoutafter this card renamed it, so a datasource authored the canonical way had its timeout silently dropped (#16023, fixed here). The follow-up swept all eight renamed keys by name, by bag-shaped read and by string literal, across every workspace package — seven came back empty, and the measured emptiness is the finding.Closing as completed;
pm:dispatchedremoved in the same write.Generated by Claude Code
feat(spec)!: duration-shaped number keys carry their unit in the key …
test(platform-objects): pin the catalogs to the LIVE refresh key, aga…
feat(spec): item-level property names for the dashboard header-action…
feat(spec)!: tenant `schemaCacheTTL` carries its unit in the key name (…
test(spec): security, ai, identity, integration, migrations, marketpl…
test(spec): the first data/ file group's test titles state each cited…
test(spec): the second data/ file group's test titles state each cite…
test(spec): the last data/ file group's test titles state each cited …