mustflow 2.18.21 → 2.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/cli/commands/classify.js +2 -3
  2. package/dist/cli/commands/doctor.js +46 -6
  3. package/dist/cli/commands/run/output.js +1 -1
  4. package/dist/cli/commands/run/receipt.js +1 -0
  5. package/dist/cli/commands/verify.js +7 -8
  6. package/dist/cli/i18n/en.js +1 -0
  7. package/dist/cli/i18n/es.js +1 -0
  8. package/dist/cli/i18n/fr.js +1 -0
  9. package/dist/cli/i18n/hi.js +1 -0
  10. package/dist/cli/i18n/ko.js +1 -0
  11. package/dist/cli/i18n/zh.js +1 -0
  12. package/dist/cli/lib/local-index/index.js +3 -3
  13. package/dist/cli/lib/repo-map.js +3 -2
  14. package/dist/cli/lib/run-plan.js +8 -4
  15. package/dist/core/check-issues.js +1 -1
  16. package/dist/core/command-contract-validation.js +24 -10
  17. package/dist/core/command-output-limits.js +2 -1
  18. package/dist/core/line-endings.js +12 -4
  19. package/dist/core/repeated-failure.js +3 -3
  20. package/dist/core/run-performance-history.js +4 -4
  21. package/dist/core/run-profile.js +2 -3
  22. package/dist/core/run-receipt.js +11 -3
  23. package/dist/core/run-write-drift.js +60 -12
  24. package/dist/core/safe-filesystem.js +155 -0
  25. package/package.json +1 -1
  26. package/schemas/commands.schema.json +1 -0
  27. package/schemas/doctor-report.schema.json +23 -1
  28. package/schemas/run-receipt.schema.json +6 -2
  29. package/templates/default/i18n.toml +13 -13
  30. package/templates/default/locales/en/.mustflow/skills/INDEX.md +13 -13
  31. package/templates/default/locales/en/.mustflow/skills/adapter-boundary/SKILL.md +72 -4
  32. package/templates/default/locales/en/.mustflow/skills/command-contract-authoring/SKILL.md +16 -10
  33. package/templates/default/locales/en/.mustflow/skills/command-pattern/SKILL.md +64 -7
  34. package/templates/default/locales/en/.mustflow/skills/database-change-safety/SKILL.md +249 -16
  35. package/templates/default/locales/en/.mustflow/skills/dependency-reality-check/SKILL.md +37 -7
  36. package/templates/default/locales/en/.mustflow/skills/migration-safety-check/SKILL.md +74 -10
  37. package/templates/default/locales/en/.mustflow/skills/performance-budget-check/SKILL.md +132 -5
  38. package/templates/default/locales/en/.mustflow/skills/pure-core-imperative-shell/SKILL.md +12 -5
  39. package/templates/default/locales/en/.mustflow/skills/result-option/SKILL.md +4 -2
  40. package/templates/default/locales/en/.mustflow/skills/security-privacy-review/SKILL.md +112 -29
  41. package/templates/default/locales/en/.mustflow/skills/state-machine-pattern/SKILL.md +17 -4
  42. package/templates/default/locales/en/.mustflow/skills/structure-discovery-gate/SKILL.md +193 -2
  43. package/templates/default/manifest.toml +1 -1
@@ -1,9 +1,14 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { lstatSync, readlinkSync, readdirSync } from 'node:fs';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync, lstatSync, readFileSync, readlinkSync, readdirSync } from 'node:fs';
3
4
  import path from 'node:path';
4
5
  import { normalizeCommandEffects } from './command-effects.js';
5
6
  const MAX_SNAPSHOT_FILES = 20_000;
6
7
  const MAX_REPORTED_PATHS = 200;
8
+ const GIT_STATUS_TIMEOUT_MS = 10_000;
9
+ const GIT_STATUS_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
10
+ const GIT_STATUS_UNTRACKED_MODE = 'normal';
11
+ const MAX_HASH_BYTES = 5 * 1024 * 1024;
7
12
  const RECURSIVE_SNAPSHOT_ENV = 'MUSTFLOW_WRITE_DRIFT_SNAPSHOT';
8
13
  const EXCLUDED_DIRECTORY_NAMES = new Set(['.git', 'node_modules']);
9
14
  const EXCLUDED_RELATIVE_DIRECTORY_PATHS = new Set(['.mustflow/state/runs']);
@@ -33,6 +38,24 @@ function signatureForPath(fullPath) {
33
38
  const type = stat.isDirectory() ? 'directory' : stat.isFile() ? 'file' : 'other';
34
39
  return `${type}:${stat.size}:${stat.mtimeMs}`;
35
40
  }
41
+ function signatureForGitStatusPath(projectRoot, relativePath, status) {
42
+ const fullPath = path.join(projectRoot, ...relativePath.split('/'));
43
+ if (!existsSync(fullPath)) {
44
+ return `git:${status}:missing`;
45
+ }
46
+ const stat = lstatSync(fullPath);
47
+ if (stat.isSymbolicLink()) {
48
+ return `git:${status}:symlink:${readlinkSync(fullPath)}`;
49
+ }
50
+ if (!stat.isFile()) {
51
+ return `git:${status}:${stat.isDirectory() ? 'directory' : 'other'}:${stat.size}:${stat.mtimeMs}`;
52
+ }
53
+ if (stat.size > MAX_HASH_BYTES) {
54
+ return `git:${status}:file:${stat.size}:${stat.mtimeMs}:unhashed`;
55
+ }
56
+ const digest = createHash('sha256').update(readFileSync(fullPath)).digest('hex');
57
+ return `git:${status}:file:${stat.size}:${digest}`;
58
+ }
36
59
  function collectSnapshotEntries(projectRoot, currentPath, entries) {
37
60
  const names = readdirSync(currentPath).sort((left, right) => left.localeCompare(right));
38
61
  for (const name of names) {
@@ -59,7 +82,7 @@ function captureSnapshot(projectRoot) {
59
82
  }
60
83
  if (!isRecursiveSnapshotEnabled()) {
61
84
  return {
62
- ok: false,
85
+ status: 'unavailable',
63
86
  entries: new Map(),
64
87
  reason: 'git_status_unavailable_recursive_snapshot_disabled',
65
88
  source: 'unavailable',
@@ -68,11 +91,11 @@ function captureSnapshot(projectRoot) {
68
91
  try {
69
92
  const entries = new Map();
70
93
  collectSnapshotEntries(projectRoot, projectRoot, entries);
71
- return { ok: true, entries, reason: null, source: 'recursive_snapshot' };
94
+ return { status: 'checked', entries, reason: null, source: 'recursive_snapshot' };
72
95
  }
73
96
  catch (error) {
74
97
  return {
75
- ok: false,
98
+ status: 'unavailable',
76
99
  entries: new Map(),
77
100
  reason: error instanceof Error && error.message.length > 0 ? error.message : 'snapshot_unavailable',
78
101
  source: 'unavailable',
@@ -80,12 +103,33 @@ function captureSnapshot(projectRoot) {
80
103
  }
81
104
  }
82
105
  function captureGitStatusSnapshot(projectRoot) {
83
- const result = spawnSync('git', ['-C', projectRoot, 'status', '--porcelain=v1', '-z', '--untracked-files=all'], {
106
+ const result = spawnSync('git', ['-C', projectRoot, 'status', '--porcelain=v1', '-z', `--untracked-files=${GIT_STATUS_UNTRACKED_MODE}`], {
84
107
  encoding: 'utf8',
85
108
  input: '',
109
+ maxBuffer: GIT_STATUS_MAX_BUFFER_BYTES,
86
110
  stdio: ['ignore', 'pipe', 'pipe'],
111
+ timeout: GIT_STATUS_TIMEOUT_MS,
87
112
  windowsHide: true,
88
113
  });
114
+ const errorCode = typeof result.error === 'object' && result.error && 'code' in result.error
115
+ ? String(result.error.code)
116
+ : null;
117
+ if (errorCode === 'ETIMEDOUT') {
118
+ return {
119
+ status: 'unavailable',
120
+ entries: new Map(),
121
+ reason: 'git_status_timeout',
122
+ source: 'unavailable',
123
+ };
124
+ }
125
+ if (errorCode === 'ENOBUFS') {
126
+ return {
127
+ status: 'unavailable',
128
+ entries: new Map(),
129
+ reason: 'git_status_output_limit_exceeded',
130
+ source: 'unavailable',
131
+ };
132
+ }
89
133
  if (result.error || result.status !== 0) {
90
134
  return null;
91
135
  }
@@ -98,15 +142,15 @@ function captureGitStatusSnapshot(projectRoot) {
98
142
  if (filePath.length === 0) {
99
143
  continue;
100
144
  }
101
- entries.set(filePath, `git:${status}`);
145
+ entries.set(filePath, signatureForGitStatusPath(projectRoot, filePath, status));
102
146
  if (status.includes('R') || status.includes('C')) {
103
147
  index += 1;
104
148
  }
105
149
  }
106
150
  return {
107
- ok: true,
151
+ status: 'partial',
108
152
  entries,
109
- reason: null,
153
+ reason: 'git_status_untracked_files_normal',
110
154
  source: 'git_status',
111
155
  };
112
156
  }
@@ -150,7 +194,7 @@ export function startRunWriteTracking(projectRoot, contract, intentName) {
150
194
  };
151
195
  }
152
196
  export function finishRunWriteTracking(tracker) {
153
- if (!tracker.before.ok) {
197
+ if (tracker.before.status === 'unavailable') {
154
198
  return {
155
199
  status: 'unavailable',
156
200
  declared_paths: tracker.declaredPaths,
@@ -165,7 +209,7 @@ export function finishRunWriteTracking(tracker) {
165
209
  };
166
210
  }
167
211
  const after = captureSnapshot(tracker.projectRoot);
168
- if (!after.ok) {
212
+ if (after.status === 'unavailable') {
169
213
  return {
170
214
  status: 'unavailable',
171
215
  declared_paths: tracker.declaredPaths,
@@ -185,8 +229,12 @@ export function finishRunWriteTracking(tracker) {
185
229
  const observed = truncatePaths(observedPaths);
186
230
  const declaredObserved = truncatePaths(declaredObservedPaths);
187
231
  const undeclared = truncatePaths(undeclaredPaths);
232
+ const status = tracker.before.status === 'partial' || after.status === 'partial' ? 'partial' : 'checked';
233
+ const reason = status === 'partial'
234
+ ? tracker.before.reason ?? after.reason ?? 'partial_snapshot'
235
+ : null;
188
236
  return {
189
- status: 'checked',
237
+ status,
190
238
  declared_paths: tracker.declaredPaths,
191
239
  observed_paths: observed.paths,
192
240
  declared_observed_paths: declaredObserved.paths,
@@ -195,6 +243,6 @@ export function finishRunWriteTracking(tracker) {
195
243
  undeclared_count: undeclaredPaths.length,
196
244
  has_undeclared_changes: undeclaredPaths.length > 0,
197
245
  truncated: observed.truncated || declaredObserved.truncated || undeclared.truncated,
198
- reason: null,
246
+ reason,
199
247
  };
200
248
  }
@@ -0,0 +1,155 @@
1
+ import { closeSync, constants, lstatSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
2
+ import { randomBytes } from 'node:crypto';
3
+ import path from 'node:path';
4
+ const NOFOLLOW_FLAG = typeof constants.O_NOFOLLOW === 'number' ? constants.O_NOFOLLOW : 0;
5
+ function isMissingPathError(error) {
6
+ return error instanceof Error && 'code' in error && error.code === 'ENOENT';
7
+ }
8
+ function tempFilePath(targetPath) {
9
+ const suffix = `${process.pid}-${Date.now()}-${randomBytes(6).toString('hex')}`;
10
+ return path.join(path.dirname(targetPath), `.${path.basename(targetPath)}.${suffix}.tmp`);
11
+ }
12
+ export function ensureInside(parentPath, childPath) {
13
+ const parent = path.resolve(parentPath);
14
+ const child = path.resolve(childPath);
15
+ const relative = path.relative(parent, child);
16
+ if (relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))) {
17
+ return;
18
+ }
19
+ throw new Error(`Path escapes allowed directory: ${childPath}`);
20
+ }
21
+ export function ensureInsideWithoutSymlinks(parentPath, childPath, options = {}) {
22
+ ensureInside(parentPath, childPath);
23
+ const parent = path.resolve(parentPath);
24
+ const child = path.resolve(childPath);
25
+ const relative = path.relative(parent, child);
26
+ const segments = relative === '' ? [] : relative.split(path.sep).filter((segment) => segment.length > 0);
27
+ let currentPath = parent;
28
+ const parentStats = lstatSync(parent);
29
+ if (parentStats.isSymbolicLink()) {
30
+ throw new Error(`Path must not contain symlinks: ${childPath}`);
31
+ }
32
+ for (const [index, segment] of segments.entries()) {
33
+ currentPath = path.join(currentPath, segment);
34
+ const isLeaf = index === segments.length - 1;
35
+ try {
36
+ const stats = lstatSync(currentPath);
37
+ if (stats.isSymbolicLink()) {
38
+ throw new Error(`Path must not contain symlinks: ${childPath}`);
39
+ }
40
+ if (!isLeaf && !stats.isDirectory()) {
41
+ throw new Error(`Path component is not a directory: ${currentPath}`);
42
+ }
43
+ }
44
+ catch (error) {
45
+ if (isMissingPathError(error) && options.allowMissingLeaf) {
46
+ return;
47
+ }
48
+ throw error;
49
+ }
50
+ }
51
+ }
52
+ function ensureDirectoryInsideWithoutSymlinks(parentPath, directoryPath) {
53
+ ensureInside(parentPath, directoryPath);
54
+ const parent = path.resolve(parentPath);
55
+ const directory = path.resolve(directoryPath);
56
+ const relative = path.relative(parent, directory);
57
+ const segments = relative === '' ? [] : relative.split(path.sep).filter((segment) => segment.length > 0);
58
+ let currentPath = parent;
59
+ const parentStats = lstatSync(parent);
60
+ if (parentStats.isSymbolicLink()) {
61
+ throw new Error(`Path must not contain symlinks: ${directoryPath}`);
62
+ }
63
+ for (const segment of segments) {
64
+ currentPath = path.join(currentPath, segment);
65
+ try {
66
+ const stats = lstatSync(currentPath);
67
+ if (stats.isSymbolicLink()) {
68
+ throw new Error(`Path must not contain symlinks: ${directoryPath}`);
69
+ }
70
+ if (!stats.isDirectory()) {
71
+ throw new Error(`Path component is not a directory: ${currentPath}`);
72
+ }
73
+ }
74
+ catch (error) {
75
+ if (!isMissingPathError(error)) {
76
+ throw error;
77
+ }
78
+ mkdirSync(currentPath);
79
+ const stats = lstatSync(currentPath);
80
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
81
+ throw new Error(`Path component is not a directory: ${currentPath}`);
82
+ }
83
+ }
84
+ }
85
+ }
86
+ export function ensureFileTargetInsideWithoutSymlinks(parentPath, childPath, options = {}) {
87
+ const absoluteChildPath = path.resolve(childPath);
88
+ ensureInside(parentPath, absoluteChildPath);
89
+ ensureInsideWithoutSymlinks(parentPath, path.dirname(absoluteChildPath), { allowMissingLeaf: true });
90
+ try {
91
+ const stats = lstatSync(absoluteChildPath);
92
+ if (stats.isSymbolicLink()) {
93
+ throw new Error(`Path must not contain symlinks: ${childPath}`);
94
+ }
95
+ if (!stats.isFile()) {
96
+ throw new Error(`Path must be a regular file: ${childPath}`);
97
+ }
98
+ }
99
+ catch (error) {
100
+ if (isMissingPathError(error) && options.allowMissingLeaf) {
101
+ return;
102
+ }
103
+ throw error;
104
+ }
105
+ }
106
+ export function readFileInsideWithoutSymlinks(parentPath, childPath) {
107
+ const absoluteChildPath = path.resolve(childPath);
108
+ ensureInsideWithoutSymlinks(parentPath, absoluteChildPath);
109
+ const fileDescriptor = openSync(absoluteChildPath, constants.O_RDONLY | NOFOLLOW_FLAG);
110
+ try {
111
+ return readFileSync(fileDescriptor);
112
+ }
113
+ finally {
114
+ closeSync(fileDescriptor);
115
+ }
116
+ }
117
+ export function writeFileInsideWithoutSymlinks(parentPath, childPath, content) {
118
+ const absoluteChildPath = path.resolve(childPath);
119
+ const directoryPath = path.dirname(absoluteChildPath);
120
+ ensureDirectoryInsideWithoutSymlinks(parentPath, directoryPath);
121
+ ensureFileTargetInsideWithoutSymlinks(parentPath, absoluteChildPath, { allowMissingLeaf: true });
122
+ const temporaryPath = tempFilePath(absoluteChildPath);
123
+ let fileDescriptor = null;
124
+ try {
125
+ fileDescriptor = openSync(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | NOFOLLOW_FLAG);
126
+ writeFileSync(fileDescriptor, content);
127
+ closeSync(fileDescriptor);
128
+ fileDescriptor = null;
129
+ ensureFileTargetInsideWithoutSymlinks(parentPath, absoluteChildPath, { allowMissingLeaf: true });
130
+ renameSync(temporaryPath, absoluteChildPath);
131
+ }
132
+ catch (error) {
133
+ if (fileDescriptor !== null) {
134
+ try {
135
+ closeSync(fileDescriptor);
136
+ }
137
+ catch {
138
+ // Best-effort cleanup before removing the temporary file.
139
+ }
140
+ }
141
+ try {
142
+ unlinkSync(temporaryPath);
143
+ }
144
+ catch {
145
+ // Best-effort cleanup for a temporary file that may not have been created.
146
+ }
147
+ throw error;
148
+ }
149
+ }
150
+ export function writeUtf8FileInsideWithoutSymlinks(parentPath, childPath, content) {
151
+ writeFileInsideWithoutSymlinks(parentPath, childPath, content);
152
+ }
153
+ export function writeJsonFileInsideWithoutSymlinks(parentPath, childPath, value) {
154
+ writeUtf8FileInsideWithoutSymlinks(parentPath, childPath, `${JSON.stringify(value, null, 2)}\n`);
155
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustflow",
3
- "version": "2.18.21",
3
+ "version": "2.21.1",
4
4
  "description": "Agent workflow documents and CLI for mustflow repository roots.",
5
5
  "type": "module",
6
6
  "license": "MIT-0",
@@ -137,6 +137,7 @@
137
137
  "cmd": { "type": "string" },
138
138
  "cwd": { "type": "string" },
139
139
  "timeout_seconds": { "type": "integer" },
140
+ "kill_after_seconds": { "type": "integer" },
140
141
  "stdin": { "type": "string" },
141
142
  "success_exit_codes": {
142
143
  "type": "array",
@@ -13,6 +13,7 @@
13
13
  "ok",
14
14
  "check",
15
15
  "context",
16
+ "command_environment",
16
17
  "effective_policy",
17
18
  "state_policy",
18
19
  "blocked_actions",
@@ -29,13 +30,18 @@
29
30
  "check": {
30
31
  "type": "object",
31
32
  "additionalProperties": false,
32
- "required": ["ok", "issue_count", "issues"],
33
+ "required": ["ok", "issue_count", "issues", "warning_count", "warnings"],
33
34
  "properties": {
34
35
  "ok": { "type": "boolean" },
35
36
  "issue_count": { "type": "integer" },
36
37
  "issues": {
37
38
  "type": "array",
38
39
  "items": { "type": "string" }
40
+ },
41
+ "warning_count": { "type": "integer" },
42
+ "warnings": {
43
+ "type": "array",
44
+ "items": { "type": "string" }
39
45
  }
40
46
  }
41
47
  },
@@ -83,6 +89,21 @@
83
89
  "latest_run_exists": { "type": "boolean" }
84
90
  }
85
91
  },
92
+ "command_environment": {
93
+ "type": "object",
94
+ "additionalProperties": false,
95
+ "required": ["inherited_intents", "inherited_network_intents"],
96
+ "properties": {
97
+ "inherited_intents": {
98
+ "type": "array",
99
+ "items": { "type": "string" }
100
+ },
101
+ "inherited_network_intents": {
102
+ "type": "array",
103
+ "items": { "type": "string" }
104
+ }
105
+ }
106
+ },
86
107
  "effective_policy": { "$ref": "#/$defs/effectivePolicy" },
87
108
  "state_policy": { "$ref": "#/$defs/statePolicy" },
88
109
  "blocked_actions": {
@@ -102,6 +123,7 @@
102
123
  "validation",
103
124
  "skill_routes",
104
125
  "commands",
126
+ "environment",
105
127
  "read_order",
106
128
  "optional_read_order",
107
129
  "repo_map",
@@ -55,7 +55,9 @@
55
55
  },
56
56
  "cmd": { "type": "string" },
57
57
  "timeout_seconds": { "type": "integer" },
58
+ "kill_after_seconds": { "type": "integer" },
58
59
  "max_output_bytes": { "type": "integer" },
60
+ "max_output_bytes_scope": { "const": "per_stream" },
59
61
  "success_exit_codes": {
60
62
  "type": "array",
61
63
  "items": { "type": "integer" }
@@ -174,8 +176,10 @@
174
176
  "properties": {
175
177
  "stdout_bytes": { "type": "integer", "minimum": 0 },
176
178
  "stderr_bytes": { "type": "integer", "minimum": 0 },
179
+ "total_bytes": { "type": "integer", "minimum": 0 },
177
180
  "stdout_truncated": { "type": "boolean" },
178
- "stderr_truncated": { "type": "boolean" }
181
+ "stderr_truncated": { "type": "boolean" },
182
+ "max_output_bytes_scope": { "const": "per_stream" }
179
183
  }
180
184
  },
181
185
  "result_summary": {
@@ -256,7 +260,7 @@
256
260
  "reason"
257
261
  ],
258
262
  "properties": {
259
- "status": { "enum": ["checked", "unavailable"] },
263
+ "status": { "enum": ["checked", "partial", "unavailable"] },
260
264
  "declared_paths": {
261
265
  "type": "array",
262
266
  "items": { "type": "string" }
@@ -56,13 +56,13 @@ translations = {}
56
56
  [documents."skills.index"]
57
57
  source = "locales/en/.mustflow/skills/INDEX.md"
58
58
  source_locale = "en"
59
- revision = 60
59
+ revision = 73
60
60
  translations = {}
61
61
 
62
62
  [documents."skill.adapter-boundary"]
63
63
  source = "locales/en/.mustflow/skills/adapter-boundary/SKILL.md"
64
64
  source_locale = "en"
65
- revision = 3
65
+ revision = 11
66
66
  translations = {}
67
67
 
68
68
  [documents."skill.artifact-integrity-check"]
@@ -104,7 +104,7 @@ translations = {}
104
104
  [documents."skill.database-change-safety"]
105
105
  source = "locales/en/.mustflow/skills/database-change-safety/SKILL.md"
106
106
  source_locale = "en"
107
- revision = 1
107
+ revision = 16
108
108
  translations = {}
109
109
 
110
110
  [documents."skill.dependency-injection"]
@@ -116,7 +116,7 @@ translations = {}
116
116
  [documents."skill.dependency-reality-check"]
117
117
  source = "locales/en/.mustflow/skills/dependency-reality-check/SKILL.md"
118
118
  source_locale = "en"
119
- revision = 3
119
+ revision = 6
120
120
  translations = {}
121
121
 
122
122
  [documents."skill.line-ending-hygiene"]
@@ -152,13 +152,13 @@ translations = {}
152
152
  [documents."skill.command-pattern"]
153
153
  source = "locales/en/.mustflow/skills/command-pattern/SKILL.md"
154
154
  source_locale = "en"
155
- revision = 4
155
+ revision = 13
156
156
  translations = {}
157
157
 
158
158
  [documents."skill.command-contract-authoring"]
159
159
  source = "locales/en/.mustflow/skills/command-contract-authoring/SKILL.md"
160
160
  source_locale = "en"
161
- revision = 1
161
+ revision = 2
162
162
  translations = {}
163
163
 
164
164
  [documents."skill.cross-platform-filesystem-safety"]
@@ -170,13 +170,13 @@ translations = {}
170
170
  [documents."skill.pure-core-imperative-shell"]
171
171
  source = "locales/en/.mustflow/skills/pure-core-imperative-shell/SKILL.md"
172
172
  source_locale = "en"
173
- revision = 6
173
+ revision = 7
174
174
  translations = {}
175
175
 
176
176
  [documents."skill.result-option"]
177
177
  source = "locales/en/.mustflow/skills/result-option/SKILL.md"
178
178
  source_locale = "en"
179
- revision = 2
179
+ revision = 3
180
180
  translations = {}
181
181
 
182
182
  [documents."skill.docs-update"]
@@ -223,7 +223,7 @@ translations = {}
223
223
  [documents."skill.migration-safety-check"]
224
224
  source = "locales/en/.mustflow/skills/migration-safety-check/SKILL.md"
225
225
  source_locale = "en"
226
- revision = 1
226
+ revision = 8
227
227
  translations = {}
228
228
 
229
229
  [documents."skill.multi-agent-work-coordination"]
@@ -241,7 +241,7 @@ translations = {}
241
241
  [documents."skill.performance-budget-check"]
242
242
  source = "locales/en/.mustflow/skills/performance-budget-check/SKILL.md"
243
243
  source_locale = "en"
244
- revision = 1
244
+ revision = 12
245
245
  translations = {}
246
246
 
247
247
  [documents."skill.pattern-scout"]
@@ -271,13 +271,13 @@ translations = {}
271
271
  [documents."skill.structure-discovery-gate"]
272
272
  source = "locales/en/.mustflow/skills/structure-discovery-gate/SKILL.md"
273
273
  source_locale = "en"
274
- revision = 12
274
+ revision = 26
275
275
  translations = {}
276
276
 
277
277
  [documents."skill.state-machine-pattern"]
278
278
  source = "locales/en/.mustflow/skills/state-machine-pattern/SKILL.md"
279
279
  source_locale = "en"
280
- revision = 1
280
+ revision = 4
281
281
  translations = {}
282
282
 
283
283
  [documents."skill.strategy-pattern"]
@@ -325,7 +325,7 @@ translations = {}
325
325
  [documents."skill.security-privacy-review"]
326
326
  source = "locales/en/.mustflow/skills/security-privacy-review/SKILL.md"
327
327
  source_locale = "en"
328
- revision = 7
328
+ revision = 16
329
329
  translations = {}
330
330
 
331
331
  [documents."skill.security-regression-tests"]