Feature 09: rollout integration (docs/features/09) #20

Merged
crueber merged 1 commit from feat/integration into main 2026-09-04 08:18:01 +00:00
Owner

Feature 09: rollout integration (docs/features/09)

Wave E, pure integration — no new product surface. Spec: docs/features/09_rollout.md §§4–7 (now landed with as-built amendments). One behavior fix ships with a spec amendment (see Deviations).

§4 touch-point audit (as-built; amendments in 09 §"As-built amendments")

# Touch point Verdict Evidence
1a checks:write capability AMENDED: not a Principal field, not a config scope — bucket-native wct_ CI token, secret verified handler-side per repo internal/checks/auth.go:11-19, internal/checks/service.go:165-181, internal/checks/model.go:134, Seam 2 hook internal/server/auth.go:39-54, registered cmd/walhub/checks.go:chainChecks
1b require_read↔visibility LANDED as specified gate internal/identity/gate.go:31-49, seam internal/server/server.go:50-51, internal/server/bind_api.go:57-63,99-107, wired cmd/walhub/collab.go:buildCollab + cmd/walhub/serve.go:readGateOf, consulted internal/server/smart.go, internal/server/lfs.go:53, per-package CheckRead seams
1c config token schema AMENDED: StaticToken unchanged (internal/config/config.go:28-34) — CI tokens are bucket objects, not config 09 as-built note; capability is per-repo so a global flag is the wrong shape
2a team:/role: actor sources LANDED as specified internal/policy/expansion.go:9-46, internal/identity/gate.go:94-192, bound cmd/walhub/collab.go:buildCollab (apiEnv.GroupExpander)
2b merge-time require_checks / required-reviews LANDED as specified, evaluated by the merge task internal/policy/effect_protect.go:15,101-118, internal/policy/effect_required_reviews.go, seams internal/pulls/checks.go:18-38, internal/pulls/review.go, call site internal/pulls/merge.go:159
2c push path push-enforceable-only LANDED + fenced ProtectEffect.Evaluate never consults RequireChecks (internal/policy/effect_protect.go:154-157); fence cmd/walhub/push_budget_test.go fails on any collab key touched by a push
3 RouteProvider registration, one block per package + per-user SSE LANDED in composition cmd/walhub/collab.go:chainCollab (identity, issues, pulls, review, checks, releases, social, notify); SSE internal/notify/http.go:tray/stream, internal/notify/collab.go:collabStream, internal/notify/stream.go; assembly extracted from serveHTTP into buildCollab (pure move) so the measured composition IS the shipped one

§5 invariant sweep (09 §"Wave E verification" + EVIDENCE.md E10)

Bucket-only (families enumerated, fence-classified), WAL git-only (no collab import of internal/wal; PR heads via the WAL funnel cmd/walhub/pulls.go:pullsPublisher), push +0 round trips (measured: cold 8 ops, warm 9 ops, 0 collab keys), P3/P4 discipline, no LIST on git hot paths (E4–E6), tasks+SSE (E7/E9; chain observes merge task error then ok).

E2E (internal/e2e/collab_test.go, tiered per docs/go/15_testing.md §5.4)

Token-mode server (alice admin + bob writer), real git (preemptive Bearer header — git never answers a Bearer 401), real webhook sink: org→team→bindings→watch+webhook→issue #1→branch push→PR #2 (fixes #1)→bob APPROVED review→policy (require_checks: [ci/build], blocked merge task error, reported success → combined green)→merge→issue closed→tag+release+latest→alice tray + webhook delivery→fork→cross-fork PR #3 (P2 numbering). Wall 2.2 s. As-built scope pinned in-test: fork manifest-sharing deferred (ForkExecutor nil), so the fork head is seeded by direct push and the chain proves the cross-fork open path.

CI

  • make ci → vet test race cover contract e2e (Makefile); cover auto-discovers all eight collab packages at ≥95%.
  • Woodpecker: no change needed — race/cover/e2e/contract steps already run the new suites by discovery.
  • GH workflow (docker.yml test job): added make cover + make e2e (e2e skips on old git).
  • Tier docs (docs/go/15_testing.md §§1/5.4/7) describe the new suites; sim noted as not-yet-existing (e2e is the proof ci runs until then).

Test results

  • make vet clean; gofmt -l clean (make fmt's empty-list -w quirk is pre-existing).
  • make cover exit 0 — 44 packages, every internal/... ≥95% (pulls 97.8% with the new test).
  • go test -race -short ./... exit 0 (after the race fix below).
  • go test ./internal/e2e/... green 45 s (chain 7 s).
  • make contract green; node --test web/test/unit/*.test.js green (no web changes).

Deviations (each with spec amendment, law 12)

  1. checks:write as-built differs from the 09 plan (Seam 2 shape + handler-side scope, no Principal/config field) — amendment in 09 §4.
  2. taskTable.end data race (pre-existing, Wave C): stamped Finished under the table mutex while polls snapshot under the record mutex — caught once by -race under full-suite load, pinned by TestCoverTaskEndSnapshotRace (fails pre-fix, passes post-fix), lock rule in 03 Decisions. Same-shape latent notes (NOT fixed here): internal/notify returns the live webhook record but discards it at all production sites; internal/wal uses its own broadcast protocol, unexamined.
  3. Fork manifest-sharing deferred (ForkExecutor nil) — recorded in 09 §4 + §7 risk re-check (retention rule stands as contract, nothing shared yet so nothing to mis-collect).
  4. make fmt fails on a clean tree (gofmt -w with empty list) — pre-existing, untouched.

Hygiene

Dirty worktree on feat/issues untouched (status identical before/after; all work in a separate worktree). No merge — review requested.

# Feature 09: rollout integration (docs/features/09) Wave E, pure integration — no new product surface. Spec: `docs/features/09_rollout.md` §§4–7 (now landed with as-built amendments). One behavior fix ships with a spec amendment (see Deviations). ## §4 touch-point audit (as-built; amendments in 09 §"As-built amendments") | # | Touch point | Verdict | Evidence | |---|---|---|---| | 1a | `checks:write` capability | AMENDED: not a `Principal` field, not a config scope — bucket-native `wct_` CI token, secret verified handler-side per repo | `internal/checks/auth.go:11-19`, `internal/checks/service.go:165-181`, `internal/checks/model.go:134`, Seam 2 hook `internal/server/auth.go:39-54`, registered `cmd/walhub/checks.go:chainChecks` | | 1b | `require_read`↔visibility | LANDED as specified | gate `internal/identity/gate.go:31-49`, seam `internal/server/server.go:50-51`, `internal/server/bind_api.go:57-63,99-107`, wired `cmd/walhub/collab.go:buildCollab` + `cmd/walhub/serve.go:readGateOf`, consulted `internal/server/smart.go`, `internal/server/lfs.go:53`, per-package `CheckRead` seams | | 1c | config token schema | AMENDED: `StaticToken` unchanged (`internal/config/config.go:28-34`) — CI tokens are bucket objects, not config | 09 as-built note; capability is per-repo so a global flag is the wrong shape | | 2a | `team:`/`role:` actor sources | LANDED as specified | `internal/policy/expansion.go:9-46`, `internal/identity/gate.go:94-192`, bound `cmd/walhub/collab.go:buildCollab` (`apiEnv.GroupExpander`) | | 2b | merge-time `require_checks` / `required-reviews` | LANDED as specified, evaluated by the merge task | `internal/policy/effect_protect.go:15,101-118`, `internal/policy/effect_required_reviews.go`, seams `internal/pulls/checks.go:18-38`, `internal/pulls/review.go`, call site `internal/pulls/merge.go:159` | | 2c | push path push-enforceable-only | LANDED + fenced | `ProtectEffect.Evaluate` never consults `RequireChecks` (`internal/policy/effect_protect.go:154-157`); fence `cmd/walhub/push_budget_test.go` fails on any collab key touched by a push | | 3 | RouteProvider registration, one block per package + per-user SSE | LANDED in composition | `cmd/walhub/collab.go:chainCollab` (identity, issues, pulls, review, checks, releases, social, notify); SSE `internal/notify/http.go:tray/stream`, `internal/notify/collab.go:collabStream`, `internal/notify/stream.go`; assembly extracted from `serveHTTP` into `buildCollab` (pure move) so the measured composition IS the shipped one | ## §5 invariant sweep (09 §"Wave E verification" + EVIDENCE.md E10) Bucket-only (families enumerated, fence-classified), WAL git-only (no collab import of `internal/wal`; PR heads via the WAL funnel `cmd/walhub/pulls.go:pullsPublisher`), push +0 round trips (measured: cold 8 ops, warm 9 ops, 0 collab keys), P3/P4 discipline, no LIST on git hot paths (E4–E6), tasks+SSE (E7/E9; chain observes merge task `error` then `ok`). ## E2E (`internal/e2e/collab_test.go`, tiered per `docs/go/15_testing.md` §5.4) Token-mode server (alice admin + bob writer), real git (preemptive Bearer header — git never answers a Bearer 401), real webhook sink: org→team→bindings→watch+webhook→issue #1→branch push→PR #2 (`fixes #1`)→bob APPROVED review→policy (`require_checks: [ci/build]`, blocked merge task `error`, reported success → combined green)→merge→issue closed→tag+release+latest→alice tray + webhook delivery→fork→cross-fork PR #3 (P2 numbering). Wall 2.2 s. As-built scope pinned in-test: fork manifest-sharing deferred (`ForkExecutor` nil), so the fork head is seeded by direct push and the chain proves the cross-fork *open* path. ## CI - `make ci` → `vet test race cover contract e2e` (Makefile); `cover` auto-discovers all eight collab packages at ≥95%. - Woodpecker: no change needed — `race`/`cover`/`e2e`/`contract` steps already run the new suites by discovery. - GH workflow (`docker.yml` test job): added `make cover` + `make e2e` (e2e skips on old git). - Tier docs (`docs/go/15_testing.md` §§1/5.4/7) describe the new suites; `sim` noted as not-yet-existing (e2e is the proof `ci` runs until then). ## Test results - `make vet` clean; `gofmt -l` clean (`make fmt`'s empty-list `-w` quirk is pre-existing). - `make cover` exit 0 — 44 packages, every `internal/...` ≥95% (pulls 97.8% with the new test). - `go test -race -short ./...` exit 0 (after the race fix below). - `go test ./internal/e2e/...` green 45 s (chain 7 s). - `make contract` green; `node --test web/test/unit/*.test.js` green (no web changes). ## Deviations (each with spec amendment, law 12) 1. `checks:write` as-built differs from the 09 plan (Seam 2 shape + handler-side scope, no `Principal`/config field) — amendment in 09 §4. 2. `taskTable.end` data race (pre-existing, Wave C): stamped `Finished` under the table mutex while polls snapshot under the record mutex — caught once by `-race` under full-suite load, pinned by `TestCoverTaskEndSnapshotRace` (fails pre-fix, passes post-fix), lock rule in 03 Decisions. Same-shape latent notes (NOT fixed here): `internal/notify` returns the live webhook record but discards it at all production sites; `internal/wal` uses its own broadcast protocol, unexamined. 3. Fork manifest-sharing deferred (`ForkExecutor` nil) — recorded in 09 §4 + §7 risk re-check (retention rule stands as contract, nothing shared yet so nothing to mis-collect). 4. `make fmt` fails on a clean tree (`gofmt -w` with empty list) — pre-existing, untouched. ## Hygiene Dirty worktree on `feat/issues` untouched (status identical before/after; all work in a separate worktree). No merge — review requested.
Wave E (09 sections 4-7, pure integration, no new product surface):
audit the three frozen-code touch points with as-built amendments,
prove section 5 invariants (push fast path +0 bucket round trips,
measured), land the org-to-fork full-chain e2e, extend make ci and the
tier docs, flip 09 to landed with risk re-check.

Includes one behavior fix with spec amendment: taskTable.end stamped
Finished outside the record mutex (data race under -race); pinned by
TestCoverTaskEndSnapshotRace, rule recorded in 03 Decisions.
Sign in to join this conversation.
No description provided.