Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e6b58c3
WIP: Compiler Spine Manager — authority over T-8 resolve, T-9 infer/g…
briansrls May 30, 2026
32bab80
docs(planning): add tree spot-check appendix to rung 3-4 runner inter…
briansrls May 30, 2026
38dd6b4
docs(planning): merge Runtime amendments A1-A4 and §4.4 rung-split
briansrls May 30, 2026
397bf56
docs(planning): record W2 default A4 attachment on Verdict.Fail
briansrls May 30, 2026
163b603
Merge origin/main (PR #3938 merged)
briansrls May 30, 2026
b33ede5
docs(planning): refresh §7 sign-off after A4 W2-kickoff agreement
briansrls May 30, 2026
852599c
WIP: Compiler Spine Manager — authority over T-8 resolve, T-9 infer/g…
briansrls May 30, 2026
1c4b372
docs(planning): address api-review BLOCKING on HostExit + falsification
briansrls May 30, 2026
804fab1
Merge remote-tracking branch 'origin/main' into session/smart-stag-871
briansrls May 30, 2026
66f2651
WIP: Compiler Spine Manager — authority over T-8 resolve, T-9 infer/g…
briansrls May 30, 2026
b6b0159
WIP: Compiler Spine Manager — authority over T-8 resolve, T-9 infer/g…
briansrls May 30, 2026
1602b22
docs(planning): align shadow §5 Skip with valid cache_digest gate
briansrls May 30, 2026
341f5a9
docs(planning): fix Verdict generic mismatch in rung34 runner spec
briansrls May 30, 2026
d977f43
docs(planning): A4 — concrete Verdict<RuntimeValue> Fail, no unbound S
briansrls May 30, 2026
018781e
docs(planning): CiStepSelection.cache_digest as Outcome<ContentHash>
briansrls May 30, 2026
5728c2f
docs(planning): CiStepSelection.cache_digest as Outcome<ContentHash>
briansrls May 30, 2026
86783b0
Merge remote-tracking branch 'origin/main' into session/smart-stag-871
briansrls May 30, 2026
6e307aa
Merge remote-tracking branch 'origin/main' into session/smart-stag-871
briansrls May 30, 2026
a7e26a7
WIP: Compiler Spine Manager — authority over T-8 resolve, T-9 infer/g…
briansrls May 30, 2026
951577c
docs(planning): align v4-ci-overhaul §5 cache_digest with Outcome
briansrls May 30, 2026
598b96d
docs(planning): add gate rows to CI shadow registry
briansrls May 30, 2026
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
192 changes: 192 additions & 0 deletions docs/planning/compiler-spine-ci-selection-receipt-shadow-2026-05-30.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Compiler Spine — CiSelectionReceipt shadow mode (Phase 2.0)

**Authority:** PR [#3938](https://github.com/gunb-ai/gunbc/pull/3938) §11.4 dispatch step 2; PR [#3959](https://github.com/gunb-ai/gunbc/pull/3959) §5 (`CiSelectionReceipt`); Modeling DFS Step 1 approval (`proud-pike-680`, 2026-05-30).
**Owner:** Compiler Spine (`smart-stag-871`).
**Consumers:** operator review, Close/Receipt (structural gate), Phase 2.5 activation (gate workflow on `selected`).
**Out of scope:** `CiUpsertStep<T>` implementation (blocked on Phase 1.4 `Upsert<T>` substrate); `CiRunPolicy` / `dependency_set` naming; active step skipping in GitHub Actions (shadow only).

---

## 1. Purpose

Produce a **per-PR `CiSelectionReceipt`** that records, for every modeled CI step, a **justified** `Run | Skip | CarvedOut` decision from structural facts (`ChangeSet`, `AffectedSet`, carve-out list). In **shadow mode** the existing workflow **still executes all steps**; the receipt is emitted first (or alongside) for human and manager review. Transition to **active** minimal CI (Phase 2.5 per `docs/planning/v4-ci-overhaul-2026-05-30.md`) gates on **`receipt.selected` = every `CiStepSelection` whose `decision` is `Run | CarvedOut`** (executable projection). `skipped` holds `Skip` only; `carved_out` is the carveout-config snapshot (`List<CiCarveout>`), not a substitute for the runnable set.

**Structural success criterion** (operator 2026-05-30): correct iff every step's `decision` is justified by (a) non-empty `inputs ∩ affected_set` evidence (`Run`), (b) empty intersection plus `cache_digest` projection `Accepted { value: … }` (`Skip`), or (c) explicit `ci_always_run_carveouts` match (`CarvedOut`). Wall-clock reduction is downstream, not the gate.

---

## 2. Approved substrate (Modeling DFS — do not re-litigate)

| Symbol | Shape | Notes |
| ------ | ----- | ----- |
| `CiUpsertStep<T>` | `Upsert<T> { inputs, verify, create, resolve }` | **Not implemented** until Phase 1.4 lands usable `Upsert<T>` |
| `UpsertInputRef` | `FileSet \| SubstrateNodeSet \| LensOutputRef \| TestClaimRef \| UpstreamUpsert` | **No `Always` variant** |
| `ci_always_run_carveouts` | `List<CiCarveout>` | `reason_code`, `reason_detail`, `dissolution_target` |
| `CiSelectionReceipt` | See §3 | First observable artifact |
| Forbidden | `List<Symbol>` inputs; `CiRunPolicy`/`CiRunMode`; `DependencySource`/`dependency_set` | P1 closed-system / P2 single-authority |

---

## 3. Receipt carriers (canonical)

Aligned with `docs/planning/v4-ci-overhaul-2026-05-30.md` §5 (**this PR amends** parent `CiStepSelection.cache_digest` to `Outcome<ContentHash>` — single authority, P3 fail-closed). `Outcome<ContentHash>` is `std.diagnostic.Outcome` (Practice 10 — no parallel projection carrier).

```dag
type CiSelectionReceipt {
pr: ChangeSet
affected: AffectedSet
selected: List<CiStepSelection>
skipped: List<CiStepSelection>
carved_out: List<CiCarveout> // snapshot of matching carveouts for this receipt
}

type CiStepSelection {
step_id: CiStepId
inputs_consulted: List<UpsertInputRef> // shadow: bridge rows until CiUpsertStep lands
affected_intersection: List<AffectedNode>
decision: SelectionDecision
cache_digest: Outcome<ContentHash> // Accepted = projected hash; Rejected = diagnostics — no sentinel (P3)
reason: Symbol
}

type SelectionDecision
= Run
| Skip
| CarvedOut { carveout_reason: Symbol }
```

`CiStepId` — typed step identity (Phase 2.0: alias `Symbol` job/gate id from `ci_pipeline` with explicit registry; Phase 1.5: dedicated carrier when `CiUpsertStep` rows land).

---

## 4. Shadow mode semantics

| Mode | Workflow behavior | Receipt |
| ---- | ----------------- | ------- |
| **Shadow** (this deliverable) | GitHub Actions / hand `ci.yml` runs **unchanged** (all jobs that today's `if:` allows) | Compute + emit `CiSelectionReceipt` (structured log / artifact) |
| **Active** (Phase 2.5) | Gate each step on `decision == Run ∨ CarvedOut` | Same receipt; workflow trusts it |

**Fail-closed on receipt construction:** if `ChangeSet` read is `Rejected` or `AffectedSet` is fail-closed, every step is `Run` in the receipt **and** `reason` documents superset selection (mirrors `ci_component_affected_fail_closed` today). Receipt must not claim `Skip` unless intersection is empty **and** the projected `cache_digest` is valid (§5); invalid projection → `Run` with `cache_digest_projection_fail_closed`.

**Cache projection carrier (Phase 2.1).** `project_cache_digest(row, pipeline) -> Outcome<ContentHash>` projects the **complete step subgraph** (interim: `ci_job_cache_digest` / `ci_gate_cache_digest` on the modeled `CiJob`/`CiGate` projection node — B1 / T-21 discipline, not inputs-only). `CiStepSelection.cache_digest` stores that **full `Outcome`** — never a fabricated hash when projection fails. **Valid for `Skip`** iff `cache_digest` is `Accepted { value: digest }`; `Rejected { diagnostics }` forces `Run` (fail-closed). Close/Receipt consumers must not treat `Rejected` as a hash-shaped fact.

---

## 5. Selection algorithm (spine contract)

Aligned with §1 and `docs/planning/v4-ci-overhaul-2026-05-30.md` §5 structural gate — `Skip` only when intersection is empty **and** cache projection is valid; otherwise fail-closed to `Run`.

```text
ci_selection_receipt_shadow(
pr: ChangeSet,
pipeline: CiPipeline,
carveouts: List<CiCarveout>,
registry: List<CiShadowStepRow>,
) -> CiSelectionReceipt

affected := affected_set_from_diff(pr) // v4.lens.affected_set authority
if change_set_fail_closed(pr) || affected_set_fail_closed(affected):
return superset_run_receipt(pr, affected, registry, reason: receipt_inputs_fail_closed)

for each row in registry:
projected := project_cache_digest(row, pipeline) // Outcome<ContentHash>

if row.step_id ∈ carveouts.step_ids:
decision := CarvedOut { carveout_reason := matching.reason_code }
reason := carveout_matched
else if intersect(row.inputs, affected) ≠ ∅:
decision := Run
reason := affected_intersection_nonempty
else match projected {
Accepted { value: digest, diagnostics: _ } =>
decision := Skip
reason := affected_intersection_empty_cache_valid
Rejected { diagnostics: _ } =>
decision := Run // P3 fail-closed — cannot skip safely
reason := cache_digest_projection_fail_closed
}

append CiStepSelection { ..., decision, cache_digest := projected, reason }
partition per decision:
Run | CarvedOut → selected // Phase 2.5 runnable set (workflow executes these)
Skip → skipped
(carved_out on receipt = snapshot of matching ci_always_run_carveouts rows, not step partition)
```

**Registry** (`CiShadowStepRow`): spine-owned bridge table mapping **every** `ci_pipeline` job **and** gate id to provisional `List<UpsertInputRef>` until Phase 1.5. The selection loop (`:91-113`) is the **only** surface that emits `CiStepSelection`; carveout config (`ci_always_run_carveouts`) only overrides `decision` for rows already in the registry — it does not add steps by itself. Rows are **data**, reviewable, versioned with `ci.dag`.

---

## 6. Phase 2.0 shadow registry (interim bridge)

Maps current `ci_pipeline` jobs and gates (`src/v4/workflow/ci.dag`) to **typed** `FileSet` / `UpstreamUpsert` selectors (not bare path strings). Component flags (`CiComponentAffected`) remain a **coarse GitHub `if:` bridge** only — not authoritative for per-step receipt; receipt uses node-level `AffectedSet` from T-21 lens.

| `CiStepId` (job or gate id) | Provisional `inputs_consulted` | Shadow notes |
| --------------------------- | ------------------------------ | ------------ |
| `v2_compile_src_v4` | `FileSet { src/v2/**, Cargo.toml, Cargo.lock }` + `SubstrateNodeSet` over v4 compiler bootstrap nodes | Carveout via §7 → `CarvedOut` in `selected` |
| `structural_v2_compile` | `UpstreamUpsert { step_id: v2_compile_src_v4 }` | Gate on v2 compile job |
| `lens_ci_registry_execution` | `FileSet { src/v4/**, dsl/std/** }` + `LensOutputRef` for declared lenses | |
| `lens_ci_registry_signal` | `UpstreamUpsert { step_id: lens_ci_registry_execution }` | Interim `Always` → `CarvedOut` via §7 |
| `m1_rust_emit_probe_execution` | `FileSet { src/v4/**, scripts/v4-m1*, Cargo.* }` | |
| `m1_rust_emit_probe_signal` | `UpstreamUpsert { step_id: m1_rust_emit_probe_execution }` | Interim `Always` → `CarvedOut` via §7 |
| `testclaim_corpus_eval_execution` | `TestClaimRef` roster + `SubstrateNodeSet` rerun frontier from `ci_select_from_affected_set` | Selection fn must stay `ci_select_from_affected_set` |
| `testclaim_corpus_eval_signal` | `UpstreamUpsert { step_id: testclaim_corpus_eval_execution }` | Gate on corpus eval job |

Interim `CiGateRunPolicy::Always` is **not** a receipt policy enum: registry rows for those gates exist above; when listed in `ci_always_run_carveouts` (§7) the algorithm sets `decision := CarvedOut` and the row lands in `selected` (Phase 2.5 runnable projection).

---

## 7. Initial `ci_always_run_carveouts` (shadow seed)

Honest seed list for operator review (expand only with `reason_code` + `reason_detail` + `dissolution_target`):

```dag
// data ci_always_run_carveouts — lands in ci.dag with receipt types (Phase 2.1)
{ step_id: v2_compile_src_v4,
reason_code: v2_substrate_circular_dep,
reason_detail: "v2 compile gate; affected-set lens imports v2 substrate — circular dependency unmodeled",
dissolution_target: ModelMissingSubstrate { what: v2_substrate_dependency_modeled_in_affected_set }
}
{ step_id: lens_ci_registry_signal,
reason_code: integrity_always_run_interim,
reason_detail: "interim Always gate policy until Phase 2.5 activates receipt-driven gating",
dissolution_target: ModelMissingSubstrate { what: ci_gate_run_policy_dissolved_to_receipt }
}
{ step_id: m1_rust_emit_probe_signal,
reason_code: integrity_always_run_interim,
reason_detail: "interim Always gate policy; non-blocking probe",
dissolution_target: ModelMissingSubstrate { what: ci_gate_run_policy_dissolved_to_receipt }
}
```

---

## 8. Implementation phases (spine-owned)

| Phase | Deliverable | Blocked on |
| ----- | ----------- | ---------- |
| **2.0** (this doc) | Contract + registry + carveout seed | Modeling DFS Step 1 ✓ |
| **2.1** | `CiSelectionReceipt` types + `ci_selection_receipt_shadow` in `ci.dag`; unit claims on fixture `ChangeSet` | — |
| **2.2** | Host transport: CI job writes receipt JSON (shadow artifact) before existing steps | 2.1 |
| **2.5** | Active gating on `selected` | Operator sign-off on shadow stability |

**Worker brief guard:** no worker may add `CiUpsertStep` bodies or `dependency_set` fields; shadow work consumes this doc + `v4-ci-overhaul-2026-05-30.md` only.

---

## 9. Cross-lane

| Lane | Action |
| ---- | ------ |
| Modeling DFS | Phase 1.4 `Upsert<T>` substrate; then Phase 1.5 worksheet |
| Close/Receipt | Consume receipt for structural close predicates (not wall-clock) |
| Self-host/Release | Phase 2 YAML emit continues; shadow receipt does not change `ci.yml` yet |

---

## 10. Related artifacts

- `docs/planning/v4-ci-overhaul-2026-05-30.md` — operator-ratified target + sequencing
- `src/v4/workflow/ci.dag` — `ci_pipeline`, `ci_select_from_affected_set`, component affected bridge
- `src/v4/lens/affected_set.dag` — `AffectedSet` authority
- `src/v4/std/change.dag` — `ChangeSet`, `AffectedNode`
Loading
Loading