Repository navigation
Upgrade the @objectstack/* dependencies from 17.3.0 to 17.4.0 - #37
Merged
Merged
Conversation
The declared ranges (`^17.0.0`) already admitted 17.4.0; the lockfile was what held the app at 17.3.0, so this is a lockfile move — `pnpm update "@objectstack/*"` inside the range, package.json untouched. All 57 @objectstack packages the tree resolves are now 17.4.0. Verified on a real boot and in a real browser rather than by the version string changing: - Gates: validate / lint / typecheck / lint:i18n-gate all exit 0. - Clean-database `pnpm demo` boot: 820 rows, identical to the 17.3.0 baseline taken on the same worktree, both read at seeder quiescence. Every `readonly: true` stamp the seeder writes still lands — the 17.4 static-readonly INSERT strip treats a seed as the system act its own changeset says it is. - The strip IS live for a non-system caller: a REST insert carrying a forged `contract_number` gets the hook's number instead, and the field-level-security families (`submitted_at`, `approved_at`, `route_*`) refuse loudly with PERMISSION_DENIED before the strip can apply silently. - `readonly: true` still does not lock the ENGINE out (the claim at contract.object.ts:26, re-verified because 17.4 re-opened it): a non-system child insert moved the parent's readonly `planned_amount` roll-up from 0 to 12345. - Browser: sign-in, 我的合同 with rows, all three dashboards with real numbers, all seven contract-detail tabs, one zh-CN pass — zero HTTP >= 400 from any app surface. Rider, declared on the card: closes the two prose numbers of #34. `src/data/index.ts` said 780 rows and installs 820. The interval that `scripts/demo.mjs` and `src/data/plan-contracts.ts` gave two answers for is re-measured on 17.4 and now lives in demo.mjs only; plan-contracts.ts states the reason rung 1 resolves to nobody without depending on which boot mints the admin, which #21's handover changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
zhuangjianguo
marked this pull request as ready for review
September 9, 2026 14:40
This was referenced Sep 9, 2026
This was referenced Oct 9, 2026
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.
Fixes #35
Fixes #34
What changed
pnpm-lock.yaml— and nothing else that runs. The declared ranges (^17.0.0) alreadyadmitted 17.4.0, so
pnpm update "@objectstack/*"moved the pin inside the range andpackage.jsonis byte-identical tomain.engines: { protocol: '^17' }is untouched: 17.4is inside it.
spec,runtime,plugin-hono-server,cli)specifier: ^17.0.0→version: 17.4.0@objectstack/*the tree resolvespnpm install --frozen-lockfilebetter-authoverride familyThree prose lines also move, as the declared rider for #34 — see the last section.
Gates — exit code captured before any pipe
Run through this container's shared heavy-verify lock; each command redirected to its own log
and
$?read on the next line, never across a pipe. The 21lintwarnings are new in 17.4and are discussed under Findings — they are warnings, not failures, and none of them is caused
by this diff.
The boot-log diff against
mainBoth boots: same worktree, clean
.objectstack/data,pnpm demo, read at seederquiescence — three consecutive stable row-count reads and 20s of log silence — not at the
ready banner. (The card is right to insist: the 17.4 boot's ready banner is at 14:06:28 and the
seeder's completion line is at 14:07:12, 44 seconds and 140 log lines later.)
main@ b9a2524 (17.3.0)"inserted":820)"inserted":820)errored":120,referencesDropped":0)readonly: truestamps on 120 contractscontract_number0 null ·category0 ·direction0 ·route_legal_head0 ·execution_formalities0 ·submitted_at60 null (the 48 backfilled + 12 not yet submitted)planned_amountsum 47,700,500, no nullsSystem started with degraded capabilitiesno such table: sys_organization[sql-driver] … declared field 'id' asks for storage the platform's own 'id' column does not providesys_*tables (see Findings)onFieldsDropped/droppedFieldson the app's objectsThe measurement the card asked for first:
b398ad2The card's front-loaded risk was that 17.4 strips a static
readonlyfield from a non-systemcaller's INSERT payload, and that the ~20
readonly: truefields onclm_contractwould comeback NULL the way #26's
owner_iddid. The PM's assumption held, and it was tested threeways rather than by counting rows.
exactly the same counts as the 17.3 baseline (table above). A seed is the system act the
changeset's own text says it is.
POST /api/v1/data/clm_contractas the signed-in admin —a non-system caller — carrying
contract_number: 'FORGED-0001',category: 'nda',direction: 'purchase'answered201and storedORD-2026-0017/sales/sales: thecaller's values were dropped and
contract.hook.tsstamped its own. Read back out of SQLite,not from the response body.
submitted_at,approved_at,route_legal_head,route_gmin the payload answered403 PERMISSION_DENIED — "[Security] Field write denied: not permitted to edit [approved_at, route_gm, route_legal_head, submitted_at]", and so did aPATCHofapproved_at. Thefield-level security of DESIGN.md §04 sits in front of the strip for every family whose
forging would matter.
contract.object.ts:26re-verified, and left aloneThat line asserts "which is why
readonly: truedoes not lock the engine out" about the fivesummary roll-ups, and it was written against 17.3. Re-measured on 17.4: a non-system caller
inserted one
clm_payment_planchild (planned_amount: 12345) and the parent'sreadonly: trueplanned_amountmoved 0 → 12345.ObjectQL.recomputeSummariesstill writes throughreadonlyunder its elevated context. The sentence is still true, so it is not edited.Browser — what was clicked and what the browser did
Chromium at
/opt/pw-browsers/chromium-1194/chrome-linux/chrome, noplaywright install.The README's operator setup was performed first, because it is load-bearing for the surface the
card names: the three
Business Requesteraccounts were created (self-registration is closed onthis environment —
SELF_REGISTRATION_CLOSED, postureinvite_only— so they were createdthrough the admin endpoint), granted
clm_requester, andpnpm demowas re-run so its upsertshand the contracts over. The dev admin was put in the
clm_adminposition, without whichGET /api/v1/meta/app/clmserves itnavigation: [], exactly as README says./_console/mustChangePassword, a platform posture, not a regression)Business Requester 1Status Active 16,AMD-2026-0004,ICA-2026-0001,LSE-2026-0005,SUP-2026-0017… The #26 surface is alive on 17.4owner_idafter the operator stepBusiness Ownercolumn readsBusiness Requester 1compareTodelta rendering, plus the stage funnelRoutes:tiles 11 / 41 / 23 / 4 · Signed Value by Direction over 12 monthsObligations & Paymentslisting its two obligations andSigning & Archiveits archive blockzh-CNpassAccept-Language: zh-CN— 预览版 / 合同 / 我发起的 / 法务工作台 / 管理层看板 / 财务看板, widget titles and descriptions included. #32's bundle still resolves on 17.4Console and network, every pass: zero HTTP responses of 400 or worse from any app or
metadata endpoint, and zero page errors. Two lines recur on console boot and are neither
app-caused nor new-in-this-diff:
GET /favicon.ico→ 404./_console/favicon.icoand/_console/favicon.svgboth answer 200;the console shell asks for the root path. Cosmetic.
GET /api/v1/auth/organization/list→net::ERR_ABORTED, with[AuthProvider] Failed to load organizations: TypeError: Failed to fetch. The route itself isfine — called directly with the same session cookie it answers
200 [{"name":"Default Organization", …}]. It is a startup race in the Console's own AuthProvider (the request iscancelled, not refused). The route is new in
plugin-auth17.4 (17.3.0's bundle carries nosuch path), so the message is new too.
The
233222erisk the card flagged for dashboards —timeDimensions[].dateRangeclosing to apreset vocabulary — does not bite here: this app authors no
dateRangestring anywhere, onlyfilter: { approved_at: { $gte, $lte } }pluscompareToonapproval_throughput(
legal.dashboard.ts:193), which the executor lowers itself. That tile rendered its number andits comparison, and
ANALYTICS_DATE_RANGE_UNRECOGNIZEDappears nowhere in any boot log orbrowser session.
Findings — reported, not adopted
Per the card: 17.4 capabilities that make a workaround here obsolete are reported, not taken
up in this PR.
[sql-driver] declared field 'id' asks for storage…boot warnings are gone. Nothing in this repo caused or suppressed them; they simply stopped.
Nothing to do — recorded so the next reader does not go looking for them.
better-auth1.7.2 override block inpnpm-workspace.yaml. It is still load-bearing on 17.4 — the sign-in thatpnpm demo'shandover verifies passes with the pins in place, and the block names
objectstack-ai/objectstack#16186as its removal condition. Not touched.field-no-consumers. 17.4 runs 44 author-time rules where17.3 ran 42, and one of them reports a field "declared but nothing in this stack reads or
displays it". It fires on 21 fields —
clm_clause.standard_text/fallback_text,clm_party.address/bank_account/contact_phone/ …,clm_signature.signers/envelope_id,clm_review.internal_note/comments,clm_deviation.deviation_text, andmore. Verdict in every case: carrier-only — a translation label names it, nothing reads it.
Gates stay green (warnings), and deciding between "give it a view column" and "remove the
declaration" is product work on 21 fields, not a dependency bump. Worth its own card.
sys_*tables, on any boot after the first. Neithertouches app data; both are platform-side and new in 17.4:
does not exist yet, logged at error level.
ERROR Insert operation failed {"object":"sys_oauth_resource","error":{"message":"UNIQUE constraint failed: sys_oauth_resource.identifier …— a platform bootstrap row re-inserted ona database that already has it. Raised from
createWithAutonumberResync; 17.3 logged nothinghere, which is consistent with
65846bcsurfacing unique-constraint refusals that used to beswallowed. AGENTS.md's rule for these is report, never patch.
trusts a row count.
CLM_DEMO_SEED=1 objectstack dev --compile --ui --seed-adminon a cleandatabase (i.e. not
pnpm demo) installs 760 rows, not 820 — all 60clm_reviewrowsare refused with
Reviewer is required, exactly the failurescripts/demo.mjsexists toprevent — and the loader then logs
owner_id stays NULLon all 120 contracts while thedatabase ends up holding the dev admin's id in
owner_idon all 120.pnpm demonever takesthat path, so nothing here is broken for the product; the divergence between the log and the
table is a platform finding.
Rider: the two prose numbers of #34
Both re-measured on this run, which is the only condition under which the card allowed
Fixes #34:src/data/index.tssaid the demo installs 780 rows. Measured 820 on both 17.3.0 and17.4.0,
"inserted":820in both boot logs. One word.scripts/demo.mjscarried17.3.0's
18:59:54.118Z/18:59:54.931Z(0.813s) andsrc/data/plan-contracts.tscarried~1.8s (measured). Re-measured on 17.4.0 in the ordering demo.mjs is describing (one boot,demo on,
--seed-admin, empty.objectstack/data): last pass-1 seed write2026-09-09T14:26:53.833Z, account created2026-09-09T14:26:57.396Z— 3.563s. demo.mjsnow states that and says it is the only place the number lives.
plan-contracts.tsno longer rests on it. Its argument was "the seed loader runs BEFORE thefirst user account exists, so rung 1 (
type: 'manager') has nobody to resolve". On the pathpnpm demoactually takes that premise is false — Seed data:demo-en(default) anddemo-zh(card 08, M2) #21's handover mints the dev admin at14:06:08.123Z and the seed's last write lands at 14:07:12.072Z, 64 seconds later — while the
conclusion is still true for a different reason: every contract is launched by one of the three
business requesters DESIGN.md §10 leaves to the operator, so there is no owner to have a
manager (120
owner_idreferences unresolved after pass 2, measured). The comment now saysthat, and cites demo.mjs for the ordering rather than restating it.
Premises this run falsified
changeset-check.yml;.github/workflows/holds onlyci.yml, there is no.changeset/directory, no
@changesets/clidependency, and theskip-changesetlabel does not exist inthis repo. AGENTS.md is the consistent authority here: "Release notes: None per PR.
CHANGELOG.mdis written at release time by the maintainer." So this PR carries neither achangeset nor that label, deliberately. Creating the label would install a gate nothing reads.
sides of the bump.
timeout/ttl/idleTimeout/sessionTimeoutacross every.tsoutsidenode_modules(control on the same pattern style: 35 hits for
readonly: trueundersrc/), and zero forrecord_picker,'tree',ESignatureConfig,deadline.🤖 Generated with Claude Code
https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
Generated by Claude Code