Skip to content

0.1.9 — eight corrections to the report guide, and a gate against absent fields

Choose a tag to compare

@github-actions github-actions released this 30 Aug 17:54
5ca90b8

This release changes what the kit SAYS, not what it does. Every consumer-visible
change is in the check-report JSON guide — the page acc show how-to-read-the-check-report-json --body prints and the page a script author is
pointed at. Eight of its statements were wrong; at least one of them describes a
check a reader can write today that will never fire.

What did not change

Nothing about the tool's behaviour, and this is the more useful half of the note
if you are deciding whether to upgrade.

  • No report field was added, removed, renamed or retyped. The same target checked
    with the previous release's kit and this one produces documents that differ only
    where any two runs differ: capturedAt and the millisecond timings.
  • acc schema is byte-identical across the range.
  • No rule, checker, verdict, count, exit code, flag or command changed. The only
    source file in the range is src/acc/kit/report.ts, and its changes are two
    new exported constants used by the repo's own lint plus corrected comments; no
    interface gained, lost or altered a field.

The upgrade itself breaks nothing. The corrections below are a different
matter: several describe checks that were wrong when you wrote them and are
wrong now, so read them as a work list rather than as reference.

Corrections you may have to act on

Each was checked against the code or the tests before being written here.

  • spawnFailed is always false in any report this kit writes. The guide
    previously invited a CI check on it. That check can never fire: the first probe
    that cannot spawn aborts the run (record.ts) and surfaces as a not_found
    error envelope (commands/check.ts), so no report is produced at all. If you
    branch on spawnFailed to catch a target that will not execute, you have a
    check that passes silently forever — test for the not_found envelope instead.
    This is the one correction that turns a working-looking pipeline into a known
    blind spot.
  • exitCode and signal are not "exactly one of the two". Both are null on
    the path where the target's close never arrived, and exitCode is also
    nulled when the kit killed the target itself — timedOut OR truncated, and
    the guide never mentioned the second. Code shaped as exitCode === null →
    "signal names the death" breaks on both. Read crashed, timedOut and
    truncated before attributing a death.
  • counts is not a partition; do not derive one count by subtracting others.
    Every count is taken over the applicable, unwaived findings, and a passing
    diagnostic finding is counted by none of them.
  • evidenceGaps is not scoped to passes. Fails and unverifieds contribute
    rows too, opening with the verdict and the finding's detail. Reading a row as
    a qualified pass is the misreading this field most invites; check the
    matching finding's verdict first.
  • sweep reads in one direction only. The hash covers observation ids, exit
    codes, signals and the two stream digests, and nothing else. Two different
    marks prove the evidence differs; equal marks do NOT prove it identical — a
    hang and an outside kill that both died silently collide, and so do two runs
    differing only in timing. The old text claimed the equal direction.
  • launchAdjustment is a replay aid, not a delivery check. It reports the
    argv the kit put on the WIRE; what the target receives is args, which is the
    point of the compensation. Presence tells you the compensation fired; absence
    tells you nothing, because the ordinary absence is a target with no bun layer to
    compensate for. So you cannot read the field, either way, as confirming that the
    target got what was recorded — including in the one documented case where
    delivery really does differ, a bun layer hidden inside a wrapper script, where
    nothing fires and nothing is reported.
  • excused was never explained at all — it appeared only as a value in the
    specimen, so this is a new statement rather than a corrected one, listed here
    because it is equally actionable. It is true only while a knownFailures entry
    is suppressing a live fail or unverified; it goes false the moment the rule
    passes, and the config entry then surfaces in staleExpectations.
  • timeToFirstByteMs: null is not a hang. A silent success produces the same
    null; timedOut is what separates them.
  • A finding's detail carries no per-observation clause. The lookup table
    promised one clause per observation that decided the verdict, which the body of
    the same page denied. Use probes for what was sent, never the clause order.

The page also gained entries rather than corrections for fields it never
mentioned at all — capturedAt, declaration, recordedSurfaces,
stdoutDigest and stderrDigest — and for fields that previously appeared only
inside the JSON specimen or in passing, where a reader got a value and no
statement: probeLevel, waived, unresolved, notApplicable, and
the staleExpectations/inertExpectations pair. It also gained an
advertisedVerbs specimen printed by a command the page gives you, run against
the kit's own CLI, so its shape can be checked rather than trusted.

For contributors: the gate behind it

Two report fields shipped undocumented in two consecutive releases, each caught
only because a reviewer happened to look. Two chained gates now make that a build
failure rather than a habit. DOCUMENTED_REPORT_FIELDS in
src/acc/kit/report.ts is a spec bound by a mapped type to the four interfaces
a report is built from — Report, ReportedFinding, ReportedObservation and
EvidenceProbe — so adding a field to an interface fails tsc until the field
is listed; being listed then fails the artifact docs lint
(bun run docs:lint:artifacts) until the field is either defined in the guide or
declared in UNDOCUMENTED_REPORT_FIELDS. The lint prints two numbers on every
run — currently report fields: 64 listed, 0 declared undocumented — because one
number cannot separate a field that got documented from a field that left the
type.

State the limits, because a green check is a claim:

  • Top-level keys only. A key whose value is an object — surface, counts,
    configSource, targetIdentity, declaration, recordedSurfaces,
    advertisedVerbs, the elements of waivers — is a leaf to the gate, and the
    fields inside it are covered by neither half. Both misses that motivated the
    work were top-level keys; a recursive spec is separate, larger work.
  • Shape, not truth. The gate certifies that a definition-shaped sentence
    exists. It cannot read one. A bullet that restates the field's own name passes,
    and that is worse than absence, because absence at least signals undocumented.
  • Forward only. A field removed from a type but left in the guide is not
    caught.
  • Declared absence is a legal answer. A field may pass by being named in
    UNDOCUMENTED_REPORT_FIELDS instead of being explained. The property is "no
    field is absent without someone saying so", not "every field is documented".

The set is empty at this revision, so nothing is currently declared undocumented.

0.1.9 (2026-08-30)

Features

  • docs: declared absence, so no field is silently undocumented (a547130)
  • docs: the JSON guide cannot omit a report field (1031c1a)
  • docs: the JSON guide cannot silently omit a report field (0739e1d)

Bug Fixes

  • docs: correct four invented claims in the JSON guide rewrite (1299465)
  • docs: drop the overreaching counts clause and anchor the term fallback (0bb7a60)
  • docs: repair an inverted field description and six review findings (4f8ca86)
  • docs: three more false bullets, and both homes of the sweep claim (a69fb5a)