@jstn-sdk/ma 0.1.12 → 0.1.13
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/.agents/plugins/marketplace.json +0 -0
- package/.codex/agents/Architect.toml +0 -2
- package/.codex/agents/Auditor.toml +0 -2
- package/.codex/agents/Builder.toml +0 -2
- package/.codex/agents/Flow.toml +0 -2
- package/.codex/agents/Sage.toml +0 -2
- package/.codex/agents/Vibe.toml +0 -2
- package/.codex/hooks.json +14 -13
- package/.codex/prompts/enforcement.md +4 -0
- package/.codex/prompts/skill-contract.md +4 -0
- package/COVERAGE.md +211 -0
- package/DEMO.md +265 -0
- package/README.md +159 -16
- package/bin/ma.js +219 -14
- package/data/clone-data.ledger.json +41 -0
- package/data/clone-data.proof.json +50 -0
- package/data/clone-data.rvf +37 -0
- package/docs/README.md +4 -1
- package/docs/assets/image/Screenshot(1).png +0 -0
- package/docs/assets/image/Screenshot(2).png +0 -0
- package/docs/assets/image/Screenshot(3).png +0 -0
- package/docs/assets/image/Screenshot(4).png +0 -0
- package/docs/assets/image/Screenshot(5).png +0 -0
- package/docs/assets/image/Screenshot(6).png +0 -0
- package/docs/assets/image/Screenshot(7).png +0 -0
- package/docs/assets/image/Screenshot(8).png +0 -0
- package/docs/assets/image/Screenshot(9).png +0 -0
- package/docs/assets/meta-architect-logo.png +0 -0
- package/docs/assets/meta-architect-logo.svg +0 -0
- package/docs/getting-started.md +38 -22
- package/docs/installed-sdk.md +0 -0
- package/docs/mcp-setup.md +35 -0
- package/docs/onboarding.md +18 -2
- package/docs/prompt-guidance-contract.md +17 -0
- package/docs/prompt-guidance-fragments/active-autonomy-core.md +7 -0
- package/docs/qa/release-issue-gates-0.1.13.json +644 -0
- package/docs/qa/{release-readiness-0.1.12.md → release-readiness-0.1.13.md} +25 -9
- package/docs/qa/release-readiness-0.1.5.md +1 -1
- package/docs/reference/native-engineering-patterns.md +0 -0
- package/docs/reference/native-security-playbooks.md +6 -0
- package/docs/reference/native-source-selection.md +6 -0
- package/docs/reference/native-style-and-deslop.md +0 -0
- package/docs/release-spec.md +25 -9
- package/docs/skills.md +14 -1
- package/index.js +222 -2
- package/mcp/collections.json +23 -6
- package/mcp/local/code-intel.js +0 -0
- package/mcp/local/memory.js +0 -0
- package/mcp/local/playbooks.js +0 -0
- package/mcp/local/state.js +0 -0
- package/mcp/local/team-run.js +48 -1
- package/mcp/local/trace.js +0 -0
- package/mcp/local-capabilities.json +0 -0
- package/mcp/native-playbooks.json +0 -0
- package/mcp/servers.json +24 -0
- package/package.json +5 -2
- package/plugins/meta-architect/.app.json +1 -1
- package/plugins/meta-architect/.codex-plugin/plugin.json +1 -1
- package/plugins/meta-architect/.mcp.json +1 -1
- package/plugins/meta-architect/README.md +8 -1
- package/plugins/meta-architect/obsidian/main.js +401 -0
- package/plugins/meta-architect/obsidian/manifest.json +9 -0
- package/plugins/meta-architect/obsidian/styles.css +7 -0
- package/plugins/meta-architect/skills/align/SKILL.md +0 -0
- package/plugins/meta-architect/skills/align/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/align/references/shared-language.md +0 -0
- package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
- package/plugins/meta-architect/skills/arch/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/build/SKILL.md +3 -0
- package/plugins/meta-architect/skills/build/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/cleanup/SKILL.md +0 -0
- package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +0 -0
- package/plugins/meta-architect/skills/diagnose/SKILL.md +0 -0
- package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
- package/plugins/meta-architect/skills/flow/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/maestro/SKILL.md +0 -0
- package/plugins/meta-architect/skills/maestro/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/maestro/references/core-release-rules.md +0 -0
- package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +0 -0
- package/plugins/meta-architect/skills/sage/SKILL.md +2 -0
- package/plugins/meta-architect/skills/sage/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/sage/references/source-selection.md +8 -0
- package/plugins/meta-architect/skills/tdd/SKILL.md +0 -0
- package/plugins/meta-architect/skills/tdd/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/vet/SKILL.md +2 -0
- package/plugins/meta-architect/skills/vet/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/vet/references/security-playbooks.md +7 -0
- package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
- package/plugins/meta-architect/skills/vibe/agents/openai.yaml +0 -0
- package/scripts/active-autonomy-hook.mjs +96 -0
- package/scripts/biome-staged.js +0 -0
- package/scripts/build-linux-packages.mjs +0 -0
- package/scripts/doctor.js +17 -4
- package/scripts/install.sh +28 -0
- package/scripts/linux-package-lib.mjs +0 -0
- package/scripts/linux-package-smoke.mjs +0 -0
- package/scripts/plugin-sync.js +0 -0
- package/scripts/postinstall.js +0 -0
- package/scripts/ralph/prompt.md +35 -0
- package/scripts/release-metadata.js +0 -0
- package/scripts/release-sync.js +0 -0
- package/scripts/release-verify.js +166 -1
- package/scripts/setup-npmrc.js +0 -0
- package/skills/align/SKILL.md +0 -0
- package/skills/align/agents/openai.yaml +0 -0
- package/skills/align/references/shared-language.md +0 -0
- package/skills/arch/SKILL.md +2 -0
- package/skills/arch/agents/openai.yaml +0 -0
- package/skills/build/SKILL.md +3 -0
- package/skills/build/agents/openai.yaml +0 -0
- package/skills/cleanup/SKILL.md +0 -0
- package/skills/cleanup/agents/openai.yaml +0 -0
- package/skills/cleanup/references/style-and-deslop.md +0 -0
- package/skills/diagnose/SKILL.md +0 -0
- package/skills/diagnose/agents/openai.yaml +0 -0
- package/skills/flow/SKILL.md +2 -0
- package/skills/flow/agents/openai.yaml +0 -0
- package/skills/maestro/SKILL.md +0 -0
- package/skills/maestro/agents/openai.yaml +0 -0
- package/skills/maestro/references/core-release-rules.md +0 -0
- package/skills/maestro/references/native-ingest-map.md +0 -0
- package/skills/sage/SKILL.md +2 -0
- package/skills/sage/agents/openai.yaml +0 -0
- package/skills/sage/references/source-selection.md +8 -0
- package/skills/tdd/SKILL.md +0 -0
- package/skills/tdd/agents/openai.yaml +0 -0
- package/skills/vet/SKILL.md +2 -0
- package/skills/vet/agents/openai.yaml +0 -0
- package/skills/vet/references/security-playbooks.md +7 -0
- package/skills/vibe/SKILL.md +2 -0
- package/skills/vibe/agents/openai.yaml +0 -0
- package/src/bootstrap.js +17 -0
- package/src/build-gate.js +2 -2
- package/src/launcher.js +4 -0
- package/src/mcp-live-client.js +289 -3
- package/src/paths.js +4 -0
- package/src/policy.js +1 -1
- package/src/release-issue-gates.js +190 -0
- package/src/runtime/active-autonomy-core.js +208 -0
- package/src/runtime/alignment-sentinel.js +165 -0
- package/src/runtime/architect-review.js +88 -0
- package/src/runtime/build-readiness.js +28 -4
- package/src/runtime/code-graph-rehearse.js +113 -0
- package/src/runtime/context-economy-core.js +276 -0
- package/src/runtime/continuity-notes.js +0 -0
- package/src/runtime/core-source-ingest.js +379 -0
- package/src/runtime/detached-provider.js +74 -0
- package/src/runtime/environment-awareness-core.js +460 -0
- package/src/runtime/exposure-catalog.js +276 -0
- package/src/runtime/guidance-stack.js +0 -0
- package/src/runtime/helper-orchestration-core.js +307 -0
- package/src/runtime/learning-loop-core.js +238 -0
- package/src/runtime/maestro-events.js +18 -0
- package/src/runtime/maestro-manager.js +19 -0
- package/src/runtime/maestro-state.js +125 -0
- package/src/runtime/mcp-policy.js +192 -0
- package/src/runtime/obsidian-integration-core.js +851 -0
- package/src/runtime/obsidian-plugin-bridge.js +739 -0
- package/src/runtime/orchestrator.js +0 -0
- package/src/runtime/prompt-strategy-core.js +230 -0
- package/src/runtime/quorum-review.js +90 -0
- package/src/runtime/ralph-execution-core.js +217 -0
- package/src/runtime/redaction-gateway.js +174 -0
- package/src/runtime/runtime-state.js +843 -2
- package/src/runtime/semantic-recording-core.js +147 -0
- package/src/runtime/signal-hooks.js +15 -0
- package/src/runtime/skills-registry-export.js +670 -0
- package/src/runtime/startup-path.js +0 -0
- package/src/runtime/universal-plugin-broker-core.js +575 -0
- package/src/runtime/workspace-intelligence-runtime.js +674 -0
- package/src/runtime/workspace-virtualizer.js +102 -0
- package/src/runtime/workspaces.js +0 -0
- package/src/runtime-artifacts.js +225 -62
- package/src/skill-installer.js +0 -0
- package/src/skills.js +876 -84
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { ensureDir, readJson, writeFileIfMissing } from "../fs-utils.js";
|
|
2
|
+
import { getRuntimeSubsystemPath } from "../paths.js";
|
|
3
|
+
|
|
4
|
+
export const learningLoopCoreSchemaVersion = "0.1.0";
|
|
5
|
+
|
|
6
|
+
export const learningLoopDomains = [
|
|
7
|
+
"core_orchestration",
|
|
8
|
+
"memory_knowledge",
|
|
9
|
+
"intelligence_learning",
|
|
10
|
+
"code_quality_testing",
|
|
11
|
+
"security_compliance",
|
|
12
|
+
"architecture_methodology",
|
|
13
|
+
"devops_observability",
|
|
14
|
+
"extensibility",
|
|
15
|
+
"domain_specific",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export function getLearningLoopCorePath() {
|
|
19
|
+
return getRuntimeSubsystemPath("context", "learning-loop-core.json");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createDefaultLearningLoopCore() {
|
|
23
|
+
return {
|
|
24
|
+
schemaVersion: learningLoopCoreSchemaVersion,
|
|
25
|
+
product: "Meta-Architect",
|
|
26
|
+
purpose:
|
|
27
|
+
"First-party learning loop that turns verified workspace outcomes into durable, lane-aware improvements over time.",
|
|
28
|
+
loop: {
|
|
29
|
+
cadence: "every_verified_workflow_or_release_gate",
|
|
30
|
+
sequence: [
|
|
31
|
+
"observe_runtime_outcome",
|
|
32
|
+
"classify_learning_domain",
|
|
33
|
+
"record_semantic_receipt",
|
|
34
|
+
"promote_verified_learning",
|
|
35
|
+
"apply_to_next_run",
|
|
36
|
+
"verify_effectiveness_delta",
|
|
37
|
+
],
|
|
38
|
+
fail_closed_rule:
|
|
39
|
+
"Unverified observations stay as candidate learnings and must not change gates, release state, or durable policy.",
|
|
40
|
+
},
|
|
41
|
+
domains: [
|
|
42
|
+
{
|
|
43
|
+
id: "core_orchestration",
|
|
44
|
+
label: "Core & Orchestration",
|
|
45
|
+
learns_from: ["maestro_runs", "lane_handoffs", "manager_events"],
|
|
46
|
+
writes_to: [".ma/state/manager-runs.json", ".ma/context/workspace-effectiveness.json"],
|
|
47
|
+
promotion_gate: "handoff_replay_or_release_check_passed",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "memory_knowledge",
|
|
51
|
+
label: "Memory & Knowledge",
|
|
52
|
+
learns_from: ["memory_notes", "obsidian_vault_context", "semantic_receipts"],
|
|
53
|
+
writes_to: [".ma/memory/notes.md", ".ma/memory/index.json", ".ma/context/project.md"],
|
|
54
|
+
promotion_gate: "source_provenance_and_authority_boundary_verified",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "intelligence_learning",
|
|
58
|
+
label: "Intelligence & Learning",
|
|
59
|
+
learns_from: ["prompt_strategy_results", "context_budget_outcomes", "rehearsal_traces"],
|
|
60
|
+
writes_to: [".ma/context/prompt-strategy-core.json", ".ma/context/learning-loop-core.json"],
|
|
61
|
+
promotion_gate: "strategy_result_has_verification_delta",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "code_quality_testing",
|
|
65
|
+
label: "Code Quality & Testing",
|
|
66
|
+
learns_from: ["test_failures", "lint_results", "build_results", "ralph_progress"],
|
|
67
|
+
writes_to: [".ma/specs/logic.md", ".ma/plans/build.md"],
|
|
68
|
+
promotion_gate: "fresh_test_or_build_evidence_passed",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "security_compliance",
|
|
72
|
+
label: "Security & Compliance",
|
|
73
|
+
learns_from: ["vet_findings", "exposure_catalog", "redaction_receipts"],
|
|
74
|
+
writes_to: [".ma/specs/security.md", ".ma/evidence/audits.json"],
|
|
75
|
+
promotion_gate: "security_review_passed_or_blocker_preserved",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "architecture_methodology",
|
|
79
|
+
label: "Architecture & Methodology",
|
|
80
|
+
learns_from: ["architecture_decisions", "methodology_reviews", "tradeoff_outcomes"],
|
|
81
|
+
writes_to: [".ma/specs/architecture.md", ".ma/decisions.json"],
|
|
82
|
+
promotion_gate: "architecture_review_accepts_reusable_pattern",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "devops_observability",
|
|
86
|
+
label: "DevOps & Observability",
|
|
87
|
+
learns_from: ["release_checks", "package_smokes", "runtime_traces", "hook_audits"],
|
|
88
|
+
writes_to: [".ma/runbook.md", ".ma/logs/", ".ma/hooks/audit.log"],
|
|
89
|
+
promotion_gate: "release_or_smoke_gate_passed",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "extensibility",
|
|
93
|
+
label: "Extensibility",
|
|
94
|
+
learns_from: ["skill_exports", "plugin_installs", "mcp_policy_results", "host_payloads"],
|
|
95
|
+
writes_to: [".ma/context/skills-registry-export.json", "mcp/servers.json"],
|
|
96
|
+
promotion_gate: "compatibility_matrix_verified",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: "domain_specific",
|
|
100
|
+
label: "Domain-Specific",
|
|
101
|
+
learns_from: ["workspace_stack", "project_domain_notes", "trusted_sources"],
|
|
102
|
+
writes_to: [
|
|
103
|
+
".ma/context/workspace-context-pack.json",
|
|
104
|
+
".ma/evidence/semantic-receipts.json",
|
|
105
|
+
],
|
|
106
|
+
promotion_gate: "domain_claim_has_source_or_user_authority",
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
learning_record_schema: {
|
|
110
|
+
domain: "one_of_learningLoopDomains",
|
|
111
|
+
claim: "string",
|
|
112
|
+
source: "path_or_runtime_surface",
|
|
113
|
+
evidence: ["path_or_command_or_receipt"],
|
|
114
|
+
status: "candidate | verified | rejected | superseded",
|
|
115
|
+
applies_to: ["agent_or_role_or_lane"],
|
|
116
|
+
cannot_mutate: ["release_state_without_owning_lane", "source_evidence", "security_boundary"],
|
|
117
|
+
next_verification: "string",
|
|
118
|
+
},
|
|
119
|
+
hard_rules: [
|
|
120
|
+
"Learning records require domain, claim, source, evidence, status, applies_to, and next_verification.",
|
|
121
|
+
"Candidate learnings can inform context but cannot mutate gates, release state, or durable policy.",
|
|
122
|
+
"Verified learnings must preserve lane authority and semantic channel boundaries.",
|
|
123
|
+
"Obsidian and memory context can inform planning but do not become build evidence without owning-lane promotion.",
|
|
124
|
+
"Security, compliance, redaction, and irreversible-action warnings must not be compressed or auto-promoted.",
|
|
125
|
+
],
|
|
126
|
+
records: [],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function validateLearningLoopCore(value) {
|
|
131
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
132
|
+
throw new Error("learning loop core must be an object");
|
|
133
|
+
}
|
|
134
|
+
if (value.schemaVersion !== learningLoopCoreSchemaVersion) {
|
|
135
|
+
throw new Error(`Unsupported learning loop core schemaVersion: ${value.schemaVersion}`);
|
|
136
|
+
}
|
|
137
|
+
if (!Array.isArray(value.domains) || value.domains.length !== learningLoopDomains.length) {
|
|
138
|
+
throw new Error("learning loop core must include every required learning domain");
|
|
139
|
+
}
|
|
140
|
+
const ids = new Set(value.domains.map((domain) => domain.id));
|
|
141
|
+
for (const required of learningLoopDomains) {
|
|
142
|
+
if (!ids.has(required)) {
|
|
143
|
+
throw new Error(`learning loop core missing domain: ${required}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const domain of value.domains) {
|
|
147
|
+
if (
|
|
148
|
+
typeof domain.label !== "string" ||
|
|
149
|
+
!Array.isArray(domain.learns_from) ||
|
|
150
|
+
!Array.isArray(domain.writes_to) ||
|
|
151
|
+
typeof domain.promotion_gate !== "string"
|
|
152
|
+
) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
"learning loop domains require label, learns_from, writes_to, and promotion_gate",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!Array.isArray(value.records)) {
|
|
159
|
+
throw new Error("learning loop core requires records array");
|
|
160
|
+
}
|
|
161
|
+
if (JSON.stringify(value).includes(".omx")) {
|
|
162
|
+
throw new Error("learning loop core must not expose OMX runtime paths");
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function createLearningRecord({
|
|
168
|
+
domain,
|
|
169
|
+
claim,
|
|
170
|
+
source,
|
|
171
|
+
evidence = [],
|
|
172
|
+
status = "candidate",
|
|
173
|
+
appliesTo = ["all_applicable_agents", "all_applicable_roles"],
|
|
174
|
+
nextVerification,
|
|
175
|
+
}) {
|
|
176
|
+
if (!learningLoopDomains.includes(domain)) {
|
|
177
|
+
throw new Error(`Unsupported learning domain: ${domain}`);
|
|
178
|
+
}
|
|
179
|
+
if (!claim || !source || !nextVerification) {
|
|
180
|
+
throw new Error("Learning record requires domain, claim, source, and nextVerification");
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
domain,
|
|
184
|
+
claim,
|
|
185
|
+
source,
|
|
186
|
+
evidence: evidence.filter((entry) => typeof entry === "string" && entry.trim()),
|
|
187
|
+
status,
|
|
188
|
+
applies_to: appliesTo.filter((entry) => typeof entry === "string" && entry.trim()),
|
|
189
|
+
cannot_mutate: ["release_state_without_owning_lane", "source_evidence", "security_boundary"],
|
|
190
|
+
next_verification: nextVerification,
|
|
191
|
+
created_at: new Date().toISOString(),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function addLearningRecord(core, record) {
|
|
196
|
+
const validated = validateLearningLoopCore(core);
|
|
197
|
+
if (!learningLoopDomains.includes(record.domain)) {
|
|
198
|
+
throw new Error(`Unsupported learning domain: ${record.domain}`);
|
|
199
|
+
}
|
|
200
|
+
if (!["candidate", "verified", "rejected", "superseded"].includes(record.status)) {
|
|
201
|
+
throw new Error(`Unsupported learning record status: ${record.status}`);
|
|
202
|
+
}
|
|
203
|
+
return validateLearningLoopCore({
|
|
204
|
+
...validated,
|
|
205
|
+
records: [...validated.records, record],
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function evaluateLearningLoopReadiness(core) {
|
|
210
|
+
const validated = validateLearningLoopCore(core);
|
|
211
|
+
const coveredDomains = new Set(validated.domains.map((domain) => domain.id));
|
|
212
|
+
const verifiedDomains = new Set(
|
|
213
|
+
validated.records
|
|
214
|
+
.filter((record) => record.status === "verified")
|
|
215
|
+
.map((record) => record.domain),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
record_type: "learning_loop_readiness",
|
|
220
|
+
ready: learningLoopDomains.every((domain) => coveredDomains.has(domain)),
|
|
221
|
+
domains: learningLoopDomains.length,
|
|
222
|
+
verified_domains: verifiedDomains.size,
|
|
223
|
+
candidate_records: validated.records.filter((record) => record.status === "candidate").length,
|
|
224
|
+
promotion_rule: validated.loop.fail_closed_rule,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function seedLearningLoopCoreArtifacts() {
|
|
229
|
+
await ensureDir(getRuntimeSubsystemPath("context"));
|
|
230
|
+
await writeFileIfMissing(
|
|
231
|
+
getLearningLoopCorePath(),
|
|
232
|
+
`${JSON.stringify(createDefaultLearningLoopCore(), null, 2)}\n`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export async function loadLearningLoopCore() {
|
|
237
|
+
return validateLearningLoopCore(await readJson(getLearningLoopCorePath()));
|
|
238
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { getRuntimeSubsystemPath } from "../paths.js";
|
|
3
|
+
|
|
4
|
+
export function getMaestroEventsPath() {
|
|
5
|
+
return getRuntimeSubsystemPath("logs", "maestro-events.ndjson");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function appendMaestroEvent(record) {
|
|
9
|
+
const event = {
|
|
10
|
+
schemaVersion: "0.1.0",
|
|
11
|
+
timestamp: new Date().toISOString(),
|
|
12
|
+
...record,
|
|
13
|
+
};
|
|
14
|
+
const target = getMaestroEventsPath();
|
|
15
|
+
await fs.mkdir(getRuntimeSubsystemPath("logs"), { recursive: true });
|
|
16
|
+
await fs.appendFile(target, `${JSON.stringify(event)}\n`, "utf8");
|
|
17
|
+
return event;
|
|
18
|
+
}
|
|
@@ -524,6 +524,25 @@ export function chooseMaestroManagerAction({
|
|
|
524
524
|
};
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
if (["READY", "RUNNING"].includes(releaseState.build_status)) {
|
|
528
|
+
return {
|
|
529
|
+
mode: "helper+gated",
|
|
530
|
+
nextAction: "dispatch-gated",
|
|
531
|
+
dispatchPlan: {
|
|
532
|
+
helpers: [],
|
|
533
|
+
gated: [
|
|
534
|
+
createGatedPlan(
|
|
535
|
+
"$build",
|
|
536
|
+
"Advance the bounded build execution substep and persist lane-owned build evidence.",
|
|
537
|
+
{ prerequisites: ["build_status=READY|RUNNING"] },
|
|
538
|
+
),
|
|
539
|
+
],
|
|
540
|
+
team: null,
|
|
541
|
+
},
|
|
542
|
+
reason: recommendation?.why ?? "The build lane still owns the next bounded execution step.",
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
527
546
|
if (releaseState.merge_status !== "MERGED_TO_DEVELOPMENT") {
|
|
528
547
|
return {
|
|
529
548
|
mode: "team",
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
|
|
3
|
+
import { getRuntimeStatePath } from "../paths.js";
|
|
4
|
+
|
|
5
|
+
const defaultDependencyGraph = {
|
|
6
|
+
description: "Topological map of gate execution dependencies.",
|
|
7
|
+
nodes: {
|
|
8
|
+
$arch: { dependencies: [] },
|
|
9
|
+
$sage: { dependencies: ["$arch"] },
|
|
10
|
+
$flow: { dependencies: ["$sage"] },
|
|
11
|
+
$vet: { dependencies: ["$sage"] },
|
|
12
|
+
$vibe: { dependencies: ["$sage"] },
|
|
13
|
+
$build: { dependencies: ["$flow", "$vet", "$vibe"] },
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const validGlobalStatuses = new Set([
|
|
18
|
+
"IDLE",
|
|
19
|
+
"READY",
|
|
20
|
+
"RUNNING",
|
|
21
|
+
"COMPLETED",
|
|
22
|
+
"DEGRADED_HEALING",
|
|
23
|
+
"LOCKED",
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
export function getMaestroStatePath() {
|
|
27
|
+
return getRuntimeStatePath("maestro-state.json");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createDefaultMaestroState() {
|
|
31
|
+
return {
|
|
32
|
+
schemaVersion: "0.1.0",
|
|
33
|
+
orchestration_id: null,
|
|
34
|
+
timestamp: null,
|
|
35
|
+
global_status: "IDLE",
|
|
36
|
+
configuration: {
|
|
37
|
+
auto_heal: false,
|
|
38
|
+
max_healing_attempts: 2,
|
|
39
|
+
concurrency_limit: 4,
|
|
40
|
+
},
|
|
41
|
+
dependency_graph: defaultDependencyGraph,
|
|
42
|
+
runtime_tracks: {},
|
|
43
|
+
downstream_lock_table: {},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function validateMaestroState(state) {
|
|
48
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) {
|
|
49
|
+
throw new Error("Maestro state must be an object");
|
|
50
|
+
}
|
|
51
|
+
if (state.schemaVersion !== "0.1.0") {
|
|
52
|
+
throw new Error(`Unsupported maestro state schemaVersion: ${state.schemaVersion}`);
|
|
53
|
+
}
|
|
54
|
+
if (!(state.orchestration_id === null || typeof state.orchestration_id === "string")) {
|
|
55
|
+
throw new Error("maestro_state.orchestration_id must be null or a string");
|
|
56
|
+
}
|
|
57
|
+
if (!(state.timestamp === null || typeof state.timestamp === "string")) {
|
|
58
|
+
throw new Error("maestro_state.timestamp must be null or a string");
|
|
59
|
+
}
|
|
60
|
+
if (!validGlobalStatuses.has(state.global_status)) {
|
|
61
|
+
throw new Error(`Unsupported maestro_state.global_status: ${state.global_status}`);
|
|
62
|
+
}
|
|
63
|
+
if (!state.configuration || typeof state.configuration !== "object") {
|
|
64
|
+
throw new Error("maestro_state.configuration must be an object");
|
|
65
|
+
}
|
|
66
|
+
if (!state.dependency_graph || typeof state.dependency_graph !== "object") {
|
|
67
|
+
throw new Error("maestro_state.dependency_graph must be an object");
|
|
68
|
+
}
|
|
69
|
+
if (!state.runtime_tracks || typeof state.runtime_tracks !== "object") {
|
|
70
|
+
throw new Error("maestro_state.runtime_tracks must be an object");
|
|
71
|
+
}
|
|
72
|
+
if (!state.downstream_lock_table || typeof state.downstream_lock_table !== "object") {
|
|
73
|
+
throw new Error("maestro_state.downstream_lock_table must be an object");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return state;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function seedMaestroStateArtifacts() {
|
|
80
|
+
await writeFileIfMissing(
|
|
81
|
+
getMaestroStatePath(),
|
|
82
|
+
`${JSON.stringify(createDefaultMaestroState(), null, 2)}\n`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function loadMaestroState() {
|
|
87
|
+
return validateMaestroState(await readJson(getMaestroStatePath()));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function loadMaestroStateOrDefault() {
|
|
91
|
+
try {
|
|
92
|
+
return await loadMaestroState();
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
95
|
+
return createDefaultMaestroState();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function saveMaestroState(state) {
|
|
103
|
+
validateMaestroState(state);
|
|
104
|
+
await writeJson(getMaestroStatePath(), state);
|
|
105
|
+
return state;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function beginMaestroOrchestration(state, { globalStatus, configuration = null } = {}) {
|
|
109
|
+
const next = structuredClone(state);
|
|
110
|
+
if (
|
|
111
|
+
next.orchestration_id === null ||
|
|
112
|
+
["IDLE", "COMPLETED", "LOCKED"].includes(next.global_status)
|
|
113
|
+
) {
|
|
114
|
+
next.orchestration_id = `ma_run_${Date.now()}_${randomUUID().slice(0, 8)}`;
|
|
115
|
+
}
|
|
116
|
+
next.timestamp = new Date().toISOString();
|
|
117
|
+
next.global_status = globalStatus;
|
|
118
|
+
if (configuration) {
|
|
119
|
+
next.configuration = {
|
|
120
|
+
...next.configuration,
|
|
121
|
+
...configuration,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return next;
|
|
125
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readJson } from "../fs-utils.js";
|
|
3
|
+
import {
|
|
4
|
+
getMcpLocalCapabilitiesPath,
|
|
5
|
+
getMcpServersPath,
|
|
6
|
+
getRuntimeMcpPolicyPath,
|
|
7
|
+
} from "../paths.js";
|
|
8
|
+
|
|
9
|
+
const allowedExecutionModes = new Set(["READ_ONLY", "SANDBOX_MUTABLE"]);
|
|
10
|
+
const hardenedProfiles = new Set(["project", "deep", "hardened"]);
|
|
11
|
+
|
|
12
|
+
function createFinding({ sourcePath, severity = "high", diagnostic, recommendedAction }) {
|
|
13
|
+
return {
|
|
14
|
+
record_type: "policy:mcp_validation",
|
|
15
|
+
severity,
|
|
16
|
+
confidence: "high",
|
|
17
|
+
source_path: sourcePath,
|
|
18
|
+
diagnostic,
|
|
19
|
+
recommended_action: recommendedAction,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isSafeRelativePath(value) {
|
|
24
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (path.isAbsolute(value)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const normalized = path.normalize(value);
|
|
31
|
+
return normalized !== ".." && !normalized.startsWith(`..${path.sep}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeArray(value) {
|
|
35
|
+
return Array.isArray(value) ? value : [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function validateRuntimeMcpPolicy({
|
|
39
|
+
policy,
|
|
40
|
+
localCapabilities,
|
|
41
|
+
servers,
|
|
42
|
+
activeProfile = "project",
|
|
43
|
+
sourcePath = ".ma/.mcp.json",
|
|
44
|
+
} = {}) {
|
|
45
|
+
const findings = [];
|
|
46
|
+
const supportedCapabilities = new Set(
|
|
47
|
+
normalizeArray(localCapabilities?.capabilities).map((descriptor) => descriptor.capability),
|
|
48
|
+
);
|
|
49
|
+
const approvedEndpoints = new Set(
|
|
50
|
+
normalizeArray(servers?.servers)
|
|
51
|
+
.map((server) => server.endpoint)
|
|
52
|
+
.filter(Boolean),
|
|
53
|
+
);
|
|
54
|
+
const addFinding = (finding) => findings.push(createFinding({ sourcePath, ...finding }));
|
|
55
|
+
|
|
56
|
+
if (!policy || typeof policy !== "object" || Array.isArray(policy)) {
|
|
57
|
+
addFinding({
|
|
58
|
+
diagnostic: "Runtime-local MCP policy must be a JSON object.",
|
|
59
|
+
recommendedAction: "Replace .ma/.mcp.json with an object that narrows repo-owned MCP access.",
|
|
60
|
+
});
|
|
61
|
+
return findings;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (policy.schemaVersion !== undefined && policy.schemaVersion !== "0.1.0") {
|
|
65
|
+
addFinding({
|
|
66
|
+
severity: "warning",
|
|
67
|
+
diagnostic: `Unsupported runtime-local MCP policy schemaVersion: ${policy.schemaVersion}.`,
|
|
68
|
+
recommendedAction:
|
|
69
|
+
"Use schemaVersion 0.1.0 or omit schemaVersion until the contract changes.",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const policyMode = policy.executionMode ?? policy.execution_mode ?? "READ_ONLY";
|
|
74
|
+
if (!allowedExecutionModes.has(policyMode)) {
|
|
75
|
+
addFinding({
|
|
76
|
+
diagnostic: `Runtime-local MCP policy requests unsupported execution mode '${policyMode}'.`,
|
|
77
|
+
recommendedAction:
|
|
78
|
+
"Use READ_ONLY, or SANDBOX_MUTABLE only for explicitly permitted non-hardened runs.",
|
|
79
|
+
});
|
|
80
|
+
} else if (hardenedProfiles.has(activeProfile) && policyMode !== "READ_ONLY") {
|
|
81
|
+
addFinding({
|
|
82
|
+
diagnostic: `Runtime-local MCP policy cannot use ${policyMode} while profile '${activeProfile}' is hardened.`,
|
|
83
|
+
recommendedAction:
|
|
84
|
+
"Switch the runtime-local policy to READ_ONLY for project/deep/hardened profiles.",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const capability of normalizeArray(policy.capabilities)) {
|
|
89
|
+
const capabilityName = capability.capability ?? capability.name;
|
|
90
|
+
if (!supportedCapabilities.has(capabilityName)) {
|
|
91
|
+
addFinding({
|
|
92
|
+
diagnostic: `Runtime-local MCP policy references unsupported capability '${capabilityName ?? "(missing)"}'.`,
|
|
93
|
+
recommendedAction:
|
|
94
|
+
"Keep runtime-local capability entries as a subset of mcp/local-capabilities.json.",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const modulePath = capability.module;
|
|
99
|
+
if (modulePath !== undefined && !isSafeRelativePath(modulePath)) {
|
|
100
|
+
addFinding({
|
|
101
|
+
diagnostic: `Runtime-local MCP policy capability '${capabilityName ?? "(missing)"}' uses unsafe module path '${modulePath}'.`,
|
|
102
|
+
recommendedAction:
|
|
103
|
+
"Use safe repo-relative paths only; reject absolute paths and traversal.",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const capabilityMode = capability.executionMode ?? capability.execution_mode;
|
|
108
|
+
if (capabilityMode !== undefined && !allowedExecutionModes.has(capabilityMode)) {
|
|
109
|
+
addFinding({
|
|
110
|
+
diagnostic: `Runtime-local MCP policy capability '${capabilityName ?? "(missing)"}' requests unsupported execution mode '${capabilityMode}'.`,
|
|
111
|
+
recommendedAction:
|
|
112
|
+
"Use READ_ONLY, or SANDBOX_MUTABLE only where the active profile allows it.",
|
|
113
|
+
});
|
|
114
|
+
} else if (
|
|
115
|
+
capabilityMode !== undefined &&
|
|
116
|
+
hardenedProfiles.has(activeProfile) &&
|
|
117
|
+
capabilityMode !== "READ_ONLY"
|
|
118
|
+
) {
|
|
119
|
+
addFinding({
|
|
120
|
+
diagnostic: `Runtime-local MCP policy capability '${capabilityName ?? "(missing)"}' cannot use ${capabilityMode} while profile '${activeProfile}' is hardened.`,
|
|
121
|
+
recommendedAction: "Switch capability execution to READ_ONLY for hardened profiles.",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (const allowedPath of normalizeArray(policy.allowedPaths ?? policy.allowed_paths)) {
|
|
127
|
+
if (!isSafeRelativePath(allowedPath)) {
|
|
128
|
+
addFinding({
|
|
129
|
+
diagnostic: `Runtime-local MCP policy contains unsafe allowed path '${allowedPath}'.`,
|
|
130
|
+
recommendedAction: "Use safe repo-relative allowed paths only.",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const endpoint of normalizeArray(policy.evidenceSources ?? policy.evidence_sources)) {
|
|
136
|
+
if (!approvedEndpoints.has(endpoint)) {
|
|
137
|
+
addFinding({
|
|
138
|
+
diagnostic: `Runtime-local MCP policy references unapproved evidence source '${endpoint}'.`,
|
|
139
|
+
recommendedAction:
|
|
140
|
+
"Keep runtime-local evidence sources as a subset of mcp/servers.json exact GitMCP endpoints.",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const envAllowlist =
|
|
146
|
+
policy.envAllowlist ?? policy.env_allowlist ?? policy.env?.allowlist ?? policy.env?.allow;
|
|
147
|
+
if (envAllowlist !== undefined) {
|
|
148
|
+
if (!Array.isArray(envAllowlist)) {
|
|
149
|
+
addFinding({
|
|
150
|
+
diagnostic: "Runtime-local MCP policy env allowlist must be an array.",
|
|
151
|
+
recommendedAction: "Use an explicit array of environment variable names.",
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
for (const entry of envAllowlist) {
|
|
155
|
+
if (
|
|
156
|
+
typeof entry !== "string" ||
|
|
157
|
+
entry.trim() === "" ||
|
|
158
|
+
entry === "*" ||
|
|
159
|
+
entry.includes("*")
|
|
160
|
+
) {
|
|
161
|
+
addFinding({
|
|
162
|
+
diagnostic: `Runtime-local MCP policy env allowlist contains unsafe entry '${entry}'.`,
|
|
163
|
+
recommendedAction:
|
|
164
|
+
"List exact environment variable names only; wildcard env access is not allowed.",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return findings;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function validateRuntimeMcpPolicyFile({ activeProfile = "project" } = {}) {
|
|
175
|
+
let policy;
|
|
176
|
+
try {
|
|
177
|
+
policy = await readJson(getRuntimeMcpPolicyPath());
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return validateRuntimeMcpPolicy({
|
|
186
|
+
policy,
|
|
187
|
+
localCapabilities: await readJson(getMcpLocalCapabilitiesPath()),
|
|
188
|
+
servers: await readJson(getMcpServersPath()),
|
|
189
|
+
activeProfile,
|
|
190
|
+
sourcePath: getRuntimeMcpPolicyPath(),
|
|
191
|
+
});
|
|
192
|
+
}
|