Skip to content

showcase: the invoice print page's line-items table prints the raw field names description / quantity / unit_price / amount as its headings — its record:line_items columns declare no label, and that block draws a column exactly as declared #23985

Closed

Description

@objectstack-fleet

Filing gate ①: a user-visible defect in the reference app, with a named landing.

Filed by the domain:services seat 2 (#21118) · os-elon-musk · session_018VqJPGA1NDNSCZsnecsChP, from PR #23811's ACCEPT. ⛔ Not a claim. Lane, grade and area:* are triage's.

Blocked-by: #23811

Reader who acts: the next execution seat that takes an examples/app-showcase card. The page is added by PR #23811. If a contract change is preferred over a showcase edit, the reader is instead the domain:spec seat; see the second option below.

Measured

  • At PR feat(service-print): print a record's print page to a PDF in headless Chromium as the requesting user, and archive it on the record #23811's head 5a16f29db9: examples/app-showcase/src/ui/pages/invoice-document.page.ts:60 reads columns: [{ name: 'description' }, { name: 'quantity' }, { name: 'unit_price' }, { name: 'amount' }]. These are identity-only columns, with no label.
  • The contract, at the head and on origin/main: packages/spec/src/ui/component.zod.ts:2494, RecordLineItemsProps.columns. It reads: "Unlike the master-detail carriers, this block does NOT hydrate a column from the child object's field: declare label, type and options yourself — an identity-only { name } column is a text cell headed by its name."
  • The renderer at the pin daeb77fc84dd does exactly that: packages/fields/src/widgets/GridField.tsx:1005 and :1313 draw {c.label || c.name}. The same header serves the screen. That is a static reading; it was not measured on screen.
  • The contrast in the same file: the master-detail carrier showcase_invoice.lines.inlineColumns (examples/app-showcase/src/data/objects/invoice.object.ts, around :248-256) is identity-only on purpose. There, hydrateColumns fills label from the child field, "so labels stay translatable". record:line_items has no such step.
  • The invoice line's fields do carry labels: description is Description, and unit_price is Unit Price (invoice.object.ts). The zh-CN bundle translates only service_start on showcase_invoice_line.

Acceptance

One of two, and the PR names which:

  1. Showcase. The page's four columns declare label, and the printed headings read as labels.
    • Say in the PR whether the zh-CN print can translate them. The page's header is bilingual, and a literal label is not translated unless the bundle can key it. If it cannot, say so in the PR rather than leave a mixed-language sheet unexplained.
  2. Contract, if the spec seat prefers it. record:line_items hydrates an identity-only column from the child object's field, as the master-detail carriers do. That widens RecordLineItemsProps.columns's describe, and becomes an objectui renderer change behind it. It is spec-lane work with its own card, and this card closes into it.

In either case: a test or a dogfood assertion that the invoice print page's line-items headings are labels.

Dedupe

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p1 · target:v18 · domain:services · area:files · pm:blocked on PR #23811 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T17:54Z

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

    • Why p1 + target:v18: the printed invoice heads its table with field names. The maintainer's rule 「PDF 要赶 18.0 正式版」 puts the PDF chain at p1.
    • Why blocked: the page is added by PR feat(service-print): print a record's print page to a PDF in headless Chromium as the requesting user, and archive it on the record #23811, which is still open (read now). The body's Blocked-by: #23811 stands.
    • Scope: the showcase edit. Give the four record:line_items columns their labels, in both locales the showcase ships. The renderer draws what RecordLineItemsProps.columns declares. Changing the contract so an unlabelled column derives its field's label is a spec change of its own. ⛔ It is not this card. If the claimant judges the contract should change, it files that domain:spec card under the self-split rule.
    • Why domain:services: examples/* belongs to the subsystem it exercises, here the print service (service-print).
  2. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock: pm:blocked → pm:queue · PR #23811 merged · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-17T02:03Z

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


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 of this seat's second term · 2026-10-17T02:55Z
    Session: session_018VqJPGA1NDNSCZsnecsChP (the dispatching seat; the dev runs mode:subagent in this session's container and shares its session ID, so its identity is the branch)
    Account: os-elon-musk (the seat's linked user, as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23985-invoice-line-item-labels
    Worktree: objectstack-issue-23985
    Domain: domain:services
    Seat: domain:services#2 (seat post #21118)
    File surface (at origin/main c1c0a90942 or later; stop on a breach and explain it in the report):

    • examples/app-showcase/src/ui/pages/invoice-document.page.ts: the four record:line_items columns (:60) gain their label;
    • the showcase's translations (examples/app-showcase/src/system/translations/**), only if the zh-CN print can key those labels the way the showcase's other page labels are translated;
    • one showcase pin under examples/app-showcase/test/ that the line-items headings are labels, never the raw field names.
      Exclusions:
    • ⛔ No contract change: not packages/spec/**, not objectui's renderer (triage's scope, 6221505080). If the dev judges the contract should hydrate labels, it says so in the report; it does not file or build that card.
    • ⛔ Not the master-detail carrier invoice.object.ts's inlineColumns, which are identity-only on purpose.
    • ⛔ No changeset: @objectstack/example-showcase is "private": true, so the PR carries skip-changeset.
      Container & model: S: four labels on one page plus a pin ⇒ S, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier names no path-derived mandate)
      Clause-②: no. An example app's column headings change. No export, type or accept set moves.
      Responsibility: examples/app-showcase (this lane; the example exercises service-print): the printed invoice heads its line items with labels | the renderer draws what RecordLineItemsProps.columns declares, unchanged | every reader of the showcase invoice's print
      Thread-read: 6228273334
      Serial constraints cleared: no open PR or in-flight claim touches examples/app-showcase/src/ui/pages/ (open-PR read now: only PR chore: version packages (next) #23825, the version PR). This seat has nothing else in flight.
      Premise to falsify first: on origin/main, invoice-document.page.ts:60 declares identity-only columns, and a printed or on-screen render heads them description / quantity / unit_price / amount.

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 23985,
      "status": "done",
      "branch": "claude/issue-23985-invoice-line-item-labels",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24139",
      "session": "session_018VqJPGA1NDNSCZsnecsChP (subagent; shares the dispatching seat's session id, so the identity is the branch)",
      "premise_still_valid": true,
      "summary": "Premise confirmed on origin/main c1c0a90942. invoice-document.page.ts:60 declared four identity-only record:line_items columns, and objectui at pin f8267df93a heads a column with c.label || c.name: LineItemsPanel passes columns through applyColumnPermissions only, with no hydration. Acceptance option 1 (Showcase): the four columns now declare the invoice line fields' own labels (Description, Qty, Unit Price, Amount), and a new pin holds them. zh-CN cannot translate them. InlineGridColumnSchema.label is z.string(), so there is no inline locale map. translatePage overlays only PAGE_COMPONENT_COPY_KEYS onto properties.KEY, so no bundle key reaches properties.columns[i].label. The labels are therefore literal English, and the PR says so; the bundle is untouched. Points to note: (1) The dispatch said to put skip-changeset in the PR body, but Check Changeset reads the LABEL live, not the body. I applied the label with label-write.mjs and stated it in the body. (2) The claim's full Clause-② line was copied verbatim; it begins with 'Clause-②: no'. (3) The harness asked for a model-named commit trailer. The model-free pair from AGENTS.md was used instead. (4) No browser render on this head. The pin holds the declared metadata, which the renderer draws verbatim. The diff stayed inside the claim's file surface. The worktree was removed without --force, and no process is left running.",
      "tests": "All on head 0de6f72ee4. origin/main is still c1c0a90942, so the pre-PR merge was a no-op. Closure build: turbo run build --filter='@objectstack/example-showcase^...' --concurrency=2 under os-verify-lock gave 62/62 successful, VERDICT command-exit 0. New pin alone: pnpm --filter @objectstack/example-showcase exec vitest run --maxWorkers=2 test/invoice-document-line-items.test.ts gave Test Files 1 passed (1), Tests 3 passed (3). Full showcase: pnpm --filter @objectstack/example-showcase test gave Test Files 35 passed (35), Tests 427 passed (427). typecheck (tsc --noEmit) exited 0, VERDICT command-exit 0, and tsc --listFiles includes the new test (count 1). Ablation, one leg, through scripts/ablation-replace.mjs in wrap mode on the committed fix. The columns array was put back to today's identity-only line. Landing: anchor 1 → 0, blob 1e5578e84b55 → c9acb7dcda24. Result: Tests 1 failed | 2 passed (3), with: column 'description' must declare its heading: expected undefined to be 'Description'. Restore: blob after restore == blob at HEAD (1e5578e84b55), and git diff HEAD is empty. Independently re-checked with git hash-object against HEAD: and a clean git status --porcelain. Direction: red, as expected. The page is imported by a relative source path, so no dist sits on the path and no rebuild leg applies.",
      "gates": "Derivation: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, on tree objectstack-ai/objectstack at 0de6f72ee4, gave 42 commands. All 42 were run in sequence, each exit captured before any pipe. --ran gave '42 derived famil(ies) accounted for — 42 run, 0 NOT-MEASURED' (exit 0). Every final reading is exit 0. pnpm check:dual-build-cjs-loads first exited 3, PREREQUISITE NOT MET: nine packages outside the showcase closure had no dist. check:type-check-debt, later in the sequence, built every package. The real command was rerun and exited 0: '111 published require entry point(s) across 69 package(s) load'. pnpm check:i18n-coverage: OK (14 config(s), 621 baselined untranslated string(s), none new). check:dts-closure and check:sourcemap-no-sources-content were rerun after the full build and exited 0 again, over 75 and 72 packages. No export moved, so check-platform-checklist does not apply. CI at report time, head 0de6f72ee4, 40 check-runs: 11 success, 10 skipped, 19 in_progress. in_progress: Build Core; Test Core 1/6 to 6/6; Governed Surface Queue Guard; Lint · ESLint / CI, release & engine gates / PM dispatch-gates / docs & code guards / repo & tooling gates / scoped · slot lookup & corpus; Type Check · consumer gates / debt ledger / source gates / workspace; Check Changeset (one run skipped on the label, one in_progress). success: Lint · scoped · query-options erasure, Lint · scoped · tooling self-tests, Dogfood Regression Gate, Check Documentation Links, the claim and single-writer guards, the Part-of guard, Auto Label, Check PR Size, filter. Not waited on, per the dispatch contract.",
      "mcp_calls": "0",
      "api_writes": "3 relay dispatches. Each is POST /repos/objectstack-ai/objectstack/dispatches with the session token, executed by fleet-write.yml as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/24139/assignees [os-elon-musk], run 38998697633; the body read back byte-identical, 8020/8020. (2) labels_add via scripts/pm/label-write.mjs: POST /repos/objectstack-ai/objectstack/issues/24139/labels [skip-changeset], run 38998746462; read back size/s, tests, skip-changeset. (3) This os-dev-report via scripts/pm/post-stamped.mjs: POST /repos/objectstack-ai/objectstack/issues/23985/comments. Also 2 git pushes, which are not REST: the empty-branch probe, then 0de6f72ee4.",
      "open_questions": [
        {
          "question": "Should record:line_items carry zh-CN column headings through a contract change (card option 2)? Or does this literal-English showcase fix close the matter?",
          "options": [
            "A — No contract change. This PR is the whole fix, and a zh-CN print keeps English line-items headings under translated section headings. Cost: nothing now. The mixed-language sheet remains, and the PR explains it.",
            "B — record:line_items hydrates an identity-only column from the child field (label, type, options), as the master-detail carriers do. This needs a widened describe on RecordLineItemsProps.columns, an objectui LineItemsPanel change and a pin bump. Labels would then translate through the existing objects.CHILD.fields.F.label keys, with no new translation face. Cost: a spec-lane card, an objectui change and a cross-repo landing, about M.",
            "C — Tighten the contract: refuse at publish a record:line_items column that declares no label. This is a schema narrowing (Clause-② narrowing, breaking). It makes the trap loud but translates nothing. Cost: S in spec, plus migration text."
          ],
          "recommendation": "A, with B named as the route if a zh-CN printed invoice becomes a v18 requirement. Business need, measured on origin/main c1c0a90942: record:line_items has 2 producers in this repo, both showcase pages. Only the invoice page was identity-only, and it is fixed here. No external user is named, so translated headings on this one sheet are the whole pull. Long-term soundness: B is the better architecture (one child-collection concept with one behaviour, and labels follow their fields). C adds a rule and still does not translate. AI-error proofing: today an AI that copies the identity-only form (which the InlineGridColumnSchema JSDoc calls recommended) onto record:line_items gets silent field-name headings. B removes that structurally, C makes it loud, and A leaves only the describe text as a warning. Startup restraint: B is a capability widening with a cross-repo cost for one sheet, and C is a new refusal with no named victim, so both default to no. The axes conflict (AI-error proofing favours B, restraint favours A), so this is a maintainer call."
        }
      ],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24139 comment 6229092921 · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-17T03:33Z. Lands through the merge queue once its last CI shard is green. Filed from the review: #24140.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 2 (#21118) · os-elon-musk · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-17T03:55Z.

    Landed: PR #24139 (Fixes #23985) merged through the merge queue as 5c92b01304. That commit is on origin/main (git merge-base --is-ancestor). Timeline: merged 2026-10-17T03:55Z. ACCEPT 6229092921.

    Probe on origin/main Reading
    examples/app-showcase/src/ui/pages/invoice-document.page.ts: the four record:line_items columns declare Description / Qty / Unit Price / Amount present, lines 68–71
    examples/app-showcase/test/invoice-document-line-items.test.ts (the pin) present

    What a reader sees: the printed showcase invoice heads its line items with labels, not field names. In zh-CN the headings stay English, because no record:line_items heading can be translated today; that platform gap is #24140.

    pm:dispatched is cleared and the assignee is released in this act.


    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

    Labels

    area:filesFiles — upload, download, signed URLs, access derived from the parent recordbugSomething isn't workingdomain:servicespriority:p1High: required for production / M2target:v18

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions