Skip to content

Label 门控的 opt-in 预合并 pack-smoke —— 破坏性 auth/audience 面 PR 在合并前跑一次打包安装冒烟(#14000 附问裁决派生) #14214

Description

@huangyiirene

由 #14000 的维护者裁决派生(2026-09-01,总监批 #23,维护者逐字:「同意」,附问推荐「label 门控 opt-in」获批)。

背景(一句话)

#11767(audience 默认改 invite_only,破坏性 auth 变更)合并时其 PR 上零打包安装覆盖 —— pack-smoke 只在 Release run 之后以 workflow_run 触发,判决落在 release 分支 head 上,没有任何 PR 作者会看那里 ⇒ RC 无声连红 7 天(#14000 实测)。

交付

  1. 一个 label(如 needs:pack-smoke)挂在自声明「破坏性 auth/audience 面变更」的 PR 上时,CI 追加运行一次打包安装冒烟(与 publish-smoke.yml 的 pack-smoke 同一驱动,目标树 = PR 合并预览);
  2. ⛔ 不进默认内环 —— 无 label 的 PR 零新增成本(裁决理由:成本留在 opt-in);
  3. 文档一行:什么样的 PR 该自挂此 label(判据 = 改变 auth/audience 的默认或 accept/reject 行为)。

边界

Refs: #14000(主裁决与 7 天断边测量)· #11767(样本:零 PR 覆盖的破坏性 auth 变更)· #14190(仪器缺陷)· #3091(pack-smoke 存在的理由)。

Activity

  1. added theissue type on Sep 1, 2026
  2. self-assigned this
    on Sep 1, 2026
  3. baozhoutao commented on Sep 1, 2026

    @baozhoutao
    Contributor

    Claim:


    Generated by Claude Code

  4. claude commented on Sep 1, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 14214,
      "status": "done",
      "premise_still_valid": true,
      "pr": "https://github.com/objectstack-ai/objectstack/pull/14266",
      "branch": "claude/issue-14214-labeled-pack-smoke",
      "files_changed": [
        ".github/workflows/pack-smoke-optin.yml (new)",
        "scripts/pm/ensure-pm-labels.sh (declares the needs:pack-smoke label object)",
        "CONTRIBUTING.md (the one documentation line, in the Before Submitting checklist)"
      ],
      "summary": "Added the label-gated opt-in pre-merge pack smoke as a TRIGGER only. Premise re-checked on fresh origin/main at 035951faf and it holds: no labeled trigger of any kind existed in .github/workflows (every grep hit is prose or pr-automation.yml explicitly excluding labeled/unlabeled), and the positive control is exactly as the card describes - publish-smoke.yml runs pack-smoke on workflow_run after Release and reports a commit status on the release branch head. Driver contract verified by reading scripts/publish-smoke.sh rather than inventing one: invocation is bash scripts/publish-smoke.sh, SMOKE_MODE defaults to pack, prereq is pnpm install + pnpm run build (the script fails fast without packages/cli/dist and the create-objectstack bin), and it packs from REPO_ROOT - i.e. whatever tree is checked out. So the merge preview needed NO driver plumbing and none was written: publish-smoke.yml's release-branch coupling lives entirely in its resolve job, never in the driver. On pull_request the default checkout ref is already refs/pull/N/merge and the check run lands on the PR, so this workflow needs no resolve job, no resolve-guard and no statuses:write - it runs contents:read only. Boundary honoured: scripts/publish-smoke.sh has ZERO changes; assertion content was #14000's deliverable (#14255). #14190 untouched.",
      "tests": "Gate families derived mechanically from the real changeset via 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack' (no hand-written path list; the script takes its own merge-base changeset) = 31 families, harvested with --commands and all 31 run. 30 green. 1 NOT MEASURED, not a red: scripts/check-test-completeness.mjs exits 3 with no test log to read and says so itself ('the local reading for this gate is NOT MEASURED ... it is not a red, and there is nothing here to fix'). Re-run union on the FINAL commit 820a49923 (12 families, all green), quoting each gate's own verdict line: 'check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level if: expression(s); 10 read needs.*.outputs.*, all naming a status function)'; 'check:pm-label-desc-cap: 22 label descriptions in scripts/pm/ensure-pm-labels.sh, all <=100 characters (longest: 100, repo:objectui)'; 'check-required-contexts: 6 required context name(s) pinned across 2 workflow(s)'; 'check-stall-guard-budget: OK (30 workflow file(s), 53 job(s), 572 step(s), 7 guard-wrapped step(s); every effective cap clears its job budget by at least one stall window)'; 'check-nul-bytes: OK (scanned 7794 text file(s); no raw ASCII control bytes)'; 'check:declared-population-live - 156 of 200 famil(ies) declare a path population, and every one reaches this tree'; 'check-bash32-floor: 26 tracked shell file(s) name no bash 4+ construct outside a comment'. Also requested for the scripts/** touch and both green: 'check-ratchet-remedy-authority: 183 scripts swept' and 'dispatch-gates self-test: 1140 cases pass'. The family list re-derived identically after the commit. New workflow additionally parsed with the repo's own yaml lib to confirm on: did not fall into the YAML-1.1 true trap and the folded guard expression survived intact. No ablation applies - this card adds a CI trigger, and the demonstration below is the live equivalent.",
      "checks_run": "31 derived families (30 green, 1 self-declared NOT MEASURED) + 12 re-run on final commit 820a49923 + check:ratchet-remedy-authority + check:pm-dispatch-gates. Zero reds.",
      "demo_run": "https://github.com/objectstack-ai/objectstack/actions/runs/33540022809",
      "demo_evidence": "All THREE arms of the guard observed live on PR #14266, not asserted: run 33539960970 (opened, no label) job SKIPPED = the ruling's zero-cost boundary for unlabelled PRs; run 33540022686 (labeled with skip-changeset, not ours) job SKIPPED = the guard's second limb, which is what stops every size/* and ci/cd label the automation adds from starting another 45-minute smoke; run 33540022809 (labeled with needs:pack-smoke) job IN_PROGRESS = the trigger fires against the merge preview. Reported at draft-PR time per the dispatch contract rather than waiting ~45 minutes for convergence.",
      "label_created": "needs:pack-smoke did not exist (confirmed absent before creation). Created on objectstack-ai/objectstack via REST with colour 006b75 and a 94-char description, and DECLARED in scripts/pm/ensure-pm-labels.sh so it is reproducible and its description falls under check:pm-label-desc-cap. Live object read back and it matches the declaration exactly. Never auto-applied: no .github/labeler.yml rule, deliberately - path-derived application is a different design from the one ruled and would put the ~45-minute cost back in the default inner loop.",
      "labels_applied": "skip-changeset (releases nothing: .github/, scripts/pm/, CONTRIBUTING.md - route 2 of the Check Changeset step's own text) and needs:pack-smoke (to demonstrate). Both written with an additive POST, never a whole-set PUT, and contrast read-back confirms both survived: target minus readback = none.",
      "mcp_calls": "3 - issue_read(get), issue_read(get_comments), get_label. All other GitHub work went through repo-scoped REST, probed green (200) first; gh CLI is absent in this container.",
      "open_questions": [
        {
          "question": "I declared needs:pack-smoke in scripts/pm/ensure-pm-labels.sh, which is a PM-owned file whose header says 'the label set IS the PM state machine'. This is the first row there that is NOT a PM state label - it is written by the PR author about their own diff. Is that the home you want?",
          "options": [
            "A. Keep it there (as shipped). It is the repo's only declared home for a label OBJECT; leaving it undeclared reproduces the grey/empty-description drift the file's own header describes - auto-created by first application and unrepairable by any rerun - and it gains check:pm-label-desc-cap coverage. The added comment states at length that it is not a card state, is one-of with nothing, and no scripts/pm/ sweep reads it.",
            "B. Move it out to a new non-PM label home and keep ensure-pm-labels.sh purely PM state.",
            "C. Drop the declaration; rely on the live object alone."
          ],
          "recommendation": "A. The file already carries several non-state labels (needs:contract-review is a PR gate, repo:* is routing, target:v* is the release board), so the precedent exists; and the file's own #13991 block argues at length that an UNDECLARED absence is indistinguishable from an oversight, which is exactly what C would leave behind. B buys purity for a one-row file. Cheap to reverse either way - it is one row plus its comment."
        }
      ],
      "out_of_scope_findings": [],
      "followups_filed": []
    }

    Generated by Claude Code

  5. baozhoutao commented on Sep 1, 2026

    @baozhoutao
    Contributor

    PM adjudication (devx seat, session session_01WLJQhde67SeTccsmnBVarV) on the dev's open question — where the needs:pack-smoke label object is declared:

    A — keep it in scripts/pm/ensure-pm-labels.sh, as shipped. Reasoning, within the delegated lane: that file is the repo's only declared home for a label OBJECT; an undeclared label reproduces exactly the grey/empty-description drift its own header warns about (auto-created by first application, unrepairable by rerun), and the declaration buys check:pm-label-desc-cap coverage. The precedent already exists in-file (needs:contract-review is a PR gate, repo:* is routing, target:v* is the release board — none of those is a card state either), and the shipped comment spends its length disclaiming kinship with the PM state machine, which is the honest shape. Option B buys purity at the cost of a second one-row label home (a new file nothing else reads); option C leaves the absence indistinguishable from an oversight. Cheap to reverse later if the maintainer ever wants a non-PM label home.

    This does not touch the maintainer floor: the label-gated opt-in shape itself was ruled (2026-09-01, director batch #23); this is only its bookkeeping location.


    Generated by Claude Code

  6. baozhoutao commented on Sep 1, 2026

    @baozhoutao
    Contributor

    ✅ ACCEPT + close-out (same window as the merge)

    domain:devx, session session_01WLJQhde67SeTccsmnBVarV, R1 wave 2. PR #14266 MERGED as 54c37fb8 (merge-queue witness: timeline added_to_merge_queue @ 18:03:47Z; 39/39 checks on the head, zero red).

    Acceptance probes re-run on origin/main after the merge, not on the report:

    • .github/workflows/pack-smoke-optin.yml present, two-limb guard verbatim (contains(...labels..., 'needs:pack-smoke') && (action != 'labeled' || label.name == 'needs:pack-smoke'));
    • CONTRIBUTING.md:367 carries the self-apply criterion line;
    • scripts/pm/ensure-pm-labels.sh declares the needs:pack-smoke label object (adjudicated option A on this card).

    All three ruling deliverables verified: (1) label-gated smoke against the merge preview — demonstrated live on the PR itself, all three guard arms observed (unlabeled → skipped; other-label → skipped; needs:pack-smoke → ran the real pack smoke, green, run 33540022809); (2) zero cost for unlabeled PRs — the skip arms are the evidence; (3) the documentation line sits in the Before Submitting checklist. Boundaries held: scripts/publish-smoke.sh zero changes; #14190 untouched; no merge_group trigger and not a required context, by design and documented.

    Residual risk stated on the workflow header (self-declaration covers only the author who recognises the change as breaking; the release-time leg remains the backstop) — that is the ruled shape, not a gap this card owed.

    pm:dispatched stripped as the second write (auto-close strips no labels). Card complete.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions