Repository navigation
chore(deps): upgrade @objectstack/* to 17.7.0 - #2008
Merged
Merged
Conversation
Move all 21 @objectstack/* dependencies 17.6.0 -> 17.7.0 together, with specVersion and engines.protocol at ^17.7.0 (still protocol 17), and regenerate pnpm-lock.yaml (706 -> 704 packages; 54 @objectstack keys moved 1:1; @noble/ciphers and jose left the tree; no third-party version moved). Adopt what 17.7.0 changes for this app, per its release page and upgrade checklist: - translations: the 17.7.0 console's flow runner reads the `flows` group (liveness `live`), and `os lint` reports 108 i18n/missing-flow keys. Translate the seven screen flows in zh-CN, es-ES and ja-JP; the discount label interpolates QUOTE_DISCOUNT_CEILING (#1711). Fixes #1210, #1975. - pages: drop the lead page Tasks list's `new_task` / `edit` / `complete` action ids. crm_task defines no action, and 17.7.0's action-name-undefined rule refuses them. - scripts: the mailing-address backfill (#1836) takes `--unmapped FILE` from `os migrate unmapped-columns`, because 17.7.0 serves declared fields only and no REST read returns an orphaned column. - tests: register the platform identity objects in nine harnesses (the engine refuses unregistered names, objectstack#21545); expect RECORD_NOT_FOUND for writes to unreadable rows (objectstack#21812); give the share-link stub the header() the 17.7.0 route calls. - docs: What's new and docs/STATUS.md name 17.7.0; five comments that called 17.6.0 the pin now date themselves. Exercised in place on a database HotCRM created on 17.6.0 (migrate meta --stored: 0 rows; audit-metadata-bodies: nothing; plan: in sync; boot: 0 errors; a 17.6.0 approval decided on 17.7.0), and on a fresh 17.7.0 boot, with a 38-step browser pass in en and zh-CN. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This was referenced Oct 7, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 9, 2026
…tions — no app hooks, flows, approvals or notifications on the first sign-up (objectstack-ai#22069) Fixes objectstack-ai#22067 Clause-②: no ## What changed `claimSeedOwnership` hands every seeded row to the first platform admin. Its one write, `reown` (a predicate write per unowned shape, paged only when the engine refuses it for the per-row hook ceiling), now runs with `{ isSystem: true, skipAutomations: true }` instead of a bare `{ isSystem: true }`. This is triage's ruling on the card (comment 6035454999), verbatim: "Run `reown` with `{ isSystem: true, skipAutomations: true }`. This is the seed's own principle (`engine.ts:5413`: seed loads end-state data, not user events), carried to the attribution write that completes the seed." The page read (`readPage`) is unchanged. | file | change | | --- | --- | | `packages/plugins/plugin-security/src/claim-seed-ownership.ts` | `CLAIM_WRITE_CTX`, used by `reown` only; the header says why, and which hooks still run | | `packages/plugins/plugin-security/src/claim-seed-ownership.test.ts` | every `update` call (refused whole-set attempts, fallback pages, the closing whole-set write) carries exactly `{ isSystem: true, skipAutomations: true }` | | `packages/plugins/plugin-security/src/claim-seed-ownership-dispatch.pin.test.ts` (new) | real `ObjectQL` over `SqlDriver`: what the flag reaches, and the per-row hook ceiling | | `packages/qa/dogfood/test/seed-ownership-claim-dispatch.dogfood.test.ts` (new) | booted app, first sign-up over REST: no automation in the claim window; audit rows and sharing grants still land | | `.changeset/22067-seed-claim-skip-automations.md` | `patch` for `@objectstack/plugin-security`, with the line that app hooks no longer fire for the seed ownership claim | No `packages/objectql` change, the claim stays inside the request, and no narrower flag was added. ## Latency A/B, built from source Example: `examples/app-showcase`, the in-repo example that carries metadata hooks and record-change flows on seeded objects. Each run: `OS_SEED_ADMIN=0 objectstack dev --fresh --no-seed-admin --log-level debug` on an empty database. The seed inserts 132 rows; 20 s after `[Seeder] Seed loading complete`, the first user signs up with `POST /api/v1/auth/sign-up/email` (curl `time_total`). The claim window runs from `first user promoted to platform admin` to `handed 127 seeded record(s) to platform admin` in the same request's log. - **before**: this branch with `claim-seed-ownership.ts` restored to `56bf27affb` (blob `78f3850d4a`), `@objectstack/plugin-security` rebuilt; `dist/` verified to carry the base write (`multi: true, context: SYSTEM_CTX9 }` x1, `CLAIM_WRITE_CTX` x0), then restored by blob and rebuilt. - **after**: this branch, rebuilt. - One shared container for every run: absolute seconds are shared-box readings, and the ratios are what carries. | leg | sign-up wall time (3 runs) | claim window (3 runs) | | --- | --- | --- | | before | 1.540 / 1.670 / 1.675 s | 1.113 / 1.200 / 1.145 s | | after | 0.947 / 0.954 / 1.016 s | 0.499 / 0.466 / 0.557 s | Claim-window counts, identical in all three runs of each leg: | in the claim window | before | after | | --- | --- | --- | | rows claimed | 127 (18 of 22 eligible objects) | 127 (18 of 22) | | predicate writes (`Update operation starting`) | 44 | 44 | | app hook bodies run (`[BodyRunner] hook fired`) | 10 | 0 | | record-change flow dispatches (start condition evaluated) | 117 | 0 | | flows run / approvals opened (`approval node suspended run`) | 2 / 2 | 0 / 0 | | emails handed to the transport (`[LogTransport] would send email`) | 0 | 0 | | metadata-bound dispatches skipped (`Skipping metadata-bound hook (skipAutomations)`) | 0 | 416 | - The showcase's flows gate on transitions (`status == "done" && previous.status != "done"` and the like), so an owner-only change evaluates 117 start conditions and runs none of them, with one exception. The draft `showcase_approver_bindings` flow has no condition, so the claim ran it on both seeded `showcase_field_zoo` rows and opened 2 approval requests, each "resolved to no concrete approver". The showcase wires no email transport, so 0 emails before and after. - The 416 skipped dispatches: 35 app-hook dispatches (`showcase_normalize_task_title` 10, `showcase_guard_task_reopen` 10, `showcase_audit_task_completion` 10, `showcase_warn_over_budget` 5), 127 `sys_stamp_audit_update`, and 254 `sys_stamp_audit_insert` on plugin-audit's 127 `sys_audit_log` and 127 `sys_activity` inserts. - The card's hotcrm reading (17.7.0, a 354-row seed, about 45 s, 1,254 app hooks, 8 flow runs, 2 approvals, 8 emails) is far larger than anything in this repo's examples. The dogfood fixture below carries the hotcrm shape: a condition-free flow that notifies and opens an approval, on seeded rows. ## The pins - **The claim dispatches no automation**, measured on a booted app (`seed-ownership-claim-dispatch.dogfood.test.ts`). Four seeded deals carry a condition-free metadata hook and a condition-free record-change flow (notify the owner, then an approval). The real chain is mounted: automation, `RecordChangeTriggerPlugin`, approvals, messaging, `AuditPlugin` and sharing. The first user signs up over `POST /api/v1/auth/sign-up/email` (dev admin seed off). The claim window shows 0 metadata-hook dispatches, 0 flow runs, 0 approvals and 0 notifications, and every deal's `owner_id` is the admin. A positive control (the admin's own `PATCH` of one deal) fires the hook twice and runs the flow once, with its approval. - **Audit and sharing still run (objectstack-ai#2922).** The same window has one `sys_audit_log` update row per claimed deal, each recording the new owner and carrying `created_at`. It also has one `sys_record_share` rule grant per deal, from a criteria rule (`record.owner_id != null`, shared with the `owner_id` field) whose grants only the claim's owner change can earn. Before the sign-up there are 0 grants. - **What the flag reaches**, measured on a real engine (`claim-seed-ownership-dispatch.pin.test.ts`). A hook bound through `bindHooksToEngine` (so it carries `meta`, like an app hook) gets 0 dispatches. A code-registered hook gets one per-row dispatch per claimed row and phase, each with `isSystem`, `skipAutomations` and `skipTriggers` true. `skipTriggers` is the one field `RecordChangeTrigger`'s handler reads before dispatching a flow. Control: a plain system predicate write over the same rows fires the metadata hook 2 x N. - **The per-row hook ceiling (mechanism hypothesis 2), measured.** With `MAX_BULK_PER_ROW_HOOK_ROWS + 500` unowned rows, a `{ isSystem: true, skipAutomations: true }` predicate write is still refused whole (`code` and `limit` asserted, nothing written, no hook run). The engine counts matched rows against the ceiling whenever any hook covers the object and does not consult the flag. The claim then pages to every row (10 500 of 10 500), with 0 metadata-hook dispatches. `CLAIM_PAGE_ROWS` is unchanged, because its derivation is still right. - **Every caller (mechanism hypothesis 3).** Both callers write through `claimSeedOwnership`: the promotion pass (`bootstrap-platform-admin.ts`, inside the first sign-up) and the `app:seeded` settle pass (`security-plugin.ts` `claimSeedOwnershipOnSettle`, on every boot including warm boots). The changeset names both. **Ablation** (`node scripts/ablation-replace.mjs`, anchor `CLAIM_WRITE_CTX = { isSystem: true, skipAutomations: true };` replaced with `CLAIM_WRITE_CTX = { isSystem: true };`, landed x1 to x0, blob `fa63cffb37` to `7b581f3b26`): - `ablation-dist-preflight` found the marker in `dist/index.js` and `dist/index.mjs`. - The claim unit test went red: `expected { isSystem: true } to deeply equal { isSystem: true, …(1) }`. - The real-engine pin went red twice: `a metadata-bound hook fired for the claim: expected [ …(80) ] to deeply equal []`, and on the fallback `expected [ …(21000) ] to deeply equal []`. - The dogfood pin went red: `appHooks: 8, flowRuns: 4, approvals: 4, notifications: 4` against all-zero. - Restore: blob == HEAD and `git diff HEAD` empty, `@objectstack/plugin-security` rebuilt, and `--absent` preflight shows the marker gone from all 6 built files with a clean tree. ## Owner-change readers (first-party, in tree) I enumerated every metadata-bound hook and record-triggered flow in `examples/**` and `packages/**`. Outside test fixtures, `packages/**` ships none. **None reads an `owner_id` change**, so this needs no decision: - **app-crm.** Hook `opportunity_stage_probability` (`crm_opportunity`, before insert and update) reads `stage`. `convert-lead` is a screen flow, not record-triggered. - **app-showcase hooks** (`src/data/hooks/index.ts`). `showcase_normalize_task_title` reads `title`, `showcase_audit_task_completion` reads `done`, `showcase_warn_over_budget` reads `spent`/`budget`, `showcase_stamp_inquiry_defaults` reads `status`/`source` on insert, and `showcase_guard_task_reopen` reads `done`. - **app-showcase record-triggered flows** (`src/automation/flows/index.ts` unless noted). Their start conditions read `status` (`showcase_task_completed`, `_task_completed_slack`, `_task_completed_rest_ping`, `_task_done_notify_owner`, `_fan_out_notify`, `_resilient_sync`, `_project_closure`, `_invoice_signoff`, `_expense_signoff`, `_committee_quorum`), `assignee` (`showcase_task_assigned_notify`), `budget` (`showcase_budget_approval`), `health` (`showcase_project_escalation`), `priority` (`showcase_urgent_task_alert`) and `title` (`showcase_dynamic_approval`, `dynamic-approval.flow.ts`). Three are insert-only (`showcase_declarative_connector_ping`, `_mcp_connector_echo`, `_task_follow_up`). The draft `showcase_approver_bindings` (`approver-bindings.flow.ts`) has no condition. Several notify `{record.owner}`, a business field, not `owner_id`. - **app-todo.** Hook `task_logic` and flow `task_completion` read `status`. - **Fixture only.** `packages/verify/src/handle.fixture.ts` hooks on `hnd_deal` read no owner field. ## Gates and tests (head `aa21b6c3f9`) - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, re-derived on this change with no paths, gives 69 families: the 57 at dispatch plus 12 for the changeset and the new files. All 69 were run on `aa21b6c3f9`, exit code captured before any pipe, every one `exit 0`. `--ran` with the exit codes: "69 derived famil(ies) accounted for — 69 run, 0 NOT-MEASURED (a DERIVED zero …)". - `check-tenant-audit-census`: "OK -- 233 write call sites certified". The write context is a plain literal on purpose. Spelled `{ … } as const satisfies ExecutionContext`, it moved this write from "decidably elevated" to "elevation undecidable" in the census (122/103 against the page's 123/102), because the census does not unwrap `satisfies`. - `check:engine-double-contract`: "OK — 980 pinned". `check:nul-bytes`: "OK (scanned 10064 text file(s) …)". `check:dual-build-cjs-loads`: "106 published require entry point(s) across 66 package(s) load", after building the 8 packages its prerequisite named. - `@objectstack/plugin-security`, after `pnpm --filter '@objectstack/plugin-security^...' build`: - `typecheck` passes: `tsc` plus `check:test-typecheck: OK … 0 error(s)`; - `vitest run` gives 169 files, 3640 passed, 45 skipped. - Dogfood tests that drive the seed claim. Every `bootStack` boot runs it through the dev admin seed; the new one runs it through REST. - `--project isolated`: `seed-ownership-claim-dispatch`, `owner-anchor-and-bulk-writes`, `authored-row-write-scope`, `predicate-write-unreadable-not-matched`, `bulk-widener-probe` and `admin-platform-admin-standing`, 6 files, 46 passed. - `--project shared-showcase`: `showcase-private-owd` and `showcase-public-read-owd`, 2 files, 8 passed. - `@objectstack/dogfood` `typecheck` passes. - The rest of the dogfood suite is left to CI's Dogfood Regression Gate. - `eslint --no-inline-config --format json` on the 4 changed `.ts` files gives 4 files, 0 errors, 0 warnings. Each file matches the config (`--print-config` exits 0). This repo's config enables no type-aware linting (`eslint.config.mjs` near line 327: no `parserOptions.project`), so this diff cannot move any untouched file's verdict. The repo-wide `pnpm lint` is CI's. ## Acceptance notes - **ObjectQL's `sys_stamp_audit_insert` / `sys_stamp_audit_update` are skipped under `skipAutomations`.** They are bound through `ql.bindHooks` (`packages/objectql/src/plugin.ts`, packageId `sys:audit`), so they carry `meta`. The A/B log shows them among the 416 skipped dispatches. - For this write that changes nothing. The claim has no user to stamp into `updated_by`, and every driver stamps `created_at`/`updated_at` itself. Measured on the dogfood fixture: the claimed rows' `updated_at` advances (`…:55.456Z` to `…:57.042Z`), `updated_by` stays null as before, and the claim's audit rows carry `created_at`. - It does contradict the `ExecutionContext.skipAutomations` description, which lists audit among the hooks that still run. For a data import with "run automations" unchecked, `created_by`/`updated_by` stamping would be skipped. That is NOT MEASURED. Noted, not filed. Carrier: none. - **The multi-tenant twin, `claimOrgSeedOwnership`** (`packages/plugins/organizations/src/claim-org-seed-ownership.ts`), re-owns seed rows with single-id writes under a bare `{ isSystem: true }`, so it has the same dispatch shape. It is not touched here. Its reach is NOT MEASURED (it needs a multi-tenant boot). Carrier: none. - **The draft `showcase_approver_bindings`** says "Draft on purpose", but draft flows fire their triggers (service-automation logs that at boot). At `56bf27affb` the first sign-up's claim ran it and opened 2 approval requests with no concrete approver. On this branch the claim no longer reaches it. A user edit of a `showcase_field_zoo` row still would; that is NOT MEASURED. Carrier: none. References only: objectstack-ai#14530, objectstack-ai#14719, objectstack-ai#21486, objectstack-ai/hotcrm#2008. --- _Generated by [Claude Code](https://claude.ai/code/session_01WMQprn46CND82KmY8sZWBu)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
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.
Description
Upgrade HotCRM from
@objectstack/*17.6.0 to 17.7.0, following the official upgrade guide (/docs/upgrading, Moving the dependency pins) and the 17.7.0 release page's upgrade checklist. The page says 17.6.0 → 17.7.0 had not been exercised on any application; this PR is that exercise, on this app, in place and fresh.The upgrade turned
pnpm verifyred in three places. Each one is a 17.7.0 change the release notes name, and each is answered here at the place it reached this app:os validaterefused three action ids on the lead page's Tasks related list (action-name-undefined).lint:i18n-gatereported 108i18n/missing-flowkeys. The 17.7.0 console now reads screen-flow translations, andos lintreports the missing ones.sys_userwithout registering it, and the engine now refuses unregistered names (objectstack#21545);RECORD_NOT_FOUND(objectstack#21812);header()the 17.7.0 share-link route calls.Type of Change
Related Issues
Fixes #1210
Fixes #1975
Related to #1836 (the mailing-address conversion now has a 17.7.0 route) · #1992 (its
Restart-when:is met once this lands: the installed@objectstack/objectql17.7.0CHANGELOG.mdcontains#21646)Changes Made
@objectstack/*packages move 17.6.0 → 17.7.0, exact.specVersionandengines.protocolmove to^17.7.0in the manifest and the stack config. The lockfile was regenerated:@objectstack/*/create-objectstackkeys moved 1:1;@noble/ciphersandjoseleft the tree;src/sales/translations/{zh-CN,es-ES,ja-JP}/app.ts): a newflowssection covers the 7 screen flows, 36 keys × 3 locales.QUOTE_DISCOUNT_CEILING, so the ceiling stays in_thresholds.tsalone (feat(quote): show the discount ceiling on the quote-generation screen #1711).flowsliveness row readslive, with evidence pointing at the 17.7.0 console'sFlowRunner.tsx.lead_detail.page.ts: three action ids removed.crm_taskdefines no action, so they never drew anything. The platform's own New and View all stay.scripts/backfill-contact-mailing-address.ts: a new--unmapped FILEoption takes the JSON fromobjectstack migrate unmapped-columns --object crm_contact --json, because no REST read returns an orphaned column on 17.7.0. Without the flag, the script behaves as before.test/helpers/identity-objects.tsregisters the identity objectsplugin-authregisters in a real boot, mounted in nine harnesses;RECORD_NOT_FOUNDfor writes to unreadable rows; the writes are still refused and still unwritten;header();docs/STATUS.mdnow name 17.7.0;.changeset/objectstack-17-7-0.md(minor).Testing
pnpm verifygreen:os-verify-lock: VERDICT command-exit 0.Validation passed(18 Objects, 359 Fields)1 warning(s), 18 suggestion(s)0 i18n/missing-* issuessource hygiene clean,source token ratchet cleanBuild completeTest Files 177 passed (177),Tests 3797 passed | 1 skippedos migrate meta --from 17: 12 mechanical edits, all the decision-nodemode: 'inclusive'the 17.6.0 upgrade already reviewed and left unwritten. None of 17.7.0's new conversions touches this app.os migrate meta --stored: 0 stored rows, so it attests nothing here.os migrate audit-metadata-bodies: nothing to rewrite.os migrate plan: in sync.status: approvedandapproval_status: approved;折扣 % (≤ 60).sharing.enabled: trueholds on 17.7.0;mailing_streetcolumn held a value:mailing_*key;os migrate unmapped-columnsread the value;--unmapped … --applyconverted 1/1, and the address read back;Screenshots
The 38 step screenshots, with before/after zh-CN dialogs, were taken in the session that ran this upgrade and shared with the maintainer there. They are not committed.
Checklist
pnpm changeset) — required on every PR, or theskip-changesetlabel is appliedpage:cardon Sales Home has adescriptionthe card does not declare. That is a long-known gap recorded inhome.page.ts, not touched here.@objectstack/*17.7.0 is on npmlatest)Additional Notes
FLOW_SCREEN_FIELD_NO_OPTIONS), so they stay as authored.signingSecretonly when the build setsHOTCRM_BILLING_SIGNING_SECRET, and no run here set it.🤖 Generated with Claude Code
https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT