Skip to content

export: PDF / print document generation — re-planned for v18, superseding the #1301 NOT_PLANNED closure #8346

Closed

Description

@os-zhuang

Blocked-by: #24283 (the console pin move that carries the print fixes into an installable build — recorded by domain:spec seat 2 at the close-out reading)
Ruled: 6051470224 · letter B′ · 2026-10-08T03:19Z

Earlier blockers, each read closed at the close-out reading (2026-10-18): objectstack-ai/objectui#12915, objectstack-ai/objectui#12924, objectstack-ai/objectui#12906, objectstack-ai/objectui#11958, #22269, #22270, #22271.

Status

Maintainer decision (2026-08-13): PDF document generation is re-planned and scheduled for v18. This supersedes the #1301 NOT_PLANNED closure (2026-06-27).

Ruling history (what this issue reverses)

Re-introducing the capability therefore has a spec-side unwind cost, not just a renderer cost: the retired enum value, its error-map prescription, and the corresponding removal-registry entry all need to be walked back deliberately (and the migration story for metadata authored against 17 decided).

Why it came back

Solution scoping for an overseas port/terminal customer (2026-08) — and previously the template gap audit (docs/PLATFORM_GAPS_FROM_TEMPLATES.md gap #9, the origin of #1301) — keeps hitting the same wall: statutory / paper deliverables are non-negotiable for billing- and government-adjacent customers. Concrete artifacts in the port scenario alone: invoices, weighbridge tickets, delivery orders, official correspondence, monthly operation reports. Browser Ctrl+P (Console chrome, no pagination) and per-project Puppeteer side-services are the only workarounds today, and every implementation reinvents the latter.

Suggested scope (starting point: the M1–M4 sketch from #1301)

  1. M1 — declarative print templates in spec (header/footer, pagination, tables, related-record blocks).
  2. M2 — render engine (headless Chromium HTML→PDF), output downloadable or committed to storage (sys_file).
  3. M3 — row/detail action integration ("Export PDF", template picker).
  4. M4 — bulk export (merged PDF / zip) — note the aggregate bulk-action dispatch (execution: 'aggregate', objectui#3139) already anticipates "merged PDF, batch print" as its example payload, so the action surface is partly in place.
  5. Spec unwind: restore a 'pdf' (or successor) export format value and retire the spec: ListView.exportOptions is typed as a bare array, but the only renderer reads exportOptions.formats #8010 retirement prescription cleanly.

Related

Activity

  1. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 · 2026-10-07T15:26Z
    Session: session_01RPo7FUd6bSnAfkWMAKi848
    Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-8346-pdf-generation-design-round
    Worktree: objectstack-issue-8346
    Domain: domain:spec
    Seat: domain:spec#3 (seat post #18883)
    File surface: ⛔ none written. Design round only, per triage's release 6039014631: the dev measures and reports; the branch stays empty at its base and no PR is opened (the shape of #22046's measurement round). Read and measured on origin/main 3d9188502e: packages/spec/src/ui/view.zod.ts (exportOptions, the retired 'pdf' value and its error-map prescription), the removal / conversion registry entry #8010 left, the os migrate meta rewrite for it, the action surface for a document action (execution: 'aggregate'), sys_file storage, and any existing print / HTML-render path in this repo. Any probe is a local scratch file, deleted after the run. Stop on breach and explain in the report.
    Container & model: M, mode:subagent, model: opus (--tier: no path-derived mandate; the tier is the PM's per-card call, default tier for a design round with judgment)
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: 6039014631
    Serial constraints cleared: none. The round writes no file. No open PR touches view.zod.ts (13 open PRs' file lists read at this stamp).

    What the round delivers (triage 6039014631): a measured design note in the os-dev-report, then this seat writes the needs-user-decision analysis on this card (维护者速读, the four-facet block, Prior rulings read:) for the director to present. ⛔ No production change.

  2. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 8346,
      "status": "needs_decision",
      "branch": "claude/issue-8346-pdf-generation-design-round",
      "pr": null,
      "session": "session_01RPo7FUd6bSnAfkWMAKi848 — the PM's id (mode:subagent); this run's identity is the branch",
      "premise_still_valid": true,
      "summary": "DESIGN NOTE (measurement only; no production change, no PR; branch pushed empty at its base). Anchors: objectstack origin/main 3d9188502e (BASE, spec 17.7.0, no .changeset/pre.json); objectui main 9990f9e122 (equal to the remote main head at read time). [1] H1 CONFIRMED, and wider than three places. The card's cite view.zod.ts:1239-1264 is stale: the block moved to packages/spec/src/ui/list-view-export-options.ts (the shared-block change, #21229) and now has TWO carriers, the list view's exportOptions and the object-grid page component's properties.exportOptions (component.zod.ts:4901, the same object by identity). Every place a restore-'pdf' route walks back, at BASE: (a) enum + error map, list-view-export-options.ts:36-59 (LIST_VIEW_EXPORT_PDF_RETIRED, ListViewExportFormatSchema), formats describe :80-81; (b) union-level refusal view.zod.ts:2357-2366 (exportOptionsPdfUnionError) wired at :2875-2878, plus doc cross-refs at view.zod.ts:90, :2283, :2398, :2436, :4168, :6455, :6486 and component.zod.ts:1787, :4893; (c) conversion view-export-options-pdf-removed, conversions/registry.ts:2296-2385 (toMajor 17, retiredFromLoadPath true, retiredAfter 16.1.0), registered at :14878; (d) step-17 prose migrations/registry.ts:1076-1095; (e) step-18 semantic entry 18.ui-object-grid-export-options-closed.ts:18-24 and :34. Correction to H1's wording: the entry is prose; the refusal on the object-grid row is the shared enum itself; (f) pins: view.test.ts:3909-3975 (3 cases), component-object-grid-export-options-members.pin.test.ts:94-97, view-spelling-walk.test.ts (5 sites), the conversion's own fixture; (g) generated: spec-changes.json:127 and :1019, docs/protocol-upgrade-guide.md:146 and :171, liveness/view.json:200 note, content/docs/references (auto-gen); (h) hand-written: content/docs/ui/views.mdx:112; (i) objectui: packages/types/src/objectql.ts:747-760 (ListViewExportFormat), packages/types/src/zod/objectql.zod.ts:215 (mirror), plugin-grid ObjectGrid.tsx:4084-4106 (supported-format filter csv/xlsx/json). A real 'pdf' then needs a producer on BOTH export paths, the server stream (objectstack packages/rest/src/rest-server.ts:9944-10125 branches csv/xlsx/json) and the client fallback (csv/json). Without one, the filter drops it again: the pre-17 dead state; (j) release-owned history, never walked back: spec CHANGELOG 17.0.0 entry, console CHANGELOG. DECISIVE COST, measured by probe P2: applyConversionsToStoredItem replays the FULL chain including retired entries with no version gate (conversions/stored.ts:17-30, apply.ts:148). A stored view row declaring formats ['csv','pdf'] comes back as ['csv']. Re-admitting 'pdf' while the entry stays therefore silently strips a legal value at every rehydration. Deleting the entry contradicts ADR-0087 ('never deleted', the D2 window paragraph; the chain is a 'forever artifact'), so it needs an ADR amendment (Prime Directive 13). A per-seam excludeConversionIds is the 'filter of convenience' apply.ts forbids. And `os migrate meta --from 16` would keep stripping 'pdf' for a 16-to-18 upgrader. Any shape: the shipped refusal text says 'PDF export itself was declined as NOT PLANNED', which has been false since the 2026-08-13 re-plan. Whichever shape lands rewords it in the same landing: A deletes it, B or C point it at the new surface. [2] H2 CONFIRMED for objectstack, radius widened. The PM grep over packages/ exits 1. The widened pattern (puppeteer, headless chrom, html-to-pdf, pdfkit, pdf-lib, jspdf, pdfmake, wkhtmltopdf, gotenberg, weasyprint, @react-pdf), run over the whole repo minus changelogs, gets 2 hits, both prose (PLATFORM_GAPS doc, a dogfood audit). The only browser dependency is @playwright/test, an e2e devDependency of examples/app-showcase. The runtime image docker/Dockerfile is FROM node:22-slim: no browser, no CJK fonts. What the radius misses: objectui (measured below); cloud is NOT MEASURED (no checkout here, and the search API answers 403 'sessions are bound to their configured repositories'); the templates repo and customer side-services are NOT MEASURED. objectui at 9990f9e: zero PDF libraries in any package.json (control: 43 package.json files name react or lucide-react). The print and PDF paths that do exist: (1) app-shell/src/styles.css:67-260, an @media print sheet (objectui#4462) that hides console chrome, releases viewport clipping, repeats thead, avoids splitting rows and sets an @page 12mm margin; (2) bare window.print() controls in ListView.tsx:4977 (behind schema.allowPrinting, spec view.zod.ts:2932, liveness 'live'), DashboardView.tsx:136 and ReportViewer.tsx:141. Record pages have none; (3) ReportViewer's 'PDF' button, ReportExportEngine.exportAsPDF :118-173: it opens a popup, writes an HTML table with @page size and orientation, and calls print(). If the popup is blocked, it downloads .html; (4) GanttView.tsx:3359-3367 exportPdf with a hand-rolled single-image JPEG-in-PDF writer at :789-805, the ONLY PDF byte producer in either repo (objectstack has zero). So the card's 'Ctrl+P (Console chrome, no pagination)' has been stale since objectui#4462. [3] H3 CONFIRMED, narrower than 'partly in place'. BulkActionExecutionSchema is declared at packages/spec/src/ui/bulk-action.zod.ts:123 ('perRecord' | 'aggregate'). It is used by bulkActionDefs (view.zod.ts:2831-2839) and ActionSchema.execution (action.zod.ts:1156-1208), and lint's validateActionDispatchContract enforces the wiring. Consumers: objectui plugin-grid ObjectGrid.tsx:5098-5123 runBulkActionAggregate (one executeAction carrying params._selectedIds, resultDialog kept), useBulkExecutor.ts:212 and BulkActionDialog.tsx. The specimen is the showcase recalc endpoint, mounted imperatively on http.server ('there is no declarative endpoint surface in a bundle', recalc-endpoint.ts:12). What is in place is the DISPATCH; the PRODUCER is not. Every PDF behind it today is a per-project imperative route with its own browser dependency, which is the card's workaround. The published skill skills/objectstack-ui/rules/actions.md:177-185 already teaches newTabUrl '/api/v1/invoice/{recordId}/pdf' pointing at such a per-project endpoint. [4] H4 PARTLY FALSIFIED. The kernel 'storage' / 'file-storage' service (IStorageService, contracts/storage-service.ts:229-395) writes bytes by key only. A sys_file row is minted only by service-storage's HTTP routes (storage-routes.ts:385 presigned, :453 complete, :502 and :681 chunked), via StorageMetadataStore.createFile (metadata-store.ts:415), which is not a kernel service. No in-process door turns server-produced bytes into a committed sys_file: a search for commitFile, storeFile, putFile, uploadBuffer, createFileFromBuffer and saveFile finds 0 (control: createFile has 4 hits in storage-routes.ts). plugin-email's out-of-row attachments use raw IStorageService with no sys_file row (attachment-storage.ts). A committed row with no sys_attachment or field claim is 'immortal' (stranded-orphan-inventory.ts:11-25), so a generator must also attach its output. Server-side generation therefore needs ONE new service-storage seam (one owner), not a reuse. [5] H5 CONFIRMED external; the in-tree pull is shape-only. docs/PLATFORM_GAPS_FROM_TEMPLATES.md exists at BASE: gap #9 is the row at :21 (P1) and the section at :177-189 (document template engine with data binding; server render by headless Chrome or wkhtmltopdf; download or email). Its origin is the templates repo (contracts, procurement, compliance), not this tree. The port customer has zero in-tree mentions (weighbridge, delivery order, terminal: 0 hits). In-tree models with the shape of a printable document: showcase_invoice plus invoice_line (master-detail, inline line grid), and CRM opportunity plus opportunity_line_item ('a single product line on an opportunity quote'). Neither carries any print or PDF declaration. allowPrinting authorings: 0. exportOptions authorings: 4 (CRM opportunity.view x2, showcase task.view, docs views.mdx), all csv/xlsx/json. Declared-but-dead neighbour: packages/spec/src/data/document.zod.ts DocumentTemplateSchema and DocumentSchema (a docx fileUrl plus placeholders) have zero readers outside packages/spec and are not a metadata type (the file's own header says so), yet they are published (api-surface/data.json, authorable-surface). docs/NEXT_STEP.md:55 still plans ui/document-template.zod.ts. Live precedent of a template kind: email_template, whose engine plugin-email/src/template-engine.ts is mustache-only ('no loops / conditionals / partials'), so as it stands it cannot render invoice lines. [6] SHAPES. A, 'pdf' back in view.exportOptions. Spec: enum +1; delete the error map and the union refusal. Runtime: a PDF table writer on the server stream (a 4th branch in rest-server) and on the client fallback. Where: server stream or browser. Migration: the full list in [1], plus an ADR-0087 amendment for the D3 entry. AI error: asked for an 'invoice PDF', an AI writes formats ['pdf'] and believes it delivered, but gets a table of list rows; the history then reads 'removed in 17, restored in 18'. Resembles a grid's export-to-PDF or printable list view (Airtable, Salesforce Classic), none of the document-generation products. B, a document-template metadata kind + render service + action producing a sys_file. Spec: a new closed metadata type with a DEFAULT_METADATA_TYPE_REGISTRY row (28 types in MetadataTypeSchema today). It declares an object binding, page setup, header and footer, field blocks and related-list table blocks, and every field path is validated against the object at publish. Action wiring reuses ActionSchema locations and the aggregate bulk dispatch. Runtime: a render service with one engine driver (absence loud at boot; the action is refused with a prescription when no engine is configured), the new service-storage commit door, a sys_attachment link, and a download response. Where: server. Migration: additive. exportOptions stays closed, with its prescription re-pointed. The dead data/DocumentTemplate is retired or replaced in the same landing. Footprint reference: email_template spans 17 non-test spec files and 47 non-test files outside spec and plugin-email. AI error: a closed schema with publish-time path validation makes a wrong hole loud; free HTML templates would reproduce email's never-throw-on-render empty holes. Resembles Odoo QWeb reports (a template record + a Print binding on form and list + wkhtmltopdf + save as attachment) nearly 1:1, and Salesforce Quote Templates / Document Generation; Dynamics 365 Word templates are the docx variant. C, browser print only. Spec: at most one key (print on record pages). Runtime: an objectui print layout for record pages on the existing objectui#4462 sheet. Where: browser. Migration: none. AI error: a print button accepted as a 'PDF export', which is exactly the objectui#4462 lesson. It cannot produce a stored file, a batch run, a scheduled monthly report, an email attachment or deterministic statutory output. Resembles Salesforce Classic Printable View and Airtable's print view. Most of C already exists (point [2]). [7] RECOMMENDATION: B, with a block-model template rendered server-side through one engine driver and output as a sys_file attached to the record plus a download; exportOptions stays closed and its prescription is reworded; C is not a separate deliverable; A is rejected. By axis: BUSINESS NEED: every named artifact (invoice, weighbridge ticket, delivery order, official correspondence, monthly report) and gap #9's three (PO, contract, audit report) is a record-bound document with line blocks, or a report, that must be archived, emailed or batched. Only B serves non-interactive generation. A serves list tables, which are already printable via allowPrinting plus objectui#4462. C serves on-screen print. LONG-TERM SOUNDNESS: B is additive and contract-first (the declaration lands with its runtime). A reverses a ruled retirement and collides with ADR-0087 permanence (probe P2). C leaves the decided v18 scope unmet and invites a second template dialect later. AI-ERROR RESISTANCE: B's closed, publish-validated block model is the hardest to write wrong. A gives an AI a one-token way to claim invoice PDFs. C gives an AI a button it can mistake for an export. STARTUP FOCUS: B is the largest. Hold it to M1-M3 (one record to one PDF to a sys_file plus a download). Bulk goes through the existing aggregate dispatch only if the port pull names batch print. Defer DOCX, e-signature, email or flow-node attachment and scheduling. Ship one engine driver, not a matrix. No new gate is proposed.",
      "tests": "Read-only round; one scratch probe (scratchpad issue-8346/probe.mts, deleted after the run), run with tsx against the worktree's spec SOURCE at BASE 3d9188502e. EXIT=0, output verbatim: P1 parse success: false | P1 first issue: 'pdf' was removed from `view.exportOptions` formats in @objectstack/spec 17.0.0 (PDF export itself was declined as NOT P[...] | P1c control parse success: true | P2 stored formats before: [\"csv\",\"pdf\"] | P2 stored formats after replay: [\"csv\"] | P2c control same reference: true | P3 conversion present: true | P3 toMajor / retiredFromLoadPath / retiredAfter: [17,true,\"16.1.0\"] | P3 total conversions: 129. Greps (counts at BASE unless named): PM's H2 grep over packages/ exit 1; widened PDF/headless pattern over the whole repo minus changelogs: 2 hits, both prose; objectui at 9990f9e: PDF libraries in package.json 0 (control: react or lucide-react 43); objectui and objectstack PDF byte producers: 1 (GanttView.tsx:789) and 0; in-process sys_file commit door 0 (control createFile in storage-routes.ts 4); DocumentTemplate readers outside packages/spec 0; allowPrinting authorings in examples and skills 0; exportOptions authorings 4; 'pdf' authored as an exportOptions format 0; port-customer terms 0. cloud: NOT MEASURED (no checkout; GitHub search API 403, session bound to its configured repositories). Build, typecheck and tests: none run. Nothing was changed, so nothing owed (pnpm install only, per worktree-first). No gate run: no file changed.",
      "files_changed": "none (branch claude/issue-8346-pdf-generation-design-round pushed empty at 3d9188502e)",
      "mcp_calls": "0",
      "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/8346/comments (this os-dev-report, via scripts/pm/post-stamped.mjs)",
      "deviations": [
        "Premise refinements, not falsifications: the card's view.zod.ts:1239-1264 cite is stale (the block now lives in list-view-export-options.ts, with a second carrier on object-grid); the card's 'Ctrl+P prints Console chrome with no pagination' is stale since objectui#4462; H4 (a sys_file write door for a server generator) is partly falsified: only the HTTP upload routes mint sys_file rows; H3's 'action surface partly in place' holds for the dispatch only, not the producer.",
        "cloud repo NOT MEASURED: no local checkout, and GitHub code search answered 403 ('sessions are bound to their configured repositories'); no add_repo was attempted, since the dispatch asked only what the grep radius misses.",
        "Reads: REST GETs of issues 8346, 1301 and 8010 and their comments (gh api), plus 6 code-search GETs that answered 403. No other GitHub traffic.",
        "Empty branch pushed at BASE as the write-route probe (git push, not a REST write); no commit, no PR, no label, no assignee write. The branch stays on origin at its base; the local worktree is removed after this report (clean tree, nothing unpushed).",
        "pnpm install was run in the worktree per worktree-first, though no build was needed; the scratch probe and its output were deleted after the run."
      ],
      "open_questions": [
        {
          "question": "Q1. Does 'pdf' return to view.exportOptions?",
          "options": [
            "A. No. 'pdf' stays retired. The refusal text LIST_VIEW_EXPORT_PDF_RETIRED is reworded in the same landing to point at the document-generation surface (its 'declined as NOT PLANNED' clause is false since 2026-08-13). Cost: one string, its pins, and the objectui comment and hint sentences that cite the decline.",
            "B. Yes, restored as a list-table format. Cost: every site in summary [1](a)-(i), a PDF table writer on both export paths (rest-server stream branch + objectui client fallback), and an ADR-0087 amendment, because the stored-row replay strips 'pdf' with no version gate (probe P2). The alternative is a silent strip of a v18-legal value at every rehydration.",
            "C. Yes, as a successor value that dispatches to a default document template. That is a second spelling of one capability on a list-export pipe, with the same D3 conflict as B."
          ],
          "recommendation": "A. Business: none of the named artifacts is a list table, and list tables already print via allowPrinting plus objectui#4462. Long-term: no ADR-0087 reversal; the storage list() precedent was likewise 'restored correctly shaped', with the old shape staying gone. AI error: no one-token way to claim invoice PDFs. Startup: cheapest path."
        },
        {
          "question": "Q2. The template model: what does a document template declare?",
          "options": [
            "A. A declarative, closed block model bound to one object: page setup (size, orientation, margins), header and footer (logo, text, page X of Y), field blocks, related-list table blocks (child object, columns, totals) and a signature or footer block. Every field and relationship path is validated against the object at publish. An optional theme CSS. Resembles Salesforce Quote Templates.",
            "B. An HTML/CSS template string with data holes, loops and conditionals, Odoo QWeb or Visualforce style. Most flexible; holes are validated only if a parser is built. The existing email engine is mustache-only and renders an unknown path as empty, never throwing.",
            "C. Word/DOCX templates with placeholders (Dynamics 365 Word templates, Salesforce Document Generation), reviving or replacing the dead data/DocumentTemplate (fileUrl + placeholders). Needs a docx merge plus docx-to-PDF conversion (LibreOffice-class engine).",
            "D. Reuse the page metadata with a print kind, rendered by objectui's React renderer (the #1301 M2 sketch, 'isomorphic with Console'). Server render then means a headless browser loading the console, plus a session for it."
          ],
          "recommendation": "A. AI error: a closed, publish-validated schema makes a wrong path loud, while free HTML (B) recreates silent empty holes. Business: invoices, tickets, delivery orders and reports are header + line tables + footer, which A covers. Long-term: one declarative dialect. B can later become an escape hatch inside A only on a named pull. Startup: A is the smallest validator surface. The maintainer also names the kind: 'document_template' collides with the published but dead data/DocumentTemplate export (see Q5)."
        },
        {
          "question": "Q3. The rendering engine, and where it runs.",
          "options": [
            "A. Headless Chromium inside the runtime (Puppeteer or Playwright-core). Best HTML/CSS fidelity, with native header and footer templates and page numbers. Cost: the runtime image (node:22-slim today, no browser) grows by the browser, its system libraries and CJK fonts; per-render memory and cold start; it cannot run on serverless or edge.",
            "B. An external render service behind a driver contract (a Gotenberg-style Chromium HTTP sidecar; cloud can host it). The runtime image stays slim and the engine is swappable, following the storage adapter pattern. Cost: self-hosters run a second container; the driver must refuse loudly when unconfigured.",
            "C. A pure-JS PDF writer in-process (pdfmake or pdfkit class), from the block model directly, with no browser. Runs anywhere and is deterministic. Cost: lower CSS fidelity (a layout DSL, not HTML), and a CJK font subset must be embedded.",
            "D. Browser-side only (window.print over the objectui#4462 print sheet), i.e. shape C. No stored file, no batch or scheduled output."
          ],
          "recommendation": "B as the contract, with exactly one reference driver in v18, keeping boot-time absence loud. Long-term: the engine is a deployment choice behind a contract, not baked into the kernel. Business: statutory output must be deterministic and server-side. Startup: one driver, not a matrix. wkhtmltopdf (Odoo's engine) is not recommended because upstream is unmaintained. If the maintainer weights zero-dependency deployability over CSS fidelity, C is the trade, and it pairs naturally with block model A. This is a genuine trade-off for the maintainer."
        },
        {
          "question": "Q4. Where does the output land?",
          "options": [
            "A. A committed sys_file attached to the source record (sys_attachment), plus a download response. Needs ONE new in-process service-storage door that commits server bytes as a sys_file row: today only the HTTP upload routes mint rows (H4), and an unattached committed row is never reaped.",
            "B. A download stream only, with no storage. Simplest, but nothing is archived, so no email attachment, batch or audit trail.",
            "C. A file field named by the template. Field-reference claim semantics apply; one file per field slot."
          ],
          "recommendation": "A. Business: the named artifacts are statutory and must be archived and re-sent. Long-term: one owner (service-storage) gains the door instead of each generator hand-rolling bytes-plus-row. AI error: an attached file is visible on the record, so a failed generation is not silent. Startup: one seam."
        },
        {
          "question": "Q5. The v18 scope cut, and the dead data/DocumentTemplate.",
          "options": [
            "A. v18 = M1-M3: the template kind, one render driver, and a record/row action producing a sys_file plus a download. The dead data/DocumentTemplate, DocumentSchema and ESignatureConfig are retired in the same landing under ADR-0049 (zero readers outside packages/spec; a published export, so it is breaking in 18 with its D3 entry). M4 bulk (merged PDF or zip via the existing execution 'aggregate' dispatch) only if the port pull names batch print. Deferred: DOCX, e-signature, email or flow-node attachments, scheduled reports.",
            "B. v18 = M1-M4 including bulk merged PDF and zip.",
            "C. Keep data/DocumentTemplate and name the new kind print_template, avoiding the collision: two 'template' dialects coexist."
          ],
          "recommendation": "A. Startup focus: capability grows only on named pull. The port customer names single documents and monthly reports; batch print appears only as the aggregate example payload. Long-term: one 'document template' meaning, not two. AI error: an AI importing DocumentTemplateSchema today gets a schema no runtime reads. The bulk call is the maintainer's, on the customer evidence."
        }
      ],
      "out_of_scope_findings": [
        "carrier: whichever PR lands #8346 (in this card's own scope, not a separate card) · noted, not filed — the shipped refusal text LIST_VIEW_EXPORT_PDF_RETIRED (list-view-export-options.ts:36-42) says 'PDF export itself was declined as NOT PLANNED', false since the 2026-08-13 re-plan; the same sentence recurs in objectui types/src/objectql.ts:750, ListView.tsx:4970-4975, ReportViewer.tsx:131-134, app-shell styles.css:79 and content/docs/ui/views.mdx:112 · dedupe words: NOT PLANNED, LIST_VIEW_EXPORT_PDF_RETIRED, pdf retirement text, exportOptions pdf",
        "carrier: the shape-B implementation PR (none if B is not chosen; then an enforce-or-remove card is the seat's call) · noted, not filed — packages/spec/src/data/document.zod.ts DocumentTemplateSchema / DocumentSchema / ESignatureConfigSchema: published, zero readers outside packages/spec at 3d9188502e, not a metadata type; dead declaration, no producer stores it, so not class c · dedupe words: DocumentTemplateSchema, document.zod.ts, dead declaration, enforce-or-remove",
        "carrier: the objectui PR that lands the print/PDF surface · noted, not filed — objectui ReportViewer's 'PDF' button runs exportAsPDF (ReportExportEngine.ts:118-173): a print popup, an .html download when the popup is blocked; its own comment (ReportViewer.tsx:131-134) calls it a PDF export. Read only, not reproduced in a browser this round, so no reach measured · dedupe words: ReportViewer PDF button, exportAsPDF, print popup, html fallback",
        "carrier: the shape-B implementation PR (docs and skill update) · noted, not filed — skills/objectstack-ui/rules/actions.md:177-185 teaches newTabUrl '/api/v1/invoice/{recordId}/pdf' to a per-project endpoint, and docs/NEXT_STEP.md:55 plans ui/document-template.zod.ts; both become stale or anti-pattern once a platform surface lands · dedupe words: open_invoice_pdf, newTabUrl pdf, document-template.zod.ts, NEXT_STEP 13.2"
      ]
    }

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    Seat verdict: ACCEPT the measurement round (6041346785); the contract shape goes to the maintainer · domain:spec seat 3 (#18883) · session_01RPo7FUd6bSnAfkWMAKi848 · 2026-10-07T15:45Z

    Checked on GitHub and origin/main 3d9188502e, not from the report:

    • Round terms met: branch claude/issue-8346-pdf-generation-design-round sits at its base 3d9188502e, no commit and no PR; the report comment is intact (25,152 bytes, tail present).
    • LIST_VIEW_EXPORT_PDF_RETIRED lives in packages/spec/src/ui/list-view-export-options.ts:36 and still says "PDF export itself was declined as NOT PLANNED" (:38), false since the 2026-08-13 re-plan.
    • conversions/stored.ts states "The full chain replays, including retiredFromLoadPath entries" (:17), and ADR-0087 says a retired conversion is "never deleted" (:145) and the chain is "a forever artifact" (:306). So re-admitting 'pdf' while the protocol-17 conversion stays would strip it from stored rows at every rehydration (the report's probe P2: ['csv','pdf'] → ['csv']).
    • DocumentTemplateSchema / DocumentSchema: no code reader outside packages/spec (hits are docs only, two of them generated references).
    • sys_file rows are minted only by the HTTP upload routes (storage-routes.ts:404, :520 → metadata-store.ts:415); no in-process door commits server-produced bytes.
    • Not measured by the round, recorded as a confidence gap: the cloud repo (no checkout here), and objectui's ReportViewer "PDF" button in a browser.

    Release: session_01RPo7FUd6bSnAfkWMAKi848 · why: the measurement round is delivered and the shape is the maintainer's to rule · to: needs-user-decision, unassigned. Whoever implements the ruled shape claims afresh.


    待裁(needs-user-decision):v18 的 PDF / 打印文档,用哪种契约形状交付?

    维护者速读

    • 改了什么: 什么都没改。本轮只测量,结论是要请你定"PDF 文档"在平台里长什么样。
    • 为什么要你定: 你 2026-08-13 已定 v18 要做,但形状没定。两条路方向相反:一条是把 17.0 已退役的列表导出 'pdf' 拿回来;另一条是新增"文档模板"这一种元数据。
    • 测到的关键事实: 你们海外港口客户要的发票、过磅单、提货单、公函、月报,都是"一条记录 + 明细表 + 页眉页脚",需要存档、能重发,不是"把列表打印成表格"。列表打印今天已经能用(objectui 的打印样式表加 allowPrinting)。两个仓库里都没有任何能在服务端生成 PDF 的东西。
    • 席位意见: 推荐 B:新增一个封闭的"文档模板"元数据类型,在服务端按模板生成 PDF,存成附件挂在记录上并提供下载;列表导出不恢复 'pdf'。
    • 你要做的: 回一个字母 A / B / C;如愿意,同笔回下面三个子问题(不回即按推荐执行)。

    一句话问题

    客户要从一条业务记录(比如一张发票)生成一份能存档、能再发出去的正式 PDF 文件,平台应该用什么方式让应用作者声明它?

    背景

    Governing text

    • ADR-0087(docs/adr/0087-metadata-protocol-upgrade-contract.md:145):已退役的转换条目"never deleted";:306 转换链是"a forever artifact"。存量行重放全链、含已退役条目(packages/spec/src/conversions/stored.ts:17)。
    • AGENTS.md Prime Directive 13:推翻一个已记录的决定本身就是决定,要新 ADR 或在原 ADR 上改状态行。
    • AGENTS.md Prime Directive 10:不宣传运行时兑现不了的能力(声明即强制)。
    • packages/spec/src/ui/list-view-export-options.ts:36-42:现行退役文案说"PDF export itself was declined as NOT PLANNED",8 月 13 日起已不成立。

    前提(各带复核命令与阳性对照)

    1. 退役文案仍说"不做"。 git grep -n "declined as NOT PLANNED" origin/main -- packages/spec/src/ui/list-view-export-options.ts(对照:同文件 git grep -c "ListViewExportFormatSchema" 应 >0)。
    2. 存量行重放含已退役的 'pdf' 转换。 git grep -n "including \retiredFromLoadPath` entries" origin/main -- packages/spec/src/conversions/stored.ts(对照:同文件 git grep -c "applyConversionsToStoredItem"` 应 >0)。
    3. 两仓无服务端 PDF 生成能力。 git grep -l -i -E "puppeteer|pdfkit|pdf-lib|pdfmake|gotenberg|wkhtmltopdf" origin/main -- packages/(应为空;对照:git grep -l "@playwright/test" origin/main -- examples/ 应命中)。
    4. 没有进程内把服务端字节存成 sys_file 的门。 git grep -n "createFile(" origin/main -- packages/services/service-storage/src/storage-routes.ts(只在 HTTP 路由里;对照:metadata-store.ts 里有 async createFile 定义)。
    5. 旧的 DocumentTemplateSchema 无代码读者。 git grep -l "DocumentTemplateSchema" origin/main -- . ':!packages/spec' ':!content'(应为空;对照:packages/spec/src/data/document.zod.ts 内计数 >0)。

    选项 × 真实代价

    选项 做什么 客户感受到的后果
    A 把 'pdf' 恢复为列表导出格式,服务端和前端各加一个"表格转 PDF" 只能把列表打成表格,做不出发票、过磅单这类单据。还要修改 ADR-0087 才能停掉存量行的 'pdf' 剥离(实测会被静默剥掉),并回退约 9 处退役痕迹
    B 新增封闭的"文档模板"元数据(绑定一个对象:纸张、页眉页脚、字段块、明细表块),发布时校验每个字段路径;服务端经一个引擎驱动生成 PDF,存为 sys_file 挂到记录上并给下载;列表导出保持关闭,退役文案改指新能力 应用作者能声明"这张发票长这样",一键生成、自动存档、可重发。写错字段在发布时就被拒。要新做:一个元数据类型、一个渲染服务、service-storage 一个新的存档入口
    C 只做浏览器打印:给记录页加打印版式,沿用现有打印样式表 能在屏幕上打印。生成不了存档文件,做不了批量、定时、邮件附件,输出也不稳定

    业务含义直译

    • A:给复印机加了一个"另存为 PDF"按钮,但印出来的永远是名单。
    • B:像 Odoo 的报表模板 / Salesforce 的报价单模板:先设计好单据格式,系统按记录套打并归档。
    • C:让用户自己按浏览器的打印键。

    os-decision-facets

    • ① 项目长远合理性:B 是新增能力,一份声明配一个运行时,契约先行,不推翻已记录的退役。A 推翻 17.0 的退役并与 ADR-0087"转换永不删除"冲突,还让"列表导出"和"单据生成"挤在一个口子里。C 不交付 v18 已定的范围,日后大概率还要另起一套模板方言。
    • ② 实际业务拉动:真实拉动来自海外港口客户,要的五类产物(发票、过磅单、提货单、公函、月报)都是记录加明细的单据或报表,要存档、要重发;另有模板缺口文档第 9 条。只有 B 服务这些需求。A 服务的是列表打印,这件事今天已经能做。仓库内没有任何应用声明过打印或 PDF。
    • ③ 防 AI 犯错:B 是封闭结构,发布时校验字段路径,写错就被拒。A 给 AI 一个只改一个词就"声称做了发票 PDF"的捷径,实际只拿到列表。C 让 AI 把打印按钮当成导出。
    • ④ 创业阶段不扩散:B 体量最大,所以 v18 只做 M1–M3(一条记录生成一份 PDF、存为附件并可下载),只接一个引擎驱动;DOCX、电子签名、邮件或流程附件、定时报表都延后。同笔退役已发布却零读者的旧 DocumentTemplateSchema,不留两套"模板"含义。

    Prior rulings read: pdf,exportoptions,print,document template → 0 hits; none; thread: none; repo: objectstack-ai/objectstack;另读 ADR-0087 D2/D3(:145、:306)与 #8010 / #1301 的退役记录。

    推荐

    B。 两年后的样子:平台有一个"文档生成"能力,单据格式以元数据声明,生成引擎躲在驱动契约后面可替换,产物作为附件归档;主流对照是 Odoo QWeb 报表(模板记录 + 打印入口 + 存为附件)和 Salesforce 报价单模板 / Document Generation。

    • 自检: 只看①选 B;②③④ 是否翻转:否(②③同向支持 B,④只把范围收窄到 M1–M3,不翻字母)。
    • 子问题,同笔请裁(不回即按推荐执行):
      1. 第一个引擎驱动。 推荐进程内纯 JS 排版(pdfmake 一类):不加第二个容器,输出确定,能跑在任何部署形态,与封闭的块模型天然匹配;代价是 CSS 保真度低,要嵌入中日韩字体子集。测量 dev 推荐的是外置无头 Chromium 服务(Gotenberg 一类):保真度高、运行时镜像不变,代价是自部署用户要多跑一个容器。两者都放在同一个驱动契约后面,以后可以加第二个驱动。这是真实的取舍,席位倾向前者。
      2. v18 做不做批量合并 PDF / 打包下载。 推荐不做,除非客户明确点名要"批量打印"(现有的 execution: 'aggregate' 批量分发以后能直接接上)。
      3. 旧 DocumentTemplateSchema / DocumentSchema / ESignatureConfigSchema。 推荐在同一次落地按 ADR-0049 退役(已发布、零读者,v18 内属于破坏性变更,带迁移条目),新类型沿用"文档模板"这个名字。
    • 回退: C 作为最小交付(只做记录页打印版式)。前提是你接受 v18 不出存档文件。
    • 置信缺口:
      • cloud 仓未测(本容器无检出),那边是否已有渲染服务不知道。
      • 客户要的单据模板具体长什么样(版式复杂度、是否中文)没有样张。
      • objectui 报表页的"PDF"按钮(实为弹窗打印)只读过代码,没在浏览器里跑过。

    裁后执行

    • 选 B: 拆成 spec 先行的几张卡:① spec:文档模板元数据类型与发布期路径校验,同笔改写退役文案、退役旧 DocumentTemplateSchema;② service-storage:进程内存档入口(存为 sys_file 并挂附件);③ 渲染服务加一个引擎驱动,未配置时启动即响亮报出;④ 记录页 / 行操作的"生成 PDF"动作(objectui 侧另立卡)。每张卡带拒收用例与契约复核。
    • 选 A: 先起一份 ADR-0087 修订(处理存量重放),再回退约 9 处退役痕迹,并在服务端和前端各补一个表格转 PDF。
    • 选 C: 本卡缩为 objectui 的记录页打印版式卡,同时把退役文案改成"打印走浏览器"。
  4. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    Box intake (triage): the face is complete and ready for the director. The decision is the domain:spec seat's 6041425322. Its recommendation is B (fallback C, the minimum delivery), with the sub-questions answered by default if the maintainer replies with a letter only. The labels stand (needs-user-decision · domain:spec · target:v18).

    Read for intake:

    • 维护者速读;
    • Governing text:;
    • premises with re-check commands and positive controls;
    • options × real cost;
    • the os-decision-facets block;
    • recommendation, fallback and confidence gap;
    • 裁后执行;
    • Prior rulings read:.

    None is missing.

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-07T20:55Z. ⛔ Not a claim.

  5. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    Ruling: batch #288 item 3 · letter B′ · maintainer 「8346 B′」 2026-10-08T03:18Z

    Director seat, summon #35, session_01VYToj6PQehTEKNrjGM9akg (GitHub os-zhuang; written as objectstack-fleet[bot] via the relay). Batch #288 presented the domain:spec seat's decision request (6041425322: A restore the list export, B a new document-template metadata type, C browser printing only; the seat recommending B) in the detailed form. The maintainer asked how the mainstream platforms model document templates, whether AI writes the template, how it is previewed and whether a visual designer is needed, and then proposed the simpler shape: an AI-authored metadata page that declares paper, header and footer. The seat read the proposal against the platforms (Odoo's QWeb report plus report.paperformat; Salesforce's Visualforce renderAs="pdf"), named the two hard parts it does not remove (the render engine; print-safe blocks) and reframed it as B′; the maintainer answered 「8346 B′」. Freshness gate: the body and all thirteen comments (newest 6046696492, triage's box intake) were re-read before this record; nothing newer exists. Thread-read: 6046696492.

    The ruling

    B′. A document is a page with a print declaration; no new template type.

    • The template is a page. The body vocabulary is the page's own blocks (field blocks, static tables of all rows, text, images), which AI already authors through the Studio author's metadata-authoring skill and the console already renders and previews. The page gains an optional print declaration: paper size and orientation, margins, header and footer blocks, page numbering, page-break hints, mapped to print CSS (@page, repeated table heads, break-inside). A printable block subset is defined and linted: a block that virtualises rows or lays out responsively is refused inside a print page, with pins.
    • Preview is the page itself in the console, plus the browser's print preview; no new visual designer: Studio's page designer is the designer, the print declaration is a few fields on it.
    • M1, zero server: the print declaration, the printable subset and its lint, and a "print" action on the record page that opens the browser's print preview of the bound page.
    • M2, the archive: a render service with one engine driver, headless Chromium, which renders the same page under the requesting user's principal (a short-lived credential; no system principal) and stores the PDF as a sys_file attached to the record, downloadable and usable as an email or flow attachment; a deployment without the driver configured reports it loudly at startup, it does not fail silently at the first request. ⛔ No in-process layout driver: a page model needs a browser to render.
    • The 17.0 retirement stands and its text changes. List export does not regain 'pdf'; the retirement sentence in packages/spec/src/ui/list-view-export-options.ts ("PDF export itself was declined as NOT PLANNED") is rewritten to point at the print page, because it is no longer true. The zero-reader DocumentTemplateSchema, DocumentSchema and ESignatureConfigSchema retire in v18 under ADR-0049 with ADR-0087 entries, so that "template" means one thing.
    • ⛔ Not taken: A (restoring 'pdf' on list export: it prints lists, not documents, and reverses a recorded retirement against ADR-0087's forever-chain), B (a second authoring vocabulary beside pages; withdrawn by the seat on the maintainer's proposal), C as the end state (browser printing alone yields no archive), a Word-upload template kind, batch merging or packaged downloads (not in v18 unless a customer names them), and the in-process pdfmake-class driver.

    Why B′ on the first axis: one authoring vocabulary for every rendered surface, screen and paper alike; the two platforms whose model this follows (Odoo, Salesforce) have kept it for a decade. The second axis holds: the port customer's five document kinds (invoice, weighbridge ticket, delivery order, official letter, monthly report) are record-plus-lines documents a print page expresses. The third: the page's blocks are validated at publish today and the declaration adds a few checked fields. The fourth: M1 adds almost no parts; M2 adds one driver.

    State

    • needs-user-decision → pm:queue in this act; enhancement · priority:p2 · domain:spec · target:v18 unchanged, no assignee. The domain:spec seat claims this card as the design parent and splits it, spec first: ① spec, the print declaration on PageSchema, the printable subset and its lint, the export-options text, the three retirements; ② objectui, the print CSS mapping, the printable blocks, the record page's print action (domain:ui); ③ the render service, the Chromium driver and the service-storage in-process archive entry (domain:services); ④ the record-page "generate PDF" action and the attachment on the record. Each card carries its refusal cases and the contract review; the Studio author's skill learns the print page when ① lands. Body: Ruled: line added in the same act.

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 (the design parent, per the ruling 6051470224: "The domain:spec seat claims this card as the design parent and splits it, spec first") · 2026-10-08T03:22Z
    Session: session_01RPo7FUd6bSnAfkWMAKi848
    Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-8346-pdf-generation-design-round (the measurement round's empty branch; no code lands on the parent)
    Worktree: none (the parent carries no code)
    Domain: domain:spec
    Seat: domain:spec#3 (seat post #18883)
    File surface: none on the parent. Each split card declares its own at its claim.
    Container & model: n/a — a design parent; each split card is dispatched on its own claim
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: 6051470224
    Serial constraints cleared: n/a for the parent. The split follows the ruling's order:

    • ① spec, filed in this act's round as its own card: the print declaration on PageSchema, the printable block subset and its lint, the list-view-export-options.ts retirement sentence, and the three zero-reader retirements (DocumentTemplateSchema, DocumentSchema, ESignatureConfigSchema) under ADR-0049 with ADR-0087 entries.
    • ② objectui (domain:ui), ③ the render service (domain:services) and ④ the record-page "generate PDF" action and attachment: each is filed once ①'s contract shape is on a PR, so their cards cite the declared shape rather than predict it.

    The parent stays pm:dispatched with this seat until ①–④ close. Each split card carries Part of #8346 and its own refusal cases and contract review.

  7. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    The split is filed. pm:dispatched → pm:blocked on the remaining cards, and this seat's claim is released

    domain:spec seat 3 (#18883) · os-justin · session session_01RPo7FUd6bSnAfkWMAKi848 · 2026-10-08T10:52Z · holder of claim 6051507786 (the design parent), which this act releases.

    The ruling of record 6051470224 (letter B′) ordered this seat to claim the card as the design parent and split it, spec first. The split is complete:

    card what where state
    ① #22158 PageSchema.print, the printable subset and its lint, the export-options sentence, the four document schemas retired domain:spec landed: PR #22193 → 326a90a4a3 (6058198073)
    ② objectstack-ai/objectui#11958 the print CSS mapping, the 13 printable blocks on paper (and the record:line_items cap under print), the record page's "print" action, objectui's leftover "NOT PLANNED" sentence domain:ui pm:on-hold, Restart-when: an installable @objectstack/spec exports PagePrintSchema
    ③ #22269 the render service, one headless-Chromium driver, the user's principal, the sys_file archive, loud at startup domain:services pm:queue
    ④ #22270 the record-page "generate PDF" action and the attachment domain:spec, inherited; triage routes it when it unblocks pm:blocked, Blocked-by: #22269
    skill #22271 the Studio author's skill learns the print page; actions.md's per-project /pdf endpoint goes domain:skills (Tier H) pm:queue

    This card: pm:dispatched → pm:blocked, unassigned. The body gains one Blocked-by: line per open card. When all of them have closed, the unlock scan brings the card back so it can be closed as delivered.

    Release: session_01RPo7FUd6bSnAfkWMAKi848 · why: the design parent's split is filed and its spec half has landed · to: pm:blocked, unassigned.

  8. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    Contributor

    Grade p2 → p1: the maintainer names PDF for 18.0 GA · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-15T23:14Z

    Source. The maintainer said 「PDF 要赶 18.0 正式版」 in this session's live chat on 2026-10-15. Their standing rule is 「发版本优先的都p1」, so this parent and its open children move to p1:

    Unchanged: the ruling B′ (6051470224), the lane (domain:spec, the design parent) and target:v18.

    The critical path to GA is serial. #22269's build runs in stages: S1 the archive entry, S2 the credential mint (with its spec part), then S3 + S4, the print service with both placements (6204149204). #22270 follows it. Cloud's adoption card for the remote placement follows the option's name.

    ⛔ Not a claim.

  9. added
    priority:p1High: required for production / M2
    and removed on Oct 15, 2026
  10. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    Contributor

    Re-blocked on the chain's new child objectui#12906 · stays p1 · target:v18 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-17T19:00Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  11. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    Contributor

    Re-blocked: objectui#12906 closed; two print-path p1s found since · stays p1 · target:v18 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-17T22:55Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  12. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    Contributor

    Unlock: pm:blocked → pm:queue for close-out · p1 · target:v18 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-18T07:47Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  13. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 · 2026-10-18T07:58Z · the design parent's close-out, per triage 6247300226: read ruling B′ (6051470224) against main and the console pin, then close this card or name what remains
    Session: session_019oFY6Hi35J7atvgVFnmoaK
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: none (a seat reading; no code lands on the parent)
    Worktree: none
    Domain: domain:spec
    Seat: domain:spec#2 (seat post #18549)
    File surface: none on the parent. A reading of origin/main and of objectui at the .objectui-sha pin.
    Container & model: n/a — a seat reading by this session, no dev is dispatched
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: 6247300226
    Serial constraints cleared: n/a for a reading. No open PR or in-flight claim names this card.


    Generated by Claude Code

  14. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    Contributor

    Close-out reading: ruling B′ against main and the console pin · stays open, pm:dispatched → pm:blocked on #24283 · domain:spec seat 2 · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-18T08:03Z

    ⛔ Not a dispatch. This is the design parent's close-out that triage's unlock 6247300226 asked for, under claim 6247391043.

    Readings: objectstack origin/main 93b4acb57e; objectui origin/main, deepened 400 commits. Every negative ancestry reading has a control leg: the pin's own ~3 answers exit 0.

    ruling B′ item reading on main / at the pin state
    ① print declaration on PageSchema, the printable subset and its lint PagePrintSchema in packages/spec/src/ui/page.zod.ts (5 hits); rule print-page-block-unprintable in packages/lint/src/validate-print-page-blocks.ts (#22158, PR #22193) ✅ on main
    The 17.0 retirement stands; its sentence points at the print page packages/spec/src/ui/list-view-export-options.ts:40–:43 names the print page and allowPrinting ✅ on main
    DocumentTemplateSchema, DocumentSchema, ESignatureConfigSchema retire only the tombstone comment in packages/spec/src/data/index.ts:244–:255 remains under packages/spec/src outside tests and conversions (control: PageSchema in 61 files) ✅ on main
    M2: one headless-Chromium driver, the user's principal, the sys_file archive, loud at startup packages/services/service-print/src/chromium-driver.ts, print-service.ts, print-service-plugin.ts (#22269, closed 2026-10-17) ✅ on main
    ④ the record-page "generate PDF" action and the attachment #22270, PR #24194 → d9a749bae0 ✅ on main
    The Studio author's skill learns the print page #22271 (closed 2026-10-08) ✅
    ② objectui: print CSS, printable blocks, the record print action 2312bd9a6 (objectui PR #12356) and 263e9ddcc (objectui PR #12729) are in the pin ✅ in the pin
    objectui#12906: a print record page draws its header actions (④ reachable) d637be321 (objectui PR #12921) ❌ not in the pin
    objectui#12915: read-only line items print display text 8523e81db (objectui PR #12923) ❌ not in the pin
    objectui#12924: boolean / file line cells print in the session language 32b492b67 (objectui PR #12937) ❌ not in the pin

    Release: session session_019oFY6Hi35J7atvgVFnmoaK · claim 6247391043 · why: the close-out reading is done, and the remainder is #24283 · to: pm:blocked on Blocked-by: #24283 (body rewritten in this act; the seven closed targets move to a prose line), assignee cleared.


    Generated by Claude Code

  15. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    Contributor

    Unlock: pm:blocked → pm:queue · p1 · target:v18 · domain:spec (the close-out) · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-18T13:02Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  16. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    Contributor

    Close-out reading 2: ruling B′ against main and the console pin · closed completed · domain:spec seat 2 · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-18T13:40Z

    ⛔ Not a dispatch. This is the second close-out reading that triage's unlock (6250236268, 2026-10-18T13:02Z) asked for. The first reading (6247425715) left one row open: the console pin.

    Readings, taken in this act: objectstack origin/main .objectui-sha = 0707b4ae37120d45d6401170666a0b76cb7a3546, moved by PR #24311 (#24283, closed completed). In an objectui clone fetched in this act, git merge-base --is-ancestor COMMIT 0707b4ae37 returns exit 0 for each fix below. The control leg, the pin's own ~3, also returns exit 0.

    ruling B′ item reading state
    ① the print declaration, the printable subset and its lint PagePrintSchema in packages/spec/src/ui/page.zod.ts (5 hits on main); #22158 closed completed ✅ on main
    The 17.0 retirement text, and the three retired schemas unchanged from reading 1 (6247425715) ✅ on main
    M2: the headless-Chromium render service and the archive packages/services/service-print/src/chromium-driver.ts on main; #22269 closed completed ✅ on main
    ④ the record-page "generate PDF" action and the attachment #22270 closed completed ✅ on main
    The Studio author's skill learns the print page #22271 closed completed ✅
    ② objectui: print CSS, printable blocks, the record print action 2312bd9a6 and 263e9ddcc are ancestors of the pin ✅ in the pin
    objectui#12906 (a print record page draws its header actions) d637be321 is an ancestor of the pin; the issue is closed completed ✅ in the pin (was ❌)
    objectui#12915 (read-only line items print display text) 8523e81db is an ancestor of the pin; the issue is closed completed ✅ in the pin (was ❌)
    objectui#12924 (boolean and file line cells print in the session language) 32b492b67 is an ancestor of the pin; the issue is closed completed ✅ in the pin (was ❌)

    Why this closes now, and does not wait for a publish. Ruling B′ (6051470224) names the parts (①–④, M1, M2) and their split. It sets no condition on a release. This repository consumes objectui by pin, and for a pin-consumed repo the pin move is the installable point. npm next reads 18.0.0-next.3 in this act (npm view @objectstack/spec dist-tags). An installed next build carries the chain from next.4 on. That publish is the maintainer's act (#24168), and this card does not track it.

    State: closed completed; pm:queue is removed in the same act. domain:spec, target:v18 and the type labels stay. The earlier claim (6247391043) ended with reading 1's release to pm:blocked. This reading needed no new claim: it closes the card in one write.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions