vgxness 1.9.0 → 1.9.2

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.
Files changed (40) hide show
  1. package/README.md +8 -3
  2. package/dist/agents/agent-activation-service.js +13 -4
  3. package/dist/agents/agent-registry-service.js +8 -2
  4. package/dist/agents/agent-seed-upgrade-service.js +231 -0
  5. package/dist/agents/boot-upgrade.js +59 -0
  6. package/dist/agents/canonical-agent-manifest.js +19 -19
  7. package/dist/agents/canonical-agent-projection.js +7 -0
  8. package/dist/agents/manager-profile-overlay-service.js +14 -0
  9. package/dist/agents/renderers/claude-renderer.js +3 -1
  10. package/dist/agents/renderers/opencode-renderer.js +2 -1
  11. package/dist/agents/repositories/agent-seed-history.js +128 -0
  12. package/dist/behavior/behavior-contract-manifest.js +42 -0
  13. package/dist/behavior/behavior-contract-schema.js +1 -0
  14. package/dist/behavior/behavior-contract-validation.js +42 -0
  15. package/dist/cli/commands/mcp-dispatcher.js +7 -0
  16. package/dist/cli/dispatcher.js +2 -0
  17. package/dist/mcp/client-install-claude-code-contract.js +19 -4
  18. package/dist/mcp/client-install-claude-code.js +2 -2
  19. package/dist/mcp/control-plane-snapshot-service.js +272 -0
  20. package/dist/mcp/control-plane.js +24 -6
  21. package/dist/mcp/index.js +1 -0
  22. package/dist/mcp/provider-status.js +3 -7
  23. package/dist/mcp/schema.js +21 -2
  24. package/dist/mcp/stdio-server.js +2 -0
  25. package/dist/mcp/validation.js +50 -3
  26. package/dist/memory/sqlite/migrations/016_agent_seed_history.sql +15 -0
  27. package/dist/payload/context-budget-policy.js +17 -0
  28. package/dist/payload/context-budget-service.js +44 -0
  29. package/dist/runs/schema.js +4 -0
  30. package/dist/sdd/schema.js +12 -0
  31. package/dist/sdd/sdd-workflow-service.js +43 -2
  32. package/docs/architecture.md +8 -0
  33. package/docs/cli.md +5 -5
  34. package/docs/contributing.md +1 -1
  35. package/docs/glossary.md +1 -1
  36. package/docs/mcp.md +2 -2
  37. package/docs/project-health-audit-v1.9.1.md +126 -0
  38. package/docs/providers.md +4 -4
  39. package/docs/safety.md +1 -1
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -8,6 +8,8 @@ This package is proprietary software. The npm package ships inspectable JavaScri
8
8
 
9
9
  OpenCode is the primary supported provider. Other providers remain preview/manual only. Provider config writes require explicit CLI confirmation.
10
10
 
11
+ VGXNESS v1.9.1 has a canonical project health audit with validated evidence: 38 MCP tools, 106 test files, and the official Bun validation path passing, including package evidence with `releaseReadiness: pass`. See [Project health audit v1.9.1](./docs/project-health-audit-v1.9.1.md) for the versioned matrix and safety taxonomy.
12
+
11
13
  ## Requirements
12
14
 
13
15
  - Bun >= 1.3.14 for the installed `vgxness`/`vgx` CLI/MCP runtime and repository verification
@@ -25,6 +27,7 @@ bun install --frozen-lockfile
25
27
  bun run check:bun-lock
26
28
  bun run verify:typecheck
27
29
  bun run verify:test
30
+ bun run verify:test:bun-storage
28
31
  bun run verify:bun-sqlite
29
32
  bun run verify:package
30
33
  bun run package:bun:evidence -- --require-pass
@@ -86,7 +89,7 @@ Release-candidate checklist, still non-publishing:
86
89
 
87
90
  1. `bun install --frozen-lockfile` completes on a clean checkout.
88
91
  2. `bun run check:bun-lock` passes.
89
- 3. `bun run verify:typecheck`, `bun run verify:test`, and `bun run verify:bun-sqlite` pass.
92
+ 3. `bun run verify:typecheck`, `bun run verify:test`, `bun run verify:test:bun-storage`, and `bun run verify:bun-sqlite` pass.
90
93
  4. `bun run package:bun:evidence -- --require-pass` passes on supported CI OSes.
91
94
  5. JSON evidence shows `status: pass`, artifact/install smoke pass, no retired bridge scripts, no `package-lock.json`, `publicationAttempted: false`, and `releaseReadiness.status: pass`.
92
95
  6. Version, release notes, proprietary license boundary, and rollback criteria are reviewed.
@@ -156,7 +159,7 @@ Apply only after reviewing the plan:
156
159
  vgxness setup apply --yes
157
160
  ```
158
161
 
159
- `vgxness setup plan` and `vgxness setup status` are human-readable and read-only by default. `vgxness doctor`, `vgxness sdd status`, `vgxness sdd next`, and `vgxness sdd accept-artifact` are also human-readable by default. Pass `--json` when you need parseable automation output. `vgxness setup apply --yes` is the explicit provider-config write path.
162
+ `vgxness setup plan` and `vgxness setup status` are human-readable and do not write provider config by default; local VGXNESS store initialization may occur when a command needs the selected SQLite store. `vgxness doctor`, `vgxness sdd status`, `vgxness sdd next`, and `vgxness sdd accept-artifact` are also human-readable by default. Pass `--json` when you need parseable automation output. `vgxness setup apply --yes` is the explicit provider-config write path.
160
163
 
161
164
  ## Code runtime (`vgxness code`)
162
165
 
@@ -173,7 +176,7 @@ Edits, shell, network, git mutations, SDD persistence, and memory saves route th
173
176
 
174
177
  ## Safety model
175
178
 
176
- - Preview, status, and plan commands are read-only.
179
+ - Preview, status, and plan commands do not write provider config; local VGXNESS store initialization may occur where the command needs SQLite-backed state.
177
180
  - Provider config writes require explicit `--yes` confirmation.
178
181
  - Setup/status TUI surfaces are preview-oriented; run copied commands explicitly when you choose to act.
179
182
  - SDD artifacts are SQLite-backed through VGXNESS services. Do not create or write `openspec/`.
@@ -236,6 +239,7 @@ bun install --frozen-lockfile
236
239
  bun run check:bun-lock
237
240
  bun run verify:typecheck
238
241
  bun run verify:test
242
+ bun run verify:test:bun-storage
239
243
  bun run verify:bun-sqlite
240
244
  bun run package:bun:evidence -- --require-pass
241
245
  ```
@@ -255,6 +259,7 @@ Remove any OpenCode config entries and local/global VGXNESS data manually if you
255
259
  ## More docs
256
260
 
257
261
  - [CLI reference](./docs/cli.md)
262
+ - [Project health audit v1.9.1](./docs/project-health-audit-v1.9.1.md)
258
263
  - [Architecture](./docs/architecture.md)
259
264
  - [Code runtime](./docs/code-runtime.md)
260
265
  - [MCP tools](./docs/mcp.md)
@@ -98,14 +98,23 @@ export class AgentActivationService {
98
98
  return this.dependencies.agents.getAgentByName(input.project, input.scope, defaultAgentName);
99
99
  }
100
100
  failAfterRun(runId, code, message) {
101
- this.dependencies.runs.appendEvent({
101
+ const nestedFailures = [];
102
+ const eventResult = this.dependencies.runs.appendEvent({
102
103
  runId,
103
104
  kind: 'timeline',
104
105
  title: 'Agent activation failed',
105
- payload: { error: { code: String(code), message }, nextSafeActionReturned: false },
106
+ payload: toJson({ error: { code: String(code), message }, nextSafeActionReturned: false }),
106
107
  });
107
- this.dependencies.runs.updateFinalStatus({ runId, status: 'failed', outcome: 'failure', outcomeReason: message });
108
- return { ok: false, error: { code, message } };
108
+ if (!eventResult.ok) {
109
+ nestedFailures.push({ step: 'append-event', code: String(eventResult.error.code), message: eventResult.error.message });
110
+ }
111
+ const finalStatusResult = this.dependencies.runs.updateFinalStatus({ runId, status: 'failed', outcome: 'failure', outcomeReason: message });
112
+ if (!finalStatusResult.ok) {
113
+ nestedFailures.push({ step: 'update-final-status', code: String(finalStatusResult.error.code), message: finalStatusResult.error.message });
114
+ }
115
+ return nestedFailures.length === 0
116
+ ? { ok: false, error: { code, message } }
117
+ : { ok: false, error: { code, message, cause: { nestedFailures } } };
109
118
  }
110
119
  }
111
120
  function validateActivationInput(input) {
@@ -32,8 +32,14 @@ export class AgentRegistryService {
32
32
  this.database.close();
33
33
  }
34
34
  listAgentDefinitions(input) {
35
- void input;
36
- const summaries = this.agents.list();
35
+ const filters = {};
36
+ if (input.project !== undefined)
37
+ filters.project = input.project;
38
+ if (input.scope !== undefined)
39
+ filters.scope = input.scope;
40
+ if (input.mode !== undefined)
41
+ filters.mode = input.mode;
42
+ const summaries = this.agents.list(filters);
37
43
  if (!summaries.ok)
38
44
  return summaries;
39
45
  const definitions = [];
@@ -0,0 +1,231 @@
1
+ import { canonicalAgentManifest, canonicalPromptContractVersion, canonicalSddSubagentNames } from './canonical-agent-manifest.js';
2
+ import { projectCanonicalAgentManifestToSeed } from './canonical-agent-projection.js';
3
+ export class AgentSeedUpgradeService {
4
+ agents;
5
+ history;
6
+ database;
7
+ constructor(dependencies) {
8
+ this.agents = dependencies.agents;
9
+ this.history = dependencies.history;
10
+ this.database = dependencies.database;
11
+ }
12
+ upgrade(input) {
13
+ if (!input.project.trim())
14
+ return validationFailure('Agent seed upgrade project is required');
15
+ if (input.scope !== 'project' && input.scope !== 'personal')
16
+ return validationFailure('Agent seed upgrade scope is invalid');
17
+ const seed = projectCanonicalAgentManifestToSeed();
18
+ const canonicalByName = new Map();
19
+ for (const agent of seed.agents)
20
+ canonicalByName.set(agent.name, agent);
21
+ for (const subagent of seed.subagents)
22
+ canonicalByName.set(subagent.name, subagent);
23
+ const source = input.source ?? 'boot-upgrade';
24
+ const summary = {
25
+ created: [],
26
+ upgraded: [],
27
+ noop: [],
28
+ overwritten: [],
29
+ errors: [],
30
+ skipped: false,
31
+ };
32
+ const expected = canonicalPromptContractVersion;
33
+ const transaction = this.database.transaction(() => {
34
+ for (const [name, definition] of canonicalByName) {
35
+ const outcome = this.upgradeOne(input.project, input.scope, name, definition, source, expected);
36
+ if (outcome.ok) {
37
+ switch (outcome.value.outcome) {
38
+ case 'created':
39
+ summary.created.push(name);
40
+ break;
41
+ case 'upgraded':
42
+ summary.upgraded.push(name);
43
+ break;
44
+ case 'overwrote-custom-instructions':
45
+ summary.overwritten.push(name);
46
+ break;
47
+ case 'noop':
48
+ summary.noop.push(name);
49
+ break;
50
+ }
51
+ }
52
+ else {
53
+ const failureOutcome = outcome.error.code === 'validation_failed' ? 'validation_failed' : 'db_error';
54
+ summary.errors.push({ agentName: name, outcome: failureOutcome, reason: outcome.error.message });
55
+ }
56
+ }
57
+ return summary;
58
+ });
59
+ if (!transaction.ok) {
60
+ return { ok: false, error: transaction.error };
61
+ }
62
+ return transaction;
63
+ }
64
+ hasDrift(input) {
65
+ const expected = input.expectedVersion ?? canonicalPromptContractVersion;
66
+ try {
67
+ const canonicalNames = [canonicalAgentManifest.defaultAgentName, ...canonicalSddSubagentNames];
68
+ const placeholders = canonicalNames.map(() => '?').join(', ');
69
+ const row = this.database.connection
70
+ .prepare(`
71
+ SELECT COUNT(*) AS stale_count
72
+ FROM agents
73
+ WHERE project = ? AND scope = ? AND name IN (${placeholders})
74
+ AND json_extract(adapters_json, '$.opencode.config.options.vgxnessPromptContractVersion') <> ?
75
+ `)
76
+ .get(input.project, input.scope, ...canonicalNames, expected);
77
+ return ok((row?.stale_count ?? 0) > 0);
78
+ }
79
+ catch (cause) {
80
+ return fail('Failed to detect agent seed drift', cause);
81
+ }
82
+ }
83
+ upgradeOne(project, scope, name, definition, source, expected) {
84
+ const existing = this.agents.getByName(project, scope, name);
85
+ const fromVersion = existing.ok ? readPromptContractVersion(existing.value) : null;
86
+ if (existing.ok && fromVersion === expected && isByteEqualToCanonical(existing.value, definition)) {
87
+ return ok({ outcome: 'noop', fromVersion: expected });
88
+ }
89
+ const parentAgentId = 'parentAgentName' in definition ? this.resolveParentId(project, scope, definition.parentAgentName) : undefined;
90
+ if ('parentAgentName' in definition && parentAgentId === undefined) {
91
+ return validationFailure(`Unresolved parentAgentName: ${definition.parentAgentName}`);
92
+ }
93
+ const registerInput = toRegisterInput(project, scope, definition, parentAgentId);
94
+ const registered = this.agents.register(registerInput);
95
+ if (!registered.ok) {
96
+ this.appendHistory({
97
+ project,
98
+ scope,
99
+ agentName: name,
100
+ fromVersion,
101
+ toVersion: expected,
102
+ outcome: registered.error.code === 'validation_failed' ? 'validation_failed' : 'db_error',
103
+ reason: registered.error.message,
104
+ source,
105
+ });
106
+ return registered;
107
+ }
108
+ let outcome;
109
+ if (!existing.ok) {
110
+ outcome = 'created';
111
+ }
112
+ else if (fromVersion !== null && fromVersion !== expected) {
113
+ outcome = 'upgraded';
114
+ }
115
+ else {
116
+ outcome = 'overwrote-custom-instructions';
117
+ }
118
+ this.appendHistory({
119
+ project,
120
+ scope,
121
+ agentName: name,
122
+ fromVersion,
123
+ toVersion: expected,
124
+ outcome,
125
+ reason: outcomeReason(outcome, fromVersion, expected, existing.ok),
126
+ source,
127
+ });
128
+ return ok({ outcome, fromVersion });
129
+ }
130
+ resolveParentId(project, scope, parentAgentName) {
131
+ const parent = this.agents.getByName(project, scope, parentAgentName);
132
+ if (!parent.ok || parent.value.mode !== 'agent')
133
+ return undefined;
134
+ return parent.value.id;
135
+ }
136
+ appendHistory(input) {
137
+ this.history.append({
138
+ project: input.project,
139
+ scope: input.scope,
140
+ agentName: input.agentName,
141
+ fromVersion: input.fromVersion,
142
+ toVersion: input.toVersion,
143
+ outcome: input.outcome,
144
+ reason: input.reason,
145
+ source: input.source,
146
+ });
147
+ }
148
+ }
149
+ function toRegisterInput(project, scope, definition, parentAgentId) {
150
+ const input = {
151
+ project,
152
+ scope,
153
+ mode: 'parentAgentName' in definition ? 'subagent' : 'agent',
154
+ name: definition.name,
155
+ description: definition.description,
156
+ instructions: definition.instructions,
157
+ };
158
+ if (definition.capabilities !== undefined)
159
+ input.capabilities = definition.capabilities;
160
+ if (definition.permissions !== undefined)
161
+ input.permissions = definition.permissions;
162
+ if (definition.memory !== undefined)
163
+ input.memory = definition.memory;
164
+ if (definition.workflows !== undefined)
165
+ input.workflows = definition.workflows;
166
+ if (definition.skills !== undefined)
167
+ input.skills = definition.skills;
168
+ if (definition.adapters !== undefined)
169
+ input.adapters = definition.adapters;
170
+ if (parentAgentId !== undefined)
171
+ input.parentAgentId = parentAgentId;
172
+ return input;
173
+ }
174
+ function isByteEqualToCanonical(existing, definition) {
175
+ if (existing.description !== definition.description)
176
+ return false;
177
+ if (existing.instructions.value !== definition.instructions.value)
178
+ return false;
179
+ if (!adaptersEqual(existing.adapters, definition.adapters ?? {}))
180
+ return false;
181
+ if (!arraysEqual(existing.capabilities, definition.capabilities ?? []))
182
+ return false;
183
+ if (!arraysEqual(existing.workflows, definition.workflows ?? []))
184
+ return false;
185
+ if (!arraysEqual(existing.skills, definition.skills ?? []))
186
+ return false;
187
+ return true;
188
+ }
189
+ function adaptersEqual(a, b) {
190
+ return JSON.stringify(a ?? {}) === JSON.stringify(b ?? {});
191
+ }
192
+ function arraysEqual(a, b) {
193
+ if (a.length !== b.length)
194
+ return false;
195
+ for (let i = 0; i < a.length; i += 1) {
196
+ if (a[i] !== b[i])
197
+ return false;
198
+ }
199
+ return true;
200
+ }
201
+ function readPromptContractVersion(agent) {
202
+ const opencode = agent.adapters?.opencode;
203
+ if (opencode === undefined)
204
+ return null;
205
+ const options = opencode.config?.options;
206
+ const value = options?.vgxnessPromptContractVersion;
207
+ if (typeof value !== 'number' || !Number.isInteger(value))
208
+ return null;
209
+ return value;
210
+ }
211
+ function outcomeReason(outcome, fromVersion, toVersion, existed) {
212
+ if (outcome === 'created')
213
+ return `created canonical row at v${toVersion}`;
214
+ if (outcome === 'upgraded')
215
+ return `upgraded v${fromVersion ?? 'unknown'} -> v${toVersion}`;
216
+ if (outcome === 'overwrote-custom-instructions')
217
+ return `overwrote customized row (was v${fromVersion ?? 'unknown'}) with v${toVersion} canonical`;
218
+ return existed ? `noop at v${toVersion}` : `noop at v${toVersion}`;
219
+ }
220
+ function ok(value) {
221
+ return { ok: true, value };
222
+ }
223
+ function validationFailure(message) {
224
+ return { ok: false, error: { code: 'validation_failed', message } };
225
+ }
226
+ function fail(message, cause) {
227
+ const error = { code: 'validation_failed', message };
228
+ if (cause !== undefined)
229
+ error.cause = cause;
230
+ return { ok: false, error };
231
+ }
@@ -0,0 +1,59 @@
1
+ import { AgentSeedUpgradeService } from './agent-seed-upgrade-service.js';
2
+ import { AgentRepository } from './repositories/agents.js';
3
+ import { AgentSeedHistoryRepository } from './repositories/agent-seed-history.js';
4
+ import { canonicalAgentManifest } from './canonical-agent-manifest.js';
5
+ const skipEnvVar = 'VGXNESS_SKIP_AGENT_SEED_AUTO_UPGRADE';
6
+ export function runBootAgentSeedUpgrade(database, env = process.env, project = canonicalAgentManifest.project, scope = canonicalAgentManifest.scope) {
7
+ if (isOptOut(env)) {
8
+ return { ok: true, skipped: true, created: 0, upgraded: 0, overwritten: 0, noop: 0, errors: 0 };
9
+ }
10
+ try {
11
+ const service = new AgentSeedUpgradeService({
12
+ agents: new AgentRepository(database),
13
+ history: new AgentSeedHistoryRepository(database),
14
+ database,
15
+ });
16
+ const result = service.upgrade({ project, scope, source: 'boot-upgrade' });
17
+ if (!result.ok) {
18
+ const message = `boot-upgrade failed: ${result.error.message} (code=${result.error.code})`;
19
+ process.stderr.write(`[vgxness] ${message}\n`);
20
+ return { ok: false, skipped: false, created: 0, upgraded: 0, overwritten: 0, noop: 0, errors: 1, message };
21
+ }
22
+ const value = result.value;
23
+ const touched = value.created.length + value.upgraded.length + value.overwritten.length + value.errors.length;
24
+ if (touched > 0) {
25
+ const parts = [];
26
+ if (value.created.length > 0)
27
+ parts.push(`created ${value.created.length}`);
28
+ if (value.upgraded.length > 0)
29
+ parts.push(`upgraded ${value.upgraded.length}`);
30
+ if (value.overwritten.length > 0)
31
+ parts.push(`overwrote ${value.overwritten.length}`);
32
+ if (value.errors.length > 0)
33
+ parts.push(`errors ${value.errors.length}`);
34
+ process.stderr.write(`[vgxness] boot-upgrade: ${parts.join(', ')}\n`);
35
+ for (const error of value.errors) {
36
+ process.stderr.write(`[vgxness] boot-upgrade: ${error.agentName} ${error.outcome}: ${error.reason}\n`);
37
+ }
38
+ }
39
+ return {
40
+ ok: true,
41
+ skipped: false,
42
+ created: value.created.length,
43
+ upgraded: value.upgraded.length,
44
+ overwritten: value.overwritten.length,
45
+ noop: value.noop.length,
46
+ errors: value.errors.length,
47
+ };
48
+ }
49
+ catch (cause) {
50
+ const detail = cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
51
+ const message = `boot-upgrade threw: ${detail}`;
52
+ process.stderr.write(`[vgxness] ${message}\n`);
53
+ return { ok: false, skipped: false, created: 0, upgraded: 0, overwritten: 0, noop: 0, errors: 1, message };
54
+ }
55
+ }
56
+ function isOptOut(env) {
57
+ const value = env[skipEnvVar];
58
+ return value === '1' || value === 'true';
59
+ }
@@ -1,5 +1,6 @@
1
+ import { canonicalBehaviorContractVersion } from '../behavior/behavior-contract-manifest.js';
1
2
  export const canonicalDefaultAgentName = 'vgxness-manager';
2
- export const canonicalPromptContractVersion = 6;
3
+ export const canonicalPromptContractVersion = 7;
3
4
  export const canonicalSddSubagentNames = [
4
5
  'vgxness-sdd-explore',
5
6
  'vgxness-sdd-propose',
@@ -81,7 +82,7 @@ function managerDefinition() {
81
82
  memory: { scopes: ['project'] },
82
83
  workflows: ['explore', 'quickfix', 'plan', 'build', 'debug', 'sdd', 'agent-seeding', 'opencode-install'],
83
84
  skills: ['vgxness-sdd-manager'],
84
- adapters: { opencode: { model: 'openai/gpt-5.5', config: { options: { reasoningEffort: 'high', vgxnessPromptContractVersion: canonicalPromptContractVersion }, permission: { task: createCanonicalOpenCodeSddTaskPermissions() } } } },
85
+ adapters: { opencode: { model: 'openai/gpt-5.5', config: { options: { reasoningEffort: 'high', vgxnessPromptContractVersion: canonicalPromptContractVersion, vgxnessBehaviorContractVersion: canonicalBehaviorContractVersion }, permission: { task: createCanonicalOpenCodeSddTaskPermissions() } } } },
85
86
  providerSupport: commonSupport(),
86
87
  };
87
88
  }
@@ -100,7 +101,7 @@ function subagentDefinition(name) {
100
101
  memory: { scopes: ['project'] },
101
102
  workflows: data.workflows,
102
103
  skills: data.skills,
103
- adapters: { opencode: { model: 'openai/gpt-5.5', config: { hidden: true, options: { vgxnessPromptContractVersion: canonicalPromptContractVersion } } } },
104
+ adapters: { opencode: { model: 'openai/gpt-5.5', config: { hidden: true, options: { vgxnessPromptContractVersion: canonicalPromptContractVersion, vgxnessBehaviorContractVersion: canonicalBehaviorContractVersion } } } },
104
105
  providerSupport: commonSupport(),
105
106
  };
106
107
  }
@@ -118,45 +119,44 @@ export const canonicalOpenCodeManagerPrompt = `# VGXNESS Manager - compact SDD o
118
119
  Bind only to the primary \`vgxness-manager\` agent. Executor agents (for example \`vgxness-sdd-apply\`) use their own prompts.
119
120
 
120
121
  ## Role
121
- Coordinate SDD; do not become a monolithic executor. Keep the chat thin, use VGXNESS MCP as durable state, delegate phase work to the smallest exact hidden SDD subagent, then synthesize concise evidence for the user. Be direct, practical, repository-grounded, user-controlled, and willing to challenge unsafe or weak assumptions.
122
-
123
- Coach while coordinating: teach briefly when helpful, explain practical tradeoffs, stay realistic about risk/effort/unknowns, respectfully challenge weak assumptions, keep the user comfortable and in control, and avoid lectures or unnecessary verbosity.
122
+ Coordinate SDD; keep chat thin, use VGXNESS MCP as durable state, delegate to the smallest exact hidden SDD subagent, then synthesize evidence. Coach while coordinating: teach briefly, explain practical tradeoffs, stay realistic about risk/effort/unknowns, respectfully challenge weak assumptions, keep the user comfortable and in control, and avoid lectures or unnecessary verbosity.
124
123
 
125
124
  ## Non-negotiable governance
126
- - SDD artifact acceptance is human-only. Never infer acceptance from generated output, file presence, confidence, or legacy artifacts. Treat draft/rejected/superseded/unaccepted artifacts as not accepted until a human acceptance record exists.
125
+ - SDD artifact acceptance is human-only. Never infer or fabricate acceptance from generated output, subagent/model output, file presence, confidence, or legacy artifacts. Treat draft/rejected/superseded/stale/unaccepted artifacts as not accepted until a human acceptance record exists.
127
126
  - Before phase advancement, call readiness/status tools: \`vgxness_sdd_status\`/\`vgxness_sdd_next\` and \`vgxness_sdd_ready\` or \`vgxness_sdd_get_readiness\`.
128
127
  - Before risky VGX-managed side effects (edit, shell/tests, git, network, provider-tool, secrets, external-directory, destructive, privileged, ambiguous), call \`vgxness_run_preflight\` with runId/workflow/phase/agent context when available. If approval/block is required, stop; do not invent approval.
128
+ - Direct human acceptance of an exact SDD artifact via \`vgxness_sdd_accept_artifact\` is not a generic SDD write for manager routing: do not call \`vgxness_run_preflight\` solely for that acceptance when the user explicitly accepted the exact project/change/phase artifact, \`acceptedBy.type\` is \`"human"\`, \`acceptedBy.id\` is non-empty, and status/readiness confirms the artifact is eligible. This shortcut applies only to \`vgxness_sdd_accept_artifact\`, not \`vgxness_sdd_save_artifact\`, edits, shell/tests, git, provider config, memory writes, external paths, secrets, destructive/privileged/ambiguous operations.
129
129
  - OpenCode native/provider tools are governance-v1 audit-only/non-hard-blocking. Report warnings; do not say native OpenCode tools are hard-blocked by config.
130
130
  - Do not mutate provider/global OpenCode config unless explicitly requested. Do not write to \`openspec/\`. Never revert/overwrite unrelated user work. Preserve \`permission.task\` deny-by-default with only exact known SDD subagents allowed.
131
131
  - Do not publish packages unless explicitly requested.
132
132
  - Do not change model or reasoning effort.
133
133
 
134
134
  ## Flexible governance routing
135
- Use the lightest safe path: Tier 0-2 direct/explore/plan/debug/quickfix/build; keywords alone do not force SDD. Tier 3 needs preflight/explicit validation but not automatic SDD. Tier 4 governance, permission model, SDD acceptance, architecture/security semantics, or cross-surface workflow behavior uses formal SDD. Provider status/doctor/preview/handoff are read-only audit-only; provider config writes stay gated.
135
+ Use the lightest safe path: Tier 0-2 direct; Tier 3 preflight/explicit validation; Tier 4 formal SDD for governance, permission model, SDD acceptance, architecture/security, or cross-surface behavior. Provider status/doctor/preview/handoff are read-only audit-only; provider config writes stay gated.
136
136
 
137
137
  ## Provider-native daily flow
138
- Normal SDD progression happens inside OpenCode through conversation, VGXNESS MCP, and hidden SDD subagents. Do not tell users to run terminal SDD phase commands for daily flow. CLI is an escape hatch only for bootstrap, doctor, rollback/recovery, MCP unavailable/setup missing, provider-native repair out of scope, or explicit user request.
138
+ Daily SDD happens inside OpenCode through conversation, VGXNESS MCP, and hidden SDD subagents. Do not tell users to run terminal SDD phase commands for normal flow. CLI is an escape hatch for bootstrap, doctor, recovery, setup gaps, or explicit request.
139
139
 
140
140
  ## MCP playbook
141
- - For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\` with project + workspace; treat \`vgxness_session_restore\` as one signal inside the cockpit, not authoritative truth. If cockpit is unavailable, use \`vgxness_session_restore\` with project + workspace before inferring state. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with current session id and actor \`manager\`; if no id, do not invent one, say so and include summary in final response.
142
- - SDD artifacts: list/read with \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`; use \`payloadMode: "compact"\` by default for manager-facing reads/lists so the primary context stays clean. Use \`payloadMode: "verbose"\` only when full content is truly required, preferably inside the delegated phase subagent rather than the manager context. Save phase output with \`vgxness_sdd_save_artifact\` only after the appropriate flow. SDD artifacts are not generic memory.
143
- - Acceptance/readiness: confirm explicit human acceptance, use \`vgxness_sdd_accept_artifact\` only for explicit human acceptance, then \`vgxness_sdd_get_readiness\`/readiness tools before reporting state. Use \`vgxness_governance_report\` for readiness, artifact states, preflight posture, and audit warnings before risky/ambiguous transitions and in apply/verify summaries.
144
- - Memory: call \`vgxness_memory_search\`/\`vgxness_memory_get\` when prior work or unclear project context is referenced; call \`vgxness_memory_save\` for durable discoveries, decisions, bug fixes, config, patterns, or preferences; use \`vgxness_memory_update\` only to correct/evolve a known id. Do not duplicate full SDD artifacts as memory.
145
- - Agents/profile/payloads: resolve exact phase with \`vgxness_agent_resolve\`. \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, and \`vgxness_skill_payload\` prepare/read preview context only; agent_activate does not execute a provider or write provider config. Use \`vgxness_manager_profile_get\` before behavior changes; \`vgxness_manager_profile_set\` requires explicit human authorization.
141
+ - For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\` with project + workspace; treat \`vgxness_session_restore\` as one signal, not truth. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with current session id and actor \`manager\`; if no id, do not invent one and summarize.
142
+ - SDD artifacts: list/read with \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`; use \`payloadMode: "compact"\` by default so the primary context stays clean. Use verbose only when required, preferably inside the delegated phase subagent. Save with \`vgxness_sdd_save_artifact\` only after the appropriate flow. SDD artifacts are not generic memory.
143
+ - Acceptance/readiness: check SDD status/readiness for the exact project/change/phase, confirm explicit human acceptance of that exact artifact, call \`vgxness_sdd_accept_artifact\` directly with audit context (\`acceptedBy.type: "human"\`, non-empty \`acceptedBy.id\`, runId, agentId, note/rationale when useful), then re-check status/readiness before reporting state. Do not add \`vgxness_run_preflight\` solely for that exact direct acceptance call. Ambiguous replies count only when tied to an immediate exact acceptance prompt. Use \`vgxness_governance_report\` for readiness, artifact states, preflight posture, and audit warnings before risky/ambiguous transitions and in apply/verify summaries.
144
+ - Memory: call \`vgxness_memory_search\`/\`vgxness_memory_get\` for prior work or unclear context; call \`vgxness_memory_save\` for durable discoveries, decisions, bug fixes, config, patterns, or preferences; use \`vgxness_memory_update\` only to correct/evolve a known id. Do not duplicate full SDD artifacts as memory.
145
+ - Agents/profile/payloads: resolve exact phase with \`vgxness_agent_resolve\`. \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, and \`vgxness_skill_payload\` are preview context only; agent_activate does not execute a provider or write provider config. Use \`vgxness_manager_profile_get\` before behavior changes; \`vgxness_manager_profile_set\` requires explicit human authorization.
146
146
  - Runs: use \`vgxness_run_start\`/\`vgxness_run_list\`/\`vgxness_run_get\` for significant implementation/verification or multi-step delegated work; checkpoint with \`vgxness_run_checkpoint\`, preflight with \`vgxness_run_preflight\`, and close with \`vgxness_run_finalize\`.
147
147
  - Provider diagnostics: \`vgxness_provider_status\` and \`vgxness_provider_doctor\` are read-only reports.
148
148
 
149
149
  ## Minimum flows
150
- - Simple answer: respond inline; memory only if prior context is referenced; no run by default.
151
- - Proposal/spec/design/tasks: status/next -> readiness -> read prerequisites -> resolve exact subagent -> delegate -> persist returned output only according to acceptance/governance.
152
- - Apply/verify: require tasks/apply-progress as appropriate -> read artifacts -> resolve exact subagent -> recover/start run -> preflight writes/shell/git/tests -> delegate -> checkpoint -> save apply-progress/verify -> finalize when clear.
150
+ - Simple answer: inline; memory only for prior context; no run by default.
151
+ - Proposal/spec/design/tasks: status/next -> readiness -> read prerequisites -> resolve exact subagent -> delegate -> persist by governance.
152
+ - Apply/verify: require prerequisites -> read artifacts -> resolve exact subagent -> start/recover run -> preflight writes/shell/git/tests -> delegate -> checkpoint -> save output -> finalize when clear.
153
153
  - Config/provider/prompt change: inspect manager/profile or payload first; persistent changes require explicit human authorization.
154
154
 
155
155
  ## Delegation thresholds
156
- Inline only small decisions, 1-3 file reads, status commands, and atomic one-file mechanical edits. Delegate broad exploration (4+ files), substantial implementation, writes with analysis/new logic, and execution-heavy verification. Never delegate to unknown agents; use exact SDD phase mapping: explore/propose/spec/design/tasks/apply/verify/archive/init/onboard.
156
+ Inline only small decisions, 1-3 file reads, status commands, and atomic one-file mechanical edits. Delegate broad exploration, substantial implementation, writes with analysis/new logic, and execution-heavy verification. Never delegate to unknown agents; use exact SDD phase mapping.
157
157
 
158
158
  ## Output
159
- Be concise: what was delegated, what came back, files/decisions changed, evidence/tests, risks, and next step.`;
159
+ Be concise: delegated work, results, files/decisions, evidence/tests, risks, next step.`;
160
160
  const registryManagerInstructions = 'You are the VGXNESS SDD coordinator, not a monolithic executor. Coach briefly while coordinating: explain useful tradeoffs, be realistic about risks and unknowns, respectfully challenge weak assumptions with better options, keep the user comfortable and in control, and stay concise. Use VGXNESS MCP as the durable control plane: restore session context with vgxness_session_restore before inferring start/resume state from chat, and close/pause/compact with vgxness_session_close using actor manager plus an actionable summary when a current session id exists. Check SDD status/next/ready, read prerequisites with sdd_get_artifact or sdd_list_artifacts, save accepted phase output with sdd_save_artifact, search/get/save/update memory only for reusable knowledge, resolve exact SDD subagents before substantial phase work, use runs/checkpoints/preflight/finalize for significant implementation or verification, and use vgxness_provider_status for configured/phase/next questions plus vgxness_provider_doctor for read-only OpenCode MCP/manager health. Prefer payloadMode=compact for manager-facing status/context reads, SDD artifact reads/lists, and activation handoffs so the primary context stays clean; request payloadMode=verbose only when full artifact contents, provider payloads, or skill context are actually needed, preferably inside delegated phase subagents. CLI is an escape hatch for bootstrap, doctor, rollback, recovery, MCP unavailable/setup missing, or explicit user request; do not tell users to run terminal SDD phase commands for normal daily flow. Delegate real SDD phase work to the smallest exact vgxness-sdd-* subagent allowed by permission.task, synthesize results, and persist artifacts/checkpoints. Do not perform substantial multi-file implementation inline. Do not mutate global/provider OpenCode config, install skills, publish packages, or write openspec/ unless explicitly authorized. Checked-in manager and subagent instructions are self-contained; external sdd-* skill files are optional registry assets, not requirements.';
161
161
  const subagentData = {
162
162
  'vgxness-sdd-explore': { seedDescription: 'Investigates codebase context and identifies options before proposals.', seedInstructions: 'You are the explore phase executor, not the orchestrator. Do not delegate. Explore repository evidence for the requested SDD change. Do not implement code changes. Return concise findings, risks, and recommended next artifacts.', capabilities: ['sdd-exploration', 'codebase-research', 'discovery'], permissions: { read: 'allow', edit: 'deny', shell: 'ask', git: 'deny', memory: 'allow', 'provider-tool': 'deny', secrets: 'deny' }, workflows: ['sdd:explore'], skills: ['vgxness-sdd-explore'], phaseContract: 'Investigate codebase context, constraints, options, and risks. Do not implement code changes. Return findings and recommended next artifacts.' },
@@ -73,6 +73,13 @@ export function projectCanonicalAgentManifestToClaudeCode(manifest = canonicalAg
73
73
  }
74
74
  return { defaultAgent: canonicalDefaultAgentName, agents };
75
75
  }
76
+ export function withEffectiveManagerInstructions(projection, instructions) {
77
+ const trimmed = instructions?.trim() ?? '';
78
+ if (trimmed === '')
79
+ return projection;
80
+ const agents = projection.agents.map((agent) => agent.canonicalName === projection.defaultAgent ? { ...agent, instructions: trimmed } : agent);
81
+ return { defaultAgent: projection.defaultAgent, agents };
82
+ }
76
83
  export function projectCanonicalAgentManifestToClaudeProjectMemory(manifest = canonicalAgentManifest) {
77
84
  assertValidCanonicalManifest(manifest);
78
85
  return {
@@ -1,3 +1,5 @@
1
+ import { AgentRegistryService } from './agent-registry-service.js';
2
+ import { ManagerProfileOverlayRepository } from './repositories/manager-profile-overlays.js';
1
3
  export class ManagerProfileOverlayService {
2
4
  dependencies;
3
5
  constructor(dependencies) {
@@ -36,3 +38,15 @@ function ok(value) {
36
38
  function validationFailure(message) {
37
39
  return { ok: false, error: { code: 'validation_failed', message } };
38
40
  }
41
+ export function computeEffectiveManagerInstructions(database, project = 'vgxness', scope = 'project', managerName = 'vgxness-manager') {
42
+ const registry = new AgentRegistryService(database);
43
+ const overlays = new ManagerProfileOverlayRepository(database);
44
+ const service = new ManagerProfileOverlayService({ agents: registry, overlays });
45
+ const resolved = service.resolveEffectiveManager({ project, scope, managerName });
46
+ if (!resolved.ok || resolved.value.overlay === undefined)
47
+ return undefined;
48
+ const overlayInstructions = resolved.value.overlay.instructions.trim();
49
+ if (overlayInstructions === '')
50
+ return undefined;
51
+ return overlayInstructions;
52
+ }
@@ -1,3 +1,5 @@
1
+ import { canonicalPromptContractVersion } from '../canonical-agent-manifest.js';
2
+ import { canonicalBehaviorContractVersion } from '../../behavior/behavior-contract-manifest.js';
1
3
  import { ok, validationFailure } from './provider-adapter.js';
2
4
  const previewWarnings = [
3
5
  'Claude rendering returns installable preview artifacts only; it does not install and does not write provider configuration.',
@@ -66,7 +68,7 @@ export class ClaudeAgentRenderer {
66
68
  }
67
69
  }
68
70
  function renderClaudeAgentMarkdown(agent, key) {
69
- return `---\nname: ${JSON.stringify(key)}\ndescription: ${JSON.stringify(agent.description)}\n---\n\n<!-- VGXNESS-GENERATED claude-code-provider-support provider=claude artifact=claude-code-subagent promptContractVersion=6 safe-update=true -->\n\n${agent.instructions.value.trim()}\n`;
71
+ return `---\nname: ${JSON.stringify(key)}\ndescription: ${JSON.stringify(agent.description)}\n---\n\n<!-- VGXNESS-GENERATED claude-code-provider-support provider=claude artifact=claude-code-subagent promptContractVersion=${canonicalPromptContractVersion} behaviorContractVersion=${canonicalBehaviorContractVersion} safe-update=true -->\n\n${agent.instructions.value.trim()}\n`;
70
72
  }
71
73
  function claudeAgentKey(value) {
72
74
  return pathSegment(value);
@@ -1,3 +1,4 @@
1
+ import { behaviorContractProjectionMetadata } from '../../behavior/behavior-contract-manifest.js';
1
2
  import { ok, validationFailure } from './provider-adapter.js';
2
3
  const openCodeSchema = 'https://opencode.ai/config.json';
3
4
  const previewWarning = 'Rendering returns preview artifacts only; it does not install or write .opencode/, .claude/, or provider configuration.';
@@ -50,7 +51,7 @@ export class OpenCodeAgentRenderer {
50
51
  const artifact = {
51
52
  relativePath: `rendered/opencode/${pathSegment(input.agent.project)}/${input.agent.scope}/${pathSegment(input.agent.name)}/opencode.json`,
52
53
  contentType: 'application/json',
53
- contents: `${JSON.stringify({ $schema: openCodeSchema, agent: renderedAgents, safety: previewSafety }, null, 2)}\n`,
54
+ contents: `${JSON.stringify({ $schema: openCodeSchema, behaviorContract: behaviorContractProjectionMetadata, agent: renderedAgents, safety: previewSafety }, null, 2)}\n`,
54
55
  };
55
56
  return ok({
56
57
  provider: this.provider,