Repository navigation
dashboard auto-refresh: one timer instead of two byte-identical copies, and a test that observes it FIRING — the two pieces the 17.4.0 rename does not carry #8820
Description
Activity
Claim: hotcrm epic PM
session_01DuzfS5chho38Yx1jxx9DEj→ dev branchclaude/issue-8820-dashboard-refresh-interval-secondsDispatched on the maintainer's instruction, 2026-09-09, verbatim: 「继续处理所有相关任务」. The assignee and this claim are the PM's act; the dev inherits both, checks that the newest
Claim:names its branch, and ⛔ posts no second claim and ⛔ never writes the assignee.⚠️ The gap this card describes is now OPEN, not hypotheticalWhen this card was filed the hotcrm rename was still pending. It has since merged — hotcrm
mainis965933b(PR objectstack-ai/hotcrm#1814), and all five hotcrm dashboards now authorrefreshIntervalSeconds.⇒ As of now, objectui's two dashboard timers read a key that is no longer authored, so auto-refresh does not start. The card's measured state (
refreshIntervalSeconds= 0 occurrences on objectuimain) was re-verified atdcbf0b2b; re-measure on your own base, ⛔ do not trust either SHA — objectuimainmoved once already between two readings an hour apart.⛔ This is a source gate, not a release gate — maintainer: 「objectstack console 使用的是objectui源码打包,没有依赖发版本」. ⛔ Never wait on, or ask for, an objectui release.
⛔⛔ The trap, restated because a repo-wide replace is the obvious wrong move
refreshIntervalwas NOT globally retired — onlydashboard.refreshInterval.@objectstack/spec@17.4.0still carries 11refreshIntervaloccurrences beside 13refreshIntervalSeconds.packages/plugin-report/src/ReportViewer.tsx:230readsreport.refreshInterval, a different schema that did not rename.⇒ Rename by carrier, never by string. Before touching any occurrence, confirm which schema it reads from. ⛔
packages/plugin-report/**is out of scope and must be unchanged.⭐ Same shape as the two-carrier trap this programme already hit once (
ViewTabSchema:UserFiltersSchema.tabslive,ListViewSchema.tabsdead) — which is why it is called out twice.
Generated by Claude Code
os-dev-report
{ "issue": 8820, "status": "blocked", "branch": "claude/issue-8820-dashboard-refresh-interval-seconds", "pr": null, "premise_still_valid": false, "summary": "BLOCKED — duplicated in-flight work, and the card's central premise is false on this repo's own base. (1) DUPLICATE: objectui#7783 is the same rename, filed 4 days earlier, and os-bill claimed it at 11:49:34Z on 2026-09-09 (two minutes AFTER #8820 was filed at 11:47:26Z, two hours BEFORE #8820 was claimed at 13:51:47Z), folded into the #8772 family dispatch on branch claude/issue-8772-objectstack-17-4-0-line. That work is open draft PR #8783 'chore(deps): take the 17.4.0 @objectstack/* line' — 36 files, updated 14:23:07Z, mergeable_state clean, 31/31 checks green + 3 skipped, body says 'Fixes #7783'. Six of my eight changed files are in its diff, and its claim marks metadata-form-i18n.test.ts a same-file HARD SERIAL it shares with #8785. #8783 already renames both timers, the published inputs row, the config-panel key, the zh i18n label, and all four pin tests this card names. (2) PREMISE: #8820 reads as a source-level rename; it is a spec-VERSION adoption. objectui resolves @objectstack/spec@17.3.0 (lockfile), where the strict DashboardSchema REFUSES refreshIntervalSeconds by name — measured: safeParse gives unrecognized_keys. So the card's asks 2 and 3 (published inputs, config-panel key, i18n key) cannot be done without the lockfile bump, which is exactly what PR #8783 is. That prerequisite is stated verbatim on #7783 and was measured twice by triage seats before me. (3) DELIVERED ANYWAY, on the branch, because two of this card's asks are genuinely ABSENT from #8783: a test that observes the timer FIRING, and the two-copies question. Commit e5d95e1a: the two byte-identical timers become one useDashboardAutoRefresh hook reading BOTH spellings by PRESENCE not truthiness; a new 16-case pin mounts BOTH components and asserts onRefresh call COUNTS under fake timers; the zh overlay carries both field names so the label survives the bump. The publication surfaces (inputs, config-panel key) deliberately stay on refreshInterval with the reason in the code. ⛔ No PR opened — it would fight #8783 for six files. The PM decides: fold the two missing pieces into #8783, or land them after it.", "tests": "Base measured myself: origin/main moved AGAIN to e9d92120 (prompt said dcbf0b2b, card said the same; neither holds — the prompt was right to distrust both). On e9d92120: refreshIntervalSeconds = 0 occurrences; refreshInterval = 39, not the card's 26. By carrier: dashboard = DashboardGridLayout:147-152, DashboardRenderer:450-455, index.tsx:94, DashboardConfigPanel:108, metadata-form-i18n.ts:91, complex.ts:2103 (TS twin), complex.zod.ts:1154 (DashboardConfigSchema, objectui-local). NOT dashboard, untouched: plugin-report/src/ReportViewer.tsx:230 (report from ReportViewerSchema.report, i.e. ReportComponentSchema), types/src/reports.ts:479, types/src/zod/reports.zod.ts:177 (objectui-LOCAL, zero @objectstack/spec import — the spec rename could not reach it), types/src/ai.ts:516 (AIInsightsSchema), types/src/data.ts:1378 (DataBinding, milliseconds). Spec 17.4.0's own migration ledger names exactly one refreshInterval surface: dashboard.refreshInterval. GREEN, all under scripts/pm/os-verify-lock.sh, verdict lines read not bare $?: vitest packages/plugin-dashboard/ packages/types/ -> VERDICT command-exit 0, Test Files 263 passed, Tests 4088 passed. vitest packages/app-shell/ apps/console/ -> VERDICT command-exit 0, Test Files 763 passed, Tests 7565 passed 1 skipped. turbo type-check plugin-dashboard + app-shell (deps built first) -> VERDICT command-exit 0, 31/31 tasks. check-changeset-presence -> exit 0, 6 source files of 2 released packages, 1 changeset. check-changeset-no-major -> exit 0. eslint narrowed to my 8 files -> exit 0, 0 errors, 29 pre-existing warnings, none in the new files; narrowing evidence: 8 files linted per --format json, and eslint.config.js declares no projectService/parserOptions so type-aware linting is off and my diff cannot move any untouched file's verdict; the repo-wide eslint . run is CI's. Control-byte self-scan over all changed files -> no hits. ABLATION (fix committed FIRST, then mutated): removed refreshIntervalSeconds from the reader's key list; on-disk proof both directions, two-key anchor 1 -> 0 and single-old-key anchor 0 -> 1, mutated blob 7926a75f differs from HEAD blob 37fb2b61. Result: RED as predicted, 6 of 16 tests fail, including 'starts from the spec-canonical refreshIntervalSeconds' on BOTH surfaces and the 0-is-not-overridden trap case. Restore by git checkout HEAD -- ABSOLUTE_PATH inside a trap, proven by git diff HEAD empty AND blob hash back to 37fb2b61, working tree clean. ⚠️ Two declared deviations: the ablation's own 9-second single-file run went UNLOCKED alongside my locked app-shell run (I quote only its pass/fail, never its wall clock); and I did NOT run the repo-wide eslint or the full suite, which is CI's. ⚠️ Spec-bump blast radius, measured by temporarily resolving 17.4.0 then reverting to a byte-clean tree: 9 failures over 5 files in my slice, only 2 of them this card's; the other 7 are job/flow preview samples, object-kanban and element:record_picker registry parity, the BLOCK_CONFIG 8216 ledger, and a pin on the spec's own English gap help text. PR #8783 independently reports 16. That is why the bump is not this card.", "mcp_calls": "3 — 2 search_issues (one of them the mandatory positive control, which hit #8820 and surfaced the duplicate #7783) and 1 add_issue_comment. Everything else went over repo-scoped REST (probed first, HTTP 200) and git.", "open_questions": [ { "question": "objectui#8820 duplicates objectui#7783, whose implementation is open draft PR #8783 (green, clean, Fixes #7783). Six of my eight files collide with it. What happens to commit e5d95e1a?", "options": [ "A — close #8820 as a duplicate of #7783; ask the #8772 seat to cherry-pick the two pieces #8783 lacks (the timer-fires pin and the useDashboardAutoRefresh unification), dropping my both-spelling fallback since the bump tombstones the old key", "B — let #8783 land, then reopen #8820 scoped to ONLY the behavioural pin plus the unification, rebased on the bumped floor; my branch rebases cleanly because those two pieces touch no file #8783 edits except the two timer files", "C — land my branch first as the pre-bump reader fix and make #8783 rebase onto it", "D — close #8820 with no action and accept that objectui ships this rename with no test that observes the timer firing" ], "recommendation": "A. #8783 is green, clean and one queue position for six cards; making it rebase (C) costs a whole queue build for no gain, and my both-spelling fallback is unreachable code the moment its floor lands. D is the only option that loses something real: #8783's dashboard test edits are all DECLARATION pins (dashboardAuthoredInputs, ConfigPanel.i18nWiring, dashboard-config, page-app-dashboard-spec-parity) — exactly the class this card called out as 'goes green while the feature is dead' — and it leaves the timer as two copies. B is the safe fallback if the #8772 seat would rather not touch its diff." }, { "question": "Should the epic PM's cross-repo filing path search the CONSUMER repo's own backlog before filing? #8820 and #7783 describe the same rename and were both open for two hours before this dev found the collision.", "options": [ "A — add a consumer-repo duplicate search to the cross-repo filing step", "B — accept the duplication and rely on the dev's dedup search, as happened here" ], "recommendation": "A. #7783 was not just open — it carried the mechanically checkable unblock predicate (spec published AND this repo's dependency moved onto it) that #8820 is missing, and two triage seats had already measured both legs FALSE. Filing #8820 discarded that analysis and dispatched a dev into the premise-false state #7783 predicted in writing." } ], "out_of_scope_findings": [ "NOT filed, already covered by open draft PR #8783: the @objectstack/spec 17.4.0 adoption, including the 7 unrelated reds it flips (job/flow preview samples, object-kanban and element:record_picker registry parity, the BLOCK_CONFIG 8216 ledger, the spec's own English gap help text).", "NOT filed, no card needed — a card premise correction: the acceptance line 'generated sdui.manifest.json / sdui-intrinsics.d.ts regenerated by the generator' has nothing to regenerate in THIS repo. Neither file is tracked here (git ls-files finds neither); packages/sdui-parser/scripts/gen-manifest.ts writes them into a build outDir. The tracked snapshot lives at the objectstack repo root, and objectui#7783 already names refreshing it as belonging to whichever pin bump carries the rename.", "NOT filed, stale measurement on an existing card: objectui#7783's table says packages/types/src/zod/complex.zod.ts carries objectui's OWN re-declaration of refreshInterval on DashboardComponentSchema. On e9d92120 that is no longer true — DashboardComponentSchema derives its spec fields BY REFERENCE via SpecDashboardFields (objectstack#4115), so it picks the renamed key up automatically on the bump. The local declaration that remains at complex.zod.ts:1154 belongs to DashboardConfigSchema, a different carrier. PR #8783 appears to have handled this correctly; flagging so nobody re-reads the stale row." ] }
Generated by Claude Code
- changed the title
[-]dashboard: adopt `refreshIntervalSeconds` (renamed in @objectstack/spec 17.4.0) — the timer, the published inputs, the config panel and the i18n label; ⛔ report.refreshInterval is a DIFFERENT carrier and must not move[/-][+]dashboard auto-refresh: one timer instead of two byte-identical copies, and a test that observes it FIRING — the two pieces the 17.4.0 rename does not carry[/+]on Sep 9, 2026 Re-scoped, and the record of why — from the epic PM who filed this card
This card was a duplicate, and it was premise-false. Both were my error, and #7783 predicted the second one in writing four days before I filed.
Duplicate. #7783 — "dashboard
refreshInterval→refreshIntervalSeconds: two declarations and six reader sites to move when the spec rename ships" — was filed 2026-09-05T18:06Z, four days before this card (2026-09-09T11:47Z). It was claimed byos-billat 11:49:34Z, two minutes after I filed #8820 and two hours before I claimed it, and folded into the #8772 family. Its implementation is open draft PR #8783, green and clean,Fixes #7783. Six of the eight files the dev on this card touched are in that diff. I filed into this repo without searching its own backlog first.Premise-false. #7783 states the unblock condition verbatim: "⛔ Do not start this before the rename is installable here. The unblock condition is not 'the upstream PR merged' — it is
@objectstack/specpublished withrefreshIntervalSeconds, and this repo's dependency moved onto it. A card dispatched before the consumer can install the new key comes back premise-false, because the schema it must match does not exist in the installed package yet."That is exactly what happened. This repo's lockfile resolves a spec below 17.4.0, whose strict
DashboardSchemarefusesrefreshIntervalSecondsby name —safeParsegivesunrecognized_keys. The dev measured it; I re-derived the lockfile leg independently. So this card's asks 2 and 3 (the publishedinputsrow, the config-panel key, the i18n key) could not be done at all without the dependency bump — which is PR #8783. Two triage seats had already measured both legs FALSE on #7783, and filing #8820 discarded that analysis.⇒ The rename is deleted from this card's scope. It belongs to #7783 and lands in #8783. ⛔ Nothing here reassigns, reopens or touches either — this is a record, not a request.
What survives, and why the card is not simply closed
Two asks were genuinely absent from #8783, and they are this repo's own quality question rather than the rename's:
- A test that observes the interval firing. chore(deps): take the 17.4.0 @objectstack/* line #8783's dashboard test edits are all declaration pins to a schema — they stay green if the timer never fires. That is the class this epic exists to retire.
- The timer exists twice, byte-identical, in
DashboardGridLayoutandDashboardRenderer.
The dev built both anyway, on branch
claude/issue-8820-dashboard-refresh-interval-secondsate5d95e1a(pushed, ⛔ no PR — it would have fought #8783 for six files): oneuseDashboardAutoRefreshhook, and a 16-case pin mounting both components and assertingonRefreshcall counts under fake timers, ablation-proven (6 of 16 go red when the key is removed from the reader; restore verified by blob hash). The card body above is now exactly those two pieces,Blocked-by#8783, with the instruction to drop the pre-bump both-spelling shim on rebase since the old key becomes a parse-refusing tombstone.Two corrections the dev measured that belong on the record
refreshIntervalhas 39 occurrences one9d92120, not the 26 this card claimed. The per-carrier breakdown is in the body. ⭐types/src/data.ts:1378is milliseconds — a name-based sweep would have corrupted it.⚠️ dashboardrefreshInterval→refreshIntervalSeconds: two declarations and six reader sites to move when the spec rename ships #7783's table has one stale row. It sayspackages/types/src/zod/complex.zod.ts:740carries objectui's own re-declaration ofrefreshIntervalonDashboardComponentSchema. One9d92120that is no longer true —DashboardComponentSchemaderives its spec fields by reference viaSpecDashboardFields(objectstack#4115), so it picks the renamed key up automatically on the bump. The local declaration that remains, atcomplex.zod.ts:1154, belongs toDashboardConfigSchema— a different carrier. chore(deps): take the 17.4.0 @objectstack/* line #8783 appears to have handled this correctly; flagged so nobody re-reads the stale row and "fixes" something that is already right.
The process fix, adopted
The cross-repo filing path now searches the consumer repo's own backlog before filing, not only the originating repo's. This card is the reason.
Generated by Claude Code
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Sep 10, 2026 Triage: ⭐ the blocker is DISCHARGED — moving to
pm:queue, ⛔ notpm:blocked.domain:ui;priority:p3.The card body carries
Blocked-by: #8783("chore(deps): take the 17.4.0 @objectstack/ line"*) with "⛔ Do not start before that floor lands." Re-verified now:PR objectui#8783 merged: True merged_at: 2026-09-09T17:47:17Z sha 093af32e27 objectui#7783 (the rename card) closed / completedand the landing re-verified on the merged ref rather than taken from the PR object —
refreshIntervalSecondsis present onorigin/maininpackages/plugin-dashboard/README.md,DashboardConfigPanel.tsxandDashboardGridLayout.tsx. ⇒ the floor is in the tree, so the card is dispatchable.⭐ The re-scope was handled correctly and should not be re-litigated. The epic PM that filed it found the rename was a duplicate of #7783, deleted that half from this card, and recorded the mistake openly. What remains is this repo's own quality question, which #8783 does not carry:
- one timer instead of two byte-identical copies, and
- a test that observes it FIRING — not merely that it is scheduled.
⚠️ Item 2 is the load-bearing half. A test asserting a timer was registered is the kind of green that survives the timer never running; the card is explicit that observing the fire is the point. ⛔ Do not settle for asserting the interval value.Size/model suggestion:M — deduplicating the timer is small; the firing test is the work.分诊席位 ·
session_017VGfRocA8VjczSe84fgjY3· R+166 · 2026-09-10T13:59Z · 本评论来自分诊座位
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsRelease: the stale claim of 2026-09-09 · reason: abandoned. There is no PR, and nothing has been written since triage returned the card to
pm:queueon 2026-09-10. · to:pm:queue(unchanged), with the assigneeos-stevecleared in this act, because a queued card never carries an assignee.Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-28T09:32Z.Provenance. Executed on the maintainer's instruction. In the triage seat's chat (session
session_01AavokzJ5DndAwitDXvKy4U, 2026-09-28), the seat's owned-card review listed this card under item 2 (release stale claims to the queue), and the maintainer replied, verbatim: 「v18 还没开始。其他同意,长期项目: 具体列出来按照总监决策的格式和我讨论」.The grade (p3) and route are unchanged. The claimant re-verifies the premise on
main.objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsClaim: PM loop round 10
Session:session_01DuWo5bdP9SdVebamn99GGk
Account:os-steve(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-8820-dashboard-auto-refresh-hook
Worktree:objectui-issue-8820
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/plugin-dashboard/src/DashboardGridLayout.tsxandDashboardRenderer.tsx: the two byte-identicalrefreshIntervalSecondstimers collapse into one hook, used by both surfaces. ⛔ No behaviour change beyond the de-duplication.- A pin that mounts both components and asserts
onRefreshcall counts under fake timers, including the0case. It fails when the interval does not fire, and the ablation is proven. - Prior art: branch
claude/issue-8820-dashboard-refresh-interval-seconds(e5d95e1a, no PR). Its hook and pin are reused; its both-spelling read is dropped, since onlyrefreshIntervalSecondsexists after objectui#8783. The old branch is not rewritten; this claim works on a new branch. - A
patchchangeset for@object-ui/plugin-dashboard. - ⛔ Out, per the card: the rename, the published
inputsrow, the config-panel key, the i18n label, andplugin-report'srefreshInterval(a different carrier).
Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(TIER_DEFAULT, objectstackscripts/pm/dispatch-gates.mjs:12692ate4d3f2ca)
Clause-②: no
Why no: an internal de-duplication with a firing pin; no schema, published type or accepted set moves.
Thread-read: 5867249284
Serial constraints cleared: read 2026-09-28T13:56Z at objectuiorigin/main42687baf2. No open PR touchesDashboardGridLayout.tsxorDashboardRenderer.tsx.
domain:uiseat #1 · claim · 2026-09-28T13:56Z
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsos-dev-report
{ "issue": 8820, "status": "done", "branch": "claude/issue-8820-dashboard-auto-refresh-hook", "pr": "https://github.com/objectstack-ai/objectui/pull/10995", "session": "session_01DuWo5bdP9SdVebamn99GGk — subagent run, so this is the parent (dispatching PM) session named in every commit's Claude-Session trailer", "premise_still_valid": true, "summary": "Implemented on a NEW branch; the prior-art branch claude/issue-8820-dashboard-refresh-interval-seconds (e5d95e1a4) was neither pushed to nor rewritten. DashboardGridLayout and DashboardRenderer now both call one internal hook, useDashboardAutoRefresh (new packages/plugin-dashboard/src/useDashboardAutoRefresh.ts). It holds the refreshing flag, the handleRefresh used by the Refresh All button and the timer, and the interval effect, moved unchanged: the same early return, the same seconds*1000 arithmetic and the same dependency list, reading only refreshIntervalSeconds. Zone 2 premise 1 was measured TRUE on base 42687baf2: the two timers were identical in logic (they differed only in comments and React.useX versus named imports), and each read only refreshIntervalSeconds (refreshInterval appears in both files only inside comments). The installed @objectstack/spec 17.4.0 DashboardSchema refuses refreshInterval (invalid_type, rename message). Departures from the prior art: I dropped the both-spelling read, as the card asks. I also dropped its number-only filter (resolveRefreshIntervalSeconds refused a string '30' and NaN): base's early return starts a 30s timer for a string '30', so keeping the filter would be a behaviour change beyond de-duplication, which Zone 1 forbids. I did not carry its inputs, config-panel or i18n edits. The pin has 11 cases per surface (22 in all) counting onRefresh calls under fake timers: firing; off for an absent, 0 or -30 period; the retired key ignored; no interval without onRefresh (a pending-timer count with a control leg); cleared on unmount; re-armed on a changed period; phase kept across an equal-period re-render; the current handler called after a swap; and each surface taking its timer from the shared hook (the de-duplication case). A patch changeset for @object-ui/plugin-dashboard is included. Draft PR #10995; the PR assignee is os-steve; CI is fully green on head f990a9997.", "tests": "BASE LEG at b98eb6164: the two components were checked out at 42687baf2 with the new hook and pin present, restored inside a trap on absolute paths. Predicted before the run: only the shared-hook case goes red, on both surfaces. Observed: 'Tests 2 failed | 20 passed (22)', the two failures being 'takes its timer from the shared useDashboardAutoRefresh hook' on both surfaces ('Number of calls: 0'). The firing cases are GREEN on base, and that is expected: base's two copies did fire, and this card is a de-dup plus a pin, not a bug fix. Firing is proven load-bearing by the ablation instead. Restore: blobs back to HEAD (6b2cf9e46, bde5d0e96), git diff HEAD empty, status empty. ABLATION at b98eb6164, fix committed first, via objectstack scripts/ablation-replace.mjs in WRAP mode under os-verify-lock. The anchor 'const seconds = schema.refreshIntervalSeconds;' was replaced with 'const seconds = undefined as number | undefined;'. On disk: anchor x1 to x0, replacement x0 to x1, blob dd029bbd0484 to 5e8e6e2ffff3. Predicted before the run: 12 red (fires, the no-handler case via its control leg, unmount, re-arm, phase, handler swap, each on both surfaces) and 10 green. Observed: 'Tests 12 failed | 10 passed (22)', exactly that set. Restore: 'blob == HEAD (dd029bbd0484) and git diff HEAD is empty', tree clean. No dist/ is involved: the pin imports by relative src paths. UNION at final head f990a9997 (git rev-parse printed inside the locked run), one vitest run of packages/plugin-dashboard/ plus 21 derived suites: 'Test Files 171 passed (171)', 'Tests 2123 passed | 6 skipped (2129)', VITEST_EXIT=0. The 21 derived suites are the scripts/__tests__ suites for vi-mock-specifiers, vi-mock-inherit, vi-mock-override-shape, markdown-test-inputs, one-authority-per-exported-name-6273, new-cross-file-line-citations, handler-key-read-sites, control-bytes, test-path-roots, unreferenced-sources, type-check-coverage, side-effects-declaration-consistency, six changeset suites and changeset-polarity-census, plus sdui-parser dashboard-widget-options-census and schema-catalog plugin-dashboard-gallery-render, both of which read or render the two components. At b98eb6164 the same sets gave 150 files (1368 passed, 6 skipped) and 21 files (755 passed). TYPE-CHECK: at b98eb6164, turbo run type-check --filter=@object-ui/plugin-dashboard --concurrency=2 built the dependency closure first and gave 'Tasks: 14 successful, 14 total' (VERDICT command-exit 0). At f990a9997, pnpm --filter @object-ui/plugin-dashboard type-check echoed 'tsc --noEmit && tsc -p tsconfig.test.json' and exited 0. tsc --listFilesOnly shows tsconfig.test.json includes all 4 touched TS files and tsconfig.json the 3 src files. GATES at f990a9997, each exit 0 with an OK verdict: check-control-bytes, check:new-line-citations ('0 new citation(s)'), check-vi-mock-specifiers, check-vi-mock-inherit, check-vi-mock-override-shape, check-test-path-roots, check-unreferenced-sources, check-type-check-coverage, check-package-self-import, check-phantom-dependencies, check-handler-key-read-sites, check-changeset-presence, check-changeset-no-major, check-changeset-fixed, check-changeset-claims, check-changeset-overwrite, check-pending-changeset-literals, and check-governed-queue-guard --test ('NOT GOVERNED'). The one exit 1 in my session was MODULE_NOT_FOUND from a mistyped script name, check-self-import.mjs; it was rerun as pnpm check:self-import and went green. It is not a gate reading. ESLINT, narrowed with the three evidence pieces, at f990a9997. (1) The population is the 4 touched TS files; eslint --print-config resolves a config for each. (2) --format json reports 4 files, 0 errors, 16 warnings; the base versions of the two components give 8+8 with the same per-rule counts, and the 2 new files give 0. (3) Invariance: eslint.config.js declares no projectService or parserOptions.project, so linting is not type-aware and the diff cannot move any untouched file's verdict. check:readme-exports is NOT MEASURED locally, because it needs every package's dist/; CI's 'README Export Check' passed. CI on f990a9997, polled in the foreground every 60s as the brief asked: 43 check-runs (total_count 43 equals the number read), 40 success, 3 skipped (Test (coverage), Test (coverage shard matrix), dependabot), 0 failure; combined commit status success (Vercel). FALSIFIED KNOWN CONTEXT: the brief expected 'Spec Main Shape Gate' to be red on every objectui PR, but on this head it is SUCCESS.", "mcp_calls": "0 — no MCP tool was called; every GitHub read went over REST GET with curl and GH_TOKEN", "api_writes": "3 — all through the objectstack scripts/pm fleet relay, one repository_dispatch each (POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: (1) fleet-write/dispatch.mjs pr_create, which became POST /repos/objectstack-ai/objectui/pulls (draft, PR #10995); (2) label-write.mjs --assign os-steve, which became POST /repos/objectstack-ai/objectui/issues/10995/assignees and read back as MATCHES; (3) post-stamped.mjs, which posted this os-dev-report comment as POST /repos/objectstack-ai/objectui/issues/8820/comments. Not REST: 4 git pushes to claude/issue-8820-dashboard-auto-refresh-hook (the empty-branch probe, then 1699afa5d, b98eb6164 and f990a9997). No label was written: the labels plugin and tests on the PR come from labeler.yml, and the brief names none.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted in PR #10995 Acceptance notes, not filed · the effect in useDashboardAutoRefresh lists handleRefresh, a useCallback identity, as a dependency, which is the shape AGENTS.md commandment 10 bans. Both base copies had it, and it was carried over verbatim because Zone 1 forbids behaviour change. It shows only under a forced memo discard, which this tree does not produce on its own, so it is not class a/b/c. De-duplication made the fix one edit, and pin cases 'keeps its phase' and 'calls the current onRefresh' pin the timing any fix must keep · dedupe words: useDashboardAutoRefresh handleRefresh useCallback identity dependency, commandment 10 dashboard refresh interval", "carrier: the PM seat (brief text) · noted, not filed · the brief's Known context expects 'Spec Main Shape Gate' red on every objectui PR because of objectui#10987; on PR #10995 head f990a9997 that check-run concluded success, so the premise did not hold for this head · dedupe words: Spec Main Shape Gate, ListViewSchema.tabs, p1-spec-alignment" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsACCEPT — PR objectui#10995 (
Fixes) ·domain:uiseat #1 · 2026-09-28T15:22Zsession_01DuWo5bdP9SdVebamn99GGk(os-steve), the dispatching seat. Checked against GitHub andorigin/main, ⛔ not against the reports.Checklist
- Shape. Draft →
main. The body opensFixes #8820.Clause-②: nosits at line start. PR assigneeos-steve. The work was done on a new branch; the prior-art branchclaude/issue-8820-dashboard-refresh-interval-secondswas neither pushed to nor rewritten. - Scope. 5 files, +324 / −44.
DashboardGridLayoutandDashboardRendererboth call one internal hook,useDashboardAutoRefresh, which carries the two byte-identical timers moved unchanged. It reads onlyrefreshIntervalSeconds, and it is not exported from the package entry.- A 22-case pin counts
onRefreshcalls under fake timers. patchfor@object-ui/plugin-dashboard.
- Governed-surface predicate. 5 paths:
NOT governed(AGENTS.md lit control: exit 3). - Checks on head
f990a9997. 43 check-runs: 40 success, 3 skipped, 0 failure. - Commits. Three, every trailer model-free.
- Contract review
5873010691:Served-tier: CONTRACT_REVIEW_TIER, headf990a99971e5abd0286028309b37e44716d96449(the current head), independence pair present, PASS. The reviewer:- compared the hook predicate by predicate against both base copies: the same early return, arithmetic, dependency list and
handleRefresh; - agreed that the prior art's number-only filter was correctly dropped, because base starts a timer for a string
'30'; - re-ran the pin's three legs: head 22 passed; base 2 failed (the shared-hook case); ablation 12 failed, the predicted set;
- found that all five acceptance bullets are met.
- compared the hook predicate by predicate against both base copies: the same early return, arithmetic, dependency list and
Filed at the merge, not in this PR. The hook's effect lists
handleRefresh, auseCallbackidentity, as a dependency. That is the shape AGENTS.md commandment 10 bans. It was carried verbatim from both base copies, because the card forbids a behaviour change, and the de-duplication made its repair one edit. The seat files it as its own card, with the pin cases on phase and handler swap as the constraints any fix must keep.Landing: not governed, green, review PASS on the current head ⇒
pr_readythenautomerge_enablethrough the relay, in this act. The card closes viaFixes.domain:uiseat #1 · ACCEPT · 2026-09-28T15:22Z
Generated by Claude Code
- Shape. Draft →
- added a commit that references this issue
on Oct 7, 2026
session_01DuzfS5chho38Yx1jxx9DEj). The rename itself is NOT this card's — it is #7783's, and it is implemented in open draft PR #8783. What this card was originally asking for has been deleted from it. See the re-scope comment for the full record of the mistake.Blocked-by: #8783 (
chore(deps): take the 17.4.0 @objectstack/* line). ⛔ Do not start before that floor lands.Why this card still exists after the duplicate was found
#8783 renames the key everywhere it is declared and read. Two things it does not do, both of them squarely this repo's own quality question rather than the rename's:
dashboardAuthoredInputs,ConfigPanel.i18nWiring,dashboard-config,page-app-dashboard-spec-parity— are declaration pins to a schema. Every one of them stays green if the interval never fires. That is exactly the class of gate the 2026-08-31 ruling is about: a test that goes green while the feature is dead.packages/plugin-dashboard/src/DashboardGridLayout.tsx:147-152andDashboardRenderer.tsx:450-455carry the sameif (!schema.<key> || <= 0 || !onRefresh) return; setInterval(handleRefresh, <key> * 1000). Two copies means the rename — and every future change to the refresh contract — has to be made twice and can drift once.Prior art on the branch — read it before you write anything
Branch
claude/issue-8820-dashboard-refresh-interval-seconds, commite5d95e1a, pushed, no PR (it would have fought #8783 for six files). It already contains a working version of both pieces:useDashboardAutoRefreshhook;onRefreshcall counts under fake timers — including the0trap case (0is a configured value, not "unset");37fb2b61, tree clean.⛔ One thing on that branch must be dropped when you rebase: it reads both spellings by presence, as a pre-bump compatibility shim. Once #8783's floor lands the old key is a tombstone that refuses at parse, so the fallback becomes unreachable code. Keep the hook and the pin; delete the both-spelling read.
Acceptance
refreshIntervalSeconds.⛔ Not in this card
refreshInterval→refreshIntervalSeconds: two declarations and six reader sites to move when the spec rename ships #7783 / PR chore(deps): take the 17.4.0 @objectstack/* line #8783. ⛔ Do not touch the publishedinputsrow, the config-panel key, the i18n label, or the pin tests; they are in that diff.plugin-report'srefreshIntervalis a DIFFERENT carrier and must not move. Measured one9d92120:refreshIntervalhas 39 occurrences repo-wide (not the 26 this card originally claimed). Dashboard carriers areDashboardGridLayout:147-152·DashboardRenderer:450-455·index.tsx:94·DashboardConfigPanel:108·metadata-form-i18n.ts:91·complex.ts:2103·complex.zod.ts:1154(that last one isDashboardConfigSchema, objectui-local). Untouched, different schemas:plugin-report/src/ReportViewer.tsx:230·types/src/reports.ts:479·types/src/zod/reports.zod.ts:177(objectui-local, zero@objectstack/specimport — the spec rename cannot reach it) ·types/src/ai.ts:516(AIInsightsSchema) ·types/src/data.ts:1378(DataBinding, milliseconds). Spec 17.4.0's own migration ledger names exactly one surface:dashboard.refreshInterval.sdui.manifest.jsonrefresh — belongs to whichever pin bump carries the rename, per dashboardrefreshInterval→refreshIntervalSeconds: two declarations and six reader sites to move when the spec rename ships #7783.Refs: #7783 (the rename, the duplicate this card was) · #8783 (its implementation, the floor) · hotcrm#1579 (the epic that filed this).