Skip to content

Docs App workflow never runs for a docs-only PR: it compiles content/docs but triggers only on apps/docs/** #1169

Description

@os-zhuang

What is wrong

apps/docs is the only thing in this repo that compiles the MDX under content/docs:

// apps/docs/source.config.ts
export const docs = defineDocs({
  dir: '../../content/docs',
  ...
});

But .github/workflows/docs-app.yml only triggers on changes to apps/docs itself:

  pull_request:
    branches: [ main, develop ]
    paths:
      - 'apps/docs/**'
      - '.github/workflows/docs-app.yml'

So a PR that changes only content/docs/** — which is what every documentation PR in this repo is — never runs the one job that would compile those pages.

Why it matters

The workflow's own header comment states the failure it exists to prevent, and that failure is currently live for the majority of the PRs that could cause it:

apps/docs is a full Next.js + Fumadocs site (231 mdx pages) with its own package.json and its own lockfile — it is NOT part of the root pnpm project, so pnpm install / pnpm typecheck / pnpm build at the root have never touched a single file of it. A broken import, a bad mdx frontmatter block, or a dead internal link could land on main with every check green.

This workflow is the only thing that compiles it.

Nothing else covers the gap. Root pnpm validate / lint / typecheck / build walk authored metadata and never open content/docs; the repo's docs guards (test/docs-*.test.ts) read the MDX as text to pin claims against source, which catches a wrong sentence but not malformed frontmatter or MDX that fails to compile. link-check.yml does trigger on **/*.mdx, but it only checks links.

Net effect: a bad frontmatter block or an MDX syntax error in content/docs lands on main with a fully green PR, and is first seen by the push build on main (same path filter, so in fact not even then) or by a later PR that happens to touch apps/docs.

Suggested fix

Add content/docs/** (and content/blog/**, if the blog collection is sourced the same way — worth checking source.config.ts's other collections) to both the push and pull_request path filters in .github/workflows/docs-app.yml.

Worth confirming while there: the filter list should be derived from what source.config.ts actually reads, so the two cannot drift apart again the same way.

How it was found

Noticed while working #1162 (PR #1168), a docs-only change to six content/docs/revenue/*.mdx pages. Because Docs App would not run on it, the site was built locally to verify the pages compile:

$ cd apps/docs && pnpm install --frozen-lockfile && pnpm run build
✓ built — prerendered 201+ doc paths

That build passing is the reason this is filed as a CI-coverage gap rather than a broken page.

Out of #1162's declared file surface, so filed rather than folded into that PR. Unassigned — no work started.

Activity

  1. added
    documentationImprovements or additions to documentation
    ci/cdCI plumbing and the verification pipeline
    on Aug 16, 2026
  2. added
    pm:queueReady for the PM dispatch loop
    and removed on Aug 25, 2026
  3. huangyiirene commented on Aug 25, 2026

    @huangyiirene
    Collaborator

    定级(首触)→ pm:queue。本批里最该先做的一张:一整类 PR 今天没有任何东西编译它们。

    按本席常设授权定级。finding 同笔摘除。

    前提对 origin/main @ 6ed7b8d 重测

    .github/workflows/docs-app.yml:17-21
      pull_request:
        branches: [ main, develop ]
        paths:
          - 'apps/docs/**'
          - '.github/workflows/docs-app.yml'

    content/docs/** 不在过滤器里。而 apps/docs/source.config.ts 的 defineDocs({ dir: '../../content/docs' }) 是全仓唯一编译这些 MDX 的东西。前提一字未变。

    为什么这是本批优先级最高的

    其余四张记的是「守卫覆盖不到某条事实」;这张记的是一整类 PR 完全没有编译检查:

    • 本仓的文档 PR 就是只改 content/docs/** 的 PR —— 也就是说,最可能弄坏文档站的那类改动,恰恰是唯一不触发文档站构建的那类。
    • workflow 自己的头注释写明了它存在的理由:apps/docs 有自己的 package.json 与 lockfile,不属于根 pnpm 工程,根的 install / typecheck / build 从未碰过它一个文件。
    • 兜底也确实没有:根 pnpm validate / lint / typecheck / build 走的是编写的元数据,从不打开 content/docs;test/docs-*.test.ts 把 MDX 当文本读来钉claims,抓得住写错的句子,抓不住坏掉的 frontmatter 或编译不过的 MDX;link-check.yml 只查链接(而且按 守卫能力:给 content/docs 的站内链接加「锚点存在性 + 语言前缀一致性」检查(#749 / #764 / #866 各自的成因都能被它抓住) #867 的实测,它的 file-extension: '.md' 让它对 .mdx 根本空转)。

    ⇒ 一个坏 frontmatter 或 MDX 语法错误,会带着全绿的 PR 落到 main,而且因为 push 用同一套路径过滤,连 main 上都不会被发现。这不是覆盖率不足,是这条路径上没有信号。

    裁定:按卡片的建议做,并加一条防复发

    1. 把 content/docs/** 加进 docs-app.yml 的 push 与 pull_request 两处路径过滤。
    2. ⚠️ 顺带确认 source.config.ts 的其它 collection(卡片点名了 content/blog/**,若存在)—— ⛔ 不要只加一条就收工,那正是本卡记的失效模式。
    3. 过滤器清单应当从 source.config.ts 实际读的目录推导,而不是手抄一份。卡片这条建议是对的:两份手工维护的清单一定会再次漂开,而这次漂开的代价是「一整类 PR 无人编译」。若推导做不到(YAML 里没有这个能力),那就加一道断言:某个测试读 source.config.ts 的 dir 并核对 workflow 的 paths 覆盖了它。

    验收判据

    ⚠️ 必须证明它现在会跑。 加完过滤器后,本卡的验收不是「workflow 文件改了」,而是一个只改 content/docs/** 的 PR 上 Docs App 确实运行并变绿。可以用本 PR 自己做证据:顺手改一个 content/docs 下的无害字符(或就用卡片作者已经跑过的本地构建做对照),并在报告里贴出 job 的运行记录。

    ⛔ 「加了路径就完事」与「加错了路径」在证据上不可区分 —— 这条卡本身就是一个路径过滤器写了却不覆盖目标的例子。

    面:.github/workflows/docs-app.yml(+ 可能的一个断言测试)。src/ 零改动。


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 25, 2026
  5. self-assigned this
    on Aug 25, 2026
  6. huangyiirene commented on Aug 25, 2026

    @huangyiirene
    Collaborator

    Claim: PM loop round 2
    Session: session_01STpYzSP2LiZwZinh6m6Qgf
    Branch: claude/issue-1169-docs-app-path-filter
    Worktree: hotcrm-issue-1169
    Domain: (hotcrm has no domain:* taxonomy — repo-wide seat, objectstack#10282)
    File surface: .github/workflows/docs-app.yml, plus at most one assertion test if you take the derivation route (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus — S but not mechanical: the acceptance criterion is a live CI observation, not a diff
    Clause-②: no — a CI path filter.
    Serial constraints cleared: 4 open PRs, all dependabot, none touching .github/. Batch siblings #1207 (src/pages, src/actions, src/flows, src/translations) and #1196 (test/docs-drift.test.ts + siblings) — both disjoint. ⚠️ #1238 is pm:queue and also lives in .github/workflows/ — but in code-quality.yml, a different file. ⛔ Do not touch code-quality.yml here.

    Read the full issue body and every comment on GitHub yourself before starting, and verify the body is not truncated.


    Why this one is worth an S-card's care

    This is not "a workflow is missing a path". An entire class of PR has no compile check today, and that class is the majority of what this repo ships in docs:

    • apps/docs/source.config.ts does defineDocs({ dir: '../../content/docs' }) — it is the only thing in the repo that compiles those MDX pages.
    • .github/workflows/docs-app.yml:17-21 triggers on apps/docs/** and the workflow file itself. content/docs/** is not in the filter (measured on origin/main @ 6ed7b8d).
    • The workflow's own header says why it exists: apps/docs has its own package.json and lockfile and is not part of the root pnpm project, so root install / typecheck / build have never touched a file of it.
    • Nothing else covers it: root gates walk authored metadata and never open content/docs; test/docs-*.test.ts read the MDX as text; link-check.yml only checks links (and per 守卫能力:给 content/docs 的站内链接加「锚点存在性 + 语言前缀一致性」检查(#749 / #764 / #866 各自的成因都能被它抓住) #867's measurement its file-extension: '.md' makes it inert on .mdx anyway).

    ⇒ A bad frontmatter block or an MDX syntax error lands on main with a fully green PR — and because push carries the same filter, not even then.

    Scope

    1. Add content/docs/** to both the push and pull_request path filters.
    2. ⚠️ Check source.config.ts's other collections (the card names content/blog/** if the blog is sourced the same way). ⛔ Do not add one path and stop — adding a filter that misses a sibling collection is the exact failure this card records.
    3. Preferred, if cheap: make the filter list derivable from what source.config.ts actually reads, or add an assertion that the workflow's paths cover the config's dirs. If YAML cannot express it and an assertion test is disproportionate, say so and skip it — ⛔ do not build something elaborate for an S card.

    ⚠️ Acceptance is a live observation, not a diff

    ⛔ "The path was added" is not the deliverable. "Added a path filter that doesn't cover the target" is literally the bug being fixed, so a diff proves nothing.

    Prove Docs App actually runs on a content/docs-only change. The cheapest honest way: include a harmless content/docs/** edit in this very PR (a typo fix, a trailing-newline normalisation — something real, not a marker comment), then read the check runs by name on the PR and record that Docs App executed and its conclusion. Paste the evidence into the report.

    ⚠️ If it runs and goes red, that is a finding, not a failure of this card: it means content/docs has a real compile error that has been invisible. Report it — do not fix a page under this card's surface, and do not disable the workflow to get green.

    Non-negotiable

    • Gates: pnpm verify = validate → typecheck → lint → lint:i18n-gate → hygiene → hygiene:tokens → build → test. Exit codes after redirect, never through a pipe.
    • Live changeset gate (newly-added .changeset/*.md vs base sha, or skip-changeset label, or empty frontmatter). private: true does not exempt it.
    • Landing note: .github/workflows/** is not a governed surface (governed = docs/adr/** + .claude/** + skills/** + AGENTS.md + CLAUDE.md), so this is loop-mergeable.
    • PR draft, base main, first body line Fixes #1169.

    Report

    Terminal JSON report as a comment on this issue first (first line the literal os-dev-report), then as the return message. Leave your own claim comment with your own session ID and branch.

    ⚠️ Out-of-scope findings: report to the PM, do not try to file. os-dev seats get HTTP 403 from api.github.com (measured this round).


    Generated by Claude Code

  7. huangyiirene commented on Aug 25, 2026

    @huangyiirene
    Collaborator

    Claim: os-dev subagent, dispatched by the PM seat above.
    Session: session_01STpYzSP2LiZwZinh6m6Qgf (this seat runs mode:subagent under the PM session, so it reports the same session id — the distinguishing identity is the branch below)
    Branch: claude/issue-1169-docs-app-path-filter (pushed empty at start as a write probe — push OK)
    Worktree: /home/user/hotcrm-issue-1169, branched off origin/main @ 6ed7b8dd
    Assignee left untouched.

    Premise re-verified on origin/main @ 6ed7b8dd: .github/workflows/docs-app.yml push/pull_request paths = apps/docs/** + the workflow file only; apps/docs/source.config.ts exports two collections — docs (dir: '../../content/docs') and blog (dir: '../../content/blog'). Both content roots are outside the filter. Premise holds, and the sibling collection the card warned about is real.


    Generated by Claude Code


    Generated by Claude Code

  8. huangyiirene commented on Aug 25, 2026

    @huangyiirene
    Collaborator

    os-dev-report

    {
      "issue": 1169,
      "status": "done",
      "branch": "claude/issue-1169-docs-app-path-filter",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/1284",
      "premise_still_valid": true,
      "summary": "Premise re-verified on origin/main @ 6ed7b8dd and unchanged: docs-app.yml's push and pull_request paths listed only 'apps/docs/**' and the workflow file, while apps/docs/source.config.ts sources TWO collections from the repo root — docs (dir '../../content/docs') and blog (dir '../../content/blog'), both rendered by apps/docs/lib/source.ts as /docs and /blog. Both content roots added to BOTH filters. Added test/docs-app-workflow-paths.test.ts: it reads the collection dirs out of source.config.ts, walks the real files under each, and requires each file to be MATCHED by both filters through GitHub's glob semantics (same minimatch subset test/labeler-config.test.ts already uses, so no YAML or glob dependency is added); it also pins the two filters to each other and rejects a negated pattern it would mis-model. Changeset is empty-frontmatter (the sanctioned 'releases nothing' declaration changeset-check.yml documents); no src/ metadata changed; code-quality.yml untouched. SCALE OF THE GAP, measured on origin/main: Docs App has run 10 times in its entire life (this PR is run 10) and the previous run was 2026-08-05; 14 merges changed content/docs since the workflow was created on 2026-07-31 and exactly ONE ran this build, and only incidentally because it also touched apps/docs — the other 13 landed with nothing compiling them. 1 merge changed content/blog; it ran nothing either. ⚠️ CORRECTION TO THE ACCEPTANCE CRITERION, please read: the proposed evidence does not discriminate. paths: is an OR over the PR's changed files, and this PR changes .github/workflows/docs-app.yml, which was ALREADY in the filter before this branch — so Docs App fires on this PR with or without the new entries, and a token content/docs edit in the diff would not change that. A discriminating live run needs a content/**-only changed-file set WHILE the new filter is in effect, and that is not constructible pre-merge: pull_request is gated on branches [main, develop] so a docs-only PR based on this branch does not trigger at all; a docs-only PR based on main resolves the workflow from the merge commit with main, i.e. the OLD filter; push is likewise gated on main/develop. No harmless content/docs edit was included, for a second reason too: the corpus is clean (0 common typos, 0 duplicated words, 0 missing frontmatter title/description across 201 pages, 0 files without a trailing newline, 0 broken internal /docs links, 0 stray control or invisible characters), so any edit would have been a marker comment in disguise, which the dispatch rules out. The proof is therefore split into a mechanically discriminating offline half (the ablated guard, below) and the live half (Docs App green on this PR), with the post-merge observation named in the PR body as the one that closes the loop.",
      "tests": "All commands run on 8bacb346 (the final commit; the union below was run on that tree). Exit codes captured after redirect, never through a pipe. HEAVY RUNS SERIALISED through the container's shared lock: bash /home/user/objectstack/scripts/pm/os-verify-lock.sh -c '... pnpm verify' → 'os-verify-lock: VERDICT command-exit 0 · held the lock 127s (2m07s) · waited 0s'. GATE VERDICT LINES, quoted as the gates printed them: '✓ Validation passed (3418ms)' · typecheck 'tsc --noEmit' exit 0 · objectstack lint exit 0 (55 advisory issues, unchanged) · '✓ i18n lint gate: 0 `i18n/missing-*` issues' · '✓ source hygiene clean' (its control-byte scan covers .github/ and .changeset/) · '✓ source token ratchet clean' · '✓ Build complete (1296ms)' · 'Test Files  125 passed (125)' / 'Tests  2908 passed | 1 skipped (2909)'. NOT-MEASURED CHECK: the package's typecheck could have excluded test files, which would make '✓ typecheck' a true statement that says nothing about the new file — so `npx tsc --noEmit --listFiles` was run separately and grepped: 1 hit for test/docs-app-workflow-paths.test.ts, TSC_EXIT=0. The new file is genuinely in the typecheck program. ABLATION (reverse verification), predicted direction: transition to RED, and observed as predicted in both legs. No build or dist is involved — vitest reads the TypeScript sources directly and the guard reads the workflow YAML off disk — so there is no rebuild leg to state; what there IS on both legs is an on-disk mutation proof and an on-disk restore proof. The fix was COMMITTED FIRST so a restore point existed. The script carried trap '<restore>' EXIT INT TERM with an absolute REPO_ROOT from git rev-parse --show-toplevel. Mutation landed on disk was proven by fixed-string grep counts of the exact removed text, never by the editor's exit code — and that guard fired for real: the first attempt used `grep -c` (BRE, so '**' was read as a regex), counts read 2→1 instead of 2→0, the script declared 'MUTATION-PROOF: FAILURE — this ablation did NOT run' and refused to report anything; it was rerun with grep -cF. LEG 1, both content dirs removed (reproducing origin/main's filter exactly): PRE content/docs/**=2 content/blog/**=2, POST 0 and 0, 'MUTATION-PROOF: OK'; VITEST_EXIT=1 with 'Tests  2 failed | 6 passed (8)' and the guard naming the live bug — 'AssertionError: content/docs: 240/240 file(s) are not matched by on.push.paths (apps/docs/**, .github/workflows/docs-app.yml)', and the identical failure for on.pull_request. LEG 2, only content/blog/** removed (the sibling-collection failure this card warns about): PRE 2, POST 0, 'MUTATION-PROOF: OK'; VITEST_EXIT=1, 'content/blog: 3/3 file(s) are not matched by on.push.paths (apps/docs/**, content/docs/**, .github/workflows/docs-app.yml)', same for on.pull_request. RESTORE proven on BOTH legs, not assumed from the trap running: `git checkout HEAD -- \"$REPO_ROOT/<path>\"` (naming HEAD, never a bare checkout, so a polluted index cannot hand the mutation back), then blob comparison — 'RESTORE-PROOF: OK — blob c8924bd117b30f6034e05bca41e5f86a283411c2, git diff HEAD empty' on each leg, with an empty hash treated as FAILURE rather than 'nothing to compare'. LIVE CI, read by name on PR #1284 (head 8bacb346): workflow 'Docs App' = run 32856468590, event pull_request, job 'Typecheck and Build' → status completed, conclusion SUCCESS (Typecheck 4s, Build 39s, whole job 13:57:21→13:58:22Z). So the 'if it runs and goes RED that is a finding' branch resolves clean: content/docs and content/blog have no hidden compile error — the first CI evidence of that in twenty days. ⚠️ As stated in the summary, that run is necessary but NOT discriminating, because this PR touches the workflow file which was already in the filter. Other checks at report time: Quality Checks success, Check Changeset success (the empty-frontmatter changeset is accepted), link-check success, Label Pull Request success, Vercel Preview Comments success; Build and Test (22.x), Playwright and CodeQL still in_progress — left to the PM per the report-at-draft-PR rule.",
      "open_questions": [
        {
          "question": "The card's acceptance criterion ('prove Docs App runs on a content/docs-only change') cannot be discharged before this PR merges — see the summary for why the proposed evidence does not discriminate. How should acceptance be settled?",
          "options": [
            "A — accept on the split proof shipped here (offline ablation that reds on exactly main's filter, plus the live green Docs App run), and record the real observation as a one-line post-merge check on the next content/docs-only PR",
            "B — hold this PR until someone lands the filter change on main and then opens a throwaway content/docs-only PR to observe, i.e. merge-then-verify with the card left open",
            "C — widen the workflow's pull_request base-branch filter so a docs-only PR based on this branch would trigger it, purely to make the observation constructible"
          ],
          "recommendation": "A. B is the same evidence as A plus a merge, and it leaves a pm:queue card open across a merge for a check that costs one API read afterwards. C is the only option that would produce the literal observation pre-merge, and it is the wrong trade: it edits trigger semantics for the benefit of the test rather than the repo, and the branch filter is not what this card is about. What A gives up is nothing the other two recover — the residual risk after the ablation is only 'is the literal prefix right', and that is precisely what the new guard derives from source.config.ts rather than trusting. Worth noting the pattern SHAPE is separately proven live: 'apps/docs/**' in this same list demonstrably fires (run 9 was a push triggered by an apps/docs/public/** change), and 'content/docs/**' differs from it only in the prefix."
        }
      ],
      "out_of_scope_findings": [
        "NOT FILED (os-dev seats get HTTP 403 from api.github.com — PM to triage): none of the defect class found elsewhere. I surveyed every path filter in .github/workflows/ for the same 'filter does not cover what the job reads' shape and it is clean: deploy-docs.yml correctly filters docs/** + README.md for the internal-docs Pages site (a different site from the Fumadocs one); label-sync.yml correctly filters .github/labels.yml; link-check.yml correctly triggers on **/*.mdx (its known inertness is in the action's file-extension config, already recorded under #867, not in the trigger); changeset-check.yml, ci.yml, codeql.yml and e2e.yml carry no paths filter at all. code-quality.yml was not touched or assessed — it is queued separately.",
        "NOT FILED (observation, low value, PM may drop it): test/docs-app-workflow-paths.test.ts and test/labeler-config.test.ts now each carry their own copy of the same ~15-line globToRegExp minimatch subset and the same SKIP_DIRS directory walk. Deliberate for now — extracting a shared helper would have widened this S card's file surface beyond the declared one — but if a third workflow-glob guard ever appears, that is the moment to lift both into test/helpers/."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  9. huangyiirene commented on Aug 25, 2026

    @huangyiirene
    Collaborator

    PM review — ACCEPT, and my acceptance criterion was wrong. Open question answered: A. PR #1284, landing now.

    Reviewed against the repo and against GitHub, not against the report.

    Checklist: draft ✓ · base main ✓ · first body line Fixes #1169 ✓ · 3 files, all inside the declared surface ✓ · changeset newly added with empty frontmatter — the sanctioned "releases nothing" form, Check Changeset green ✓ · mergeable_state: clean ✓ · all 10 checks green, Build and Test (22.x) confirmed by NAME as completed: success ✓ · path fork: .github/workflows/ + test/ + .changeset/, none governed ⇒ loop-mergeable ✓ · code-quality.yml untouched, as fenced ✓

    ⚠️ The acceptance criterion I wrote could not discriminate, and the dev was right to say so

    My claim demanded: "include a real, harmless content/docs edit in this PR, then read the check runs by name and record that Docs App executed."

    That proves nothing. paths: is an OR over the PR's changed files, and this PR changes .github/workflows/docs-app.yml — which was already in the filter before this branch. So Docs App fires here with or without the new entries. A token content/docs edit would not have changed that by one bit.

    And the discriminating run is not constructible pre-merge, for three reasons the dev enumerated and I verified are structural: pull_request is gated on branches: [main, develop], so a docs-only PR based on this branch never triggers; a docs-only PR based on main resolves the workflow from the merge commit with main, i.e. the old filter; push is gated the same way.

    I wrote "the acceptance is a live CI observation, not a diff" — the instinct was right, the specified observation was not. Second defective dispatch instruction I have authored today (the first was #1185's assumption 3). Both have the same shape: I reasoned from an adjacent green instead of from the mechanism.

    Answer to the open question: A, on the dev's own reasoning

    • B is A plus a merge, and it leaves a pm:queue card open across a merge for a check that costs one API read afterwards.
    • ⛔ C is refused. Widening the workflow's base-branch filter so that the test becomes constructible edits trigger semantics for the benefit of the observation rather than the repo. That is the "make the gate fit the test" move, and it is the wrong direction on a card whose entire subject is a trigger that did not match reality.
    • A gives up nothing the others recover. The residual risk after the ablation is only "is the literal prefix right" — and that is precisely what the new guard derives from source.config.ts rather than trusting.

    The post-merge observation is named in the PR body as the loop-closer; it belongs on the next content/docs-only PR and costs one read.

    What I verified myself rather than reading off the report

    The sibling collection is real — this was the load-bearing half of my "don't add one path and stop" instruction, so I checked it:

    apps/docs/source.config.ts:12-13   docs  → dir: '../../content/docs'   (240 files)
    apps/docs/source.config.ts:27-29   blog  → dir: '../../content/blog'   (3 files)
    apps/docs/lib/source.ts:18-21      blogSource = loader({ baseUrl: '/blog', … })
    

    content/blog is a rendered collection, not a hypothetical — and the ablation numbers match these counts exactly (240/240, 3/3). Dropping it would have reproduced this very bug one directory over, which is the failure mode the card exists to record.

    The best things in this run

    • The guard derives its targets from source.config.ts and asserts every real file is MATCHED, through GitHub's glob semantics, reusing the minimatch subset test/labeler-config.test.ts already carries — so no YAML or glob dependency is added. Comparing path lists as strings would have been the obvious shortcut and would have been worthless: "a path filter that does not cover its target" is the bug being repaired, so the presence of a path proves nothing.
    • The mutation-proof guard fired for real. The first ablation attempt used grep -c — BRE, so ** was read as a regex — and counts read 2→1 instead of 2→0. The script declared MUTATION-PROOF: FAILURE — this ablation did NOT run and refused to report anything, then was rerun with grep -cF. That is the difference between a reverse verification and a decoration, and it caught its own defect.
    • tsc --noEmit --listFiles was run separately to confirm the new test file is genuinely in the typecheck program — because "typecheck is clean" could otherwise be a true statement about the file's absence.
    • The scale measurement, which turns this from a tidy-up into the round's most valuable card: Docs App has run 10 times in its entire life, the previous run was 2026-08-05, and of the 14 merges that changed content/docs since the workflow was created, exactly one ran this build — incidentally, because it also touched apps/docs. The other 13 landed with nothing compiling them.

    One thing the green run does establish

    Not the filter — but it is the first CI evidence in twenty days that these pages compile. So the "if it runs and goes red, that is a finding" branch of my criterion resolves clean: content/docs and content/blog carry no hidden compile error.

    Out-of-scope findings — I will file the first, dropping the second

    1. Nothing to file. The dev surveyed every other path filter in .github/workflows/ for the same "filter does not cover what the job reads" shape and found it clean (deploy-docs.yml, label-sync.yml, link-check.yml, and four workflows with no paths at all). ⇒ The class is closed, which is worth more than a card would have been.
    2. Dropped, deliberately: the duplicated ~15-line globToRegExp subset now in two test files. The dev's own read is right — extracting a helper would have widened an S card's surface, and the honest trigger is a third occurrence. Recorded here rather than as a card, so the next author meets it in the right place.

    Landing: flip ready → enable_pr_auto_merge SQUASH → verify main actually moved.


    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

Labels

ci/cdCI plumbing and the verification pipelinedocumentationImprovements or additions to documentationpm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions