dsh-env-manager-plugin 0.0.0-stage → 0.1.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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +249 -2
  3. package/client.js +2581 -0
  4. package/cordis.patch.yml +4 -0
  5. package/docs/00-project/current-state.md +246 -0
  6. package/docs/00-project/glossary.md +215 -0
  7. package/docs/00-project/project-brief.md +114 -0
  8. package/docs/00-project/roadmap.md +89 -0
  9. package/docs/01-architecture/DSH_Environment_Service_Memory_Skill_Brainstorm.md +493 -0
  10. package/docs/01-architecture/api-verification.md +416 -0
  11. package/docs/01-architecture/architecture-overview.md +222 -0
  12. package/docs/01-architecture/data-model.md +930 -0
  13. package/docs/01-architecture/decisions/ADR-0001-plugin-as-bundle.md +29 -0
  14. package/docs/01-architecture/decisions/ADR-0002-file-based-storage.md +25 -0
  15. package/docs/01-architecture/decisions/ADR-0003-skill-router-over-search.md +25 -0
  16. package/docs/01-architecture/decisions/ADR-0004-typescript-host-js-client.md +31 -0
  17. package/docs/01-architecture/decisions/ADR-0005-layered-rule-resolution.md +36 -0
  18. package/docs/01-architecture/decisions/ADR-0006-credentials-as-references.md +30 -0
  19. package/docs/01-architecture/decisions/ADR-0007-session-scoped-default-environment.md +28 -0
  20. package/docs/01-architecture/decisions/ADR-0008-memory-bridge-not-second-store.md +31 -0
  21. package/docs/01-architecture/decisions/ADR-0009-client-host-http-transport.md +87 -0
  22. package/docs/01-architecture/decisions/ADR-0010-environment-plane-model.md +87 -0
  23. package/docs/01-architecture/decisions/ADR-0011-k8s-node-execution-model.md +92 -0
  24. package/docs/01-architecture/design.md +666 -0
  25. package/docs/01-architecture/detailed-design.md +961 -0
  26. package/docs/01-architecture/implementation-plan.md +323 -0
  27. package/docs/01-architecture/module-boundaries.md +306 -0
  28. package/docs/01-architecture/security-model.md +227 -0
  29. package/docs/01-architecture/storage-contract.md +224 -0
  30. package/docs/01-architecture/tool-contract.md +438 -0
  31. package/docs/01-architecture/ui-design.md +587 -0
  32. package/docs/02-harness/agent-behavior.md +36 -0
  33. package/docs/02-harness/change-snapshot-policy.md +29 -0
  34. package/docs/02-harness/client-ui-integration.md +110 -0
  35. package/docs/02-harness/context-policy.md +43 -0
  36. package/docs/02-harness/harness-standard.md +91 -0
  37. package/docs/02-harness/task-execution-policy.md +26 -0
  38. package/docs/02-harness/verification-policy.md +97 -0
  39. package/docs/03-openspec/archive-rule.md +25 -0
  40. package/docs/03-openspec/bugfix-rule.md +30 -0
  41. package/docs/03-openspec/design-rule.md +33 -0
  42. package/docs/03-openspec/openspec-usage-guide.md +31 -0
  43. package/docs/03-openspec/proposal-rule.md +70 -0
  44. package/docs/03-openspec/spec-rule.md +33 -0
  45. package/docs/03-openspec/tasks-rule.md +31 -0
  46. package/docs/04-development/coding-style.md +67 -0
  47. package/docs/04-development/dependency-policy.md +43 -0
  48. package/docs/04-development/development-guide.md +85 -0
  49. package/docs/04-development/directory-convention.md +105 -0
  50. package/docs/04-development/local-run-guide.md +55 -0
  51. package/docs/04-development/testing-guide.md +102 -0
  52. package/docs/05-domain/deployment-template.md +76 -0
  53. package/docs/05-domain/environment-template.md +165 -0
  54. package/docs/05-domain/operation-binding-template.md +55 -0
  55. package/docs/05-domain/operation-record-template.md +55 -0
  56. package/docs/05-domain/prompt-template.md +90 -0
  57. package/docs/05-domain/rule-template.md +73 -0
  58. package/docs/05-domain/service-template.md +39 -0
  59. package/docs/06-operations/deployment-guide.md +54 -0
  60. package/docs/06-operations/release.md +188 -0
  61. package/docs/06-operations/security-policy.md +53 -0
  62. package/docs/06-operations/troubleshooting.md +91 -0
  63. package/docs/07-templates/adr-template.md +21 -0
  64. package/docs/07-templates/bugfix-template.md +11 -0
  65. package/docs/07-templates/case-template.md +15 -0
  66. package/docs/07-templates/design-template.md +29 -0
  67. package/docs/07-templates/proposal-template.md +19 -0
  68. package/docs/07-templates/spec-template.md +15 -0
  69. package/docs/07-templates/tasks-template.md +12 -0
  70. package/docs/99-archive/README.md +13 -0
  71. package/docs/README.md +142 -0
  72. package/icon.svg +4 -0
  73. package/index.js +8 -0
  74. package/lib/app/app.d.ts +67 -0
  75. package/lib/app/app.js +494 -0
  76. package/lib/app/app.js.map +1 -0
  77. package/lib/config.d.ts +55 -0
  78. package/lib/config.js +42 -0
  79. package/lib/config.js.map +1 -0
  80. package/lib/domain/constants.d.ts +21 -0
  81. package/lib/domain/constants.js +59 -0
  82. package/lib/domain/constants.js.map +1 -0
  83. package/lib/domain/errors.d.ts +20 -0
  84. package/lib/domain/errors.js +22 -0
  85. package/lib/domain/errors.js.map +1 -0
  86. package/lib/domain/schemas.d.ts +28 -0
  87. package/lib/domain/schemas.js +445 -0
  88. package/lib/domain/schemas.js.map +1 -0
  89. package/lib/domain/types.d.ts +412 -0
  90. package/lib/domain/types.js +2 -0
  91. package/lib/domain/types.js.map +1 -0
  92. package/lib/index.d.ts +18 -0
  93. package/lib/index.js +89 -0
  94. package/lib/index.js.map +1 -0
  95. package/lib/logger.d.ts +8 -0
  96. package/lib/logger.js +13 -0
  97. package/lib/logger.js.map +1 -0
  98. package/lib/memory/bridge.d.ts +18 -0
  99. package/lib/memory/bridge.js +13 -0
  100. package/lib/memory/bridge.js.map +1 -0
  101. package/lib/registry/integrity.d.ts +10 -0
  102. package/lib/registry/integrity.js +204 -0
  103. package/lib/registry/integrity.js.map +1 -0
  104. package/lib/registry/registry.d.ts +14 -0
  105. package/lib/registry/registry.js +182 -0
  106. package/lib/registry/registry.js.map +1 -0
  107. package/lib/resolver/merge.d.ts +23 -0
  108. package/lib/resolver/merge.js +90 -0
  109. package/lib/resolver/merge.js.map +1 -0
  110. package/lib/resolver/resolve.d.ts +18 -0
  111. package/lib/resolver/resolve.js +288 -0
  112. package/lib/resolver/resolve.js.map +1 -0
  113. package/lib/resolver/template.d.ts +10 -0
  114. package/lib/resolver/template.js +42 -0
  115. package/lib/resolver/template.js.map +1 -0
  116. package/lib/router/params.d.ts +3 -0
  117. package/lib/router/params.js +83 -0
  118. package/lib/router/params.js.map +1 -0
  119. package/lib/router/router.d.ts +11 -0
  120. package/lib/router/router.js +204 -0
  121. package/lib/router/router.js.map +1 -0
  122. package/lib/session/session.d.ts +16 -0
  123. package/lib/session/session.js +64 -0
  124. package/lib/session/session.js.map +1 -0
  125. package/lib/skill.d.ts +17 -0
  126. package/lib/skill.js +56 -0
  127. package/lib/skill.js.map +1 -0
  128. package/lib/storage/atomic.d.ts +10 -0
  129. package/lib/storage/atomic.js +107 -0
  130. package/lib/storage/atomic.js.map +1 -0
  131. package/lib/storage/paths.d.ts +10 -0
  132. package/lib/storage/paths.js +50 -0
  133. package/lib/storage/paths.js.map +1 -0
  134. package/lib/storage/records.d.ts +16 -0
  135. package/lib/storage/records.js +112 -0
  136. package/lib/storage/records.js.map +1 -0
  137. package/lib/storage/sessions.d.ts +17 -0
  138. package/lib/storage/sessions.js +65 -0
  139. package/lib/storage/sessions.js.map +1 -0
  140. package/lib/tools/tools.d.ts +14 -0
  141. package/lib/tools/tools.js +472 -0
  142. package/lib/tools/tools.js.map +1 -0
  143. package/lib/transport/http.d.ts +3 -0
  144. package/lib/transport/http.js +217 -0
  145. package/lib/transport/http.js.map +1 -0
  146. package/locale/en.json +6 -0
  147. package/locale/zh.json +6 -0
  148. package/package.json +97 -4
@@ -0,0 +1,438 @@
1
+ # Tool Contract (V1)
2
+
3
+ > Status: normative model-facing contract. Registration mechanics are in
4
+ > [`api-verification.md`](api-verification.md) F2–F5; implementation in
5
+ > [`detailed-design.md`](detailed-design.md) §11.
6
+ > An implementation whose tool schema differs from this document is a bug in one of
7
+ > them; update both in the same change.
8
+
9
+ ## 1. Conventions
10
+
11
+ ### 1.1 Registration
12
+
13
+ Every tool is a `defineTool` definition registered on `ctx.tools` during `apply`:
14
+
15
+ ```ts
16
+ ctx.tools.register(defineTool({
17
+ name, description, parameters,
18
+ output: { schema, render },
19
+ async execute(args, exec) { /* returns the declared JSON value */ },
20
+ }))
21
+ ```
22
+
23
+ - The tool name is stable and matches `^env_[a-z_]+$`.
24
+ - `parameters` uses DSH's value-schema DSL, not raw JSON Schema. Property-level
25
+ `required: true` marks required arguments. `additionalProperties` must be explicit.
26
+ - `output.schema` is the enforced JSON Schema subset: `type`, `oneOf`, `properties`,
27
+ `required`, `additionalProperties`, `items`, `enum`, `const` plus annotations.
28
+ - `render(args, value)` returns `ContentBlock[]`. V1 emits exactly one text block: a
29
+ one-line summary plus the ids a human needs. The canonical JSON value is what the
30
+ model receives.
31
+ - Descriptions start with the decision rule ("Use when …"), not with a restatement of
32
+ the name.
33
+
34
+ ### 1.2 Errors
35
+
36
+ Success returns the declared value. Every expected failure throws:
37
+
38
+ ```ts
39
+ class EnvManagerError extends HarnessError {
40
+ constructor(code: ErrorCode, message: string, details?: JsonValue)
41
+ }
42
+ ```
43
+
44
+ `HarnessError` is imported from `@deepseek-ai/dsh-llm` and constructed as
45
+ `super(message, code)`. DSH records `{ name, code }` beside the message, so the model
46
+ and the UI can branch on `code` without parsing text.
47
+
48
+ Rules:
49
+
50
+ - No tool returns `{ ok: false }`.
51
+ - No tool throws a bare string.
52
+ - Messages never contain a credential value, a file body, or a stack trace.
53
+ - Unexpected exceptions are wrapped as `INTERNAL_ERROR` with a redacted message.
54
+ - A cancelled call surfaces DSH's own abort error; do not translate it.
55
+
56
+ Codes are defined in [`data-model.md`](data-model.md) §11.
57
+
58
+ ### 1.3 Approval
59
+
60
+ | Tool | Approval |
61
+ |---|---|
62
+ | `env_delete` | always, when `confirm: true` and `writePolicy.requireApprovalForDelete` |
63
+ | `env_save` | when the record is a production environment, or a deployment whose environment is production, and `writePolicy.requireApprovalForProductionSave` |
64
+ | everything else | no |
65
+
66
+ Approval uses `ctx.approval.request` with the executing agent, the tool name, the call
67
+ id, an English `reason`, and a localized `displayReason`. Only `'allowed-once'` grants;
68
+ `'rejected'`, `'cancelled'` and `'unavailable'` all become `POLICY_DENIED`.
69
+
70
+ A session-scoped tool invoked without `exec.agent` fails with
71
+ `SESSION_CONTEXT_UNAVAILABLE`; it never guesses a session.
72
+
73
+ ### 1.4 Session default
74
+
75
+ `env_resolve`, `env_resolve_operation` and `env_set_context` operate on the calling
76
+ agent's session. When `environmentId` is omitted, the order is:
77
+
78
+ 1. the explicit argument;
79
+ 2. the session default (`sessions/<sessionId>.json`);
80
+ 3. `config.defaults.environmentId`;
81
+ 4. `INVALID_ARGUMENT` naming both ways to supply an environment.
82
+
83
+ ### 1.5 Shared value shapes
84
+
85
+ ```ts
86
+ interface Diagnostic {
87
+ severity: 'info' | 'warning' | 'error'
88
+ code: string
89
+ message: string
90
+ file?: string
91
+ path?: string
92
+ }
93
+
94
+ interface RecordSummary {
95
+ kind: RecordKind
96
+ id: string
97
+ name: string
98
+ summary?: string // kind-specific one-liner
99
+ }
100
+
101
+ interface WarningList { warnings: Diagnostic[] }
102
+ ```
103
+
104
+ `RecordKind = 'environment' | 'service' | 'deployment' | 'ruleProfile' | 'operationBinding'`.
105
+
106
+ ## 2. Read tools
107
+
108
+ ### 2.1 `env_list`
109
+
110
+ Use when the caller needs an inventory or wants to discover ids before a specific call.
111
+
112
+ ```ts
113
+ parameters: {
114
+ kind: {
115
+ type: 'string',
116
+ required: false,
117
+ enum: ['environment', 'service', 'deployment', 'ruleProfile', 'operationBinding'],
118
+ description: 'Restrict to one record kind.',
119
+ },
120
+ query: {
121
+ type: 'string',
122
+ required: false,
123
+ description: 'Case-insensitive match over id, name and alias.',
124
+ },
125
+ }
126
+ ```
127
+
128
+ Returns:
129
+
130
+ ```json
131
+ { "items": [ { "kind": "environment", "id": "test-a", "name": "Test A", "summary": "test · 2 hosts" } ],
132
+ "warnings": [] }
133
+ ```
134
+
135
+ - Sorted by kind, then id.
136
+ - An empty registry is `{ "items": [], "warnings": [] }`, not an error.
137
+ - Registry diagnostics are summarized in `warnings` (at most 20, with a count of the
138
+ rest).
139
+
140
+ Errors: `INVALID_ARGUMENT` for an unknown `kind`.
141
+
142
+ Render: `"5 environments, 2 services (2 warnings)."`
143
+
144
+ ### 2.2 `env_get`
145
+
146
+ Use when the caller already has an id and needs the full record.
147
+
148
+ ```ts
149
+ parameters: {
150
+ kind: { type: 'string', required: true, enum: [/* five kinds */] },
151
+ id: { type: 'string', required: true, description: 'Record id, e.g. test-a.' },
152
+ }
153
+ ```
154
+
155
+ Returns:
156
+
157
+ ```json
158
+ { "record": { "id": "test-a", "schemaVersion": 1, "...": "..." },
159
+ "yaml": "id: test-a\n..." }
160
+ ```
161
+
162
+ - `record` is the parsed document; `yaml` is the on-disk text (helpful for edits).
163
+ - Unknown fields in the file are preserved in both.
164
+
165
+ Errors: `ENV_NOT_FOUND`, `SERVICE_NOT_FOUND`, `DEPLOYMENT_NOT_FOUND`,
166
+ `RULE_PROFILE_NOT_FOUND`, `BINDING_NOT_FOUND` depending on `kind`; `INVALID_ARGUMENT`
167
+ for a malformed id (fails before any I/O).
168
+
169
+ Render: `"environment test-a (Test A), updated 2026-11-01."`
170
+
171
+ ### 2.3 `env_find`
172
+
173
+ Use when the caller has a name or fragment and wants candidate ids. Prefer this over
174
+ guessing an id.
175
+
176
+ ```ts
177
+ parameters: {
178
+ query: {
179
+ type: 'string',
180
+ required: true,
181
+ description: 'Fragment of an id, name, alias or category (min 2 characters).',
182
+ },
183
+ }
184
+ ```
185
+
186
+ Returns:
187
+
188
+ ```json
189
+ { "hits": [ { "kind": "service", "id": "aicoservice", "name": "Aico Service", "field": "alias" } ] }
190
+ ```
191
+
192
+ - Matching is case-insensitive over `id`, `name`, `aliases`, `category`.
193
+ - Ranked: exact id, prefix id, name, alias, category. At most 25 hits.
194
+ - No hit is not an error.
195
+
196
+ Errors: `INVALID_ARGUMENT` when `query` is shorter than 2 characters.
197
+
198
+ Render: `"3 matches for \"aico\"."`
199
+
200
+ ### 2.4 `env_resolve`
201
+
202
+ Use when the caller needs concrete paths, log sources, class locations or variables
203
+ for a service in an environment. This is the authoritative answer; do not reconstruct
204
+ paths from templates.
205
+
206
+ ```ts
207
+ parameters: {
208
+ environmentId: { type: 'string', required: false, description: 'Defaults to the session environment.' },
209
+ serviceId: { type: 'string', required: true },
210
+ deploymentId: {
211
+ type: 'string',
212
+ required: false,
213
+ description: 'Required when the service has more than one binding in the environment; a single binding is selected automatically.',
214
+ },
215
+ }
216
+ ```
217
+
218
+ Returns the Service Context from `data-model.md` §8.2, including `provenance` and
219
+ `warnings`.
220
+
221
+ Errors: `ENV_NOT_FOUND`, `SERVICE_NOT_FOUND`, `DEPLOYMENT_NOT_FOUND`,
222
+ `DEPLOYMENT_AMBIGUOUS` (lists candidates), `RULE_*`, `PARAMETER_RESOLUTION_FAILED`.
223
+
224
+ Render: `"Resolved aicoservice in test-a (kubernetes): 7 paths, 2 variables, 1 warning."`
225
+
226
+ ### 2.5 `env_resolve_operation`
227
+
228
+ Use before acting: ask which Skill handles an operation for this context and what
229
+ parameters it expects. This tool **does not execute** the Skill.
230
+
231
+ ```ts
232
+ parameters: {
233
+ operation: {
234
+ type: 'string',
235
+ required: true,
236
+ description: 'deploy | redeploy | restart | rollback | collect-logs | health-check | … (standard id or a bound custom id).',
237
+ },
238
+ environmentId: { type: 'string', required: false },
239
+ serviceId: { type: 'string', required: false },
240
+ deploymentId: { type: 'string', required: false },
241
+ }
242
+ ```
243
+
244
+ - At least one of `serviceId` / `deploymentId` is required; `environmentId` falls back
245
+ to the session default.
246
+
247
+ Returns `OperationResolution` from `data-model.md` §8.3:
248
+
249
+ ```json
250
+ {
251
+ "ok": true,
252
+ "operation": "redeploy",
253
+ "requested": { "environmentId": "test-a", "serviceId": "aicoservice", "deploymentId": "deploy-001" },
254
+ "matchedBindingId": "binding-001",
255
+ "matchLevel": "environment-service",
256
+ "skill": { "name": "redeploy-k8s-service", "available": true, "source": "project-dsh", "provider": "dsh-skills" },
257
+ "parameters": { "namespace": "dv-sop", "workload": "aicoservice", "cluster": "k8s-01" },
258
+ "policy": { "requiresApproval": true, "allowProduction": false },
259
+ "diagnostics": []
260
+ }
261
+ ```
262
+
263
+ Errors: `ENV_NOT_FOUND`, `SERVICE_NOT_FOUND`, `DEPLOYMENT_NOT_FOUND`,
264
+ `DEPLOYMENT_AMBIGUOUS`, `BINDING_NOT_FOUND`, `BINDING_CONFLICT` (lists every candidate),
265
+ `SKILL_UNAVAILABLE`, `POLICY_DENIED`, `OPERATION_UNKNOWN`, `PARAMETER_RESOLUTION_FAILED`.
266
+
267
+ Incomplete Skill catalog: `skill.available: "unknown"` plus a
268
+ `SKILL_REGISTRY_UNAVAILABLE` warning; the call still succeeds.
269
+
270
+ Render: `"redeploy aicoservice/test-a → redeploy-k8s-service (approval required)."`
271
+
272
+ ### 2.6 `env_get_context`
273
+
274
+ Use to check what environment the current session defaults to.
275
+
276
+ ```ts
277
+ parameters: {}
278
+ ```
279
+
280
+ Returns:
281
+
282
+ ```json
283
+ { "sessionId": "01J…", "defaultEnvironmentId": "test-a", "persisted": true }
284
+ ```
285
+
286
+ Errors: `SESSION_CONTEXT_UNAVAILABLE` when there is no owning agent.
287
+
288
+ Render: `"Session default: test-a."`
289
+
290
+ ### 2.7 `env_set_context`
291
+
292
+ Use when the user says "work on test-a" and later calls should omit `environmentId`.
293
+
294
+ ```ts
295
+ parameters: {
296
+ environmentId: {
297
+ oneOf: [{ type: 'string' }, { type: 'null' }],
298
+ required: true,
299
+ description: 'Environment id, or null to clear the session default.',
300
+ },
301
+ }
302
+ ```
303
+
304
+ Returns the same shape as `env_get_context`. Writing an unknown environment fails with
305
+ `ENV_NOT_FOUND`.
306
+
307
+ A file write failure returns `persisted: false` plus a `SESSION_CONTEXT_UNPERSISTED`
308
+ warning; it does not throw, because the process-local cache still honors the choice for
309
+ this process.
310
+
311
+ Render: `"Session default set to test-a."` / `"Session default cleared."`
312
+
313
+ ## 3. Management tools
314
+
315
+ ### 3.1 `env_save`
316
+
317
+ Use to create or replace a record. Prefer `env_get` first, edit the YAML, then save the
318
+ whole record; do not send a partial patch.
319
+
320
+ ```ts
321
+ parameters: {
322
+ kind: { type: 'string', required: true, enum: [/* five kinds */] },
323
+ record: {
324
+ type: 'json',
325
+ required: true,
326
+ description: 'Complete record; id and schemaVersion are required. Host timestamps are set by the plugin and ignored if supplied.',
327
+ },
328
+ dryRun: {
329
+ type: 'boolean',
330
+ required: false,
331
+ description: 'Validate and report without writing.',
332
+ },
333
+ }
334
+ ```
335
+
336
+ Returns:
337
+
338
+ ```json
339
+ { "id": "test-a", "kind": "environment", "created": false, "dryRun": false,
340
+ "warnings": [ { "severity": "warning", "code": "CREDENTIAL_MISSING", "message": "…" } ] }
341
+ ```
342
+
343
+ Behavior:
344
+
345
+ - Zod validation, id/filename equality, record cap, then atomic write, then reload.
346
+ - `created` is `true` only when no file existed before.
347
+ - Broken references after the write are returned as warnings; the write is not rolled
348
+ back because the record itself is valid.
349
+ - Production records require approval unless the config disables it.
350
+
351
+ Errors: `RECORD_INVALID` (with `details.issues`), `RECORD_LIMIT_EXCEEDED`,
352
+ `RECORD_TOO_LARGE`, `STORAGE_UNWRITABLE`, `POLICY_DENIED`.
353
+
354
+ Render: `"Saved environment test-a (updated, 1 warning)."`
355
+
356
+ ### 3.2 `env_delete`
357
+
358
+ Use only when the user explicitly asks to remove a record.
359
+
360
+ ```ts
361
+ parameters: {
362
+ kind: { type: 'string', required: true, enum: [/* five kinds */] },
363
+ id: { type: 'string', required: true },
364
+ confirm: {
365
+ type: 'boolean',
366
+ required: true,
367
+ description: 'false checks references only; true deletes after approval.',
368
+ },
369
+ }
370
+ ```
371
+
372
+ Returns:
373
+
374
+ ```json
375
+ { "id": "test-a", "kind": "environment", "deleted": true, "blockers": [] }
376
+ ```
377
+
378
+ Behavior:
379
+
380
+ - `confirm: false` never deletes; it returns `blockers` describing references.
381
+ - `confirm: true` asks for approval, then refuses with `RECORD_IN_USE` when referenced.
382
+ - A backup copy is written before the unlink.
383
+
384
+ Errors: not found by kind, `RECORD_IN_USE`, `POLICY_DENIED`, `STORAGE_UNWRITABLE`.
385
+
386
+ Render: `"Deleted environment test-a (backup kept)."`
387
+
388
+ ### 3.3 `env_validate`
389
+
390
+ Use after hand-editing YAML or before trusting a resolve result.
391
+
392
+ ```ts
393
+ parameters: {}
394
+ ```
395
+
396
+ Returns:
397
+
398
+ ```json
399
+ {
400
+ "ok": false,
401
+ "counts": {
402
+ "environment": 2, "service": 1, "deployment": 1,
403
+ "ruleProfile": 1, "operationBinding": 2
404
+ },
405
+ "diagnostics": [
406
+ { "severity": "error", "code": "RULE_PROFILE_NOT_FOUND",
407
+ "message": "service aicoservice references unknown rule profile java-commn",
408
+ "file": "services/aicoservice.yaml", "path": "ruleProfileRefs[0]" }
409
+ ]
410
+ }
411
+ ```
412
+
413
+ - Always succeeds at the tool level; `ok: false` is a report, not a failure.
414
+ - Read-only: no write, no reload side effect beyond the normal request load.
415
+
416
+ Render: `"Registry invalid: 2 errors, 1 warning."`
417
+
418
+ ## 4. Cross-tool rules
419
+
420
+ 1. **Ids are data, not paths.** A tool never accepts a filesystem path.
421
+ 2. **No credentials.** `credentialRef` status is reported as
422
+ `{ ref, configured, source }`; values are never resolved.
423
+ 3. **No execution.** No tool runs a Skill, a shell command, or a network request.
424
+ 4. **Determinism.** Same registry and inputs produce the same result; no random ids,
425
+ no wall-clock ordering.
426
+ 5. **Read-only means read-only.** `env_validate`, `env_get_context`, `env_list`,
427
+ `env_get`, `env_find`, `env_resolve`, `env_resolve_operation` never write a file.
428
+ 6. **Fail closed.** A conflict, missing Skill, broken reference or production denial is
429
+ an error, never a fallback.
430
+
431
+ ## 5. Non-goals for V1 tools
432
+
433
+ - Executing an operation or invoking a Skill.
434
+ - Writing operation records to memory.
435
+ - Bulk import/export.
436
+ - Editing plugin configuration.
437
+ - Reading or writing session context for a session other than the caller's.
438
+ - Any network or shell access.