Lands L1 of the agent-first coverage architecture (ADR-020) — the
cover-the-diff gate. Reads a merged lcov + git diff against a base
ref, asserts every changed *executable* line was exercised.
Script: scripts/coverage/diff.mjs (zero-dep Node ESM)
- parseLcov: SF -> Map<line, count>; only DA records read
- parseGitDiff: parses --unified=0 output into Map<file, Set<line>>
- computeDiffCoverage: cross-references both, emits result tree
- Allowlist of paths that don't gate (tests, configs, docs, .sh,
DI bootstrap, interfaces, CMS, factories, contracts, UI)
- Path matching handles three lcov path conventions: absolute,
repo-relative, and per-package relative
- CLI flags: --base (default origin/main), --lcov (default
coverage/lcov.info), --json (suppress stderr summary)
- stdout: machine-readable JSON for the dispatch loop
- stderr: human summary
- Exit 0 on pass, 1 on fail or error
Test surface: scripts/coverage/diff.test.mjs (14 tests, all green)
- Fixtures at scripts/coverage/__fixtures__/{sample.lcov,sample-diff.patch}
- Covers: lcov parsing, diff parsing, pass path, uncovered lines,
non-executable line skipping, no-coverage-data detection,
allowlist filtering, end-to-end mixed case, path matching
Wired:
- root package.json adds "coverage:diff" script
- .gitignore anchored so per-package coverage/ stays ignored but
scripts/coverage/ stays tracked
Smoke-tested end-to-end against packages/auth/coverage/lcov.info —
correctly skips shell scripts + manifest files (via allowlist + path
suffix match), correctly flags files not present in the per-package
lcov (which is expected; full repo coverage needs the L2 aggregate
that the next story lands).
CI integration deferred to the L2 aggregate story (the merged
coverage/lcov.info this script reads doesn't exist yet — pnpm
coverage:aggregate produces it).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
50 lines
1.8 KiB
Diff
50 lines
1.8 KiB
Diff
diff --git a/packages/auth/src/application/use-cases/sign-in.use-case.ts b/packages/auth/src/application/use-cases/sign-in.use-case.ts
|
|
index abc..def 100644
|
|
--- a/packages/auth/src/application/use-cases/sign-in.use-case.ts
|
|
+++ b/packages/auth/src/application/use-cases/sign-in.use-case.ts
|
|
@@ -1,0 +2,2 @@
|
|
+const a = 1;
|
|
+const b = 2;
|
|
@@ -4,1 +5,2 @@
|
|
-old
|
|
+const c = 3;
|
|
+const d = 4;
|
|
diff --git a/packages/auth/src/application/use-cases/sign-in.use-case.test.ts b/packages/auth/src/application/use-cases/sign-in.use-case.test.ts
|
|
index abc..def 100644
|
|
--- a/packages/auth/src/application/use-cases/sign-in.use-case.test.ts
|
|
+++ b/packages/auth/src/application/use-cases/sign-in.use-case.test.ts
|
|
@@ -10,0 +11 @@
|
|
+new test line
|
|
diff --git a/packages/auth/src/entities/models/user.ts b/packages/auth/src/entities/models/user.ts
|
|
index abc..def 100644
|
|
--- a/packages/auth/src/entities/models/user.ts
|
|
+++ b/packages/auth/src/entities/models/user.ts
|
|
@@ -2,1 +2,1 @@
|
|
-old
|
|
+modified
|
|
diff --git a/packages/blog/src/application/use-cases/get-article.use-case.ts b/packages/blog/src/application/use-cases/get-article.use-case.ts
|
|
index abc..def 100644
|
|
--- a/packages/blog/src/application/use-cases/get-article.use-case.ts
|
|
+++ b/packages/blog/src/application/use-cases/get-article.use-case.ts
|
|
@@ -11,0 +12 @@
|
|
+const uncovered = true;
|
|
diff --git a/packages/media/src/application/use-cases/upload.use-case.ts b/packages/media/src/application/use-cases/upload.use-case.ts
|
|
new file mode 100644
|
|
index 0000000..abc
|
|
--- /dev/null
|
|
+++ b/packages/media/src/application/use-cases/upload.use-case.ts
|
|
@@ -0,0 +1,5 @@
|
|
+export const uploadUseCase = () => {
|
|
+ return "uploaded";
|
|
+};
|
|
+// Line 4
|
|
+// Line 5
|
|
diff --git a/CLAUDE.md b/CLAUDE.md
|
|
index abc..def 100644
|
|
--- a/CLAUDE.md
|
|
+++ b/CLAUDE.md
|
|
@@ -1,1 +1,2 @@
|
|
-old text
|
|
+new text
|
|
+more text
|