Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions docs/design-t-wad-slice-7-binary-shim-affected-set-selection-canvas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# T-WAD Slice 7 — Affected-set selection via `BinaryShim` (gate `ci_uses_affected_set_selection`, program row 103)

**Status:** Verification-lane design canvas (implementation **not** in this PR).
**PR:** [gunbc#2760](https://github.com/gunb-ai/gunbc/pull/2760) — **draft by default** for canvas iteration (confirm with `gh pr view 2760 --repo gunb-ai/gunbc --json isDraft`). While **draft**, scheduled auto-reviews / default CI gates are skipped; manual triggers still run. To opt into full GitHub auto-coverage when intentionally desired: `gh pr ready 2760 --repo gunb-ai/gunbc`.
**Authority:** `docs/r3-structure.md` (gate `ci_uses_affected_set_selection`), `docs/r3-program-plan.md` row 103, `docs/r3-t-workflow-as-data-full-r3-close-scope.md` §0–§1, PR #2744 WI / FULL R3-close scope context, `docs/design-ci-workflow-emitter-dispatch.md`, `docs/design-affected-set-lens.md`.
**Parent emitter canvas:** `docs/design-ci-workflow-emitter-dispatch.md` §5.2, §6 (this document **specializes** Slice 7; it does not reopen (c-refined) placement or `WorkflowRuntime` shape).
**Upstream lens:** PR #2713 (affected-set lens substrate; merged per scope docs).
**Blocking emitter:** Slice 5 `BinaryShim` projection arm + runtime — **Substrate Mgr** session **warm-wolf-698** (see §6).

**PM sequencing (warm-wolf-698 lane):** **Slice 4** (`YamlStatic` parity) lands **first**. **Slices 5 and 8** (`BinaryShim` emitter + `ci.yml` hand-authority dissolution per FULL R3-close plan) proceed **in parallel** after Slice 4. Slice 7 **implementation** still **waits on the Slice 5 hand-off** (compiled runner + thin shim + projection hook); Slice 8 can advance repository hygiene in parallel but does **not** replace binary-side affected-set consumption.

---

## §1. How `BinaryShim` consumes affected-set output (PR #2713)

### §1.1 Data flow (conceptual)

1. **Invocation boundary:** `project_github_actions(ci_workflow_dag, BinaryShim)` yields a provider-faithful `Workflow` plus emitted **thin shim** YAML and a **compiled CI runner binary** (exact artifact layout is Slice 5; see §6).
2. **At PR time** the runner binary obtains `Dag_before` (merge-base / base ref compilation) and `Dag_after` (PR head compilation), using the same compiler revision and feature flags as the repository policy encodes in `CIWorkflowDag` / pinned toolchain facts.
3. The binary **does not** re-derive affectedness from path globs. It calls the **affected-set lens pipeline** landed with PR #2713: per-dimension `affected_set(Dag_before, Dag_after, dim)` then aggregates per `docs/design-affected-set-lens.md` §2.
4. **Interchange shape (structural):** consumption is **typed lens output**, not stderr text or ad-hoc file lists:
- Aggregate: `Set<NodeRef>` (or the NodeRef-keyed record carrying dimension + provenance if the landed lens emits the richer form — the consumer must treat the **authoritative serialized form** from PR #2713 as the single source of truth).
- Per-dimension: proof receipts for exclusions / unknown deltas as required by `docs/design-affected-set-lens.md` §2 fail-closed discipline (“without that receipt, the consumer falls back to the default-include branch”).
5. The binary **maps** `NodeRef` sets to **executable CI actions** (cargo filters, job labels, gate commands) using metadata anchored in `CIWorkflowDag`, `TestClaim` declarations, and gate records — not by parsing GitHub `paths`/`paths-ignore` in YAML.

### §1.2 PM-assumed BinaryShim hand-off (implementation waits on Slice 5)

Until warm-wolf-698 lands Slice 5, the following is **assumed interface / dependency text** (not executable in this canvas PR):

- **Projection:** `project_github_actions(ci_workflow_dag, BinaryShim)` emits a provider `Workflow` value that **points at** a **compiled CI binary entry-point** plus **thin shim YAML** (bootstrap only; policy in binary — matches `docs/design-ci-workflow-emitter-dispatch.md` §5.2).
- **PR-time runner (PM end-state vision):** the binary reads the **git diff / event**, materializes `Dag_before` / `Dag_after`, runs the PR #2713 affected-set lens, then **decides which checks run**. That decision may ultimately be realized as a **GitHub dynamic `strategy.matrix`**, fan-out jobs, and/or other orchestration — but that is **not** required on **day one** (see §2 **staged vs end-state**).
- **Caveat:** no gunbc code in **this** document PR implements that path; integration PRs queue **behind** Slice 5.

### §1.3 Interface stub (Slice 5–owned details)

The **exact** Rust (or generated) API between “emitted binary `main`” and “lens evaluation” is owned by the Slice 5 implementation PR. This canvas still requires:

- A **stable, versioned** internal contract: “runner requests affected-set bundle for `(before_ref, after_ref)`; receives `Ok(structured_receipt)` or `Err/Unknown`.”
- **No** dependency on YAML `github` context expressions for that bundle.

Verification Mgr may request a written **Slice 5 public surface** from warm-wolf-698 / PM before first integration PR; until then, §1.2 is the assumed hand-off.

---

## §2. Matrix-spec emission vs job-skip emission — tradeoff and **chosen initial shape**

| Surface | Pros | Cons |
|--------|------|------|
| **Dynamic matrix JSON** (`strategy.matrix` from a prior step output) | Native GitHub fan-out, visible per-matrix-cell checks | GH matrix limits, expression fragility, harder local reproduction |
| **In-runner selection** (single or few jobs; **selected test list** / **selected job list** executed inside the runner) | Matches `design-ci-workflow-emitter-dispatch.md` §6.1 preference; simpler shim; easier fail-closed logging | Less visible fan-out in Actions UI |

**Staged vs end-state (resolves §1.2 wording):**

| Phase | What “dynamic dispatch” means | GitHub `strategy.matrix` emission |
|-------|--------------------------------|-----------------------------------|
| **Staged first landing** (Slice 7 gate / minimal integration) | Binary computes affected-set and runs the **selected** tests/gates **inside the runner process** (fixed small YAML job graph; filters passed to `cargo test` / scripts). | **Not required.** No obligation to emit dynamic matrix JSON for row **103** to read as satisfied — selection is **in-runner**. |
| **End-state** (PM / emitter-canvas vision) | Same lens inputs; runner may **fan out** work for wall-clock or UI visibility. | **Optional escalation** when native GH parallelism is worth the complexity (`design-ci-workflow-emitter-dispatch.md` §6.1). |

**Chosen initial shape (Slice 7 first landing):** **In-runner selection** as primary:

- The thin YAML runs a **small fixed job graph** (bootstrap checkout, toolchain, invoke `gunbc-ci` or emitted binary name TBD by Slice 5).
- The binary computes affected-set, derives **selected test names / gate commands / internal phase list**, and invokes `cargo test`, scripts, or dag-run equivalents **directly** with those filters.
- **Dynamic matrix JSON** remains a **documented escalation path** when a workflow **requires** GitHub-native parallelism (e.g., shard budget > single runner) — not required for gate `ci_uses_affected_set_selection` to pass initially.

**Rationale:** Aligns with emitter canvas §6.1 (“first implementation should prefer selected test/job lists”); minimizes duplicate policy in YAML; keeps fail-closed selection in one typed process.

---

## §3. Fail-closed behavior (unavailable / diagnostic-bearing / unknown)

When **any** of the following holds, the runner MUST select the **conservative superset** (run all checks that are “required” for the workflow policy — identical to pre-narrowing behavior, modulo unrelated skips such as draft PR workflow policy if retained at YAML level per repository policy):

| Condition | Action |
|-----------|--------|
| `Dag_before` or `Dag_after` cannot be built (missing refs, compile error, toolchain mismatch) | Superset + **actionable diagnostic** (non-zero exit if the failure is infra; otherwise treat as unknown-affectedness per policy table below) |
| Lens returns **unknown** dimension delta for any consumer edge needed for a narrowing decision | Superset for that dimension’s consumers |
| Missing **per-dimension proof receipt** for an exclusion (`design-affected-set-lens.md` §2) | Treat as **not proven narrow** → include consumer |
| Lens output carries **diagnostic-bearing** incomplete state (partial evaluation, staged substrate gap) | Superset until diagnostics cleared at source |
| `TestClaim` / job metadata does not declare asserted dimensions (§4) | That test participates in **superset** path (cannot prove skip-safe) |
| Workflow node referenced by YAML job is **unmapped** to `CIWorkflowDag` / test metadata | Superset for that job’s obligations |

**No silent skips:** skipping is only allowed when the chain `(proven receipt ∪ structural metadata)` proves the check is not in the aggregate affected set for its asserted dimensions.

**Observability:** emit `::notice::` / structured log lines summarizing: mode (`superset` vs `narrow`), reason code, counts of nodes in affected set, and which dimensions forced superset.

---

## §4. Deriving selected checks — union over dimensions + `TestClaim` / job metadata

Per `docs/design-affected-set-lens.md` §2 and §5:

1. **Compute** `A = ⋃_dim affected_set(Dag_before, Dag_after, dim)` (aggregate affected nodes).
2. **Baseline obligation set** `B`: all tests / gates / jobs the workflow treats as **merge-blocking** when no narrowing is active (from `CIWorkflowDag` + gunbc `ci.dag` gate intent, projected through the same source as Slice 4–5).
3. For each **TestClaim** or test-shaped obligation `t ∈ B`:
- Read **asserted dimensions** `D(t)` from substrate metadata (must be modeled; absent `D(t)` → §3 superset for `t`).
- Let `Δ(t)` be the set of dimensions with **non-empty proven delta** OR **unknown delta** on any node referenced by `t`’s structural closure (per lens rules).
- **Select** `t` if `D(t) ∩ Δ(t) ≠ ∅` OR `t` references a node in `A` (aggregate) — implementation must use one **coherent** rule consistent with lens math; default rule: **if any asserted dimension may have changed** (`unknown` counts as may-have-changed), run `t`.
4. **Union semantics:** dimension-aware selection is **not** value-equivalence-only; cost / complexity / effect / refinement assertions keep tests live when those dimensions move (`design-affected-set-lens.md` §4.1, §5).
5. **Non-test jobs** (fmt, bootstrap verify, shell policy): map job steps to **NodeRef** / gate anchors in `CIWorkflowDag`. If mapping is absent, job stays in **superset**.

---

## §5. Invariant: YAML path-regex shortcuts — removed or **non-authoritative**

**Gate text (`docs/r3-structure.md`):** Layer 2 path-regex `if:` gates are removed from workflow files once `ci_uses_affected_set_selection` is claimed.

**Canvas rule (two-tier):**

1. **Selection authority:** After Slice 7 lands, **no** workflow `if:` condition may **encode** “run vs skip heavy compute” based on `git diff` path patterns, `paths-filter` equivalents, or hand-maintained regex allowlists. That logic lives **only** in the `BinaryShim` runner using PR #2713 output (+ §3 fail-closed).
2. **Non-selection `if:`:** Workflow conditions that are **orthogonal** to affected-set (e.g., `github.event.pull_request.draft != true`, concurrency keys) may remain in YAML as **platform/event** mechanics, not as a second selection engine. They must be **non-authoritative** for “which tests/gates prove merge safety.”
3. **Dissolution evidence:** Removal of path-regex selection is **proven** by (a) grep/ratchet `workflow_no_path_regex_policy` from `design-ci-workflow-emitter-dispatch.md` §11 and (b) code review showing selection callsite is binary-only.

---

## §6. Explicit dependency on warm-wolf-698 — Slice 5 `BinaryShim` emitter

Slice 7 **does not** implement:

- `emit_binary_shim` / `project_github_actions(..., BinaryShim)` body,
- Rust crate layout for the CI binary,
- Shim YAML bytes beyond policy constraints in `design-ci-workflow-emitter-dispatch.md` §5.2.

**Hard dependency:** warm-wolf-698 Slice 5 delivers:

- Emitted **thin** `.github/workflows/ci.yml` shim and **compiled runner** artifact path(s),
- A **hook point** (library or CLI) where Slice 7 wires lens evaluation and selection,
- **Local reproduction** command documented in-repo.

Until that lands, Slice 7 integration PRs are **blocked** except for documentation and offline fixtures.

---

## §7. Implementation dispatch checklist (post–Slice 5)

Use this as the ordered work queue for the **implementation** PR(s) after warm-wolf-698 Slice 5 merges:

1. [ ] **Pin** `Dag_before` / `Dag_after` acquisition to the same compiler + std revision policy as CI (no drift between lens and compile).
2. [ ] **Wire** PR #2713 affected-set evaluation into the Slice 5 runner entrypoint; serialize receipts for debugging.
3. [ ] **Implement** §4 mapping from `Set<NodeRef>` + metadata → `cargo test` filters / script invocations / dag-run list.
4. [ ] **Implement** §3 superset fallback table + structured reason codes + logs.
5. [ ] **Implement** §2 initial shape (in-runner selection); document matrix escalation if sharding is needed later.
6. [ ] **Delete** Layer 1/Layer 2 path-regex selection jobs and `needs:` edges that exist only for those bridges; rewire `needs:` / branch-protection assumptions (including skipped-parent semantics if any).
7. [ ] **Add** ratchet tests: `workflow_no_path_regex_policy`, `workflow_affected_set_fail_closed` (per emitter canvas §11).
8. [ ] **Add** fixture-driven tests from `design-ci-workflow-emitter-dispatch.md` §7 table (draft PR, docs-only, unknown receipt, workflow edit → superset).
9. [ ] **Prove** gate `ci_uses_affected_set_selection`: BinaryShim path consumes PR #2713 output; YAML contains no authoritative path-regex selection.
10. [ ] **Coordinate** Substrate Mgr for any `CIWorkflowDag` metadata gaps discovered when mapping jobs → nodes.

---

## §8. Review / test plan (canvas PR — no runtime code here)

**Reviewers should verify:**

- All seven parent bullets (§1–§7 here) are addressed in prose with **no contradiction** to `design-ci-workflow-emitter-dispatch.md` or `design-affected-set-lens.md`.
- Fail-closed and “no silent skips” match `design-affected-set-lens.md` §2.
- Initial shape choice (§2) is explicit and justified.
- Path-regex invariant (§5) distinguishes selection vs orthogonal `if:`.
- **§9** states a concrete future test matrix aligned with `feedback_lenses_not_passes` + `TESTING.md` without pretending runtime code exists in this PR.

**After implementation (future PR test obligations — not this canvas):**

- Unit tests for §3 reason codes (mock lens returning unknown / missing receipt).
- Integration test: small repo fixture where only one dimension changes and only the expected `cargo test` filter runs.
- Ratchet: no `paths:`/`paths-ignore:`-style selection in `.github/workflows/*.yml` (exact pattern set to be defined in implementation PR).
- Shapes in **§9** land as real `#[test]` / `TestClaim` rows when Slice 5 runtime exists — this PR only records the harness contract.

---

## §9. Future verification test harness (skeleton — no runtime in this PR)

PM explicitly allows **pre-authoring** the **verification test shape** here: the relationship **affected-set ↔ workflow-dispatch** must become a **structurally testable behavioral claim**, not a hand-waved “CI went green.”

### §9.1 Discipline anchors

- **`feedback_lenses_not_passes`** (`CODING.md`, design-index cites): selection logic reads **lens physics** (structured receipts over `Dag_before` × `Dag_after` × dimensions) plus **declared metadata** (`CIWorkflowDag`, `TestClaim`, gate records). It does **not** re-derive merge safety from YAML `if:` text, log scraping, or path heuristics parallel to the lens.
- **`TESTING.md`**: tests are **hermetic** (each declares its own inputs), **behavior-driven** (name the interface + one behavior), **unit-first**; reserve full `compile_to_dag` / full-pipeline tests for cases where the pipeline itself is the subject.

### §9.2 Recommended harness layers

| Layer | Interface under test | Example behavior (one claim per test) |
|-------|------------------------|----------------------------------------|
| **Unit — dispatch planner** | Pure `plan_dispatch(receipt, obligation_metadata) -> RunPlan` (name TBD at implementation) | Given a **synthetic** receipt with `Unknown` on a narrowing edge, output equals full baseline obligation set `B` (§4 superset). |
| **Unit — receipt gaps** | Same planner | Missing per-dimension exclusion proof → obligation stays **selected** (fail-closed). |
| **Unit — narrow path** | Same planner | Receipt proves only subgraph `S` changed; metadata maps obligations outside proven non-affected closure → those obligations **absent** from `RunPlan`. |
| **Integration — lens + planner (optional, cost-gated)** | Real affected-set lens on **tiny** paired `.dag` fixtures | Expected `NodeRef` set matches golden; planner output matches expected run list. |
| **Ratchet / static** | Emitted workflow artifact | No authoritative path-regex selection patterns (denylist spelled in implementation PR; aligns with `workflow_no_path_regex_policy` in `design-ci-workflow-emitter-dispatch.md` §11). |

**Fixture discipline:** inject **mock structured receipts** or minimal compiled DAG pairs **inside** each test; no shared mutable fixtures across tests (`TESTING.md` §Hermetic).

### §9.3 What this PR does *not* ship

No Rust modules, no `gunbc-ci` binary, no GitHub workflow edits — only the contract above so the follow-on implementation PR has an explicit test matrix.

---

**End of canvas.**
Loading