@jstn-sdk/ma 0.1.11 → 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/.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 +287 -37
- package/bin/ma.js +232 -71
- 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 +7 -1
- package/docs/getting-started.md +108 -37
- package/docs/installed-sdk.md +23 -0
- package/docs/mcp-setup.md +65 -1
- 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.13.md +116 -0
- package/docs/qa/release-readiness-0.1.5.md +1 -1
- package/docs/reference/native-engineering-patterns.md +35 -0
- package/docs/reference/native-security-playbooks.md +27 -0
- package/docs/reference/native-source-selection.md +27 -0
- package/docs/reference/native-style-and-deslop.md +20 -0
- package/docs/release-spec.md +37 -10
- package/docs/skills-publishing.md +25 -1
- package/docs/skills.md +37 -12
- package/index.js +222 -2
- package/mcp/collections.json +23 -6
- package/mcp/local/code-intel.js +113 -0
- package/mcp/local/memory.js +46 -0
- package/mcp/local/playbooks.js +168 -0
- package/mcp/local/state.js +71 -0
- package/mcp/local/team-run.js +113 -0
- package/mcp/local/trace.js +60 -0
- package/mcp/local-capabilities.json +56 -0
- package/mcp/native-playbooks.json +117 -0
- package/mcp/servers.json +34 -0
- package/package.json +8 -2
- package/plugins/meta-architect/.app.json +1 -1
- package/plugins/meta-architect/.codex-plugin/plugin.json +4 -4
- package/plugins/meta-architect/.mcp.json +1 -1
- package/plugins/meta-architect/README.md +34 -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 +24 -0
- package/plugins/meta-architect/skills/align/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/align/references/shared-language.md +24 -0
- package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
- package/plugins/meta-architect/skills/build/SKILL.md +3 -0
- package/plugins/meta-architect/skills/cleanup/SKILL.md +23 -0
- package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +18 -0
- package/plugins/meta-architect/skills/diagnose/SKILL.md +24 -0
- package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
- package/plugins/meta-architect/skills/maestro/SKILL.md +36 -3
- package/plugins/meta-architect/skills/maestro/agents/openai.yaml +2 -2
- package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +44 -0
- package/plugins/meta-architect/skills/sage/SKILL.md +5 -1
- package/plugins/meta-architect/skills/sage/references/source-selection.md +36 -0
- package/plugins/meta-architect/skills/tdd/SKILL.md +24 -0
- package/plugins/meta-architect/skills/tdd/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/vet/SKILL.md +5 -1
- package/plugins/meta-architect/skills/vet/references/security-playbooks.md +30 -0
- package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
- package/scripts/active-autonomy-hook.mjs +96 -0
- package/scripts/build-linux-packages.mjs +328 -0
- package/scripts/doctor.js +36 -4
- package/scripts/install.sh +28 -0
- package/scripts/linux-package-lib.mjs +40 -0
- package/scripts/linux-package-smoke.mjs +103 -0
- package/scripts/ralph/prompt.md +35 -0
- package/scripts/release-sync.js +13 -6
- package/scripts/release-verify.js +166 -1
- package/skills/align/SKILL.md +24 -0
- package/skills/align/agents/openai.yaml +4 -0
- package/skills/align/references/shared-language.md +24 -0
- package/skills/arch/SKILL.md +2 -0
- package/skills/build/SKILL.md +3 -0
- package/skills/cleanup/SKILL.md +23 -0
- package/skills/cleanup/agents/openai.yaml +4 -0
- package/skills/cleanup/references/style-and-deslop.md +18 -0
- package/skills/diagnose/SKILL.md +24 -0
- package/skills/diagnose/agents/openai.yaml +4 -0
- package/skills/flow/SKILL.md +2 -0
- package/skills/index.json +21 -6
- package/skills/maestro/SKILL.md +36 -3
- package/skills/maestro/agents/openai.yaml +2 -2
- package/skills/maestro/references/native-ingest-map.md +44 -0
- package/skills/sage/SKILL.md +5 -1
- package/skills/sage/references/source-selection.md +36 -0
- package/skills/tdd/SKILL.md +24 -0
- package/skills/tdd/agents/openai.yaml +4 -0
- package/skills/vet/SKILL.md +5 -1
- package/skills/vet/references/security-playbooks.md +30 -0
- package/skills/vibe/SKILL.md +2 -0
- package/src/bootstrap.js +141 -24
- package/src/build-gate.js +2 -2
- package/src/decision-log.js +12 -9
- package/src/launcher.js +4 -0
- package/src/mcp-config.js +130 -8
- package/src/mcp-live-client.js +289 -3
- package/src/paths.js +37 -1
- package/src/policy.js +1 -1
- package/src/release-issue-gates.js +190 -0
- package/src/release-state.js +30 -1
- 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 +62 -0
- package/src/runtime/code-graph-rehearse.js +113 -0
- package/src/runtime/context-economy-core.js +276 -0
- package/src/runtime/continuity-notes.js +79 -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 +42 -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 +605 -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 +158 -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 +1223 -0
- package/src/runtime/semantic-recording-core.js +147 -0
- package/src/runtime/signal-hooks.js +67 -0
- package/src/runtime/skills-registry-export.js +670 -0
- package/src/runtime/startup-path.js +14 -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 +25 -0
- package/src/runtime-artifacts.js +407 -84
- package/src/skill-installer.js +53 -5
- package/src/skills.js +1454 -78
- package/src/state-sync.js +51 -8
- package/docs/qa/release-readiness-0.1.11.md +0 -79
- package/plugins/meta-architect/skills/meta-architect/SKILL.md +0 -32
- package/plugins/meta-architect/skills/meta-architect/agents/openai.yaml +0 -4
- package/skills/meta-architect/SKILL.md +0 -32
- package/skills/meta-architect/agents/openai.yaml +0 -4
- /package/plugins/meta-architect/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
- /package/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const ALLOWED_STATUSES = new Set(["pending", "in_progress", "blocked", "failed", "passed"]);
|
|
2
|
+
const REQUIRED_PROOF_FIELDS = [
|
|
3
|
+
"implementationEvidence",
|
|
4
|
+
"verificationEvidence",
|
|
5
|
+
"productionEvidence",
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
function isNonEmptyString(value) {
|
|
9
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hasStringItems(value) {
|
|
13
|
+
return Array.isArray(value) && value.length > 0 && value.every(isNonEmptyString);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function validateReleaseIssueGates(document, options = {}) {
|
|
17
|
+
const version = options.version;
|
|
18
|
+
const requirePassed = options.requirePassed ?? true;
|
|
19
|
+
const errors = [];
|
|
20
|
+
|
|
21
|
+
if (!document || typeof document !== "object" || Array.isArray(document)) {
|
|
22
|
+
return {
|
|
23
|
+
valid: false,
|
|
24
|
+
errors: ["release issue gate document must be an object"],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const expectedTag = version ? `v${version}` : document.releaseTag;
|
|
29
|
+
|
|
30
|
+
if (document.schemaVersion !== "1.0.0") {
|
|
31
|
+
errors.push("schemaVersion must be 1.0.0");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (version && document.releaseVersion !== version) {
|
|
35
|
+
errors.push(`releaseVersion must match package version ${version}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!isNonEmptyString(document.releaseTag)) {
|
|
39
|
+
errors.push("releaseTag is required");
|
|
40
|
+
} else if (expectedTag && document.releaseTag !== expectedTag) {
|
|
41
|
+
errors.push(`releaseTag must be ${expectedTag}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (document.passContract?.allIssuesMustPassProduction !== true) {
|
|
45
|
+
errors.push("passContract.allIssuesMustPassProduction must be true");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (document.passContract?.allIssuesMustHaveLabels !== true) {
|
|
49
|
+
errors.push("passContract.allIssuesMustHaveLabels must be true");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!Array.isArray(document.issues) || document.issues.length === 0) {
|
|
53
|
+
errors.push("issues must be a non-empty array");
|
|
54
|
+
return { valid: errors.length === 0, errors };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
for (const issue of document.issues) {
|
|
59
|
+
const issueLabel = Number.isInteger(issue?.number) ? `#${issue.number}` : "<unknown issue>";
|
|
60
|
+
|
|
61
|
+
if (!Number.isInteger(issue?.number) || issue.number <= 0) {
|
|
62
|
+
errors.push(`${issueLabel}: number must be a positive integer`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (seen.has(issue.number)) {
|
|
67
|
+
errors.push(`${issueLabel}: duplicate issue number`);
|
|
68
|
+
}
|
|
69
|
+
seen.add(issue.number);
|
|
70
|
+
|
|
71
|
+
if (!isNonEmptyString(issue.title)) {
|
|
72
|
+
errors.push(`${issueLabel}: title is required`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!isNonEmptyString(issue.url) || !issue.url.includes(`/issues/${issue.number}`)) {
|
|
76
|
+
errors.push(`${issueLabel}: url must reference the GitHub issue`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (issue.releaseVersion !== document.releaseVersion) {
|
|
80
|
+
errors.push(`${issueLabel}: releaseVersion must match document releaseVersion`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (issue.releaseTag !== document.releaseTag) {
|
|
84
|
+
errors.push(`${issueLabel}: releaseTag must match document releaseTag`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (issue.milestone !== document.releaseTag) {
|
|
88
|
+
errors.push(`${issueLabel}: milestone must match document releaseTag`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!ALLOWED_STATUSES.has(issue.status)) {
|
|
92
|
+
errors.push(`${issueLabel}: invalid status ${issue.status}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (document.passContract?.allIssuesMustHaveLabels === true && !hasStringItems(issue.labels)) {
|
|
96
|
+
errors.push(`${issueLabel}: labels must contain at least one issue label`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!hasStringItems(issue.requiredProof)) {
|
|
100
|
+
errors.push(`${issueLabel}: requiredProof must contain at least one proof requirement`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!isNonEmptyString(issue.loopAction)) {
|
|
104
|
+
errors.push(`${issueLabel}: loopAction is required until production proof passes`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const proof = issue.proof;
|
|
108
|
+
if (!proof || typeof proof !== "object" || Array.isArray(proof)) {
|
|
109
|
+
errors.push(`${issueLabel}: proof object is required`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const field of REQUIRED_PROOF_FIELDS) {
|
|
114
|
+
if (!Array.isArray(proof[field])) {
|
|
115
|
+
errors.push(`${issueLabel}: proof.${field} must be an array`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (issue.status === "passed") {
|
|
120
|
+
for (const field of REQUIRED_PROOF_FIELDS) {
|
|
121
|
+
if (!hasStringItems(proof[field])) {
|
|
122
|
+
errors.push(`${issueLabel}: status passed requires non-empty proof.${field}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (requirePassed && issue.status !== "passed") {
|
|
128
|
+
errors.push(`${issueLabel}: status is ${issue.status}; continue loopAction before release`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
valid: errors.length === 0,
|
|
134
|
+
errors,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function summarizeReleaseIssueGateStatus(document, options = {}) {
|
|
139
|
+
const structural = validateReleaseIssueGates(document, {
|
|
140
|
+
...options,
|
|
141
|
+
requirePassed: false,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!structural.valid) {
|
|
145
|
+
return {
|
|
146
|
+
valid: false,
|
|
147
|
+
total: 0,
|
|
148
|
+
passed: 0,
|
|
149
|
+
pending: 0,
|
|
150
|
+
inProgress: 0,
|
|
151
|
+
blocked: 0,
|
|
152
|
+
failed: 0,
|
|
153
|
+
blockers: structural.errors,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const summary = {
|
|
158
|
+
valid: true,
|
|
159
|
+
total: document.issues.length,
|
|
160
|
+
passed: 0,
|
|
161
|
+
pending: 0,
|
|
162
|
+
inProgress: 0,
|
|
163
|
+
blocked: 0,
|
|
164
|
+
failed: 0,
|
|
165
|
+
blockers: [],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
for (const issue of document.issues) {
|
|
169
|
+
if (issue.status === "passed") {
|
|
170
|
+
summary.passed += 1;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (issue.status === "pending") {
|
|
175
|
+
summary.pending += 1;
|
|
176
|
+
} else if (issue.status === "in_progress") {
|
|
177
|
+
summary.inProgress += 1;
|
|
178
|
+
} else if (issue.status === "blocked") {
|
|
179
|
+
summary.blocked += 1;
|
|
180
|
+
} else if (issue.status === "failed") {
|
|
181
|
+
summary.failed += 1;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
summary.blockers.push(
|
|
185
|
+
`#${issue.number}: ${issue.status}; ${issue.loopAction || "continue implementation loop"}`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return summary;
|
|
190
|
+
}
|
package/src/release-state.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import { writeJson } from "./fs-utils.js";
|
|
3
3
|
import { getRuntimeReadPath, getRuntimeWritePath } from "./paths.js";
|
|
4
4
|
|
|
5
|
-
const allowedStatuses = {
|
|
5
|
+
export const allowedStatuses = {
|
|
6
6
|
idea_status: ["DRAFT", "CLEAR", "BLOCKED"],
|
|
7
7
|
architecture_status: ["DRAFT", "REVIEWED", "APPROVED"],
|
|
8
8
|
evidence_status: ["MISSING", "PARTIAL", "VERIFIED"],
|
|
@@ -27,6 +27,23 @@ function validateWaiver(state) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export function createDefaultReleaseState() {
|
|
31
|
+
return {
|
|
32
|
+
schemaVersion: "0.1.0",
|
|
33
|
+
idea_status: "DRAFT",
|
|
34
|
+
architecture_status: "DRAFT",
|
|
35
|
+
evidence_status: "MISSING",
|
|
36
|
+
logic_status: "PENDING",
|
|
37
|
+
security_status: "PENDING",
|
|
38
|
+
experience_status: "PENDING",
|
|
39
|
+
build_status: "LOCKED",
|
|
40
|
+
merge_status: "LOCKED",
|
|
41
|
+
release_status: "LOCKED",
|
|
42
|
+
waiver: null,
|
|
43
|
+
updatedAt: null,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
30
47
|
export function validateReleaseState(state) {
|
|
31
48
|
if (!state || typeof state !== "object" || Array.isArray(state)) {
|
|
32
49
|
throw new Error("Release state must be an object");
|
|
@@ -48,6 +65,18 @@ export async function loadReleaseState() {
|
|
|
48
65
|
return validateReleaseState(parsed);
|
|
49
66
|
}
|
|
50
67
|
|
|
68
|
+
export async function loadReleaseStateOrDefault() {
|
|
69
|
+
try {
|
|
70
|
+
return await loadReleaseState();
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
73
|
+
return createDefaultReleaseState();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
51
80
|
export async function saveReleaseState(state) {
|
|
52
81
|
validateReleaseState(state);
|
|
53
82
|
const next = {
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { ensureDir, readJson, writeFileIfMissing } from "../fs-utils.js";
|
|
2
|
+
import { getRuntimeSubsystemPath } from "../paths.js";
|
|
3
|
+
|
|
4
|
+
export const activeAutonomyCoreSchemaVersion = "0.1.0";
|
|
5
|
+
|
|
6
|
+
export function getActiveAutonomyCorePath() {
|
|
7
|
+
return getRuntimeSubsystemPath("context", "active-autonomy-core.json");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createDefaultActiveAutonomyCore() {
|
|
11
|
+
return {
|
|
12
|
+
schemaVersion: activeAutonomyCoreSchemaVersion,
|
|
13
|
+
product: "Meta-Architect",
|
|
14
|
+
purpose:
|
|
15
|
+
"Canonical MA contract that prevents passive chatbot behavior across prompts, skills, runtime hooks, and contributor rules.",
|
|
16
|
+
autonomy_directive: {
|
|
17
|
+
placement: "top_of_workspace_contract_before_other_guidance",
|
|
18
|
+
text: [
|
|
19
|
+
"YOU ARE AN AUTONOMOUS META-ARCHITECT WORKSPACE AGENT.",
|
|
20
|
+
"EXECUTE CLEAR, SAFE, REVERSIBLE, ALREADY-REQUESTED WORK TO VERIFIED COMPLETION.",
|
|
21
|
+
"DO NOT ASK WHETHER TO PROCEED ON AUTO-CONTINUE BRANCHES.",
|
|
22
|
+
"ASK ONLY FOR DESTRUCTIVE, IRREVERSIBLE, CREDENTIAL-GATED, EXTERNAL-PRODUCTION, OR MATERIALLY SCOPE-CHANGING ACTIONS.",
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
decision_rule: {
|
|
26
|
+
modes: ["AUTO-CONTINUE", "ASK"],
|
|
27
|
+
auto_continue_when: [
|
|
28
|
+
"clear_user_requested_work",
|
|
29
|
+
"local_or_workspace_scoped",
|
|
30
|
+
"low_risk",
|
|
31
|
+
"reversible",
|
|
32
|
+
"non_destructive",
|
|
33
|
+
"verification_path_available",
|
|
34
|
+
],
|
|
35
|
+
ask_only_when: [
|
|
36
|
+
"destructive_action",
|
|
37
|
+
"irreversible_action",
|
|
38
|
+
"credential_gated_action",
|
|
39
|
+
"external_production_side_effect",
|
|
40
|
+
"material_scope_change",
|
|
41
|
+
"missing_authority_blocks_progress",
|
|
42
|
+
],
|
|
43
|
+
banned_auto_continue_phrasing: [
|
|
44
|
+
"Should I proceed?",
|
|
45
|
+
"Would you like me to continue?",
|
|
46
|
+
"If you want, I can",
|
|
47
|
+
"If you'd like, I can",
|
|
48
|
+
"Tell me if you want me to",
|
|
49
|
+
"I can continue if you want",
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
completion_loop_contract: {
|
|
53
|
+
required_for: [
|
|
54
|
+
"$maestro",
|
|
55
|
+
"$build",
|
|
56
|
+
"ralph_execution_core",
|
|
57
|
+
"executor_roles",
|
|
58
|
+
"team_workers",
|
|
59
|
+
],
|
|
60
|
+
stop_conditions: [
|
|
61
|
+
"verified_complete",
|
|
62
|
+
"user_cancelled",
|
|
63
|
+
"closed_ask_condition_requires_user_input",
|
|
64
|
+
"same_blocker_repeated_three_times_with_no_safe_alternative",
|
|
65
|
+
],
|
|
66
|
+
completion_requires: [
|
|
67
|
+
"fresh_verification_evidence",
|
|
68
|
+
"explicit_outcome",
|
|
69
|
+
"changed_artifacts_or_no_change_reason",
|
|
70
|
+
"known_gaps_or_none",
|
|
71
|
+
],
|
|
72
|
+
terminal_outcomes: ["finished", "blocked", "failed", "cancelled", "askuserQuestion"],
|
|
73
|
+
},
|
|
74
|
+
runtime_enforcement: {
|
|
75
|
+
hook_layer: "active_autonomy_runtime",
|
|
76
|
+
stall_patterns: [
|
|
77
|
+
"should i proceed",
|
|
78
|
+
"would you like me to continue",
|
|
79
|
+
"if you want, i can",
|
|
80
|
+
"if you'd like, i can",
|
|
81
|
+
"tell me if you want me to",
|
|
82
|
+
"i can continue if you want",
|
|
83
|
+
"let me know if you want me to continue",
|
|
84
|
+
],
|
|
85
|
+
default_response:
|
|
86
|
+
"AUTO-CONTINUE: continue the current safe branch, execute the next reversible step, then verify before reporting.",
|
|
87
|
+
native_stop_policy: "block_passive_permission_handoff_when_auto_continue_applies",
|
|
88
|
+
},
|
|
89
|
+
contributor_contract: {
|
|
90
|
+
required_surfaces: [
|
|
91
|
+
"workspace_contract",
|
|
92
|
+
"role_prompts",
|
|
93
|
+
"skill_contracts",
|
|
94
|
+
"runtime_hook_policy",
|
|
95
|
+
"docs",
|
|
96
|
+
"tests",
|
|
97
|
+
],
|
|
98
|
+
regression_targets: [
|
|
99
|
+
"closed_ask_list_present",
|
|
100
|
+
"auto_continue_mode_present",
|
|
101
|
+
"permission_handoff_banned",
|
|
102
|
+
"completion_requires_fresh_evidence",
|
|
103
|
+
"terminal_outcome_required",
|
|
104
|
+
"hook_stall_patterns_present",
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
hard_rules: [
|
|
108
|
+
"Prompt instructions alone are insufficient; runtime hook policy must backstop passive handoffs.",
|
|
109
|
+
"AUTO-CONTINUE branches must state next action or evidence-backed result, not request permission.",
|
|
110
|
+
"ASK branches must name the exact closed-list reason that requires the user.",
|
|
111
|
+
"Completion claims require fresh verification evidence or an explicit validation gap.",
|
|
112
|
+
"Contributor edits must preserve anti-passive prompt patterns and hook policy.",
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function validateActiveAutonomyCore(value) {
|
|
118
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
119
|
+
throw new Error("active autonomy core must be an object");
|
|
120
|
+
}
|
|
121
|
+
if (value.schemaVersion !== activeAutonomyCoreSchemaVersion) {
|
|
122
|
+
throw new Error(`Unsupported active autonomy core schemaVersion: ${value.schemaVersion}`);
|
|
123
|
+
}
|
|
124
|
+
if (!value.decision_rule || typeof value.decision_rule !== "object") {
|
|
125
|
+
throw new Error("active autonomy core requires decision_rule");
|
|
126
|
+
}
|
|
127
|
+
if (!value.decision_rule.modes?.includes("AUTO-CONTINUE")) {
|
|
128
|
+
throw new Error("active autonomy core requires AUTO-CONTINUE mode");
|
|
129
|
+
}
|
|
130
|
+
if (!value.decision_rule.modes?.includes("ASK")) {
|
|
131
|
+
throw new Error("active autonomy core requires ASK mode");
|
|
132
|
+
}
|
|
133
|
+
if (
|
|
134
|
+
!Array.isArray(value.decision_rule.ask_only_when) ||
|
|
135
|
+
value.decision_rule.ask_only_when.length === 0
|
|
136
|
+
) {
|
|
137
|
+
throw new Error("active autonomy core requires closed ask_only_when list");
|
|
138
|
+
}
|
|
139
|
+
if (!value.runtime_enforcement?.stall_patterns?.includes("should i proceed")) {
|
|
140
|
+
throw new Error("active autonomy core requires passive stall pattern coverage");
|
|
141
|
+
}
|
|
142
|
+
if (
|
|
143
|
+
!value.completion_loop_contract?.completion_requires?.includes("fresh_verification_evidence")
|
|
144
|
+
) {
|
|
145
|
+
throw new Error("active autonomy core requires fresh verification evidence");
|
|
146
|
+
}
|
|
147
|
+
if (JSON.stringify(value).includes(".omx")) {
|
|
148
|
+
throw new Error("active autonomy core must not expose OMX runtime paths");
|
|
149
|
+
}
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function classifyAutonomyBranch({ core, requested = true, riskSignals = [] }) {
|
|
154
|
+
const policy = validateActiveAutonomyCore(core);
|
|
155
|
+
const signals = new Set(riskSignals.map((signal) => `${signal}`.toLowerCase()));
|
|
156
|
+
const askReason = policy.decision_rule.ask_only_when.find((reason) =>
|
|
157
|
+
signals.has(reason.toLowerCase()),
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (!requested) {
|
|
161
|
+
return {
|
|
162
|
+
mode: "ASK",
|
|
163
|
+
reason: "missing_authority_blocks_progress",
|
|
164
|
+
permission_handoff_allowed: true,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (askReason) {
|
|
169
|
+
return {
|
|
170
|
+
mode: "ASK",
|
|
171
|
+
reason: askReason,
|
|
172
|
+
permission_handoff_allowed: true,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
mode: "AUTO-CONTINUE",
|
|
178
|
+
reason: "clear_safe_reversible_requested_work",
|
|
179
|
+
permission_handoff_allowed: false,
|
|
180
|
+
next_action_contract:
|
|
181
|
+
"state the next safe action or evidence-backed result; do not ask whether to proceed",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function detectPassivePermissionHandoff(text, core = createDefaultActiveAutonomyCore()) {
|
|
186
|
+
const policy = validateActiveAutonomyCore(core);
|
|
187
|
+
const normalized = `${text}`.toLowerCase();
|
|
188
|
+
return policy.decision_rule.banned_auto_continue_phrasing.some((phrase) =>
|
|
189
|
+
normalized.includes(
|
|
190
|
+
phrase
|
|
191
|
+
.toLowerCase()
|
|
192
|
+
.replace(/\.\.\.$/, "")
|
|
193
|
+
.trim(),
|
|
194
|
+
),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export async function seedActiveAutonomyCoreArtifacts() {
|
|
199
|
+
await ensureDir(getRuntimeSubsystemPath("context"));
|
|
200
|
+
await writeFileIfMissing(
|
|
201
|
+
getActiveAutonomyCorePath(),
|
|
202
|
+
`${JSON.stringify(createDefaultActiveAutonomyCore(), null, 2)}\n`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function loadActiveAutonomyCore() {
|
|
207
|
+
return validateActiveAutonomyCore(await readJson(getActiveAutonomyCorePath()));
|
|
208
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
|
|
2
|
+
import { getRuntimeStatePath } from "../paths.js";
|
|
3
|
+
|
|
4
|
+
const driftRuleDefinitions = [
|
|
5
|
+
{
|
|
6
|
+
skill: "$arch",
|
|
7
|
+
expectedDecisionStatus: "APPROVED",
|
|
8
|
+
releaseField: "architecture_status",
|
|
9
|
+
allowedReleaseValues: ["APPROVED"],
|
|
10
|
+
findingCode: "architecture_status_regressed",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
skill: "$sage",
|
|
14
|
+
expectedDecisionStatus: "VERIFIED",
|
|
15
|
+
releaseField: "evidence_status",
|
|
16
|
+
allowedReleaseValues: ["VERIFIED"],
|
|
17
|
+
findingCode: "evidence_status_regressed",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
skill: "$flow",
|
|
21
|
+
expectedDecisionStatus: "GREEN",
|
|
22
|
+
releaseField: "logic_status",
|
|
23
|
+
allowedReleaseValues: ["GREEN"],
|
|
24
|
+
findingCode: "logic_status_regressed",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
skill: "$vet",
|
|
28
|
+
expectedDecisionStatus: "GREEN",
|
|
29
|
+
releaseField: "security_status",
|
|
30
|
+
allowedReleaseValues: ["GREEN"],
|
|
31
|
+
findingCode: "security_status_regressed",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
skill: "$vibe",
|
|
35
|
+
expectedDecisionStatus: "GREEN",
|
|
36
|
+
releaseField: "experience_status",
|
|
37
|
+
allowedReleaseValues: ["GREEN", "WAIVED"],
|
|
38
|
+
findingCode: "experience_status_regressed",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
skill: "$build",
|
|
42
|
+
expectedDecisionStatus: "DONE",
|
|
43
|
+
releaseField: "build_status",
|
|
44
|
+
allowedReleaseValues: ["DONE"],
|
|
45
|
+
findingCode: "build_status_regressed",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
export function getAlignmentSentinelPath() {
|
|
50
|
+
return getRuntimeStatePath("alignment-sentinel.json");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function createDefaultAlignmentSentinelReport() {
|
|
54
|
+
return {
|
|
55
|
+
schemaVersion: "0.1.0",
|
|
56
|
+
lastRunAt: null,
|
|
57
|
+
driftStatus: "CLEAR",
|
|
58
|
+
rebootPlanned: false,
|
|
59
|
+
rebootReason: null,
|
|
60
|
+
resumeTrigger: "$maestro",
|
|
61
|
+
baselineArtifacts: [
|
|
62
|
+
".ma/release.json",
|
|
63
|
+
".ma/decisions.json",
|
|
64
|
+
".ma/state/manager-runs.json",
|
|
65
|
+
".ma/state/maestro-state.json",
|
|
66
|
+
],
|
|
67
|
+
findings: [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function seedAlignmentSentinelArtifacts() {
|
|
72
|
+
await writeFileIfMissing(
|
|
73
|
+
getAlignmentSentinelPath(),
|
|
74
|
+
`${JSON.stringify(createDefaultAlignmentSentinelReport(), null, 2)}\n`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function loadAlignmentSentinelReportOrDefault() {
|
|
79
|
+
try {
|
|
80
|
+
return await readJson(getAlignmentSentinelPath());
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
83
|
+
return createDefaultAlignmentSentinelReport();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function saveAlignmentSentinelReport(report) {
|
|
91
|
+
await writeJson(getAlignmentSentinelPath(), report);
|
|
92
|
+
return report;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getLatestDecisionBySkill(decisions = []) {
|
|
96
|
+
const latestBySkill = new Map();
|
|
97
|
+
for (const decision of decisions) {
|
|
98
|
+
if (!decision?.skill) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
latestBySkill.set(decision.skill, decision);
|
|
103
|
+
}
|
|
104
|
+
return latestBySkill;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function evaluateAlignmentDrift({ releaseState, decisionLog }) {
|
|
108
|
+
const latestDecisions = getLatestDecisionBySkill(decisionLog?.decisions ?? []);
|
|
109
|
+
const findings = [];
|
|
110
|
+
|
|
111
|
+
for (const rule of driftRuleDefinitions) {
|
|
112
|
+
const latestDecision = latestDecisions.get(rule.skill);
|
|
113
|
+
if (!latestDecision || latestDecision.status !== rule.expectedDecisionStatus) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const actualReleaseValue = releaseState?.[rule.releaseField];
|
|
118
|
+
if (rule.allowedReleaseValues.includes(actualReleaseValue)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
findings.push({
|
|
123
|
+
severity: "blocking",
|
|
124
|
+
findingCode: rule.findingCode,
|
|
125
|
+
skill: rule.skill,
|
|
126
|
+
expectedDecisionStatus: rule.expectedDecisionStatus,
|
|
127
|
+
releaseField: rule.releaseField,
|
|
128
|
+
expectedReleaseValues: rule.allowedReleaseValues,
|
|
129
|
+
actualReleaseValue,
|
|
130
|
+
decisionTimestamp: latestDecision.timestamp ?? null,
|
|
131
|
+
decisionSummary: latestDecision.decision ?? null,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
driftStatus: findings.length > 0 ? "DRIFTED" : "CLEAR",
|
|
137
|
+
rebootPlanned: findings.length > 0,
|
|
138
|
+
rebootReason:
|
|
139
|
+
findings.length > 0
|
|
140
|
+
? "Release state drifted from the latest approved lane-owned decisions."
|
|
141
|
+
: null,
|
|
142
|
+
resumeTrigger: "$maestro",
|
|
143
|
+
findings,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function createAlignmentRecoveryPlan(report) {
|
|
148
|
+
const findings = report?.findings ?? [];
|
|
149
|
+
return {
|
|
150
|
+
record_type: "alignment_recovery_plan",
|
|
151
|
+
driftStatus: report?.driftStatus ?? (findings.length > 0 ? "DRIFTED" : "CLEAR"),
|
|
152
|
+
rebootPlanned: findings.length > 0,
|
|
153
|
+
resumeTrigger: report?.resumeTrigger ?? "$maestro",
|
|
154
|
+
requiredActions: findings.map((finding) => ({
|
|
155
|
+
skill: finding.skill,
|
|
156
|
+
repairField: finding.releaseField,
|
|
157
|
+
expectedValues: finding.expectedReleaseValues,
|
|
158
|
+
actualValue: finding.actualReleaseValue,
|
|
159
|
+
action:
|
|
160
|
+
"Rehydrate from baseline runtime artifacts, then rerun the owning lane or restore lane-owned release state before worker dispatch resumes.",
|
|
161
|
+
})),
|
|
162
|
+
mayDispatchWorkers: findings.length === 0,
|
|
163
|
+
mayMutateReleaseStateDirectly: false,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
|
|
4
|
+
import { getRuntimeStatePath } from "../paths.js";
|
|
5
|
+
import { redactProviderBoundText, seedRedactionVault } from "./redaction-gateway.js";
|
|
6
|
+
|
|
7
|
+
export function getArchitectReviewPath() {
|
|
8
|
+
return getRuntimeStatePath("architect-review.json");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createDefaultArchitectReview() {
|
|
12
|
+
return {
|
|
13
|
+
schemaVersion: "0.1.0",
|
|
14
|
+
verdict: "NOT_RUN",
|
|
15
|
+
reviewer: null,
|
|
16
|
+
summary: null,
|
|
17
|
+
findings: [],
|
|
18
|
+
reviewedAt: null,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function seedArchitectReviewArtifacts() {
|
|
23
|
+
await writeFileIfMissing(
|
|
24
|
+
getArchitectReviewPath(),
|
|
25
|
+
`${JSON.stringify(createDefaultArchitectReview(), null, 2)}\n`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function loadArchitectReviewOrDefault() {
|
|
30
|
+
try {
|
|
31
|
+
return await readJson(getArchitectReviewPath());
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
34
|
+
return createDefaultArchitectReview();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function saveArchitectReview(review) {
|
|
42
|
+
await writeJson(getArchitectReviewPath(), review);
|
|
43
|
+
return review;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function runExternalArchitectReview({ prompt = "" } = {}) {
|
|
47
|
+
const command = process.env.MA_ARCHITECT_REVIEW_CMD?.trim();
|
|
48
|
+
if (!command) {
|
|
49
|
+
throw new Error("External architect review is not configured. Set MA_ARCHITECT_REVIEW_CMD.");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await seedRedactionVault();
|
|
53
|
+
const redactedPrompt = await redactProviderBoundText(prompt, {
|
|
54
|
+
kind: "architect-review-prompt",
|
|
55
|
+
});
|
|
56
|
+
const promptPath = getRuntimeStatePath("architect-review.prompt.md");
|
|
57
|
+
const outputPath = getRuntimeStatePath("architect-review.output.json");
|
|
58
|
+
await fs.writeFile(promptPath, `${redactedPrompt.sanitizedText.trimEnd()}\n`, "utf8");
|
|
59
|
+
|
|
60
|
+
const result = spawnSync("/bin/sh", ["-lc", command], {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: {
|
|
63
|
+
...process.env,
|
|
64
|
+
MA_ARCHITECT_REVIEW_PROMPT: redactedPrompt.sanitizedText,
|
|
65
|
+
MA_ARCHITECT_REVIEW_PROMPT_FILE: promptPath,
|
|
66
|
+
MA_ARCHITECT_REVIEW_OUTPUT: outputPath,
|
|
67
|
+
},
|
|
68
|
+
encoding: "utf8",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (result.status !== 0) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
result.stderr?.trim() || result.stdout?.trim() || "External architect review failed",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const parsed = JSON.parse(await fs.readFile(outputPath, "utf8"));
|
|
78
|
+
const review = {
|
|
79
|
+
schemaVersion: "0.1.0",
|
|
80
|
+
verdict: parsed.verdict ?? "UNKNOWN",
|
|
81
|
+
reviewer: parsed.reviewer ?? "external",
|
|
82
|
+
summary: parsed.summary ?? null,
|
|
83
|
+
findings: Array.isArray(parsed.findings) ? parsed.findings : [],
|
|
84
|
+
reviewedAt: new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
await saveArchitectReview(review);
|
|
87
|
+
return review;
|
|
88
|
+
}
|