@a2f0/agent-tool 0.1.7 → 0.1.9

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.
package/README.md CHANGED
@@ -101,6 +101,7 @@ agent-tool skills install --apply
101
101
  | `versions plan\|bump\|check <base-oid>` | Plan, rewrite, or check package versions against an exact base |
102
102
  | `versions prepare <base-oid>` | Bump, refresh `bun.lock`, validate, and commit versions; print a JSON receipt |
103
103
  | `versions resolve-conflicts` | Resolve only version-field conflicts in configured manifests |
104
+ | `dependencies check-terraform-plan <json-file>` | Read-only screening for destructive or incomplete Terraform JSON plans |
104
105
  | `skills list` | List embedded portable skills |
105
106
  | `skills install [--harness all\|claude\|codex\|opencode] [--apply]` | Preview by default; install or update managed skills |
106
107
  | `skills check [--harness all\|claude\|codex\|opencode]` | Read-only CI check for current managed skills |
@@ -220,7 +221,8 @@ if new skills do not appear. OpenCode may list identical skills from both
220
221
  paths.
221
222
 
222
223
  The bundle provides `cross-agent-review`, `open-pr`, `squash-merge`, `ship-pr`,
223
- and `reset`. They make no package-manager or project-layout assumptions:
224
+ `reset`, and `update-dependencies`. They make no package-manager or project-layout
225
+ assumptions:
224
226
 
225
227
  - Keep validation commands, review-bot rules, and deployment checks in the
226
228
  project's `AGENTS.md`.
@@ -236,6 +238,52 @@ is no force mode. After updating the dependency, run
236
238
  fails for missing, outdated, unmanaged, or edited skills without writing
237
239
  anything. Use the same `--harness` for install and check.
238
240
 
241
+ ### Dependency upgrades
242
+
243
+ Invoke `update-dependencies` in Claude Code, Codex, or OpenCode to inventory and
244
+ upgrade package dependencies, runtimes, mise tools, Actions, native toolchains,
245
+ Ansible, Terraform providers/modules, and other repository-owned pins. It reads
246
+ official migration guides, upgrades coupled dependencies together, resolves
247
+ deprecations, and records validated, constrained, and skipped upgrades. It follows
248
+ the repository's existing upgrade skills and support policy.
249
+
250
+ Read-only advisory checks cover baseline and candidate lockfiles, including
251
+ resolved transitive and native dependencies. The skill traces findings to their
252
+ owning dependency and prefers an upstream-supported fix. A scoped security patch
253
+ override requires compatibility evidence, a regression check of the affected
254
+ integration, and a documented removal condition; blind audit fixes and blanket
255
+ compatibility overrides are excluded. Remaining advisories and audit limits are
256
+ reported explicitly. For published packages, the skill also checks tarballs and
257
+ consumer installs with their documented supported managers, including override
258
+ syntax and runtime requirements.
259
+
260
+ The skill requires a dry run before any infrastructure mutation, including ones
261
+ triggered by hooks or CI on push/merge. Resource destruction or replacement, and
262
+ previews that cannot establish safety, skip the affected upgrade group. A Wrangler
263
+ bundle dry run alone does not prove remote resource safety. The skill does not
264
+ itself authorize deployment or shipping; use `ship-pr` when requested.
265
+
266
+ For a full refreshed Terraform saved plan, export its private JSON and screen it:
267
+
268
+ ```sh
269
+ terraform show -json /private/path/upgrade.tfplan > /private/path/upgrade.json
270
+ agent-tool dependencies check-terraform-plan /private/path/upgrade.json
271
+ ```
272
+
273
+ The check returns JSON with `ok`, `issues`, and `resourceActions`, and exits 1 for
274
+ unsafe or unsupported plans. It rejects delete and replacement actions, unknown
275
+ actions, destructive drift, incomplete/deferred plans, unpassed checks, and opaque
276
+ provider action invocations. It requires JSON format 1.x and `complete: true`
277
+ (available from Terraform 1.8); older plans without completeness evidence fail.
278
+ OpenTofu's current JSON format omits `complete` and is not accepted; report that
279
+ limitation rather than fabricating the field.
280
+ Missing `resource_changes` is valid for a complete empty or output-only plan;
281
+ removing an output is not a resource deletion. Invalid JSON fails without echoing
282
+ the input. The command never applies or contacts a backend and prints no attribute
283
+ values. It cannot establish plan freshness, environment/identity, provider or
284
+ provisioner side effects, or authorization; inspect those before deploying and
285
+ apply the exact approved saved plan. Keep state and plans out of Git.
286
+
239
287
  ## Review guarantees and limits
240
288
 
241
289
  Reviews run against exact base and head snapshots. Review policy comes from the
@@ -271,7 +319,10 @@ can resolve fork PR bases; `pr open` supports same-repository branches.
271
319
 
272
320
  ## Development
273
321
 
274
- Development and the CI workflow use Bun 1.4.2.
322
+ Development and all build/release workflows use Bun 1.4.2 and TypeScript 7.0.2.
323
+ The compiler is used through `tsc`; this repository does not depend on its removed
324
+ JavaScript compiler API. Runtime consumers still support Bun 1.3.11 or newer;
325
+ CI also runs the installed-package smoke and plan-guard tests on that minimum.
275
326
 
276
327
  ```sh
277
328
  bun install --frozen-lockfile
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2f0/agent-tool",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "description": "Portable cross-agent review, PR workflows, and shared skills",
6
6
  "license": "UNLICENSED",
@@ -31,7 +31,7 @@
31
31
  "build": "bun run generate && bun build --compile --minify src/index.ts --outfile dist/agent-tool"
32
32
  },
33
33
  "devDependencies": {
34
- "@types/bun": "1.3.11",
35
- "typescript": "5.9.3"
34
+ "@types/bun": "1.4.2",
35
+ "typescript": "7.0.2"
36
36
  }
37
37
  }
@@ -0,0 +1,227 @@
1
+ ---
2
+ name: update-dependencies
3
+ description: Update a repository's dependency manifests, lockfiles, runtimes, tools, and CI pins with upstream migrations, compatibility checks, and non-destructive infrastructure previews.
4
+ ---
5
+
6
+ # Update Dependencies
7
+
8
+ Use the repository's package managers and installed `agent-tool`. Read `AGENTS.md`,
9
+ README, linked setup/release documents, and repository-owned upgrade skills before
10
+ editing. Follow their hooks, version policy, checks, and requested stopping point.
11
+ Update applicable dependencies throughout the repository, including workspaces
12
+ and examples. This skill does not authorize deploying, merging, publishing, or
13
+ changing global tools; use existing user authorization and `ship-pr` when shipping
14
+ is requested. Default dependency-update merge subject: `chore: update dependencies`,
15
+ subject to repository title policy and any explicit user subject.
16
+
17
+ ## Safety before execution
18
+
19
+ **Always dry-run before any infrastructure apply or deployment. Never destroy,
20
+ replace, or recreate a resource during an upgrade.** This includes Terraform,
21
+ OpenTofu, Wrangler, Ansible, other infrastructure tools, and effects triggered by
22
+ installation, tests, hooks, Git push, opening a PR, CI, or merging. Greenfield
23
+ projects and approval to ship do not waive this rule.
24
+
25
+ Before running project commands or pushing, trace their scripts, hooks, workflow
26
+ triggers, reusable workflows, and deployment commands. A dry-run flag alone is
27
+ insufficient: inspect tasks, provisioners, plugins, external programs, and actions
28
+ that can mutate despite preview mode. Avoid such a preview unless its effects
29
+ are proven read-only. Do not run destroy/delete, taint/replace, state removal,
30
+ backend migration, forced database reset, or an automatic "repair" that bypasses
31
+ the rule. Do not hide destruction with targeting, disabled refresh, ignored
32
+ changes, new resource identities, or weakened policy.
33
+
34
+ Preview with the proposed versions and complete configuration for each affected
35
+ environment, using its real backend/workspace/account, variables, inventory, and
36
+ resource identities. An empty local state, mocked credentials, validate-only run,
37
+ or bundle build cannot establish safety for production. Check the current baseline
38
+ as well when needed to distinguish existing drift from the upgrade. Keep state,
39
+ binary plans, JSON plans, and secret-bearing diffs out of Git and public reports.
40
+
41
+ If a preview proposes deletion or replacement, fails, is incomplete, omits
42
+ affected resources, or cannot establish safety, skip that dependency and its
43
+ coupled migrations. Restore only this task's edits to the affected compatibility
44
+ group, retain independent safe upgrades, and record the reason and affected
45
+ resources. Never apply to find out. A preview is not deployment authorization.
46
+
47
+ Before an authorized apply/deploy, verify the exact validated commit, toolchain,
48
+ configuration, environment, and current state still match the preview. Re-preview
49
+ after relevant changes or drift. CI must enforce the same order and stop on an
50
+ unsafe preview before mutation; a successful local preview does not excuse an
51
+ unguarded push- or merge-triggered deployment. Establish a narrowly scoped guard
52
+ when authorized and reviewable, or hold the affected upgrade before triggering
53
+ that workflow. Do not disable protections to ship. A plan must be inspected before
54
+ the operation; inspecting deployment logs afterward is too late.
55
+ An unsafe workflow triggered on every branch push also blocks pushing otherwise
56
+ independent upgrades until that trigger has a proven safe guard.
57
+
58
+ ### Terraform and OpenTofu
59
+
60
+ Read core, provider, and module upgrade guides, including intermediate major
61
+ migrations. Regenerate provider locks with the pinned CLI, preserving supported
62
+ platform hashes. Backend initialization/migration is also subject to the safety
63
+ gate. Inspect external data programs and provisioners before planning.
64
+
65
+ Use a full refreshed saved plan with locking and the real variables, for example
66
+ `terraform plan -input=false -out=<private-plan>`; if using `-detailed-exitcode`,
67
+ 0 and 2 are success, 1 is failure. Inspect `terraform show -json <private-plan>`.
68
+ Run `agent-tool dependencies check-terraform-plan <private-plan.json>` to check
69
+ supported JSON resource actions. It fails for every action containing `delete`,
70
+ including both replacement orders, unknown actions, incomplete/deferred plans,
71
+ failed checks, or opaque provider action invocations. It prints actions and
72
+ diagnostics without resource attribute values; it does not prove that a plan is
73
+ fresh, comprehensive, authentic, or that provider code has no hidden effects.
74
+ Older/unsupported plan formats that cannot establish completeness are skipped.
75
+ OpenTofu currently omits `complete` in its JSON format and is rejected by this
76
+ helper; report that limitation rather than adding a synthetic completeness flag.
77
+ Also inspect resource identity, drift, warnings, provider semantics, and any
78
+ effects outside resource actions. For an authorized apply, consume the exact
79
+ approved saved plan, not an uninspected newly generated plan.
80
+
81
+ Source: [Terraform JSON plan format](https://developer.hashicorp.com/terraform/internals/json-format).
82
+
83
+ ### Wrangler and other deployment tools
84
+
85
+ Use the proposed pinned Wrangler for `wrangler deploy --dry-run` with the same
86
+ build/configuration/environment used by deployment. This checks bundling, not a
87
+ complete remote resource plan. Separately verify account, worker name, routes,
88
+ bindings and resource IDs against the existing deployment and read migration
89
+ semantics. Review Durable Object deleted/renamed classes, D1 schema/data
90
+ migrations, storage resources, queues, containers, and deployment integrations.
91
+ Never infer "no destruction" from a successful bundle. Resource removals,
92
+ replacement, or unprovable remote effects skip the affected group. Apply the
93
+ equivalent preview and identity checks to other tools rather than assuming they
94
+ share Terraform's guarantees.
95
+
96
+ Source: [Wrangler command documentation](https://developers.cloudflare.com/workers/wrangler/commands/).
97
+
98
+ ### Ansible
99
+
100
+ Check controller Python, ansible-core, collections/roles, and target Python support
101
+ together; read porting guides. Inspect all tasks and invoked scripts before
102
+ `ansible-playbook --check --diff` with the real inventory and variables.
103
+ `check_mode: false` forces mutation even with `--check`; unsupported modules and
104
+ conditions based on registered results can omit effects. Inspect deletion,
105
+ recreation, service/data reset, and package removal semantics beyond the diff.
106
+ Skipped or opaque tasks are a safety gap, not a clean preview. Redact diffs.
107
+
108
+ Source: [Ansible check mode limits](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_checkmode.html).
109
+
110
+ ## Inventory and choose compatible targets
111
+
112
+ Record each dependency source, current resolved version, latest stable candidate,
113
+ selected target, compatibility constraints, upstream migration links, and checks.
114
+ Query authoritative registries, official releases, support matrices, and migration
115
+ guides at execution time; do not treat remembered versions or prereleases as
116
+ latest stable. Inspect runtime requirements, peer dependencies, ABI/API changes,
117
+ platform availability, maintained release lines, and changed defaults, including
118
+ security, privacy/telemetry, data retention, and postinstall/native behavior.
119
+ Check transitive dependencies' peer/runtime constraints as well as direct ones. Unsupported
120
+ or unpublished targets stay pinned with an explanation.
121
+
122
+ Inventory manifests, locks, tool configuration, CI/reusable actions, containers,
123
+ scripts, deployment files, and executable documentation. Search duplicate version
124
+ pins and aliases, not just filenames; exclude vendored/generated snapshots unless
125
+ the repository owns their update process. Examples of sources to inspect:
126
+
127
+ | Ecosystem | Sources and compatibility group |
128
+ | --- | --- |
129
+ | JavaScript/TypeScript | All package.json dependency fields, overrides/resolutions, workspace catalogs, packageManager/engines, npm/Bun/pnpm/Yarn locks, Node/Bun pins, compiler and lint/build/test plugins |
130
+ | Toolchains | mise.toml/.mise.toml, mise.lock, .tool-versions, runtime version files, tool URLs/checksums, CI pins and container images; include every tool declared by mise, not just mise itself |
131
+ | Python/Ansible | pyproject/requirements, uv/Poetry/pip locks, Python pins, ansible-core, collection/role requirements, lint plugins and managed host constraints |
132
+ | Ruby/iOS | .ruby-version, Gemfile/lock, Bundler, CocoaPods, Podfile/lock, podspecs, Swift packages and Xcode/iOS deployment requirements |
133
+ | Infrastructure | Terraform/OpenTofu CLI constraints, provider locks/constraints, module versions/Git refs, Wrangler and Cloudflare integration dependencies |
134
+ | CI/system/native | Actions/reusable workflow refs, Docker tags/digests, OS images/packages, Rust/Cargo toolchain and locks, Go modules, Gradle/AGP/Kotlin/JDK and other repository-specific manifests |
135
+
136
+ Build connected compatibility groups before editing. Select the newest supported
137
+ combination, which can be older than an individual package's latest. Upgrade the
138
+ framework first or keep its compatible toolchain pinned; do not force peer
139
+ resolution, drop compatibility checks, or add blanket overrides to make installs
140
+ pass. Preserve source pin policy (exact version, digest, full Git SHA) and resolve
141
+ immutable pins from verified upstream releases. Preserve intentional runtime
142
+ minimums unless migration requires changing the support contract.
143
+
144
+ For React Native, use its version-specific upgrade guide/template diffs and
145
+ repository upgrade skills. Couple React and its renderer, native modules,
146
+ Metro/Babel, CLI, codegen, Hermes/New Architecture, CocoaPods/Bundler/Ruby,
147
+ Xcode/iOS deployment targets, and Android Gradle/AGP/Kotlin/JDK/SDK/NDK. For Expo,
148
+ use the chosen SDK's supported React Native/dependency matrix and its dependency
149
+ checks. Do not independently update framework-managed pods or generated native
150
+ files; use the framework's regeneration workflow and prove iOS/Android builds.
151
+ Permissive peer ranges alone do not prove native renderer or toolchain compatibility;
152
+ check the framework's actual supported versions and native implementation.
153
+ An unavailable native toolchain is a reported validation limit; do not claim the
154
+ group verified. See [React Native upgrading](https://reactnative.dev/docs/upgrading).
155
+
156
+ Check developer tools' own dependencies too: a compiler release without its old
157
+ programmatic API may break test/lint runners that import it even when `tsc` passes.
158
+ Action runtime upgrades can require newer self-hosted runners; preserve workflow
159
+ permissions, action inputs/outputs, caching, artifact semantics, and job names.
160
+
161
+ Check current advisories with read-only ecosystem audits against the baseline and
162
+ proposed lockfiles, using the repository's configured registry/advisory sources.
163
+ Do not send private dependency metadata to an unconfigured or unauthorized service;
164
+ report the resulting coverage limit. Cover direct and resolved transitive dependencies,
165
+ including native/platform-specific binaries. Latest direct packages can still
166
+ pin vulnerable transitive versions. Trace each finding to its owning dependency,
167
+ verify affected versions, runtime conditions, and remediation in the maintainer's
168
+ advisory and fixed release. When the owner's range permits a fixed version, use a
169
+ targeted refresh with the lockfile's owning manager and preserve unrelated
170
+ resolutions; otherwise prefer a supported owner upgrade.
171
+ If the owner still pins a vulnerable version, a narrowly scoped security patch
172
+ override may be used only with API/ABI/runtime compatibility evidence and focused
173
+ regression checks of the actual affected integration. Document its rationale and
174
+ removal condition; a passing install or bundle alone is insufficient. Do not run
175
+ blind audit fixes or use blanket overrides to bypass compatibility constraints.
176
+ Report unresolved advisories and unavailable audit coverage accurately; current
177
+ direct pins or a partial clean audit do not
178
+ establish that the whole dependency graph is safe.
179
+
180
+ ## Migrate and validate
181
+
182
+ Capture relevant baseline checks and warnings first, after screening their side
183
+ effects. Update one compatibility group at a time. Read the upstream instructions
184
+ for every breaking step; implement required source/config/schema migrations and
185
+ removed API replacements. Use the manager that owns each lockfile, on the selected
186
+ runtime, and inspect the resulting diff. Refresh transitive dependencies through
187
+ their owning direct dependency; do not hand-edit integrity hashes or lock entries.
188
+ Review intentional patches, overrides, and pins before removal; retain them until
189
+ upstream fixes and regression checks demonstrate they are unnecessary.
190
+ Avoid unrelated global installations or environment changes. Use task-local or
191
+ repository-scoped tools for testing changed runtime pins.
192
+ For published packages, also validate tarballs and consumer installs with their
193
+ documented supported package managers, including manifest/override syntax and
194
+ runtime requirements. A successful owning-manager install does not prove that
195
+ published metadata works for those consumers.
196
+
197
+ Run the repository's setup/hooks, frozen installs, lint, typechecks, tests, builds,
198
+ package/platform smoke checks, and relevant safe integration previews. Resolve
199
+ upgrade-related deprecation warnings at their source. Do not suppress warnings,
200
+ weaken tests, or disable checks; report remaining upstream/pre-existing warnings
201
+ with evidence and keep a target pinned when migration cannot be completed.
202
+ Add focused regression coverage when a migration changes behavior; avoid tests
203
+ that merely assert version strings. Re-run checks after repairs. Report checks
204
+ blocked by missing credentials, platforms, or external services accurately.
205
+ Re-audit the final resolved graph after migrations and repairs.
206
+
207
+ Confirm every inventory row is upgraded, already current, intentionally constrained,
208
+ or skipped with a concrete reason. Do not present an unresolved group as complete.
209
+ When upgrading agent-tool itself, install its managed skills with the same harness
210
+ scope and commit the ownership manifest; never edit generated managed skill copies.
211
+
212
+ When changes depend on another repository's release, ship the producer first and
213
+ confirm publishing and registry availability before pinning the consumer; include
214
+ its exact version/commit and API migration notes. Independent groups may proceed.
215
+ Use the installed `ship-pr` for authorized shipping and independently review every
216
+ changed HEAD. Check deployment safety again before its push/open/merge operations.
217
+
218
+ ## Report
219
+
220
+ Keep a compact ledger of old/selected versions, completed migrations, validation
221
+ and warning results, preview environment/commit/tool versions and safe action
222
+ summary, and every skipped/constrained dependency with the reason. Include baseline
223
+ and final audit results, security overrides with their rationale and removal
224
+ condition, unresolved advisories, and audit coverage limits. For shipping,
225
+ include branch, reviewed commit/verdict, PR, merge, publish/deploy result, and final
226
+ checkout state. For a repository sweep, keep one entry per repository, including
227
+ unready or unchanged checkouts. Do not publish credentials, state, or plan contents.
package/src/cli.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { execFileSync } from "node:child_process";
2
- import { writeFileSync, writeSync } from "node:fs";
2
+ import { readFileSync, writeFileSync, writeSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { DEFAULT_CONFIG, loadConfig } from "./config";
5
5
  import { doctor } from "./doctor";
6
+ import { checkTerraformPlan } from "./dependencies/terraformPlan";
6
7
  import { review } from "./harnesses";
7
8
  import { runAgentToolAction } from "./runAgentToolAction";
8
9
  import { BUNDLED_SKILLS } from "./skills/bundled";
@@ -23,6 +24,7 @@ Usage: agent-tool [--repo <directory>] [--config <file>] <command>
23
24
  pr merge <subject-or-empty> <head-oid> <base-branch>
24
25
  versions <plan|bump|check|prepare> <base-oid>
25
26
  versions resolve-conflicts
27
+ dependencies check-terraform-plan <json-file> Read-only destructive-plan check
26
28
  skills list
27
29
  skills install [--harness <all|claude|codex|opencode>] [--apply]
28
30
  skills check [--harness <all|claude|codex|opencode>]
@@ -58,6 +60,14 @@ export function main(argv = process.argv.slice(2)): number {
58
60
  writeSync(1, HELP); return 0;
59
61
  }
60
62
  if (command === "--version") { usage(args.length === 0, "Unexpected version arguments."); writeSync(1, `${metadata.version}\n`); return 0; }
63
+ if (command === "dependencies") {
64
+ usage(args.length === 2 && args[0] === "check-terraform-plan", "Usage: agent-tool dependencies check-terraform-plan <json-file>");
65
+ const source = readFileSync(args[1], "utf8");
66
+ let plan: unknown;
67
+ try { plan = JSON.parse(source); } catch { throw new Error("Invalid Terraform plan JSON."); }
68
+ const result = checkTerraformPlan(plan);
69
+ output(result); return result.ok ? 0 : 1;
70
+ }
61
71
  if (command === "init") {
62
72
  usage(args.length === 0, "Usage: agent-tool init");
63
73
  writeFileSync(path.join(process.cwd(), "agent-tool.json"), `${JSON.stringify(DEFAULT_CONFIG, null, 2)}\n`, { flag: "wx" });
@@ -0,0 +1,69 @@
1
+ /** Read-only screening of Terraform show -json output, not deployment approval. */
2
+ export interface TerraformPlanCheck {
3
+ ok: boolean;
4
+ issues: string[];
5
+ resourceActions: { address: string; actions: string[] }[];
6
+ }
7
+
8
+ function record(value: unknown): value is Record<string, unknown> {
9
+ return value !== null && typeof value === "object" && !Array.isArray(value);
10
+ }
11
+
12
+ export function checkTerraformPlan(value: unknown): TerraformPlanCheck {
13
+ const issues: string[] = [];
14
+ const resourceActions: TerraformPlanCheck["resourceActions"] = [];
15
+ const result = () => ({ ok: issues.length === 0, issues, resourceActions });
16
+ if (!record(value)) {
17
+ issues.push("Expected a Terraform JSON plan object.");
18
+ return result();
19
+ }
20
+ if (typeof value.format_version !== "string" || !/^1\.\d+$/.test(value.format_version)) issues.push("Unsupported or missing plan format_version; expected 1.x.");
21
+ if (typeof value.terraform_version !== "string" || !value.terraform_version) issues.push("Missing terraform_version.");
22
+ if (!record(value.planned_values) || !record(value.configuration)) issues.push("Expected saved-plan planned_values and configuration; state JSON is not a plan.");
23
+ if (value.complete !== true) issues.push("Plan completeness is not established (complete must be true).");
24
+ if (value.errored !== undefined && value.errored !== false) issues.push("Planning errored or errored is malformed.");
25
+
26
+ for (const name of ["deferred_changes", "action_invocations", "deferred_action_invocations"]) {
27
+ if (value[name] !== undefined && (!Array.isArray(value[name]) || value[name].length !== 0)) issues.push(`${name} contains unsupported or incomplete operations.`);
28
+ }
29
+
30
+ // Missing resource_changes is valid for a complete empty/output-only plan.
31
+ for (const name of ["resource_changes", "resource_drift"]) {
32
+ const changes = value[name];
33
+ if (changes === undefined) continue;
34
+ if (!Array.isArray(changes)) { issues.push(`${name} must be an array.`); continue; }
35
+ for (const [index, item] of changes.entries()) {
36
+ const label = record(item) && typeof item.address === "string" && item.address ? item.address : `${name}[${index}]`;
37
+ if (!record(item) || typeof item.address !== "string" || !item.address || (item.mode !== "managed" && item.mode !== "data") || !record(item.change)) {
38
+ issues.push(`${label}: malformed resource change.`);
39
+ continue;
40
+ }
41
+ const actions = item.change.actions;
42
+ if (!Array.isArray(actions) || actions.length === 0 || !actions.every((action): action is string => typeof action === "string")) {
43
+ issues.push(`${label}: missing or malformed actions.`);
44
+ continue;
45
+ }
46
+ if (name === "resource_changes") resourceActions.push({ address: item.address, actions });
47
+ if (actions.includes("delete")) issues.push(`${label}: deletion or replacement is forbidden.`);
48
+ else if (actions.length !== 1 || !["no-op", "create", "read", "update"].includes(actions[0])) issues.push(`${label}: unsupported resource action.`);
49
+ if (item.change.replace_paths !== undefined && (!Array.isArray(item.change.replace_paths) || item.change.replace_paths.length !== 0)) issues.push(`${label}: replacement paths are forbidden or malformed.`);
50
+ }
51
+ }
52
+
53
+ const checks = value.checks;
54
+ if (checks !== undefined) {
55
+ if (!Array.isArray(checks)) issues.push("checks must be an array.");
56
+ else for (const [index, check] of checks.entries()) {
57
+ if (record(check) && check.status === "unknown") issues.push(`checks[${index}]: condition cannot be established before apply; skip this upgrade.`);
58
+ else if (!record(check) || check.status !== "pass") issues.push(`checks[${index}]: check did not pass or is malformed.`);
59
+ if (record(check) && check.instances !== undefined) {
60
+ if (!Array.isArray(check.instances)) issues.push(`checks[${index}]: instances must be an array.`);
61
+ else for (const instance of check.instances) {
62
+ if (record(instance) && instance.status === "unknown") issues.push(`checks[${index}]: instance condition cannot be established before apply; skip this upgrade.`);
63
+ else if (!record(instance) || instance.status !== "pass") issues.push(`checks[${index}]: instance check did not pass or is malformed.`);
64
+ }
65
+ }
66
+ }
67
+ }
68
+ return result();
69
+ }
@@ -4,5 +4,6 @@ export const BUNDLED_SKILLS: Readonly<Record<string, string>> = {
4
4
  "open-pr": "---\nname: open-pr\ndescription: Open a GitHub pull request for the current feature branch with a validated title and a reviewable description.\n---\n\n# Open PR\n\nUse the installed `agent-tool`. Follow repository policy for validation, commits,\nand branch naming. Resolve the repository and default branch with Git and `gh`;\ndo not assume the branch is named main. Preserve unrelated work and finish\nrequested changes and appropriate checks before publishing them.\n\nEnsure the intended commit is on a feature branch and push it through the\nrepository's normal push workflow, naming the PR repository's remote and branch\nexplicitly, as in `git push -u origin HEAD`; a bare push follows whatever\nupstream the branch tracks, which may be a local branch. Respect existing push\nauthorization; do not bypass hooks. If the user only requested a draft\ndescription, prepare that text without publishing a PR.\n\nWrite a title that satisfies the data-only `agent-tool.json` subject policy.\nDescribe the concrete resulting behavior and relevant validation. Put multiline\nbody text in a temporary file, then run:\n\n```sh\nagent-tool pr open 'feat: describe the change' < /path/to/pr-body.txt\n```\n\nOmitting the title uses the latest commit subject. The tool refuses duplicate\nopen PRs and requires the branch to be pushed at its local HEAD. It does not\npush for you. Resolve a fork's head/base identities explicitly if the repository\nuses fork PRs; the initial open helper supports same-repository branches.\n\nReport the created PR link and any remaining validation or review work.\n",
5
5
  "reset": "---\nname: reset\ndescription: Return a clean checkout to its updated default branch after confirmed shipping or an explicit checkout cleanup request.\n---\n\n# Reset\n\nThis is checkout cleanup, not a destructive Git reset. Read repository guidance\nfor setup and hooks. Determine the default branch through the repository's\nremote metadata rather than assuming main.\n\nCheck worktree and index first. Preserve user edits; if they prevent switching,\nreport the condition instead of discarding or automatically stashing them. For\npost-merge cleanup, confirm the PR is MERGED before removing a feature branch.\n\nFetch the default branch, switch to it, and update by fast-forward only. A\ndiverged default branch requires inspection rather than forced replacement.\nDelete a merged feature branch only within the requested cleanup scope and\nafter verifying its exact commit is the shipped commit; squash merging does not\nmake Git ancestry-based branch deletion proof of shipping. Honor keep-branch.\n\nRun only the hook installer or dependency setup documented by this repository\nwhen the updated checkout requires it. Do not assume a package manager or copy\nsetup commands from another project. Report final branch, commit, and any\nunfinished cleanup.\n",
6
6
  "ship-pr": "---\nname: ship-pr\ndescription: Complete an authorized commit, independent review, repair, PR, CI, and squash-merge workflow for a GitHub repository.\n---\n\n# Ship PR\n\nUse the installed `agent-tool`. Read repository guidance and policy first;\nvalidation, versioning, package management, and hook installation remain project\nconcerns. Shipping authorizes this workflow within the user's stated scope.\nPreserve unrelated edits and honor requested stopping points such as open-PR\nonly, report-only review, or keeping the feature branch.\n\n1. Finish the requested change on a feature branch. Create it with\n `git switch --no-track -c <branch>`; settings such as\n `branch.autosetupmerge=always` otherwise make it track a local branch.\n Validate it with appropriate project checks and commit only the intended\n files.\n2. Determine the current PR's base repository and branch, or the repository's\n default branch when no PR exists. Fetch and pin its exact base. Integrate it\n using the repository's normal workflow, resolving conflicts without losing\n user work. If project policy versions packages, prepare versions against the\n pinned base after every integration or repair and before each review, using\n the project's commands or `agent-tool versions prepare <base-oid>`, which\n commits bumped manifests and the refreshed lockfile.\n `agent-tool versions resolve-conflicts` resolves conflicts confined to\n version fields. Review the resulting committed HEAD with an independent\n reviewer using the bundled cross-agent-review skill. Repair blocking findings\n and review every changed HEAD again. Review failure blocks shipping.\n3. Push the reviewed HEAD through normal hooks, then verify the pushed SHA still\n equals the reviewed SHA. If a hook changed content or HEAD, review that result.\n Open the PR using the open-pr skill, or update the existing PR as needed.\n4. Allow any configured review bots time to respond, then fetch unresolved PR\n review threads. Follow repository policy for feedback: fix valid findings,\n reply in the original threads, and resolve only fully addressed comments.\n Validate, commit, push, and independently review every feedback or CI repair.\n Wait for CI, recheck the live base, PR base branch, and local and remote heads.\n A changed base requires integration, validation, and another review. When\n versions are managed, `agent-tool versions check <base-oid>` must pass for the\n live base. Use the squash-merge skill to merge the exact reviewed HEAD.\n5. After confirmed MERGED, honor keep-branch; otherwise use the reset skill to\n return to the updated default branch and apply any documented project setup.\n6. When the project deploys the merged branch, verify the deployment for the\n merge commit and run its documented live checks. Report deployment failures\n separately from a successful merge; follow project policy for repairs.\n\nNever infer a clean review from process success alone or carry a review across\ncontent changes. Do not weaken configured CI policy to get a merge through.\nIf a step fails, preserve useful intermediate work and report the concrete\nremaining issue. Report PR link, merged commit, review verdict, validation, and\nfinal checkout state.\n",
7
- "squash-merge": "---\nname: squash-merge\ndescription: Squash-merge a reviewed GitHub pull request while binding the mutation to the reviewed commit and checking CI.\n---\n\n# Squash Merge\n\nUse the installed `agent-tool` and repository merge policy. Merge only within\nthe user's authorized scope. Have the exact reviewed HEAD, base commit, and base\nbranch available, and read the verdict and unresolved findings. BLOCKER or MAJOR\nfindings require repair and a new review before merging.\nFollow the repository's review-bot policy and require all actionable blocking\nreview feedback to be addressed before merging.\n\nWait for required checks using `gh pr checks --watch --fail-fast`. Refresh the\nPR and live base from the repository that owns the PR. Confirm local HEAD and PR\nHEAD both equal the reviewed commit, the base branch is unchanged, the live base\nstill equals the reviewed base, and that base is an ancestor of reviewed HEAD.\nIf any identity changed, refresh, validate, and review again.\n\nUse a title that satisfies `agent-tool.json` and pass both reviewed HEAD and\nbase branch:\n\n```sh\nagent-tool pr merge 'feat: describe the change' <reviewed-head-oid> <base-branch>\n```\n\nThe helper enforces HEAD atomically through GitHub's `expectedHeadOid`, checks\nCI, rejects queued or automatic merges, and creates a subject-only squash with\nthe PR reference. GitHub has no atomic expected-base argument in this mutation;\nrepository branch protection must enforce any required base freshness. With\n`merge.requireStrictBaseFreshness`, the helper refuses to merge unless an\nactive strict status ruleset that the actor cannot bypass protects the base. Do\nnot retarget the PR concurrently with merging.\n\nConfirm GitHub reports MERGED before any cleanup. A failed or uncertain mutation\nis not evidence of a merge. Preserve the branch on failure. After a confirmed\nmerge, perform checkout cleanup only when requested or as part of an authorized\nshipping workflow. Report the PR and merge result.\n"
7
+ "squash-merge": "---\nname: squash-merge\ndescription: Squash-merge a reviewed GitHub pull request while binding the mutation to the reviewed commit and checking CI.\n---\n\n# Squash Merge\n\nUse the installed `agent-tool` and repository merge policy. Merge only within\nthe user's authorized scope. Have the exact reviewed HEAD, base commit, and base\nbranch available, and read the verdict and unresolved findings. BLOCKER or MAJOR\nfindings require repair and a new review before merging.\nFollow the repository's review-bot policy and require all actionable blocking\nreview feedback to be addressed before merging.\n\nWait for required checks using `gh pr checks --watch --fail-fast`. Refresh the\nPR and live base from the repository that owns the PR. Confirm local HEAD and PR\nHEAD both equal the reviewed commit, the base branch is unchanged, the live base\nstill equals the reviewed base, and that base is an ancestor of reviewed HEAD.\nIf any identity changed, refresh, validate, and review again.\n\nUse a title that satisfies `agent-tool.json` and pass both reviewed HEAD and\nbase branch:\n\n```sh\nagent-tool pr merge 'feat: describe the change' <reviewed-head-oid> <base-branch>\n```\n\nThe helper enforces HEAD atomically through GitHub's `expectedHeadOid`, checks\nCI, rejects queued or automatic merges, and creates a subject-only squash with\nthe PR reference. GitHub has no atomic expected-base argument in this mutation;\nrepository branch protection must enforce any required base freshness. With\n`merge.requireStrictBaseFreshness`, the helper refuses to merge unless an\nactive strict status ruleset that the actor cannot bypass protects the base. Do\nnot retarget the PR concurrently with merging.\n\nConfirm GitHub reports MERGED before any cleanup. A failed or uncertain mutation\nis not evidence of a merge. Preserve the branch on failure. After a confirmed\nmerge, perform checkout cleanup only when requested or as part of an authorized\nshipping workflow. Report the PR and merge result.\n",
8
+ "update-dependencies": "---\nname: update-dependencies\ndescription: Update a repository's dependency manifests, lockfiles, runtimes, tools, and CI pins with upstream migrations, compatibility checks, and non-destructive infrastructure previews.\n---\n\n# Update Dependencies\n\nUse the repository's package managers and installed `agent-tool`. Read `AGENTS.md`,\nREADME, linked setup/release documents, and repository-owned upgrade skills before\nediting. Follow their hooks, version policy, checks, and requested stopping point.\nUpdate applicable dependencies throughout the repository, including workspaces\nand examples. This skill does not authorize deploying, merging, publishing, or\nchanging global tools; use existing user authorization and `ship-pr` when shipping\nis requested. Default dependency-update merge subject: `chore: update dependencies`,\nsubject to repository title policy and any explicit user subject.\n\n## Safety before execution\n\n**Always dry-run before any infrastructure apply or deployment. Never destroy,\nreplace, or recreate a resource during an upgrade.** This includes Terraform,\nOpenTofu, Wrangler, Ansible, other infrastructure tools, and effects triggered by\ninstallation, tests, hooks, Git push, opening a PR, CI, or merging. Greenfield\nprojects and approval to ship do not waive this rule.\n\nBefore running project commands or pushing, trace their scripts, hooks, workflow\ntriggers, reusable workflows, and deployment commands. A dry-run flag alone is\ninsufficient: inspect tasks, provisioners, plugins, external programs, and actions\nthat can mutate despite preview mode. Avoid such a preview unless its effects\nare proven read-only. Do not run destroy/delete, taint/replace, state removal,\nbackend migration, forced database reset, or an automatic \"repair\" that bypasses\nthe rule. Do not hide destruction with targeting, disabled refresh, ignored\nchanges, new resource identities, or weakened policy.\n\nPreview with the proposed versions and complete configuration for each affected\nenvironment, using its real backend/workspace/account, variables, inventory, and\nresource identities. An empty local state, mocked credentials, validate-only run,\nor bundle build cannot establish safety for production. Check the current baseline\nas well when needed to distinguish existing drift from the upgrade. Keep state,\nbinary plans, JSON plans, and secret-bearing diffs out of Git and public reports.\n\nIf a preview proposes deletion or replacement, fails, is incomplete, omits\naffected resources, or cannot establish safety, skip that dependency and its\ncoupled migrations. Restore only this task's edits to the affected compatibility\ngroup, retain independent safe upgrades, and record the reason and affected\nresources. Never apply to find out. A preview is not deployment authorization.\n\nBefore an authorized apply/deploy, verify the exact validated commit, toolchain,\nconfiguration, environment, and current state still match the preview. Re-preview\nafter relevant changes or drift. CI must enforce the same order and stop on an\nunsafe preview before mutation; a successful local preview does not excuse an\nunguarded push- or merge-triggered deployment. Establish a narrowly scoped guard\nwhen authorized and reviewable, or hold the affected upgrade before triggering\nthat workflow. Do not disable protections to ship. A plan must be inspected before\nthe operation; inspecting deployment logs afterward is too late.\nAn unsafe workflow triggered on every branch push also blocks pushing otherwise\nindependent upgrades until that trigger has a proven safe guard.\n\n### Terraform and OpenTofu\n\nRead core, provider, and module upgrade guides, including intermediate major\nmigrations. Regenerate provider locks with the pinned CLI, preserving supported\nplatform hashes. Backend initialization/migration is also subject to the safety\ngate. Inspect external data programs and provisioners before planning.\n\nUse a full refreshed saved plan with locking and the real variables, for example\n`terraform plan -input=false -out=<private-plan>`; if using `-detailed-exitcode`,\n0 and 2 are success, 1 is failure. Inspect `terraform show -json <private-plan>`.\nRun `agent-tool dependencies check-terraform-plan <private-plan.json>` to check\nsupported JSON resource actions. It fails for every action containing `delete`,\nincluding both replacement orders, unknown actions, incomplete/deferred plans,\nfailed checks, or opaque provider action invocations. It prints actions and\ndiagnostics without resource attribute values; it does not prove that a plan is\nfresh, comprehensive, authentic, or that provider code has no hidden effects.\nOlder/unsupported plan formats that cannot establish completeness are skipped.\nOpenTofu currently omits `complete` in its JSON format and is rejected by this\nhelper; report that limitation rather than adding a synthetic completeness flag.\nAlso inspect resource identity, drift, warnings, provider semantics, and any\neffects outside resource actions. For an authorized apply, consume the exact\napproved saved plan, not an uninspected newly generated plan.\n\nSource: [Terraform JSON plan format](https://developer.hashicorp.com/terraform/internals/json-format).\n\n### Wrangler and other deployment tools\n\nUse the proposed pinned Wrangler for `wrangler deploy --dry-run` with the same\nbuild/configuration/environment used by deployment. This checks bundling, not a\ncomplete remote resource plan. Separately verify account, worker name, routes,\nbindings and resource IDs against the existing deployment and read migration\nsemantics. Review Durable Object deleted/renamed classes, D1 schema/data\nmigrations, storage resources, queues, containers, and deployment integrations.\nNever infer \"no destruction\" from a successful bundle. Resource removals,\nreplacement, or unprovable remote effects skip the affected group. Apply the\nequivalent preview and identity checks to other tools rather than assuming they\nshare Terraform's guarantees.\n\nSource: [Wrangler command documentation](https://developers.cloudflare.com/workers/wrangler/commands/).\n\n### Ansible\n\nCheck controller Python, ansible-core, collections/roles, and target Python support\ntogether; read porting guides. Inspect all tasks and invoked scripts before\n`ansible-playbook --check --diff` with the real inventory and variables.\n`check_mode: false` forces mutation even with `--check`; unsupported modules and\nconditions based on registered results can omit effects. Inspect deletion,\nrecreation, service/data reset, and package removal semantics beyond the diff.\nSkipped or opaque tasks are a safety gap, not a clean preview. Redact diffs.\n\nSource: [Ansible check mode limits](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_checkmode.html).\n\n## Inventory and choose compatible targets\n\nRecord each dependency source, current resolved version, latest stable candidate,\nselected target, compatibility constraints, upstream migration links, and checks.\nQuery authoritative registries, official releases, support matrices, and migration\nguides at execution time; do not treat remembered versions or prereleases as\nlatest stable. Inspect runtime requirements, peer dependencies, ABI/API changes,\nplatform availability, maintained release lines, and changed defaults, including\nsecurity, privacy/telemetry, data retention, and postinstall/native behavior.\nCheck transitive dependencies' peer/runtime constraints as well as direct ones. Unsupported\nor unpublished targets stay pinned with an explanation.\n\nInventory manifests, locks, tool configuration, CI/reusable actions, containers,\nscripts, deployment files, and executable documentation. Search duplicate version\npins and aliases, not just filenames; exclude vendored/generated snapshots unless\nthe repository owns their update process. Examples of sources to inspect:\n\n| Ecosystem | Sources and compatibility group |\n| --- | --- |\n| JavaScript/TypeScript | All package.json dependency fields, overrides/resolutions, workspace catalogs, packageManager/engines, npm/Bun/pnpm/Yarn locks, Node/Bun pins, compiler and lint/build/test plugins |\n| Toolchains | mise.toml/.mise.toml, mise.lock, .tool-versions, runtime version files, tool URLs/checksums, CI pins and container images; include every tool declared by mise, not just mise itself |\n| Python/Ansible | pyproject/requirements, uv/Poetry/pip locks, Python pins, ansible-core, collection/role requirements, lint plugins and managed host constraints |\n| Ruby/iOS | .ruby-version, Gemfile/lock, Bundler, CocoaPods, Podfile/lock, podspecs, Swift packages and Xcode/iOS deployment requirements |\n| Infrastructure | Terraform/OpenTofu CLI constraints, provider locks/constraints, module versions/Git refs, Wrangler and Cloudflare integration dependencies |\n| CI/system/native | Actions/reusable workflow refs, Docker tags/digests, OS images/packages, Rust/Cargo toolchain and locks, Go modules, Gradle/AGP/Kotlin/JDK and other repository-specific manifests |\n\nBuild connected compatibility groups before editing. Select the newest supported\ncombination, which can be older than an individual package's latest. Upgrade the\nframework first or keep its compatible toolchain pinned; do not force peer\nresolution, drop compatibility checks, or add blanket overrides to make installs\npass. Preserve source pin policy (exact version, digest, full Git SHA) and resolve\nimmutable pins from verified upstream releases. Preserve intentional runtime\nminimums unless migration requires changing the support contract.\n\nFor React Native, use its version-specific upgrade guide/template diffs and\nrepository upgrade skills. Couple React and its renderer, native modules,\nMetro/Babel, CLI, codegen, Hermes/New Architecture, CocoaPods/Bundler/Ruby,\nXcode/iOS deployment targets, and Android Gradle/AGP/Kotlin/JDK/SDK/NDK. For Expo,\nuse the chosen SDK's supported React Native/dependency matrix and its dependency\nchecks. Do not independently update framework-managed pods or generated native\nfiles; use the framework's regeneration workflow and prove iOS/Android builds.\nPermissive peer ranges alone do not prove native renderer or toolchain compatibility;\ncheck the framework's actual supported versions and native implementation.\nAn unavailable native toolchain is a reported validation limit; do not claim the\ngroup verified. See [React Native upgrading](https://reactnative.dev/docs/upgrading).\n\nCheck developer tools' own dependencies too: a compiler release without its old\nprogrammatic API may break test/lint runners that import it even when `tsc` passes.\nAction runtime upgrades can require newer self-hosted runners; preserve workflow\npermissions, action inputs/outputs, caching, artifact semantics, and job names.\n\nCheck current advisories with read-only ecosystem audits against the baseline and\nproposed lockfiles, using the repository's configured registry/advisory sources.\nDo not send private dependency metadata to an unconfigured or unauthorized service;\nreport the resulting coverage limit. Cover direct and resolved transitive dependencies,\nincluding native/platform-specific binaries. Latest direct packages can still\npin vulnerable transitive versions. Trace each finding to its owning dependency,\nverify affected versions, runtime conditions, and remediation in the maintainer's\nadvisory and fixed release. When the owner's range permits a fixed version, use a\ntargeted refresh with the lockfile's owning manager and preserve unrelated\nresolutions; otherwise prefer a supported owner upgrade.\nIf the owner still pins a vulnerable version, a narrowly scoped security patch\noverride may be used only with API/ABI/runtime compatibility evidence and focused\nregression checks of the actual affected integration. Document its rationale and\nremoval condition; a passing install or bundle alone is insufficient. Do not run\nblind audit fixes or use blanket overrides to bypass compatibility constraints.\nReport unresolved advisories and unavailable audit coverage accurately; current\ndirect pins or a partial clean audit do not\nestablish that the whole dependency graph is safe.\n\n## Migrate and validate\n\nCapture relevant baseline checks and warnings first, after screening their side\neffects. Update one compatibility group at a time. Read the upstream instructions\nfor every breaking step; implement required source/config/schema migrations and\nremoved API replacements. Use the manager that owns each lockfile, on the selected\nruntime, and inspect the resulting diff. Refresh transitive dependencies through\ntheir owning direct dependency; do not hand-edit integrity hashes or lock entries.\nReview intentional patches, overrides, and pins before removal; retain them until\nupstream fixes and regression checks demonstrate they are unnecessary.\nAvoid unrelated global installations or environment changes. Use task-local or\nrepository-scoped tools for testing changed runtime pins.\nFor published packages, also validate tarballs and consumer installs with their\ndocumented supported package managers, including manifest/override syntax and\nruntime requirements. A successful owning-manager install does not prove that\npublished metadata works for those consumers.\n\nRun the repository's setup/hooks, frozen installs, lint, typechecks, tests, builds,\npackage/platform smoke checks, and relevant safe integration previews. Resolve\nupgrade-related deprecation warnings at their source. Do not suppress warnings,\nweaken tests, or disable checks; report remaining upstream/pre-existing warnings\nwith evidence and keep a target pinned when migration cannot be completed.\nAdd focused regression coverage when a migration changes behavior; avoid tests\nthat merely assert version strings. Re-run checks after repairs. Report checks\nblocked by missing credentials, platforms, or external services accurately.\nRe-audit the final resolved graph after migrations and repairs.\n\nConfirm every inventory row is upgraded, already current, intentionally constrained,\nor skipped with a concrete reason. Do not present an unresolved group as complete.\nWhen upgrading agent-tool itself, install its managed skills with the same harness\nscope and commit the ownership manifest; never edit generated managed skill copies.\n\nWhen changes depend on another repository's release, ship the producer first and\nconfirm publishing and registry availability before pinning the consumer; include\nits exact version/commit and API migration notes. Independent groups may proceed.\nUse the installed `ship-pr` for authorized shipping and independently review every\nchanged HEAD. Check deployment safety again before its push/open/merge operations.\n\n## Report\n\nKeep a compact ledger of old/selected versions, completed migrations, validation\nand warning results, preview environment/commit/tool versions and safe action\nsummary, and every skipped/constrained dependency with the reason. Include baseline\nand final audit results, security overrides with their rationale and removal\ncondition, unresolved advisories, and audit coverage limits. For shipping,\ninclude branch, reviewed commit/verdict, PR, merge, publish/deploy result, and final\ncheckout state. For a repository sweep, keep one entry per repository, including\nunready or unchanged checkouts. Do not publish credentials, state, or plan contents.\n"
8
9
  };