Repository navigation
fix(cli): Console-dist warning names the remedy for where it runs; README states the Console step and command costs - #22224
Conversation
…ADME states the Console step and each command's cost The "Console dist not found" boot warning named @object-ui/console and an objectui-workspace filter in every context. It now asks the nearest package.json above the resolved console package: one that declares `objectui:build` (the framework repo) gets `pnpm objectui:build` and the directory to run it in; anywhere else gets the @objectstack/console install. README "Hack on the framework" adds `pnpm objectui:build` as the Console step, the measured cost of install/build/objectui:build/test, and the `pnpm turbo run test --affected` path for a contributor's own change. Claude-Session: https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw Co-authored-by: Claude <noreply@anthropic.com>
…e one new export in the ./console partition pin The ./console subpath pin partitions every export of utils/console.ts into public and not-public lists, so the new internal export is recorded there (the two "exactly three names" checks keep it off the published subpath). Claude-Session: https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 28 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3788587880472349aea61099ba9fdcaa5de40422 && git checkout 3788587880472349aea61099ba9fdcaa5de40422
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3b493184e1e395549d80723e267d2ff80ce33da4 0ef75bd7d96b5d91b96e669f0617094947a1cfb0 && git checkout -B drift-repro 3b493184e1e395549d80723e267d2ff80ce33da4 && git merge --no-ff 0ef75bd7d96b5d91b96e669f0617094947a1cfb0
node scripts/docs-audit/affected-docs.mjs --json 3b493184e1e395549d80723e267d2ff80ce33da4
|
…real showcase run (objectstack-ai#22241) Fixes objectstack-ai#22232 Clause-②: no Part A of the card: the README now opens on a thirty-second GIF recorded from a really running app, rendered by a committed script. Part B (repository topics and the About line) is the maintainer's click on the repository settings page and is not in this PR. ## What changed | File | Change | |:---|:---| | `docs/screenshots/readme-demo.gif` | new: 1200 x 675, 43 frames, 29.88 s, loops forever, **497,614 bytes** | | `docs/screenshots/render-readme-demo.mjs` | new: the generator (boots the app, makes the calls, screenshots the Console, renders the frames, assembles the GIF) | | `README.md` | top only: the GIF replaces the embedded cover image under the chip row, a one-line caption names the three beats, the "Watch: ObjectStack in 90 Seconds" link stays as text under it. `docs/screenshots/hero-cover-dark.png` is byte-untouched (the docs site's og:image). The architecture diagram and everything below it are unchanged. | No `packages/**`, `apps/docs`, `content/**`, `CHANGELOG.md` or `package.json` change; nothing publishes. ## The recording Example and object: `examples/app-showcase`, object `showcase_team` (three fields, so the whole typed definition fits one frame without scrolling). 1. **One definition, and the app runs.** The Team object exactly as `examples/app-showcase/src/data/objects/team.object.ts` declares it (comment lines omitted; the frame says so), then `pnpm dev:showcase -- --fresh -p PORT` booting it on a throwaway database, then the Console's Team list (2 rows). 2. **An agent creates a record over MCP.** A JSON-RPC `tools/call` of `create_record` against the running app's `/api/v1/mcp`, sent with the dev admin's API key; the real response; then the Console list with the new "Data Guild" row (3 rows) and its record page reading "Created by Dev Admin". 3. **A read-only identity is refused.** The byte-identical call sent with the Auditor persona's API key; the real refusal; then the Console list, unchanged (3 rows). Closing frame: "The ontology is the software." and the four chips Executable · AI-writable · Agent-operable · You own it. Measured in the run that rendered the committed GIF (port 39497): `showcase_team` went 2 → 3 on the admin's call and stayed 3 on the refused call (read back over REST after each call). - Admin's call: `200`, record `QZu-Fcgx29Po-Sfn` "Data Guild", `created_by` = the admin's user id. - Read-only call, the real response bytes: ```json {"result":{"content":[{"type":"text","text":"You do not have permission to perform this action. Contact your administrator if you need access."}],"isError":true},"jsonrpc":"2.0","id":1} ``` ### Keys and identities - Both keys are minted through the documented `POST /api/v1/keys` (content/docs/ai/connect-mcp.mdx, "Headless: API keys"), each from a session opened with `POST /api/v1/auth/sign-in/email`. - Writer: the dev admin that `--fresh` seeds (`admin@objectos.ai`). - Read-only identity: the showcase's own Auditor persona, `auditor.demo@example.com` ("Ada Auditor (demo)"), which `examples/app-showcase/src/security/seed-approval-demo.ts` provisions on every dev boot. It holds only the `auditor` position, bound to `showcase_auditor` (no Team grant), plus the member baseline `showcase_member_default`, which grants `showcase_team` read and no create. No code was changed to get a refusal. Before the GIF calls it "read-only", the script proves it can read: its `query_records` on `showcase_team` returned both rows. ### Honesty rules, enforced by the script rather than trusted - Every browser frame is a screenshot of the real Console against the app that run booted; the only additions are an outline around the new row and around the record count. - The request panel shows the bytes sent: the body is sent pretty-printed, exactly as drawn; the API key is masked after its public prefix. - The response panel shows the bytes received, with one re-layout for legibility: the escapes inside `result.content[0].text` are drawn as the characters they encode. - Terminal lines are lines that run's dev server printed, picked by pattern in order, with a dim ellipsis at each gap. A pattern that stops matching is a refusal (exit 5), not a stand-in line. - No assistant chat transcript, no model identifier, no record the run did not create; the caller is "an agent" / "any MCP client". - The script also refuses (exit 5) when the admin's call does not create the row, when the read-only identity cannot read the object, when its create is NOT refused, or when the row count moves on the refusal; and (exit 6) when the GIF is not 1200 px wide, runs outside 20-35 s, or exceeds 4,000,000 bytes. ## Re-render ```bash node docs/screenshots/render-readme-demo.mjs # rewrite the committed GIF node docs/screenshots/render-readme-demo.mjs --out /tmp/demo # preview: GIF, every frame and story.json, nothing committed touched ``` Prerequisites are the ones `pnpm dev` checks itself: a built workspace (`pnpm build`) and a built Console (`pnpm objectui:build`), plus `ffmpeg` on PATH. Playwright's Chromium is resolved from `examples/app-showcase` (preinstalled binary under `PLAYWRIGHT_BROWSERS_PATH`, never `playwright install`), `sharp` from the docs app's Next dependency (it verifies screenshot and GIF dimensions and duration), and ffmpeg assembles the GIF in two passes (palettegen, then paletteuse with bayer dithering). Four end-to-end runs in this container produced the same beats; byte-identity is not a goal (ids, timestamps, port). **Fonts:** the frames the script draws use Inter and IBM Plex Mono from Google Fonts, the hero cover's pair; all eight faces loaded in the committed render. Without them the script refuses to write the committed GIF (exit 3), as `render-hero-cover.mjs` does; `--out` previews the system fallback (Liberation Sans / DejaVu Sans Mono). The Console screenshots carry the Console's own fonts. No font file is committed. ## Verification Run on the final head `8336490e` (change set: the three files above, 662 changed lines, the binary counted 0). - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 13 families. All 13 exited 0: check-ci-filter-parity, check-closing-keyword-parity (and its `--self-test`), check-comment-mask-corpus, `@objectstack/lint` check:doc-formula-expressions, check:cross-package-test-inputs, check:doc-anchors, check:doc-authoring, check:driver-memory-census, check:gitlink-declared, check:nul-bytes, check:refd-timer-probe, check:watch-hint-literal. `--ran` reconciliation: "13 derived, 13 run, 0 NOT-MEASURED, 0 UNRUN". - `pnpm check:published-readme-links` exited 0: "221 outbound link(s) across 101 published markdown file(s) ... 144/144 relative target(s) found in the tree." - Narrowed eslint, spelled as `pnpm lint` runs it (`--no-inline-config`), over the three changed paths: - population, read from the config: `eslint --print-config` answers a config object for `render-readme-demo.mjs` and `undefined` for `README.md` and the GIF; - count, read from `--format json`: 3 results, 1 file linted with 0 errors and 0 warnings, 2 "File ignored because no matching configuration was supplied"; - invariance: that config's `parserOptions` is `{ ecmaVersion, sourceType }` with no `project` or `projectService` (type-aware linting is off repo-wide), and nothing imports the new script, so this diff cannot move any untouched file's verdict. - Not run locally: the repo-wide `pnpm lint` and the rest of CI's farm, which CI owns. ## Acceptance notes - Serial constraint: objectstack-ai#22224 (the README contributor-path section, lower in the file) is still open; `origin/main` at `3ae59661` has no README change since this branch's base `7b926f76`, so no merge was needed. Both PRs edit different hunks of README.md. - Observed in a fresh `pnpm dev:showcase -- --fresh` boot whose full output was read during this work, not investigated, not filed (outside this card): the boot-diagnostics block prints a `sys_migration` "UNIQUE constraint failed" insert warning, a pre-bind settings read warning, an analytics `admitObjectRead` warning and a sharing-rule "expands to NO recipients" warning. The terminal frame shows none of them because it shows a pattern-picked subset of lines; nothing in the GIF depends on them. - The MCP refusal is an `isError: true` tool result carrying prose only, with HTTP 200 and no machine-readable error code; the GIF shows it as received. --- _Generated by [Claude Code](https://claude.ai/code/session_011hRnra93sK5Q2gTYYTbdJR)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #22155
Fixes #22164
Clause-②: no
One PR for the two-card bundle that edits the same README section. #22156 is serial after this PR and is not addressed here: its README items (the first
curl, the pnpm floors) andCONTRIBUTING.mdare untouched.What changes
README, "Hack on the framework"
This covers #22164 and the README half of #22155.
The command block gains
pnpm objectui:buildas the Console step, betweenpnpm buildandpnpm dev.A paragraph under the block says what the step does:
.objectui-shainto the gitignoredpackages/console/dist;../objectuicheckout when one exists, otherwise from a shallow clone into.cache/;pnpm devstill serves the REST API, but no Console;.objectui-shamoves.This matches
content/docs/getting-started/examples.mdxandscripts/build-console.sh, both read onorigin/mainc6fe02d7ac.A table gives each command's measured cost, quoted as the cards measured it on a fresh clone (4-vCPU container, Node 22.22, pnpm 10.31):
pnpm installpnpm buildpnpm objectui:buildpnpm testThese were not re-measured here.
For a contributor's own change, the section now points to
pnpm turbo run test --affectedandpnpm --filter @objectstack/PKG test. A note says that--affectedcompares the branch with the localmain.The
pnpm installline's text is unchanged; only its comment column moved.CLI boot warning
This is the warning half of #22155, cross-lane to
domain:clias declared on the claim.packages/cli/src/commands/serve.tsused to print the same remedy wherever it ran:install @object-ui/console (already built) or run pnpm --filter @object-ui/console build in the objectui workspace. It now callsformatConsoleDistMissingWarning(consolePath), which is new inpackages/cli/src/utils/console.tsbeside the drift formatters. The function picks the remedy from one signal: whether the nearestpackage.jsonabove the resolved console package declares anobjectui:buildscript.Console dist not found at packages/console/dist, sayspnpm builddoes not produce it, and says to build it withpnpm objectui:buildin the root directory, which it names. The directory matters becausepnpm devruns inexamples/app-showcase, where the root script does not resolve.@objectstack/console, a dependency of@objectstack/cli, to reinstall at the CLI's version. A scaffolded project depends on@objectstack/cli(packages/create-objectstack/src/templates/blank/package.json), and the CLI depends on@objectstack/console(packages/cli/package.json). The resolver reads@objectstack/consoleonly. An installed@object-ui/consoleis never consulted.Why this signal (Zone 2, item 3):
.objectui-shapin that the drift guard keys on would also match another repository that carries a pin without the script.packages/consoleorscripts/build-console.shcouples the CLI to this repo's file tree.Only the text changes; mount and refuse decisions are untouched. There is a patch changeset for
@objectstack/cli.The new export is internal.
test/published-subpath-console.pin.test.tspartitions every export ofutils/console.tsinto public and not-public lists, and it went red until the name was recorded there asINTERNAL_SINCE_RETIREMENT. The two "exactly three names" checks still hold it off the published./consolesubpath. The helper's build-root walk is not exported.Not done here
pnpm install && pnpm build && pnpm devserves no Console on a fresh clone:/_console/and/answer 404, and the boot warning names a remedy that does not exist in this repo #22155, which belongs todomain:cli.GET /_console/on a composition with no Console still answers404; this boot measured it at404.Verification
All runs below were on
0ef75bd7d9unless noted.Unit test, both branches with controls (
packages/cli/test/console-resolve.test.ts, four new cases):pnpm objectui:buildand the root;@objectstack/console;Every case asserts that
@object-ui/consoleis absent.Ablations, each landed on disk via
scripts/ablation-replace.mjsand restored with blob equal to HEAD and an emptygit diff HEAD:null: 2 of 4 red (the framework case and the stop-rule control);Live boot, framework context:
pnpm dev -- --fresh -p 38592from this worktree, with nopackages/console/distand no../objectuibeside it. The boot printed:/api/v1/healthanswered 200 and/_console/answered 404. The non-framework branch was verified by the unit test only; no scaffolded project was booted.--affected, verified withpnpm turbo run test --affected --dry-run=json(turbo 2.11.5):TURBO_SCM_BASE=origin/mainbefore the change: 0 packages;//,@objectstack/cliand its six dependentsdogfood,downstream-contractandexample-{crm,multi-package,showcase,todo};main(9cc2c7954a, 108 commits behind): 81 packages. That is why the README says a stalemainwidens the set.@objectstack/cli:build: exit 0;typecheck, includingcheck:test-typecheck: exit 0;vitest run --project unit: 263 files and 3878 tests passed;--project integration, run locally becauseserve.tsis on the boot path: 93 files, 897 tests passed and 2 skipped.Gates:
node scripts/pm/dispatch-gates.mjs --commandsover this diff derived 68 commands. That is the dispatch's 53 plus 15 that the changeset and test files bring in. All 68 exited 0.--ranreconciliation: 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN.Lint, a proven narrowing: eslint
--no-inline-config --format jsonon the 4 changed TS files reported 4 files, 0 errors and 0 warnings.--print-configshows each file is in the config's population.eslint.config.mjsenables no type-aware linting (noparserOptions.projectorprojectService), so this diff cannot move a verdict on an untouched file. The repo-widepnpm lintis left to CI.Acceptance notes
os serve --helpstill describes--[no-]uiasEnable the bundled Console portal at /_console/ when @object-ui/console is installed. That is the same wrong package name, atserve.ts:1198and mirrored inpackages/cli/README.md:244. It is outside this claim's file surface, so it is reported to the PM rather than changed here.packages/spec/scripts/publish-smoke-boot-failure.test.tsand a comment inscripts/publish-smoke.shquote the old warning line. Those quotes are verbatim records of past CI runs, kept unchanged on purpose. The smoke's boot-failure predicate does not key on this line, and neither does the new wording.Generated by Claude Code