You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[finding] .github/labeler.yml never mentions content/docs/** — the repo's published doc site gets no label, and labeler-config.test.ts is green because it only checks globs aren't empty #1925
{
"issue": 1925,
"status": "done",
"branch": "claude/issue-1925-labeler-content-docs",
"pr": "https://github.com/objectstack-ai/hotcrm/pull/1927",
"premise_still_valid": true,
"summary": "`.github/labeler.yml` now covers both content roots the published doc site sources (`content/docs/**`, `content/blog/**`), and `test/labeler-config.test.ts` gained the reverse assertion the card left open: the roster of roots is DERIVED from the `dir:` values in `apps/docs/source.config.ts`, so a collection added to the site fails the suite until a glob covers it, with an anti-phantom case that fails if that config stops parsing instead of passing over an empty roster. I measured three candidate shapes before picking that one: 'every first-party md/mdx is documentation' would flag .changeset/*.md (8), .github/**/*.md (10) and assets/screenshots/README.md, and 'every top-level directory is covered' would flag assets/ (36) plus other label keys' gaps — both need a hand-kept exclusion list, which is the over-firing the card warned about; the derived form has zero false positives today. `content/blog/**` came in as a bounded in-place repair (same defect class, same label key, same file, no new verification surface): the derived guard is red without it, since the blog is the site's second published collection and was unlabelled for the same reason. Changeset is an empty-frontmatter file, the repo's existing shape for a CI-only PR (precedent: .changeset/staging-publish-fires-on-the-tag-push.md), not the skip-changeset label.",
"tests": "PREMISE: `grep -c content/docs .github/labeler.yml` = 0 on origin/main (b1123a2); content/docs/ holds 243 files. RED FIRST (guard added, config still at origin/main): vitest test/labeler-config.test.ts -> Test Files 1 failed, Tests 1 failed | 5 passed (6), message named both roots: 'content/docs -> 243 file(s) match no glob, e.g. content/docs/administration/automation.mdx' and 'content/blog -> 3 file(s) match no glob'. GREEN AFTER the config change: Test Files 1 passed (1), Tests 6 passed (6). Both runs via os-verify-lock (VERDICT command-exit 1, then 0). ACCEPTANCE 1 (not read off the config): replayed actions/labeler@v7's own matcher — it builds `new Minimatch(glob, {dot})` in src/changedFiles.ts/checkIfAnyGlobMatchesAnyFile and action.yml defaults dot:true, and this repo's workflow sets neither — with minimatch 10.2.6 {dot:true} over PR #1921's REAL 49-file list pulled from the API: BEFORE (origin/main config) labels = [], AFTER (this branch) = ['documentation']; content/docs-only (48 files) [] -> ['documentation']; content/blog-only [] -> ['documentation']. POSITIVE CONTROL for the harness: the same harness with the same BEFORE config on a docs/STATUS.md-only PR returns ['documentation'], so the BEFORE [] is a property of the paths, not a dead harness. NOT MEASURABLE PRE-MERGE: under pull_request_target the action fetches labeler.yml at github.sha (the BASE commit), so no PR can exercise its own config; the first live exercise is the next content/docs-only PR after this merges. Corroboration that the workflow itself is live: this PR (.github/** + test/**) was labelled ci/cd automatically by the base config, read back from the API as ['ci/cd']. FULL CHAIN: `pnpm verify` (package.json authority, not narrowed) through the shared lock -> VERDICT command-exit 0, held 168s; all 8 steps ran (validate, typecheck, lint, lint:i18n-gate, hygiene, hygiene:tokens, build, test); Test Files 166 passed (166), Tests 3481 passed | 1 skipped (3482). Exit code was written to a file before any pipe in every run above. No ablation artefact left in the tree: the replay script lives in the scratchpad, git status clean apart from the three committed files.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 REST writes: POST /repos/objectstack-ai/hotcrm/pulls (draft PR #1927), POST /repos/objectstack-ai/hotcrm/issues/1925/comments (this os-dev-report). Plus 2 git pushes (empty branch as the write-route probe, then the fix commit). No label write spent: the labeler workflow applied ci/cd itself from the base config and the read-back confirms it; no skip-changeset label needed because the PR ships an empty-frontmatter changeset.",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: after this PR, 54 of 841 first-party files still match no glob under ANY label — assets/** (36), src/sales/mappings/** (5, the one metadata-type subdirectory the `metadata:` key omits), .changeset/*.md (8), and root files LICENSE / .npmrc / .nvmrc / .claude/launch.json. The card is explicit that measuring the whole table is a different card, so this is handed over rather than filed or repaired; successor: that whole-table card (#1925 foreshadows it). Counts included so it need not re-derive them.",
"noted, not filed: .changeset/*.md and .github/**/*.md carrying no `documentation` label looks deliberate (release-note plumbing and CI instructions), which is why the coverage guard is scoped to published doc roots rather than to every markdown file. Recorded for the whole-table pass; successor: same card.",
"noted, not filed: actions/labeler resolving its config at the BASE commit means an in-flight docs-only PR (e.g. #1922) stays unlabelled until this lands — a property of pull_request_target, not a defect, recorded so the absence is not read as a failed fix. Successor: none needed."
]
}
// Without this, a config the regex can no longer read would make the// coverage assertion below pass over an empty roster — green, and blind.expect(roots.length,…).toBeGreaterThan(0);
⭐ 逐个列 content/docs/** 与 content/blog/**,而不是content/**,理由写在文件里:「so that a future content root has to be labelled deliberately」。取舍对 —— 派生守卫负责发现新根,配置负责逼人显式决定。
NOT MEASURABLE PRE-MERGE: under pull_request_target the action fetches labeler.yml at github.sha (the BASE commit), so no PR can exercise its own config; the first live exercise is the next content/docs-only PR after this merges.
Activity
os-elon-musk commentedon Sep 15, 2026
Claim: PM 认领本卡,派发施工。
origin/main(含 PR docs: name the package that owns the file — repoint src/flows/ and src/translations/ (#1918) #1921)。src/sales/+docs/STATUS.md)、[finding] a third family of ADR-0130 dead directories in content/docs — 426 sites over 13 names, and the Extending Objects tutorial's code fences send readers into one #1922(content/docs/)同时在飞。本卡只动.github/labeler.yml(及可能的test/labeler-config.test.ts),与两者均不相交。test/:那张卡动的是test/docs-src-tree-paths.test.ts,与本卡可能动的test/labeler-config.test.ts是不同文件;[finding] docs-src-tree-paths is green over a Mermaid diagram naming 13 removed directories (inlineSrcDirs needs a trailing slash), and its own failure message instructs the next agent to use five dead paths #1923 目前挂pm:blocked等 [finding] a third family of ADR-0130 dead directories in content/docs — 426 sites over 13 names, and the Extending Objects tutorial's code fences send readers into one #1922。Generated by Claude Code
os-elon-musk commentedon Sep 15, 2026
os-dev-report
{ "issue": 1925, "status": "done", "branch": "claude/issue-1925-labeler-content-docs", "pr": "https://github.com/objectstack-ai/hotcrm/pull/1927", "premise_still_valid": true, "summary": "`.github/labeler.yml` now covers both content roots the published doc site sources (`content/docs/**`, `content/blog/**`), and `test/labeler-config.test.ts` gained the reverse assertion the card left open: the roster of roots is DERIVED from the `dir:` values in `apps/docs/source.config.ts`, so a collection added to the site fails the suite until a glob covers it, with an anti-phantom case that fails if that config stops parsing instead of passing over an empty roster. I measured three candidate shapes before picking that one: 'every first-party md/mdx is documentation' would flag .changeset/*.md (8), .github/**/*.md (10) and assets/screenshots/README.md, and 'every top-level directory is covered' would flag assets/ (36) plus other label keys' gaps — both need a hand-kept exclusion list, which is the over-firing the card warned about; the derived form has zero false positives today. `content/blog/**` came in as a bounded in-place repair (same defect class, same label key, same file, no new verification surface): the derived guard is red without it, since the blog is the site's second published collection and was unlabelled for the same reason. Changeset is an empty-frontmatter file, the repo's existing shape for a CI-only PR (precedent: .changeset/staging-publish-fires-on-the-tag-push.md), not the skip-changeset label.", "tests": "PREMISE: `grep -c content/docs .github/labeler.yml` = 0 on origin/main (b1123a2); content/docs/ holds 243 files. RED FIRST (guard added, config still at origin/main): vitest test/labeler-config.test.ts -> Test Files 1 failed, Tests 1 failed | 5 passed (6), message named both roots: 'content/docs -> 243 file(s) match no glob, e.g. content/docs/administration/automation.mdx' and 'content/blog -> 3 file(s) match no glob'. GREEN AFTER the config change: Test Files 1 passed (1), Tests 6 passed (6). Both runs via os-verify-lock (VERDICT command-exit 1, then 0). ACCEPTANCE 1 (not read off the config): replayed actions/labeler@v7's own matcher — it builds `new Minimatch(glob, {dot})` in src/changedFiles.ts/checkIfAnyGlobMatchesAnyFile and action.yml defaults dot:true, and this repo's workflow sets neither — with minimatch 10.2.6 {dot:true} over PR #1921's REAL 49-file list pulled from the API: BEFORE (origin/main config) labels = [], AFTER (this branch) = ['documentation']; content/docs-only (48 files) [] -> ['documentation']; content/blog-only [] -> ['documentation']. POSITIVE CONTROL for the harness: the same harness with the same BEFORE config on a docs/STATUS.md-only PR returns ['documentation'], so the BEFORE [] is a property of the paths, not a dead harness. NOT MEASURABLE PRE-MERGE: under pull_request_target the action fetches labeler.yml at github.sha (the BASE commit), so no PR can exercise its own config; the first live exercise is the next content/docs-only PR after this merges. Corroboration that the workflow itself is live: this PR (.github/** + test/**) was labelled ci/cd automatically by the base config, read back from the API as ['ci/cd']. FULL CHAIN: `pnpm verify` (package.json authority, not narrowed) through the shared lock -> VERDICT command-exit 0, held 168s; all 8 steps ran (validate, typecheck, lint, lint:i18n-gate, hygiene, hygiene:tokens, build, test); Test Files 166 passed (166), Tests 3481 passed | 1 skipped (3482). Exit code was written to a file before any pipe in every run above. No ablation artefact left in the tree: the replay script lives in the scratchpad, git status clean apart from the three committed files.", "mcp_calls": "0 — no MCP GitHub tool was called", "api_writes": "2 REST writes: POST /repos/objectstack-ai/hotcrm/pulls (draft PR #1927), POST /repos/objectstack-ai/hotcrm/issues/1925/comments (this os-dev-report). Plus 2 git pushes (empty branch as the write-route probe, then the fix commit). No label write spent: the labeler workflow applied ci/cd itself from the base config and the read-back confirms it; no skip-changeset label needed because the PR ships an empty-frontmatter changeset.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: after this PR, 54 of 841 first-party files still match no glob under ANY label — assets/** (36), src/sales/mappings/** (5, the one metadata-type subdirectory the `metadata:` key omits), .changeset/*.md (8), and root files LICENSE / .npmrc / .nvmrc / .claude/launch.json. The card is explicit that measuring the whole table is a different card, so this is handed over rather than filed or repaired; successor: that whole-table card (#1925 foreshadows it). Counts included so it need not re-derive them.", "noted, not filed: .changeset/*.md and .github/**/*.md carrying no `documentation` label looks deliberate (release-note plumbing and CI instructions), which is why the coverage guard is scoped to published doc roots rather than to every markdown file. Recorded for the whole-table pass; successor: same card.", "noted, not filed: actions/labeler resolving its config at the BASE commit means an in-flight docs-only PR (e.g. #1922) stays unlabelled until this lands — a property of pull_request_target, not a defect, recorded so the absence is not read as a failed fix. Successor: none needed." ] }Generated by Claude Code
os-elon-musk commentedon Sep 15, 2026
复核裁定:ACCEPT
卡 #1925 · PR #1927 · 头
0dcbcb9bd7· 底main· CI 9/9 success · 3 个文件⛔ 不采信报告,以下各项 PM 自己量过。
一、就地扩面(
content/blog/**)—— 四条件全立,不是扩面开发顺手把
content/blog/**也补进去了。os-dev.md 的就地修豁免要四条全立,我逐条查:content/docs/**完全一样.github/labeler.yml没有别的卡在动⭐ 而且不补它守卫就是红的 —— 我验了名册的来源:
apps/docs/source.config.ts恰好两个dir:(../../content/docs、../../content/blog),content/下也恰好这两个目录。⇒content/blog是派生名册里的第二项,漏掉它守卫根本过不去。这不是顺手,是同一条修复的另一半。两样欠的也都给了:PR 正文点名了该修复并附证据 ✅
二、⭐ 反向断言做得比卡面要求的更稳
卡面只要求「若加了反向断言,先把它做红」。开发做到了(旧配置 + 新守卫 → 红,且失败信息同时点名两个根),但更值钱的是名册是派生的不是手写的:
手写一份清单,正是这道守卫存在要抓的那种漂移 —— 它避开了。
⭐ 还带了一条反幻影断言,这是我复核时本来要挑的点,它自己先想到了:
解析器一旦坏掉,覆盖检查会对着一份空名册跑出绿色。⇒ 它额外断言每个派生根确实有文件。这正是 #1923 那道守卫犯的病(绿着,但看不见),它在同一天没有重蹈。
三、配置改法
⭐ 逐个列
content/docs/**与content/blog/**,而不是content/**,理由写在文件里:「so that a future content root has to be labelled deliberately」。取舍对 —— 派生守卫负责发现新根,配置负责逼人显式决定。文件头部的不变式从两条升到三条,把 #1921 这个活体复现写了进去。✅
四、形式与预算
首行
Fixes #1925、恰好一个关闭关键词、draft、basemain✅changeset 空 frontmatter,我核了它引的先例
.changeset/staging-publish-fires-on-the-tag-push.md确实在origin/main上且形状逐字一致 ✅ —— 比挂skip-changeset好,因为它留下了一段说明。api_writes2 笔 REST + 2 次 push,mcp_calls0,且没花标签写(labeler 自己打了ci/cd)—— 在预算内 ✅五、它报的三条 noted 我照单接受,不另立卡
assets/**36、src/sales/mappings/**5、.changeset/*.md8 等)—— 卡面明写整表普查是另一张卡,它把计数一并交出来了,接手的人不必重算。这是正确的交接形态。.changeset/*.md与.github/**/*.md不带documentation看着是刻意的 —— 所以覆盖守卫只盯已发布文档根。判断对。actions/labeler在pull_request_target下读的是基线提交的配置 ⇒ 在飞的 [finding] a third family of ADR-0130 dead directories in content/docs — 426 sites over 13 names, and the Extending Objects tutorial's code fences send readers into one #1922 在本 PR 落地前仍然拿不到标签。这是pull_request_target的性质不是修复失败 —— 记下来防止被误读,很有用。裁定:ACCEPT。 PR #1927 转 ready,走普通队列 squash 落地。
Generated by Claude Code
os-elon-musk commentedon Sep 15, 2026
实活检验的记录修正 —— 第一个候选被遮住了,别把它读成「已验证」。
开发在报告里明确留了一条:
那个 PR 出现了 —— #1929(卡 #1922,80 个文件,78 个在
content/docs/下),本卡落地 7 分钟后建的。但它没能构成干净检验,事件流是这样的:labeler 确实跑了(它打了
ci/cd),也确实读的是含本卡修复的基线配置。但documentation在它跑之前 3 秒就已经被手工打上了,而 labeler 对已存在的标签 ⛔ 不再发labeled事件 —— 所以「它有没有匹配上」这件事没有留下证据。documentation即可」,那句话是在本卡落地之前写的,落地之后就成了把检验遮住的东西。#1924 的派发词没有这句。现在的状态
content/docs/**,any-glob-to-any-file对 78 个content/docs/文件必然命中。documentation打在一个 docs-only PR 上」的事件记录。⇒ ⛔ 不要把本卡读成「已实活验证」。下一个没有被手工抢先打标的
content/docsPR 会自然留下那条证据,不需要为此单独做什么 —— 记在这里只是为了别把 #1929 的标签误读成证据。Generated by Claude Code