Skip to content

fix(validate): the ready-ack advisory names the reason it fell back, and wraps - #269

Open
ZacxDev wants to merge 2 commits into
mainfrom
fix/258-readyack-advisory-names-the-cause
Open

fix(validate): the ready-ack advisory names the reason it fell back, and wraps#269
ZacxDev wants to merge 2 commits into
mainfrom
fix/258-readyack-advisory-names-the-cause

Conversation

@ZacxDev

@ZacxDev ZacxDev commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Closes #258.

The defect

In the canonical #206 shape — a static scaffold whose civitai-host.js has
been deleted — the presence-tier ready-ack advisory listed reasons it might have
fallen back, and none of them was the actual reason:

there is no index.html at the project root, or it holds a reference this CLI
cannot follow — a bundler alias, a generated file, an off-project URL

Not one is true of a five-file no-build app. The real reason is that
<script src="./civitai-host.js"> points at a file that is not there — and
EntryGraph.Gaps had already recorded exactly that, per reference, before
readyAckChecks returned the constant readyAckAdvicePresenceOnly and
discarded the slice.

The tiering is unchanged and correct. AGENTS.md item 20 deliberately treats
a reference to a missing file as a gap rather than a decided absence, and
records this case as a known trade. The message was the defect.

The fix

Surface Gaps generally, not the dangling reference specially — so all six
gap kinds (dangling reference, bare specifier, off-project URL, unreadable file,
file budget, depth truncation) reach the author from one change:

… it did NOT check that the file is loaded. Here is what it could not follow, in
this project's own terms — one of these is usually the actual bug: (1)
index.html <script src> "./civitai-host.js" points at civitai-host.js, which
does not exist — restore that file or fix the reference. …
  • Capped at 3, with the overflow counted out loud (; and 2 more this message does not list). A silently truncated list reads as "that was all of
    them" — the same class of lie as the guess it replaced.
  • The gap strings got a copy pass now that they are author-facing: one carried
    "this resolver's model of the project is incomplete", a fact about us.
    What sets a gap is untouched — only how it reads.
  • The reasons are spliced between …PresenceOnlyHead and …PresenceOnlyTail,
    before the shared remedy rather than after it: the remedy is the longest
    fragment, so appending put the one project-specific sentence two thirds of the
    way down a wall of generic advice.

Second half: the 1938-character line

The advisory is ~2 kB and app validate printed it as one line. Layout now
happens at the printer (internal/cmd/validate_print.go), wrapping every
finding to 79 columns with a hanging indent — one place fixes every long
message, including ones added later.

Wrapping inside Finding.Message would corrupt --json. This is the inverse of
AGENTS.md item 23: the field comes from the producer, the layout does not.
wrapRunes is reused as-is from exitcodes_doc.go (same package — no export,
no edit to that file).

Mutation matrix

--- FAIL leaf lines counted from output (never an exit code); every mutation
checksum-gated, so an edit that silently failed to apply aborts instead of
reading as a survivor.

mutation FAIL
discard the gaps (pre-#258 behaviour) 11
restore the guess to the head 1
remove the cap 2
truncate silently (no "+K more") 2
drop the one-line collapse 1
newline inside the message 2 (incl. the --json guard)
revert the printer to one unwrapped line 1
revert the gap wording to the maintainer form 3
splice the report after the tail 24
weak tier loses its disclosure 2
strong tier gains the disclosure (inverse) 1
report leaks another tier's own literal 1
comment-only null mutant 0 (survives, as it must)

🔴 One mutant survived the first round and is why an assertion moved.
Restoring the guess to …PresenceOnlyHead while keeping the real reasons
the most likely regression — reddened 0 subtests, because the absence check
was scoped to the gap report. TestPresenceAdviceNoLongerSpeculates now reads
the whole emitted message at a fixture where every quoted phrase is provably
impossible, with a positive control that the real cause is present so "says none
of the wrong things" is not satisfied by a message that says nothing.

TestReadyAckAdvisoriesStateTheirOwnStrength still passes and still can fail
in both directions (K: 2, L: 1 above). It reads the FIXED bases, so it
structurally cannot see text appended at runtime —
TestGapReportCannotSatisfyAnotherTiersStrengthAssertion covers that, and kills
the "report leaks orphan" mutant the strength test cannot.

Coverage

  • static and page-vite with the emitter deleted: the report names the
    referencing file (index.html / src/main.jsx), the specifier and the
    missing target — asserted on the gap section, not the whole message, since
    the shared remedy names index.html in every tier and a whole-message
    Contains would be vacuous.
  • Three further gap kinds: bare specifier, unreadable (over-cap) file,
    no root index.html, plus an off-project URL.
  • Controls: the orphan case keeps readyAckAdviceUnwired; all shipped templates
    stay silent; the strong tiers never acquire the gap apparatus.
  • --json is asserted on the decoded map[string]any (a Contains over
    raw stdout cannot tell a real newline from the \n escape).
  • The cap, at the fixture and at the function.

make ci

--- FAIL: 0 · build failed: 0 · 18 packages ok · gofmt -s -l .
clean over 299 .go files (count quoted as the positive control).

🤖 Generated with Claude Code

ZacxDev and others added 2 commits August 7, 2026 15:07
…and wraps

The presence-tier advisory GUESSED at why the entry graph had not resolved —
"there is no index.html at the project root, or it holds a reference this CLI
cannot follow — a bundler alias, a generated file, an off-project URL" — while
`EntryGraph.Gaps` already held the real, per-reference reason and readyack.go
discarded it. In the canonical #206 shape (a `static` scaffold whose
`civitai-host.js` has been deleted) none of the guesses is true: index.html
plainly references a file that is not there. A five-file no-build app sent its
author hunting for a bundler alias that cannot exist in it. Issue #258.

The gaps are now surfaced GENERALLY rather than special-casing the dangling
reference, so all six gap kinds reach the author at once — capped at 3 with the
overflow counted out loud, because a silently truncated list reads as "that was
all of them". The tiering is unchanged: AGENTS.md item 20's judgement that a
missing target is a GAP rather than a decided absence stands. The message was
the defect.

Second half: `app validate` printed the ~2 kB advisory as ONE 1938-character
line. Layout now happens at the printer (internal/cmd/validate_print.go), which
fixes every finding rather than the one that provoked it. Wrapping inside the
message would have corrupted `--json` — the inverse of item 23: the field comes
from the producer, the layout does not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…p report

A mutation found the hole: restoring the shipped speculation to
`readyAckAdvicePresenceOnlyHead` while KEEPING the real reasons — the most
likely way #258 regresses — reddened 0 subtests, because the absence assertion
was scoped to the gap report. `TestPresenceAdviceNoLongerSpeculates` now reads
the emitted message at a fixture where every quoted phrase is provably
impossible, with a positive control so "says none of the wrong things" cannot be
satisfied by a message that says nothing.

Records the measurement, the tiering-unchanged boundary and the mutation matrix
in AGENTS.md items 18 and 20.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

the ready-ack advisory names every cause except the actual one (a dangling script reference)

1 participant