Skip to content

[quality] docs/contributing.md phantom-branch section still lists close.ts:37 and reopen.ts:34 (and 1815/1819), which #642 removed #679

Open

Description

@hivecommons-hive

Finding

docs/contributing.md ("Phantom "uncovered" branches after an await", lines 93-96) says the unit run lists four branches as uncovered — close.ts:37, reopen.ts:34, approve.ts:516 and auth.ts:237 — reporting 1815/1819 branches, with the e2e run repeating "the last two".

That was true when #641 (81da9cb) wrote it, but #642 (9c45581, merged the same day) inverted the guards in src/issueComment/close.ts and src/issueComment/reopen.ts from if (isAuthUser) { …awaited work… } into an early-throw if (!isAuthUser) { throw … }. With a short then-block the implicit-else count the v8 provider derives no longer goes negative there, so the artefact is gone from those two sites.

Evidence from the latest main build (run 42151611102, main@9aa12b8):

  • unit artifact coverage-9aa12b8… lcov.info: BRF 1913 / BRH 1911; the only negative records are approve.ts BRDA:516,83,1,-7 and auth.ts BRDA:237,9,1,-85. close.ts BRDA:37,3,0,5 / BRDA:37,3,1,4 and reopen.ts BRDA:34,3,0,4 / BRDA:34,3,1,4 are both positive.
  • e2e artifact coverage-e2e-9aa12b8…: approve.ts:516 (-37) and auth.ts:237 (-44) — so the e2e run repeats both remaining sites, not "the last two" of four.

The awk one-liner in the same section already finds the right answer; only the prose and the 1815/1819 figure are stale. A contributor following the doc would look for two phantom arms that no longer exist and might take a genuine new miss on close.ts/reopen.ts for a known artefact.

Recommendation

Docs-only change to docs/contributing.md: say two branches (approve.ts:516, auth.ts:237), 1911/1913, and that the e2e run repeats both; mention that close.ts:37/reopen.ts:34 showed the same artefact until #642 inverted their guards, so the count is a property of the current code and test mix rather than a fixed list. No test, ignore marker or threshold change — the section's own policy forbids those.

Priority

  • Impact: low
  • Effort: low

Filed by quality agent (hold-gated mode)


🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: 9aa12b8

— hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88

Activity

  1. added
    testingCreated by Hive for agent-filed issue provenance
    agent/qualityCreated by Hive for agent-filed issue provenance
    on Nov 7, 2026
  2. added
    kind/cleanupCategorizes issue or PR as related to cleaning up code, process, or technical debt.
    and removed on Nov 7, 2026
  3. hivecommons-hive commented on Nov 8, 2026

    @hivecommons-hive
    ContributorAuthor

    Folded finding. This was filed as a separate issue, but 1 open agent-filed issue (plus 6 closed as completed in the last 14 days) already cite docs/contributing.md, so it was folded into the oldest of them instead of opening another. Treat this issue as the tracker for that component: prefer one structural fix that covers every folded variant over a PR per variant.

    [quality] coverageIgnoreHints.test.ts fails inside a Stryker sandbox whenever the probed file is imported by tests/setup.ts

    Finding

    npm run test:mutation -- --mutate src/adapters/actions.ts (or any glob that includes src/adapters/actions.ts or src/platform/index.ts) stops at the dry run on main 9aa12b8:

    ERROR DryRunExecutor One or more tests failed in the initial test run:
        v8 ignore hints in src/ > arm-level hints (ignore next) > src/labels/lgtm.ts:67 names the unit test that drives the ignored arm
            -- bindLgtm rethrows every failure as an Error; the String(e) arm is unreachable
    — no __tests__/**/<name>.test.ts named, or the named file does not exist: expected undefined to be defined
    ERROR Stryker There were failed tests in the initial test run.
    

    Two things combine:

    • Stryker's sandbox prepends // @ts-nocheck to every file it copies (disableTypeChecks, default on), so inside .stryker-tmp/sandbox-*/ the hint at src/labels/lgtm.ts:66 is on line 67, and the driversOutsideTheHint key 'src/labels/lgtm.ts:66' in __tests__/coverageIgnoreHints.test.ts no longer matches. Two cases fail (names the unit test that drives the ignored arm and records out-of-hint drivers only for hints that still exist).
    • Stryker's vitest runner runs only test files related to the mutated files (vitest.related). __tests__/setup.ts imports ../src/adapters/actions and ../src/platform, so mutating either makes every test file related, including this one. The nightly set (stryker.nightly.config.mjs: mergeGate, labelMatch, hold, duration, tide, mergeQueue) never reaches it, which is why the scheduled mutation job is green (run 42454617747, 1827 tests in the dry run) and why npx stryker run stryker.nightly.config.mjs --dryRunOnly passes locally while the same sandbox copy fails under plain vitest run __tests__/coverageIgnoreHints.test.ts.

    Reproduced at 9aa12b8 under Node 24.22.2 and 26.12.0. docs/contributing.md ("Mutation checks") offers --mutate <glob> to probe any other file; for the two modules above, and for src/server/* passes that include src/platform/index.ts, that path is currently broken.

    Recommendation

    Make the guard number hints as the tree does regardless of which copy of the file it reads: strip a leading // @ts-nocheck line in coverageIgnoreHints.test.ts before counting lines (and in exportedNames, which slices the same text by those numbers), with a unit case that feeds a prefixed copy through the same path. Test-only; no src/, dist/ or config change.

    Decision taken by hive: normalise the text rather than re-key driversOutsideTheHint by reason, so the existing file:line keys and their "drop or move the entry" check keep working unchanged.

    Priority

    • Impact: medium (the documented ad-hoc mutation probe fails on two of the most-imported modules; CI unaffected)
    • Effort: low

    Filed by quality agent (hold-gated mode)


    🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: 8817bbf

    — hive: agent=quality backend=copilot model=claude-fable-5.1


    Folded finding. This was filed as a separate issue, but 1 open agent-filed issue (plus 6 closed as completed in the last 14 days) already cite docs/contributing.md, so it was folded into the oldest of them instead of opening another. Treat this issue as the tracker for that component: prefer one structural fix that covers every folded variant over a PR per variant.

    [quality] Gate proposal: add src/plugins/approve.ts to the nightly Stryker set

    Finding

    src/plugins/approve.ts decides the approved half of the merge decision (which approvers cover which changed files, when the label is added or removed, and the mirrored approve.github_review), yet it is outside both Stryker sets: the local default (stryker.config.mjs) and the scheduled mutation job (stryker.nightly.config.mjs), which today adds only src/plugins/tide.ts and src/utils/mergeQueue.ts. A behavioural regression in approve.ts that line coverage still reaches would therefore not fail anything, while the same regression in tide.ts fails the nightly run.

    This is a gate-scope finding rather than a coverage-gap finding: the repository already has the gate (mutation testing in CI) and a written policy for survivors (docs/contributing.md, "Mutation checks"), so no per-survivor issue is filed. A full read of the file's survivors is attached below so that adding it is mechanical.

    Evidence

    • Unit coverage (npm run test:coverage, coverage/lcov.info, 9aa12b8, 2026-11-08): approve.ts is fully line-covered, as the 99 % floor requires.
    • End-to-end coverage (npm run test:coverage:e2e, coverage-e2e/lcov.info, 9aa12b8, 2026-11-08): the bundle harness drives approve.ts as well; these runs are reported separately by design and are not combined (see docs/contributing.md).
    • Mutation run of the file, local, npx stryker run --mutate src/plugins/approve.ts through stryker.config.mjs at 9aa12b8 (2026-11-08): 503 killed, 33 survived, 0 no coverage (93.84 %). A second targeted run of lines 338-347, 395-420 and 457-492 with --force (no incremental reuse) reproduced the 11 survivors of those ranges exactly.
    • Nightly baseline: scheduled run 42454617747 at 9aa12b8, mutation job 9 min 15 s, artifact mutation-9aa12b8…: 762 killed / 31 survived over the six files (96.09 %). With approve.ts added at 503/33 the set scores 95.18 %, above the current break: 95 by about two survivors.

    Reviewed survivors of approve.ts (all 33)

    Each was read against the rule in docs/contributing.md: a survivor is a finding only when it changes an observable effect. None of these does.

    Mutant Why it is left
    approve.ts:99, :100 (×2), :116 — comment.body ?? '', body === '', (review.body ?? '') → a placeholder string A body of "Stryker was here!" carries no command and no reviewMarker, so the comment or review yields no event either way
    :138 'add' → ''; :230, :232 'ignore' → ''; :179 default: commandKind's non-cancel result is only ever compared to 'cancel'; effectiveKind's non-add/remove result falls into the switch's default either way
    :206 (×4) settings.require_self_approval && author !== '' → true, ||, placeholder With require_self_approval off the author is already in approving, so any file they could be suggested for is already covered; '' is never an approver login
    :241 remaining.size > 0 → >= 0; :256 excluded.add(best) removed An empty remaining yields an empty coverage map and breaks; every file the chosen approver covers leaves remaining, so they never score again
    :335 approvers: [] → placeholder for a file without an OWNERS path renderNotifier prints only entry.file for such an entry
    :341 entry === undefined → true; :344 else {}; :345 .sort() dropped Files that share a deepest OWNERS file share its approver set, so the merged, overwritten or first-kept list is the same already-sorted list
    :378, :379 [] → placeholder arrays The placeholder reviews are only reached with ignore_review_state on and no identity, where nothing reads them, or by approvalEvents, which skips a review without a user
    :383 (×3), :384 info text; :491 debug text Log output is not asserted
    :398 (×2) present.length > 0 → true, >= 0 removeLabels with an empty list issues no request (the hold.ts:70 case)
    :414 body.trim() → body The rendered notifier starts with [APPROVALNOTIFIER] and ends with the marker; it has no surrounding whitespace
    :460, :486 action === undefined → false Set.has(undefined) is false, so the skip is taken anyway (the tide.ts:445 case)
    :464, :490 ?? '' → placeholder The placeholder is neither approved nor contains reviewMarker
    :514 pull_request?.base → pull_request.base pullNumber is read from pull_request first and the missing-pull-request throw precedes this line

    Recommendation

    Add src/plugins/approve.ts to the nightly set and record the table above in docs/contributing.md next to the tide.ts / mergeQueue.ts table. No workflow change is needed: at ~9 min today, the job stays far inside its 60-minute timeout with ~536 more mutants (~6-7 min on the GitHub runner, by the tide.ts rate).

    stryker.nightly.config.mjs, exact replacement:

      mutate: [...base.mutate, 'src/plugins/tide.ts', 'src/utils/mergeQueue.ts', 'src/plugins/approve.ts'],
      thresholds: { high: 90, low: 75, break: 94 },

    and the doc comment's "tide.ts decides and performs the merge and mergeQueue.ts enqueues it" sentence gains "approve.ts decides the approved label they read".

    Decision taken by hive: break goes to 94, following the file's own rule ("just under what main reaches"): the set would score 95.18 %, and keeping 95 leaves a margin of two survivors, which a single new equivalent survivor plus a Timeout/Survived swing could consume and fail a scheduled run on a non-regression. If the maintainers prefer to keep 95, the only cost is that margin.

    • Accept or decline adding approve.ts to the nightly Stryker set (this is a change to the scheduled gate's scope and runtime, so it is parked for a maintainer's call rather than opened as a PR)

    Once accepted, a PR can land the config line, the threshold and the docs table together; nothing in it touches .github/workflows/.

    Priority

    • Impact: medium (the approval half of the merge decision is unguarded against behavioural regressions line coverage still reaches)
    • Effort: low (one config line, one threshold, one docs table)

    Risk: governance


    Filed by quality agent (hold-gated mode)


    🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: 9aa12b8

    — hive: agent=quality backend=copilot model=claude-fable-5.1


    Folded finding. This was filed as a separate issue, but 1 open agent-filed issue (plus 5 closed as completed in the last 14 days) already cite docs/contributing.md, so it was folded into the oldest of them instead of opening another. Treat this issue as the tracker for that component: prefer one structural fix that covers every folded variant over a PR per variant.

    [quality] Mutation gate does not cover src/utils/auth.ts; its two memo keys are never asserted

    Finding

    src/utils/auth.ts decides who may command the bot (org membership, collaborator, OWNERS, authorization.* policies). It is 100 % line- and function-covered by both the unit run and the bundle run, but it is outside both Stryker sets (stryker.config.mjs and stryker.nightly.config.mjs cover mergeGate, labelMatch, hold, duration, tide, mergeQueue), so the nightly mutation job never looks at it.

    Running it by hand at 9aa12b8 (npx stryker run stryker.config.mjs --mutate src/utils/auth.ts): 284 mutants, 267 killed, 9 timed out, 8 survived (97.18 %). Two of the survivors change observable behaviour:

    Mutant Effect
    auth.ts:146 memo key `${owner}/${repo}#${issueNum}` → ` ` checkIssueComments for a second issue in the same run answers from the first issue's commenters
    auth.ts:457 memo key `${owner}/${repo}` → ` ` retrieveOwnersFile for a second repository in the same run returns the first repository's root OWNERS

    No test pins either key: every existing memoization test (reads the root OWNERS file once per run, reads the comment thread once) uses one issue in one repository, so a collision is indistinguishable from the cache hit it asserts.

    The other six are equivalent (platform().log.debug text at :464, :475, :489; the contents === '' guard of rootOwnersIncludes at :439, which only saves a parse of an empty file).

    Evidence

    • Unit: npm run test:coverage at 9aa12b8 — auth.ts 100 / 98.71 / 100 / 100, the single branch miss being the documented phantom at :237.
    • End-to-end: CI artifact coverage-e2e-9aa12b89ea354112926584a27fb4dc06a7ac3ec2 of run https://github.com/cncf/prow-github-actions/actions/runs/42768459328 — the same figures for auth.ts.
    • Mutation: the local run above; reports/mutation/report.json is reproducible with the command given.

    Recommendation

    • Pin both memo keys in __tests__/utils/auth.test.ts: two issues in one run get separate comment reads and separate answers; two repositories in one run get separate OWNERS reads and separate answers.
    • Add src/utils/auth.ts to the mutate set of stryker.nightly.config.mjs so the gate that already exists covers the authorization code, and record the six equivalent survivors in the docs/contributing.md survivor tables as the other files do. At 97–99 % the file keeps the nightly total above the break: 95 floor.

    Decision taken by hive: extend the existing nightly set rather than the local default, which the docs deliberately keep small (~150 mutants, ~12 minutes); auth.ts adds ~280 mutants, the same order as tide.ts.

    Priority

    • Impact: medium (unit-covered; the collision needs a run that spans issues or repositories, which the Actions adapter does not and the App adapter isolates per event — but the key is the only thing preventing it)
    • Effort: low

    Filed by quality agent (hold-gated mode)


    🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: 9aa12b8

    — hive: agent=quality backend=copilot model=claude-fable-5.1


    Folded finding. This was filed as a separate issue, but 1 open agent-filed issue (plus 5 closed as completed in the last 14 days) already cite docs/contributing.md, so it was folded into the oldest of them instead of opening another. Treat this issue as the tracker for that component: prefer one structural fix that covers every folded variant over a PR per variant.

    [quality] Gate proposal: exact (100 %) line floor for the bundle coverage run once its four structural misses carry v8-ignore hints

    Finding

    The bundle coverage run (npm run test:coverage:e2e, job e2e-coverage of test.yml) gates at 99 % lines / statements / functions / branches. vitest.e2e-coverage.config.mjs says so itself: "A single lost branch is ~0.06 pp and will not cross the floor on its own; the floor bounds drift, it does not detect one-branch regressions." Every miss that run still reports on main is structural, so the floor could be exact for lines instead of approximate.

    Evidence — CI artifact coverage-e2e-9aa12b8… of run 42768459328 (main@9aa12b8), reproduced locally with the same command at the same revision (2613/2617 lines, 1769/1779 branches, 587/591 functions):

    Miss Why the bundle cannot reach it Unit evidence
    src/platform/index.ts:61 — the "no platform installed" throw in platform() src/main.ts installs the platform before any dispatch __tests__/platform/platformGuards.test.ts
    src/plugins/requireMatchingLabel.ts:126 — resumeRequiredLabels body only called from src/deferred.ts, the App/Worker resume path (already excluded from this run) __tests__/plugins/requireMatchingLabel.test.ts
    src/plugins/requireMatchingLabel.ts:163-164 — the === 'deferred' arm actionsPlatform().defer always returns 'continue'; only the App adapter defers __tests__/plugins/requireMatchingLabel.test.ts (line 192 stub)
    src/cronJobs/sweep.ts:139 branch String(e) arm, non-Error rejection #693 (held) hints it
    approve.ts:516, auth.ts:237 branches documented phantom await branches (docs/contributing.md); must not be hinted —
    actionsContext, sleep, platform reported FNDA:0 while their lines are hit source-map/remap artefact of the function record, not a behaviour miss —

    This is the sixth-plus instance of the same class — a bundle-unreachable arm lowers the e2e figure and is then hinted one PR at a time: #483, #537, #544, #549, #587 (merged), #693 (held). Per hive policy, three of a class means the gate is missing, so this is one proposal instead of another instance.

    Recommendation

    Tool: the thresholds that already exist in vitest.e2e-coverage.config.mjs, run by the existing e2e-coverage job — no workflow change.

    • Add /* v8 ignore next -- … unreachable from dist/index.js; <test>.test.ts drives it */ hints to platform/index.ts:61 and requireMatchingLabel.ts:125-126 and :163-164, in the form __tests__/coverageIgnoreHints.test.ts already enforces (reason + the unit test that drives the arm). Three hints, one PR.
    • Once those land (and chore(coverage): v8-ignore sweep.ts's unreachable String(e) arm in the per-pull-request setup catch #693), raise thresholds.lines in vitest.e2e-coverage.config.mjs from 99 to 100 and update the comment. Statements follow the same lines; leave branches at 99 because the two phantom branches cannot be hinted (the docs forbid it), and leave functions at 99 until the three FNDA:0 records are understood.
    • Update the npm run test:coverage:e2e row in docs/contributing.md ("Fails below 99 % lines …").

    Decision requested from the maintainers: accept an exact line floor for the bundle run (every new bundle-unreachable line must then be hinted or driven in the same PR that adds it), or decline and keep the drift-bounding 99. Decision taken by hive: propose lines only; branches and functions stay at 99 for the reasons above.

    Risk: governance — this changes what every future PR must satisfy, not just a number.

    Priority

    • Impact: medium (the gate's blind spot is self-documented; nothing is broken today)
    • Effort: low (three comment lines, one threshold, one doc row)

    Filed by quality agent (hold-gated mode)


    🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: 9aa12b8

    — hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88

  4. hivecommons-hive commented on Nov 9, 2026

    @hivecommons-hive
    ContributorAuthor

    [quality] Gate-scope addendum: src/utils/auth.ts belongs in the same nightly Stryker set change as approve.ts

    Same deliverable as the folded "add src/plugins/approve.ts to the nightly Stryker set" proposal above — one line in stryker.nightly.config.mjs — so this is a comment, not a second issue.

    Why auth.ts. It decides who may run /lgtm, /approve, /hold, /close and the label commands (policyAllows, assertPolicy, closePolicyAllows, assertAuthorizedByOwnersOrMembership), and #606/#607/#639/#641/#642 rewrote it (+129 lines net of deletions) after its last mutation pass. Like approve.ts it sits in neither Stryker set, so a behavioural regression that line coverage still reaches fails nothing.

    Evidence (main@9aa12b8). Local npx stryker run over src/utils/{auth,config,owners}.ts (perTest, concurrency 12, vitest.mutation.config.mjs minus the bundle harness and the src/-text-scanning tests; dry run 1656 tests in 2m39s):

    file mutants killed timeout survived score
    auth.ts 267 251 8 8 97.18 %
    config.ts 774 760 12 2 99.75 %
    owners.ts 327 314 12 1 99.71 %

    49m20s for all three. Every survivor, read at its exact column span, is equivalent or text-only:

    • auth.ts:146 (issueCommenters memo key → '') and :457 (rootOwners memo key → ''): RunState is built per delivery (appPlatform, src/adapters/app/platform.ts:92), and a delivery — cron included — carries one repository and one issue, so a collapsed key can never serve another issue's or repository's answer. The per-login authChecks keys (member:, collaborator:) were killed.
    • auth.ts:439 (contents === '' guard in rootOwnersIncludes): parseOwners('OWNERS', '') yields empty sets, so the guard is a short-circuit only — the same reading as the :422 survivor triaged earlier.
    • auth.ts:464/475/489, owners.ts:313: debug strings.
    • config.ts:312 (kind: 'repo' → ''): the discriminant is only ever compared with === 'url'.
    • config.ts:799 (toLowerCase → toUpperCase): a dedup Set key applied consistently to both sides; the stored logins are untouched.

    So adding auth.ts to stryker.nightly.config.mjs would hold the job's break: 95 today (the set's combined score stays above it), at roughly +270 mutants per nightly run. config.ts and owners.ts are not proposed: they are parsing/data code, not the decision, and at 774 + 327 mutants they would more than double the job for no decision-side gain.

    Unit/e2e line coverage for context (run 42573820881, artifacts coverage-9aa12b8… and coverage-e2e-9aa12b8…): all three files 100 % lines in both runs; auth.ts:237 is the documented await artefact. No per-survivor issue or PR is filed — nothing behavioural to pin.

    Decision taken by hive: fold auth.ts into the existing approve.ts nightly-set proposal rather than open a parallel one, because both are the same one-line edit to the same file.


    Filed by quality agent (hold-gated mode)


    🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: 9aa12b8

    — hive: agent=quality backend=copilot model=claude-fable-5.1

  5. hivecommons-hive commented on Nov 10, 2026

    @hivecommons-hive
    ContributorAuthor

    [quality] Reconciliation: the auth.ts memo-key survivors are equivalent today; nothing new to land

    The folded finding above ("Mutation gate does not cover src/utils/auth.ts; its two memo keys are never asserted", 2026-11-10) was filed before reading the gate-scope addendum of 2026-11-09 on this issue. Both runs saw the same eight survivors at 9aa12b8 (97.18 %). The addendum's reading stands: RunState is built per delivery and a delivery carries one issue and one repository, so collapsing the auth.ts:146 / :457 memo keys cannot serve another target's answer today. The later finding's "behavioural" label is withdrawn; no test PR is opened for it, and the nightly-set change remains parked on the approve.ts proposal's accept/decline box above.

    For the record, two tests that would kill those mutants were written and confirmed to fail against them (two issues in one run read separately; two repositories in one run read separately, with the org-membership answer shared per org). They are worth adding only if the sweep or another multi-target delivery ever starts calling checkIssueComments or retrieveOwnersFile across targets — that is the condition under which the keys become observable.


    🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: 9aa12b8

    — hive: agent=quality backend=copilot model=claude-fable-5.1

  6. hivecommons-hive commented on Nov 11, 2026

    @hivecommons-hive
    ContributorAuthor

    [quality] Gate proposal (folded here by the hive, component docs/contributing.md): exact 100 % line floor for the bundle coverage run once its structural misses carry v8-ignore hints

    Filed 2026-11-11 as a separate needs-decision issue; the hive consolidated it into this one because the last deliverable edits the same docs/contributing.md row. It is otherwise independent of the phantom-branch doc fix held in #680.

    Finding

    The bundle coverage run (npm run test:coverage:e2e, job e2e-coverage of test.yml) gates at 99 % lines / statements / functions / branches. vitest.e2e-coverage.config.mjs says so itself: "A single lost branch is ~0.06 pp and will not cross the floor on its own; the floor bounds drift, it does not detect one-branch regressions." Every miss that run still reports on main is structural, so the floor could be exact for lines instead of approximate.

    Evidence — CI artifact coverage-e2e-9aa12b8… of run 42768459328 (main@9aa12b8), reproduced locally with the same command at the same revision (2613/2617 lines, 1769/1779 branches, 587/591 functions):

    Miss Why the bundle cannot reach it Unit evidence
    src/platform/index.ts:61 — the "no platform installed" throw in platform() src/main.ts installs the platform before any dispatch __tests__/platform/platformGuards.test.ts
    src/plugins/requireMatchingLabel.ts:126 — resumeRequiredLabels body only called from src/deferred.ts, the App/Worker resume path (already excluded from this run) __tests__/plugins/requireMatchingLabel.test.ts
    src/plugins/requireMatchingLabel.ts:163-164 — the === 'deferred' arm actionsPlatform().defer always returns 'continue'; only the App adapter defers __tests__/plugins/requireMatchingLabel.test.ts (line 192 stub)
    src/cronJobs/sweep.ts:139 branch String(e) arm, non-Error rejection #693 (held) hints it
    approve.ts:516, auth.ts:237 branches documented phantom await branches (docs/contributing.md); must not be hinted —
    actionsContext, sleep, platform reported FNDA:0 while their lines are hit source-map/remap artefact of the function record, not a behaviour miss —

    This is the sixth-plus instance of the same class — a bundle-unreachable arm lowers the e2e figure and is then hinted one PR at a time: #483, #537, #544, #549, #587 (merged), #693 (held). Per hive policy, three of a class means the gate is missing, so this is one proposal instead of another instance.

    Recommendation

    Tool: the thresholds that already exist in vitest.e2e-coverage.config.mjs, run by the existing e2e-coverage job — no workflow change.

    • Add /* v8 ignore next -- … unreachable from dist/index.js; <test>.test.ts drives it */ hints to platform/index.ts:61 and requireMatchingLabel.ts:125-126 and :163-164, in the form __tests__/coverageIgnoreHints.test.ts already enforces (reason + the unit test that drives the arm). Three hints, one PR.
    • Once those land (and chore(coverage): v8-ignore sweep.ts's unreachable String(e) arm in the per-pull-request setup catch #693), raise thresholds.lines in vitest.e2e-coverage.config.mjs from 99 to 100 and update the comment. Statements follow the same lines; leave branches at 99 because the two phantom branches cannot be hinted (the docs forbid it), and leave functions at 99 until the three FNDA:0 records are understood.
    • Update the npm run test:coverage:e2e row in docs/contributing.md ("Fails below 99 % lines …").

    Decision requested from the maintainers: accept an exact line floor for the bundle run (every new bundle-unreachable line must then be hinted or driven in the same PR that adds it), or decline and keep the drift-bounding 99. Decision taken by hive: propose lines only; branches and functions stay at 99 for the reasons above.

    Risk: governance — this changes what every future PR must satisfy, not just a number.

    Priority

    • Impact: medium (the gate's blind spot is self-documented; nothing is broken today)
    • Effort: low (three comment lines, one threshold, one doc row)

    Filed by quality agent (hold-gated mode)


    🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: 9aa12b8

    — hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/qualityCreated by Hive for agent-filed issue provenancekind/cleanupCategorizes issue or PR as related to cleaning up code, process, or technical debt.stage/040-covered-by-prAn open PR covers this issuetestingCreated by Hive for agent-filed issue provenance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions