@launchsecure/launch-kit 0.0.26 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/beacon/beacon.mjs +1003 -440
  2. package/dist/beacon/beacon.mjs.map +1 -1
  3. package/dist/beacon/beacon.umd.js +45 -24
  4. package/dist/beacon/beacon.umd.js.map +1 -1
  5. package/dist/beacon/types/capture/events.d.ts +20 -0
  6. package/dist/beacon/types/capture/events.d.ts.map +1 -0
  7. package/dist/beacon/types/element.d.ts +1 -0
  8. package/dist/beacon/types/element.d.ts.map +1 -1
  9. package/dist/beacon/types/index.d.ts +2 -1
  10. package/dist/beacon/types/index.d.ts.map +1 -1
  11. package/dist/beacon/types/monitor/dom.d.ts +13 -0
  12. package/dist/beacon/types/monitor/dom.d.ts.map +1 -0
  13. package/dist/beacon/types/monitor/index.d.ts +19 -0
  14. package/dist/beacon/types/monitor/index.d.ts.map +1 -0
  15. package/dist/beacon/types/monitor/network.d.ts +12 -0
  16. package/dist/beacon/types/monitor/network.d.ts.map +1 -0
  17. package/dist/beacon/types/monitor/transport.d.ts +27 -0
  18. package/dist/beacon/types/monitor/transport.d.ts.map +1 -0
  19. package/dist/beacon/types/monitor/types.d.ts +117 -0
  20. package/dist/beacon/types/monitor/types.d.ts.map +1 -0
  21. package/dist/beacon/types/types.d.ts +10 -0
  22. package/dist/beacon/types/types.d.ts.map +1 -1
  23. package/dist/beacon/types/ui/drawer.d.ts +3 -1
  24. package/dist/beacon/types/ui/drawer.d.ts.map +1 -1
  25. package/dist/beacon/types/ui/monitor-panel.d.ts +19 -0
  26. package/dist/beacon/types/ui/monitor-panel.d.ts.map +1 -0
  27. package/dist/chart-client/assets/index-CJ4mgRRF.css +1 -0
  28. package/dist/chart-client/assets/{index-Bk1hawjD.js → index-Ccy-DpI-.js} +46 -42
  29. package/dist/chart-client/index.html +2 -2
  30. package/dist/client/assets/index-DI5qSR_w.css +32 -0
  31. package/dist/client/assets/index-Dp0_okva.js +294 -0
  32. package/dist/client/index.html +2 -2
  33. package/dist/council-client/assets/index-C_-vAM9L.css +1 -0
  34. package/dist/council-client/index.html +2 -2
  35. package/dist/deck-client/assets/{_baseUniq-C2xT_eYu.js → _baseUniq-W2JQDmje.js} +1 -1
  36. package/dist/deck-client/assets/{arc-CmVL9pGd.js → arc-DIBWAId9.js} +1 -1
  37. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-BSFgdjve.js → architectureDiagram-Q4EWVU46-CAIRMvJK.js} +1 -1
  38. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-DuLzscvP.js → blockDiagram-DXYQGD6D-BeNaNiOi.js} +1 -1
  39. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CfCJB8eY.js → c4Diagram-AHTNJAMY-B9Ozi62h.js} +1 -1
  40. package/dist/deck-client/assets/channel-CRdozqbp.js +1 -0
  41. package/dist/deck-client/assets/{chunk-4BX2VUAB-DxmLYTWZ.js → chunk-4BX2VUAB-D7AZ47dt.js} +1 -1
  42. package/dist/deck-client/assets/{chunk-4TB4RGXK-CCnf7GFE.js → chunk-4TB4RGXK-DnVnNPcI.js} +1 -1
  43. package/dist/deck-client/assets/{chunk-55IACEB6-Db9DApcj.js → chunk-55IACEB6-UKYs-YNd.js} +1 -1
  44. package/dist/deck-client/assets/{chunk-EDXVE4YY-DmYDq8ZI.js → chunk-EDXVE4YY-D43b-SKn.js} +1 -1
  45. package/dist/deck-client/assets/{chunk-FMBD7UC4-BGhUlF20.js → chunk-FMBD7UC4-QzBAoyyW.js} +1 -1
  46. package/dist/deck-client/assets/{chunk-OYMX7WX6-CpEnicQZ.js → chunk-OYMX7WX6-Cjif4r6W.js} +1 -1
  47. package/dist/deck-client/assets/{chunk-QZHKN3VN-Doa7LKwf.js → chunk-QZHKN3VN-CqLDirEI.js} +1 -1
  48. package/dist/deck-client/assets/{chunk-YZCP3GAM-CpkIlH6V.js → chunk-YZCP3GAM-_FQvmMs4.js} +1 -1
  49. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-lIZMp57W.js +1 -0
  50. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-lIZMp57W.js +1 -0
  51. package/dist/deck-client/assets/clone-BtWeSTyJ.js +1 -0
  52. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-Bkh8Bfcb.js → cose-bilkent-S5V4N54A-rfrocesE.js} +1 -1
  53. package/dist/deck-client/assets/{dagre-KV5264BT-Bp0XpTgH.js → dagre-KV5264BT-Bv_7DJat.js} +1 -1
  54. package/dist/deck-client/assets/{diagram-5BDNPKRD-ZHiyGYPQ.js → diagram-5BDNPKRD-4F1414G5.js} +1 -1
  55. package/dist/deck-client/assets/{diagram-G4DWMVQ6-BW-Q8_H5.js → diagram-G4DWMVQ6-C4-Pszqm.js} +1 -1
  56. package/dist/deck-client/assets/{diagram-MMDJMWI5-6I3LTafu.js → diagram-MMDJMWI5-B647TIx9.js} +1 -1
  57. package/dist/deck-client/assets/{diagram-TYMM5635-CyM5YK28.js → diagram-TYMM5635-BFAqpezd.js} +1 -1
  58. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-CjNxVJHk.js → erDiagram-SMLLAGMA-BfBfrJOC.js} +1 -1
  59. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-BDQHuAJR.js → flowDiagram-DWJPFMVM-DX9YAYes.js} +1 -1
  60. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-B7MnkpbP.js → ganttDiagram-T4ZO3ILL-DCuiy7wF.js} +1 -1
  61. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-C9dZAcYD.js → gitGraphDiagram-UUTBAWPF-CGp1IXUh.js} +1 -1
  62. package/dist/deck-client/assets/{graph-CjdBnzUy.js → graph-B7g8aoxv.js} +1 -1
  63. package/dist/deck-client/assets/{index-DeIVPW63.js → index-Dg1r-WSN.js} +3 -3
  64. package/dist/deck-client/assets/index-DsIZ3LqL.css +1 -0
  65. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-C7d3iRC3.js → infoDiagram-42DDH7IO-L3fahMkF.js} +1 -1
  66. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-BcYGKj09.js → ishikawaDiagram-UXIWVN3A-aS_EjWBZ.js} +1 -1
  67. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-DqFlRrOL.js → journeyDiagram-VCZTEJTY-djTSQZF9.js} +1 -1
  68. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-BJhPp1NR.js → kanban-definition-6JOO6SKY-CcTHo4CM.js} +1 -1
  69. package/dist/deck-client/assets/{layout-DIeS6GvK.js → layout-mEJiadb7.js} +1 -1
  70. package/dist/deck-client/assets/{linear-He_yJy5H.js → linear-XgTKqyRu.js} +1 -1
  71. package/dist/deck-client/assets/{min-DQ6Kx06t.js → min-Ct9jZdpd.js} +1 -1
  72. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-sQ62L8T2.js → mindmap-definition-QFDTVHPH-BaFxCGNU.js} +1 -1
  73. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-BqCWmU2K.js → pieDiagram-DEJITSTG-CIbYYjtw.js} +1 -1
  74. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-rQ1TJOoe.js → quadrantDiagram-34T5L4WZ-D9EtCOvh.js} +1 -1
  75. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BO2MPBOM.js → requirementDiagram-MS252O5E-xeni9eVG.js} +1 -1
  76. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BgsHEVex.js → sankeyDiagram-XADWPNL6-LYeknz9h.js} +1 -1
  77. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-B3j1yMLU.js → sequenceDiagram-FGHM5R23-RDbsKFZf.js} +1 -1
  78. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-C8jFlZou.js → stateDiagram-FHFEXIEX-BH1Zjglk.js} +1 -1
  79. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BrV78NDR.js +1 -0
  80. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-tM-qo4Zk.js → timeline-definition-GMOUNBTQ-IFXxKptt.js} +1 -1
  81. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-B0-6kOEu.js → vennDiagram-DHZGUBPP-D-sLkQs9.js} +1 -1
  82. package/dist/deck-client/assets/{wardley-RL74JXVD-HpBk07P-.js → wardley-RL74JXVD-C010F8l4.js} +1 -1
  83. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-BkA1NLDE.js → wardleyDiagram-NUSXRM2D-BTjjuDU3.js} +1 -1
  84. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CEKGSuI-.js → xychartDiagram-5P7HB3ND-AYbv92n-.js} +1 -1
  85. package/dist/deck-client/index.html +2 -2
  86. package/dist/server/beacon-monitor-entry.js +353 -0
  87. package/dist/server/chart-serve.js +3836 -3750
  88. package/dist/server/cli.js +8789 -8219
  89. package/dist/server/council-entry.js +17 -5
  90. package/dist/server/council-serve.js +8 -3
  91. package/dist/server/course-entry.js +246 -0
  92. package/dist/server/deck-mcp-entry.js +24 -12
  93. package/dist/server/deck-serve.js +11 -8
  94. package/dist/server/graph-mcp-entry.js +5005 -4865
  95. package/dist/server/init-entry.js +939 -0
  96. package/dist/server/orbit-entry.js +2435 -0
  97. package/dist/server/parse-worker-entry.js +4721 -0
  98. package/dist/server/recall-entry.js +356 -18
  99. package/package.json +11 -4
  100. package/scaffolds/ls-marketplace/.claude-plugin/marketplace.json +15 -0
  101. package/scaffolds/ls-marketplace/plugins/ls/.claude-plugin/plugin.json +28 -0
  102. package/scaffolds/ls-marketplace/plugins/ls/commands/activate-beacon.md +216 -0
  103. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-array.md +92 -0
  104. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-clear.md +68 -0
  105. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-pulse.md +80 -0
  106. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-scan.md +62 -0
  107. package/scaffolds/ls-marketplace/plugins/ls/commands/show-mcp-status.md +109 -0
  108. package/scaffolds/ls-marketplace/plugins/ls/commands/standup.md +177 -0
  109. package/scaffolds/migrate-safety/.github/workflows/backup-on-migration.yml +72 -0
  110. package/scaffolds/migrate-safety/docs/migrations-runbook.md +172 -0
  111. package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +294 -0
  112. package/scaffolds/recall-hook/scripts/ensure-recall.sh +69 -0
  113. package/dist/chart-client/assets/index-DpaGa3bY.css +0 -1
  114. package/dist/client/assets/index-Bfel4OQ5.css +0 -32
  115. package/dist/client/assets/index-eC-WuUWB.js +0 -291
  116. package/dist/council-client/assets/index-P5kMsT5a.css +0 -1
  117. package/dist/deck-client/assets/channel-B4aNO8ZB.js +0 -1
  118. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-BHTI0yWz.js +0 -1
  119. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-BHTI0yWz.js +0 -1
  120. package/dist/deck-client/assets/clone-HduFm7qU.js +0 -1
  121. package/dist/deck-client/assets/index-LKZDAS9S.css +0 -1
  122. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BoqepHW0.js +0 -1
  123. /package/dist/council-client/assets/{index-Cs_MVXHf.js → index-Dt4zWKSj.js} +0 -0
@@ -0,0 +1,177 @@
1
+ ---
2
+ description: Draft a daily standup from work done since the last push, group themes using launch-chart, show the draft, and post to LaunchSecure Comm Hub as a daily_update after explicit confirmation.
3
+ ---
4
+
5
+ # Standup
6
+
7
+ Generates a daily-update comment for the LaunchSecure Comm Hub. Pulls commits since the last push (with fallbacks), groups them by codebase layer/module using `launch-chart`, drafts a summary in the project's house style, shows it to the user, and posts only after the user confirms. Never posts without explicit "yes".
8
+
9
+ ## Preflight
10
+
11
+ 1. Verify `.launch-secure.cred.config` exists at the repo root. If missing, abort and tell the user to run `npx launch-kit init` first — without the cred file the `launch-secure` MCP cannot authenticate, so we cannot read prior standups or post the new one.
12
+ 2. Verify we are inside a git repo (`git rev-parse --git-dir`). If not, abort with a clear message.
13
+ 3. Record the current branch (`git rev-parse --abbrev-ref HEAD`).
14
+ 4. Detect whether `launch-chart` is wired by calling `mcp__launch-chart__detect_project_stack`. If the call fails or returns nothing, fall back to git-only grouping (note this in the final summary).
15
+
16
+ ## Gather
17
+
18
+ Pull data from layered sources, in this order:
19
+
20
+ ### 1. Determine the window
21
+
22
+ - **Primary**: `git log --reverse --pretty=format:'%h%x09%an%x09%ae%x09%s%n%b%n%x00' @{push}..HEAD`. The `@{push}` revspec resolves to where this branch was last pushed; the diff is commits ahead of upstream.
23
+ - **Fallback 1**: if the primary returns empty OR `@{push}` errors with `unknown revision`, query the `launch-secure` MCP via `communication_read({ tag: "daily_update", limit: 1 })`. Take the most recent comment's `createdAt`; gather commits since then via `git log --since="<that timestamp>"`.
24
+ - **Fallback 2**: if no prior `daily_update` exists either, use `git log --since="24 hours ago"`.
25
+
26
+ Tell the user which window was used in one short line ("Using commits since last push (12 commits)" or "No commits since last push — falling back to commits since 2026-05-20 14:01 (last standup)").
27
+
28
+ ### 2. Collect change context
29
+
30
+ - `git diff --stat @{push}..HEAD` (or whichever window was chosen) — file paths + line counts. Drives theme grouping in the next step.
31
+ - `git log --pretty=format:'%h %s' <window>` — short subject lines for quick scan.
32
+ - Branch names + PR references in commit messages (look for `#<digits>`, `LS-<id>`, branch slugs like `fix/foo-bar`). These are work-item handles.
33
+
34
+ ### 3. Group by layer/module using launch-chart
35
+
36
+ This is the chart's job — do **not** group by file path string-matching. For each changed file (deduped from the diff stat):
37
+
38
+ - Call `mcp__launch-chart__read_graph({ search: "<file basename>", layer: "<best guess: ui|api|db|static>" })` to resolve the file to a graph node, OR
39
+ - Call `mcp__launch-chart__read_graph({ node_id: "<full path>" })` if the file path matches a known node id format
40
+
41
+ From the resolved nodes, pull each node's `module` and `layer` fields. Group commits/files into themes by `module` first, then by `layer` when `module` is missing. Common LS modules: `auth`, `pda`, `pda-guides`, `pda-shell`, `radar`, `chart`, `orbit`, `recall`, `comms`, `board`, `webhooks`, `feedback`, `briefs`, `mcp`, etc.
42
+
43
+ If a file doesn't resolve in the chart (new file, non-TS, config), bucket it under "Misc" or by directory (`scripts/`, `docs/`, `.github/`).
44
+
45
+ ### 4. Audit pass (optional, skip if it slows things down)
46
+
47
+ Call `mcp__launch-chart__audit_layer({ layer: "all" })` to surface any drift introduced today (schema_drift, unprotected_routes, dead_screens, hardcoded_values). If the audit reports new issues that didn't exist before (best-effort — compare against the prior standup's audit if possible, otherwise just report current state), flag them in the closing `----` block.
48
+
49
+ ### 5. Blast radius for the biggest changes (deploy-safety signal)
50
+
51
+ For the top-3 files by lines-changed in `git diff --stat`, call `mcp__launch-chart__blast_points({ node_id: "<file>", hops: 2 })`. If any of them has a blast radius of >10 dependent nodes, note it as a deploy-risk signal in the `----` block ("touched X which has 24 downstream consumers — review before deploy").
52
+
53
+ ### 6. Work-item linkage
54
+
55
+ For each work-item handle found in commit messages, call `mcp__launch-secure__work_items_list` (or `work_item_get` if you have an ID) to pull title + status. If any work items were closed today (status changed to DONE/COMPLETED), call them out in the closing block.
56
+
57
+ ### 7. Release detection
58
+
59
+ A commit qualifies the post for the `release` tag if ANY of the following are true:
60
+ - `package.json` `version` field changed in `git diff @{push}..HEAD -- package.json`
61
+ - A migration file under `prisma/migrations/` was added or `prisma/schema.prisma` changed
62
+ - A deploy/publish was mentioned in commit subjects (regex: `\b(publish|release|deploy|bump)\b`)
63
+ - A new bin or export was added to a package's `package.json`
64
+
65
+ If any are true, set `addReleaseTag = true`. Otherwise `false`.
66
+
67
+ ## Draft
68
+
69
+ Produce the standup in the **exact** house format. This is the format Prajyot uses for human-written daily updates (verified against the May 18, 19, 20 posts in the Comm Hub):
70
+
71
+ ```
72
+ Hey @everyone
73
+
74
+ Pushed <N> commits to <branch> today. Highlights:
75
+
76
+ → <Theme 1>
77
+ - <outcome bullet, not a commit message>
78
+ - <outcome bullet>
79
+
80
+ → <Theme 2>
81
+ - <outcome bullet>
82
+
83
+ → <Theme N>
84
+ - <outcome bullet>
85
+
86
+ ----
87
+
88
+ <one-line PSA or deploy-safety note. Examples: "TS clean, no schema/migration changes, safe to deploy." or "Includes prisma migration <name> — run migrate-with-backup.sh before deploy.">
89
+
90
+ Thanks
91
+ ```
92
+
93
+ **Strict format rules:**
94
+
95
+ - `Hey @everyone` — exactly this. Not "Hey team", not "Hi everyone". Single line, blank line after.
96
+ - `Pushed N commits to <branch> today. Highlights:` — replace `<branch>` with the actual branch from preflight. If the window was a fallback (not "since last push"), say "Drafted N commits' worth of work on <branch> today. Highlights:" instead — be honest about the window.
97
+ - `→ <Theme>` — right-arrow + space + theme name. Theme names come from chart's `module` grouping (e.g. "→ Radar", "→ Webhooks", "→ Chart / Freshness"). Capitalize the first letter.
98
+ - `- <bullet>` — plain hyphen + space. Bullets are **outcomes**, not commit messages. Translate "feat(radar): add transcript view" into "Structured transcript view with overlay reply drawer". Drop scope prefixes and verb tense.
99
+ - Blank line between themes.
100
+ - `----` — four hyphens, on its own line, blank line above and below.
101
+ - The closing block: one short line is fine. Don't pad.
102
+ - `Thanks` — exact word, no comma, no name signature (the post's author is attached automatically).
103
+
104
+ **Constraints:**
105
+
106
+ - Plain text only. No `**bold**`, no `*italic*`, no backticks, no `# headers`, no markdown link syntax. The Comm Hub renders as plain text.
107
+ - Aim for ≤ 350 words total. Standups are skim-able; trim aggressively.
108
+ - Top 3–6 themes is the sweet spot. If you have 20 commits in 12 different modules, collapse the small ones into a "→ Misc" theme.
109
+ - Bullets should be **outcome-shaped**: what landed in the product, not what the diff did. "Multi-select EventTypesPicker on subscription forms" beats "modified components/webhooks/EventTypesPicker.tsx".
110
+ - Mention work-item closures and any deploy-affecting changes (migrations, env var changes, breaking API changes) in the closing block.
111
+
112
+ ## Confirm
113
+
114
+ Show the draft to the user verbatim in a code-fenced block (so they see plain-text formatting as-is), then ask **exactly**:
115
+
116
+ > "Post this as a daily_update to LS Comm Hub? Reply `yes` to post, `edit` to revise, or `cancel` to abort."
117
+
118
+ Responses:
119
+
120
+ - **`yes`, `y`, `ok`, `post it`** — proceed to duplicate check + post.
121
+ - **`edit`, `change <thing>`, free-form revisions** — apply the edits, regenerate the draft, re-show, re-ask.
122
+ - **`cancel`, `no`, `nope`** — abort. Don't post. Don't keep partial state.
123
+
124
+ Anything ambiguous → treat as "edit, what would you like changed?".
125
+
126
+ ## Duplicate check
127
+
128
+ Before posting, call `mcp__launch-secure__communication_read({ tag: "daily_update", limit: 5 })`. If any returned comment has:
129
+ - `resourceType: "comment"` (not `"daily_update"` — that's bot-only, distinct stream)
130
+ - `author.email` matches the current user's email (from `git config user.email`)
131
+ - `createdAt` within the last 12 hours
132
+
133
+ …then a manual daily_update already exists from today. Offer:
134
+
135
+ > "A daily_update already exists from you today (posted at <time>, <preview-50-chars>). Choose: `replace` to update it (communication_update), `append` to post another, `cancel` to stop."
136
+
137
+ - **replace** → call `mcp__launch-secure__communication_update({ id: "<existing-id>", content: <new-draft> })`. Preserve tags.
138
+ - **append** → proceed to post.
139
+ - **cancel** → abort.
140
+
141
+ ## Post
142
+
143
+ Call `mcp__launch-secure__communication_write`:
144
+
145
+ ```
146
+ {
147
+ resourceType: "comment",
148
+ content: "<the final draft, plain text>",
149
+ tags: addReleaseTag ? ["daily_update", "release"] : ["daily_update"]
150
+ }
151
+ ```
152
+
153
+ `org_slug` and `project_slug` are auto-supplied from the cred config via the MCP's headersHelper — do NOT pass them yourself.
154
+
155
+ On success: report the comment ID and a friendly "posted to LS Comm Hub" line. If the response includes a URL field, surface it.
156
+
157
+ On failure: surface the error verbatim. Don't retry automatically — auth or schema errors deserve human attention. The draft is preserved in chat so the user can retry manually.
158
+
159
+ ## Tag handling
160
+
161
+ Before adding any tag, you may verify it exists via `mcp__launch-secure__tags_list`. The `daily_update` and `release` tags are standard in LS projects; if either is missing, the post will still succeed but the tag won't attach. Surface this as a warning, not a blocker.
162
+
163
+ ## Idempotency
164
+
165
+ Re-running `/ls:standup`:
166
+ - Always re-pulls the window fresh. No cached state.
167
+ - Duplicate check handles same-day reposts.
168
+ - The draft is never written to disk — only shown in chat. User can copy it manually if they cancel.
169
+
170
+ ## Notes for the assistant
171
+
172
+ - Use `launch-chart` for grouping, not grep/glob. The whole point is producing themes that match how the codebase is organized, not how files are named.
173
+ - The user's preference is short, terse standups (verified style across May 18→20 posts). Default to under-rather-than-over.
174
+ - "Outcome bullet, not commit message" is the single most important transform. A standup full of "feat(x): add y" reads like a changelog, not a status.
175
+ - Never assume `release` tag without evidence (see Release detection above). False positives confuse downstream consumers.
176
+ - Don't ask the user clarifying questions before drafting — produce a first cut, then iterate. The draft is cheap; the conversation isn't.
177
+ - If `launch-chart` is unavailable (no `.launchchart.json`, MCP not responding), fall back to grouping by top-level path segment (`src/app/api/` → "API", `src/client/components/board/` → "Board", etc.) and note "chart unavailable, grouped by path" in chat (not in the post).
@@ -0,0 +1,72 @@
1
+ name: Backup prod DB before migration deploys
2
+
3
+ # Triggers a logical pg_dump of prod whenever a migration file lands on master
4
+ # or implementation. Backup is uploaded as a workflow artifact (90-day retention)
5
+ # tied to the commit SHA — see docs/migrations-runbook.md for restore steps.
6
+ #
7
+ # Required secrets (configure under repo Settings → Secrets and variables → Actions):
8
+ # PROD_DATABASE_URL — full DATABASE_URL for the production DB.
9
+ # Treat this as production credential — anyone who can
10
+ # download the artifact effectively has prod data.
11
+ #
12
+ # Required permissions: this workflow only reads from the DB and writes an
13
+ # artifact; it does not push code, deploy, or modify anything.
14
+
15
+ on:
16
+ push:
17
+ branches: [master, implementation]
18
+ paths:
19
+ - 'prisma/migrations/**'
20
+ - 'prisma/schema.prisma'
21
+ workflow_dispatch: # allow manual runs for ad-hoc snapshots
22
+
23
+ permissions:
24
+ contents: read
25
+
26
+ jobs:
27
+ backup:
28
+ runs-on: ubuntu-latest
29
+ timeout-minutes: 15
30
+ steps:
31
+ - name: Checkout
32
+ uses: actions/checkout@v4
33
+
34
+ - name: Run wrapper in backup-only mode
35
+ env:
36
+ # The same env-var contract used for local — only the destination differs.
37
+ DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
38
+ BACKUP_DIR: artifacts
39
+ # Force docker so the wrapper auto-detects the server's major version
40
+ # and pulls the matching postgres:<major> image. Avoids hardcoding a
41
+ # client version that drifts when prod Postgres is upgraded.
42
+ # ubuntu-latest runners ship with docker pre-installed.
43
+ PG_DUMP_VIA_DOCKER: '1'
44
+ run: |
45
+ if [[ -z "$DATABASE_URL" ]]; then
46
+ echo "::error::PROD_DATABASE_URL secret is not configured."
47
+ echo "Set it under repo Settings → Secrets and variables → Actions."
48
+ exit 1
49
+ fi
50
+ bash scripts/migrate-with-backup.sh backup-only
51
+
52
+ - name: Upload dump as artifact
53
+ uses: actions/upload-artifact@v4
54
+ with:
55
+ name: prod-db-backup-${{ github.sha }}
56
+ path: artifacts/*.sql.gz
57
+ retention-days: 90
58
+ if-no-files-found: error
59
+
60
+ - name: Summary
61
+ run: |
62
+ echo "### Backup complete" >> $GITHUB_STEP_SUMMARY
63
+ echo "" >> $GITHUB_STEP_SUMMARY
64
+ echo "Triggered by: \`${{ github.event_name }}\` on \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY
65
+ echo "Commit: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
66
+ echo "" >> $GITHUB_STEP_SUMMARY
67
+ echo "Download via:" >> $GITHUB_STEP_SUMMARY
68
+ echo '```bash' >> $GITHUB_STEP_SUMMARY
69
+ echo "gh run download ${{ github.run_id }} -n prod-db-backup-${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
70
+ echo '```' >> $GITHUB_STEP_SUMMARY
71
+ echo "" >> $GITHUB_STEP_SUMMARY
72
+ echo "Restore steps: see [docs/migrations-runbook.md](../blob/${{ github.sha }}/docs/migrations-runbook.md)." >> $GITHUB_STEP_SUMMARY
@@ -0,0 +1,172 @@
1
+ # Migrations runbook
2
+
3
+ Operational reference for the three-layer migration safety system. Read this before running a migration in prod, and again when something has gone wrong.
4
+
5
+ ## The three layers (recap)
6
+
7
+ | Layer | What | Where defined |
8
+ |---|---|---|
9
+ | 1 — Process | Expand-and-contract: never combine "add + backfill + drop" in one migration. Two PRs, two deploys. | `CLAUDE.md` |
10
+ | 2 — Tooling | `scripts/migrate-with-backup.sh` — pg_dump before every `prisma migrate`, abort if dump fails. | `scripts/migrate-with-backup.sh` |
11
+ | 3 — In-migration SQL | Pre-flight count + abort-on-orphan + sidecar backup table before any column drop. | `CLAUDE.md` |
12
+
13
+ Recovery never depends on a single layer. PITR (if your provider has it) is a fourth, continuous layer underneath.
14
+
15
+ ## Routine: running a migration
16
+
17
+ ### Local (against `.env.testing` or `.env`)
18
+
19
+ ```bash
20
+ # Always use these — they wrap pg_dump around prisma migrate.
21
+ npm run db:migrate # = prisma migrate dev (creates a new migration)
22
+ npm run db:migrate:deploy # = prisma migrate deploy (applies pending migrations)
23
+ npm run db:backup # dump only, no migration (manual snapshot)
24
+ ```
25
+
26
+ Backups land in `.backups/pre-migrate-<mode>-<YYYYMMDD-HHMMSS>-<git-sha>.sql.gz`. The directory is gitignored. Prune old ones manually when convenient.
27
+
28
+ If your local `pg_dump` is older than the server, the wrapper auto-falls-back to a Docker container with `postgres:16`. Override the image if your server is on a different major:
29
+
30
+ ```bash
31
+ PG_DUMP_DOCKER_IMAGE=postgres:15 PG_DUMP_VIA_DOCKER=1 npm run db:backup
32
+ ```
33
+
34
+ ### Production (Vercel build)
35
+
36
+ The Vercel `build` script still runs `prisma migrate deploy` directly — the *prod backup* is decoupled from the Vercel build and runs on GitHub Actions instead (see `.github/workflows/backup-on-migration.yml`). Whenever a commit on `master` or `implementation` touches `prisma/migrations/**` or `prisma/schema.prisma`, the workflow runs the same wrapper in `backup-only` mode against `PROD_DATABASE_URL` and uploads the dump as a GHA artifact.
37
+
38
+ **One-time setup before the workflow can run**:
39
+
40
+ 1. Repo → Settings → Secrets and variables → Actions → "New repository secret"
41
+ 2. Name: `PROD_DATABASE_URL`. Value: full Postgres URL of prod (with credentials).
42
+ 3. Confirm the workflow file is on `master`. The first migration commit afterward will trigger it.
43
+
44
+ ## Recovery
45
+
46
+ ### Find the right backup
47
+
48
+ **Local backups** — listed by timestamp + commit SHA:
49
+
50
+ ```bash
51
+ ls -lt .backups/
52
+ ```
53
+
54
+ **Prod backups (GHA artifacts)**:
55
+
56
+ - Web UI: repo → **Actions** tab → "Backup prod DB before migration deploys" workflow → click the run for the relevant commit → scroll to "Artifacts" at the bottom → download the `.zip`.
57
+ - CLI:
58
+ ```bash
59
+ gh run list --workflow=backup-on-migration.yml --limit 10
60
+ gh run download <run-id> -n prod-db-backup-<commit-sha>
61
+ ```
62
+
63
+ GHA artifacts are retained for 90 days by default (configurable in the workflow file).
64
+
65
+ ### Restore commands
66
+
67
+ The dump format is identical regardless of where it came from (local or GHA), so the restore commands work for both. Decompress with `gunzip -c` (portable; macOS `zcat` does NOT decompress `.gz`).
68
+
69
+ **1. Inspect a dump without restoring:**
70
+
71
+ ```bash
72
+ gunzip -c .backups/pre-migrate-deploy-<ts>-<sha>.sql.gz | head -50
73
+ gunzip -c .backups/pre-migrate-deploy-<ts>-<sha>.sql.gz | grep -E '^(CREATE TABLE|INSERT INTO "Project")' | head
74
+ ```
75
+
76
+ **2. Restore to a fresh scratch database (for forensics, dry-run, or recovering a specific row):**
77
+
78
+ ```bash
79
+ # Create a scratch DB (psql must point at a server you control; do NOT do this against prod)
80
+ createdb scratch_restore
81
+ # Or via psql: psql -c 'CREATE DATABASE scratch_restore' "$ADMIN_DB_URL"
82
+
83
+ gunzip -c .backups/pre-migrate-deploy-<ts>-<sha>.sql.gz \
84
+ | psql "postgresql://USER:PASS@HOST:PORT/scratch_restore"
85
+ ```
86
+
87
+ Now you have an exact copy of the pre-migration state. Inspect, copy specific rows back to prod, or run the migration against the scratch DB to verify what *would* happen.
88
+
89
+ **3. Full prod rollback (last resort — see provider section below first):**
90
+
91
+ ```bash
92
+ # DESTRUCTIVE — drops the existing schema and replaces it with the dumped contents.
93
+ # Only do this if PITR is unavailable AND you've decided a full rollback is the right call.
94
+ # The dump uses --no-owner --no-privileges, so Postgres roles are NOT modified.
95
+
96
+ # 1. Take a current dump first (in case rollback itself goes wrong)
97
+ DATABASE_URL="$PROD_DATABASE_URL" npm run db:backup
98
+
99
+ # 2. Drop and recreate public schema
100
+ psql "$PROD_DATABASE_URL" -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'
101
+
102
+ # 3. Restore from the pre-migration dump
103
+ gunzip -c .backups/pre-migrate-deploy-<ts>-<sha>.sql.gz | psql "$PROD_DATABASE_URL"
104
+
105
+ # 4. Re-run prisma migrate so the migrations table reflects current state
106
+ DATABASE_URL="$PROD_DATABASE_URL" npx prisma migrate resolve --applied <last-good-migration>
107
+ ```
108
+
109
+ ### Provider-specific recovery (preferred over manual restore when available)
110
+
111
+ | Provider | Recovery feature | Notes |
112
+ |---|---|---|
113
+ | Neon | Branch from PITR | Console → Branches → "Create branch from a previous state" → pick a timestamp before the bad migration. Update `DATABASE_URL` to the new branch. Free tier: 7-day retention. |
114
+ | Vercel Postgres | Same as Neon (it's Neon) | Use the Vercel dashboard or Neon console. |
115
+ | Supabase | Daily snapshots (paid tiers) | Dashboard → Database → Backups. |
116
+ | RDS / Aurora | Automated snapshots + PITR | AWS console. PITR up to backup retention period. |
117
+ | Self-hosted | Whatever you set up | Hopefully `pg_basebackup` + WAL archiving. |
118
+
119
+ **Rule of thumb**: provider PITR > GHA artifact > local `.backups/`. Try them in that order — PITR is closest to "the exact moment before things broke," GHA artifacts are the moment before deploy, local backups are whoever ran the last migration on their laptop.
120
+
121
+ ## Dry-running a migration before merging
122
+
123
+ Before merging a destructive migration PR, restore a fresh GHA artifact (or a `pg_dump` you took manually against prod) into a scratch DB and run the migration there. Check row counts before/after. If the wrapper aborts on orphan check, the migration is broken — fix the SQL.
124
+
125
+ ```bash
126
+ # Pull most recent prod backup
127
+ gh run download $(gh run list --workflow=backup-on-migration.yml --limit 1 --json databaseId -q '.[0].databaseId') \
128
+ -n prod-db-backup-<sha> -D /tmp/prod-snapshot
129
+
130
+ # Spin up a scratch DB and restore
131
+ createdb migration_dryrun
132
+ gunzip -c /tmp/prod-snapshot/*.sql.gz | psql "postgresql://localhost/migration_dryrun"
133
+
134
+ # Apply the pending migration
135
+ DATABASE_URL="postgresql://localhost/migration_dryrun" \
136
+ bash scripts/migrate-with-backup.sh deploy
137
+
138
+ # Spot-check: did the rows you expected get backfilled?
139
+ psql "postgresql://localhost/migration_dryrun" -c 'SELECT count(*) FROM "Project" WHERE ...;'
140
+ ```
141
+
142
+ If the dry-run looks right, merge. If it doesn't, fix the migration and try again.
143
+
144
+ ## Security
145
+
146
+ **Treat dump files like prod credentials.** They contain:
147
+
148
+ - Hashed passwords (still attackable offline)
149
+ - OAuth tokens stored in `Integration.metadata` — these are live credentials for Vercel, GitHub, Neon, etc. Anyone with the dump can impersonate the integration.
150
+ - User PII (emails, names, anything stored in user-facing tables)
151
+
152
+ **Therefore:**
153
+
154
+ - `.backups/` is gitignored and must stay that way. Never commit a `.sql.gz` from this directory.
155
+ - GHA artifacts inherit the repo's access — only collaborators on the repo can download them. If you make the repo public or add an external collaborator, they get historical artifacts too. Audit before changing access.
156
+ - When sharing a dump for debugging, scrub the sensitive tables first (`Integration`, `Account`, `Session`, anything with tokens). A quick scrub:
157
+ ```bash
158
+ gunzip -c backup.sql.gz | grep -v '^COPY public."Integration"' | gzip > backup-scrubbed.sql.gz
159
+ ```
160
+ (This is a blunt instrument — it drops the entire `Integration` COPY block. Inspect before sharing.)
161
+ - Rotate any tokens that may have been exposed if a dump leaks. Vercel/GitHub/Neon integration tokens can all be revoked from their respective dashboards.
162
+
163
+ ## Adding support for a new DB engine
164
+
165
+ Today the wrapper supports Postgres only. To add MySQL/MongoDB/etc.:
166
+
167
+ 1. Add a case branch in `scripts/migrate-with-backup.sh` matching the URL scheme (`mysql://`, `mongodb://`, etc.).
168
+ 2. Use the appropriate dump tool (`mysqldump`, `mongodump`).
169
+ 3. Update the sanity check to recognize the new dump format.
170
+ 4. Update this runbook with restore commands for the new engine.
171
+
172
+ The orchestration logic (dump → validate → migrate, abort on dump failure) stays identical.