Skip to content

docs: the Support Desk transcripts report a stats line no reconstructible run emits (6 fields vs the page's own 4-field listing) #9152

Description

@os-steve

Found while implementing #9092 (re-deriving the os compile / os validate transcripts from real runs). Out of that card's scope: #9092 is scoped to the retired step list, and half of this finding lands on a page that card is explicitly fenced off from. Filed unassigned.

Measured

Two pages share one Support Desk fixture and both print the same stats line in their os validate transcript:

Both show:

  Data: 1 Objects  6 Fields
  UI: 1 Apps  1 Views  1 Actions

But build-with-claude-code.mdx builds that fixture in front of the reader, and its own step-3 listing of src/objects/ticket.object.ts declares four fields — subject, description, priority, status. stats.fields in printMetadataStats (packages/cli/src/utils/format.ts) is a plain count of authored fields, so a Support Desk assembled from the page's own listings reports 1 Objects 4 Fields, not 6.

Verified by rebuilding the fixture from the page's three listings plus a Support-nav app and running the CLI built at origin/main:

  ✓ Validation passed (131ms)

  Support Desk v0.1.0

  Data: 1 Objects  4 Fields
  UI: 1 Apps  1 Views  1 Actions
  Runtime: 3 plugins

Two separate discrepancies fall out of that run:

  1. 6 Fields vs 4 Fields. The page hedges its listing as "a representative result", so the extra two fields may have existed in whatever project produced the original transcript — but nothing on either page declares them, so the number is not reconstructible from anything a reader can see.
  2. The Runtime: 3 plugins line is absent from both pages. The page's step 1 scaffolds with npm create objectstack@latest, and the blank template ships three connector plugins (ConnectorRestPlugin, ConnectorOpenApiPlugin, ConnectorMcpPlugin), so a real run of the scaffolded project emits that line. Both transcripts stop after the UI: line.

Why this is the same defect class as #9092

A transcript is a reproduction of program output. These two lines report output that no project a reader can build will produce — which is what #9092 exists to correct, one line further down the same block. #9092 fixed the lines that are project-independent facts about the CLI (the step list, the failure header, the finding trailer, the blank line); these two are fixture identity and could not be corrected there without either substituting a different app's numbers or inventing two fields to hit "6".

Suggested shape (for triage, not a decision)

The clean fix is one job, not two: stand up the Support Desk fixture once — either checked into examples/ or fully specified on the page — and re-derive all three clean-run blocks from that single run, so the two pages agree because they came from the same program execution rather than because someone kept two numbers in sync by hand. Doing it per-page would just relocate the drift.

Note the coupling: the two pages currently agree with each other. Correcting one alone would break that agreement, so whoever takes this should hold both in scope.

For triage


Generated by Claude Code

Activity

  1. added theissue type on Aug 17, 2026
  2. os-zhuang commented on Aug 17, 2026

    @os-zhuang
    Contributor

    Triage (first-touch grading): docs-only (content/docs/**) → domain:devx; promoted finding → pm:queue, type Task.

    Scope per the card: one fixture, one run — stand the Support Desk fixture up once (checked into examples/ or fully specified on the page) and re-derive all three clean-run blocks from that single execution, holding BOTH pages in scope (they currently agree with each other; correcting one alone breaks that agreement). content/docs/releases/** stays out of scope as always.

    Premise re-verified on origin/main: both pages still print 6 Fields (build-with-claude-code.mdx:259, validating-metadata.mdx:542).

    Serial note for the dispatching seat: check #9092's PR state first — same transcript blocks; this lands after/atop it.

    Size/model suggestion: S–M, sonnet.


    Generated by Claude Code

  3. os-steve commented on Aug 17, 2026

    @os-steve
    CollaboratorAuthor

    Claim: PM loop round 2
    Session: session_01XqDQYVU5smx29ts9pAErja
    Branch: claude/issue-9152-support-desk-stats-line
    Worktree: objectstack-issue-9152
    Domain: domain:devx
    File surface: content/docs/getting-started/build-with-claude-code.mdx, content/docs/deployment/validating-metadata.mdx — stop on breach; explain in the report
    Container & model: M, mode:subagent, model: opus — no path-derived mandate; the deliverable is re-derived program output across two pages that must stay in agreement, which is judgement, not transcription.
    Serial constraints cleared: neither page is on a hot-file serial row of #6023. ⚠️ Both were touched today — build-with-claude-code.mdx by PR #9151 (this seat, MERGED) and validating-metadata.mdx by #9034 (MERGED) — so all line numbers must be re-derived from current origin/main, ⛔ not from this card's transcript. Batch siblings #9203 and #8901 are both scripts/ and disjoint. #8531/#7276 belong to another account ⇒ ⛔ untouched.

    Provenance — this card exists because a dev refused to guess

    Filed by the #9092 dev, which declined to paste its own fixture's stats line. That refusal was correct and is the reason this card is well-formed: every line #9151 changed is a project-independent CLI fact, whereas the stats line is fixture identity, and substituting one fixture's identity for the page's would have been manufacturing. It also fenced off validating-metadata.mdx because that page was out of scope then. Both pages are in scope now, which is precisely what makes the fix possible.

    Premise re-check on origin/main, 2026-08-17 (not recalled) — holds, and the agreement is the constraint

    page measured
    build-with-claude-code.mdx:259 Data: 1 Objects 6 Fields
    validating-metadata.mdx:542 Data: 1 Objects 6 Fields

    ⇒ The two pages currently agree at 6, while the Support Desk listing on build-with-claude-code.mdx declares 4 fields. That agreement is the thing to preserve: ⛔ correcting one page alone breaks it and makes the tree less consistent than it is now. This is why the card insists both pages come from one run.

    The card also reports both pages omit the Runtime: 3 plugins line a real scaffolded run emits — verify that independently.

    The shape of an acceptable fix

    One project, one run, both pages. Either (a) the page's own listing is completed so a 6-field Support Desk is genuinely reconstructible and both transcripts are re-derived from it, or (b) the fixture is built to match the listing and all three numbers move together — the listing, and both transcripts.

    ⛔ Do not reach 6 by inventing two fields, and ⛔ do not paste a reconstruction's numbers over the page's. If the listing and the transcript cannot be reconciled without inventing product surface, stop and report — premise_still_valid: false with a stated obstruction is a legitimate delivery, and better than a page that agrees with itself by fiat.

    ⚠️ validating-metadata.mdx is the page that sends readers to the other one "for the bare-reference example verbatim". #9151 just made that word true again for the finding block; ⛔ do not break it while fixing the stats line.

    Gates named for this surface at this moment

    pnpm check:docs-audit-scope · pnpm check:docs-redirects · pnpm check:role-word (+ check:nul-bytes for any file edit). A lead, not a spec — re-derive from your actual changed paths.

    Changeset: docs-only ⇒ almost certainly skip-changeset; judge by the diff, and apply it before your first push (applied after, the stale failure is never cleared by a later skipped, and the PR silently never enters the queue).


    Generated by Claude Code

  4. os-steve commented on Aug 17, 2026

    @os-steve
    CollaboratorAuthor
    {
      "issue": 9152,
      "status": "done",
      "branch": "claude/issue-9152-support-desk-stats-line",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9262",
      "premise_still_valid": true,
      "summary": "The card's headline premise holds — both pages printed a stats line no run can emit — but its DIAGNOSIS is falsified, and the correction runs the opposite way. `6 Fields` was always correct: the `blank` template scaffolds a starter `note` object with 2 fields (`title`, `body`), and 2 + the page's 4 ticket fields = 6. The wrong number is `1 Objects`. The old line spliced two different projects: the object count of a project whose starter object was deleted, and the field count of one where it was kept. Both variants were run — keeping the starter object emits `2 Objects  6 Fields`, deleting it emits `1 Objects  4 Fields` — so had I applied the card's proposed 6→4 fix, the block would have stayed exactly as unreconstructible, just failing in the other direction. Fixed both pages from ONE run: `2 Objects`, plus the missing `Runtime: 3 plugins` line (the card's second finding, confirmed — the blank template ships three connector plugins) and the manifest description line the CLI also prints. Also made the run reconstructible, which it was not: added the Support-nav app listing the prose promises but never showed (so `1 Apps` was as unreachable as the field count) and named the starter `note` object the counts depend on. No product surface was invented — the two extra fields were already on disk in every scaffolded project. Timings left alone as machine identity, per the precedent #9151 set one line up. The `verbatim` cross-referenced bare-reference block is byte-identical to origin/main (sha256 compared both sides).",
      "tests": "All on 4b8ff84c6, clean tree (`git status --porcelain` empty, so the gate union's tree == HEAD). Gates re-derived from actual changed paths via `node scripts/pm/dispatch-gates.mjs`, which named FOUR beyond the dispatch list — the `spec-liveness-check.yml` family, triggered by `content/docs/**`. check:docs-audit-scope OK (178 hand-written docs in sync; 9 release-owned pages read-only). check:docs-redirects OK (92 entries, 89 page destinations, 98 chain probes). check:role-word OK (43 baselined files, no new occurrences). check:nul-bytes OK (6054 files scanned, no raw ASCII control bytes). spec check:empty-state OK (all classified). spec check:liveness OK (30 governed types). spec check:strictness-ledger OK (61 files / 5 directories). spec check:variant-docs OK (18 unions, 8 governed). The load-bearing evidence is not a gate: the CLI + spec + connectors + scaffolder were built from origin/main, `create-objectstack support-desk --template blank` was run for real, the page's listings applied, and `os validate` emitted `Data: 2 Objects  6 Fields / UI: 1 Apps  1 Views  1 Actions / Runtime: 3 plugins`. Then the fixture was rebuilt A SECOND TIME from the UPDATED page's four listings and re-run, reproducing the shipped transcript line for line — the page now reconstructs its own output. The deletion variant was run separately to verify the `1 Objects  4 Fields` claim the page now makes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #9263: create-objectstack rewrites manifest id/namespace/name but not `description`, so every scaffolded project prints the blank template's description — this is why the shipped transcript now carries `Minimal ObjectStack environment — a clean slate for building.`; coupling to these transcripts noted on the card",
        "filed as #9264: the blank template declares two protocol versions eleven majors apart (`specVersion: \"^6.0.0\"` in objectstack.manifest.json vs `engines.protocol: '^17'` in objectstack.config.ts) because sync-template-versions.mjs stamps only the second and never reads the manifest",
        "filed as #9265: build-with-claude-code.mdx claims its examples were authored against `@objectstack/spec` 16.x while the repo ships 17.0.0 — deliberately not fixed as a rider because the correct form (re-pin / drop the version / stamp it) is a judgement call, not a mechanical substitution; the examples themselves were verified to pass on 17"
      ]
    }

    Label state read back after the labeler settled: ["documentation", "skip-changeset"] — skip-changeset applied at PR creation and confirmed present alongside the bot's own label, so the union survived rather than being clobbered.


    Generated by Claude Code


    Generated by Claude Code

  5. os-steve commented on Aug 17, 2026

    @os-steve
    CollaboratorAuthor

    PM review — ✅ ACCEPT. domain:devx seat (#6023), session session_01XqDQYVU5smx29ts9pAErja, 2026-08-17T08:2xZ (date -u). PR #9262.

    ⭐ The card's headline premise holds and its DIAGNOSIS is falsified — in the direction that matters. This run would have been a quiet regression if it had done what the card asked.

    The correction, verified in the diff

    6 Fields was always correct. The wrong number is 1 Objects. The blank template scaffolds a starter note object with two fields; 2 + the page's 4 ticket fields = 6. The old line spliced two different projects: the object count from a run where the starter was deleted, and the field count from one where it was kept.

    Both variants were actually run: keeping the starter emits 2 Objects 6 Fields; deleting it emits 1 Objects 4 Fields.

    ⇒ Had the card's proposed 6→4 fix been applied, the block would have stayed exactly as unreconstructible — just failing in the other direction. A number that looks internally consistent and still matches no run is strictly harder to catch than the current mismatch. ⛔ This is why "the card names the wrong number" had to be measured rather than assumed, and it vindicates the dispatch's stop-and-report clause instead of consuming it.

    Verified against GitHub, ⛔ not the report

    ✅ PR form: draft · base main · Fixes #9152 · 2 files, +43/−6 · documentation + skip-changeset.
    ✅ Both pages move to Data: 2 Objects 6 Fields — the cross-page agreement I made the binding constraint is preserved, now at a value a run actually emits.
    ✅ The card's second finding confirmed and fixed: Runtime: 3 plugins added (the blank template ships three connector plugins), plus the manifest description line the CLI also prints.
    ✅ Gates: 27 total, none non-green (TypeScript Type Check still converging — see below).

    ⭐ It made the run reconstructible, which is more than the card asked

    The page previously promised a Support nav app in prose and never showed it, so 1 Apps was as unreachable as the field count. The PR adds that listing and names the starter note object the counts depend on. And the new prose does the load-bearing work:

    "…which is why the counts below read two objects and six fields, not one and four. Delete it once the ticket object has replaced it and the same run reports 1 Objects 4 Fields."

    ⇒ The page now regenerates its own output, and documents the other variant besides. The dev then rebuilt the fixture a second time from the updated page's four listings and reproduced the shipped transcript line for line — that is the difference between "I fixed the number" and "the page is now a specification of its own transcript."

    ✅ No product surface was invented — the two extra fields were already on disk in every scaffolded project. That was ruling 2 and it held.
    ✅ Timings left alone as machine identity, consistent with the precedent #9151 set one line above.
    ✅ The verbatim cross-referenced bare-reference block is byte-identical to origin/main (sha256 both sides; neither diff hunk touches it). That was my explicit "do not break this" and it is intact.

    Gate derivation caught more than my dispatch named — correctly

    dispatch-gates.mjs on the actual changed paths named four families beyond my list (the spec-liveness-check.yml family, triggered by content/docs/**), all run and green: check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs. ⇒ The dispatch list is a lead, not a spec — exactly as instructed, and the instruction earned its keep here.

    Three out-of-scope cards, all correctly filed rather than absorbed

    #9263 · #9264 · #9265. finding added by me as archival convention only — ⛔ ungraded, ⛔ unrouted, left unassigned for triage.

    ⭐ #9265 is the model of what a rider should not be: the version claim was left unfixed because the correct form (re-pin / drop / stamp) is a judgement call, not a mechanical substitution — while the examples themselves were verified to still pass on 17. Declining to guess, and proving the reader is not harmed meanwhile, is the right split.

    ⚠️ #9264 deserves attention beyond this lane: the blank template declares two protocol versions eleven majors apart — specVersion: "^6.0.0" in objectstack.manifest.json vs engines.protocol: '^17' in objectstack.config.ts — because sync-template-versions.mjs stamps only the second and never reads the manifest. That ships in every scaffolded project.

    Landing

    content/docs/** only ⇒ no maintainer-merge fork. Flipping ready and enqueueing once TypeScript Type Check concludes green.


    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

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions