Repository navigation
chore: version packages - #21988
Open
github-actions[bot] wants to merge 1 commit into
Open
chore: version packages#21988github-actions[bot] wants to merge 1 commit into
github-actions[bot] wants to merge 1 commit into
Conversation
This was referenced Oct 6, 2026
Merged
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
October 6, 2026 18:22
3deab49 to
db6c3cd
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
October 7, 2026 00:29
db6c3cd to
c2bbee9
Compare
This was referenced Oct 7, 2026
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
October 7, 2026 06:17
c2bbee9 to
3732751
Compare
This was referenced Oct 7, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 7, 2026
…ui#11670 and objectstack-ai#11669) (objectstack-ai#22015) Fixes objectstack-ai#21996 Clause-②: no This moves the bundled Console's objectui pin from `0abd4f9f8769` (objectstack-ai#21807, PR objectstack-ai#21827) to `a58626c88dc85954bd0af24f16ebb69454c03eee`, which was objectui `main` when this run started. The new pin carries `5ba255538a` (objectui#11670): the Studio flow designer saves a screen field's `Min` / `Max` as numbers. That is the commit the timing clause of objectstack-ai#21898 (PR objectstack-ai#21974) waits on. The range also carries `c3623eb1` (objectui PR 11669, Studio's shared picklists). The bump follows the repo's own procedure (`scripts/bump-objectui.sh`, then `pnpm objectui:build` and `node scripts/gen-sdui-manifest-node.mjs`) and the gates it derives. It adds no step to that procedure. Every file below is one the procedure or a gate wrote, and nothing else rides here. ⛔ This is not a release act. Version Packages PR objectstack-ai#21988 is untouched. It edits `packages/console/CHANGELOG.md` and `packages/console/package.json`, and this diff edits neither. The console's release input is the new `.changeset/console-a58626c88dc8.md`. ## Range - objectui `git ls-remote origin refs/heads/main` read `a58626c88dc85954bd0af24f16ebb69454c03eee` at 2026-10-06T15:32:48Z, the same sha the dispatch read at 15:08Z. - **REST `compare` on objectstack-ai/objectui proves containment** (base...head, head = the new pin): - `5ba255538a...a58626c88d`: `status: ahead`, `ahead_by: 18`, `behind_by: 0`, merge base `5ba255538a1115e2dc2d52fa094ecf491621a5fe`. The pin contains objectui#11670. - `c3623eb1...a58626c88d`: `ahead`, 15 ahead, 0 behind. - `0abd4f9f87...a58626c88d`: `ahead`, 36 ahead, 0 behind. - `0abd4f9f8769..a58626c88dc8` has 36 commits, 0 merges and 252 changed paths. - objectui declared 36 changesets over the range, and all 36 release. There are 0 release-nothing changesets and 0 commits without a changeset. None declares `major`. Three carry the author's breaking annotation. **The highest declared level is `minor`, so the console changeset is `minor`.** The script takes the highest level objectui itself declared over OLD..NEW. These counts are the bump's own digest output. - No commit subject in the range carries `!`. `git log --format='%h %s' 0abd4f9f8..a58626c88 | grep -E '^[0-9a-f]+ [a-z]+(\([^)]*\))?!:'` matches nothing (exit 1). - Re-read at 16:25:59Z, objectui `main` reads `834c5594b`, four commits past the pin (objectui#11685, objectui PR 11724, objectui#11682 and objectui#11687). This PR does not carry them. ## Declared-breaking entries and the ADR-0087 disposition The script wrote its `adr-0087: TODO` placeholder into the console changeset. It is answered `not-required (no-migration-prescription)`, in the wording the previous bumps used. `check-adr-0087-registration --base origin/main` reports "1 declared-breaking changeset(s), each carrying an ADR-0087 disposition". `check-changeset-no-major --base origin/main` exits 0. Each entry is objectui's own package surface: 1. **objectui#4425 (`eb4552e71`)**: objectui's `@object-ui/types` faces refuse `label` on a `metric-card` in an objectui `dashboard` node's `widgets[]` slot. 2. **objectui#11709 (`89cc738da`)**: inside a widget's legacy `component` envelope, a `metric-card` is judged by the slot's component arm alone. - `metric-card` is not a member of `@objectstack/spec`'s `PageComponentType` and has no `ComponentPropsMap` row. - The ObjectStack dashboard widget's `type` vocabulary is the chart types (`metric`, `kpi` and the rest). - No example or package source here authors a `metric-card` node, and the regenerated `sdui.manifest.json` has no `metric-card` entry. 3. **objectui#11678 (`48c82c9d5`)**: `@object-ui/app-shell`'s `RecentItem` becomes a union, and `addRecentItem` stops taking a label for an object, dashboard, page or report entry. No code here imports `@object-ui/app-shell`. The only code here that names the `ui.recent` preference is the SQL driver's tests, which store the row as an opaque value. ## What changed here (22 files, +491 / -161) | file | written by | gate that required it | |---|---|---| | `.objectui-sha` | `scripts/bump-objectui.sh a58626c88dc85954bd0af24f16ebb69454c03eee --no-commit` | Console Pin Gate builds what it names | | `.changeset/console-a58626c88dc8.md` | the same run (the digest); the only later edit is the ADR-0087 answer above | `check-adr-0087-registration`, `check:objectui-changeset` | | `sdui.manifest.json`, `scripts/sdui-manifest.record.json` | `node scripts/gen-sdui-manifest-node.mjs` over `.cache/objectui-a58626c88dc8` | `check-sdui-manifest.mjs` | | `packages/sdui-parser/objectui-lockstep.json` | `pnpm gen:sdui-lockstep` against the same tree | `check:sdui-lockstep` | | 54 asserting citations in 12 files under `packages/spec/src`; `migrations/registry.ts` via `gen:migration-registry` | re-measured by hand (below) | `check:objectui-pin-citations` | | `.changeset/objectui-pin-citations-a58626c88dc8.md` | hand-written, `@objectstack/spec` patch | `check:published-files` / Check Changeset (the shipped describes name the pin) | | `content/docs/references/ui/view.mdx` | `check:generated --fix` | `check:docs` | - **The bump script.** It ran against the sibling objectui checkout after `git fetch origin main` there, with the target sha passed explicitly. The range walked completely without a deepen. The checkout was read and fetched only. - **The manifest.** It still has 107 components. The sha256 moves from `f95d406a584e…` to `e0654735a318…`. - One input moved: `record:details` `sections` gained a sentence in its description. A section that names a field group now takes the group's `visibleWhen` as well (objectui#11630). - No key, type or required flag moves. - The record moves its pin and `modulesRoot`. objectui's workspace version stays 17.7.0. - **The lockstep.** It records 214 grammar lines (blob `0131f27cf86d`), 25 diagnostic codes and containment predicate `76c18fb95d1f`. All are unchanged, so no port is owed. - **The 54 asserting pin citations were re-measured, not restamped.** - **Method.** Each record's cited objectui files were resolved against the tree at `0abd4f9f8` and intersected with the range's 252 changed paths. Every anchor in a changed file was mapped through `git diff -U0 0abd4f9f8 a58626c88` and its text compared at both pins. - **Fifteen cited files changed:** `ObjectKanban.tsx`, `KanbanImpl.tsx`, `ObjectTree.tsx`, `ObjectTimeline.tsx`, `record-details.tsx`, `action-group.tsx`, `action-menu.tsx`, `static-params.ts`, `MetricWidget.tsx`, `MetricCard.tsx`, `form.tsx`, `plugin-kanban.mdx` and the `en` / `zh` / `de` packs. - **Result.** Every cited line in them is byte-identical at the new pin. 97 anchor citations in the records' current text MOVED and were re-pointed, and each record's new hop sentence says by how much. No read point an asserting record cites changed content or died. Every other cited file is byte-identical across the hop (`git diff --quiet`). - **Records.** Each of the 41 comment records gains a dated 2026-10-06 hop sentence and keeps its earlier history. The `FormField.span` describe changes its sha only: `WIDE_FIELD_TYPES`, the field-type alias table and `spanLadderFor` are byte-identical. - **Counts.** Each was re-taken by the record's own method and reads as before: the `keyboardNavigation` hit lines (15, against 3 for `schema.editable`), `ObjectKanban.tsx`'s `quickAdd` / `onQuickAdd` (2 each, against 11 for `onCardClick`), and the `ElementDataSourceGate` occurrences in the five `src/index.tsx` shells (0, 3, 3, 3 and 4). - **Corpus counts.** The six migration entries' counts were re-taken with `git grep -o -F`. That method first reproduced every `0abd4f9f8` number: 7650 files, `objectstack` 17390, `@objectstack/spec` 7209, `timeout` 1360, `RuntimeConfig` 293, `resourceLimits` 2, `useState` 2478, `window` 4194, `period` 238, `interval` 195, `metrics` 401, `Span` 508, `SpanSchema` 57, `TTL` 182 and `tenant` 1318. - The new readings are 7754 files, `objectstack` 17468, `@objectstack/spec` 7246, `timeout` 1431, `RuntimeConfig` 299, `resourceLimits` 2 (the same two `packages/app-shell` hits), `useState` 2491, `window` 4255, `period` 247, `interval` 200, `metrics` 401, `TTL` 184 and `tenant` 1319. - All 99 checked tokens still read zero at both pins: the export lists of `plugin-lifecycle-advanced.zod.ts`, `tracing.zod.ts` and `metrics.zod.ts`, plus every named key. The exceptions are `Span` / `SpanSchema`, which read 509 / 57. The one new `Span` hit is a `colSpan`. No example, test or gate needed adapting, and no code changed outside generated records, citations and changesets. ## Console build (the local Console Pin Gate equivalent) Both steps ran under the verify lock. - **Dependency build.** `turbo run build --filter=@objectstack/client --filter=@objectstack/spec --filter=@objectstack/sdui-parser` ran 33 tasks and exited 0 in 2m05s. - **Console build.** `pnpm objectui:build` exited 0 in 7m03s on the shared box. - `OBJECTUI_ROOT` pointed at a scratch repository holding only the pin commit, so the build's `git worktree add` registered nothing in the sibling objectui checkout. - The log reports "Bundle canary 'import/jobs' present". - It reports "Single-zod canary: exactly one zod version literal {major:4,minor:6,patch:5}". - It reports **"Console bundle carries THIS tree's @objectstack/spec, and only it"**. - It reports "@objectstack/console dist ready (64400 KB) from objectui@a58626c88dc8". - `check:console-sha` and `check:console-injection` exit 0 against that dist. ## Gates and tests (head `b86e6f264`) - **Derived gates.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 127 commands from the 22-path diff. A throwaway merge onto current `main` (`1bc6ca1d3`) derives the same 127. - All 127 ran at `b86e6f264` after a full workspace build (`turbo run build --filter=!@objectstack/docs`), each exit code written to disk. All 127 exit 0. `--ran` reports "127 derived, 127 run, 0 NOT-MEASURED, 0 UNRUN", with every exit code recorded. - An earlier pass also ran all 127. There, `check:skill-examples` and `check:dual-build-cjs-loads` answered PREREQUISITE NOT MET (exit 3). `check:dts-closure` and `check:docs-transcript-drift` read a `packages/spec/dist` that a concurrent build of mine was rewriting. All four are green on the rerun above. - **Pin citations.** `check:objectui-pin-citations --verify-anchors` reads the sibling checkout at the pin. It reports "54 asserting objectui pin citation(s) match .objectui-sha (a58626c88)" and "7 anchor content assertion(s) verified against objectui at a58626c88". - **Other pin gates.** `check:sdui-lockstep` reports "this copy is byte-identical to objectui@a58626c88dc8". `check-sdui-manifest` reports "recorded at the live objectui pin a58626c88dc8… @object-ui 17.7.0 is the version objectui a58626c88dc8… declares". - **`@objectstack/spec`.** `vitest run --project local` passed 619 files: 18485 tests, 1 todo. `typecheck` exits 0. - **`@objectstack/sdui-parser`.** 14 files and 225 tests pass, and `typecheck` exits 0. - **Suites that read the regenerated manifest:** - `@objectstack/lint` `src/validate-jsx-pages.production-witness.test.ts`: 5 passed. - `@objectstack/metadata-protocol` `src/protocol.runtime-authoring-gate.test.ts`: 70 passed. - `@objectstack/cli` unit tier `src/utils/sdui-manifest.test.ts`, `src/utils/console.sha-drift.test.ts` and `test/validate-build-gate-parity.test.ts`: 3 files, 87 passed. - The CLI integration tier (`test/jsx-gate-manifest-notice.e2e.test.ts` spawns the CLI) is declared to CI. - **Lint.** `eslint --no-inline-config --format json` over the 15 changed TS files reports 15 files, 0 errors and 0 warnings. - The lint population is `**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` (`eslint.config.mjs:971`). - The config enables no type-aware linting (`:328`), so this diff cannot move a verdict on an untouched file. - Repo-wide `pnpm lint` is left to CI. - **Console Pin Gate.** It is CI's to run on this head, and its verdict is read on the PR. - **Browser smoke.** NOT MEASURED. The dispatch asked for none, and the range's own landings carry their objectui-side tests. ## Acceptance notes - **objectui#11638 retired a read that a dated record still lists.** The two action containers no longer read a member's `properties.params`. - `ui/component.zod.ts`'s action-container member docblock still lists that read under "Read, and refused anyway". The docblock is in the historical spelling, measured at `.objectui-sha` pin `2e818d0b51ec`. - The spec refuses the key either way, so the accept set and the save gate do not move, and the renderer and the save gate still agree. - The record is a dated measurement that the pin-citation gate does not hold to the current pin, so nothing here re-measures it. Noted, not filed. Carrier: none. - **The shipped console now reads `KanbanConfig.summarizeField`** (objectui#11629): each kanban column header totals it. The key is declared in `ui/view.zod.ts`, and the liveness ledger carries no row for it, so nothing here is owed. - **The console's sign-up gating now reads `features.audiencePosture`** from `/api/v1/auth/config` (objectui#11691, objectui#11705). This repo's auth plugin serves that key (`auth-manager.ts`, `getPublicConfig`) and serves `GET /auth/bootstrap-status`, so that seam holds. - `main` moved four commits past this branch's base (`f0022c46c..1bc6ca1`). None of them touches a file this diff touches, and `git merge-tree --write-tree HEAD origin/main` is clean. No merge was made; the merge queue rebuilds on the current `main`. - **Writes.** One draft PR through the relay, one label write on the PR (assignee `os-justin`), and the report comment on objectstack-ai#21996. --- _Generated by [Claude Code](https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
October 7, 2026 12:17
3732751 to
eb92180
Compare
This was referenced Oct 7, 2026
This was referenced Oct 8, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 9, 2026
…-pr lane, and stop prerelease cuts re-dating the last GA (objectstack-ai#22095) Part of objectstack-ai#22085. This PR carries the half of the card that holds on measurement. The half it does not carry needs a decision first (see "What still stops the refresh"). Clause-②: no ## What this changes 1. **`sync-release-index-currency` no longer re-dates the newest GA on a prerelease cut.** `rewriteStatusField` re-stamped today's date into a `current series:` field that already named the newest GA. The gate it serves (`indexCurrencyFindings`) judges the version only. The date it holds is the release date of the version the run moves the field TO, so a run that moves nothing (every `next` or `rc` cut) is not that version's version commit. A same-version field is now left alone. Battery B's case that pinned the re-date is inverted, and a new battery, `Control H: a prerelease cut never re-dates the newest GA` (7 cases), pins the `next` cut, the `rc` cut, a positive control (a stale entry on the same later day still gets the version and that day), and `syncIndex` end to end on a temp checkout (no write, bytes identical). The roster floor goes from 7 to 8. 2. **`release.yml` `version-pr` › `Validate the post-version tree` validates the blank template's three major-boundary paths instead of refusing them.** It reuses gates the repo already runs, with no build: - `pnpm --filter create-objectstack test`: the template rendered by the scaffolder's own copy and identity rewrite, from a template packed the way npm ships it, plus the ratchets in `template-consistency.test.ts` that judge all three stamps against create-objectstack's NEW major; - `pnpm --filter @objectstack/spec check:template-manifests`. `protocol-version.ts` gets its gates too: - `protocol-version.test.ts` as ONE file (5 s). It does not need the whole spec suite, which is what the step's comment used to claim; - `check:spec-changes` and `check:upgrade-guide`, the two artifacts derived from `PROTOCOL_MAJOR`. It **stays refused**, for the measured reason below. The refusal is now collected instead of exiting first, so a boundary run reports every gate's verdict together. Every pnpm filter carries `--fail-if-no-match`, because a filter that matches nothing exits 0 having run nothing (measured: 0 without it, 1 with it). ## Pin: the boundary train, replayed (throwaway tree, never committed) Tree: this branch at `3079e4aef0` plus PR objectstack-ai#22084's `.changeset/pre.json` and `.changeset/22080-v18-line-opens.md`, carried by one local commit that was never pushed. The steps were extracted from this branch's `release.yml` with a YAML parser and run verbatim with `RUNNER_TEMP` / `GITHUB_OUTPUT` / `GITHUB_STEP_SUMMARY` set. | step | exit | reading | |---|---|---| | `Render the post-version tree` (the full root `version` script) | 0 | 69 of 69 `fixed` members at `18.0.0-next.0`, `pre.json` unchanged (`mode: pre`, `tag: next`). `PROTOCOL_VERSION 17.0.0 → 18.0.0`. Template stamps at `^18.0.0` / `'^18'`. 9 docs pins moved to `18.0.0-next.0`. Release index: "already names the newest GA … nothing rewritten" | | release index v17 entry | unchanged | `(current series: 17.7.0, released 2026-10-06)`; `git diff HEAD -- content/docs/releases/index.mdx` empty. Before this PR, the same replay wrote `released 2026-10-07` (reproduced on PR objectstack-ai#22084's head `52f7a509c6`) | | shape assertion | pass | 235 paths moved, 0 outside the reviewed surface. It was 236 before this PR; the difference is the release index | | `Validate the post-version tree` | **1** | 11 gates green: the 9 existing content gates except `check:release-notes`, plus the create-objectstack suite, `check:template-manifests` and the lockstep test. 3 gates red: `check:release-notes`, `check:spec-changes`, `check:upgrade-guide`. 1 unvalidated: `protocol-version.ts` | | `Restore the pre-version tree` | 0 | 37 pending changesets, tree clean | **Control on an ordinary train.** This branch without the two opening files is the 17.8.0 refresh objectstack-ai#21988 gets today. `Render` exits 0 (194 paths, and the release index stamps `17.8.0, released 2026-10-07`, version and date together). `Validate` exits **0** with all 14 gates green, holding 44 s. `Restore` exits 0. **Negative control for the template gate.** On the boundary tree, `specVersion` in the blank manifest was set back to `^17.0.0`. `template-consistency.test.ts` then exits 1 with 2 failed, and the file was restored by hash (`0956082cfd8e` both sides). **Baseline.** `main`'s own step, replayed on PR objectstack-ai#22084's head, exits 1 at the old blanket refusal and names all 4 paths. This reproduces the measurement the card rests on. ## Why `protocol-version.ts` is still refused (measured, not guessed) Moving the protocol major at version time does more than move a constant: - **Two derived artifacts go stale.** On the boundary tree, `check:spec-changes` exits 1 ("spec-changes.json is stale") and `check:upgrade-guide` exits 1. Both pass on the pre-version tree (control). Regenerating them in the throwaway changes `packages/spec/spec-changes.json` by 6342 lines, and adds a 1024-line `Protocol 17 → 18` section to `docs/protocol-upgrade-guide.md`. The version pass regenerates neither. Both gates run in the required `TypeScript Type Check` job, so a version PR let through as things stand would turn `main` red on its next ordinary PR. - **The handshake refuses this repository's own example apps.** `assertProtocolCompat` runs on the app load seam (`packages/runtime/src/app-plugin.ts:421`). Probed with the post-version constant, `checkProtocolCompat` gives `^17` → `incompatible` (`OS_PROTOCOL_INCOMPATIBLE`) and `^18` → `ok`. On the pre-version tree it is the reverse. `examples/app-crm`, `app-showcase` and `app-todo`, plus the two packages in `app-multi-package`, declare `engines: { protocol: '^17' }`. No gate this lane can afford boots them. So the refusal stays, as Done-when 1's last clause provides ("The refusal stays for any major-only path still unvalidated"), with its message rewritten to say exactly this. ## What still stops objectstack-ai#21988 refreshing into 18.0.0-next.0 These are three decisions, set out with options and a recommendation in the card's `os-dev-report`. None of them is made here: 1. **How the protocol major moves at the opening.** Either the version pass regenerates the two artifacts and restamps in-repo `engines.protocol` (the version chain grows), or an ordinary PR with CI moves the protocol major ahead of the version PR (the lockstep test's definition changes). 2. **`check:release-notes` is red at the boundary** (it is already in this step). It counts `## 18.0.0-next.0` as "shipped a 18.x release" and asks for `content/docs/releases/v18.mdx` plus its `meta.json` entry. That content is release-owned, and this PR does not write it. 3. **Docs image pins during the `next` line.** These were measured and not edited (Done-when 4). On the first prerelease, `sync-docs-image-tags` moves 9 pins from `17.7.0` to `18.0.0-next.0`: - `docker/README.md`: 3 image tags and 1 build-arg; - `content/docs/deployment/self-hosting.mdx`: 3 image tags and 1 npm pin; - `content/docs/upgrading.mdx`: 1 image tag. Meanwhile `npm view` gives `latest: 17.7.0` (and `rc: 17.0.0-rc.6`, no `next` yet) for `@objectstack/cli`, `@objectstack/spec` and `create-objectstack`. The ghcr `latest` tag does not move for a prerelease (`docker-publish.yml:83`). ## Verification - Derived gates (`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `a4cbcfdb16`): 50 commands, all exit 0, exit codes written to disk before any pipe. `--ran`: `50 derived, 50 run, 0 NOT-MEASURED, 0 UNRUN`. The battery `pnpm check:pm-dispatch-gates` passed 1976 cases in 997.8 s. - `node scripts/sync-release-index-currency.mjs --self-test`: exit 0, 42 cases. - **Ablation, committed first.** With `ablation-replace.mjs`, the pre-fix logic (`const rewritten = …; return rewritten === field ? null : rewritten;`) was put back in place of the fix. The self-test exits 1 with exactly 5 failures: battery B's inverted case and 4 of Control H's 7. H's three controls stay green, as they should. The file was restored with blob equal to HEAD and `git diff HEAD` empty. A first, cruder ablation that only deleted the guard line also reddened B and C. That mutation was too strong, so the faithful one above is the reading. - Not run locally: the whole spec suite (the step no longer needs it), and the repo-wide lint farm, which belongs to CI. ## Acceptance notes (noted, not fixed here) - The comment above `Create or update the "chore: version packages" PR` in `release.yml` still says `pnpm run version` is FOUR rewriters. It is five (`sync-release-index-currency.mjs` joined). - `cut-rc.yml` says "On an RC cut this rewriter writes NOTHING". That was false for the date before this PR (an rc cut on a later day re-dated the entry) and is true after it. `cut-rc.yml` is not edited, by the card. - `release.yml`'s step comment said the lockstep test was "reachable only through the whole @objectstack/spec suite". That is corrected in place, because it is the step this card owns. Changeset: none. The diff touches `.github/workflows/release.yml` and `scripts/sync-release-index-currency.mjs`, and neither is in any package's `files[]`. Commits carry this repository's model-free trailer pair (AGENTS.md). --- _Generated by [Claude Code](https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 9, 2026
…ollow the newest GA, not the prerelease (objectstack-ai#22134) Fixes objectstack-ai#22131 Clause-②: no Refs objectstack-ai#22085 (the parent; this PR carries its Q3 half). Ruling record: comment 6049734955 on objectstack-ai#22085, quoted as the dispatch carried it: > **Q3 → B. During the `next` line, the documented image and install versions follow the newest GA, not the prerelease.** In pre mode `sync-docs-image-tags` and `check-docs-image-tag` pin the newest GA (read from the CHANGELOG, the same way the GA is already judged); outside pre mode nothing changes. ⛔ Not taken: A (self-hosting and upgrade documents reading `18.0.0-next.N` while npm `latest` and the published image stay 17.7.0, so a production user copying the documents is led into the breaking prerelease line). ## What changed Two files, both under `scripts/`, nothing published: - `scripts/check-docs-image-tag.mjs` gains `expectedVersion(root)`, the ONE function that answers which version the doc surfaces must pin. - No `.changeset/pre.json`: `packages/cli/package.json`'s version (`VERSION_SOURCE`, kept with its name), as before. - `.changeset/pre.json` in mode `pre` or `exit`: the newest GA in `packages/spec/CHANGELOG.md`, overall (not of the CLI's major). - It reads through the existing readers only: `SPEC_CHANGELOG`, `gaVersions` and `newestGaOfMajor` from `check-release-section-coverage.mjs` (unchanged), and `readPre` from `check-changeset-no-major.mjs` (unchanged). No second GA reader and no third `pre.json` reader. - Every pre-mode state it cannot read throws instead of falling back: a `pre.json` that is present but does not parse, a mode Changesets never writes, no spec CHANGELOG, or a CHANGELOG with no GA heading. - `main()` judges against it. The STALE detail, the scope line and the red footer name the source used. A pre-mode red also prints why the expectation is not `packages/cli`'s prerelease. Outside pre mode the gate's output is byte-identical to `main`'s. - `scripts/sync-docs-image-tags.mjs` gains `syncRepo({ root })`, which `main()` runs: the target comes from the gate's `expectedVersion()`, followed by the existing `syncSurfaces()`. The rewriter and the gate therefore read one value. A pre-mode run logs why the docs did not follow `packages/cli`. Not touched: `content/docs/**`, `release.yml`, `cut-rc.yml`, `lint.yml`, the root `version` script, `.changeset/**`, objectstack-ai#21988. ## Premises measured on `origin/main` `8fc50b7647` 1. The root `version` script (`package.json:20`) is `changeset version && sync-protocol-version && sync-template-versions && sync-docs-image-tags && sync-release-index-currency`. Holds. 2. `VERSION_SOURCE`, `SURFACES` and `PROSE_CLAIMS` are as the card states, and the rewriter imports them from the gate. Holds. 3. `check-release-section-coverage.mjs` exports `SPEC_CHANGELOG`, `gaVersions` (ascending, GA-only, end-anchored) and `newestGaOfMajor`. Holds. - On a pre-mode tree the answer has to be the newest GA overall. After the first `next` cut the CHANGELOG's top heading is `## 18.0.0-next.0` and there is no GA of 18, so `newestGaOfMajor(versions, 18)` is `null`. - `@objectstack/spec` and `@objectstack/cli` are in one `fixed` group in `.changeset/config.json`, so the spec CHANGELOG's newest GA is also the CLI's. 4. Pre mode is `.changeset/pre.json`. **Refined:** Changesets writes it in two modes, and both are the prerelease line. - `changeset pre exit` only rewrites the mode to `"exit"`, so the tree stays on `18.0.0-next.N`. - The next `changeset version` then computes the GA and deletes `pre.json`. Measured in `@changesets/apply-release-plan` 8.1.1, which removes the file when the mode is `exit`; replayed below. - That `exit` commit reaches `main` before the Version Packages PR does. Keying on `"pre"` alone would expect `18.0.0-next.N` in that window while the docs read the GA, which reds this required gate on every PR in between, the exiting PR included. - So `exit` counts as pre mode here. The self-tests pin this case. 5. Other consumers: no change in meaning except the rc lane below. - `release.yml:569` and `cut-rc.yml:605` import `SURFACES` only. Re-run after the change, the import prints the same 3 paths and exits 0; the two new imports are side-effect-free, and `check:entry-guard` is green. - `lint.yml:2649` and `lint.yml:2672` run the two gates. Outside pre mode the output is identical. - **Correction to the dispatch's reading of `cut-rc.yml`:** it does not refuse pre mode. It refuses unless `pre.json` is mode `pre` **with tag `rc`** (`cut-rc.yml:232-236`). So it never runs on the `next` line, but it does run in pre mode on an rc line. - Under this change, an rc cut's version pass leaves the doc surfaces at the newest GA instead of stamping `X.Y.Z-rc.N`. That follows the ruling's text ("in pre mode") and its reason, a production reader led into a prerelease, which applies to an rc as well. - Its allowlist permits the doc surfaces to change and does not require it (`git add -A -- … "${DOCS_SURFACES[@]}"`), so a cut that leaves them unchanged passes. The self-test pins the rc case: CLI `17.8.0-rc.0`, expected `17.7.0`. ## The pin, replayed Each run used a throwaway `git worktree add --detach` under the session scratch directory, then `pnpm install --frozen-lockfile --offline --ignore-scripts` and `pnpm run version`. All trees were removed afterwards. PR objectstack-ai#22084's two files were copied from its head `a630de657d`: `.changeset/pre.json` (`{"mode": "pre", "tag": "next"}`) and `.changeset/22080-v18-line-opens.md`. **Tree 1: this branch (`a4b1b5106d`) plus PR objectstack-ai#22084's two `.changeset` files.** `pnpm run version` exit 0. `packages/cli` went from `17.7.0` to `18.0.0-next.0`, and the spec CHANGELOG's top heading is now `## 18.0.0-next.0`, above `## 17.7.0`. ```text ✓ sync-docs-image-tags: all 9 concrete pin(s) across 3 surface(s) already at the newest GA in packages/spec/CHANGELOG.md (pre mode) 17.7.0 — nothing rewritten. Pre mode: .changeset/pre.json is in mode "pre" (tag "next"), so the documented versions follow the newest GA, 17.7.0, and not packages/cli/package.json's 18.0.0-next.0. ... $ git diff -- docker/README.md content/docs/deployment/self-hosting.mdx content/docs/upgrading.mdx | wc -l 0 pins on the 3 surfaces: 7 x ghcr.io/objectstack-ai/objectstack:17.7.0, 1 x OS_CLI_VERSION=17.7.0, 1 x @objectstack/cli@17.7.0 $ pnpm check:docs-image-tag # exit 0 check-docs-image-tag: OK (3/3 enumerated surface(s) read, 9 concrete pin(s) compared against the newest GA in packages/spec/CHANGELOG.md (pre mode) 17.7.0, ...) ``` **Tree 1, continued: the exit window and the GA pass.** These are the states premise 4 is about. ```text $ pnpm exec changeset pre exit # .changeset/pre.json → {"mode": "exit", "tag": "next"}; packages/cli still 18.0.0-next.0 $ pnpm check:docs-image-tag # exit 0, compared against the newest GA in packages/spec/CHANGELOG.md (pre mode) 17.7.0 $ pnpm run version # exit 0; .changeset/pre.json deleted; packages/cli 18.0.0 ✓ sync-docs-image-tags: 9 pin(s) across 3 surface(s) → 18.0.0 (lockstep with packages/cli/package.json). $ pnpm check:docs-image-tag # exit 0, compared against packages/cli/package.json 18.0.0 ``` **Tree 2: this branch on plain `main`, without `pre.json`.** `pnpm run version` exit 0. `packages/cli` went from `17.7.0` to `17.8.0` (the 58 pending changesets), and the surfaces move to the CLI's version, as they do today. ```text ✓ sync-docs-image-tags: 9 pin(s) across 3 surface(s) → 17.8.0 (lockstep with packages/cli/package.json). pins on the 3 surfaces: 7 x ghcr.io/objectstack-ai/objectstack:17.8.0, 1 x OS_CLI_VERSION=17.8.0, 1 x @objectstack/cli@17.8.0 $ pnpm check:docs-image-tag # exit 0, compared against packages/cli/package.json 17.8.0 ``` **Control tree: `main` at `8fc50b7647`, without this change, plus PR objectstack-ai#22084's two files.** This is the state the ruling rejects (⛔ A): ```text ✓ sync-docs-image-tags: 9 pin(s) across 3 surface(s) → 18.0.0-next.0 (lockstep with packages/cli/package.json). pins on the 3 surfaces: 7 x ghcr.io/objectstack-ai/objectstack:18.0.0-next.0, 1 x OS_CLI_VERSION=18.0.0-next.0, 1 x @objectstack/cli@18.0.0-next.0 ``` ## Self-tests and the ablation New batteries, each case with its positive control: - In the gate, `Pre mode: the expectation is the newest GA (objectstack-ai#22131)` has 18 cases. The roster floor goes from 11 to 12, and the run has 112 assertions. - In the rewriter, `Control I: in pre mode the target is the newest GA, from the gate's one function` has 10 cases and runs `syncRepo()`. The roster floor goes from 8 to 9, and the run has 45 assertions. The three Done-when cases, and what each is controlled against: | Case | Expected result | Positive control | |---|---|---| | Pre mode, CLI `18.0.0-next.0`, newest GA `17.7.0` | expects `17.7.0` | The same tree's CLI reads `18.0.0-next.0`, and the CHANGELOG has no GA of 18 | | The same tree without `pre.json` | expects the CLI's `18.0.0-next.0` | Case 1 answers differently, and `pre.json` is the only difference between the two trees | | Pre mode, docs already at the GA | gate green, rewriter writes nothing (byte-identical, mtime unchanged) | The same docs against the CLI's version are 3 STALE in the gate and 3 rewrites in the rewriter | Further cases cover mode `exit` and an rc pre mode, which both expect the GA. Pins moved onto the prerelease in pre mode are STALE, and the finding names the pre-mode source. Each of the four unreadable states is refused, and the rewriter refuses before any write. **Ablation, at `a4b1b5106d`.** The change was committed first. `node scripts/ablation-replace.mjs` rewrote the pre-mode test in `expectedVersion()` (`if (!existsSync(join(root, PRE_STATE))) {` became `if (true) {`), which is the old logic: `pre.json` ignored. - The mutation landed: the anchor count went 1 → 0, the marker count during the mutation was 1, and the blob went `e27045098c80` → `97307ba4e54a`. - `node scripts/check-docs-image-tag.mjs --self-test` exited 1 with **13 failures**, every one a new pre-mode case. - `node scripts/sync-docs-image-tags.mjs --self-test` exited 1 with **6 failures**, every one a new pre-mode case. - The controls that do not depend on the branch stayed green. - The restore was proven by blob hash: after the restore the blob is `e27045098c80`, equal to `HEAD:scripts/check-docs-image-tag.mjs`, and `git diff HEAD` is empty. ## Gates, at `a4b1b5106d` `node scripts/pm/dispatch-gates.mjs --commands` was derived from this worktree over the actual diff (2 paths, +548/-39) and gave 31 commands, the same 31 the dispatch named. All 31 were run and every one exited 0. - `pnpm check:pm-dispatch-gates` passed 1976 cases (856s). - `pnpm check:docs-image-tag` (112 assertions, then OK on the live tree) and `pnpm check:docs-image-tag-sync` (45 assertions) passed. - `check:entry-guard`, `check:nul-bytes`, `check:scripts-symbol-anchors` and `check:declaration-mirrors` passed. - `dispatch-gates --ran`: 31 derived, 31 run, 0 NOT-MEASURED, 0 UNRUN. Lint was a proven narrowing to the changed files, not the repo-wide `pnpm lint`, which CI runs: - Population, read from eslint's own config: `isPathIgnored` is `false` for both files. - Count, from `--format json`: 2 results, 0 errors and 0 warnings, with `--no-inline-config`. - Invariance: this repo's eslint config never enables type-aware linting (no `parserOptions.project`), so this diff cannot change the verdict on any untouched file. No changeset is needed. The root package is private and no package's `files[]` ships `scripts/`. Labelled `skip-changeset`. ## Acceptance notes None of these blocks this PR, and none is a defect. Each is wording that this change makes slightly inaccurate, in a file outside this PR's declared surface. Carrier for each: none. - `scripts/check-changeset-no-major.mjs`'s entry-guard comment says "Nothing imports this file today". `check-docs-image-tag.mjs` now imports `readPre`. The guard already makes the import inert, as `check:entry-guard` confirms. - `release.yml`'s post-version comment ("check:docs-image-tag — the 3 doc surfaces against packages/cli's NEW version") and `lint.yml`'s step name "Docs image tags track packages/cli's version" are true outside pre mode only. In pre mode the expectation is the newest GA. - `cut-rc.yml`'s staging comment (around line 497) lists "the 3 doc surfaces" among a cut's paths. An rc cut now leaves them unchanged (premise 5). - dispatch-gates now routes `.changeset/pre.json` to `check:docs-image-tag`, because the gate spells it. It does not route `packages/spec/CHANGELOG.md`, which is read only through an import from a gate module. That file is release-owned and only the version pass writes it. --- _Generated by [Claude Code](https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 9, 2026
…arker, so v18 opens at 18.0.0-next.0 (objectstack-ai#22084) Fixes objectstack-ai#22080 Clause-②: no Opens the v18 line on `main`, as ruled B on objectstack-ai#22050 (record `6037890422`). Changesets enters pre mode with the tag `next`, and one `major` changeset takes the `fixed` group to 18, so the first version cut is `18.0.0-next.0`. File surface: `.changeset/` plus one line in `scripts/check-adr-0087-registration.mjs` (the seat's surface amendment `6040954045` on objectstack-ai#22080; see the patch round below). ⛔ No edit to `.changeset/config.json`, to either changeset guard's logic, or to `.github/workflows/release.yml`. ⛔ No `changeset version` output is committed, and nothing was published. ## The change 1. **`.changeset/pre.json`**, written by `pnpm changeset pre enter next` (`@changesets/cli` 3.0.3) and committed as the tool wrote it (commit `d3d35a1029`): ```json { "mode": "pre", "tag": "next" } ``` Under v3 the file holds `mode` and `tag` only. There is no `initialVersions` and no `changesets` list. 2. **`.changeset/22080-v18-line-opens.md`**, front matter `"@objectstack/spec": major` (commit `52f7a509c6`). Its body is the v18 line's opening note (ADR-0131; objectstack-ai#22050). It carries `Clause-②: no` and its ADR-0087 disposition (see Gates). - **Step 2 was not skipped.** The card's count command, re-run on the cut `3d9188502e`, prints `17 : minor` and `32 : patch`, and no `major`. No `major` was pending, so without this marker pre mode would version `17.8.0-next.0`. - **Why `@objectstack/spec`.** Any `fixed`-group member moves all 69 to the same version, so the choice decides only where the note lands and what the major means. `@objectstack/spec` is the protocol package. `scripts/sync-protocol-version.mjs`, which the root `version` script runs, derives `PROTOCOL_VERSION` from this package's major, and ADR-0131 C8 calls the line "protocol 18". So the major sits on the package whose major is the protocol version, and the note lands where an upgrading reader looks first: `packages/spec/CHANGELOG.md`, under `### Major Changes`. ## The no-major guard reads `pre.json` from the checkout it runs in (the head), so steps 1 and 2 ship together - `scripts/check-changeset-no-major.mjs:396` sets `REPO_ROOT` to the checkout the script runs in. `readPre(root)` (`:2018`) reads `join(root, '.changeset', 'pre.json')` from that working tree, `:2098` hands the result to `judge`, and `judge` returns `exempt` when `pre?.mode === 'pre'` (`:662`). `--base` sets only where the diff starts. It is never where `pre.json` is read. - In CI, the `Check Changeset` job in `.github/workflows/pr-automation.yml` checks out the PR merge ref (the default for a `pull_request` event) and runs `node scripts/check-changeset-no-major.mjs --base "$MERGE_BASE"` (`:1130`). The merge ref carries this PR's `pre.json`. - Both legs were measured at `52f7a509c6`: - **As committed:** exit 0, `✓ Changesets is in pre-release mode (tag: next) — ... skipping the no-major guard.`, plus a notice naming `@objectstack/spec`. - **Control:** with `.changeset/pre.json` deleted from the working tree only, exit 1, `⛔ This PR introduces changeset(s) that declare a major bump.` The base `3d9188502e` has no `pre.json` at all, so a guard that read the base could never have answered `exempt`. Restored with `git checkout HEAD -- .changeset/pre.json`: `git diff HEAD` is empty, and the guard exits 0 again. - So no split is needed, and this PR carries steps 1 and 2. ## Pins | Pin | Reading | |---|---| | In a throwaway worktree, never committed, `changeset version` puts the `fixed` group at `18.0.0-next.0` | Detached throwaway worktree at `52f7a509c6`: `pnpm changeset version` exits 0. All 69 `fixed`-group members read `18.0.0-next.0`, and `pre.json` is unchanged. The 37 consumed changesets (36 pending plus this one) moved to `.changeset/pre/`. `packages/spec/CHANGELOG.md` opens `## 18.0.0-next.0` / `### Major Changes` with this note. The worktree was then removed. | | `check-changeset-no-major` and `check-changeset-fixed` pass on the PR | `node scripts/check-changeset-no-major.mjs --base origin/main` exits 0 on the pre-release exemption above. Driven with a `pull_request` payload carrying this body (`--event`), it also exits 0 with the level axis read. `node scripts/check-changeset-fixed.mjs` exits 0: `✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.` | | `.changeset/pre.json` is present at the merge, with `"mode": "pre"` and `"tag": "next"` | Added in `d3d35a1029` with the content above. No later commit touches it. It is in the merge ref CI checks out, which is the file the guard's exemption reads. | ## `release.yml`: in pre mode, `changeset publish` publishes under `next`, and `latest` stays 17.7.0 This is a reading of the file. Nothing was edited, and no publish was run. - `.github/workflows/release.yml:1762` is job `publish`, step `Publish to npm + push version tags`. At `:1772` it runs `pnpm run release`. - The root `package.json` `release` script is `pnpm run build && bash scripts/build-console.sh && bash scripts/release-publish.sh`. - `scripts/release-publish.sh:64` runs `changeset publish` with no `--tag`. - In `@changesets/cli` 3.0.3, `dist/getPublishPlan.mjs:599-603` `getReleaseTag` works like this: with no `--tag` and a pre state present, the tag is `preState.tag`, which is `next`. The one exception is a package whose every published version is already a `next` prerelease. Separately, `dist/publish.mjs:61-63` refuses a custom `--tag` in pre mode. - `npm view` read today: all 69 `fixed`-group packages have `latest` = `17.7.0` and no `next` tag. So each publishes to `next`, and `latest` stays `17.7.0`. ## Gates (run on `52f7a509c6`, exit codes captured before any pipe) `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 20 commands for this diff. All 20 exit 0, and `--ran` reconciles: `20 derived, 20 run, 0 NOT-MEASURED, 0 UNRUN`. **Patch round, head `a630de657d` (seat-appended from addendum `6041313497`).** At `52f7a509c6` the required `Lint & Repo Gates` was red at `PM dispatch-gates self-test`, case `the extension narrowing costs no lead … (refused: .changeset/pre)`: with `pre.json` tracked, the battery's loose and strict hint rules disagreed on the literal `.changeset/pre/` at `scripts/check-adr-0087-registration.mjs:536`. That line now names the existing exclusion as `CONSUMED_PRERELEASE_EXCLUDED` (the predicate's match is unchanged), which the extractor reads as an excluded surface. At `a630de657d`: 41 derived commands, 41 run, all exit 0; `pnpm check:pm-dispatch-gates` → `1976 cases pass` (965.7 s), the case reads `(refused: none)`; `check-adr-0087-registration --self-test` 441 assertions and `--base origin/main` exit 0. - **Changeset gates:** - `check-adr-0087-registration --base origin/main` exits 0. The gate requires a disposition for a `major`: a probe commit without one was refused, exit 1, with `declares a breaking change (major) but no adr-0087: disposition marker`. The probe was never pushed. The changeset now carries the `not-required (no-migration-prescription)` marker in the ADR-0087 HTML-comment form, because the marker moves no authorable key, export, type or stored shape. - `check-changeset-no-major --base origin/main` and `check-empty-changeset --base origin/main` exit 0. - The self-tests of all three, plus `check:changeset-gate-self-tests`, exit 0. - **Remaining derived commands, all exit 0:** `check-closing-keyword-parity` and its self-test, `check-comment-mask-corpus`, `release-rehearsal-clone --self-test`, `release-pending-publish --self-test`, `check:driver-memory-census`, `check:gitlink-declared`, `check:nul-bytes`, `check:objectui-changeset`, `check:pm-changeset-deadline-census`, `check:published-files`, `check:refd-timer-probe` and `check:watch-hint-literal`. - **Run beyond the derivation, all exit 0:** `check-changeset-fixed` (an artifact roster under `.changeset/`), `check:future-spec-major`, `check:lockstep-package-count` and `check:docs-image-tag`. - **NOT MEASURED:** the four type-check lanes. The diff touches no TypeScript. ## Read before merging: after this lands, the version-PR refresh goes red until its major-boundary gates are wired The card expects changesets/action to refresh objectstack-ai#21988 into the `18.0.0-next.0` version PR once this lands. On the files as they stand, it will not. - The `version-pr` job in `release.yml` (cron `0 */6 * * *`) runs `pnpm run version` in the step `Render the post-version tree` (`:450`). The step `Validate the post-version tree` (`:518`) then exits 1 at `:610-613` when the pass wrote any major-boundary path: "this version pass crossed a MAJOR boundary ... Wire them in before letting this refresh through." The step `Create or update the "chore: version packages" PR` has no `if:`, so it does not run after that failure. - I measured this in the same throwaway. After `changeset version`, the other three rewriters of the root `version` script exit 0. Replaying the step's shape assertion gives 0 unexpected paths and 4 major-only paths: `packages/spec/src/kernel/protocol-version.ts` (`PROTOCOL_VERSION 17.0.0` to `18.0.0`) and the blank template's `objectstack.config.ts`, `objectstack.manifest.json` and `package.json`. - The other lane, `cut-rc.yml`, refuses a pre tag other than `rc` (`:232-237`) and a version not shaped `X.Y.Z-rc.N` (`:156-159`). The ruled tag is `next`. - What this means: once this lands, no lane in this repository cuts `18.0.0-next.0` without a workflow change. That change is outside this card, which forbids any `release.yml` edit. The red is the lane's own designed refusal. It publishes nothing and queues nothing. objectstack-ai#21988 stays at its current 17.8.0 content and, as the card says, ⛔ is not merged. ## Acceptance notes - The same throwaway `pnpm run version` shows two more things that the lane wiring has to account for. - `scripts/sync-release-index-currency.mjs` re-dates the v17 entry in `content/docs/releases/index.mdx` from `17.7.0, released 2026-10-06` to `released 2026-10-07`. That is the prerelease cut's date. 17.7.0's version commit `4e4e881427` is dated 2026-10-06. Reported to the seat as a finding. - `scripts/sync-docs-image-tags.mjs` moves the 9 pins in `docker/README.md`, `content/docs/deployment/self-hosting.mdx` and `content/docs/upgrading.mdx` from `17.7.0` to `18.0.0-next.0`. The self-hosting docs on `main` would then name a prerelease while `latest` is 17.7.0. Noted only. - The opening note says the dist-tag `latest` stays on 17.x until GA. The pre-mode publish path above is what makes that true. ## 维护者速读(草稿) **改了什么**:在 `main` 上进入 Changesets 预发布模式(标签 `next`),并加一条把 `@objectstack/spec` 标为 `major` 的 changeset。合并后,下一次切版本得到的是 `18.0.0-next.0`,而不是 `17.8.0`。改动是 `.changeset/` 下的两个文件,加上门禁脚本里的一行重命名:`scripts/check-adr-0087-registration.mjs` 把已有的排除路径 `.changeset/pre/` 写成 `_EXCLUDED` 命名常量(匹配逻辑不变),这样 `pre.json` 存在后 PM 门禁自检能正确识别它。没有代码或工作流改动。 **为什么改**:按 objectstack-ai#22050 的裁决 B,v18 直接在 `main` 上开发。v18 的破坏性改动要以 `18.0.0-next.N` 预发布版发到 npm 的 `next` 标签上;`latest` 保持 17.7.0,普通安装的用户不受影响,直到 18.0 正式版。 **风险与代价(含回滚)**:合并后,版本 PR 的自动刷新(`release.yml` 的 `version-pr`,每 6 小时一次)会在 "Validate the post-version tree" 一步变红。原因是这一版跨了大版本,会改写 `PROTOCOL_VERSION` 和模板文件,而这条通道对这些文件还没有门禁,按设计会拒绝。它不发布,也不排队任何东西,但在另开卡把这些门禁接上之前,objectstack-ai#21988 不会变成 `18.0.0-next.0`。`cut-rc.yml` 只接受 `rc` 标签,也走不通。objectstack-ai#21988 无论如何都不要合并。回滚办法:删除 `.changeset/pre.json` 和 `.changeset/22080-v18-line-opens.md` 两个文件(在切出任何预发布版之前,这一步没有副作用)。 **席位意见**: **你要做的**:确认后合并本 PR。合并后需要另开一张卡,让版本通道能切出 `18.0.0-next.0`;这张卡由席位提出。 --- _Generated by [Claude Code](https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 9, 2026
…project's own lockfile (objectstack-ai#22217) Fixes objectstack-ai#22150 Clause-②: no ## What was wrong Where pnpm is on PATH, `create-objectstack` installs with pnpm, so the project gets `pnpm-lock.yaml` and no `package-lock.json`. The `Dockerfile` it writes copied `package*.json` and ran `npm ci`, so `docker build` stopped at `RUN npm ci` with `EUSAGE` before `os build` ran. `content/docs/deployment/self-hosting.mdx` showed the same stage. ## What changed - `packages/create-objectstack/src/templates/blank/Dockerfile`, build stage only: - `COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml* package-lock.json* ./` copies whichever of these exist. - `RUN` installs from the lockfile that is present: `pnpm-lock.yaml` gets `corepack pnpm@10 install --frozen-lockfile`, `package-lock.json` gets `npm ci`. With neither, the build stops with "No lockfile: run pnpm install or npm install, then commit it." - A four-line comment says the same in the file. - `FROM node:22-slim AS build`, `COPY . .`, `RUN npx os build` and the whole runtime stage are byte-identical to `main`. - `content/docs/deployment/self-hosting.mdx`: the Dockerfile block's build stage now shows the same lines. This is the cross-lane `domain:devx` edit declared on the claim. The block's image-tag line and the page's other tag lines (the ones the Version Packages PR objectstack-ai#21988 edits) are untouched. - New pin: `packages/create-objectstack/src/dockerfile-build-stage.test.ts` (below). - `.changeset/22150-scaffold-dockerfile-lockfile.md`: `create-objectstack` patch. - Not touched: `index.ts`, `runtime-image.ts`, `detect-package-manager.ts`, `packages/cli`, `packages/spec`. ## Route: the stage follows the lockfile; the scaffolder does not rewrite it (differs from the PM's H2 lean) The PM leaned toward emitting the build stage after install, from the detected package manager. Before writing that, I measured two reasons against it: 1. **The CI docker leg scaffolds one way and installs another.** `scaffold-e2e.yml` `scaffold-local` scaffolds with `--skip-install` on a runner whose `pnpm` is a Corepack shim (`setup-pnpm` runs `corepack enable`). It then runs `npm install` and `docker build`s the scaffolded Dockerfile. A stage emitted from the detected manager would be pnpm-shaped there and `COPY` a `pnpm-lock.yaml` that does not exist. The alternative is to emit only after a successful install. Then every `--skip-install` pnpm user keeps the `npm ci` stage, and the scaffolder already tells that user to run `pnpm install`. 2. **The detected manager is not stable there.** Outside the repo, `pnpm --version` resolves through Corepack's LastKnownGood or the registry's latest. Today that is pnpm 12, which the probe below shows Corepack 0.34 cannot run, so the same runner can detect either manager. A stage that reads the lockfile at build time is right on every path: a pnpm install, an npm install, `--skip-install` followed by either, and a later switch of package manager. It needs no scaffolder code, and the docs page can show the one file. The ruling's outcome is unchanged: pnpm gets Corepack plus `pnpm install --frozen-lockfile`, npm gets `npm ci`, and the docs show the same file. ## Readings | | Reading | |---|---| | H1 reproduce | Confirmed. I scaffolded with this branch's base scaffolder, pnpm on PATH, `--skip-skills`: the project has `pnpm-lock.yaml` and no `package-lock.json`. The old stage, run from a clean copy, copies only `package.json`. `npm ci` exits 1 with `EUSAGE`. | | H2 emission point | Not taken. The reasons are in the section above. | | H3 what pnpm must copy | `package.json` and `pnpm-lock.yaml` are enough for `--frozen-lockfile` to pass on pnpm 10.34.6. Without `pnpm-workspace.yaml` the install still exits 0 but prints `Ignored build scripts: better-sqlite3@13.0.3, esbuild@0.28.2`, because the template's build approvals live in that file. So the stage copies it. The template has no `packageManager` field, so Corepack has nothing to read; see the version pin below. | | H3 per manager | `detect-package-manager.ts` answers `pnpm` or `npm`. A project with `pnpm-lock.yaml` gets the pnpm branch, one with `package-lock.json` gets `npm ci`. No manager was added. If both lockfiles exist, `pnpm-lock.yaml` wins, which is the one the template's `ci.yml` installs from. With no lockfile the build fails loudly; it does not run an unpinned install. | | H4 unchanged | The runtime stage and its pin code are byte-identical to `main`. `runtime-image.test.ts` and `template-consistency.test.ts` pass unchanged inside the full package run. `RUN npx os build` is byte-identical, and it built the artifact in both legs below. | | H5 docs | The block now shows the template's build stage. A new pin holds the two equal (below). | **Why pnpm is pinned to a major.** These readings use Node 22.22.0 and its bundled Corepack 0.34.0, each run with a fresh `COREPACK_HOME`: - Unpinned (`corepack enable pnpm` then `pnpm install --frozen-lockfile`): Corepack resolves the registry's latest, pnpm 12.10.1, and dies with `MODULE_NOT_FOUND` on `pnpm/12.10.1/bin/pnpm.cjs`. pnpm 12 ships `bin/pnpm.mjs` plus a native wrapper. - `COREPACK_DEFAULT_TO_LATEST=0`: Corepack falls back to its bundled pnpm 10.13.1, which is below the template's `engines.pnpm >=10.15`. - `corepack pnpm@10`: resolves 10.34.6 and works. `corepack pnpm@11.28.2` also runs. The stage pins major 10, the same major the template's `.github/workflows/ci.yml` passes to `pnpm/action-setup`, and a pin keeps the two equal. ## Measured: the stage run from clean copies (no Docker daemon here) Each run takes the COPY and RUN text from the committed Dockerfile itself. It COPYs with BuildKit glob semantics into an empty directory (no `node_modules`), then runs the RUN line under `/bin/sh`, which is dash here and in `node:22-slim`. Each run gets a fresh HOME, a fresh `COREPACK_HOME` and a fresh package store, against the real registry. After the install it runs `COPY . .` (honouring the template's `.dockerignore`) and `RUN npx os build`. | Project (scaffolded for real) | Old stage | New stage | |---|---|---| | pnpm-installed (pnpm 10.28.0 wrote the lockfile) | copies `package.json` only; `npm ci` exit 1 `EUSAGE` | copies `package.json pnpm-lock.yaml pnpm-workspace.yaml`; pnpm 10.34.6 "Lockfile is up to date"; install exit 0; `os build` exit 0, `dist/objectstack.json` written | | npm-installed (scaffolded with pnpm absent from PATH) | install exit 0 | copies `package-lock.json package.json pnpm-workspace.yaml`; "added 524 packages"; install exit 0; `os build` exit 0 | | no lockfile | n/a | exit 1, "No lockfile: run pnpm install or npm install, then commit it." | ## The pin, and its ablation `dockerfile-build-stage.test.ts` reads the stage from scaffolded output (`copyDir`) and emulates the COPY into an empty directory. It then runs the RUN line under `/bin/sh` for three legs: - a pnpm-installed project, - an npm-installed project, - a project with no lockfile. To stay hermetic: - The fixture swaps the registry dependencies for one local-directory dependency. With no dependencies at all, `pnpm install --frozen-lockfile` answers "Already up to date" even with no lockfile. With one dependency, a missing lockfile gives `ERR_PNPM_NO_LOCKFILE` / `EUSAGE` and a drifted one gives `ERR_PNPM_OUTDATED_LOCKFILE` / `EUSAGE`, offline. - `corepack` is a stub that runs the repository's own pinned pnpm (resolved from inside the package), and it refuses when that pnpm is outside the major the stage asks for. pnpm itself is not on the stage's PATH. - Every install runs with `npm_config_offline=true`. Two more cases in the file: the docs block's build stage equals the template's, and the Dockerfile's pnpm major equals `ci.yml`'s. Ablation, run once inside one lock hold on head `cc944d6b`, via `scripts/ablation-replace.mjs`. It put the old `COPY package*.json ./` and `RUN npm ci` lines back: anchor 1 to 0, blob `ce699d28904a` to `04316706900a`, on-disk counts of the new line 0 and the old line 1. - Green before: `Tests 5 passed (5)`. - Mutated: `Tests 3 failed | 2 passed (5)`. - Red: the pnpm leg (`npm error code EUSAGE`, the card's failure), the docs-equality pin and the pnpm-major pin. - Green: the npm leg and the no-lockfile leg. - Restored: blob back to `ce699d28904a`, which equals HEAD, and `git diff HEAD` is empty. An earlier ablation also reddened the npm leg. The only cause was that the leg pinned `pnpm-workspace.yaml` in the npm copy set. Commit `cc944d6b` narrowed that assertion to what `npm ci` needs, and the run above is on that commit. ## Verification Everything below ran on head `cc944d6b`, the final commit, with a clean worktree. - **Package.** The dependency closure was built first: `pnpm --filter 'create-objectstack^...' build` (`@objectstack/spec`), exit 0. - `pnpm --filter create-objectstack typecheck`: exit 0. `tsc --listFiles` includes all 17 `src/*.test.ts`, the new one among them. - `pnpm --filter create-objectstack test`: exit 0, `Test Files 17 passed (17)`, `Tests 254 passed (254)`. The package has no integration tier. - **Builds for gates that read built packages.** `turbo run build --filter='!@objectstack/docs' --concurrency=2`: 72/72 tasks successful. - **Gate union.** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derives 93 commands for this diff, the same 93 the dispatch order lists; the order adds `pnpm lint`. - All 93, plus `pnpm lint`, exited 0. - `--ran` over that record: "93 derived, 93 run, 0 NOT-MEASURED, 0 UNRUN". - Five gates had first answered exit 3 (`PREREQUISITE NOT MET`) before the builds above. Their exit-0 runs here are real measurements. - **Verdict lines:** - `check:nul-bytes`: "OK (scanned 10161 text file(s) … no raw ASCII control bytes)". - `check:cross-package-test-inputs`: "OK: 30 package(s) read outside themselves, all declared". The new test reads `content/docs` through the existing `$TURBO_ROOT$/content/**` input of `create-objectstack#test`. - `check:docs-image-tag`: "OK (3/3 enumerated surface(s) read, 9 concrete pin(s) compared …)". - `check-empty-changeset`: "1 declaring changeset(s) added". - **`pnpm lint`** (`eslint . --no-inline-config`): exit 0, no findings, about 108 s on a shared box. - **Narrowed lint, also run.** eslint lints 1 of the 4 changed paths, the new `.ts` test. The `.mdx`, the changeset and the `Dockerfile` each answer "File ignored because no matching configuration was supplied". Result: 0 errors, 0 warnings. - `eslint.config.mjs` never enables type-aware linting, so this change cannot move a verdict on an untouched file. - **Left to CI:** the path-scheduled `scaffold-e2e.yml` docker build, which exercises the `npm ci` branch. ## Acceptance notes - CI's only docker build of a scaffold (`scaffold-e2e.yml` `scaffold-local`) installs with `npm install`, so it exercises the `npm ci` branch alone. The pnpm branch is proven by the measurement above and the hermetic pin, not by a CI `docker build`. This is a coverage note, not filed; `scaffold-e2e.yml` is outside this card's file surface. - The Corepack readings are from Node 22.22.0 / Corepack 0.34.0 in this container. `node:22-slim` tracks the newest 22.x, so a later Corepack may run pnpm 12; the major pin keeps the image on the same pnpm line as CI either way. - A project scaffolded before this release keeps its own Dockerfile. The changeset says how to update one. --- _Generated by [Claude Code](https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 9, 2026
…l, organization, ownership, and every guest key's fate (objectstack-ai#22239) Part of objectstack-ai#22146 Clause-②: no ## What this PR is Round 3 of objectstack-ai#22146: one new decision record, `docs/adr/0138-guest-model-anonymous-principal-doors-grants-and-organization.md`, **Status: Proposed**. It transcribes the maintainer's ruling [`6054113537`](objectstack-ai#22146 (comment)) (batch objectstack-ai#290 item 1, 「22146 同意」: A on all eight questions and G2 on the gap, with the Q4 and Q2 clarifications) into nine decisions. Each one states its contract and its enforcement point. The revision round applies the ruling supplement [`6056614963`](objectstack-ai#22146 (comment)) (maintainer 「D1 A′ D2b R」): D1 is revised to A′, and D2b is closed as R. - **No code changes.** Nothing changes in `packages/**`, `content/docs/**` or `skills/**`. The ruling places every code change after acceptance, as execution cards E1 to E4 (the record's *Execution plan*). - **Tier H.** The diff touches `docs/adr/**`, so this PR stays draft and lands only by the maintainer's hand. - **The card stays open** for the execution cards, so line 1 is `Part of`. ## What the record says, one line per decision - **D1, identity and ownership (A′).** The guest is a principal and never owns a record; a forged owner is refused; the audit names the guest as the actor. Who owns a guest-written row is the business scenario's own metadata: the door's declaration, the object's hooks, record-change flows and assignment rules. The platform stamps nothing and declares no default owner. Empty state: the owner stays unset, as the form doors do today, with the existing authoring advisory and no publish refusal. The enforcer is the guest branch of the owner-anchor stamp in `SecurityPlugin` (card E3). - **D2, the closed list of doors.** Exactly five door classes serve an unauthenticated request: - the public form doors; - share links; - the public book and doc reads; - `authRequired: false` endpoints of type `object_operation`; - `authRequired: false` endpoints of type `flow`. Everything else answers 401, decided once per domain by `shouldDenyAnonymous`. The control-plane allowlist, the signed inbound-hook channel and MCP are credentialed or infrastructure, so they are outside the guest model. - **D2b, anonymous door × elevated flow (R).** Publish refuses an `authRequired: false` flow endpoint whose target declares `runAs: 'system'`, in both directions, with a prescription: an authenticated endpoint, the signed inbound-hook channel, a public form, and `runAs: 'automation'` once ADR-0073 M2 lands. No door triggers an elevated flow directly. Card E2 implements it with a registered ADR-0087 semantic entry. Record-change flows fired by a guest-written row stay recorded and undecided. - **D3, the grants channel (ADR-0090 D9, enforced).** The `guest` anchor's bindings resolve for the guest, read through the one binding reader every position uses. An empty set denies all. There is no second channel: not the baseline, not `everyone`, and not the position-name fold. The binding tier is unchanged. The row scope runs on one pipeline, and E1 pins it per sharing model. - **D4, organization.** The guest's organization is resolved only when the deployment has a unique organization, using the same predicate as ADR-0131 D9. On a multi-organization deployment the request is refused until D5 exists. The question is asked only where the organization is needed. - **Clarification (1), carried into the record:** the form doors' declared default-organization binding stays as it is. Nothing that serves today starts refusing. - **D5, site binding.** The record gives the shape only: match, organization, guest grants and allowed doors. ⛔ It declares no metadata type and reserves no key. The binding is built when a named deployment needs it. - **D6 and D7.** ADR-0106 D7, explain's `EXTERNAL` floor and ADR-0121 D6 are unchanged. The webhook signature vocabulary goes to a follow-up card, F1. That card is named in the record and not filed. - **D8, guest keys.** Every declared guest key gets a fate and an ADR-0087 disposition. - `sys_record_share`'s `guest` recipient is to be **removed** on its own card, E4. The basis is ADR-0090 D11 and the already-registered `sharing-rule-recipient-reconcile` entry. - The form doors' `guest_portal` set name was not on the card's list. It is recorded too, with the fate keep. - **D9.** The record now holds the maintainer's ruling of 2026-08-08 (Option A, `f586f1a89`), which until now lived only in the module doc of `assemble-execution-context.ts`. ## What the revision changed (supplement [`6056614963`](objectstack-ai#22146 (comment))) - **D1 → A′.** Points 3, 5 and 6 of the draft (the organization-level default owner, its cardinality, the empty-state refusal) are replaced: ownership is the scenario's own metadata, and the empty state is the owner left unset. Points 1, 2 and 4 are kept. - **D5.** The default-owner element is removed from the shape. - **D2b → R**, with its enforcement text. The four-axis table stays as the reasoning. M and the first-drafted default owner move to *Alternatives considered*. - **Execution plan.** E2 carries R and its registered ADR-0087 semantic entry. E3 shrinks to the stamp's guest branch (never the guest, never the system principal, a forged owner refused, the owner unset unless the scenario sets it): no new key and no disposition. - **Elsewhere.** The Consequences paragraph on owner-assigning hooks is withdrawn. *What the ruling did not settle* loses D2b and D1's empty state. Acceptance criterion 3 (D2b chosen) is met. The supplement is added to *Decided by*. - **Consistency edits the ruled changes forced:** the Status line, the Consumers line (the spec change is now D2b's refusal, not a D1 key), D4 point 3 (no owner stamp left to need the organization), D2's class 5 row, follow-up F3 (M2 now only extends R's prescription), and the References. ## What stays open - **The indirect path:** record-change flows fired by a guest-written row are recorded and not decided. - **The spelling of D5's binding** belongs to the card that builds it. - **The guest's row scope** is stated as a contract; E1 pins its measured outcome. ## How the number was chosen: 0138 - `origin/main` at `73a0a6bf1d`, after this round's merge, tops out at 0137, and 0136 is absent. Still no open PR adds 0136 or 0138; objectstack-ai#22198, the one ADR PR at the first count, has landed on ADR-0048. - **I checked every open PR's file list.** That is all 20 open PRs, paged to the end for objectstack-ai#22142 (104 files) and objectstack-ai#21988 (229 files). Only objectstack-ai#22198 touches `docs/adr/`, and it touches `0048-cross-package-metadata-collision.md`. No open PR adds 0136 or 0138. - **0136 is not reused.** It was handed to a decision once: unmerged PR objectstack-ai#18480 added `0136-declared-journeys-as-priority-anchor.md`, and objectstack-ai#18985 renumbered its own record from 0136 to 0137 because of it. `scripts/check-adr-anchors.mjs` computes the next free number as the highest number plus one, which gives 0138. ## Back-pointers: none in this PR, by house practice - **Where the lines go.** The house form for an amended record is a status-line continuation. ADR-0042, ADR-0046 and ADR-0131 carry lines of the form "· **Amended** (date, ADR-NNNN Dk) — …". These lines are written when the amendment is in force. No record in the registry carries such a line pointing at a Proposed record. So the exact lines for **ADR-0090** (D9) and **ADR-0056** (D2) are written into the record's *Acceptance criteria*, to land with the accepting change. - **Which ADRs get no line.** ADR-0106 D7, ADR-0121 D6 and ADR-0096 D5/E1 are left unchanged by this record, so they get none. **ADR-0135** gets none either: it mirrors cloud ADR-0024 and adds no clause of its own, and this record amends none of its decisions. ## Verification at `2d69b2b714` (the revision, on a merge of main `73a0a6bf1d`) I ran every command in the claim-time gate list at the revision head. Each exit code was captured before any pipe. | Command | Exit | |:--|:--| | `node scripts/check-adr-links.mjs` (and `--self-test`) | 0, 0 | | `node scripts/check-adr-symbol-anchors.mjs` (and `--self-test`) | 0, 0 | | `node scripts/check-ci-filter-parity.mjs` | 0 | | `node scripts/check-closing-keyword-parity.mjs` (and `--self-test`) | 0, 0 | | `node scripts/check-comment-mask-corpus.mjs` | 0 | | `pnpm --filter @objectstack/lint run check:doc-formula-expressions` | 0 (see note) | | `pnpm check:adr-anchors` | 0 | | `pnpm check:cross-package-test-inputs` | 0 | | `pnpm check:doc-authoring` | 0 | | `pnpm check:driver-memory-census` | 0 | | `pnpm check:gitlink-declared` | 0 | | `pnpm check:nul-bytes` | 0 | | `pnpm check:pm-governed-merges` | 0 | | `pnpm check:pm-prior-rulings` | 0 | | `pnpm check:refd-timer-probe` | 0 | | `pnpm check:watch-hint-literal` | 0 | - **`check-adr-symbol-anchors`.** It reports "2225 anchors across 141 records resolve". Positive control: the record count is 141, which is the 140 at base plus this record. No anchor carries a line number. - **`check:pm-prior-rulings`.** The self-test passes 155 cases. The tool's `--card 22146` read returns 16 ADR decision hits and 1 ruling on the thread (`6054113537`). - **`check:doc-formula-expressions`.** In the first round its first run exited 3 (PREREQUISITE NOT MET: the closure was not built), which measured nothing. I rebuilt the `@objectstack/formula` and `@objectstack/lint` closure under the verify lock after this round's merge (VERDICT command-exit 0), and the gate exited 0. - **Re-derivation.** `dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `2d69b2b714` gives the same 19 families. The `--ran` reconciliation shows 19 run and 0 NOT-MEASURED, a zero derived from the recorded exit codes. The change set is one file, +640/−0, against merge base `73a0a6bf1`. ## Not measured, and named - **The G2 sweep (the booted per-door-class anonymous sweep).** I did not run it, by the ruling: it is an acceptance precondition, to be run in an environment that permits the probe. This round wrote no driver and no probe. - **The platform documentation links.** I could not re-fetch them, because the container's proxy answers 403 to CONNECT for those hosts. The record carries the URLs that the measurement round recorded. - **The cloud repository's anonymous surfaces.** This round did not read them. ## Changeset I read the Check Changeset job in `pr-automation.yml`. It has two exemptions: the `skip-changeset` label (read live, twice) and the Changesets release PR. Its failure text prescribes: "if it releases nothing …, apply the 'skip-changeset' label". This PR adds no `.changeset/*.md` and changes none, and `docs/adr/**` ships in no package's `files[]`. So the label is `skip-changeset`. ## Acceptance notes - **A doc comment that describes the old form-door semantics.** The comment above `RestServer.registerFormEndpoints` in `packages/rest/src/rest-server.ts` still says that security is delegated to a `guest_portal` set carried on the context, and that the middleware falls open when none is registered. What admits a form submission today is the `publicFormGrant` branch in `SecurityPlugin`. - This is comment drift only, and no reach was measured. It is noted, not filed. - Carrier: none. Card E2, once cut, edits that domain. ## 维护者速读(草稿) - **改了什么:** 按你「D1 A′ D2b R」的补充裁决修订 ADR-0138 草稿,其余内容不动。 - D1:访客永不拥有记录、伪造的 owner 一律拒绝、操作留痕记在访客名下,这三条保留。"本组织声明一个默认 owner"整条删掉:访客写进来的记录归谁,由各业务场景在元数据里自己定(入口声明、对象钩子、记录触发流程、分配规则),平台不打任何默认值。没人设置时 owner 就空着,跟今天公开表单一样;表单的作者视图照常提示,发布不拒绝。 - D2b:匿名入口不能触发以系统身份运行的流程。发布时直接拒绝,端点和流程两头都检查,并给出替代办法(带凭据的端点、签名 webhook 通道、公开表单,以及 M2 落地后的 automation)。 - 站点绑定的形状里去掉"默认 owner";执行卡 E3 缩成只校验访客分支,不新增任何键。 - **为什么改:** 你指出归属是业务场景的事,平台级默认值在多数部署里是错的,还会抢在对象自己的分配逻辑前面。D2b 选 R 之后,AI 照着 `runAs` 的说明写出"匿名入口 + 系统身份流程"时,发布就会被拦下。 - **风险与代价(含回滚):** - 本 PR 仍只改一个文档文件,合并后运行时行为不变;回滚就是删掉这个文件。 - D2b 的拒绝会让"匿名端点指向系统身份流程"这种写法从此发布不了。仓内没有这样的声明;仓外的部署本轮没有测。 - 接受前提还剩一项:在允许探测的环境里做一次启动后的逐类匿名入口实测(G2)。 - **席位意见:** - **你要做的:** 修订版就绪后,批准这份 ADR(Tier H,点 Approve 或亲手合并)。 --- _Generated by [Claude Code](https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 9, 2026
…D_TOO_LARGE (objectstack-ai#22359) Fixes objectstack-ai#22314 Clause-②: yes (widening: a new owner provenance row in the published error-code ledger) The upload size refusal that PR objectstack-ai#22311 added (card objectstack-ai#22283) now answers `413` with the registered code `PAYLOAD_TOO_LARGE` at all four `service-storage` upload doors, in place of the status-derived `VALIDATION_ERROR`. The error-code ledger lists `PAYLOAD_TOO_LARGE` under `@objectstack/service-storage`, beside `@objectstack/rest`. This is ruling B from the `domain:services` seat in PR objectstack-ai#22311's ACCEPT, carried on the card. It is not a new code. The status (`413`), the message, the four doors, and what is stored or refused are all unchanged. ## What changed - **`packages/spec/src/api/error-code-ledger.zod.ts`:** one provenance row, `'PAYLOAD_TOO_LARGE'`, under the `@objectstack/service-storage` owner key. - It follows the shape the ledger's other multi-emitter rows use (`PACKAGE_DELETE_PARTIAL` and `WRITABLE_PACKAGE_REQUIRED` under `@objectstack/runtime`): a comment naming the wire path and the other emitter, then "Provenance, not identity". - `ErrorCode`, `RegisteredErrorCode` and `REGISTERED_ERROR_CODES` are unchanged, because the union dedupes. - Every sentence of the docblock at `standardSynonymOf` stays true, including "a phrase for a status the catalog does not name (`PAYLOAD_TOO_LARGE`, 413) is NOT a synonym". - **`packages/services/service-storage/src/storage-routes.ts`:** the one constant changes from `UPLOAD_TOO_LARGE_CODE: StandardErrorCode = 'VALIDATION_ERROR'` to `UPLOAD_TOO_LARGE_CODE: RegisteredErrorCode = 'PAYLOAD_TOO_LARGE'`. Its docblock is rewritten to state the new choice and the reason for it. - **Tests:** they now expect `PAYLOAD_TOO_LARGE` and still assert both `code` and `status` at every door. - `storage-limits.test.ts`: the shared `expectTooLarge` helper asserts `413` + `PAYLOAD_TOO_LARGE`, and every door case calls it: - presigned; - chunked start; - local raw PUT, by bytes read and by `content-length`; - chunk PUT, by bytes read (three cases) and by `content-length`. - `error-envelope.conformance.test.ts`: the case for the over-limit upload. - `packages/qa/dogfood/test/storage-upload-limits.dogfood.test.ts`: three assertions over a real boot. - **Text that describes this refusal:** - the `service-storage` README limits row; - `content/docs/api/error-catalog.mdx`: the `VALIDATION_ERROR` 413 callout moves to a new `PAYLOAD_TOO_LARGE` entry, the quick-reference 413 row changes, and the wire-code count goes from 51 to 52; - `content/docs/protocol/kernel/error-handling.mdx`: the 413 row and the `VALIDATION_ERROR` entry. - **Changesets:** - `.changeset/22314-storage-payload-too-large-provenance.md` is new: `@objectstack/spec` `minor`, carrying the Clause-② line above. - The pending `.changeset/22283-storage-limits-honoured.md` gets a one-sentence correction (see the next section). ## Pending release note corrected: confirmation requested (`Check Changeset` is red by design) `.changeset/22283-storage-limits-honoured.md` is objectstack-ai#22283's pending, unreleased note (`@objectstack/service-storage` `minor`). This PR changes one sentence in it: - **Before:** "is refused with `413` and the standard code `VALIDATION_ERROR`, in the usual error envelope," - **After:** "is refused with `413` and the registered code `PAYLOAD_TOO_LARGE`, in the usual error envelope," Nothing else in that note changes. With this edit, the release that first ships the refusal names one code. `check-empty-changeset` refuses a changed foreign changeset by design. This PR is the DELIBERATE CORRECTION class: the gate's own remedy is to say so on the PR and have it confirmed. Do not restore the old sentence, which would put the false code back. Do not apply `skip-changeset`. Please confirm the correction here. The `service-storage` code change rides on that note. This PR's own changeset declares `@objectstack/spec` only. No `check-changeset*` gate asked for `@objectstack/service-storage` in an added changeset: - `check-changeset-no-major` exits 0. - `check-adr-0087-registration` exits 0. - `Require a changeset` counts the added `22314` changeset. ## Release coupling, checked before building - npm `@objectstack/service-storage`: - `latest` is `17.7.0`, published 2026-10-06T12:09Z. - Its tarball has 0 dist files matching `limitsSnapshot` or `UPLOAD_TOO_LARGE_CODE`. The control, `upload/presigned`, matches in 4 dist files. - PR objectstack-ai#22311 merged on 2026-10-08T16:35Z, and its changeset is still pending on `main`. - The Version Packages PR objectstack-ai#21988 is open. So no published version ships the `VALIDATION_ERROR` refusal. ## Measurements (the dispatch's hypotheses) - **H1 holds.** - `PAYLOAD_TOO_LARGE` was listed under `@objectstack/rest` only, which answers it for both import routes' row ceilings (`import-prepare.ts`, 5,000 and 50,000 rows). - `HTTP_REASON_PHRASE_STATUS` maps it to 413. - The new row is one entry in the existing multi-emitter shape. - **H2 holds, with the typed path that already exists.** - `sendError` (`@objectstack/types`) takes `ErrorCode` = `StandardErrorCode | RegisteredErrorCode`. - `RegisteredErrorCode` is the type `service-analytics` already uses for ledger-code constants (`read-scope-refusal.ts`, `dataset-refusal.ts`). - No shared type was widened, and no cast was added. - Reverse check: a misspelled code fails to compile (see Tests). - **H3: every test that pins this refusal now expects `PAYLOAD_TOO_LARGE`.** Sweep hits outside the tests, with line numbers at the base `41d0d4038`: - **Edited** (each describes this refusal): the service-storage README `:113`, `error-catalog.mdx` `:67` and `:920`, `error-handling.mdx` `:217`, `:405` and `:421`, the dogfood test `:68`, `:91` and `:102`, and the pending changeset `:15`. - **Not edited** (none describes this refusal): - `storage-adapter-list.conformance.test.ts` `:436` and `:447` (`VALIDATION_ERROR` for the list `limit`/`cursor`); - `import-mappings.mdx` `:224` and `import-job-integration.test.ts` `:202` (rest's import 413, already `PAYLOAD_TOO_LARGE`); - `references/api/contract.mdx` and `references/api/error-code-ledger.mdx` (generated union listings, which this change does not move); - `error-code-ledger.test.ts` `:87` (the synonym pin, still true). - The sibling `objectui` checkout has no branch on this refusal's code. - **H4 holds.** `check:error-code-provenance` is the gate that makes the row required: - **Before**, at the base `41d0d4038`: exit 0, with 334 stamp sites (315 listed, 19 waived). - **Ablation**, at commit `b5f9d332f`: the storage constant switched and the ledger row removed. The removal went through `scripts/ablation-replace.mjs`, which showed the anchor count go from 1 to 0 and the blob change, then restored the file to equal `HEAD`. Result: exit 1, `@objectstack/service-storage stamps 'PAYLOAD_TOO_LARGE' (constdef) at packages/services/service-storage/src/storage-routes.ts:93 — not listed under its own owner key`. - **After**, at `5b5b812ce`: exit 0, with 335 stamp sites (316 listed, 19 waived). - **Generated artifacts:** none moves. After `pnpm --filter @objectstack/spec build`, `check:generated` reports all 15 generated artifacts up to date, and `git status` is clean. - **Doc ⇄ runtime status reconciliation:** `check:error-status-conformance` now reconciles `PAYLOAD_TOO_LARGE`. Its scope line reads "4 ledger code(s) a doc page publishes a status for (INVALID_REQUEST, PAYLOAD_TOO_LARGE, UNIQUE_VIOLATION, VALIDATION_FAILED)", and the derived producer is `413 storage-routes.ts:315`. `VALIDATION_ERROR` keeps no 413 producer and no 413 doc claim. Exit 0. ## Tests At `5b5b812ce`, each run under the shared verify lock: - `pnpm --filter @objectstack/service-storage exec vitest run --maxWorkers=2`: 45 files and 749 tests passed. - The targeted spec run (`src/api/` plus every spec test that reads the ledger) with `vitest run --project local --maxWorkers=2`: 59 files and 1724 tests passed. - `pnpm --filter @objectstack/service-storage run typecheck`: exit 0. - `pnpm --filter @objectstack/spec run typecheck`: exit 0. At `b5f9d332f`. The only change from there to `5b5b812ce` is comment and changeset wording in three files (5 lines changed): - `pnpm --filter @objectstack/spec test`: 626 files passed, with 18740 tests passed and 1 todo. - The dogfood file over a real boot, `storage-upload-limits.dogfood.test.ts`: 1 file and 2 tests passed. The `service-storage` dist it loaded had 1 `PAYLOAD_TOO_LARGE` match. - Reverse type check: the constant was set to `'PAYLOAD_TOO_LARGE_ABLATED'` through `ablation-replace.mjs`, and `tsc --noEmit` failed with `TS2820 ... Did you mean '"PAYLOAD_TOO_LARGE"'?`. The file was then restored to equal `HEAD`, with an empty `git diff HEAD`. ## Gates At `5b5b812ce`: - `dispatch-gates --commands` derived 115 commands. All 115 ran: - 114 exited 0. - `node scripts/check-empty-changeset.mjs --base origin/main` exited 1, by design (the DELIBERATE CORRECTION above). - The `--ran` verdict: `✓ dispatch-gates --ran: 115 derived famil(ies) accounted for — 115 run, 0 NOT-MEASURED`. - Also run: `check:generated`, plus the roster gates `check:authz-resolver` and `check:filter-alias-parity`. All exited 0. ESLint was narrowed to the touched files, with this proof: 1. **What eslint checks:** `eslint.config.mjs` lints `**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}`. The touched TypeScript files are the five listed in the next point. The `.md` and `.mdx` files match no `files` glob. 2. **What ran:** `pnpm exec eslint --no-inline-config --format json` over those 5 files at `5b5b812ce` reported 5 files, 0 errors and 0 warnings. 3. **Why other files are unaffected:** type-aware linting is not enabled (no `parserOptions.project` and no typed rules; the config says so at `:327`). The config's only file reads are two baselines this diff does not touch. So this diff cannot change the lint result of any untouched file. ## Acceptance notes - **Landing beyond the claim's file surface:** two hand-written docs pages, the `service-storage` README, and the dogfood test. The H3 sweep named each one as describing this refusal, and each is edited only where it does. - The catalog's new `PAYLOAD_TOO_LARGE` entry also names `@objectstack/rest`'s import row ceilings. The entry catalogs the code, not one door. - Documenting a status for the code makes it a reconciled wire code. That is what requires the heading and the count of 52, by `error-catalog-docs.test.ts`. - `content/docs/references/**` is unchanged and regenerates byte-identically. --- _Generated by [Claude Code](https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 9, 2026
github-merge-queue Bot
pushed a commit
that referenced
this pull request
Oct 10, 2026
…t scaffold with the workspace protocol (nightly tiers) (#22699) Fixes #22622 Clause-②: no Two nightly-tier CLI test files change and nothing else: no accepted input, key, export or error code moves, and nothing publishes. Two independent reds on the nightly tiers, both in `domain:cli`. **Both are fixed inside the nightly tests**, following the seat's review 6099406666 on #22622. Nothing else changes: - the family-roster check; - the noise budget and every assertion in that file; - the `create-objectstack` template, the version-time stamp (`scripts/sync-template-versions.mjs`) and their tests, which are byte-for-byte as `main` has them. ## (a) Both reds, reproduced before any fix Base `9646991283`, `OS_TEST_TIERS=nightly`, `--project integration`, through the verify lock: `Tests 2 failed | 51 passed (53)`. - `json-stdout-purity.e2e.test.ts`, `is exactly the set listed here`: `expected [ 'meta resync', …(17) ] to deeply equal [ 'meta resync', …(15) ]`, with `+ "migrate organization-ownership"` and `+ "migrate security-catalog-overlays"`. `migrate organization-ownership` came from PR #22643, after the nightly that filed the card. The members were taken from `discoverFamily()`'s output. - `serve-boot-diagnostics-noise-budget.e2e.test.ts`, `highlights the dead button once…`: `expected 0 to be greater than 0` at `:176`. The boot printed `✗ package 'com.example.support-desk' targets protocol ^17 (engines.protocol) but this runtime is protocol 18.0.0. This is a major-version break. Run: objectstack migrate meta --from 17`. That is a refusal, so no *Boot diagnostics* block is printed at all. After, on `3d63ed7db5`: `Test Files 2 passed (2)`, `Tests 59 passed (59)`. That is 53, plus 3 cases for each of the two new members. ## Failure 1: the two new `--json` members are driven `FAMILY` gains both members, each in its bare form. The CLI was first run against the purity fixture to see each one's face: - `migrate organization-ownership` shows its refusal face. Over a database with none of the platform tables it prints `{"error":"plan_refused","reason":"not-an-objectstack-database",…}` and exits 1. The JSON is emitted after `stack.shutdown()`, and no plan file is written. - `migrate security-catalog-overlays` shows its read-only preview: `listed: 0`, exit 0. There is no `sys_metadata` hydration and nothing is deleted. Both print all three boot diagnostics on stderr. The discovery and the `toEqual` roster check are untouched. ## Failure 2: the noise-budget e2e aligns the pair it boots ### (b) What the version-time stamp writes Measured read-only at `9646991283`: - `changeset status --output` (`pre.json` is `{"mode":"pre","tag":"next"}`) gives `@objectstack/spec major 17.7.0 -> 18.0.0-next.0` and `create-objectstack major 17.7.0 -> 18.0.0-next.0` (the `fixed` group). - `loadScaffolderVersion()` on `18.0.0-next.0` gives `{"version":"18.0.0-next.0","major":"18","range":"^18.0.0"}`. So the version pass stamps `engines: { protocol: '^18' }` into the released template. **Triage's "every project that v18's `create-objectstack` scaffolds would boot with the protocol-gap warning" is false.** No released v18 scaffold carries the gap. PR #22215's changeset says the same ("the template keeps `'^17'` until the version pass stamps it"). The red is `main`'s pre-mode window: packages at `17.7.0`, `PROTOCOL_VERSION` at `18.0.0`. Only an in-repo pairing sees it: a published-line scaffold booted on the workspace runtime with nothing installed. ### Round 1 changed the product side, and CI falsified that Round 1 (`bff640d98`) stamped the template's `engines.protocol` from `PROTOCOL_VERSION`'s major. On that head, `Scaffold with repo dist` (`scaffold-e2e.yml`, job 1) went red: "package 'com.example.e2e-app' targets protocol ^18 (engines.protocol) but this runtime is protocol 17.0.0". That gate guards the path a user takes. It scaffolds with the repo-built scaffolder and installs `@objectstack/*` from the registry (`^17.0.0`, so protocol 17). On that path a template's range must match the protocol of the packages it installs, which is the package major. The sync script and the `template-consistency` ratchet already enforce exactly that on `main`. Commit `60fa01c7a7` removes round 1's product-side change: - `templates/blank/objectstack.config.ts`, `template-consistency.test.ts`, `template-version-stamps.test.ts` and `scripts/sync-template-versions.mjs` are back at their `main` blobs (`6308f29e`, `d32c819c`, `48ba146e`, `f7704db8`, all equal at base and `origin/main`); - the `create-objectstack` changeset is deleted. ### This round: the setup aligns the pair it boots This follows the seat's verification-strategy ruling in 6099406666. After scaffolding, and before the fixture files are written, `alignProtocolRange()` rewrites the scaffold's `engines.protocol` to `'^' + PROTOCOL_MAJOR`. `PROTOCOL_MAJOR` is read from `@objectstack/spec/kernel`, the workspace spec this file boots, the same import other CLI e2e files use. - **Only the major's digits are rewritten.** When the majors agree, the scaffold is left byte-for-byte as written, and the helper does not write the file at all. - Measured on the template: at `'^17'` (the window) the hash moves `684e38ed0d1a` → `5d6e6468255e`, and only the `engines` line differs. - At `'^18'` (majors agree) the hash stays `5d6e6468255e` → `5d6e6468255e`, byte-identical. - **A missing stamp throws in `beforeAll`.** A template that stops declaring the key fails this file loudly instead of skipping the alignment. - **Why a direct rewrite of the one key, and not `objectstack migrate meta --from 17 --write`.** The `--write` command is the step the refusal prescribes, and it was measured first. On a scaffold under `packages/cli/node_modules/` it exited 0 with `not written [outside-project]` and left the config unchanged. The codemod refuses any file whose real path has a `node_modules` segment (`authored-source-codemod.ts`, the `outside-project` refusal). This project has to live under this package's `node_modules` so that its imports resolve to workspace copies. - **What stays the same:** the noise budget, all three assertions, and the ticket object and actions. Once the version pass lands, the window closes and the alignment becomes a no-op. ## Ablations Both ran through `scripts/ablation-replace.mjs` in wrap mode on committed trees, under the verify lock. Each restore was proven by blob equal to HEAD and an empty `git diff HEAD`. Neither subject resolves through `dist/`: the FAMILY and the setup live in the test files. | Mutation | Anchor | Result | |---|---|---| | `alignProtocolRange(join(dir, 'objectstack.config.ts'));` deleted from the setup | x1 → x0, blob `058fbecec7dc` → `bfedb0522aa5` | noise-budget red with the original refusal: `✗ package 'com.example.support-desk' targets protocol ^17 … this runtime is protocol 18.0.0 …`, `expected 0 to be greater than 0`, 1 failed and 2 passed. After the restore to blob `058fbecec7dc`: 3 passed. | | `'migrate security-catalog-overlays': [],` deleted from `FAMILY` (round 1, same blob as now) | x1 → x0, blob `698034ba9bcb` → `5dfe8780897a` | roster pin red: `expected [ 'meta resync', …(17) ] to deeply equal [ 'meta resync', …(16) ]`, `+ "migrate security-catalog-overlays"` | ## Tests (on `3d63ed7db5`) - **Nightly tier** (`OS_TEST_TIERS=nightly`, `--project integration`, verify lock): the two card files, 59 passed. - **`create-objectstack`:** suite 17 files and 254 tests passed. `pnpm check:template-version-sync` is green with 40 assertions, the same count as on `main`. Both are unchanged, because this diff does not touch the package or the script. - **`@objectstack/cli`:** - unit layer: 279 files and 4126 tests passed; - `typecheck` (`tsc --noEmit` plus `check:test-typecheck`) is OK, and `tsconfig.test.json`'s `--listFiles` includes both edited files. - **Changeset:** `node scripts/check-empty-changeset.mjs --base origin/main` exits 0. It reports no empty-frontmatter changeset added and no merge-base changeset modified or deleted. - **Gates:** `dispatch-gates --commands` (no paths) derived 50 commands from the 2-file change set. `--ran` reports `50 derived, 50 run, 0 NOT-MEASURED, 0 UNRUN`, a derived zero: every command recorded `exit 0`. `check:type-check-debt` and `check:dual-build-cjs-loads` ran last, after every locked suite. - `dispatch-gates` noted that the tree is behind `origin/main`: 6 derived-from files changed there (`ci.yml`, `check-shard-attestation.mjs` and the fleet-write relay scripts). - No conflict exists, so per the round's direction there was no merge. CI judges the merge ref. - **Lint, proven narrowing:** 1. Population is read from ESLint's own config: both changed files return results with no "File ignored". 2. File count is from `--format json`: 2 results, 0 errors, 0 warnings. 3. Invariance: `eslint.config.mjs` enables no type-aware linting, and this diff touches neither the config nor a baseline it reads. ## Changeset Nothing publishes: `@objectstack/cli` ships `dist`, `README.md` and `CHANGELOG.md`, and this diff is two `test/` files. By the repo's rule this PR takes the `skip-changeset` label. An empty-frontmatter changeset would be the risky path that `check:empty-changeset` refuses. The seat applies the label. ## Acceptance notes - `objectstack migrate meta --write` on a project whose path has a `node_modules` segment says `the literal is in …/objectstack.config.ts, outside the project at …`, although the file is inside the project. The real cause is the `node_modules` segment rule. Only a test layout reaches it, so this is a wording polish, not a finding. No carrier. - Version Packages PR #21988 was last refreshed on 2026-10-08T06:19Z, before pre mode was entered (`a87d8be29`). This is release-lane state, recorded as a reading, ⛔ not acted on. --- _Generated by [Claude Code](https://claude.ai/code/session_01BmsuLyUeuG5CNpZFMH1jzS)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This branch has not been deployed
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.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@objectstack/hono@17.8.0
@objectstack/cli@17.8.0
@objectstack/console@17.8.0
@objectstack/core@17.8.0
@objectstack/lint@17.8.0
@objectstack/metadata-protocol@17.8.0
@objectstack/platform-objects@17.8.0
@objectstack/plugin-auth@17.8.0
@objectstack/plugin-email@17.8.0
@objectstack/plugin-hono-server@17.8.0
@objectstack/plugin-security@17.8.0
@objectstack/plugin-sharing@17.8.0
@objectstack/rest@17.8.0
@objectstack/runtime@17.8.0
@objectstack/service-analytics@17.8.0
@objectstack/service-automation@17.8.0
@objectstack/service-messaging@17.8.0
@objectstack/service-realtime@17.8.0
@objectstack/service-storage@17.8.0
@objectstack/spec@17.8.0
@objectstack/types@17.8.0
@objectstack/verify@17.8.0
@objectstack/account@17.8.0
@objectstack/setup@17.8.0
@objectstack/studio@17.8.0
@objectstack/client@17.8.0
@objectstack/client-react@17.8.0
@objectstack/cloud-connection@17.8.0
@objectstack/connector-mcp@17.8.0
@objectstack/connector-openapi@17.8.0
@objectstack/connector-rest@17.8.0
@objectstack/connector-slack@17.8.0
@objectstack/driver-memory@17.8.0
@objectstack/driver-mongodb@17.8.0
@objectstack/driver-sql@17.8.0
@objectstack/driver-sqlite-wasm@17.8.0
@objectstack/driver-turso@17.8.0
@objectstack/formula@17.8.0
@objectstack/mcp@17.8.0
@objectstack/metadata@17.8.0
@objectstack/metadata-core@17.8.0
@objectstack/metadata-fs@17.8.0
@objectstack/objectql@17.8.0
@objectstack/observability@17.8.0
@objectstack/embedder-openai@17.8.0
@objectstack/knowledge-memory@17.8.0
@objectstack/knowledge-ragflow@17.8.0
@objectstack/organizations@17.8.0
@objectstack/plugin-approvals@17.8.0
@objectstack/plugin-audit@17.8.0
@objectstack/plugin-dev@17.8.0
@objectstack/plugin-pinyin-search@17.8.0
@objectstack/plugin-webhooks@17.8.0
@objectstack/service-cache@17.8.0
@objectstack/service-cluster@17.8.0
@objectstack/service-cluster-redis@17.8.0
@objectstack/service-datasource@17.8.0
@objectstack/service-i18n@17.8.0
@objectstack/service-job@17.8.0
@objectstack/service-knowledge@17.8.0
@objectstack/service-package@17.8.0
@objectstack/service-queue@17.8.0
@objectstack/service-settings@17.8.0
@objectstack/service-sms@17.8.0
@objectstack/trigger-api@17.8.0
@objectstack/trigger-record-change@17.8.0
@objectstack/trigger-schedule@17.8.0
create-objectstack@17.8.0
@objectstack/sdui-parser@17.8.0
@objectstack/example-crm@4.0.100
@objectstack/example-multi-package@0.0.7
@objectstack/example-showcase@0.3.22
@objectstack/example-todo@4.0.100
@objectstack/example-embed-objectql@0.0.40
@objectstack/dogfood@0.0.48
@objectstack/downstream-contract@0.0.46
@objectstack/http-conformance@0.1.8