feat(spec): verifiedAt re-verification clock; re-verify action.undoable/type against objectui (#3714 follow-up) - #3845
Merged
Conversation
…le/type against objectui (#3714 follow-up) A ledger entry is a claim with a timestamp, and twice now one has been falsified by code moving under it — flow.status (#3711) and action.undoable (#3714), both understated, both found only because a sweep aimed at the opposite failure walked past them. Nothing in the gate asked how old a claim was. 1. verifiedAt clock. Entries may carry "verifiedAt": "YYYY-MM-DD". Age never fails CI (re-verification is a worklist, not a merge gate) — one summary line per run, full worklist under `check:liveness --stale-verification[=days]`, default 180d. A malformed or future date DOES fail: it would silently exempt the entry from every staleness window, the same silent-no-op shape this ledger exists to catch. Calendar-invalid dates are rejected too, since new Date('2026-02-30') rolls over rather than throwing. New pure module verification.mts + 11 unit tests, incl. a contract test that every verifiedAt in the shipped ledgers parses. 2. Re-verified against objectui 732b1bf: - action.undoable — both readers stand; evidence now closes the call graph end to end (gate -> result.undo -> globalUndoManager + toast -> undoCtl -> UndoManager -> dataSource). The cited RecordDetailView lines had already drifted 545->573 / 404->432 one day after the issue was filed, hence the pinned sha. - action.type — api -> executeAPI, form -> executeForm, both real. 3. content/docs/ui/actions.mdx told authors `api` and `form` have "no runtime executor / renderer today". Both have executors in objectui's ActionRunner, and the ledger's own action.type entry recorded `form` as live since #2377 — the same understatement shape as #3714, one page over. Added table rows for both, kept the true half of the callout (shortcut/bulkEnabled really are unwired), and added `undoable` to the UX property list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ajwvrmd1hDC9RBofYBhGuR
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 28, 2026 10:26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #3829 / #3714, covering the three next steps agreed there. The objectui repo was added to the session for this pass, so the claims below are verified against
objectstack-ai/objectui@732b1bfrather than carried over from the issue.1.
action.undoablere-verified — call graph closed by handBoth readers stand. The evidence now records the whole chain rather than two isolated gates:
app-shell/useConsoleActionRuntime.tsx:409andapp-shell/RecordDetailView.tsx:573each gateif (action.undoable …)on buildingresult.undo;core/ActionRunner.ts:640-643pushes it ontoglobalUndoManagerand passesundoto the toast handler;undoCtl.undo()→useGlobalUndo→UndoManager→dataSource.That's the "close the call graph by hand" standard this ledger's own methodology section asks for, short of booting the app.
The cited line numbers had already drifted —
RecordDetailViewmoved 545→573 and 404→432 in the ~1 day between the issue being filed and this pass. Nothing behavioural, but it's a live demonstration of why an evidence string with bare line numbers rots. objectui-side evidence now pins the commit (objectui @732b1bf).2.
content/docs/ui/actions.mdx— the same understatement, one page overThe page told authors:
Both are false at objectui HEAD:
api→executeAPI(ActionRunner.ts:974, string-or-ApiConfigendpoint withmethod/headers/queryParams/responseType),form→executeForm(:920, routes to/forms/:target?recordId=…). The ledger's ownaction.typeentry has recordedformas live since #2377 — the doc and the ledger have been contradicting each other.apiandformrows to the type table.shortcutandbulkEnabledreally are unwired, with the reason for each, plus a link to the ledger.undoableto the UX property list — the author-facing payoff of [P3] liveness:action.undoableis marked experimental but has two real runtime readers #3714, which nothing in the guide mentioned.action.typeledger entry re-verified and dated.3.
verifiedAt— the re-verification clockTwo entries have now been falsified by code moving under them (
flow.status#3711,action.undoable#3714), both understated, and both found only because a sweep aimed at the opposite failure walked past them. Nothing in the gate asked how old a claim was.Entries may now carry
"verifiedAt": "YYYY-MM-DD". The asymmetry is the design:pnpm check:liveness --stale-verification[=days]prints the worklist, stale oldest-first then undated. Default 180d.new Date('2026-02-30')rolls over to March 2 rather than throwing.New pure module
verification.mts(injectablenow/threshold, so the gate, the tests and any future sweep share one arithmetic) +verification.test.ts.Currently 2 of 401 entries are dated. The rest predate the field and report as undated — the README says to date them as you re-verify rather than back-fill guesses.
Verification
pnpm --filter @objectstack/spec check:liveness— exits 0.verifiedAt: "2026-13-99"→✗ 1 malformed \verifiedAt` value(s)`, gate exits 1. Value restored.pnpm --filter @objectstack/spec test— 260 files, 6763 tests passed (includes the 11 newverification.test.tscases).pnpm vitest run src/utils/lint-liveness-properties.test.ts(cli) — 15 passed.packages/spec/scripts/**is outside the lint config (pre-existing, same ascheck-liveness.mts).Not done: booting the app and clicking Undo. The static chain is closed end to end, which the README treats as sufficient for a presence claim — but it also says the decisive check is "author the property, boot the app and look." Worth a
dogfood-verificationpass before this pattern gets copied to other objectui-backed entries.Generated by Claude Code