Skip to content

docs: align pages with framework 17.0 - #82

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-78-align-docs-with-17-0
Aug 18, 2026
Merged

docs: align pages with framework 17.0#82
os-zhuang merged 1 commit into
mainfrom
claude/issue-78-align-docs-with-17-0

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#78

The docs were aligned to 16.0 and had not moved since. This brings them to 17.0.0 (released 2026-08-14).

Every claim below was checked against packages/spec on objectstack@origin/main, not against the release prose. The card said its findings came from release notes plus a grep and had not been schema-checked; three of them changed shape once measured, and one is refuted outright. Those are called out in their own section.

English only — 19 files, zero locale siblings. The pages edited now report stale in the freshness gate, which is the designed outcome.


The four live contradictions (the priority)

PageWasNowVerified against
quickstart.mdx"Node.js 20 or newer""Node.js 22 or newer"package.json and packages/cli/package.json both declare engines.node: ">=22.0.0"
resources/faq.mdx"Node 20+ and the CLI are enough""Node 22+ …"same
resources/faq.mdx"GraphQL is on the roadmap""No. REST is the API surface … /graphql now returns 404"packages/spec/src/system/core-services.zod.ts (the entry left in #4451/v17); no graphql module remains under packages/spec/src
build/data/index.mdxenable: { trash: true } documented as the shipped replacement for softDelete, "on by default"the whole note rewritten — see belowpackages/spec/src/data/object.zod.ts, CAPABILITIES_RETIRED_KEY_GUIDANCE

I also found a fifth in the same class the card's grep missed: resources/changelog.mdx compatibility matrix said "Node.js — 20 LTS or newer". Same fix.

On enable.trash — the correction is bigger than "the key was removed"

The card called this the worst of the four and it is, but not quite for the stated reason. The schema's retirement guidance says:

enable.trash was removed from @objectstack/spec in the 16.x line (#2377/#3207, ADR-0049) — it never had a runtime consumer: every delete has always been a hard delete, and a default-true flag promising a recycle bin was a false affordance (authors wrote trash: false believing they were opting out of a soft-delete that never ran).

So the page was not merely pointing at a key that stopped parsing — it was describing a capability that never existed. "Soft-delete with restore, on by default" was wrong on the day it was written. The rewrite says that plainly, gives the real recoverability options the schema names (per-field trackHistory, a lifecycle policy), states that soft delete is not a shipped capability, and gives the migration (os migrate meta --from 16).

Two dating notes, since the card and the release notes disagree with the schema and I would rather flag it than paper over it: the schema attributes the removal to the 16.x line, while content/docs/releases/v17.mdx lists object.enable.trash / enable.mru in its 17.0 retirement table. Both agree the key is now strict-rejected, which is the only part the doc asserts, so I wrote the doc without a version attribution for the removal itself.

While in that section I closed the honesty gap it opened: the page now claims enable is a closed vocabulary, so it has to list the vocabulary. The flag list is corrected against ObjectCapabilities (searchable and clone were missing; trackHistory and files were shown with the wrong defaults — both are opt-in, default false), and enable.apiMethods is documented, including the six-primitive shrink and the deny-all cliff a pure-legacy whitelist falls into.


Where the card was wrong

configure/mcp.mdx:157 — refuted, no change made

The card flagged ai.requiresConfirmation against the tool.requiresConfirmation that 17.0 removed, and said to read both schemas rather than assume the similar name meant the same key. They are different keys on different schemas, and the removed one's own retirement guidance prescribes the one this page documents:

tool.requiresConfirmation was removed … it never had a consumer, and a SAFETY flag that is merely accepted is false compliance … For a REAL gate on a destructive operation, put it behind an action and set action.ai.requiresConfirmation — that is the flag the HITL approval queue reads (packages/runtime/src/action-execution.ts), and it is the only path that actually stops execution.

ActionAiSchema.requiresConfirmation is live in packages/spec/src/ui/action.zod.ts, and its docstring also confirms the page's second sentence — "When unset, the bridge defaults to true for actions that look destructive (confirmText set, mode:'delete', or variant:'danger')". The page is correct as written. Editing it would have replaced a true statement with a false one.

Verified against: packages/spec/src/ai/tool.zod.ts (TOOL_RETIRED_KEY_GUIDANCE.requiresConfirmation) and packages/spec/src/ui/action.zod.ts (ActionAiSchema).

Export — the pages were silent, not wrong

The card flagged this as unverified and it was right to. None of the three pages asserts the old "export inherits read" behaviour. They are silent: the object-permission flag tables list allowRead / allowCreate / allowEdit / allowDelete / viewAllRecords / modifyAllRecords and omit allowExport entirely.

So the correct change was to add the semantics, not correct a wrong statement — with one real error underneath: all three pages filed export under system permissions ("Access Console, run reports, export data"), and it is an object permission. That mis-routing is what would send an admin to the wrong screen.

Added: allowExport in both flag tables, a dedicated Export is its own grant section on permission-sets.mdx (the three-state table, the most-permissive merge, package-shipped sets re-seeded but environment-authored sets not, member_default deliberately without the grant, and the high-privilege anchor ban), and a corrected routing row on managing-access.mdx. Export removed from the system-permission examples on all three.

Verified against: packages/spec/src/security/permission.zod.ts (ObjectPermissionSchema.allowExport — "unset/false = no export … NOT implied by viewAllRecords/modifyAllRecords", enforced at GET /data/:object/export with 403 EXPORT_NOT_PERMITTED) and packages/spec/src/security/high-privilege.ts (the anchor ban, and the explicit note that member_default carries no allowExport).

Record access — also silent, not wrong

Same finding. record-access.mdx describes sharing rules as "grant access based on declarative criteria" and never says what an empty criteria does; neither page mentions RLS enabled: false at all. Silent, so again the fix is an addition — a Both switches fail closed section stating both contracts and pointing a reader who audited under an earlier release back at the explain engine, since the real answer may have been wider than their metadata said.

Verified against: packages/spec/src/security/sharing.zod.ts (SharingRuleSchema — "Both now reject a match-all criteria, and the evaluator treats one as matching NOTHING") and packages/spec/src/security/rls.zod.ts (enabled — "Disabled policies are not evaluated").


Stale framing

GraphQL, elsewhere. Each of the four sites needed a different call, as the card predicted:

  • reference/runtime-capabilities.mdx paired GraphQL with OData as optional framework capabilities. OData is still real (packages/spec/src/api/odata.zod.ts), GraphQL is not, so the sentence keeps OData and gains an explicit "GraphQL is not one of them — do not document it as available or optional".
  • extend-existing-systems.mdx — "REST/GraphQL endpoints" was a passing mention; the word is simply dropped.
  • The two skill blurbs (reference/skills-cli.mdx, build/ai-skills.mdx) were quoting upstream text that has since changed, exactly as the card guessed. skills/objectstack-api/SKILL.md on objectstack@origin/main now reads "REST endpoints, auth providers, realtime channels, error envelopes, batch/versioning contracts". Both blurbs are realigned to that.

Both surviving GraphQL mentions (faq, runtime-capabilities) preserve the one thing that is not removed: graphql remains a valid protocol on an external datasource, which is a system ObjectOS queries rather than a surface it serves (content/docs/releases/v17.mdx, "Not removed: the 'graphql' protocol option on external datasource lookups").

Media fields.reference/field-types.mdx gains a What a media field stores section. The card's read was right — the page was silent about the thing that changed, not wrong about it — so nothing was corrected, only stated: an opaque sys_file id is stored, the { url, name, size, … } object is the expanded read form with url derived from the /files/:fileId resolver, and that is whyaccept / maxSize are server-enforced rather than browser hints. configure/storage.mdx was checked for agreement as the card asked and already agrees ("metadata persists in sys_file"; "Files are tracked in the sys_file system object — never as raw paths in your records"), so it is untouched.

Verified against: packages/spec/src/data/field-value.zod.ts (FileReferenceIdValueSchema — "an opaque sys_file id"; FileValueSchema — "Wave 2 … makes THIS the expanded read shape, with url derived from the /files/:fileId resolver rather than stored") and packages/spec/src/data/field.zod.ts (accept / maxSize — "Offered to the file picker AND enforced on write").

Approvals.build/automation/approvals.mdx gains a Dynamic routing section covering all three 17.0 additions — expression approvers with their closed root set and resolveAs expansion, author-declared decisionOutputs resuming as nodeId.key flow variables, and the onEmptyApprovers policy table. The approver-type table is also completed: the page listed three kinds where the schema declares eight.

Verified against: packages/spec/src/automation/approval.zod.ts (ApproverType, APPROVER_EXPRESSION_ROOTS, resolveAs, onEmptyApprovers, decisionOutputs).

Boot and readiness. The card asked whether these pages under-promise, and they did — all three described /api/v1/ready as readiness without saying what makes it go red. deploy/docker.mdx and deploy/kubernetes.mdx now state the 503-on-dead-driver contract and, for k8s, why it belongs on the readiness probe and not the liveness one. operate/backup.mdx said to "confirm the runtime surfaces a clear 503"; that is contractual now, so the rehearsal is repointed at the recovery instead.

Verified against: content/docs/releases/v17.mdx § "A datasource that cannot connect fails the boot (#3741, #3758, #3826)" — shipped docs rather than schema, because boot and probe behaviour is runtime, not declarable. No numbers or migration steps were invented; the 10s connection bound is quoted from that section.

resources/changelog.mdx. A 17.0 entry, covering the eleven changes an ObjectOS operator or app author would act on, each linked to the page in these docs that now explains it. Also: the Support windows table pinned "14.x (current) / 13.x security fixes / ≤ 12.x unsupported", which would have been a fresh contradiction the moment a 17.0 entry sat above it. Rather than invent support dates for 17.x and 16.x — which this repository has no authority to state — the table is now expressed relative to the current major, with dates deferred to the release notes. That is the only change here that removes a concrete claim rather than fixing one, and it is deliberate.


One page fixed outside the card's list

configure/data-sources.mdx — not on the card, found by grep during the GraphQL sweep.

  • Line 10 said "the REST/GraphQL API" — same defect class as the four GraphQL sites, mechanical, dropped.
  • It is also the page where a reader actually declares a datasource, so it is the load-bearing place for the 17.0 boot contract. The card's boot-and-readiness item found only pages matching /api/v1/ready and so missed it. Added a note that a datasource objects bind to must connect or the boot fails, with the concrete failure it replaces ("started clean, exited zero, then failed every read and write with Datasource 'x' is not registered").

Verification

pnpm turbo run type-check and pnpm turbo run build — both pass, both run with --force. This is load-bearing: turbo.json declares no inputs for either task, so content/docs/** does not move the hash and the first run replayed a cached green from a sibling worktree (FULL TURBO, 55ms). Filed as #81. Forced runs: type-check 15.6s, build 59.7s, 550+ doc paths prerendered.

All three translation checks, run the way .github/workflows/translations.yml runs them:

$ node .github/scripts/check-translation-ownership.mjs --actor os-zhuang --files changed.txt
⚠ TRANSLATION_BOT_LOGIN is not set — ownership is not enforced yet.
This PR touches 0 translation artifact(s) and 19 other file(s).
→ exit 0
$ node .github/scripts/check-translations.mjs
English pages: 79 · translations: 256 · guide rev 1
| Locale | Stale | Missing | Guide-stale |
| zh-Hans | 15 | 17 | 0 |
| ja | 15 | 40 | 0 |
| de | 15 | 41 | 0 |
| es | 15 | 40 | 0 |
| fr | 15 | 40 | 0 |
| ko | 15 | 40 | 0 |
✓ translations gate passed
→ exit 0
$ node .github/scripts/check-translation-output.mjs --self-test
✓ self-test: 20 case(s) on locale "zh-Hans", every rule demonstrated able to fail
→ exit 0
$ node .github/scripts/check-translation-output.mjs --files changed-output.txt
✓ translation output gate passed (104 pre-existing finding(s) reported)
→ exit 0

The freshness gate reports the edited pages as stale and still passes, which is the expected shape for an English-only PR. The output validator's 104 findings are pre-existing corpus debt in license.* / support.* locale files this PR does not touch; none is unsafe, so none blocks.

Rendering. The production build prerenders static HTML, so I read the rendered output rather than the diff — all 18 changed doc pages extracted and reviewed from .next/server/app/en/docs/**.html. Every anchor introduced was confirmed to resolve against a real generated heading id: export-is-its-own-grant, both-switches-fail-closed, media, data-lifecycle-retention.

Byte hygiene: grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over all 19 files — clean.


Out of scope, filed

#70 (the multi-Environment model, six pages) is left separate rather than folded in: different defect class, and #79 is already filed Blocked-by: this card for the overlapping file surface.


Generated by Claude Code

The docs were aligned to 16.0 and had not moved since. Several pages
described a contract the runtime no longer has.
Live contradictions fixed (a reader following these failed):
- Node.js floor 20 -> 22 (quickstart, faq, changelog compatibility matrix)
- faq promised a GraphQL roadmap for a surface 17.0 deleted
- build/data directed readers to `enable: { trash: true }`, a key that
now fails at parse time -- and which never had a runtime consumer
Stale framing corrected against packages/spec:
- GraphQL removed from runtime-capabilities, extend-existing-systems,
data-sources and the two skill blurbs
- media fields: state that a sys_file id is stored and the {url,...}
object is the expanded read form; accept/maxSize are server-enforced
- export: allowExport added to the object-permission tables as an opt-in
grant; it is not a system permission and read does not confer it
- record access: sharing rules with no criteria and RLS enabled:false
both fail closed
- approvals: expression approvers, onEmptyApprovers, decisionOutputs
- boot/readiness: a bound datasource must connect or the boot fails;
/api/v1/ready answers 503 on a dead data driver
- changelog: 17.0 entry; support windows stated relative to the current
major instead of pinning majors that rot
configure/mcp.mdx is deliberately unchanged: `action.ai.requiresConfirmation`
is a different, live key from the removed `tool.requiresConfirmation`.
English only; locale siblings are left to the translation pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJPxtTxoxTUnjNdTbiEaRa
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Align the docs with framework 17.0 — the pages still describe the 16.x contract

2 participants

@os-zhuang@claude