Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Test & quality alignment plan

Open
#1 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Refactor
Clarity
Clearly specified
Activity status
Active
Tech stack
bun, eslint, nextjs, react, tailwindcss, typescript

Research direction

Start with the referenced root package.json, apps/web/package.json, and bun.lock, then inspect the phased gates and the existing apps/web/src/lib files. Run the Phase 0 checks first, followed by the local verification commands described in the plan; the work is done when the planned test, lint, type-check, build, and report-only quality gates pass without introducing CI.

Written by the indexing model from the issue text.

Description

Test & Quality Alignment Plan — dbuild

  • Project: Dbuild.dev (/home/didi/workspace/dbuild, origin DimitriGilbert/dbuild)
  • Date: 2026-09-05
  • Generated by the fleet test-and-quality alignment initiative (28-repo audit + synthesis)
  • Audit report: ../.test-alignment-audit/reports/dbuild.md
  • Fleet synthesis / decisions: ../.test-alignment-audit/synthesis-decisions.md
  • Stack reality: BTS v2.22.10 next-only monorepo — Next.js 15.1.3 / React 19 / Tailwind v4 / Turborepo, Bun-managed (packageManager: [email protected], bun.lock), statically exported to GitHub Pages (dbuild.dev). One app (apps/web), empty packages/. ~9k LOC, 0 test files, no hooks, dead lint script (no CI — by owner design, D5; the gate is local commands).

Revised 2026-09-05: CI removed by owner decision — quality gates are local commands run by the owner/agents; no GitHub Actions.


1. Adopted decisions (synthesis D1–D7, adapted to a Bun-managed Next.js static site)

Decision Adoption for dbuild
D1 — Runner & layout bun:test (Bun's built-in runner), NOT vitest. D1 explicitly rules: "Bun repos: bun:test stays … do not introduce vitest" (precedent: email-sdk + FeedElity, 806 cases combined). Adding vitest would bolt a Vite toolchain onto a repo with zero test infra for no benefit. Tests colocated as *.test.ts(x) next to source. Root one-command entry bun run test via turbo. Environment: node-only — no jsdom, no RTL. This is the explicit pick synthesis §5 demands from Next-variant repos: v1 has zero component tests (static site; the value is pure logic), so the DOM stack would be dead weight.
D2 — Coverage Report-only. Bun's coverage reporter is explicitly not threshold-grade (synthesis §5). Run bun test --coverage, record the measured baseline in AGENTS.md, never gate on it. Revisit thresholds when Bun coverage matures.
D3 — Lint ESLint 9 flat config + typescript-eslint ^8 + eslint-plugin-react-hooks (fleet standard). This retires the dead lint: "next lint" script (no ESLint dep or config exists — audit P1) and un-dangles the lint task in turbo.json. Prettier deferred in v1 per D3's allowance — stated in AGENTS.md rather than pretended.
D4 — Quality metrics knip + jscpd, report-only, local report scripts run on demand by the owner/agents (writing a git-ignored local report). No CRAP (requires threshold-grade coverage Bun can't give), no mutation testing (dbuild is not one of the five nominated cores), no circular-dep tooling in v1. jscpd uses the launch-mommy canonical config (minLines: 8, minTokens: 80, exitCode: 0).
D5 — no CI by design (owner decision 2026-09-05) GitHub Actions rejected fleet-wide: no .github/workflows, no reusable-workflow convergence target, no scheduled jobs. Enforcement = the root-level headless verification entrypoint (bun run verify: lint → check-types → test → build, composed in Phase 4) run by the owner and by agents; the plan's phase validation gates are run by implementer/validator agents before any phase is declared done. GH Pages deploys stay manual, guarded by a green bun run verify.
D6 — Shared config Adopt @dg/config presets opportunistically (D6: "when it touches lint/tsconfig/vitest anyway") — Phase 1 touches lint, so if @dg/config is published at execution time, the ESLint config extends it; otherwise write the local flat config and note it for later convergence. Non-blocking either way.
D7 — Hooks No git hooks. Local gates run by the owner/agents are the only authoritative gate. AGENTS.md states it explicitly: "the headless local verification command (bun run verify) is the quality gate; hooks are local convenience." A repo with no hooks and a green local run is compliant.
Deviations from the synthesis (declared, not silent)
  1. Runner: the dbuild delta block (§4) says "Seed vitest (Bun-compatible)"; the core D1 decision and §5 non-goals say Bun repos keep bun:test and must not introduce vitest. Follow D1: bun:test. Rationale above; zero install, native TS + tsconfig paths support, fleet precedent.
  2. CI workflow Removed by the 2026-09-05 revision: no workflow is created (D5); the Bun-vs-pnpm reusable-workflow mismatch that motivated this deviation is moot — the gate is the local bun run verify entrypoint.
  3. Package manager: Bun stays. Switching to the pnpm fleet default would mean regenerating the lockfile, re-verifying the GH Pages build chain, and retraining habits for zero test-quality gain — synthesis §5 explicitly lists dbuild among "stay Bun". Kept.
  4. No packages/config: dbuild has no shared-config package (empty packages/) — the fleet cross-constant doesn't apply; apps/web/tsconfig.json stays hand-maintained. Optional tightening (noUncheckedIndexedAccess) is deliberately not in scope: it would churn 9k LOC with no test payoff in v1.
  5. Renovate: the local>DimitriGilbert preset reference requires the org preset to exist first (fleet-level task). If it doesn't at execution time, land the one-liner anyway and expect a Renovate warning until the preset lands — or temporarily extend config:recommended and switch in a follow-up.

2. Test-runner decision in one line

bun test over src/lib/* pure logic in node env; no jsdom/RTL; coverage report-only. The testable core (grounded in the audit + read of the code):

  • apps/web/src/lib/seo.ts (375 LOC) — fully pure: formatCanonicalUrl, formatTitle, formatAbsoluteImageUrl, formatOpenGraphLocale, serializeJsonLd, parseTaxonomySlug/formatTaxonomySlug, create*JsonLd, createStaticPageMetadata, createNotFoundMetadata. Highest-value, zero-mock target.
  • apps/web/src/lib/xml-sitemap.ts (58 LOC) — pure string builders (createUrlSet, createSitemapIndex); createXmlResponse uses the global Response, which Bun provides.
  • apps/web/src/lib/blog.ts (598 LOC) — exported slugify; module-private pure helpers that must be exported (behavior-preserving, mechanical): readString, readDateString, readStringArray, readFrontmatter, calculateReadTime, generateTOC, chooseCanonicalTagName. fs-backed getters (getAllPosts etc.) get one deterministic integration test over the in-repo content/blog corpus (55 files, static data — hermetic enough; assert structure, not exact counts).
  • apps/web/src/lib/projects.ts (62 LOC) — reads in-repo data/projects.json: cheap deterministic tests.
  • apps/web/src/lib/mindmap.ts (440 LOC) — fs + dagre heavy, single re-export surface: deferred to a later ratchet; not worth a refactor in v1.
  • apps/web/scripts/validate-content.js — the de-facto content suite (audit: "a genuine validation program"); predicates are pure but the script exports nothing and self-invokes. Phase 3 splits it so it becomes testable and build-gated.

3. Phased plan

Orchestration model per the subagent-orchestration skill: every phase = 1 implementer dispatch → implementer runs gatekeeping commands → 1 validator dispatch (different agent; must read the code, not just run commands) → fix loop (fixer repairs ALL validator findings at once; re-validate; max 3 attempts) → phase done. All phases are sequential (each builds on the previous gate). Every implementer/fixer dispatch carries the NO-SLOP policy verbatim (no any/as any, no TODO/FIXME, no unused imports, import type for type-only imports, no dev servers — all of which AGENTS.md also mandates). No commits/pushes unless the owner asks; the fleet initiative's rule against destroying uncommitted work applies (the tree currently has uncommitted content/source edits — phases must build around them, never reset them).

Phase 0 — Resolve the Next.js two-major drift + workspace hygiene

Goal: one Next major in the lockfile; root stops shadowing the app's dependency versions; no npm in a Bun repo.

Tasks

  1. package.json (root): delete the dependencies block entirely — it hoists next ^16.1.3, react ^19.2.3, react-dom ^19.2.3, @types/node ^25.0.9 while apps/web pins next 15.1.3 / react ^19.0.3 / @types/node ^22.19.7 (two Next majors and two @types/node majors confirmed in bun.lock). Nothing at root imports them (root has no source; only turbo devDep + scripts/optimize-image.sh). Keep the resolutions block (correct place for Bun overrides). Optionally rename root package from Dbuild.dev to dbuild to kill the root/app name collision (nothing references the root name).
  2. apps/web/package.json: fix the setup script — "setup": "bun run init && bun run setup-content && bun run validate-content" (currently npm run … in a Bun repo; audit P3).
  3. From repo root: bun install to regenerate bun.lock; verify exactly one next version (15.1.3) and one @types/node version remain.
  4. Do NOT touch AGENTS.md/CLAUDE.md here — the docs sweep is Phase 5 (one coherent edit instead of churn).

Orchestration: implementer → validator. Validator must read both package.json files, diff-check no other script referenced the removed root deps, and confirm lockfile state.

Validation gate (from repo root): bun install --frozen-lockfile → bun run check-types → bun run build; plus grep -o '"next@[0-9.]*"' bun.lock | sort -u yields exactly [email protected].

Effort: S (small, but it unblocks everything else — the lockfile must be sane before any gated run from a clean checkout with --frozen-lockfile).

Phase 1 — Implement ESLint 9 flat config; retire the dead next lint

Goal: lint runs, lints, and passes; the turbo lint task stops being dangling; the "no linting" claim in AGENTS.md becomes false in the good direction.

Tasks

  1. Install (repo root, Bun workspace): bun add -d -w eslint@^9 @eslint/js@^9 typescript-eslint@^8 eslint-plugin-react-hooks@^5 globals
  2. Create apps/web/eslint.config.mjs (flat config): @eslint/js recommended + typescript-eslint type-checked (recommendedTypeChecked with projectService: true — feasible: single tsconfig at apps/web/tsconfig.json) + eslint-plugin-react-hooks recommended (it's a React repo). Ignores: node_modules/, .next/, out/, next-env.d.ts, content/. Plain-JS scripts/*.js get js.configs.recommended if type-checked parsing of JS proves noisy — implementer's call, requirement is zero errors, zero rule disables without cause.
  3. apps/web/package.json: replace "lint": "next lint" with "lint": "eslint .".
  4. package.json (root): add "lint": "turbo lint" (the turbo lint task already exists — it becomes real).
  5. apps/web/next.config.mjs: keep eslint.ignoreDuringBuilds: true deliberately — lint is a standalone gate step (in bun run verify), not a build-time side effect; add nothing.
  6. If @dg/config (D6) is published by execution time, extend its ESLint preset instead of hand-rolling; note the choice in the config header comment (comment rule: AGENTS.md bans comments in production code — a one-line provenance note in config files is acceptable, or omit).
  7. AGENTS.md: replace the "No Linting/Formatting Config" section with the reality (ESLint 9 flat, bun run lint, Prettier deliberately deferred in v1 — D3's "say so rather than pretend").

Orchestration: implementer → validator. Validator must read the config and spot-check that the rules actually fire (e.g. introduce nothing; verify ignores don't swallow src/), and enforce NO-SLOP.

Validation gate: bun run lint (root, via turbo) exits 0 with zero errors → bun run check-types → bun run build. Any pre-existing lint errors are fixed, not suppressed (AGENTS.md: never silence; fix the cause — same doctrine as the fleet).

Effort: M (type-checked lint over 9k LOC can surface a tail of real findings to fix).

Phase 2 — Seed bun:test over src/lib pure logic

Goal: the repo's first real test suite; headless one-command entry from root.

Tasks

  1. apps/web/package.json: add "test": "bun test" and "test:coverage": "bun test --coverage". Root package.json: add "test": "turbo test". turbo.json: add a "test" task (no dependsOn build; cacheable).
  2. apps/web/bunfig.toml: coverage report-only config ([test] coverage settings; reporters text; no thresholds — D2 Bun exception).
  3. Create colocated suites (bun:test, node env, static imports only — AGENTS.md forbids await import/require):
    • src/lib/seo.test.ts — canonical URL formatting, title suffixing, OG locale mapping, taxonomy slug parse/format roundtrip, serializeJsonLd output, each create*JsonLd/create*Metadata factory's required fields (~25–35 cases).
    • src/lib/xml-sitemap.test.ts — URL-set rendering, sitemap index, createXmlResponse (Bun's global Response).
    • src/lib/blog.test.ts — slugify (assert current behavior, e.g. non-ASCII stripping, as regression pins); newly exported generateTOC, calculateReadTime, readFrontmatter, chooseCanonicalTagName (make them named exports — behavior-preserving, mechanical diff in src/lib/blog.ts); one integration test over the real content/blog corpus via getAllPosts(): every non-draft post has title/date/tags/summary — assert structure, never exact counts (content churns weekly).
    • src/lib/projects.test.ts — getAllProjects non-empty, getProjectById hit/miss→null, tag filter, unique tag list.
  4. Explicitly no jsdom / no @testing-library/* / no component tests in v1 (the pick synthesis §5 requires; static site, zero DOM logic under test).
  5. NO-SLOP applies doubly: AGENTS.md itself bans any/as any — tests use real types from the source modules.

Orchestration: this is the one phase worth two sub-phases, each with its own validator, plus a mandatory phase-wide validator reading both together (skill Rule 3): (a) infra (scripts, bunfig, turbo) + seo + xml-sitemap; (b) blog (incl. the export refactor) + projects + corpus integration. Phase-wide validator checks: import coherence, no duplicated fixtures, coverage config sane.

Validation gate: bun run test (root) green; bun run test:coverage produces a report (record the numbers, gate nothing); bun run check-types → bun run lint → bun run build.

Effort: M.

Phase 3 — Gate validate-content into the build; make it testable

Goal: invalid frontmatter can no longer ship (audit P2: validator is manual-only today, wired only into setup).

Tasks

  1. Split apps/web/scripts/validate-content.js (behavior-preserving): extract the pure predicates/collectors — hasValue, readString, isDraft, isValidDate, isStringArray, isCategoryIndex, normalizeTranslationCandidate, hasFrenchSignals, likelyTranslatedContent, getTranslationCandidate, buildTranslationCandidateCounts, validatePost — into apps/web/scripts/lib/content-rules.js (ESM static exports; no require, no await import). validate-content.js keeps the fs walk, console output, and process.exitCode semantics byte-identical (the build gate depends on the exit code).
  2. Wire the build: apps/web/package.json "build": "node scripts/validate-content.js && node scripts/generate-rss.js && next build" — validate first, so bad frontmatter fails before RSS is generated from it.
  3. Create apps/web/scripts/lib/content-rules.test.js (bun:test, plain JS): draft skip, category _index warnings-vs-errors, invalid date, tags array rule, empty body, French-heuristic detection, duplicate-translation-candidate ⇒ translationKey warning, ogImageAlt warning — table-driven against synthetic frontmatter objects (no fs).
  4. Contingency: the live corpus has uncommitted edits; if validate-content fails on it during implementation, the implementer must stop and report — never weaken the validator or "fix" content silently. That's a real finding for the owner.
  5. Add bun run validate-content to the AGENTS.md web-commands list (Phase 5 reconciles fully).

Orchestration: implementer → validator. Validator diffs old vs new script output on the real corpus (must be identical) and reads the new tests for tautologies (DoD item 9).

Validation gate: bun run validate-content exits 0 on the current corpus → bun test green → bun run build (now content-gated) → bun run check-types.

Effort: S.

Phase 4 — Local gate composition: bun run verify

Goal: DoD 2 under D5 (no CI by design): typecheck + lint + test + build become one headless root command the owner and agents run before any phase is declared done.

Tasks

  1. Root package.json: add "verify": "bun run lint && bun run check-types && bun run test && bun run build" — the headless verification entrypoint (D5). Phases 0–3 already landed each ingredient; this composes them.
  2. AGENTS.md: document bun run verify as the pre-deploy and pre-phase-done gate.
  3. No .github/ directory is created and no workflow file is written anywhere (D5 — owner decision 2026-09-05).

Orchestration: implementer → validator. Validator reads the root package.json, confirms the chain is exactly lint → check-types → test → build, then runs bun run verify from a clean-install state; fail-fast proven once (deliberate failing test in a scratch copy → non-zero exit at the test step).

Validation gate: bun run verify exits 0 from repo root; every command in the chain exists in package.json files.

Effort: S.

Phase 5 — Dependency hygiene, quality report, docs truth

Goal: DoD items 5, 7, 8 closed; documentation stops contradicting reality.

Tasks

  1. renovate.json (repo root): {"$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["local>DimitriGilbert"]} (deviation D5-5 ordering note applies). dbuild has no "latest" pins today — Renovate keeps it that way and watches the next 15→16 upgrade path deliberately instead of by lockfile accident.
  2. Quality report (report-only, on demand — D4): bun add -d -w knip jscpd; add .jscpd.json with the launch-mommy canonical config (minLines: 8, minTokens: 80, exitCode: 0, exclude tests/generated — here: .next, out, content); minimal knip.json for the Next app. Root scripts "quality:knip" / "quality:jscpd" write reports to a git-ignored local directory; run on demand by the owner or during a periodic agent pass — no workflow, no schedule, no artifacts (D5).
  3. Coverage baseline: run bun run test:coverage, record measured lines/branches in AGENTS.md's Testing section as the ratchet baseline (ratchet +5 whenever Bun's tooling becomes threshold-grade; never lower).
  4. Docs reconciliation (audit P3, DoD item 5):
    • AGENTS.md: [email protected] (says 1.2.15); Testing section = runner (bun test), locations (src/lib/*.test.ts, scripts/lib/*.test.js), commands (incl. bun run verify), coverage baseline + report-only status, the D7 sentence ("the headless local verification command (bun run verify) is the quality gate, run by the owner/agents; hooks are local convenience"); Package Management section consistent with the new scripts.
    • CLAUDE.md: fix "No linting or type checking commands defined at root level" (false — root check-types/lint exist via turbo), drop the stale bun run lint implication, or better: reduce CLAUDE.md to a pointer at AGENTS.md to end the two-file drift permanently (owner's preference wins if they want both files).
  5. Optional (only if frictionless): adopt @dg/config tsconfig/vitest presets per D6 — explicitly skippable; never a churn PR for its own sake.

Orchestration: implementer → validator. Validator greps both docs for every claim and verifies each against the repo (the exact failure mode this phase exists to kill: phantom docs).

Validation gate: full chain from root — bun install --frozen-lockfile && bun run verify && bun run validate-content; knip and jscpd run exit 0 (report-only) and produce their local reports.

Effort: M.


4. Alignment Definition of Done — per-item status for dbuild

# DoD item (synthesis §3) Current status Closed by
1 Root-level headless test command, documented in AGENTS.md Missing — no test script anywhere Phase 2 + 5
2 Headless verification entrypoint runs typecheck + test + lint in one command; agent records green before a phase is done Missing — no root aggregate command Phase 4 (bun run verify)
3 Coverage measured & reported; baseline recorded; no threshold ever lowered Missing — no runner (Bun exception: report-only, no thresholds) Phase 2 + 5
4 Lint installed, zero errors, wired into the headless entrypoint; dead turbo lint task implemented or deleted Dead — next lint with no ESLint dep/config; turbo lint dangling; ignoreDuringBuilds: true Phase 1 + 4
5 AGENTS.md "Testing" section matches reality; no phantom docs Partial — honest about absence, but stale ([email protected], npm run in setup, CLAUDE.md false claims) Phase 5
6 Dead scaffold test deps removed or used Met (vacuously) — no RTL/jsdom scaffold deps present; nothing to remove n/a (Phase 2 keeps it true: no DOM deps added)
7 Renovate one-liner; no "latest" deps Half — no "latest" pins exist; no Renovate config Phase 5
8 Quality-report script (knip + jscpd) runnable locally, report-only Missing Phase 5
9 Nothing satisfied by a fake pass (no passWithNoTests, no echo-tests, no tautologies) Met today (nothing exists to fake); enforced going forward by validators reading every test All phases

5. Risks & repo-specific notes

  • Static-site scope discipline. No e2e, no component tests, no jsdom/RTL in v1 — value is pure-logic tests + content gating + the local build gate. Resist scope creep toward Playwright; the site has no backend to integration-test.
  • GH Pages deploy coupling. Deploys are manual gh-pages pushes from the owner's machine — that does not change under D5. The contract becomes: a green bun run verify immediately before deploy ⇒ the deploy is safe. Risk: the deployed site drifts from main if deploys lag — acceptable for a personal site; keep the pre-deploy verify habit documented in AGENTS.md.
  • Content corpus is live data. 55 markdown files, edited constantly (uncommitted edits in the tree right now). Corpus-integration tests assert structure, never counts; the Phase 3 build gate makes the validator an owner-facing contract — if it fails on real content, that's reported, not papered over.
  • Bun coverage is experimental — report-only forever until Bun's reporter is threshold-grade (synthesis §5); do not let a later PR add thresholds on top of misreportable numbers.
  • Export-refactor risk (blog.ts, Phase 2) is mechanical but touches a 598-line file with uncommitted edits — implementer must make the minimal diff (add export to existing pure functions, change nothing else) and the validator must diff behavior.
  • validate-content.js exit-code semantics are load-bearing once Phase 3 wires it into build — the refactor must not change output format or exit codes.
  • AGENTS.md hard bans (await import, require, any) bind all test code too; bun:test + static imports comply naturally.
  • Uncommitted work in the tree: all phases proceed on top of it; no stashing/resetting (fleet rule). Suggest the owner land or set aside current edits before Phase 0 touches the lockfile — bun install will rewrite bun.lock, which already has uncommitted modifications.

6. Effort summary

Phase Goal Effort
0 Next-drift resolution + workspace hygiene S
1 ESLint 9 flat config; kill dead next lint M
2 Seed bun:test over src/lib (2 sub-phases + phase-wide validation) M
3 validate-content build-gated + unit-tested S
4 Local gate composition (bun run verify) S
5 Renovate + quality report + docs truth M

Total: ~2 focused days of implementer/validator cycles. Sequencing rule: no phase starts before the previous gate is green; every gate is runnable from the repo root with the commands listed above.

Dominant language
TypeScript
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Getting set up

We have not checked this project's setup files yet. Start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.