mustflow 2.32.0 → 2.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/api/actions.js +55 -0
- package/dist/cli/commands/api/report-runner.js +62 -0
- package/dist/cli/commands/api/serve.js +149 -0
- package/dist/cli/commands/api/workspace-recommendations.js +13 -0
- package/dist/cli/commands/api.js +15 -275
- package/dist/cli/lib/local-index/search-read-model.js +44 -7
- package/dist/cli/lib/validation/frontmatter.js +75 -0
- package/dist/cli/lib/validation/index.js +4 -86
- package/dist/cli/lib/validation/safe-read.js +13 -0
- package/dist/core/active-run-locks.js +110 -10
- package/dist/core/run-performance-history.js +14 -1
- package/dist/core/validation-ratchet.js +1 -1
- package/package.json +1 -1
- package/templates/default/i18n.toml +55 -19
- package/templates/default/locales/en/.mustflow/docs/agent-workflow.md +15 -7
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +20 -8
- package/templates/default/locales/en/.mustflow/skills/adapter-boundary/SKILL.md +9 -2
- package/templates/default/locales/en/.mustflow/skills/ai-generated-code-hardening/SKILL.md +249 -0
- package/templates/default/locales/en/.mustflow/skills/api-contract-change/SKILL.md +16 -11
- package/templates/default/locales/en/.mustflow/skills/auth-permission-change/SKILL.md +11 -4
- package/templates/default/locales/en/.mustflow/skills/backend-reliability-change/SKILL.md +289 -0
- package/templates/default/locales/en/.mustflow/skills/css-code-change/SKILL.md +24 -14
- package/templates/default/locales/en/.mustflow/skills/dependency-upgrade-review/SKILL.md +18 -7
- package/templates/default/locales/en/.mustflow/skills/frontend-render-stability/SKILL.md +144 -0
- package/templates/default/locales/en/.mustflow/skills/go-code-change/SKILL.md +70 -18
- package/templates/default/locales/en/.mustflow/skills/html-code-change/SKILL.md +20 -13
- package/templates/default/locales/en/.mustflow/skills/http-delivery-streaming/SKILL.md +205 -0
- package/templates/default/locales/en/.mustflow/skills/performance-budget-check/SKILL.md +9 -7
- package/templates/default/locales/en/.mustflow/skills/proactive-risk-surfacing/SKILL.md +198 -0
- package/templates/default/locales/en/.mustflow/skills/python-code-change/SKILL.md +27 -11
- package/templates/default/locales/en/.mustflow/skills/routes.toml +37 -1
- package/templates/default/locales/en/.mustflow/skills/rust-code-change/SKILL.md +41 -17
- package/templates/default/locales/en/.mustflow/skills/service-boundary-architecture/SKILL.md +167 -0
- package/templates/default/locales/en/.mustflow/skills/tailwind-code-change/SKILL.md +37 -23
- package/templates/default/locales/en/.mustflow/skills/tauri-code-change/SKILL.md +27 -10
- package/templates/default/locales/en/.mustflow/skills/typescript-code-change/SKILL.md +22 -4
- package/templates/default/locales/en/.mustflow/skills/unocss-code-change/SKILL.md +34 -15
- package/templates/default/locales/en/.mustflow/skills/version-freshness-check/SKILL.md +29 -5
- package/templates/default/locales/en/AGENTS.md +3 -2
- package/templates/default/locales/ko/.mustflow/docs/agent-workflow.md +13 -8
- package/templates/default/locales/ko/AGENTS.md +2 -2
- package/templates/default/manifest.toml +37 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { SKILL_SECTION_MARKER_PATTERN } from './constants.js';
|
|
2
|
+
export function readSkillSectionIds(content) {
|
|
3
|
+
return new Set([...content.matchAll(SKILL_SECTION_MARKER_PATTERN)].map((match) => match[1]));
|
|
4
|
+
}
|
|
5
|
+
function findFrontmatterEnd(content) {
|
|
6
|
+
const match = /\n---(?:\r?\n|$)/u.exec(content.slice(3));
|
|
7
|
+
return match ? 3 + match.index : -1;
|
|
8
|
+
}
|
|
9
|
+
export function parseSimpleFrontmatter(content) {
|
|
10
|
+
if (!content.startsWith('---')) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
const end = findFrontmatterEnd(content);
|
|
14
|
+
if (end === -1) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
const frontmatter = {};
|
|
18
|
+
for (const line of content.slice(3, end).split(/\r?\n/)) {
|
|
19
|
+
const separatorIndex = line.indexOf(':');
|
|
20
|
+
if (separatorIndex === -1) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
24
|
+
const value = line.slice(separatorIndex + 1).trim().replace(/^["']|["']$/g, '');
|
|
25
|
+
if (key.length > 0 && value.length > 0) {
|
|
26
|
+
frontmatter[key] = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return frontmatter;
|
|
30
|
+
}
|
|
31
|
+
function readFrontmatterLines(content) {
|
|
32
|
+
if (!content.startsWith('---')) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
const end = findFrontmatterEnd(content);
|
|
36
|
+
if (end === -1) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return content
|
|
40
|
+
.slice(3, end)
|
|
41
|
+
.split(/\n/u)
|
|
42
|
+
.map((line) => line.replace(/\r$/u, ''));
|
|
43
|
+
}
|
|
44
|
+
function stripScalarMarkers(value) {
|
|
45
|
+
return value.trim().replace(/^["'`]|["'`]$/g, '').trim();
|
|
46
|
+
}
|
|
47
|
+
export function readFrontmatterList(content, key) {
|
|
48
|
+
const lines = readFrontmatterLines(content);
|
|
49
|
+
const values = [];
|
|
50
|
+
let keyIndent;
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
const keyMatch = line.match(new RegExp(`^(\\s*)${key}:\\s*$`, 'u'));
|
|
53
|
+
if (keyIndent === undefined) {
|
|
54
|
+
if (keyMatch) {
|
|
55
|
+
keyIndent = keyMatch[1].length;
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (line.trim().length === 0) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const lineIndent = line.match(/^\s*/u)?.[0].length ?? 0;
|
|
63
|
+
const itemMatch = line.match(/^\s*-\s+(.+)$/u);
|
|
64
|
+
if (lineIndent <= keyIndent && !itemMatch) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (itemMatch) {
|
|
68
|
+
const value = stripScalarMarkers(itemMatch[1]);
|
|
69
|
+
if (value.length > 0) {
|
|
70
|
+
values.push(value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return values;
|
|
75
|
+
}
|
|
@@ -13,13 +13,15 @@ import { readGitChangedFiles } from '../git-changes.js';
|
|
|
13
13
|
import { inspectManifestLock } from '../manifest-lock.js';
|
|
14
14
|
import { generateRepoMap } from '../repo-map.js';
|
|
15
15
|
import { parseTomlText, readMustflowTomlFile } from '../toml.js';
|
|
16
|
-
import { MUSTFLOW_JSON_MAX_BYTES
|
|
16
|
+
import { MUSTFLOW_JSON_MAX_BYTES } from '../mustflow-read.js';
|
|
17
17
|
import { getContractModelDefinitions, validateCandidateContractModelConfig, } from '../../../core/contract-models.js';
|
|
18
18
|
import { VERSIONING_CONFIG_PATH, detectVersionSourcePaths, readDeclaredVersionSources, releaseVersioningIsEnabled, } from '../../../core/version-sources.js';
|
|
19
19
|
import { normalizeTechnologyPreferencesTable, TECHNOLOGY_CONFIG_RELATIVE_PATH, } from '../../../core/technology-preferences.js';
|
|
20
|
-
import { ALLOWED_APPROVAL_ACTIONS, ALLOWED_APPROVAL_GATES, ALLOWED_BUDGET_LIMIT_ACTIONS, ALLOWED_CAPABILITY_STATES, ALLOWED_COMMIT_MESSAGE_STYLES, ALLOWED_COMPACTION_CATEGORIES, ALLOWED_COMPACTION_LONG_LIMIT_ACTIONS, ALLOWED_COMPACTION_RAW_LIMIT_ACTIONS, ALLOWED_COMPACTION_STATE_STORES, ALLOWED_COMPACTION_STRATEGIES, ALLOWED_CONTEXT_AUTHORITIES, ALLOWED_CONTEXT_DOCUMENT_AUTHORITIES, ALLOWED_CONTEXT_READ_POLICIES, ALLOWED_HANDOFF_MODES, ALLOWED_HARNESS_FRESH_CONTEXT_MODES, ALLOWED_HARNESS_MODES, ALLOWED_HARNESS_PHASES, ALLOWED_ISOLATION_PREFERENCES, ALLOWED_MAP_MODES, ALLOWED_MAP_PRIVACY_LEVELS, ALLOWED_PROJECT_PROFILES, ALLOWED_REPO_MAP_DEGRADED_VALUES, ALLOWED_REPO_MAP_GIT_LS_FILES_STATUSES, ALLOWED_PROMPT_CACHE_STABLE_PREFIX_POLICIES, ALLOWED_PROMPT_CACHE_STRATEGIES, ALLOWED_PROMPT_CACHE_TASK_READ_POLICIES, ALLOWED_REFRESH_CHECKPOINTS, ALLOWED_REFRESH_METHODS, ALLOWED_REFRESH_MODES, ALLOWED_REFRESH_STATE_STORES, ALLOWED_SKILL_RESOURCE_TYPES, ALLOWED_SKILL_ROUTE_CATEGORIES, ALLOWED_SKILL_ROUTE_PROFILES, ALLOWED_SKILL_ROUTE_TYPES, ALLOWED_STALE_TEST_ACTIONS, ALLOWED_TEST_AUTHORING_POLICIES, ALLOWED_TEST_DELETION_REASONS, ALLOWED_TESTING_POLICIES, ALLOWED_TRANSLATION_POLICIES, ALLOWED_VERIFICATION_SELECTION_STRATEGIES, ALLOWED_VERSION_SOURCE_AUTHORITIES, ALLOWED_VERSION_SOURCE_KINDS, CAPABILITY_BOOLEAN_FIELDS, CAPABILITY_STATE_FIELDS, CONTEXT_AUTHORITY_DRIFT_PATTERNS, DESIGN_TOKEN_DEFINITION_PATTERNS, FORBIDDEN_RELEASE_VERSIONING_CONTRACT_FIELDS, FORBIDDEN_TEST_DELETION_REASONS, FORBIDDEN_VERIFICATION_SELECTION_AUTHORITY_FIELDS, LOCAL_ABSOLUTE_PATH_PATTERNS, LOCAL_TASK_STATE_ROOTS, RAW_COMMAND_FENCE_PATTERN, RELEASE_VERSIONING_BOOLEAN_FIELDS, REPO_MAP_DOC_ID, REPO_MAP_GENERATOR, REPO_MAP_LIFECYCLE, REPO_MAP_PRIVACY_MODE, REPO_MAP_RELATIVE_ROOT, REPO_MAP_REMOTE_OR_BRANCH_PATTERNS, REPO_MAP_SOURCE_FINGERPRINT_PATTERN, REPO_MAP_SOURCE_POLICY, REQUIRED_AGENT_LOOP_PHASES, REQUIRED_SKILL_SCRIPT_RUN_POLICY, REQUIRED_SKILL_SECTION_IDS, ROUTER_INDEX_FILES, ROUTER_INDEX_PROCEDURE_SECTION_PATTERN, SECRET_LIKE_CONTEXT_PATTERNS, SKILL_COMMAND_PERMISSION_CLAIM_PATTERNS, SKILL_INDEX_PATH, SKILL_PACK_ID_PATTERN, SKILL_RESOURCE_MANIFEST, SKILL_RESOURCE_ROOTS, SKILL_RESOURCE_TYPE_BY_ROOT, SKILL_ROUTE_CATEGORY_LABELS, SKILL_ROUTES_METADATA_PATH,
|
|
20
|
+
import { ALLOWED_APPROVAL_ACTIONS, ALLOWED_APPROVAL_GATES, ALLOWED_BUDGET_LIMIT_ACTIONS, ALLOWED_CAPABILITY_STATES, ALLOWED_COMMIT_MESSAGE_STYLES, ALLOWED_COMPACTION_CATEGORIES, ALLOWED_COMPACTION_LONG_LIMIT_ACTIONS, ALLOWED_COMPACTION_RAW_LIMIT_ACTIONS, ALLOWED_COMPACTION_STATE_STORES, ALLOWED_COMPACTION_STRATEGIES, ALLOWED_CONTEXT_AUTHORITIES, ALLOWED_CONTEXT_DOCUMENT_AUTHORITIES, ALLOWED_CONTEXT_READ_POLICIES, ALLOWED_HANDOFF_MODES, ALLOWED_HARNESS_FRESH_CONTEXT_MODES, ALLOWED_HARNESS_MODES, ALLOWED_HARNESS_PHASES, ALLOWED_ISOLATION_PREFERENCES, ALLOWED_MAP_MODES, ALLOWED_MAP_PRIVACY_LEVELS, ALLOWED_PROJECT_PROFILES, ALLOWED_REPO_MAP_DEGRADED_VALUES, ALLOWED_REPO_MAP_GIT_LS_FILES_STATUSES, ALLOWED_PROMPT_CACHE_STABLE_PREFIX_POLICIES, ALLOWED_PROMPT_CACHE_STRATEGIES, ALLOWED_PROMPT_CACHE_TASK_READ_POLICIES, ALLOWED_REFRESH_CHECKPOINTS, ALLOWED_REFRESH_METHODS, ALLOWED_REFRESH_MODES, ALLOWED_REFRESH_STATE_STORES, ALLOWED_SKILL_RESOURCE_TYPES, ALLOWED_SKILL_ROUTE_CATEGORIES, ALLOWED_SKILL_ROUTE_PROFILES, ALLOWED_SKILL_ROUTE_TYPES, ALLOWED_STALE_TEST_ACTIONS, ALLOWED_TEST_AUTHORING_POLICIES, ALLOWED_TEST_DELETION_REASONS, ALLOWED_TESTING_POLICIES, ALLOWED_TRANSLATION_POLICIES, ALLOWED_VERIFICATION_SELECTION_STRATEGIES, ALLOWED_VERSION_SOURCE_AUTHORITIES, ALLOWED_VERSION_SOURCE_KINDS, CAPABILITY_BOOLEAN_FIELDS, CAPABILITY_STATE_FIELDS, CONTEXT_AUTHORITY_DRIFT_PATTERNS, DESIGN_TOKEN_DEFINITION_PATTERNS, FORBIDDEN_RELEASE_VERSIONING_CONTRACT_FIELDS, FORBIDDEN_TEST_DELETION_REASONS, FORBIDDEN_VERIFICATION_SELECTION_AUTHORITY_FIELDS, LOCAL_ABSOLUTE_PATH_PATTERNS, LOCAL_TASK_STATE_ROOTS, RAW_COMMAND_FENCE_PATTERN, RELEASE_VERSIONING_BOOLEAN_FIELDS, REPO_MAP_DOC_ID, REPO_MAP_GENERATOR, REPO_MAP_LIFECYCLE, REPO_MAP_PRIVACY_MODE, REPO_MAP_RELATIVE_ROOT, REPO_MAP_REMOTE_OR_BRANCH_PATTERNS, REPO_MAP_SOURCE_FINGERPRINT_PATTERN, REPO_MAP_SOURCE_POLICY, REQUIRED_AGENT_LOOP_PHASES, REQUIRED_SKILL_SCRIPT_RUN_POLICY, REQUIRED_SKILL_SECTION_IDS, ROUTER_INDEX_FILES, ROUTER_INDEX_PROCEDURE_SECTION_PATTERN, SECRET_LIKE_CONTEXT_PATTERNS, SKILL_COMMAND_PERMISSION_CLAIM_PATTERNS, SKILL_INDEX_PATH, SKILL_PACK_ID_PATTERN, SKILL_RESOURCE_MANIFEST, SKILL_RESOURCE_ROOTS, SKILL_RESOURCE_TYPE_BY_ROOT, SKILL_ROUTE_CATEGORY_LABELS, SKILL_ROUTES_METADATA_PATH, SUPPORTED_SKILL_SCHEMA_VERSION, TEST_AUTHORING_BOOLEAN_FIELDS, VERIFICATION_SELECTION_BOOLEAN_FIELDS, VOLATILE_REPO_MAP_PATTERNS } from './constants.js';
|
|
21
21
|
import { hasOwn, isPositiveInteger, isSafeRelativePath, pushStrictIssue, pushStrictWarning, validateAllowedStringField, validateBooleanField, validateExactStringArrayField, validateNestedTable, validatePathArrayField, validatePathField, validatePositiveIntegerField, validateRequiredFiles, validateRequiredPathField, validateRequiredStringField, validateStringArrayField, validateStringArrayMembers, validateStringField, validateTable, validateToml, validateWorkspaceRoots } from './primitives.js';
|
|
22
22
|
import { isConfiguredCommandIntent, isDeclaredCommandIntent } from './command-intents.js';
|
|
23
|
+
import { parseSimpleFrontmatter, readFrontmatterList, readSkillSectionIds } from './frontmatter.js';
|
|
24
|
+
import { readStrictMustflowText } from './safe-read.js';
|
|
23
25
|
import { validateStrictTestSelectionConfig } from './test-selection.js';
|
|
24
26
|
import { getDefaultTemplate, getTemplateFiles } from '../templates.js';
|
|
25
27
|
export { describeCheckIssues, getCheckIssueId, } from '../../../core/check-issues.js';
|
|
@@ -481,90 +483,6 @@ function validateSkills(projectRoot, issues) {
|
|
|
481
483
|
}
|
|
482
484
|
}
|
|
483
485
|
}
|
|
484
|
-
function readSkillSectionIds(content) {
|
|
485
|
-
return new Set([...content.matchAll(SKILL_SECTION_MARKER_PATTERN)].map((match) => match[1]));
|
|
486
|
-
}
|
|
487
|
-
function findFrontmatterEnd(content) {
|
|
488
|
-
const match = /\n---(?:\r?\n|$)/u.exec(content.slice(3));
|
|
489
|
-
return match ? 3 + match.index : -1;
|
|
490
|
-
}
|
|
491
|
-
function parseSimpleFrontmatter(content) {
|
|
492
|
-
if (!content.startsWith('---')) {
|
|
493
|
-
return {};
|
|
494
|
-
}
|
|
495
|
-
const end = findFrontmatterEnd(content);
|
|
496
|
-
if (end === -1) {
|
|
497
|
-
return {};
|
|
498
|
-
}
|
|
499
|
-
const frontmatter = {};
|
|
500
|
-
for (const line of content.slice(3, end).split(/\r?\n/)) {
|
|
501
|
-
const separatorIndex = line.indexOf(':');
|
|
502
|
-
if (separatorIndex === -1) {
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
const key = line.slice(0, separatorIndex).trim();
|
|
506
|
-
const value = line.slice(separatorIndex + 1).trim().replace(/^["']|["']$/g, '');
|
|
507
|
-
if (key.length > 0 && value.length > 0) {
|
|
508
|
-
frontmatter[key] = value;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
return frontmatter;
|
|
512
|
-
}
|
|
513
|
-
function readStrictMustflowText(projectRoot, relativePath, issues, options = {}) {
|
|
514
|
-
const result = readMustflowTextFileResult(projectRoot, relativePath, options);
|
|
515
|
-
if (result.ok) {
|
|
516
|
-
return result.content;
|
|
517
|
-
}
|
|
518
|
-
if (result.exists && result.error) {
|
|
519
|
-
pushStrictIssue(issues, `${toPosixPath(relativePath)} could not be read safely: ${result.error}`);
|
|
520
|
-
}
|
|
521
|
-
return undefined;
|
|
522
|
-
}
|
|
523
|
-
function readFrontmatterLines(content) {
|
|
524
|
-
if (!content.startsWith('---')) {
|
|
525
|
-
return [];
|
|
526
|
-
}
|
|
527
|
-
const end = findFrontmatterEnd(content);
|
|
528
|
-
if (end === -1) {
|
|
529
|
-
return [];
|
|
530
|
-
}
|
|
531
|
-
return content
|
|
532
|
-
.slice(3, end)
|
|
533
|
-
.split(/\n/u)
|
|
534
|
-
.map((line) => line.replace(/\r$/u, ''));
|
|
535
|
-
}
|
|
536
|
-
function stripScalarMarkers(value) {
|
|
537
|
-
return value.trim().replace(/^["'`]|["'`]$/g, '').trim();
|
|
538
|
-
}
|
|
539
|
-
function readFrontmatterList(content, key) {
|
|
540
|
-
const lines = readFrontmatterLines(content);
|
|
541
|
-
const values = [];
|
|
542
|
-
let keyIndent;
|
|
543
|
-
for (const line of lines) {
|
|
544
|
-
const keyMatch = line.match(new RegExp(`^(\\s*)${key}:\\s*$`, 'u'));
|
|
545
|
-
if (keyIndent === undefined) {
|
|
546
|
-
if (keyMatch) {
|
|
547
|
-
keyIndent = keyMatch[1].length;
|
|
548
|
-
}
|
|
549
|
-
continue;
|
|
550
|
-
}
|
|
551
|
-
if (line.trim().length === 0) {
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
const lineIndent = line.match(/^\s*/u)?.[0].length ?? 0;
|
|
555
|
-
const itemMatch = line.match(/^\s*-\s+(.+)$/u);
|
|
556
|
-
if (lineIndent <= keyIndent && !itemMatch) {
|
|
557
|
-
break;
|
|
558
|
-
}
|
|
559
|
-
if (itemMatch) {
|
|
560
|
-
const value = stripScalarMarkers(itemMatch[1]);
|
|
561
|
-
if (value.length > 0) {
|
|
562
|
-
values.push(value);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
return values;
|
|
567
|
-
}
|
|
568
486
|
function validateContextDocuments(projectRoot, issues) {
|
|
569
487
|
const contextRoot = path.join(projectRoot, '.mustflow', 'context');
|
|
570
488
|
const contextFiles = listFilesRecursive(contextRoot).filter((relativePath) => relativePath.endsWith('.md'));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { toPosixPath } from '../filesystem.js';
|
|
2
|
+
import { readMustflowTextFileResult } from '../mustflow-read.js';
|
|
3
|
+
import { pushStrictIssue } from './primitives.js';
|
|
4
|
+
export function readStrictMustflowText(projectRoot, relativePath, issues, options = {}) {
|
|
5
|
+
const result = readMustflowTextFileResult(projectRoot, relativePath, options);
|
|
6
|
+
if (result.ok) {
|
|
7
|
+
return result.content;
|
|
8
|
+
}
|
|
9
|
+
if (result.exists && result.error) {
|
|
10
|
+
pushStrictIssue(issues, `${toPosixPath(relativePath)} could not be read safely: ${result.error}`);
|
|
11
|
+
}
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
@@ -9,6 +9,7 @@ const LOCK_MUTEX_STALE_MS = 30_000;
|
|
|
9
9
|
const LOCK_MUTEX_WAIT_MS = 1_000;
|
|
10
10
|
const LOCK_MUTEX_SLEEP_MS = 25;
|
|
11
11
|
const LOCK_MUTEX_SLEEP_BUFFER = new Int32Array(new SharedArrayBuffer(4));
|
|
12
|
+
const LOCK_MUTEX_RECOVERY_DIRECTORY = 'recovery';
|
|
12
13
|
function sleep(milliseconds) {
|
|
13
14
|
Atomics.wait(LOCK_MUTEX_SLEEP_BUFFER, 0, 0, milliseconds);
|
|
14
15
|
}
|
|
@@ -24,6 +25,9 @@ function activeLockDirectory(projectRoot) {
|
|
|
24
25
|
function activeLockMutexDirectory(projectRoot) {
|
|
25
26
|
return path.join(activeLockRoot(projectRoot), 'mutex');
|
|
26
27
|
}
|
|
28
|
+
function activeLockMutexRecoveryDirectory(mutex) {
|
|
29
|
+
return path.join(mutex, LOCK_MUTEX_RECOVERY_DIRECTORY);
|
|
30
|
+
}
|
|
27
31
|
function normalizeEffect(effect) {
|
|
28
32
|
return {
|
|
29
33
|
source: effect.source,
|
|
@@ -192,6 +196,95 @@ function createRecord(projectRoot, intentName, effects, commandHash) {
|
|
|
192
196
|
writes: [...new Set(writes)],
|
|
193
197
|
};
|
|
194
198
|
}
|
|
199
|
+
function readMutexOwner(ownerPath) {
|
|
200
|
+
try {
|
|
201
|
+
const owner = JSON.parse(readFileSync(ownerPath, 'utf8'));
|
|
202
|
+
if (typeof owner.started_at !== 'string' || typeof owner.token !== 'string') {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
pid: Number(owner.pid),
|
|
207
|
+
startedAt: owner.started_at,
|
|
208
|
+
token: owner.token,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function sameMutexOwner(left, right) {
|
|
216
|
+
return right !== null && left.pid === right.pid && left.startedAt === right.startedAt && left.token === right.token;
|
|
217
|
+
}
|
|
218
|
+
function mutexOwnerIsStale(owner) {
|
|
219
|
+
const ownerStartedAt = Date.parse(owner.startedAt);
|
|
220
|
+
const staleByAge = Number.isFinite(ownerStartedAt) && Date.now() - ownerStartedAt > LOCK_MUTEX_STALE_MS;
|
|
221
|
+
return !isProcessLive(owner.pid) || staleByAge;
|
|
222
|
+
}
|
|
223
|
+
function beginMutexRecovery(mutex) {
|
|
224
|
+
const recoveryPath = activeLockMutexRecoveryDirectory(mutex);
|
|
225
|
+
try {
|
|
226
|
+
mkdirSync(recoveryPath);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
if (!error || typeof error !== 'object' || !('code' in error) || error.code !== 'EEXIST') {
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const recoveryStat = statSync(recoveryPath);
|
|
234
|
+
if (Date.now() - recoveryStat.mtimeMs > LOCK_MUTEX_STALE_MS) {
|
|
235
|
+
rmSync(recoveryPath, { recursive: true, force: true });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// A concurrent recovery may have already finished.
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
let active = true;
|
|
244
|
+
return () => {
|
|
245
|
+
if (!active) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
active = false;
|
|
249
|
+
rmSync(recoveryPath, { recursive: true, force: true });
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function recoverStaleMutexWithOwner(mutex, ownerPath, staleOwner) {
|
|
253
|
+
const releaseRecovery = beginMutexRecovery(mutex);
|
|
254
|
+
if (!releaseRecovery) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
if (!sameMutexOwner(staleOwner, readMutexOwner(ownerPath)) || !mutexOwnerIsStale(staleOwner)) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
rmSync(mutex, { recursive: true, force: true });
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
releaseRecovery();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function recoverStaleMutexWithoutOwner(mutex) {
|
|
269
|
+
const releaseRecovery = beginMutexRecovery(mutex);
|
|
270
|
+
if (!releaseRecovery) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const ownerPath = path.join(mutex, 'owner.json');
|
|
275
|
+
if (readMutexOwner(ownerPath) !== null) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
rmSync(mutex, { recursive: true, force: true });
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
releaseRecovery();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
195
288
|
function acquireMutex(projectRoot) {
|
|
196
289
|
const root = activeLockRoot(projectRoot);
|
|
197
290
|
const mutex = activeLockMutexDirectory(projectRoot);
|
|
@@ -227,22 +320,29 @@ function acquireMutex(projectRoot) {
|
|
|
227
320
|
throw error;
|
|
228
321
|
}
|
|
229
322
|
if (Date.now() - startedAt > LOCK_MUTEX_WAIT_MS) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const ownerStartedAt = typeof owner.started_at === 'string' ? Date.parse(owner.started_at) : Number.NaN;
|
|
234
|
-
const staleByAge = Number.isFinite(ownerStartedAt) && Date.now() - ownerStartedAt > LOCK_MUTEX_STALE_MS;
|
|
235
|
-
if (!isProcessLive(ownerPid) || staleByAge) {
|
|
236
|
-
rmSync(mutex, { recursive: true, force: true });
|
|
323
|
+
const owner = readMutexOwner(ownerPath);
|
|
324
|
+
if (owner) {
|
|
325
|
+
if (mutexOwnerIsStale(owner) && recoverStaleMutexWithOwner(mutex, ownerPath, owner)) {
|
|
237
326
|
startedAt = Date.now();
|
|
238
327
|
continue;
|
|
239
328
|
}
|
|
240
329
|
}
|
|
241
|
-
|
|
330
|
+
else {
|
|
331
|
+
const recoveryPath = activeLockMutexRecoveryDirectory(mutex);
|
|
332
|
+
try {
|
|
333
|
+
const recoveryStat = statSync(recoveryPath);
|
|
334
|
+
if (Date.now() - recoveryStat.mtimeMs <= LOCK_MUTEX_STALE_MS) {
|
|
335
|
+
throw new Error('active_run_lock_mutex_busy');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (recoveryError) {
|
|
339
|
+
if (recoveryError instanceof Error && recoveryError.message === 'active_run_lock_mutex_busy') {
|
|
340
|
+
throw recoveryError;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
242
343
|
try {
|
|
243
344
|
const mutexStat = statSync(mutex);
|
|
244
|
-
if (Date.now() - mutexStat.mtimeMs > LOCK_MUTEX_STALE_MS) {
|
|
245
|
-
rmSync(mutex, { recursive: true, force: true });
|
|
345
|
+
if (Date.now() - mutexStat.mtimeMs > LOCK_MUTEX_STALE_MS && recoverStaleMutexWithoutOwner(mutex)) {
|
|
246
346
|
startedAt = Date.now();
|
|
247
347
|
continue;
|
|
248
348
|
}
|
|
@@ -281,12 +281,25 @@ function serializedHistorySize(samples, today) {
|
|
|
281
281
|
Buffer.byteLength(serialize(createSummary(samples, today)), 'utf8'));
|
|
282
282
|
}
|
|
283
283
|
function enforceSizeLimit(samples, today) {
|
|
284
|
-
|
|
284
|
+
const currentSize = serializedHistorySize(samples, today);
|
|
285
|
+
if (currentSize <= MAX_TOTAL_BYTES) {
|
|
285
286
|
return samples;
|
|
286
287
|
}
|
|
288
|
+
const averageBytesPerSample = Math.max(1, currentSize / Math.max(1, samples.length));
|
|
289
|
+
const estimatedDropCount = Math.floor((currentSize - MAX_TOTAL_BYTES) / averageBytesPerSample);
|
|
287
290
|
let low = 1;
|
|
288
291
|
let high = samples.length;
|
|
289
292
|
let firstFittingIndex = samples.length;
|
|
293
|
+
const probeIndex = Math.max(1, Math.min(samples.length, estimatedDropCount));
|
|
294
|
+
if (probeIndex > 1) {
|
|
295
|
+
if (serializedHistorySize(samples.slice(probeIndex), today) <= MAX_TOTAL_BYTES) {
|
|
296
|
+
firstFittingIndex = probeIndex;
|
|
297
|
+
high = probeIndex - 1;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
low = probeIndex + 1;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
290
303
|
while (low <= high) {
|
|
291
304
|
const middle = Math.floor((low + high) / 2);
|
|
292
305
|
const candidate = samples.slice(middle);
|
|
@@ -99,7 +99,7 @@ function gitDiffLinesByPath(projectRoot, relativePaths) {
|
|
|
99
99
|
if (uniquePaths.length === 0) {
|
|
100
100
|
return new Map();
|
|
101
101
|
}
|
|
102
|
-
const result = spawnSync('git', ['diff', '--no-ext-diff', '--unified=0', '--', ...uniquePaths], {
|
|
102
|
+
const result = spawnSync('git', ['diff', 'HEAD', '--no-ext-diff', '--unified=0', '--', ...uniquePaths], {
|
|
103
103
|
cwd: projectRoot,
|
|
104
104
|
encoding: 'utf8',
|
|
105
105
|
env: createCommandEnv(projectRoot, { policy: 'minimal', allowlist: [] }),
|
package/package.json
CHANGED
|
@@ -10,8 +10,8 @@ status_values = ["current", "stale", "needs_review", "missing"]
|
|
|
10
10
|
[documents."agents.root"]
|
|
11
11
|
source = "locales/en/AGENTS.md"
|
|
12
12
|
source_locale = "en"
|
|
13
|
-
revision =
|
|
14
|
-
translations.ko = { path = "locales/ko/AGENTS.md", source_revision =
|
|
13
|
+
revision = 15
|
|
14
|
+
translations.ko = { path = "locales/ko/AGENTS.md", source_revision = 15, status = "current" }
|
|
15
15
|
translations.zh = { path = "locales/zh/AGENTS.md", source_revision = 11, status = "needs_review" }
|
|
16
16
|
translations.es = { path = "locales/es/AGENTS.md", source_revision = 11, status = "needs_review" }
|
|
17
17
|
translations.fr = { path = "locales/fr/AGENTS.md", source_revision = 11, status = "needs_review" }
|
|
@@ -40,8 +40,8 @@ translations.hi = { path = "locales/hi/.mustflow/context/PROJECT.md", source_rev
|
|
|
40
40
|
[documents."docs.agent-workflow"]
|
|
41
41
|
source = "locales/en/.mustflow/docs/agent-workflow.md"
|
|
42
42
|
source_locale = "en"
|
|
43
|
-
revision =
|
|
44
|
-
translations.ko = { path = "locales/ko/.mustflow/docs/agent-workflow.md", source_revision =
|
|
43
|
+
revision = 20
|
|
44
|
+
translations.ko = { path = "locales/ko/.mustflow/docs/agent-workflow.md", source_revision = 20, status = "current" }
|
|
45
45
|
translations.zh = { path = "locales/zh/.mustflow/docs/agent-workflow.md", source_revision = 18, status = "needs_review" }
|
|
46
46
|
translations.es = { path = "locales/es/.mustflow/docs/agent-workflow.md", source_revision = 18, status = "needs_review" }
|
|
47
47
|
translations.fr = { path = "locales/fr/.mustflow/docs/agent-workflow.md", source_revision = 18, status = "needs_review" }
|
|
@@ -62,13 +62,13 @@ translations = {}
|
|
|
62
62
|
[documents."skills.index"]
|
|
63
63
|
source = "locales/en/.mustflow/skills/INDEX.md"
|
|
64
64
|
source_locale = "en"
|
|
65
|
-
revision =
|
|
65
|
+
revision = 108
|
|
66
66
|
translations = {}
|
|
67
67
|
|
|
68
68
|
[documents."skill.adapter-boundary"]
|
|
69
69
|
source = "locales/en/.mustflow/skills/adapter-boundary/SKILL.md"
|
|
70
70
|
source_locale = "en"
|
|
71
|
-
revision =
|
|
71
|
+
revision = 12
|
|
72
72
|
translations = {}
|
|
73
73
|
|
|
74
74
|
[documents."skill.artifact-integrity-check"]
|
|
@@ -86,6 +86,18 @@ translations = {}
|
|
|
86
86
|
[documents."skill.api-contract-change"]
|
|
87
87
|
source = "locales/en/.mustflow/skills/api-contract-change/SKILL.md"
|
|
88
88
|
source_locale = "en"
|
|
89
|
+
revision = 2
|
|
90
|
+
translations = {}
|
|
91
|
+
|
|
92
|
+
[documents."skill.backend-reliability-change"]
|
|
93
|
+
source = "locales/en/.mustflow/skills/backend-reliability-change/SKILL.md"
|
|
94
|
+
source_locale = "en"
|
|
95
|
+
revision = 1
|
|
96
|
+
translations = {}
|
|
97
|
+
|
|
98
|
+
[documents."skill.http-delivery-streaming"]
|
|
99
|
+
source = "locales/en/.mustflow/skills/http-delivery-streaming/SKILL.md"
|
|
100
|
+
source_locale = "en"
|
|
89
101
|
revision = 1
|
|
90
102
|
translations = {}
|
|
91
103
|
|
|
@@ -101,6 +113,12 @@ source_locale = "en"
|
|
|
101
113
|
revision = 6
|
|
102
114
|
translations = {}
|
|
103
115
|
|
|
116
|
+
[documents."skill.ai-generated-code-hardening"]
|
|
117
|
+
source = "locales/en/.mustflow/skills/ai-generated-code-hardening/SKILL.md"
|
|
118
|
+
source_locale = "en"
|
|
119
|
+
revision = 1
|
|
120
|
+
translations = {}
|
|
121
|
+
|
|
104
122
|
[documents."skill.contract-sync-check"]
|
|
105
123
|
source = "locales/en/.mustflow/skills/contract-sync-check/SKILL.md"
|
|
106
124
|
source_locale = "en"
|
|
@@ -152,13 +170,13 @@ translations = {}
|
|
|
152
170
|
[documents."skill.dependency-upgrade-review"]
|
|
153
171
|
source = "locales/en/.mustflow/skills/dependency-upgrade-review/SKILL.md"
|
|
154
172
|
source_locale = "en"
|
|
155
|
-
revision =
|
|
173
|
+
revision = 3
|
|
156
174
|
translations = {}
|
|
157
175
|
|
|
158
176
|
[documents."skill.version-freshness-check"]
|
|
159
177
|
source = "locales/en/.mustflow/skills/version-freshness-check/SKILL.md"
|
|
160
178
|
source_locale = "en"
|
|
161
|
-
revision =
|
|
179
|
+
revision = 6
|
|
162
180
|
translations = {}
|
|
163
181
|
|
|
164
182
|
[documents."skill.line-ending-hygiene"]
|
|
@@ -173,6 +191,12 @@ source_locale = "en"
|
|
|
173
191
|
revision = 4
|
|
174
192
|
translations = {}
|
|
175
193
|
|
|
194
|
+
[documents."skill.frontend-render-stability"]
|
|
195
|
+
source = "locales/en/.mustflow/skills/frontend-render-stability/SKILL.md"
|
|
196
|
+
source_locale = "en"
|
|
197
|
+
revision = 2
|
|
198
|
+
translations = {}
|
|
199
|
+
|
|
176
200
|
[documents."skill.diff-risk-review"]
|
|
177
201
|
source = "locales/en/.mustflow/skills/diff-risk-review/SKILL.md"
|
|
178
202
|
source_locale = "en"
|
|
@@ -200,7 +224,7 @@ translations = {}
|
|
|
200
224
|
[documents."skill.auth-permission-change"]
|
|
201
225
|
source = "locales/en/.mustflow/skills/auth-permission-change/SKILL.md"
|
|
202
226
|
source_locale = "en"
|
|
203
|
-
revision =
|
|
227
|
+
revision = 2
|
|
204
228
|
translations = {}
|
|
205
229
|
|
|
206
230
|
[documents."skill.config-env-change"]
|
|
@@ -212,7 +236,7 @@ translations = {}
|
|
|
212
236
|
[documents."skill.css-code-change"]
|
|
213
237
|
source = "locales/en/.mustflow/skills/css-code-change/SKILL.md"
|
|
214
238
|
source_locale = "en"
|
|
215
|
-
revision =
|
|
239
|
+
revision = 3
|
|
216
240
|
translations = {}
|
|
217
241
|
|
|
218
242
|
[documents."skill.bun-code-change"]
|
|
@@ -254,7 +278,7 @@ translations = {}
|
|
|
254
278
|
[documents."skill.go-code-change"]
|
|
255
279
|
source = "locales/en/.mustflow/skills/go-code-change/SKILL.md"
|
|
256
280
|
source_locale = "en"
|
|
257
|
-
revision =
|
|
281
|
+
revision = 3
|
|
258
282
|
translations = {}
|
|
259
283
|
|
|
260
284
|
[documents."skill.hono-code-change"]
|
|
@@ -266,7 +290,7 @@ translations = {}
|
|
|
266
290
|
[documents."skill.html-code-change"]
|
|
267
291
|
source = "locales/en/.mustflow/skills/html-code-change/SKILL.md"
|
|
268
292
|
source_locale = "en"
|
|
269
|
-
revision =
|
|
293
|
+
revision = 3
|
|
270
294
|
translations = {}
|
|
271
295
|
|
|
272
296
|
[documents."skill.javascript-code-change"]
|
|
@@ -284,13 +308,13 @@ translations = {}
|
|
|
284
308
|
[documents."skill.python-code-change"]
|
|
285
309
|
source = "locales/en/.mustflow/skills/python-code-change/SKILL.md"
|
|
286
310
|
source_locale = "en"
|
|
287
|
-
revision =
|
|
311
|
+
revision = 3
|
|
288
312
|
translations = {}
|
|
289
313
|
|
|
290
314
|
[documents."skill.rust-code-change"]
|
|
291
315
|
source = "locales/en/.mustflow/skills/rust-code-change/SKILL.md"
|
|
292
316
|
source_locale = "en"
|
|
293
|
-
revision =
|
|
317
|
+
revision = 4
|
|
294
318
|
translations = {}
|
|
295
319
|
|
|
296
320
|
[documents."skill.runtime-target-selection"]
|
|
@@ -314,25 +338,31 @@ translations = {}
|
|
|
314
338
|
[documents."skill.tailwind-code-change"]
|
|
315
339
|
source = "locales/en/.mustflow/skills/tailwind-code-change/SKILL.md"
|
|
316
340
|
source_locale = "en"
|
|
317
|
-
revision =
|
|
341
|
+
revision = 3
|
|
318
342
|
translations = {}
|
|
319
343
|
|
|
320
344
|
[documents."skill.tauri-code-change"]
|
|
321
345
|
source = "locales/en/.mustflow/skills/tauri-code-change/SKILL.md"
|
|
322
346
|
source_locale = "en"
|
|
323
|
-
revision =
|
|
347
|
+
revision = 3
|
|
324
348
|
translations = {}
|
|
325
349
|
|
|
326
350
|
[documents."skill.typescript-code-change"]
|
|
327
351
|
source = "locales/en/.mustflow/skills/typescript-code-change/SKILL.md"
|
|
328
352
|
source_locale = "en"
|
|
329
|
-
revision =
|
|
353
|
+
revision = 3
|
|
330
354
|
translations = {}
|
|
331
355
|
|
|
332
356
|
[documents."skill.unocss-code-change"]
|
|
333
357
|
source = "locales/en/.mustflow/skills/unocss-code-change/SKILL.md"
|
|
334
358
|
source_locale = "en"
|
|
335
|
-
revision =
|
|
359
|
+
revision = 3
|
|
360
|
+
translations = {}
|
|
361
|
+
|
|
362
|
+
[documents."skill.service-boundary-architecture"]
|
|
363
|
+
source = "locales/en/.mustflow/skills/service-boundary-architecture/SKILL.md"
|
|
364
|
+
source_locale = "en"
|
|
365
|
+
revision = 1
|
|
336
366
|
translations = {}
|
|
337
367
|
|
|
338
368
|
[documents."skill.cli-output-contract-review"]
|
|
@@ -475,7 +505,7 @@ translations = {}
|
|
|
475
505
|
[documents."skill.performance-budget-check"]
|
|
476
506
|
source = "locales/en/.mustflow/skills/performance-budget-check/SKILL.md"
|
|
477
507
|
source_locale = "en"
|
|
478
|
-
revision =
|
|
508
|
+
revision = 20
|
|
479
509
|
translations = {}
|
|
480
510
|
|
|
481
511
|
[documents."skill.pattern-scout"]
|
|
@@ -484,6 +514,12 @@ source_locale = "en"
|
|
|
484
514
|
revision = 2
|
|
485
515
|
translations = {}
|
|
486
516
|
|
|
517
|
+
[documents."skill.proactive-risk-surfacing"]
|
|
518
|
+
source = "locales/en/.mustflow/skills/proactive-risk-surfacing/SKILL.md"
|
|
519
|
+
source_locale = "en"
|
|
520
|
+
revision = 1
|
|
521
|
+
translations = {}
|
|
522
|
+
|
|
487
523
|
[documents."skill.process-execution-safety"]
|
|
488
524
|
source = "locales/en/.mustflow/skills/process-execution-safety/SKILL.md"
|
|
489
525
|
source_locale = "en"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
mustflow_doc: docs.agent-workflow
|
|
3
3
|
locale: en
|
|
4
4
|
canonical: true
|
|
5
|
-
revision:
|
|
5
|
+
revision: 20
|
|
6
6
|
lifecycle: mustflow-owned
|
|
7
7
|
authority: workflow-policy
|
|
8
8
|
---
|
|
@@ -139,15 +139,23 @@ Do not write turn counters, message counts, or session activity into the reposit
|
|
|
139
139
|
|
|
140
140
|
Do not store hidden reasoning, secrets, full chat transcripts, full terminal output, raw event logs, or raw command logs in the project. If a host creates compacted summaries in the future, they must be source-linked and remain lower authority than current files and direct user instructions.
|
|
141
141
|
|
|
142
|
-
## Harness
|
|
142
|
+
## Harness and Runtime Surfaces
|
|
143
143
|
|
|
144
|
-
mustflow
|
|
144
|
+
mustflow starts from repository-local workflow files and command execution boundaries, but it may
|
|
145
|
+
grow optional runtime, coordination, work-item, adapter, and harness surfaces. New surfaces must be
|
|
146
|
+
explicit, bounded, and reviewable instead of being smuggled in through prose, generated state, or
|
|
147
|
+
inferred commands.
|
|
145
148
|
|
|
146
|
-
- Brain
|
|
147
|
-
- Hands
|
|
148
|
-
- Session
|
|
149
|
+
- Brain surface: `AGENTS.md`, this workflow file, and skill documents define the expected model behavior.
|
|
150
|
+
- Hands surface: `.mustflow/config/commands.toml` and `mf run` define safe command execution.
|
|
151
|
+
- Session surface: run records, bounded checkpoints, compact handoffs, and future work-item records
|
|
152
|
+
provide evidence for recovery.
|
|
153
|
+
- Runtime surface: worker folders, persona systems, fleet orchestration, service processes, and
|
|
154
|
+
autonomous loops require declared lifecycle, retention, isolation, approval, and verification rules.
|
|
149
155
|
|
|
150
|
-
Do not
|
|
156
|
+
Do not operate runtime or coordination surfaces from implicit conventions. Add or enable them through
|
|
157
|
+
explicit configuration, command contracts, tests, and documentation when the repository chooses to
|
|
158
|
+
support that capability.
|
|
151
159
|
|
|
152
160
|
## Long-Running Task Phases
|
|
153
161
|
|