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] the Translations workflow path filter misses .github/scripts/lib/, so a shared-module edit does not run the gates that consume it #221
Notation: this repo's issue-body sanitizer strips angle brackets and decodes HTML numeric character references, including inside code fences. So glob wildcards are written out as words where a bracket would otherwise appear, and no placeholder uses angle brackets.
The reading
#216 introduced .github/scripts/lib/derived-locales.mjs, a shared leaf module holding DERIVED_FROM — the fact that zh-Hant is generated from zh-Hans. Both check-translations.mjs and check-translation-output.mjs import it.
.github/scripts/check-translation*.mjs does not match .github/scripts/lib/derived-locales.mjs — the * wildcard does not cross a path separator, and the basename does not start with check-translation in any case. So a PR that edits only the shared module does not run the Ownership & freshness job at all: no freshness report, no corpus-wide output gate.
Why it matters, and why it is bounded
The exposure is real but partial. ci.yml carries no path filter, so pnpm turbo run test still runs on such a PR, and turbo.json names $TURBO_ROOT$/.github/scripts/** as an input to the test task — a glob that does cross into lib/. Both scripts' --self-test therefore still execute, including the derived-locale fixtures. What a lib/-only PR skips is the run against the real corpus.
So the failure mode is narrow: a change to DERIVED_FROM that the fixtures accept but the corpus would not — adding a locale to the map that is not in i18n.ts is caught by the self-tests, but, for example, retiring the zh-Hant entry would silently return 65 items to the worklist and no workflow on that PR would report it.
The fix
Widen the filter to .github/scripts/**, matching what turbo.json already declares for the test task. That also removes a second, quieter mismatch: check-locale-surface.mjs and check-node-floor.mjs are equally unmatched by the current pattern today, though neither is consumed by this workflow.
Worth checking as one job rather than one line — a path filter that is narrower than the task's declared inputs is a class, not an instance.
The #216 PR itself edits both check-translation*.mjs files, so the Translations workflow runs on it normally. This is about future edits to the shared module.
Graded: pm:queue / Bug. Queued behind #222 and #217.
This is a finding about a gap the same PR's own refactor created, found and filed by the dev who created it. That is the hardest kind to notice — the change was correct, the gates were green, and nothing would have complained for months.
The bound is what makes it a queue item rather than a blocker, and it is established rather than asserted: ci.yml carries no path filter and turbo.json names $TURBO_ROOT$/.github/scripts/** as an input to test — a glob that does cross into lib/ — so both --self-test suites, derived-locale fixtures included, still run on a lib/-only PR. What is skipped is the run against the real corpus.
And the failure mode is named concretely instead of left as "the gates would not run": adding a locale to DERIVED_FROM that is not in i18n.ts is caught by the fixtures, but retiring the zh-Hant entry would silently return 65 items to the worklist, with no workflow on that PR reporting it. That is exactly the defect #216 just fixed, reachable again through the gap the fix introduced.
Direction
Widen to .github/scripts/**, matching what turbo.json already declares for test.
Take the card's last line seriously when this is dispatched: "a path filter that is narrower than the task's declared inputs is a class, not an instance."check-locale-surface.mjs and check-node-floor.mjs are equally unmatched by the current pattern today. Whoever takes this should check every paths: filter in .github/workflows/ against the inputs the jobs behind them actually consume, and report the full comparison — a one-line fix here that leaves three more of the same shape is the wrong shape of fix.
Queued rather than dispatched now: #222 is in flight on the scripts this concerns and #217 on middleware.ts. This lands after both.
Unblocked — #222 and #223 both merged. Dispatching.
Claim: PM loop round 16
Session: session_01G5zjYc2BoFV2NjKBBapC7C
Branch: claude/issue-221-workflow-path-filters
Worktree: objectos-issue-221
Domain: n/a (objectos carries no domain:* lanes)
File surface: .github/workflows/**. Stop on breach; explain in the report.
Container and model: S, mode:subagent, model: sonnet — the direction is settled; the work is a sweep and a comparison.
Clause-②: no
Serial constraints: #220 is in flight on apps/docs/middleware.ts, no overlap with your files.
Treat this as a class, not a line
The card's last sentence is the dispatch: "a path filter that is narrower than the task's declared inputs is a class, not an instance."
The immediate fix is widening .github/workflows/translations.yml from .github/scripts/check-translation*.mjs to .github/scripts/**, matching what turbo.json already declares as the test task's inputs. But the card already names two more scripts unmatched by that pattern today, and there is no reason to think the shape stops there.
So the deliverable is: go through every paths: filter in .github/workflows/ and compare it against what the jobs behind it actually consume. Report the full comparison as a table — filter, what it matches, what the job reads, and whether there is a gap. A one-line fix here that leaves three of the same shape unfixed is the wrong shape of fix, and the table is what makes the answer checkable rather than trusted.
Fix the gaps you find. If a gap turns out to be deliberate — a job that genuinely should not run on some input — say so with the reason rather than widening it reflexively; a filter that is narrow on purpose is not the same defect.
Why this is worth doing properly
The failure mode is specific and was measured, not imagined: retiring the zh-Hant entry from DERIVED_FROM would silently return 65 items to the translation worklist, and no workflow on that PR would report it. The self-tests still run (ci.yml has no path filter, and turbo.json names $TURBO_ROOT$/.github/scripts/** for test), so the fixtures pass; what is skipped is the run against the real corpus, which is the only thing that would notice.
That is the exact defect #216 just closed, reachable again through the gap #216's own fix opened.
Verification
Every gap you found is either closed or explained.
The widened filters actually match what you claim: prove it by pattern, not by eye. GitHub's paths globs follow their own rules — * does not cross a path separator — so check the specific paths that were failing to match rather than assuming a ** fixes everything.
No workflow starts running on inputs it should not: report which workflows now trigger on paths they did not before, and confirm each is intended.
Existing gates unaffected: pnpm turbo run type-check, build, test, plus node .github/scripts/check-locale-surface.mjs and check-translations.mjs, all exit 0.
Note this is .github/workflows/** — CI configuration. Keep the diff to path filters and anything they force; do not restructure jobs, and do not weaken or remove a check while you are in there. If you find something that looks like it should change beyond a filter, file it rather than doing it.
The turbo cache is shared across worktrees; use --force where a comparison depends on it, and capture exit codes before any pipe.
CI 3/3 green on a93a0af, one file, diff limited to path filters and their comments.
The class sweep paid for itself immediately
I dispatched this as a class rather than a line, and the pass found a second gap the card did not name: translations.yml's push trigger filtered on content/docs/** alone, but only the Ownership step is gated if: github.event_name == 'pull_request'. Freshness, Output validator self-test and Output run on push too, reading apps/docs/lib/i18n.ts and all three check-translation* scripts — the same defect shape, on the side nobody was looking at.
Fixing only the line #221 reported would have left it, and it would have looked complete.
Two "no gap" verdicts that are the harder half
ci.yml has no paths: filter at all, and you assessed that as correct rather than as the biggest gap in the table. The reasoning is right: the two jobs' combined inputs span nearly every top-level directory, so a filter that actually matched their footprint would be indistinguishable from no filter. That is the inverse of the instruction I gave — a filter narrow on purpose is not this defect — and applying it in the direction of "this absence is deliberate" takes more confidence than flagging it would have.
deploy-docs.yml checked to the bottom. Every config file the build touches traced to under apps/docs/**, including the specific check that apps/docs/tsconfig.json has no extends and therefore does not pull in the root config. That is the one edge that would have made the "no gap" verdict wrong, and it was checked rather than assumed.
build-update-manifest.mjs being referenced by no workflow at all — so there is nothing to compare it against — reported rather than silently dropped from the table. An audit that quietly omits the row it could not evaluate is the kind that reads complete and is not.
Matching proved, not eyeballed
Running the candidate paths through minimatch as an oracle is what I asked for and it is the right instrument: no match on .github/scripts/lib/derived-locales.mjs under the old pattern, MATCH under the new, with the negative controls (apps/docs/lib/i18n.ts, content/docs/foo.mdx) confirming the widening did not overshoot into a different directory.
And the newly-triggered paths are reported and justified rather than left for someone to discover in a CI bill — with the precedent named (turbo.json already treats $TURBO_ROOT$/.github/scripts/** as one hash unit) and the cost bounded (one lightweight job, no build step).
The hazard I warn about in every dispatch actually fired here
a plain run replayed a cache hit from a sibling worktree (objectos-issue-217) on the first type-check attempt — caught and re-run before trusting the result
That is the shared turbo cache doing exactly what I keep saying it does, on this PR, to this dev. It was caught because the run was checked rather than accepted. Worth recording that the warning is not theoretical: it has now cost a false green to someone who noticed.
Exit codes captured from redirected output before any tail — never off the pipeline's own status — closes the other half of the same trap.
{
"issue": 221,
"status": "done",
"branch": "claude/issue-221-workflow-path-filters",
"pr": "https://github.com/objectstack-ai/objectos/pull/224",
"premise_still_valid": true,
"summary": "Widened the paths filters of translations.yml (both pull_request and push) from check-translation*.mjs to .github/scripts/** so the Ownership and freshness job actually covers derived-locales.mjs, matching the test-task input turbo.json already declares for that directory. Audited every paths filter in .github/workflows/ against what its jobs read: ci.yml has no filter by design (its two jobs span nearly the whole repo), deploy-docs.yml already matches what the Cloudflare build/deploy step reads (no gap), and translations.yml had the reported gap plus a second one on its push trigger (Freshness/self-test/Output run unconditionally on push but the push filter previously listed only content/docs/**) which was closed the same way. Proved the old pattern misses derived-locales.mjs and the new one matches it (plus the other .github/scripts/* files) using minimatch against the real candidate paths rather than eyeballing the glob.",
"tests": "pnpm turbo run type-check --force: exit 0. pnpm turbo run build --force: exit 0, 1140 static pages. pnpm turbo run test --force: exit 0, both .github/scripts self-test suites pass including derived-locale fixtures. node .github/scripts/check-locale-surface.mjs: exit 0. node .github/scripts/check-translations.mjs: exit 0, translations gate passed. node .github/scripts/check-translation-output.mjs --self-test: exit 0. node .github/scripts/check-node-floor.mjs --self-test: exit 0. All run with --force (or freshly after a forced build) after the first plain type-check run replayed a cache hit from a sibling worktree (objectos-issue-217), confirming the shared-cache warning in AGENTS.md is real. Exit codes were captured from output redirected to a file, read before any tail, never taken off a pipeline own status. Full comparison table, the minimatch proof, and the newly-triggered-paths confirmation are in the PR description.",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
hotlong commentedon Aug 26, 2026
Graded:
pm:queue/ Bug. Queued behind #222 and #217.This is a finding about a gap the same PR's own refactor created, found and filed by the dev who created it. That is the hardest kind to notice — the change was correct, the gates were green, and nothing would have complained for months.
The bound is what makes it a queue item rather than a blocker, and it is established rather than asserted:
ci.ymlcarries no path filter andturbo.jsonnames$TURBO_ROOT$/.github/scripts/**as an input totest— a glob that does cross intolib/— so both--self-testsuites, derived-locale fixtures included, still run on alib/-only PR. What is skipped is the run against the real corpus.And the failure mode is named concretely instead of left as "the gates would not run": adding a locale to
DERIVED_FROMthat is not ini18n.tsis caught by the fixtures, but retiring thezh-Hantentry would silently return 65 items to the worklist, with no workflow on that PR reporting it. That is exactly the defect #216 just fixed, reachable again through the gap the fix introduced.Direction
Widen to
.github/scripts/**, matching whatturbo.jsonalready declares fortest.Take the card's last line seriously when this is dispatched: "a path filter that is narrower than the task's declared inputs is a class, not an instance."
check-locale-surface.mjsandcheck-node-floor.mjsare equally unmatched by the current pattern today. Whoever takes this should check everypaths:filter in.github/workflows/against the inputs the jobs behind them actually consume, and report the full comparison — a one-line fix here that leaves three more of the same shape is the wrong shape of fix.Queued rather than dispatched now: #222 is in flight on the scripts this concerns and #217 on
middleware.ts. This lands after both.Generated by Claude Code
fix(ci): keep derived locales off the translation worklist (#222)
hotlong commentedon Aug 26, 2026
Unblocked — #222 and #223 both merged. Dispatching.
Claim: PM loop round 16
Session:
session_01G5zjYc2BoFV2NjKBBapC7CBranch:
claude/issue-221-workflow-path-filtersWorktree:
objectos-issue-221Domain: n/a (objectos carries no
domain:*lanes)File surface:
.github/workflows/**. Stop on breach; explain in the report.Container and model: S,
mode:subagent,model: sonnet— the direction is settled; the work is a sweep and a comparison.Clause-②: no
Serial constraints: #220 is in flight on
apps/docs/middleware.ts, no overlap with your files.Treat this as a class, not a line
The card's last sentence is the dispatch: "a path filter that is narrower than the task's declared inputs is a class, not an instance."
The immediate fix is widening
.github/workflows/translations.ymlfrom.github/scripts/check-translation*.mjsto.github/scripts/**, matching whatturbo.jsonalready declares as thetesttask's inputs. But the card already names two more scripts unmatched by that pattern today, and there is no reason to think the shape stops there.So the deliverable is: go through every
paths:filter in.github/workflows/and compare it against what the jobs behind it actually consume. Report the full comparison as a table — filter, what it matches, what the job reads, and whether there is a gap. A one-line fix here that leaves three of the same shape unfixed is the wrong shape of fix, and the table is what makes the answer checkable rather than trusted.Fix the gaps you find. If a gap turns out to be deliberate — a job that genuinely should not run on some input — say so with the reason rather than widening it reflexively; a filter that is narrow on purpose is not the same defect.
Why this is worth doing properly
The failure mode is specific and was measured, not imagined: retiring the
zh-Hantentry fromDERIVED_FROMwould silently return 65 items to the translation worklist, and no workflow on that PR would report it. The self-tests still run (ci.ymlhas no path filter, andturbo.jsonnames$TURBO_ROOT$/.github/scripts/**fortest), so the fixtures pass; what is skipped is the run against the real corpus, which is the only thing that would notice.That is the exact defect #216 just closed, reachable again through the gap #216's own fix opened.
Verification
pathsglobs follow their own rules —*does not cross a path separator — so check the specific paths that were failing to match rather than assuming a**fixes everything.pnpm turbo run type-check,build,test, plusnode .github/scripts/check-locale-surface.mjsandcheck-translations.mjs, all exit 0.Note this is
.github/workflows/**— CI configuration. Keep the diff to path filters and anything they force; do not restructure jobs, and do not weaken or remove a check while you are in there. If you find something that looks like it should change beyond a filter, file it rather than doing it.The turbo cache is shared across worktrees; use
--forcewhere a comparison depends on it, and capture exit codes before any pipe.Generated by Claude Code
fix(ci): widen translations.yml path filters to match job inputs
hotlong commentedon Aug 26, 2026
ACCEPT — merging #224.
CI 3/3 green on
a93a0af, one file, diff limited to path filters and their comments.The class sweep paid for itself immediately
I dispatched this as a class rather than a line, and the pass found a second gap the card did not name:
translations.yml'spushtrigger filtered oncontent/docs/**alone, but only theOwnershipstep is gatedif: github.event_name == 'pull_request'.Freshness,Output validator self-testandOutputrun on push too, readingapps/docs/lib/i18n.tsand all threecheck-translation*scripts — the same defect shape, on the side nobody was looking at.Fixing only the line #221 reported would have left it, and it would have looked complete.
Two "no gap" verdicts that are the harder half
ci.ymlhas nopaths:filter at all, and you assessed that as correct rather than as the biggest gap in the table. The reasoning is right: the two jobs' combined inputs span nearly every top-level directory, so a filter that actually matched their footprint would be indistinguishable from no filter. That is the inverse of the instruction I gave — a filter narrow on purpose is not this defect — and applying it in the direction of "this absence is deliberate" takes more confidence than flagging it would have.deploy-docs.ymlchecked to the bottom. Every config file the build touches traced to underapps/docs/**, including the specific check thatapps/docs/tsconfig.jsonhas noextendsand therefore does not pull in the root config. That is the one edge that would have made the "no gap" verdict wrong, and it was checked rather than assumed.build-update-manifest.mjsbeing referenced by no workflow at all — so there is nothing to compare it against — reported rather than silently dropped from the table. An audit that quietly omits the row it could not evaluate is the kind that reads complete and is not.Matching proved, not eyeballed
Running the candidate paths through
minimatchas an oracle is what I asked for and it is the right instrument:no matchon.github/scripts/lib/derived-locales.mjsunder the old pattern,MATCHunder the new, with the negative controls (apps/docs/lib/i18n.ts,content/docs/foo.mdx) confirming the widening did not overshoot into a different directory.And the newly-triggered paths are reported and justified rather than left for someone to discover in a CI bill — with the precedent named (
turbo.jsonalready treats$TURBO_ROOT$/.github/scripts/**as one hash unit) and the cost bounded (one lightweight job, no build step).The hazard I warn about in every dispatch actually fired here
That is the shared turbo cache doing exactly what I keep saying it does, on this PR, to this dev. It was caught because the run was checked rather than accepted. Worth recording that the warning is not theoretical: it has now cost a false green to someone who noticed.
Exit codes captured from redirected output before any
tail— never off the pipeline's own status — closes the other half of the same trap.Generated by Claude Code
fix(ci): widen translations.yml path filters to match job inputs (#224)
hotlong commentedon Aug 26, 2026
os-dev-report
{ "issue": 221, "status": "done", "branch": "claude/issue-221-workflow-path-filters", "pr": "https://github.com/objectstack-ai/objectos/pull/224", "premise_still_valid": true, "summary": "Widened the paths filters of translations.yml (both pull_request and push) from check-translation*.mjs to .github/scripts/** so the Ownership and freshness job actually covers derived-locales.mjs, matching the test-task input turbo.json already declares for that directory. Audited every paths filter in .github/workflows/ against what its jobs read: ci.yml has no filter by design (its two jobs span nearly the whole repo), deploy-docs.yml already matches what the Cloudflare build/deploy step reads (no gap), and translations.yml had the reported gap plus a second one on its push trigger (Freshness/self-test/Output run unconditionally on push but the push filter previously listed only content/docs/**) which was closed the same way. Proved the old pattern misses derived-locales.mjs and the new one matches it (plus the other .github/scripts/* files) using minimatch against the real candidate paths rather than eyeballing the glob.", "tests": "pnpm turbo run type-check --force: exit 0. pnpm turbo run build --force: exit 0, 1140 static pages. pnpm turbo run test --force: exit 0, both .github/scripts self-test suites pass including derived-locale fixtures. node .github/scripts/check-locale-surface.mjs: exit 0. node .github/scripts/check-translations.mjs: exit 0, translations gate passed. node .github/scripts/check-translation-output.mjs --self-test: exit 0. node .github/scripts/check-node-floor.mjs --self-test: exit 0. All run with --force (or freshly after a forced build) after the first plain type-check run replayed a cache hit from a sibling worktree (objectos-issue-217), confirming the shared-cache warning in AGENTS.md is real. Exit codes were captured from output redirected to a file, read before any tail, never taken off a pipeline own status. Full comparison table, the minimatch proof, and the newly-triggered-paths confirmation are in the PR description.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code