Skip to content

[finding] check:yaml-examples cannot see inside a page component's properties — a tagged component example is green whatever its props say #13338

Description

@claude

Found while doing the corpus clean-up in #13266 (PR #13337), which tags page-component
examples with os:check-yaml PageComponentSchema for the first time.

The gap

check:yaml-examples validates a tagged block with the declared schema's safeParse.
For a page or a page component that closes the node's own keys — and stops there.
PageComponentSchema.properties is an open record of string to unknown
(packages/spec/src/ui/page.zod.ts), and Zod strictness does not recurse, so nothing
inside properties is validated by the tag
. component.zod.ts's own header states
the same fact from the other side: "strictness does NOT recurse, so it closes the
component node's own keys and leaves everything under properties unchecked. Nothing
dispatches ComponentPropsMap by type."

This matters specifically for docs, because properties is where almost all of a
component example's authored content lives. A tagged fence reads as "this example is
verified against the live schema", and for the half an author is most likely to get
wrong, it is not.

Measured instance

While authoring the #13266 rewrites I wrote this Customer 360 component:

type: record:detailsproperties:
columns: 2fields: [name, status, industry, employee_count, website, phone]

RecordDetailsProps.columns is the enum "1"|"2"|"3"|"4" — the numeric 2 is
refused. Dispatching ComponentPropsMap['record:details'].safeParse(properties) by
hand reports:

columns: Invalid option: expected one of "1"|"2"|"3"|"4"

but os:check-yaml page over the whole page is green with that fence in place. I
caught it only because I ran the props dispatch as a separate hand audit; the committed
gate would not have. The PR ships columns: '2'.

Why it is not just "tag the props schema instead"

An author could tag a props-only fence (os:check-yaml RecordDetailsProps), but that
loses the thing the example is teaching — a component in its page context, which is the
shape a reader copies. The two claims are not interchangeable.

Prior art for the fix, already in the tree

The dispatch this gate lacks exists elsewhere: the #5068 authoring-rules gate dispatches
ComponentPropsMap by type and refuses a misspelled prop, which is why the map's rows
are maintained per component (see the objectBlockHistory / #8691 / #8744 notes in
component.zod.ts). So the verdict is available and owned — check-yaml-examples.ts
simply does not call it.

Sketch, not a prescription: after a block validates against its declared schema, walk the
parsed value for nodes carrying a type string plus properties, and dispatch
ComponentPropsMap[type] where a row exists (skipping unregistered/custom.* types the
way the authoring gate already does). That keeps this gate's "no vocabulary of its own"
posture — the verdict stays the props schema's own message, verbatim.

Scope note

Filed unassigned, recording only. Deliberately not fixed inside #13266: that card's
ruled scope is docs content triage on one page, and this is a change to a gate's
validation depth affecting every page that ever tags a component — a separate decision
about how much a green os:check-yaml should be allowed to claim.

Re-check

pnpm --filter @objectstack/spec run check:yaml-examples

is green on content/docs/protocol/objectui/layout-dsl.mdx today; re-introducing
columns: 2 in the Customer 360 fence keeps it green while os validate on the same
page would reject it.


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
     blocks
    (function() {
    function addCopyButtons() {
    document.querySelectorAll('pre code').forEach(function(codeBlock) {
    if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
    codeBlock.parentElement.setAttribute('data-copy-added', 'true');
    var btn = document.createElement('button');
    btn.textContent = 'Copy';
    btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
    btn.onmouseover = function() { this.style.opacity = '1'; };
    btn.onmouseout = function() { this.style.opacity = '0.7'; };
    btn.onclick = function() {
    navigator.clipboard.writeText(codeBlock.textContent).then(function() {
    btn.textContent = 'Copied!';
    setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
    });
    };
    codeBlock.parentElement.style.position = 'relative';
    codeBlock.parentElement.appendChild(btn);
    });
    }
    addCopyButtons();
    // Re-run on dynamic content
    var observer = new MutationObserver(addCopyButtons);
    observer.observe(document.body, { childList: true, subtree: true });
    })();
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    [finding] check:yaml-examples cannot see inside a page component's `properties` — a tagged component example is green whatever its props say · Issue #13338 · objectstack-ai/objectstack · GitHub
    Skip to content

    [finding] check:yaml-examples cannot see inside a page component's properties — a tagged component example is green whatever its props say #13338

    Description

    @claude

    Found while doing the corpus clean-up in #13266 (PR #13337), which tags page-component
    examples with os:check-yaml PageComponentSchema for the first time.

    The gap

    check:yaml-examples validates a tagged block with the declared schema's safeParse.
    For a page or a page component that closes the node's own keys — and stops there.
    PageComponentSchema.properties is an open record of string to unknown
    (packages/spec/src/ui/page.zod.ts), and Zod strictness does not recurse, so nothing
    inside properties is validated by the tag
    . component.zod.ts's own header states
    the same fact from the other side: "strictness does NOT recurse, so it closes the
    component node's own keys and leaves everything under properties unchecked. Nothing
    dispatches ComponentPropsMap by type."

    This matters specifically for docs, because properties is where almost all of a
    component example's authored content lives. A tagged fence reads as "this example is
    verified against the live schema", and for the half an author is most likely to get
    wrong, it is not.

    Measured instance

    While authoring the #13266 rewrites I wrote this Customer 360 component:

    type: record:detailsproperties:
    columns: 2fields: [name, status, industry, employee_count, website, phone]

    RecordDetailsProps.columns is the enum "1"|"2"|"3"|"4" — the numeric 2 is
    refused. Dispatching ComponentPropsMap['record:details'].safeParse(properties) by
    hand reports:

    columns: Invalid option: expected one of "1"|"2"|"3"|"4"
    

    but os:check-yaml page over the whole page is green with that fence in place. I
    caught it only because I ran the props dispatch as a separate hand audit; the committed
    gate would not have. The PR ships columns: '2'.

    Why it is not just "tag the props schema instead"

    An author could tag a props-only fence (os:check-yaml RecordDetailsProps), but that
    loses the thing the example is teaching — a component in its page context, which is the
    shape a reader copies. The two claims are not interchangeable.

    Prior art for the fix, already in the tree

    The dispatch this gate lacks exists elsewhere: the #5068 authoring-rules gate dispatches
    ComponentPropsMap by type and refuses a misspelled prop, which is why the map's rows
    are maintained per component (see the objectBlockHistory / #8691 / #8744 notes in
    component.zod.ts). So the verdict is available and owned — check-yaml-examples.ts
    simply does not call it.

    Sketch, not a prescription: after a block validates against its declared schema, walk the
    parsed value for nodes carrying a type string plus properties, and dispatch
    ComponentPropsMap[type] where a row exists (skipping unregistered/custom.* types the
    way the authoring gate already does). That keeps this gate's "no vocabulary of its own"
    posture — the verdict stays the props schema's own message, verbatim.

    Scope note

    Filed unassigned, recording only. Deliberately not fixed inside #13266: that card's
    ruled scope is docs content triage on one page, and this is a change to a gate's
    validation depth affecting every page that ever tags a component — a separate decision
    about how much a green os:check-yaml should be allowed to claim.

    Re-check

    pnpm --filter @objectstack/spec run check:yaml-examples
    

    is green on content/docs/protocol/objectui/layout-dsl.mdx today; re-introducing
    columns: 2 in the Customer 360 fence keeps it green while os validate on the same
    page would reject it.


    Generated by Claude Code

    Metadata

    Metadata

    Assignees

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [finding] check:yaml-examples cannot see inside a page component's `properties` — a tagged component example is green whatever its props say · Issue #13338 · objectstack-ai/objectstack · GitHub
      Skip to content

      [finding] check:yaml-examples cannot see inside a page component's properties — a tagged component example is green whatever its props say #13338

      Description

      @claude

      Found while doing the corpus clean-up in #13266 (PR #13337), which tags page-component
      examples with os:check-yaml PageComponentSchema for the first time.

      The gap

      check:yaml-examples validates a tagged block with the declared schema's safeParse.
      For a page or a page component that closes the node's own keys — and stops there.
      PageComponentSchema.properties is an open record of string to unknown
      (packages/spec/src/ui/page.zod.ts), and Zod strictness does not recurse, so nothing
      inside properties is validated by the tag
      . component.zod.ts's own header states
      the same fact from the other side: "strictness does NOT recurse, so it closes the
      component node's own keys and leaves everything under properties unchecked. Nothing
      dispatches ComponentPropsMap by type."

      This matters specifically for docs, because properties is where almost all of a
      component example's authored content lives. A tagged fence reads as "this example is
      verified against the live schema", and for the half an author is most likely to get
      wrong, it is not.

      Measured instance

      While authoring the #13266 rewrites I wrote this Customer 360 component:

      type: record:detailsproperties:
      columns: 2fields: [name, status, industry, employee_count, website, phone]

      RecordDetailsProps.columns is the enum "1"|"2"|"3"|"4" — the numeric 2 is
      refused. Dispatching ComponentPropsMap['record:details'].safeParse(properties) by
      hand reports:

      columns: Invalid option: expected one of "1"|"2"|"3"|"4"
      

      but os:check-yaml page over the whole page is green with that fence in place. I
      caught it only because I ran the props dispatch as a separate hand audit; the committed
      gate would not have. The PR ships columns: '2'.

      Why it is not just "tag the props schema instead"

      An author could tag a props-only fence (os:check-yaml RecordDetailsProps), but that
      loses the thing the example is teaching — a component in its page context, which is the
      shape a reader copies. The two claims are not interchangeable.

      Prior art for the fix, already in the tree

      The dispatch this gate lacks exists elsewhere: the #5068 authoring-rules gate dispatches
      ComponentPropsMap by type and refuses a misspelled prop, which is why the map's rows
      are maintained per component (see the objectBlockHistory / #8691 / #8744 notes in
      component.zod.ts). So the verdict is available and owned — check-yaml-examples.ts
      simply does not call it.

      Sketch, not a prescription: after a block validates against its declared schema, walk the
      parsed value for nodes carrying a type string plus properties, and dispatch
      ComponentPropsMap[type] where a row exists (skipping unregistered/custom.* types the
      way the authoring gate already does). That keeps this gate's "no vocabulary of its own"
      posture — the verdict stays the props schema's own message, verbatim.

      Scope note

      Filed unassigned, recording only. Deliberately not fixed inside #13266: that card's
      ruled scope is docs content triage on one page, and this is a change to a gate's
      validation depth affecting every page that ever tags a component — a separate decision
      about how much a green os:check-yaml should be allowed to claim.

      Re-check

      pnpm --filter @objectstack/spec run check:yaml-examples
      

      is green on content/docs/protocol/objectui/layout-dsl.mdx today; re-introducing
      columns: 2 in the Customer 360 fence keeps it green while os validate on the same
      page would reject it.


      Generated by Claude Code

      Metadata

      Metadata

      Assignees

      Labels

      Type

      No type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [finding] check:yaml-examples cannot see inside a page component's `properties` — a tagged component example is green whatever its props say · Issue #13338 · objectstack-ai/objectstack · GitHub
        Skip to content

        [finding] check:yaml-examples cannot see inside a page component's properties — a tagged component example is green whatever its props say #13338

        Description

        @claude

        Found while doing the corpus clean-up in #13266 (PR #13337), which tags page-component
        examples with os:check-yaml PageComponentSchema for the first time.

        The gap

        check:yaml-examples validates a tagged block with the declared schema's safeParse.
        For a page or a page component that closes the node's own keys — and stops there.
        PageComponentSchema.properties is an open record of string to unknown
        (packages/spec/src/ui/page.zod.ts), and Zod strictness does not recurse, so nothing
        inside properties is validated by the tag
        . component.zod.ts's own header states
        the same fact from the other side: "strictness does NOT recurse, so it closes the
        component node's own keys and leaves everything under properties unchecked. Nothing
        dispatches ComponentPropsMap by type."

        This matters specifically for docs, because properties is where almost all of a
        component example's authored content lives. A tagged fence reads as "this example is
        verified against the live schema", and for the half an author is most likely to get
        wrong, it is not.

        Measured instance

        While authoring the #13266 rewrites I wrote this Customer 360 component:

        type: record:detailsproperties:
        columns: 2fields: [name, status, industry, employee_count, website, phone]

        RecordDetailsProps.columns is the enum "1"|"2"|"3"|"4" — the numeric 2 is
        refused. Dispatching ComponentPropsMap['record:details'].safeParse(properties) by
        hand reports:

        columns: Invalid option: expected one of "1"|"2"|"3"|"4"
        

        but os:check-yaml page over the whole page is green with that fence in place. I
        caught it only because I ran the props dispatch as a separate hand audit; the committed
        gate would not have. The PR ships columns: '2'.

        Why it is not just "tag the props schema instead"

        An author could tag a props-only fence (os:check-yaml RecordDetailsProps), but that
        loses the thing the example is teaching — a component in its page context, which is the
        shape a reader copies. The two claims are not interchangeable.

        Prior art for the fix, already in the tree

        The dispatch this gate lacks exists elsewhere: the #5068 authoring-rules gate dispatches
        ComponentPropsMap by type and refuses a misspelled prop, which is why the map's rows
        are maintained per component (see the objectBlockHistory / #8691 / #8744 notes in
        component.zod.ts). So the verdict is available and owned — check-yaml-examples.ts
        simply does not call it.

        Sketch, not a prescription: after a block validates against its declared schema, walk the
        parsed value for nodes carrying a type string plus properties, and dispatch
        ComponentPropsMap[type] where a row exists (skipping unregistered/custom.* types the
        way the authoring gate already does). That keeps this gate's "no vocabulary of its own"
        posture — the verdict stays the props schema's own message, verbatim.

        Scope note

        Filed unassigned, recording only. Deliberately not fixed inside #13266: that card's
        ruled scope is docs content triage on one page, and this is a change to a gate's
        validation depth affecting every page that ever tags a component — a separate decision
        about how much a green os:check-yaml should be allowed to claim.

        Re-check

        pnpm --filter @objectstack/spec run check:yaml-examples
        

        is green on content/docs/protocol/objectui/layout-dsl.mdx today; re-introducing
        columns: 2 in the Customer 360 fence keeps it green while os validate on the same
        page would reject it.


        Generated by Claude Code

        Metadata

        Metadata

        Assignees

        Labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [finding] check:yaml-examples cannot see inside a page component's `properties` — a tagged component example is green whatever its props say · Issue #13338 · objectstack-ai/objectstack · GitHub
          Skip to content

          [finding] check:yaml-examples cannot see inside a page component's properties — a tagged component example is green whatever its props say #13338

          Description

          @claude

          Found while doing the corpus clean-up in #13266 (PR #13337), which tags page-component
          examples with os:check-yaml PageComponentSchema for the first time.

          The gap

          check:yaml-examples validates a tagged block with the declared schema's safeParse.
          For a page or a page component that closes the node's own keys — and stops there.
          PageComponentSchema.properties is an open record of string to unknown
          (packages/spec/src/ui/page.zod.ts), and Zod strictness does not recurse, so nothing
          inside properties is validated by the tag
          . component.zod.ts's own header states
          the same fact from the other side: "strictness does NOT recurse, so it closes the
          component node's own keys and leaves everything under properties unchecked. Nothing
          dispatches ComponentPropsMap by type."

          This matters specifically for docs, because properties is where almost all of a
          component example's authored content lives. A tagged fence reads as "this example is
          verified against the live schema", and for the half an author is most likely to get
          wrong, it is not.

          Measured instance

          While authoring the #13266 rewrites I wrote this Customer 360 component:

          type: record:detailsproperties:
          columns: 2fields: [name, status, industry, employee_count, website, phone]

          RecordDetailsProps.columns is the enum "1"|"2"|"3"|"4" — the numeric 2 is
          refused. Dispatching ComponentPropsMap['record:details'].safeParse(properties) by
          hand reports:

          columns: Invalid option: expected one of "1"|"2"|"3"|"4"
          

          but os:check-yaml page over the whole page is green with that fence in place. I
          caught it only because I ran the props dispatch as a separate hand audit; the committed
          gate would not have. The PR ships columns: '2'.

          Why it is not just "tag the props schema instead"

          An author could tag a props-only fence (os:check-yaml RecordDetailsProps), but that
          loses the thing the example is teaching — a component in its page context, which is the
          shape a reader copies. The two claims are not interchangeable.

          Prior art for the fix, already in the tree

          The dispatch this gate lacks exists elsewhere: the #5068 authoring-rules gate dispatches
          ComponentPropsMap by type and refuses a misspelled prop, which is why the map's rows
          are maintained per component (see the objectBlockHistory / #8691 / #8744 notes in
          component.zod.ts). So the verdict is available and owned — check-yaml-examples.ts
          simply does not call it.

          Sketch, not a prescription: after a block validates against its declared schema, walk the
          parsed value for nodes carrying a type string plus properties, and dispatch
          ComponentPropsMap[type] where a row exists (skipping unregistered/custom.* types the
          way the authoring gate already does). That keeps this gate's "no vocabulary of its own"
          posture — the verdict stays the props schema's own message, verbatim.

          Scope note

          Filed unassigned, recording only. Deliberately not fixed inside #13266: that card's
          ruled scope is docs content triage on one page, and this is a change to a gate's
          validation depth affecting every page that ever tags a component — a separate decision
          about how much a green os:check-yaml should be allowed to claim.

          Re-check

          pnpm --filter @objectstack/spec run check:yaml-examples
          

          is green on content/docs/protocol/objectui/layout-dsl.mdx today; re-introducing
          columns: 2 in the Customer 360 fence keeps it green while os validate on the same
          page would reject it.


          Generated by Claude Code

          Metadata

          Metadata

          Assignees

          Labels

          Type

          No type

          Projects

          No projects

            Milestone

            No milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [finding] check:yaml-examples cannot see inside a page component's `properties` — a tagged component example is green whatever its props say · Issue #13338 · objectstack-ai/objectstack · GitHub
            Skip to content

            [finding] check:yaml-examples cannot see inside a page component's properties — a tagged component example is green whatever its props say #13338

            Description

            @claude

            Found while doing the corpus clean-up in #13266 (PR #13337), which tags page-component
            examples with os:check-yaml PageComponentSchema for the first time.

            The gap

            check:yaml-examples validates a tagged block with the declared schema's safeParse.
            For a page or a page component that closes the node's own keys — and stops there.
            PageComponentSchema.properties is an open record of string to unknown
            (packages/spec/src/ui/page.zod.ts), and Zod strictness does not recurse, so nothing
            inside properties is validated by the tag
            . component.zod.ts's own header states
            the same fact from the other side: "strictness does NOT recurse, so it closes the
            component node's own keys and leaves everything under properties unchecked. Nothing
            dispatches ComponentPropsMap by type."

            This matters specifically for docs, because properties is where almost all of a
            component example's authored content lives. A tagged fence reads as "this example is
            verified against the live schema", and for the half an author is most likely to get
            wrong, it is not.

            Measured instance

            While authoring the #13266 rewrites I wrote this Customer 360 component:

            type: record:detailsproperties:
            columns: 2fields: [name, status, industry, employee_count, website, phone]

            RecordDetailsProps.columns is the enum "1"|"2"|"3"|"4" — the numeric 2 is
            refused. Dispatching ComponentPropsMap['record:details'].safeParse(properties) by
            hand reports:

            columns: Invalid option: expected one of "1"|"2"|"3"|"4"
            

            but os:check-yaml page over the whole page is green with that fence in place. I
            caught it only because I ran the props dispatch as a separate hand audit; the committed
            gate would not have. The PR ships columns: '2'.

            Why it is not just "tag the props schema instead"

            An author could tag a props-only fence (os:check-yaml RecordDetailsProps), but that
            loses the thing the example is teaching — a component in its page context, which is the
            shape a reader copies. The two claims are not interchangeable.

            Prior art for the fix, already in the tree

            The dispatch this gate lacks exists elsewhere: the #5068 authoring-rules gate dispatches
            ComponentPropsMap by type and refuses a misspelled prop, which is why the map's rows
            are maintained per component (see the objectBlockHistory / #8691 / #8744 notes in
            component.zod.ts). So the verdict is available and owned — check-yaml-examples.ts
            simply does not call it.

            Sketch, not a prescription: after a block validates against its declared schema, walk the
            parsed value for nodes carrying a type string plus properties, and dispatch
            ComponentPropsMap[type] where a row exists (skipping unregistered/custom.* types the
            way the authoring gate already does). That keeps this gate's "no vocabulary of its own"
            posture — the verdict stays the props schema's own message, verbatim.

            Scope note

            Filed unassigned, recording only. Deliberately not fixed inside #13266: that card's
            ruled scope is docs content triage on one page, and this is a change to a gate's
            validation depth affecting every page that ever tags a component — a separate decision
            about how much a green os:check-yaml should be allowed to claim.

            Re-check

            pnpm --filter @objectstack/spec run check:yaml-examples
            

            is green on content/docs/protocol/objectui/layout-dsl.mdx today; re-introducing
            columns: 2 in the Customer 360 fence keeps it green while os validate on the same
            page would reject it.


            Generated by Claude Code

            Metadata

            Metadata

            Assignees

            Labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [finding] check:yaml-examples cannot see inside a page component's `properties` — a tagged component example is green whatever its props say · Issue #13338 · objectstack-ai/objectstack · GitHub
              Skip to content

              [finding] check:yaml-examples cannot see inside a page component's properties — a tagged component example is green whatever its props say #13338

              Description

              @claude

              Found while doing the corpus clean-up in #13266 (PR #13337), which tags page-component
              examples with os:check-yaml PageComponentSchema for the first time.

              The gap

              check:yaml-examples validates a tagged block with the declared schema's safeParse.
              For a page or a page component that closes the node's own keys — and stops there.
              PageComponentSchema.properties is an open record of string to unknown
              (packages/spec/src/ui/page.zod.ts), and Zod strictness does not recurse, so nothing
              inside properties is validated by the tag
              . component.zod.ts's own header states
              the same fact from the other side: "strictness does NOT recurse, so it closes the
              component node's own keys and leaves everything under properties unchecked. Nothing
              dispatches ComponentPropsMap by type."

              This matters specifically for docs, because properties is where almost all of a
              component example's authored content lives. A tagged fence reads as "this example is
              verified against the live schema", and for the half an author is most likely to get
              wrong, it is not.

              Measured instance

              While authoring the #13266 rewrites I wrote this Customer 360 component:

              type: record:detailsproperties:
              columns: 2fields: [name, status, industry, employee_count, website, phone]

              RecordDetailsProps.columns is the enum "1"|"2"|"3"|"4" — the numeric 2 is
              refused. Dispatching ComponentPropsMap['record:details'].safeParse(properties) by
              hand reports:

              columns: Invalid option: expected one of "1"|"2"|"3"|"4"
              

              but os:check-yaml page over the whole page is green with that fence in place. I
              caught it only because I ran the props dispatch as a separate hand audit; the committed
              gate would not have. The PR ships columns: '2'.

              Why it is not just "tag the props schema instead"

              An author could tag a props-only fence (os:check-yaml RecordDetailsProps), but that
              loses the thing the example is teaching — a component in its page context, which is the
              shape a reader copies. The two claims are not interchangeable.

              Prior art for the fix, already in the tree

              The dispatch this gate lacks exists elsewhere: the #5068 authoring-rules gate dispatches
              ComponentPropsMap by type and refuses a misspelled prop, which is why the map's rows
              are maintained per component (see the objectBlockHistory / #8691 / #8744 notes in
              component.zod.ts). So the verdict is available and owned — check-yaml-examples.ts
              simply does not call it.

              Sketch, not a prescription: after a block validates against its declared schema, walk the
              parsed value for nodes carrying a type string plus properties, and dispatch
              ComponentPropsMap[type] where a row exists (skipping unregistered/custom.* types the
              way the authoring gate already does). That keeps this gate's "no vocabulary of its own"
              posture — the verdict stays the props schema's own message, verbatim.

              Scope note

              Filed unassigned, recording only. Deliberately not fixed inside #13266: that card's
              ruled scope is docs content triage on one page, and this is a change to a gate's
              validation depth affecting every page that ever tags a component — a separate decision
              about how much a green os:check-yaml should be allowed to claim.

              Re-check

              pnpm --filter @objectstack/spec run check:yaml-examples
              

              is green on content/docs/protocol/objectui/layout-dsl.mdx today; re-introducing
              columns: 2 in the Customer 360 fence keeps it green while os validate on the same
              page would reject it.


              Generated by Claude Code

              Metadata

              Metadata

              Assignees

              Labels

              Type

              No type

              Projects

              No projects

                Milestone

                No milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [finding] check:yaml-examples cannot see inside a page component's `properties` — a tagged component example is green whatever its props say · Issue #13338 · objectstack-ai/objectstack · GitHub
                Skip to content

                [finding] check:yaml-examples cannot see inside a page component's properties — a tagged component example is green whatever its props say #13338

                Description

                @claude

                Found while doing the corpus clean-up in #13266 (PR #13337), which tags page-component
                examples with os:check-yaml PageComponentSchema for the first time.

                The gap

                check:yaml-examples validates a tagged block with the declared schema's safeParse.
                For a page or a page component that closes the node's own keys — and stops there.
                PageComponentSchema.properties is an open record of string to unknown
                (packages/spec/src/ui/page.zod.ts), and Zod strictness does not recurse, so nothing
                inside properties is validated by the tag
                . component.zod.ts's own header states
                the same fact from the other side: "strictness does NOT recurse, so it closes the
                component node's own keys and leaves everything under properties unchecked. Nothing
                dispatches ComponentPropsMap by type."

                This matters specifically for docs, because properties is where almost all of a
                component example's authored content lives. A tagged fence reads as "this example is
                verified against the live schema", and for the half an author is most likely to get
                wrong, it is not.

                Measured instance

                While authoring the #13266 rewrites I wrote this Customer 360 component:

                type: record:detailsproperties:
                columns: 2fields: [name, status, industry, employee_count, website, phone]

                RecordDetailsProps.columns is the enum "1"|"2"|"3"|"4" — the numeric 2 is
                refused. Dispatching ComponentPropsMap['record:details'].safeParse(properties) by
                hand reports:

                columns: Invalid option: expected one of "1"|"2"|"3"|"4"
                

                but os:check-yaml page over the whole page is green with that fence in place. I
                caught it only because I ran the props dispatch as a separate hand audit; the committed
                gate would not have. The PR ships columns: '2'.

                Why it is not just "tag the props schema instead"

                An author could tag a props-only fence (os:check-yaml RecordDetailsProps), but that
                loses the thing the example is teaching — a component in its page context, which is the
                shape a reader copies. The two claims are not interchangeable.

                Prior art for the fix, already in the tree

                The dispatch this gate lacks exists elsewhere: the #5068 authoring-rules gate dispatches
                ComponentPropsMap by type and refuses a misspelled prop, which is why the map's rows
                are maintained per component (see the objectBlockHistory / #8691 / #8744 notes in
                component.zod.ts). So the verdict is available and owned — check-yaml-examples.ts
                simply does not call it.

                Sketch, not a prescription: after a block validates against its declared schema, walk the
                parsed value for nodes carrying a type string plus properties, and dispatch
                ComponentPropsMap[type] where a row exists (skipping unregistered/custom.* types the
                way the authoring gate already does). That keeps this gate's "no vocabulary of its own"
                posture — the verdict stays the props schema's own message, verbatim.

                Scope note

                Filed unassigned, recording only. Deliberately not fixed inside #13266: that card's
                ruled scope is docs content triage on one page, and this is a change to a gate's
                validation depth affecting every page that ever tags a component — a separate decision
                about how much a green os:check-yaml should be allowed to claim.

                Re-check

                pnpm --filter @objectstack/spec run check:yaml-examples
                

                is green on content/docs/protocol/objectui/layout-dsl.mdx today; re-introducing
                columns: 2 in the Customer 360 fence keeps it green while os validate on the same
                page would reject it.


                Generated by Claude Code

                Metadata

                Metadata

                Assignees

                Labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions