@caddis/cli 0.4.24 → 0.4.25

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 (40) hide show
  1. package/bundles/antigravity-plugin/plugin.json +1 -1
  2. package/bundles/antigravity-plugin/skills/_registry.md +1 -0
  3. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  4. package/bundles/antigravity-plugin-extras/skills/_registry.md +0 -1
  5. package/bundles/codex/.codex/skills/devops/adopt-app/SKILL.md +174 -0
  6. package/bundles/codex/.codex/skills/devops/adopt-app/profiles/_template/profile.json +55 -0
  7. package/bundles/codex/.codex/skills/devops/adopt-app/profiles/batch-job/profile.json +68 -0
  8. package/bundles/codex/.codex/skills/devops/adopt-app/profiles/dotnet-service/profile.json +67 -0
  9. package/bundles/codex/.codex/skills/devops/adopt-app/profiles/java-service/profile.json +62 -0
  10. package/bundles/codex/.codex/skills/devops/adopt-app/profiles/node-service/profile.json +66 -0
  11. package/bundles/codex/.codex/skills/devops/adopt-app/profiles/php-db/profile.json +75 -0
  12. package/bundles/codex/.codex/skills/devops/adopt-app/profiles/python-web/profile.json +73 -0
  13. package/bundles/codex/.codex/skills/devops/adopt-app/profiles/static-site/profile.json +87 -0
  14. package/bundles/codex/.codex/skills/devops/adopt-app/references/config.example.json +18 -0
  15. package/bundles/codex/.codex/skills/devops/adopt-app/references/profile-contract.md +207 -0
  16. package/bundles/codex/.codex/skills/devops/adopt-app/references/standard.md +304 -0
  17. package/bundles/codex/.codex/skills/devops/adopt-app/scripts/AdoptCommon.ps1 +812 -0
  18. package/bundles/codex/.codex/skills/devops/adopt-app/scripts/Get-AppInventory.ps1 +236 -0
  19. package/bundles/codex/.codex/skills/devops/adopt-app/scripts/New-AdoptionPlan.ps1 +311 -0
  20. package/bundles/codex/.codex/skills/devops/adopt-app/scripts/Resolve-AppProfile.ps1 +246 -0
  21. package/bundles/codex/.codex/skills/devops/adopt-app/scripts/Test-AppConformance.ps1 +343 -0
  22. package/bundles/manifest.json +3 -3
  23. package/package.json +1 -1
  24. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/SKILL.md +0 -0
  25. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/profiles/_template/profile.json +0 -0
  26. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/profiles/batch-job/profile.json +0 -0
  27. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/profiles/dotnet-service/profile.json +0 -0
  28. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/profiles/java-service/profile.json +0 -0
  29. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/profiles/node-service/profile.json +0 -0
  30. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/profiles/php-db/profile.json +0 -0
  31. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/profiles/python-web/profile.json +0 -0
  32. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/profiles/static-site/profile.json +0 -0
  33. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/references/config.example.json +0 -0
  34. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/references/profile-contract.md +0 -0
  35. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/references/standard.md +0 -0
  36. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/scripts/AdoptCommon.ps1 +0 -0
  37. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/scripts/Get-AppInventory.ps1 +0 -0
  38. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/scripts/New-AdoptionPlan.ps1 +0 -0
  39. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/scripts/Resolve-AppProfile.ps1 +0 -0
  40. /package/bundles/{antigravity-plugin-extras → antigravity-plugin}/skills/adopt-app/scripts/Test-AppConformance.ps1 +0 -0
@@ -0,0 +1,304 @@
1
+ # The adoption standard
2
+
3
+ `Test-AppConformance.ps1` (phase 3) measures an app against 20 items, in the order below - the
4
+ same order the script adds them in. Each one gets a verdict, never a guess:
5
+
6
+ | Verdict | Means |
7
+ |---|---|
8
+ | `PASS` | Observed true, from the inventory or the profile. |
9
+ | `GAP` | Observed false. The evidence is named. |
10
+ | `ASK` | Not observable from files. A person must answer it. |
11
+ | `ANSWERED` | Was `ASK`; a non-empty answer has been recorded for it (see below). Not a fourth independent state - it only exists once an `ASK` has been answered. |
12
+
13
+ Items marked **blocking** below stop the adoption exactly the same way whether they are `GAP` or
14
+ an unanswered `ASK` - a checklist that only tracks blocking failures and lists open questions as
15
+ notes at the bottom lets those questions be skipped, which is the failure this standard exists to
16
+ prevent.
17
+
18
+ ---
19
+
20
+ ### 1. `git-repo` - The source is in a git repository
21
+
22
+ **Blocking.**
23
+
24
+ **Decided by:** `PASS` if the source folder contains a `.git` directory (`inventory.git.isRepo`
25
+ is true). `GAP` otherwise, with "no `.git` in the source folder" as the evidence.
26
+
27
+ **Why it is on the list:** an app with no repository has no history and no way to see what
28
+ changed. Adoption starts by importing the folder as one commit - but only once the content
29
+ policy (item 4) is settled, because that first commit decides what is in the history forever.
30
+
31
+ ### 2. `remote-on-team-host` - The repository lives on the team's host
32
+
33
+ **Blocking.**
34
+
35
+ **Decided by:** `GAP` if there is no repository at all; `GAP` if a repository exists but has zero
36
+ remotes configured ("this folder is the ONLY copy of the history"); `GAP` if
37
+ `expectedRemotePattern` is set in the config and none of the remote URLs match it; `PASS` if a
38
+ remote exists and either no pattern is configured or one remote matches it.
39
+
40
+ **Why it is on the list:** a repository with no remote, or hosted on a personal account, is one
41
+ disk failure or one departure away from disappearing. `expectedRemotePattern` is what lets this
42
+ check tell "the team's host" from "somewhere else" - without it configured, having any remote at
43
+ all is enough to pass, which is a weaker check than it looks like.
44
+
45
+ ### 3. `canonical-branch` - Which branch is the truth is written down
46
+
47
+ **Blocking.**
48
+
49
+ **Decided by:** `ASK` when the app has a repository AND (it has no remote, OR it has more than 3
50
+ local branches). `PASS` otherwise. Note the edge case this produces: when there is no repository
51
+ at all, this item reads `PASS` (evidence: "no repository") even though `git-repo` itself is a
52
+ `GAP` for the same app - there being no branches yet is not treated as a question to ask.
53
+
54
+ **Why it is on the list:** with more than a handful of local branches and no shared remote to
55
+ compare against, which one is actually deployed cannot be read from the files. Wiring CI to the
56
+ wrong branch before this is answered means CI builds and tests code nobody ships.
57
+
58
+ ### 4. `source-only` - The repository holds source, not runtimes, data or dependencies
59
+
60
+ **Blocking.**
61
+
62
+ **Decided by:** `GAP` if either of these is true: a top-level folder that IS git-tracked also
63
+ matches an excludable content category (dependency, runtime, build-output, cache, db-data,
64
+ archive, data-export, or media - `log` is exempted here) - i.e. something was committed that
65
+ should not have been; or a top-level folder is git-UNTRACKED, is not `.git`, and is at least
66
+ `-UntrackedThresholdMB` (default 10 MB). Otherwise `PASS` - except a `PASS` is downgraded to
67
+ `ASK` if any top-level folder was scanned with `-SkipFolder` (see "Coverage" below).
68
+
69
+ **Why it is on the list:** a `.gitignore` added later does not remove what is already committed -
70
+ that needs a history rewrite. And a folder that is neither committed nor ignored is the genuinely
71
+ dangerous state: it sits there doing nothing until one `git add -A` puts it in the history
72
+ forever. A filename pattern cannot find that state; only asking git can.
73
+
74
+ ### 5. `no-secrets` - No credential is in the working tree
75
+
76
+ **Blocking.**
77
+
78
+ **Decided by:** `GAP` if the inventory's `secretsSuspected` list is non-empty (files flagged by
79
+ name, e.g. `.env`, `*.pem`, `id_rsa`, `credentials.json`; or by content, e.g. a line shaped like
80
+ `password = "..."` or a live-looking token, with comments, non-literal values, and template
81
+ references excluded). `PASS` if the heuristics found nothing - which the evidence text says
82
+ explicitly is not proof there is nothing.
83
+
84
+ **Why it is on the list:** history keeps a credential, so the fix is to ROTATE it, not delete it.
85
+ Deleting the file in a later commit does not stop anyone who already has a clone, or ever will,
86
+ from reading the old value out of history. Only rotating the credential itself closes that.
87
+
88
+ ### 6. `config-separation` - Configuration is outside the code, per environment
89
+
90
+ **Not blocking.**
91
+
92
+ **Decided by:** `PASS` if at least one config file looks like a committed template (matches a
93
+ name like `.env.example`, `*.sample`, `*.template`, or `*.dist`). `ASK` if the inventory found
94
+ zero config files at all. `GAP` if config files exist but none of them look like a template.
95
+
96
+ **Why it is on the list:** hardcoded, per-environment values in committed config are exactly
97
+ where a real credential tends to leak. A committed template proves the separation exists without
98
+ exposing a real value; its absence does not prove the opposite, which is why zero config files
99
+ reads as a question rather than a pass or a fail.
100
+
101
+ ### 7. `deps-pinned` - Dependencies are pinned
102
+
103
+ **Not blocking.**
104
+
105
+ **Decided by:** `PASS` if a recognised lock file is present among the manifests
106
+ (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `poetry.lock`, `Pipfile.lock`,
107
+ `composer.lock`, `Gemfile.lock`, `Cargo.lock`, `go.sum`). `GAP` otherwise.
108
+
109
+ **Why it is on the list:** without a lock file the build is not reproducible, and a broken deploy
110
+ cannot be told apart from a dependency that moved underneath it between two runs.
111
+
112
+ ### 8. `tests` - There is an automated test suite
113
+
114
+ **Not blocking.**
115
+
116
+ **Decided by:** `PASS` if the inventory's test-file count is greater than zero (files under a
117
+ `tests`/`spec`/`__tests__` folder, or matching a test-name convention like `*_test.py` or
118
+ `*.spec.ts`). `GAP` otherwise.
119
+
120
+ **Why it is on the list:** CI that runs no tests only proves the code compiles.
121
+
122
+ ### 9. `ci-build` - CI builds and tests on every push
123
+
124
+ **Not blocking.**
125
+
126
+ **Decided by:** `PASS` if any CI file was found (a workflow under `.github`/`.gitea`/`.forgejo`,
127
+ or a known pipeline file like `Jenkinsfile`, `azure-pipelines.yml`, `.travis.yml`,
128
+ `appveyor.yml`, `bitbucket-pipelines.yml`). `GAP` if none was found.
129
+
130
+ **Why it is on the list:** without CI, "it works" means "it worked on one machine, once."
131
+
132
+ ### 10. `ci-deploy` - Deployment is reproducible, not manual
133
+
134
+ **Not blocking.**
135
+
136
+ **Decided by:** `ASK` if CI files exist (the file LIST cannot say whether that CI actually
137
+ deploys, only that some pipeline exists). `GAP` if there is no CI at all.
138
+
139
+ **Why it is on the list:** a build pipeline is not the same claim as a deploy pipeline. Whether
140
+ pushing to the default branch actually ships the app has to be answered by a person or by reading
141
+ the workflow's job list, not assumed from its presence.
142
+
143
+ ### 11. `runtime-manifest` - Anything excluded from the repository can be got back
144
+
145
+ **Not blocking.**
146
+
147
+ **Decided by:** `PASS` if no ignored folder is 100 MB or larger among the folders that were
148
+ scanned. `ASK` if one or more ignored folders reach that size. A `PASS` is downgraded to `ASK` if
149
+ any top-level folder was scanned with `-SkipFolder` (see "Coverage" below).
150
+
151
+ **Why it is on the list:** excluding a large runtime or dependency tree from the repository is
152
+ correct. It is only SAFE once something says how to get it back - the exact version and where it
153
+ installs from - and a folder nobody looked at cannot be assumed to already have that answer.
154
+
155
+ ### 12. `health` - There is a health signal that fails loudly
156
+
157
+ **Not blocking.**
158
+
159
+ **Decided by:** `PASS` if any probed text file contains a line matching `/health`, `/healthz`,
160
+ `/api/health`, or `healthcheck` (case-insensitive). `ASK` if every matched component's profile
161
+ name contains `batch` (a batch job has no HTTP endpoint to check at all). `GAP` otherwise.
162
+
163
+ **Why it is on the list:** a check that returns 200 whatever happens is not a health check.
164
+ Finding the STRING `/health` in the source only proves a route exists - it says nothing about
165
+ whether that route actually fails when the app is broken. That is why the plan step this item
166
+ produces explicitly requires breaking the dependency on purpose and watching the check go red
167
+ before it counts as done.
168
+
169
+ ### 13. `run-declared` - How the app runs is declared and version-controlled
170
+
171
+ **Blocking.**
172
+
173
+ **Decided by:** `PASS` if the host's registered services, scheduled tasks, or web sites include
174
+ at least one whose path references this folder (matched on the folder's last path segment,
175
+ against Windows services, scheduled tasks, and IIS sites/applications). `ASK` if nothing on the
176
+ scanned host references it at all.
177
+
178
+ **Why it is on the list:** on a real run, the only "service" `run-declared` found for one app was
179
+ a database engine that happened to be installed inside the app's own folder - not the app's
180
+ process at all. That is why the evidence names each hit individually (`service X`, `task Y`, `site
181
+ Z`): a non-zero count is not proof that any of them is actually running THIS app, and the reader
182
+ has to check, not assume.
183
+
184
+ ### 14. `rollback` - There is a way back to the previous version
185
+
186
+ **Not blocking.**
187
+
188
+ **Decided by:** Always `ASK` - the code marks it "not observable from files" unconditionally.
189
+
190
+ **Why it is on the list:** whether the previous release can be restored, by what exact command,
191
+ and whether that command has ever actually been run, is not written down anywhere a scan can
192
+ read. It can only be answered by a person, and then it has to be exercised at least once to be
193
+ believed (see the plan step this item produces).
194
+
195
+ ### 15. `owner` - The app has a named owner
196
+
197
+ **Blocking.**
198
+
199
+ **Decided by:** Always `ASK` - "not observable from files" unconditionally.
200
+
201
+ **Why it is on the list:** nothing in a repository can say who is accountable for it. Without a
202
+ named owner there is nobody to decide a change and nobody to call when it breaks, so this has to
203
+ reach a person before anything else proceeds.
204
+
205
+ ### 16. `data-recovery` - State can be restored from a backup
206
+
207
+ **Not blocking.**
208
+
209
+ **Decided by:** `PASS` if no ignored folder is 100 MB or larger AND the excludable-content
210
+ summary contains no `db-data` category entry. `ASK` otherwise. A `PASS` is downgraded the same
211
+ way as items 4 and 11 if any top-level folder was scanned with `-SkipFolder`.
212
+
213
+ **Why it is on the list:** excluding a database file or a data dump from the repository is
214
+ correct. It is only safe once a backup schedule exists and a restore from it has actually been
215
+ tested - a repository holding schema and migrations is not itself a backup of the data.
216
+
217
+ ### 17. `docs-readme` - A README says what it is and how to run it
218
+
219
+ **Not blocking.**
220
+
221
+ **Decided by:** `PASS` if any doc file's name matches `README*` (case-insensitive). `GAP`
222
+ otherwise.
223
+
224
+ **Why it is on the list:** the first two questions a new person asks - what is this, and how do I
225
+ run it - should not require finding and interrupting someone.
226
+
227
+ ### 18. `agent-rules` - A rules file the coding agents read
228
+
229
+ **Not blocking.**
230
+
231
+ **Decided by:** `PASS` if a doc file is named exactly `AGENTS.md` or `CLAUDE.md`
232
+ (case-insensitive). `GAP` otherwise.
233
+
234
+ **Why it is on the list:** without it, every coding agent that touches the app re-derives its
235
+ non-obvious rules from scratch, and different agents derive them differently.
236
+
237
+ ### 19. `no-junctions` - No junction or symlink inside the tree git manages
238
+
239
+ **Not blocking.**
240
+
241
+ **Decided by:** `PASS` if no reparse point (junction or symlink) was found outside a dependency
242
+ folder (`node_modules`, `.venv`, `venv`, `vendor`). `GAP` otherwise, naming up to three.
243
+
244
+ **Why it is on the list:** git's own recursive remove (for example `git worktree remove --force`)
245
+ FOLLOWS a junction and deletes the TARGET's contents - this was proven directly, not assumed.
246
+ Ordinary Windows delete operations (`Remove-Item -Recurse`, `cmd /c rmdir /s /q`,
247
+ `[IO.Directory]::Delete`) all correctly refuse to follow a junction, which is exactly what makes
248
+ this trap easy to miss: a delete you tested yourself can look completely safe right up until git
249
+ does the deleting instead.
250
+
251
+ ### 20. `profile` - The stack is declared, not assumed
252
+
253
+ **Blocking.**
254
+
255
+ **Decided by:** `PASS` if phase 2 matched at least one component to a profile. `GAP` if it
256
+ matched none.
257
+
258
+ **Why it is on the list:** without a declared stack, every later step - runtimes, build, deploy -
259
+ is a guess dressed up as a plan. "No profile matched" means the rules do not describe this app
260
+ yet; it does not mean the app has no stack.
261
+
262
+ ---
263
+
264
+ ## Coverage: a skipped folder cannot pass a check
265
+
266
+ Three items above (`source-only`, `runtime-manifest`, `data-recovery`) can only reach `PASS`
267
+ through `Limit-ByCoverage`, which downgrades a would-be `PASS` to `ASK` whenever any top-level
268
+ folder was scanned with `-SkipFolder`. A folder passed to `-SkipFolder` is recorded as **not
269
+ scanned**, never as empty and never as clean - it is unknown. On a real run, `-SkipFolder tools`
270
+ hid a 16 GB runtime tree, and without this rule the check for "anything excluded can be got back"
271
+ would have reported `PASS` on a folder nobody had actually looked at. Coverage-dependent checks
272
+ stay open until every folder has been scanned, or the operator explicitly accepts a partial
273
+ inventory (`-AllowPartial` in phase 1).
274
+
275
+ ## Answers and waivers
276
+
277
+ **`-Answer id=text`** (phase 3) records a non-empty answer for an item that is currently `ASK`.
278
+ The tool does not judge the content - any non-empty text satisfies it - and the verdict becomes
279
+ `ANSWERED`. Answers persist in `answers.json` inside the app's output folder and are merged with
280
+ any new `-Answer` arguments on every run, so recording one once is enough for every later
281
+ re-check.
282
+
283
+ **`-Waive <id>`** records an accepted exception for an item that is not already `PASS`. It sets a
284
+ `waived` flag; **it never rewrites the verdict itself**. A waived `GAP` is still recorded as
285
+ `GAP`, permanently, with `waived: true` next to it - six months later, that difference between "we
286
+ fixed it" and "we accepted the risk" is the whole value of the record. A waived item is excluded
287
+ from the blocking counts below, so it can no longer stop the exit code, but the report still shows
288
+ what it really is.
289
+
290
+ ## Exit codes
291
+
292
+ | Code | Means |
293
+ |---|---|
294
+ | `0` | Every blocking item is `PASS`, `ANSWERED`, or waived. |
295
+ | `1` | At least one blocking item is `GAP` and not waived. Checked first - if both a blocking `GAP` and a blocking unanswered `ASK` exist at once, exit code `1` wins. |
296
+ | `2` | No blocking `GAP` remains, but at least one blocking item is still an unanswered `ASK` and not waived. |
297
+
298
+ The eight blocking items, as the code actually enforces them, are: `git-repo`,
299
+ `remote-on-team-host`, `canonical-branch`, `source-only`, `no-secrets`, `run-declared`, `owner`,
300
+ and `profile`. Which items block is decided in the script, by the `Blocking` argument to each
301
+ item, and not by the project config. An earlier draft of the config carried a `blockingItems`
302
+ list that nothing read and that had drifted out of step with this set; it was removed from the
303
+ code and from `config.example.json` together, rather than documented as a thing that looks
304
+ settable and is not.