crewforth 0.0.1 → 3.0.0-rc.1
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/CHANGELOG.md +3784 -0
- package/LICENSE +21 -0
- package/README.md +37 -7
- package/VERSION +1 -0
- package/adopt.sh +1670 -0
- package/bin/cli.js +497 -0
- package/kit/AGENT_TEMPLATE.md +92 -0
- package/kit/CLAUDE.md +228 -0
- package/kit/README.md +107 -0
- package/kit/agents/crew-backend-expert.md +95 -0
- package/kit/agents/crew-commit-agent.md +96 -0
- package/kit/agents/crew-database-expert.md +84 -0
- package/kit/agents/crew-devops-expert.md +82 -0
- package/kit/agents/crew-frontend-expert.md +85 -0
- package/kit/agents/crew-performance-expert.md +78 -0
- package/kit/agents/crew-planner.md +60 -0
- package/kit/agents/crew-privacy-agent.md +71 -0
- package/kit/agents/crew-review-agent.md +95 -0
- package/kit/agents/crew-security-expert.md +73 -0
- package/kit/agents/crew-session-manager.md +77 -0
- package/kit/agents/crew-test-expert.md +64 -0
- package/kit/eval/doctor.sh +465 -0
- package/kit/eval/gate-report.sh +137 -0
- package/kit/eval/golden-holdout.txt +53 -0
- package/kit/eval/golden-routing.txt +199 -0
- package/kit/eval/lib/crew-env.sh +14 -0
- package/kit/eval/lib/settings-json.awk +154 -0
- package/kit/eval/lib/star.sh +70 -0
- package/kit/eval/parser-conformance.sh +612 -0
- package/kit/eval/preflight.sh +186 -0
- package/kit/eval/routing-eval.sh +209 -0
- package/kit/eval/scan-skill.sh +162 -0
- package/kit/eval/smoke-test.sh +6693 -0
- package/kit/eval/update-guard.sh +81 -0
- package/kit/eval/utilization.sh +130 -0
- package/kit/hooks/board-sync.sh +106 -0
- package/kit/hooks/board.sh +962 -0
- package/kit/hooks/commit-msg +49 -0
- package/kit/hooks/context-usage.sh +353 -0
- package/kit/hooks/floor-blocklist.txt +124 -0
- package/kit/hooks/guard-bash.sh +1431 -0
- package/kit/hooks/guard-commit-scan.sh +511 -0
- package/kit/hooks/guard-write.sh +513 -0
- package/kit/hooks/pre-commit +577 -0
- package/kit/hooks/route-hint.sh +185 -0
- package/kit/hooks/secret-blocklist.txt +82 -0
- package/kit/hooks/session-guard.sh +149 -0
- package/kit/hooks/session-rehydrate.sh +41 -0
- package/kit/hooks/session-stats.sh +252 -0
- package/kit/hooks/session-update-check.sh +236 -0
- package/kit/hooks/skill-trust.sh +150 -0
- package/kit/hooks/trace-blocklist.txt +53 -0
- package/kit/settings.json +132 -0
- package/kit/skills/a11y/SKILL.md +44 -0
- package/kit/skills/adr/SKILL.md +74 -0
- package/kit/skills/api-design/SKILL.md +50 -0
- package/kit/skills/automode-policy/SKILL.md +91 -0
- package/kit/skills/automode-policy/references/policy.json +13 -0
- package/kit/skills/automode-policy/scripts/apply.sh +84 -0
- package/kit/skills/automode-policy/scripts/check.sh +102 -0
- package/kit/skills/backend-architecture/SKILL.md +103 -0
- package/kit/skills/brainstorm/SKILL.md +47 -0
- package/kit/skills/ci-pipeline/SKILL.md +38 -0
- package/kit/skills/commit-message/SKILL.md +82 -0
- package/kit/skills/commit-message/references/examples.md +8 -0
- package/kit/skills/confidence-check/SKILL.md +86 -0
- package/kit/skills/crew-board/SKILL.md +55 -0
- package/kit/skills/crew-brainstorm/SKILL.md +19 -0
- package/kit/skills/crew-code-review/SKILL.md +151 -0
- package/kit/skills/crew-code-review/references/history-search.md +19 -0
- package/kit/skills/crew-code-review/references/panel-mode.md +32 -0
- package/kit/skills/crew-code-review/references/receiving.md +23 -0
- package/kit/skills/crew-doctor/SKILL.md +36 -0
- package/kit/skills/crew-gates/SKILL.md +25 -0
- package/kit/skills/crew-handoff/SKILL.md +14 -0
- package/kit/skills/crew-plan/SKILL.md +18 -0
- package/kit/skills/crew-review/SKILL.md +23 -0
- package/kit/skills/crew-ship/SKILL.md +19 -0
- package/kit/skills/crew-skill/SKILL.md +61 -0
- package/kit/skills/crew-studio/SKILL.md +129 -0
- package/kit/skills/crew-update/SKILL.md +43 -0
- package/kit/skills/db-migration/SKILL.md +120 -0
- package/kit/skills/db-migration/references/backup-restore.md +33 -0
- package/kit/skills/db-migration/references/raw-sql.md +30 -0
- package/kit/skills/db-migration/references/tool-matrix.md +35 -0
- package/kit/skills/dependency-audit/SKILL.md +61 -0
- package/kit/skills/dependency-upgrade/SKILL.md +82 -0
- package/kit/skills/deploy/SKILL.md +100 -0
- package/kit/skills/deploy/references/proxy-ssl.md +56 -0
- package/kit/skills/deploy/references/rollback.md +18 -0
- package/kit/skills/deploy/references/scripts.md +9 -0
- package/kit/skills/deploy/references/topology-a.md +128 -0
- package/kit/skills/deploy/references/topology-b.md +44 -0
- package/kit/skills/docs-writer/SKILL.md +53 -0
- package/kit/skills/docs-writer/references/coauthoring.md +34 -0
- package/kit/skills/eval-grader/SKILL.md +75 -0
- package/kit/skills/eval-grader/references/method.md +51 -0
- package/kit/skills/frontend/SKILL.md +56 -0
- package/kit/skills/frontend/references/verify-contract.md +41 -0
- package/kit/skills/frontend-design/SKILL.md +71 -0
- package/kit/skills/frontend-design/references/design-review.md +34 -0
- package/kit/skills/frontend-rn-expo/SKILL.md +25 -0
- package/kit/skills/handoff/SKILL.md +71 -0
- package/kit/skills/handoff/references/cold-reader.md +51 -0
- package/kit/skills/i18n-integrity/SKILL.md +31 -0
- package/kit/skills/incident-runbook/SKILL.md +40 -0
- package/kit/skills/incident-runbook/references/postmortem.md +16 -0
- package/kit/skills/iterate/SKILL.md +40 -0
- package/kit/skills/mcp-builder/SKILL.md +54 -0
- package/kit/skills/mcp-builder/references/building.md +71 -0
- package/kit/skills/observability/SKILL.md +50 -0
- package/kit/skills/performance/SKILL.md +38 -0
- package/kit/skills/performance/references/bottlenecks.md +10 -0
- package/kit/skills/privacy-compliance/SKILL.md +75 -0
- package/kit/skills/red-team/SKILL.md +48 -0
- package/kit/skills/reflect/SKILL.md +57 -0
- package/kit/skills/release/SKILL.md +44 -0
- package/kit/skills/security-scan/SKILL.md +99 -0
- package/kit/skills/security-scan/references/ai-agents.md +107 -0
- package/kit/skills/security-scan/references/fronts.md +99 -0
- package/kit/skills/security-scan/references/prompting.md +23 -0
- package/kit/skills/security-scan/references/reporting.md +75 -0
- package/kit/skills/security-scan/references/verify.md +131 -0
- package/kit/skills/sonarqube-check/SKILL.md +108 -0
- package/kit/skills/sonarqube-check/references/local-engines.md +32 -0
- package/kit/skills/sonarqube-check/references/stand-up-server.md +25 -0
- package/kit/skills/spec-planning/SKILL.md +92 -0
- package/kit/skills/systematic-debugging/SKILL.md +57 -0
- package/kit/skills/systematic-debugging/references/techniques.md +44 -0
- package/kit/skills/teamboard/SKILL.md +88 -0
- package/kit/skills/teamboard/references/setup.md +37 -0
- package/kit/skills/testing/SKILL.md +49 -0
- package/kit/skills/testing/references/flaky-triage.md +51 -0
- package/kit/skills/threat-model/SKILL.md +57 -0
- package/kit/skills/threat-model/references/interview.md +51 -0
- package/kit/skills/threat-model/references/schema.md +42 -0
- package/kit/skills/token-budget/SKILL.md +51 -0
- package/kit/skills/trace-scan/SKILL.md +43 -0
- package/kit/skills/worktree/SKILL.md +40 -0
- package/kit/studio/README.md +266 -0
- package/kit/studio/ensure-node.sh +471 -0
- package/kit/studio/package.json +14 -0
- package/kit/studio/server/hooks/studio-gate.sh +169 -0
- package/kit/studio/server/index.js +902 -0
- package/kit/studio/server/lib/crew-env.js +11 -0
- package/kit/studio/server/lib/fleet.js +146 -0
- package/kit/studio/server/lib/graph.js +533 -0
- package/kit/studio/server/lib/instance.js +135 -0
- package/kit/studio/server/lib/kit-telemetry.js +182 -0
- package/kit/studio/server/lib/kit.js +158 -0
- package/kit/studio/server/lib/palette.js +118 -0
- package/kit/studio/server/lib/peers.js +72 -0
- package/kit/studio/server/lib/permissions.js +150 -0
- package/kit/studio/server/lib/projects.js +294 -0
- package/kit/studio/server/lib/roster.js +191 -0
- package/kit/studio/server/lib/session.js +309 -0
- package/kit/studio/server/lib/terminal.js +91 -0
- package/kit/studio/server/lib/transcript.js +100 -0
- package/kit/studio/web/app.js +1098 -0
- package/kit/studio/web/canvas.js +1179 -0
- package/kit/studio/web/chat.js +543 -0
- package/kit/studio/web/index.html +86 -0
- package/kit/studio/web/md.js +110 -0
- package/kit/studio/web/storage-migrate.js +23 -0
- package/kit/studio/web/style.css +1453 -0
- package/package.json +46 -8
- package/start.sh +748 -0
- package/bin/crewforth.js +0 -10
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Tool × life-cycle commands
|
|
2
|
+
|
|
3
|
+
Find your tool's row and use it. A project has one migration tool; the other eight rows are noise at the
|
|
4
|
+
moment you are running a migration, which is why they are here rather than in `SKILL.md`.
|
|
5
|
+
|
|
6
|
+
The sequence itself — **Preview → (approval) → Apply → Verify** — and the rule that a destructive migration
|
|
7
|
+
never skips the approval gate live in `SKILL.md`. This file only says which command spells each step.
|
|
8
|
+
|
|
9
|
+
| Tool | Status | Preview (dry-run) | Apply | Verify | Roll back |
|
|
10
|
+
|---|---|---|---|---|---|
|
|
11
|
+
| Prisma | `migrate status` | `migrate diff … --script` | `migrate deploy` | `migrate status` | `migrate resolve --rolled-back` + backup |
|
|
12
|
+
| Knex | `migrate:status` | `migrate:latest --dry-run` | `migrate:latest` | `migrate:status` | `migrate:rollback` |
|
|
13
|
+
| Sequelize | `db:migrate:status` | read the migration file | `db:migrate` | `db:migrate:status` | `db:migrate:undo` |
|
|
14
|
+
| TypeORM | `migration:show` | read the pending file | `migration:run` | `migration:show` | `migration:revert` |
|
|
15
|
+
| Drizzle | `drizzle-kit status` | `generate` → inspect SQL | `drizzle-kit push` | `drizzle-kit status` | `drizzle-kit drop` (prefer backup) |
|
|
16
|
+
| Alembic | `current` + `history` | `upgrade head --sql` | `upgrade head` | `current` (=head) | `downgrade -1` |
|
|
17
|
+
| Django | `showmigrations` | `sqlmigrate <app> <name>` | `migrate` | `showmigrations` ([X]) | `migrate <app> <previous>` |
|
|
18
|
+
| Rails | `db:migrate:status` | read the migration file | `db:migrate` | `db:migrate:status` (up) | `db:rollback STEP=1` |
|
|
19
|
+
| EF Core | `migrations list` | `migrations script` | `database update` | `migrations list` | `database update <previous>` |
|
|
20
|
+
|
|
21
|
+
Invocation prefix: `npx …` for the Node tools, the project's Python environment for Alembic and Django,
|
|
22
|
+
`dotnet ef …` for EF Core.
|
|
23
|
+
|
|
24
|
+
## Where a preview does not exist
|
|
25
|
+
|
|
26
|
+
Sequelize, TypeORM and Rails have no true dry-run. **Treat the migration file itself as the preview** — read
|
|
27
|
+
it out and get it approved before applying. "The tool cannot preview" is not a reason to skip the gate; it is
|
|
28
|
+
a reason the preview is manual.
|
|
29
|
+
|
|
30
|
+
## Rollback is not a substitute for the backup
|
|
31
|
+
|
|
32
|
+
Every `Roll back` command above reverses *schema* intent. None of them restores data a destructive migration
|
|
33
|
+
already dropped. That is what the mandatory production backup is for — see `references/backup-restore.md`.
|
|
34
|
+
Drizzle's entry says "prefer backup" for exactly this reason, and the same caution applies to the others
|
|
35
|
+
whenever the migration touched data rather than only structure.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dependency-audit
|
|
3
|
+
description: |
|
|
4
|
+
Dependency risk assessment, read-only: known CVEs, deprecated packages, licence compliance, maintenance
|
|
5
|
+
status, lockfile integrity, and a justification for every new dependency. Acting on it is dependency-upgrade.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Dependency Audit
|
|
9
|
+
|
|
10
|
+
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
11
|
+
Trigger phrases: "dependency audit", "npm audit", "package security", "CVE", "license", "deprecated package", "still maintained", "no longer maintained", "unmaintained", "third party library", "supply chain"
|
|
12
|
+
|
|
13
|
+
**This skill only reports.** It changes no manifest and no lockfile — bringing dependencies current is
|
|
14
|
+
[[dependency-upgrade]], which classifies each move by risk and verifies the build. Keeping the two apart keeps
|
|
15
|
+
this one safe to run any time, on any branch, including one you are only inspecting.
|
|
16
|
+
|
|
17
|
+
## Audit axes
|
|
18
|
+
1. **Known vulnerabilities (CVE):** audit appropriate to the ecosystem
|
|
19
|
+
```bash
|
|
20
|
+
npm audit --production # Node
|
|
21
|
+
dotnet list package --vulnerable # .NET (cannot be combined with --deprecated/--outdated)
|
|
22
|
+
pip-audit # Python
|
|
23
|
+
```
|
|
24
|
+
2. **License compliance:** flag licenses incompatible with the project such as copyleft/GPL (a risk in commercial closed source).
|
|
25
|
+
3. **Maintenance status:** abandoned / long-unmaintained / single-maintainer packages, and packages the registry
|
|
26
|
+
itself marks **deprecated** — `dotnet list package --deprecated`, `npm view <pkg> deprecated`. A deprecated
|
|
27
|
+
package is not a version problem: no bump fixes it, it needs a replacement.
|
|
28
|
+
4. **Transitive dependencies:** also scan vulnerabilities in indirect dependencies.
|
|
29
|
+
5. **Lockfile integrity:** lockfile committed and consistent with the manifest; versions pinned.
|
|
30
|
+
6. **Justification for new dependencies:** is it actually needed? Don't add a heavy package for a single small function (supply-chain surface).
|
|
31
|
+
7. **Install-time execution:** does any dependency run code during installation — `preinstall`/`install`/
|
|
32
|
+
`postinstall` scripts — and `prepare`, which runs only for a git, link or folder dependency and never for a
|
|
33
|
+
registry tarball — a build hook, or a setup step that fetches at install time? This is the mechanism the recent
|
|
34
|
+
registry compromises actually used: the malicious version does not have to be imported, only installed, so a
|
|
35
|
+
CVE feed and a code review both miss it. Ask two things: which packages declare such a script, and whether
|
|
36
|
+
the project can install without them at all (`npm ci --ignore-scripts`; for Python the control is refusing source
|
|
37
|
+
distributions, `pip install --only-binary=:all:`, optionally with `--require-hashes` — a wheel has no install
|
|
38
|
+
hook, an sdist does; or a vendored, pre-built artefact). A package that cannot install without running code is not disqualified —
|
|
39
|
+
it is a package whose publisher you are trusting with arbitrary execution on every machine and every runner,
|
|
40
|
+
which is a decision, not a default.
|
|
41
|
+
8. **Publisher concentration — read it from the registry, not from the repository.** "Many contributors" on a
|
|
42
|
+
forge says who can open a pull request; it does not say who can publish. The number that matters is who holds
|
|
43
|
+
the publish right on the registry (`npm owner ls <pkg>` · for PyPI the package JSON's `ownership.roles`,
|
|
44
|
+
NOT the self-reported `info.maintainer` field · the equivalent ACL for the ecosystem in use) — and whether the
|
|
45
|
+
PUBLISH PATH itself is challenged, which is not the same as "the account has 2FA": npm's `auth-only` 2FA mode
|
|
46
|
+
does not gate `npm publish`, and a granular token with bypass-2FA publishes with no second factor, while on
|
|
47
|
+
PyPI 2FA has been mandatory for every account since 2024 so the real question there is Trusted Publisher
|
|
48
|
+
versus a long-lived API token. A widely-used
|
|
49
|
+
package whose registry ACL is one unprotected account is a single compromised credential away from every
|
|
50
|
+
build that installs it, whatever the contributor graph looks like.
|
|
51
|
+
|
|
52
|
+
## Output
|
|
53
|
+
Severity-sorted list: `package · version · issue (CVE/license/maintenance) · upgrade path`.
|
|
54
|
+
|
|
55
|
+
## DoD
|
|
56
|
+
- 0 known HIGH/CRITICAL vulnerabilities; licenses compliant; lockfile consistent; every new package justified.
|
|
57
|
+
- **Every axis resolves to one of three states, and the third is a real answer:** *assessed-clean*,
|
|
58
|
+
*assessed-flagged*, or *not assessable here — and why*. An axis that was skipped because the tool is missing,
|
|
59
|
+
the registry was unreachable, or the ecosystem has no equivalent is reported as unassessed, never folded into
|
|
60
|
+
the clean count. A report that cannot tell "nothing found" from "nobody looked" is the failure this skill
|
|
61
|
+
exists to prevent, and the sibling `security-scan` already holds the same rule.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dependency-upgrade
|
|
3
|
+
description: |
|
|
4
|
+
Bring dependencies current without breaking the build: find what is vulnerable, deprecated or behind,
|
|
5
|
+
classify each target version by risk, apply what is safe, verify, and roll back what is not.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Dependency Upgrade
|
|
9
|
+
|
|
10
|
+
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
11
|
+
Trigger phrases: "upgrade dependencies", "outdated packages", "bump versions", "update packages", "keep dependencies current"
|
|
12
|
+
|
|
13
|
+
## When
|
|
14
|
+
Staying on current versions as an ongoing practice, or reacting to a CVE. [[dependency-audit]] tells you what is
|
|
15
|
+
wrong; this decides what to do about it and does it. It **mutates the manifest and the lockfile**, so it carries
|
|
16
|
+
the same shape as [[db-migration]]: detect the tool, classify by risk, gate the dangerous class behind approval,
|
|
17
|
+
apply, verify, roll back on red.
|
|
18
|
+
|
|
19
|
+
## 1 — Ask the three separate questions
|
|
20
|
+
They are three different problems with three different answers, and the tooling keeps them separate too.
|
|
21
|
+
|
|
22
|
+
| Question | Node | .NET | Python |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| What has a **known CVE**? | `npm audit --json` | `dotnet list package --vulnerable` | `pip-audit` |
|
|
25
|
+
| What is **deprecated**? | `npm view <pkg> deprecated` | `dotnet list package --deprecated` | registry / project notes |
|
|
26
|
+
| What is simply **behind**? | `npm outdated --json` | `dotnet list package --outdated` | `pip list --outdated` |
|
|
27
|
+
|
|
28
|
+
> The three `dotnet list package` flags **cannot be combined** — it is three separate runs, not one.
|
|
29
|
+
|
|
30
|
+
**Deprecated is not an upgrade.** No version bump fixes it; the package needs a replacement, which is a design
|
|
31
|
+
change with its own review — never fold it into a routine bump.
|
|
32
|
+
|
|
33
|
+
## 2 — Classify every target version before touching anything
|
|
34
|
+
The risk is not "how old is it", it is "how far does the jump go".
|
|
35
|
+
|
|
36
|
+
| Class | Rule | Approval |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| **Security patch** | fixes a known CVE, within the current major | Do it first, on its own commit — it is the one upgrade whose delay has a cost |
|
|
39
|
+
| **Patch** (`x.y.Z`) | bugfix only | Apply, batched |
|
|
40
|
+
| **Minor** (`x.Y.z`) | additive, backward compatible **by promise** | Apply, batched, only if the suite is green afterwards |
|
|
41
|
+
| **Major** (`X.y.z`) | breaking by definition | **Never automatic.** Read the changelog/migration guide, one package per commit, and say what breaks |
|
|
42
|
+
| **Pinned / transitive-only** | pinned deliberately, or not a direct dependency | Leave it. Find out *why* it is pinned before unpinning |
|
|
43
|
+
|
|
44
|
+
Semver is a promise, not a guarantee: a minor that breaks you is a bug in the package, and your suite is the
|
|
45
|
+
only thing that will tell you. That is why the verify step below is not optional.
|
|
46
|
+
|
|
47
|
+
## 3 — Apply
|
|
48
|
+
- **Group by class, one commit per group** — a patch batch and a major are not the same change and must not
|
|
49
|
+
share a commit. A single commit mixing forty bumps is unreviewable and unrevertable.
|
|
50
|
+
- **Preview first.** `npm audit fix --dry-run` before `npm audit fix`. Never reach for `npm audit fix --force`
|
|
51
|
+
without saying so out loud: it installs **semver-major** upgrades, which is precisely how this task breaks a
|
|
52
|
+
build while claiming to be a security fix.
|
|
53
|
+
- **The lockfile is part of the change.** Commit it with the manifest; a manifest bump without its lockfile is
|
|
54
|
+
a change nobody else reproduces ([[dependency-audit]] axis 5).
|
|
55
|
+
- Never hand-edit a lockfile. Let the tool regenerate it.
|
|
56
|
+
|
|
57
|
+
## 4 — Verify, then decide
|
|
58
|
+
Nothing counts as upgraded until it is observed working — install, build, full test suite, and the project's
|
|
59
|
+
quality gate. Run the app's real entry path if the change touches runtime, not just unit tests ([[testing]]).
|
|
60
|
+
|
|
61
|
+
- Green → keep it.
|
|
62
|
+
- Red → **roll back this group** (`git checkout -- <manifest> <lockfile>` then reinstall), then either pin, split
|
|
63
|
+
the batch to find the culprit, or move that package to its own major-upgrade task. Do not "fix forward" a
|
|
64
|
+
routine bump; that turns maintenance into an unplanned feature.
|
|
65
|
+
- Re-run [[dependency-audit]] afterwards: an upgrade can introduce a new transitive CVE.
|
|
66
|
+
|
|
67
|
+
## 5 — Continuous, without an autonomous loop
|
|
68
|
+
"Always current" is a **cadence**, not an agent that runs by itself — Crewforth has no timed or self-triggering
|
|
69
|
+
loops, because every mutation here needs an approval a loop cannot give. Continuity comes from somewhere that
|
|
70
|
+
already has a human gate at the end:
|
|
71
|
+
- A scheduled CI job running the step-1 commands and failing on HIGH/CRITICAL ([[ci-pipeline]]).
|
|
72
|
+
- Or a bot (Renovate / Dependabot) opening one PR per group — Crewforth does not reimplement it; the value added
|
|
73
|
+
is the policy above deciding which PRs merge on green and which need a human to read a changelog.
|
|
74
|
+
- Either way the PR passes through the ordinary gates: build, tests, [[security-scan]], review, approval.
|
|
75
|
+
|
|
76
|
+
## DoD
|
|
77
|
+
- Vulnerable, deprecated and outdated were asked as three separate questions, with the command output shown.
|
|
78
|
+
- Every applied upgrade is classed patch / minor / major, and no major went in automatically or unexplained.
|
|
79
|
+
- Security fixes landed first and separately.
|
|
80
|
+
- Manifest and lockfile moved together; no lockfile was hand-edited.
|
|
81
|
+
- Build + full suite were observed green after the change, or the group was rolled back and named.
|
|
82
|
+
- Deprecated packages are reported as replacement work, not silently bumped.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploy
|
|
3
|
+
description: |
|
|
4
|
+
Ship a build reversibly — to a host you manage or a platform that manages it. Pick the topology first, keep
|
|
5
|
+
the previous version reachable, gate on health, roll back without rebuilding. Use when shipping anywhere
|
|
6
|
+
users can reach.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Deploy
|
|
10
|
+
|
|
11
|
+
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
12
|
+
Trigger phrases: "deploy", "push to the server", "to the server", "onto the server", "build on the server", "install on the VPS", "ship it to the server", "go to production", "to production", "go live"
|
|
13
|
+
|
|
14
|
+
A deploy has a single idea, and it does not depend on where you are deploying: **make the new version live in a
|
|
15
|
+
way you can undo.** Keep the previous version reachable until the new one has proved itself; if it fails the
|
|
16
|
+
health gate, go back to the old one and nobody notices.
|
|
17
|
+
|
|
18
|
+
**Two topologies implement that idea differently, and mixing them is where deploys go wrong.** On a host you
|
|
19
|
+
manage, "reversible" means the previous release still sits on disk and the swap is a pointer you can move back.
|
|
20
|
+
On a platform that manages the host for you, "reversible" means the previous *artefact* is still addressable and
|
|
21
|
+
you re-point the environment at it — you do not rebuild it. Phase 0 picks the branch; everything after follows.
|
|
22
|
+
|
|
23
|
+
> **Crewforth adaptation (local, .claude/):** Any backend runtime (Docker recommended). **Deploy requires explicit
|
|
24
|
+
> approval (§4.4)**; a backup before every swap and a health gate after are mandatory. If `.deploy.yml` carries server
|
|
25
|
+
> credentials it goes into `.gitignore`. §4 Prohibitions apply.
|
|
26
|
+
|
|
27
|
+
## Phase 0 — Which topology
|
|
28
|
+
Answer before anything else, because the rest of the skill forks here. Ask if it is not obvious; do not infer it
|
|
29
|
+
from the language or the framework.
|
|
30
|
+
|
|
31
|
+
| | **A · Self-managed host** | **B · Managed platform** |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| You have | SSH to a machine you own or rent | an API/CLI, and no shell on the box |
|
|
34
|
+
| A release is | a directory on that host | an immutable artefact (image digest, build id, bundle) |
|
|
35
|
+
| "Deploy" means | put the files in place and restart | promote an existing artefact to an environment |
|
|
36
|
+
| Rollback means | move the pointer back to the previous directory | re-point the environment at the previous artefact |
|
|
37
|
+
| Owned by | **`references/topology-a.md`** (phases 1–5) | **`references/topology-b.md`** (phases B1–B4) |
|
|
38
|
+
|
|
39
|
+
Signals for B: the platform builds from a push, or the runtime is described as containers/functions/dynos/pods
|
|
40
|
+
that you do not administer. Signals for A: you are given a host, a user and a port. A Kubernetes cluster is B
|
|
41
|
+
when a controller reconciles it from a manifest, and A only if you are genuinely hand-placing files on nodes.
|
|
42
|
+
|
|
43
|
+
**If both are in play** — a managed platform in front, a self-managed worker behind — run the branches
|
|
44
|
+
separately and gate each on its own health check. One approval does not cover two topologies.
|
|
45
|
+
|
|
46
|
+
## Checklist (A · self-managed host)
|
|
47
|
+
- [ ] Topology decided (Phase 0)
|
|
48
|
+
- [ ] Runtime + deploy method determined
|
|
49
|
+
- [ ] Config taken from `.deploy.yml`/the user, SSH verified
|
|
50
|
+
- [ ] Reverse proxy + (if there is a domain) SSL in place
|
|
51
|
+
- [ ] Running version backed up to `releases/`
|
|
52
|
+
- [ ] User approved the deploy, new version deployed
|
|
53
|
+
- [ ] Health gate passed (otherwise automatic rollback performed)
|
|
54
|
+
- [ ] If requested, single-command scripts (`deploy.sh` / `adopt.sh`) generated
|
|
55
|
+
|
|
56
|
+
## Checklist (B · managed platform)
|
|
57
|
+
- [ ] Topology decided (Phase 0)
|
|
58
|
+
- [ ] Artefact built once and addressed immutably (digest / build id, not a moving tag)
|
|
59
|
+
- [ ] Which artefact is in which environment is recorded
|
|
60
|
+
- [ ] Config and secrets injected per environment, not baked into the artefact
|
|
61
|
+
- [ ] User approved the promotion to this environment
|
|
62
|
+
- [ ] Health gate passed from OUTSIDE — not the platform's own status
|
|
63
|
+
- [ ] Previous artefact still addressable, and the migration question settled before the deploy
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## The phases — read ONE branch
|
|
68
|
+
|
|
69
|
+
Phase 0 picked it; loading both is the mistake this skill opens with.
|
|
70
|
+
|
|
71
|
+
- **A · self-managed host** → **`references/topology-a.md`**: runtime/method detection, `.deploy.yml` + SSH,
|
|
72
|
+
reverse proxy and SSL, the backup-then-swap for Docker and bare-metal, the health gate, the rollback.
|
|
73
|
+
- **B · managed platform** → **`references/topology-b.md`**: build once and address it immutably, promote
|
|
74
|
+
without rebuilding, health-gate from outside the platform, roll back by re-pointing.
|
|
75
|
+
|
|
76
|
+
The invariant rules at the end of this file hold on both, and they are the half that must not be read from a
|
|
77
|
+
reference file: they are what an approval is checked against.
|
|
78
|
+
|
|
79
|
+
## Single-command scripts (optional)
|
|
80
|
+
|
|
81
|
+
After a successful deploy, **ask** whether to generate project-specific `deploy.sh` / `adopt.sh` (single-command
|
|
82
|
+
deploy/update). Derive the steps from the real project (package.json / Dockerfile / Makefile / compose), never a
|
|
83
|
+
generic template. Full guidance + what each script contains: **`references/scripts.md`**.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Invariant rules
|
|
88
|
+
These hold on BOTH branches; where the mechanism differs, the rule does not.
|
|
89
|
+
0. **The topology is decided, not assumed** — Phase 0, once, out loud. Everything downstream depends on it.
|
|
90
|
+
1. **No deploy without approval** — show the method/host/domain/port plan, wait for approval.
|
|
91
|
+
2. **Always back up before the swap** — timestamped into `releases/`; if the backup fails, abort.
|
|
92
|
+
3. **Always a health gate after the swap** — HTTP + process; if it doesn't pass, automatic rollback.
|
|
93
|
+
4. **Keep the last 3 versions** — don't delete them all.
|
|
94
|
+
5. **Don't deploy to prod without knowing the target** — host/deploy_path/domain must be approved.
|
|
95
|
+
6. **Verify SSH first** — if you can't connect, fail fast.
|
|
96
|
+
7. **Don't expose the port directly** — the app binds to `127.0.0.1`, outward only through the proxy.
|
|
97
|
+
8. **SSL is mandatory when there's a domain** — always set it up unless `ssl` is explicitly `false`.
|
|
98
|
+
9. **Never rebuild to roll back** (B) — promote the previous artefact. A rebuild is a different artefact.
|
|
99
|
+
10. **The platform's own status is not the health gate** (B) — verify from where a user stands.
|
|
100
|
+
11. **Rolling back code does not roll back a migration** — settle the schema question before the deploy.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Reverse proxy and SSL — the file contents
|
|
2
|
+
|
|
3
|
+
Read the half you need. A deploy uses Nginx **or** Caddy, never both, and the two paths differ most exactly
|
|
4
|
+
where it matters: Caddy obtains and renews the certificate itself, Nginx needs Certbot wired up separately.
|
|
5
|
+
|
|
6
|
+
The decision — which proxy is installed, whether SSL applies at all, and that the app binds only to
|
|
7
|
+
`127.0.0.1` — stays in `SKILL.md`. This file is what to write once that is settled.
|
|
8
|
+
|
|
9
|
+
## Nginx
|
|
10
|
+
|
|
11
|
+
Site config:
|
|
12
|
+
|
|
13
|
+
```nginx
|
|
14
|
+
server {
|
|
15
|
+
listen 80;
|
|
16
|
+
server_name DOMAIN;
|
|
17
|
+
location / {
|
|
18
|
+
proxy_pass http://127.0.0.1:APP_PORT;
|
|
19
|
+
proxy_http_version 1.1;
|
|
20
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
21
|
+
proxy_set_header Connection 'upgrade';
|
|
22
|
+
proxy_set_header Host $host;
|
|
23
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
24
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
25
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Link it into `sites-enabled` with `ln -s`, then `nginx -t && systemctl reload nginx`. Run `nginx -t` before
|
|
31
|
+
the reload every time — a reload on a broken config takes the site down, and the test costs nothing.
|
|
32
|
+
|
|
33
|
+
### SSL on the Nginx path
|
|
34
|
+
|
|
35
|
+
Skip entirely when `ssl: false` or there is no domain. **Never attempt SSL on a bare IP** — a certificate
|
|
36
|
+
authority cannot issue for one, and the attempt burns rate-limited quota on the domain you will use later.
|
|
37
|
+
|
|
38
|
+
Verify DNS resolves to this host *first*, or Certbot's challenge fails in a way that looks like a proxy fault:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
dig +short $DOMAIN # must equal $HOST
|
|
42
|
+
certbot --nginx -d $DOMAIN --non-interactive --agree-tos -m admin@$DOMAIN
|
|
43
|
+
systemctl enable certbot.timer # automatic renewal — without this the site breaks in 90 days
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Caddy
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
DOMAIN {
|
|
50
|
+
reverse_proxy 127.0.0.1:APP_PORT
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`systemctl reload caddy`. Caddy handles the certificate and its renewal on its own — no Certbot, no timer, no
|
|
55
|
+
separate SSL step. If the domain does not yet resolve, Caddy will retry rather than fail loudly, so check
|
|
56
|
+
`journalctl -u caddy` when a fresh domain serves plain HTTP longer than expected.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Phase 5 — Rollback (run on health-gate failure)
|
|
2
|
+
|
|
3
|
+
Undo the swap: put the most recent version in `releases/` back into `current`, restart the service, repeat the
|
|
4
|
+
health gate (Phase 4).
|
|
5
|
+
```bash
|
|
6
|
+
LATEST=$(ssh -i $SSH_KEY $USER@$HOST "ls -dt $DEPLOY_PATH/releases/*/ 2>/dev/null | head -1")
|
|
7
|
+
[ -z "$LATEST" ] && { echo "ERROR: no backup — rollback impossible"; exit 1; }
|
|
8
|
+
ssh -i $SSH_KEY $USER@$HOST "
|
|
9
|
+
mkdir -p $DEPLOY_PATH/current
|
|
10
|
+
rsync -a --delete ${LATEST%/}/ $DEPLOY_PATH/current/ # atomic swap — no 'rm -rf' (guard-safe)
|
|
11
|
+
{ docker rm -f $APP 2>/dev/null && docker run -d --name $APP --restart unless-stopped \
|
|
12
|
+
-p 127.0.0.1:$APP_PORT:$APP_PORT $APP:previous; } \
|
|
13
|
+
|| systemctl restart $APP || pm2 restart $APP
|
|
14
|
+
"
|
|
15
|
+
```
|
|
16
|
+
> **Note:** Rollback uses `rsync --delete` instead of `rm -rf` so `guard-bash` (the local `rm -rf` block) doesn't
|
|
17
|
+
> block the automatic rollback. The deploy verbs (`ssh`/`rsync`/`docker`) pass through the approval gate via
|
|
18
|
+
> `settings.json` `permissions.ask` — they run with approval, not silently.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Single-command scripts (optional)
|
|
2
|
+
|
|
3
|
+
After a successful deploy, **ask:** "Shall I generate `deploy.sh` and `adopt.sh` so you can deploy/update with a single command in the future?"
|
|
4
|
+
|
|
5
|
+
If you generate them, **don't use a generic template** — analyze the actual project (package.json scripts / Dockerfile / Makefile / docker-compose / `.env.example`) and derive the exact build+start steps. Two scripts:
|
|
6
|
+
- **`deploy.sh`** — first-time setup + deploy: load config · verify SSH · proxy + SSL · back up to `releases/` · project-specific build · transfer · dependencies · start · health gate with retries · rollback on failure.
|
|
7
|
+
- **`adopt.sh`** — quick update: config · backup · code sync · (if needed) build/dependencies · restart · health gate · rollback on failure.
|
|
8
|
+
|
|
9
|
+
Rules: `chmod +x`; if `.deploy.yml` carries credentials, ask about `.gitignore`; don't overwrite an existing script without approval (show a diff); no hardcoded credentials — always read from `.deploy.yml`; comment and explain every project-specific decision.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Topology A — self-managed host (phases 1-5)
|
|
2
|
+
|
|
3
|
+
Read this only when Phase 0 answered **A**: you have SSH to a machine you own or rent, a release is a directory on
|
|
4
|
+
that host, and rollback means moving a pointer back. On topology B read `references/topology-b.md` instead — never
|
|
5
|
+
both, because mixing them is where deploys go wrong.
|
|
6
|
+
|
|
7
|
+
The invariant rules in SKILL.md hold over everything here: no deploy without approval, back up before the swap,
|
|
8
|
+
health-gate after it, keep the last three versions, the app binds to `127.0.0.1` only.
|
|
9
|
+
|
|
10
|
+
## Phase 1 (A) — Surface: method and runtime
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
if [ -f Dockerfile ] || [ -f docker-compose.yml ]; then METHOD=docker
|
|
14
|
+
elif [ -f package.json ]; then METHOD=bare RUNTIME=node
|
|
15
|
+
elif [ -f requirements.txt ] || [ -f pyproject.toml ]; then METHOD=bare RUNTIME=python
|
|
16
|
+
elif [ -f go.mod ]; then METHOD=bare RUNTIME=go
|
|
17
|
+
elif ls ./*.csproj ./*.sln >/dev/null 2>&1; then METHOD=bare RUNTIME=dotnet
|
|
18
|
+
elif [ -f pom.xml ] || [ -f build.gradle ]; then METHOD=bare RUNTIME=java
|
|
19
|
+
elif [ -f Cargo.toml ]; then METHOD=bare RUNTIME=rust
|
|
20
|
+
elif [ -f Gemfile ]; then METHOD=bare RUNTIME=ruby
|
|
21
|
+
elif [ -f composer.json ]; then METHOD=bare RUNTIME=php
|
|
22
|
+
else METHOD=unknown; fi
|
|
23
|
+
```
|
|
24
|
+
If `method: docker` and there's no Dockerfile, generate one. `method: bare` → a process manager suited to the runtime; build the release artefact with the runtime's own command (`dotnet publish -c Release`, `mvn package`, `cargo build --release`, …) and run it under systemd. Prefer Docker for any runtime whose bare setup needs a toolchain on the box. If detection is ambiguous, ask the user — never guess a runtime.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Phase 2 (A) — Preparation: config, SSH, proxy, SSL
|
|
29
|
+
|
|
30
|
+
**Config** — read `.deploy.yml` from the root, and if it's missing ask for each field:
|
|
31
|
+
```yaml
|
|
32
|
+
host: 192.168.1.100 # VPS IP/hostname (required)
|
|
33
|
+
user: deploy # SSH user (required)
|
|
34
|
+
ssh_key: ~/.ssh/id_rsa # private key
|
|
35
|
+
app_port: 3000 # application port (required)
|
|
36
|
+
health_check: /api/health # HTTP path (default /)
|
|
37
|
+
deploy_path: /var/www/app # server path (required)
|
|
38
|
+
method: auto # auto | docker | bare
|
|
39
|
+
domain: app.example.com # for proxy/SSL (optional)
|
|
40
|
+
ssl: true # default true if there is a domain
|
|
41
|
+
reverse_proxy: auto # auto | nginx | caddy
|
|
42
|
+
```
|
|
43
|
+
**Verify SSH first** — if you can't connect, stop immediately:
|
|
44
|
+
```bash
|
|
45
|
+
ssh -i $SSH_KEY -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new $USER@$HOST "echo OK"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Reverse proxy** — detect the one installed on the server (`auto` → Caddy if present, otherwise Nginx). The application binds only to `127.0.0.1`; it is exposed outward **only** through the proxy.
|
|
49
|
+
|
|
50
|
+
**SSL** — skip if `ssl: false` or there's no domain, and never on a bare IP. On the Nginx path, verify DNS resolves to the host (`dig +short $DOMAIN` = `$HOST`) *before* running Certbot; on the Caddy path the certificate and its renewal are automatic and there is no separate step.
|
|
51
|
+
|
|
52
|
+
Config file contents for both proxies, the Certbot commands and the renewal timer: **`references/proxy-ssl.md`** — read the half for the proxy you found, not both.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Phase 3 (A) — Swap: keep the old, put the new
|
|
57
|
+
|
|
58
|
+
**First back up the running version** (timestamped `releases/`, last 3 kept):
|
|
59
|
+
```bash
|
|
60
|
+
ssh -i $SSH_KEY $USER@$HOST "
|
|
61
|
+
mkdir -p $DEPLOY_PATH/releases
|
|
62
|
+
[ -d $DEPLOY_PATH/current ] && cp -a $DEPLOY_PATH/current $DEPLOY_PATH/releases/$(date +%Y%m%d_%H%M%S)
|
|
63
|
+
cd $DEPLOY_PATH/releases && ls -dt */ | tail -n +4 | xargs -r rm -rf
|
|
64
|
+
"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Docker** — build the image locally, transfer it, replace the container:
|
|
68
|
+
```bash
|
|
69
|
+
docker build -t $APP:latest .
|
|
70
|
+
docker save $APP:latest | gzip | ssh -i $SSH_KEY $USER@$HOST "gunzip | docker load"
|
|
71
|
+
ssh -i $SSH_KEY $USER@$HOST "
|
|
72
|
+
docker rm -f $APP 2>/dev/null || true
|
|
73
|
+
docker run -d --name $APP --restart unless-stopped -p 127.0.0.1:$APP_PORT:$APP_PORT $APP:latest
|
|
74
|
+
"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Bare-metal** — `rsync` the source, install dependencies, start with the process manager:
|
|
78
|
+
```bash
|
|
79
|
+
rsync -avz --delete --exclude .git --exclude node_modules --exclude .venv \
|
|
80
|
+
-e "ssh -i $SSH_KEY" ./ $USER@$HOST:$DEPLOY_PATH/current/
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
| Runtime | Dependencies + startup |
|
|
84
|
+
|---|---|
|
|
85
|
+
| Node | `npm ci --production` → PM2: `pm2 start ecosystem.config.js --name $APP || pm2 start npm --name $APP -- start` → `pm2 save` |
|
|
86
|
+
| Python | `python3 -m venv venv && venv/bin/pip install -r requirements.txt` → systemd (gunicorn/uvicorn or `python main.py`) |
|
|
87
|
+
| Go | build locally `GOOS=linux GOARCH=amd64 go build -o $APP` → `scp` → systemd |
|
|
88
|
+
|
|
89
|
+
systemd unit (for Python/Go, fill in ExecStart per the runtime):
|
|
90
|
+
```ini
|
|
91
|
+
[Unit]
|
|
92
|
+
After=network.target
|
|
93
|
+
[Service]
|
|
94
|
+
Type=simple
|
|
95
|
+
User=$USER
|
|
96
|
+
WorkingDirectory=$DEPLOY_PATH/current
|
|
97
|
+
ExecStart=<runtime command>
|
|
98
|
+
Restart=always
|
|
99
|
+
Environment=PORT=$APP_PORT
|
|
100
|
+
[Install]
|
|
101
|
+
WantedBy=multi-user.target
|
|
102
|
+
```
|
|
103
|
+
`systemctl daemon-reload && systemctl enable --now $APP`.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Phase 4 (A) — Health gate
|
|
108
|
+
|
|
109
|
+
Right after the swap; retry for ~30 s until a 200 comes back. **If it doesn't pass, trigger Phase 5 automatically.**
|
|
110
|
+
```bash
|
|
111
|
+
ssh -i $SSH_KEY $USER@$HOST '
|
|
112
|
+
for i in $(seq 1 6); do
|
|
113
|
+
[ "$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:'"$APP_PORT$HEALTH_CHECK"')" = 200 ] \
|
|
114
|
+
&& { echo "Health gate PASSED"; exit 0; }
|
|
115
|
+
sleep 5
|
|
116
|
+
done
|
|
117
|
+
echo "Health gate FAILED"; exit 1
|
|
118
|
+
'
|
|
119
|
+
```
|
|
120
|
+
Also verify the process is up: `docker ps` on Docker, otherwise `systemctl is-active $APP` / `pm2 show $APP`.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Phase 5 (A) — Rollback
|
|
125
|
+
|
|
126
|
+
On health-gate failure: put the most recent `releases/` version back into `current`, restart the service, and
|
|
127
|
+
re-run the health gate (Phase 4). Guard-safe rollback script (uses `rsync --delete`, not `rm -rf`) + the
|
|
128
|
+
approval-gate note: **`references/rollback.md`**.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Topology B — managed platform (phases B1-B4)
|
|
2
|
+
|
|
3
|
+
Read this only when Phase 0 answered **B**: you have an API/CLI and no shell on the box, a release is an immutable
|
|
4
|
+
artefact, and rollback means re-pointing the environment at the previous one. On topology A read
|
|
5
|
+
`references/topology-a.md` instead — never both.
|
|
6
|
+
|
|
7
|
+
The invariant rules in SKILL.md hold over everything here, including the two that are B's alone: never rebuild to
|
|
8
|
+
roll back, and the platform's own status is not the health gate.
|
|
9
|
+
|
|
10
|
+
## Phase B1 — Build once, and know what you built
|
|
11
|
+
The artefact is produced **once**, and the same one moves through every environment. Rebuilding for production
|
|
12
|
+
means the thing you tested is not the thing you shipped — the inputs moved underneath you (a floating base
|
|
13
|
+
image, a lockfile resolved a minute later, a different builder). Two consequences:
|
|
14
|
+
- **Address it immutably.** A digest or a build id, never a moving tag. `latest`, `main` and a branch name all
|
|
15
|
+
name different bytes on different days, which makes both promotion and rollback unverifiable.
|
|
16
|
+
- **Record which artefact is in which environment.** If nobody can answer "what exactly is running in
|
|
17
|
+
production", rollback is a guess and the health gate has nothing to compare against.
|
|
18
|
+
|
|
19
|
+
## Phase B2 — Promote, do not rebuild
|
|
20
|
+
Deploying to the next environment means pointing that environment at an artefact that already exists and has
|
|
21
|
+
already passed the previous gate. Configuration is injected per environment, never baked into the artefact — the
|
|
22
|
+
same bytes must be able to run in staging and in production, or you are back to rebuilding. Secrets come from
|
|
23
|
+
the platform's own store; a secret inside the artefact ships to everyone who can pull it.
|
|
24
|
+
|
|
25
|
+
**Approval is per environment (§4.4), and per topology.** Promoting to a user-facing environment is a deploy,
|
|
26
|
+
whatever the platform calls it.
|
|
27
|
+
|
|
28
|
+
## Phase B3 — Health gate, from outside
|
|
29
|
+
Identical in spirit to Phase 4 (A) and worth stating because platforms invite the opposite: **the platform
|
|
30
|
+
reporting "deployed", "healthy" or "active" is not the verification.** It reports that its own reconciliation
|
|
31
|
+
finished. Check the thing users touch — a request through the public entry point, a real response body, the
|
|
32
|
+
version endpoint returning the version you promoted. Give it the same failure rule: if the gate does not pass,
|
|
33
|
+
go back before investigating.
|
|
34
|
+
|
|
35
|
+
## Phase B4 — Roll back by re-pointing
|
|
36
|
+
Rollback is promoting the previous artefact, not rebuilding the previous commit — a rebuild is a new artefact
|
|
37
|
+
with new inputs and it may not even reproduce the bug you are escaping. So:
|
|
38
|
+
- Keep the previous artefacts addressable for at least as long as you promise to be able to roll back. A
|
|
39
|
+
retention policy that deletes them is a rollback policy that does not work.
|
|
40
|
+
- Rolling back **code** does not roll back a **migration**. If the release included an irreversible schema
|
|
41
|
+
change, the old artefact may not run against the new schema — that is `db-migration`'s expand/contract
|
|
42
|
+
question and it has to be answered before the deploy, not during the incident.
|
|
43
|
+
- After a rollback, the environment is running an artefact older than the recorded state. Say so plainly to
|
|
44
|
+
whoever is watching; a silent rollback is how two people fix the same outage twice.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docs-writer
|
|
3
|
+
description: |
|
|
4
|
+
Keeps documentation in sync with the code: README, usage and related docs when a public API or behavior
|
|
5
|
+
changes. Leaves no dead or misleading docs behind.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Documentation
|
|
9
|
+
|
|
10
|
+
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
11
|
+
Trigger phrases: "documentation", "docs", "update README", "API docs", "write docs", "document it", "write usage"
|
|
12
|
+
|
|
13
|
+
Goal: the docs must **match the code**. Wrong/stale docs are worse than no docs (they inspire trust and mislead).
|
|
14
|
+
Trigger: when a public API, command, configuration, or user-visible behavior changes.
|
|
15
|
+
|
|
16
|
+
## When it is mandatory
|
|
17
|
+
- A public function/endpoint/CLI signature or behavior changed.
|
|
18
|
+
- A new feature, configuration key, or environment variable was added.
|
|
19
|
+
- The install/run steps changed.
|
|
20
|
+
- A breaking change was made (also coordinate via `release`/CHANGELOG).
|
|
21
|
+
|
|
22
|
+
## Checklist
|
|
23
|
+
- [ ] Docs are up to date for the changed public surface
|
|
24
|
+
- [ ] Examples **work** (copy-paste tested / mentally traced)
|
|
25
|
+
- [ ] Dead/misleading wording removed (no leftover old name/parameter)
|
|
26
|
+
- [ ] New configuration/env documented (default + whether required)
|
|
27
|
+
- [ ] Scope minimal — not a repetition of the code, but "why/how to use it"
|
|
28
|
+
- [ ] No secret/real credential in the docs (use a placeholder)
|
|
29
|
+
|
|
30
|
+
## How
|
|
31
|
+
1. **Identify the changed surface** — extract the public signature/behavior diff from the diff.
|
|
32
|
+
2. **Find the right doc** — README, `docs/`, docstring, OpenAPI, command `--help`. If there is more than one, update them all.
|
|
33
|
+
3. **Write**: what it does · how it is called (example) · input/output · limits/error cases. Short and correct.
|
|
34
|
+
4. **Verify the examples** — does the command/code example actually run.
|
|
35
|
+
5. **Clean up the old** — delete references to removed APIs/parameters.
|
|
36
|
+
6. **Translation**: if the user-visible doc is multilingual, coordinate with `i18n-integrity`.
|
|
37
|
+
|
|
38
|
+
## Principles
|
|
39
|
+
- **Single source** — behavior lives in the code; the doc *explains* it, does not copy it (a copied doc goes stale).
|
|
40
|
+
- **Example > paragraph** — a working example beats three paragraphs.
|
|
41
|
+
- **Minimal** — don't write a giant doc no one will maintain; answer the most-asked question.
|
|
42
|
+
|
|
43
|
+
## Co-authoring a longer doc (guide, tutorial, README rewrite)
|
|
44
|
+
For a substantial doc the content lives in the author's head, not the diff — don't guess it. Interview for intent
|
|
45
|
+
and audience, draft in passes, and let the user steer before you polish. The collaborative workflow (audience/intent
|
|
46
|
+
questions, outline-first, section-by-section drafting, revision loop): **`references/coauthoring.md`**.
|
|
47
|
+
|
|
48
|
+
## Invariant rules
|
|
49
|
+
1. **Correctness > completeness** — don't write a wrong doc; if unsure, flag it/ask.
|
|
50
|
+
2. **Examples must work.**
|
|
51
|
+
3. **Leave no stale/dead docs.**
|
|
52
|
+
4. **No secret/real credential** — use a placeholder (aligned with §4).
|
|
53
|
+
5. **Don't repeat the code** — don't just copy the signature; explain the usage.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Co-authoring a longer doc
|
|
2
|
+
|
|
3
|
+
Loaded on demand. For a substantial doc (a guide, tutorial, concept explainer, README rewrite) — not a mechanical
|
|
4
|
+
sync of a changed signature. Here the content lives in the author's head; your job is to draw it out and shape it,
|
|
5
|
+
not to invent it.
|
|
6
|
+
|
|
7
|
+
## Why not just write it
|
|
8
|
+
|
|
9
|
+
A generated long-form doc that no one steered reads as generic, states things the maintainer wouldn't, and quietly
|
|
10
|
+
guesses at intent — then it has to be rewritten anyway. Co-authoring front-loads the cheap questions so the expensive
|
|
11
|
+
draft is right the first time.
|
|
12
|
+
|
|
13
|
+
## The workflow
|
|
14
|
+
|
|
15
|
+
1. **Interview first (before drafting).** Ask, don't assume:
|
|
16
|
+
- **Audience** — who reads this? (new user, integrator, contributor, ops) Their assumed knowledge sets the level.
|
|
17
|
+
- **Intent** — what should the reader be able to *do* after? One doc, one job.
|
|
18
|
+
- **Scope** — what's explicitly out of scope? (prevents the doc sprawling)
|
|
19
|
+
- **Voice/constraints** — house style, terminology, things that must/mustn't be said.
|
|
20
|
+
Keep it to the few questions whose answers actually change the draft — offer options where useful, don't interrogate.
|
|
21
|
+
|
|
22
|
+
2. **Outline, get sign-off.** Propose the section structure (headings + one line each) and confirm it *before* writing prose. Re-structuring an outline is cheap; re-structuring 800 words is not.
|
|
23
|
+
|
|
24
|
+
3. **Draft in passes, not one wall.** Write section by section (or a first full pass explicitly marked as a draft). Surface the open questions inline (`> TODO: confirm default value`) rather than silently guessing — the maintainer fills the gaps you can't.
|
|
25
|
+
|
|
26
|
+
4. **Revision loop.** Invite specific feedback ("is the auth section at the right level?"), apply it, and keep the diff reviewable. Don't rewrite wholesale on each round — converge.
|
|
27
|
+
|
|
28
|
+
5. **Then apply the base discipline.** Once the content is agreed, the SKILL.md rules take over: examples must actually run, no stale/dead wording, no real secrets, single-source (explain the code, don't copy it), and coordinate `i18n-integrity` if it's multilingual.
|
|
29
|
+
|
|
30
|
+
## Keep it honest
|
|
31
|
+
|
|
32
|
+
- Don't state capabilities, defaults, or roadmap you haven't verified — ask or mark as TODO.
|
|
33
|
+
- Don't pad to look thorough; the maintainer maintains every sentence you add.
|
|
34
|
+
- The author's voice wins over yours — you're drafting *their* doc, not authoring your own.
|