Commit graph

6 commits

Author SHA1 Message Date
dan-claude-bot
5cd5968cf2 refactor: rescope to versioned installations — the release flow moves out
Maintainer direction: this PR's one goal is the versioned layout, the same
one box#79 built and rig#36 ported — the release flow (tags, release.yml,
prebuilt assets, CHANGELOG) is its own PR later, the shape rig#40 has.

So: release.yml, changelog-section.sh, CHANGELOG.md and the asset-aware
installer channels leave this branch, and in their place cast gets the
family layout for real:

- install.sh lands each build at $DEST/versions/<package.json version>,
  'current' names the default (atomic rename flips), $BINDIR/cast points
  through it. Converging no-op on an installed version (nothing rebuilt),
  CAST_REINSTALL=1 replaces, a new version installs beside and becomes
  default. Pre-versioning flat installs migrate in place, bit for bit.
  CAST_INSTALL_SOURCE=<dir|tarball> installs locally (CI/tests, rig's
  RIG_INSTALL_SOURCE precedent). No flip gate: box refuses under live
  boxes, rig warns on a converged host — cast is an API client, a flip
  strands nothing, 'cast use <old>' is one command away.
- bin/cast grows the layout verbs in bash (they must work when dist/ is
  broken): versions (marks current+running), use (atomic flip, then
  asserts the chain ANSWERS the new version), uninstall (consent gate,
  CURRENT guard, dangling-current guard, ends with the absence assert).
  valid_version/pkg_version are byte-identical copies in both files; a
  test diffs them so the gates cannot drift.
- cast --version stays: package.json is the single source of truth,
  printed with the install root, rig-style.
- ci.yml gains the install job: the real installer, from this checkout,
  layout asserted, converge no-op asserted, uninstall --all asserted
  absent — the box CI precedent.
- Tests drive the REAL install.sh and bin/cast offline (npm shim, local
  source): the layout, the chain answering end to end, no-op/reinstall/
  side-by-side/migration semantics, the hostile-version gates, refs/heads
  download, every uninstall refusal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 21:17:59 +00:00
dan-claude-bot
d992f1833d feat: versioned installs — tagged releases with a prebuilt dist asset (#96)
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>
2026-07-18 20:51:15 +00:00
dan-claude-bot
42d138edbf refactor: the author escalates — drop the agreement-body heuristic
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>
2026-07-18 19:51:55 +00:00
dan-claude-bot
00c748c879 fix: verdict contract, head-bound approvals, serialized reconcile — and a testable state machine
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>
2026-07-18 19:31:04 +00:00
dan-claude-bot
d3065cbc9a docs: reviews end in a verdict — approve or request changes, never a bare comment
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>
2026-07-18 18:50:53 +00:00
dan-claude-bot
884a95f5fa feat: label automation — the state reconciler, path-scoped labeler, and CONTRIBUTING
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>
2026-07-18 18:31:54 +00:00