dsh-desktop-kit 1.0.0 → 2.0.0

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 (144) hide show
  1. package/README.md +106 -41
  2. package/client.js +780 -28
  3. package/docs/ARCHITECTURE.md +114 -0
  4. package/docs/COMPATIBILITY.md +61 -0
  5. package/docs/INSTALL.md +84 -0
  6. package/docs/PRIVACY.md +50 -0
  7. package/docs/ROADMAP.md +57 -0
  8. package/docs/TOKENS.md +118 -0
  9. package/docs/TROUBLESHOOTING.md +68 -0
  10. package/docs/images/hero.png +0 -0
  11. package/index.js +4 -0
  12. package/package.json +15 -5
  13. package/plugins/dsh-gkb-ru-ui/lib/client.js +276 -28
  14. package/plugins/dsh-gkb-ru-ui/lib/index.js +45 -1
  15. package/plugins/dsh-gkb-ru-ui/lib/skills.js +195 -1
  16. package/plugins/dsh-gkb-ru-ui/package.json +2 -2
  17. package/plugins/dsh-gkb-ru-ui/screenshots/01-home-tiles-1440-light.png +0 -0
  18. package/plugins/dsh-gkb-ru-ui/screenshots/02-skills-1440-light.png +0 -0
  19. package/plugins/dsh-gkb-ru-ui/screenshots/03-skills-editor-1440-light.png +0 -0
  20. package/plugins/dsh-gkb-ru-ui/screenshots/04-skills-1440-dark.png +0 -0
  21. package/plugins/dsh-gkb-ru-ui/screenshots/05-skills-390-light.png +0 -0
  22. package/plugins/dsh-gkb-ru-ui/screenshots/06-home-tiles-1440-dark.png +0 -0
  23. package/plugins/dsh-peak-guard/screenshots/01-real-time-peak-start.png +0 -0
  24. package/plugins/dsh-peak-guard/screenshots/02-real-time-peak-end.png +0 -0
  25. package/plugins/dsh-peak-guard/screenshots/03-peak-card-1440-light.png +0 -0
  26. package/plugins/dsh-peak-guard/screenshots/04-peak-card-1440-dark.png +0 -0
  27. package/plugins/dsh-peak-guard/screenshots/05-peak-card-390-light.png +0 -0
  28. package/plugins/dsh-peak-guard/screenshots/05-peak-card-768-light.png +0 -0
  29. package/plugins/dsh-peak-guard/screenshots/06-peak-card-390-light.png +0 -0
  30. package/plugins/dsh-peak-guard/screenshots/07-live-preview-check.png +0 -0
  31. package/plugins/dsh-peak-guard/screenshots/07-pre-peak-1440-light.png +0 -0
  32. package/plugins/dsh-peak-guard/screenshots/08-peak-start-1440-light.png +0 -0
  33. package/plugins/dsh-peak-guard/screenshots/09-peak-end-1440-light.png +0 -0
  34. package/plugins/dsh-peak-guard/screenshots/10-pre-peak-card-1440-light.png +0 -0
  35. package/plugins/dsh-peak-guard/screenshots/11-pre-peak-card-1440-dark.png +0 -0
  36. package/plugins/dsh-peak-guard/screenshots/12-pre-peak-card-390-light.png +0 -0
  37. package/plugins/dsh-peak-guard/screenshots/qa-report.json +74 -0
  38. package/plugins/dsh-profile-transfer/screenshots/01-settings-transfer-1440-light.png +0 -0
  39. package/plugins/dsh-profile-transfer/screenshots/02-settings-transfer-preview-1440-light.png +0 -0
  40. package/plugins/dsh-profile-transfer/screenshots/03-settings-transfer-1440-dark.png +0 -0
  41. package/plugins/dsh-profile-transfer/screenshots/04-settings-transfer-390-light.png +0 -0
  42. package/plugins/dsh-profile-transfer/screenshots/05-settings-transfer-768-light.png +0 -0
  43. package/plugins/dsh-profile-transfer/screenshots/06-settings-transfer-390-dark.png +0 -0
  44. package/plugins/dsh-profile-transfer/screenshots/07-settings-transfer-export-result.png +0 -0
  45. package/plugins/dsh-profile-transfer/screenshots/08-settings-transfer-workspaces-1440-light.png +0 -0
  46. package/plugins/dsh-profile-transfer/screenshots/09-settings-transfer-workspaces-1440-dark.png +0 -0
  47. package/plugins/dsh-profile-transfer/screenshots/10-settings-transfer-workspaces-390-light.png +0 -0
  48. package/plugins/dsh-session-move/screenshots/01-row-menu-1440-light.png +0 -0
  49. package/plugins/dsh-session-move/screenshots/02-dialog-1440-light.png +0 -0
  50. package/plugins/dsh-session-move/screenshots/03-dialog-390-light.png +0 -0
  51. package/plugins/dsh-session-move/screenshots/04-dialog-1440-dark.png +0 -0
  52. package/plugins/dsh-session-move/screenshots/05-drag-highlight-1440-light.png +0 -0
  53. package/plugins/dsh-session-move/screenshots/06-drop-ungrouped-highlight.png +0 -0
  54. package/plugins/dsh-session-move/screenshots/07-after-drop-ungrouped.png +0 -0
  55. package/plugins/dsh-session-move/screenshots/08-move-dialog.png +0 -0
  56. package/plugins/dsh-session-move/screenshots/09-moved-to-second-workspace.png +0 -0
  57. package/plugins/dsh-session-move/screenshots/qa-report.json +62 -0
  58. package/plugins/dsh-skill-autopilot/cordis.patch.yml +3 -0
  59. package/plugins/dsh-skill-autopilot/index.js +444 -0
  60. package/plugins/dsh-skill-autopilot/package.json +19 -0
  61. package/plugins/dsh-token-lab/cordis.patch.yml +3 -0
  62. package/plugins/dsh-token-lab/lib/client.js +504 -0
  63. package/plugins/dsh-token-lab/lib/index.js +275 -0
  64. package/plugins/dsh-token-lab/package.json +26 -0
  65. package/plugins/dsh-token-lab/screenshots/01-token-lab-1440-light.png +0 -0
  66. package/plugins/dsh-token-lab/screenshots/02-token-lab-1440-dark.png +0 -0
  67. package/skills/code-review-checklist/SKILL.md +48 -30
  68. package/skills/content-writing/SKILL.md +64 -327
  69. package/skills/content-writing/references/process.md +123 -0
  70. package/skills/content-writing/references/quick-recipes.md +99 -0
  71. package/skills/content-writing/references/rules.md +85 -0
  72. package/skills/content-writing/references/sources.md +49 -0
  73. package/skills/context-economy/SKILL.md +60 -0
  74. package/skills/context-economy/references/long-session.md +35 -0
  75. package/skills/context-economy/references/token-math.md +42 -0
  76. package/skills/context-economy/references/tool-recipes.md +34 -0
  77. package/skills/database-safety/SKILL.md +40 -24
  78. package/skills/debugging-playbook/SKILL.md +30 -27
  79. package/skills/design-critique/SKILL.md +57 -110
  80. package/skills/design-critique/references/a11y.md +25 -0
  81. package/skills/design-critique/references/diagnoses.md +33 -0
  82. package/skills/design-critique/references/quick-tests.md +30 -0
  83. package/skills/design-critique/references/report-template.md +63 -0
  84. package/skills/design-critique/references/rubric-fast.md +73 -0
  85. package/skills/dev-workflow/SKILL.md +41 -34
  86. package/skills/dev-workflow/references/definition-of-done.md +44 -0
  87. package/skills/dev-workflow/references/memory-and-boundaries.md +31 -0
  88. package/skills/dev-workflow/references/stages.md +67 -0
  89. package/skills/git-and-ci/SKILL.md +46 -26
  90. package/skills/pg-upgrade-verify/SKILL.md +54 -35
  91. package/skills/project-deep-dive/SKILL.md +39 -164
  92. package/skills/project-deep-dive/references/recon-commands.md +12 -0
  93. package/skills/project-deep-dive/references/section-templates.md +2 -0
  94. package/skills/project-deep-dive/references/tool-and-process.md +86 -0
  95. package/skills/project-memory-maintenance/SKILL.md +39 -154
  96. package/skills/project-memory-maintenance/references/api-and-checklist.md +99 -0
  97. package/skills/project-memory-maintenance/references/journal-template.md +2 -0
  98. package/skills/project-memory-maintenance/references/section-rules.md +2 -0
  99. package/skills/release-and-deploy/SKILL.md +46 -32
  100. package/skills/requirements-and-planning/SKILL.md +44 -110
  101. package/skills/requirements-and-planning/references/antipatterns-and-checklist.md +20 -0
  102. package/skills/requirements-and-planning/references/process-steps.md +52 -0
  103. package/skills/requirements-and-planning/references/templates-and-estimates.md +29 -0
  104. package/skills/security-review/SKILL.md +33 -29
  105. package/skills/seo/SKILL.md +53 -305
  106. package/skills/seo/references/antipatterns.md +66 -0
  107. package/skills/seo/references/audit-checklist.md +14 -0
  108. package/skills/seo/references/content-seo.md +180 -174
  109. package/skills/seo/references/eeat-medical.md +2 -0
  110. package/skills/seo/references/measurement.md +28 -1
  111. package/skills/seo/references/on-page.md +5 -0
  112. package/skills/seo/references/page-templates.md +56 -0
  113. package/skills/seo/references/stack-recipes.md +2 -0
  114. package/skills/seo/references/structured-data.md +2 -0
  115. package/skills/seo/references/technical-seo.md +2 -0
  116. package/skills/service-desk-conventions/SKILL.md +40 -28
  117. package/skills/service-desk-db-safety/SKILL.md +39 -28
  118. package/skills/service-desk-release/SKILL.md +40 -22
  119. package/skills/service-desk-testing/SKILL.md +47 -33
  120. package/skills/service-desk-ui/SKILL.md +43 -24
  121. package/skills/skills-router/SKILL.md +44 -82
  122. package/skills/skills-router/references/applying-skills.md +25 -0
  123. package/skills/skills-router/references/routing-matrix.md +52 -0
  124. package/skills/testing-strategy/SKILL.md +47 -32
  125. package/skills/ui-design-system/SKILL.md +43 -38
  126. package/skills/ui-design-system/references/accessibility-and-responsive.md +50 -0
  127. package/skills/ui-design-system/references/tokens-and-components.md +67 -0
  128. package/skills/ui-design-system/references/visual-qa.md +44 -0
  129. package/skills/web-research/SKILL.md +40 -33
  130. package/skills/web-research/references/answer-and-citations.md +60 -0
  131. package/skills/web-research/references/query-recipes.md +44 -0
  132. package/skills/wow-design/SKILL.md +82 -161
  133. package/skills/wow-design/references/anti-patterns.md +3 -0
  134. package/skills/wow-design/references/critique-rubric.md +3 -0
  135. package/skills/wow-design/references/design-brief-template.md +2 -0
  136. package/skills/wow-design/references/inspiration.md +3 -0
  137. package/skills/wow-design/references/presets.md +3 -0
  138. package/skills/wow-design/references/principles.md +3 -0
  139. package/skills/wow-design/references/quick-presets.md +50 -0
  140. package/skills/wow-design/references/report-template.md +21 -0
  141. package/skills/wow-design/references/stack-recipes.md +3 -0
  142. package/skills/wow-design/references/visual-qa.md +3 -0
  143. package/skills/wow-design/references/wow-levers.md +43 -0
  144. package/tools/skill-cost.mjs +76 -0
@@ -0,0 +1,275 @@
1
+ /**
2
+ * dsh-token-lab — Host half.
3
+ *
4
+ * Reads the exact token measurement of the live session from `ctx.tokenMeter`
5
+ * (the same estimator the request path uses), groups the surface nodes by
6
+ * message kind and returns a compact report for the sidebar panel. Also prices
7
+ * the skill library: what the whole `~/.dsh/skills` tree costs if loaded, and
8
+ * what the compact cores cost.
9
+ */
10
+ import fs from "node:fs";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+
14
+ export const inject = ["connection"];
15
+
16
+ const ROUTE_BASE = "/api/dsh-token-lab";
17
+ const NAMESPACE = "dsh-token-lab";
18
+ const DEFAULT_CONTEXT_WINDOW = 128_000;
19
+
20
+ const GROUPS = {
21
+ "system/message": { id: "system", label: "Системный промпт", color: "#2F4858" },
22
+ "developer/message": { id: "developer", label: "Память и правила", color: "#5B4B8A" },
23
+ "user/message": { id: "user", label: "Ваши сообщения", color: "#0E6E5E" },
24
+ "assistant/message": { id: "assistant", label: "Ответы модели", color: "#2C5F8A" },
25
+ "tool/result": { id: "tool", label: "Результаты инструментов", color: "#B36A43" },
26
+ other: { id: "other", label: "Прочее", color: "#7A7F87" },
27
+ };
28
+
29
+ function dshHome() {
30
+ return process.env.DSH_HOME ?? path.join(os.homedir(), ".dsh");
31
+ }
32
+
33
+ function approxTokens(bytes) {
34
+ return Math.max(40, Math.round(bytes / 3.2));
35
+ }
36
+
37
+ function humanName(skill) {
38
+ return skill.replaceAll("-", " ");
39
+ }
40
+
41
+ function scanSkills() {
42
+ const directory = path.join(dshHome(), "skills");
43
+ const skills = [];
44
+ let entries = [];
45
+ try {
46
+ entries = fs.readdirSync(directory, { withFileTypes: true });
47
+ } catch {
48
+ return { skills, totalBytes: 0, totalTokens: 0 };
49
+ }
50
+ for (const entry of entries) {
51
+ if (!entry.isDirectory()) continue;
52
+ const file = path.join(directory, entry.name, "SKILL.md");
53
+ try {
54
+ const stat = fs.statSync(file);
55
+ const tokens = approxTokens(stat.size);
56
+ skills.push({ name: entry.name, title: humanName(entry.name), bytes: stat.size, tokens });
57
+ } catch {
58
+ /* not a skill folder */
59
+ }
60
+ }
61
+ skills.sort((left, right) => right.tokens - left.tokens);
62
+ return {
63
+ skills,
64
+ totalBytes: skills.reduce((sum, skill) => sum + skill.bytes, 0),
65
+ totalTokens: skills.reduce((sum, skill) => sum + skill.tokens, 0),
66
+ };
67
+ }
68
+
69
+ function readAutopilot() {
70
+ try {
71
+ const parsed = JSON.parse(fs.readFileSync(path.join(dshHome(), "dsh-autopilot.json"), "utf8"));
72
+ return { mode: parsed?.mode === "off" ? "off" : "on", maxSkills: Number(parsed?.maxSkills) || 3 };
73
+ } catch {
74
+ return { mode: "on", maxSkills: 3 };
75
+ }
76
+ }
77
+
78
+ function messageText(message) {
79
+ if (!message) return "";
80
+ const content = message.content;
81
+ if (typeof content === "string") return content;
82
+ if (!Array.isArray(content)) return "";
83
+ const parts = [];
84
+ for (const block of content) {
85
+ if (block && block.type === "text" && typeof block.text === "string" && block.text.trim().length > 0) {
86
+ parts.push(block.text.trim());
87
+ }
88
+ }
89
+ return parts.join(" ").replace(/\s+/gu, " ").trim();
90
+ }
91
+
92
+ function pickSession(sessions, sessionId) {
93
+ if (!sessions) return undefined;
94
+ if (typeof sessionId === "string" && sessionId.length > 0) {
95
+ const direct = sessions.get(sessionId);
96
+ if (direct) return direct;
97
+ }
98
+ const list = sessions.list();
99
+ return list[list.length - 1];
100
+ }
101
+
102
+ function toolNameMap(session) {
103
+ const names = new Map();
104
+ try {
105
+ for (const event of session.snapshotEvents()) {
106
+ if (event.type === "tool/call" && event.data?.callId && event.data?.name) {
107
+ names.set(String(event.data.callId), { name: String(event.data.name), hint: argumentHint(event.data.arguments) });
108
+ }
109
+ }
110
+ } catch {
111
+ /* cold or truncated log: labels stay generic */
112
+ }
113
+ return names;
114
+ }
115
+
116
+ function argumentHint(raw) {
117
+ let args = raw;
118
+ if (typeof raw === "string") {
119
+ try {
120
+ args = JSON.parse(raw);
121
+ } catch {
122
+ return "";
123
+ }
124
+ }
125
+ if (!args || typeof args !== "object") return "";
126
+ for (const key of ["service", "method", "path", "url", "query", "action", "name", "command", "file_path", "pattern"]) {
127
+ const value = args[key];
128
+ if (typeof value === "string" && value.trim().length > 0) return value.trim().slice(0, 32);
129
+ }
130
+ return "";
131
+ }
132
+
133
+ function toolLabel(names, message) {
134
+ const callId = message?.source?.callId ?? message?.toolCallId;
135
+ const entry = callId ? names.get(String(callId)) : undefined;
136
+ if (!entry) return "Результат инструмента";
137
+ return entry.hint ? `Результат · ${entry.name} · ${entry.hint}` : `Результат · ${entry.name}`;
138
+ }
139
+
140
+ function buildReport(ctx, sessionId) {
141
+ const sessions = ctx.get("sessions");
142
+ const meter = ctx.get("tokenMeter");
143
+ const session = pickSession(sessions, sessionId);
144
+ if (!session) {
145
+ const error = new Error("нет активной сессии");
146
+ error.code = "tokenlab/no-session";
147
+ throw error;
148
+ }
149
+ if (!meter) {
150
+ const error = new Error("сервис tokenMeter недоступен в этой сборке DSH");
151
+ error.code = "tokenlab/no-meter";
152
+ throw error;
153
+ }
154
+ const measurement = meter.measure(session);
155
+ const toolNames = toolNameMap(session);
156
+ const groups = new Map();
157
+ const nodes = [];
158
+ for (const node of measurement.nodes) {
159
+ let event;
160
+ try {
161
+ event = session.eventAt(node.seq);
162
+ } catch {
163
+ event = undefined;
164
+ }
165
+ const type = event?.type ?? "other";
166
+ const group = GROUPS[type] ?? GROUPS.other;
167
+ const bucket = groups.get(group.id) ?? { ...group, tokens: 0, count: 0, items: [] };
168
+ bucket.tokens += node.tokens;
169
+ bucket.count += 1;
170
+ const message = event ? safeDerive(session, event) : undefined;
171
+ const text = type === "tool/result" ? toolLabel(toolNames, message) : messageText(message);
172
+ if (bucket.items.length < 3 && text.length > 0) {
173
+ bucket.items.push({ seq: Number(node.seq), tokens: node.tokens, text: text.slice(0, 120) });
174
+ }
175
+ groups.set(group.id, bucket);
176
+ nodes.push({
177
+ seq: Number(node.seq),
178
+ tokens: node.tokens,
179
+ type,
180
+ group: group.id,
181
+ label: text.slice(0, 80),
182
+ });
183
+ }
184
+ const groupList = [...groups.values()]
185
+ .map((group) => ({ ...group, items: group.items.sort((a, b) => b.tokens - a.tokens) }))
186
+ .sort((left, right) => right.tokens - left.tokens);
187
+ const top = [...nodes].sort((left, right) => right.tokens - left.tokens).slice(0, 6);
188
+ let contextWindow = Number(session.requestContext()?.contextWindow) || DEFAULT_CONTEXT_WINDOW;
189
+ const total = measurement.totalTokens;
190
+ const series = [];
191
+ let running = 0;
192
+ const stride = Math.max(1, Math.ceil(nodes.length / 48));
193
+ for (let index = 0; index < nodes.length; index += stride) {
194
+ running += nodes.slice(index, index + stride).reduce((sum, node) => sum + node.tokens, 0);
195
+ series.push(running);
196
+ }
197
+ series.push(total);
198
+ const library = scanSkills();
199
+ const autopilot = readAutopilot();
200
+ const average = library.skills.length > 0 ? Math.round(library.totalTokens / library.skills.length) : 0;
201
+ const typical = average * 3;
202
+ const heaviest = library.skills.slice(0, 6);
203
+ return {
204
+ sessionId: String(session.id),
205
+ title: session.header?.cwd ? path.basename(session.header.cwd) : "",
206
+ totalTokens: total,
207
+ surfaceTokens: measurement.surfaceTokens,
208
+ surfaceDeltaTokens: measurement.surfaceDeltaTokens,
209
+ baseline: {
210
+ kind: measurement.baseline?.kind ?? "none",
211
+ tokens: measurement.baseline?.tokens ?? 0,
212
+ usage: measurement.baseline?.usage ?? null,
213
+ },
214
+ contextWindow,
215
+ pressure: contextWindow > 0 ? Math.min(1, total / contextWindow) : 0,
216
+ groups: groupList,
217
+ top,
218
+ series,
219
+ nodes: nodes.length,
220
+ library: {
221
+ count: library.skills.length,
222
+ totalTokens: library.totalTokens,
223
+ typicalTokens: typical,
224
+ heaviest,
225
+ },
226
+ autopilot,
227
+ generatedAt: new Date().toISOString(),
228
+ };
229
+ }
230
+
231
+ function safeDerive(session, event) {
232
+ try {
233
+ return session.deriveEventMessage(event);
234
+ } catch {
235
+ return event?.data?.message;
236
+ }
237
+ }
238
+
239
+ function okEnvelope(rpcId, value) {
240
+ return Response.json({ type: "server-response", rpcId, result: { ok: true, value } });
241
+ }
242
+
243
+ function errorEnvelope(rpcId, code, message) {
244
+ return Response.json({ type: "server-response", rpcId, result: { ok: false, error: { code, message } } });
245
+ }
246
+
247
+ export function apply(ctx) {
248
+ const register = (operation, handler) => {
249
+ const route = {
250
+ path: `${ROUTE_BASE}/${operation}`,
251
+ methods: ["POST"],
252
+ requestBody: "buffered",
253
+ fetch: async (request) => {
254
+ let rpcId = "invalid-request";
255
+ try {
256
+ const body = await request.json();
257
+ rpcId = typeof body?.rpcId === "string" ? body.rpcId : rpcId;
258
+ if (body?.type !== "client-request" || body?.method !== `${NAMESPACE}/${operation}`) {
259
+ return errorEnvelope(rpcId, "tokenlab/bad-request", "некорректный конверт Connection");
260
+ }
261
+ const value = await handler(body?.payload ?? {});
262
+ return okEnvelope(rpcId, value);
263
+ } catch (error) {
264
+ const code = typeof error?.code === "string" ? error.code : "tokenlab/internal";
265
+ if (code === "tokenlab/internal") ctx.logger?.warn?.(`[token-lab] ${operation} failed: ${String(error)}`);
266
+ return errorEnvelope(rpcId, code, error instanceof Error ? error.message : String(error));
267
+ }
268
+ },
269
+ };
270
+ ctx.effect(() => ctx.connection.fetch.register(route), `token-lab: ${operation}`);
271
+ };
272
+
273
+ register("report", async (payload) => buildReport(ctx, payload?.sessionId));
274
+ register("ping", async () => ({ ok: true, home: dshHome() }));
275
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "dsh-token-lab",
3
+ "version": "1.0.0",
4
+ "description": "Live token laboratory for DSH: context composition, top token consumers, skill-library cost and context-window pressure in one sidebar tile.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "default": "./lib/index.js"
10
+ },
11
+ "./client": {
12
+ "default": "./lib/client.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "dsh": {
17
+ "manifestVersion": 1,
18
+ "bundle": {
19
+ "patch": "./cordis.patch.yml"
20
+ },
21
+ "client": {
22
+ "platform": "web",
23
+ "inject": []
24
+ }
25
+ }
26
+ }
@@ -1,37 +1,55 @@
1
1
  ---
2
2
  name: code-review-checklist
3
- description: "Чек-лист ревью кода перед сдачей/релизом — безопасность, данные, права, тесты, стиль, деплой."
4
- whenToUse: "Перед тем как считать задачу выполненной, перед коммитом/релизом или при ревью чужого кода."
3
+ description: "Ревью перед сдачей/релизом: блокеры, важное, мелочи, Definition of Done — данные, права, тесты, стиль, деплой."
4
+ whenToUse: "Перед коммитом/релизом, когда считаешь задачу готовой или ревьюишь чужой код; после конфликтов и хотфиксов."
5
5
  user-invocable: true
6
6
  ---
7
7
 
8
8
  # Чек-лист ревью кода
9
9
 
10
- ## Данные и БД
11
- - [ ] Нет деструктивных операций (`migrate:fresh`, `db:wipe`, `TRUNCATE`, `dropdb`) без явного разрешения.
12
- - [ ] Миграции аддитивные, с `down()`, не редактируют старые миграции.
13
- - [ ] Многозаписные операции — в транзакции; при гонках — `lockForUpdate()`.
14
- - [ ] Массовые обновления/удаления ограничены условиями; проверены на копии данных.
15
- - [ ] Если менялись данные — есть план бэкапа/отката.
16
-
17
- ## Безопасность
18
- - [ ] Все входные данные валидируются; нет конкатенации пользовательских данных в SQL.
19
- - [ ] Вывод HTML — через экранирование/`@html` (санитайзер); нет `{!! !!}` для пользовательского контента.
20
- - [ ] RBAC: новые маршруты закрыты `auth` + `permission:*`; проверены админ и обычный пользователь.
21
- - [ ] Загрузки: whitelist типов, лимит размера, безопасная отдача (attachment для не-изображений).
22
- - [ ] Нет секретов/паролей/ключей в коде, логах, коммитах, памяти ассистента.
23
- - [ ] Нет IDOR: проверка владельца записи (например, заявки).
24
-
25
- ## Код и тесты
26
- - [ ] Понятные имена, нет мёртвого кода и дублирования, нет N+1.
27
- - [ ] Обработаны ошибки/пустые состояния; тексты для пользователя — по-русски и понятны.
28
- - [ ] Есть тесты: feature на сценарий/права + unit на логику; новые тесты падают без правки — проходят с ней.
29
- - [ ] Полный PHPUnit-набор зелёный (baseline не ухудшен).
30
- - [ ] Для UI — скриншоты/Playwright «до/после», светлая+тёмная тема, mobile.
31
- - [ ] Pint/линтер по изменённым файлам проходит; статический анализ не добавил ошибок.
32
-
33
- ## Релиз
34
- - [ ] Версия поднята по правилам (feature=minor, fix=patch).
35
- - [ ] Сборка и миграции проверены на копии (см. `pg-upgrade-verify`).
36
- - [ ] План выкатки/отката записан; бэкап предусмотрен.
37
- - [ ] Обновлены документация/память (`SESSIONS.md`, досье проекта).
10
+ ## 0. Когда применять
11
+ - Перед коммитом/релизом и перед ответом «готово».
12
+ - Ревью чужого кода, после конфликта или хотфикса.
13
+ - Нужен приоритет: что блокирует, что потом.
14
+
15
+ ## 1. Главное
16
+ **Порядок:** `git status --short` → `git diff --stat` → `git diff` → находки по приоритетам → блокеры → гейт.
17
+
18
+ **Блокеры (не сдавать):**
19
+ - Деструктив с БД (`migrate:fresh`, `db:wipe`, `TRUNCATE`, `dropdb`) без явного разрешения и бэкапа.
20
+ - Секреты/пароли/ключи в коде, логах, коммите, памяти.
21
+ - Маршрут без `auth`/права (`permission:*`) или без проверки admin/user/guest; IDOR; эскалация прав.
22
+ - SQL-конкатенация; пользовательский HTML без экранирования/санитайзера (`{!! !!}`, `innerHTML`, `v-html`).
23
+ - Загрузка без whitelist/лимита/безопасной отдачи (attachment); исполняемые файлы в webroot.
24
+ - Красные тесты/ухудшенный baseline; баг без регрессионного теста.
25
+ - Рискованная миграция/массовая правка без плана отката.
26
+ - `APP_DEBUG=true`, дебаг-код или временные логи в проде.
27
+
28
+ **Важное:**
29
+ - Миграции аддитивные, с `down()`, не редактируют применённые; многозаписные — в транзакции, гонки — `lockForUpdate()`.
30
+ - Массовые `UPDATE/DELETE` с условием; проверены на копии.
31
+ - Валидация всех входных данных на сервере (тип/длина/диапазон/формат); клиентская — только UX.
32
+ - Ошибки/пустые состояния; тексты пользователю по-русски и понятны.
33
+ - Нет N+1, мёртвого кода, дублирования; имена понятные.
34
+ - Тесты: feature на сценарий/права/валидацию + unit на логику; новые падают без правки.
35
+ - Полный PHPUnit-набор зелёный; линтер (`php -l`, Pint) и статанализ без новых ошибок.
36
+ - UI: скриншоты/Playwright «до/после», светлая/тёмная, mobile.
37
+ - Версия поднята (feature=minor, fix=patch); сборка и миграции проверены на копии (`pg-upgrade-verify`).
38
+ - План выкатки/отката, бэкап; документация/память обновлены.
39
+
40
+ **Мелочи:**
41
+ - Форматирование, комментарии, мёртвые импорты, `TODO` без тикета, размер диффа, сообщение коммита.
42
+
43
+ **Команды:** `git diff --check`, `git status --short`, тесты/линтер проекта.
44
+
45
+ ## 2. Куда грузить дальше
46
+ Смежные: `security-review`, `database-safety`, `testing-strategy`, `release-and-deploy`.
47
+
48
+ ## 3. Гейт перед сдачей — Definition of Done
49
+ - [ ] Блокеров нет; важное закрыто или вынесено с причиной.
50
+ - [ ] Дифф минимален и по задаче; нет случайных файлов, секретов, дебага.
51
+ - [ ] Тесты/линтер/статанализ зелёные; baseline не хуже.
52
+ - [ ] Данные: бэкап/аддитивные миграции/план отката.
53
+ - [ ] UI: скриншоты 1440/768/390, light/dark (если интерфейс).
54
+ - [ ] Версия/CHANGELOG/память обновлены.
55
+ - [ ] Отчёт: сделано, как проверено, риски.