A strictTemplates type error broke master's production app build for at least one merge cycle without CI going red (see #823 — humanizeDuration(number | null) in photo-lightbox). The merge gates did not catch it because:
The full nx build spikersoft --configuration production (which type-checks every component template it pulls in) is not run as a required merge check, and
feature-photo-gallery:test — whose vitest builder performs the same template type-check — is among the Angular projects not gated (path-filtered / untested), per the known coverage-gap epic.
Net effect: PR #560 merged green having only satisfied the eqeqeq ESLint rule, while the underlying template type error remained and broke the deployable build.
Impact
master can be in a state where no deployable app bundle builds, and nothing surfaces it until someone runs a local/production build. Lint + the gated unit subset are green, giving false confidence.
Suggested fixes (pick per cost)
Add a required CI job that runs nx build spikersoft --configuration production (or at least nx affected -t build) on PRs touching libraries/** or projects/**.
Ensure feature-*:test (which template-type-checks via the vitest builder) is not path-filtered out for changes in its project graph — nx affected -t test rather than a static path allowlist.
Longer term: land the root typecheck gate (blocked by #664) so template errors fail fast without a full build.
Relates to the CI test-coverage-gaps thread and #823.
## Problem
A strictTemplates type error broke `master`'s production app build for at least one merge cycle without CI going red (see #823 — `humanizeDuration(number | null)` in photo-lightbox). The merge gates did not catch it because:
- The full **`nx build spikersoft --configuration production`** (which type-checks every component template it pulls in) is not run as a required merge check, and
- **`feature-photo-gallery:test`** — whose vitest builder performs the same template type-check — is among the Angular projects not gated (path-filtered / untested), per the known coverage-gap epic.
Net effect: PR #560 merged green having only satisfied the `eqeqeq` ESLint rule, while the underlying template type error remained and broke the deployable build.
## Impact
`master` can be in a state where **no deployable app bundle builds**, and nothing surfaces it until someone runs a local/production build. Lint + the gated unit subset are green, giving false confidence.
## Suggested fixes (pick per cost)
1. Add a required CI job that runs `nx build spikersoft --configuration production` (or at least `nx affected -t build`) on PRs touching `libraries/**` or `projects/**`.
2. Ensure `feature-*:test` (which template-type-checks via the vitest builder) is not path-filtered out for changes in its project graph — `nx affected -t test` rather than a static path allowlist.
3. Longer term: land the root `typecheck` gate (blocked by #664) so template errors fail fast without a full build.
Relates to the CI test-coverage-gaps thread and #823.
Second instance of this exact gap today, worth folding into the scope here.
#828: PR #564 added a route to `routes.ts` without classifying it in `e2e/support/route-manifest.ts`. That is only caught by `route-coverage.spec.ts` in the `e2e-anonymous` job, which is not required for merge — so it merged green and then failed on master, and because `publish` is gated on the e2e jobs, the frontend stopped deploying entirely (learn.spikersoft.com served a pre-#564 bundle for over an hour without anyone being paged).
So the blast radius of the missing gate is bigger than a red badge: a post-merge-only failure in an e2e job silently halts production deploys. Whatever gating this ticket lands should cover both `nx build spikersoft` and the route-manifest check, and ideally a notification when `publish` is skipped on master.
— Opus 5 Agent
Second instance of this exact gap today, worth folding into the scope here.
#828: PR #564 added a route to \`routes.ts\` without classifying it in \`e2e/support/route-manifest.ts\`. That is only caught by \`route-coverage.spec.ts\` in the \`e2e-anonymous\` job, which is not required for merge — so it merged green and then failed on master, and because \`publish\` is gated on the e2e jobs, **the frontend stopped deploying entirely** (learn.spikersoft.com served a pre-#564 bundle for over an hour without anyone being paged).
So the blast radius of the missing gate is bigger than a red badge: a post-merge-only failure in an e2e job silently halts production deploys. Whatever gating this ticket lands should cover both \`nx build spikersoft\` and the route-manifest check, and ideally a notification when \`publish\` is skipped on master.
— Opus 5 Agent
Resolved — all three suggested fixes have landed, via the epic #722 CI restructure (the workflow is now .gitea/workflows/main.yml; the old tests.yml this ticket was written against no longer exists). Verified against origin/master:
Fix 1 — the production app build is now a PR gate.main.yml:114 defines a build job with needs: [compute_version], running pnpm run build at :191-192. That script is nx build spikersoft --configuration production --verbose --skip-nx-cache, i.e. the full build that type-checks every template it pulls in. The workflow triggers on pull_request: branches: [master] (:5-6) with no paths: filter, and the job carries no continue-on-error and no if: guard — so it runs on every PR regardless of which files changed. publish additionally needs: [test-and-lint, e2e-anonymous, build, compute_version] (:479), so a broken app build blocks the pipeline rather than merging green.
Fix 2 — the static path allowlist is gone, replaced by nx affected.main.yml:407-410 runs pnpm nx affected -t lint --base=origin/master --head=HEAD on PRs, and the test step does the same with -t test, falling back to the full run-many/test-all on pushes to master. The comment at :401-405 records the reasoning: PRs lint/test the affected graph slice, pushes keep the full run so merge-batch effects can't slip through an affected diff against HEAD~1. That's exactly what this ticket asked for — feature-photo-gallery:test is now reached whenever it's in the changed project graph, rather than depending on a path match.
Fix 3 — the typecheck gate landed too, which is notable because this ticket recorded it as blocked by #664. Rather than the root tsc --noEmit (unusable across mixed project configs, which was #664's problem), main.yml:421-427 runs a per-projecttypecheck target inferred by the local tools/workspace-plugin from tsconfig.lib.json / tsconfig.spec.json plus the app tsconfig — same affected-on-PR / full-on-push split. So template errors now fail fast without needing a full build, and #664's blocker was routed around rather than waited on.
Net effect: the #823 scenario — a strictTemplates error merging green because only eqeqeq was checked — is now caught three separate ways.
Closing.
Resolved — **all three suggested fixes have landed**, via the epic #722 CI restructure (the workflow is now `.gitea/workflows/main.yml`; the old `tests.yml` this ticket was written against no longer exists). Verified against `origin/master`:
**Fix 1 — the production app build is now a PR gate.** `main.yml:114` defines a `build` job with `needs: [compute_version]`, running `pnpm run build` at `:191-192`. That script is `nx build spikersoft --configuration production --verbose --skip-nx-cache`, i.e. the full build that type-checks every template it pulls in. The workflow triggers on `pull_request: branches: [master]` (`:5-6`) with **no `paths:` filter**, and the job carries no `continue-on-error` and no `if:` guard — so it runs on every PR regardless of which files changed. `publish` additionally `needs: [test-and-lint, e2e-anonymous, build, compute_version]` (`:479`), so a broken app build blocks the pipeline rather than merging green.
**Fix 2 — the static path allowlist is gone, replaced by `nx affected`.** `main.yml:407-410` runs `pnpm nx affected -t lint --base=origin/master --head=HEAD` on PRs, and the test step does the same with `-t test`, falling back to the full `run-many`/`test-all` on pushes to master. The comment at `:401-405` records the reasoning: PRs lint/test the affected graph slice, pushes keep the full run so merge-batch effects can't slip through an affected diff against `HEAD~1`. That's exactly what this ticket asked for — `feature-photo-gallery:test` is now reached whenever it's in the changed project graph, rather than depending on a path match.
**Fix 3 — the typecheck gate landed too**, which is notable because this ticket recorded it as blocked by #664. Rather than the root `tsc --noEmit` (unusable across mixed project configs, which was #664's problem), `main.yml:421-427` runs a **per-project** `typecheck` target inferred by the local `tools/workspace-plugin` from `tsconfig.lib.json` / `tsconfig.spec.json` plus the app tsconfig — same affected-on-PR / full-on-push split. So template errors now fail fast without needing a full build, and #664's blocker was routed around rather than waited on.
Net effect: the #823 scenario — a strictTemplates error merging green because only `eqeqeq` was checked — is now caught three separate ways.
Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
A strictTemplates type error broke
master's production app build for at least one merge cycle without CI going red (see #823 —humanizeDuration(number | null)in photo-lightbox). The merge gates did not catch it because:nx build spikersoft --configuration production(which type-checks every component template it pulls in) is not run as a required merge check, andfeature-photo-gallery:test— whose vitest builder performs the same template type-check — is among the Angular projects not gated (path-filtered / untested), per the known coverage-gap epic.Net effect: PR #560 merged green having only satisfied the
eqeqeqESLint rule, while the underlying template type error remained and broke the deployable build.Impact
mastercan be in a state where no deployable app bundle builds, and nothing surfaces it until someone runs a local/production build. Lint + the gated unit subset are green, giving false confidence.Suggested fixes (pick per cost)
nx build spikersoft --configuration production(or at leastnx affected -t build) on PRs touchinglibraries/**orprojects/**.feature-*:test(which template-type-checks via the vitest builder) is not path-filtered out for changes in its project graph —nx affected -t testrather than a static path allowlist.typecheckgate (blocked by #664) so template errors fail fast without a full build.Relates to the CI test-coverage-gaps thread and #823.
Second instance of this exact gap today, worth folding into the scope here.
#828: PR #564 added a route to `routes.ts` without classifying it in `e2e/support/route-manifest.ts`. That is only caught by `route-coverage.spec.ts` in the `e2e-anonymous` job, which is not required for merge — so it merged green and then failed on master, and because `publish` is gated on the e2e jobs, the frontend stopped deploying entirely (learn.spikersoft.com served a pre-#564 bundle for over an hour without anyone being paged).
So the blast radius of the missing gate is bigger than a red badge: a post-merge-only failure in an e2e job silently halts production deploys. Whatever gating this ticket lands should cover both `nx build spikersoft` and the route-manifest check, and ideally a notification when `publish` is skipped on master.
— Opus 5 Agent
Resolved — all three suggested fixes have landed, via the epic #722 CI restructure (the workflow is now
.gitea/workflows/main.yml; the oldtests.ymlthis ticket was written against no longer exists). Verified againstorigin/master:Fix 1 — the production app build is now a PR gate.
main.yml:114defines abuildjob withneeds: [compute_version], runningpnpm run buildat:191-192. That script isnx build spikersoft --configuration production --verbose --skip-nx-cache, i.e. the full build that type-checks every template it pulls in. The workflow triggers onpull_request: branches: [master](:5-6) with nopaths:filter, and the job carries nocontinue-on-errorand noif:guard — so it runs on every PR regardless of which files changed.publishadditionallyneeds: [test-and-lint, e2e-anonymous, build, compute_version](:479), so a broken app build blocks the pipeline rather than merging green.Fix 2 — the static path allowlist is gone, replaced by
nx affected.main.yml:407-410runspnpm nx affected -t lint --base=origin/master --head=HEADon PRs, and the test step does the same with-t test, falling back to the fullrun-many/test-allon pushes to master. The comment at:401-405records the reasoning: PRs lint/test the affected graph slice, pushes keep the full run so merge-batch effects can't slip through an affected diff againstHEAD~1. That's exactly what this ticket asked for —feature-photo-gallery:testis now reached whenever it's in the changed project graph, rather than depending on a path match.Fix 3 — the typecheck gate landed too, which is notable because this ticket recorded it as blocked by #664. Rather than the root
tsc --noEmit(unusable across mixed project configs, which was #664's problem),main.yml:421-427runs a per-projecttypechecktarget inferred by the localtools/workspace-pluginfromtsconfig.lib.json/tsconfig.spec.jsonplus the app tsconfig — same affected-on-PR / full-on-push split. So template errors now fail fast without needing a full build, and #664's blocker was routed around rather than waited on.Net effect: the #823 scenario — a strictTemplates error merging green because only
eqeqeqwas checked — is now caught three separate ways.Closing.