Skip to content

fix(spec): rewrite skill.zod.ts's two @example blocks off the retired triggerPhrases - #11402

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-11026-skill-examples
Aug 23, 2026
Merged

fix(spec): rewrite skill.zod.ts's two @example blocks off the retired triggerPhrases#11402
os-steve merged 1 commit into
mainfrom
claude/issue-11026-skill-examples

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes#11026

Sub-issue of #10924. Both TSDoc @example blocks in packages/spec/src/ai/skill.zod.ts passed triggerPhrases — removed in @objectstack/spec 17.0.0 (#3896 audit close-out) and carried as a retiredKey() tombstone ~120 lines below the first of them. defineSkill calls SkillSchema.parse(), so both documented blocks threw when run.

Measured, before and after

Premise re-verified on origin/main @ 735f5c7 (the card said ~250/~436; the PM's re-check said :256/:442 @ b863f01 — both blocks are where the PM placed them). Both blocks executed verbatim against the freshly built packages/spec/dist:

BeforeFAILING_BLOCKS=2, exit 1:

BLOCK1(SkillSchema @example): THREW [ { "expected": "never", "code": "invalid_type",
"path": [ "triggerPhrases" ], "message": "`skill.triggerPhrases` was removed in
@objectstack/spec 17.0.0 (#3896 audit close-out) …
BLOCK2(defineSkill @example): THREW [ … same, path: [ "triggerPhrases" ] … ]
FAILING_BLOCKS=2

AfterFAILING_BLOCKS=0, exit 0:

BLOCK1(SkillSchema @example): PARSED OK -> {"name":"case_management","surface":"ask",
"active":true,"tools":["create_case","update_case","resolve_case","query_cases"]}
BLOCK2(defineSkill @example): PARSED OK -> {"name":"order_management","surface":"ask",
"active":true,"tools":["create_order","update_order","cancel_order"],
"triggerConditions":[{"field":"objectName","operator":"eq","value":"order"}]}
FAILING_BLOCKS=0

What the blocks say now

The tombstone's prescription is a split, not a rename — routing intent to triggerConditions, natural-language intent to description / instructions, the strings actually put in front of the model. The blocks now demonstrate that instead of contradicting it:

  • SkillSchema — the minimal skill. The intent the three deleted phrases were carrying moves into instructions, where the model actually reads it.
  • defineSkill — shows both halves, each named in a comment: natural language in instructions, programmatic routing in triggerConditions.

tools is required with no default, so both blocks carry it. Each block now opens with its own import { defineSkill } from '@objectstack/spec';, matching the convention the marked SDK examples in packages/client-react/src already use — the block is self-contained the way a consumer resolves it.

The retiredKey() tombstone and every line of its guidance prose are unchanged, verbatim — they were always correct, and they are what the repaired examples now agree with. Verified mechanically: git diff against the base commit matches zero lines touching retiredKey, the REMOVED comment, #3896, or the os migrate meta line. The four surviving triggerPhrases mentions in the file are exactly the guidance prose (:276), the tombstone comment (:380), the declaration (:385) and its message (:386) — zero in @example content, so the card's own re-check command now reads clean.

Reverse verification — would #10924's gate have caught this?

Yes, and this is the useful half for the parent card. Extracting the blocks the way check-skill-examples.ts does (JSDoc gutter stripped) and running tsc --noEmit against the built dist:

blocksresult
the original twoTS2322: Type 'string[]' is not assignable to type 'undefined' — both, at exactly the triggerPhrases line
the repaired twoexit 0, no diagnostics

So a compile-only gate does red on this defect class (the retired key surfaces as undefined in z.input), and the harness can produce red — the green on the repaired blocks is a real measurement, not a vacuous one.

Verification

Union re-run after the final commit, at 63bc7746b2:

  • 23 of 24 derived gate families green. Gates derived from the actual diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (22 families), plus check:nul-bytes and check:skill-examples.
  • The one red is check:dev-prereqs, on its scan half: "The workspace is not built — 34 of 67 workspace packages declare an entry point under dist/ that is not on disk". It names none of this PR's paths. lint.yml runs only node scripts/check-dev-prereqs.mjs --self-test"self-test half only, never the scan" — and that half is green here (exit 0, 16 cases).
  • pnpm --filter @objectstack/spec exec tsc --noEmit — exit 0, no output.
  • pnpm --filter @objectstack/spec test419 test files, 11136 tests, all passed.
  • Regeneration discipline: pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated"All 14 generated artifacts are up to date."Nothing moved, confirming the card's reading that the generated reference page carries neither example.
  • check:skill-examples — green over all 246 marked blocks across 2 surfaces.

Changeset

Graded patch, not skip-changeset. The docblock ships to consumers: the rewritten blocks are present in the built packages/spec/dist/skill.zod-CFjrFQj9.d.ts (lines 442 and 525), which is what an editor surfaces on hover, and @objectstack/spec's files list publishes src/**/*.zod.ts so the text also travels in the npm tarball as source. Same reasoning the repo already applies to docblock-prose changesets.

Scope

packages/spec/src/ai/skill.zod.ts (the two @example blocks) plus the changeset — nothing else. No schema shape, no .describe() text, no runtime behaviour, no authorable-surface movement. scripts/check-skill-examples.ts is deliberately untouched: the gate extension is #10924's work, held behind this card, and #11355 has an open finding on that script's extraction loop.

Clause-② content limb is no (docblock prose; accept/reject behaviour unchanged). The path limb fires mechanically on packages/spec/src/**, so this is held for contract review — expected, not an error.

Out-of-scope finding

While verifying, check:skill-examples refused the client SDK surface with "packages/spec/dist holds no .d.ts declarations" at a moment when packages/spec/dist held 44 of them — the unbuilt packages were client / client-react. That is already filed as #11250; rather than open a duplicate I added the live confirmation plus one addition that card had not recorded — the printed remedy is hardcoded to spec at the call site too, so following it verbatim does not clear the red. Not fixed here: #11250 is out of this card's scope and needs its pinned test assertions updated.


Generated by Claude Code

… triggerPhrases
Both TSDoc examples in packages/spec/src/ai/skill.zod.ts passed
`triggerPhrases`, removed in @objectstack/spec 17.0.0 (#3896 audit
close-out) and carried as a retiredKey() tombstone ~120 lines below the
first of them. defineSkill calls SkillSchema.parse(), so both documented
blocks threw when run: invalid_type at path triggerPhrases, expected
never. Measured against the built dist before the repair: 2 of 2 blocks
threw; after it, 2 of 2 parse.
The blocks now demonstrate the tombstone's own prescription instead of
contradicting it. That prescription is a SPLIT, not a rename: routing
intent to triggerConditions, natural-language intent to
description/instructions. The defineSkill block shows both halves with
each named. `tools` is required with no default, so both blocks carry it,
and each block opens with its own import so it is self-contained the way
a consumer resolves it.
The retiredKey() tombstone and every line of its guidance prose are
unchanged, verbatim.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9cDbY2NBiVJWYx3BpWfH2
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/ai/skill.zod.ts) — pages documenting those are invisible to this run

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 365e3340f5c7807eb0f59af115f93476e508a094packageMentionDocs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation protocol:ai tooling labels Aug 23, 2026
@os-steve
os-steve marked this pull request as ready for review August 23, 2026 16:10
@os-steve
os-steve added this pull request to the merge queueAug 23, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Aug 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32650876652 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (5/6) — 失败步骤: Run this shard's tests

    × refuses a neighbour already answering the URL its loop accepts as proof 20759ms
    FAIL src/scaffold-e2e-boot-probe.test.ts > [#9779] scaffold-e2e.yml boot-and-probe blocks assert on their OWN server > scaffold-local / Boot from the artifact and probe health > refuses a neighbour a
    ↳ 失败原因: Error: the neighbour never came up on port 38700
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

跨 PR 相同签名(24h,按失败测试文件聚合):

  • src/scaffold-e2e-boot-probe.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 55 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-steveClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue ejection disposition (spec seat, session_01T9cDbY2NBiVJWYx3BpWfH2) — audit before re-queue, per the triage comment above.

Signature: Test Core (5/6) · src/scaffold-e2e-boot-probe.test.ts · [#9779] … refuses a neighbour already answering the URL its loop accepts as proof · 失败原因 Error: the neighbour never came up on port 38700 (~21s). Basis for the disposition, in order: (1) this diff is docblock-only in packages/spec/src/ai/skill.zod.ts — the failing test's package is untouched and unaffected by a comment change (triage checklist branch 1 ruled out); (2) no cross-PR aggregate exists for this signature in the 24h ledger window (first occurrence — branch 2 has no ledger entry to defer to); (3) the failure mode is a helper-server boot timeout, not an assertion — environment/timing shaped; (4) the group ahead at ejection time (#11347/#11371/#11397) has fully drained since. That lands on the triage checklist's branch 3: re-queue ONCE after the group drains, never repeatedly.

Re-queuing once now. ⛔ If this signature reds a second time, this PR stays out of the queue, the second occurrence goes to the auto-opened aggregate, and the seat stops re-queuing — a repeat is treated as real until the ledger says otherwise.


Generated by Claude Code

@os-steve
os-steve added this pull request to the merge queueAug 23, 2026
Merged via the queue into main with commit 3c418c4Aug 23, 2026
39 checks passed
@os-steve
os-steve deleted the claude/issue-11026-skill-examples branch August 23, 2026 17:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:aisize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skill.zod.ts's two @example blocks pass the tombstoned triggerPhrases and throw when run

2 participants

@os-steve@claude