Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(tooling): .github/labeler.yml has no package: fields entry — PRs changing packages/fields are invisible to any package:* sweep #7746

Closed

Description

@claude

Filed by the domain:ui PM seat from the contract review of PR #7621 — a tooling gap the review surfaced, unrelated to that PR's content.

The gap

.github/labeler.yml has no package: fields entry. Measured on PR #7621, which changes packages/fields/src/index.tsx (+31/-18 in its pre-revision, and still a changed file in the revised diff): the PR carries package: core, package: components and tests, and no fields label.

Why it matters rather than being cosmetic

The path labels are not decoration in this repo — they are how a sweeping seat finds the PRs that touch a package. objectui#7654's carrier defect earlier today was the same class in a different register: a Clause-② delivery is discoverable only if both carriers carry the label, and a sweep that keys on a label silently under-reads when the label is never applied. A package whose PRs never get labelled is a package whose PRs are invisible to any query keyed on package: *.

⚠️ Not measured, and it is the first thing to measure: whether fields is the only missing entry. Enumerate packages/* against .github/labeler.yml's entries and report the full gap set with a lit control (a package that IS in the file, e.g. core), rather than patching the one instance this review happened to trip over.

Refs: PR #7621 · objectui#7443 · objectui#7654 (the sibling carrier-label defect).


Generated by Claude Code

Activity

  1. claude commented on Sep 5, 2026

    @claude
    ContributorAuthor

    Claim: PM loop, round 5 — all-opus lane (Clause-② no)
    Session: session_01KbJQ1y1J12nZxYzFWhP8Q3
    Branch: claude/issue-7746-labeler-package-coverage
    Worktree: objectui-issue-7746
    Domain: domain:ui
    File surface: .github/labeler.yml, plus any pin/test the repo has for it. ⛔ No package source.
    Container & model: S, mode:subagent, model: opus
    Clause-②: no — CI labelling configuration. No accept-set change, no published surface, no runtime source. ⇒ needs:contract-review deliberately not applied. ⚠️ Void condition: a change under packages/*/src that is not a test file.

    ⚠️ This card was filed by me from PR #7621's contract review, and its own body names the part that is not measured: whether fields is the only missing entry. ⛔ Patching the one instance this review tripped over, without the enumeration, is the wrong shape — the card says so.

    Serial constraints: measured at claim time; reported in the dispatch brief. ⚠️ Re-measure at implementation time; ⛔ do not inherit.


    Generated by Claude Code

  2. claude commented on Sep 5, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 7746,
      "status": "done",
      "branch": "claude/issue-7746-labeler-package-coverage",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7784",
      "premise_still_valid": true,
      "summary": "Re-derived every claim on a tree merged up to origin/main (554f2b6be, 9 commits ahead of the killed run's base; neither .github/ nor the packages/ directory set moved in that range, and no open PR touches either labeler file). `.github/labeler.yml` named `package: *` for 4 of the 40 directories under packages/; 16 drew no label at all. Added 14 entries in the file's existing shape, left test-support and vscode-extension out as private with the reason recorded, and left the 19 plugin-* dirs plus data-objectstack alone because the `plugin` and `data-adapter` families already cover them. Kept the previous run's workflow edit after verifying it against upstream source, and added a pin that reddens in four independent directions.",
      "tests": "All runs on tree 69c1810e2, this branch's only commit, under scripts/pm/os-verify-lock.sh (exit codes captured before any pipe; verdict read from the wrapper's VERDICT line, never a bare $?). (1) NEW PIN GREEN: `pnpm exec vitest run scripts/__tests__/labeler-package-coverage.test.ts` -> VERDICT command-exit 0, `Test Files 1 passed (1) / Tests 6 passed (6)`. (2) ABLATION, red then green -- one lock hold, four mutations, each confirmed on disk by marker count BEFORE the run (a no-op count would have aborted the script), each restored with `git checkout HEAD -- ABSOLUTE_PATH` (never a bare `git checkout --`, which restores from the index) and proven by `git hash-object` against the HEAD blob; trap '...' EXIT INT TERM with paths resolved from `git rev-parse --show-toplevel` up front. A1 delete the `package: fields` entry -> exit 1, `gives every package under packages/ at least one label` reddened. A2 delete `issues: write` from the workflow -> exit 1, `keeps the labeler workflow able to create labels this config names` reddened. A3 repoint the designer exemption -> exit 1, `never keeps a packages/ rule pointing at a directory that does not exist` + `keeps both exemption lists honest` reddened. A4 blind globToRegExp so it matches nothing -> exit 1, the LIT CONTROL reddened (the control on the control: it proves the lit control is not vacuous). Final restored run `Tests 6 passed (6)`, `git diff HEAD --stat` empty. NO REBUILD WAS OWED OR PERFORMED: the pin imports only vitest, node:fs/path/url and `yaml`, and reads .github/*.yml off disk -- no package specifier resolves through any dist/, and the tree in fact has 0 of 40 packages built, so there is no stale-artifact path for the ablation to measure. (3) UNION: `pnpm exec vitest run scripts/__tests__/` -> `Test Files 102 passed (102) / Tests 3009 passed (3009)`. (4) `node scripts/check-changeset-presence.mjs` -> exit 0, its own verdict line: 'No source or published contract of a released package changed in this range, so no changeset is owed.' (4 files changed, 0 published source). No changeset and no skip-changeset label; nothing here is under packages/*/src. (5) `node scripts/check-control-bytes.mjs` -> exit 0, 'scanned 6343 tracked text file(s)'; plus a direct `grep -naP` control-byte scan over the four changed files, no hits. (6) ESLINT, a DECLARED NARROWING rather than a repo-wide `pnpm lint`: ran eslint --no-inline-config over the 4 changed files, exit 0, `--format json` reports 4 file entries, 0 errors; the 3 warnings are all 'File ignored because no matching configuration was supplied' on the .yml/.md files, i.e. eslint's own config -- not my guess -- places them outside the linted population, leaving exactly the one .ts file at 0/0. The narrowing is a measurement rather than a skip because eslint.config.js declares NO `project`/`projectService` (type-aware linting is off), so no rule can read another file's types and this diff cannot move the verdict on any untouched file. CI runs the repo-wide lint regardless.",
      "mcp_calls": "0 -- every GitHub read and write in this run went through repo-scoped REST with GH_TOKEN (probe: GET /repos/objectstack-ai/objectui returned 200; `gh` is not installed in this container). No MCP GitHub tool was called.",
      "audit_of_leftover_edits": {
        "note": "The killed run wrote no report, so nothing was inherited. Each of the three files was re-derived before any of it was kept.",
        ".github/labeler.yml": "KEPT, and re-derived independently. I did not trust its hand-rolled matching: I enumerated packages/* against the config with MINIMATCH -- the matcher actions/labeler itself uses -- from a scratchpad script, and reproduced its numbers exactly (40 dirs; 16 drawing nothing; 14 to add). The 14 entries and their glob shape are correct and match the file's existing convention. CHANGED one thing: the leftover appended the 14 under the file's existing `# Core packages` comment, which would then have been false of app-shell/cli/runner. That comment is now replaced by one that states the family's actual scope (one entry per packages/ dir except plugin-* and data-*) and names the gate, so the next person cannot recreate the gap by accident.",
        ".github/workflows/labeler.yml": "KEPT -- and yes, it needed touching; this was the one claim I most expected to drop. Verified from upstream, not from the leftover comment: actions/labeler v7 src/labeler.ts raises 'The workflow does not have permission to create labels. Ensure the issues: write permission is granted' on a 403, and its README's 'Recommended Permissions' says pull-requests: write covers adding EXISTING labels while issues: write is required to create new ones. Then measured the missing half the leftover comment did not have: the repository's live label registry (labels REST endpoint, 64 labels) already contains `package: fields` and `package: app-shell` but NOT the other twelve this config now names. The action applies the whole set in ONE setLabels call, so without the permission the first PR touching e.g. packages/auth gets NO labels at all -- strictly worse than the gap being closed. The leftover comment was rewritten to carry those measured numbers instead of a bare assertion, and A2 above turns the claim into a gate rather than a comment.",
        "scripts/__tests__/labeler-package-coverage.test.ts": "KEPT with corrections. Verified independently and true: the 16-of-40 enumeration; test-support and vscode-extension both being private:true (read from their package.json); packages/designer having been deleted in 21396ca4d; objectui#7771 existing, open, and deciding exactly the delete-vs-retarget question the exemption defers to; the DOCUMENTATION_EXEMPT idiom it copies existing in ci-cd-pipeline-doc.test.ts; PR #7621 carrying exactly `package: core`, `package: components`, `tests`. REMOVED one claim I could not stand behind: it asserted the config held rules for 'the four packages the monorepo started with'. This checkout is SHALLOW (`git rev-parse --is-shallow-repository` = true) and 53dc89db8 -- the only commit `git log` shows for that file, and the one --diff-filter=A calls its addition -- is the shallow BOUNDARY (no parent present), so 'added there' and 'started with' are artifacts of the clone depth, not history. Replaced with the present-tense fact that needs no depth. ADDED a fifth direction the leftover did not guard: the workflow keeping both permissions, parsed with the yaml library rather than grepped so a permissions block that only DISCUSSES issues: write in a comment cannot satisfy it. Also added data-adapter to the lit control.",
        "extra_file_i_added": "content/docs/guide/ci-cd-pipeline.md -- the Auto-Labeler section now says why the job holds issues: write. Not mechanically forced (ci-cd-pipeline-doc.test.ts only requires a heading per workflow, which labeler.yml already had), but AGENTS.md #2 is docs-driven and the page describes this workflow; leaving it silent about a permission grant that is load-bearing would make the page quietly wrong."
      },
      "full_enumeration": {
        "method": "Scratchpad script, minimatch (the matcher actions/labeler uses) with {dot:true}, probing `packages/PKG/src/index.tsx` against every glob in the config. A hypothetical probe path, deliberately not README.md or package.json -- both are matched by the repo-wide `documentation`/`dependencies` rules and would report coverage the per-package families do not provide.",
        "population": "40 directories under packages/",
        "lit_control": "packages/core/src/index.tsx -> `package: core` (LIT). Three more: packages/plugin-kanban -> `plugin` + `plugin: kanban` (LIT); packages/data-objectstack -> `data-adapter` (LIT); and the negative half, README.md -> does NOT draw `package: core`. So the query discriminates, and the empty readings below are real.",
        "before_drew_nothing_16_of_40": ["app-shell", "auth", "cli", "collaboration", "create-plugin", "fields", "i18n", "layout", "mobile", "permissions", "providers", "react-runtime", "runner", "sdui-parser", "test-support", "vscode-extension"],
        "before_had_package_star_for_only": ["core", "types", "react", "components"],
        "after_drew_nothing_2_of_40": ["test-support", "vscode-extension"],
        "after_package_star_covers_18": ["app-shell", "auth", "cli", "collaboration", "components", "core", "create-plugin", "fields", "i18n", "layout", "mobile", "permissions", "providers", "react", "react-runtime", "runner", "sdui-parser", "types"],
        "card_instance_reproduced": "PR #7621 (merged 2026-09-05) changes packages/fields/src/index.tsx and packages/fields/src/__tests__/datetime-compact-style-7443.test.tsx and carries exactly `package: core` | `package: components` | `tests`. No fields label. Read from the REST API in this run, not carried from the card."
      },
      "gap_set_fixed_and_deliberate_exclusions": {
        "added_14": ["app-shell", "auth", "cli", "collaboration", "create-plugin", "fields", "i18n", "layout", "mobile", "permissions", "providers", "react-runtime", "runner", "sdui-parser"],
        "left_out_private": {
          "test-support": "@object-ui/test-support, private:true (read from packages/test-support/package.json) -- an internal test helper, never published, so it has no consumer-facing surface a `package: *` sweep is looking for. Whether the maintainer WANTS unpublished packages in this taxonomy is a decision the gate must not make for them; recorded in UNLABELLED_BY_DESIGN with that reason, so adding it later is a one-line config change plus deleting the row.",
          "vscode-extension": "Published as the VS Code extension `object-ui` (private:true), not as an @object-ui/* npm package -- it sits outside the scope the label family names. Same disposition and same escape hatch."
        },
        "left_out_covered_by_another_label": "The 19 plugin-* directories already draw `plugin` (four of them also a `plugin: X` sub-label), and data-objectstack draws `data-adapter`. This config runs three parallel families keyed on directory prefix; giving a plugin a `package:` label would invent a convention beside the existing ones, which the brief forbids. That is why the gate asserts 'at least one label' rather than 'a `package: *` label' -- a plugin is discoverable, just not through the `package: *` key. Note the honest residue: 22 directories still draw no `package: *` label, and a sweep keyed strictly on that prefix still will not see them. Making the plugin family granular is a taxonomy decision (16 new labels) and is raised in objectui#7771 item 2, not taken here."
      },
      "the_pin_red_then_green": "scripts/__tests__/labeler-package-coverage.test.ts, 6 tests, run under the shared verify lock. Red: A1 (drop `package: fields`) exit 1; A2 (drop `issues: write`) exit 1; A3 (repoint the designer exemption) exit 1, two tests; A4 (blind globToRegExp) exit 1, lit control reddens. Green: restored tree, `Tests 6 passed (6)`, `git diff HEAD --stat` empty, and every restore proven by `git hash-object` equal to the HEAD blob (an empty hash would have been read as FAILURE, not as 'nothing to compare'). Full detail in `tests`.",
      "build_state_and_preexisting_red": {
        "build_state": "COMPLETELY UNBUILT -- 0 of 40 packages have a dist/ directory (`ls -d packages/*/dist` matched nothing). Not a half-built tree and not a fully built one.",
        "result": "scripts/__tests__/ was FULLY GREEN in that state: 102 files, 3009 tests, 0 failures. I hit NEITHER of the two reds objectui#7671 records -- check-sdui-registration-pins.test.ts and check-readme-exports.test.ts both passed here. Reporting that as an observation about this build state, not as a contradiction of that card.",
        "one_thing_worth_flagging": "I also ran a BASELINE pass with all four files reverted to origin/main, to separate pre-existing reds. It came back 2 failed / 99 passed: check-doc-component-types.test.ts and unconsumed-widget-option-claim-6186.test.ts. Those two reds are an ARTIFACT OF MY BASELINE METHOD, not pre-existing: to build the baseline I deleted scripts/__tests__/labeler-package-coverage.test.ts from the working tree while it was still TRACKED at HEAD, and both failing tests re-derive their claim by walking `git ls-files` and then readFileSync-ing each entry (unconsumed-widget-option-claim-6186.test.ts lines 239 and 255), so a tracked-but-absent path breaks the walk and the scan finds nothing. Both pass in the real state of the branch (they are inside the 102/102 above). I am reporting the reading rather than suppressing it, but it is not evidence about main."
      },
      "how_labeler_behaviour_was_verified": "Partly live, and I am explicit about the half I could not run. LIVE: opening PR #7784 triggered the Auto Label PRs workflow, run 33984659976 on head sha 69c1810e2, conclusion SUCCESS, and the PR now carries exactly `configuration` | `ci/cd` | `tests` -- which is what the config predicts for a diff touching .github/** (configuration), .github/workflows/** (ci/cd) and scripts/__tests__/*.test.ts (tests), and correctly NOT `documentation`, whose globs are docs/**/*, *.md and packages/**/README.md, none of which match content/docs/guide/ci-cd-pipeline.md. So the edited config parses, the workflow still runs, and the added permission did not break it. NOT PROVEN BY THAT RUN, stated so it is not over-read: this PR touches no file under packages/, so none of the 14 new `package: *` rules fired, and the predicted label set is identical under the base and head versions of the config -- the run therefore cannot distinguish which config it read. The first real exercise of the new entries will be the next PR that touches one of those packages. Everything else is a claim about the config and workflow files, which is what the gate reads.",
      "files_touched": [".github/labeler.yml", ".github/workflows/labeler.yml", "content/docs/guide/ci-cd-pipeline.md", "scripts/__tests__/labeler-package-coverage.test.ts"],
      "open_questions": [
        {
          "question": "Should the two private packages (test-support, vscode-extension) carry `package: *` labels? They are the only two directories under packages/ that still draw nothing at all.",
          "options": ["A -- leave them out (what this PR does): the `package: *` family names published consumer surface, and neither is published.", "B -- add both: the family is about FINDING PRs by directory, and a PR touching test-support is just as invisible to a sweep as one touching fields was."],
          "recommendation": "A, because the label family's four pre-existing members are all published packages, so A is the reading the file already encodes and B would widen the family's meaning. It is recorded as a reviewable exemption with a reason rather than a silent omission, so B costs one config line plus deleting the row if you disagree."
        },
        {
          "question": "The `issues: write` grant on the labeler workflow widens that job's token beyond what it had. Acceptable, or would you rather hand-create the twelve missing labels in the registry and keep the token narrow?",
          "options": ["A -- keep `issues: write` (what this PR does), pinned by the gate.", "B -- create `package: auth`, `cli`, `collaboration`, `create-plugin`, `i18n`, `layout`, `mobile`, `permissions`, `providers`, `react-runtime`, `runner`, `sdui-parser` in the label registry by hand, then drop the permission."],
          "recommendation": "A, because B is a server-side change no PR can carry or verify, it silently re-breaks the moment anyone adds the 41st package, and upstream's own README names A as the supported route. If you take B, the gate's failure message already tells the next person exactly what has to be true first."
        }
      ],
      "out_of_scope_findings": [
        "already filed as #7771 (open, verified in this run, NOT re-filed): the labeler `designer` rule targets packages/designer, deleted in 21396ca4d, so it can never fire; plus `plugin: chatbot` registered in the label registry with no rule, and a `plugin-view` label spelled outside the `plugin: X` convention. Filed by this card's killed run. I re-measured all three and its contents hold. Carried in this PR as a NAMED exemption (UNRESOLVABLE_GLOB_EXEMPT) so the gate is honest about the dangling rule today and reddens the moment #7771 is resolved either way -- ablation A3 proves that."
      ]
    }

    Generated by Claude Code

  3. claude commented on Sep 5, 2026

    @claude
    ContributorAuthor

    落地确认:PR #7784 已合并,本卡由 Fixes 自动关闭。落地后读数如下。

    domain:ui PM 席(session_01KbJQ1y1J12nZxYzFWhP8Q3,round 6)。

    合并读数(不读聚合,逐项读)

    项 读数
    merged true
    merged_at 2026-09-05T19:07:00Z
    merge_commit_sha 6771c805acb75224ca324a4c4c03f80978d03d32
    base main
    diff 4 files, +420 / −1
    入队 timeline added_to_merge_queue @ 18:53:34Z,无 removed_from_merge_queue ⇒ 一次入队、一次出队即合并

    ⛔ 本席读的是 merged 字段本身,不是 mergeable_state(合并后它读 unknown,那不是合并证据)。

    交付内容

    .github/labeler.yml 的 package: * 规则原先只覆盖 40 个包目录中的 4 个,其中 16 个一个标都拿不到;本 PR 补上 14 条。同时给 workflow 加 issues: write —— actions/labeler v7 用一次 setLabels 整体套用标签集,而活的标签注册表(64 个)里有 package: fields 与 package: app-shell、没有另外十二个,缺写权限会整批失败。

    ⚠️ 后续载体存活确认

    评审期间析出的后续件 objectui#7771(labeler.yml 的 designer 规则指向一个已删除的包,外加两处配置看不见的标签注册表漂移)确认 open,未被 Fixes 连带关闭。本席已给它补 pm:queue —— 它此前无任何 pm:* 状态标,那是半状态:卡活着但对派发扫描不可见。

    ⇒ 三条判据全过:① merged: true 已读 ② 卡由 Fixes 正常关闭 ③ 后续载体存活且已恢复可见。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions