Skip to content

docs: expressions/architecture guides teach text.value for expressions — switch all 23 occurrences to content, the ruled sole evaluation channel (objectstack#13670 option 2) #7015

Description

@huangyiirene

Provenance: maintainer ruling 2026-08-31 (director seat summon #7, decision batch #17, verbatim 「同意」 on objectstack#13670's corrected recommendation, option 2). Ruling record: objectstack#13670 comment 5478581056. Filed by the director seat, session session_01KGtaLpkW1mycWgkbSb3H6t.

The ruled facts

  • text's intended evaluation channel is content, solely. content: "${…}" evaluates today (its own memo leg); value: "${…}" renders the literal (no carriage row, and none will be added — ruled).
  • The guides teach the broken spelling: 23 occurrences of type: "text" with ${…} in value across the authored corpus, concentrated in content/docs/guide/expressions.md and architecture.md (census recorded on objectstack#13670; counts from PR feat(react): evaluate the spec-declared expression-bindable text keys #6981's discussion).

The change

Switch every one of the 23 occurrences from "value": "…${…}…" to "content": "…${…}…". Mechanical; each example then works as taught, zero spec change.

⚠️ Ordering constraint (from the ruling, binding)

objectui#4795's second ruled half (build-time rejection of ${…} outside the carriage map) will legitimately reject text.value — this docs fix must land BEFORE or in the same stroke as that gate, or the gate reddens the platform's own 23 examples.

Not this card

  • Adding a text: ['value'] carriage row — ⛔ ruled out.
  • Retiring the TextSchema.value fallback spelling itself — separate ADR-0049 enforce-or-remove card (filed alongside this one).
  • The spec-side docblock known-omitted entry — that is objectstack#13670's own dispatch scope.

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 1, 2026
  2. self-assigned this
    on Sep 1, 2026
  3. os-warren commented on Sep 1, 2026

    @os-warren
    Collaborator

    🔒 Claimed — domain:ui execution seat (objectui)

    • Session: session_012wwHa4aaFybxXrfmfHioDM
    • Branch: claude/issue-7015-expressions-docs-content
    • Build tier: opus (⚠️ Fable 5 quota exhausted; standing maintainer exemption 2026-08-13 「fable 如果用完了,可以用 opus」 lowers the build tier only)

    Not clause ②, and that is why it was selected this round. The card states zero spec change — this edits authored prose in content/docs/guide/** and changes nothing about what the platform accepts or refuses. No CONTRACT_REVIEW_TIER is owed, so the exhausted quota cannot strand it.

    Selected as time-sensitive, not merely ripe. This card is the prerequisite half of the ordering constraint in its own body: objectui#4795's second ruled half will legitimately reject text.value, and if that gate lands first it reddens the platform's own 23 examples. Dispatching this ahead of the gate is the whole point of the constraint.

    Domain label added. The card carried documentation + pm:queue with no domain label; content/docs/guide/expressions.md teaching text schema authoring is squarely this seat's surface, so I labelled it domain:ui rather than leaving it un-routed.

    ⚠️ Also recording the small trap in the body for the implementer: the census says the 23 occurrences are "concentrated in" two files — concentrated in ≠ only in. The census gets re-measured against the whole authored corpus, not those two files.


    Generated by Claude Code

  4. os-warren commented on Sep 1, 2026

    @os-warren
    Collaborator

    os-dev-report

    {
      "issue": 7015,
      "status": "done",
      "branch": "claude/issue-7015-expressions-docs-content",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7114",
      "premise_still_valid": true,
      "files_changed": [
        "content/docs/guide/expressions.md",
        "content/docs/guide/architecture.md",
        "content/docs/guide/schema-rendering.md",
        "content/docs/guide/dashboard-filters.md",
        "content/docs/blocks/block-schema.mdx",
        ".changeset/7015-text-expression-content-channel.md"
      ],
      "census_before": {
        "ruled_scope_text_nodes_with_expression_in_value": 29,
        "by_file": {
          "content/docs/guide/expressions.md": 20,
          "content/docs/guide/architecture.md": 3,
          "content/docs/guide/schema-rendering.md": 3,
          "content/docs/blocks/block-schema.mdx": 2,
          "content/docs/guide/dashboard-filters.md": 1
        },
        "total_raw_hits_in_content_docs": 35,
        "note": "The card's 23 is exactly expressions.md (20) + architecture.md (3) — the two files it names. 'Concentrated in' was not 'only in': three further files carry six more."
      },
      "census_after": {
        "ruled_scope_text_nodes_with_expression_in_value": 0,
        "content_with_expression_lines": 33,
        "note": "33 = the 29 ruled + the 4 typeless judgment-call fragments."
      },
      "occurrences_fixed": 29,
      "occurrences_excluded_with_reasons": [
        "expressions.md:388 — type 'progress', not a text node. Excluded as an unruled behaviour edit. Also outside the carriage map, so it likely renders a literal too.",
        "expressions.md:453 — type 'input', not a text node. Same reasoning.",
        "skills/ — 7 hits, ALL correct as written and destructive to change: schema-expressions.md and page-builder.md use text+value as DELIBERATE counter-examples teaching this exact rule, each paired with its correct 'content' fix; the remainder are 'statistic', which genuinely declares a value carriage row. Zero files under skills/ touched.",
        "packages/plugin-dashboard/README.md — type 'metric-card', non-text. Reported in issue 7115.",
        "packages/react/README.md — type 'input', non-text. Reported in issue 7115.",
        "README.md (root) x4 — type 'stat-card', non-text AND registered nowhere. Different defect class; filed as issue 7115.",
        "packages/react/CHANGELOG.md — generated release artifact, never hand-edited.",
        "packages/**/src/** — source and tests that deliberately exercise the broken spelling (they pin the defect).",
        "schema-rendering.md:34 — text node whose value holds NO expression; renders correctly via the fallback. Out of scope by the card's own rule.",
        "examples/schema-catalog/.../javascript-editor.json — FALSE POSITIVE: the ${name} is JavaScript template-literal source inside a code-editor widget's sample content, not an ObjectUI expression."
      ],
      "changeset_grade_and_gate_verdict": "EMPTY frontmatter (not patch, not major). Gate verdict, verbatim: 'No source or published contract of a released package changed in this range, so no changeset is owed.' exit 0 — i.e. the gate says a changeset was NOT OWED at all, since 0 of the changed files are published package source. Included anyway as an explicit statement of release intent, a first-class form per AGENTS.md. check-changeset-no-major exit 0.",
      "gates": [
        { "name": "check-doc-snippet-types", "exit": 0, "verdict": "green", "raw": "Every covered documentation snippet compiles against the built types. Semantic phase: 272 of 272 block(s) judged, 0 failed. Self-controls fired: sentinel produced 1 diagnostic (TS2305), undeclared produced 1 diagnostic (TS2307), resolution resolved to packages/types/dist/index.d.ts." },
        { "name": "check-doc-snippet-types (FIRST attempt, unbuilt tree)", "exit": 2, "verdict": "NOT_MEASURED", "raw": "PRECONDITION NOT MET (exit 2) — The snippet program was NOT run: the packages it resolves against are not built. Resolved by building the 21-package closure (turbo: 32 successful, 32 total), then re-running for the green above." },
        { "name": "check-doc-component-types", "exit": 0, "verdict": "green", "raw": "Every documented component type is registered. 185 doc files, 1068 code blocks, 900 type literals — counts byte-identical to the pre-edit baseline, confirming no type literal moved." },
        { "name": "check-doc-fence-languages", "exit": 0, "verdict": "green", "raw": "every TypeScript block in 224 document(s) is fenced ts/tsx/typescript" },
        { "name": "check-doc-links", "exit": 0, "verdict": "green", "raw": "Links are valid across 17 scan roots." },
        { "name": "check-control-bytes", "exit": 0, "verdict": "green", "raw": "OK (scanned 5893 tracked text file(s); skipped 85 binary)." },
        { "name": "check-changeset-presence", "exit": 0, "verdict": "green", "raw": "No source or published contract of a released package changed in this range, so no changeset is owed." },
        { "name": "check-changeset-no-major", "exit": 0, "verdict": "green", "raw": "No changeset declares a major bump." },
        { "name": "vitest: 6 gate suites (doc-component-types, doc-fence-languages, doc-links, changeset-presence, changeset-no-major, control-bytes)", "exit": 0, "verdict": "green", "raw": "Test Files 6 passed (6) / Tests 266 passed (266). Root-relative invocation via pnpm exec vitest run, under the shared verify lock (VERDICT command-exit 0)." }
      ],
      "controls_used": [
        "Zero-hit control: the after-census zero for text-node value-with-expression is reported beside the SAME query in the SAME run still returning 2 live hits (expressions.md:388 progress, :453 input). The query is demonstrably not dead.",
        "Positive control on the injected text: content-with-expression counted 33 lines post-edit (29 ruled + 4 typeless), matching the predicted total exactly.",
        "stat-card zero-registration control: the sweep returning 0 registrations for 'stat-card' was paired with 'statistic', which returned a real registration at statistic.tsx:64.",
        "Gate self-controls: check-doc-snippet-types printed its own sentinel (TS2305) and undeclared (TS2307) controls firing, proving the harness can detect errors rather than vacuously passing.",
        "Baseline control: check-doc-component-types / fence-languages / control-bytes were each run BEFORE the edits and were green, so the post-edit greens are differential, not first-sightings.",
        "On-disk mutation proof: every replacement batch was verified by git diff --stat plus a per-file grep count of the INJECTED text, never by the editing tool's exit code.",
        "Tooling control: an early ripgrep sweep used 'rg -ril', where -r is ripgrep's --replace, not 'recursive' — it silently rewrote matched text to 'il' in the output. Caught and re-run with -l; noted because the mangled output was superficially readable."
      ],
      "assumptions_falsified": [
        "FALSIFIED — the count is 29 in ruled scope, not 23, across 5 files not 2. The 23 is reproducible as exactly expressions.md (20) + architecture.md (3); schema-rendering.md (3), block-schema.mdx (2) and dashboard-filters.md (1) were missed by the original census.",
        "FALSIFIED — not every hit is a text node. Of 35 raw hits in content/docs: 29 are declared type text, 2 are non-text (progress, input), and 4 declare NO type at all. The typeless class was not anticipated by either zone.",
        "FALSIFIED — this is NOT purely prose. 3 of the 29 in-scope edits live in COMPILED snippets: architecture.md:151 (tsx) and block-schema.mdx:306/311 (ts, inside a 'const cardBlock: BlockSchema' annotation). All five touched files are gate-COVERED by check-doc-snippet-types. Risk resolved to nil because all three reach BaseSchema, whose [key: string]: any index signature accepts both spellings identically — then measured green.",
        "FALSIFIED — the dispatch instruction that this docs card OWES an empty-frontmatter changeset. The gate's own verdict is that no changeset is owed at all, because 0 changed files are published package source. One was added anyway as a statement of intent, but the instruction's premise was wrong.",
        "CONFIRMED (premise) — the card's premise holds exactly. text.tsx:51 renders {schema.content || schema.value}, so value IS read back but never evaluated, putting the literal ${...} on screen. content is evaluated unconditionally with no type gate at all."
      ],
      "open_questions": [
        {
          "question": "The 4 TYPELESS fragments in expressions.md Best Practices (lines 591/596/619/624) declare no type, so they fall outside the ruling's literal wording ('on type: text nodes'), yet both halves of both correct/incorrect pairs used value as the carriage. I fixed them, ISOLATED in commit 39d3eb151 so the decision is reversible in one command.",
          "options": [
            "A — keep them (current state): the carriage map answers the empty set for an ABSENT type, so value evaluated for nothing there; the 'correct' half of each pair was teaching a non-working spelling. content is right whatever type a reader substitutes, since its evaluation leg has no type gate.",
            "B — revert commit 39d3eb151 to hold strict ruling scope, and let a follow-up card adjudicate the typeless class."
          ],
          "recommendation": "A, because the fix is behaviourally free (docs-only, and content is universally evaluated) while B knowingly leaves 4 occurrences that objectui#4795's second ruled half would legitimately redden — which is the exact ordering-constraint harm this card exists to prevent. But it IS a scope judgment, so it is quarantined in its own commit for a one-command revert."
        },
        {
          "question": "Does the ordering constraint still hold as stated? The card says objectui#4795's build-time rejection must not land first or it reddens the platform's own examples.",
          "options": [
            "A — the constraint is now discharged for content/docs: text-node value-with-expression is 0 there.",
            "B — it is NOT fully discharged repo-wide: 2 non-text occurrences (progress, input) and the package/root READMEs still carry ${...} in keys outside the carriage map, and would redden a strict gate."
          ],
          "recommendation": "B is the accurate reading and the PM should route it. This card discharged the text-node half only; the non-text carriage question is unruled and was deliberately not touched. Filed the README half as issue 7115."
        }
      ],
      "out_of_scope_findings": [
        "filed as #7115: the root README's flagship dashboard example teaches `stat-card`, registered nowhere (4 occurrences), and the root README.md sits outside EVERY doc gate's scan surface (which is content/docs plus packages/NAME/README.md) — so the most-read authored file in the repo is the one teaching surface no gate reads. Includes the metric-card / input README observations."
      ],
      "mcp_calls": 7
    }

    Generated by Claude Code

  5. os-warren commented on Sep 1, 2026

    @os-warren
    Collaborator

    🟢 LANDED — closing as completed

    PR #7114 merged to main as 71d83a6b1 at 2026-09-01T02:15:08Z:
    "docs: teach content as text's expression channel, not value (29 occurrences, not 23)".

    Seat state cleared: pm:dispatched removed, assignee cleared.

    Landing verified by mutual control on the landed tree

    The subject and the deliberately-retained residue serve as each other's control, so neither number rests on a query nobody checked:

    probe, content/docs before (03380aa14) after (71d83a6b1)
    "content" holding an expression — subject 0 30
    "value" holding an expression — residue 32 2

    Both moved in the predicted direction, by the predicted magnitude, in the same query. The surviving 2 are exactly expressions.md's progress and input nodes — non-text, deliberately untouched, and now routed on #7115. A zero on one row would have been ambiguous; the pair is not.

    ⇒ The ruled deliverable is complete: no authored text node under content/docs carries an expression in value.

    ⭐ The card's census was low, and finding that was worth more than the fix

    The card said 23, "concentrated in" two files. The real ruled scope was 29 across five files — and the implementer showed why the 23 was wrong rather than merely replacing it: it reproduces exactly as expressions.md (20) + architecture.md (3), identifying it as a two-file census rather than a miscount. schema-rendering.md (3), block-schema.mdx (2) and dashboard-filters.md (1) had simply never been looked at.

    ⚠️ "Concentrated in" is not "only in". That phrase now reads, on this lane, as a flag that the census was scoped rather than exhaustive.

    Three PM assumptions falsified, one of them a plain error of mine

    • The count — 29, not 23; five files, not two.
    • "Docs-only, therefore prose" — three of the 29 edits live in compiled ts/tsx snippets under check-doc-snippet-types. The risk resolved to nil (both spellings reach BaseSchema's [key: string]: any index signature), but it was measured, not assumed, and the gate is green in CI.
    • ⛔ My changeset instruction was wrong. I said this card owed an empty-frontmatter changeset. The gate's verdict: "No source or published contract of a released package changed in this range, so no changeset is owed." One was included anyway as an explicit statement of release intent, which is a first-class form here. I later gave the opposite wrong answer on finding(app-shell): PALETTE_EXCLUSIONS['app:launcher'] says "the app shell renders it, not a page" — PR #7091 makes that clause false #7092 — so the standing rule is now simply: run the gate and quote its verdict; never predict it.

    Scope discipline: the highest-value edit was one not made

    The 7 hits under the published skills surface were left untouched. schema-expressions.md and page-builder.md use text + value as deliberate counter-examples teaching this exact rule, each paired with its correct content fix. A mechanical sweep would have destroyed the lesson and reported a clean census — success-shaped, and wrong.

    ⚠️ What this card did NOT discharge

    The ordering constraint against objectui#4795's gate is satisfied only for text nodes under content/docs. Still outstanding, and routed on #7115: the two non-text in-corpus occurrences, the root README.md (which teaches stat-card, registered nowhere, and sits outside every doc gate's scan surface), and the plugin-dashboard / react READMEs. The underlying fork — carriage rows for non-text types, or stop authoring expressions in those keys — is unruled and is the maintainer's.


    Generated by Claude Code

  6. removed their assignment
    on Sep 1, 2026
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

    documentationImprovements or additions to documentationdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions