Skip to content

Run the test suite against TypeScript sources behind an uncheatable coverage gate - #22

Merged
unbraind merged 9 commits into
mainfrom
coverage-gate-typescript-sources
Jul 29, 2026
Merged

unbraind merged 9 commits into
mainfrom
coverage-gate-typescript-sources

Conversation

@unbraind

@unbraind unbraind commented Jul 28, 2026 •

Copy link
Copy Markdown
Owner

Why

Coverage in this package was never measured. Tests imported the compiled output from dist/, so the only numbers obtainable pointed at generated JavaScript offsets rather than lines an author can act on, and no threshold was wired into any gate — coverage could regress silently on every merge.

What changed

Tests run against the TypeScript sources. Node has stripped types without a flag since 22.18.0, which is the floor this package already declares in engines, so this needs no new tooling and no build step for the test run. Tests import source modules directly (../index.ts, ../projects.ts), which puts the coverage report on real source lines.

rewriteRelativeImportExtensions keeps the published artifact unchanged. Source files now import siblings as ./x.ts; tsc still emits ./x.js into dist/, so the shipped module graph is identical.

scripts/coverage-gate.ts is the gate. It enforces per-dimension thresholds and reconciles the reported file list against a walk of the source tree. That second check is why this is a script rather than a bare --test-coverage-lines flag:

Node omits files that never load from its coverage report entirely, rather than reporting them at zero. A threshold flag alone is therefore satisfiable by narrowing what the suite imports — a package can show 100% while an entire module goes unexercised.

Negative control, run before opening this PR: adding an untested file and listing it in coverageGate.sources makes the gate exit 1 and name the file. The check is not vacuous.

Thresholds ratchet. Pinned in package.json under coverageGate at the measured baseline so the number can only move up, and visible in review as a deliberate diff whenever it changes. release:check runs the gate in place of a bare test run, and CI runs it on a Node 22 + 26 matrix.

Measured baseline

Both source files, reported against TypeScript lines:

file line % branch % funcs %
projects.ts 97.00 81.41 100.00
index.ts 73.08 78.30 73.03
all files 76.14 78.93 75.74

index.ts is the target of the follow-up work to reach the mandated 100.

Verification

  • npm run check — passes
  • npm run coverage — 172 tests, 0 failures, both source files present in the report
  • npm run release:check — passes end to end (typecheck, build, coverage gate, prod audit, pack dry-run, changelog check)
  • Negative control — untested source file ⇒ exit 1 naming the file

The Node 22 matrix leg is unobserved locally (this host runs 26.5.0) and is confirmed by this PR's CI.

pm items

  • pm-github-uv1a — Enforce a real coverage gate by running tests against TypeScript sources

Summary by Sourcery

Enforce a source-level coverage gate by running the Node test suite directly against TypeScript entrypoints and wiring coverage thresholds into local and CI workflows.

New Features:

  • Introduce a configurable coverage gate script that runs tests with Node’s built‑in coverage and validates thresholds plus source file presence.
  • Add a dedicated coverage npm script that generates an lcov report and is used as the primary test check in release validation.

Enhancements:

  • Switch tests and helpers to import TypeScript sources instead of compiled dist outputs so coverage maps to real source lines.
  • Enable TypeScript options to rewrite relative import extensions and allow importing .ts modules, keeping the emitted JavaScript graph unchanged.
  • Record coverage gate configuration in package.json, including source roots, test patterns, thresholds, and ignores.

Build:

  • Update npm scripts so release:check runs the coverage gate instead of a plain test run.

CI:

  • Run the coverage-gated test suite in CI against a Node 22 and 26 version matrix instead of a single Node version.

Tests:

  • Point all test entrypoints and helpers at the TypeScript modules and test files directly rather than the built dist outputs.

Chores:

  • Add project management artifacts documenting the coverage gate feature.

Summary by cubic

Run tests directly against TypeScript sources behind a strict coverage gate that enforces thresholds and checks file presence. CI runs the gate on Node 22 and 26; the published JS output is unchanged.

  • Bug Fixes
    • Gate enumerates sources for --test-coverage-include; rejects missing, non‑TS, or .d.ts entries; normalizes lcov SF: paths to repo‑relative POSIX.
    • Deletes stale coverage/lcov.info before each run; exits with runner status before reading the report; pins TZ=UTC for stable coverage.
    • Validates coverageGate.ignore by verifying emitted JS is type‑only (strips block and line comments); resolves effective tsconfig via tsc --showConfig and fails closed if it cannot be resolved.
    • Chains build:test into npm run coverage; tsconfig.test.json set to no‑emit with repo rootDir; thresholds in package.json set to lines 76, branches 78, functions 75; npm test runs node --test on test/*.test.ts.

Written for commit 293c9e7. Summary will update on new commits.

Review in cubic

…overage gate

Coverage was unmeasured here. The suite imported the compiled output from
dist/, so the only numbers obtainable pointed at generated JavaScript offsets
rather than lines an author edits, and no threshold was wired into any gate.

Tests now import the TypeScript sources directly. Node has stripped types
without a flag since 22.18.0, which is the floor this package already declares
in engines, so this needs no new tooling. Enabling rewriteRelativeImportExtensions
lets a source file import its siblings as ./x.ts while tsc still emits ./x.js
into dist, leaving the published artifact's module graph unchanged.

The gate is scripts/coverage-gate.ts. Beyond enforcing per-dimension
thresholds it reconciles the reported file list against a walk of the source
tree, and that second check is the reason it is a script rather than a bare
flag: Node omits files that never load from its coverage report entirely
instead of reporting them at zero, so a threshold alone is satisfiable by
narrowing what the suite imports. A negative control confirms the check is not
vacuous - adding an untested source file makes the gate exit 1 and name it.

Thresholds are pinned at the measured baseline (76 line, 78 branch, 75
function) so the number can only ratchet up. release:check now runs the gate in
place of a bare test run, and CI runs it on both Node 22 and 26.

Baseline across the two source files: 76.14 line, 78.93 branch, 75.71
function. The weakest is index.ts at 73.08/78.30/73.03; projects.ts is at
97.00/81.41/100.00. Both are the targets of the follow-up work to reach the
mandated 100.

Tracked in pm-github-uv1a.
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @unbraind, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Changes

The project now runs tests against TypeScript sources, enforces configured line, branch, and function coverage thresholds, verifies required sources appear in LCOV output, and executes the coverage gate during release checks and CI on Node 22 and 26.

Coverage gate

Layer / File(s) Summary
TypeScript source test execution
tsconfig.json, tsconfig.test.json, index.ts, test/*, package.json
Test configuration, imports, and scripts now execute TypeScript source files and helpers directly.
Coverage gate configuration and enforcement
scripts/coverage-gate.ts, package.json, .gitignore
Coverage sources, thresholds, LCOV parsing, missing-source detection, and release-check wiring are added.
CI validation and coverage records
.github/workflows/ci.yml, .agents/pm/features/*, .agents/pm/history/*
CI runs the coverage command across Node versions, with baseline, negative-control, learning, and closure records documented.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI
  participant CoverageGate
  participant NodeTestRunner
  participant LCOV
  CI->>CoverageGate: Run npm run coverage
  CoverageGate->>NodeTestRunner: Execute TypeScript tests with coverage thresholds
  NodeTestRunner->>LCOV: Write coverage/lcov.info
  CoverageGate->>LCOV: Compare reported files with required sources
  CoverageGate-->>CI: Return success or failure
Loading

Possibly related PRs

  • unbraind/pm-github#28: Related Projects v2 implementation and test expectations touched by the source-import changes.
  • unbraind/pm-github#5: Related atomic import APIs and test suite referenced by the source-import changes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: running tests against TypeScript sources with a coverage gate.
Description check ✅ Passed The description is directly related to the changeset and accurately explains the coverage-gate and source-testing updates.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch coverage-gate-typescript-sources

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Reviewer's Guide

Runs the Node test suite directly against TypeScript sources and introduces a scripted coverage gate that enforces ratcheting coverage thresholds and verifies that all configured source files appear in the coverage report, wiring it into local scripts and CI on a Node 22/26 matrix without changing the published JS artifact graph.

Flow diagram for the coverage-gate TypeScript script

flowchart TD
  A[Start coverage-gate.ts] --> B[Read package.json]
  B --> C{coverageGate block present?}
  C -- No --> Z1[Print error and exit 1]
  C -- Yes --> D[Collect source files from coverageGate.sources]
  D --> E{Any source files found?}
  E -- No --> Z2[Print error and exit 1]
  E -- Yes --> F[Build required list excluding coverageGate.ignore]
  F --> G[Ensure coverage directory and lcov.info path]
  G --> H[spawnSync node --test with coverage flags and config.tests]
  H --> I{spawnSync error?}
  I -- Yes --> Z3[Print error and exit 1]
  I -- No --> J[Read lcov.info and collect reported files]
  J --> K{lcov.info readable?}
  K -- No --> Z4[Print error and exit 1]
  K -- Yes --> L[Compute missing = required - reported]
  L --> M{missing files?}
  M -- Yes --> Z5[Print missing files and exit 1]
  M -- No --> N{Test exit status === 0?}
  N -- No --> Z6[Exit with test status]
  N -- Yes --> O[Print success message and exit 0]
Loading

File-Level Changes

Change Details Files
Run tests directly against TypeScript sources and add a reusable coverage script entrypoint.
  • Change the npm test script to execute Node’s built-in test runner over TypeScript test files instead of compiled dist-test JavaScript.
  • Add a dedicated npm coverage script that builds once and invokes the new coverage gate script.
  • Update the release:check script to use the coverage script instead of a plain test run.
package.json
Introduce a configurable TypeScript-aware coverage gate that enforces thresholds and ensures all source files are represented in coverage output.
  • Add a coverageGate configuration block to package.json specifying source roots, test globs, threshold percentages, and ignore exemptions.
  • Implement scripts/coverage-gate.ts to run node --test with experimental coverage flags against the configured tests and sources, emit lcov output, and enforce per-dimension thresholds wired from package.json.
  • Walk configured source paths to collect .ts files (excluding .d.ts), normalize them to repo-relative POSIX paths, and compare the expected set (minus ignores) against files present in the lcov report, failing with a descriptive message when any are missing.
  • Ensure coverage directory and lcov file are created/read, propagate the underlying test runner exit code, and print a success summary when thresholds and presence checks pass.
package.json
scripts/coverage-gate.ts
Run CI against a Node 22 and 26 matrix and gate on coverage instead of plain tests.
  • Add a Node.js version matrix (22 and 26) with fail-fast disabled to the CI workflow test job.
  • Use the matrix node-version variable when setting up Node in CI.
  • Replace the test step in CI with the npm run coverage script so coverage gating runs in CI, followed by the existing production audit step.
.github/workflows/ci.yml
Point tests at TypeScript sources instead of built JavaScript and align helper references.
  • Update all test imports that previously pulled from ../dist/*.js to import directly from the TypeScript sources (../index.ts, ../projects.ts, and helper .ts files).
  • Adjust the helper child process script references and usage messages from .js to .ts so spawned tests run against TS entrypoints.
  • Ensure type imports in tests also come from the TS modules, keeping the type shapes aligned with the source entrypoints.
test/helpers/comment-sync-child.ts
test/import-lock.test.ts
test/atomic.test.ts
test/comments-sync.test.ts
test/dryrun-preview.test.ts
test/link-deps.test.ts
test/smoke.test.ts
test/projects.test.ts
Enable importing .ts extensions in source and keep compiled artifact imports stable via TypeScript compiler options.
  • Switch the internal import in index.ts from ./projects.js to ./projects.ts to reflect the TS source layout.
  • Enable rewriteRelativeImportExtensions and allowImportingTsExtensions in tsconfig.json so that relative .ts imports in source are rewritten to .js in the emitted dist output, preserving the published JS module graph.
index.ts
tsconfig.json
Add PM metadata files for the coverage gate feature.
  • Add a feature definition toon file describing the coverage gate work.
  • Add a corresponding history JSONL entry for the new pm-github-uv1a item.
.agents/pm/features/pm-github-uv1a.toon
.agents/pm/history/pm-github-uv1a.jsonl

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@greptile-apps

greptile-apps Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

Greptile Summary

Runs the package test suite against TypeScript sources and enforces coverage via a custom gate in CI and release checks.

  • Tests import .ts sources directly; rewriteRelativeImportExtensions keeps published dist/ imports as .js.
  • Adds scripts/coverage-gate.ts with thresholds, source-tree presence reconciliation, ignore type-only checks, stale lcov deletion, and fail-closed tsconfig resolution.
  • CI matrix runs coverage on Node 22 and 26; release:check uses npm run coverage instead of bare tests.

Confidence Score: 5/5

The PR appears safe to merge; no blocking failures remain from this follow-up pass.

No blocking failure remains.

Important Files Changed

Filename Overview
scripts/coverage-gate.ts Coverage gate runs Node test coverage, enforces thresholds, reconciles loaded sources, and verifies ignore entries are type-only.
package.json Adds coverage script and coverageGate config; release:check and test scripts point at TS tests and the gate.
.github/workflows/ci.yml CI test job matrices Node 22/26 and runs the coverage gate.
tsconfig.json Enables allowImportingTsExtensions and rewriteRelativeImportExtensions for .ts imports with unchanged emit.
tsconfig.test.json Switches test project to noEmit and includes test/scripts sources for typecheck without dist-test emit.

Reviews (9): Last reviewed commit: "Fail closed when the effective tsconfig ..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.agents/pm/features/pm-github-uv1a.toon:
- Line 20: Correct the baseline measurement date from July 29, 2026 to July 28,
2026 in the feature entry at .agents/pm/features/pm-github-uv1a.toon:20-20 and
the matching body payload at .agents/pm/history/pm-github-uv1a.jsonl:2-2; keep
all coverage metrics and other event data unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c0068dcd-1d62-4353-a489-3732536fd943

📥 Commits

Reviewing files that changed from the base of the PR and between 8f12d7b and 24b4146.

📒 Files selected for processing (17)
  • .agents/pm/features/pm-github-uv1a.toon
  • .agents/pm/history/pm-github-uv1a.jsonl
  • .github/workflows/ci.yml
  • .gitignore
  • index.ts
  • package.json
  • scripts/coverage-gate.ts
  • test/atomic.test.ts
  • test/comments-sync.test.ts
  • test/dryrun-preview.test.ts
  • test/helpers/comment-sync-child.ts
  • test/import-lock.test.ts
  • test/link-deps.test.ts
  • test/projects.test.ts
  • test/smoke.test.ts
  • tsconfig.json
  • tsconfig.test.json

Comment thread .agents/pm/features/pm-github-uv1a.toon
unbraind added 6 commits July 29, 2026 02:02
…urce walk

Brings this package onto the final form of the gate after three defects were
found and fixed on the reference implementation while this rollout was in
flight.

Pin TZ=UTC when spawning the runner. Code that branches on a timestamp's UTC
offset takes different paths under a local offset than under UTC, so the
reported percentage moved between a contributor's machine and CI and a
threshold pinned to one failed on the other.

Chain build:test into the coverage script. release:check no longer runs
`npm test`, so tsconfig.test.json - the only config that typechecks the test
tree and the gate script itself - had dropped out of every gate, and Node
strips types without checking them. Adding it exposed that the config also
inherited rootDir from the base config, which tsc rejects once the test build
stops emitting; rootDir is now overridden to the repository root.

Enumerate sources by walking a directory instead of listing files. A file list
freezes the required set, so a module added later was never required and an
untested new file slipped the gate entirely - the exact blind spot the presence
check exists to close. The walk prunes non-source directories, and the coverage
include flags are derived from the enumerated list so the measured set and the
required set are the same set by construction.

Thresholds re-measured against the corrected gate.
CI measured 78.93% branch where this host measured marginally higher, so the
threshold rejected its own baseline. CI is the authoritative gate, so the
ratchet is set from its floor.
…e edges

Three review findings raised on this rollout, all valid.

Report the runner's exit status before the never-loaded diagnostic. A failing
suite or an unmet threshold can legitimately leave sources out of the lcov
output, and the gate then blamed a missing import for what was actually a test
failure the author needed to see first.

Normalise lcov `SF:` paths to repository-relative POSIX form. The reporter
emits working-directory-relative paths on Linux, but that is not contractual
and absolute paths have been seen on Windows runners; unnormalised, the
presence check inverts into a permanently red build accusing every source file
of never loading.

Diagnose a non-existent `coverageGate.sources` entry instead of throwing a raw
ENOENT stack out of statSync, which was the one failure mode in this script
that did not produce a `coverage-gate:` message.
…s, ignore abuse

Delete the previous lcov report before the run. mkdirSync left an existing
coverage/lcov.info in place, so a run that produced no report at all was
checked against the previous run's file - the gate could pass by reading
history instead of by measuring anything.

Validate `coverageGate.ignore`. The exemption exists for type-only modules,
which erase to nothing and can never appear in a report, but it was trusted
blindly - making it the one supported way to remove an executable module from
both the measured set and the required set, the exact escape this gate exists
to prevent. Entries are now checked against the compiled output: TypeScript
emits `export {};` and nothing else for a module that erases completely.

Reject a non-TypeScript or declaration-file entry in `coverageGate.sources`
rather than requiring a file that can never be reported.
… at all

The status check sat between the report parse and the presence check, so it
covered the "sources missing from the report" diagnostic but not the "no report
was written" one directly above it. A runner that exits non-zero without
producing lcov - a test file that fails to load, an aborted run - still hit the
missing-report branch first and exited with that message, so CI showed a
coverage-report failure where the author needed to see a test failure.

The check now runs immediately after the spawn, before the report is touched.
…only

The type-only check normalised away line comments but not block comments, so a
documented type-only module whose JSDoc survived emit would have been read as
runtime code and rejected - a false failure on the exemption path.

Applied as hardening rather than as a reproduced bug: with tsc's default emit
settings the leading JSDoc on a fully-erased module is not carried into the
output. Comment-preserving configurations exist, the normalisation is a single
expression, and a false rejection on this path is confusing enough to pre-empt.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/coverage-gate.ts`:
- Around line 98-100: Update the configuration loading used by the coverage
mapping logic around tsConfig, tsOutDir, and tsRootDir to resolve the effective
TypeScript compiler options, including the tsconfig.test.json extends chain,
rather than parsing tsconfig.json directly with JSON.parse. Use TypeScript’s
config parser or an equivalent resolution mechanism so emitted-file paths,
especially scripts/**/*.ts, are mapped against the inherited outDir and rootDir.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 52b75309-3e80-4cb0-b53a-59e1192e57c7

📥 Commits

Reviewing files that changed from the base of the PR and between 3f52d38 and 8b4c9ea.

📒 Files selected for processing (1)
  • scripts/coverage-gate.ts

Comment thread scripts/coverage-gate.ts Outdated
unbraind added 2 commits July 29, 2026 04:00
… as JSON

The ignored-module check located a file's emitted output by reading outDir and
rootDir straight out of tsconfig.json with JSON.parse. That is wrong twice
over: tsconfig may be JSONC, where JSON.parse throws on a perfectly valid
config, and either option may be inherited through an `extends` chain, where
reading direct fields silently yields the wrong paths and the check then looks
for compiled output that was never going to be there.

It now asks `tsc --showConfig`, which returns the flattened configuration the
compiler actually used, and warns and falls back to the conventional defaults
if the compiler cannot be reached. The resolution runs only when
`coverageGate.ignore` is non-empty, so no package pays for it unless it uses
the exemption.
The fallback warned and assumed outDir dist / rootDir "." when
`tsc --showConfig` could not be reached. That was the wrong default for this
consumer: the resolved paths feed the check deciding whether an exempted module
is genuinely type-only, so guessing the emit layout can clear an executable
module by inspecting the wrong file - the one outcome this gate exists to make
impossible. Leniency here is indistinguishable from a bypass.

It now exits 1 and prints the compiler's stderr.
@unbraind
unbraind merged commit 60862dc into main Jul 29, 2026
7 checks passed
@unbraind
unbraind deleted the coverage-gate-typescript-sources branch July 29, 2026 02:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant