⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
validateFetchableSourceUrl has two failure outcomes: "invalid_url" (unparseable / non-http
protocol) and "source_host_not_checked" (parses as http(s) but fails isSafeHttpUrl — plain
http://, or a private/loopback/link-local host, see src/review/content-lane/safe-url.ts:105-114).
Two call sites consume it and disagree.
Redirect-loop path (src/review/content-lane/source-evidence.ts:345-357) — unconditional:
const validation = validateFetchableSourceUrl(currentUrl);
if (!validation.ok) {
return withSourceDefaults(item, { status: "hard_failure", outcome: validation.outcome, error: validation.error }, spec);
}
First-hop path (src/review/content-lane/source-evidence.ts:419-430) — conditional:
const validation = validateFetchableSourceUrl(item.url);
if (!validation.ok) {
const invalidProtocol = validation.outcome === "invalid_url";
return withSourceDefaults(item, { status: invalidProtocol ? "hard_failure" : "passed", outcome: validation.outcome, … }, spec);
}
So a source URL that was never fetched at all — because it is http:// or points at a private
host — is reported as status: "passed". It is then also excluded from the report's warnings array,
which filters status !== "passed" (src/review/content-lane/source-evidence.ts:531), so it is
invisible to a maintainer too.
That the hard_failure shape is the intended one is provable from the file itself:
isDowngradableInconclusiveSource (src/review/content-lane/source-evidence.ts:489-494) is written
for exactly the combination the first-hop path can never produce:
return item.status === "hard_failure" && item.role === "distribution" && item.outcome === "source_host_not_checked";
That downgrade — "a distribution source we could not check is non-blocking when a verifiable canonical
source exists" — is the deliberate, spec-aware relaxation. The first-hop "passed" short-circuits it
and applies the relaxation unconditionally, to every role, with no canonical-source precondition.
Requirements
- The first-hop path must return
status: "hard_failure" for BOTH validateFetchableSourceUrl
failure outcomes, identical to the redirect path — the invalidProtocol ternary must be removed.
outcome and error must continue to carry the validator's own values unchanged.
isDowngradableInconclusiveSource / downgradeInconclusiveSourceWarnings must remain the ONLY
mechanism that makes a source_host_not_checked result non-blocking, unchanged.
- No change to
validateFetchableSourceUrl, isSafeHttpUrl, or the warnings filter.
⚠️ Required pattern: mirror the redirect-loop branch at
src/review/content-lane/source-evidence.ts:345-357 exactly — the same unconditional
status: "hard_failure". What does NOT satisfy this issue: introducing a new status value for
"not checked"; making the redirect path lenient to match the first hop; widening
isDowngradableInconclusiveSource to cover more roles; or adding a spec flag that selects between
the two behaviours.
Deliverables
All Deliverables above are required in a single PR. A PR that satisfies only some of them — for
example flipping the status (Deliverable 1) without proving the distribution-role downgrade still
works (Deliverable 3), which would turn this fix into a new false-close class — does not resolve this
issue.
Test Coverage Requirements
This repo enforces 99%+ Codecov patch coverage, branch-counted, and vitest.config.ts's
coverage.include covers src/**/*.ts — this file is measured. The removed ternary means both prior
arms must be re-covered by the new tests (invalid_url and source_host_not_checked), and both arms
of isDowngradableInconclusiveSource's role check need a test.
Expected Outcome
A source URL the gate could not fetch is never reported as having passed verification. Whether such a
source blocks is decided solely by the spec-aware isDowngradableInconclusiveSource downgrade, which
is the mechanism written for that decision.
Links & Resources
src/review/content-lane/source-evidence.ts:415-435 (the defect), :340-360 (the sibling), :485-500 (the downgrade), :525-535 (the warnings filter)
src/review/content-lane/safe-url.ts:105-114 (isSafeHttpUrl)
test/unit/content-lane-source-evidence.test.ts
Context
validateFetchableSourceUrlhas two failure outcomes:"invalid_url"(unparseable / non-httpprotocol) and
"source_host_not_checked"(parses as http(s) but failsisSafeHttpUrl— plainhttp://, or a private/loopback/link-local host, seesrc/review/content-lane/safe-url.ts:105-114).Two call sites consume it and disagree.
Redirect-loop path (
src/review/content-lane/source-evidence.ts:345-357) — unconditional:First-hop path (
src/review/content-lane/source-evidence.ts:419-430) — conditional:So a source URL that was never fetched at all — because it is
http://or points at a privatehost — is reported as
status: "passed". It is then also excluded from the report'swarningsarray,which filters
status !== "passed"(src/review/content-lane/source-evidence.ts:531), so it isinvisible to a maintainer too.
That the
hard_failureshape is the intended one is provable from the file itself:isDowngradableInconclusiveSource(src/review/content-lane/source-evidence.ts:489-494) is writtenfor exactly the combination the first-hop path can never produce:
That downgrade — "a distribution source we could not check is non-blocking when a verifiable canonical
source exists" — is the deliberate, spec-aware relaxation. The first-hop
"passed"short-circuits itand applies the relaxation unconditionally, to every role, with no canonical-source precondition.
Requirements
status: "hard_failure"for BOTHvalidateFetchableSourceUrlfailure outcomes, identical to the redirect path — the
invalidProtocolternary must be removed.outcomeanderrormust continue to carry the validator's own values unchanged.isDowngradableInconclusiveSource/downgradeInconclusiveSourceWarningsmust remain the ONLYmechanism that makes a
source_host_not_checkedresult non-blocking, unchanged.validateFetchableSourceUrl,isSafeHttpUrl, or thewarningsfilter.Deliverables
src/review/content-lane/source-evidence.tsreturnsstatus: "hard_failure"for ahttp://example.com/xURL and for ahttps://127.0.0.1/xURL, asserted by two new named cases intest/unit/content-lane-source-evidence.test.ts.outcomeis still"source_host_not_checked"(not"invalid_url") for thosetwo cases.
role: "distribution"http://source alongside a verifiablecanonical source is downgraded to
blocking: falsebydowngradeInconclusiveSourceWarnings—i.e. the intended relaxation still applies through the correct mechanism.
role: "canonical"http://source with no other verifiable canonical sourcemakes the overall report
status: "failed"(today:"passed").All Deliverables above are required in a single PR. A PR that satisfies only some of them — for
example flipping the status (Deliverable 1) without proving the distribution-role downgrade still
works (Deliverable 3), which would turn this fix into a new false-close class — does not resolve this
issue.
Test Coverage Requirements
This repo enforces 99%+ Codecov patch coverage, branch-counted, and
vitest.config.ts'scoverage.includecoverssrc/**/*.ts— this file is measured. The removed ternary means both priorarms must be re-covered by the new tests (
invalid_urlandsource_host_not_checked), and both armsof
isDowngradableInconclusiveSource's role check need a test.Expected Outcome
A source URL the gate could not fetch is never reported as having passed verification. Whether such a
source blocks is decided solely by the spec-aware
isDowngradableInconclusiveSourcedowngrade, whichis the mechanism written for that decision.
Links & Resources
src/review/content-lane/source-evidence.ts:415-435(the defect),:340-360(the sibling),:485-500(the downgrade),:525-535(the warnings filter)src/review/content-lane/safe-url.ts:105-114(isSafeHttpUrl)test/unit/content-lane-source-evidence.test.ts