# Data Access Management V1 Execution Order

> Status: execution guide for [#809](https://github.com/pingcap-inc/Helix/issues/809).
> Contract authority: [DATA_ACCESS.md](DATA_ACCESS.md) and [DECISIONS.md](DECISIONS.md#2026-06-09--data-access-management-v1-is-bigquery-dataset-read-access-for-google-groups-through-semantic-helix-bindings).

This document orders the data-access management V1 implementation work. It is
not a competing design authority; it sequences the contract in `DATA_ACCESS.md`.

## Critical Path

```text
M0 design/config
  -> M1 domain + Feishu approval without provider mutation
  -> M2 BigQuery driver + reconciler in observe mode
  -> M3 canary BigQuery mutation
  -> M4 UI/ops surfaces
  -> M5 catalog cutover
```

Hard gates:

- Do not start model/API work until canonical names are fixed.
- Do not start request/binding model work until the Google Group principal
  identity contract is fixed across `email_groups.id`, `google_group_id`,
  primary email snapshots, and aliases.
- Do not grant resource-owner authorization until `data_access_resources` owns
  the projected owner field and owner-mismatch behavior fails closed.
- Do not create Feishu-backed requests until request/resource/group validation
  and dry-run compile exist, and the security-bar rejected cases have tests.
- Do not mutate BigQuery until observe mode proves bounded deltas and BigQuery
  IAM behavior is verified with an exact permission/ACL note.
- Do not finish catalog-missing UI until catalog-missing backend state exists.
- Do not cut over from bootstrap to catalog until catalog ingestion and
  deterministic bootstrap replacement exist.

Gate phrases in this file use the operational definitions in
[`DATA_ACCESS.md`](DATA_ACCESS.md#operational-gate-definitions). In short:

| Gate | Observable proof |
| --- | --- |
| Canonical names fixed | Constants exist and API/schema/compiler tests reject aliases such as `user`, `table`, raw provider roles, `gcp`, and `cmdb_data_access`. |
| Principal identity fixed | Request/binding models use `email_groups.id`; validator tests cover `google_group_id` rename continuity, primary email recompilation, alias collision, duplicate active group ids, deleted-without-bridge revocation, and missing-id ambiguity. |
| Resource owner authority fixed | `data_access_resources.owner_person_id` is the only runtime resource-owner authorization source; tests cover catalog owner changes, unresolved bootstrap owner fallback, exact cutover replacement, and owner-mismatch suspension. |
| Feishu-backed request safe to create | Validation, authorization, dry-run compile, flow config lookup, and security rejected-case tests all run before Feishu submission. |
| No BigQuery mutation in M1 | Request submission, payload build, and callback tests spy on the BigQuery write surface and observe zero calls. |
| Bounded deltas proven | Observe reconcile emits add/remove/unchanged/unmanaged/error counts only for selected compiled effects and dataset locators; no broad dataset scan, project IAM, or `jobUser` call occurs. |
| BigQuery IAM behavior verified | Canary note/test proves exact Google permissions/OAuth scope and dataset IAM get/set behavior without damaging unrelated ACL/IAM entries. |
| Cutover complete | Report has zero unresolved duplicate-locator, owner-mismatch, unresolved-owner/fallback, or invalid rows; bootstrap creation is rejected; unmatched bootstrap rows are non-requestable/suspended; no provider grant is revoked solely because a bootstrap row missed catalog. |

## Merge Waves

### Wave 0: Freeze The Contract

| Mode | Issue | Depends on | Gate |
| --- | --- | --- | --- |
| Serial | [#812 DAX-001](https://github.com/pingcap-inc/Helix/issues/812) | none | Design and ADR are the implementation contract. |

### Wave 1: Canonical Names And Config Inputs

| Mode | Issues | Depends on | Notes |
| --- | --- | --- | --- |
| Serial | [#813 DAX-002](https://github.com/pingcap-inc/Helix/issues/813) | #812 | Constants become the vocabulary used by schema/API work, including principal identity field names. |
| Parallel | [#814 DAX-003](https://github.com/pingcap-inc/Helix/issues/814), [#815 DAX-004](https://github.com/pingcap-inc/Helix/issues/815) | #813 | Feishu flow config and bootstrap input format are independent once names are fixed. |

### Wave 2: Domain Skeleton And Durable Tables

Prefer one schema PR or tightly ordered PRs. Alembic-chain conflicts are not
worth parallel merge pain.

Recommended order:

```text
#816 -> (#817, #818) -> #819 -> #820 -> #821
```

| Mode | Issues | Depends on |
| --- | --- | --- |
| Serial cluster | [#816 DAX-005](https://github.com/pingcap-inc/Helix/issues/816), [#817 DAX-006](https://github.com/pingcap-inc/Helix/issues/817), [#818 DAX-007](https://github.com/pingcap-inc/Helix/issues/818), [#819 DAX-008](https://github.com/pingcap-inc/Helix/issues/819), [#820 DAX-009](https://github.com/pingcap-inc/Helix/issues/820), [#821 DAX-010](https://github.com/pingcap-inc/Helix/issues/821) | #813, #815 |

### Wave 3: Validation, Authorization, And Dry Compile

| Mode | Issues | Depends on | Notes |
| --- | --- | --- | --- |
| Parallel | [#822 DAX-011](https://github.com/pingcap-inc/Helix/issues/822), [#823 DAX-012](https://github.com/pingcap-inc/Helix/issues/823) | #817, #818, #816 | Resource validation and Google Group validation can be built independently, but each must honor the owner/principal authority matrices. |
| Serial | [#824 DAX-013](https://github.com/pingcap-inc/Helix/issues/824) | #823 | Requester authorization depends on group ownership validation. |
| Serial | [#825 DAX-014](https://github.com/pingcap-inc/Helix/issues/825) | #821, #822, #823 | Compiler needs resource/principal/entitlement/effect shapes. |

### Wave 4: Feishu Request Lifecycle

| Mode | Issues | Depends on | Notes |
| --- | --- | --- | --- |
| Serial | [#826 DAX-015](https://github.com/pingcap-inc/Helix/issues/826) | #814, #825 | Feishu payload requires configured flow and dry-run preview. |
| Serial | [#827 DAX-016](https://github.com/pingcap-inc/Helix/issues/827) | #819, #822, #823, #824, #825, #826 | First end-to-end stateful path; still no BigQuery mutation. |
| Parallel | [#828 DAX-017](https://github.com/pingcap-inc/Helix/issues/828) | #820, #821, #824, #825, #827 | Callback approval consumes bindings/effects. |
| Parallel | [#830 DAX-019](https://github.com/pingcap-inc/Helix/issues/830) | #820, #821, #824, #825, #827 | Revoke service owns owner/admin/system revoke intent, including the deleted/invalid Google Group trigger from the email-group read model. |
| Serial | [#829 DAX-018](https://github.com/pingcap-inc/Helix/issues/829) | #828 | Staleness follows callback registration. |
| Serial closeout | [#831 DAX-020](https://github.com/pingcap-inc/Helix/issues/831) | #827, #828, #829, #830 | Complete audit only after all M1 transitions exist. |

M1 gate: Feishu approval can create bindings/effects; no BigQuery driver is
called.

### Wave 5: BigQuery Driver And Observe Reconciler

| Mode | Issues | Depends on | Notes |
| --- | --- | --- | --- |
| Parallel | [#832 DAX-021](https://github.com/pingcap-inc/Helix/issues/832), [#833 DAX-022](https://github.com/pingcap-inc/Helix/issues/833) | #813, #821, #825 | Registration and interface shape can land together or adjacent. |
| Serial | [#834 DAX-023](https://github.com/pingcap-inc/Helix/issues/834) | #832, #833 | IAM read path depends on driver registration and interface. |
| Serial | [#835 DAX-024](https://github.com/pingcap-inc/Helix/issues/835) | #821, #825, #833, #834 | Observe reconciler consumes compiled effects and driver read path. |
| Parallel | [#836 DAX-025](https://github.com/pingcap-inc/Helix/issues/836), [#837 DAX-026](https://github.com/pingcap-inc/Helix/issues/837), [#838 DAX-027](https://github.com/pingcap-inc/Helix/issues/838), [#839 DAX-028](https://github.com/pingcap-inc/Helix/issues/839) | #835 | Locking, run history, unmanaged reporting, and negative IAM assertions follow observe reconcile. |

M2 gate: dry-run reconcile reports bounded deltas as defined above and proves no
project IAM, `bigquery.jobUser`, or broad scan path.

### Wave 6: BigQuery Canary Mutation

Wave 6 must not start until the full M2 gate is satisfied, including #839's
negative assertions that the driver/reconciler path has no project IAM,
`bigquery.jobUser`, or broad scan operation.

| Mode | Issues | Depends on | Notes |
| --- | --- | --- | --- |
| Parallel | [#840 DAX-029](https://github.com/pingcap-inc/Helix/issues/840), [#841 DAX-030](https://github.com/pingcap-inc/Helix/issues/841) | #834, #835 | Permission and ACL/IAM behavior must be verified before mutation code. |
| Parallel | [#842 DAX-031](https://github.com/pingcap-inc/Helix/issues/842), [#843 DAX-032](https://github.com/pingcap-inc/Helix/issues/843) | #835, #839, #840, #841 | Apply/revoke implementation and canary guard can be built together only after the negative no-project-IAM/no-jobUser/no-broad-scan proof lands. |
| Parallel | [#844 DAX-033](https://github.com/pingcap-inc/Helix/issues/844), [#845 DAX-034](https://github.com/pingcap-inc/Helix/issues/845) | #830, #842, #843 | Present and absent effects can be verified separately. |
| Parallel | [#846 DAX-035](https://github.com/pingcap-inc/Helix/issues/846), [#847 DAX-036](https://github.com/pingcap-inc/Helix/issues/847) | #842, #844, #845 | Adoption and failure semantics depend on real apply/revoke mechanics. |
| Serial closeout | [#848 DAX-037](https://github.com/pingcap-inc/Helix/issues/848) | #831, #844, #845, #846, #847 | End-to-end audit comes last. |

M3 gate: canary apply/revoke touches only exact dataset member/role effects:
`group:<email>` + `roles/bigquery.dataViewer` on the allowlisted dataset
locator, with unrelated dataset IAM/ACL entries unchanged.

> **Superseded runtime scope (2026-08-24):** this was the historical M3 canary
> rollout gate. Current reconciler scope reads active/requestable/owner-resolved
> `data_access_resources` directly; `service_account_apply_dataset_allowlist`
> no longer exists. Exact effect/member/role/etag and no-project-IAM/jobUser
> constraints remain unchanged, and absent effects remain executable after a
> resource leaves the requestable catalog.

### Wave 7: Product UI And Operations

UI work can start after M1 APIs exist. Pages that display apply status, retry,
and run history should finish after M2/M3 supplies that state.

| Mode | Issues | Depends on |
| --- | --- | --- |
| Parallel | [#849 DAX-038](https://github.com/pingcap-inc/Helix/issues/849), [#851 DAX-040](https://github.com/pingcap-inc/Helix/issues/851) | #827, #830 |
| Parallel | [#850 DAX-039](https://github.com/pingcap-inc/Helix/issues/850), [#852 DAX-041](https://github.com/pingcap-inc/Helix/issues/852), [#853 DAX-042](https://github.com/pingcap-inc/Helix/issues/853) | #837 |
| Deferred partial | [#854 DAX-043](https://github.com/pingcap-inc/Helix/issues/854) | #830 and #858 |
| Serial closeout | [#855 DAX-044](https://github.com/pingcap-inc/Helix/issues/855) | #849, #850, #851, #852, #853, #854 |

M4 gate: user-facing surfaces are localized and reflect request, binding, effect,
stale, failed, and history states without exposing single-user access or turning
catalog-missing notices into the first writer of backend state.

### Wave 8: Catalog Federation Cutover

This can run in parallel with provider work only if the external catalog contract
is stable. If not, bootstrap remains the V1 requestable source until M3 is safe.
Cutover cleanup is report/apply work, not a change to request/revoke execution:
match bootstrap rows to catalog rows, preserve existing bindings/effects on
matches, suspend unmatched bootstrap rows from new authoring, and require zero
unresolved duplicate-locator, owner-mismatch, unresolved-owner/fallback, or
invalid rows before closing bootstrap creation.

| Mode | Issues | Depends on |
| --- | --- | --- |
| Optional parallel | [#856 DAX-045](https://github.com/pingcap-inc/Helix/issues/856) | #817 and stable external catalog contract |
| Parallel | [#857 DAX-046](https://github.com/pingcap-inc/Helix/issues/857), [#858 DAX-047](https://github.com/pingcap-inc/Helix/issues/858) | #829, #856 |
| Parallel | [#859 DAX-048](https://github.com/pingcap-inc/Helix/issues/859) | #845, #858 |
| Parallel | [#860 DAX-049](https://github.com/pingcap-inc/Helix/issues/860) | #815, #856 |
| Serial closeout | [#861 DAX-050](https://github.com/pingcap-inc/Helix/issues/861) | #856, #857, #858, #859, #860 |

M5 gate: new requestable datasets come only from catalog feed; bootstrap creation
is rejected; the cleanup report has zero unresolved duplicate-locator /
owner-mismatch / unresolved-owner/fallback / invalid rows; unmatched bootstrap
rows are non-requestable or suspended; and no provider grant was revoked solely
because a bootstrap resource failed to match catalog.

## Split Questions To Resolve Before Implementation Waves

1. Should Wave 2 land as one schema PR, or split into separate PRs despite
   Alembic-chain coordination cost?
2. Should UI Wave 7 begin immediately after M1, or wait until M2 run history
   lands so request/detail screens do not churn?
3. Should catalog Wave 8 start as soon as `data_access_resources` exists, or
   wait until the external catalog contract is stable enough to avoid rework?
4. Should #854 split into one issue for group-deleted notices and one issue for
   catalog-missing notices because its current acceptance criteria crosses M4
   and M5?
