mustflow 2.74.0 → 2.74.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/commands/check.js +38 -26
- package/dist/cli/commands/doctor.js +17 -5
- package/dist/cli/commands/index.js +24 -9
- package/dist/cli/commands/map.js +20 -7
- package/dist/cli/commands/run.js +2 -1
- package/dist/cli/commands/update.js +52 -39
- package/dist/cli/lib/active-command-lock.js +96 -0
- package/dist/core/active-run-locks.js +7 -1
- package/package.json +1 -1
- package/templates/default/i18n.toml +7 -1
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +6 -1
- package/templates/default/locales/en/.mustflow/skills/routes.toml +6 -0
- package/templates/default/locales/en/.mustflow/skills/support-surface-advisor/SKILL.md +156 -0
- package/templates/default/manifest.toml +8 -1
package/README.md
CHANGED
|
@@ -334,7 +334,7 @@ Command environments remove the project-local `node_modules/.bin` path from `PAT
|
|
|
334
334
|
|
|
335
335
|
Use `mf verify --reason <event> --plan-only --json` to inspect matching verification intents, command eligibility, risk-priced evidence requirements, remaining gaps, and missing runnable coverage without executing commands. Use `mf run <intent> --dry-run --json` to inspect one resolved command intent without spawning a process or writing a run receipt. Plan-only verification includes a `decision_graph` that connects changed surfaces, classification reasons, command candidates, eligibility checks, effects, and gaps. When `.mustflow/cache/mustflow.sqlite` is fresh, scheduled entries also include read-only `effectGraph` metadata for write locks and lock conflicts. These graph rows are marked `explanation_only` and never grant command authority; `.mustflow/config/commands.toml` remains the only runnable command source.
|
|
336
336
|
|
|
337
|
-
Each executed command run writes a run record under `.mustflow/state/runs/run-*` and atomically updates `.mustflow/state/runs/latest.json`. The record includes the intent name, working directory, timeout, exit code, timeout status, and the tail of stdout and stderr.
|
|
337
|
+
Each executed command run writes a run record under `.mustflow/state/runs/run-*` and atomically updates `.mustflow/state/runs/latest.json`. The record includes the intent name, working directory, timeout, exit code, timeout status, and the tail of stdout and stderr. `latest.json` is a root-scoped convenience pointer, not session-scoped proof; in multi-agent or multi-terminal workflows, use the per-run `receipt_path` or `mf run <intent> --json` output as the evidence for a specific run.
|
|
338
338
|
|
|
339
339
|
## Language and profiles
|
|
340
340
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
2
|
+
import { acquireActiveCommandLock, GENERATED_SURFACE_READ_EFFECTS, reportActiveCommandLockConflict, } from '../lib/active-command-lock.js';
|
|
2
3
|
import { t } from '../lib/i18n.js';
|
|
3
4
|
import { formatCliOptionParseError, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
4
5
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
@@ -43,36 +44,47 @@ export function runCheck(args, reporter, lang = 'en') {
|
|
|
43
44
|
return 1;
|
|
44
45
|
}
|
|
45
46
|
const strict = hasParsedCliOption(options, '--strict');
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
reporter.stdout(JSON.stringify({
|
|
52
|
-
ok,
|
|
53
|
-
strict,
|
|
54
|
-
issueCount: issues.length,
|
|
55
|
-
issues,
|
|
56
|
-
warningCount: warnings.length,
|
|
57
|
-
warnings,
|
|
58
|
-
issueDetails: report.issueDetails,
|
|
59
|
-
}, null, 2));
|
|
60
|
-
return ok ? 0 : 1;
|
|
47
|
+
const projectRoot = resolveMustflowRoot();
|
|
48
|
+
const activeLock = acquireActiveCommandLock(projectRoot, 'mf check', GENERATED_SURFACE_READ_EFFECTS);
|
|
49
|
+
if (!activeLock.ok) {
|
|
50
|
+
reportActiveCommandLockConflict(reporter, 'mf check', activeLock.conflicts, 'mf check --help', lang);
|
|
51
|
+
return 1;
|
|
61
52
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
try {
|
|
54
|
+
const report = checkMustflowProjectReport(projectRoot, { strict });
|
|
55
|
+
const issues = report.issues;
|
|
56
|
+
const warnings = report.warnings;
|
|
57
|
+
const ok = issues.length === 0;
|
|
58
|
+
if (hasParsedCliOption(options, '--json')) {
|
|
59
|
+
reporter.stdout(JSON.stringify({
|
|
60
|
+
ok,
|
|
61
|
+
strict,
|
|
62
|
+
issueCount: issues.length,
|
|
63
|
+
issues,
|
|
64
|
+
warningCount: warnings.length,
|
|
65
|
+
warnings,
|
|
66
|
+
issueDetails: report.issueDetails,
|
|
67
|
+
}, null, 2));
|
|
68
|
+
return ok ? 0 : 1;
|
|
65
69
|
}
|
|
66
|
-
if (
|
|
67
|
-
|
|
70
|
+
if (ok) {
|
|
71
|
+
for (const warning of warnings) {
|
|
72
|
+
reporter.stderr(warning);
|
|
73
|
+
}
|
|
74
|
+
if (strict) {
|
|
75
|
+
reporter.stdout(t(lang, 'check.result.strictPassed'));
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
reporter.stdout(t(lang, 'check.result.passed'));
|
|
68
79
|
return 0;
|
|
69
80
|
}
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
for (const issue of issues) {
|
|
82
|
+
reporter.stderr(issue);
|
|
83
|
+
}
|
|
84
|
+
reporter.stderr(t(lang, 'check.result.failed', { count: issues.length }));
|
|
85
|
+
return 1;
|
|
72
86
|
}
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
finally {
|
|
88
|
+
activeLock.handle.release();
|
|
75
89
|
}
|
|
76
|
-
reporter.stderr(t(lang, 'check.result.failed', { count: issues.length }));
|
|
77
|
-
return 1;
|
|
78
90
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
4
|
+
import { acquireActiveCommandLock, GENERATED_SURFACE_READ_EFFECTS, reportActiveCommandLockConflict, } from '../lib/active-command-lock.js';
|
|
4
5
|
import { getAgentContext, } from '../lib/agent-context.js';
|
|
5
6
|
import { t } from '../lib/i18n.js';
|
|
6
7
|
import { getLocalIndexDatabasePath } from '../lib/local-index.js';
|
|
@@ -278,11 +279,22 @@ export function runDoctor(args, reporter, lang = 'en') {
|
|
|
278
279
|
printUsageError(reporter, formatCliOptionParseError(options.error, lang), 'mf doctor --help', getDoctorHelp(lang), lang);
|
|
279
280
|
return 1;
|
|
280
281
|
}
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
const projectRoot = resolveMustflowRoot();
|
|
283
|
+
const activeLock = acquireActiveCommandLock(projectRoot, 'mf doctor', GENERATED_SURFACE_READ_EFFECTS);
|
|
284
|
+
if (!activeLock.ok) {
|
|
285
|
+
reportActiveCommandLockConflict(reporter, 'mf doctor', activeLock.conflicts, 'mf doctor --help', lang);
|
|
286
|
+
return 1;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const output = createDoctorOutput(hasParsedCliOption(options, '--strict'));
|
|
290
|
+
if (hasParsedCliOption(options, '--json')) {
|
|
291
|
+
reporter.stdout(JSON.stringify(output, null, 2));
|
|
292
|
+
return output.ok ? 0 : 1;
|
|
293
|
+
}
|
|
294
|
+
reporter.stdout(renderDoctorOutput(output, lang));
|
|
284
295
|
return output.ok ? 0 : 1;
|
|
285
296
|
}
|
|
286
|
-
|
|
287
|
-
|
|
297
|
+
finally {
|
|
298
|
+
activeLock.handle.release();
|
|
299
|
+
}
|
|
288
300
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
2
|
+
import { acquireActiveCommandLock, LOCAL_INDEX_WRITE_EFFECTS, reportActiveCommandLockConflict, } from '../lib/active-command-lock.js';
|
|
2
3
|
import { t } from '../lib/i18n.js';
|
|
3
4
|
import { createLocalIndex } from '../lib/local-index.js';
|
|
4
5
|
import { formatCliOptionParseError, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
@@ -83,15 +84,29 @@ export async function runIndex(args, reporter, lang = 'en') {
|
|
|
83
84
|
printUsageError(reporter, formatCliOptionParseError(parsed.error, lang), 'mf index --help', getIndexHelp(lang), lang);
|
|
84
85
|
return 1;
|
|
85
86
|
}
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
const dryRun = hasParsedCliOption(parsed, '--dry-run');
|
|
88
|
+
const projectRoot = resolveMustflowRoot();
|
|
89
|
+
const activeLock = dryRun ? null : acquireActiveCommandLock(projectRoot, 'mf index', LOCAL_INDEX_WRITE_EFFECTS);
|
|
90
|
+
if (activeLock && !activeLock.ok) {
|
|
91
|
+
reportActiveCommandLockConflict(reporter, 'mf index', activeLock.conflicts, 'mf index --help', lang);
|
|
92
|
+
return 1;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const result = await createLocalIndex(projectRoot, {
|
|
96
|
+
dryRun,
|
|
97
|
+
includeSource: hasParsedCliOption(parsed, '--source'),
|
|
98
|
+
incremental: hasParsedCliOption(parsed, '--incremental'),
|
|
99
|
+
});
|
|
100
|
+
if (hasParsedCliOption(parsed, '--json')) {
|
|
101
|
+
reporter.stdout(JSON.stringify(result, null, 2));
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
reporter.stdout(renderIndexSummary(result, lang));
|
|
93
105
|
return 0;
|
|
94
106
|
}
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
finally {
|
|
108
|
+
if (activeLock?.ok) {
|
|
109
|
+
activeLock.handle.release();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
97
112
|
}
|
package/dist/cli/commands/map.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
2
2
|
import { t } from '../lib/i18n.js';
|
|
3
|
+
import { acquireActiveCommandLock, REPO_MAP_WRITE_EFFECTS, reportActiveCommandLockConflict, } from '../lib/active-command-lock.js';
|
|
3
4
|
import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
4
5
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
5
6
|
import { generateRepoMap, writeRepoMap } from '../lib/repo-map.js';
|
|
@@ -71,13 +72,25 @@ export function runMap(args, reporter, lang = 'en') {
|
|
|
71
72
|
shouldPrint = true;
|
|
72
73
|
}
|
|
73
74
|
const projectRoot = resolveMustflowRoot();
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
const activeLock = shouldWrite ? acquireActiveCommandLock(projectRoot, 'mf map --write', REPO_MAP_WRITE_EFFECTS) : null;
|
|
76
|
+
if (activeLock && !activeLock.ok) {
|
|
77
|
+
reportActiveCommandLockConflict(reporter, 'mf map --write', activeLock.conflicts, 'mf map --help', lang);
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const content = generateRepoMap(projectRoot, { depth, includeNested });
|
|
82
|
+
if (shouldWrite) {
|
|
83
|
+
writeRepoMap(projectRoot, content);
|
|
84
|
+
reporter.stdout(t(lang, 'map.wrote'));
|
|
85
|
+
}
|
|
86
|
+
if (shouldPrint) {
|
|
87
|
+
reporter.stdout(content);
|
|
88
|
+
}
|
|
89
|
+
return 0;
|
|
78
90
|
}
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
finally {
|
|
92
|
+
if (activeLock?.ok) {
|
|
93
|
+
activeLock.handle.release();
|
|
94
|
+
}
|
|
81
95
|
}
|
|
82
|
-
return 0;
|
|
83
96
|
}
|
package/dist/cli/commands/run.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { performance } from 'node:perf_hooks';
|
|
3
|
-
import { acquireActiveRunLock } from '../../core/active-run-locks.js';
|
|
3
|
+
import { ACTIVE_RUN_LOCK_ID_ENV, acquireActiveRunLock } from '../../core/active-run-locks.js';
|
|
4
4
|
import { createCommandEnv } from '../../core/command-env.js';
|
|
5
5
|
import { createCorrelationId } from '../../core/correlation-id.js';
|
|
6
6
|
import { printUsageError, renderCliError, renderHelp } from '../lib/cli-output.js';
|
|
@@ -360,6 +360,7 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
|
|
|
360
360
|
try {
|
|
361
361
|
const runReceiptPolicy = profiler.measure('retention_policy', () => resolveRunReceiptRetentionPolicy(readMustflowConfigIfExists(projectRoot)));
|
|
362
362
|
const env = profiler.measure('environment', () => createCommandEnv(projectRoot, { policy: plan.envPolicy, allowlist: plan.envAllowlist }));
|
|
363
|
+
env[ACTIVE_RUN_LOCK_ID_ENV] = activeRunLock.handle.record.run_id;
|
|
363
364
|
const writeTracker = profiler.measure('write_drift_before', () => startRunWriteTracking(projectRoot, contract, intentName, {
|
|
364
365
|
additionalDeclaredPaths: options.additionalDeclaredWritePaths,
|
|
365
366
|
env,
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { copyFileInsideWithoutSymlinks, ensureFileTargetInsideWithoutSymlinks, ensureInside, writeUtf8FileInsideWithoutSymlinks, } from '../lib/filesystem.js';
|
|
5
5
|
import { MANIFEST_LOCK_RELATIVE_PATH, readManifestLock, sha256File } from '../lib/manifest-lock.js';
|
|
6
6
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
7
|
+
import { acquireActiveCommandLock, MUSTFLOW_UPDATE_APPLY_EFFECTS, reportActiveCommandLockConflict, } from '../lib/active-command-lock.js';
|
|
7
8
|
import { t } from '../lib/i18n.js';
|
|
8
9
|
import { formatCliOptionParseError, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
9
10
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
@@ -430,53 +431,65 @@ export function runUpdate(args, reporter, lang = 'en') {
|
|
|
430
431
|
return 1;
|
|
431
432
|
}
|
|
432
433
|
const projectRoot = resolveMustflowRoot();
|
|
433
|
-
const
|
|
434
|
-
if (
|
|
435
|
-
|
|
436
|
-
reporter.stdout(JSON.stringify(withMode(planOutput(publicPlanItems(plan.items), plan.error, false), requestedMode), null, 2));
|
|
437
|
-
return 1;
|
|
438
|
-
}
|
|
439
|
-
reporter.stderr(plan.error);
|
|
434
|
+
const activeLock = wantsApply ? acquireActiveCommandLock(projectRoot, 'mf update --apply', MUSTFLOW_UPDATE_APPLY_EFFECTS) : null;
|
|
435
|
+
if (activeLock && !activeLock.ok) {
|
|
436
|
+
reportActiveCommandLockConflict(reporter, 'mf update --apply', activeLock.conflicts, 'mf update --help', lang);
|
|
440
437
|
return 1;
|
|
441
438
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
439
|
+
try {
|
|
440
|
+
const plan = createUpdatePlan(projectRoot);
|
|
441
|
+
if (plan.error) {
|
|
442
|
+
if (wantsJson) {
|
|
443
|
+
reporter.stdout(JSON.stringify(withMode(planOutput(publicPlanItems(plan.items), plan.error, false), requestedMode), null, 2));
|
|
444
|
+
return 1;
|
|
445
|
+
}
|
|
446
|
+
reporter.stderr(plan.error);
|
|
447
|
+
return 1;
|
|
448
|
+
}
|
|
449
|
+
const outputItems = wantsDiff ? withDiffPreviews(projectRoot, plan.items) : publicPlanItems(plan.items);
|
|
450
|
+
const dryRunOutput = withMode(planOutput(outputItems, undefined, false), requestedMode);
|
|
451
|
+
if (wantsDryRun) {
|
|
452
|
+
if (wantsJson) {
|
|
453
|
+
reporter.stdout(JSON.stringify(dryRunOutput, null, 2));
|
|
454
|
+
return dryRunOutput.ok ? 0 : 1;
|
|
455
|
+
}
|
|
456
|
+
printPlan(dryRunOutput, reporter, lang);
|
|
457
|
+
if (wantsDiff) {
|
|
458
|
+
printDiffPreviews(dryRunOutput.items, reporter, lang);
|
|
459
|
+
}
|
|
460
|
+
reporter.stdout(t(lang, 'update.plan.noFilesWritten'));
|
|
447
461
|
return dryRunOutput.ok ? 0 : 1;
|
|
448
462
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
463
|
+
if (!dryRunOutput.ok) {
|
|
464
|
+
if (wantsJson) {
|
|
465
|
+
reporter.stdout(JSON.stringify(dryRunOutput, null, 2));
|
|
466
|
+
return 1;
|
|
467
|
+
}
|
|
468
|
+
printPlan(dryRunOutput, reporter, lang);
|
|
469
|
+
reporter.stdout(t(lang, 'update.plan.noFilesWritten'));
|
|
470
|
+
return 1;
|
|
452
471
|
}
|
|
453
|
-
reporter.stdout(t(lang, 'update.plan.noFilesWritten'));
|
|
454
|
-
return dryRunOutput.ok ? 0 : 1;
|
|
455
|
-
}
|
|
456
|
-
if (!dryRunOutput.ok) {
|
|
457
472
|
if (wantsJson) {
|
|
458
|
-
|
|
459
|
-
|
|
473
|
+
const applicableItems = plan.items.filter((item) => item.action === 'create' || item.action === 'update');
|
|
474
|
+
const applyResult = applyUpdate(projectRoot, applicableItems, {
|
|
475
|
+
stdout: () => undefined,
|
|
476
|
+
stderr: (message) => reporter.stderr(message),
|
|
477
|
+
}, lang);
|
|
478
|
+
reporter.stdout(JSON.stringify(withMode(planOutput(publicPlanItems(plan.items), undefined, applyResult.wroteFiles), 'apply'), null, 2));
|
|
479
|
+
return 0;
|
|
460
480
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
stdout: () => undefined,
|
|
469
|
-
stderr: (message) => reporter.stderr(message),
|
|
470
|
-
}, lang);
|
|
471
|
-
reporter.stdout(JSON.stringify(withMode(planOutput(publicPlanItems(plan.items), undefined, applyResult.wroteFiles), 'apply'), null, 2));
|
|
481
|
+
const applyResult = applyUpdate(projectRoot, plan.items, reporter, lang);
|
|
482
|
+
if (!applyResult.wroteFiles) {
|
|
483
|
+
reporter.stdout(t(lang, 'update.plan.noUpdates'));
|
|
484
|
+
reporter.stdout(t(lang, 'update.plan.noFilesWritten'));
|
|
485
|
+
return 0;
|
|
486
|
+
}
|
|
487
|
+
reporter.stdout(t(lang, 'update.complete', { updated: applyResult.updated, created: applyResult.created }));
|
|
472
488
|
return 0;
|
|
473
489
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
return 0;
|
|
490
|
+
finally {
|
|
491
|
+
if (activeLock?.ok) {
|
|
492
|
+
activeLock.handle.release();
|
|
493
|
+
}
|
|
479
494
|
}
|
|
480
|
-
reporter.stdout(t(lang, 'update.complete', { updated: applyResult.updated, created: applyResult.created }));
|
|
481
|
-
return 0;
|
|
482
495
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ACTIVE_RUN_LOCK_ID_ENV, acquireActiveRunLock, } from '../../core/active-run-locks.js';
|
|
2
|
+
import { renderCliError } from './cli-output.js';
|
|
3
|
+
import { t } from './i18n.js';
|
|
4
|
+
export const REPO_MAP_WRITE_EFFECTS = [
|
|
5
|
+
{ type: 'write', mode: 'replace', path: 'REPO_MAP.md', concurrency: 'exclusive' },
|
|
6
|
+
];
|
|
7
|
+
export const LOCAL_INDEX_WRITE_EFFECTS = [
|
|
8
|
+
{
|
|
9
|
+
type: 'write',
|
|
10
|
+
mode: 'replace',
|
|
11
|
+
path: '.mustflow/cache/**',
|
|
12
|
+
lock: 'local_index_cache',
|
|
13
|
+
concurrency: 'exclusive',
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
export const MUSTFLOW_UPDATE_APPLY_EFFECTS = [
|
|
17
|
+
{ type: 'write', mode: 'replace', path: 'AGENTS.md', concurrency: 'exclusive' },
|
|
18
|
+
{ type: 'write', mode: 'replace', path: '.mustflow/config/manifest.lock.toml', concurrency: 'exclusive' },
|
|
19
|
+
{ type: 'write', mode: 'replace', path: '.mustflow/config/commands.toml', concurrency: 'exclusive' },
|
|
20
|
+
{ type: 'write', mode: 'replace', path: '.mustflow/config/mustflow.toml', concurrency: 'exclusive' },
|
|
21
|
+
{ type: 'write', mode: 'replace', path: '.mustflow/config/preferences.toml', concurrency: 'exclusive' },
|
|
22
|
+
{ type: 'write', mode: 'replace', path: '.mustflow/context/**', concurrency: 'exclusive' },
|
|
23
|
+
{ type: 'write', mode: 'replace', path: '.mustflow/docs/**', concurrency: 'exclusive' },
|
|
24
|
+
{ type: 'write', mode: 'replace', path: '.mustflow/skills/**', concurrency: 'exclusive' },
|
|
25
|
+
{ type: 'write', mode: 'replace', path: '.mustflow/backups/**', concurrency: 'exclusive' },
|
|
26
|
+
];
|
|
27
|
+
export const GENERATED_SURFACE_READ_EFFECTS = [
|
|
28
|
+
{ type: 'read', mode: 'read', path: 'REPO_MAP.md', concurrency: 'shared' },
|
|
29
|
+
{ type: 'read', mode: 'read', path: '.mustflow/config/manifest.lock.toml', concurrency: 'shared' },
|
|
30
|
+
{
|
|
31
|
+
type: 'read',
|
|
32
|
+
mode: 'read',
|
|
33
|
+
path: '.mustflow/cache/**',
|
|
34
|
+
lock: 'local_index_cache',
|
|
35
|
+
concurrency: 'shared',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
function effectToToml(effect) {
|
|
39
|
+
const output = {
|
|
40
|
+
type: effect.type,
|
|
41
|
+
};
|
|
42
|
+
if (effect.mode) {
|
|
43
|
+
output.mode = effect.mode;
|
|
44
|
+
}
|
|
45
|
+
if (effect.path) {
|
|
46
|
+
output.path = effect.path;
|
|
47
|
+
}
|
|
48
|
+
if (effect.paths) {
|
|
49
|
+
output.paths = [...effect.paths];
|
|
50
|
+
}
|
|
51
|
+
if (effect.lock) {
|
|
52
|
+
output.lock = effect.lock;
|
|
53
|
+
}
|
|
54
|
+
if (effect.concurrency) {
|
|
55
|
+
output.concurrency = effect.concurrency;
|
|
56
|
+
}
|
|
57
|
+
return output;
|
|
58
|
+
}
|
|
59
|
+
function createSyntheticCommandContract(intentName, effects) {
|
|
60
|
+
return {
|
|
61
|
+
defaults: { default_cwd: '.' },
|
|
62
|
+
resources: {},
|
|
63
|
+
intents: {
|
|
64
|
+
[intentName]: {
|
|
65
|
+
cwd: '.',
|
|
66
|
+
writes: [],
|
|
67
|
+
effects: effects.map(effectToToml),
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function parentRunId() {
|
|
73
|
+
const value = process.env[ACTIVE_RUN_LOCK_ID_ENV]?.trim();
|
|
74
|
+
return value && value.length > 0 ? value : null;
|
|
75
|
+
}
|
|
76
|
+
export function acquireActiveCommandLock(projectRoot, displayName, effects) {
|
|
77
|
+
return acquireActiveRunLock(projectRoot, createSyntheticCommandContract(displayName, effects), displayName, {
|
|
78
|
+
commandHash: null,
|
|
79
|
+
ignoreRunId: parentRunId(),
|
|
80
|
+
ignorePid: process.ppid,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
export function renderActiveCommandLockConflictMessage(displayName, conflicts, lang) {
|
|
84
|
+
const [first] = conflicts;
|
|
85
|
+
const detail = first
|
|
86
|
+
? t(lang, 'run.error.activeLockConflictDetail', {
|
|
87
|
+
lock: first.lock,
|
|
88
|
+
intent: first.conflictsWithIntent,
|
|
89
|
+
pid: first.conflictsWithPid,
|
|
90
|
+
})
|
|
91
|
+
: t(lang, 'run.error.activeLockConflictUnknown');
|
|
92
|
+
return t(lang, 'run.error.activeLockConflict', { intent: displayName, detail });
|
|
93
|
+
}
|
|
94
|
+
export function reportActiveCommandLockConflict(reporter, displayName, conflicts, helpCommand, lang) {
|
|
95
|
+
reporter.stderr(renderCliError(renderActiveCommandLockConflictMessage(displayName, conflicts, lang), helpCommand, lang));
|
|
96
|
+
}
|
|
@@ -6,6 +6,7 @@ import { readUtf8FileInsideWithoutSymlinks, writeJsonFileInsideWithoutSymlinks,
|
|
|
6
6
|
const ACTIVE_LOCK_SCHEMA_VERSION = '1';
|
|
7
7
|
const ACTIVE_LOCK_KIND = 'active_run_lock';
|
|
8
8
|
const LOCK_ROOT_RELATIVE_PATH = '.mustflow/state/locks';
|
|
9
|
+
export const ACTIVE_RUN_LOCK_ID_ENV = 'MUSTFLOW_ACTIVE_RUN_LOCK_ID';
|
|
9
10
|
const LOCK_MUTEX_STALE_MS = 30_000;
|
|
10
11
|
const LOCK_MUTEX_WAIT_MS = 1_000;
|
|
11
12
|
const LOCK_MUTEX_SLEEP_MS = 25;
|
|
@@ -462,7 +463,12 @@ export function acquireActiveRunLock(projectRoot, contract, intentName, options
|
|
|
462
463
|
}
|
|
463
464
|
}
|
|
464
465
|
const staleRecordIds = new Set(staleRecords.map((stale) => stale.runId));
|
|
465
|
-
const liveRecords = records.filter((record) =>
|
|
466
|
+
const liveRecords = records.filter((record) => {
|
|
467
|
+
if (staleRecordIds.has(record.run_id)) {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
return record.run_id !== options.ignoreRunId || record.pid !== options.ignorePid;
|
|
471
|
+
});
|
|
466
472
|
const conflicts = findConflicts(intentName, effects, liveRecords);
|
|
467
473
|
if (conflicts.length > 0) {
|
|
468
474
|
return { ok: false, conflicts, recoveredStaleRecords: staleRecords };
|
package/package.json
CHANGED
|
@@ -62,7 +62,7 @@ translations = {}
|
|
|
62
62
|
[documents."skills.index"]
|
|
63
63
|
source = "locales/en/.mustflow/skills/INDEX.md"
|
|
64
64
|
source_locale = "en"
|
|
65
|
-
revision =
|
|
65
|
+
revision = 173
|
|
66
66
|
translations = {}
|
|
67
67
|
|
|
68
68
|
[documents."skill.adapter-boundary"]
|
|
@@ -640,6 +640,12 @@ source_locale = "en"
|
|
|
640
640
|
revision = 1
|
|
641
641
|
translations = {}
|
|
642
642
|
|
|
643
|
+
[documents."skill.support-surface-advisor"]
|
|
644
|
+
source = "locales/en/.mustflow/skills/support-surface-advisor/SKILL.md"
|
|
645
|
+
source_locale = "en"
|
|
646
|
+
revision = 1
|
|
647
|
+
translations = {}
|
|
648
|
+
|
|
643
649
|
[documents."skill.svelte-code-change"]
|
|
644
650
|
source = "locales/en/.mustflow/skills/svelte-code-change/SKILL.md"
|
|
645
651
|
source_locale = "en"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
mustflow_doc: skills.index
|
|
3
3
|
locale: en
|
|
4
4
|
canonical: true
|
|
5
|
-
revision:
|
|
5
|
+
revision: 173
|
|
6
6
|
authority: router
|
|
7
7
|
lifecycle: mustflow-owned
|
|
8
8
|
---
|
|
@@ -309,6 +309,10 @@ refer to `AGENTS.md` and `.mustflow/config/commands.toml` to implement the most
|
|
|
309
309
|
rules, public contracts, external I/O, operational safety, failure handling, concurrency,
|
|
310
310
|
data flow, or future change cost could be shaped by early structure decisions. Do not use it for
|
|
311
311
|
surface-only changes or tiny local edits with no boundary pressure.
|
|
312
|
+
- Use `support-surface-advisor` as a primary route before building a product, app, service, CLI,
|
|
313
|
+
API, SDK, library, desktop app, automation tool, or developer tool when the agent must decide
|
|
314
|
+
which user, developer, operator, automation, integration, recovery, upgrade, or observability
|
|
315
|
+
routes are supported now, deferred, or explicitly not supported.
|
|
312
316
|
- Use `secret-exposure-response` as an event route when a real or plausible secret value appears;
|
|
313
317
|
do not treat redaction as proof that rotation, revocation, or history exposure is handled.
|
|
314
318
|
- Use `evidence-stall-breaker` as an event route when the same read, list, search, path, or review
|
|
@@ -407,6 +411,7 @@ routes. Event routes stay inactive until their event occurs.
|
|
|
407
411
|
| Code review or implementation needs transaction-boundary integrity triage for transactions, ORM atomic blocks, unit-of-work code, database write paths, service workflows, command handlers, webhook processors, queue consumers, framework transaction annotations, isolation levels, lock usage, rollback behavior, after-commit side effects, outbox patterns, retry handling, or transactional tests that can break a business invariant even when a transaction exists | `.mustflow/skills/transaction-boundary-integrity-review/SKILL.md` | Business invariant, transaction boundary, decision ledger, durable guard evidence, framework behavior, side-effect ledger, failure and retry evidence, test evidence, and configured command intents | Whole read-decision-write transaction boundaries, durable constraints, atomic upsert or conditional update, affected-row checks, version checks, correct lock target, transaction narrowing, after-commit or outbox side effects, idempotent retry classification, focused tests, and directly synchronized docs or templates | app-only `exists()` or `count` guard, stale read-decision-write, absent-row `FOR UPDATE` gap, `SKIP LOCKED` consistency misuse, READ COMMITTED snapshot myth, REPEATABLE READ engine mismatch, SERIALIZABLE without full retry, deadlock or serialization failure treated as plain 500, swallowed rollback trigger, Spring `rollbackFor` miss, self-invocation bypass, `readOnly` write assumption, inner rollback-only surprise, `REQUIRES_NEW` pool pressure, `NESTED` savepoint confusion, Django nested `atomic()` durability myth, pre-commit email/cache/queue side effect, HTTP API inside transaction, Hibernate flush-as-commit confusion, SQLAlchemy implicit transaction surprise, missing optimistic lock, advisory lock scope leak, wrong transaction manager, or transactional test hiding commit behavior | `changes_status`, `changes_diff_summary`, `lint`, `build`, `test_related`, `test`, `docs_validate_fast`, `test_release`, `mustflow_check` | Transaction boundary reviewed, invariant and decision ledger, durable-guard/lock/isolation/retry/rollback/side-effect findings, evidence level, verification, and remaining transaction-boundary integrity risk |
|
|
408
412
|
| Code review or implementation needs testability-boundary triage for hidden decision inputs, direct time or randomness, direct I/O, constructor side effects, static or singleton state, oversized private logic, branch policy sprawl, boolean mode flags, broad option objects, implicit environment or request context, void side effects, swallowed errors, log-only outcomes, cache order dependence, ORM lazy loading, transaction and external-call coupling, hidden event publication, fire-and-forget async work, real-time retry waits, nondeterministic collection order, hidden defaults, mixed validation and policy, scattered authorization, framework magic, smart controllers, conditional DTO mapping, mock-heavy classes, call-order assertions, inheritance coupling, or reflection-only tests | `.mustflow/skills/testability-boundary-review/SKILL.md` | User goal, current diff or target files, behavior or decision under test, decision-input ledger, side-effect ledger, observability ledger, test friction evidence, and configured command intents | Explicit inputs, fixed clocks or generators, dependency seams, pure decision cores, shell and adapter boundaries, policy or strategy tables, observable results, deterministic ordering, focused tests, and directly synchronized docs or templates | hidden time/random, constructor work, global state, fat private method, boolean mode flags, broad option object, void side effect, swallowed or log-only failure, cache order dependence, ORM lazy loading, transaction/external call coupling, hidden event publication, fire-and-forget sleeps, real-time retry wait, nondeterministic order, hidden defaults, mixed validation/policy, scattered auth, framework magic, smart controller, conditional DTO policy, five or more mocks, fragile call-order tests, deep inheritance, or reflection-only tests | `changes_status`, `changes_diff_summary`, `lint`, `build`, `test_related`, `test`, `test_audit`, `docs_validate_fast`, `test_release`, `mustflow_check` | Testability boundary reviewed, decision inputs and side effects exposed, observability fixed or recommended, test friction evidence, verification, and remaining testability risk |
|
|
409
413
|
| A coding task has missing intent, scope, domain, data, security, UX, dependency, architecture, or verification decisions that cannot be safely inferred from repository evidence | `.mustflow/skills/clarifying-question-gate/SKILL.md` | User request, inspected repository evidence, unresolved decisions, reversibility classification, recommended option, and tradeoffs | Blocking questions, safe assumptions, and the smallest safe implementation boundary | over-questioning, lazy questions, expensive wrong assumptions, user-owned decision drift, data loss, auth bypass, public-contract drift, dependency bloat, or unverifiable completion | `changes_status`, `changes_diff_summary`, `mustflow_check` | Repository evidence inspected, blocking questions with recommendations, safe assumptions, selected scope, verification, and remaining ambiguity |
|
|
414
|
+
| Product, app, service, CLI, API, SDK, library, desktop app, automation tool, or developer tool work needs a decision about which user, developer, operator, automation, integration, recovery, upgrade, documentation, or observability surfaces are supported now, deferred, explicitly unsupported, or internal-only | `.mustflow/skills/support-surface-advisor/SKILL.md` | Product stage, primary actors, main usage path, integration need, maintenance capacity, public-contract willingness, explicit non-goals, recovery and observability expectations, and current repository evidence | Support-surface plan, selected implementation boundaries, docs, tests, route metadata, core-engine boundary, and directly synchronized templates when installed | support-contract bloat, accidental public API, UI/CLI/API duplicate core logic, hidden integration promise, unsupported automation route, unowned recovery path, stale compatibility promise, or implementation explanation leaking into user-facing UI copy | `changes_status`, `changes_diff_summary`, `docs_validate_fast`, `test_release`, `mustflow_check` | Product stage, actors, recommended surfaces, deferred and unsupported surfaces, blocking questions, maintenance and compatibility risks, core engine versus shell boundary, staged plan, verification, and remaining support-surface risk |
|
|
410
415
|
| A task chooses, migrates, rewrites, or justifies a primary language, runtime, framework, compile target, or execution environment | `.mustflow/skills/runtime-target-selection/SKILL.md` | Current runtime surfaces, target options, product or system need, environment constraints, migration boundary, smoke targets, and performance or reliability claims | Decision records, skill procedures, route metadata, migration plans, command-contract proposals, tests, fixtures, docs, and smallest selected migration scaffold | language-preference rewrite, unsupported runtime target, unusable build loop, cache or artifact blowup, missing smoke target, deployment drift, or false performance claim | `changes_status`, `changes_diff_summary`, `docs_validate_fast`, `test_related`, `test_release`, `mustflow_check` | Decision boundary, candidate targets, environment and build-loop evidence, smoke targets, migration boundary, calibrated claims, verification, and remaining runtime-target risk |
|
|
411
416
|
| Non-trivial code work needs early structure decisions around domain rules, public contracts, external I/O, operational safety, failure handling, concurrency, data flow, or future change cost | `.mustflow/skills/structure-first-engineering/SKILL.md` | User request, target files, project context, core boundary, data flow, expected failures, public contracts, I/O surfaces, and verification contract | Risk block, focused boundaries, DTOs, adapters, pure functions, error models, tests, and directly synchronized docs or contracts | under-designed hard boundary, speculative abstraction, vague service layer, mixed I/O and domain rules, hidden partial failure, or untestable behavior | `changes_status`, `changes_diff_summary`, `test_related`, `test`, `lint`, `build`, `docs_validate_fast`, `test_release`, `mustflow_check` | Work risk, structure decision, data flow, failure model, I/O and concurrency boundaries, tests, verification, and remaining structure risk |
|
|
412
417
|
| HTTP, REST, GraphQL, tRPC, Hono RPC, Elysia Eden, gRPC, protobuf, OpenAPI, request/response schema, status code, header, content negotiation, cache header, error envelope, pagination, filtering, sorting, search, generated client, SDK, mock, fixture, or API docs contract is created or changed | `.mustflow/skills/api-contract-change/SKILL.md` | API style, contract source of truth, changed operations, request and response schemas, status and headers, content negotiation, error envelope, auth and permission behavior, pagination/filter/sort/search semantics, generated clients, SDKs, mocks, fixtures, callers, docs, and command contract entries | Routes, handlers, resolvers, validators, schemas, generated clients, SDKs, mocks, fixtures, docs, tests, and directly synchronized examples | route-only change, schema drift, generated-client breakage, hidden breaking change, status or error drift, pagination/search semantic drift, auth/permission drift, cache/header drift, or stale docs examples | `changes_status`, `changes_diff_summary`, `lint`, `build`, `test_related`, `test`, `docs_validate_fast`, `test_release`, `mustflow_check` | API contract source, changed operations, compatibility classification, synchronized client/schema/docs/tests surfaces, verification, and remaining API contract risk |
|
|
@@ -366,6 +366,12 @@ route_type = "adjunct"
|
|
|
366
366
|
priority = 42
|
|
367
367
|
applies_to_reasons = ["unknown_change", "code_change", "behavior_change", "public_api_change", "security_change", "privacy_change", "data_change", "migration_change", "package_metadata_change"]
|
|
368
368
|
|
|
369
|
+
[routes."support-surface-advisor"]
|
|
370
|
+
category = "general_code"
|
|
371
|
+
route_type = "primary"
|
|
372
|
+
priority = 67
|
|
373
|
+
applies_to_reasons = ["unknown_change", "code_change", "behavior_change", "public_api_change", "docs_change", "product_change", "package_metadata_change"]
|
|
374
|
+
|
|
369
375
|
[routes."runtime-target-selection"]
|
|
370
376
|
category = "general_code"
|
|
371
377
|
route_type = "primary"
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
mustflow_doc: skill.support-surface-advisor
|
|
3
|
+
locale: en
|
|
4
|
+
canonical: true
|
|
5
|
+
revision: 1
|
|
6
|
+
lifecycle: mustflow-owned
|
|
7
|
+
authority: procedure
|
|
8
|
+
name: support-surface-advisor
|
|
9
|
+
description: Apply this skill when designing, scoping, implementing, or reviewing an app, service, CLI, API, SDK, library, desktop app, automation tool, or developer tool and the agent must decide which user, developer, operator, automation, integration, recovery, upgrade, documentation, or observability surfaces to expose, defer, or explicitly not support.
|
|
10
|
+
metadata:
|
|
11
|
+
mustflow_schema: "1"
|
|
12
|
+
mustflow_kind: procedure
|
|
13
|
+
pack_id: mustflow.core
|
|
14
|
+
skill_id: mustflow.core.support-surface-advisor
|
|
15
|
+
command_intents:
|
|
16
|
+
- changes_status
|
|
17
|
+
- changes_diff_summary
|
|
18
|
+
- docs_validate_fast
|
|
19
|
+
- test_release
|
|
20
|
+
- mustflow_check
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Support Surface Advisor
|
|
24
|
+
|
|
25
|
+
<!-- mustflow-section: purpose -->
|
|
26
|
+
## Purpose
|
|
27
|
+
|
|
28
|
+
Help agents turn vague product or program-development requests into a bounded support contract. A support surface is not just a feature; once exposed, it becomes a compatibility, documentation, testing, and maintenance promise.
|
|
29
|
+
|
|
30
|
+
<!-- mustflow-section: use-when -->
|
|
31
|
+
## Use When
|
|
32
|
+
|
|
33
|
+
- A user asks to build, scope, review, or evolve an app, service, CLI, API, SDK, library, desktop app, automation tool, or developer tool.
|
|
34
|
+
- It is unclear whether the product should expose UI, CLI, programming API, network API, webhooks, plugins, configuration, import/export, docs, logs, diagnostics, test support, recovery, performance limits, security/privacy, accessibility/i18n, or deployment/upgrade support.
|
|
35
|
+
- The user may be a solo founder, inexperienced developer, or product owner who knows the core idea but not the support routes real users will need.
|
|
36
|
+
- Multiple shells could reach the same core behavior, such as UI plus CLI, CLI plus API, or UI plus webhook automation.
|
|
37
|
+
- A support route might accidentally become a public API, stable file format, integration promise, operational runbook, or backward-compatibility burden.
|
|
38
|
+
|
|
39
|
+
<!-- mustflow-section: do-not-use-when -->
|
|
40
|
+
## Do Not Use When
|
|
41
|
+
|
|
42
|
+
- The task is a tiny local bug fix with no product, integration, operation, or public-contract decision.
|
|
43
|
+
- The user has already selected the support surfaces and only asks for implementation inside one route.
|
|
44
|
+
- A narrower API, CLI output, UI, security, data, or release skill already owns the specific changed contract.
|
|
45
|
+
- The work is only visual polish, copy editing, or one-off internal tooling with no exposed support promise.
|
|
46
|
+
|
|
47
|
+
<!-- mustflow-section: required-inputs -->
|
|
48
|
+
## Required Inputs
|
|
49
|
+
|
|
50
|
+
- Product stage: prototype, personal tool, team-internal tool, public SaaS, developer tool, library, CLI, desktop app, or another explicit stage.
|
|
51
|
+
- Primary actors: end users, developers, operators, automation systems, support staff, admins, or third-party services.
|
|
52
|
+
- Main usage path and the job it completes.
|
|
53
|
+
- Integration need: scripts, CI, external services, webhooks, plugins, SDK calls, imports, exports, or manual-only use.
|
|
54
|
+
- Public-contract willingness: which routes the user is ready to document, test, keep compatible, deprecate carefully, and support operationally.
|
|
55
|
+
- Explicit non-goals: routes not supported now and routes kept internal-only.
|
|
56
|
+
- Failure, recovery, upgrade, data movement, observability, security, privacy, accessibility, and localization expectations.
|
|
57
|
+
- Current repository evidence, existing product docs, command contracts, APIs, or public examples when working in an existing codebase.
|
|
58
|
+
|
|
59
|
+
<!-- mustflow-section: preconditions -->
|
|
60
|
+
## Preconditions
|
|
61
|
+
|
|
62
|
+
- Treat every exposed support route as a maintenance contract, not as a free feature checkbox.
|
|
63
|
+
- Do not ask for or recommend all support surfaces by default.
|
|
64
|
+
- Distinguish what is supported now, deferred, explicitly not supported, and internal-only.
|
|
65
|
+
- Keep command execution governed by `.mustflow/config/commands.toml`; this skill names intent concepts but does not authorize raw commands.
|
|
66
|
+
|
|
67
|
+
<!-- mustflow-section: allowed-edits -->
|
|
68
|
+
## Allowed Edits
|
|
69
|
+
|
|
70
|
+
- Ask focused scoping questions and propose a support-surface plan.
|
|
71
|
+
- Update product docs, implementation notes, tests, route metadata, or code boundaries directly tied to the selected support surfaces.
|
|
72
|
+
- Add only the selected surfaces and their tests/docs/contracts when the user asks for implementation.
|
|
73
|
+
- Do not create duplicate business logic in UI, CLI, API, SDK, webhook, or plugin shells.
|
|
74
|
+
- Do not expose internal functions, schemas, logs, error messages, settings, or file formats as public contracts by accident.
|
|
75
|
+
- Do not put internal implementation explanations in user-facing UI copy.
|
|
76
|
+
|
|
77
|
+
<!-- mustflow-section: procedure -->
|
|
78
|
+
## Procedure
|
|
79
|
+
|
|
80
|
+
1. Classify the product stage and primary actors first. Do not recommend surfaces until you know who must use, integrate, operate, or recover the product.
|
|
81
|
+
2. Identify the primary path. Pick the one route users should use first, such as UI for non-technical users, CLI for developer automation, SDK for library consumers, or network API for cross-service integration.
|
|
82
|
+
3. Define the core engine boundary before adding shells.
|
|
83
|
+
- The core engine owns domain workflow, validation, state transitions, permissions, errors, data movement, and side-effect policy.
|
|
84
|
+
- UI, CLI, API, SDK, webhook, and plugin shells should translate inputs and outputs, call the core, and present results.
|
|
85
|
+
- UI, CLI, and API shells must not reimplement the same core rules separately.
|
|
86
|
+
4. Ask only blocking questions. Prefer at most three at a time, with a recommended default when evidence supports it:
|
|
87
|
+
- Who will use this first: general users, developers, operators, admins, automation, or other services?
|
|
88
|
+
- What is the main route for the core job right now: UI, CLI, programming API/SDK, network API, or another route?
|
|
89
|
+
- Do scripts, CI, webhooks, imports, exports, or other services need integration in the next milestone?
|
|
90
|
+
- Which routes are you willing to keep stable, document, test, and migrate later?
|
|
91
|
+
- Which routes should be explicitly unsupported for now?
|
|
92
|
+
- What failure, recovery, upgrade, data movement, logging, privacy, and accessibility level is needed for the first real users?
|
|
93
|
+
5. Use the support surface list as a risk prompt, not a checklist to fill:
|
|
94
|
+
- UI, CLI, programming API/SDK, network API, webhook/events, plugin/extension, configuration, auth/permissions, import/export, version/backcompat, deprecation/migration, docs/examples, errors/diagnostics, logs/observability, test support, failure recovery, performance/resource limits, security/privacy, accessibility/i18n, deployment/upgrade/recovery.
|
|
95
|
+
6. Classify each relevant surface:
|
|
96
|
+
- `support_now`: a named actor needs it in the next milestone, the team can maintain it, the contract can be tested, and failure behavior is understood.
|
|
97
|
+
- `defer`: plausible later value exists, but there is no near-term user, integration, or operational capacity.
|
|
98
|
+
- `explicitly_not_supported`: exposing it would imply stable API, schema, data, security, operational, or compatibility promises the product is not ready to carry.
|
|
99
|
+
- `internal_only`: useful behind the scenes but not documented, not stable, and not safe for external reliance.
|
|
100
|
+
7. Apply stage heuristics:
|
|
101
|
+
- Prototype or personal tool: one primary path, basic configuration, clear errors, a simple export or backup when data matters, and no SDK/webhooks/plugins unless they are the product.
|
|
102
|
+
- Team-internal tool: UI or CLI, role or environment boundaries, logs, import/export when data moves, upgrade notes, and enough docs for handoff.
|
|
103
|
+
- Public SaaS: UI, auth/permissions, security/privacy, diagnostics, recovery, observability, deployment/upgrade behavior, and docs; API/webhooks only when customers truly integrate.
|
|
104
|
+
- Developer tool or CLI: non-interactive execution, help text, exit codes, machine-readable output when scripts need it, configuration precedence, errors, docs, dry-run or test fixtures when side effects matter.
|
|
105
|
+
- Library or SDK: a narrow public API, typed errors, cancellation/resource cleanup where relevant, version/backcompat rules, examples, tests, and deprecation policy; no UI or network API unless that is the product.
|
|
106
|
+
- Desktop app: UI, settings, import/export or backup, upgrade/rollback expectations, crash diagnostics, accessibility/i18n baseline, and CLI/API only when automation is a real user need.
|
|
107
|
+
8. Recommend a surface only when it has a real actor, near-term workflow, maintenance owner, compatibility story, and verification path.
|
|
108
|
+
9. Explicitly defer or reject surfaces that duplicate the primary path, create a second core implementation, require unsupported operations capacity, or would lock internal data formats too early.
|
|
109
|
+
10. For user-facing UI text, write natural product language. Do not leak implementation notes such as "this component handles..." or "this feature processes..." into visible copy.
|
|
110
|
+
11. If implementation follows, activate the narrower skill for each selected surface before changing that surface, such as API contract, CLI output, UI quality, security/privacy, data migration, docs, or tests.
|
|
111
|
+
|
|
112
|
+
<!-- mustflow-section: postconditions -->
|
|
113
|
+
## Postconditions
|
|
114
|
+
|
|
115
|
+
- The recommended support surfaces are tied to actors, workflows, and maintenance capacity.
|
|
116
|
+
- Deferred and explicitly unsupported surfaces are named instead of left as vague future work.
|
|
117
|
+
- The core engine versus shell boundary prevents duplicated business logic across UI, CLI, API, SDK, webhook, and plugin routes.
|
|
118
|
+
- Compatibility, docs, tests, failure, recovery, security, privacy, observability, accessibility, and upgrade risks are visible for every selected public route.
|
|
119
|
+
|
|
120
|
+
<!-- mustflow-section: verification -->
|
|
121
|
+
## Verification
|
|
122
|
+
|
|
123
|
+
Use configured oneshot command intents when available:
|
|
124
|
+
|
|
125
|
+
- `changes_status`
|
|
126
|
+
- `changes_diff_summary`
|
|
127
|
+
- `docs_validate_fast`
|
|
128
|
+
- `test_release`
|
|
129
|
+
- `mustflow_check`
|
|
130
|
+
|
|
131
|
+
When implementation changes code, schemas, templates, package metadata, or tests, also use the narrower configured intent named by the matching implementation skill.
|
|
132
|
+
|
|
133
|
+
<!-- mustflow-section: failure-handling -->
|
|
134
|
+
## Failure Handling
|
|
135
|
+
|
|
136
|
+
- If the user cannot name users or a primary path, stop at questions and recommend the smallest reversible default.
|
|
137
|
+
- If a desired route implies an unsupported public contract, mark it `defer` or `explicitly_not_supported` and explain the contract cost.
|
|
138
|
+
- If UI, CLI, API, SDK, webhook, or plugin paths start duplicating core rules, move the rule decision to the core engine boundary before continuing.
|
|
139
|
+
- If implementation requires a support surface whose verification intent is missing, report the missing verification instead of claiming readiness.
|
|
140
|
+
- If the scope expands into every support route, reduce it to one primary route plus the minimum safety surfaces needed for the next real users.
|
|
141
|
+
|
|
142
|
+
<!-- mustflow-section: output-format -->
|
|
143
|
+
## Output Format
|
|
144
|
+
|
|
145
|
+
- Product stage and primary actors
|
|
146
|
+
- Recommended support surfaces
|
|
147
|
+
- Deferred support surfaces
|
|
148
|
+
- Explicitly unsupported or internal-only surfaces
|
|
149
|
+
- Immediate questions and recommended defaults
|
|
150
|
+
- Maintenance contract and compatibility risks
|
|
151
|
+
- Core engine versus shell boundary
|
|
152
|
+
- Staged adoption plan
|
|
153
|
+
- Follow-up skills needed for implementation
|
|
154
|
+
- Command intents run
|
|
155
|
+
- Skipped checks and reasons
|
|
156
|
+
- Remaining support-surface risk
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
id = "default"
|
|
2
2
|
name = "default"
|
|
3
|
-
version = "2.74.
|
|
3
|
+
version = "2.74.2"
|
|
4
4
|
description = "Minimal workflow for LLM agents to read, edit, and verify their work in a repository."
|
|
5
5
|
common_root = "common"
|
|
6
6
|
locales_root = "locales"
|
|
@@ -93,6 +93,7 @@ creates = [
|
|
|
93
93
|
".mustflow/skills/rust-code-change/SKILL.md",
|
|
94
94
|
".mustflow/skills/runtime-target-selection/SKILL.md",
|
|
95
95
|
".mustflow/skills/structure-first-engineering/SKILL.md",
|
|
96
|
+
".mustflow/skills/support-surface-advisor/SKILL.md",
|
|
96
97
|
".mustflow/skills/svelte-code-change/SKILL.md",
|
|
97
98
|
".mustflow/skills/tailwind-code-change/SKILL.md",
|
|
98
99
|
".mustflow/skills/tauri-code-change/SKILL.md",
|
|
@@ -331,6 +332,7 @@ minimal = [
|
|
|
331
332
|
"task-instruction-authoring",
|
|
332
333
|
"structure-discovery-gate",
|
|
333
334
|
"structure-first-engineering",
|
|
335
|
+
"support-surface-advisor",
|
|
334
336
|
"test-design-guard",
|
|
335
337
|
"test-maintenance",
|
|
336
338
|
"vertical-slice-tdd",
|
|
@@ -472,6 +474,7 @@ patterns = [
|
|
|
472
474
|
"strategy-pattern",
|
|
473
475
|
"structure-discovery-gate",
|
|
474
476
|
"structure-first-engineering",
|
|
477
|
+
"support-surface-advisor",
|
|
475
478
|
"test-design-guard",
|
|
476
479
|
"test-maintenance",
|
|
477
480
|
"vertical-slice-tdd",
|
|
@@ -631,6 +634,7 @@ oss = [
|
|
|
631
634
|
"strategy-pattern",
|
|
632
635
|
"structure-discovery-gate",
|
|
633
636
|
"structure-first-engineering",
|
|
637
|
+
"support-surface-advisor",
|
|
634
638
|
"test-design-guard",
|
|
635
639
|
"test-maintenance",
|
|
636
640
|
"vertical-slice-tdd",
|
|
@@ -775,6 +779,7 @@ team = [
|
|
|
775
779
|
"strategy-pattern",
|
|
776
780
|
"structure-discovery-gate",
|
|
777
781
|
"structure-first-engineering",
|
|
782
|
+
"support-surface-advisor",
|
|
778
783
|
"test-design-guard",
|
|
779
784
|
"test-maintenance",
|
|
780
785
|
"vertical-slice-tdd",
|
|
@@ -919,6 +924,7 @@ product = [
|
|
|
919
924
|
"strategy-pattern",
|
|
920
925
|
"structure-discovery-gate",
|
|
921
926
|
"structure-first-engineering",
|
|
927
|
+
"support-surface-advisor",
|
|
922
928
|
"test-design-guard",
|
|
923
929
|
"test-maintenance",
|
|
924
930
|
"vertical-slice-tdd",
|
|
@@ -1078,6 +1084,7 @@ library = [
|
|
|
1078
1084
|
"strategy-pattern",
|
|
1079
1085
|
"structure-discovery-gate",
|
|
1080
1086
|
"structure-first-engineering",
|
|
1087
|
+
"support-surface-advisor",
|
|
1081
1088
|
"test-design-guard",
|
|
1082
1089
|
"test-maintenance",
|
|
1083
1090
|
"vertical-slice-tdd",
|