Skip to content

[finding] objectstack-platform skill still teaches that an unknown top-level defineStack key is silently stripped — #8687 made it throw #9907

Description

@os-steve

Found while working #9867 (rewriting packages/drivers/driver-sql/README.md), where skills/objectstack-platform/rules/bootstrap-patterns.md was the authority for the correct form. Its prescription is right — drivers are plugins, not a driver: key — but the mechanism it states for why the wrong form is wrong has been stale since #8687.

The claim

Three places say an undeclared top-level key is dropped without error:

  • skills/objectstack-platform/rules/bootstrap-patterns.md:6-9
    "There is no driver: key on defineStack() — unknown keys are silently stripped by strict parsing, so a driver: entry is a no-op."
  • skills/objectstack-platform/rules/bootstrap-patterns.md:69
    the heading "### ❌ Incorrect — driver: Key (Silently Stripped)"
  • skills/objectstack-platform/rules/bootstrap-patterns.md:74
    the inline comment "// ❌ Not a defineStack key — no-op"

skills/objectstack-platform/SKILL.md:189-190 states the general form of the same rule:
"Unknown keys are silently stripped by strict parsing — a phantom key like roles: or policies: is a no-op, not an error."

Measured

ObjectStackDefinitionSchema is built with strictObject(...) (packages/spec/src/shared/strict-object.ts:327), which is z.object(shape, …).strict(). The schema's own history slot records the change: "Until #8687 closed this surface (the outermost #4001 door), an unknown top-level stack key parsed green and its value was silently dropped."

Executed against the built @objectstack/spec:

defineStack({ manifest: {…}, driver: {…} })
  -> THREW: defineStack validation failed (1 issue):
     (root): Unrecognized key(s) on this stack definition: `driver`. …

tsc refuses it too: TS2353: Object literal may only specify known properties, and 'driver' does not exist in type 'ObjectStackDefinitionInput'.

Why it matters

An agent that believes the key is a silent no-op will rank the mistake as cosmetic and may leave it in place, or spend a debugging pass looking for a runtime symptom that a load-time refusal already produced loudly. The direction of the error is the harmful one: the doc understates the failure, so it reads as "harmless" when the real behaviour is "the stack does not load".

The general sentence in SKILL.md:189 also needs re-measuring per key family rather than a blanket rewrite — ObjectSchema / FieldSchema still only warn (warnUnknownAuthoringKeys in packages/spec/src/stack.zod.ts, scheduled for #4001's strict tiers), so "stripped" remains true at those two sites and is false at the top level. Whoever fixes this should state which surfaces refuse and which warn, not flip the whole page.

Scope note: these are governed surfaces under skills/, so #9867 did not touch them. Filing unassigned for triage.

Refs: #9867 · #8687 · #4001


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 19, 2026
  2. os-warren commented on Aug 19, 2026

    @os-warren
    Collaborator

    Claim: session ae0d8bd1-5667-5f58-8ff8-fa22ec154325 · branch claude/issue-9907-definestack-throws

    首触定级(own-lane,S):pm:queue→派发。方向照卡:三处「silently stripped」改为实测行为(#8687 后顶层 throw + tsc 拒绝),SKILL.md:189 的通则逐 key family 重测后分面陈述(顶层 refuse / Object・Field 仍 warn,#4001 tiers 待到),⛔ 不整页翻转。skills/** 治理面 ⇒ draft PR + review request os-zhuang,人合;发布目录无棘轮(#9923 裁定有意不覆盖),行数无付账约束但语域从文件现状。模型档:路径无强制、不改合同行为(改的是对行为的描述)→ opus。


    Generated by Claude Code

  3. os-warren commented on Aug 19, 2026

    @os-warren
    Collaborator
    {
      "issue": 9907,
      "status": "done",
      "branch": "claude/issue-9907-definestack-throws",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/10038",
      "premise_still_valid": true,
      "premise_note": "The CARD's primary premise holds and is confirmed by live measurement: the three bootstrap-patterns.md sites and SKILL.md:189-190 taught silent stripping, and the top level now refuses. The card's SECONDARY premise — that ObjectSchema/FieldSchema 'still WARN' — is FALSIFIED: both refuse now on the same parse path. The page therefore states the split as measured (refused vs warned-then-dropped), not the split the card and dispatch predicted.",
      "summary": "Corrected four sites in skills/objectstack-platform/ that stated an undeclared top-level defineStack key is silently stripped and therefore a no-op. Prescriptions unchanged (drivers are plugins); only the stated mechanism moved to the measured load-time refusal plus the TS2353 compile-time refusal, quoting real output. SKILL.md's blanket sentence was replaced with the re-measured per-surface split rather than flipped wholesale: refused = defineStack top level, ObjectSchema, FieldSchema; warned-then-dropped = the still-strip-mode surfaces, of which listLintableAuthoringCollections() reports exactly connectors and views. A fourth site (SKILL.md feature-flags aside) came from the prescribed sweep and was fixed under the bounded in-place exemption, named in the PR body.",
      "tests": "All at final commit 5e075eb78 (rev-parse --short HEAD from the same run). BUILD: `scripts/pm/os-verify-lock.sh -c 'pnpm --filter @objectstack/spec build'` -> `os-verify-lock: VERDICT command-exit 0 · held the lock 216s (3m36s) · waited 0s`. GATE DERIVATION: `node scripts/pm/dispatch-gates.mjs` no args -> `change set derived from git — 2 path(s) vs merge base 09b880b0c ... (committed 2, working tree 0, untracked 0)`, matched 1 family; residue placed all 115 (1 matched / 35 undetermined / 79 silent). GATE UNION re-run on the final commit, exit codes captured by redirect-then-$? (never through a pipe), each row the gate's OWN verdict line: check:pm-governed-merges EXIT=0 `✓ check-governed-merges --self-test: 81 assertions ...`; check:skill-compatibility EXIT=0 `✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 77 workspace packages`; check:skill-frame-sync EXIT=0 `✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files`; check:skill-frame-freshness EXIT=0 `✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).`; check:pm-skill-ratchet EXIT=0 `✓ check-skill-line-ratchet: AGENTS.md is 958 lines (ceiling 958; headroom 0).`; check:pm-skill-id-lint EXIT=0 `✓ check-skill-id-lint: 15 file(s) clean (pattern /#[0-9]{3,}/g).`; check:pm-governed-prose EXIT=0 `✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces (docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md) and claim no others.`; check:nul-bytes EXIT=0 `check-nul-bytes: OK (scanned 6355 text file(s) -- 6355 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).` No ablation was performed and none is claimed — this is a docs-only change with no guard to mutate; the two live measurements are direct positive legs, each with its own negative control (below), so no rebuild-of-a-mutated-dist question arises.",
      "re_measurements_verbatim": {
        "runtime_top_level_refusal": "defineStack validation failed (1 issue):\n\n  ✗ (root): Unrecognized key(s) on this stack definition: `driver`. Until #8687 closed this surface (the outermost #4001 door), an unknown top-level stack key parsed green and its value was silently dropped — a one-character typo could ship an artifact missing a whole metadata family while `os validate` exited 0. The declared keys are enumerated by `ObjectStackDefinitionSchema` (@objectstack/spec, stack.zod.ts) and in the stack-definition reference docs.",
        "compile_time_refusal": "probe.ts(5,3): error TS2353: Object literal may only specify known properties, and 'driver' does not exist in type 'ObjectStackDefinitionInput'.   [tsc exit 2]",
        "object_schema_MEASURED_REFUSES_not_warns": "✗ objects.0: Unrecognized key(s) on this object: `bogusObjectKey`. Until #4001 closed this shape these were dropped silently on the PARSE path — `ObjectSchema.create()` has rejected them since #1535, but `defineStack({ objects })`, `/api/v1/meta/types/object` and the Studio form all go through `parse()`, which did not.",
        "field_schema_MEASURED_REFUSES_not_warns": "✗ objects.0.fields.title: Unrecognized key(s) on this field: `bogusFieldKey`. Until #4001 closed this shape these were dropped silently — the field was still created, minus whatever the key was meant to constrain, protect or compute.",
        "warn_family_live_case_connectors": "WARN: defineStack: connectors.stripe.bogusConnectorKey: 'bogusConnectorKey' is not a declared connector key, so its value is dropped at load.\nNO THROW.\nbogusConnectorKey survived the parse? false",
        "top_level_lint_now_silent_by_construction": "lintUnknownStackKeys({manifest, driver:{}}, ObjectStackDefinitionSchema) -> []  (returns [] unless posture is 'strip'; the strict root reports once, not twice)",
        "remaining_warn_population": "listLintableAuthoringCollections() -> [{collection:'connectors',type:'connector'},{collection:'views',type:'view'}]"
      },
      "reverse_verification": {
        "predicted_direction_in_writing": "Predicted BEFORE running: with the offending key present, both legs go RED (runtime throw; tsc non-zero); with it removed, both go GREEN. Standard direction — no inversion or diagnostic-count effect expected, since nothing here feeds a counting gate.",
        "observed": "As predicted, both legs. Runtime positive: defineStack THREW on `driver` and on `roles`. Runtime control: a well-formed stack returned normally (the connectors case returned an object). Compile positive: tsc exit 2 with TS2353 naming 'driver'. Compile control: the identical probe with `driver:` deleted compiled clean, exit 0 — proving the probe measures the key and not a broken tsconfig/type resolution.",
        "note": "Both legs read the FRESHLY BUILT dist (packages/spec/dist/index.mjs and dist/index.d.mts) produced by the verify-locked build above, not a cached or source-aliased path."
      },
      "per_site_edits": [
        "skills/objectstack-platform/rules/bootstrap-patterns.md (opening) — 'unknown keys are silently stripped by strict parsing, so a driver: entry is a no-op' -> prescription retained verbatim in substance; mechanism restated as load-time refusal, quoting the measured error block and the TS2353 line.",
        "skills/objectstack-platform/rules/bootstrap-patterns.md (heading) — '### ❌ Incorrect — `driver:` Key (Silently Stripped)' -> '### ❌ Incorrect — `driver:` Key (Rejected at Load)'.",
        "skills/objectstack-platform/rules/bootstrap-patterns.md (inline comment) — '// ❌ Not a defineStack key — no-op' -> '// ❌ Not a defineStack key — defineStack throws'.",
        "skills/objectstack-platform/SKILL.md (Full Configuration Reference, was 189-190) — blanket 'Unknown keys are silently stripped ... a no-op, not an error' -> the measured per-surface split: Refused (top level, ObjectSchema, FieldSchema, incl. TS2353) vs Warned-then-dropped (still-open shapes, connectors named), with the real refusal output and the real connector warning quoted. Page NOT flipped wholesale.",
        "skills/objectstack-platform/SKILL.md (Feature Flags, was 1045) — '(strict parsing silently strips unknown keys)' -> '(writing one is refused at load, not stripped)'. FOURTH site, found by the prescribed sweep, taken under the bounded in-place exemption (same defect class; mechanical, correct form pinned by the measurements above; no other claim on the file; same gate family; no new verification surface) and named explicitly in the PR body."
      ],
      "sweep_result": "Rescan of skills/objectstack-platform/ for `silently strip|silently stripped|stripped by strict|no-op|Silently Stripped` now returns exactly two hits, both the NEGATIONS this PR wrote ('is not a harmless no-op', 'is not a silent no-op'). skills/objectstack-platform/evals/README.md:16 mentions phantom keys (driver:, workflows:, approvals:) as a test description — still accurate, deliberately untouched.",
      "labels_read_back": ["documentation", "size/s", "skip-changeset"],
      "changeset_decision": "skip-changeset, not a changeset. Verified rather than assumed: `skills/` appears in no package's published `files` list (73 package manifests scanned, 0 hits), so the PR releases nothing; and the label is a real mechanism in THIS repo (pr-automation.yml gates changeset-check on it, with the live label read as authority). Precedent followed: PR #9924, a skills-only PR merged the same day, carries documentation + size/s + skip-changeset with the same stated reasoning. Label applied by me as a union write (read size/s first, wrote size/s + documentation + skip-changeset) and confirmed by read-back above, not by declaration.",
      "pr_state_read_back": {
        "number": 10038,
        "url": "https://github.com/objectstack-ai/objectstack/pull/10038",
        "state": "open",
        "draft": true,
        "merged": false,
        "requested_reviewers": ["os-zhuang"],
        "head_sha": "5e075eb788ddc67c8d7d73deafec5cf62d1e77ba",
        "base": "main",
        "changed_files": 2,
        "additions": 47,
        "deletions": 8,
        "body_first_line": "Fixes #9907",
        "signoff_footer": "session-URL form, survived creation",
        "note": "Never flipped ready, never queued, never armed for auto-merge. CI convergence deliberately NOT awaited — reported at draft-PR time per the standing contract."
      },
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #10039: `views` unknown-key lint warns 'its value is dropped at load' for a key the very next parse step REFUSES (strict view container), the disagreeing-second-voice failure lintUnknownStackKeys explicitly avoids at the top level — root cause is the walker reading posture off the strip-mode view ITEM schema while defineStack parses the strict CONTAINER; same issue records that lintUnknownAuthoringKeys returns the identical finding twice per key, masked only by warnUnknownAuthoringKeys' module-level dedupe set. Filed unassigned, no label (concrete defect, left for PM triage)."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  4. os-warren commented on Aug 19, 2026

    @os-warren
    Collaborator

    ACCEPT — PR #10038 复核通过(治理面 published catalog:保持 draft、review request os-zhuang 已挂、等维护者合并)。

    复核记录(session ae0d8bd1-5667-5f58-8ff8-fa22ec154325):

    • 亲取 diff:四处「silently stripped」修正为实测行为,处方零改动(drivers 是 plugins 的规则原样),错误输出与 TS2353 逐字引自新鲜构建的 dist 实测(verify-lock 入口,held 216s)✓。
    • 卡的次要前提被实测推翻并如实改写:ObjectSchema/FieldSchema 并非「仍 warn」—— parse 路径已 refuse;真正的 warn 族由 listLintableAuthoringCollections() 实测枚举 = connectors + views。文档按测得的分面写(refused vs warned-then-dropped),不按卡与派发词预测的分面写 —— 这正是「逐 family 重测」条款要买的东西 ✓。
    • 双腿反向验证各带阴性对照(删 key 后 runtime 正常返回 / tsc exit 0,证明探针测的是 key 不是坏 tsconfig)✓。第四处(Feature Flags 旁注)来自扫尾,bounded 豁免,PR 正文点名 ✓;残留 grep 仅剩本 PR 写下的两处否定句 ✓。
    • changeset 决策查证而非沿袭:73 个 manifest 扫描 skills/ 零发布 + pr-automation.yml 的 label 门实读 + docs(skills): state the published heavy-verify lock as properties, not a broken recipe #9924 先例 ✓。
    • Out-of-scope views unknown-key lint says "dropped at load" for a key the next step refuses — the second disagreeing voice lintUnknownStackKeys avoids at the top level #10039(views lint 「второй声音」缺陷,spec 侧)未标签留分诊路由 —— 非本道,正确。

    Generated by Claude Code

  5. os-warren commented on Aug 20, 2026

    @os-warren
    Collaborator

    Supersedes the earlier report on this issue (fix round for the CI red).

    {
      "issue": 9907,
      "status": "done",
      "branch": "claude/issue-9907-definestack-throws",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/10038",
      "premise_still_valid": true,
      "summary": "Fix round for a REAL red in my own diff, confirmed reproducible locally — not the registry.ts flake. My SKILL.md addition quoted the measured refusal using the ADR-0090 D3 reserved word as the phantom-key example, taking that file from its baselined 2 occurrences to 3. Remedied author-side by swapping the example key to `policies` (already the sibling example in the sentence directly above), re-measured live rather than hand-edited. Added as a second commit 8e1b43c01; no amend, no force-push. Baseline untouched.",
      "tests": "All at final commit 8e1b43c01. REPRODUCED FIRST: `pnpm check:role-word` EXIT=1 -> `check-role-word: 1 problem(s)` / `• skills/objectstack-platform/SKILL.md: role-word count grew 2 → 3. New occurrences are banned (ADR-0090 D3).` — identical to the CI line, so the failure was mine and local. REBUILD for the re-measurement: `scripts/pm/os-verify-lock.sh -c 'pnpm --filter @objectstack/spec build'` -> `os-verify-lock: VERDICT command-exit 0 · held the lock 128s (2m08s) · waited 0s`. AFTER THE FIX, union re-run at the new head, exit codes captured by redirect-then-$? (never through a pipe), each row the gate's OWN verdict line: check:role-word EXIT=0 `check-role-word: OK, no new occurrences of the reserved word.` (`Scanned: 216 .md/.mdx file(s) across 2 root(s) — content/docs 180, skills 36. Ledger: 43 baselined file(s) still carrying it (129 occurrence(s))`); check:pm-governed-merges EXIT=0 `✓ check-governed-merges --self-test: 81 assertions ...`; check:skill-compatibility EXIT=0 `✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 77 workspace packages`; check:skill-frame-sync EXIT=0 `✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files`; check:skill-frame-freshness EXIT=0 `✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).`; check:pm-skill-ratchet EXIT=0 `✓ check-skill-line-ratchet: AGENTS.md is 958 lines (ceiling 958; headroom 0).`; check:pm-skill-id-lint EXIT=0 `✓ check-skill-id-lint: 15 file(s) clean (pattern /#[0-9]{3,}/g).`; check:pm-governed-prose EXIT=0 `✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces ...`; check:nul-bytes EXIT=0 `check-nul-bytes: OK (scanned 6355 text file(s) ... no raw ASCII control bytes).` Re-derivation at the new head: `node scripts/pm/dispatch-gates.mjs` no args -> merge base 09b880b0c, committed 2 / working tree 0 / untracked 0, 1 family matched.",
      "remedy_detail": {
        "gate_header_read_first": "scripts/check-role-word.mjs — WORD = /\\brole(?:s)?\\b/gi, case-insensitive, ROOTS = ['content/docs', 'skills'], .md/.mdx, names count too. It is a RATCHET: fails on a new file carrying the word, on a baselined count INCREASING, and on a count DECREASING (ratchet down via --update). `--update` rewrites the ENTIRE baseline from the current tree and is marked ⛔ MAINTAINER-ONLY — so it was not touched.",
        "occurrences_before": "3 in skills/objectstack-platform/SKILL.md — line 189 `roles:` (pre-existing, part of the phantom-key sentence), line 195 `roles` (MINE, in the quoted refusal), line 292 `Database role` (pre-existing).",
        "occurrences_after": "2 — line 189 and line 292, both pre-existing and byte-identical. Only my line 195 changed.",
        "baseline_untouched": "`git diff --stat scripts/role-word-baseline.json` -> empty. Entry for this file remains 2.",
        "quote_is_a_real_measurement_not_an_edit": "Re-ran the defineStack probe against the rebuilt spec with `policies` as the offending key. Measured output: `defineStack validation failed (1 issue):` / `  ✗ (root): Unrecognized key(s) on this stack definition: `policies`. Until #8687 closed this surface (the outermost #4001 door), an unknown top-level stack key parsed green and its value was silently dropped — ...`. Control in the same run: the same stack with the phantom key deleted loaded normally (`NO THROW — loaded, manifest id: com.example.ctl`), so the probe measures the key."
      },
      "reverse_verification": {
        "predicted_direction_in_writing": "Predicted before running: check:role-word RED at count 3 while the reserved word is present in my added quote, GREEN at count 2 once and only once my occurrence is removed — no baseline edit involved in either direction. Separately, the re-measured refusal with `policies` was predicted to throw with the same envelope as `driver`/`roles`, differing only in the echoed key.",
        "observed": "Exactly as predicted, both. The gate went 1 -> 0 exit on removing my single occurrence; the refusal reproduced with `policies` echoed and the rest of the message identical. No inversion, no diagnostic-count effect."
      },
      "pr_state_read_back": {
        "number": 10038,
        "url": "https://github.com/objectstack-ai/objectstack/pull/10038",
        "state": "open",
        "draft": true,
        "merged": false,
        "requested_reviewers": ["os-zhuang"],
        "head_sha": "8e1b43c0122114c2b53f0d99a6edfbfd5fd06bf9",
        "commits": 2,
        "push_kind": "fast-forward (5e075eb78..8e1b43c01), never forced",
        "body_updated_to_new_head": true,
        "ci_at_report_time": "Only the Vercel context had reported (`success`, 'Canceled by Ignored Build Step'); the Lint & Repo Gates run had not yet posted. NOT awaited, per the standing draft-PR reporting contract."
      },
      "labels_read_back": ["documentation", "size/s", "skip-changeset"],
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #10039 (previous round): `views` unknown-key lint warns 'its value is dropped at load' for a key the very next parse step REFUSES, plus a duplicate finding per key masked by the module-level dedupe set. Unassigned, no label.",
        "NOT FILED — for PM triage, the #9964 class one gate over: `check-role-word.mjs` does NOT declare its `skills/` population in a form `dispatch-gates` can extract, which is exactly why my derived union missed it. The script's population is `ROOTS = ['content/docs', 'skills']`, but `node scripts/pm/dispatch-gates.mjs --residue` shows the gate as `names: content/docs, scripts/role-word-baseline.json` — the `skills` half is absent. Cause is the documented `hintCovers` refusal: a bare single-segment literal with no path separator is rejected as too generic (dispatch-gates.mjs prints this verdict verbatim as `'<hint>' — the tree HAS it; the covering rule refuses the literal as too generic (no path separator)`). So the gate scores `silent` for EVERY `skills/**` card while genuinely covering skills/, and any skills-only card that trusts the derivation will miss it the same way mine did. Remedy shape is already established in the same docblock — declare the subtree spelling (`skills/**`), the way AGENTS.md reached its own coverage. Deliberately NOT fixed in this PR (out of scope, different gate); noted for the PM to file."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  6. os-warren commented on Aug 20, 2026

    @os-warren
    Collaborator

    PM 修复轮验收:ACCEPT(PR #10038 @ 8e1b43c)

    亲取 diff 复核(PR #10038 全量 diff,两文件,+47/−8):

    • 引用的拒绝示例键已由 roles 换为 policies,且是活测重录而非手改引文 —— 报告附了同轮控制组(删掉幻键后 NO THROW — loaded, manifest id: com.example.ctl),证明探针测的是键本身。policies 正是上一句既有的姊妹示例,语义零漂移。
    • 首轮的四处「silently stripped→载入即拒」修正原样保留;per-surface 拆分(拒绝:顶层/ObjectSchema/FieldSchema;警告后丢弃:connectors/views)未动。
    • scripts/role-word-baseline.json 未触碰(git diff --stat 为空),该文件回到基线 2 处 —— 作者侧收敛,未走 maintainer-only 的 --update。
    • 第二 commit fast-forward 推送(5e075eb..8e1b43c),无 amend 无 force。
    • 修复纪律正确:先在本地复现了 CI 的同一行红(role-word count grew 2 → 3),再修,再全 union 重跑(9 gate 全绿,含 check:role-word: OK … Scanned: 216 … across 2 root(s)),方向预测双向命中。

    CI 现状:8e1b43c01 上 Lint & Repo Gates 又红,但这次是 #10071 既知 flake 第 6 击(packages/spec/src/migrations/registry.ts 0:0 Parsing error: Maximum call stack size exceeded,run 32324337564)—— 本 PR 只动 skills/** 两个 .md,该文件与 diff 无关,签名与前 5 击逐字相同。已按 #10041 先例做了一次 branch-update 重触发(本 PR 对该 flake 是第一击,未越过我在 #10075 上宣布的停手线;⛔ 未用空 commit)。若重触发再中,本 PR 也进入 #10071 冻结队列。

    状态注记:PR 已被会话外操作翻为 ready(约 02:21Z,与 #9982/#10089 同模式,推定 maintainer 所为,不回翻)。治理面(skills/**)按规仍由 maintainer 合并;os-zhuang review request 在位。

    Out-of-scope 发现(check-role-word 的 skills 根对 dispatch-gates 不可见)由 PM 另立卡跟进。


    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

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions