Skip to content

ci(notice) + test(connscale): DAST nightly notified nobody, empty_claims SLO recorded only excursions (BACKLOG #318, #1211 limb one) - #594

Open
wshallwshall wants to merge 2 commits into
mainfrom
claude/builder-2-1211-capture
Open

ci(notice) + test(connscale): DAST nightly notified nobody, empty_claims SLO recorded only excursions (BACKLOG #318, #1211 limb one)#594
wshallwshall wants to merge 2 commits into
mainfrom
claude/builder-2-1211-capture

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Two commits from Builder 2, verified independently before pushing (tests run locally, ruff clean)
rather than taken on the handoff's word.

#318: dast.yml has no pull_request trigger, deliberately, and isn't a required context, so
every finding from the authenticated authorization sweep surfaced in the Actions tab and nowhere
else. nightly-notice.yml now watches it. Also fixed in the same pass: the issue body hardcoded
"Nightly (scheduled) CI failed." regardless of which workflow actually ran -- it now reads
$WF_NAME, same as the title already did. New guard: a watched name that resolves to no workflow,
or to one with no schedule: trigger, can never match the notice job's schedule gate, so it would
sit in the list looking like protection and matching nothing -- test_every_watched_workflow_exists_and_can_actually_fire
pins both arms for every watched name.

#1211 limb one: the empty_claims_monotonic SLO recorded a reading only once it had already
left its band, so a passing run kept nothing and the only samples that ever survived were the
excursions. Now records every reading on every run via GITHUB_STEP_SUMMARY, from the module
fixture (runs before any assertion, so green and red are recorded alike). Does not widen the band
-- that's limb two and stays blocked; no constant here is a judgement about the band.
_monotonic_slo was refactored onto a shared monotonic_pairs helper as part of this; its detail
string is byte-identical, checked with a 4,648-case differential against the old inline version
(0 divergence).

Neither commit touches engine code. Verified locally: 30/30 in
test_connscale_empty_claims_per_msg.py + test_nightly_notice.py, ruff check clean on all
changed Python files.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

wshallwshall and others added 2 commits August 25, 2026 14:53
…the excursions (BACKLOG #1211)

The `empty_claims_monotonic` SLO writes a number into `observed` only once a reading has
already left its band. A passing run records the literal string "monotonic" and discards
every value, so the only samples that ever survived a run were the excursions -- and a
sample selected on having excursioned cannot measure the distribution it excursioned
from. #1211 requires that variance before anyone may touch the band, and the readings to
establish it were being produced on every CI run and thrown away.

This records them. The emission sits in the module fixture, which runs before any
assertion, so a passing run and a failing one are recorded alike.

WHY THE STEP SUMMARY AND NOT A PRINT. A bare `print` in the smoke test does not work:
pytest captures a PASSING test's stdout and shows it only on failure, which leaves the
tail-only sampling exactly as it was. Measured on a throwaway repo -- the sample line was
absent from a green run's output. `$GITHUB_STEP_SUMMARY` renders on pass and fail alike,
is already the idiom in 19 workflow files here, and needs no ci.yml change, because CI
runs connscale through pytest rather than through a step this could hang an upload on.

NOTHING HERE WIDENS THE BAND. That is limb two and it stays blocked until the samples
exist. No constant in this change is a judgement about what the band should be.

The pairing is EXTRACTED rather than copied. `monotonic_pairs` and `lane_label` are now
the single definition of how readings are grouped into lanes and compared, read by both
the SLO that fails on an excursion and the emitter that records every reading -- so the
numbers a green run reports and the numbers a red run reports cannot drift apart. The
group-by-(sweep_mode, claim_mode) rule that BACKLOG #1101 records as load-bearing moves
with it. `_monotonic_slo` keeps its exact detail string: a differential over 4,648 cases
(exhaustive over orderings of None / zero / ties / the exact band boundary, plus 4,000
randomized multi-lane sets) found zero divergence from the old inline implementation, and
the differential was shown able to fail against a deliberately broken variant.

Verified end to end: a real passing `tests/test_connscale_smoke.py` run wrote four
readings to a step summary and left a prior step's content intact.

Seven mutants, all killed, each by a distinct set of tests: emit only the excursions;
hard-code the band instead of reading the tolerance; drop the claim-mode qualification
from the lane label; render an undefined reading as zero; cap the table silently;
truncate the summary instead of appending; swallow a failed write. Every mutant asserted
a unique anchor and a changed file hash before scoring, and every restore was
byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…med the wrong workflow (BACKLOG #318)

#318 carries this as a tracked follow-up rather than deferred scope: `dast.yml` has NO
`pull_request` trigger, deliberately, and is not a required context, so every finding
from the authenticated authorization sweep surfaced in the Actions tab and nowhere else.
An authenticated security sweep reporting into the void is the exact shape
`nightly-notice.yml` exists to end -- it just was not watching it.

Widened `workflows:` to include DAST, which is the fix the item names.

ALSO FIXED, because widening made it worse rather than introducing it: the issue BODY
opened with a hardcoded "Nightly (scheduled) CI failed." whatever had actually run. The
TITLE was already derived from $WF_NAME; the body was not, so a red Security run has been
opening an issue whose first line names CI. With a third watched workflow that is a
reader deciding what broke from a wrong sentence. It now reads from the same $WF_NAME the
title does.

NEW GUARD, one level up from the item. A watched name that no workflow answers to, or one
whose workflow has no `schedule:` trigger, can never match: the notice job fires only when
the completed run's event was `schedule`, so such an entry sits in the list looking like
protection and matches nothing, forever, silently. That is the notice's own failure mode
turned on itself. `test_every_watched_workflow_exists_and_can_actually_fire` asserts both
arms for EVERY watched name, so a fourth workflow added later cannot be added wrongly. Its
scan carries a positive control, because an empty name map would make every assertion in
it vacuous.

The module docstring claimed it "pins the three ways" the notice could stop working; there
were already six tests. A count in prose has no checker and has to be maintained by
whoever adds the next test, so it now states the kind rather than the number (SDS-3.6).

Four mutants, all killed: drop DAST from the list; watch a name no workflow answers to;
watch a real workflow that has no cron; restore the hardcoded CI in the body. Every mutant
asserted a unique anchor and a changed file hash before scoring, and every restore was
byte-identical. Two share a test by design -- they are its two arms -- and their failure
messages were checked to differ, the typo case listing every name actually present.

Scope: the nightly-notice widening only. Increment 2 of #318 (schema-driven breadth, the
MLLP/TCP/X12 ingress fuzzing, the /ui plane, a TLS black-box target) is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@wshallwshall
wshallwshall enabled auto-merge (squash) August 25, 2026 20:12
@wshallwshall
wshallwshall disabled auto-merge August 25, 2026 20:15
@wshallwshall
wshallwshall enabled auto-merge (squash) August 25, 2026 20:16
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