cast gets the family's release flow (box#83's shape), plus the piece
unique to cast: because cast compiles, the source tarball is not the
package — so release.yml builds ONCE in CI and attaches cast-X.Y.Z.tgz,
and the installer's default channel extracts that asset instead of
running npm ci + tsc on the operator's machine.
- cast --version: package.json is the single source of truth (no VERSION
file); prints the install root too, rig-style.
- CHANGELOG.md with Unreleased; release notes are the curated section
(scripts/changelog-section.sh), never the auto-generated PR list.
- release.yml on a bare X.Y.Z tag: assert tag == package.json version,
check + build + test, prune, tar the runnable tree, gh release create.
- install.sh channels: unset → latest release asset (resolved via the
releases/latest redirect — no API, no token); CAST_REF=X.Y.Z → that
tag's asset; CAST_REF=<branch> → build-from-source, the old path.
- Tests drive the REAL install.sh offline via curl/npm PATH shims (all
three channels, plus the broken-asset and no-release refusals), and
the real changelog-section.sh against fixture changelogs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Maintainer direction: body-parsing agreement was a guess, and the machine
must not guess. COMMENTED is now unconditionally a non-verdict; the judgment
that a comment-only reviewer's round passed belongs to the PR AUTHOR, who
escalates by requesting the human's review — an explicit request is a fact,
and it is the machine's top-precedence input. Auto-request survives only for
the no-judgment case: three formal head-current approvals. CONTRIBUTING and
LABELS.md state the handoff; fixtures updated (14 transitions, including
author-escalation and the three-formal-approvals path).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-1 blockers, all three reviewers concurring:
- COMMENTED agreement now counts: agreement_signal recognizes the live bots'
durable markers (Verdict: Approve / I agree with everything / leading ✅) —
the gate to needs-human can actually close. Formal verdicts remain the
contract (CONTRIBUTING), this is the documented transitional workaround.
- Every counting verdict is bound to the head SHA; a stale approval parks the
PR in addressing (agent owes re-request) instead of promoting unreviewed
code. CHANGES_REQUESTED blocks at any head, per GitHub's own semantic.
- reconcile serializes under ONE job-level concurrency group; scope stays
per-PR. No more cron-vs-event race on the request-the-human-once guard.
- Sweep resilience: per-PR subshell (one failure logs and continues), label
edits warn instead of wedging; the self-heal claim now matches reality
(dispatch-only bootstrap).
- The state machine is extracted pure (globals in, state out) and sourceable:
test/labels-reconcile.sh proves 14 fixture transitions — comment-only
agreement, stale approval, comment-without-verdict, human precedence and
human-block — wired into CI.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Maintainer rule: a comment-only review is a non-verdict the state machine
(and the board) cannot read. Verdict carries blockingness only; nits ride an
approval, blockers — including verdict-gating questions — are request-changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The machinery LABELS.md promised. labels.yml runs the reconciler on a
15-minute cron plus PR events (pull_request_target — every PR here is from a
fork, where pull_request gets a read-only token; no PR code is ever checked
out). The script derives each open PR's state:* from GitHub's own facts and
converges labels statelessly; stale is judged from real activity (commits,
comments, reviews), never label churn, so the sweep cannot un-stale its own
mark. actions/labeler applies scope:* from changed paths. CONTRIBUTING.md is
the guideline: the PR loop, and who sets which labels. Rehearsed with
DRY_RUN=1 against the live repo; shellcheck-clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>