|
| 1 | +# Stateful Data + UI PR Checklist |
| 2 | + |
| 3 | +Use this checklist for any PR that changes stateful data flow across layers. |
| 4 | + |
| 5 | +## Operating rule |
| 6 | + |
| 7 | +> **Any PR that adds or changes stateful data flow across layers must ship with explicit invariants, degraded-mode behavior, and interaction tests before opening.** |
| 8 | +
|
| 9 | +If the change touches any combination of: |
| 10 | + |
| 11 | +- Envio schema/entities |
| 12 | +- event handlers / entity writers |
| 13 | +- generated types / GraphQL queries / dashboard types |
| 14 | +- paginated, sortable, filterable, or searchable UI state |
| 15 | +- partial failure behavior (count query failure, stale RPC, missing metadata, old rows after schema rollout) |
| 16 | + |
| 17 | +then this checklist is mandatory. |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## 1. Define invariants first |
| 22 | + |
| 23 | +Write down the rules the system must obey before coding. |
| 24 | + |
| 25 | +Examples: |
| 26 | + |
| 27 | +- Every event/snapshot entity must persist `txHash` |
| 28 | +- Charts must not depend on paginated table slices |
| 29 | +- Paginated tables must have deterministic ordering |
| 30 | +- Aggregate-query failure must degrade visibly, not silently |
| 31 | +- Client-side search over large datasets must be bounded and disclosed |
| 32 | + |
| 33 | +If you cannot state the invariant in one sentence, the design is not ready. |
| 34 | + |
| 35 | +--- |
| 36 | + |
| 37 | +## 2. Cross-layer audit |
| 38 | + |
| 39 | +For every new field / changed field / changed behavior, walk the full path: |
| 40 | + |
| 41 | +### Schema / source of truth |
| 42 | + |
| 43 | +- [ ] `schema.graphql` updated if entity shape changed |
| 44 | +- [ ] field names/types/nullability are intentional |
| 45 | +- [ ] backward-compatibility / rollout behavior considered for old rows |
| 46 | + |
| 47 | +### Writers |
| 48 | + |
| 49 | +- [ ] every entity constructor / writer is updated consistently |
| 50 | +- [ ] all event handlers that produce the entity were checked, not just the obvious one |
| 51 | +- [ ] generated/codegen artifacts refreshed where applicable |
| 52 | + |
| 53 | +### Readers |
| 54 | + |
| 55 | +- [ ] GraphQL queries updated |
| 56 | +- [ ] dashboard/runtime types updated |
| 57 | +- [ ] derived formatting / rendering logic updated |
| 58 | +- [ ] search/sort/filter fields updated intentionally |
| 59 | + |
| 60 | +### Tests |
| 61 | + |
| 62 | +- [ ] producer/indexer tests updated |
| 63 | +- [ ] consumer/UI tests updated |
| 64 | +- [ ] fixtures reflect the new schema reality |
| 65 | + |
| 66 | +If one layer is missing, stop and fix it before opening the PR. |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +## 3. Stateful table rubric |
| 71 | + |
| 72 | +If the PR touches a table with pagination, sort, filter, search, or linked charts, answer all of these explicitly. |
| 73 | + |
| 74 | +### Sorting |
| 75 | + |
| 76 | +- [ ] Is sorting server-side, client-side, or hybrid? |
| 77 | +- [ ] Are page boundaries deterministic for non-unique sort fields? |
| 78 | +- [ ] Is there a unique tiebreaker (`id`, tx hash, composite key, etc.)? |
| 79 | +- [ ] Do headers expose sort state accessibly (`aria-sort`)? |
| 80 | + |
| 81 | +### Pagination |
| 82 | + |
| 83 | +- [ ] What determines total row count? |
| 84 | +- [ ] What happens when count/aggregate fails? |
| 85 | +- [ ] Does pagination remain usable after transient failure? |
| 86 | +- [ ] Are controls actual buttons with `type="button"`? |
| 87 | + |
| 88 | +### Search / filtering |
| 89 | + |
| 90 | +- [ ] Does search operate on current page, fetched window, or full dataset? |
| 91 | +- [ ] Is that behavior documented in code comments and PR notes? |
| 92 | +- [ ] If bounded, is the cap explicit and user-visible? |
| 93 | +- [ ] If unbounded, can the backend/query path actually support it? |
| 94 | + |
| 95 | +### Coupled visualizations |
| 96 | + |
| 97 | +- [ ] Do charts use dedicated queries instead of inheriting paginated/sorted table state? |
| 98 | +- [ ] If not, is that coupling intentional and documented? |
| 99 | + |
| 100 | +### URL / local state |
| 101 | + |
| 102 | +- [ ] Is table state URL-backed or intentionally local? |
| 103 | +- [ ] If local-only, is that explicitly called out as an intentional scope decision? |
| 104 | + |
| 105 | +--- |
| 106 | + |
| 107 | +## 4. Degraded-mode checklist |
| 108 | + |
| 109 | +For each non-happy path, decide the behavior explicitly. |
| 110 | + |
| 111 | +- [ ] count query fails |
| 112 | +- [ ] chart query fails |
| 113 | +- [ ] some rows predate a new schema field |
| 114 | +- [ ] RPC-derived metadata is missing |
| 115 | +- [ ] total dataset is much larger than the current happy-path sample |
| 116 | +- [ ] search term matches data outside the currently fetched window |
| 117 | +- [ ] empty state vs loading state vs partial-data state are distinct |
| 118 | + |
| 119 | +The key question: |
| 120 | + |
| 121 | +> What will the user see, and will they understand that the data is partial or degraded? |
| 122 | +
|
| 123 | +Silent degradation is not acceptable. |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## 5. Required test matrix |
| 128 | + |
| 129 | +For nontrivial stateful data/UI changes, tests must cover all 3 buckets: |
| 130 | + |
| 131 | +### Happy path |
| 132 | + |
| 133 | +- [ ] normal render / query wiring |
| 134 | +- [ ] new field is displayed/used correctly |
| 135 | + |
| 136 | +### State transition |
| 137 | + |
| 138 | +- [ ] sort toggle changes query/order state |
| 139 | +- [ ] page transition changes offset/page state |
| 140 | +- [ ] search input resets/updates the right state |
| 141 | +- [ ] links/actions resolve to the expected target |
| 142 | + |
| 143 | +### Failure / degraded mode |
| 144 | + |
| 145 | +- [ ] count error fallback |
| 146 | +- [ ] capped search behavior |
| 147 | +- [ ] missing field / legacy row behavior |
| 148 | +- [ ] user-visible warning or fallback state |
| 149 | + |
| 150 | +If the risky behavior is interactive, a static markup assertion is not enough. |
| 151 | + |
| 152 | +--- |
| 153 | + |
| 154 | +## 6. PR description requirements |
| 155 | + |
| 156 | +Before opening the PR, include these sections: |
| 157 | + |
| 158 | +### What this PR changes |
| 159 | + |
| 160 | +Short factual summary. |
| 161 | + |
| 162 | +### Invariants |
| 163 | + |
| 164 | +List the system rules this PR relies on or introduces. |
| 165 | + |
| 166 | +### Degraded behavior |
| 167 | + |
| 168 | +What happens on count/query/RPC failure, old rows, large datasets, etc. |
| 169 | + |
| 170 | +### Intentional non-goals |
| 171 | + |
| 172 | +Examples: |
| 173 | + |
| 174 | +- URL-backed sort/page state deferred |
| 175 | +- full server-side search deferred |
| 176 | +- abstraction cleanup out of scope |
| 177 | + |
| 178 | +This prevents reviews from repeatedly rediscovering scope boundaries. |
| 179 | + |
| 180 | +--- |
| 181 | + |
| 182 | +## 7. Repo-specific lessons already paid for |
| 183 | + |
| 184 | +These are not theoretical. |
| 185 | + |
| 186 | +- New UI fields must not assume schema support without verifying all writers. |
| 187 | +- Shared presentational components should forward DOM props unless intentionally constrained. |
| 188 | +- Count fallback must preserve prior total, not collapse to current page length. |
| 189 | +- Search behavior must be bounded and disclosed when not truly global. |
| 190 | +- Charts and tables should usually be decoupled. |
| 191 | +- Cross-layer features need both indexer and UI regression coverage. |
| 192 | + |
| 193 | +--- |
| 194 | + |
| 195 | +## 8. Final pre-PR questions |
| 196 | + |
| 197 | +If you answer “no” to any of these, do not open yet. |
| 198 | + |
| 199 | +- [ ] Could another engineer explain the invariants from the PR description alone? |
| 200 | +- [ ] Would a transient backend failure produce a sensible UI instead of a misleading one? |
| 201 | +- [ ] Are the largest-cardinality paths still bounded? |
| 202 | +- [ ] Do tests prove behavior, not just markup? |
| 203 | +- [ ] Did review stop being the place where design gets finished? |
| 204 | + |
| 205 | +If not, one more local pass is cheaper than three more review rounds. |
0 commit comments