Skip to content

docs(content,spec): search-ready page descriptions — 52 authored rewrites and derived reference descriptions - #20258

Merged
hotlong merged 4 commits into
mainfrom
claude/issue-12238-description-rule
Sep 28, 2026
Merged

hotlong merged 4 commits into
mainfrom
claude/issue-12238-description-rule

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12238
Clause-②: no

Draft for the maintainer's voice review — do not merge or ready it. One card, both halves, per the rulings recorded on the card (comment 5856895911): 「一张卡全做」 (authored pages AND the generator), 「只改超范围的 52 页」 (only the out-of-range authored pages), 「不加门禁」 (no new check:* gate).

The rule

A page's frontmatter description is the one line a search result shows under its title.

  • Length: 70–160 characters (under 70 the engine discards it and writes its own snippet; over 160 it is cut mid-sentence). Rewritten authored lines aim at 120–155.
  • Content: what the page lets you do, in the words a reader would search, ending on why to click.
  • Form: a sentence, not a truncated title.

Population, before → after

Measured on this branch's tree (4b7d63cc) against its base e0f17a3; releases/** is release-owned and out of scope.

group pages median before median after under 70 70–160 over 160
authored (content/docs/** minus references/**, releases/**) 181 117 129 15 → 0 129 → 181 37 → 0
generated references/** 211 28 138 210 → 0 1 → 211 0 → 0

Every one of the 392 pages' frontmatter parses as YAML (js-yaml) with a string description in range. No exceptions remain.

Authored half — 52 rows

Only the description: line changes (git diff over the 52 files: 52 insertions, 52 deletions, zero other lines). title: / navTitle: lines, the 129 in-range pages, releases/**, apps/docs/** and meta.json are untouched. Over-long lines were trimmed toward their original meaning rather than rewritten. Rows 1–15 were under 70; rows 16–52 were over 160.

# page before len after len
1 concepts/north-star.mdx ObjectStack's current product and architecture direction. 57 Where ObjectStack is headed: a metadata-native backend that humans and AI agents can both operate safely — its principles, runtime shape and non-goals. 151
2 data-modeling/drivers.mdx Configuration reference for supported database drivers 54 Connect ObjectStack to PostgreSQL, MySQL, SQLite, MongoDB, Turso or memory — URL-based driver inference, per-driver config keys and dialect caveats. 148
3 getting-started/quick-reference.mdx Fast lookup table for all ObjectStack protocols 47 Look up any ObjectStack metadata type fast — the key schemas of every protocol category on one page, with common patterns and search tips. 138
4 index.mdx Technical documentation for ObjectStack. 40 ObjectStack documentation: build business apps from typed metadata that AI agents write and humans verify — start here for guides, concepts and APIs. 149
5 kernel/architecture.mdx Deep dive into the ObjectKernel architecture 44 How the ObjectKernel boots and runs: its core architecture, the plugin bootstrap lifecycle, and the public kernel API you call from a plugin. 141
6 kernel/events.mdx System-wide event bus for loose coupling between plugins 56 Every kernel lifecycle hook and data lifecycle hook in ObjectStack — when each fires, its payload and ordering, and which of the two systems to pick. 149
7 kernel/runtime-services/email-service.mdx Outbound email delivery and template rendering APIs. 52 Send email from a plugin or flow with services.email — send() and sendTemplate(), the result they return, and the typed error codes to handle. 142
8 kernel/runtime-services/queue-service.mdx Async queue publish/subscribe and DLQ operations. 49 Publish and subscribe to async job queues with services.queue — queue size, purge, and dead-letter listing, replay and cleanup for failed messages. 147
9 kernel/runtime-services/sharing-service.mdx Record-level sharing and editability checks. 44 Check and manage record-level sharing with services.sharing — read filters, canEdit and canDelete checks, and granting or revoking record shares. 145
10 kernel/services.mdx Dependency Injection mechanism for loose coupling between plugins 65 How plugins expose and consume services in ObjectStack — register a service, resolve it by name, see the standard services, and replace a core one. 147
11 protocol/index.mdx The formal ObjectStack protocol for Data, UI, and System layers 63 The ObjectStack protocol specification: how the Data, UI and System layers describe a complete business application as metadata, and its design principles. 155
12 protocol/objectui/actions.mdx Buttons, triggers, navigation, and user interaction definitions 63 The ObjectUI action protocol: declare buttons, menu items and triggers as metadata that run business logic, navigate, or call external integrations. 148
13 protocol/objectui/concept.mdx The philosophy and architecture of metadata-driven user interfaces 66 Why ObjectUI defines interfaces as data, not code: forms, dashboards and reports as declarative metadata that any renderer can draw consistently. 145
14 protocol/objectui/index.mdx Server-driven UI protocol - Define interfaces as data, not code 63 ObjectUI, the server-driven UI protocol: define layouts, forms and dashboards as JSON or YAML metadata that renderers turn into working interfaces. 147
15 protocol/objectui/widget-contract.mdx Standard props, events, and lifecycle for ObjectUI components 61 Build a custom ObjectUI field widget: the standard props, events and lifecycle it implements, how to register it, and accessibility and theme rules. 148
16 api/declarative-endpoints.mdx Expose your app to systems outside the platform by declaring an apis: endpoint as metadata — which channel to pick, the four publish gates, and the obligation that comes with an anonymous endpoint. 197 Expose your app to outside systems by declaring an apis: endpoint as metadata — pick the channel, pass the four publish gates, secure anonymous calls. 150
17 api/plugin-endpoints.mdx REST endpoints that become available when the corresponding plugin is installed — auth, workflow, automation, views, realtime, notifications, AI, i18n, and file storage. 169 REST endpoints each installed plugin adds — auth, workflow, automation, views, realtime, notifications, AI, i18n and files — and how to discover them. 150
18 automation/approvals.mdx Route a record for sign-off — who can configure the automation, and the run-identity decision that keeps an approval flow from quietly bypassing row-level security. 164 Route a record for sign-off with an approval flow — who may configure it, and the run-identity choice that keeps it from bypassing row-level security. 150
19 automation/connectors.mdx Call external systems from flows — plugin-registered connectors, and declarative provider-bound instances (rest / openapi / mcp) authored as pure metadata with reference-based credentials. 188 Call external systems from flows with plugin connectors or declarative rest, openapi and mcp instances — pure metadata with reference-based credentials. 152
20 capabilities/index.mdx The platform's capabilities in business language — data, views, automation, approvals, permissions, analytics, AI, integrations — each with a real CRM as the running example 173 What the platform can do, in business language — data, views, automation, approvals, permissions, analytics, AI and integrations, shown on a real CRM. 150
21 concepts/metadata-lifecycle.mdx How metadata flows through Repository → Change Log → Cache → Registry — the canonical event stream that powers Studio HMR, REST writes, and future cloud editing. 161 How metadata flows through Repository, Change Log, Cache and Registry — the event stream behind Studio hot reload, REST writes and cloud editing. 145
22 deployment/index.mdx Two things deploy on this platform and they move on separate clocks — the platform runtime you operate, and the metadata app you build. Which one you are doing decides which pages in this section are yours. 206 Deploy the platform runtime or ship your metadata app — two things on separate clocks. Find out which one you are doing and which pages cover it. 145
23 deployment/publish-and-preview.mdx A metadata app is versioned in your catalog while the platform moves on its own release train. Compile the app into an artifact, then pick how it reaches a running platform — installed from the catalog, or pinned as the runtime's boot artifact. 244 Compile a metadata app into a versioned artifact, then install it from the catalog or pin it as the runtime's boot artifact to preview and publish. 147
24 deployment/seed-tenancy-repair.mdx The automatic repair that stamps organization_id on untenanted seed rows and merges the global autonumber counter — when it runs, what it changes, what it deliberately leaves alone, and the manual remedy for a multi-organization install. 241 The automatic repair that stamps organization_id on untenanted seed rows — when it runs, what it changes, and the manual fix for multi-org installs. 148
25 deployment/self-hosting.mdx Run a compiled ObjectStack app on your own infrastructure with the official Docker image — plus Compose with Postgres, Kubernetes, and the bare Node.js fallback, including health checks, reverse-proxy wiring, and the secrets you must pin. 238 Run a compiled ObjectStack app on your own servers with the official Docker image, Compose and Postgres, or Kubernetes — health checks and secrets too. 151
26 deployment/tenancy-modes.mdx The three tenancy postures (single / group / isolated), how OS_TENANCY_POSTURE resolves, the membership policy for new users, and the degraded-tenancy boot guard. 162 Choose single, group or isolated tenancy: how OS_TENANCY_POSTURE resolves, the membership policy for new users, and the degraded-tenancy boot guard. 148
27 getting-started/build-with-claude-code.mdx The core ObjectStack workflow — Claude Code authors the metadata, you verify in the visual Console, guardrails catch mistakes, and the app you build is itself AI-operable over MCP. 180 Build an ObjectStack app with Claude Code: the AI writes the metadata, you verify it in the Console, guardrails catch mistakes, and the app speaks MCP. 151
28 getting-started/how-ai-development-works.mdx The division of labor behind ObjectStack — AI authors the typed metadata, you verify in the visual UI, and layered guardrails keep the AI from shipping mistakes. 161 How building with ObjectStack splits the work: AI writes the typed metadata, you verify it in the visual UI, and layered guardrails stop its mistakes. 150
29 getting-started/your-first-project.mdx Scaffold a standalone ObjectStack project with npm, understand what was generated, extend the data model, call the REST API, and build a deployable artifact — no monorepo checkout, no AI agent required. 202 Scaffold a standalone ObjectStack project with npm, extend the data model, call the REST API and build a deployable artifact — no AI agent required. 148
30 permissions/access-recipes.mdx Map a concrete access requirement onto the platform's layers — object CRUD, field-level security, row-level security, capabilities, app/nav gating, and the run-identity of automations. 184 Map a real access requirement onto the right layer — object CRUD, field-level and row-level security, capabilities, navigation gating and automations. 150
31 permissions/administrator-guide.mdx The task-first operations manual for customer system administrators — onboard a tenant in four steps (build the org tree, add people, assign positions, verify), with the 90% rule — daily administration is assigning positions; the capability plumbing ships built-in. 265 The permissions manual for tenant administrators: onboard a tenant in four steps, then run daily access by assigning positions — no plumbing needed. 148
32 permissions/attachments-access.mdx How access to record attachments is decided — the parent-derived read/create/delete model, authenticated downloads, the enable.files opt-in gate, and the storage-byte lifecycle. Covers sys_attachment and sys_file. 213 Who can read, upload or delete a record's attachments: the parent-derived access model, authenticated downloads, the enable.files gate and file cleanup. 152
33 permissions/authorization.mdx The one-page map of ObjectStack authorization — the enforcement chain, combination semantics, package provenance, lifecycle coverage, and the CI governance — with its limits — behind "declared" equals "enforced". Stitches ADR-0049/0054/0056/0057/0066/0068/0069/0078/0086 into a single narrative. 295 One-page map of ObjectStack authorization — the enforcement chain, how grants combine, package provenance, lifecycle coverage and the CI checks behind it. 154
34 permissions/capabilities.mdx How a package DEFINES an authorization capability with defineCapability — the declaration half of ADR-0066 D1 — and how that name travels from source to the sys_capability catalogue to a permission-set grant to a requiredPermissions check. 239 Define an authorization capability in a package with defineCapability, then follow it into the catalogue, a permission-set grant and a runtime check. 149
35 permissions/delegated-administration.mdx Administration itself as a scoped grant — a business-unit subtree, an action set, and an assignable-set allowlist, with self-escalation structurally impossible (ADR-0090 D12). 175 Hand out scoped admin rights: a business-unit subtree, an action set and an assignable-set allowlist, with self-escalation structurally impossible. 147
36 permissions/index.mdx Authentication, authorization, and record- and field-level access control in ObjectStack — a cross-protocol capability enforced by the ObjectStack runtime and declared as ObjectQL security metadata. 198 Authentication, authorization, and record- and field-level access control in ObjectStack — declared as security metadata and enforced by the runtime. 149
37 permissions/permission-sets.mdx The only capability container — object CRUD + FLS + scope depth + capabilities, union-merged across everything a user holds. Covers the built-in sets, assignment tables, access depth, the isDefault suggestion, and delegated-admin scopes. 237 Permission sets are the only capability container: object CRUD, field security, scope depth and capabilities, union-merged across what a user holds. 148
38 permissions/permissions-matrix.mdx Visual reference for ObjectStack's security model — permission types, the object × permission-set matrix, field-level security, sharing rules, business-unit depth, and the access-matrix snapshot gate 199 Visual reference for ObjectStack security — permission types, the object × permission-set matrix, field security, sharing rules and business-unit depth. 152
39 permissions/positions.mdx Positions (岗位) are flat capability-distribution groups — users hold positions, positions bind permission sets. The visibility hierarchy lives on business units, never here. Includes the built-in identity positions and the everyone/guest audience anchors. 254 Positions are flat groups that hand permission sets to users — the built-in identity positions, the everyone and guest anchors, and where hierarchy lives. 154
40 permissions/profiles.mdx The Profile concept was removed by ADR-0090 D2. Baseline access is now authored with the everyone audience anchor, package isDefault suggestions, and ordinary permission sets distributed via positions. 201 Profiles were removed. Author baseline access with the everyone audience anchor, package isDefault suggestions and permission sets given via positions. 151
41 permissions/record-view-auditing.mdx Who viewed this record, and when — the read action in sys_audit_log: its per-object opt-in, the four edges of its scope, and what a view row deliberately does not carry. 171 Find out who viewed a record and when: the read action in sys_audit_log, its per-object opt-in, the edges of its scope and what a view row leaves out. 150
42 permissions/sharing-rules.mdx Record-level access: the organization-wide default (OWD) baseline per object, the external sharing dial, criteria sharing rules and recipient types, and the RLS-safe analytics read scope. 187 Record-level access in ObjectStack: organization-wide defaults per object, the external sharing dial, criteria sharing rules and RLS-safe analytics reads. 154
43 permissions/system-context.mdx The authoritative table of every platform behaviour keyed off ExecutionContext.isSystem — what an elevated write gets, what it loses, and what the flag deliberately does NOT do. Built by census over the whole repo, not by recall. 231 Every platform behaviour that ExecutionContext.isSystem changes — what an elevated write gets, what it loses, and what the flag deliberately does not do. 153
44 permissions/tenant-audit-census.mdx The authoritative enumeration of every application-surface write call site against a tenancy-enabled object — how many thread an execution context, how many thread none, and how much of the population a static instrument can decide at all. Built by census over the whole repo, not by recall. 291 A census of every write call site against a tenancy-enabled object — which pass an execution context, which pass none, and what static checks can decide. 153
45 ui/actions.mdx Declarative buttons with server-side behavior — define once, bind to lists, records, and navigation, permission-check on both surfaces, and optionally expose to AI. 164 Declare buttons as metadata with server-side behavior — bind them to lists, records and navigation, permission-check both sides, and expose them to AI. 151
46 ui/audience-based-interfaces.mdx The same data serves different audiences. Give end users a curated app/page and keep builder surfaces — Studio, raw object tables, automation config — out of their view. Separate consumer and builder paths by default. 217 Serve one dataset to different audiences: give end users a curated app and keep builder surfaces like Studio and raw tables out of their view by default. 153
47 ui/create-vs-edit-form.mdx The new-record form asks 5 fields; the full edit form shows 40 grouped into sections. Derive both from one flat field set; only hand-shape the create form when layout or flow genuinely diverges. 194 Derive a short create form and a full sectioned edit form from one flat field set, and hand-shape the create form only when its layout truly differs. 149
48 ui/field-grouping-and-order.mdx The data model is a flat field set, but forms need sections. Where grouping actually lives — semantic field.group vs form sections vs a table's row grouping — and why those three "groups" are different things. 209 Where form sections really come from: field.group versus form sections versus table row grouping — three different groups over one flat field set. 146
49 ui/forms.mdx Render any FormView either publicly (anonymous, /f/:slug) or internally (authed operators, /forms/:name). The same metadata drives both, with URL prefill, configurable post-submit behavior, and declarative open-form actions. 224 Render any FormView publicly at /f/:slug or internally at /forms/:name from one metadata source, with URL prefill, post-submit behavior and form actions. 153
50 ui/public-data-collection.mdx Expose one form to anonymous visitors (web-to-lead, contact-us, intake) without opening the underlying base. Authorization is derived from the form's own declaration; only whitelisted fields are accepted. 204 Collect web-to-lead, contact and intake submissions from anonymous visitors through one public form, without opening the underlying data to guests. 147
51 ui/react-pages.mdx Author a page body as real React (kind:'react') or as constrained JSX that is parsed and never executed (kind:'html') — the two source-authoring tiers, and how to choose 169 Write a page body as real React or as constrained JSX that is parsed but never executed — the two source-authoring tiers and how to choose between them. 152
52 upgrading.mdx ObjectStack upgrades come in two halves that run on separate clocks — the platform runtime and your metadata app. Which one you are doing, what each one moves, and where the per-major checklists live. 200 Upgrade ObjectStack in two halves on separate clocks — the platform runtime and your metadata app. What each moves, and where each major's checklist lives. 155

Generator half — packages/spec/scripts/build-docs.ts

Before: every module page was written description: TITLE protocol schemas, every category overview description: Complete reference for all TITLE schemas — 210 of 211 generated pages under 70.

Derivation (new packages/spec/scripts/lib/page-description.ts, pinned by scripts/page-description.test.ts, 18 cases). No packages/spec/src/** file is edited: the rule reads the module's leading doc block through the existing findModuleDocBlock — the same block renderFileDescription already renders as the page's opening.

  1. Lead — the doc block's prose paragraphs before its first list, table, fence or tag, after an optional title line. Markdown and {@link} are flattened to text; citation-only parentheticals ((#NNN), [ADR-NNNN …]), bare URLs, one-word run-in labels and the Implements P0 requirement … boilerplate are dropped; a sentence that only introduced a list keeps its clause before the last comma or dash.
  2. Fit — whole sentences while the total stays ≤ 160; later paragraphs join only while it is still under 70; then the title line is prefixed if that fits. A single sentence over 160 is cut at the longest clause boundary that keeps ≥ 70 characters and leaves no bracket open — only when none exists, at a word boundary with an ellipsis.
  3. Complete — a lead still under 70 is followed by the page's schema names: … Reference for A, B and N more: every property with its type and default.
  4. Fallback — a module with no doc block gets TITLE schemas of the ObjectStack CATEGORY: A, B and N more — each property with its type, default and a TypeScript example. Names are listed while they fit, the rest counted.
  5. Category overviews — The ObjectStack CATEGORY in N reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example.

The value is emitted double-quoted (JSON.stringify, a subset of YAML's double-quoted style), because doc-block prose carries : and quotes. Everything is a pure function of the source, so check:docs stays a plain regenerate-and-compare. Each gen:docs run prints one tally line.

Which rule wrote the 196 module pages: 115 from the doc block alone, 19 doc block + schema names, 62 on the schema-name fallback (their module has no module-level doc block — e.g. data/object, api/contract, kernel/plugin). Plus 14 category overviews from rule 5. Two doc-block pages end on the word-boundary ellipsis (data/validation, marketplace/package): their opening sentence has no clause boundary that keeps ≥ 70 characters. They are in range.

Sample — 10 regenerated pages

page before after len rule
references/data/object.mdx Object protocol schemas Object schemas of the ObjectStack Data Protocol: ApiMethod, ApiOperation, Index and 13 more — each property with its type, default and a TypeScript example. 156 schema names
references/ui/view.mdx View protocol schemas View protocol schemas — the view metadata type and its three persisted body spellings. 86 doc block
references/api/dispatcher.mdx Dispatcher protocol schemas Defines how the ObjectStack HttpDispatcher routes incoming API requests to the correct kernel service based on URL prefix matching. 131 doc block
references/automation/control-flow.mdx Control Flow protocol schemas Structured control-flow constructs — the native + AI-authored flow model: a loop container, a parallel block, and structured try/catch/retry. 141 doc block
references/kernel/cluster.mdx Cluster protocol schemas Defines the runtime semantics required for ObjectStack to behave correctly when more than one Node.js process is involved. 122 doc block
references/security/rls.mdx Rls protocol schemas Implements fine-grained record-level access control inspired by PostgreSQL RLS and Salesforce Criteria-Based Sharing Rules. 123 doc block
references/api/error-code-ledger.mdx Error Code Ledger protocol schemas Error-Code Ledger. Reference for ErrorCode, ProvenanceWaiver, StandardSynonymWaiver: every property with its type and default. 126 doc block + schema names
references/system/object-storage.mdx Object Storage protocol schemas Object Storage Protocol. Reference for AccessControlConfig, BucketConfig, FileMetadata, LifecycleAction and 11 more: every property with its type and default. 158 doc block + schema names
references/kernel/plugin.mdx Plugin protocol schemas Plugin schemas of the ObjectStack Kernel Protocol: Plugin — each property with its type, default and a TypeScript example. 122 schema names
references/data/index.mdx Complete reference for all data protocol schemas The ObjectStack Data Protocol in 29 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example. 149 category index

Exact hunks in build-docs.ts (for #15403's rebase)

#15403 remains open; it will edit the title emission. This PR does not touch the title line (md += \title: ${zodTitle}\n``, base line 478) or anything else in the title path. Hunks, in base-file line numbers:

  • @@ -64,0 +65,6 — import of lib/page-description.
  • @@ -457,0 +464,3 — the descriptionSources tally, after PAGE_SECTION_LEVEL.
  • @@ -465 +474,2, @@ -470 +480 — the module source is read once into source and handed to renderFileDescription (behaviour unchanged).
  • @@ -476,0 +487,9 — the modulePageDescription(...) call, just above the frontmatter.
  • @@ -479 +498 — the one description line of module pages.
  • @@ -912 +931 — the one description line of category overviews.
  • @@ -1102,0 +1122,8 — the tally's console.log, before flush.

content/docs/references/** is regenerated by gen:docs, never hand-edited: against origin/main, the only changed lines under it are 210 description: lines (the root references/index.mdx was already in range and is unchanged).

Acceptance

  • every content/docs/** description outside releases/** is 70–160 characters — 392 of 392, no exceptions
  • a check:* gate enforces the range — dropped by the ruling 「不加门禁」
  • descriptions read as sentences, not as truncated titles (authored: hand-written; generated: sentence-fitted from the doc block, two ellipsis cuts named above)
  • the rule and the full before/after table are in this body; the PR stays draft for the maintainer's voice review

Changeset

skip-changeset: nothing here ships. packages/spec publishes files: [dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json]; scripts/** is not in it. Measured after a real build: modulePageDescription / categoryIndexDescription have 0 hits across every shipped path, while the positive control ObjectSchema has 56 hits in dist. content/docs/** belongs to no package.

Verification (on 4b7d63cc, after merging origin/main via os-regen-merge.sh)

  • pnpm --filter @objectstack/spec build, then check:generated — every gate ✓, including check:docs ("226 generated files in sync").
  • dispatch-gates --commands derived 94 families. --ran with recorded exit codes: 94 derived, 92 run, 2 NOT-MEASURED, 0 UNRUN. All 92 exit 0.
  • NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt's --re-measure. Both exit 3 because they need the full packages/* build closure. This diff changes no package source they read; CI builds that closure.
  • pnpm --filter @objectstack/spec typecheck (tsc + check:scripts-typecheck + check:test-typecheck) — exit 0.
  • vitest: scripts/page-description.test.ts 18/18; neighbours file-description, references-banner, category-title, root-index, category-index, schema-section — 3 files (local) + 4 files (repo), 68 + 146 tests pass.
  • Merge: main's feat(spec,automation): create_record / update_record fields.* accept the CEL value envelope — declared and evaluated together #20205 changed automation/builtin-node-config on both sides. It was regenerated from the merged tree in its own commit (4b7d63cc); its body is byte-identical to origin/main apart from the description line.

Acceptance notes

  • 62 spec modules carry no module-level doc block, so their pages use the schema-name fallback. Writing those doc blocks is a packages/spec/src/** edit and was out of this card's surface on purpose (clause-② path limb). The tally line printed by gen:docs is the running count.
  • Some doc-block leads read as internal notes rather than reader copy (e.g. kernel/metadata-protection: "Phase 1 introduces the item-level lock …"). The rule reproduces the source's own first sentence faithfully; improving those needs a src docblock edit, not a generator change.

Size: 265 files, +801 / −266 = 1,067 changed lines, generated files included — under the 5,000-line threshold.

Seat domain:devx#2, dispatched by session_018mA64scZ8fmpiPkrHVAwXj; implemented in session_01RCEEP3Z95jrpXCeF3it3Y2.

🤖 Generated with Claude Code


Generated by Claude Code

build-docs.ts wrote every generated module page as "<Title> protocol
schemas" and every category overview as "Complete reference for all
<title> schemas" — 210 of 211 generated pages under the 70 characters a
search result keeps. The new lib/page-description.ts reads the same
module doc block the page body opens with and condenses its lead into
70-160 characters; a thin lead is completed with the page's schema
names, and a module with no doc block gets a sentence naming them. The
value is emitted double-quoted so prose punctuation cannot change the
YAML. content/docs/references regenerated with gen:docs.

Claude-Session: https://claude.ai/code/session_01RCEEP3Z95jrpXCeF3it3Y2
Co-authored-by: Claude <noreply@anthropic.com>
…aracters

15 descriptions were under 70 characters and 37 over 160. Each is
rewritten to 120-155 characters as a sentence: what the page lets a
reader do, in the words they would search. Only the description line
changes; title and navTitle lines are untouched, as are the 129
in-range pages and releases/.

Claude-Session: https://claude.ai/code/session_01RCEEP3Z95jrpXCeF3it3Y2
Co-authored-by: Claude <noreply@anthropic.com>
builtin-node-config.mdx changed on both sides (main's #20205 edited the
module; this branch changed its description emission) and os-regen
deferred it; regenerated with gen:docs from the merged tree.

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 0 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 443b2f4fdce681f1d3656475c9c72f3ffacfee4c → packageMentionDocs.

@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 · 需要你过目 description 文案,并定一处小取舍 · 2026-09-27T16:52Z

本 PR 交付 #12238(p1,devx 车道最后一张开着的 p1)。席位复核结论 ACCEPT(#12238 评论 5857839052),CI 全绿。按卡面要求,它等你审过文案才落地。

改了什么

  • 作者页 52 行: 原来长度超出 70–160 字符的 description 全部重写,改后都在 138–155 字符。其余 129 页没动,这是你「只改超范围的 52 页」的裁决。
  • 生成页 210 个(references/**): 生成器 build-docs.ts 不再输出 "Field protocol schemas" 这类固定模板,改为从源码模块已有的说明文字里取第一两句;说明文字为空时,用 schema 名拼一句完整的话。改后全部落在 70–160 字符,中位数从 28 变成 139。
  • 没改的: 没改 packages/spec/src/**,也没加门禁,这是你「不加门禁」的裁决。

举几行作者页的例子(完整对照表在 PR 正文里)

页面 改前 → 改后(字符数) 新文案
kernel/architecture.mdx 44 → 143 How the ObjectKernel boots and runs: its core architecture, the plugin bootstrap lifecycle, and the public kernel API you call from a plugin.
permissions/authorization.mdx 301 → 156 One-page map of ObjectStack authorization — the enforcement chain, how grants combine, package provenance, lifecycle coverage and the CI checks behind it.
deployment/publish-and-preview.mdx 246 → 147 Compile a metadata app into a versioned artifact, then install it from the catalog or pin it as the runtime's boot artifact to preview and publish.

为什么改:搜索结果里的摘要只有这一行是我们能控制的。太短时 Google 会弃用、自己从正文里截一段;太长会被截断。

风险与代价(含回滚)

  • 只改 frontmatter 里的 description 一行,页面正文零改动。
  • 回滚就是 revert 一个 squash 提交。

席位意见: 建议通过。另有一处小取舍请你定:

  • 有 2 个生成页,它们的源码说明第一句超过 160 字符、又没有合适的断句点,所以被截断并加了省略号:
    • references/data/validation:"This module defines the validation schema protocol for ObjectStack, providing a comprehensive type-safe validation system similar to Salesforce's validation…"
    • references/marketplace/package:"A package (also called a Solution in Power Platform, an Unlocked Package in Salesforce, or an Application in ServiceNow) is the first-class unit of…"
  • A: 保留截断。
  • B: 这 2 页改用 schema 名拼成的完整句子。
  • 席位建议 B,因为卡的验收要求 description「读起来是完整句子」。改动只有一行,dev 改完我复核后直接落地。

你要做的(一个动作): 在本 PR 回复「同意,选 A」或「同意,选 B」。如果某行文案要改,直接点名那一行。


Generated by Claude Code

akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…el, so `os g` scaffolds reach the stack (objectstack-ai#20333) (objectstack-ai#20363)

Fixes objectstack-ai#20333
Clause-②: no

## Summary

`npm create objectstack`'s blank starter imported `./src/objects` alone,
so everything `os g view|action|flow|dashboard|app|skill` wrote was
never loaded and `os validate` counted 0 of it. The starter now wires
the seven generator barrels `os init` wires since PR objectstack-ai#20329, in the
lines `os init` renders: `exportsOf` over `export {};` barrels, and
`requires: ['automation', 'triggers']`. The copy is bound to the CLI's
single source (`SCAFFOLD_WIRED_BARRELS` / `SCAFFOLD_WIRED_REQUIRES`,
derived from `GENERATOR_SCAFFOLD_TARGETS`) by a parity pin, so it is not
a second wiring rule. A per-PR pin drives `npm create objectstack` → `os
g object` (control) → `os g flow` → `os validate` and reads `Logic: 1
Flows`.

## What changed

-
`packages/create-objectstack/src/templates/blank/objectstack.config.ts`:
imports every wired barrel, declares the `exportsOf` helper, and hands
each barrel to its stack key. The objects import changes from
`'./src/objects/index.js'` to `'./src/objects'`, the extensionless form
`os init` renders, which the parity pin compares verbatim; the
template's `moduleResolution: bundler` resolves the directory index, and
a fresh scaffold type-checks. It carries `requires: ['automation',
'triggers']`. `automation` was already there for the three connector
plugins, and its comment keeps that reason.
- Six new `src/{views,actions,flows,dashboards,apps,skills}/index.ts`
barrels, byte-identical to what `os init` writes.
- Two pins in `packages/cli/test/` (below). The CLI is the only package
that can call the renderer, and it already depends on
`create-objectstack`.
- `packages/cli/package.json` gains
`@objectstack/connector-{rest,openapi,mcp}` as devDependencies (lockfile
+9 lines, one importer block). They exist only so the scaffolded project
the chain pin builds under the CLI's `node_modules` can resolve the
blank config's connector imports, and so CI builds them in
`@objectstack/cli#test`'s closure.
- `scripts/cross-package-test-inputs.mjs` and `turbo.json` declare the
blank config and `src/**` as inputs of `@objectstack/cli#test`, with a
witness for the barrel glob the scan cannot name.
- Docs this change made false (see below), and a `create-objectstack`
patch changeset.

## Why a static copy, and what binds it

`create-objectstack` cannot import the roster. The dependency edge runs
the other way, and the npx entry must not pull the CLI's closure: the
boundary `scripts/sync-scaffold-emission-policy.mjs` already documents.
Measured options:

- **Generate at build time.** The roster is computed from the
`GENERATORS` literal in `generate.ts`. Reading it at
`create-objectstack`'s build would need either text-parsing that
literal, or evaluating the CLI's source before the CLI's own
dependencies are built, which is a build-order cycle.
- **Parity pin over a static copy.** Chosen as the least machinery.
`create-objectstack-wiring-parity.test.ts` reads every expected line off
the CLI: the barrel import lines, the `exportsOf` line and the stack-key
lines of `TEMPLATES.app.configContent`, the `requires` tokens as a
superset of `SCAFFOLD_WIRED_REQUIRES`, and each empty barrel byte for
byte from `TEMPLATES.app.srcFiles`. A generator added to the roster, a
renderer change or a hand edit of the template reddens it (ablations A1
to A3).

## Measured before and after, through the real commands

The on-ramp's real `bin/` scaffolded `my-app --skip-install
--skip-skills` into a directory where the config's imports resolve, then
this repo's CLI ran.

| step | `origin/main` `c74de10a9` | this branch |
|:---|:---|:---|
| `os g object order_line` (control) | exit 0, reaches the stack | exit
0, reaches the stack |
| `os g flow order_line` | exit 0, **Not wired** | exit 0, reaches the
stack |
| `os validate` | exit 0, `Data: 2 Objects`, `Logic: 0 Flows` | exit 0,
`Data: 2 Objects`, `Logic: 1 Flows` |

- **`exportsOf` is required here too.** A fresh starter type-checks
(`tsc --noEmit`, 6.0.3, exit 0). The same starter with `Object.values`
on the empty barrels fails with 4 x TS2322 (actions, flows, dashboards,
apps). After generating the object and the flow it still type-checks.
- **`requires` boots.** `os dev --fresh` on a random port: the flow-less
fresh starter was healthy after about 22s, `/api/v1/ready` answered 200,
and `AutomationServicePlugin` and the record-change, schedule,
time-relative and api trigger plugins loaded, resolved through the CLI's
own dependencies. With the generated flow it was healthy after about 24s
and reported `Flows: 1 flow(s) 1 bound to triggers`. Neither boot
printed "not enabled" or "NOT installed".
- **Census.** `src/templates/` holds one starter, `blank`, which is also
the registry's only entry.

## Pins

- `packages/cli/test/create-objectstack-wiring-parity.test.ts` (unit,
per-PR): 20 cases, described above.
- `packages/cli/test/create-objectstack-stack-reach.test.ts`
(integration, per-PR, not `.e2e`): the chain above, with item names read
off the generator roster. It asserts the exit codes, the named subjects,
the absence of the wiring lines and of a `requires` line from `os g
flow`, and the `Data: 2 Objects` / `Logic: 1 Flows` counts. No prose is
pinned.

## Ablations

Each ran after the fix was committed. Mutations went through
`scripts/ablation-replace.mjs` in wrap mode, which verified the anchor
count and the blob change and restored with blob equal to HEAD and an
empty `git diff HEAD`.

- **A1, the wiring reverted** (the `flows: exportsOf(flows),` line
deleted, then `create-objectstack` rebuilt). `ablation-dist-preflight
--absent` confirmed the line was gone from `dist/`. Chain pin: 2 failed,
2 passed. The control and the scaffold stayed green, and `os g flow`
printed the wiring lines while validate read no `Logic: 1 Flows`. Parity
pin: 1 failed, 19 passed, on the stack-key comparison. Direction: red.
- **A1 restore.** Rebuilt; `ablation-dist-preflight` found the marker
present in `dist/templates/blank/objectstack.config.ts`, and the whole
tree was clean. Chain pin 4/4, parity pin 20/20.
- **A2, a barrel dropped from the template** (the `skills` key deleted):
parity 1 failed, 19 passed. Red.
- **A3, one barrel's bytes drifted from what `os init` writes**
(`views/index.ts` reworded): parity 1 failed, 19 passed. Red.
- After A2 and A3 the whole tree was clean, and parity was 20/20.

## Verification

Patch round 1, at HEAD `702a27775` (origin/main `a88a1bb39` merged at
`df0c0c846`): the 124 derived gates, `check-issue-citations --base
origin/main` and `check:scaffold-emission-policy` all exited 0 on the
first pass (`--ran`: 124 derived, 124 run, 0 NOT-MEASURED, 0 UNRUN),
including `check:doc-anchors`, `check:docs-audit-scope` and
`check-affected-docs`; `pnpm lint` exited 0; the parity pin 20/20, the
chain pin 4/4, and `create-objectstack` 16 files, 232 passed.

Round 0: all of the following ran at HEAD `d50d46fe0` (origin/main
`26daf0b03` merged).

- `pnpm --filter create-objectstack test`: 16 files, 232 passed.
`typecheck`: exit 0.
- `pnpm --filter @objectstack/cli typecheck`: exit 0, including
`check:test-typecheck`, whose ledger is unchanged.
- CLI `unit` project: 231 files, 3316 passed.
- CLI `integration`: this chain pin plus `generate-stack-reach.test.ts`,
2 files, 11 passed.
- `pnpm lint`: exit 0 over the whole repo, not narrowed.
- `node scripts/check-issue-citations.mjs --base origin/main`: exit 0.
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--ran`: 124 derived, 124 run, 0 NOT-MEASURED, 0 UNRUN. Three gates first
exited 3 with PREREQUISITE NOT MET (`check:skill-examples`,
`check:dual-build-cjs-loads`, `check:i18n-coverage`) and exited 0 after
a full build.
- `pnpm check:scaffold-emission-policy`: exit 0.

## Docs this change made false, and a surface note

These published lines described an objects-only starter and are
corrected in place:

- the blank starter's `README.md` Layout, plus its app remedy, which now
says to export the file from `src/apps/index.ts`;
- the shipped `AGENTS.md` rule 3, which prescribed `Object.values()`
(measured TS2322 on the now-empty barrels);
- the package `README.md` tree;
- `content/docs/getting-started/your-first-project.mdx`: its section-2
tree and config block;
- `content/docs/getting-started/build-with-claude-code.mdx` (patch round
1): step 3 said the agent wires the action, view and app through
`actions:` / `views:` / `apps:` keys in `defineStack()`. It now says
each file is exported from its directory's barrel
(`src/actions/index.ts`, `src/views/index.ts`, `src/apps/index.ts`),
which the starter's config already hands to `defineStack()`, matching
the shipped `AGENTS.md` rule 3. A sweep of `content/docs/` found no
other sentence telling a starter author to add a collection key;
- `content/docs/deployment/cli.mdx`.

In `cli.mdx`, the `os generate` section's "Not wired" example named "the
`npm create objectstack` starter", and its first-app walkthrough ran `os
generate action approve`. On the wired starter that action is refused
with exit 1: "Action 'approve' references object 'my_app_approve' which
is not defined in objects". The walkthrough now runs `object customer`,
then `flow customer`, then `action customer`, measured `UI: 1 Actions`
and `Logic: 1 Flows`. Its fixture callout now names the extra action.

`content/docs/**` and `packages/create-objectstack/README.md` were
outside the claim's first file surface; the seat amended the claim in
place to name them. They are edited under the agent contract's rule that
a published line this change makes false is repaired in the same PR. PR
objectstack-ai#20341 edits `cli.mdx` around lines 1619 to 1690, disjoint from these
hunks; PR objectstack-ai#20258 edited lines 1 to 7 of `your-first-project.mdx` and
`build-with-claude-code.mdx`, has since landed, and merged into this
branch without conflict.

`skills/objectstack-platform/SKILL.md` line 192 says the template
declares `requires: ['automation']`. That is now stale, but `skills/**`
is a governed Tier H surface, so it is **not** edited here; the seat
files it for the skills lane once this PR lands.

## Acceptance notes

- Byte-identical barrels inherit the article slip in `init.ts`'s
`renderEmptyWiredBarrel` ("a action", "a app"). `init.ts` is read-only
here. Whoever next edits that renderer carries it, and the parity pin
will then require the starter to follow.
- The old walkthrough's `os generate flow onboarding` also bound its
flow to an undeclared object. That was not silent: `os dev` warned "the
flow will never fire". The new walkthrough binds to the object it
creates.
- Measured in patch round 1, on a scaffolded starter holding the Build
with Claude Code step-3 files: exporting each from its barrel, with the
config untouched, gives `os validate` exit 0 with `Data: 2 Objects 6
Fields` and `UI: 1 Apps 1 Views 1 Actions`, the page's step-4 counts.
Adding `actions:` / `views:` / `apps:` keys beside the wired ones
instead still validates (the later key wins), but the starter's `tsc
--noEmit` fails with 3 x TS1117, and a later `os g view customer` then
reports Not wired, while the barrel-wired project reports it reaches the
stack.
- The `requires` pair is PR objectstack-ai#20329's shape. The standing family cards
for the rest of that seam are objectstack-ai#20331 and objectstack-ai#20332, both named on objectstack-ai#20215.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01UYBdGBzWSrAMzpW8ah3GbP)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
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/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs content: meta descriptions too thin to serve as snippets — median 46 chars, 228 of 403 under 70

3 participants