Skip to content

fix: give the post-release triage sweep a scheduler-level trigger (#1386) - #1402

Merged
fdaviddpt merged 3 commits into
mainfrom
fix/1386
Sep 9, 2026
Merged

fix: give the post-release triage sweep a scheduler-level trigger (#1386)#1402
fdaviddpt merged 3 commits into
mainfrom
fix/1386

Conversation

@fdaviddpt

Copy link
Copy Markdown
Contributor

What

A post-release triage sweep is a stated cadence rule (three to four ticks, then a release, then a
triage sweep, #855) that has never fired on its own -- the first sweep this loop ever recorded was
run by hand, across 31 tagged releases. scripts/oss_state.py --triage-recorded/--last-triage
already record and read the last sweep in three states; nothing consumed the answer, because the
cadence rule lives in skills/manager/phases/accounting.md, read only by a sub-manager that dies
with its own context before a release and cannot count ticks or releases across its own spawns.

Closes #1386.

How

scripts/triage_trigger.py follows the same shape scripts/release_trigger.py (#966) already
gives its own cadence step: a real threshold in config (release.triggers.triage_after_release,
absent means not wanted, the same rule curate_route_threshold already uses), evaluated
mechanically against the most recent tag's own commit date and the last recorded sweep, in three
states -- due / not-due / could-not-tell, never folding an unreadable input into not-due.

commands/tick.md reads it at the RELEASE: released handback -- the one point in the scheduler
that spans ticks -- and dispatches oss:triager on due. It then records the sweep's completion
with oss_state.py --triage-recorded once the triager's own final message confirms the sweep ran,
closing the loop back to not-due; without that step the trigger would fire on every release
forever (a self-review finding, see below).

scripts/oss_config.py gains TRIGGER_BOOLEAN_KEYS to validate the new boolean trigger key
alongside the existing numeric merged_prs/soak_hours ones. skills/manager/phases/ accounting.md's Cadence section and docs/overview.md's "What is not true yet" section (the exact
worked example this issue's own body is built from) are both updated to describe the mechanism
rather than the gap.

Out of scope, on purpose

The label-coverage measurement (how much of the open board carries no priority label) that
accounting.md itself still calls "still unbuilt" is a separate, second half of the same defect
this issue's body names, and this lane does not touch it -- confirmed against the maintainer's own
issue comment narrowing the fix to the trigger alone.

Self-review

Two spawns (Explore, oss:auditor) against the committed diff. Both findings fixed in place:

  • docs/overview.md's "What is not true yet" section still described the exact defect this diff
    closes as current. Reworded to the past tense.
  • The automated oss:triager dispatch never advanced --last-triage, so due would never clear.
    commands/tick.md now records the sweep once the triager confirms it ran.
  • A third finding (the new TRIGGER_BOOLEAN_KEYS validator branch was correct but had no
    exercising test) closed with two new tests in tests/test_oss_config.py.

Tree-snapshot compare showed mutated, attributable entirely to this session's own legitimate
self-review fix commits made after the pre-review snapshot -- both reviewers explicitly reported
performing no mutation of their own.

Tests

Red: before scripts/triage_trigger.py existed, tests/test_triage_trigger_1386.py failed at
import (ModuleNotFoundError: No module named 'triage_trigger').

Green: python3 -m pytest tests/test_triage_trigger_1386.py -q --no-cov -> 8 passed.

Guards derived via scripts/lane_setup.py 1386 --lane <each touched file> (six named:
test_bare_gh_git_spawn_sweep_1165, test_claude_md_currency, test_command_references,
test_content_invariants, test_gate_state_consumers_328, test_unwired_scripts_253), plus the
byte-budget and config test files directly relevant to the touched files -- 787 tests total,
all green. Full pytest tests/ -q was not run locally, per this repo's own rule that CI's 13-leg
matrix is the authority and a local full run answers a weaker question than CI does.

🤖 Generated with Claude Code

https://claude.ai/code/session_012UYdzDHX3k3C2Eh7B5hRVH

[AI-generated]

fdaviddpt and others added 3 commits September 9, 2026 23:59
)

`scripts/oss_state.py --triage-recorded`/`--last-triage` (#855) already
record and read the last sweep in three states, and nothing consumed
the answer: the cadence rule in `skills/manager/phases/accounting.md`
lives where its only reader, a sub-manager, dies with its own context
before it can act on it and cannot count ticks or releases across its
own spawns.

`scripts/triage_trigger.py` follows the same shape `release_trigger.py`
(#966) already gives its own cadence step: a real threshold in config
(`release.triggers.triage_after_release`, absent means not wanted,
same rule `curate_route_threshold` uses), evaluated mechanically against
the most recent tag's own commit date and the last recorded sweep, in
three states -- `due` / `not-due` / `could-not-tell`, never folding an
unreadable input into `not-due`. The scheduler reads it at the
`RELEASE: released` handback in `commands/tick.md` -- the one actor
spanning ticks -- and dispatches `oss:triager` when it fires.

`scripts/oss_config.py` gains `TRIGGER_BOOLEAN_KEYS` to validate the
new boolean trigger key alongside the existing numeric ones without
folding it into their "expected a number" check.

`skills/manager/phases/accounting.md`'s own Cadence section now points
at the scheduler's own check rather than restating a second copy of
when the trigger fires, and its measured size crossed its budget
ceiling (raised, with the usual ~10% headroom, in the same commit);
`commands/tick.md` grew under its own existing ceiling. `CLAUDE.md`'s
byte-budget table rows for both files are updated in the same commit,
per its own third exception for a lane that changes a budgeted file's
measured size.

Explicitly out of scope, named rather than silently done: the
label-coverage measurement (how much of the open board carries no
priority label) `accounting.md` itself still calls "unbuilt" -- a
separate, second half of the same defect this issue's body names but
does not ask this lane to close.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UYdzDHX3k3C2Eh7B5hRVH
…#1386)

Self-review found `docs/overview.md`'s own "What is not true yet" section
naming the exact defect this diff fixes -- "no file on the release path
mentions triage at all... the recorder was built and the consumer was not"
-- as still current, contradicted in the same commit that added the
consumer (`scripts/triage_trigger.py`) and the file on the release path
that now mentions it (`commands/tick.md`). Reworded to the past tense and
pointed at the fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UYdzDHX3k3C2Eh7B5hRVH
Two findings from the self-review spawns, both fixed in place:

- The scheduler dispatched `oss:triager` on `due` but nothing ever
  advanced `--last-triage` afterward -- the triager itself only writes
  labels and a proposed-cluster report, never the state file. Without
  recording completion, `triage_trigger.compute` would read the same
  stale record on every later `RELEASE: released` handback and
  dispatch a sweep on every release forever, never reaching `not-due`.
  `commands/tick.md` now runs `oss_state.py --triage-recorded` once the
  triager's own final message confirms the sweep ran.
- `scripts/oss_config.py`'s new `TRIGGER_BOOLEAN_KEYS` validation
  branch had no exercising test -- confirmed correct by direct call,
  but a correct-and-untested branch is exactly the gap this repo's own
  guard tests exist to close. Added positive (`true`/`false`/absent)
  and negative (`1`/`"yes"`) cases to `tests/test_oss_config.py`.

Also fixed: `docs/overview.md`'s "What is not true yet" section still
described the exact defect this diff closes ("no file on the release
path mentions triage... the recorder was built and the consumer was
not") as current -- reworded to the past tense and pointed at the fix,
alongside the byte-budget bookkeeping `commands/tick.md`'s further
growth requires.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UYdzDHX3k3C2Eh7B5hRVH
@fdaviddpt
fdaviddpt merged commit b81ef76 into main Sep 9, 2026
10 checks passed
@fdaviddpt
fdaviddpt deleted the fix/1386 branch September 9, 2026 22:29
fdaviddpt added a commit that referenced this pull request Sep 10, 2026
Logged from the merge train on 2026-09-10. PR #1403 read MERGEABLE with no
conflicts throughout, including after #1402 had already landed -- a true
statement about a base that no longer existed. A local git merge conflicted
immediately, in two adjacent budget-table rows where neither side was right
whole.

Cost nothing because the merge was tested locally first; would have cost a
red default branch and a silently reverted re-baseline otherwise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UYdzDHX3k3C2Eh7B5hRVH
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 triage sweep after a release is stated as a rule and implemented by nothing

1 participant