Skip to content

docs, cli: the on-ramp a first-time reader walks reads true (CONTRIBUTING, README curl, one pnpm floor, five examples, os dev / os init strings, tutorial transcript) - #22280

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-22156-docs-onramp-drift
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-22156-docs-onramp-drift

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #22156
Clause-②: no

This PR fixes the seven on-ramp items #22156 lists, one by one, each measured on this branch. Five are docs-only. Two are CLI strings, and patch round 1 adds os start's help text to them. Those carry the one patch changeset for @objectstack/cli. Clause-②: no: the CLI change is help text and printed text, with no change to the accept set or the public surface.

Item by item

# Item Change Files
1 CONTRIBUTING.md described the retired spec repository Rewritten for this repository. It names objectstack-ai/objectstack in the clone, upstream, issue and discussion URLs, and the internal/planning links are gone. It lists real docs trees and says which are generated (references/) or release-owned (releases/), where it used to send authors to hand-write MDX in references/. The bilingual .cn.mdx section is gone (0 such files), and so is the dead QUICK_START_IMPLEMENTATION.md link. The pnpm floor is now 10, and the title covers the whole stack. It points to AGENTS.md for the rules instead of restating them: the restated naming, Zod-first and single-source copies are removed. The flow now includes the changeset step and the worktree-per-task note. The needs:pack-smoke criterion is kept word for word, because pack-smoke-optin.yml says authors meet it in this file. CONTRIBUTING.md
2 README's first curl answered 401 It now signs in as the dev admin os dev seeds and sends the cookie. These are the two calls the scaffolded README and your-first-project.mdx show, and the sentence before them says a data call needs a session. README.md
3 Three pnpm floors All four places now say pnpm 10, with corepack enable installing the pinned version. That is the floor the workspace enforces (measured below). Patch round 1 adds two more lines of the same drift. examples/app-todo/README.md said pnpm 8+ for the monorepo and now says pnpm 10 (corepack enable). your-first-project.mdx's prerequisites row said npm 9+ / pnpm 8+ / yarn / bun for a scaffolded project and now says npm 9+ / pnpm 10.15+ / yarn / bun, the floor both scaffolders write (engines.pnpm in create-objectstack/src/templates/blank/package.json and SCAFFOLD_PNPM_RANGE in init.ts). The npm, yarn and bun parts were not measured and are unchanged, and so is the engines.protocol line #22215 edits. content/docs/getting-started/index.mdx, examples.mdx, your-first-project.mdx, CONTRIBUTING.md, examples/app-todo/README.md (README.md already said pnpm 10)
4 "three ready-to-run examples" against five The opening now says five. It names the three runnable apps (each has a pnpm dev:* script), then app-multi-package (linked to its section lower on the page) and embed-objectql. content/docs/getting-started/examples.mdx
5 os dev --help named only $PORT Server port (overrides $OS_PORT; $PORT is the legacy alias). That is the order readEnvWithDeprecation('OS_PORT', 'PORT') reads them in. Patch round 1 fixes os start --help the same way: Port to listen on (overrides $PORT, default 3000) becomes Port to listen on, default 3000 (overrides $OS_PORT; $PORT is the legacy alias). os start reads the same pair at start.ts:327, and "default 3000" is kept because it measured true. No test, snapshot or generated docs page pins either old string, and deployment/cli.mdx is hand-written and already says OS_PORT / PORT. packages/cli/src/commands/dev.ts, packages/cli/src/commands/start.ts
6 The tutorial's "clean" transcript left out a warning The example now is clean. ticket.view.ts declares the create and edit form that places description, and the prompt asks for that form. One paragraph explains that a field nothing names draws field-no-consumers. In patch round 1 its consumer list became visibly non-exhaustive ("a view, a form, a flow, a formula or an action, among the others the rule counts"), because the rule also credits page blocks, dataset members, validations, view filters and hooks. The transcript also carries the Logic: and Security: lines the command prints today. content/docs/getting-started/build-with-claude-code.mdx
7 os init --no-install printed npm install The package manager is now resolved before the install branch, by the same rule as before: the flag, then the invoking package manager, then npm. Before, a --no-install run kept the literal 'npm' initialiser, whatever was asked. packages/cli/src/commands/init.ts, new init-next-steps-package-manager.test.ts

Route choices

Item 6: a clean example, not a printed warning

I chose the PM's suggested route, measured against both alternatives on the four axes:

  • Real business need. A ticket's description is typed on the create form. The published Console already draws it there through the default form (PR docs(getting-started): the tutorial's Resolve action resolves the ticket; one routing line opens the index #22204's Acceptance notes), and the lint rule deliberately does not credit that unkeyed fallback layout (validate-field-consumers.ts docblock). Declaring the form makes the placement explicit. A grid column for long text, the one-line alternative, would teach the wrong UI, and the prompt asked for (subject, status, priority) columns.
  • Long-term fit. The page promises every example passes os validate verbatim. A clean example keeps that promise whatever the rule later credits. A transcript that prints the ~900-character warning would drift again the day the rule changes.
  • Keeping AI from writing bad metadata. This is a page agents copy. Showing the warning with "this is expected" teaches that advisories are noise. Showing the field placed teaches that every declared field has a consumer, which is the rule's intent.
  • Startup scope. Docs only: one form block (11 lines) and one paragraph. No new gate.

Narrative check: the Console step ("Create a ticket. Confirm the fields…") reads the same, and the counts stay 2 Objects 6 Fields and 1 Apps 1 Views 1 Actions.

Item 7: what changes and what does not

Measured (below): under --no-install all three invocations printed npm install, including --package-manager pnpm, because the variable was assigned only inside if (flags.install). That contradicts the flag's own help and the contract create-objectstack pins in scaffold-next-steps-pm.test.ts (never a hardcoded npm). After the fix, --package-manager pnpm and a pnpm-invoked run print pnpm install and pnpm exec objectstack ….

A plain npx os init … --no-install, the card's literal command, still prints npm install, and that is correct. The scaffold deliberately supports npm, yarn and bun: it declares engines.pnpm, never a packageManager stamp, and init.test.ts pins this ("does NOT pin a packageManager — the scaffold also supports npm, yarn and bun"). pnpm-workspace.yaml is inert for npm. So the card's alternative, dropping the pnpm files when recommending npm, would change which files the scaffold writes. That is outside this card, and the design says it is not needed. Rollback: revert the init.ts hunk. The variable goes back to being assigned in the install branch, and nothing else reads it.

Measurements (this branch; the CLI built from the worktree)

  • Item 2. I booted a tutorial-shaped scaffold with os dev --fresh on a random port. GET /api/v1/data/support_desk_ticket with no session gave 401 {"error":"UNAUTHENTICATED",…}. POST /api/v1/auth/sign-in/email as the dev admin gave 200. The same GET with the cookie gave 200 {"object":"support_desk_ticket","records":[],…}. /api/v1/health and /api/v1/discovery answer without a session, but neither shows that the object's API exists, so the README signs in instead. The server was stopped by its recorded PID.

  • Item 3. Root engines declares only a Node floor (22.0.0). packageManager is pnpm@10.31.0. The global pnpm here is 10.28.0 outside the repo and runs as 10.31.0 inside it, so pnpm 10 switches to the pin. In a throwaway worktree at the base commit, pnpm@8.15.9 install --frozen-lockfile --lockfile-only failed with ERR_PNPM_LOCKFILE_BREAKING_CHANGE, and pnpm@9.15.9 failed with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH … "overrides" configuration doesn't match. The floor is therefore pnpm 10.

  • Item 5. os dev --help before (base source): Server port (overrides $PORT). After: Server port (overrides $OS_PORT; $PORT is the legacy alias). os start --help before, on the CLI built at 8fe75a222: Port to listen on (overrides $PORT, default 3000). After, on the CLI built at fae81259b: Port to listen on, default 3000 (overrides $OS_PORT; $PORT is the legacy alias). The behaviour the new text states was measured on that build. Each os start ran in an empty directory, inside a private network namespace (unshare -n) so the shared box's port 3000 stayed untouched. With no port variable, the banner read API: http://localhost:3000/. With OS_PORT=41077 it read :41077. With OS_PORT=41077 --port 41078 it read :41078.

  • Item 3, patch round 1. On an os init scaffold, pnpm@9.15.9 install fails with ERR_PNPM_UNSUPPORTED_ENGINE … Expected version 10.15 or later, Got: 9.15.9. The monorepo's pnpm 8 refusal is the item 3 reading above.

  • Item 6. I scaffolded the blank starter from create-objectstack source (--skip-install --skip-skills), wrote the page's four files verbatim and wired their barrels, then ran os validate. With the page's original view: ✓ Validation passed, then the field-no-consumers warning for support_desk_ticket.description (verdict inert). With the edited page, re-run at fae81259b: Running author-time rules (50)…, ✓ Validation passed, no warning, and the six summary lines the transcript now shows. tsc --noEmit on the scaffold passes, and --listFiles confirms it compiled ticket.view.ts.

  • Item 7. os init APP --no-install before and after the fix:

    invocation before after
    npm user agent, no flag npm install npm install (control)
    npm user agent, --package-manager pnpm npm install pnpm install
    pnpm user agent, no flag npm install pnpm install

Tests and gates, at fae81259b (this branch after patch round 1 and merging main at c8bb3c8d9)

  • New packages/cli/src/commands/init-next-steps-package-manager.test.ts (unit tier, in-process Init.run, nothing spawned): 3 cases.
    • Ablation. With the fix committed, scripts/ablation-replace.mjs put the old semantics back (flags.install ? (flag ?? detect) : 'npm'): anchor 1→0, blob c4c2caca → 9b93b146. Result: 2 failed (the flag case and the pnpm-agent case), and the npm control passed. Restore: blob equals HEAD, and git diff HEAD is empty.
  • pnpm --filter @objectstack/cli typecheck: exit 0 at fae81259b (tsc --noEmit compiles src/**, the new test included, and check:test-typecheck says OK).
  • pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 265 files, 3918 tests passed, at fae81259b and before that at 8fe75a222. The integration tier is left to CI: the diff touches no spawn entry and no integration-tier file.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the 12-path diff gave 95 commands; the round-1 files added check:examples-live-imports. All 95 exited 0 at fae81259b. --ran reconciliation: 95 derived, 95 run, 0 NOT-MEASURED (a derived zero: every command has a recorded exit code). The earlier round gave 94 of 94 at 8fe75a222. On the first pass, check-docs-section-name caught the new form section's missing name, which is fixed.

Acceptance notes

  • Two scaffolders, two detection rules. create-objectstack names pnpm whenever pnpm --version succeeds. os init reads npm_config_user_agent. So under npx with pnpm installed, one says pnpm and the other says npm. Both instructions install, so this is an observation, not a defect. Carrier: none.
  • The field-no-consumers headline. Its text says "nothing in this stack reads or displays it" for a field the default form does draw. The rule's docblock excludes that unkeyed fallback layout on purpose: crediting it would credit every visible field. I read it as working to its contract and did not edit packages/lint. Carrier: none.
  • No browser check of the new form. The Console was not driven against the new form view: no objectui build in this container. That the form view is what the create and edit surfaces render is a reading of examples/app-crm/src/views/lead.view.ts, not a measurement here.
  • packages/cli/test/start-port-banner-agreement.e2e.test.ts:204 quotes the old os start help text ("overrides $PORT") in a comment. It asserts nothing, and the file is outside the surface, so it is left as is.

Generated by Claude Code

claude added 5 commits October 8, 2026 10:33
…md; one pnpm floor; the examples page counts five

CONTRIBUTING.md still described the retired protocol-only repository: clone
and upstream URLs, issue and discussion links, an internal/planning directory
that does not exist, docs trees that do not exist, a bilingual .cn.mdx
convention with zero files, and a pnpm >= 8 floor. It now names this
repository, sends contributors to AGENTS.md for the rules instead of
restating them, and carries the changeset step of the flow that gates a PR.

The getting-started prerequisites and the examples Quick Run now say pnpm 10,
the floor the workspace enforces, matching README.md and CONTRIBUTING.md.
The examples page opens with the five examples examples/ holds.

Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q
Co-authored-by: Claude <noreply@anthropic.com>
… package manager it resolved

`os dev --port`'s help read "overrides $PORT" while the command reads
OS_PORT first and PORT only as the legacy alias. It now names both, in
that order.

`os init` resolved its package manager only inside the install branch, so
a --no-install run kept the literal 'npm' initialiser and printed
`npm install` / `npx objectstack` even under --package-manager pnpm or a
pnpm invocation. The package manager is now resolved before that branch,
by the same rule (flag, then the invoking package manager, then npm).

Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q
Co-authored-by: Claude <noreply@anthropic.com>
…idate is clean

README.md's first data call answered 401 UNAUTHENTICATED against a
scaffolded project: data endpoints run under the same permissions as the UI.
It now signs in as the dev admin `os dev` seeds and sends the session
cookie, the same two calls the scaffolded README and Your First Project show.

build-with-claude-code.mdx promises every example passes `os validate`
verbatim and prints a clean transcript, but its `description` field sat on
no view, so every run printed a field-no-consumers warning. The view file now
declares the create and edit form that places it, the prompt asks for that
form, one paragraph says why, and the transcript carries the two summary
lines the command prints today.

Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q
Co-authored-by: Claude <noreply@anthropic.com>
…t test a 60s budget

check-docs-section-name requires a teaching example's form section to carry
a `name` (its i18n anchor). The init Next-steps test now takes the 60s budget
the in-process doctor tests use: the first oclif load in a busy worker ran
past vitest's 5s default.

Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 2 documentable anchor(s).

19 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json c8bb3c8d9cdae41c51b72f1cb3cb5028988e1fe4.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: os dev (command, 33 pages)
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 28 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json c8bb3c8d9cdae41c51b72f1cb3cb5028988e1fe4 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from a9611a24ca1de2e66c60eeeef30b5bb10e490e9d — the merge of head fae81259be665e11fa23e333bbec8bbed2c42943 into base c8bb3c8d9cdae41c51b72f1cb3cb5028988e1fe4, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a9611a24ca1de2e66c60eeeef30b5bb10e490e9d && git checkout a9611a24ca1de2e66c60eeeef30b5bb10e490e9d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c8bb3c8d9cdae41c51b72f1cb3cb5028988e1fe4 fae81259be665e11fa23e333bbec8bbed2c42943 && git checkout -B drift-repro c8bb3c8d9cdae41c51b72f1cb3cb5028988e1fe4 && git merge --no-ff fae81259be665e11fa23e333bbec8bbed2c42943

node scripts/docs-audit/affected-docs.mjs --json c8bb3c8d9cdae41c51b72f1cb3cb5028988e1fe4

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs c8bb3c8d9cdae41c51b72f1cb3cb5028988e1fe4 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

claude added 2 commits October 8, 2026 11:47
…odo pnpm floors; a non-exhaustive consumer list

`os start --port`'s help read "overrides $PORT" while the command reads
OS_PORT first, the same drift as `os dev`. It now names OS_PORT with PORT
as the legacy alias, and keeps "default 3000". The changeset names it.

your-first-project.mdx said pnpm 8+ for a scaffolded project whose
package.json declares the pnpm floor 10.15; it now says 10.15+.
examples/app-todo/README.md said pnpm 8+ for the monorepo; it now says
pnpm 10, as the other on-ramp pages do.

The tutorial's field-no-consumers sentence named five consumers as if the
list were complete; it now says they are among the ones the rule counts.

Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 8, 2026 12:32
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit ea4aa5c Oct 8, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22156-docs-onramp-drift branch October 8, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants