vgxness 1.5.1 → 1.6.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.
Files changed (56) hide show
  1. package/README.md +23 -2
  2. package/dist/agents/agent-seed-service.js +10 -0
  3. package/dist/agents/canonical-agent-manifest.js +177 -0
  4. package/dist/agents/canonical-agent-projection.js +164 -0
  5. package/dist/agents/renderers/claude-renderer.js +30 -52
  6. package/dist/cli/bun-bin.js +6 -0
  7. package/dist/cli/cli-flags.js +1 -1
  8. package/dist/cli/cli-help.js +7 -4
  9. package/dist/cli/commands/agent-skill-dispatcher.js +6 -5
  10. package/dist/cli/commands/interactive-entrypoint-dispatcher.js +2 -2
  11. package/dist/cli/commands/mcp-dispatcher.js +75 -3
  12. package/dist/cli/commands/setup-dispatcher.js +9 -0
  13. package/dist/cli/index.js +1 -1
  14. package/dist/cli/tui/main-menu/main-menu-read-model.js +41 -44
  15. package/dist/cli/tui/main-menu/main-menu-render-shape.js +15 -15
  16. package/dist/cli/tui/opentui/main-menu/screen.js +39 -41
  17. package/dist/cli/tui/opentui/main-menu/smoke.js +1 -1
  18. package/dist/cli/tui/opentui/main-menu/view.js +1 -1
  19. package/dist/cli/tui/setup/setup-tui-read-model.js +15 -12
  20. package/dist/governance/governance-report-builder.js +45 -26
  21. package/dist/mcp/claude-code-agent-config.js +95 -0
  22. package/dist/mcp/claude-code-cli.js +71 -0
  23. package/dist/mcp/claude-code-config.js +84 -0
  24. package/dist/mcp/claude-code-project-memory.js +127 -0
  25. package/dist/mcp/claude-code-scope.js +18 -0
  26. package/dist/mcp/client-install-claude-code-contract.js +114 -0
  27. package/dist/mcp/client-install-claude-code.js +136 -0
  28. package/dist/mcp/index.js +8 -0
  29. package/dist/mcp/opencode-default-agent-config.js +7 -113
  30. package/dist/mcp/provider-canonical-agent-manifest.js +39 -0
  31. package/dist/mcp/provider-change-plan.js +109 -1
  32. package/dist/mcp/provider-doctor.js +105 -1
  33. package/dist/mcp/provider-health-types.js +4 -0
  34. package/dist/mcp/provider-status.js +159 -3
  35. package/dist/mcp/schema.js +6 -5
  36. package/dist/mcp/validation.js +1 -1
  37. package/dist/memory/memory-service.js +4 -0
  38. package/dist/sdd/sdd-workflow-service.js +129 -59
  39. package/dist/setup/providers/claude-setup-adapter.js +13 -8
  40. package/dist/setup/setup-plan.js +60 -1
  41. package/docs/architecture.md +55 -113
  42. package/docs/cli.md +90 -2
  43. package/docs/code-runtime.md +218 -0
  44. package/docs/contributing.md +120 -0
  45. package/docs/glossary.md +211 -0
  46. package/docs/mcp.md +144 -0
  47. package/docs/prd.md +23 -26
  48. package/docs/providers.md +150 -0
  49. package/docs/roadmap.md +88 -0
  50. package/docs/safety.md +147 -0
  51. package/docs/storage.md +93 -0
  52. package/package.json +1 -1
  53. package/docs/funcionamiento-del-sistema.md +0 -865
  54. package/docs/harness-gap-analysis.md +0 -243
  55. package/docs/vgxcode.md +0 -87
  56. package/docs/vgxness-code.md +0 -48
@@ -21,7 +21,9 @@ export class SddWorkflowService {
21
21
  if (!validated.ok)
22
22
  return validated;
23
23
  const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
24
- const readiness = getReadinessFromStatuses(validated.value.change, validated.value.phase, phases);
24
+ if (!phases.ok)
25
+ return phases;
26
+ const readiness = getReadinessFromStatuses(validated.value.change, validated.value.phase, phases.value);
25
27
  return {
26
28
  ok: true,
27
29
  value: {
@@ -36,20 +38,27 @@ export class SddWorkflowService {
36
38
  if (!validated.ok)
37
39
  return validated;
38
40
  const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
39
- return statusFromPhases(validated.value.change, phases);
41
+ if (!phases.ok)
42
+ return phases;
43
+ return statusFromPhases(validated.value.change, phases.value);
40
44
  }
41
45
  getNext(input) {
42
46
  const validated = validateProjectAndChange(input.project, input.change);
43
47
  if (!validated.ok)
44
48
  return validated;
45
49
  const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
46
- return ok(nextDecisionFromStatuses(validated.value.change, phases));
50
+ if (!phases.ok)
51
+ return phases;
52
+ return ok(nextDecisionFromStatuses(validated.value.change, phases.value));
47
53
  }
48
54
  getCockpit(input) {
49
55
  const validated = validateProjectAndChange(input.project, input.change);
50
56
  if (!validated.ok)
51
57
  return validated;
52
- const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
58
+ const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
59
+ if (!snapshot.ok)
60
+ return snapshot;
61
+ const phases = snapshot.value.phases;
53
62
  const next = nextDecisionFromStatuses(validated.value.change, phases);
54
63
  const cockpitPhases = phases.map((phaseStatus) => {
55
64
  const readiness = {
@@ -57,9 +66,7 @@ export class SddWorkflowService {
57
66
  phase: phaseStatus.phase,
58
67
  ...getReadinessFromStatuses(validated.value.change, phaseStatus.phase, phases),
59
68
  };
60
- const artifact = phaseStatus.present
61
- ? this.artifactSummaryForPhase(validated.value.project, validated.value.change, phaseStatus.phase, phaseStatus.topicKey)
62
- : undefined;
69
+ const artifact = phaseStatus.present ? cockpitArtifactSummaryFromSnapshotItem(phaseStatus) : undefined;
63
70
  const blockers = cockpitBlockersForPhase(phaseStatus, readiness);
64
71
  return {
65
72
  phase: phaseStatus.phase,
@@ -100,6 +107,39 @@ export class SddWorkflowService {
100
107
  };
101
108
  return ok(cockpit);
102
109
  }
110
+ getGovernanceSnapshot(input) {
111
+ const validated = validateProjectAndChange(input.project, input.change);
112
+ if (!validated.ok)
113
+ return validated;
114
+ const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
115
+ if (!snapshot.ok)
116
+ return snapshot;
117
+ const phases = snapshot.value.phases;
118
+ const status = statusFromPhases(validated.value.change, phases);
119
+ if (!status.ok)
120
+ return status;
121
+ const warnings = [];
122
+ const artifacts = phases.flatMap((phase) => {
123
+ if (phase.artifact === undefined)
124
+ return [];
125
+ const envelope = normalizeSddArtifact(phase.artifact);
126
+ warnings.push(...envelope.warnings);
127
+ if ((input.payloadMode ?? 'compact') === 'compact') {
128
+ const compactArtifact = compactGovernanceArtifact(envelope.artifact);
129
+ return [
130
+ {
131
+ phase: phase.phase,
132
+ topicKey: phase.topicKey,
133
+ artifact: compactArtifact,
134
+ envelope: { ...envelope, artifact: compactArtifact },
135
+ },
136
+ ];
137
+ }
138
+ return [{ phase: phase.phase, topicKey: phase.topicKey, artifact: phase.artifact, envelope }];
139
+ });
140
+ const readiness = input.phase === undefined ? undefined : { change: validated.value.change, phase: input.phase, ...getReadinessFromStatuses(validated.value.change, input.phase, phases) };
141
+ return ok({ status: status.value, artifacts, ...(readiness === undefined ? {} : { readiness }), warnings });
142
+ }
103
143
  saveArtifact(input) {
104
144
  const validated = this.validatePhaseInput(input);
105
145
  if (!validated.ok)
@@ -161,12 +201,33 @@ export class SddWorkflowService {
161
201
  if (input.payloadMode !== 'compact')
162
202
  return artifact;
163
203
  const statuses = this.getPhaseStatuses(validated.value.project, validated.value.change);
164
- return ok(compactArtifactProjection(artifact.value, validated.value.change, validated.value.phase, getReadinessFromStatuses(validated.value.change, validated.value.phase, statuses)));
204
+ if (!statuses.ok)
205
+ return statuses;
206
+ return ok(compactArtifactProjection(artifact.value, validated.value.change, validated.value.phase, getReadinessFromStatuses(validated.value.change, validated.value.phase, statuses.value)));
165
207
  }
166
208
  listArtifacts(input) {
167
209
  const validated = validateProjectAndChange(input.project, input.change);
168
210
  if (!validated.ok)
169
211
  return validated;
212
+ if (input.payloadMode === 'compact') {
213
+ const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
214
+ if (!snapshot.ok)
215
+ return snapshot;
216
+ return ok({
217
+ project: validated.value.project,
218
+ change: validated.value.change,
219
+ artifacts: snapshot.value.phases.flatMap((phase) => {
220
+ if (phase.artifact === undefined)
221
+ return [];
222
+ return [compactArtifactProjection(phase.artifact, validated.value.change, phase.phase, getReadinessFromStatuses(validated.value.change, phase.phase, snapshot.value.phases))];
223
+ }),
224
+ fullRetrieval: {
225
+ tool: 'vgxness_sdd_list_artifacts',
226
+ payloadMode: 'verbose',
227
+ input: { project: validated.value.project, change: validated.value.change },
228
+ },
229
+ });
230
+ }
170
231
  const listed = this.memory.listArtifactsByTopicPrefix(validated.value.project, `sdd/${validated.value.change}/`, this.context);
171
232
  if (!listed.ok)
172
233
  return listed;
@@ -174,22 +235,7 @@ export class SddWorkflowService {
174
235
  const artifacts = sddPhases
175
236
  .map((phase) => artifactsByTopicKey.get(sddTopicKey(validated.value.change, phase)))
176
237
  .filter((artifact) => artifact !== undefined);
177
- if (input.payloadMode !== 'compact')
178
- return ok({ project: validated.value.project, change: validated.value.change, artifacts });
179
- const statuses = this.getPhaseStatuses(validated.value.project, validated.value.change);
180
- return ok({
181
- project: validated.value.project,
182
- change: validated.value.change,
183
- artifacts: artifacts.map((artifact) => {
184
- const phase = isSddPhase(artifact.phase) ? artifact.phase : phaseFromTopicKey(validated.value.change, artifact.topicKey);
185
- return compactArtifactProjection(artifact, validated.value.change, phase, getReadinessFromStatuses(validated.value.change, phase, statuses));
186
- }),
187
- fullRetrieval: {
188
- tool: 'vgxness_sdd_list_artifacts',
189
- payloadMode: 'verbose',
190
- input: { project: validated.value.project, change: validated.value.change },
191
- },
192
- });
238
+ return ok({ project: validated.value.project, change: validated.value.change, artifacts });
193
239
  }
194
240
  validatePhaseInput(input) {
195
241
  const validated = validateProjectAndChange(input.project, input.change);
@@ -200,31 +246,38 @@ export class SddWorkflowService {
200
246
  return { ok: true, value: { ...validated.value, phase: input.phase } };
201
247
  }
202
248
  getPhaseStatuses(project, change) {
203
- return sddPhases.map((phase) => {
249
+ const snapshot = this.loadPhaseSnapshot(project, change);
250
+ if (!snapshot.ok)
251
+ return snapshot;
252
+ return ok(snapshot.value.phases.map(({ artifact, acceptance, createdAt, updatedAt, warnings, ...status }) => status));
253
+ }
254
+ loadPhaseSnapshot(project, change) {
255
+ const listed = this.memory.listArtifactsByTopicPrefixNoTrace(project, `sdd/${change}/`);
256
+ if (!listed.ok)
257
+ return listed;
258
+ const artifactsByTopicKey = new Map(listed.value.map((artifact) => [artifact.topicKey, artifact]));
259
+ const phases = sddPhases.map((phase) => {
204
260
  const topicKey = sddTopicKey(change, phase);
205
- const artifact = this.memory.getArtifact(project, topicKey, this.context);
206
- if (!artifact.ok)
207
- return { phase, topicKey, present: false, state: 'missing', accepted: false, legacy: false };
208
- return phaseStatusFromArtifact(phase, topicKey, artifact.value);
261
+ const artifact = artifactsByTopicKey.get(topicKey);
262
+ if (artifact === undefined)
263
+ return { phase, topicKey, present: false, state: 'missing', accepted: false, legacy: false, warnings: [] };
264
+ const envelope = normalizeSddArtifact(artifact);
265
+ return {
266
+ phase,
267
+ topicKey,
268
+ present: true,
269
+ state: envelope.metadata.status,
270
+ accepted: envelope.metadata.status === 'accepted',
271
+ legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
272
+ artifactId: artifact.id,
273
+ artifact,
274
+ ...(envelope.metadata.acceptance === undefined ? {} : { acceptance: envelope.metadata.acceptance }),
275
+ createdAt: artifact.createdAt,
276
+ updatedAt: artifact.updatedAt,
277
+ warnings: envelope.warnings,
278
+ };
209
279
  });
210
- }
211
- artifactSummaryForPhase(project, change, phase, topicKey) {
212
- const artifact = this.memory.getArtifact(project, topicKey, this.context);
213
- if (!artifact.ok)
214
- return undefined;
215
- const envelope = normalizeSddArtifact(artifact.value);
216
- return {
217
- phase,
218
- topicKey,
219
- present: true,
220
- accepted: envelope.metadata.status === 'accepted',
221
- legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
222
- state: envelope.metadata.status,
223
- artifactId: artifact.value.id,
224
- ...(envelope.metadata.acceptance === undefined ? {} : { acceptance: envelope.metadata.acceptance }),
225
- createdAt: artifact.value.createdAt,
226
- updatedAt: artifact.value.updatedAt,
227
- };
280
+ return ok({ project, change, phases });
228
281
  }
229
282
  }
230
283
  export function nextDecisionFromStatuses(change, phases) {
@@ -336,18 +389,6 @@ function getReadinessFromStatuses(change, phase, phases) {
336
389
  blockedPrerequisites,
337
390
  };
338
391
  }
339
- function phaseStatusFromArtifact(phase, topicKey, artifact) {
340
- const envelope = normalizeSddArtifact(artifact);
341
- return {
342
- phase,
343
- topicKey,
344
- present: true,
345
- state: envelope.metadata.status,
346
- accepted: envelope.metadata.status === 'accepted',
347
- legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
348
- artifactId: artifact.id,
349
- };
350
- }
351
392
  function compactArtifactProjection(artifact, change, phase, readiness) {
352
393
  const envelope = normalizeSddArtifact(artifact);
353
394
  const summary = summarizePayloadContent(artifact.content);
@@ -370,6 +411,35 @@ function compactArtifactProjection(artifact, change, phase, readiness) {
370
411
  updatedAt: artifact.updatedAt,
371
412
  };
372
413
  }
414
+ function cockpitArtifactSummaryFromSnapshotItem(item) {
415
+ if (item.artifact === undefined || item.artifactId === undefined || item.createdAt === undefined || item.updatedAt === undefined)
416
+ return undefined;
417
+ return {
418
+ phase: item.phase,
419
+ topicKey: item.topicKey,
420
+ present: true,
421
+ accepted: item.accepted === true,
422
+ legacy: item.legacy === true,
423
+ state: item.state ?? 'draft',
424
+ artifactId: item.artifactId,
425
+ ...(item.acceptance === undefined ? {} : { acceptance: item.acceptance }),
426
+ createdAt: item.createdAt,
427
+ updatedAt: item.updatedAt,
428
+ };
429
+ }
430
+ function compactGovernanceArtifact(artifact) {
431
+ return {
432
+ id: artifact.id,
433
+ project: artifact.project,
434
+ topicKey: artifact.topicKey,
435
+ phase: artifact.phase,
436
+ observationId: artifact.observationId,
437
+ createdAt: artifact.createdAt,
438
+ updatedAt: artifact.updatedAt,
439
+ contentLength: artifact.content.length,
440
+ contentOmitted: true,
441
+ };
442
+ }
373
443
  function cockpitBlockersForPhase(status, readiness) {
374
444
  const blockers = [];
375
445
  if (!status.present)
@@ -3,24 +3,29 @@ import { noWriteActionSafety } from './provider-setup-adapter.js';
3
3
  export const claudeSetupAdapter = {
4
4
  id: 'claude',
5
5
  displayName: 'Claude',
6
- supportLevel: 'preview-only',
7
- capabilities: ['mcp-preview', 'manual-guidance'],
8
- targets: [{ kind: 'manual', label: 'Claude MCP config snippet', writableBySetup: false }],
6
+ supportLevel: 'supported-secondary',
7
+ capabilities: ['mcp-preview', 'mcp-install-plan', 'cli-mcp-plan', 'agent-preview', 'doctor', 'manual-guidance'],
8
+ targets: [
9
+ { kind: 'project-config', label: 'Project .mcp.json', path: '.mcp.json', writableBySetup: false },
10
+ { kind: 'project-config', label: 'Project Claude agents', path: '.claude/agents/*.md', writableBySetup: false },
11
+ { kind: 'project-config', label: 'Project Claude memory', path: 'CLAUDE.md', writableBySetup: false },
12
+ { kind: 'user-config', label: 'User Claude agents', path: '~/.claude/agents/*.md', writableBySetup: false },
13
+ ],
9
14
  getStatus(context) {
10
15
  return {
11
16
  providerId: 'claude',
12
- status: 'preview-only',
13
- summary: 'Claude setup is preview-only with manual MCP guidance.',
17
+ status: 'supported-secondary',
18
+ summary: 'Claude is supported as a secondary, non-default provider for CLI MCP registration planning/apply, project .mcp.json compatibility, and project/user agent planning; explicit guarded apply is required for writes or CLI execution.',
14
19
  evidence: context.databasePath !== undefined
15
20
  ? ['Claude MCP preview can be generated from the selected database path.']
16
21
  : ['Claude MCP preview uses a placeholder until a database path is selected.'],
17
- guidance: ['Copy snippets manually after reviewing them. The TUI does not install or apply Claude config.'],
22
+ guidance: ['Use `vgxness mcp install claude --scope local|project|user --yes --run-id <id>` for explicit guarded apply. VGXNESS may write project-root CLAUDE.md only through confirmed, preflighted Claude project install; it never manually writes ~/.claude.json or .claude/CLAUDE.md. Read-only setup/status surfaces do not execute Claude Code.'],
18
23
  actions: [
19
24
  {
20
25
  id: 'claude-manual-guidance',
21
- label: 'Review Claude MCP snippet',
26
+ label: 'Review guarded Claude install guidance',
22
27
  kind: 'manual-guidance',
23
- description: 'Manual preview guidance only; no install/apply action is available.',
28
+ description: 'Supported secondary guidance only in setup status; writes are available only through the guarded mcp install claude flow with run preflight metadata.',
24
29
  safety: noWriteActionSafety,
25
30
  },
26
31
  ],
@@ -1,4 +1,5 @@
1
1
  import { join, resolve } from 'node:path';
2
+ import { planClaudeCodeMcpInstall } from '../mcp/client-install-claude-code-contract.js';
2
3
  import { planOpenCodeMcpInstall } from '../mcp/client-install-opencode-contract.js';
3
4
  import { resolveMemoryDatabasePath } from '../memory/storage-paths.js';
4
5
  import { vgxnessSetupDefaults } from './setup-defaults.js';
@@ -45,8 +46,66 @@ export function createSetupPlan(input) {
45
46
  },
46
47
  };
47
48
  }
49
+ if (provider === 'claude') {
50
+ const claude = planClaudeCodeMcpInstall({
51
+ cwd: input.workspaceRoot,
52
+ databasePath: database.value.path,
53
+ databasePathSource: database.value.planSource,
54
+ ...(input.overwriteVgxness === undefined ? {} : { overwriteVgxness: input.overwriteVgxness }),
55
+ });
56
+ const targetPath = claude.targetPath;
57
+ if (claude.status === 'refused') {
58
+ return {
59
+ ok: true,
60
+ value: {
61
+ ...common,
62
+ provider: 'claude',
63
+ db: { mode: database.value.mode, path: database.value.path, source: database.value.source },
64
+ status: 'conflict',
65
+ actions: [],
66
+ conflicts: [
67
+ {
68
+ id: claude.reason,
69
+ severity: 'blocking',
70
+ message: claude.message,
71
+ targetPath,
72
+ recovery: claude.reason === 'malformed_claude_project_memory' || claude.reason === 'conflicting_claude_project_memory' ? 'Manually reconcile the VGXNESS markers in project-root CLAUDE.md, then rerun `vgxness setup plan --provider claude`.' : 'Inspect the Claude project config, resolve the conflict, then rerun `vgxness setup plan --provider claude`.',
73
+ },
74
+ ],
75
+ backupsPlanned: [],
76
+ nextCommands: ['vgxness mcp install claude --plan --scope project', 'Resolve the reported Claude project conflict before guarded apply.'],
77
+ },
78
+ };
79
+ }
80
+ return {
81
+ ok: true,
82
+ value: {
83
+ ...common,
84
+ provider: 'claude',
85
+ db: { mode: database.value.mode, path: database.value.path, source: database.value.source },
86
+ status: 'ready',
87
+ actions: [
88
+ {
89
+ id: 'claude-guarded-install',
90
+ description: `Review supported secondary Claude plan for CLI MCP registration, .mcp.json compatibility, ${claude.agentNames.length} .claude/agents/*.md file(s), and guarded project-root CLAUDE.md managed block; apply only with vgxness mcp install claude --scope project --yes --run-id <id>.`,
91
+ mutating: false,
92
+ targetPath,
93
+ backupRequired: claude.backupRequired,
94
+ },
95
+ ],
96
+ conflicts: [],
97
+ backupsPlanned: claude.targets
98
+ .filter((target) => target.kind !== 'cli-mcp-registration' && ((target.kind === 'project-memory' && target.backupRequired) || target.action === 'merge' || target.action === 'update-vgxness'))
99
+ .map((target) => ({
100
+ targetPath: target.path,
101
+ reason: target.kind === 'project-memory' ? 'A future guarded Claude apply would create a managed VGXNESS backup before appending or updating the project-root CLAUDE.md managed block.' : 'A future guarded Claude apply would create a managed VGXNESS backup before merging or updating Claude project configuration.',
102
+ })),
103
+ nextCommands: ['vgxness mcp install claude --plan --scope project', 'vgxness mcp install claude --scope project --yes --run-id <id>', 'OpenCode remains the default/primary provider.'],
104
+ },
105
+ };
106
+ }
48
107
  if (provider !== 'opencode')
49
- return { ok: false, error: { code: 'validation_failed', message: 'Provider must be opencode or none for the setup plan.' } };
108
+ return { ok: false, error: { code: 'validation_failed', message: 'Provider must be opencode, claude, or none for the setup plan.' } };
50
109
  const opencode = planOpenCodeMcpInstall({
51
110
  cwd: input.workspaceRoot,
52
111
  databasePath: database.value.path,
@@ -1,10 +1,12 @@
1
1
  # vgxness Architecture
2
2
 
3
+ > **Scope:** this document describes the v1.5.1 architecture as it is actually built. It is the source of truth for how the product works today. Planned work that is not yet shipped lives in [Roadmap](./roadmap.md); historical planning context that no longer reflects reality has been retired. Where this doc disagrees with code, code wins — file a doc-sync task against the relevant module.
4
+
3
5
  `vgxness` is a local-first, provider-agnostic, Gentle-AI-like harness for agentic development. Its core architecture separates the product domain from provider-specific tooling so agents, skills, memory, SDD workflows, runs, and traces can work across OpenCode, Claude Code, and future adapters such as Pi.
4
6
 
5
- The architectural goal is not only to install better prompts or agent configs. `vgxness` should combine an ecosystem configurator with a runtime control plane: configured agents may execute the work, but the product keeps explicit state for phase readiness, artifacts, runs, approvals, checkpoints, and audit evidence.
7
+ The architectural goal is not only to install better prompts or agent configs. `vgxness` combines an ecosystem configurator with a runtime control plane and a native code runtime: configured agents may execute the work, the control plane keeps explicit state for phase readiness, artifacts, runs, approvals, checkpoints, and audit evidence, and the code runtime performs bounded agentic work in the local workspace with explicit permission decisions.
6
8
 
7
- The user-facing shape is deliberately three-surface: **MCP for agents**, **CLI for scriptable operator control**, and **TUI for guided setup and visual local operations**. All three must call the same core services instead of reimplementing workflow rules.
9
+ The user-facing shape is deliberately four-surface: **MCP for agents**, **CLI for scriptable operator control**, **TUI for guided setup and visual local operations**, and **code runtime (`vgxness code`)** for bounded agentic work in the workspace. All four call the same core services instead of reimplementing workflow rules.
8
10
 
9
11
  ## Architecture decision summary
10
12
 
@@ -14,9 +16,9 @@ The user-facing shape is deliberately three-surface: **MCP for agents**, **CLI f
14
16
  | Reference model | Similar product surface to Gentle-AI/`gentle-pi`: agent setup, SDD orchestration, memory wiring, skills, profiles, permissions, verification. |
15
17
  | Differentiator | Verifiable runtime state engine: SDD phases, artifacts, runs, approvals, checkpoints, and audit trails are queryable product state, not prompt-only convention. |
16
18
  | Core workflow | SDD-first canonical state: explore → proposal → spec → design → tasks → apply-progress → verify → archive. |
17
- | Interfaces | MCP server for AI tools, CLI for automation/power users, TUI for guided install/status/profile/SDD workflows. |
19
+ | Interfaces | MCP server for AI tools, CLI for automation/power users, TUI for guided install/status/profile/SDD workflows, and `vgxness code` runtime for bounded workspace work. |
18
20
  | Installation UX | Step-based guided setup with doctor checks, dry-run support, and no manual provider JSON editing on the happy path. |
19
- | Provider strategy | Provider-agnostic domain model with OpenCode and Claude Code first; Pi/`gentle-pi` compatibility is a future adapter/reference target. |
21
+ | Provider strategy | Provider-agnostic domain model with OpenCode as the primary/default supported provider; Claude Code is supported secondary for CLI MCP registration, project `.mcp.json` compatibility, guarded project-root `CLAUDE.md` managed memory, and project/user agent planning. Claude scopes are `local`, `project`, and `user` (`personal`/`global` map to `user`). Read-only status/doctor/change-plan never execute Claude Code or inspect private Claude config, and VGXNESS never manually mutates `~/.claude.json` or `.claude/CLAUDE.md`. Pi/`gentle-pi` compatibility is a future adapter/reference target. The code runtime speaks to any OpenAI-compatible endpoint through `src/code/providers/openai-compatible-provider-adapter.ts`. |
20
22
  | Memory | Project memory plus personal/global memory, backed locally. |
21
23
  | Agents | Agents/subagents are registered in a neutral schema, then rendered into provider-specific config. |
22
24
  | Skills | Skills are first-class, versioned, attachable to agents/workflows/adapters, and improved through reviewable proposals. |
@@ -98,7 +100,7 @@ Gentle-AI/`gentle-pi` are strong references for the configurator and agent-behav
98
100
  | Project | Repo-specific memory, SDD artifacts, run history, adapter config, project agents/skills. | `.vgx/` or project-local SQLite store. |
99
101
  | Personal/global | User preferences, reusable skills, cross-project patterns, global agents. | User-level config/data directory. |
100
102
 
101
- The exact path format is still open, but the architectural rule is fixed: **project data and personal data must not be collapsed into one scope**.
103
+ The exact path format is still open, but the architectural rule is fixed: **project data and personal data must not be collapsed into one scope**. See [Storage](./storage.md) for schema, migration layout, and lifecycle.
102
104
 
103
105
  ## SDD workflow engine
104
106
 
@@ -116,18 +118,20 @@ Current phase artifacts use one canonical topic key each:
116
118
  sdd/{change}/{phase}
117
119
  ```
118
120
 
119
- Readiness is based on artifact presence only:
121
+ Readiness is a combination of prerequisite phase artifacts, explicit human acceptance, and aggregate blockers — artifact presence alone is not enough:
120
122
 
121
- | Phase | Required artifacts |
122
- |---|---|
123
- | `explore` | none |
124
- | `proposal` | `explore` |
125
- | `spec` | `proposal` |
126
- | `design` | `proposal`, `spec` |
127
- | `tasks` | `proposal`, `spec`, `design` |
128
- | `apply-progress` | `tasks` |
129
- | `verify` | `apply-progress` |
130
- | `archive` | `verify` |
123
+ | Phase | Required prerequisites | Acceptance required |
124
+ |---|---|---|
125
+ | `explore` | none | no |
126
+ | `proposal` | `explore` | yes (on `explore`) |
127
+ | `spec` | `proposal` | yes (on `proposal`) |
128
+ | `design` | `proposal`, `spec` | yes (on `proposal`, `spec`) |
129
+ | `tasks` | `proposal`, `spec`, `design` | yes (on `proposal`, `spec`, `design`) |
130
+ | `apply-progress` | `tasks` | yes (on `tasks`) |
131
+ | `verify` | `apply-progress` | yes (on `apply-progress`) |
132
+ | `archive` | `verify` | yes (on `verify`) |
133
+
134
+ `SddWorkflowService.getReady(...)` returns a structured `SddReadiness` with `blockedPrerequisites`, while `getCockpit(...)` aggregates `SddCockpitBlocker`s of kind `missing-topic-key`, `unaccepted-phase`, `legacy-artifact`, or `readiness`. Acceptance is recorded only by a human actor (`acceptedBy.type === 'human'`); the runtime rejects agent or anonymous acceptance.
131
135
 
132
136
  Current service API:
133
137
 
@@ -409,7 +413,7 @@ NO silent mutation of active skills. Eso es una línea roja.
409
413
 
410
414
  ## Provider adapter contract
411
415
 
412
- Adapters render registry definitions into provider artifacts without changing the registry model.
416
+ Adapters render registry definitions into provider artifacts without changing the registry model. The full adapter contract, render API, and how to add a new provider live in [Providers](./providers.md); this section is a high-level map.
413
417
 
414
418
  Current contract:
415
419
 
@@ -435,7 +439,7 @@ vgxness agents render --provider opencode --project vgxness --name apply-agent
435
439
  Rendering is intentionally read-only: it returns generated content in the CLI response. It does **not** write `.opencode/`, `.claude/`, or any user/global provider config.
436
440
  OpenCode agent keys are sanitized deterministically from registry names, and rendering rejects key collisions instead of overwriting generated config.
437
441
 
438
- Claude Code rendering remains follow-up work after its exact install-safe artifact shape is specified.
442
+ Claude Code install planning/apply is supported through the guarded MCP install path. Rendering remains read-only; confirmed project compatibility writes are limited to `.mcp.json`, `.claude/agents/*.md`, and the project-root `CLAUDE.md` managed block with run preflight metadata. Local/user MCP registration is represented through Claude CLI argv after confirmation/preflight, not private config mutation.
439
443
 
440
444
  ### OpenCode injection preview
441
445
 
@@ -462,15 +466,15 @@ The checked-in OpenCode default config and `seeds/agents/agent-seed-v1.json` def
462
466
 
463
467
  ## Run lifecycle
464
468
 
465
- A run is the core unit of execution. The current foundation stores local, provider-neutral run records in SQLite; deeper orchestration and approval enforcement are follow-up work.
466
-
467
- Current terminal lifecycle rules:
469
+ A run is the core unit of execution. Run records are stored locally in SQLite and stay provider-neutral. The lifecycle is complete in v1.5.1:
468
470
 
469
471
  ```text
470
- created → completed | failed | blocked | cancelled
472
+ created → planned running needs-human
473
+
474
+ completed | failed | blocked | cancelled
471
475
  ```
472
476
 
473
- The broader planned lifecycle still includes `planned`, `running`, and `needs-human`, but this slice only enforces safe finalization: terminal runs cannot be finalized again, and final outcomes must match the terminal status.
477
+ All eight statuses are first-class on `RunStatus` (`src/runs/schema.ts:1`). The control plane exposes lifecycle operations through `vgxness_run_start`, `vgxness_run_checkpoint`, `vgxness_run_finalize`, and the read-only `vgxness_run_resume_inspect` and `vgxness_run_resume_gate` tools. Finalize is safe by default: terminal runs cannot be finalized again, and the final `outcome` must match the terminal `status` (`success`/`partial`/`failure`/`blocked`/`cancelled`).
474
478
 
475
479
  Current run fields:
476
480
 
@@ -556,14 +560,13 @@ runService.appendCheckpoint({
556
560
  });
557
561
  ```
558
562
 
559
- Follow-up runtime work:
563
+ Open follow-up for run execution lives in [Roadmap](./roadmap.md). The remaining work is:
564
+
565
+ - real provider/tool invocation behind sandboxed executors (the lifecycle and policy recording are stable; the actual executor is still test-only)
566
+ - CLI/MCP orchestration for `resume-after-approval` once a safe executor exists outside tests
567
+ - richer verification evidence summaries that link runs, tasks, and verifications
560
568
 
561
- - active state transitions for `planned`, `running`, and `needs-human`
562
- - real provider/tool invocation behind sandboxed executors
563
- - CLI or adapter orchestration for resume-after-approval once a safe executor exists outside tests
564
- - operator UX for retry admission and retry execution, with clear separation between reservation and actual execution
565
- - sandbox/worktree execution strategies after decision recording is stable
566
- - richer verification evidence summaries
569
+ What is already shipped: 8-state lifecycle, approval records, reserved attempts, retry policy evaluation with `never`/`after-abandoned`/`after-failure`/`after-failure-or-abandoned`, run insights with debug summary, run snapshot export (`RunSnapshotPackageV1`), and a `runs retry-check --approval <id> [--policy <json>]` operator command.
567
570
 
568
571
  ## Trace model
569
572
 
@@ -584,9 +587,7 @@ Minimum trace events:
584
587
 
585
588
  ## Permission model
586
589
 
587
- Permissions must be defined in `vgxness` first, then mapped to adapters.
588
-
589
- Minimum categories:
590
+ Permissions are defined in `vgxness` first, then mapped to adapters through the neutral `permissions` field on agents and subagents. The full safety model, including approval flow, redactors, and runtime gates, is in [Safety model](./safety.md). Categories:
590
591
 
591
592
  | Category | Examples |
592
593
  |---|---|
@@ -600,15 +601,9 @@ Minimum categories:
600
601
  | `provider-tool` | opaque adapter/provider tool calls |
601
602
  | `secrets` | environment variables, credentials, tokens |
602
603
 
603
- Operations can resolve to:
604
-
605
- - `allow`
606
- - `ask`
607
- - `deny`
608
-
609
- Default stance for destructive or external operations: **ask or deny**, never implicit allow.
604
+ Operations resolve to `allow`, `ask`, or `deny`. Destructive or external operations default to **ask or deny**, never implicit allow.
610
605
 
611
- Current foundation API: `evaluatePermission(request)` in `src/permissions/` returns `allow`, `ask`, or `deny` with a reason. Defaults are intentionally conservative:
606
+ Current foundation API: `evaluatePermission(request)` in `src/permissions/policy-evaluator.ts` returns `allow`, `ask`, or `deny` with a reason. Defaults are intentionally conservative:
612
607
 
613
608
  - workspace reads are allowed only when the target path stays inside `workspaceRoot`
614
609
  - edits, shell, git, network, memory writes/searches, and provider-specific tools ask by default
@@ -616,84 +611,31 @@ Current foundation API: `evaluatePermission(request)` in `src/permissions/` retu
616
611
  - destructive, external, privileged, or ambiguous requests require ask even when an agent override would otherwise allow the category
617
612
  - workspace boundary denials cannot be relaxed by agent/subagent overrides
618
613
 
619
- Agent and subagent registry definitions keep neutral `permissions` such as `{ "shell": "ask", "provider-tool": "deny" }`. Provider names and tool details remain opaque metadata; enforcement and sandbox execution are follow-up runtime work.
614
+ The code runtime layers a second, finer-grained decision on top of the policy evaluator: per-tool definitions declare whether a tool is `read`, `confirm`, or `restricted`; `PolicyApprovalBroker`, `StdioApprovalBroker`, and `ConservativePermissionGateway` (in `src/code/runtime/approval-coordinator.ts`) wire approval prompts to the runtime event stream. See [Code runtime](./code-runtime.md) for the workspace-side contract.
620
615
 
621
- ## Future interface surface
616
+ ## Interface surface (current)
622
617
 
623
- Current and near-term CLI surface should build on the plural domain commands documented in `docs/cli.md`. Future shortcuts can be added later, but they should not imply separate singular command families:
618
+ CLI surface groups are documented in [CLI reference](./cli.md). The plural form is canonical singular shortcuts are not added.
624
619
 
625
- ```bash
626
- vgxness init
627
- vgxness memory search|get|save|update
628
- vgxness agents list|register|get|resolve|render
629
- vgxness skills list|register|propose|approve-proposal|apply-proposal
630
- vgxness sdd status|next|ready|save-artifact|accept-artifact|list-artifacts
631
- vgxness runs list|get|timeline|debug|resume-inspect|resume-gate
632
- vgxness mcp doctor|install
633
- vgxness opencode preview
634
- ```
635
-
636
- Representative MCP tools mirror the same core services for agent use. For the current exact tool names, use `SUPPORTED_VGX_MCP_TOOL_NAMES`:
637
-
638
- ```text
639
- vgxness_sdd_status
640
- vgxness_sdd_next
641
- vgxness_sdd_ready
642
- vgxness_sdd_save_artifact
643
- vgxness_sdd_get_artifact
644
- vgxness_sdd_list_artifacts
645
- vgxness_memory_search
646
- vgxness_memory_get
647
- vgxness_memory_save
648
- vgxness_memory_update
649
- vgxness_run_start
650
- vgxness_run_list
651
- vgxness_run_get
652
- vgxness_run_preflight
653
- vgxness_run_checkpoint
654
- vgxness_run_finalize
655
- vgxness_agent_resolve
656
- vgxness_agent_activate
657
- vgxness_manager_profile_get
658
- vgxness_manager_profile_set
659
- vgxness_skill_payload
660
- vgxness_opencode_manager_payload
661
- ```
662
-
663
- The CLI and TUI are human/operator control surfaces. MCP is the agent-facing control surface. Provider integrations are the execution/configuration plane.
620
+ MCP tools mirror the same core services for agent use. The full, current list of 38 tools is in [MCP tools](./mcp.md) and `SUPPORTED_VGX_MCP_TOOL_NAMES` (`src/mcp/schema.ts`); treat that array as the source of truth. The CLI and TUI are human/operator control surfaces. MCP is the agent-facing control surface. Provider integrations and the code runtime sit on the execution plane.
664
621
 
665
622
  ## Evaluation strategy
666
623
 
667
- Minimum eval/test targets:
668
-
669
- - Agent resolution selects the expected agent.
670
- - Skill resolution injects the expected skill.
671
- - Adapter rendering produces valid provider config.
672
- - Permission rules block unsafe operations.
673
- - SDD artifact chains remain complete.
674
- - Memory upserts preserve revisions.
675
- - Run resume restores expected state.
676
- - Skill improvement proposals are versioned and require approval.
677
- - MCP tools call the same core services as CLI/TUI and return actionable blocked states.
678
- - TUI setup screens expose loading, empty, error, success, blocked, and permission states.
624
+ Minimum eval/test targets (asserted through `node:test` files under `test/`, totaling 95 files as of v1.5.1):
625
+
626
+ - Agent resolution selects the expected agent (`test/agents/agent-resolver.test.ts`).
627
+ - Skill resolution injects the expected skill (`test/skills/`).
628
+ - Adapter rendering produces valid provider config (`test/agents/provider-renderer.test.ts`).
629
+ - Permission rules block unsafe operations (`test/permissions/policy-evaluator.test.ts`).
630
+ - SDD artifact chains remain complete and human acceptance is enforced (`test/sdd/sdd-workflow-service.test.ts`).
631
+ - Memory upserts preserve revisions (`test/memory/`).
632
+ - Run resume restores expected run state and respects retry policies (`test/runs/`).
633
+ - Skill improvement proposals are versioned and require approval (`test/skills/`).
634
+ - MCP tools call the same core services as CLI/TUI and return actionable blocked states (`test/mcp/`).
635
+ - Code runtime behavior across inspect, plan, craft-preview, and craft modes (`test/code/`).
636
+ - OpenCode config rendering and drift detection (`test/mcp/opencode-agent-config-drift.test.ts`).
679
637
  - Installation dry-run reports the exact provider config changes before mutation.
680
638
 
681
- ## Immediate implementation recommendation
682
-
683
- The next SDD change should be `harness-runtime-foundation`.
684
-
685
- Scope:
686
-
687
- - define schemas for agents, skills, runs, traces, permissions, and adapters
688
- - add local persistence for these entities where missing
689
- - add adapter validation/render skeleton
690
- - add CLI/MCP/TUI interface boundaries for validation, inspection, and guided setup
691
- - add tests for schema validation, permission decisions, and adapter rendering
692
-
693
- Out of scope:
639
+ ## Future work
694
640
 
695
- - cloud sync
696
- - team collaboration
697
- - web console
698
- - distributed workers
699
- - fully autonomous skill mutation
641
+ Planned work that is not yet shipped lives in [Roadmap](./roadmap.md). This document stays focused on the architecture as built; the roadmap is the place to track what is next and what is explicitly deferred.