@launchsecure/launch-kit 0.0.33 → 0.0.35

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.
@@ -1,30 +1,32 @@
1
1
  ---
2
- description: End-to-end ship flow — runs /kit:deploy-check, prints the exact commit list, pushes the named branch only after explicit confirmation, then appends a structured entry to .launchsecure/ship-log.ndjson describing what shipped. Pushing to a deploy-tracked branch (default master) triggers Vercel automatically; this skill never calls `vercel deploy` directly. No LS Comm Hub post the local log is the source of truth and can be republished later by a separate skill.
2
+ description: End-to-end ship flow — runs /kit:deploy-check, prints the exact commit list, authors a shipping-log Brief (doc/shipping-logs/*.md) that joins the push payload, then pushes the named branch only after explicit confirmation. The Brief publishes itself to the LS Comm Hub via file-content-sync when the push lands no separate post, no local log file. Stack-agnostic: detects the project's migrations tooling and deploy platform at runtime rather than assuming any one stack. Never calls a host's deploy CLI directly pushing to a deploy-tracked branch lets the host's git integration deploy.
3
3
  ---
4
4
 
5
5
  # /kit:ship
6
6
 
7
- Compose `/kit:deploy-check` + `git push` + local ship-log append into one gated workflow. The skill is the human checklist for "I'm shipping now" — same checks a careful human would run, encoded so they cannot be skipped or reordered.
7
+ Compose `/kit:deploy-check` + shipping-log Brief + `git push` into one gated workflow. The skill is the human checklist for "I'm shipping now" — same checks a careful human would run, encoded so they cannot be skipped or reordered.
8
8
 
9
- The ship log at `.launchsecure/ship-log.ndjson` is the durable record of what shipped, when, and by whom. Each successful ship appends one JSON object. A future skill (`/kit:ship-publish` or similar) can tail this log and emit an internal activity update / Comm Hub post / changelog page but that's a separate concern. This skill writes the log; it does not publish.
9
+ This skill ships with `launch-kit init` into **any** project Node, Python, Ruby, Go, Rust, a static site, a library so it makes **no stack assumptions**. It detects the migrations tooling and deploy platform at runtime (Preflight step 7) and adapts; anything it can't detect, it degrades to a safe, platform-neutral path rather than referencing a tool the repo doesn't have.
10
10
 
11
- Hard rule from project memory: **never push commits the user didn't explicitly name**, and **pushing to master = auto-deploy** so this skill always asks per-action before pushing, even when invoked with arguments. There is no `--yes-i-mean-it` flag.
11
+ The shipping log is a **Brief**: a markdown doc at `doc/shipping-logs/<date>-<target>-<sha>.md`, committed as part of the ship itself. If the project syncs its docs to LaunchSecure (the `shipping-logs` doc category mapped to the discussion handler — the same file-content-sync mechanism the LS server uses for any `doc/**` category), the moment the push lands the doc materializes as a Comm Hub comment under the "Shipping Logs" header — team-visible, branch-aware, no separate publish step. If the project is not wired to LaunchSecure, the Brief is still a durable in-repo record of every ship. The repo records what shipped; the Comm Hub renders it when present. One artifact, one or two surfaces.
12
+
13
+ Hard rule from project memory: **never push commits the user didn't explicitly name**, and **pushing to a deploy-tracked branch may auto-deploy** — so this skill always asks per-action before pushing, even when invoked with arguments. There is no `--yes-i-mean-it` flag.
12
14
 
13
15
  Parse `$ARGUMENTS`:
14
16
 
15
17
  - (empty) — full flow on the current branch → its upstream (resolved via `git rev-parse --abbrev-ref @{u}` or, missing that, `origin/<current-branch>`).
16
- - **--to=&lt;branch&gt;** — push to a different remote branch (e.g. `--to=staging-mirror`). The skill never creates a new remote branch silently — if the target doesn't exist on origin, surface that and ask.
17
- - **--dry-run** — run deploy-check + build the ship-log entry preview, but do NOT push and do NOT append to the log. Useful for "what would shipping look like right now?".
18
- - **--note=&lt;text&gt;** — free-form human note attached to the log entry's `note` field. Useful for hotfix context, rollout caveats, or named reviewer.
18
+ - **--to=&lt;branch&gt;** — push to a different remote branch (e.g. `--to=staging`). The skill never creates a new remote branch silently — if the target doesn't exist on origin, surface that and ask.
19
+ - **--dry-run** — run deploy-check + build the shipping-log Brief preview, but do NOT write the doc, do NOT commit, do NOT push. Useful for "what would shipping look like right now?".
20
+ - **--note=&lt;text&gt;** — free-form human note rendered as the Brief's `**Note**:` line. Useful for hotfix context, rollout caveats, or named reviewer.
19
21
  - **--skip-check=&lt;list&gt;** — forwarded verbatim to `/kit:deploy-check --skip=<list>`. Surface skipped checks in the final report; do not let `--skip-check=build` slip through silently.
20
22
  - **--quick** — forwarded to deploy-check (skips the slow `build` step). Refuse if the target is a deploy-tracked branch (`master` or whatever the repo's default is) — for prod, build must run.
21
23
 
22
24
  Examples:
23
25
 
24
- - `/kit:ship` → check, confirm, push current branch to its upstream, append ship-log entry.
25
- - `/kit:ship --dry-run` → check + preview the log entry only; no push, no log write.
26
+ - `/kit:ship` → check, author Brief, confirm, push current branch (including the Brief) to its upstream.
27
+ - `/kit:ship --dry-run` → check + preview the Brief only; no writes, no push.
26
28
  - `/kit:ship --to=master` from `implementation` → cross-branch merge-and-deploy flow (see "Cross-branch ship" below).
27
- - `/kit:ship --note="hotfix for feedback widget 500s"` → push + log entry with the note attached.
29
+ - `/kit:ship --note="hotfix for feedback widget 500s"` → Brief carries the note.
28
30
 
29
31
  ## Preflight
30
32
 
@@ -42,10 +44,17 @@ Examples:
42
44
  ship: <current-branch> → <target-branch> (prod: yes|no)
43
45
  ```
44
46
  5. Stash check — if `git status --porcelain` shows uncommitted changes, refuse with `✗ working tree not clean — commit, stash, or discard before shipping`. Shipping with dirty state is never the right move; the deploy-check skill is lenient about it because it's read-only, this skill is not.
47
+ 6. Confirm the shipping-logs category exists — `doc/shipping-logs/` directory present OR `shipping-logs` listed in the project's docCategories config (e.g. a `.launch-secure.config` / launch-kit config). If neither, warn: `⚠ shipping-logs doc category not registered — the Brief is still written to the repo, but won't sync to the Comm Hub` and continue.
48
+ 7. **Detect the project stack** (this is what makes the skill portable — do it once here, reuse in steps 3 and 5). Probe the working tree; do not assume:
49
+ - **`MIGRATIONS_DIR`** — the first of these that exists, else `none`:
50
+ `prisma/migrations/` (Prisma) · `supabase/migrations/` (Supabase) · `drizzle/` or a dir containing `drizzle/meta/` (Drizzle) · `db/migrate/` (Rails/ActiveRecord) · `migrations/` with `alembic.ini` nearby or `alembic/versions/` (Alembic) · `*/migrations/` under a Django app · `src/main/resources/db/migration/` (Flyway) · a knex/TypeORM/Sequelize migrations dir named in the repo's ORM config. If a launch-kit/project config declares an explicit `migrationsPath`, that wins over detection. If none match, `MIGRATIONS_DIR=none` → the migration gate (step 3) is skipped entirely. Non-DB projects (libraries, CLIs, static sites) legitimately have no migrations.
51
+ - **`BACKUP_STORY`** — detect what protects a migration: a backup script (`scripts/*backup*`, `scripts/migrate-with-backup.*`) and/or a CI workflow (`.github/workflows/*backup*`, `*migrat*`, or the equivalent under `.gitlab-ci.yml` / other CI). Record whatever exists, or `none`.
52
+ - **`DEPLOY_PLATFORM`** — the first marker that matches, else `unknown`:
53
+ `vercel.json` / `.vercel/` → `vercel` · `netlify.toml` → `netlify` · `fly.toml` → `fly` · `render.yaml` → `render` · `railway.json`/`railway.toml` → `railway` · an app-deploying workflow under `.github/workflows/` → `github-actions` · a `Dockerfile` with no platform marker → `container` · none → `unknown`. Drives the post-push guidance in step 5; never hardcode a single host.
45
54
 
46
55
  ## Step 1 — Gate (run /kit:deploy-check)
47
56
 
48
- Invoke `/kit:deploy-check` against the resolved target. Forward `--quick` / `--skip-check=` / `--target=`. Do NOT re-implement any check here — single source of truth lives in `deploy-check/SKILL.md`.
57
+ Invoke `/kit:deploy-check` against the resolved target. Forward `--quick` / `--skip-check=` / `--target=`. Do NOT re-implement any check here — single source of truth lives in `deploy-check/SKILL.md`, which does its own stack detection (typecheck/build/migration-drift adapt to the detected toolchain).
49
58
 
50
59
  Verdict handling:
51
60
 
@@ -57,7 +66,7 @@ Verdict handling:
57
66
  Hard rules:
58
67
 
59
68
  - If `IS_PROD_PUSH=true` and the user passed `--quick` or `--skip-check=build`, refuse before running deploy-check: `✗ --quick / --skip-check=build are not allowed for prod pushes. Re-run without them.`
60
- - If deploy-check's `migration_safety` row is anything other than ✓, treat as NO-GO regardless of overall verdict. Migration corruption incidents are why the project has the three-layer migration rule in CLAUDE.md — this skill enforces it.
69
+ - If deploy-check reports a `migration_safety` row (it only does when the project has migrations) and it is anything other than ✓, treat as NO-GO regardless of overall verdict. Migration corruption is why the project carries a migration-safety discipline — this skill enforces it when migrations exist, and is silent about it when they don't.
61
70
 
62
71
  ## Step 2 — Show the exact push payload
63
72
 
@@ -71,6 +80,7 @@ About to push <current-branch> → origin/<target-branch>:
71
80
 
72
81
 
73
82
  <N> commits, <M> files changed (<+adds>/<-dels>)
83
+ (+ 1 shipping-log Brief commit, authored in step 4)
74
84
  ```
75
85
 
76
86
  Generated from:
@@ -82,18 +92,24 @@ If the list is empty → abort: `nothing to push — local branch is at or behin
82
92
 
83
93
  If any commit author is not the current `git config user.email`, surface a `⚠ <N> commits authored by someone else (<emails>)` line — common in shared branches but worth flagging once.
84
94
 
85
- ## Step 3 — Migration backup gate
95
+ ## Step 3 — Migration backup gate (only when MIGRATIONS_DIR ≠ none)
96
+
97
+ If `MIGRATIONS_DIR=none` (detected in Preflight step 7), **skip this step entirely** — there's nothing to gate.
86
98
 
87
- If `git diff origin/<target>...HEAD --name-only -- prisma/migrations/` returns any files, surface:
99
+ Otherwise, if `git diff origin/<target>...HEAD --name-only -- <MIGRATIONS_DIR>` returns any files, surface:
88
100
 
89
101
  ```
90
102
  ⚠ This push includes <N> new migration(s):
91
- - prisma/migrations/<dir>/migration.sql
103
+ - <MIGRATIONS_DIR>/<file>
92
104
 
93
105
 
94
106
  Backup story:
95
- - CI: .github/workflows/backup-on-migration.yml runs pg_dump server-side once the migration lands on <target-branch>.
96
- - Local: if you have already applied locally, you've used scripts/migrate-with-backup.sh (per CLAUDE.md). If not, the CI workflow is the safety net.
107
+ <BACKUP_STORY, described concretely e.g.:>
108
+ - CI: <detected workflow> runs a DB dump server-side once the migration lands on <target-branch>.
109
+ - Local: if you applied locally, you used <detected backup script> per the repo's migration docs.
110
+ <or, if BACKUP_STORY=none:>
111
+ - ⚠ No automated pre-migration backup detected in this repo. Take a manual dump before this lands,
112
+ or add a backup step to CI. Recovery is impossible without one.
97
113
  ```
98
114
 
99
115
  Then ask:
@@ -102,122 +118,122 @@ Then ask:
102
118
 
103
119
  This is a deliberate friction point — typing a word is more honest than `y`. Do not accept `yes` here; require `understood`.
104
120
 
105
- ## Step 4 — Push confirmation
121
+ ## Step 4 — Author the shipping-log Brief
106
122
 
107
- Ask **exactly**:
123
+ Build the Brief BEFORE pushing so it ships inside the same payload — the record and the code land atomically, and (when synced) file-content-sync publishes both on the same webhook delivery.
108
124
 
109
- - If `IS_PROD_PUSH=false`:
110
- > "Push <N> commits to origin/<target-branch>? Reply `yes` to push, `cancel` to abort."
111
- - If `IS_PROD_PUSH=true`:
112
- > "Push <N> commits to origin/<target-branch>. This is a prod-tracked branch — Vercel will deploy automatically. Reply `ship it` to push, `cancel` to abort."
125
+ If `--dry-run`, build and show the Brief in a fenced block but do NOT write the file and do NOT commit.
113
126
 
114
- Accept exactly `yes` / `ship it` (case-insensitive). Anything else → treat as cancel. **Never** accept silence, `ok`, `sure`, free-form approval. If `--dry-run`, skip this step entirely.
115
-
116
- On confirm: `git push origin <current-branch>:<target-branch>`. Stream stdout. If the push fails (non-fast-forward, hook reject, auth) — surface the error verbatim, do not retry, do not `--force`.
117
-
118
- After a successful push to a deploy-tracked branch, surface the Vercel deployment lookup hint:
127
+ ### Path & naming
119
128
 
120
129
  ```
121
- Pushed. Vercel deploy will pick up the latest commit on <target-branch>.
122
- → check status: `vercel ls --prod` or watch the Vercel dashboard
123
- (do NOT run `vercel deploy` from here — that bypasses the GitHub→Vercel flow and double-deploys)
130
+ doc/shipping-logs/<YYYY-MM-DD>-<target-branch>-<shortsha>.md
124
131
  ```
125
132
 
126
- ## Step 5 Append ship-log entry
127
-
128
- After a successful push, append exactly one JSON line to `.launchsecure/ship-log.ndjson`. This is the durable record of the ship event the source of truth a future `/kit:ship-publish` (or any reader) consumes to emit activity feeds, changelogs, or Comm Hub posts.
129
-
130
- If `--dry-run`, build the entry and show it in a fenced block but do NOT write it. If the push failed in step 4, do NOT write the entry (the log only records successful ships).
131
-
132
- ### File location & format
133
-
134
- - Path: `.launchsecure/ship-log.ndjson` (sibling of `.launchsecure/graphs/`, `.launchsecure/beacon-*.ndjson`).
135
- - Format: NDJSON — one JSON object per line, append-only, never rewrite or compact.
136
- - If the file does not exist, create it. Do NOT add a header line; readers should tolerate empty file = zero ship events.
137
- - Ensure `.launchsecure/ship-log.ndjson` is gitignored — on first run, check `.gitignore` for `.launchsecure/ship-log.ndjson` or a covering pattern (`.launchsecure/*.ndjson`); if absent, surface a one-line warning suggesting the user add it. Do NOT silently edit `.gitignore` — that's a separate user decision per project memory on extending vs adding things.
138
-
139
- ### Entry schema
140
-
141
- Every entry conforms to schema `v: 1`. Forward-compatible reader rule: ignore unknown fields, never break on missing optional fields.
142
-
143
- ```json
144
- {
145
- "v": 1,
146
- "shipId": "<ulid or `${ts}-${shortSha}`>",
147
- "ts": "2026-05-29T12:45:00.000Z",
148
- "branch": "implementation",
149
- "target": "master",
150
- "isProdPush": true,
151
- "author": { "name": "Prajyot", "email": "prajyot@automatewith.us" },
152
- "commits": [
153
- { "sha": "abc1234", "subject": "feat(ship): add ship skill", "author": "Prajyot", "ts": "2026-05-29T12:30:00.000Z" }
154
- ],
155
- "stats": { "files": 3, "insertions": 187, "deletions": 0 },
156
- "migrations": ["prisma/migrations/2026_05_29_add_ship_log/migration.sql"],
157
- "envVarsAdded": ["FEED_BACKEND_URL"],
158
- "deployCheck": {
159
- "verdict": "GO",
160
- "warnings": ["lint: 12 warnings"],
161
- "skipped": []
162
- },
163
- "themes": [
164
- { "scope": "ship", "outcomes": ["End-to-end ship skill with deploy-check gate and append-only ship log"] }
165
- ],
166
- "summary": "Shipped 1 commit to master — ship skill scaffold",
167
- "note": "<--note value, or null>",
168
- "pushedAt": "2026-05-29T12:45:00.000Z",
169
- "vercelDeployHint": "auto-triggered on push to master"
170
- }
133
+ - `<YYYY-MM-DD>` — ship date (local date).
134
+ - `<target-branch>` — slugified target (e.g. `master`, `implementation`).
135
+ - `<shortsha>` short sha of the current HEAD (pre-Brief-commit). Keeps same-day ships to the same target from colliding, and makes the filename greppable back to the commit range.
136
+
137
+ One ship = one new file. Never append to or rewrite a previous ship's Brief corrections happen in a new `## Update <ISO-date>` section added to the SAME ship's Brief.
138
+
139
+ ### Brief format
140
+
141
+ Plain markdown, no HTML, no emojis (project comms convention). Frontmatter carries only the title — the discussion handler manages its own sync fields.
142
+
143
+ ```markdown
144
+ ---
145
+ title: Ship <YYYY-MM-DD> — <branch> → <target>: <one-line summary>
146
+ ---
147
+
148
+ **Shipped**: <ISO timestamp>
149
+ **Branch**: <branch> → <target>
150
+ **Prod push**: yes | no
151
+ **Commits**: <N> (<oldest-sha>..<newest-sha>)
152
+ **Deploy check**: <verdict>
153
+ **Note**: <--note value — omit this line entirely when absent>
154
+
155
+ ## Summary
156
+
157
+ <One paragraph: what this ship is, why it went out, what a teammate needs to know.>
158
+
159
+ ## What shipped
160
+
161
+ ### <theme scope, e.g. db-providers>
162
+ - <outcome bullet short product-facing sentence, NOT a raw commit subject>
163
+ - <outcome bullet>
164
+
165
+ ### <next theme>
166
+ -
167
+
168
+ ## Migrations
169
+
170
+ <List each migration path + its backup story, or "None.">
171
+
172
+ ## Deploy check
173
+
174
+ <verdict>
175
+ - <warning lines from the deploy-check report, verbatim>
176
+
177
+ ## Commits
178
+
179
+ - `<shortsha>` <subject>
180
+ - `<shortsha>` <subject>
181
+ - …
171
182
  ```
172
183
 
173
184
  Field sources:
174
185
 
175
- - `shipId`generate `${pushedAt-as-yyyymmdd-hhmmss}-${shortSha-of-HEAD}` for human-greppable IDs. ULID is fine if a generator is available.
176
- - `ts` / `pushedAt`current ISO timestamp at the moment the entry is written (just after `git push` succeeds).
177
- - `branch` / `target` / `isProdPush` from preflight (step 1).
178
- - `author``git config user.name` + `git config user.email`.
179
- - `commits` — `git log --pretty=format:'%H%x09%s%x09%an%x09%aI' origin/<target>..HEAD` (use the pre-push range — these are the commits that just landed).
180
- - `stats` — `git diff --shortstat origin/<target>...<sha>` where `<sha>` is the new HEAD.
181
- - `migrations` — `git diff --name-only origin/<target>...HEAD -- prisma/migrations/` (if step 3 fired, surface those exact paths; else empty array).
182
- - `envVarsAdded` — pulled from deploy-check's `env_coverage` row, parsed from its `detail`. If deploy-check was skipped or didn't run that check, `[]`.
183
- - `deployCheck.verdict` / `warnings` / `skipped` — from the deploy-check report in step 1.
184
- - `themes` — group commits by conventional-commit scope (same default rule as `/kit:standup` step 3). Each theme: `{ scope, outcomes: [<outcome-bullets translated from commit subjects>] }`. Outcomes are short product-facing sentences, NOT raw subject lines. If commits lack scopes and there are ≥20 changed files across ≥5 dirs, escalate to launch-chart module grouping (same rule as standup step 3); otherwise bucket into `{scope: "misc", outcomes: […]}`.
185
- - `summary` — one-line human-readable. Format: `Shipped <N> commit(s) to <target>${migrations ? " — includes migration" : ""}${themes ? " — " + topTheme : ""}`.
186
- - `note` — verbatim `--note=` value, or `null` if absent.
187
- - `vercelDeployHint` — `"auto-triggered on push to <target>"` if `isProdPush`, else `"no auto-deploy (target is not deploy-tracked)"`.
186
+ - **Theme grouping** group commits by conventional-commit scope (same default rule as `/kit:standup` step 3). Outcomes are short product-facing sentences. If commits lack scopes and there are ≥20 changed files across ≥5 dirs, escalate to launch-chart module grouping; otherwise bucket into a `misc` theme.
187
+ - **Commits / stats**`git log --pretty=format:'%h%x09%s' origin/<target>..HEAD` + `git diff --shortstat origin/<target>...HEAD`.
188
+ - **Migrations** if `MIGRATIONS_DIR none`: `git diff --name-only origin/<target>...HEAD -- <MIGRATIONS_DIR>` (exactly what step 3 saw); else `None.`
189
+ - **Deploy check** verdict + warnings from step 1's report.
188
190
 
189
191
  ### Preview + confirm
190
192
 
191
- After step 4's push succeeds, build the entry and show it pretty-printed in a fenced code block:
193
+ Show the full Brief in a fenced block, then ask:
192
194
 
193
- ```
194
- About to append to .launchsecure/ship-log.ndjson:
195
+ > "Write this Brief to `doc/shipping-logs/<filename>` and commit it? Reply `yes` to write + commit, `edit` to tweak (summary, themes, note), `skip` to ship without a log, or `cancel` to abort the ship."
195
196
 
196
- {
197
- "v": 1,
198
- "shipId": "20260529-124500-abc1234",
199
-
200
- }
197
+ - `yes` / `y` → write the file, then `git add doc/shipping-logs/<filename> && git commit -m "docs(shipping-logs): ship <YYYY-MM-DD> — <target> (<one-line summary>)"`. The Brief commit is now the payload's tip.
198
+ - `edit` → ask what to change, redraft, re-show, re-ask.
199
+ - `skip` → no file, no commit. The ship proceeds unlogged — say so plainly: `shipping without a log entry; there will be no record of this ship.`
200
+ - `cancel` → abort the entire ship. Nothing was written, nothing was pushed.
201
201
 
202
- Append? Reply `yes` to write, `edit summary` to tweak the summary or themes, `skip` to ship without logging, or `cancel` to abort the append.
203
- ```
202
+ ## Step 5 Push confirmation
204
203
 
205
- - `yes` / `y` → append the entry as a single minified line (`JSON.stringify(entry) + "\n"`) using `>> .launchsecure/ship-log.ndjson`. Confirm with `appended shipId=<id>`.
206
- - `edit summary` → ask what to change (summary text, theme grouping, note), redraft the entry, re-show, re-ask.
207
- - `skip` → do not write. Print the entry once more so the user can manually save it elsewhere if they want. Ship is still considered successful — the push landed.
208
- - `cancel` → do not write. Same as `skip` but no echo. **Do not** attempt to revert the push — surface clearly: `Push already landed on origin/<target>; cancel only stops the log append.`
204
+ Ask **exactly**:
209
205
 
210
- ### Atomicity
206
+ - If `IS_PROD_PUSH=false`:
207
+ > "Push <N> commits to origin/<target-branch>? Reply `yes` to push, `cancel` to abort."
208
+ - If `IS_PROD_PUSH=true`:
209
+ > "Push <N> commits to origin/<target-branch>. This is a deploy-tracked branch — <if DEPLOY_PLATFORM≠unknown: '<platform> will deploy automatically'; else: 'whatever deploy pipeline is wired to this branch will pick it up'>. Reply `ship it` to push, `cancel` to abort."
211
210
 
212
- Append must be atomic partial writes corrupt the NDJSON file. Use:
211
+ `<N>` includes the Brief commit from step 4 (when not skipped). Accept exactly `yes` / `ship it` (case-insensitive). Anything else → treat as cancel. **Never** accept silence, `ok`, `sure`, free-form approval. If `--dry-run`, skip this step entirely.
213
212
 
214
- ```
215
- echo "<minified-json>" >> .launchsecure/ship-log.ndjson
216
- ```
213
+ On confirm: `git push origin <current-branch>:<target-branch>`. Stream stdout. If the push fails (non-fast-forward, hook reject, auth) — surface the error verbatim, do not retry, do not `--force`.
217
214
 
218
- NOT `cat ... > tmp && mv tmp ...` (rewrite is wrong for an append-only log). NOT multi-line JSON. One line, one ship, one append.
215
+ If the push fails AND a Brief commit was created in step 4, leave it in place it's a normal local commit; the user resolves the push problem and re-runs `/kit:ship` (step 2 will show it as part of the payload).
219
216
 
220
- If the write fails (disk full, permission), surface the entry verbatim in chat so the user can save it manually, and exit non-zero. Do NOT retry.
217
+ After a successful push:
218
+
219
+ - The Brief is now live in the repo. If the project is wired to LaunchSecure, file-content-sync picks it up from the webhook delivery and materializes the Comm Hub comment under the "Shipping Logs" category (branch-aware: a ship to a non-default branch shows with a branch pill until it reaches the default branch).
220
+ - If `IS_PROD_PUSH=true`, surface deploy guidance **for the detected platform** (`DEPLOY_PLATFORM`), never a hardcoded host:
221
+
222
+ | DEPLOY_PLATFORM | Post-push line |
223
+ |---|---|
224
+ | `vercel` | `Vercel's git integration will deploy the latest commit on <target>. Check: vercel ls --prod. Do NOT run vercel deploy from here — it bypasses the git→Vercel flow and double-deploys.` |
225
+ | `netlify` | `Netlify's git integration will build the latest commit on <target>. Check the Netlify dashboard / netlify status. Do NOT run netlify deploy --prod from here.` |
226
+ | `fly` | `If a deploy GitHub Action is wired, it will run flyctl deploy on push. Otherwise a maintainer runs it. Check: fly status.` |
227
+ | `render` / `railway` | `<platform>'s git integration will deploy the latest commit on <target>. Check the <platform> dashboard.` |
228
+ | `github-actions` | `The deploy workflow under .github/workflows/ will run on push to <target>. Check: gh run list --branch <target>.` |
229
+ | `container` / `unknown` | `Push complete. This repo has no detected managed-deploy integration — whatever CI/CD or manual process is wired to <target> takes over from here. This skill does not deploy.` |
230
+
231
+ ```
232
+ Pushed.
233
+ <platform-specific line from the table above>
234
+
235
+ Shipping log written: doc/shipping-logs/<filename><if synced: → Comm Hub "Shipping Logs">
236
+ ```
221
237
 
222
238
  ## Cross-branch ship (--to=master from a feature branch)
223
239
 
@@ -226,49 +242,49 @@ When the user runs `/kit:ship --to=master` from a non-master branch, the flow is
226
242
  1. After deploy-check passes, before step 2, verify the current branch is ahead of `origin/master` (`git rev-list --left-right --count origin/master...HEAD` → left=behind, right=ahead). If behind, abort: `✗ <current> is behind origin/master by <N>; rebase or merge master first`.
227
243
  2. The push command becomes `git push origin <current-branch>:master`. Make sure step 2's "About to push" preamble shows this asymmetric refspec so the user sees it.
228
244
  3. On non-fast-forward reject from origin, do NOT suggest `--force`. Surface and stop.
245
+ 4. The Brief's `**Branch**:` line shows the asymmetric flow (`<current> → master`) and its filename uses `master` as the target slug.
229
246
 
230
247
  ## Constraints
231
248
 
232
- - **Never auto-push and auto-log in one breath.** Always two explicit confirmations (step 4 push, step 5 append). The `--dry-run` flag exists instead of bundling.
233
- - **Never call `vercel deploy` CLI.** Deploys come from Vercel's GitHub integration on push to deploy-tracked branches. Calling the CLI double-deploys and bypasses the gate.
249
+ - **Never auto-commit and auto-push in one breath.** Always two explicit confirmations (step 4 Brief commit, step 5 push). The `--dry-run` flag exists instead of bundling.
250
+ - **Never call a host's deploy CLI** (`vercel deploy`, `netlify deploy`, `flyctl deploy`, etc.). Managed deploys come from the host's git integration on push to a deploy-tracked branch. Calling the CLI double-deploys and bypasses the gate. The one exception is a project whose `DEPLOY_PLATFORM` is genuinely CLI-only with no git integration — and even then, this skill stops at the push and leaves the deploy to the user.
234
251
  - **Never `git push --force` / `--force-with-lease`.** If a push fails, the user investigates.
235
252
  - **Never `git push <branch>` without an explicit refspec.** Always `<local-branch>:<remote-branch>` so the destination is unambiguous in the printed command (and in shell history).
236
- - **Never bypass migration_safety.** A ✓ on the deploy-check migration_safety row is the gate; anything else is NO-GO even if the overall verdict isn't.
253
+ - **Never bypass migration_safety when migrations exist.** A ✓ on the deploy-check migration_safety row is the gate; anything else is NO-GO. When the project has no migrations, there is no such row and nothing to bypass.
254
+ - **No stack assumptions.** Everything stack-specific (migrations tooling, deploy host, backup story) is resolved by detection in Preflight step 7. If detection comes up empty, degrade to the neutral path — never reference a tool, path, or command the repo doesn't have.
237
255
  - **Single source of truth for checks.** Do not duplicate `/kit:deploy-check` logic inline; invoke it.
238
- - **Append-only log.** Never edit, delete, or rewrite past entries in `ship-log.ndjson`. Corrections happen via a new entry referencing the previous `shipId` (e.g. `correctionOf: "<prev shipId>"` readers handle the merge).
239
- - **Log only successful ships.** If `git push` fails, do NOT write an entry. The log records what actually landed on the remote, not what was attempted.
240
- - **No publishing from this skill.** This skill writes the log; it does NOT post to LS Comm Hub, does NOT update activity feeds, does NOT trigger webhooks. Those are downstream consumers (future `/kit:ship-publish` or a server-side tailer).
241
- - **Honest about deploy state.** This skill ends when the push completes and the log entry lands. It does NOT wait for Vercel to finish building, does NOT verify the prod URL is up, does NOT roll back. If the user needs deploy-monitoring, point them at `vercel ls --prod` or the Vercel dashboard.
242
- - **Dry-run is truly dry.** `--dry-run` must NOT call `git push`, must NOT write to `ship-log.ndjson`. The preview is shown in chat only.
256
+ - **One ship, one Brief.** Never edit, delete, or rewrite a previous ship's Brief. Corrections go in an `## Update <ISO-date>` section appended to that ship's own Brief in a follow-up commit.
257
+ - **Log only what actually ships.** The Brief is committed before the push, but it only publishes when the push lands — if the user cancels at step 5, tell them the Brief commit is still local and will ride along with the next ship (or can be dropped by them; never drop it yourself).
258
+ - **Honest about deploy state.** This skill ends when the push completes. It does NOT wait for a build to finish, does NOT verify the prod URL is up, does NOT roll back. Point the user at the detected platform's status command for deploy-monitoring.
259
+ - **Dry-run is truly dry.** `--dry-run` must NOT write any file, must NOT commit, must NOT push. The preview is shown in chat only.
260
+ - **Respect doc/ ownership rules.** Only ever write under `doc/shipping-logs/`. Never touch `doc/ideas/` (personal scratch) or other categories from this skill.
243
261
 
244
262
  ## Idempotency
245
263
 
246
264
  Re-running `/kit:ship` after a successful run:
247
265
 
248
- - Step 2 will show "nothing to push" if the previous push landed cleanly — the natural no-op. No new log entry results.
249
- - Each successful ship is a separate line in `ship-log.ndjson`. There is no per-day or per-branch coalescing — that's a reader's job. If a hotfix ships 10 minutes after a feature, both entries exist independently.
250
- - The preview is never persisted before confirmation; cancelling at any step leaves no partial entry behind.
266
+ - Step 2 will show "nothing to push" if the previous push landed cleanly — the natural no-op. No new Brief results.
267
+ - Each successful ship is a separate file in `doc/shipping-logs/`. There is no per-day or per-branch coalescing — the Comm Hub renders them as separate entries. If a hotfix ships 10 minutes after a feature, both Briefs exist independently.
268
+ - A Brief committed but not pushed (cancelled at step 5) is picked up by the next run: step 2 shows it in the payload, step 4 detects an existing un-pushed Brief for this range and offers to reuse or rewrite it instead of authoring a duplicate.
251
269
 
252
270
  ## Reading the log
253
271
 
254
- For ad-hoc inspection while the publisher skill doesn't exist yet:
272
+ The shipping history lives in the repo, and (when synced) in the Comm Hub:
255
273
 
256
274
  ```
257
- # last ship
258
- tail -n 1 .launchsecure/ship-log.ndjson | jq
259
-
260
- # ships to master in the last 7 days
261
- jq -c 'select(.target == "master" and (.ts | fromdateiso8601) > (now - 7*86400))' .launchsecure/ship-log.ndjson
275
+ # repo side — every ship, newest first
276
+ ls -t doc/shipping-logs/
262
277
 
263
- # count ships per day
264
- jq -r '.ts[:10]' .launchsecure/ship-log.ndjson | sort | uniq -c
278
+ # one ship's full record
279
+ cat doc/shipping-logs/<file>.md
265
280
  ```
266
281
 
267
- These are documented for the user, not for this skill `/kit:ship` itself never reads back the log.
282
+ Comm Hub side (when the project is LaunchSecure-wired): the "Shipping Logs" category header in the LS sidebar lists every published ship Brief with the BookOpen icon. Filter chips (Published / In Development) follow the branch-aware brief tiers.
268
283
 
269
284
  ## Notes for the assistant
270
285
 
271
286
  - The user has explicit project-memory rules against unauthorised pushes and dumb reverts. Treat every confirmation as load-bearing — don't paraphrase the prompt wording, don't accept ambiguous approval.
272
- - "Ship" is the user's verb for "I want this in production." If the target isn't actually deploy-tracked, the skill should still work but should say so clearly in the scope line (step 4 phrasing changes).
273
- - The ship log is the durable artifact. Future skills will read from itkeep the schema stable, additive only. Bump `v:` when you make a breaking schema change and update this file's schema block in the same commit.
274
- - This skill assumes `/kit:deploy-check` exists. If missing (older launch-kit install), surface that as a preflight error pointing to `npx @launchsecure/launch-kit init`. No dependency on `/kit:standup` anymore the log replaces the Comm Hub post.
287
+ - "Ship" is the user's verb for "I want this in production." If the target isn't actually deploy-tracked, the skill should still work but should say so clearly in the scope line (step 5 phrasing changes).
288
+ - The shipping-log Brief is the durable artifact and (when synced) the published record at once there is no separate "publish" step and no local NDJSON file. If you find `.launchsecure/ship-log.ndjson` in a repo, it's from a pre-Brief version of this skill; leave it alone (don't migrate, don't delete) and mention it once.
289
+ - **Portability is the contract.** This skill is installed by `launch-kit init` into projects on stacks you can't predict. Anything you hardcode that isn't in *this* repo is a bug for the next repo. When in doubt, detect or degrade — never assume.
290
+ - This skill assumes `/kit:deploy-check` exists. If missing (older launch-kit install), surface that as a preflight error pointing to `npx @launchsecure/launch-kit init`.