universal-dev-standards 6.8.0 → 6.10.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 (225) hide show
  1. package/bin/uds.js +12 -2
  2. package/bundled/ai/standards/acceptance-criteria-traceability.ai.yaml +14 -2
  3. package/bundled/ai/standards/adr-standards.ai.yaml +14 -2
  4. package/bundled/ai/standards/code-review.ai.yaml +13 -3
  5. package/bundled/ai/standards/commit-message.ai.yaml +8 -4
  6. package/bundled/ai/standards/deferred-item-exit.ai.yaml +225 -0
  7. package/bundled/ai/standards/feature-discovery-standards.ai.yaml +14 -2
  8. package/bundled/ai/standards/governance-layer.ai.yaml +128 -2
  9. package/bundled/ai/standards/logging.ai.yaml +2 -2
  10. package/bundled/ai/standards/retrospective-standards.ai.yaml +14 -2
  11. package/bundled/ai/standards/reverse-engineering-standards.ai.yaml +73 -2
  12. package/bundled/ai/standards/security-standards.ai.yaml +2 -2
  13. package/bundled/ai/standards/spec-driven-development.ai.yaml +14 -2
  14. package/bundled/ai/standards/tech-debt-standards.ai.yaml +87 -3
  15. package/bundled/ai/standards/turn-completion-integrity.ai.yaml +131 -0
  16. package/bundled/core/acceptance-criteria-traceability.md +5 -2
  17. package/bundled/core/adr-standards.md +26 -2
  18. package/bundled/core/agent-communication-protocol.md +8 -0
  19. package/bundled/core/branch-completion.md +8 -0
  20. package/bundled/core/change-batching-standards.md +8 -0
  21. package/bundled/core/code-review-checklist.md +5 -2
  22. package/bundled/core/context-aware-loading.md +1 -1
  23. package/bundled/core/deferred-item-exit.md +254 -0
  24. package/bundled/core/execution-history.md +8 -0
  25. package/bundled/core/feature-discovery-standards.md +5 -1
  26. package/bundled/core/governance-layer.md +114 -2
  27. package/bundled/core/pipeline-integration-standards.md +8 -0
  28. package/bundled/core/retrospective-standards.md +4 -2
  29. package/bundled/core/reverse-engineering-standards.md +81 -2
  30. package/bundled/core/spec-driven-development.md +8 -2
  31. package/bundled/core/tech-debt-standards.md +67 -8
  32. package/bundled/core/turn-completion-integrity.md +196 -0
  33. package/bundled/core/workflow-enforcement.md +8 -0
  34. package/bundled/core/workflow-state-protocol.md +8 -0
  35. package/bundled/hooks/check-dangerous-cmd.mjs +60 -0
  36. package/bundled/hooks/check-logging-standard.mjs +59 -0
  37. package/bundled/hooks/check-turn-completion.mjs +233 -0
  38. package/bundled/hooks/inject-standards.mjs +183 -0
  39. package/bundled/hooks/telemetry-wrapper.mjs +77 -0
  40. package/bundled/hooks/turn-completion/detect.mjs +99 -0
  41. package/bundled/hooks/turn-completion/locales/en.mjs +159 -0
  42. package/bundled/hooks/turn-completion/locales/zh-TW.mjs +166 -0
  43. package/bundled/hooks/validate-commit-msg.mjs +104 -0
  44. package/bundled/locales/zh-CN/CHANGELOG.md +73 -3
  45. package/bundled/locales/zh-CN/CLAUDE.md +1 -1
  46. package/bundled/locales/zh-CN/README.md +2 -2
  47. package/bundled/locales/zh-CN/SECURITY.md +1 -1
  48. package/bundled/locales/zh-CN/core/adr-standards.md +1 -1
  49. package/bundled/locales/zh-CN/core/agent-communication-protocol.md +7 -0
  50. package/bundled/locales/zh-CN/core/branch-completion.md +7 -0
  51. package/bundled/locales/zh-CN/core/change-batching-standards.md +7 -0
  52. package/bundled/locales/zh-CN/core/execution-history.md +7 -0
  53. package/bundled/locales/zh-CN/core/governance-layer.md +118 -6
  54. package/bundled/locales/zh-CN/core/pipeline-integration-standards.md +7 -0
  55. package/bundled/locales/zh-CN/core/retrospective-standards.md +1 -1
  56. package/bundled/locales/zh-CN/core/tech-debt-standards.md +71 -4
  57. package/bundled/locales/zh-CN/core/turn-completion-integrity.md +190 -0
  58. package/bundled/locales/zh-CN/core/workflow-enforcement.md +7 -0
  59. package/bundled/locales/zh-CN/core/workflow-state-protocol.md +7 -0
  60. package/bundled/locales/zh-CN/docs/CHEATSHEET.md +8 -1
  61. package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +29 -68
  62. package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +25 -15
  63. package/bundled/locales/zh-CN/docs/MIGRATION-v6.md +8 -4
  64. package/bundled/locales/zh-CN/docs/USAGE-MODES-COMPARISON.md +1 -2
  65. package/bundled/locales/zh-CN/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
  66. package/bundled/locales/zh-CN/integrations/google-antigravity/README.md +3 -3
  67. package/bundled/locales/zh-CN/skills/atdd-assistant/SKILL.md +2 -0
  68. package/bundled/locales/zh-CN/skills/bdd-assistant/SKILL.md +2 -0
  69. package/bundled/locales/zh-CN/skills/brainstorm-assistant/SKILL.md +22 -12
  70. package/bundled/locales/zh-CN/skills/brainstorm-assistant/guide.md +12 -9
  71. package/bundled/locales/zh-CN/skills/code-review-assistant/SKILL.md +1 -0
  72. package/bundled/locales/zh-CN/skills/commands/brainstorm.md +17 -13
  73. package/bundled/locales/zh-CN/skills/commands/config.md +0 -1
  74. package/bundled/locales/zh-CN/skills/commands/init.md +1 -2
  75. package/bundled/locales/zh-CN/skills/commit-standards/SKILL.md +2 -0
  76. package/bundled/locales/zh-CN/skills/contract-test-assistant/SKILL.md +1 -0
  77. package/bundled/locales/zh-CN/skills/dev-methodology/SKILL.md +2 -0
  78. package/bundled/locales/zh-CN/skills/observability-assistant/SKILL.md +1 -0
  79. package/bundled/locales/zh-CN/skills/project-structure-guide/SKILL.md +1 -0
  80. package/bundled/locales/zh-CN/skills/release-standards/SKILL.md +3 -0
  81. package/bundled/locales/zh-CN/skills/requirement-assistant/SKILL.md +2 -0
  82. package/bundled/locales/zh-CN/skills/reverse-engineer/SKILL.md +3 -0
  83. package/bundled/locales/zh-CN/skills/runbook-assistant/SKILL.md +1 -0
  84. package/bundled/locales/zh-CN/skills/slo-assistant/SKILL.md +1 -0
  85. package/bundled/locales/zh-CN/skills/tdd-assistant/SKILL.md +2 -0
  86. package/bundled/locales/zh-TW/CHANGELOG.md +74 -3
  87. package/bundled/locales/zh-TW/CLAUDE.md +1 -1
  88. package/bundled/locales/zh-TW/README.md +2 -2
  89. package/bundled/locales/zh-TW/SECURITY.md +1 -1
  90. package/bundled/locales/zh-TW/core/acceptance-criteria-traceability.md +2 -0
  91. package/bundled/locales/zh-TW/core/adr-standards.md +26 -5
  92. package/bundled/locales/zh-TW/core/agent-communication-protocol.md +7 -0
  93. package/bundled/locales/zh-TW/core/branch-completion.md +7 -0
  94. package/bundled/locales/zh-TW/core/change-batching-standards.md +7 -0
  95. package/bundled/locales/zh-TW/core/code-review-checklist.md +2 -0
  96. package/bundled/locales/zh-TW/core/container-image-standards.md +2 -2
  97. package/bundled/locales/zh-TW/core/contract-testing-standards.md +2 -2
  98. package/bundled/locales/zh-TW/core/cross-flow-regression.md +8 -7
  99. package/bundled/locales/zh-TW/core/data-contract.md +2 -2
  100. package/bundled/locales/zh-TW/core/data-migration-testing.md +2 -2
  101. package/bundled/locales/zh-TW/core/data-pipeline.md +2 -2
  102. package/bundled/locales/zh-TW/core/deferred-item-exit.md +251 -0
  103. package/bundled/locales/zh-TW/core/documentation-writing-standards.md +228 -3
  104. package/bundled/locales/zh-TW/core/execution-history.md +7 -0
  105. package/bundled/locales/zh-TW/core/full-coverage-testing.md +15 -2
  106. package/bundled/locales/zh-TW/core/governance-layer.md +118 -5
  107. package/bundled/locales/zh-TW/core/iac-design-principles.md +2 -2
  108. package/bundled/locales/zh-TW/core/incident-response.md +2 -2
  109. package/bundled/locales/zh-TW/core/model-provenance.md +4 -2
  110. package/bundled/locales/zh-TW/core/pii-classification.md +42 -6
  111. package/bundled/locales/zh-TW/core/pipeline-integration-standards.md +7 -0
  112. package/bundled/locales/zh-TW/core/prd-standards.md +4 -2
  113. package/bundled/locales/zh-TW/core/product-metrics-standards.md +4 -2
  114. package/bundled/locales/zh-TW/core/release-readiness-gate.md +2 -2
  115. package/bundled/locales/zh-TW/core/resource-cost-boundary.md +2 -2
  116. package/bundled/locales/zh-TW/core/retrospective-standards.md +5 -3
  117. package/bundled/locales/zh-TW/core/reverse-engineering-standards.md +83 -5
  118. package/bundled/locales/zh-TW/core/runbook.md +2 -2
  119. package/bundled/locales/zh-TW/core/schema-evolution.md +2 -2
  120. package/bundled/locales/zh-TW/core/secret-management-standards.md +2 -2
  121. package/bundled/locales/zh-TW/core/slo-sli.md +2 -2
  122. package/bundled/locales/zh-TW/core/spec-driven-development.md +2 -0
  123. package/bundled/locales/zh-TW/core/tech-debt-standards.md +71 -4
  124. package/bundled/locales/zh-TW/core/turn-completion-integrity.md +190 -0
  125. package/bundled/locales/zh-TW/core/user-journey-testing.md +2 -2
  126. package/bundled/locales/zh-TW/core/user-story-mapping.md +2 -2
  127. package/bundled/locales/zh-TW/core/verification-oracle.md +2 -2
  128. package/bundled/locales/zh-TW/core/workflow-enforcement.md +7 -0
  129. package/bundled/locales/zh-TW/core/workflow-state-protocol.md +7 -0
  130. package/bundled/locales/zh-TW/docs/CHEATSHEET.md +8 -1
  131. package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +29 -68
  132. package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +25 -15
  133. package/bundled/locales/zh-TW/docs/MIGRATION-v6.md +8 -4
  134. package/bundled/locales/zh-TW/docs/USAGE-MODES-COMPARISON.md +1 -2
  135. package/bundled/locales/zh-TW/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
  136. package/bundled/locales/zh-TW/integrations/google-antigravity/README.md +3 -3
  137. package/bundled/locales/zh-TW/skills/adr-assistant/SKILL.md +1 -1
  138. package/bundled/locales/zh-TW/skills/atdd-assistant/SKILL.md +2 -0
  139. package/bundled/locales/zh-TW/skills/bdd-assistant/SKILL.md +2 -0
  140. package/bundled/locales/zh-TW/skills/brainstorm-assistant/SKILL.md +22 -12
  141. package/bundled/locales/zh-TW/skills/brainstorm-assistant/guide.md +12 -9
  142. package/bundled/locales/zh-TW/skills/code-review-assistant/SKILL.md +1 -0
  143. package/bundled/locales/zh-TW/skills/commands/brainstorm.md +17 -13
  144. package/bundled/locales/zh-TW/skills/commands/config.md +0 -1
  145. package/bundled/locales/zh-TW/skills/commands/init.md +1 -2
  146. package/bundled/locales/zh-TW/skills/commit-standards/SKILL.md +2 -0
  147. package/bundled/locales/zh-TW/skills/contract-test-assistant/SKILL.md +2 -1
  148. package/bundled/locales/zh-TW/skills/dev-methodology/SKILL.md +2 -0
  149. package/bundled/locales/zh-TW/skills/dev-workflow-guide/SKILL.md +1 -1
  150. package/bundled/locales/zh-TW/skills/knowledge-graph/guide.md +2 -2
  151. package/bundled/locales/zh-TW/skills/migration-assistant/SKILL.md +1 -1
  152. package/bundled/locales/zh-TW/skills/observability-assistant/SKILL.md +1 -0
  153. package/bundled/locales/zh-TW/skills/project-discovery/SKILL.md +1 -0
  154. package/bundled/locales/zh-TW/skills/project-structure-guide/SKILL.md +1 -0
  155. package/bundled/locales/zh-TW/skills/release-standards/SKILL.md +3 -0
  156. package/bundled/locales/zh-TW/skills/requirement-assistant/SKILL.md +2 -0
  157. package/bundled/locales/zh-TW/skills/reverse-engineer/SKILL.md +3 -0
  158. package/bundled/locales/zh-TW/skills/runbook-assistant/SKILL.md +1 -0
  159. package/bundled/locales/zh-TW/skills/slo-assistant/SKILL.md +1 -0
  160. package/bundled/locales/zh-TW/skills/tdd-assistant/SKILL.md +2 -0
  161. package/bundled/skills/atdd-assistant/SKILL.md +2 -0
  162. package/bundled/skills/bdd-assistant/SKILL.md +2 -0
  163. package/bundled/skills/brainstorm-assistant/SKILL.md +31 -13
  164. package/bundled/skills/brainstorm-assistant/guide.md +9 -6
  165. package/bundled/skills/code-review-assistant/SKILL.md +1 -0
  166. package/bundled/skills/commands/brainstorm.md +12 -9
  167. package/bundled/skills/commands/config.md +0 -1
  168. package/bundled/skills/commands/init.md +2 -3
  169. package/bundled/skills/commit-standards/SKILL.md +2 -0
  170. package/bundled/skills/contract-test-assistant/SKILL.md +1 -0
  171. package/bundled/skills/dev-methodology/SKILL.md +4 -0
  172. package/bundled/skills/observability-assistant/SKILL.md +1 -0
  173. package/bundled/skills/project-discovery/SKILL.md +1 -0
  174. package/bundled/skills/project-structure-guide/SKILL.md +1 -0
  175. package/bundled/skills/release-standards/SKILL.md +3 -0
  176. package/bundled/skills/requirement-assistant/SKILL.md +2 -0
  177. package/bundled/skills/reverse-engineer/SKILL.md +3 -0
  178. package/bundled/skills/runbook-assistant/SKILL.md +1 -0
  179. package/bundled/skills/slo-assistant/SKILL.md +1 -0
  180. package/bundled/skills/tdd-assistant/SKILL.md +2 -0
  181. package/bundled/templates/.ai-context.yaml.template +194 -0
  182. package/bundled/templates/CLAUDE.md.template +145 -0
  183. package/bundled/templates/DESIGN.md +237 -0
  184. package/bundled/templates/SKILL-BRIEF-TEMPLATE.md +57 -0
  185. package/bundled/templates/SKILL-CANDIDATES.md +39 -0
  186. package/bundled/templates/gates/check-error-exit.mjs +309 -0
  187. package/bundled/templates/mcp-config.json +10 -0
  188. package/bundled/templates/methodology-template.yaml +209 -0
  189. package/bundled/templates/migration-template.md +408 -0
  190. package/bundled/templates/requirement-checklist.md +410 -0
  191. package/bundled/templates/requirement-document-template.md +591 -0
  192. package/bundled/templates/requirement-template.md +881 -0
  193. package/bundled/templates/reverse-spec-template.md +409 -0
  194. package/bundled/templates/test-case-template.md +74 -0
  195. package/bundled/templates/test-plan-template.md +74 -0
  196. package/package.json +12 -9
  197. package/src/commands/audit.js +82 -0
  198. package/src/commands/check.js +227 -34
  199. package/src/commands/config.js +19 -19
  200. package/src/commands/init.js +174 -20
  201. package/src/commands/spec.js +2 -2
  202. package/src/commands/update.js +537 -43
  203. package/src/compilers/claude-code-compiler.js +4 -1
  204. package/src/config/ai-agent-paths.js +62 -17
  205. package/src/core/constants.js +42 -11
  206. package/src/core/manifest.js +201 -3
  207. package/src/core/paths.js +2 -2
  208. package/src/i18n/messages.js +42 -29
  209. package/src/installers/hooks-installer.js +167 -75
  210. package/src/installers/integration-installer.js +13 -9
  211. package/src/installers/skills-installer.js +4 -4
  212. package/src/installers/standards-installer.js +3 -3
  213. package/src/prompts/init.js +47 -18
  214. package/src/reconciler/diff-engine.js +57 -2
  215. package/src/reconciler/plan-executor.js +31 -11
  216. package/src/utils/detector.js +21 -1
  217. package/src/utils/effect-boundary.js +1093 -0
  218. package/src/utils/hasher.js +166 -1
  219. package/src/utils/hook-stats.js +1 -1
  220. package/src/utils/integration-generator.js +288 -82
  221. package/src/utils/reference-sync.js +107 -8
  222. package/src/utils/registry.js +57 -0
  223. package/src/utils/spinner.js +31 -0
  224. package/src/utils/yaml-generator.js +51 -9
  225. package/standards-registry.json +32 -9
@@ -33,9 +33,12 @@ export class ClaudeCodeCompiler extends BaseCompiler {
33
33
  hooks[trigger] = [];
34
34
  }
35
35
 
36
+ // Claude Code dispatches on `type`; an entry without one is skipped
37
+ // silently. Measured 2026-09-08 with both shapes in the same event of the
38
+ // same session: the object ran, the bare string did not.
36
39
  hooks[trigger].push({
37
40
  matcher,
38
- hooks: [`node ${hook_script}`],
41
+ hooks: [{ type: 'command', command: `node ${hook_script}` }],
39
42
  });
40
43
  }
41
44
 
@@ -139,7 +139,19 @@ export const AI_AGENT_PATHS = {
139
139
  user: join(homedir(), '.roo', 'workflows')
140
140
  },
141
141
  supportsMarketplace: false,
142
- fallbackSkillsPath: '.claude/skills/',
142
+ // 🔴 Was '.claude/skills/', with no provenance. Roo Code's docs ENUMERATE where it
143
+ // looks for skills — `.roo/skills/`, `.agents/skills/`, and their `~` twins — and
144
+ // `.claude/skills/` is not among them. An enumerated discovery list that omits a
145
+ // path is a statement, not silence (same shape as the Codex list that this field
146
+ // was falsified against by measurement).
147
+ //
148
+ // ⚠️ Docs only, 2026-09-08: NOT measured, because there is no Roo Code CLI on this
149
+ // machine. Acting on it anyway because the asymmetry is one-sided — wrong here
150
+ // means skills land in `.roo/skills/`, which the docs confirm is read; leaving it
151
+ // meant `uds init` routed Roo Code to Claude's marketplace and installed **zero**
152
+ // skills, which is what it actually did until this change.
153
+ // (roocodeinc.github.io/Roo-Code/features/skills)
154
+ fallbackSkillsPath: null,
143
155
  supportsSkills: true,
144
156
  supportsTask: true,
145
157
  supportsAgents: true
@@ -169,7 +181,14 @@ export const AI_AGENT_PATHS = {
169
181
  },
170
182
  workflows: null,
171
183
  supportsMarketplace: false,
172
- fallbackSkillsPath: '.claude/skills/',
184
+ // 🔴 Was '.claude/skills/'. MEASURED FALSE 2026-09-08 (XSPEC-408 §18.3): two probe
185
+ // arms differing only in install path. At `.agents/skills` Codex reported "Skill
186
+ // descriptions were shortened to fit the 2% skills context budget — Codex can
187
+ // still see every skill", 18,096 input tokens. At `.claude/skills` it said
188
+ // nothing, 15,235. The discovery list documented above never included that
189
+ // directory; this field asserted otherwise with no provenance, and `check.js`
190
+ // printed it to adopters as "Can use fallback".
191
+ fallbackSkillsPath: null,
173
192
  supportsSkills: true,
174
193
  supportsTask: false,
175
194
  supportsAgents: true
@@ -251,28 +270,54 @@ export const AI_AGENT_PATHS = {
251
270
  },
252
271
  'antigravity': {
253
272
  name: 'Google Antigravity',
254
- tier: 'minimal',
255
- // Skills install path is UNVERIFIED against a real Antigravity CLI, so it is null:
256
- // `supportsSkills && skills` is the install guard everywhere (init.js, init-flow.js,
257
- // update.js, config.js), and a null `skills` makes it decline rather than write to a
258
- // path the tool may never read.
273
+ // Raised from 'minimal' 2026-09-08. It was minimal because UDS installed
274
+ // nothing for it — the tier was describing our own gap, not the tool. It now
275
+ // gets 55 skills and an instruction file, the same as the other 'partial'
276
+ // tools, and passes all six of that tier's rules.
277
+ tier: 'partial',
278
+ // Skills path CONFIRMED 2026-09-08, three independent ways:
279
+ // 1. The agy 1.0.14 binary carries the literal template
280
+ // `{workspace}/.agents/skills/{skill_name}/SKILL.md`.
281
+ // 2. The guide shipped with the tool
282
+ // (~/.gemini/antigravity-cli/builtin/skills/antigravity_guide/references/ide.md)
283
+ // says `<project-root>/.agents/` holds "project-specific rules, custom skills,
284
+ // and plugins".
285
+ // 3. A live two-arm run: a skill at `.agents/skills/` was listed back with its
286
+ // description verbatim; a decoy at `.agent/skills/` was not listed at all.
287
+ //
288
+ // 🔴 BOTH candidates previously recorded here were wrong — the official-plugin path
289
+ // and `.agent/skills/` (singular). Neither had been tested; declining to install was
290
+ // the right call for six months, and the answer came from the tool's own binary, not
291
+ // from either document.
259
292
  //
260
- // Two candidates conflict, and neither has been tested:
261
- // a) ~/.gemini/antigravity-cli/plugins/<name>/skills/ -- official plugin docs
262
- // b) .agent/skills/ + ~/.gemini/antigravity/skills -- UDS's own 2026-02 spec,
263
- // written while Gemini CLI was still the product; Antigravity replaced it on
264
- // 2026-06-18, so (b) inherits an assumption that may no longer hold.
293
+ // 🔴 PRECONDITION the adopter must meet: agy only discovers `.agents/` when the
294
+ // directory is a registered Antigravity project (~/.gemini/projects.json). Measured:
295
+ // the same probe repo returned only the builtin skill before `agy --new-project` and
296
+ // both skills after. Installing here is correct, but a user who never opens the
297
+ // project in Antigravity will see nothing — that is the tool's condition, not ours.
265
298
  //
266
- // Installing to the wrong path fails SILENTLY -- the user sees a successful init and
267
- // an assistant that never picks the skills up. Declining is the safer default until
268
- // one candidate is confirmed. Tracked as XSPEC-355 OQ6.
269
- skills: null,
299
+ // No `user` path: the binary has exactly one skills template and it is
300
+ // workspace-scoped. A user-level path would be invented, which is how the two wrong
301
+ // candidates above got written down in the first place.
302
+ skills: {
303
+ project: '.agents/skills/'
304
+ },
305
+ // Shown to the adopter after a successful install. A field rather than a
306
+ // special case for this tool: the next tool with a precondition fills it in
307
+ // instead of adding a branch. Without it the condition lives only in a spec,
308
+ // and the adopter — who reads neither — sees a successful install and an
309
+ // assistant that picked nothing up.
310
+ installNote:
311
+ 'Antigravity only discovers .agents/ once the directory is a registered '
312
+ + 'Antigravity project. Open it in the IDE, or run `agy --new-project` here '
313
+ + 'once. Measured 2026-09-08: the same repo reported only the built-in skill '
314
+ + 'before that and all of them after.',
270
315
  commands: null,
271
316
  agents: null,
272
317
  workflows: null,
273
318
  supportsMarketplace: false,
274
319
  fallbackSkillsPath: null,
275
- supportsSkills: true, // The tool does support skills; only our path for it is unverified.
320
+ supportsSkills: true,
276
321
  supportsTask: false,
277
322
  supportsAgents: false
278
323
  }
@@ -94,16 +94,25 @@ export const SUPPORTED_AI_TOOLS = {
94
94
  category: 'secondary',
95
95
  supports: ['skills']
96
96
  },
97
- 'roo': {
98
- name: 'Roo',
99
- file: 'ROO.md',
97
+ // 🔴 Key was 'roo' and the file was 'ROO.md'. Every other module calls this tool
98
+ // `roo-code` (`ai-agent-paths.js`, `REGISTRY.json`, `agent-adapter.js`,
99
+ // `agents-installer.js`), so every lookup here MISSED — and the miss was silent:
100
+ // `getToolFileName()` ends in `return \`${tool}.md\``, so a real `uds init` in a Roo
101
+ // Code repo wrote its instructions to `roo-code.md` in the repo root. Roo Code's
102
+ // docs name `.roo/rules/`, `.roorules`, and `AGENTS.md`; `roo-code.md` is read by
103
+ // nothing, and `ROO.md` was never written by anything either.
104
+ // File value matches REGISTRY.json's `instructionFile` for this agent.
105
+ // (roocodeinc.github.io/Roo-Code/features/custom-instructions, read 2026-09-08)
106
+ 'roo-code': {
107
+ name: 'Roo Code',
108
+ file: '.roo/rules/uds-standards.md',
100
109
  format: 'markdown',
101
110
  category: 'secondary',
102
111
  supports: ['skills', 'workflows']
103
112
  },
104
113
  'antigravity': {
105
114
  name: 'Antigravity',
106
- file: 'INSTRUCTIONS.md',
115
+ file: '.agents/AGENTS.md',
107
116
  format: 'markdown',
108
117
  category: 'secondary',
109
118
  supports: ['skills', 'workflows']
@@ -122,7 +131,12 @@ export const SUPPORTED_AI_TOOLS = {
122
131
  */
123
132
  export const LEGACY_TOOL_MAPPINGS = {
124
133
  'codex': 'opencode',
125
- 'copilot': 'github-copilot'
134
+ 'copilot': 'github-copilot',
135
+ // Maps the OLD key to the CORRECT one, never the reverse — an alias pointing at
136
+ // the wrong name would make the wrong name authoritative. No manifest in any of
137
+ // the five adopting repos records 'roo' (checked 2026-09-08); this exists for
138
+ // manifests written before the rename.
139
+ 'roo': 'roo-code'
126
140
  };
127
141
 
128
142
  /**
@@ -141,10 +155,13 @@ export const FILE_EXTENSIONS = {
141
155
  /**
142
156
  * Content modes for integration files
143
157
  */
158
+ // `FULL: 'full'` was removed here (XSPEC-357 R7). It never produced different
159
+ // output from INDEX — the generator's only test on the mode is `=== 'minimal'`
160
+ // — so it was a second name for one behaviour, offered to adopters as a
161
+ // distinct choice. See SUPPORTED_CONTENT_MODES in core/manifest.js.
144
162
  export const CONTENT_MODES = {
145
163
  MINIMAL: 'minimal', // Reference-only content
146
- INDEX: 'index', // Standard index with descriptions
147
- FULL: 'full' // Full standard content embedded
164
+ INDEX: 'index' // Standard index with descriptions + compliance instructions
148
165
  };
149
166
 
150
167
  /**
@@ -541,13 +558,27 @@ export function resolveToolKey(entry) {
541
558
  if (typeof entry !== 'string' || entry.length === 0) return null;
542
559
  if (SUPPORTED_AI_TOOLS[entry]) return entry;
543
560
  const norm = entry.replace(/\\/g, '/');
561
+
562
+ // Exact or path-suffix only. A bare basename match would let `MY-CLAUDE.md`
563
+ // or `CLAUDE.md.bak` pass as the managed integration file.
564
+ //
565
+ // 🔴 Two passes, and the order matters. Antigravity's file is
566
+ // `.agents/AGENTS.md` while OpenCode's is `AGENTS.md`, so a single pass
567
+ // resolved `.agents/AGENTS.md` to OpenCode — whichever tool happened to be
568
+ // declared first won a path the other had spelled out in full. An exactly
569
+ // declared path must beat another tool's suffix, and among suffixes the
570
+ // longest (most specific) one wins.
571
+ for (const [key, cfg] of Object.entries(SUPPORTED_AI_TOOLS)) {
572
+ if (cfg?.file && norm === cfg.file) return key;
573
+ }
574
+ let best = null;
544
575
  for (const [key, cfg] of Object.entries(SUPPORTED_AI_TOOLS)) {
545
576
  if (!cfg?.file) continue;
546
- // Exact or path-suffix only. A bare basename match would let `MY-CLAUDE.md`
547
- // or `CLAUDE.md.bak` pass as the managed integration file.
548
- if (norm === cfg.file || norm.endsWith(`/${cfg.file}`)) return key;
577
+ if (norm.endsWith(`/${cfg.file}`) && (!best || cfg.file.length > best.len)) {
578
+ best = { key, len: cfg.file.length };
579
+ }
549
580
  }
550
- return null;
581
+ return best ? best.key : null;
551
582
  }
552
583
 
553
584
  /** Entry (tool key OR file path) → repo-relative integration file, or null. */
@@ -55,6 +55,62 @@ export const DEFAULT_MANIFEST = {
55
55
  integrationBlockHashes: {}
56
56
  };
57
57
 
58
+ /**
59
+ * Content modes an adopter may choose.
60
+ *
61
+ * `full` was retired in favour of `index` (XSPEC-357 R7). It was never a third
62
+ * behaviour: the only place the mode is read is a single `=== 'minimal'` test
63
+ * in `generateComplianceInstructions`, so `full` and `index` took the same
64
+ * branch and produced byte-identical files — 24 of 24 tool × language
65
+ * combinations, against a control confirming the comparison does detect a real
66
+ * difference (`full` vs `minimal` differed in all 24). Meanwhile the manifest
67
+ * schema accepted it, `--content-mode` offered it, the init prompt listed it as
68
+ * a distinct trade-off ("highest compliance, uses more context"), and every
69
+ * `partial`-tier tool was assigned it automatically. Adopters were choosing
70
+ * between two labels for one behaviour, and the label promised a difference
71
+ * that did not exist.
72
+ */
73
+ export const SUPPORTED_CONTENT_MODES = ['minimal', 'index'];
74
+
75
+ /**
76
+ * Retired content modes and what they now resolve to.
77
+ *
78
+ * The rewrite changes no adopter's output, because the two modes already
79
+ * generated the same bytes — which is what makes this breaking change cheap
80
+ * enough to do at all. Kept as a map rather than special-cased at each call
81
+ * site so the next retirement has somewhere to go.
82
+ */
83
+ export const RETIRED_CONTENT_MODES = { full: 'index' };
84
+
85
+ /**
86
+ * Resolve a content mode, migrating retired ones and flagging unknown ones.
87
+ *
88
+ * An unknown value resolves to `fallback` and is reported in `unrecognized`, so
89
+ * the caller can warn. The original intent was to throw — a *silent* fallback
90
+ * turns `--content-mode indx` into a normal run with nothing saying so — and
91
+ * that remains the goal for 7.0.0. It does not throw in 6.x because 6.8.0
92
+ * accepted unknown values and generated the `index` output, and
93
+ * `core/versioning.md` requires a deprecation warning in a MINOR release before
94
+ * behaviour that used to work starts failing. The warning is what removes the
95
+ * silence; the error is the MAJOR-version step.
96
+ *
97
+ * @param {string|undefined} mode - Requested mode
98
+ * @param {string} fallback - Mode to use when nothing was requested, or when the value is unknown
99
+ * @returns {{mode: string, migratedFrom: string|null, unrecognized: string|null}}
100
+ */
101
+ export function normalizeContentMode(mode, fallback = 'index') {
102
+ if (mode === undefined || mode === null || mode === '') {
103
+ return { mode: fallback, migratedFrom: null, unrecognized: null };
104
+ }
105
+ if (SUPPORTED_CONTENT_MODES.includes(mode)) {
106
+ return { mode, migratedFrom: null, unrecognized: null };
107
+ }
108
+ if (Object.prototype.hasOwnProperty.call(RETIRED_CONTENT_MODES, mode)) {
109
+ return { mode: RETIRED_CONTENT_MODES[mode], migratedFrom: mode, unrecognized: null };
110
+ }
111
+ return { mode: fallback, migratedFrom: null, unrecognized: mode };
112
+ }
113
+
58
114
  /**
59
115
  * Validation schema for manifest
60
116
  */
@@ -63,10 +119,125 @@ const MANIFEST_SCHEMA = {
63
119
  properties: {
64
120
  version: { type: 'string', pattern: '^\\d+\\.\\d+\\.\\d+$' },
65
121
  format: { type: 'string', enum: ['ai', 'human', 'both'] },
66
- contentMode: { type: 'string', enum: ['minimal', 'index', 'full'] }
122
+ contentMode: { type: 'string', enum: SUPPORTED_CONTENT_MODES }
67
123
  }
68
124
  };
69
125
 
126
+ /**
127
+ * Provenance — the record of which files UDS itself wrote. (XSPEC-384 R1)
128
+ *
129
+ * Ownership is a question about *origin*, and origin is something you record at
130
+ * the moment you write, not something you infer afterwards from another table.
131
+ * `fileHashes` was doing the inferring: absence from it was read as "not ours",
132
+ * although a manifest loses entries through version migrations, hand edits,
133
+ * merge conflicts, and older CLI bugs. The tool then picked the destructive
134
+ * branch for the case it could not actually distinguish.
135
+ *
136
+ * Two properties matter and neither is shared with `fileHashes`:
137
+ *
138
+ * - It is append-only. `fileHashes` is current state and gets rewritten every
139
+ * run; provenance is history. If both were written and pruned together,
140
+ * provenance would add nothing — it would just be a second copy that fails
141
+ * the same way at the same moment.
142
+ * - It is keyed by the path actually written, taken from the copy result,
143
+ * not by re-deriving the path from the source name. The re-derivation is
144
+ * wrong for nested option files (`options/<category>/<name>.ai.yaml` is
145
+ * flattened to `options/<name>.ai.yaml`), so a path-math-based record would
146
+ * claim ownership of files that do not exist while disowning ones that do.
147
+ *
148
+ * An entry is removed only when UDS deliberately deletes the file — deleting it
149
+ * is how we relinquish the claim, so that a file a user later creates under the
150
+ * same name is not silently inherited.
151
+ */
152
+ export const PROVENANCE_SCHEMA_VERSION = 1;
153
+
154
+ /**
155
+ * Valid writers, used so a typo becomes an error rather than an unrecognised
156
+ * provenance record that reads as "not ours".
157
+ */
158
+ export const PROVENANCE_WRITERS = ['init', 'update', 'config', 'sync'];
159
+
160
+ /**
161
+ * Record that UDS wrote a file.
162
+ * @param {Object} manifest - Manifest (not mutated)
163
+ * @param {string} relPath - Path relative to project root
164
+ * @param {string} writer - One of PROVENANCE_WRITERS
165
+ * @returns {Object} Updated manifest
166
+ */
167
+ export function recordFileProvenance(manifest, relPath, writer) {
168
+ if (!relPath) return manifest;
169
+ const normalized = relPath.replace(/\\/g, '/');
170
+ const at = new Date().toISOString();
171
+ const existing = manifest?.provenance?.files?.[normalized];
172
+ return {
173
+ ...manifest,
174
+ provenance: {
175
+ schema: PROVENANCE_SCHEMA_VERSION,
176
+ ...(manifest?.provenance || {}),
177
+ files: {
178
+ ...(manifest?.provenance?.files || {}),
179
+ [normalized]: {
180
+ firstWrittenBy: existing?.firstWrittenBy || writer,
181
+ firstWrittenAt: existing?.firstWrittenAt || at,
182
+ lastWrittenBy: writer,
183
+ lastWrittenAt: at
184
+ }
185
+ }
186
+ }
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Drop a provenance record, relinquishing UDS's claim on a path.
192
+ * @param {Object} manifest - Manifest (not mutated)
193
+ * @param {string} relPath - Path relative to project root
194
+ * @returns {Object} Updated manifest
195
+ */
196
+ export function forgetFileProvenance(manifest, relPath) {
197
+ const normalized = (relPath || '').replace(/\\/g, '/');
198
+ const files = { ...(manifest?.provenance?.files || {}) };
199
+ delete files[normalized];
200
+ return {
201
+ ...manifest,
202
+ provenance: {
203
+ schema: PROVENANCE_SCHEMA_VERSION,
204
+ ...(manifest?.provenance || {}),
205
+ files
206
+ }
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Mark provenance as a complete account of what UDS owns.
212
+ *
213
+ * Called at the end of a run that wrote the full installed set. Until this has
214
+ * happened at least once, a file with no provenance record is `unknown` rather
215
+ * than `foreign`, because the absence of a record proves nothing yet.
216
+ *
217
+ * @param {Object} manifest - Manifest (not mutated)
218
+ * @returns {Object} Updated manifest
219
+ */
220
+ export function establishProvenance(manifest) {
221
+ return {
222
+ ...manifest,
223
+ provenance: {
224
+ schema: PROVENANCE_SCHEMA_VERSION,
225
+ files: {},
226
+ ...(manifest?.provenance || {}),
227
+ establishedAt: manifest?.provenance?.establishedAt || new Date().toISOString()
228
+ }
229
+ };
230
+ }
231
+
232
+ /**
233
+ * Whether provenance can be trusted as a complete list of UDS-owned files.
234
+ * @param {Object} manifest - Manifest
235
+ * @returns {boolean}
236
+ */
237
+ export function isProvenanceEstablished(manifest) {
238
+ return !!manifest?.provenance?.establishedAt;
239
+ }
240
+
70
241
  /**
71
242
  * Get manifest file path for a project
72
243
  * @param {string} projectPath - Project root path
@@ -354,10 +525,25 @@ function migrateToV320(manifest) {
354
525
  return {
355
526
  ...manifest,
356
527
  version: '3.2.0',
357
- contentMode: manifest.contentMode || 'index'
528
+ contentMode: migrateRetiredContentMode(manifest.contentMode)
358
529
  };
359
530
  }
360
531
 
532
+ /**
533
+ * Rewrite a retired content mode to its replacement, leaving anything else
534
+ * alone. Unlike `normalizeContentMode` this never throws: it runs while reading
535
+ * a manifest, and refusing to load a project because of one stale string would
536
+ * be a worse outcome than loading it and letting `validateManifest` report.
537
+ * @param {string|undefined} mode
538
+ * @returns {string}
539
+ */
540
+ function migrateRetiredContentMode(mode) {
541
+ if (mode && Object.prototype.hasOwnProperty.call(RETIRED_CONTENT_MODES, mode)) {
542
+ return RETIRED_CONTENT_MODES[mode];
543
+ }
544
+ return mode || 'index';
545
+ }
546
+
361
547
  /**
362
548
  * Migration to version 3.3.0
363
549
  * @param {Object} manifest - V3.2.x manifest
@@ -502,11 +688,23 @@ function ensureRequiredFields(manifest) {
502
688
  ...manifest,
503
689
  options,
504
690
  standards,
505
- contentMode: manifest.contentMode || 'index',
691
+ // Rewrites a stored `full` to `index`. This is the migration path for
692
+ // existing adopters and it runs on every read, so a manifest written by an
693
+ // older CLI is corrected the first time a newer one touches it — without
694
+ // changing a single byte of what that project's AI tool files receive.
695
+ contentMode: migrateRetiredContentMode(manifest.contentMode),
506
696
  fileHashes: normalizedHashes,
507
697
  skillHashes: manifest.skillHashes || {},
508
698
  commandHashes: manifest.commandHashes || {},
509
699
  integrationBlockHashes: manifest.integrationBlockHashes || {},
700
+ // Present but deliberately NOT established. Filling in `establishedAt`
701
+ // here would declare "these records are complete" about a manifest that
702
+ // has never had a provenance-aware run, which is the one claim that turns
703
+ // every pre-existing file into `foreign` — and `foreign` is the state the
704
+ // deletion path acts on. Only a completed run may establish it.
705
+ // A fresh object per call: a shared default would be one mutable record
706
+ // handed to every manifest in the process. (XSPEC-384 R1)
707
+ provenance: manifest.provenance || { schema: PROVENANCE_SCHEMA_VERSION, files: {} },
510
708
  };
511
709
  }
512
710
 
package/src/core/paths.js CHANGED
@@ -149,8 +149,8 @@ export class PathResolver {
149
149
  'copilot': 'copilot-instructions.md',
150
150
  'aider': '.aider.conf.yml',
151
151
  'opencode': 'AGENTS.md',
152
- 'roo': 'ROO.md',
153
- 'antigravity': 'INSTRUCTIONS.md'
152
+ 'roo-code': '.roo/rules/uds-standards.md',
153
+ 'antigravity': '.agents/AGENTS.md'
154
154
  };
155
155
 
156
156
  const targetFile = toolTargets[tool] || fileName;