@codyswann/lisa 2.345.1 → 2.346.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/all/copy-overwrite/scripts/check-state-classification.mjs +609 -0
  2. package/all/copy-overwrite/scripts/lisa-command-envelope.mjs +194 -0
  3. package/all/copy-overwrite/scripts/lisa-schema-validate.mjs +208 -0
  4. package/all/copy-overwrite/scripts/schemas/lisa-command-envelope.v1.schema.json +119 -0
  5. package/all/copy-overwrite/scripts/schemas/lisa-state-contract.v1.schema.json +191 -0
  6. package/all/create-only/state/README.md +41 -0
  7. package/all/create-only/state/state-contract.example.json +46 -0
  8. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  9. package/dist/core/upstream-evidence-manifest.js +56 -9
  10. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  11. package/package.json +1 -1
  12. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  14. package/plugins/lisa/.codex-plugin/skills/lisa-acceptance-criteria/SKILL.md +1 -0
  15. package/plugins/lisa/.codex-plugin/skills/lisa-codify-verification/SKILL.md +4 -0
  16. package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +2 -0
  17. package/plugins/lisa/.codex-plugin/skills/lisa-research/SKILL.md +7 -1
  18. package/plugins/lisa/.codex-plugin/skills/lisa-task-decomposition/SKILL.md +2 -0
  19. package/plugins/lisa/.codex-plugin/skills/lisa-test-strategy/SKILL.md +1 -0
  20. package/plugins/lisa/.codex-plugin/skills/lisa-verification-lifecycle/SKILL.md +1 -0
  21. package/plugins/lisa/.codex-plugin/skills/lisa-verify/SKILL.md +1 -1
  22. package/plugins/lisa/rules/eager/reset-seed-coverage.md +48 -0
  23. package/plugins/lisa/rules/reference/reset-seed-coverage.md +231 -0
  24. package/plugins/lisa/skills/lisa-acceptance-criteria/SKILL.md +1 -0
  25. package/plugins/lisa/skills/lisa-codify-verification/SKILL.md +4 -0
  26. package/plugins/lisa/skills/lisa-implement/SKILL.md +2 -0
  27. package/plugins/lisa/skills/lisa-research/SKILL.md +7 -1
  28. package/plugins/lisa/skills/lisa-task-decomposition/SKILL.md +2 -0
  29. package/plugins/lisa/skills/lisa-test-strategy/SKILL.md +1 -0
  30. package/plugins/lisa/skills/lisa-verification-lifecycle/SKILL.md +1 -0
  31. package/plugins/lisa/skills/lisa-verify/SKILL.md +1 -1
  32. package/plugins/lisa-agy/plugin.json +1 -1
  33. package/plugins/lisa-agy/skills/lisa-acceptance-criteria/SKILL.md +1 -0
  34. package/plugins/lisa-agy/skills/lisa-codify-verification/SKILL.md +4 -0
  35. package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +2 -0
  36. package/plugins/lisa-agy/skills/lisa-research/SKILL.md +7 -1
  37. package/plugins/lisa-agy/skills/lisa-task-decomposition/SKILL.md +2 -0
  38. package/plugins/lisa-agy/skills/lisa-test-strategy/SKILL.md +1 -0
  39. package/plugins/lisa-agy/skills/lisa-verification-lifecycle/SKILL.md +1 -0
  40. package/plugins/lisa-agy/skills/lisa-verify/SKILL.md +1 -1
  41. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  44. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-copilot/rules/eager/reset-seed-coverage.md +48 -0
  48. package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +231 -0
  49. package/plugins/lisa-copilot/skills/lisa-acceptance-criteria/SKILL.md +1 -0
  50. package/plugins/lisa-copilot/skills/lisa-codify-verification/SKILL.md +4 -0
  51. package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +2 -0
  52. package/plugins/lisa-copilot/skills/lisa-research/SKILL.md +7 -1
  53. package/plugins/lisa-copilot/skills/lisa-task-decomposition/SKILL.md +2 -0
  54. package/plugins/lisa-copilot/skills/lisa-test-strategy/SKILL.md +1 -0
  55. package/plugins/lisa-copilot/skills/lisa-verification-lifecycle/SKILL.md +1 -0
  56. package/plugins/lisa-copilot/skills/lisa-verify/SKILL.md +1 -1
  57. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +236 -0
  59. package/plugins/lisa-cursor/rules/reset-seed-coverage.mdc +53 -0
  60. package/plugins/lisa-cursor/skills/lisa-acceptance-criteria/SKILL.md +1 -0
  61. package/plugins/lisa-cursor/skills/lisa-codify-verification/SKILL.md +4 -0
  62. package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +2 -0
  63. package/plugins/lisa-cursor/skills/lisa-research/SKILL.md +7 -1
  64. package/plugins/lisa-cursor/skills/lisa-task-decomposition/SKILL.md +2 -0
  65. package/plugins/lisa-cursor/skills/lisa-test-strategy/SKILL.md +1 -0
  66. package/plugins/lisa-cursor/skills/lisa-verification-lifecycle/SKILL.md +1 -0
  67. package/plugins/lisa-cursor/skills/lisa-verify/SKILL.md +1 -1
  68. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-expo/.codex-plugin/skills/ops-db-ops/SKILL.md +93 -2
  71. package/plugins/lisa-expo/skills/ops-db-ops/SKILL.md +94 -3
  72. package/plugins/lisa-expo/skills/ops-db-ops/agents/openai.yaml +2 -2
  73. package/plugins/lisa-expo-agy/plugin.json +1 -1
  74. package/plugins/lisa-expo-agy/skills/ops-db-ops/SKILL.md +94 -3
  75. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-expo-copilot/skills/ops-db-ops/SKILL.md +94 -3
  77. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-expo-cursor/skills/ops-db-ops/SKILL.md +94 -3
  79. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  82. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  86. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  87. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  91. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  92. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  96. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  97. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  101. package/plugins/lisa-rails-agy/plugin.json +1 -1
  102. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  106. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  107. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  111. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  112. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  113. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  114. package/plugins/src/base/rules/eager/reset-seed-coverage.md +48 -0
  115. package/plugins/src/base/rules/reference/reset-seed-coverage.md +231 -0
  116. package/plugins/src/base/skills/lisa-acceptance-criteria/SKILL.md +1 -0
  117. package/plugins/src/base/skills/lisa-codify-verification/SKILL.md +4 -0
  118. package/plugins/src/base/skills/lisa-implement/SKILL.md +2 -0
  119. package/plugins/src/base/skills/lisa-research/SKILL.md +7 -1
  120. package/plugins/src/base/skills/lisa-task-decomposition/SKILL.md +2 -0
  121. package/plugins/src/base/skills/lisa-test-strategy/SKILL.md +1 -0
  122. package/plugins/src/base/skills/lisa-verification-lifecycle/SKILL.md +1 -0
  123. package/plugins/src/base/skills/lisa-verify/SKILL.md +1 -1
  124. package/plugins/src/expo/skills/ops-db-ops/SKILL.md +94 -3
  125. package/scripts/check-state-classification.mjs +27 -0
@@ -0,0 +1,236 @@
1
+ ---
2
+ description: "Reset/Seed Coverage & State Classification"
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Reset/Seed Coverage & State Classification
7
+
8
+ Automated suites create state, and the state outlives them. The historical pattern is per-flow
9
+ self-cleanup: a flow creates a uniquely-marked record at the top and deletes it as its last step, so
10
+ the record survives every failure that happens in between. Nothing sweeps the survivors and nothing
11
+ reports them, so the leak is invisible until a list view, a uniqueness constraint, a pagination
12
+ boundary, or an exact-count assertion breaks — at which point it presents as a product bug or an
13
+ unreproducible flake. The same story has now played out on several entity types in several
14
+ codebases, always with the same three ingredients: state a suite creates, cleanup that only runs on
15
+ the happy path, and no inventory that would have noticed.
16
+
17
+ This contract removes the discipline requirement. Instead of asking every author to remember to
18
+ sweep what they created, it makes the *absence* of a decision a failing check.
19
+
20
+ It is a **single vendor-neutral contract** consumed by `lisa-research` (specs state what persistent
21
+ state the work introduces), `lisa-acceptance-criteria` and `lisa-task-decomposition` (the
22
+ classification and the sweep are explicit deliverables on the work item, not implied work),
23
+ `lisa-test-strategy` and `lisa-implement` (building the item includes both),
24
+ `lisa-codify-verification` (the verify step asserts the post-reset baseline), and
25
+ `lisa-verification-lifecycle` / `lisa-verify` (the check is run, and a miss fails the item). Those
26
+ skills cite this slug; they do not restate it.
27
+
28
+ ## Vendor neutrality is the point
29
+
30
+ This contract never names a database engine, a cloud, an identity provider, an object store, a
31
+ search engine, a queue, or a test runner. Projects use different ones, several at once, and change
32
+ them without touching this contract. What is fixed is the *obligation*: every entity that holds
33
+ state across process boundaries carries exactly one policy, and the running system's inventory is
34
+ compared against that declaration.
35
+
36
+ The words "table" and "row" appear in this document only as examples. An entity is anything with a
37
+ durable identity and a lifetime: a table, a collection, a bucket prefix, an index, a topic, a
38
+ user-pool group, a materialized view, a scheduled job.
39
+
40
+ ## Why "new entities are cleared by default" is the wrong safety model
41
+
42
+ An earlier version of this pattern derived the clear-list by subtracting a hand-maintained keep-list
43
+ from a schema listing: anything new is cleared unless someone deliberately exempts it. It is
44
+ attractive because it fails toward action, and the failure it prevents (a new entity silently
45
+ accumulating) is real. It is still wrong as the *boundary*, for reasons that are structural rather
46
+ than stylistic:
47
+
48
+ - **It erases unrelated data on first contact.** The first change that adds an entity nobody has
49
+ classified deletes its contents on the next run, in whatever non-production environment happened
50
+ to run first. "Non-production" is not "worthless" — shared development environments hold hand-built
51
+ scenarios, support reproductions, and partner sandboxes.
52
+ - **A migrations-directory diff does not model reality.** It cannot see renames (which read as one
53
+ deletion plus one unclassified addition), multiple schemas or namespaces, framework- or
54
+ ORM-generated entities that no migration file names, views, partitions and inheritance children,
55
+ entities created by a deploy rather than a migration, or row-level ownership — the common case
56
+ where an entity holds both fixture rows and rows that must survive.
57
+ - **It only sees the database.** Identity-provider accounts, object-storage prefixes, search indexes,
58
+ queue backlogs, and third-party side effects are invisible to it, and those are precisely where
59
+ leaked test state is most expensive.
60
+
61
+ Subtraction survives in this contract as a **detector**: run it, and treat every entity it finds
62
+ that the contract has not classified as a finding. The safety decision is the classification.
63
+
64
+ ## The state contract
65
+
66
+ Default location `state/state-contract.json` (a project with an equivalent artifact keeps its own
67
+ path and records it in `.lisa.config.json`). It is checked in, reviewed like code, and versioned:
68
+
69
+ | Field | Meaning |
70
+ |---|---|
71
+ | `schemaVersion` | The schema this document conforms to. Pinned; a bump is a deliberate migration. |
72
+ | `contractVersion` | The project's own version for *its* declared state. Bumped whenever policies or ownership predicates change, so evidence from one run cannot be read as evidence for a different contract. |
73
+ | `mode` | `contract` (the normal case) or `declared-noop`. |
74
+ | `owner` | Who answers questions about this file. A team or role, not an individual's personal account. |
75
+ | `environments` | The environments a reset may target, and the identity source each is resolved from. Never a list the caller can extend at runtime. |
76
+ | `fixtureIdentity` | How fixture-owned records are recognized: reserved id shape, reserved account/namespace, marker attribute. This is the ownership predicate the whole model rests on. |
77
+ | `entities[]` | One entry per persistent entity. |
78
+ | `assurances` | The required properties below, each with a pointer to the evidence that proves it. |
79
+ | `waivers[]` | Dated IOUs for entities that genuinely cannot be classified yet. |
80
+
81
+ Each `entities[]` entry carries `id` (stable, fully qualified — namespace plus name), `kind` (the
82
+ project's own vocabulary for what sort of state it is), `policy` (one of the four), `reason` (why,
83
+ in one sentence a reviewer can check), `owner`, and policy-specific obligations:
84
+
85
+ - `fixture-owned` → `ownership` (the predicate identifying rows/objects the reset may delete) and
86
+ `sweptBy` (the routine that removes them). An entity classified `fixture-owned` with no sweep is
87
+ the exact leak this rule exists to catch and fails the check.
88
+ - `preserve` → `reason` must name the provenance (migration-seeded, ingested catalog, human-authored,
89
+ belongs to a real person) and, where available, `enforcedBy`.
90
+ - `derived-rebuild` → `rebuiltFrom` (the sources) and `rebuiltBy` (the routine). The reset rebuilds
91
+ and proves convergence rather than clearing.
92
+ - `forbidden` → `enforcedBy` is **required** and must name a control outside the reset process: a
93
+ least-privilege role, a revoked grant, a separate credential or account boundary, a database
94
+ constraint. "The script does not touch it" is not an enforcement.
95
+
96
+ ### Waivers are dated IOUs, never classifications
97
+
98
+ An entity nobody can classify today is recorded with its id, a reason, an owner, a ticket, and a
99
+ `recordedAt` date. It stays visible in the report, it never counts as classified, and it never
100
+ authorizes the reset to touch the entity — a waived entity is treated as `preserve` for safety while
101
+ remaining a finding. A waiver is invalid, and fails the check, when it names an entity that is not in
102
+ the inventory, duplicates an existing classification, or carries no owner, ticket, or date.
103
+
104
+ ## The check
105
+
106
+ The check compares a **runtime inventory** — what the system actually holds, enumerated from the
107
+ running environment — against the contract. Static sources are inputs to discovery, never a
108
+ substitute for the inventory: the whole point is to catch what the repository does not describe.
109
+
110
+ Semantics, in order:
111
+
112
+ 1. **Contract absent** → `not-adopted`. Reported visibly, exits 0, and the context is not required.
113
+ Adoption is an explicit act; a not-yet-adopted repo is not silently passing a gate it never wired.
114
+ 2. **Contract malformed, unparseable, or failing schema validation** → fail. A broken contract is
115
+ never treated as an empty one.
116
+ 3. **`mode: "declared-noop"`** → the noop must carry a reason, an owner, and a capability-manifest
117
+ reference, **and** the detectors must find no persistence signals in the repository. A repo with
118
+ persistence cannot declare a reset noop; the check rejects it rather than trusting the
119
+ declaration.
120
+ 4. **Entity in the inventory with no classification and no waiver** → **fail closed**, naming the
121
+ entity. This is the primary failure this contract exists to produce.
122
+ 5. **Entity classified but absent from the inventory** → fail as stale, unless the entry is marked
123
+ retired with a date. A contract that describes a system that no longer exists is not protection.
124
+ 6. **Policy obligations unmet** — `fixture-owned` with no ownership predicate or no sweep,
125
+ `forbidden` with no external enforcement, `derived-rebuild` with no rebuild routine → fail.
126
+ 7. **A required assurance missing or unevidenced** → fail.
127
+ 8. **Detector findings that the inventory did not surface** (a schema or migration source naming an
128
+ entity the inventory did not return) → fail, because it means the inventory is incomplete and
129
+ every other conclusion is unsound.
130
+ 9. **No inventory available** → `detection-only`. The detectors run, findings are reported, and the
131
+ result is explicitly labeled non-authoritative. It never reports the same status as a verified
132
+ run, and it never satisfies the definition of done on its own.
133
+
134
+ Every exit path emits the standard command envelope, so a caller can tell "verified clean" from
135
+ "could not look" without parsing prose.
136
+
137
+ ### Non-database discovery
138
+
139
+ The inventory is only complete when it enumerates every kind of state the project holds. At minimum,
140
+ discovery asks each of these and records either the enumeration or a documented absence: identity
141
+ provider (accounts, groups, memberships, long-lived sessions and devices), object storage (buckets
142
+ and prefixes written by the app or by tests), search indexes and their aliases, queues and topics
143
+ including dead-letter backlogs, caches with a persistence tier, derived views and projections,
144
+ runtime-created scheduled jobs and webhooks, feature-flag and configuration overrides, and outbound
145
+ side effects (email, SMS, billing, analytics events, third-party records). "We checked and there is
146
+ none" is an acceptable answer and must be written down; not asking is not.
147
+
148
+ ## Required assurances
149
+
150
+ These are the properties a reset must have. Each is declared in `assurances` with a pointer to the
151
+ test, guard, or role that proves it — a declaration with no evidence pointer fails the check.
152
+
153
+ | Assurance | What it means | Why |
154
+ |---|---|---|
155
+ | `preserves-non-fixture-data` | Catalog, reference, and any non-fixture data survive a reset unchanged. | The expensive-to-rebuild data is usually the data a blunt reset destroys, and it is often not restorable by re-running any seed in the repo. |
156
+ | `rejects-reserved-id-collision` | A reserved fixture identifier that collides with a real record aborts rather than overwriting. | The ownership predicate is the entire safety boundary; a collision means it is no longer identifying. |
157
+ | `rejects-foreign-references` | A fixture record referencing a preserved record it does not own is refused, never cascaded. | Cascades are how a fixture-scoped delete reaches outside its scope. |
158
+ | `requires-write-acknowledgment` | Every mutation is confirmed by the system, not assumed from a returned status. | An unacknowledged write is an unknown state, and "unknown" reported as success is how a half-applied reset becomes a mystery failure downstream. |
159
+ | `converges-on-second-apply` | Applying twice produces the same state and the second run reports no further change. | Convergence is what makes the reset safe to re-run, and re-running is what makes partial failure recoverable. |
160
+ | `verifies-exact-counts` | Verification asserts the exact expected count per fixture entity. | "At least one" passes against a leak. Exact counts are what detect one. |
161
+ | `production-fails-closed` | Production is refused with no override, escape hatch, or environment variable that changes the answer. | A single variable that authorizes production mutation will eventually be set by something that is not a person. |
162
+ | `guard-at-the-choke-point` | The environment check runs immediately before every outbound mutation, not only at startup. | A startup-only check does not survive a refactor that adds a second code path. |
163
+ | `enumerates-before-mutating` | `--dry-run` enumerates exactly what would change and fails on anything whose ownership it cannot establish. | Unknown ownership must block, not default to delete. |
164
+
165
+ Prefer platform enforcement over process enforcement wherever it exists: a role that cannot see the
166
+ forbidden entities, grants revoked rather than merely unused, constraints that make a cross-boundary
167
+ reference impossible, separate credentials per environment. A test proves the code behaves today; a
168
+ revoked grant makes the behavior unavailable.
169
+
170
+ ## Standard command envelope
171
+
172
+ Every adapter in this family — reset, seed, verify, inventory, classification check — returns **one**
173
+ JSON object on stdout and nothing else. Human-readable narration goes to stderr, redacted of secrets
174
+ and personal data.
175
+
176
+ ```json
177
+ {
178
+ "schemaVersion": "lisa-command-envelope-v1",
179
+ "capability": "<reset|seed|reset-verify|state-inventory|state-classification|...>",
180
+ "mode": "<real|declared-noop|not-applicable>",
181
+ "operation": "<the operation performed>",
182
+ "environment": "<server-resolved environment identity>",
183
+ "contractVersion": "<the project's state-contract version>",
184
+ "dryRun": true,
185
+ "status": "<completed|no-op|denied|invalid|failed|verification-mismatch|not-adopted|detection-only>",
186
+ "correlationId": "<stable id joining logs, audit records, and CI output>",
187
+ "summary": { "deleted": 0, "created": 0, "preserved": 0 }
188
+ }
189
+ ```
190
+
191
+ Rules that make the envelope load-bearing rather than decorative:
192
+
193
+ - **Exit 0 means completed *and* verified.** Any nonzero exit means denied, invalid, failed, or
194
+ verification-mismatch, and the envelope's `status` says which. An adapter that mutated successfully
195
+ but could not verify exits nonzero.
196
+ - **`--dry-run` is mandatory for every destructive adapter**, enumerates the intended mutations, and
197
+ fails on any entity whose ownership it cannot establish.
198
+ - **`--stage` (or equivalent) is a *request*, never the source of truth.** The adapter resolves the
199
+ environment from server-side deployment identity and fails if the request disagrees. A
200
+ caller-supplied stage, URL, host, or public build-time variable never authorizes a destructive
201
+ operation.
202
+ - **`--idempotency-key` is required in CI**, so a retried invocation is recognized rather than
203
+ repeated.
204
+ - **A noop returns `mode: "declared-noop"`** with a reason, an owner, and a capability-manifest
205
+ reference. A bare exit 0 with no envelope is indistinguishable from a successful destructive run
206
+ and is never an acceptable noop.
207
+ - **`correlationId` appears in the envelope, the audit record, and the CI log**, so a run can be
208
+ reconstructed after the fact.
209
+ - The audit record for a destructive run additionally names the actor, the CI run, the resolved
210
+ environment and account, the contract version, and before/after counts — and contains no secrets
211
+ or personal data.
212
+
213
+ ## Bootstrap procedure
214
+
215
+ 1. Enumerate the runtime inventory from a non-production environment, including the non-database
216
+ sources listed above. Record what could not be enumerated and why.
217
+ 2. Classify every entity. Start from provenance: who writes it, and can anything put it back? Prefer
218
+ `preserve` when unsure — an over-preserved entity leaks; an over-cleared one destroys.
219
+ 3. Declare `fixtureIdentity` and the ownership predicate before classifying anything as
220
+ `fixture-owned`.
221
+ 4. Write the assurances with pointers to the tests or roles that prove them. Missing evidence is a
222
+ work item, not a field to leave blank.
223
+ 5. Wire the check into CI so the next unclassified entity fails the pull request that introduces it.
224
+ 6. Record everything still unclassified as dated waivers with owners, and burn them down.
225
+
226
+ The first work item does not backfill the whole system. It classifies what it touches, scaffolds the
227
+ file, and turns the check on; everything else is burndown that stays visible.
228
+
229
+ ## What this contract does not do
230
+
231
+ It does not decide *how* a reset is executed, where it runs, or what invokes it. It does not
232
+ prescribe intra-run isolation. It does not replace a threat model for a destructive control plane —
233
+ where a reset is reachable from CI, that control plane has its own security obligations (identity,
234
+ least privilege, leases, audit) which this contract assumes rather than specifies. What it
235
+ guarantees is narrower and load-bearing: **no persistent state exists in the system that nobody has
236
+ decided about, and no work item adds some without saying so.**
@@ -0,0 +1,53 @@
1
+ ---
2
+ description: "Reset/Seed Coverage & State Classification (load-bearing)"
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Reset/Seed Coverage & State Classification (load-bearing)
7
+
8
+ **Every persistent entity a project owns carries exactly one declared reset policy, and a work item that adds or changes persistent state is not done until the reset/seed contract covers it.** An entity the running system holds but the contract does not classify is a contract violation, and the check that finds it **fails closed** — an unclassified entity is never treated as safe to keep and never treated as safe to delete.
9
+
10
+ **One vendor-neutral contract, cited by** `lisa-research`, `lisa-acceptance-criteria`, `lisa-task-decomposition`, `lisa-test-strategy`, `lisa-implement`, `lisa-codify-verification`, `lisa-verification-lifecycle`, and `lisa-verify` (the `leaf-only-lifecycle` / `repo-scope-split` precedent: one shared slug, never divergent per-skill prose). It never names a database engine, cloud, identity provider, or test runner — those are project configuration.
11
+
12
+ ## Why this exists
13
+
14
+ Test suites create state. Without a contract, cleanup is a habit: a flow creates a uniquely-marked record and deletes it on the happy path only, so every early failure leaks one more. Nothing sweeps it, nothing complains, and the suite degrades until someone reads a flake as a product bug. The observed shape is always the same — leaked records accumulate silently until a list view, a uniqueness constraint, or a count assertion breaks. Per-flow self-cleanup is not coverage; it is the failure mode.
15
+
16
+ ## Membership
17
+
18
+ Membership is **state, not repo name, ticket label, or storage engine**: an entity is in scope the moment something the project writes **outlives the process that wrote it**. That includes rows and tables, but rows are only one kind of state. Also in scope: identity-provider objects (accounts, groups, memberships, sessions), object storage (buckets, prefixes, uploaded files), search indexes, queues and topics (in-flight and dead-letter), caches with a persistence tier, materialized/derived views and projections, feature-flag and configuration overrides, scheduled jobs created at runtime, and analytics or third-party side effects a run leaves behind. Out of scope: values that live only in memory for the duration of one process.
19
+
20
+ A work item is in scope when it **adds** such an entity, **changes what owns or writes** one, or **changes the lifetime** of one. Renames count as changes, not as new entities plus deletions.
21
+
22
+ ## The four policies
23
+
24
+ Every entity is classified as exactly one of:
25
+
26
+ - **`fixture-owned`** — the reset may create, mutate, and delete rows here, but only those it owns. Ownership is declared as a predicate (a reserved id shape, a reserved account or namespace, a marker attribute), never as "everything in this entity."
27
+ - **`preserve`** — the reset must leave this untouched. Catalog, reference data, anything a migration seeds, anything expensive or impossible to rebuild, and any state belonging to someone who is not the fixture.
28
+ - **`derived-rebuild`** — not authored by anyone; recomputed from its sources after the reset (projections, materialized views, search indexes, caches). The reset rebuilds it rather than clearing or preserving it, and proves it converged.
29
+ - **`forbidden`** — the reset must be structurally unable to touch it: ledgers, payments, wallets, withdrawals, audit trails, anything irreversible. Declaring `forbidden` obliges naming the enforcement outside the script — a least-privilege role, a revoked grant, a separate account or credential boundary — because a script-only promise dies in the refactor that drops the safe caller.
30
+
31
+ **Classify by provenance, never by observation.** "The table looked empty" is not evidence it is unused; runtime statistics are approximations, and a table that has never been analyzed reports rows it holds. Ask who writes it and whether anything can put it back.
32
+
33
+ ## Keep-lists are a detector, not the safety model
34
+
35
+ Deriving the clear-list by subtracting a keep-list from a schema listing is a **useful detector** — run it, and let it flag entities the contract has not seen. It is not the boundary. It cannot model renames, multiple schemas, framework-generated entities, views, partitions, row-level ownership, or any of the non-DB state above, and "new entities are cleared unless exempted" erases unrelated data the first time something adds one. The boundary is the classification; subtraction only tells you the classification is stale.
36
+
37
+ ## Required assurances
38
+
39
+ A reset contract is not complete until it declares, and points at evidence for, all of: non-fixture and catalog data survive; reserved fixture identifiers are rejected on collision with anything real; foreign references into preserved entities are refused rather than cascaded; every write is **acknowledged** (a mutation reported by the caller and not confirmed by the system is a failure, not a success); a second apply converges to the same state and reports no further change; the post-state is verified by **exact expected counts**, not "at least one"; and production fails closed with no override of any kind. Where the platform can enforce a boundary with roles, grants, or constraints, it does — the in-process guard remains as defense in depth, never as the primary control.
40
+
41
+ ## Definition of done
42
+
43
+ A work item that adds or changes persistent state is not done until: (1) every entity it introduces or changes is classified in the project's state contract with a reason and an owner; (2) `fixture-owned` entities it introduces declare their ownership predicate **and** are actually swept by the reset — an entity a suite creates but nothing removes is the leak this rule exists to prevent; (3) `preserve`/`forbidden` entities it introduces name their enforcement; (4) any seed the suite depends on covers the new state, with the verify step asserting exact counts; and (5) the state-classification check passes in the same PR. **A missing or stale classification is a verification failure, not a warning** — it blocks completion exactly as a `PARTIAL` spec-conformance verdict does, and is never demoted to "optional", "if cheap", or a follow-up without a linked build-ready ticket created before merge.
44
+
45
+ ## Bootstrap, noop, and degradation
46
+
47
+ A project with no state contract yet is not exempt: the first work item touching persistent state scaffolds the minimum (the contract file, its own entities classified, the check wired into CI) and covers **its own** state. It never backfills the whole system — pre-existing unclassified entities are recorded as burndown with an owner and a date, not treated as this item's blocker. A project that genuinely holds no persistent state declares a **machine-readable noop** (`mode: "declared-noop"` with reason, owner, and a capability-manifest reference) which the check verifies against the repo rather than taking on faith — a bare exit 0 is indistinguishable from a successful destructive run and never satisfies this rule. A project that cannot produce a runtime inventory records the checked sources and the absence, exactly as the regression-spec absence path in `verification` does; a silent skip is never one of the exits.
48
+
49
+ ## Command envelope
50
+
51
+ Every reset, seed, verify, inventory, and classification adapter answers the **same interface** — one validated JSON result on stdout, exit 0 only when the operation both completed **and** verified, `--dry-run` mandatory before anything destructive, and a requested stage always re-checked against server-resolved identity rather than trusted. The envelope is specified in the reference body and is what makes "every repo answers the same question the same way" checkable instead of aspirational.
52
+
53
+ Full contract (state-contract schema, check semantics, detectors, envelope schema, enforcement patterns, bootstrap procedure): [reference/reset-seed-coverage.md](reset-seed-coverage-reference.mdc).
@@ -65,6 +65,7 @@ For each acceptance criterion:
65
65
  - Every user flow must include at least one error path
66
66
  - Use Gherkin format (Given/When/Then) for user flows to enable direct translation into test cases
67
67
  - For frontend work, the `bdd-e2e-coverage` rule applies: name the scenario IDs the item will add or change in the project's behavior contract and the platforms each must hold on, and list the contract update plus the aligned e2e automation as explicit acceptance criteria -- they are deliverables, not implied work. List the passing coverage gate and the regenerated matrix and burndown as explicit acceptance criteria too -- the item is not done on a code-complete mapping alone
68
+ - For work that adds or changes persistent state, the `reset-seed-coverage` rule applies: name the persistent entities the item introduces or changes and the reset policy each takes (`fixture-owned` / `preserve` / `derived-rebuild` / `forbidden`), and list the state-contract update, the sweep for anything `fixture-owned`, and the passing state-classification check as explicit acceptance criteria -- they are deliverables, not implied work. Rows are only one kind of state: identity-provider objects, object storage, search indexes, queues, caches and derived views count too
68
69
  - When verifying, always run the feature -- never review by only reading code
69
70
  - If you cannot run the feature (missing dependencies, services unavailable), report as a blocker -- do not guess
70
71
  - If the changes are purely internal (refactoring, config, tooling), report "No user-facing impact" and explain why
@@ -83,6 +83,10 @@ For **frontend work** — any verification whose validation journey exercised a
83
83
 
84
84
  Every artifact encodes the SAME verified journey against a different platform. One is never a substitute for another, and a passing test on one platform never seals another platform's obligation.
85
85
 
86
+ ### 2b. Codifying persistent state (non-demotable)
87
+
88
+ When the verified journey **created, changed, or depended on persistent state**, the `reset-seed-coverage` rule governs what else this codification owes. Classify every entity the work touched in the project's state contract, give anything `fixture-owned` an ownership predicate and a sweep, and run the project's state-classification check the same way the coverage gate is run above — a contract edited but never checked is not proof. Where the journey depended on seeded state, the seed's verify step asserts **exact expected counts** for that state: "at least one" passes against a leak, which is precisely the condition being guarded. Cite the rule for the policy vocabulary and the assurances; do not restate them here.
89
+
86
90
  Permitted exits, mirroring the regression-spec rule in `lisa-implement` (never a silent skip, never "optional", and never a bare `N/A`):
87
91
 
88
92
  - The project genuinely has no runner configured for that platform → record a dated `platformWaivers` entry naming the locations checked and "no runner configured" as the reason, exactly like any other unsealable obligation, per the rule. This is never left as a bare `N/A` — an undated absence has no forcing function to ever get revisited.
@@ -149,6 +149,8 @@ IF it is a Fix (bug), execute the Reproduce sub-flow FIRST:
149
149
 
150
150
  For any Fix flow, and for any Build flow that changes user-visible behavior, regression coverage is a required deliverable at the highest practical observation level for the reported surface. If the project has a browser, device, or end-to-end harness for that platform (for example Playwright, Maestro, Detox, Cypress, or an equivalent runtime), the task plan and definition of done MUST include a deterministic regression spec against the reported surface, using mocked or seeded data where needed. This is alongside unit or integration coverage, not a substitute for it. For **frontend work** the deliverable is defined by the `bdd-e2e-coverage` rule and has two halves, both landing in this PR: the Gherkin scenario (stable ID, required platforms) added or updated in the project's behavior contract, and aligned automation in the project's configured runner for **every** platform that scenario requires — no runner substitutes for another, because they guard different platforms of the same behavior. The coverage gate must pass and the matrix and burndown be regenerated before the item is done. Cite the rule; do not restate its scenario, waiver, or bootstrap mechanics here.
151
151
 
152
+ For work that adds or changes **persistent state**, the `reset-seed-coverage` rule adds a second non-demotable deliverable landing in this same PR: every entity the item introduces or changes is classified in the project's state contract (`fixture-owned` / `preserve` / `derived-rebuild` / `forbidden`) with a reason and an owner, anything `fixture-owned` declares its ownership predicate and is actually swept, and the state-classification check passes. Writing a flow that creates a record and deletes it only on its happy path does NOT satisfy this — that is the leak, not the coverage. Cite the rule; do not restate its policy, waiver, or bootstrap mechanics here.
153
+
152
154
  The team lead may not waive, defer, demote, or phrase this regression spec as "optional", "if cheap", "nice to have", or equivalent. The only permitted exits are:
153
155
 
154
156
  1. The project genuinely has no end-to-end harness for the affected platform; record the checked locations and that absence in the task metadata, PR, and work-item evidence.
@@ -62,7 +62,13 @@ EARS-pattern shape, each with a measurable fit criterion, plus the non-functiona
62
62
  story with frontend scope, its user-facing behavior MUST be expressed as — or in a shape directly
63
63
  convertible to — Given/When/Then scenarios, naming the platforms each behavior must hold on, per the
64
64
  `bdd-e2e-coverage` rule; narrative-only frontend behavior forces that shape to be invented later,
65
- inconsistently, by whoever picks up the ticket. Each scenario stub carries its originating requirement
65
+ inconsistently, by whoever picks up the ticket. Where a requirement introduces or changes
66
+ **persistent state** — anything the system writes that outlives the process that wrote it, rows being
67
+ only one kind — the PRD MUST name that state and the reset policy it is expected to take, per the
68
+ `reset-seed-coverage` rule, so the classification is a stated requirement rather than something
69
+ discovered when a leaked record breaks a suite; that rule also covers the non-row cases (identity
70
+ objects, object storage, search indexes, queues, caches, derived views) a narrative spec routinely
71
+ omits. Each scenario stub carries its originating requirement
66
72
  atom (`R1`, `R2`, …) even though the stable `@BDD-<DOMAIN>-<NNN>` ID is not minted until Plan or
67
73
  Implement — this is what lets a later stage's provenance tag, and `spec-conformance`'s traceability
68
74
  check, walk back from a shipped scenario to the PRD requirement it was written to satisfy, instead of
@@ -56,6 +56,8 @@ For each task, define what "done" looks like:
56
56
 
57
57
  For a **frontend task** -- one that adds or changes a user-observable surface -- the `bdd-e2e-coverage` rule makes two further criteria mandatory on the item itself, never left implied: (a) the Gherkin scenarios it adds or changes in the project's behavior contract, with their stable IDs and required platforms, and (b) aligned e2e automation in the project's configured runner for each of those platforms, with the coverage gate passing and the matrix and burndown regenerated. Carry both into the item's Validation Journey. A project with no behavior contract yet does not get an exemption -- the first such task carries the bootstrap scaffolding as a deliverable, scoped to its own behavior (cite the rule; do not restate its bootstrap steps).
58
58
 
59
+ For a task that adds or changes **persistent state** -- anything the system writes that outlives the process that wrote it, including identity-provider objects, object storage, search indexes, queues, caches and derived views, not only rows -- the `reset-seed-coverage` rule makes two further criteria mandatory on the item itself, never left implied: (a) every entity it introduces or changes is classified in the project's state contract with a reason and an owner, and (b) anything classified `fixture-owned` has a declared ownership predicate and an actual sweep, with the state-classification check passing. Carry both into the item's Validation Journey. A project with no state contract yet does not get an exemption -- the first such task carries the bootstrap scaffolding as a deliverable, scoped to its own state (cite the rule; do not restate its bootstrap steps).
60
+
59
61
  ### 3. Assign Verification Type
60
62
 
61
63
  Each task must have a verification method. Choose the most appropriate:
@@ -58,6 +58,7 @@ Structure findings as:
58
58
  - Always run `bun run test` to understand current test state before recommending or writing new tests
59
59
  - Match existing test conventions -- do not introduce new test patterns
60
60
  - For frontend work, the strategy is not complete until every required scenario-platform obligation has aligned e2e automation in the project's configured runner for that platform, or a dated waiver naming the runner limitation (`bdd-e2e-coverage`). A unit test, a route boot, or a passing test on a different platform never seals an obligation
61
+ - For work that adds or changes persistent state, the strategy is not complete until each new entity is classified in the project's state contract and anything `fixture-owned` has a sweep, per the `reset-seed-coverage` rule. Per-flow self-cleanup is not a strategy: a flow that deletes what it created only on its happy path leaks on every failure in between, which is how state pollution becomes an unreproducible flake months later
61
62
  - Every test must have a clear "why" -- no tests for testing's sake
62
63
  - Focus on testing behavior, not implementation details
63
64
  - Verification commands must be runnable locally (no CI/CD dependencies)
@@ -374,6 +374,7 @@ A task is done only when:
374
374
  - Proof artifacts are captured
375
375
  - Every passing empirical verification is codified as a regression test (or has an explicit, documented skip reason from the allowed set)
376
376
  - For frontend work, the `bdd-e2e-coverage` contract is satisfied: every user-facing behavior added or changed exists as a Gherkin scenario with a stable ID traceable to the work item, every required scenario-platform obligation is mapped to aligned e2e automation in the project's configured runner for that platform or carries a dated waiver, and the coverage gate passes with the matrix and burndown regenerated and committed. Missing BDD coverage is a verification failure, not a warning
377
+ - For work that adds or changes persistent state, the `reset-seed-coverage` contract is satisfied: every entity introduced or changed is classified in the project's state contract with a reason and an owner, anything `fixture-owned` declares its ownership predicate and is actually swept, and the state-classification check passes in the same PR. An unclassified entity fails closed; per-flow self-cleanup is not coverage
377
378
  - For a leaf work unit, every typed `[EVIDENCE: <artifact-type>: <name>]` marker declared in its Validation Journey has a captured, non-empty artifact of the declared type attached to the ticket (the evidence manifest is fully satisfied)
378
379
  - Cross-work-item `EVIDENCE-REF` pointers were excluded from the local manifest and did not satisfy S14 or completion; a runtime-changing leaf has at least one local claiming marker
379
380
  - Spec conformance verdict is `CONFORMS` (not `PARTIAL`, not `DIVERGES`)
@@ -35,7 +35,7 @@ Treat the first successful lead-spawn request (or, on the Codex fallback, the fi
35
35
 
36
36
  Execute the **Verify** flow as defined in the `intent-routing` rule (loaded via the lisa plugin). The flow includes:
37
37
 
38
- 1. **Pre-flight: codification gate** — confirm that every passing local empirical verification on this branch was codified as a regression test (the Implement flow's codify step). If any verification has no committed test and no allowed skip reason (PR / Documentation / Deploy / Investigate-Only), invoke `codify-verification` now and amend the PR before shipping. For frontend work the gate additionally enforces the `bdd-e2e-coverage` rule: the shipped behavior exists as a Gherkin scenario with a stable ID, every required scenario-platform obligation has aligned automation in the project's configured runner for that platform or a dated waiver, and the project's coverage gate passes with the matrix regenerated. A missing runner is never a bare recorded absence — it needs a dated `platformWaivers` entry (per the rule) and, where the runner could reasonably be added, a linked build-ready follow-up ticket referenced from it; never a silent skip. Missing BDD coverage is a verification failure that blocks the ship, not a warning. A change cannot ship until its verifications are guarded.
38
+ 1. **Pre-flight: codification gate** — confirm that every passing local empirical verification on this branch was codified as a regression test (the Implement flow's codify step). If any verification has no committed test and no allowed skip reason (PR / Documentation / Deploy / Investigate-Only), invoke `codify-verification` now and amend the PR before shipping. For frontend work the gate additionally enforces the `bdd-e2e-coverage` rule: the shipped behavior exists as a Gherkin scenario with a stable ID, every required scenario-platform obligation has aligned automation in the project's configured runner for that platform or a dated waiver, and the project's coverage gate passes with the matrix regenerated. A missing runner is never a bare recorded absence — it needs a dated `platformWaivers` entry (per the rule) and, where the runner could reasonably be added, a linked build-ready follow-up ticket referenced from it; never a silent skip. Missing BDD coverage is a verification failure that blocks the ship, not a warning. A change cannot ship until its verifications are guarded. For work that adds or changes persistent state the gate additionally enforces the `reset-seed-coverage` rule: every entity the work introduces or changes is classified in the project's state contract, anything `fixture-owned` has an ownership predicate and a sweep, and the state-classification check passes — an unclassified entity fails closed and blocks the ship exactly as missing BDD coverage does.
39
39
  2. **Commit** any pending changes via `lisa-git-commit`
40
40
  3. **Push and PR** via `lisa-git-submit-pr`
41
41
  4. **PR Watch Loop** — drive the PR to MERGED via `lisa-drive-pr-to-merge`, the single source of truth for clearing every blocker: auto-merge with direct-merge fallback, `BEHIND` re-sync, conflict resolution, failing-check fixes, human + bot review-comment handling with thread resolution (it invokes `lisa-pull-request-review` itself), stale `CHANGES_REQUESTED` dismissal, and post-merge ancestry verification. Do not re-implement the loop or its terminal conditions.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.345.1",
3
+ "version": "2.346.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.345.1",
3
+ "version": "2.346.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -8,9 +8,10 @@ allowed-tools:
8
8
 
9
9
  # Ops: Database Operations
10
10
 
11
- Manage database migrations, schema generation, and GraphQL code generation.
11
+ Manage database migrations, schema generation, GraphQL code generation, and the project's
12
+ reset/seed/verify state operations.
12
13
 
13
- **Argument**: `$ARGUMENTS` — operation (`migrate`, `revert`, `generate`, `schema`, `codegen`) and optional environment (default: `dev`)
14
+ **Argument**: `$ARGUMENTS` — operation (`migrate`, `revert`, `generate`, `schema`, `codegen`, `seed`, `reset`, `reset:verify`, `state:inventory`, `state:classify`) and optional environment (default: `dev`)
14
15
 
15
16
  ## Path Convention
16
17
 
@@ -21,6 +22,21 @@ Manage database migrations, schema generation, and GraphQL code generation.
21
22
 
22
23
  **CRITICAL**: Never run migrations or reverts against production without explicit human confirmation.
23
24
 
25
+ **Destructive state operations are stricter than that.** `reset` (and any `seed` that clears before
26
+ it writes) is governed by the `reset-seed-coverage` rule and never runs on the strength of a
27
+ confirmation alone:
28
+
29
+ - Run `--dry-run` first, always, and read what it says it will change. A dry run that reports an
30
+ entity whose ownership it cannot establish is a STOP, not a warning.
31
+ - The environment is whatever the adapter resolves server-side. A `--stage`, `TEST_ENV`, URL, host,
32
+ or public build-time variable is a *request* the adapter checks, never the answer. If the requested
33
+ stage and the resolved environment disagree, that is a refusal to investigate — not something to
34
+ re-run with a different flag.
35
+ - **Production has no override, in any repo, ever.** There is no flag, variable, or confirmation
36
+ phrase that makes a production reset correct. If one appears to exist, stop and report it.
37
+ - Never invent a reset. If the project ships no reset adapter, say so and stop; do not improvise
38
+ deletions with ad-hoc queries or the migration tooling.
39
+
24
40
  ## Discovery
25
41
 
26
42
  Read the backend `package.json` to discover available migration and schema scripts:
@@ -31,10 +47,22 @@ Read the backend `package.json` to discover available migration and schema scrip
31
47
  - `generate:sql-schema*` — regenerate SQL schema for MCP
32
48
  - AWS credential/profile scripts such as `aws:signin:*` and any environment-backed remote profile
33
49
 
50
+ Read the backend and frontend `package.json` to discover the state operations. Discover them; do not
51
+ assume these exact names — a project may expose them under its own, and this skill runs whatever the
52
+ project declares:
53
+ - `db:seed` / `seed:*` — write the fixture baseline
54
+ - `db:reset` / `reset:*` — converge state back to that baseline
55
+ - `db:reset:verify` / `reset:verify:*` — prove the post-state, by exact counts
56
+ - `state:inventory` — enumerate what the running environment actually holds
57
+ - `check:state-classification` — compare that inventory against the state contract
58
+
34
59
  Read the frontend `package.json` to discover codegen scripts:
35
60
  - `fetch:graphql:schema:*` — fetch GraphQL schema
36
61
  - `generate:types:*` — generate TypeScript types
37
62
 
63
+ If a state script the operation needs does not exist, report the absence and the scripts that DO
64
+ exist. Do not substitute a different script, and do not fall back to running SQL by hand.
65
+
38
66
  ## AWS Prerequisite
39
67
 
40
68
  All database operations (except `codegen`) require AWS credentials. Verify the target profile first:
@@ -113,11 +141,74 @@ STAGE={env} bun run generate:sql-schema
113
141
 
114
142
  **Note**: The backend must be running (locally or deployed) for schema fetching to work.
115
143
 
144
+ ### seed (write the fixture baseline)
145
+
146
+ Run the project's discovered seed script for the target environment. A seed is additive or
147
+ convergent, never a blind wipe; if the project's seed clears first, treat it as a reset and follow
148
+ the reset sequence below.
149
+
150
+ ```bash
151
+ cd "${BACKEND_DIR:-../backend-v2}"
152
+ {package-manager} run {discovered-seed-script} --stage {env}
153
+ ```
154
+
155
+ ### reset (converge state back to the baseline)
156
+
157
+ Never a single command. The sequence is fixed:
158
+
159
+ 1. **Dry run.** `{discovered-reset-script} --dry-run --stage {env}`. Read the enumeration. Stop on
160
+ any entity of unknown ownership, on any `forbidden`-classified entity appearing at all, and on
161
+ any disagreement between the requested stage and the resolved environment.
162
+ 2. **Classify first if the contract is stale.** If `check:state-classification` reports an
163
+ unclassified entity, the reset does not run — an unclassified entity is neither safe to keep nor
164
+ safe to delete. Fix the state contract, then start over at step 1.
165
+ 3. **Apply**, passing an idempotency key when running in CI:
166
+ `{discovered-reset-script} --stage {env} --idempotency-key {run-id}`.
167
+ 4. **Verify** with `reset:verify`. A reset that mutated but did not verify is a failure, not a
168
+ partial success.
169
+ 5. **Converge check.** Applying a second time must report no further change. If the second run keeps
170
+ deleting, the ownership predicate is wrong — report it rather than re-running.
171
+
172
+ ### reset:verify (prove the post-state)
173
+
174
+ ```bash
175
+ {package-manager} run {discovered-verify-script} --stage {env}
176
+ ```
177
+
178
+ Verification asserts **exact** expected counts per fixture entity. "At least one" passes against a
179
+ leak, which is the condition being guarded.
180
+
181
+ ### state:inventory (enumerate what the environment actually holds)
182
+
183
+ Produces the runtime inventory the classification check compares against. A complete inventory
184
+ covers more than rows: identity-provider objects, object storage prefixes, search indexes, queues
185
+ and dead-letter backlogs, caches with a persistence tier, derived and materialized views, and
186
+ runtime-created jobs. Record anything that could not be enumerated, with the reason.
187
+
188
+ ### state:classify (compare the inventory to the contract)
189
+
190
+ ```bash
191
+ node scripts/check-state-classification.mjs
192
+ ```
193
+
194
+ Exit 0 = every entity the environment holds is classified and every policy obligation is met.
195
+ Nonzero = an unclassified entity, an unswept `fixture-owned` entity, a stale classification, an
196
+ incomplete inventory, or a missing assurance. Report the findings verbatim — each names the entity
197
+ and what is missing.
198
+
116
199
  ## Output Format
117
200
 
201
+ Every state operation returns the standard command envelope on stdout — one JSON object with
202
+ `schemaVersion`, `capability`, `mode`, `operation`, `environment`, `contractVersion`, `dryRun`,
203
+ `status`, `correlationId`, and `summary{deleted,created,preserved}`. Report from that envelope, not
204
+ from prose scraped off stderr. A destructive operation that produced no envelope has not been
205
+ verified, whatever its exit code said.
206
+
118
207
  Report operation result:
119
208
 
120
209
  | Operation | Environment | Target | Status | Details |
121
210
  |-----------|-------------|--------|--------|---------|
122
211
  | migrate | dev | local DB | SUCCESS | 2 migrations applied |
123
212
  | codegen | dev | frontend | SUCCESS | Types regenerated |
213
+ | reset | dev | reset adapter | completed | deleted 12 / preserved 31, converged on second apply, correlation `abc123` |
214
+ | state:classify | dev | state contract | failed | `public.notes` unclassified — fail closed |