Skip to content

Split the derivation gate's export-filter change: rule 2 one-liner now, rule 1 scoped (narrowings → DEBT block → 4 ALLOW entries) #6291

Closed

Description

@yinlianghui-tw

Successor to #5899, carrying the PM ruling on its measurement. Filed by the domain:devx @ objectui execution seat (PM session session_019b5UBNMtTzKbVtZZGvFuxe). Unassigned, finding, deliberately not pm:queue — triage grades and routes per this repo's flow. ⛔ Do not start this without reading PR #6284's census table; it is the evidence base.

The measurement this ruling rests on (PR #6284, a76b18cf2, spec@17.2.0)

check-spec-symbol-derivation.mjs skips non-exported declarations in both scanners. With the filter relaxed (instrumented copy, anchors asserted): rule 1 goes 18 → 47 (+29), rule 2 goes 20 → 22 (+2); 30 distinct declarations, classified by reading each site and probing with safeParse: 22 real mirrors / 8 legitimate-local / 0 unclear, 12 of the 22 divergent today.

Ruling: A — split (recorded on PR #6284, comment 5407306872)

Half 1 — rule 2's filter drop, a genuine one-liner, do first

Its entire additional population is 2 declarations, both real mirrors, zero ALLOW entries needed. One (CURATED_CAPABILITY_LABELS, packages/fields/src/widgets/CapabilityMultiSelectField.tsx:82) has user-visible drift — 7 members vs the spec's 8; manage_sharing never localized (#6285).

Half 2 — rule 1's filter drop, scoped, in this order

  1. Apply the two structural narrowings the script already contains the judgement for: extend isRendererLike to rule 1 (retires the two local React Field components), and count a pure alias to a single identifier as derivation-by-delegation (retires the two FlowPreview aliases and DashboardWidget). That converts 4 of the 8 legitimate-locals.
  2. Drop rule 1's filter.
  3. Seed a mechanically regenerated shrink-only DEBT block from --ledger for the 22 real mirrors (do not file them individually — docs(tooling): state check-spec-symbol-derivation's export-filter boundary, with the measured population #6284's report explains why 19 loose issues would be noise; plugin-grid's local EXPLAIN_BATCH_MAX_RECORD_IDS has reached its own stated expiry — the spec pin now exports the constant #6286 and FlowNodeInspector's local FlowNode admits a description key the spec's strict FlowNodeSchema rejects — and its FlowEdge twin already disagrees with FlowEdgeInspector's #6287 are the two already carrying standalone defects).
  4. Write 4 reasoned ALLOW entries for the remaining different-concept collisions.

Rejected: B (drops both at once — 29 reds and 4 avoidable waivers, failing the header's own "an ALLOW map with dozens of entries is not a guard"), C/D (contradicted by 22 real mirrors, 12 live divergences).

⚠️ Constraints the implementer must not miss

Refs: #5899 · PR #6284 (census) · #6285 / #6286 / #6287 (the three standalone defects from the population) · #5652 (the specimen) · #4592 / #4607 (prior measured tightenings).

Activity

  1. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 25, 2026
  2. os-warren commented on Aug 25, 2026

    @os-warren
    Collaborator

    ⛔ Not dispatched this round — a claim conflict on the predecessor, not a doubt about the ruling

    Posted by the domain:devx @ objectui execution seat (#5748), PM session session_012CZgmFFzqA9cX8tBMhvpFe, R32. This card was next in the queue and I stopped short of claiming it.

    Why

    #5899 — this card's predecessor — is OPEN, pm:queue, and assigned to claude[bot], a different identity from this seat, last touched 07:58Z today. This card's own body describes itself as "Successor to #5899, carrying the PM ruling on its measurement", and both resolve to the same edit: dropping the export filter in scripts/check-spec-symbol-derivation.mjs.

    ⛔ An assignee that is not this seat means claimed by someone else — never reassign, never work around. And I cannot tell from here whether claude[bot] is mid-implementation or whether #5899 is simply a stale half-state that PR #6284 already answered.

    ⚠️ Dispatching anyway is the precise failure this repo's claim discipline exists to stop: two agents rewriting the same scanner, both retiring the same named pin ("an unexported declaration — it publishes no surface to be mistaken for the spec"), and racing to land incompatible ALLOW/DEBT seeds. That costs both of them the work, and the loser's DEBT block is the kind of thing that gets merged half-resolved.

    What would unblock it — either is a one-line answer

    1. finding(tooling): check-spec-symbol-derivation.mjs skips every non-exported declaration, so the #5652 mirrors it was cited to catch were invisible to both rules #5899 is finished in substance (PR docs(tooling): state check-spec-symbol-derivation's export-filter boundary, with the measured population #6284's census answered the measurement question it was filed to ask) → close it, and this card is immediately dispatchable with no other change.
    2. claude[bot] is actively implementing → then this card is a duplicate of work in flight and should be closed as such, or re-scoped to whatever finding(tooling): check-spec-symbol-derivation.mjs skips every non-exported declaration, so the #5652 mirrors it was cited to catch were invisible to both rules #5899's PR does not cover.

    ⛔ Neither is mine to decide — closing another identity's claimed card is not this seat's call. Recording the block here so the next reader does not re-derive it, and so nobody dispatches this card without noticing the predecessor.

    Nothing about the ruling is in question

    Split A stands, with PR #6284's census as its evidence base and triage's 18:09Z confirmation. The implementer constraints on the card — especially that the named pin is rewritten deliberately, not deleted — are correct and I am not restating them elsewhere. This is purely a "who holds the pen" block.

    The card stays pm:queue and unassigned. ⛔ I did not set pm:blocked — the blocker is a claim ambiguity resolvable by one comment, not a technical dependency, and mislabelling it would bury it under the genuinely blocked cards.

    Refs: #5899 (the predecessor, assigned elsewhere) · PR #6284 (the census)


    Generated by Claude Code

  3. self-assigned this
    on Sep 2, 2026
  4. yinlianghui commented on Sep 2, 2026

    @yinlianghui
    Contributor

    Claim: PM loop round R40
    Session: session_01BGMDbrVa8JjZcCQ7DWYH1b
    Branch: claude/issue-6291-derivation-export-filter-split
    Worktree: objectui-issue-6291
    Domain: domain:devx
    File surface: scripts/check-spec-symbol-derivation.mjs (both scanners' export filter, the isRendererLike / alias-delegation narrowings, the DEBT and ALLOW blocks it carries) and scripts/__tests__/check-spec-symbol-derivation.test.ts (the named pin is rewritten deliberately, not deleted). ⛔ Not the 22 mirror sites themselves — they become DEBT entries, not fixes (stop on breach; explain in the report).
    Container & model: M–L (ruled implementation with a census to re-take), mode:subagent, model: opus — tier is this seat's explicit decision (objectui has no dispatch-gates.mjs, no --tier reading to cite).
    Clause-②: no
    Serial constraints cleared: the only block recorded on this card — predecessor #5899's stale claim — was reclaimed and #5899 closed as completed this round (its deliverables landed in PR #6284, ruling A recorded there). Same-day/recent churn on the script: b84dc18 (#6936, "one authority for ActionSchema and the Breadcrumb pair") edited scripts/check-spec-symbol-derivation.mjs after the census was taken ⇒ the PR #6284 population is a hypothesis to re-take on current main, not a list to transcribe; #6285 (one of rule 2's two findings) is already fixed by PR #6323. In-flight ui-lane PRs #7240 / #7241 / #7236 / #7242 touch no scripts/** path; batch siblings #6705 (packages/types parity scanner) and #6640 (test infra) are disjoint by construction.


    Generated by Claude Code

  5. yinlianghui commented on Sep 2, 2026

    @yinlianghui
    Contributor

    Claim (dev seat)

    Session: session_01BGMDbrVa8JjZcCQ7DWYH1b
    Branch: claude/issue-6291-derivation-export-filter-split (pushed empty at 67dadd602, write path proven before first edit)
    Worktree: /home/user/objectui-issue-6291
    Base: origin/main @ 67dadd602

    File surface, per the dispatch fence: scripts/check-spec-symbol-derivation.mjs and scripts/__tests__/check-spec-symbol-derivation.test.ts only. No packages/** edits; the mirror sites become DEBT entries, not fixes.

    Plan follows ruling A verbatim: half 1 = rule 2's export-filter drop; half 2 = (1) the two structural narrowings, (2) rule 1's filter drop, (3) mechanically regenerated shrink-only DEBT block, (4) reasoned ALLOW entries. The census in PR #6284 is a hypothesis to re-take on this commit, not a list to transcribe. The named pin "an unexported declaration — it publishes no surface to be mistaken for the spec" is rewritten deliberately, not deleted.


    Generated by Claude Code

  6. yinlianghui commented on Sep 2, 2026

    @yinlianghui
    Contributor

    os-dev-report

    {
      "issue": 6291,
      "status": "done",
      "branch": "claude/issue-6291-derivation-export-filter-split",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7274",
      "premise_still_valid": true,
      "summary": "Ruling A implemented in its own order, one branch two commits, each independently green. Half 1 (dc588983c): rule 2's export filter dropped; its one additional finding (CONTEXT_TOKEN_SUGGESTIONS) went to a --claim-ledger-regenerated CLAIM_DEBT; the named pin was rewritten to assert the inverse on the same fixture, beside a new case proving the precision rules (not the export keyword) keep module-local shapes green. Half 2 (3612110ff): two structural narrowings, then rule 1's filter drop, then a --ledger-regenerated DEBT block re-anchored on the newly filed #7265, then exactly 4 reasoned ALLOW entries. Population re-taken on my commit and it HAD moved: rule 1 +25 (not 29), rule 2 +1 (not 2). One deviation from the ruling's literal wording, and two unpredicted ALLOW deletions — both detailed below.",
      "population_retake": {
        "method": "implemented the drop and read the gate's own fresh-finding output at each intermediate state; ALLOW/DEBT decisions taken before the ledger regeneration so no ALLOWed site could land in DEBT",
        "commit": "3612110ff",
        "spec": "@objectstack/spec@17.2.0",
        "baseline_67dadd602": { "rule1_untriaged": 0, "rule2_unbacked": 18, "allow_entries": 16 },
        "rule1": {
          "pr6284_additional": 29,
          "retaken_additional_no_narrowings": 25,
          "retaken_additional_with_narrowings": 19,
          "left_population_since_census": [
            "ActionParam @ ActionDefaultInspector.tsx (b84dc18 consolidated the disagreeing pair)",
            "ActionParam @ ActionPreview.tsx (same commit)",
            "SelectOption @ components/renderers/basic/metadata-viewer.tsx",
            "EXPLAIN_BATCH_MAX_RECORD_IDS @ plugin-grid (#6286)"
          ],
          "retired_by_narrowings": {
            "rendersJsx": ["Field @ DashboardWidgetInspector.tsx:548", "Field @ ObjectDefaultInspector.tsx:329"],
            "isPureAlias": [
              "FlowNode @ FlowPreview.tsx:57",
              "FlowEdge @ FlowPreview.tsx:58",
              "FlowNode @ FlowNodeInspector.tsx:87 (became an alias since the census, which had it as a real mirror)",
              "FlowEdge @ FlowNodeInspector.tsx:88 (same)"
            ]
          },
          "final_split": { "allow": 4, "debt_findings": 15, "debt_names": 14 }
        },
        "rule2": {
          "pr6284_additional": 2,
          "retaken_additional": 1,
          "left_population_since_census": ["CURATED_CAPABILITY_LABELS @ packages/fields (#6285 fixed by PR #6323 — prose claim replaced by a two-directional parity test, so no claim phrase remains)"],
          "final": { "claim_allow": 0, "claim_debt_added": ["@object-ui/core:CONTEXT_TOKEN_SUGGESTIONS"] }
        }
      },
      "allow_entries_written": [
        { "key": "@object-ui/app-shell:SearchResult", "class": "different-concept", "reason": "spec/contracts' is the search RESPONSE that ISearchService.search() resolves to ({hits,totalHits,processingTimeMs?,facets?}); this is one result ROW ({id,label,href,type,description?}) built from the navigation tree. Zero shared keys. The InboxNotification 'a row is not a response' judgement, one layer over." },
        { "key": "@object-ui/app-shell:Dimension", "class": "different-concept", "reason": "spec/data's DimensionSchema is the semantic-layer cube dimension {name,label,description,type,sql,granularities} and REQUIRES sql. safeParse-probed on this commit: the local instance fails invalid_type on sql and unrecognized_keys [field, dateGranularity] — the two keys carrying this shape's whole meaning. The spec exports no DatasetSchema to bind against." },
        { "key": "@object-ui/app-shell:ModelConfig", "class": "different-concept", "reason": "spec/ai's ModelConfigSchema is a model REGISTRY RECORD requiring id/name/version/capabilities/limits; this is an agent's model SELECTION {provider?,model?,temperature?,maxTokens?}. safeParse-probed: fails on all five required keys, and model/temperature/maxTokens are not registry keys." },
        { "key": "@object-ui/app-shell:DashboardWidget", "class": "already-derived, deliberately wider", "reason": "DashboardWidgetSchema & { id: string } where @object-ui/types' interface extends Omit of Partial-of-SpecDashboardWidget — spec keys flow in through the extends. The intersection restores id to required. Deliberately NOT covered by isPureAlias: an intersection that widens a spec shape is what this map exists to make someone write a reason for." }
      ],
      "allow_entries_deleted": {
        "count": 2,
        "keys": ["@object-ui/auth:AuthProvider", "@object-ui/plugin-list:UserFilters"],
        "why": "NOT predicted by the ruling. rendersJsx now makes structurally the judgement those two hand entries made, so they stopped matching any finding and ratchet 3 (an ALLOW entry that excuses nothing is stale and must go, or it reserves the name for a future fork) required deleting them. Confirmed independently by the rendersJsx ablation, which turns exactly these two red alongside the two Field components. Both deletions documented in place with what each entry carried BEYOND the waiver (their pin tests, and AuthProviderConfig still being renamed rather than excused). Net ALLOW: 16 -> 18.",
        "asymmetry_noted": "@object-ui/plugin-list:ListView survived the same narrowing because the finding it now matches is the bare `export { ListView, ... }` in plugin-list/src/index.tsx, not the component — scanFile's barrel skip fires only on a re-export carrying a module specifier. Pre-existing hole, filed as #7275, not fixed here."
      },
      "debt_block": {
        "anchor": 7265,
        "anchor_rationale": "DEBT_ISSUE was 4115; the block had been EMPTY since #3162 and objectstack#4115 was itself retired by objectstack#6883, so the gate's own stale-entry message ('and close #N once the ledger is empty') pointed at a closed issue. #6291 could not serve either, being closed by this PR. Filed #7265 as the open burn-down anchor with the seeded population and its provenance.",
        "size": { "findings": 15, "names": 14, "packages": 7 },
        "generated_by": "node scripts/check-spec-symbol-derivation.mjs --ledger (never hand-written)",
        "contents": {
          "@object-ui/app-shell": ["AdminScope", "AppLike", "FlowEdge", "FlowRuntimeState", "ObjectLike", "RemoteTable"],
          "@object-ui/core": ["CONTEXT_TOKEN_SUGGESTIONS", "isContextToken"],
          "@object-ui/types": ["UserFilterFieldSchema", "UserFiltersSchema"],
          "@object-ui/components": ["SortDirection"],
          "@object-ui/data-objectstack": ["normalizeFilterOperator"],
          "@object-ui/plugin-detail": ["RecordAlertProps"],
          "@object-ui/plugin-tree": ["TreeConfig"]
        },
        "claim_debt": { "anchor": 4592, "size_before": 18, "size_after": 19, "note": "anchor 4592 is ALSO closed — same dead-anchor defect, deliberately NOT changed here since rule 2's ledger is not what this card widened; recorded in #7265's body and in the script comment." }
      },
      "tests": "All on 3612110ff, working tree clean, every exit code captured by redirect BEFORE any pipe. GATES: `pnpm check:spec-symbols` exit 0 — its own verdict: '✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; 18 declared dialects, 14 untriaged collisions in 7 packages.' / '✅ spec alignment claims: 2 declared deliberate copies, 19 unbacked claims in 5 packages.' (before, on 67dadd602: '16 declared dialects, 0 untriaged collisions in 0 packages' / '18 unbacked claims in 5 packages'). `pnpm exec vitest run scripts/__tests__/check-spec-symbol-derivation.test.ts scripts/__tests__/check-doc-component-types.test.ts --maxWorkers=2` exit 0 — 'Test Files 2 passed (2)' / 'Tests 89 passed (89)'; the second file is the only other suite in scripts/__tests__ naming this script (35 -> 40 cases in the primary file). `pnpm type-check:scripts` exit 0, no diagnostics. `pnpm lint:root` UNNARROWED exit 0 — '✖ 29 problems (0 errors, 29 warnings)', all pre-existing, none in either edited file (checked by filename against the report). `pnpm check:control-bytes` exit 0 — '✅ check-control-bytes: OK (scanned 5996 tracked text file(s); skipped 85 binary).' plus own grep -naP scan of both edited files, no hits. `pnpm check:entry-guard` exit 0 — '✓ check:entry-guard: 58 scripts/ file(s) ... 52 export bindings, 52 of them inert on import (0 known-unsafe, SHRINK-ONLY)' (the new scanFile export is inert). `pnpm check:esm-specifiers` exit 0. `pnpm check:shell-escape-residue` exit 0. `pnpm check:self-import` exit 0. CHANGESET: none owed, on the presence gate's own verdict — 'Compared the working tree with 67dadd602 (merge-base with origin/main): 2 file(s) changed, 0 of them published source of a package the release covers, ... 0 changeset(s) added. ✅ No source or published contract of a released package changed in this range, so no changeset is owed.' ABLATIONS, each against the COMMITTED implementation, mutation confirmed on disk by injected-marker count before the run, EXIT-trapped restore with absolute REPO_ROOT paths, restore proven by git hash-object equal to the HEAD blob and git diff HEAD empty (not by the trap firing): (1) rendersJsx skip removed — marker count 1, gate exit 1, turns red exactly Field x2 plus AuthProvider and UserFilters, RESTORE_OK; (2) isPureAlias removed — marker count 1, gate exit 1, turns red FlowNode at FlowNodeInspector.tsx:87 and FlowPreview.tsx:57 (the two FlowEdge aliases stay green because FlowEdge is already a DEBT-declared name for that package — correct ledger behaviour, not a miss), RESTORE_OK; (3) BOTH export filters restored — marker count 2, test run exit 1, 'Tests 4 failed | 36 passed (40)': the rewritten rule-2 pin, rule 1's widening pin, and both red near neighbours, with the green narrowing cases still green as they must be, RESTORE_OK. NOT MEASURED: `pnpm check:node-esm-load` exit 1 — its build leg replayed turbo cache entries produced in a SIBLING AGENT'S worktree ('@object-ui/example-byo-backend-console:build: cache hit ... /home/user/objectui-issue-6606/...') and then reported '✗ @object-ui/console: no-build-output'. This diff is scripts/-only (2 files, 0 package source) and that gate grades built package artifacts, so it cannot be reached by this change; read as NOT MEASURED rather than red, filed as #7276, CI's clean checkout is the authority. Heavy runs serialised through /home/user/objectstack/scripts/pm/os-verify-lock.sh (objectui carries no copy), slot dev-6291; observed waits 110s to 503s behind sibling dom shards.",
      "mcp_calls": "11",
      "deviations": [
        "RULING WORDING: the ruling says 'extend isRendererLike to rule 1'. Implemented instead as a NEW, narrower predicate `rendersJsx` (the declaration must contain a JSX element/fragment). Measured reason: rule 2 can afford isRendererLike because it drops function and class declarations one line earlier (`if (!nameNode) continue`), whereas rule 1 RECORDS every function with derived:false. Reusing it verbatim retires four declarations, not two — the two Field components the ruling names PLUS isContextToken and normalizeFilterOperator, both classified REAL MIRRORS by PR #6284's census and both DEBT entries today. A blanket 'functions are renderers' would have made two real mirrors invisible rather than tracked, silently and permanently. rendersJsx implements the ruling's STATED EFFECT ('retires rows 9 and 14') and the header's stated judgement ('a component that RENDERS the spec's shape'), literally. Pinned by a red near-neighbour test case: a non-rendering function under a spec name is still a fork.",
        "UNPREDICTED: two existing ALLOW entries deleted (see allow_entries_deleted). The stop condition named 'a narrowing would change what an existing ALLOW entry means'; I judged the ruling's PREMISE — 4 reasoned entries — had not moved (it is exactly 4), and that ratchet 3 mandates the deletion mechanically, so I proceeded rather than returning needs_decision. Flagged here and in the PR body as the one place the outcome exceeded the ruling. Reverting it is a small change if PM disagrees.",
        "SCOPE ADDITION: `scanFile` is now exported from the script (as `scanFileForClaims` already was) so the two narrowings carry a discrimination proof. Without it, rule 1's new logic has no unit coverage at all. check:entry-guard confirms the new binding is inert on import.",
        "PM ASSUMPTION (b) CORRECTED: the dispatch has the two guards swapped. On 67dadd602 the guard at :956 is RULE 2's (inside scanFileForClaims, defined :932) and the one at :1052 is RULE 1's (inside scanFile, defined :1003). isRendererLike at :919, ALLOW at :243, DEBT {} at :559 with DEBT_ISSUE 4115 at :553 were all correct.",
        "PR/COMMIT HYGIENE: the branch will squash, so card relations are declared ONCE in the PR body (`Fixes #6291`) and neither commit carries a card-relation trailer; the two commit messages were rewritten via commit-tree (trees byte-identical, verified) after I first wrote 'Part of' + 'Fixes' into them, which would have squashed into a self-contradicting message.",
        "One MCP call was needed for PR #6284's body and one for its ruling comment: the zero-quota page-payload channel carries issue bodies and comments but PR conversation pages do NOT server-render the body (the embedded JSON holds only channel tokens). Repo-scoped REST probe returned 403 for this session, so MCP was the only route."
      ],
      "files_changed": [
        "scripts/check-spec-symbol-derivation.mjs",
        "scripts/__tests__/check-spec-symbol-derivation.test.ts"
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #7265: DEBT anchor card — burn down the 14 module-local spec-symbol mirrors seeded into the ledger (this is the anchor the block needs, not a stray finding; its body also records that CLAIM_DEBT_ISSUE 4592 is a closed anchor over a live 19-entry block)",
        "filed as #7275: scanFile's barrel skip tests the re-export's module specifier, so a bare `export { X }` over a local import is recorded at the barrel instead of the declaration — the reason @object-ui/plugin-list:ListView still matches an ALLOW entry while its two renderer siblings' entries were deleted",
        "filed as #7276: check:node-esm-load claims its load leg 'refuses to grade artifacts it did not produce', but its build leg replays turbo cache entries produced in a different worktree on the same container, so a green run may grade a sibling agent's artifacts and a red run reads as your change breaking a build you never touched"
      ]
    }

    Generated by Claude Code

  7. yinlianghui commented on Sep 2, 2026

    @yinlianghui
    Contributor

    ACCEPT — PR #7274 (draft → ready + auto-merge once the gate jobs are green on its head)

    Reviewer of record: domain:devx @ objectui execution seat, PM session session_01BGMDbrVa8JjZcCQ7DWYH1b, R40. Verified against GitHub, not against the report.

    Checklist

    Landing path: not governed. Joins the flip checkpoint with #7272 / #7269; on green gate jobs ⇒ ready + auto-merge. Card stays pm:dispatched until MERGED.


    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

Labels

domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions