Skip to content

ci(scaffold-e2e): build the skills-boundary expected set from the whole curated skills/ catalog - #26285

Merged
os-zhuang merged 3 commits into
mainfrom
claude/project-thread-qe39y2
Oct 26, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/project-thread-qe39y2

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #26204

Clause-②: no
Metadata-protocol: none

⛔ Tier H (.github/workflows/**): this PR stays a draft until a GOVERNED_APPROVERS approval. No seat readies it, queues it or arms auto-merge before that.

维护者速读(草稿)

  • 改了什么:Scaffold E2E 里「Skills boundary」这一步的期望技能集合,从「只认 skills/objectstack-*」改成「skills/ 下凡是带 SKILL.md 的目录都算」。另外加了一道保护:若 skills/ 读出来是空集,这一步直接失败。两条防泄漏断言(安装集合相等、仓根发现数相等)一字未动。
  • 为什么改:objectui 技能并入根 skills/ 之后,这一步把它当成「泄漏」,所有动到 packages/create-objectstack/** 的 PR 都挂红。@objectstack/skills 发布包本来就是把整个 skills/ 原样打包,所以 skills/ 本身就是策展目录;在工作流里再维护一份白名单,等于以后每加一个技能都要改一次受管文件。
  • 风险与代价(含回滚):只改一个 CI 步骤,不影响任何发布包。代价是:往 skills/ 新放一个普通技能目录会被当成策展技能放行(它本来也会随 npm 包发布);内部技能(metadata.internal)放进 skills/、或放在 skills/ 之外却没标 internal 的技能,仍然会让这一步失败(本地已实测,见下)。回滚就是 revert 这一个提交。
  • 席位意见:
  • 你要做的:看一眼 diff,同意的话批准这个 PR。批准后由席位负责转 ready 并入队。

What changed

.github/workflows/scaffold-e2e.yml, step "Skills boundary: only the curated skills/ catalog installs":

  • expected was ls -d objectstack-*/ inside skills/. It is now every skills/ subdirectory that holds a SKILL.md, read with the same loop shape as the later "Skills: the scaffolder installed exactly the @objectstack/skills catalog" step.
  • An empty expected now fails the step loudly instead of comparing two empty sets.
  • expected_count, and therefore the Found N skill check, follows the new set. Both assertions (installed set equality, repo-root discovery count, and the dogfood-verification grep) are unchanged.

Why a directory read and not an allow-list. The card allowed either. @objectstack/skills ships the whole skills/ tree (packages/skills/scripts/sync-catalog.mjs copies skills/** and --check refuses any difference), so skills/ is the curated catalog by definition. An allow-list in this file would be a second registry of the same fact, and every new curated skill would red this step until a Tier H edit of the workflow. That is the same failure this card fixes. With the directory read, what the boundary still catches is anything that is not curated: a skill outside skills/ without the internal marker, an internal-marked skill placed in skills/, and a nested skill inside a curated directory. All three are rehearsed below.

Rehearsal

The step's run: block was extracted verbatim from the workflow (the YAML parsed with the yaml package) and run with bash -e, the runner's default shell for a step with no shell:. GITHUB_WORKSPACE was the checkout, or a git archive HEAD copy for the controls. RUNNER_TEMP was a fresh temp dir. skills CLI 1.7.4 via npx -y skills. create-objectstack was built from this tree. The final head's run block is byte-identical to the rehearsed one (cmp).

run workspace exit what the log shows
old step (origin/main e05e0de) checkout 1 installed = ten objectstack-* + objectui, expected = ten objectstack-*, ::error::scaffolded skill set != curated skills/ catalog. The card's failure, reproduced.
new step checkout at a9585ae 0 installed = expected = the 11 skills, Found 11 skills twice
new step archive copy, unmodified 0 control leg for the three negatives below
negative A: unmarked skill at .claude/skills/leak-probe/ archive copy 1 Found 12 skills, ::error::repo-root discovery does not match the curated catalog (expected 11 skills)
negative B: metadata.internal: true skill at skills/internal-probe/ archive copy 1 expected lists internal-probe, installed does not, ::error::scaffolded skill set != curated skills/ catalog
negative C: nested skills/objectui/guides/nested-probe/SKILL.md archive copy 1 installed lists nested-probe, ::error::scaffolded skill set != curated skills/ catalog
control D: plain new skill at skills/new-curated-probe/ archive copy 0 Found 12 skills. A new curated skill passes without a workflow edit, by design.
new step after removing every probe archive copy 0 restore leg

Negative B shows the skills CLI hides metadata.internal skills even under --all today, so an internal skill in skills/ trips assertion 1. If an older CLI did install it, assertion 2's count would trip instead. The step's comment says both.

Gates

At head a9585ae, all 38 commands from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (change set: this one file, 14 changed lines) were run one by one with stdin from /dev/null, and each exit was captured before any pipe. Every one exited 0, including pnpm check:pm-dispatch-gates (fast tier, 1899 cases). The --ran reconciliation reads 38 derived, 38 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero because every line recorded its exit code. The families the derivation prints as NOT MEASURED (workflow-valued argv, the Test Core shard steps, the type-check lanes) are CI's. Their verdicts are this PR's check-runs.

Changeset: none. The diff is one CI workflow and publishes nothing, so skip-changeset rides the create.

Acceptance notes

  • The card's negative control reads "an extra non-curated skill directory". Under the directory-read rule, a plain directory added to skills/ is curated by definition (control D passes) because it also ships in @objectstack/skills. The non-curated shapes are A, B and C, and each fails.
  • The workflow's pull_request.paths lists .github/workflows/scaffold-e2e.yml itself, so this PR runs Scaffold E2E. The card's first acceptance line is read from this PR's Scaffold with repo dist check-run, which is a real CI run of the edited step against unchanged skills.

Generated by Claude Code

…le curated skills/ catalog

The step globbed skills/objectstack-*/ only, so the objectui skill that
joined skills/ made every create-objectstack PR red. Expected is now every
skills/<dir>/ holding a SKILL.md — the tree @objectstack/skills ships whole —
and the step refuses an empty catalog. Both leak assertions are unchanged.

Claude-Session: https://claude.ai/code/session_014kunMthReAU7eVraV1qLsb
Co-authored-by: Claude <noreply@anthropic.com>
…a skills/ entry has no SKILL.md

Claude-Session: https://claude.ai/code/session_014kunMthReAU7eVraV1qLsb
Co-authored-by: Claude <noreply@anthropic.com>
…trips, as measured

Claude-Session: https://claude.ai/code/session_014kunMthReAU7eVraV1qLsb
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 26, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · devx seat 2 · 2026-10-26T18:54Z

Seat domain:devx#2 (#20163) · session_01Q4MDXRbEJCNdPAP4SiXwp9 · reviewed head a9585aeca2 · card #26204 (wave 6, delta P). PR carries Fixes #26204.

  • Scope: one file, .github/workflows/scaffold-e2e.yml, +13/−1. ⛔ Tier H (check-governed-merges --branch pr/26285 → workflows). This seat ACCEPT does not lift the gate: the PR stays a draft until a GOVERNED_APPROVERS approval, and the seat will neither ready nor queue it before that.
  • Root cause, confirmed on main: skills/ holds 11 skill directories. Ten are objectstack-*, plus objectui/. The old ls -d objectstack-*/ glob expected 10, while the CLI installed 11, so assertion 1 failed. That is the red on the card.
  • The fix: expected becomes every skills/<dir>/ that holds a SKILL.md. That is the same tree @objectstack/skills ships whole, so a new curated skill no longer needs a governed edit of this file. README.md is not a directory and is excluded. An empty set now fails loudly instead of comparing two empty strings.
  • Leak guards unchanged: the dogfood-verification grep and the Found $expected_count skill count are untouched. As the new comment says, an internal skill placed in skills/ still trips assertion 1 or 2, because the CLI hides it from the set the directory read includes.
  • Evidence from the dev thread: the old step exits 1 (the card's red reproduced). The new step exits 0 (Found 11 skills). Negative controls A, B and C exit 1, and control D (a new plain skill) exits 0 by design. 38/38 derived gates exit 0, with 0 NOT-MEASURED on --ran.
  • CI: the PR touches its own workflow, so Scaffold with repo dist runs on it. That is the check that proves the fix. The seat lands only once it and every other check are green or skipped.
  • Landing: after a GOVERNED_APPROVERS approval on this head, the seat runs ready + auto-merge.

Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 26, 2026 22:45
@os-zhuang
os-zhuang enabled auto-merge October 26, 2026 22:45
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 7e6fca0 Oct 26, 2026
54 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-qe39y2 branch October 26, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants