mustflow 2.103.20 → 2.103.22
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 +2 -2
- package/dist/cli/commands/run/execution.js +1 -1
- package/dist/cli/commands/verify.js +4 -1
- package/dist/cli/lib/option-parser.js +26 -0
- package/dist/cli/script-packs/repo-env-contract.js +4 -17
- package/dist/cli/script-packs/repo-secret-risk-scan.js +4 -17
- package/dist/cli/script-packs/repo-security-pattern-scan.js +4 -17
- package/dist/core/retention-policy.js +4 -2
- package/dist/core/run-receipt-state.js +247 -0
- package/dist/core/run-receipt.js +5 -1
- package/package.json +1 -1
- package/templates/default/common/.mustflow/config/mustflow.toml +2 -2
- package/templates/default/i18n.toml +8 -2
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +7 -1
- package/templates/default/locales/en/.mustflow/skills/clarifying-question-gate/SKILL.md +30 -5
- package/templates/default/locales/en/.mustflow/skills/http-api-semantics-review/SKILL.md +286 -0
- package/templates/default/locales/en/.mustflow/skills/routes.toml +6 -0
- package/templates/default/manifest.toml +8 -1
package/README.md
CHANGED
|
@@ -142,7 +142,7 @@ mustflow installs and validates an agent workflow for user projects.
|
|
|
142
142
|
- Runs only allowed one-shot commands within a timeout via `mf run <intent>` or `mf verify` when the selected intent is runnable.
|
|
143
143
|
- Records blockers, contradictions, verification gaps, and remaining risks as a structured conflict ledger in verify, evidence, and dashboard reports.
|
|
144
144
|
- Stores bounded failure replay capsules for failed `mf verify` runs so future agents can reproduce the intent, receipt, command fingerprint, and changed-file state without copying raw command output.
|
|
145
|
-
- Writes command receipts under `.mustflow/state/runs/run
|
|
145
|
+
- Writes bounded command receipts under `.mustflow/state/runs/run-*`, atomically updates `.mustflow/state/runs/latest.json`, and rebuilds `.mustflow/state/runs/latest.index.json` for recent retained runs.
|
|
146
146
|
- Generates a concise repository navigation map, `REPO_MAP.md`, with `mf map`.
|
|
147
147
|
- Indexes and searches mustflow docs, skills, skill routes, command rules, command-effect locks, file fingerprints, and opt-in source anchor metadata with SQLite via `mf index` and `mf search`. The local SQLite file is a rebuildable lookup cache, not a memory store, audit log, command transcript store, command-authority source, or source-content database.
|
|
148
148
|
- Tracks agent-created or agent-modified documentation needing prose review with `mf docs review`.
|
|
@@ -373,7 +373,7 @@ Command environments remove the project-local `node_modules/.bin` path from `PAT
|
|
|
373
373
|
|
|
374
374
|
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.
|
|
375
375
|
|
|
376
|
-
Each executed command run writes a run record under `.mustflow/state/runs/run
|
|
376
|
+
Each executed command run writes a run record under `.mustflow/state/runs/run-*`, atomically updates `.mustflow/state/runs/latest.json`, and rebuilds `.mustflow/state/runs/latest.index.json` from retained `run-*` and `verify-*` directories. 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`, the retained index, or `mf run <intent> --json` output as the evidence for a specific run.
|
|
377
377
|
|
|
378
378
|
## Language and profiles
|
|
379
379
|
|
|
@@ -290,7 +290,7 @@ export async function executeRunCommand(request, reporter, lang = 'en', options
|
|
|
290
290
|
stderrTailBytes: runReceiptPolicy.stderrTailBytes,
|
|
291
291
|
}));
|
|
292
292
|
if (options.writeLatestReceipt !== false) {
|
|
293
|
-
profiler.measure('receipt_write', () => writeRunReceipt(projectRoot, receipt));
|
|
293
|
+
profiler.measure('receipt_write', () => writeRunReceipt(projectRoot, receipt, runReceiptPolicy));
|
|
294
294
|
}
|
|
295
295
|
if (options.recordPerformanceHistory !== false) {
|
|
296
296
|
profiler.measure('performance_history_write', () => recordRunPerformanceHistory(projectRoot, receipt));
|
|
@@ -15,7 +15,9 @@ import { riskPricedEvidenceRiskCount, } from '../../core/risk-priced-evidence.js
|
|
|
15
15
|
import { countValidationRatchetVerdictEffects, createValidationRatchetRisks, } from '../../core/validation-ratchet.js';
|
|
16
16
|
import { finishRunWriteBatchTracking, startRunWriteBatchTracking, } from '../../core/run-write-drift.js';
|
|
17
17
|
import { createCommandEnv } from '../../core/command-env.js';
|
|
18
|
-
import { readCommandContract } from '../../core/config-loading.js';
|
|
18
|
+
import { readCommandContract, readMustflowConfigIfExists } from '../../core/config-loading.js';
|
|
19
|
+
import { resolveRunReceiptRetentionPolicy } from '../../core/retention-policy.js';
|
|
20
|
+
import { updateRunReceiptState } from '../../core/run-receipt-state.js';
|
|
19
21
|
import { evaluateCommandPreconditions, } from '../../core/command-preconditions.js';
|
|
20
22
|
import { DEFAULT_VERIFY_PARALLELISM, parseVerifyArgs, resolveVerifyParallelism, } from './verify/args.js';
|
|
21
23
|
import { createInputFromChanged, createSyntheticClassificationReport, planErrorMessageKey, readInputFromClassificationReport, resolveVerifyInputPath, writeChangedPlan, } from './verify/input.js';
|
|
@@ -764,6 +766,7 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
|
|
|
764
766
|
manifest_path: statePaths.manifestPath,
|
|
765
767
|
};
|
|
766
768
|
writeJsonFileInsideWithoutSymlinks(projectRoot, resolveLatestVerifyRunReceiptPath(projectRoot), latest);
|
|
769
|
+
updateRunReceiptState(projectRoot, resolveRunReceiptRetentionPolicy(readMustflowConfigIfExists(projectRoot)));
|
|
767
770
|
return outputWithReceiptPaths;
|
|
768
771
|
}
|
|
769
772
|
async function createVerifyOutput(input, planSource, projectRoot, lang, reproEvidence = null, externalChecks = [], parallelism = DEFAULT_VERIFY_PARALLELISM, parallelismReport = null) {
|
|
@@ -85,6 +85,32 @@ export function getParsedCliStringOption(parsed, name) {
|
|
|
85
85
|
const value = parsed.values.get(name);
|
|
86
86
|
return typeof value === 'string' ? value : null;
|
|
87
87
|
}
|
|
88
|
+
export function parsePositiveIntegerCliOption(value, option, invalidMessageKey, lang) {
|
|
89
|
+
if (value === null) {
|
|
90
|
+
return { value: null };
|
|
91
|
+
}
|
|
92
|
+
if (!/^[1-9]\d*$/u.test(value)) {
|
|
93
|
+
return { value: null, error: t(lang, invalidMessageKey, { option, value }) };
|
|
94
|
+
}
|
|
95
|
+
const parsed = Number(value);
|
|
96
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
97
|
+
return { value: null, error: t(lang, invalidMessageKey, { option, value }) };
|
|
98
|
+
}
|
|
99
|
+
return { value: parsed };
|
|
100
|
+
}
|
|
101
|
+
export function parseNonNegativeIntegerCliOption(value, option, invalidMessageKey, lang) {
|
|
102
|
+
if (value === null) {
|
|
103
|
+
return { value: null };
|
|
104
|
+
}
|
|
105
|
+
if (!/^(?:0|[1-9]\d*)$/u.test(value)) {
|
|
106
|
+
return { value: null, error: t(lang, invalidMessageKey, { option, value }) };
|
|
107
|
+
}
|
|
108
|
+
const parsed = Number(value);
|
|
109
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
110
|
+
return { value: null, error: t(lang, invalidMessageKey, { option, value }) };
|
|
111
|
+
}
|
|
112
|
+
return { value: parsed };
|
|
113
|
+
}
|
|
88
114
|
export function formatCliOptionParseError(error, lang) {
|
|
89
115
|
if (error.kind === 'missing_value') {
|
|
90
116
|
return t(lang, 'cli.error.missingValue', { option: error.option });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
2
2
|
import { t } from '../lib/i18n.js';
|
|
3
|
-
import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
3
|
+
import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parsePositiveIntegerCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
4
4
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
5
5
|
import { ENV_CONTRACT_SCRIPT_REF, inspectEnvContract } from '../../core/env-contract.js';
|
|
6
6
|
const ENV_CONTRACT_OPTIONS = [
|
|
@@ -9,19 +9,6 @@ const ENV_CONTRACT_OPTIONS = [
|
|
|
9
9
|
{ name: '--max-file-bytes', kind: 'string' },
|
|
10
10
|
{ name: '--max-keys', kind: 'string' },
|
|
11
11
|
];
|
|
12
|
-
function parsePositiveInteger(value, option, lang) {
|
|
13
|
-
if (value === null) {
|
|
14
|
-
return { value: null };
|
|
15
|
-
}
|
|
16
|
-
if (!/^[1-9]\d*$/u.test(value)) {
|
|
17
|
-
return { value: null, error: t(lang, 'envContract.error.invalidPositiveInteger', { option, value }) };
|
|
18
|
-
}
|
|
19
|
-
const parsed = Number(value);
|
|
20
|
-
if (!Number.isSafeInteger(parsed)) {
|
|
21
|
-
return { value: null, error: t(lang, 'envContract.error.invalidPositiveInteger', { option, value }) };
|
|
22
|
-
}
|
|
23
|
-
return { value: parsed };
|
|
24
|
-
}
|
|
25
12
|
export function getRepoEnvContractHelp(lang = 'en') {
|
|
26
13
|
return renderHelp({
|
|
27
14
|
usage: 'mf script-pack run repo/env-contract scan [path...] [options]',
|
|
@@ -48,9 +35,9 @@ function parseEnvContractOptions(args, lang) {
|
|
|
48
35
|
const [action, ...rest] = args;
|
|
49
36
|
const parsed = parseCliOptions(rest, ENV_CONTRACT_OPTIONS, { allowPositionals: true });
|
|
50
37
|
const json = hasParsedCliOption(parsed, '--json');
|
|
51
|
-
const maxFiles =
|
|
52
|
-
const maxFileBytes =
|
|
53
|
-
const maxKeys =
|
|
38
|
+
const maxFiles = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-files'), '--max-files', 'envContract.error.invalidPositiveInteger', lang);
|
|
39
|
+
const maxFileBytes = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-file-bytes'), '--max-file-bytes', 'envContract.error.invalidPositiveInteger', lang);
|
|
40
|
+
const maxKeys = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-keys'), '--max-keys', 'envContract.error.invalidPositiveInteger', lang);
|
|
54
41
|
if (action !== 'scan') {
|
|
55
42
|
return {
|
|
56
43
|
action: 'scan',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
2
2
|
import { t } from '../lib/i18n.js';
|
|
3
|
-
import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
3
|
+
import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parsePositiveIntegerCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
4
4
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
5
5
|
import { inspectSecretRiskScan, SECRET_RISK_SCAN_SCRIPT_REF } from '../../core/secret-risk-scan.js';
|
|
6
6
|
const SECRET_RISK_SCAN_OPTIONS = [
|
|
@@ -9,19 +9,6 @@ const SECRET_RISK_SCAN_OPTIONS = [
|
|
|
9
9
|
{ name: '--max-file-bytes', kind: 'string' },
|
|
10
10
|
{ name: '--max-findings', kind: 'string' },
|
|
11
11
|
];
|
|
12
|
-
function parsePositiveInteger(value, option, lang) {
|
|
13
|
-
if (value === null) {
|
|
14
|
-
return { value: null };
|
|
15
|
-
}
|
|
16
|
-
if (!/^[1-9]\d*$/u.test(value)) {
|
|
17
|
-
return { value: null, error: t(lang, 'secretRiskScan.error.invalidPositiveInteger', { option, value }) };
|
|
18
|
-
}
|
|
19
|
-
const parsed = Number(value);
|
|
20
|
-
if (!Number.isSafeInteger(parsed)) {
|
|
21
|
-
return { value: null, error: t(lang, 'secretRiskScan.error.invalidPositiveInteger', { option, value }) };
|
|
22
|
-
}
|
|
23
|
-
return { value: parsed };
|
|
24
|
-
}
|
|
25
12
|
export function getRepoSecretRiskScanHelp(lang = 'en') {
|
|
26
13
|
return renderHelp({
|
|
27
14
|
usage: 'mf script-pack run repo/secret-risk-scan scan [path...] [options]',
|
|
@@ -48,9 +35,9 @@ function parseSecretRiskScanOptions(args, lang) {
|
|
|
48
35
|
const [action, ...rest] = args;
|
|
49
36
|
const parsed = parseCliOptions(rest, SECRET_RISK_SCAN_OPTIONS, { allowPositionals: true });
|
|
50
37
|
const json = hasParsedCliOption(parsed, '--json');
|
|
51
|
-
const maxFiles =
|
|
52
|
-
const maxFileBytes =
|
|
53
|
-
const maxFindings =
|
|
38
|
+
const maxFiles = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-files'), '--max-files', 'secretRiskScan.error.invalidPositiveInteger', lang);
|
|
39
|
+
const maxFileBytes = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-file-bytes'), '--max-file-bytes', 'secretRiskScan.error.invalidPositiveInteger', lang);
|
|
40
|
+
const maxFindings = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-findings'), '--max-findings', 'secretRiskScan.error.invalidPositiveInteger', lang);
|
|
54
41
|
if (action !== 'scan') {
|
|
55
42
|
return {
|
|
56
43
|
action: 'scan',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
2
2
|
import { t } from '../lib/i18n.js';
|
|
3
|
-
import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
3
|
+
import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parsePositiveIntegerCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
4
4
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
5
5
|
import { inspectSecurityPatternScan, SECURITY_PATTERN_SCAN_SCRIPT_REF, } from '../../core/security-pattern-scan.js';
|
|
6
6
|
const SECURITY_PATTERN_SCAN_OPTIONS = [
|
|
@@ -9,19 +9,6 @@ const SECURITY_PATTERN_SCAN_OPTIONS = [
|
|
|
9
9
|
{ name: '--max-file-bytes', kind: 'string' },
|
|
10
10
|
{ name: '--max-findings', kind: 'string' },
|
|
11
11
|
];
|
|
12
|
-
function parsePositiveInteger(value, option, lang) {
|
|
13
|
-
if (value === null) {
|
|
14
|
-
return { value: null };
|
|
15
|
-
}
|
|
16
|
-
if (!/^[1-9]\d*$/u.test(value)) {
|
|
17
|
-
return { value: null, error: t(lang, 'securityPatternScan.error.invalidPositiveInteger', { option, value }) };
|
|
18
|
-
}
|
|
19
|
-
const parsed = Number(value);
|
|
20
|
-
if (!Number.isSafeInteger(parsed)) {
|
|
21
|
-
return { value: null, error: t(lang, 'securityPatternScan.error.invalidPositiveInteger', { option, value }) };
|
|
22
|
-
}
|
|
23
|
-
return { value: parsed };
|
|
24
|
-
}
|
|
25
12
|
export function getRepoSecurityPatternScanHelp(lang = 'en') {
|
|
26
13
|
return renderHelp({
|
|
27
14
|
usage: 'mf script-pack run repo/security-pattern-scan scan [path...] [options]',
|
|
@@ -48,9 +35,9 @@ function parseSecurityPatternScanOptions(args, lang) {
|
|
|
48
35
|
const [action, ...rest] = args;
|
|
49
36
|
const parsed = parseCliOptions(rest, SECURITY_PATTERN_SCAN_OPTIONS, { allowPositionals: true });
|
|
50
37
|
const json = hasParsedCliOption(parsed, '--json');
|
|
51
|
-
const maxFiles =
|
|
52
|
-
const maxFileBytes =
|
|
53
|
-
const maxFindings =
|
|
38
|
+
const maxFiles = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-files'), '--max-files', 'securityPatternScan.error.invalidPositiveInteger', lang);
|
|
39
|
+
const maxFileBytes = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-file-bytes'), '--max-file-bytes', 'securityPatternScan.error.invalidPositiveInteger', lang);
|
|
40
|
+
const maxFindings = parsePositiveIntegerCliOption(getParsedCliStringOption(parsed, '--max-findings'), '--max-findings', 'securityPatternScan.error.invalidPositiveInteger', lang);
|
|
54
41
|
if (action !== 'scan') {
|
|
55
42
|
return {
|
|
56
43
|
action: 'scan',
|
|
@@ -5,6 +5,8 @@ export const DEFAULT_RETENTION_LIMITS = {
|
|
|
5
5
|
repoMapMaxFileKb: 128,
|
|
6
6
|
repoMapFailIfLarger: true,
|
|
7
7
|
runReceiptMaxFileKb: 128,
|
|
8
|
+
runReceiptMaxItems: 50,
|
|
9
|
+
runReceiptMaxTotalMb: 10,
|
|
8
10
|
contextMaxFileKb: 8,
|
|
9
11
|
knowledgeMaxFileKb: 128,
|
|
10
12
|
};
|
|
@@ -49,8 +51,8 @@ export function resolveRunReceiptRetentionPolicy(mustflowToml) {
|
|
|
49
51
|
return {
|
|
50
52
|
store: readString(runReceipts ?? {}, 'store') ?? 'repo_local_ignored',
|
|
51
53
|
maxFileKb: readPositiveIntegerWithDefault(runReceipts, 'max_file_kb', DEFAULT_RETENTION_LIMITS.runReceiptMaxFileKb),
|
|
52
|
-
maxItems: readPositiveIntegerWithDefault(runReceipts, 'max_items',
|
|
53
|
-
maxTotalMb: readPositiveIntegerWithDefault(runReceipts, 'max_total_mb',
|
|
54
|
+
maxItems: readPositiveIntegerWithDefault(runReceipts, 'max_items', DEFAULT_RETENTION_LIMITS.runReceiptMaxItems),
|
|
55
|
+
maxTotalMb: readPositiveIntegerWithDefault(runReceipts, 'max_total_mb', DEFAULT_RETENTION_LIMITS.runReceiptMaxTotalMb),
|
|
54
56
|
stdoutTailBytes: readPositiveIntegerWithDefault(runReceipts, 'keep_stdout_tail_bytes', DEFAULT_RUN_RECEIPT_TAIL_BYTES),
|
|
55
57
|
stderrTailBytes: readPositiveIntegerWithDefault(runReceipts, 'keep_stderr_tail_bytes', DEFAULT_RUN_RECEIPT_TAIL_BYTES),
|
|
56
58
|
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { existsSync, lstatSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ensureInside, writeJsonFileInsideWithoutSymlinks } from './safe-filesystem.js';
|
|
4
|
+
const RUN_RECEIPT_SCHEMA_VERSION = '1';
|
|
5
|
+
const RUN_RECEIPT_DIR = path.join('.mustflow', 'state', 'runs');
|
|
6
|
+
const LATEST_RUN_RECEIPT_INDEX = 'latest.index.json';
|
|
7
|
+
const STATE_DIR_PREFIXES = ['run-', 'verify-'];
|
|
8
|
+
const MIN_RETAINED_RUN_DIRS = 1;
|
|
9
|
+
function toPosixPath(value) {
|
|
10
|
+
return value.split(path.sep).join('/');
|
|
11
|
+
}
|
|
12
|
+
function stateRunsDir(projectRoot) {
|
|
13
|
+
return path.join(projectRoot, RUN_RECEIPT_DIR);
|
|
14
|
+
}
|
|
15
|
+
function latestIndexPath(projectRoot) {
|
|
16
|
+
return path.join(stateRunsDir(projectRoot), LATEST_RUN_RECEIPT_INDEX);
|
|
17
|
+
}
|
|
18
|
+
function runStateKind(name) {
|
|
19
|
+
return STATE_DIR_PREFIXES.find((prefix) => name.startsWith(prefix)) ?? null;
|
|
20
|
+
}
|
|
21
|
+
function isMissingPathError(error) {
|
|
22
|
+
return error instanceof Error && 'code' in error && error.code === 'ENOENT';
|
|
23
|
+
}
|
|
24
|
+
function getStateRunSortKey(name) {
|
|
25
|
+
const match = /^(?:run|verify)-(.+)$/u.exec(name);
|
|
26
|
+
return match?.[1] ?? name;
|
|
27
|
+
}
|
|
28
|
+
function fileSizeBytes(filePath) {
|
|
29
|
+
try {
|
|
30
|
+
const stats = lstatSync(filePath);
|
|
31
|
+
if (stats.isSymbolicLink()) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
if (stats.isFile()) {
|
|
35
|
+
return stats.size;
|
|
36
|
+
}
|
|
37
|
+
if (!stats.isDirectory()) {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
return readdirSync(filePath).reduce((total, entry) => total + fileSizeBytes(path.join(filePath, entry)), 0);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (isMissingPathError(error)) {
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function readRunStateDirectories(projectRoot) {
|
|
50
|
+
const receiptDir = stateRunsDir(projectRoot);
|
|
51
|
+
if (!existsSync(receiptDir)) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
return readdirSync(receiptDir)
|
|
55
|
+
.map((name) => {
|
|
56
|
+
const kind = runStateKind(name);
|
|
57
|
+
if (!kind) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const absolutePath = path.join(receiptDir, name);
|
|
61
|
+
const relativePath = toPosixPath(path.join(RUN_RECEIPT_DIR, name));
|
|
62
|
+
const stats = lstatSync(absolutePath);
|
|
63
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
absolutePath,
|
|
68
|
+
relativePath,
|
|
69
|
+
name,
|
|
70
|
+
kind,
|
|
71
|
+
sortKey: getStateRunSortKey(name),
|
|
72
|
+
sizeBytes: fileSizeBytes(absolutePath),
|
|
73
|
+
};
|
|
74
|
+
})
|
|
75
|
+
.filter((entry) => Boolean(entry))
|
|
76
|
+
.sort(compareRunStateDirectoriesDescending);
|
|
77
|
+
}
|
|
78
|
+
function compareRunStateDirectoriesDescending(left, right) {
|
|
79
|
+
const bySortKey = right.sortKey.localeCompare(left.sortKey);
|
|
80
|
+
if (bySortKey !== 0) {
|
|
81
|
+
return bySortKey;
|
|
82
|
+
}
|
|
83
|
+
return right.name.localeCompare(left.name);
|
|
84
|
+
}
|
|
85
|
+
function removeRunStateDirectory(projectRoot, directory) {
|
|
86
|
+
const runsDir = stateRunsDir(projectRoot);
|
|
87
|
+
ensureInside(runsDir, directory.absolutePath);
|
|
88
|
+
const relativeToRunsDir = path.relative(runsDir, directory.absolutePath);
|
|
89
|
+
if (relativeToRunsDir.startsWith('..') || path.isAbsolute(relativeToRunsDir) || runStateKind(path.basename(directory.absolutePath)) === null) {
|
|
90
|
+
throw new Error(`Refusing to remove unexpected run receipt path: ${directory.relativePath}`);
|
|
91
|
+
}
|
|
92
|
+
const stats = lstatSync(directory.absolutePath);
|
|
93
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
94
|
+
throw new Error(`Refusing to remove non-directory run receipt path: ${directory.relativePath}`);
|
|
95
|
+
}
|
|
96
|
+
rmSync(directory.absolutePath, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
function applyRunReceiptRetention(projectRoot, policy) {
|
|
99
|
+
const directories = readRunStateDirectories(projectRoot);
|
|
100
|
+
const maxItems = Math.max(MIN_RETAINED_RUN_DIRS, policy.maxItems);
|
|
101
|
+
const maxTotalBytes = Math.max(1, policy.maxTotalMb) * 1024 * 1024;
|
|
102
|
+
const kept = new Set(directories.slice(0, maxItems).map((directory) => directory.name));
|
|
103
|
+
let totalBytes = directories
|
|
104
|
+
.filter((directory) => kept.has(directory.name))
|
|
105
|
+
.reduce((total, directory) => total + directory.sizeBytes, 0);
|
|
106
|
+
for (const directory of directories.slice(maxItems)) {
|
|
107
|
+
removeRunStateDirectory(projectRoot, directory);
|
|
108
|
+
}
|
|
109
|
+
for (const directory of [...directories].reverse()) {
|
|
110
|
+
if (kept.size <= MIN_RETAINED_RUN_DIRS || !kept.has(directory.name) || totalBytes <= maxTotalBytes) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
removeRunStateDirectory(projectRoot, directory);
|
|
114
|
+
kept.delete(directory.name);
|
|
115
|
+
totalBytes -= directory.sizeBytes;
|
|
116
|
+
}
|
|
117
|
+
return readRunStateDirectories(projectRoot);
|
|
118
|
+
}
|
|
119
|
+
function readJsonObject(filePath) {
|
|
120
|
+
try {
|
|
121
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
122
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function stringField(value) {
|
|
129
|
+
return typeof value === 'string' ? value : null;
|
|
130
|
+
}
|
|
131
|
+
function stringArrayField(value) {
|
|
132
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === 'string') ? value : undefined;
|
|
133
|
+
}
|
|
134
|
+
function createRunEntry(directory) {
|
|
135
|
+
const receipt = readJsonObject(path.join(directory.absolutePath, 'receipt.json'));
|
|
136
|
+
if (!receipt || receipt.command !== 'run' || typeof receipt.receipt_path !== 'string') {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
command: 'run',
|
|
141
|
+
intent: stringField(receipt.intent),
|
|
142
|
+
status: stringField(receipt.status),
|
|
143
|
+
cwd: stringField(receipt.cwd),
|
|
144
|
+
started_at: stringField(receipt.started_at),
|
|
145
|
+
finished_at: stringField(receipt.finished_at),
|
|
146
|
+
correlation_id: stringField(receipt.correlation_id),
|
|
147
|
+
receipt_path: receipt.receipt_path,
|
|
148
|
+
run_dir: directory.relativePath,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function readVerifyIntentEntry(directory, manifest, manifestPath, manifestReceipt) {
|
|
152
|
+
const receiptPath = stringField(manifestReceipt.receipt_path);
|
|
153
|
+
if (!receiptPath) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const runsDir = path.dirname(directory.absolutePath);
|
|
157
|
+
const receiptAbsolutePath = path.resolve(runsDir, ...receiptPath.split('/').slice(3));
|
|
158
|
+
ensureInside(runsDir, receiptAbsolutePath);
|
|
159
|
+
const receipt = readJsonObject(receiptAbsolutePath);
|
|
160
|
+
return {
|
|
161
|
+
command: 'verify',
|
|
162
|
+
intent: stringField(manifestReceipt.intent),
|
|
163
|
+
status: stringField(manifestReceipt.status),
|
|
164
|
+
cwd: stringField(receipt?.cwd),
|
|
165
|
+
started_at: stringField(receipt?.started_at),
|
|
166
|
+
finished_at: stringField(receipt?.finished_at),
|
|
167
|
+
correlation_id: stringField(manifest.correlation_id),
|
|
168
|
+
verification_plan_id: stringField(manifest.verification_plan_id),
|
|
169
|
+
reason: stringField(manifest.reason),
|
|
170
|
+
reasons: stringArrayField(manifest.reasons),
|
|
171
|
+
receipt_path: receiptPath,
|
|
172
|
+
manifest_path: manifestPath,
|
|
173
|
+
run_dir: directory.relativePath,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function createVerifyEntries(directory) {
|
|
177
|
+
const manifestPath = toPosixPath(path.join(directory.relativePath, 'manifest.json'));
|
|
178
|
+
const manifest = readJsonObject(path.join(directory.absolutePath, 'manifest.json'));
|
|
179
|
+
if (!manifest || manifest.command !== 'verify') {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
const summaryEntry = {
|
|
183
|
+
command: 'verify',
|
|
184
|
+
intent: null,
|
|
185
|
+
status: stringField(manifest.status ?? manifest.execution_status),
|
|
186
|
+
correlation_id: stringField(manifest.correlation_id),
|
|
187
|
+
verification_plan_id: stringField(manifest.verification_plan_id),
|
|
188
|
+
reason: stringField(manifest.reason),
|
|
189
|
+
reasons: stringArrayField(manifest.reasons),
|
|
190
|
+
manifest_path: manifestPath,
|
|
191
|
+
run_dir: directory.relativePath,
|
|
192
|
+
};
|
|
193
|
+
const receiptEntries = Array.isArray(manifest.receipts)
|
|
194
|
+
? manifest.receipts
|
|
195
|
+
.map((receipt) => receipt && typeof receipt === 'object'
|
|
196
|
+
? readVerifyIntentEntry(directory, manifest, manifestPath, receipt)
|
|
197
|
+
: null)
|
|
198
|
+
.filter((entry) => Boolean(entry))
|
|
199
|
+
: [];
|
|
200
|
+
return [summaryEntry, ...receiptEntries];
|
|
201
|
+
}
|
|
202
|
+
function createIndexEntries(directories) {
|
|
203
|
+
return directories.flatMap((directory) => {
|
|
204
|
+
if (directory.kind === 'run-') {
|
|
205
|
+
const entry = createRunEntry(directory);
|
|
206
|
+
return entry ? [entry] : [];
|
|
207
|
+
}
|
|
208
|
+
return createVerifyEntries(directory);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function entryTargetPath(entry) {
|
|
212
|
+
return entry.receipt_path ?? entry.manifest_path ?? null;
|
|
213
|
+
}
|
|
214
|
+
function latestLookupEntries(entries) {
|
|
215
|
+
const latestByIntent = {};
|
|
216
|
+
const latestByCwdIntent = {};
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
const targetPath = entryTargetPath(entry);
|
|
219
|
+
if (!entry.intent || !targetPath) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
latestByIntent[entry.intent] ??= targetPath;
|
|
223
|
+
if (entry.cwd) {
|
|
224
|
+
latestByCwdIntent[`${entry.cwd}::${entry.intent}`] ??= targetPath;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return { latestByIntent, latestByCwdIntent };
|
|
228
|
+
}
|
|
229
|
+
export function updateRunReceiptState(projectRoot, policy) {
|
|
230
|
+
const retainedDirectories = applyRunReceiptRetention(projectRoot, policy);
|
|
231
|
+
const entries = createIndexEntries(retainedDirectories);
|
|
232
|
+
const lookups = latestLookupEntries(entries);
|
|
233
|
+
const index = {
|
|
234
|
+
schema_version: RUN_RECEIPT_SCHEMA_VERSION,
|
|
235
|
+
kind: 'run_receipt_index',
|
|
236
|
+
generated_at: new Date().toISOString(),
|
|
237
|
+
retention: {
|
|
238
|
+
max_items: policy.maxItems,
|
|
239
|
+
max_total_mb: policy.maxTotalMb,
|
|
240
|
+
retained_run_dirs: retainedDirectories.length,
|
|
241
|
+
},
|
|
242
|
+
entries,
|
|
243
|
+
latest_by_intent: lookups.latestByIntent,
|
|
244
|
+
latest_by_cwd_intent: lookups.latestByCwdIntent,
|
|
245
|
+
};
|
|
246
|
+
writeJsonFileInsideWithoutSymlinks(projectRoot, latestIndexPath(projectRoot), index);
|
|
247
|
+
}
|
package/dist/core/run-receipt.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createStateRunId } from './atomic-state-write.js';
|
|
|
4
4
|
import { COMMAND_OUTPUT_LIMIT_SCOPE } from './command-output-limits.js';
|
|
5
5
|
import { decodeUtf8Tail } from './bounded-output.js';
|
|
6
6
|
import { DEFAULT_RUN_RECEIPT_TAIL_BYTES } from './retention-policy.js';
|
|
7
|
+
import { updateRunReceiptState } from './run-receipt-state.js';
|
|
7
8
|
import { writeJsonFileInsideWithoutSymlinks } from './safe-filesystem.js';
|
|
8
9
|
import { redactSecretLikeText } from './secret-redaction.js';
|
|
9
10
|
const RUN_RECEIPT_SCHEMA_VERSION = '1';
|
|
@@ -286,7 +287,7 @@ export function createRunReceipt(input) {
|
|
|
286
287
|
receipt_path: getReceiptRelativePath(input.receiptPath),
|
|
287
288
|
};
|
|
288
289
|
}
|
|
289
|
-
export function writeRunReceipt(projectRoot, receipt) {
|
|
290
|
+
export function writeRunReceipt(projectRoot, receipt, policy) {
|
|
290
291
|
const receiptDir = path.join(projectRoot, RUN_RECEIPT_DIR);
|
|
291
292
|
const latestPath = path.join(receiptDir, LATEST_RUN_RECEIPT);
|
|
292
293
|
const receiptPath = path.resolve(projectRoot, receipt.receipt_path);
|
|
@@ -296,4 +297,7 @@ export function writeRunReceipt(projectRoot, receipt) {
|
|
|
296
297
|
}
|
|
297
298
|
writeJsonFileInsideWithoutSymlinks(projectRoot, receiptPath, receipt);
|
|
298
299
|
writeJsonFileInsideWithoutSymlinks(projectRoot, latestPath, receipt);
|
|
300
|
+
if (policy) {
|
|
301
|
+
updateRunReceiptState(projectRoot, policy);
|
|
302
|
+
}
|
|
299
303
|
}
|
package/package.json
CHANGED
|
@@ -350,8 +350,8 @@ on_limit = "report"
|
|
|
350
350
|
[retention.run_receipts]
|
|
351
351
|
store = "repo_local_ignored"
|
|
352
352
|
max_file_kb = 128
|
|
353
|
-
max_items =
|
|
354
|
-
max_total_mb =
|
|
353
|
+
max_items = 50
|
|
354
|
+
max_total_mb = 10
|
|
355
355
|
keep_stdout_tail_bytes = 65536
|
|
356
356
|
keep_stderr_tail_bytes = 65536
|
|
357
357
|
|
|
@@ -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 = 189
|
|
66
66
|
translations = {}
|
|
67
67
|
|
|
68
68
|
[documents."skill.adapter-boundary"]
|
|
@@ -89,6 +89,12 @@ source_locale = "en"
|
|
|
89
89
|
revision = 3
|
|
90
90
|
translations = {}
|
|
91
91
|
|
|
92
|
+
[documents."skill.http-api-semantics-review"]
|
|
93
|
+
source = "locales/en/.mustflow/skills/http-api-semantics-review/SKILL.md"
|
|
94
|
+
source_locale = "en"
|
|
95
|
+
revision = 1
|
|
96
|
+
translations = {}
|
|
97
|
+
|
|
92
98
|
[documents."skill.backend-reliability-change"]
|
|
93
99
|
source = "locales/en/.mustflow/skills/backend-reliability-change/SKILL.md"
|
|
94
100
|
source_locale = "en"
|
|
@@ -559,7 +565,7 @@ translations = {}
|
|
|
559
565
|
[documents."skill.clarifying-question-gate"]
|
|
560
566
|
source = "locales/en/.mustflow/skills/clarifying-question-gate/SKILL.md"
|
|
561
567
|
source_locale = "en"
|
|
562
|
-
revision =
|
|
568
|
+
revision = 4
|
|
563
569
|
translations = {}
|
|
564
570
|
|
|
565
571
|
[documents."skill.heuristic-candidate-selection"]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
mustflow_doc: skills.index
|
|
3
3
|
locale: en
|
|
4
4
|
canonical: true
|
|
5
|
-
revision:
|
|
5
|
+
revision: 189
|
|
6
6
|
authority: router
|
|
7
7
|
lifecycle: mustflow-owned
|
|
8
8
|
---
|
|
@@ -106,6 +106,11 @@ refer to `AGENTS.md` and `.mustflow/config/commands.toml` to implement the most
|
|
|
106
106
|
shapes, response shapes, pagination, idempotency, async jobs, versioning, deprecation, rate
|
|
107
107
|
limits, retry rules, observability, or caller-facing docs need caller-ergonomics and misuse-risk
|
|
108
108
|
review rather than only schema compatibility.
|
|
109
|
+
- Use `http-api-semantics-review` as an adjunct when HTTP method choices, safe/idempotent/cacheable
|
|
110
|
+
claims, GET/HEAD bodies, OPTIONS or Allow discovery, HTTP QUERY, POST versus PUT URI ownership,
|
|
111
|
+
PUT replacement, PATCH document formats, DELETE behavior, conditional requests, status codes,
|
|
112
|
+
cache headers, CORS method discovery, retry behavior, or intermediary compatibility need HTTP
|
|
113
|
+
semantics review rather than only schema compatibility.
|
|
109
114
|
- Use `api-access-control-review` as an adjunct when API security review needs BOLA or IDOR,
|
|
110
115
|
object, property, or function authorization, tenant isolation, request-supplied identity,
|
|
111
116
|
mass assignment, signed URLs, queue revalidation, webhook ownership, token/session hardening,
|
|
@@ -471,6 +476,7 @@ routes. Event routes stay inactive until their event occurs.
|
|
|
471
476
|
| 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 |
|
|
472
477
|
| 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 |
|
|
473
478
|
| 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 |
|
|
479
|
+
| HTTP API method semantics, safe/idempotent/cacheable claims, GET or HEAD request-body assumptions, OPTIONS capability discovery, HTTP QUERY, POST versus PUT URI ownership, PUT replacement, PATCH document formats, DELETE behavior, conditional requests, status-code recovery, cache headers, CORS method discovery, retry behavior, or intermediary compatibility are created, changed, reviewed, or reported | `.mustflow/skills/http-api-semantics-review/SKILL.md` | Endpoint method ledger, method contract, concurrency contract, PATCH media type and atomicity, GET/QUERY/POST search contract, cache and capability headers, caller and intermediary support evidence, and configured command intents | Route handlers, method choices, validators, OpenAPI or docs, SDK examples, gateway rules, CORS rules, cache headers, conditional request handling, status-code maps, focused tests, and directly synchronized templates | hidden GET mutation, non-portable GET or HEAD body, PUT partial merge, ambiguous PATCH JSON, PATCH partial success, DELETE body dependency, duplicate DELETE side effect, POST search semantic blur, QUERY without media type or body-aware cache key, stale-client field loss, missing If-Match, confused Allow versus CORS methods, `no-cache` treated as `no-store`, collapsed status-code recovery, or proxy/CDN/client incompatibility | `changes_status`, `changes_diff_summary`, `lint`, `build`, `test_related`, `test`, `docs_validate_fast`, `test_release`, `mustflow_check` | HTTP API semantics reviewed, endpoint method ledger, safe/idempotent/cache/header/status/conditional request findings, fixes or recommendations, synchronized API surfaces, verification, and remaining HTTP semantics risk |
|
|
474
480
|
| 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 |
|
|
475
481
|
| Backend APIs, workers, jobs, queues, caches, database write paths, external service calls, health checks, observability, feature flags, idempotency, retries, outbox/inbox processing, or operational failure handling are created, changed, reviewed, or reported | `.mustflow/skills/backend-reliability-change/SKILL.md` | Backend surface, trigger shape, idempotency boundary, external-call deadline and retry policy, persistence and transaction boundary, queue/cache behavior, observability fields, rollout gate, and command contract entries | Handlers, services, workers, retry policy, timeout policy, idempotency storage, outbox/inbox code, cache boundaries, health endpoints, observability fields, flags, tests, docs, and directly synchronized templates | duplicate side effects, retry storm, unbounded wait, DB uniqueness race, cache stampede, stale cache authority, poison message loop, missing outbox/inbox, raw ORM response, object-level authorization bypass, high-cardinality telemetry, secret or personal-data log leak, broken liveness/readiness, or missing kill switch | `changes_status`, `changes_diff_summary`, `lint`, `build`, `test_related`, `test`, `docs_validate_fast`, `test_release`, `mustflow_check` | Backend surface, idempotency and retry/timeout decisions, queue/cache/database notes, health-probe split, observability and auth/DTO notes, rollout gate, verification, and remaining backend reliability risk |
|
|
476
482
|
| HTTP delivery, content coding, compression negotiation, CDN or proxy cache behavior, streaming responses, SSE, EventSource, WebTransport, WebSocket fallback, HTTP/2 or HTTP/3 transport behavior, browser transport clients, reverse-proxy buffering, reconnect behavior, or delivery observability is created, changed, reviewed, or reported | `.mustflow/skills/http-delivery-streaming/SKILL.md` | Delivery surface, routes or assets, headers, cache and proxy/CDN path, browser/API clients, fallback behavior, streaming lifecycle, compression or dictionary choice, and observability fields | Route handlers, response headers, CDN/proxy config, browser transport code, streaming adapters, fallback clients, docs, tests, and directly synchronized templates | wrong content decoding, cache poisoning, private data cached publicly, proxy buffering, lost events, reconnect gaps, unsupported transport, unreliable datagram misuse, false compression win, or fallback failure | `changes_status`, `changes_diff_summary`, `lint`, `build`, `test_related`, `docs_validate_fast`, `test_release`, `mustflow_check` | Delivery ledger, negotiated encodings, cache/proxy behavior, stream/reconnect/fallback behavior, verification, and remaining delivery risk |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
mustflow_doc: skill.clarifying-question-gate
|
|
3
3
|
locale: en
|
|
4
4
|
canonical: true
|
|
5
|
-
revision:
|
|
5
|
+
revision: 4
|
|
6
6
|
lifecycle: mustflow-owned
|
|
7
7
|
authority: procedure
|
|
8
8
|
name: clarifying-question-gate
|
|
@@ -96,6 +96,8 @@ verification, and stop-or-ask boundary.
|
|
|
96
96
|
- Reversibility classification for each decision: cheap/reversible, moderate, or expensive/hard to
|
|
97
97
|
roll back.
|
|
98
98
|
- A recommended option for each blocking question, with the tradeoff of at least one alternative.
|
|
99
|
+
- Host input capability when known: whether a structured user-input mechanism such as Codex
|
|
100
|
+
`request_user_input` or MCP elicitation is explicitly available in the current runtime.
|
|
99
101
|
- A request-state decision: `ready`, `ready_with_assumptions`, `needs_confirmation`,
|
|
100
102
|
`blocked_by_conflict`, or `insufficient_evidence`.
|
|
101
103
|
- A normalized task contract when the original request is vague enough to risk drift: goal, current
|
|
@@ -123,6 +125,8 @@ verification, and stop-or-ask boundary.
|
|
|
123
125
|
collection, or broad product discovery.
|
|
124
126
|
- Product decisions are separated from engineering responsibilities. Do not ask whether to preserve
|
|
125
127
|
existing style, avoid swallowed errors, add appropriate tests, or follow command contracts.
|
|
128
|
+
- Structured input tools are optional host capabilities. Do not claim they exist, simulate their UI,
|
|
129
|
+
or depend on them unless the current host explicitly exposes them for this turn or tool call.
|
|
126
130
|
|
|
127
131
|
<!-- mustflow-section: allowed-edits -->
|
|
128
132
|
## Allowed Edits
|
|
@@ -215,22 +219,40 @@ verification, and stop-or-ask boundary.
|
|
|
215
219
|
and one meaningful alternative;
|
|
216
220
|
- avoid open-ended prompts like "how should I implement this?" unless no responsible options can
|
|
217
221
|
be framed from repository evidence.
|
|
218
|
-
15.
|
|
222
|
+
15. Prefer structured user input for real blocking decisions when the host exposes it:
|
|
223
|
+
- use a structured input tool such as `request_user_input` or MCP elicitation only when it is
|
|
224
|
+
explicitly listed as available in the current runtime or tool call;
|
|
225
|
+
- use it for `needs_confirmation` or for a non-blocking `ready_with_assumptions` choice whose
|
|
226
|
+
answer would materially improve the result without stopping all progress;
|
|
227
|
+
- ask at most three short questions, and prefer one question when the answer may change the next
|
|
228
|
+
question;
|
|
229
|
+
- provide two or three mutually exclusive choices, put the recommended choice first, and include
|
|
230
|
+
the concrete consequence or tradeoff for each choice;
|
|
231
|
+
- allow free-form input when the host mechanism supports it, because the listed options are a
|
|
232
|
+
steering aid rather than a closed product decision;
|
|
233
|
+
- use auto-resolution only for non-blocking choices with a narrow reversible default, and never
|
|
234
|
+
for destructive actions, publish or release decisions, credential or secret handling, data
|
|
235
|
+
deletion or migration, auth or billing policy, dependency adoption, or other explicit
|
|
236
|
+
confirmation gates;
|
|
237
|
+
- if no structured input mechanism is available, ask the same blocking decision as a concise
|
|
238
|
+
normal chat question when host policy allows it; do not invent a fake UI, long questionnaire,
|
|
239
|
+
or multiple-choice card in prose when the host explicitly forbids that fallback.
|
|
240
|
+
16. Do not ask bad engineering-delegation questions:
|
|
219
241
|
- "Should I add tests?"
|
|
220
242
|
- "Should I handle errors?"
|
|
221
243
|
- "Should I follow existing style?"
|
|
222
244
|
- "Should I check current files?"
|
|
223
245
|
- "Should I preserve existing behavior?"
|
|
224
|
-
|
|
246
|
+
17. Use prompt rewriting only as an exception:
|
|
225
247
|
- the user explicitly asks for a prompt, issue, PR body, work order, or handoff for another
|
|
226
248
|
agent;
|
|
227
249
|
- the current request is too broken to execute and a normalized contract plus confirmation is the
|
|
228
250
|
smallest safe next step.
|
|
229
251
|
Otherwise, show the normalized contract only when it materially reduces drift, then proceed in
|
|
230
252
|
the same conversation.
|
|
231
|
-
|
|
253
|
+
18. If no blocking question remains, proceed without ceremony. State only the assumptions that matter
|
|
232
254
|
to review or rollback.
|
|
233
|
-
|
|
255
|
+
19. If a blocking question remains unanswered, do not implement around it. Offer the smallest safe
|
|
234
256
|
non-blocked action, such as read-only analysis, a plan, a reproduction, or a narrow preparatory
|
|
235
257
|
refactor when another selected skill supports it.
|
|
236
258
|
|
|
@@ -241,6 +263,8 @@ verification, and stop-or-ask boundary.
|
|
|
241
263
|
- The agent has not asked for facts it could read locally.
|
|
242
264
|
- Expensive, user-owned, security-sensitive, data-affecting, dependency-affecting, and public-contract
|
|
243
265
|
decisions are resolved before implementation.
|
|
266
|
+
- Structured input tools are used only when available and only for bounded decisions that benefit
|
|
267
|
+
from user choice.
|
|
244
268
|
- Safe assumptions are narrow, reversible, and reported.
|
|
245
269
|
- Any normalized contract preserves the user's original request separately from repository-derived
|
|
246
270
|
facts and safe assumptions.
|
|
@@ -288,6 +312,7 @@ run the specific configured verification intents required by the selected implem
|
|
|
288
312
|
- Normalized task contract, only when needed, with `user_confirmed`, `repository_derived`,
|
|
289
313
|
`safe_assumption`, and `unresolved` source tags
|
|
290
314
|
- Blocking questions asked, with recommendation and tradeoff
|
|
315
|
+
- Question delivery mode: structured host input, normal chat fallback, or not needed
|
|
291
316
|
- Safe assumptions made
|
|
292
317
|
- Decisions intentionally deferred
|
|
293
318
|
- Implementation scope selected
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
---
|
|
2
|
+
mustflow_doc: skill.http-api-semantics-review
|
|
3
|
+
locale: en
|
|
4
|
+
canonical: true
|
|
5
|
+
revision: 1
|
|
6
|
+
lifecycle: mustflow-owned
|
|
7
|
+
authority: procedure
|
|
8
|
+
name: http-api-semantics-review
|
|
9
|
+
description: Apply this skill when HTTP API method semantics, safe or idempotent behavior, GET or HEAD request-body assumptions, OPTIONS or Allow discovery, HTTP QUERY, POST versus PUT URI ownership, PUT replacement, PATCH document formats, DELETE behavior, conditional requests, status codes, cache headers, CORS method discovery, retry behavior, or intermediary compatibility need review.
|
|
10
|
+
metadata:
|
|
11
|
+
mustflow_schema: "1"
|
|
12
|
+
mustflow_kind: procedure
|
|
13
|
+
pack_id: mustflow.core
|
|
14
|
+
skill_id: mustflow.core.http-api-semantics-review
|
|
15
|
+
command_intents:
|
|
16
|
+
- changes_status
|
|
17
|
+
- changes_diff_summary
|
|
18
|
+
- lint
|
|
19
|
+
- build
|
|
20
|
+
- test_related
|
|
21
|
+
- test
|
|
22
|
+
- docs_validate_fast
|
|
23
|
+
- test_release
|
|
24
|
+
- mustflow_check
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# HTTP API Semantics Review
|
|
28
|
+
|
|
29
|
+
<!-- mustflow-section: purpose -->
|
|
30
|
+
## Purpose
|
|
31
|
+
|
|
32
|
+
Review HTTP APIs as method, status, header, cache, retry, and intermediary contracts, not as CRUD
|
|
33
|
+
labels or controller naming preferences.
|
|
34
|
+
|
|
35
|
+
The review question is not "does this route work in the current framework?" It is "will browsers,
|
|
36
|
+
crawlers, caches, gateways, SDKs, retries, stale clients, and concurrent callers interpret this
|
|
37
|
+
request the same way the server does?"
|
|
38
|
+
|
|
39
|
+
<!-- mustflow-section: use-when -->
|
|
40
|
+
## Use When
|
|
41
|
+
|
|
42
|
+
- HTTP or REST routes, handlers, OpenAPI documents, API docs, SDKs, gateways, middleware, CORS
|
|
43
|
+
policy, cache policy, status-code maps, or request/response headers are created, changed,
|
|
44
|
+
reviewed, or reported.
|
|
45
|
+
- A change chooses or changes GET, HEAD, OPTIONS, POST, PUT, PATCH, DELETE, QUERY, or fallback
|
|
46
|
+
behavior.
|
|
47
|
+
- A review claims that an endpoint is safe, idempotent, cacheable, retry-safe, crawler-safe,
|
|
48
|
+
prefetch-safe, proxy-safe, CDN-compatible, or portable across HTTP clients.
|
|
49
|
+
- The task involves PUT full replacement, PATCH document semantics, DELETE bodies, POST search,
|
|
50
|
+
GET or HEAD request bodies, HTTP QUERY, Accept-Patch, Accept-Query, Allow, ETag, If-Match,
|
|
51
|
+
If-None-Match, Cache-Control, Vary, Location, Content-Location, Retry-After, or CORS method
|
|
52
|
+
discovery.
|
|
53
|
+
|
|
54
|
+
<!-- mustflow-section: do-not-use-when -->
|
|
55
|
+
## Do Not Use When
|
|
56
|
+
|
|
57
|
+
- The main work is broad API source-of-truth synchronization, generated clients, schema drift, or
|
|
58
|
+
compatibility classification; use `api-contract-change` first and this skill only for HTTP
|
|
59
|
+
method semantics.
|
|
60
|
+
- The main work is caller ergonomics, ambiguous DTOs, hidden operation modes, or SDK usability; use
|
|
61
|
+
`api-misuse-resistance-review`.
|
|
62
|
+
- The main work is duplicate business side effects or retry delivery; use
|
|
63
|
+
`idempotency-integrity-review`.
|
|
64
|
+
- The main work is cache key truth, stale data, invalidation, Redis, or cache outage behavior; use
|
|
65
|
+
`cache-integrity-review`.
|
|
66
|
+
- The main work is response streaming, compression, proxy buffering, SSE, WebTransport, or delivery
|
|
67
|
+
transport behavior; use `http-delivery-streaming`.
|
|
68
|
+
- The API is not HTTP-facing and no HTTP method, status, header, cache, CORS, retry, or
|
|
69
|
+
intermediary behavior is part of the contract.
|
|
70
|
+
|
|
71
|
+
<!-- mustflow-section: required-inputs -->
|
|
72
|
+
## Required Inputs
|
|
73
|
+
|
|
74
|
+
- Endpoint ledger: method, path, request body meaning, success statuses, error statuses, headers,
|
|
75
|
+
auth and permission requirement, cacheability, caller types, and fallback paths.
|
|
76
|
+
- Method contract: whether the caller requests a safe operation, an idempotent operation, a full
|
|
77
|
+
representation replacement, a patch document application, a server-selected creation, a
|
|
78
|
+
client-selected target URI, a delete/unlink operation, or a safe body-bearing query.
|
|
79
|
+
- Concurrency contract: ETag or version source, conditional request requirements, lost-update
|
|
80
|
+
behavior, conflict behavior, and retry behavior after unknown outcomes.
|
|
81
|
+
- Patch contract when PATCH is used: media type, patch grammar, null and omission semantics,
|
|
82
|
+
atomicity boundary, validation timing, Accept-Patch discovery, and status-code recovery map.
|
|
83
|
+
- Query contract when GET, QUERY, or POST search is used: canonical GET strategy, query body media
|
|
84
|
+
type, Accept-Query or Allow discovery, fallback behavior, cache-key inputs, canonicalization,
|
|
85
|
+
Location or Content-Location behavior, and sensitive-data handling.
|
|
86
|
+
- Cache and capability contract: Cache-Control, Vary, ETag, Last-Modified, Location,
|
|
87
|
+
Content-Location, Allow, Access-Control-Allow-Methods, Accept-Patch, Accept-Query, Retry-After,
|
|
88
|
+
and RateLimit header behavior when relevant.
|
|
89
|
+
- Relevant command-intent contract entries for tests, builds, docs, release checks, and mustflow
|
|
90
|
+
validation.
|
|
91
|
+
|
|
92
|
+
<!-- mustflow-section: preconditions -->
|
|
93
|
+
## Preconditions
|
|
94
|
+
|
|
95
|
+
- The task matches the Use When conditions and does not match the Do Not Use When exclusions.
|
|
96
|
+
- Higher-priority instructions and `.mustflow/config/commands.toml` have been checked for the
|
|
97
|
+
current scope.
|
|
98
|
+
- The current contract source of truth is known, such as OpenAPI, route validators, handler tests,
|
|
99
|
+
API docs, gateway config, or SDK examples.
|
|
100
|
+
- If HTTP semantics changes affect request or response shapes, generated clients, auth, permission,
|
|
101
|
+
cache correctness, duplicate side effects, or streaming delivery, also apply the narrower matching
|
|
102
|
+
skill before finalizing that part.
|
|
103
|
+
|
|
104
|
+
<!-- mustflow-section: allowed-edits -->
|
|
105
|
+
## Allowed Edits
|
|
106
|
+
|
|
107
|
+
- Update route handlers, method choices, validators, OpenAPI or API docs, SDK examples, gateway
|
|
108
|
+
rules, CORS rules, cache headers, conditional request handling, status-code maps, and focused
|
|
109
|
+
tests tied to the changed HTTP contract.
|
|
110
|
+
- Add explicit support or rejection for GET or HEAD bodies, DELETE bodies, unsupported methods,
|
|
111
|
+
unsupported patch media types, missing preconditions, and body-bearing safe queries.
|
|
112
|
+
- Add capability discovery through Allow, Accept-Patch, Accept-Query, and documented fallback
|
|
113
|
+
behavior when the API exposes those capabilities.
|
|
114
|
+
- Do not silently convert PUT into partial merge, PATCH into ambiguous JSON merge, GET into a
|
|
115
|
+
command, POST search into a safe operation without naming fallback semantics, or cache policy into
|
|
116
|
+
prose-only behavior.
|
|
117
|
+
|
|
118
|
+
<!-- mustflow-section: procedure -->
|
|
119
|
+
## Procedure
|
|
120
|
+
|
|
121
|
+
1. Build a method semantics ledger for every endpoint under review:
|
|
122
|
+
- method;
|
|
123
|
+
- target URI ownership;
|
|
124
|
+
- whether the method is safe, idempotent, and cacheable for this operation;
|
|
125
|
+
- request body meaning;
|
|
126
|
+
- expected automatic retry behavior;
|
|
127
|
+
- success, conflict, validation, unsupported-media, precondition, rate-limit, and overload
|
|
128
|
+
statuses.
|
|
129
|
+
2. Check safe methods first.
|
|
130
|
+
- GET, HEAD, and OPTIONS must not request business mutations.
|
|
131
|
+
- Safe does not mean unauthenticated, cheap, unmetered, or cache-public.
|
|
132
|
+
- Crawlers, link previews, browser prefetch, health checks, and monitoring can trigger safe
|
|
133
|
+
methods.
|
|
134
|
+
3. Reject public GET and HEAD request-body semantics unless the API intentionally documents a
|
|
135
|
+
non-portable private contract.
|
|
136
|
+
- Prefer GET query parameters for small, shareable, cacheable, non-sensitive reads.
|
|
137
|
+
- Prefer QUERY for safe, idempotent, body-bearing queries when clients, gateways, servers, and
|
|
138
|
+
docs can support it.
|
|
139
|
+
- Use POST as a compatibility fallback or state-changing processing request, not as an unnamed
|
|
140
|
+
safe query.
|
|
141
|
+
4. Review OPTIONS as capability discovery.
|
|
142
|
+
- OPTIONS is not only a CORS preflight shortcut.
|
|
143
|
+
- Keep `Allow` for HTTP method support separate from `Access-Control-Allow-Methods` for CORS.
|
|
144
|
+
- Ensure 405 responses include an accurate `Allow` header.
|
|
145
|
+
5. Review POST versus PUT URI ownership.
|
|
146
|
+
- Use POST when the server chooses the new resource URI or processes a resource-specific command.
|
|
147
|
+
- Use PUT when the client knows the target URI and wants that target representation created or
|
|
148
|
+
replaced.
|
|
149
|
+
- Do not let PUT write to a different URI without redirect or explicit contract.
|
|
150
|
+
6. Review PUT as replacement.
|
|
151
|
+
- A PUT body is the new representation for the target URI, not a partial DTO by default.
|
|
152
|
+
- Reject incomplete replacement bodies, require full client-owned representations, or choose
|
|
153
|
+
PATCH or a command resource instead.
|
|
154
|
+
- For create-only PUT, require `If-None-Match: *` where concurrent creation matters.
|
|
155
|
+
- For update-only PUT, require `If-Match` or another version precondition where lost updates
|
|
156
|
+
matter.
|
|
157
|
+
7. Review PATCH as patch document application.
|
|
158
|
+
- Pick and document the patch media type, such as `application/merge-patch+json`,
|
|
159
|
+
`application/json-patch+json`, or a domain-specific media type.
|
|
160
|
+
- Do not use bare `application/json` when null, omission, delete, array, and command semantics
|
|
161
|
+
are ambiguous.
|
|
162
|
+
- Advertise supported patch formats with `Accept-Patch` where capability discovery matters.
|
|
163
|
+
- Apply the whole patch atomically. Partial patch success is a contract failure.
|
|
164
|
+
- Validate the resulting resource, not only the patch document syntax.
|
|
165
|
+
8. Review DELETE as target-resource unlinking, not necessarily physical storage deletion.
|
|
166
|
+
- Do not require identical repeat response statuses for idempotency; require identical intended
|
|
167
|
+
effect and duplicate-safe side effects.
|
|
168
|
+
- Avoid DELETE request bodies for portable bulk or filtered deletes. Prefer item DELETE calls or
|
|
169
|
+
POST command resources such as deletion jobs.
|
|
170
|
+
- Document soft-delete, tombstone, retention, restore, and audit behavior when callers rely on
|
|
171
|
+
lifecycle outcomes.
|
|
172
|
+
9. Review conditional requests and lost-update protection.
|
|
173
|
+
- Use strong ETags or equivalent version tokens when concurrent writers can overwrite one
|
|
174
|
+
another.
|
|
175
|
+
- Use `If-Match` for update preconditions and `If-None-Match: *` for create-if-absent.
|
|
176
|
+
- Return `412 Precondition Failed` for failed preconditions and `428 Precondition Required` when
|
|
177
|
+
the API refuses unsafe writes without preconditions.
|
|
178
|
+
10. Review QUERY and search design.
|
|
179
|
+
- Keep simple, bookmarkable, shareable, cacheable searches on GET where possible.
|
|
180
|
+
- Use QUERY for safe, idempotent, body-bearing queries whose inputs do not belong cleanly in the
|
|
181
|
+
URI.
|
|
182
|
+
- Define the request media type and reject missing or inconsistent Content-Type.
|
|
183
|
+
- Expose support through `Allow` and supported query formats through `Accept-Query` when
|
|
184
|
+
clients need discovery.
|
|
185
|
+
- Build cache keys from request content plus relevant metadata, and canonicalize only
|
|
186
|
+
semantically insignificant differences.
|
|
187
|
+
- Decide whether `Content-Location`, `Location`, or `303 See Other` gives callers a GET-able
|
|
188
|
+
result, equivalent query, or stored job/resource.
|
|
189
|
+
- Use POST fallback only as a compatibility shell over the same internal query model.
|
|
190
|
+
11. Review status codes as recovery instructions.
|
|
191
|
+
- Use `201` plus `Location` and preferably `ETag` when a resource is created.
|
|
192
|
+
- Use `202` plus a status resource when work is accepted but unfinished.
|
|
193
|
+
- Use `204` for successful no-body responses, `206` for partial content, and `304` for
|
|
194
|
+
conditional cache validation.
|
|
195
|
+
- Separate malformed input (`400`), unsupported media (`415`), current-state conflict (`409`),
|
|
196
|
+
failed precondition (`412`), missing precondition (`428`), valid-but-unprocessable content
|
|
197
|
+
(`422`), method unsupported for the resource (`405` with `Allow`), method unknown (`501`),
|
|
198
|
+
rate limit (`429` with `Retry-After` where useful), and overload (`503` with `Retry-After`
|
|
199
|
+
where useful).
|
|
200
|
+
12. Review HTTP cache semantics.
|
|
201
|
+
- `no-cache` means revalidate before reuse. `no-store` means do not store.
|
|
202
|
+
- Use `private`, `public`, `s-maxage`, validators, and `Vary` according to viewer, auth,
|
|
203
|
+
language, encoding, content negotiation, and tenant dimensions.
|
|
204
|
+
- Do not assume a successful unsafe method invalidates every list, search, feed, recommendation,
|
|
205
|
+
CDN tag, or read model that can include the changed resource.
|
|
206
|
+
13. Check synchronized surfaces before finalizing.
|
|
207
|
+
- Route code, validators, OpenAPI or docs, generated clients, SDK examples, gateway and CORS
|
|
208
|
+
config, cache config, status-code fixtures, retry docs, tests, monitoring, and release notes
|
|
209
|
+
should agree with the HTTP method contract.
|
|
210
|
+
- If deterministic proxy, CDN, browser, or gateway evidence is unavailable, report that gap
|
|
211
|
+
instead of claiming portability.
|
|
212
|
+
|
|
213
|
+
## Strongly Forbidden Patterns
|
|
214
|
+
|
|
215
|
+
- GET, HEAD, or OPTIONS hidden business mutations.
|
|
216
|
+
- Public GET or HEAD request bodies as if they were portable HTTP contracts.
|
|
217
|
+
- PUT partial update merge without an explicit non-PUT contract.
|
|
218
|
+
- PATCH with ambiguous `application/json` semantics.
|
|
219
|
+
- PATCH partial success.
|
|
220
|
+
- DELETE request bodies for portable bulk or filtered operations.
|
|
221
|
+
- Treating DELETE idempotency as identical status codes while duplicate side effects replay.
|
|
222
|
+
- Treating `no-cache` as `no-store`.
|
|
223
|
+
- Mixing `Allow` with `Access-Control-Allow-Methods`.
|
|
224
|
+
- POST search without naming safe-query fallback and cache behavior.
|
|
225
|
+
- QUERY without media type, support discovery, body-aware cache key, and intermediary support plan.
|
|
226
|
+
- Collapsing status codes so clients cannot tell whether to fix input, refresh state, retry later,
|
|
227
|
+
update ETags, or change media type.
|
|
228
|
+
|
|
229
|
+
<!-- mustflow-section: postconditions -->
|
|
230
|
+
## Postconditions
|
|
231
|
+
|
|
232
|
+
- Method semantics, request body meaning, URI ownership, retry expectations, status-code recovery,
|
|
233
|
+
conditional requests, capability discovery, cache semantics, and intermediary assumptions are
|
|
234
|
+
explicit or reported as missing.
|
|
235
|
+
- PUT, PATCH, DELETE, GET, HEAD, OPTIONS, POST, and QUERY behavior is either aligned with HTTP
|
|
236
|
+
semantics or intentionally documented as a narrow non-portable contract.
|
|
237
|
+
- API docs, tests, SDKs, generated clients, gateways, CORS rules, cache headers, and observability
|
|
238
|
+
agree with the chosen HTTP contract where relevant.
|
|
239
|
+
|
|
240
|
+
<!-- mustflow-section: verification -->
|
|
241
|
+
## Verification
|
|
242
|
+
|
|
243
|
+
Use configured oneshot command intents when available:
|
|
244
|
+
|
|
245
|
+
- `changes_status`
|
|
246
|
+
- `changes_diff_summary`
|
|
247
|
+
- `lint`
|
|
248
|
+
- `build`
|
|
249
|
+
- `test_related`
|
|
250
|
+
- `test`
|
|
251
|
+
- `docs_validate_fast`
|
|
252
|
+
- `test_release`
|
|
253
|
+
- `mustflow_check`
|
|
254
|
+
|
|
255
|
+
Prefer the narrowest configured tests or docs checks that exercise the HTTP method, status, header,
|
|
256
|
+
cache, conditional request, QUERY, fallback, or generated-client contract from a caller's
|
|
257
|
+
perspective. Do not infer raw browser, proxy, CDN, gateway, load-test, or live-network commands
|
|
258
|
+
outside the command contract.
|
|
259
|
+
|
|
260
|
+
<!-- mustflow-section: failure-handling -->
|
|
261
|
+
## Failure Handling
|
|
262
|
+
|
|
263
|
+
- If the contract source of truth is unclear, stop and report the competing route, schema, gateway,
|
|
264
|
+
docs, and SDK sources instead of editing only one surface.
|
|
265
|
+
- If a method choice conflicts with existing callers, classify the compatibility risk and use
|
|
266
|
+
`api-contract-change` before changing public behavior.
|
|
267
|
+
- If duplicate side effects, cache sharing, security, or streaming delivery risks are found, switch
|
|
268
|
+
to the narrower matching skill before finalizing that part.
|
|
269
|
+
- If deterministic HTTP interoperability proof requires browser, gateway, CDN, proxy, or production
|
|
270
|
+
telemetry not configured in `.mustflow/config/commands.toml`, complete available local checks and
|
|
271
|
+
report the manual evidence gap.
|
|
272
|
+
|
|
273
|
+
<!-- mustflow-section: output-format -->
|
|
274
|
+
## Output Format
|
|
275
|
+
|
|
276
|
+
- HTTP API semantics reviewed
|
|
277
|
+
- Endpoint method ledger
|
|
278
|
+
- Safe, idempotent, cacheable, request-body, URI-ownership, PUT, PATCH, DELETE, QUERY, conditional
|
|
279
|
+
request, status-code, header discovery, CORS, cache, retry, and intermediary findings
|
|
280
|
+
- Fixes made or recommendations
|
|
281
|
+
- Synchronized docs, tests, SDK, generated client, gateway, and cache surfaces
|
|
282
|
+
- Evidence level: configured-test evidence, docs evidence, static review risk, manual-only,
|
|
283
|
+
missing, or not applicable
|
|
284
|
+
- Command intents run
|
|
285
|
+
- Skipped HTTP interoperability checks and reasons
|
|
286
|
+
- Remaining HTTP semantics risk
|
|
@@ -216,6 +216,12 @@ route_type = "adjunct"
|
|
|
216
216
|
priority = 69
|
|
217
217
|
applies_to_reasons = ["unknown_change", "code_change", "behavior_change", "test_change", "public_api_change", "performance_change", "security_change", "privacy_change", "data_change", "docs_change", "package_metadata_change"]
|
|
218
218
|
|
|
219
|
+
[routes."http-api-semantics-review"]
|
|
220
|
+
category = "general_code"
|
|
221
|
+
route_type = "adjunct"
|
|
222
|
+
priority = 81
|
|
223
|
+
applies_to_reasons = ["unknown_change", "code_change", "behavior_change", "public_api_change", "test_change", "docs_change", "performance_change", "security_change", "privacy_change", "data_change", "package_metadata_change", "release_risk"]
|
|
224
|
+
|
|
219
225
|
[routes."error-message-integrity-review"]
|
|
220
226
|
category = "general_code"
|
|
221
227
|
route_type = "adjunct"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
id = "default"
|
|
2
2
|
name = "default"
|
|
3
|
-
version = "2.103.
|
|
3
|
+
version = "2.103.22"
|
|
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"
|
|
@@ -14,6 +14,7 @@ creates = [
|
|
|
14
14
|
".mustflow/skills/routes.toml",
|
|
15
15
|
".mustflow/skills/adapter-boundary/SKILL.md",
|
|
16
16
|
".mustflow/skills/api-contract-change/SKILL.md",
|
|
17
|
+
".mustflow/skills/http-api-semantics-review/SKILL.md",
|
|
17
18
|
".mustflow/skills/backend-reliability-change/SKILL.md",
|
|
18
19
|
".mustflow/skills/http-delivery-streaming/SKILL.md",
|
|
19
20
|
".mustflow/skills/artifact-integrity-check/SKILL.md",
|
|
@@ -226,6 +227,7 @@ available = ["minimal", "patterns", "oss", "team", "product", "library"]
|
|
|
226
227
|
minimal = [
|
|
227
228
|
"adapter-boundary",
|
|
228
229
|
"api-contract-change",
|
|
230
|
+
"http-api-semantics-review",
|
|
229
231
|
"backend-reliability-change",
|
|
230
232
|
"http-delivery-streaming",
|
|
231
233
|
"auth-permission-change",
|
|
@@ -373,6 +375,7 @@ patterns = [
|
|
|
373
375
|
"architecture-deepening-review",
|
|
374
376
|
"service-boundary-architecture",
|
|
375
377
|
"api-contract-change",
|
|
378
|
+
"http-api-semantics-review",
|
|
376
379
|
"backend-reliability-change",
|
|
377
380
|
"http-delivery-streaming",
|
|
378
381
|
"auth-permission-change",
|
|
@@ -528,6 +531,7 @@ patterns = [
|
|
|
528
531
|
oss = [
|
|
529
532
|
"adapter-boundary",
|
|
530
533
|
"api-contract-change",
|
|
534
|
+
"http-api-semantics-review",
|
|
531
535
|
"backend-reliability-change",
|
|
532
536
|
"http-delivery-streaming",
|
|
533
537
|
"artifact-integrity-check",
|
|
@@ -705,6 +709,7 @@ team = [
|
|
|
705
709
|
"architecture-deepening-review",
|
|
706
710
|
"service-boundary-architecture",
|
|
707
711
|
"api-contract-change",
|
|
712
|
+
"http-api-semantics-review",
|
|
708
713
|
"backend-reliability-change",
|
|
709
714
|
"http-delivery-streaming",
|
|
710
715
|
"auth-permission-change",
|
|
@@ -864,6 +869,7 @@ team = [
|
|
|
864
869
|
product = [
|
|
865
870
|
"adapter-boundary",
|
|
866
871
|
"api-contract-change",
|
|
872
|
+
"http-api-semantics-review",
|
|
867
873
|
"backend-reliability-change",
|
|
868
874
|
"http-delivery-streaming",
|
|
869
875
|
"service-boundary-architecture",
|
|
@@ -1028,6 +1034,7 @@ product = [
|
|
|
1028
1034
|
library = [
|
|
1029
1035
|
"adapter-boundary",
|
|
1030
1036
|
"api-contract-change",
|
|
1037
|
+
"http-api-semantics-review",
|
|
1031
1038
|
"backend-reliability-change",
|
|
1032
1039
|
"http-delivery-streaming",
|
|
1033
1040
|
"artifact-integrity-check",
|