@iceinvein/agent-skills 0.1.39 → 0.2.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 (148) hide show
  1. package/README.md +18 -2
  2. package/dist/cli/index.js +105 -28
  3. package/package.json +1 -1
  4. package/skills/index.json +14 -2
  5. package/skills/magpie/README.md +2 -1
  6. package/skills/magpie/SKILL.md +138 -45
  7. package/skills/magpie/bin/magpie.ts +43 -0
  8. package/skills/magpie/fixtures/example-pr/brief.json +18 -0
  9. package/skills/magpie/fixtures/fake-gh-nodiff.sh +38 -0
  10. package/skills/magpie/fixtures/fake-gh.sh +14 -0
  11. package/skills/magpie/package.json +1 -1
  12. package/skills/magpie/references/peer-review.md +7 -2
  13. package/skills/magpie/references/scout.md +90 -0
  14. package/skills/magpie/references/specialists.md +117 -6
  15. package/skills/magpie/scripts/__tests__/cli.test.ts +101 -1
  16. package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +187 -0
  17. package/skills/magpie/scripts/__tests__/diff-chunks.test.ts +51 -0
  18. package/skills/magpie/scripts/__tests__/filter-diff-preservation.test.ts +54 -0
  19. package/skills/magpie/scripts/__tests__/findings-files.test.ts +35 -0
  20. package/skills/magpie/scripts/__tests__/gh.test.ts +90 -0
  21. package/skills/magpie/scripts/__tests__/git-diff.test.ts +83 -0
  22. package/skills/magpie/scripts/__tests__/helpers/git-fixture.ts +47 -0
  23. package/skills/magpie/scripts/__tests__/path-filter.test.ts +27 -0
  24. package/skills/magpie/scripts/__tests__/preview-cmd.test.ts +16 -0
  25. package/skills/magpie/scripts/__tests__/refresh.test.ts +45 -0
  26. package/skills/magpie/scripts/__tests__/render-cmd.test.ts +173 -1
  27. package/skills/magpie/scripts/__tests__/render-findings.test.ts +151 -1
  28. package/skills/magpie/scripts/__tests__/render-progress.test.ts +42 -0
  29. package/skills/magpie/scripts/__tests__/setup-cmd.test.ts +83 -1
  30. package/skills/magpie/scripts/__tests__/shard.test.ts +165 -0
  31. package/skills/magpie/scripts/__tests__/skill-lint.test.ts +207 -7
  32. package/skills/magpie/scripts/__tests__/types.test.ts +47 -0
  33. package/skills/magpie/scripts/dedupe-cmd.ts +58 -3
  34. package/skills/magpie/scripts/diff-chunks.ts +28 -0
  35. package/skills/magpie/scripts/findings-files.ts +32 -0
  36. package/skills/magpie/scripts/gh.ts +68 -14
  37. package/skills/magpie/scripts/git-diff.ts +111 -0
  38. package/skills/magpie/scripts/path-filter.ts +9 -5
  39. package/skills/magpie/scripts/preview-cmd.ts +11 -1
  40. package/skills/magpie/scripts/refresh.ts +32 -3
  41. package/skills/magpie/scripts/render-cmd.ts +35 -11
  42. package/skills/magpie/scripts/render-findings.ts +78 -2
  43. package/skills/magpie/scripts/render-progress.ts +6 -1
  44. package/skills/magpie/scripts/setup-cmd.ts +38 -1
  45. package/skills/magpie/scripts/shard.ts +171 -0
  46. package/skills/magpie/scripts/status-cmd.ts +7 -4
  47. package/skills/magpie/scripts/types.ts +50 -0
  48. package/skills/magpie/skill.json +2 -2
  49. package/skills/magpie/templates/styles.css +66 -0
  50. package/skills/migrate/README.md +194 -0
  51. package/skills/migrate/SKILL.md +197 -0
  52. package/skills/migrate/bin/migrate +15 -0
  53. package/skills/migrate/bin/migrate.ts +309 -0
  54. package/skills/migrate/biome.json +35 -0
  55. package/skills/migrate/bun.lock +24 -0
  56. package/skills/migrate/docs/architecture.md +294 -0
  57. package/skills/migrate/docs/reference.md +590 -0
  58. package/skills/migrate/fixtures/tiny-express/GROUND-TRUTH.md +39 -0
  59. package/skills/migrate/fixtures/tiny-express/app.js +29 -0
  60. package/skills/migrate/fixtures/tiny-express/cron.js +6 -0
  61. package/skills/migrate/fixtures/tiny-express/reports/daily-users.json +6 -0
  62. package/skills/migrate/fixtures/tiny-express/schema.sql +12 -0
  63. package/skills/migrate/fixtures/tiny-express/settings.json +4 -0
  64. package/skills/migrate/fixtures/tiny-express/views/users.html +9 -0
  65. package/skills/migrate/fixtures/tiny-webforms/Controllers/UsersController.cs +68 -0
  66. package/skills/migrate/fixtures/tiny-webforms/Default.aspx +7 -0
  67. package/skills/migrate/fixtures/tiny-webforms/Default.aspx.cs +14 -0
  68. package/skills/migrate/fixtures/tiny-webforms/GROUND-TRUTH.md +50 -0
  69. package/skills/migrate/fixtures/tiny-webforms/Integrations/BillingClient.cs +16 -0
  70. package/skills/migrate/fixtures/tiny-webforms/Jobs/NightlyDigestJob.cs +33 -0
  71. package/skills/migrate/fixtures/tiny-webforms/Reports/DailyUsers.rdl +11 -0
  72. package/skills/migrate/fixtures/tiny-webforms/Schema.sql +12 -0
  73. package/skills/migrate/fixtures/tiny-webforms/Site.master +16 -0
  74. package/skills/migrate/fixtures/tiny-webforms/Users.aspx +8 -0
  75. package/skills/migrate/fixtures/tiny-webforms/Users.aspx.cs +14 -0
  76. package/skills/migrate/fixtures/tiny-webforms/web.config +10 -0
  77. package/skills/migrate/install.sh +68 -0
  78. package/skills/migrate/package.json +17 -0
  79. package/skills/migrate/references/phases/enumerate.md +291 -0
  80. package/skills/migrate/references/phases/extract.md +652 -0
  81. package/skills/migrate/references/phases/parity.md +275 -0
  82. package/skills/migrate/references/phases/probe.md +135 -0
  83. package/skills/migrate/references/phases/queue.md +242 -0
  84. package/skills/migrate/references/phases/seam.md +416 -0
  85. package/skills/migrate/references/recipes/README.md +116 -0
  86. package/skills/migrate/references/recipes/aspnet.md +287 -0
  87. package/skills/migrate/references/run-ops.md +280 -0
  88. package/skills/migrate/scripts/__tests__/census.test.ts +775 -0
  89. package/skills/migrate/scripts/__tests__/check.test.ts +458 -0
  90. package/skills/migrate/scripts/__tests__/citations.test.ts +156 -0
  91. package/skills/migrate/scripts/__tests__/cli.test.ts +183 -0
  92. package/skills/migrate/scripts/__tests__/concurrency.test.ts +164 -0
  93. package/skills/migrate/scripts/__tests__/config.test.ts +112 -0
  94. package/skills/migrate/scripts/__tests__/e2e-express.test.ts +1093 -0
  95. package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +1276 -0
  96. package/skills/migrate/scripts/__tests__/e2e.test.ts +320 -0
  97. package/skills/migrate/scripts/__tests__/ids.test.ts +38 -0
  98. package/skills/migrate/scripts/__tests__/import.test.ts +155 -0
  99. package/skills/migrate/scripts/__tests__/init.test.ts +192 -0
  100. package/skills/migrate/scripts/__tests__/leaks.test.ts +176 -0
  101. package/skills/migrate/scripts/__tests__/lock.test.ts +183 -0
  102. package/skills/migrate/scripts/__tests__/paths.test.ts +129 -0
  103. package/skills/migrate/scripts/__tests__/phase-cmd.test.ts +151 -0
  104. package/skills/migrate/scripts/__tests__/phases.test.ts +70 -0
  105. package/skills/migrate/scripts/__tests__/queue.test.ts +475 -0
  106. package/skills/migrate/scripts/__tests__/report.test.ts +150 -0
  107. package/skills/migrate/scripts/__tests__/run-state.test.ts +136 -0
  108. package/skills/migrate/scripts/__tests__/status-reset.test.ts +318 -0
  109. package/skills/migrate/scripts/__tests__/store.test.ts +132 -0
  110. package/skills/migrate/scripts/__tests__/validate.test.ts +54 -0
  111. package/skills/migrate/scripts/census-cmd.ts +109 -0
  112. package/skills/migrate/scripts/census.ts +342 -0
  113. package/skills/migrate/scripts/check-cmd.ts +24 -0
  114. package/skills/migrate/scripts/check.ts +376 -0
  115. package/skills/migrate/scripts/citations.ts +92 -0
  116. package/skills/migrate/scripts/config.ts +237 -0
  117. package/skills/migrate/scripts/ids.ts +31 -0
  118. package/skills/migrate/scripts/import-cmd.ts +141 -0
  119. package/skills/migrate/scripts/init-cmd.ts +118 -0
  120. package/skills/migrate/scripts/leaks.ts +184 -0
  121. package/skills/migrate/scripts/lock.ts +188 -0
  122. package/skills/migrate/scripts/paths.ts +103 -0
  123. package/skills/migrate/scripts/phase-cmd.ts +63 -0
  124. package/skills/migrate/scripts/phases.ts +113 -0
  125. package/skills/migrate/scripts/queue-cmd.ts +98 -0
  126. package/skills/migrate/scripts/queue.ts +258 -0
  127. package/skills/migrate/scripts/report-cmd.ts +47 -0
  128. package/skills/migrate/scripts/report.ts +131 -0
  129. package/skills/migrate/scripts/reset-cmd.ts +120 -0
  130. package/skills/migrate/scripts/status-cmd.ts +52 -0
  131. package/skills/migrate/scripts/store.ts +159 -0
  132. package/skills/migrate/scripts/types.ts +137 -0
  133. package/skills/migrate/scripts/validate.ts +221 -0
  134. package/skills/migrate/skill.json +33 -0
  135. package/skills/migrate/templates/config.toml +27 -0
  136. package/skills/migrate/templates/queue-item.md +17 -0
  137. package/skills/migrate/tsconfig.json +18 -0
  138. package/skills/migrate/uninstall.sh +31 -0
  139. package/skills/sluice/SKILL.md +82 -0
  140. package/skills/sluice/references/deep-channel.md +94 -0
  141. package/skills/sluice/references/finish.md +35 -0
  142. package/skills/sluice/references/intent.md +29 -0
  143. package/skills/sluice/references/review.md +42 -0
  144. package/skills/sluice/references/root-cause.md +38 -0
  145. package/skills/sluice/references/show-or-say.md +36 -0
  146. package/skills/sluice/references/test-first.md +35 -0
  147. package/skills/sluice/references/verify.md +26 -0
  148. package/skills/sluice/skill.json +32 -0
@@ -0,0 +1,291 @@
1
+ # Phase 1: Enumerate
2
+
3
+ ## Purpose
4
+
5
+ Enumerate every surface declared in `[surfaces].types`, add every element
6
+ found to the ledger `unaccounted`, and close each surface with one lens
7
+ census record. Exit condition: every declared surface has exactly one
8
+ `lens` census record, its arithmetic balances and stays in bounds, and
9
+ `migrate phase enumerate --status done` has run.
10
+
11
+ ## Inputs
12
+
13
+ - `config.toml`: `[surfaces].types` (which surfaces you owe a census
14
+ record), `[surfaces.singular]` (id-prefix overrides for a surface that is
15
+ already singular but ends in `s`), `source.stack` (which recipe, if any,
16
+ names this surface's directions), `source.path`.
17
+ - The store: `elements.jsonl` for this surface (what the ledger already
18
+ holds, so a new candidate can be diffed against it rather than re-added),
19
+ `phases.json` (which batches have already committed, for resuming).
20
+
21
+ ## Procedure
22
+
23
+ **The lens contract, stated once.** A lens declares
24
+ `{surface, directions[], classify, census}`. It enumerates the surface from
25
+ at least two independent directions, dedupes into one list, diffs that list
26
+ against the ledger, and classifies every difference as exactly one of
27
+ **add**, **skip with a named reason**, or **queue**. It closes by writing a
28
+ census record. Nothing is silently dropped, and the CLI does the
29
+ subtraction, not you.
30
+
31
+ 1. **Enumerate from at least two independent directions.** A direction is
32
+ whatever independent method produced a count: `grep CREATE TABLE across
33
+ *.sql` is one; `grep DbSet<> in the DbContext` is another. They do not
34
+ have to be different lenses in the `code`/`nav`/`docs`/`runtime` sense
35
+ used on element rows; two different greps against two different source
36
+ artifacts, both read by the same `code` lens, count as two directions.
37
+ `migrate census` rejects a record with fewer than two, and rejects the
38
+ Milestone 1 bare-count shape (`{"ddl": 43}`) by name if you write it out
39
+ of habit.
40
+
41
+ 2. **Name the evidence.** Every direction's `evidence` field names the
42
+ command or method that produced its count, not just the number. Nothing
43
+ executes it: the CLI never re-runs `grep CREATE TABLE`, it only checks
44
+ that the field is a non-empty string. The requirement exists so a
45
+ reviewer, or you on a later pass, can retrace the count by hand, not so
46
+ the tool can verify it automatically.
47
+
48
+ 3. **Dedupe, diff, classify.** Merge the directions' findings into one list.
49
+ Diff against what `elements.jsonl` already holds for this surface. For
50
+ everything new, decide: **add** it (import as a new element,
51
+ `unaccounted`), **skip** it with a named reason (framework-owned,
52
+ out-of-scope by design, and so on), or **queue** it (open a queue item
53
+ when the right disposition is not yours to decide alone).
54
+
55
+ 4. **Record what an added element touches.** When a lens finds an element
56
+ that reads, writes, or otherwise depends on an element already in the
57
+ ledger, add a `{"kind": "ledger", "id": "<the other element's id>"}`
58
+ entry to this element's `refs`. A route that reads a table records a ref
59
+ to that table's id. A job that writes one does the same. A screen that
60
+ posts to a route does the same.
61
+
62
+ State plainly what this is for, because it reads like bookkeeping
63
+ otherwise, and gets skipped: this is the only edge data the seam phase's
64
+ fallback validator, surface-affinity clustering, has to build a graph
65
+ from (`references/phases/seam.md`). It clusters the ledger against
66
+ itself using exactly these refs and nothing else. A lens that finds a
67
+ real touch and does not record it does not just weaken that validator,
68
+ it disarms it outright: the graph it builds has no edges at all if no
69
+ lens ever writes one.
70
+
71
+ **Ordering is not a reason to skip this.** A lens enumerating routes may
72
+ find a route touching a table whose element the tables lens has not
73
+ added yet. Record the ref anyway, pointing at the id you expect that
74
+ table to receive (`<singular>-<slug>`, the same convention every element
75
+ id follows). A `ledger` ref naming an id not yet in the ledger is
76
+ expected mid-enumerate, and resolves once the other lens's batch lands.
77
+ Nothing rejects it on either end at this phase: `migrate import
78
+ elements` validates that a `ledger` ref carries a string `id`, not that
79
+ the id already exists, and `check.ts`'s `refs` gate resolves a `ledger`
80
+ *citation* on a requirement against the ledger, but never reads an
81
+ element's own `refs` at all, so nothing checks that one element's ref
82
+ resolves to another. That is a real gap, not a guarantee in disguise;
83
+ this manual does not add a check to close it, and seam is where a
84
+ dangling ref would first actually matter.
85
+
86
+ 5. **Declare `"phase": "enumerate"` on the record, not the batch's phase or
87
+ any other.** A lens census must declare `enumerate`; nothing else is
88
+ accepted. This is enforced at write time, by name: `migrate census`
89
+ rejects `"phase": "extract"` on a `lens` record with `lens census must
90
+ declare phase "enumerate", the only phase gate 10 (run-state) checks its
91
+ batch against; found "extract"`, before it can ever name a batch that
92
+ gate would silently fail to find. The gate hardcodes exactly one phase
93
+ to look in for a lens's batch (`enumerate`) and one for a closer's
94
+ (`extract`), never the record's own `phase` field, so the constraint at
95
+ write time is what keeps the two from disagreeing later.
96
+
97
+ 6. **Sanity-check the arithmetic before you submit.** Two checks the CLI
98
+ will run for you, so run them yourself first and you will not get
99
+ rejected:
100
+
101
+ - Balance: `total == in_ledger + added + skipped.length + queued.length`.
102
+ - Bounds: `max(directions) <= total <= sum(directions)`. A deduped union
103
+ can never be smaller than its largest input (dedup only removes
104
+ duplicates) or larger than their concatenation (dedup can only
105
+ shrink). `total` itself stays unverifiable either way: nothing on this
106
+ side of the source can confirm the legacy system really has exactly
107
+ that many tables. The bound only rules out the arithmetically
108
+ impossible.
109
+
110
+ A worked example, run against a real store: two elements found, one
111
+ route by `code` alone and one by both `code` and `nav`, nothing skipped
112
+ or queued.
113
+
114
+ ```json
115
+ {
116
+ "kind": "lens",
117
+ "surface": "routes",
118
+ "phase": "enumerate",
119
+ "directions": {
120
+ "code": { "count": 2, "evidence": "grep [HttpGet]/[HttpPost] attributes across Controllers/*.cs" },
121
+ "nav": { "count": 2, "evidence": "walked route registrations in Startup.cs and Swagger UI" }
122
+ },
123
+ "total": 2,
124
+ "in_ledger": 0,
125
+ "added": 2,
126
+ "skipped": [],
127
+ "queued": [],
128
+ "batch": "b-routes-census-001"
129
+ }
130
+ ```
131
+
132
+ `migrate census routes.json` accepts this and prints
133
+ `census: recorded lens:routes`. Balance: `2 == 0 + 2 + 0 + 0`. Bounds:
134
+ `max(2, 2) = 2 <= 2 <= sum(2, 2) = 4`.
135
+
136
+ ### Naming skipped elements
137
+
138
+ `skipped` element names are compared after trimming and case folding, so
139
+ `"__efmigrationshistory"` and `" __EFMigrationsHistory"` collide and are
140
+ correctly rejected, run against a real store, as the same entry recorded
141
+ twice: `skipped element __efmigrationshistory appears 2 times
142
+ (__efmigrationshistory, __EFMigrationsHistory)`. What that comparison
143
+ cannot catch is two genuinely different spellings of one real element:
144
+ `"__efmigrationshistory"` and `"EF Migrations History (framework)"` name
145
+ the same table, but neither trims nor case-folds into the other, and a
146
+ real census carrying both as separate `skipped` entries is accepted
147
+ outright, with no violation at all. This is a documented limit, not a bug
148
+ to chase: free text naming real things in a legacy source cannot be made
149
+ padding-proof in general, and Milestone 2 chose convention over
150
+ engineering here on purpose.
151
+
152
+ The convention: **name a skipped element with the source's own identifier,
153
+ verbatim, lowercased. No qualifiers, no display names.**
154
+
155
+ - Right: `"__efmigrationshistory"` (the table's real name, lowercased,
156
+ nothing added or removed).
157
+ - Wrong: `"EF Migrations History (framework)"` (a description someone
158
+ wrote instead of the identifier; a different lens run skipping the same
159
+ table under this spelling produces an undetected duplicate skip, not a
160
+ caught one).
161
+
162
+ This does not fix the gap; it narrows how often it bites. If everyone
163
+ writing a `skipped` entry uses the source's own name, any repeat is far more
164
+ likely to be a pure case or whitespace variant, which the comparison does
165
+ catch, rather than a synonym, which it does not.
166
+
167
+ ### Zero-findings rule
168
+
169
+ A lens that finds nothing still closes with a census record, not silence.
170
+ Every direction reports `{"count": 0, "evidence": "..."}` naming what you
171
+ searched and where, and `total`, `in_ledger`, `added`, `skipped`, and
172
+ `queued` are all zero or empty. This is accepted:
173
+
174
+ ```json
175
+ {
176
+ "kind": "lens",
177
+ "surface": "jobs",
178
+ "phase": "enumerate",
179
+ "directions": {
180
+ "code": { "count": 0, "evidence": "grep [Cron]/[Trigger]/IHostedService across the source tree: no matches" },
181
+ "docs": { "count": 0, "evidence": "grep 'scheduled job' and 'batch job' across docs/: no matches" }
182
+ },
183
+ "total": 0,
184
+ "in_ledger": 0,
185
+ "added": 0,
186
+ "skipped": [],
187
+ "queued": [],
188
+ "batch": "b-jobs-census-001"
189
+ }
190
+ ```
191
+
192
+ Without this record, `migrate check` cannot tell "no jobs exist in this
193
+ source" apart from "the jobs lens never ran." With it, the absence is a
194
+ recorded finding, not a gap.
195
+
196
+ ### Contract-only mode
197
+
198
+ `source.stack` is `unknown`, or is a stack with no recipe pack. There is no
199
+ "no recipe" degradation that produces one lens and a shrug: you derive your
200
+ own at least two directions for the surface (whatever two independent ways
201
+ of finding, say, tables actually apply to this source) and the census gates
202
+ them exactly as it would a recipe's own directions. Contract-only is not a
203
+ lesser path; it is the same contract with the directions supplied by you
204
+ instead of by a recipe file.
205
+
206
+ ### Fanout
207
+
208
+ Fanout unit is (surface x lens): one agent per pairing. A surface's one
209
+ lens census record aggregates the directions from however many (surface,
210
+ lens) dispatches actually ran for it. See `references/run-ops.md` for
211
+ dispatch, batching, and checkpoint mechanics; this manual does not restate
212
+ them.
213
+
214
+ ## What closes it
215
+
216
+ Every declared surface needs exactly one `lens` census record. Import the
217
+ batch, then close the surface:
218
+
219
+ ```
220
+ migrate import elements batch-routes.json
221
+ migrate census census-routes.json
222
+ ```
223
+
224
+ Real output from both, run against a scratch store:
225
+
226
+ ```
227
+ import elements: 2 added, 0 updated, batch b-routes-code-001
228
+ census: recorded lens:routes
229
+ ```
230
+
231
+ Mid-run, `migrate check --phase enumerate` will not read as clean, and that
232
+ is expected, not a defect. Run against this store with only `routes` and
233
+ `tables` closed, and `enumerate` not yet flipped to `done`, it reported all
234
+ of this:
235
+
236
+ ```
237
+ 0/3 mapped, 0 out-of-scope, 3 unaccounted
238
+
239
+ Violations (13):
240
+ coverage:
241
+ route-get-api-users (routes) is still unaccounted
242
+ route-post-api-login (routes) is still unaccounted
243
+ table-users (tables) is still unaccounted
244
+ census:
245
+ declared surface jobs has no lens census record; the lens did not run or did not close
246
+ declared surface reports has no lens census record; the lens did not run or did not close
247
+ declared surface screens has no lens census record; the lens did not run or did not close
248
+ declared surface integrations has no lens census record; the lens did not run or did not close
249
+ declared surface workflows has no lens census record; the lens did not run or did not close
250
+ declared surface settings has no lens census record; the lens did not run or did not close
251
+ declared closer cross-capability-workflow has no census record
252
+ declared closer scope-injection has no census record
253
+ declared closer read-write-symmetry has no census record
254
+ run-state:
255
+ phase enumerate is running; every phase through enumerate must be done
256
+ ```
257
+
258
+ Read this by gate, not by exit code, while a phase is still open. `census`
259
+ naming every surface and closer you have not reached yet is the whole
260
+ store being read regardless of `--phase`, the same behavior `SKILL.md`
261
+ describes; it clears only once every declared surface and closer actually
262
+ has a record, no matter when you flip the phase status. `coverage` naming
263
+ your just-imported elements as unaccounted is equally expected, and it
264
+ clears on a different schedule again: nothing disposes an element before
265
+ extract, so it stays noisy through the whole of this phase regardless.
266
+ `run-state` is the one line that is really about *this* phase's own
267
+ status, and it is also the only one of the three that clears the moment
268
+ you flip it. Once every declared surface has closed, flip it:
269
+
270
+ ```
271
+ migrate phase enumerate --status done
272
+ ```
273
+
274
+ ## Degradation
275
+
276
+ - **No recipe for the detected stack.** Contract-only mode (above): derive
277
+ your own directions, the census still gates identically.
278
+ - **No runtime environment.** The `runtime` lens records `not-applicable`
279
+ with the reason; enumerate this surface from whichever other directions
280
+ remain, and you still need at least two.
281
+ - **No documentation.** The `docs` lens records `not-applicable:no-documentation`.
282
+ - **A lens with zero findings.** Record that fact explicitly (above); it is
283
+ a finding, not an absence of one.
284
+
285
+ ## Commands
286
+
287
+ ```
288
+ migrate import elements <batch.json>
289
+ migrate census <lens-record.json>
290
+ migrate phase enumerate --status done
291
+ ```