@jstn-sdk/ma 0.1.12 → 0.14.0
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 +1 -2
- package/.codex/agents/Auditor.toml +1 -2
- package/.codex/agents/Builder.toml +1 -2
- package/.codex/agents/Flow.toml +1 -2
- package/.codex/agents/Sage.toml +1 -2
- package/.codex/agents/Vibe.toml +1 -2
- package/.codex/hooks.json +24 -13
- package/.codex/prompts/enforcement.md +4 -0
- package/.codex/prompts/onboarding.md +6 -1
- package/.codex/prompts/release-rules.md +0 -0
- package/.codex/prompts/skill-contract.md +4 -0
- package/COVERAGE.md +211 -0
- package/DEMO.md +274 -0
- package/LICENSE +0 -0
- package/README.md +132 -656
- package/bin/ma.js +748 -55
- 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 +10 -1
- package/docs/autonomous-tasks.md +46 -0
- package/docs/codex-integration.md +16 -0
- package/docs/getting-started.md +62 -22
- package/docs/mcp-setup.md +64 -0
- package/docs/qa/release-issue-gates-0.14.0.json +644 -0
- package/docs/qa/release-readiness-0.1.5.md +1 -1
- package/docs/qa/{release-readiness-0.1.12.md → release-readiness-0.14.0.md} +25 -9
- package/docs/quality.md +24 -0
- package/docs/reference/native-security-playbooks.md +6 -0
- package/docs/reference/native-source-selection.md +6 -0
- package/docs/release-spec.md +39 -10
- package/docs/skills.md +14 -1
- package/index.js +295 -2
- package/mcp/collections.json +23 -6
- package/mcp/fallback.json +0 -0
- package/mcp/local/code-intel.js +179 -41
- package/mcp/local/context.js +180 -0
- package/mcp/local/memory.js +17 -6
- package/mcp/local/quality.js +39 -0
- package/mcp/local/state.js +7 -3
- package/mcp/local/team-run.js +56 -4
- package/mcp/local-capabilities.json +9 -0
- package/mcp/servers.json +24 -0
- package/package.json +29 -4
- 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 +534 -0
- package/plugins/meta-architect/obsidian/manifest.json +9 -0
- package/plugins/meta-architect/obsidian/styles.css +7 -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/flow/SKILL.md +2 -0
- package/plugins/meta-architect/skills/sage/SKILL.md +2 -0
- package/plugins/meta-architect/skills/sage/references/source-selection.md +8 -0
- package/plugins/meta-architect/skills/vet/SKILL.md +2 -0
- package/plugins/meta-architect/skills/vet/references/security-playbooks.md +7 -0
- package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
- package/schemas/autonomous-task-queue.schema.json +56 -0
- package/schemas/handoff-packet.schema.json +36 -0
- package/schemas/setup-receipt.schema.json +28 -0
- package/schemas/skill-frontmatter.schema.json +20 -0
- package/schemas/support-bundle.schema.json +37 -0
- package/schemas/task-contract.schema.json +31 -0
- package/scripts/active-autonomy-hook.mjs +226 -0
- package/scripts/build-linux-packages.mjs +7 -3
- package/scripts/cleanup-test-fixtures.sh +55 -0
- package/scripts/context-hydration-hook.mjs +89 -0
- package/scripts/demo-smoke.js +285 -0
- package/scripts/doctor.js +27 -12
- package/scripts/install.sh +57 -0
- package/scripts/install.sh.sha256 +1 -0
- package/scripts/linux-package-smoke.mjs +2 -2
- package/scripts/package-size-check.mjs +26 -0
- package/scripts/plugin-sync.js +83 -17
- package/scripts/postinstall.js +17 -10
- package/scripts/prepack.js +9 -0
- package/scripts/ralph/prompt.md +35 -0
- package/scripts/release-sync.js +72 -7
- package/scripts/release-verify.js +260 -2
- package/scripts/setup-npmrc.js +48 -23
- package/scripts/skills-install.js +9 -2
- package/scripts/skills-manifest.js +2 -21
- package/scripts/skills-pack.js +2 -2
- package/scripts/skills-validate.js +5 -55
- package/skills/arch/SKILL.md +2 -0
- package/skills/build/SKILL.md +3 -0
- package/skills/flow/SKILL.md +2 -0
- package/skills/index.json +0 -0
- package/skills/sage/SKILL.md +2 -0
- package/skills/sage/references/source-selection.md +8 -0
- package/skills/vet/SKILL.md +2 -0
- package/skills/vet/references/security-playbooks.md +7 -0
- package/skills/vibe/SKILL.md +2 -0
- package/sprint/00-idea.md +0 -0
- package/sprint/01-architecture.md +0 -0
- package/sprint/02-oss-evidence.md +0 -0
- package/sprint/03-logic.md +0 -0
- package/sprint/04-security.md +0 -0
- package/sprint/05-dx-ux.md +0 -0
- package/sprint/06-build-plan.md +0 -0
- package/sprint/07-release.md +0 -0
- package/src/agents.js +279 -0
- package/src/bootstrap.js +268 -62
- package/src/build-gate.js +2 -2
- package/src/codex-app-server.js +291 -0
- package/src/decision-log.js +0 -0
- package/src/fs-utils.js +175 -4
- package/src/launcher.js +28 -21
- package/src/mcp-config.js +9 -1
- package/src/mcp-live-client.js +422 -3
- package/src/paths.js +10 -1
- package/src/policy.js +3 -3
- package/src/prelaunch.js +189 -0
- package/src/quality/ai-quality-orchestrator.js +328 -0
- package/src/release-issue-gates.js +252 -0
- package/src/release-operations.js +62 -0
- package/src/release-state.js +0 -0
- package/src/runtime/active-autonomy-core.js +208 -0
- package/src/runtime/agent-compat.js +31 -0
- package/src/runtime/alignment-sentinel.js +165 -0
- package/src/runtime/architect-review.js +326 -0
- package/src/runtime/autonomous-tasks.js +408 -0
- package/src/runtime/build-readiness.js +28 -4
- package/src/runtime/code-graph-rehearse.js +113 -0
- package/src/runtime/codeburn-core.js +112 -0
- package/src/runtime/context-authority.js +40 -0
- package/src/runtime/context-economy-core.js +316 -0
- package/src/runtime/continuity-graph.js +201 -0
- package/src/runtime/continuity-notes.js +71 -26
- package/src/runtime/core-source-ingest.js +379 -0
- package/src/runtime/detached-provider.js +142 -0
- package/src/runtime/doctor-report.js +18 -0
- package/src/runtime/environment-awareness-core.js +460 -0
- package/src/runtime/exposure-catalog.js +276 -0
- package/src/runtime/graphify-core.js +167 -0
- package/src/runtime/guidance-stack.js +9 -2
- package/src/runtime/handoff-packets.js +92 -0
- package/src/runtime/headroom-core.js +104 -0
- package/src/runtime/helper-orchestration-core.js +307 -0
- package/src/runtime/hook-profiles.js +20 -0
- package/src/runtime/learning-loop-core.js +344 -0
- package/src/runtime/live-agent-verification.js +122 -0
- package/src/runtime/maestro-events.js +18 -0
- package/src/runtime/maestro-manager.js +19 -0
- package/src/runtime/maestro-output.js +58 -0
- package/src/runtime/maestro-state.js +125 -0
- package/src/runtime/managed-markdown.js +29 -0
- package/src/runtime/mcp-authority.js +147 -0
- package/src/runtime/mcp-policy.js +192 -0
- package/src/runtime/obsidian-integration-core.js +1089 -0
- package/src/runtime/obsidian-plugin-bridge.js +1045 -0
- package/src/runtime/pi-maestro-core.js +89 -0
- package/src/runtime/preferences.js +79 -0
- package/src/runtime/project-context.js +636 -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 +373 -0
- package/src/runtime/runtime-state.js +935 -2
- package/src/runtime/schema-migrations.js +162 -0
- package/src/runtime/semantic-recording-core.js +147 -0
- package/src/runtime/signal-hooks.js +28 -0
- package/src/runtime/skills-registry-export.js +754 -0
- package/src/runtime/task-contracts.js +83 -0
- package/src/runtime/universal-plugin-broker-core.js +898 -0
- package/src/runtime/workspace-intelligence-runtime.js +674 -0
- package/src/runtime/workspace-virtualizer.js +102 -0
- package/src/runtime-artifacts.js +225 -62
- package/src/setup-lifecycle.js +359 -0
- package/src/skill-frontmatter.js +78 -0
- package/src/skill-installer.js +226 -71
- package/src/skills.js +1166 -96
- package/src/state-sync.js +0 -0
- package/src/test-fixtures.js +543 -0
- package/src/tui/grid.js +67 -0
- package/src/tui/status-grid.js +23 -0
- package/support-bundle.json +114 -0
- package/templates/AGENTS.md +6 -2
- package/templates/catalog-manifest.json +0 -0
- package/templates/model-instructions/core.md +0 -0
- package/templates/model-instructions/release.md +0 -0
- package/templates/model-instructions/security.md +0 -0
- package/templates/quality/ai-quality-rules.yml +37 -0
- 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 -8
- package/docs/onboarding.md +0 -65
- package/docs/skills-publishing.md +0 -255
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readJson } from "../fs-utils.js";
|
|
3
|
+
import { getMcpLocalCapabilitiesPath, getMcpServersPath, getRepoRoot } from "../paths.js";
|
|
4
|
+
import { validateRuntimeMcpPolicyFile } from "./mcp-policy.js";
|
|
5
|
+
|
|
6
|
+
const advisoryCatalog = [
|
|
7
|
+
{
|
|
8
|
+
ecosystem: "npm",
|
|
9
|
+
packageName: "tar",
|
|
10
|
+
vulnerableBelow: "6.2.1",
|
|
11
|
+
severity: "critical",
|
|
12
|
+
confidence: "high",
|
|
13
|
+
diagnostic: "tar < 6.2.1 allows arbitrary file overwrite.",
|
|
14
|
+
recommendedAction: "Upgrade tar to 6.2.1 or newer in the lockfile and reverify.",
|
|
15
|
+
advisoryReference: "advisory-2026-0042",
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function normalizePackageNameFromLockPath(lockPath) {
|
|
20
|
+
const marker = "node_modules/";
|
|
21
|
+
const markerIndex = lockPath.lastIndexOf(marker);
|
|
22
|
+
if (markerIndex === -1) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return lockPath.slice(markerIndex + marker.length);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function compareSemver(a, b) {
|
|
30
|
+
const parse = (value) =>
|
|
31
|
+
`${value}`.split(".").map((part) => Number.parseInt(part.replace(/[^0-9].*$/, ""), 10) || 0);
|
|
32
|
+
const left = parse(a);
|
|
33
|
+
const right = parse(b);
|
|
34
|
+
const maxLength = Math.max(left.length, right.length);
|
|
35
|
+
|
|
36
|
+
for (let index = 0; index < maxLength; index += 1) {
|
|
37
|
+
const leftValue = left[index] ?? 0;
|
|
38
|
+
const rightValue = right[index] ?? 0;
|
|
39
|
+
if (leftValue > rightValue) {
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
if (leftValue < rightValue) {
|
|
43
|
+
return -1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeManifestVersion(value) {
|
|
51
|
+
const match = `${value}`.match(/\d+(?:\.\d+){0,2}/);
|
|
52
|
+
return match ? match[0] : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function scanLockfilePackageExposure() {
|
|
56
|
+
const lockfilePath = path.join(getRepoRoot(), "package-lock.json");
|
|
57
|
+
let lockfile;
|
|
58
|
+
try {
|
|
59
|
+
lockfile = await readJson(lockfilePath);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return scanPackageExposureFromLockfile(lockfile, {
|
|
69
|
+
sourcePath: lockfilePath,
|
|
70
|
+
catalog: advisoryCatalog,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function scanPackageExposureFromLockfile(
|
|
75
|
+
lockfile,
|
|
76
|
+
{ sourcePath = "package-lock.json", catalog = advisoryCatalog } = {},
|
|
77
|
+
) {
|
|
78
|
+
const packages = lockfile?.packages ?? {};
|
|
79
|
+
const findings = [];
|
|
80
|
+
|
|
81
|
+
for (const [lockPath, entry] of Object.entries(packages)) {
|
|
82
|
+
if (!lockPath || lockPath === "" || !entry?.version) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const packageName = normalizePackageNameFromLockPath(lockPath);
|
|
87
|
+
if (!packageName) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const advisory of catalog) {
|
|
92
|
+
if (packageName !== advisory.packageName) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (compareSemver(entry.version, advisory.vulnerableBelow) >= 0) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
findings.push({
|
|
101
|
+
record_type: "finding:package_exposure",
|
|
102
|
+
severity: advisory.severity,
|
|
103
|
+
confidence: advisory.confidence,
|
|
104
|
+
ecosystem: advisory.ecosystem,
|
|
105
|
+
package_name: packageName,
|
|
106
|
+
package_version: entry.version,
|
|
107
|
+
source_path: sourcePath,
|
|
108
|
+
evidence_kind: "lockfile-version-match",
|
|
109
|
+
diagnostic: advisory.diagnostic,
|
|
110
|
+
recommended_action: advisory.recommendedAction,
|
|
111
|
+
advisory_reference: advisory.advisoryReference,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return findings;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function scanPackageExposureFromManifest(
|
|
120
|
+
manifest,
|
|
121
|
+
{ sourcePath = "package.json", catalog = advisoryCatalog } = {},
|
|
122
|
+
) {
|
|
123
|
+
const dependencyGroups = [
|
|
124
|
+
"dependencies",
|
|
125
|
+
"devDependencies",
|
|
126
|
+
"optionalDependencies",
|
|
127
|
+
"peerDependencies",
|
|
128
|
+
];
|
|
129
|
+
const findings = [];
|
|
130
|
+
|
|
131
|
+
for (const group of dependencyGroups) {
|
|
132
|
+
for (const [packageName, versionRange] of Object.entries(manifest?.[group] ?? {})) {
|
|
133
|
+
const normalizedVersion = normalizeManifestVersion(versionRange);
|
|
134
|
+
if (!normalizedVersion) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const advisory of catalog) {
|
|
139
|
+
if (packageName !== advisory.packageName) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (compareSemver(normalizedVersion, advisory.vulnerableBelow) >= 0) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
findings.push({
|
|
147
|
+
record_type: "finding:package_exposure",
|
|
148
|
+
severity: advisory.severity,
|
|
149
|
+
confidence: "medium",
|
|
150
|
+
ecosystem: advisory.ecosystem,
|
|
151
|
+
package_name: packageName,
|
|
152
|
+
package_version: versionRange,
|
|
153
|
+
normalized_version: normalizedVersion,
|
|
154
|
+
source_path: sourcePath,
|
|
155
|
+
evidence_kind: "manifest-version-range-match",
|
|
156
|
+
dependency_group: group,
|
|
157
|
+
diagnostic: advisory.diagnostic,
|
|
158
|
+
recommended_action: advisory.recommendedAction,
|
|
159
|
+
advisory_reference: advisory.advisoryReference,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return findings;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function validateMcpPolicyExposure({ activeProfile = "project" } = {}) {
|
|
169
|
+
const findings = [];
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const localCapabilities = await readJson(getMcpLocalCapabilitiesPath());
|
|
173
|
+
for (const capability of localCapabilities.capabilities ?? []) {
|
|
174
|
+
if (
|
|
175
|
+
typeof capability.module === "string" &&
|
|
176
|
+
!capability.module.startsWith("./local/") &&
|
|
177
|
+
capability.kind === "local-capability"
|
|
178
|
+
) {
|
|
179
|
+
findings.push({
|
|
180
|
+
record_type: "policy:mcp_validation",
|
|
181
|
+
severity: "warning",
|
|
182
|
+
confidence: "high",
|
|
183
|
+
diagnostic: `Local capability module '${capability.module}' escapes the expected ./local/ namespace.`,
|
|
184
|
+
source_path: getMcpLocalCapabilitiesPath(),
|
|
185
|
+
recommended_action:
|
|
186
|
+
"Keep packaged local capability modules inside the ./local/ namespace or document the exception explicitly.",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const servers = await readJson(getMcpServersPath());
|
|
198
|
+
for (const server of servers.servers ?? []) {
|
|
199
|
+
if (typeof server.endpoint === "string" && server.endpoint === "https://gitmcp.io/docs") {
|
|
200
|
+
findings.push({
|
|
201
|
+
record_type: "policy:mcp_validation",
|
|
202
|
+
severity: "warning",
|
|
203
|
+
confidence: "high",
|
|
204
|
+
diagnostic: "Fallback docs endpoint is configured as an approved evidence source.",
|
|
205
|
+
source_path: getMcpServersPath(),
|
|
206
|
+
recommended_action:
|
|
207
|
+
"Use exact repo-form GitMCP endpoints in mcp/servers.json instead of the docs fallback endpoint.",
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch (error) {
|
|
212
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
findings.push(...(await validateRuntimeMcpPolicyFile({ activeProfile })));
|
|
218
|
+
|
|
219
|
+
return findings;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function scanExposureProfile(profile = "baseline") {
|
|
223
|
+
const allowedProfiles = new Set(["baseline", "project", "deep"]);
|
|
224
|
+
if (!allowedProfiles.has(profile)) {
|
|
225
|
+
throw new Error(`Unsupported exposure scan profile: ${profile}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const lockfileFindings = await scanLockfilePackageExposure();
|
|
229
|
+
let manifestFindings = [];
|
|
230
|
+
if (profile !== "baseline") {
|
|
231
|
+
try {
|
|
232
|
+
const manifestPath = path.join(getRepoRoot(), "package.json");
|
|
233
|
+
manifestFindings = scanPackageExposureFromManifest(await readJson(manifestPath), {
|
|
234
|
+
sourcePath: manifestPath,
|
|
235
|
+
catalog: advisoryCatalog,
|
|
236
|
+
});
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const policyFindings =
|
|
244
|
+
profile === "baseline" ? [] : await validateMcpPolicyExposure({ activeProfile: profile });
|
|
245
|
+
const findings = [...lockfileFindings, ...manifestFindings, ...policyFindings];
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
record_type: "exposure_profile_report",
|
|
249
|
+
profile,
|
|
250
|
+
read_only: true,
|
|
251
|
+
mutates_release_state: false,
|
|
252
|
+
findings,
|
|
253
|
+
blocker_count: findings.filter((finding) =>
|
|
254
|
+
["critical", "high"].includes(`${finding.severity}`.toLowerCase()),
|
|
255
|
+
).length,
|
|
256
|
+
scanned_surfaces:
|
|
257
|
+
profile === "baseline"
|
|
258
|
+
? ["package-lock.json"]
|
|
259
|
+
: profile === "project"
|
|
260
|
+
? [
|
|
261
|
+
"package-lock.json",
|
|
262
|
+
"package.json",
|
|
263
|
+
"mcp/local-capabilities.json",
|
|
264
|
+
"mcp/servers.json",
|
|
265
|
+
".ma/.mcp.json",
|
|
266
|
+
]
|
|
267
|
+
: [
|
|
268
|
+
"package-lock.json",
|
|
269
|
+
"package.json",
|
|
270
|
+
"mcp/local-capabilities.json",
|
|
271
|
+
"mcp/servers.json",
|
|
272
|
+
".ma/.mcp.json",
|
|
273
|
+
"policy",
|
|
274
|
+
],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { ensureDir, readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
|
|
2
|
+
import { getRuntimeSubsystemPath } from "../paths.js";
|
|
3
|
+
|
|
4
|
+
export const graphifySchemaVersion = "0.1.0";
|
|
5
|
+
|
|
6
|
+
export function getGraphifyRoot() {
|
|
7
|
+
return getRuntimeSubsystemPath("context", "graphify");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getGraphifyIndexPath() {
|
|
11
|
+
return getRuntimeSubsystemPath("context", "graphify", "index.json");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function createDefaultGraphifyIndex() {
|
|
15
|
+
return {
|
|
16
|
+
schemaVersion: graphifySchemaVersion,
|
|
17
|
+
record_type: "derived_graph_index",
|
|
18
|
+
read_only: true,
|
|
19
|
+
nodes: [],
|
|
20
|
+
edges: [],
|
|
21
|
+
lastRebuiltAt: null,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function validateGraphifyIndex(value) {
|
|
26
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
27
|
+
throw new Error("graphify index must be an object");
|
|
28
|
+
}
|
|
29
|
+
if (value.schemaVersion !== graphifySchemaVersion) {
|
|
30
|
+
throw new Error(`Unsupported graphify schemaVersion: ${value.schemaVersion}`);
|
|
31
|
+
}
|
|
32
|
+
if (value.record_type !== "derived_graph_index" || value.read_only !== true) {
|
|
33
|
+
throw new Error("graphify index must be a read-only derived_graph_index");
|
|
34
|
+
}
|
|
35
|
+
if (!Array.isArray(value.nodes) || !Array.isArray(value.edges)) {
|
|
36
|
+
throw new Error("graphify index requires nodes and edges arrays");
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function seedGraphifyArtifacts() {
|
|
42
|
+
await ensureDir(getGraphifyRoot());
|
|
43
|
+
await writeFileIfMissing(
|
|
44
|
+
getGraphifyIndexPath(),
|
|
45
|
+
`${JSON.stringify(createDefaultGraphifyIndex(), null, 2)}\n`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function loadGraphifyIndex() {
|
|
50
|
+
return validateGraphifyIndex(await readJson(getGraphifyIndexPath()));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function addNode(nodes, seen, id, kind, sourceSubsystem, label) {
|
|
54
|
+
if (!id || seen.has(id)) return;
|
|
55
|
+
seen.add(id);
|
|
56
|
+
nodes.push({ id, kind, sourceSubsystem, label: String(label ?? id) });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function addEdge(edges, seen, from, to, relation) {
|
|
60
|
+
if (!from || !to || from === to) return;
|
|
61
|
+
const key = `${from}\0${to}\0${relation}`;
|
|
62
|
+
if (seen.has(key)) return;
|
|
63
|
+
seen.add(key);
|
|
64
|
+
edges.push({ from, to, relation });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function addCollection(nodes, edges, nodeIds, edgeIds, sourceSubsystem, kind, entries, labelKey) {
|
|
68
|
+
for (const [index, entry] of (Array.isArray(entries) ? entries : []).entries()) {
|
|
69
|
+
if (!entry || typeof entry !== "object") continue;
|
|
70
|
+
const id = `${sourceSubsystem}:${kind}:${entry.id ?? entry.name ?? index}`;
|
|
71
|
+
addNode(
|
|
72
|
+
nodes,
|
|
73
|
+
nodeIds,
|
|
74
|
+
id,
|
|
75
|
+
kind,
|
|
76
|
+
sourceSubsystem,
|
|
77
|
+
entry[labelKey] ?? entry.name ?? entry.id ?? id,
|
|
78
|
+
);
|
|
79
|
+
if (entry.projectId) addEdge(edges, edgeIds, id, `project:${entry.projectId}`, "belongs_to");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function rebuildGraphifyIndex(runtimeSnapshot = {}) {
|
|
84
|
+
const nodes = [];
|
|
85
|
+
const edges = [];
|
|
86
|
+
const nodeIds = new Set();
|
|
87
|
+
const edgeIds = new Set();
|
|
88
|
+
addNode(nodes, nodeIds, "project:root", "project", "project", "Project");
|
|
89
|
+
|
|
90
|
+
const continuity = runtimeSnapshot.continuityIndex;
|
|
91
|
+
if (continuity && typeof continuity === "object") {
|
|
92
|
+
addNode(nodes, nodeIds, "memory:continuity", "memory", "continuity", "Continuity memory");
|
|
93
|
+
addEdge(edges, edgeIds, "memory:continuity", "project:root", "supports");
|
|
94
|
+
}
|
|
95
|
+
const taskRegistry = runtimeSnapshot.taskRegistry ?? {};
|
|
96
|
+
addCollection(nodes, edges, nodeIds, edgeIds, "tasks", "task", taskRegistry.tasks, "title");
|
|
97
|
+
addCollection(nodes, edges, nodeIds, edgeIds, "tasks", "worker", taskRegistry.workers, "name");
|
|
98
|
+
for (const entry of runtimeSnapshot.mailboxEntries ?? []) {
|
|
99
|
+
if (typeof entry !== "string") continue;
|
|
100
|
+
const id = `tasks:mailbox:${entry}`;
|
|
101
|
+
addNode(nodes, nodeIds, id, "mailbox", "tasks", entry, entry);
|
|
102
|
+
addEdge(edges, edgeIds, id, "project:root", "supports");
|
|
103
|
+
}
|
|
104
|
+
addCollection(
|
|
105
|
+
nodes,
|
|
106
|
+
edges,
|
|
107
|
+
nodeIds,
|
|
108
|
+
edgeIds,
|
|
109
|
+
"workspaces",
|
|
110
|
+
"workspace",
|
|
111
|
+
runtimeSnapshot.workspaceIndex?.items,
|
|
112
|
+
"name",
|
|
113
|
+
);
|
|
114
|
+
addCollection(
|
|
115
|
+
nodes,
|
|
116
|
+
edges,
|
|
117
|
+
nodeIds,
|
|
118
|
+
edgeIds,
|
|
119
|
+
"manager",
|
|
120
|
+
"run",
|
|
121
|
+
runtimeSnapshot.managerRuns?.runs,
|
|
122
|
+
"state",
|
|
123
|
+
);
|
|
124
|
+
addCollection(
|
|
125
|
+
nodes,
|
|
126
|
+
edges,
|
|
127
|
+
nodeIds,
|
|
128
|
+
edgeIds,
|
|
129
|
+
"decisions",
|
|
130
|
+
"decision",
|
|
131
|
+
runtimeSnapshot.decisions?.decisions,
|
|
132
|
+
"title",
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const vault = runtimeSnapshot.obsidianVaultIndex;
|
|
136
|
+
if (vault?.note_count > 0) {
|
|
137
|
+
addNode(
|
|
138
|
+
nodes,
|
|
139
|
+
nodeIds,
|
|
140
|
+
"obsidian:vault",
|
|
141
|
+
"vault",
|
|
142
|
+
"obsidian",
|
|
143
|
+
vault.vault_path || "Obsidian vault",
|
|
144
|
+
);
|
|
145
|
+
addEdge(edges, edgeIds, "obsidian:vault", "project:root", "context");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
nodes.sort((a, b) => a.id.localeCompare(b.id));
|
|
149
|
+
edges.sort((a, b) =>
|
|
150
|
+
`${a.from}:${a.to}:${a.relation}`.localeCompare(`${b.from}:${b.to}:${b.relation}`),
|
|
151
|
+
);
|
|
152
|
+
return {
|
|
153
|
+
schemaVersion: graphifySchemaVersion,
|
|
154
|
+
record_type: "derived_graph_index",
|
|
155
|
+
read_only: true,
|
|
156
|
+
nodes,
|
|
157
|
+
edges,
|
|
158
|
+
lastRebuiltAt: new Date().toISOString(),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function writeGraphifyIndex(runtimeSnapshot) {
|
|
163
|
+
const index = validateGraphifyIndex(rebuildGraphifyIndex(runtimeSnapshot));
|
|
164
|
+
await ensureDir(getGraphifyRoot());
|
|
165
|
+
await writeJson(getGraphifyIndexPath(), index);
|
|
166
|
+
return index;
|
|
167
|
+
}
|
|
@@ -20,8 +20,8 @@ export async function seedGuidanceStackArtifacts() {
|
|
|
20
20
|
`${JSON.stringify(
|
|
21
21
|
{
|
|
22
22
|
schemaVersion: "0.1.0",
|
|
23
|
-
sources: [],
|
|
24
|
-
content:
|
|
23
|
+
sources: [ponytailGuidanceSource],
|
|
24
|
+
content: ponytailGuidanceSource.content,
|
|
25
25
|
},
|
|
26
26
|
null,
|
|
27
27
|
2,
|
|
@@ -40,3 +40,10 @@ export async function seedGuidanceStackArtifacts() {
|
|
|
40
40
|
)}\n`,
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
export const ponytailGuidanceSource = {
|
|
45
|
+
id: "ponytail",
|
|
46
|
+
label: "Minimal-diff bias",
|
|
47
|
+
content:
|
|
48
|
+
"Apply a minimal-diff bias: prefer reuse over new abstractions, the smallest coherent diff, and deletion before addition. Preserve existing behavior and add complexity only when verification proves it is needed.",
|
|
49
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { ensureDir, readJson, writeJson } from "../fs-utils.js";
|
|
3
|
+
import { getRuntimeSubsystemPath } from "../paths.js";
|
|
4
|
+
|
|
5
|
+
export const handoffPacketSchemaVersion = "0.1.0";
|
|
6
|
+
|
|
7
|
+
export function getHandoffPacketRoot() {
|
|
8
|
+
return getRuntimeSubsystemPath("tasks", "handoffs");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function safePacketId(id) {
|
|
12
|
+
if (String(id ?? "").includes("/") || String(id ?? "").includes("\\"))
|
|
13
|
+
throw new Error("handoff packet id is required");
|
|
14
|
+
const safe = String(id ?? "")
|
|
15
|
+
.trim()
|
|
16
|
+
.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
17
|
+
if (!safe || safe === "." || safe === "..") throw new Error("handoff packet id is required");
|
|
18
|
+
return safe;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createHandoffPacket({
|
|
22
|
+
goal,
|
|
23
|
+
currentState,
|
|
24
|
+
contextUsed = [],
|
|
25
|
+
decisions = [],
|
|
26
|
+
blockers = [],
|
|
27
|
+
risks = [],
|
|
28
|
+
changedFiles = [],
|
|
29
|
+
verification = [],
|
|
30
|
+
nextAction,
|
|
31
|
+
status = "active",
|
|
32
|
+
}) {
|
|
33
|
+
if (!goal || !currentState || !nextAction)
|
|
34
|
+
throw new Error("handoff packet requires goal, currentState, and nextAction");
|
|
35
|
+
return validateHandoffPacket({
|
|
36
|
+
schemaVersion: handoffPacketSchemaVersion,
|
|
37
|
+
record_type: "handoff_packet",
|
|
38
|
+
goal,
|
|
39
|
+
current_state: currentState,
|
|
40
|
+
context_used: contextUsed,
|
|
41
|
+
decisions,
|
|
42
|
+
blockers,
|
|
43
|
+
risks,
|
|
44
|
+
changed_files: changedFiles,
|
|
45
|
+
verification,
|
|
46
|
+
next_action: nextAction,
|
|
47
|
+
status,
|
|
48
|
+
created_at: new Date().toISOString(),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function validateHandoffPacket(packet) {
|
|
53
|
+
if (
|
|
54
|
+
!packet ||
|
|
55
|
+
packet.schemaVersion !== handoffPacketSchemaVersion ||
|
|
56
|
+
packet.record_type !== "handoff_packet"
|
|
57
|
+
)
|
|
58
|
+
throw new Error("invalid handoff packet schema");
|
|
59
|
+
for (const field of ["goal", "current_state", "next_action"]) {
|
|
60
|
+
if (typeof packet[field] !== "string" || packet[field].trim() === "")
|
|
61
|
+
throw new Error(`handoff packet requires ${field}`);
|
|
62
|
+
}
|
|
63
|
+
for (const field of [
|
|
64
|
+
"context_used",
|
|
65
|
+
"decisions",
|
|
66
|
+
"blockers",
|
|
67
|
+
"risks",
|
|
68
|
+
"changed_files",
|
|
69
|
+
"verification",
|
|
70
|
+
]) {
|
|
71
|
+
if (!Array.isArray(packet[field]) || packet[field].some((value) => typeof value !== "string"))
|
|
72
|
+
throw new Error(`handoff packet requires string array: ${field}`);
|
|
73
|
+
}
|
|
74
|
+
if (!["active", "blocked", "verified"].includes(packet.status))
|
|
75
|
+
throw new Error(`Unsupported handoff packet status: ${packet.status}`);
|
|
76
|
+
return packet;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function writeHandoffPacket(id, packet) {
|
|
80
|
+
const packetId = safePacketId(id);
|
|
81
|
+
const validated = validateHandoffPacket(packet);
|
|
82
|
+
await ensureDir(getHandoffPacketRoot());
|
|
83
|
+
const packetPath = path.join(getHandoffPacketRoot(), `${packetId}.json`);
|
|
84
|
+
await writeJson(packetPath, validated);
|
|
85
|
+
return packetPath;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function loadHandoffPacket(id) {
|
|
89
|
+
return validateHandoffPacket(
|
|
90
|
+
await readJson(path.join(getHandoffPacketRoot(), `${safePacketId(id)}.json`)),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ensureDir, readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
|
|
2
|
+
import { getRuntimeSubsystemPath } from "../paths.js";
|
|
3
|
+
|
|
4
|
+
export const headroomSchemaVersion = "0.1.0";
|
|
5
|
+
|
|
6
|
+
const defaultBudgets = {
|
|
7
|
+
pendingMailboxCount: { warn: 3, critical: 8 },
|
|
8
|
+
taskCount: { warn: 10, critical: 25 },
|
|
9
|
+
activeManagerRunCount: { warn: 2, critical: 5 },
|
|
10
|
+
waitingReviewManagerRunCount: { warn: 1, critical: 3 },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function getHeadroomRoot() {
|
|
14
|
+
return getRuntimeSubsystemPath("headroom");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getHeadroomBudgetsPath() {
|
|
18
|
+
return getRuntimeSubsystemPath("headroom", "budgets.json");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getHeadroomStatusPath() {
|
|
22
|
+
return getRuntimeSubsystemPath("headroom", "status.json");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createDefaultHeadroomBudgets() {
|
|
26
|
+
return { schemaVersion: headroomSchemaVersion, budgets: structuredClone(defaultBudgets) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createDefaultHeadroomStatus() {
|
|
30
|
+
return { schemaVersion: headroomSchemaVersion, evaluatedAt: null, signals: [] };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function validateHeadroomBudgets(value) {
|
|
34
|
+
if (!value || typeof value !== "object" || value.schemaVersion !== headroomSchemaVersion) {
|
|
35
|
+
throw new Error("headroom budgets require the current schemaVersion");
|
|
36
|
+
}
|
|
37
|
+
if (!value.budgets || typeof value.budgets !== "object" || Array.isArray(value.budgets)) {
|
|
38
|
+
throw new Error("headroom budgets require a budgets object");
|
|
39
|
+
}
|
|
40
|
+
for (const [counter, budget] of Object.entries(value.budgets)) {
|
|
41
|
+
if (
|
|
42
|
+
!budget ||
|
|
43
|
+
!Number.isFinite(budget.warn) ||
|
|
44
|
+
!Number.isFinite(budget.critical) ||
|
|
45
|
+
budget.warn < 0 ||
|
|
46
|
+
budget.critical < budget.warn
|
|
47
|
+
) {
|
|
48
|
+
throw new Error(`Invalid headroom budget for ${counter}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function validateHeadroomStatus(value) {
|
|
55
|
+
if (!value || typeof value !== "object" || value.schemaVersion !== headroomSchemaVersion) {
|
|
56
|
+
throw new Error("headroom status requires the current schemaVersion");
|
|
57
|
+
}
|
|
58
|
+
if (!Array.isArray(value.signals)) throw new Error("headroom status requires signals");
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function seedHeadroomArtifacts() {
|
|
63
|
+
await ensureDir(getHeadroomRoot());
|
|
64
|
+
await writeFileIfMissing(
|
|
65
|
+
getHeadroomBudgetsPath(),
|
|
66
|
+
`${JSON.stringify(createDefaultHeadroomBudgets(), null, 2)}\n`,
|
|
67
|
+
);
|
|
68
|
+
await writeFileIfMissing(
|
|
69
|
+
getHeadroomStatusPath(),
|
|
70
|
+
`${JSON.stringify(createDefaultHeadroomStatus(), null, 2)}\n`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function loadHeadroomBudgets() {
|
|
75
|
+
return validateHeadroomBudgets(await readJson(getHeadroomBudgetsPath()));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function loadHeadroomStatus() {
|
|
79
|
+
return validateHeadroomStatus(await readJson(getHeadroomStatusPath()));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function evaluateHeadroom(
|
|
83
|
+
runtimeSummary = {},
|
|
84
|
+
budgetConfig = createDefaultHeadroomBudgets(),
|
|
85
|
+
) {
|
|
86
|
+
const config = validateHeadroomBudgets(budgetConfig);
|
|
87
|
+
const signals = Object.entries(config.budgets).map(([counter, budget]) => {
|
|
88
|
+
const value = Number(runtimeSummary[counter] ?? 0);
|
|
89
|
+
const level = value >= budget.critical ? "critical" : value >= budget.warn ? "warn" : "ok";
|
|
90
|
+
return { counter, value, warn: budget.warn, critical: budget.critical, level };
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
schemaVersion: headroomSchemaVersion,
|
|
94
|
+
evaluatedAt: new Date().toISOString(),
|
|
95
|
+
signals,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function writeHeadroomStatus(runtimeSummary, budgetConfig) {
|
|
100
|
+
const status = validateHeadroomStatus(evaluateHeadroom(runtimeSummary, budgetConfig));
|
|
101
|
+
await ensureDir(getHeadroomRoot());
|
|
102
|
+
await writeJson(getHeadroomStatusPath(), status);
|
|
103
|
+
return status;
|
|
104
|
+
}
|