release-skill 0.9.17 → 0.9.19

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 (266) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/.cursor-plugin/plugin.json +10 -0
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/.qoder-plugin/plugin.json +1 -1
  7. package/CHANGELOG.md +70 -2
  8. package/INSTALL.md +66 -9
  9. package/INSTALL.zh-CN.md +49 -8
  10. package/README.md +41 -20
  11. package/README.zh-CN.md +31 -19
  12. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  13. package/adapters/claude/bin/consumer-contract-vectors.json +7 -7
  14. package/adapters/claude/bin/error-codes.json +1 -1
  15. package/adapters/claude/bin/foundation-resource-binding.json +1 -1
  16. package/adapters/claude/bin/prebuild-manifest.json +8 -4
  17. package/adapters/claude/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
  18. package/adapters/claude/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
  19. package/adapters/claude/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
  20. package/adapters/claude/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
  21. package/adapters/claude/bin/registry.json +1 -1
  22. package/adapters/claude/bin/release-skill.bundle.mjs +4425 -1519
  23. package/adapters/claude/bin/rules.json +1 -1
  24. package/adapters/claude/data/hosts/cursor/host-descriptor.json +34 -0
  25. package/adapters/claude/data/hosts/registry.json +1 -0
  26. package/adapters/claude/schemas/release-plan.schema.json +255 -25
  27. package/adapters/claude/schemas/release-project.schema.json +228 -26
  28. package/adapters/claude/skills/release-finish/SKILL.md +46 -7
  29. package/adapters/claude/skills/release-help/SKILL.md +47 -103
  30. package/adapters/claude/skills/release-verify/SKILL.md +2 -2
  31. package/adapters/claude/src/schemas/host-descriptor.schema.json +9 -2
  32. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  33. package/adapters/codex/bin/consumer-contract-vectors.json +7 -7
  34. package/adapters/codex/bin/error-codes.json +1 -1
  35. package/adapters/codex/bin/foundation-resource-binding.json +1 -1
  36. package/adapters/codex/bin/prebuild-manifest.json +8 -4
  37. package/adapters/codex/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
  38. package/adapters/codex/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
  39. package/adapters/codex/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
  40. package/adapters/codex/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
  41. package/adapters/codex/bin/registry.json +1 -1
  42. package/adapters/codex/bin/release-skill.bundle.mjs +4425 -1519
  43. package/adapters/codex/bin/rules.json +1 -1
  44. package/adapters/codex/data/hosts/cursor/host-descriptor.json +34 -0
  45. package/adapters/codex/data/hosts/registry.json +1 -0
  46. package/adapters/codex/schemas/release-plan.schema.json +255 -25
  47. package/adapters/codex/schemas/release-project.schema.json +228 -26
  48. package/adapters/codex/skills/release-finish/SKILL.md +46 -7
  49. package/adapters/codex/skills/release-help/SKILL.md +49 -105
  50. package/adapters/codex/skills/release-verify/SKILL.md +2 -2
  51. package/adapters/codex/src/schemas/host-descriptor.schema.json +9 -2
  52. package/adapters/cursor/.cursor-plugin/plugin.json +10 -0
  53. package/adapters/cursor/bin/consumer-contract-vectors.json +110 -0
  54. package/adapters/cursor/bin/error-codes.json +161 -0
  55. package/adapters/cursor/bin/foundation-legal/skill-family-contracts/LICENSE +202 -0
  56. package/adapters/cursor/bin/foundation-legal/skill-family-contracts/NOTICE +10 -0
  57. package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/LICENSE +202 -0
  58. package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/NOTICE +10 -0
  59. package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/THIRD_PARTY_NOTICES +142 -0
  60. package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/LICENSE +202 -0
  61. package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/NOTICE +10 -0
  62. package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/native-NOTICE +2 -0
  63. package/adapters/cursor/bin/foundation-resource-binding.json +1 -0
  64. package/adapters/cursor/bin/kernel-protocol.json +60 -0
  65. package/adapters/cursor/bin/license-texts/Apache-2.0.txt +201 -0
  66. package/adapters/cursor/bin/license-texts/MIT.txt +21 -0
  67. package/adapters/cursor/bin/prebuild-manifest.json +83 -0
  68. package/adapters/cursor/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
  69. package/adapters/cursor/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
  70. package/adapters/cursor/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
  71. package/adapters/cursor/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
  72. package/adapters/cursor/bin/registry.json +347 -0
  73. package/adapters/cursor/bin/release-skill-local-finish.mjs +4 -0
  74. package/adapters/cursor/bin/release-skill.bundle.mjs +152352 -0
  75. package/adapters/cursor/bin/release-skill.mjs +54 -0
  76. package/adapters/cursor/bin/rules.json +104 -0
  77. package/adapters/cursor/bin/schemas/consumer-contract-vector.schema.json +149 -0
  78. package/adapters/cursor/data/hosts/claude/host-descriptor.json +45 -0
  79. package/adapters/cursor/data/hosts/codebuddy/host-descriptor.json +26 -0
  80. package/adapters/cursor/data/hosts/codex/host-descriptor.json +45 -0
  81. package/adapters/cursor/data/hosts/cursor/host-descriptor.json +34 -0
  82. package/adapters/cursor/data/hosts/deepseek-harness/host-descriptor.json +23 -0
  83. package/adapters/cursor/data/hosts/kimi-code/host-descriptor.json +36 -0
  84. package/adapters/cursor/data/hosts/qoder/host-descriptor.json +26 -0
  85. package/adapters/cursor/data/hosts/registry.json +14 -0
  86. package/adapters/cursor/data/hosts/workbuddy/host-descriptor.json +34 -0
  87. package/adapters/cursor/data/licensing/registry.json +211 -0
  88. package/adapters/cursor/data/licensing/schema.json +207 -0
  89. package/adapters/cursor/native/safe-write/binding.gyp +41 -0
  90. package/adapters/cursor/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  91. package/adapters/cursor/native/safe-write/prebuilds.json +24 -0
  92. package/adapters/cursor/native/safe-write/src/safe_write.cc +2032 -0
  93. package/adapters/cursor/schemas/.render-manifest.json +41 -0
  94. package/adapters/cursor/schemas/approval-record.schema.json +115 -0
  95. package/adapters/cursor/schemas/artifact-lock.schema.json +111 -0
  96. package/adapters/cursor/schemas/artifact-plan.schema.json +52 -0
  97. package/adapters/cursor/schemas/artifact-policy.schema.json +76 -0
  98. package/adapters/cursor/schemas/evidence-event-v2.schema.json +164 -0
  99. package/adapters/cursor/schemas/evidence-event.schema.json +89 -0
  100. package/adapters/cursor/schemas/postpublish-approval-record.schema.json +47 -0
  101. package/adapters/cursor/schemas/release-plan.schema.json +2226 -0
  102. package/adapters/cursor/schemas/release-project.schema.json +1815 -0
  103. package/adapters/cursor/schemas/release-run.schema.json +777 -0
  104. package/adapters/cursor/skills/release-assess/SKILL.md +61 -0
  105. package/adapters/cursor/skills/release-config/SKILL.md +151 -0
  106. package/adapters/cursor/skills/release-docs/SKILL.md +120 -0
  107. package/adapters/cursor/skills/release-finish/SKILL.md +181 -0
  108. package/adapters/cursor/skills/release-help/SKILL.md +98 -0
  109. package/adapters/cursor/skills/release-marketplace/SKILL.md +157 -0
  110. package/adapters/cursor/skills/release-prepare/SKILL.md +136 -0
  111. package/adapters/cursor/skills/release-publish/SKILL.md +92 -0
  112. package/adapters/cursor/skills/release-reconcile/SKILL.md +81 -0
  113. package/adapters/cursor/skills/release-setup/SKILL.md +132 -0
  114. package/adapters/cursor/skills/release-verify/SKILL.md +110 -0
  115. package/adapters/cursor/src/schemas/adapter-build-manifest.schema.json +41 -0
  116. package/adapters/cursor/src/schemas/adapter-peer-verification-request.schema.json +54 -0
  117. package/adapters/cursor/src/schemas/adapter-peer-verification-result.schema.json +54 -0
  118. package/adapters/cursor/src/schemas/adapter-source.schema.json +47 -0
  119. package/adapters/cursor/src/schemas/audit-baseline-pin.schema.json +163 -0
  120. package/adapters/cursor/src/schemas/declared-read-surface-result.schema.json +61 -0
  121. package/adapters/cursor/src/schemas/engineering-baseline.schema.json +77 -0
  122. package/adapters/cursor/src/schemas/executable-identity-observation.schema.json +162 -0
  123. package/adapters/cursor/src/schemas/filesystem-root-binding.schema.json +14 -0
  124. package/adapters/cursor/src/schemas/filesystem-tree-observation.schema.json +71 -0
  125. package/adapters/cursor/src/schemas/fixed-set-publication-manifest.schema.json +108 -0
  126. package/adapters/cursor/src/schemas/fixed-set-publication-receipt.schema.json +148 -0
  127. package/adapters/cursor/src/schemas/host-capability-fact.schema.json +31 -0
  128. package/adapters/cursor/src/schemas/host-descriptor.schema.json +144 -0
  129. package/adapters/cursor/src/schemas/host-operation-plan.schema.json +57 -0
  130. package/adapters/cursor/src/schemas/host-operation-receipt.schema.json +81 -0
  131. package/adapters/cursor/src/schemas/host-probe-result.schema.json +30 -0
  132. package/adapters/cursor/src/schemas/host-registry.schema.json +18 -0
  133. package/adapters/cursor/src/schemas/host-verification-request.schema.json +67 -0
  134. package/adapters/cursor/src/schemas/host-verification-result.schema.json +149 -0
  135. package/adapters/cursor/src/schemas/managed-file-lock.schema.json +77 -0
  136. package/adapters/cursor/src/schemas/migration-manifest.schema.json +412 -0
  137. package/adapters/cursor/src/schemas/observation-scope.schema.json +91 -0
  138. package/adapters/cursor/src/schemas/operation-request.schema.json +58 -0
  139. package/adapters/cursor/src/schemas/operation-result.schema.json +130 -0
  140. package/adapters/cursor/src/schemas/platform-difference-registry.schema.json +110 -0
  141. package/adapters/cursor/src/schemas/plugin-verification-request.schema.json +499 -0
  142. package/adapters/cursor/src/schemas/plugin-verification-result.schema.json +1217 -0
  143. package/adapters/cursor/src/schemas/profile-adoption-declaration.schema.json +111 -0
  144. package/adapters/cursor/src/schemas/profile-descriptor.schema.json +99 -0
  145. package/adapters/cursor/src/schemas/project-manifest.schema.json +67 -0
  146. package/adapters/cursor/src/schemas/project-profile.schema.json +25 -0
  147. package/adapters/cursor/src/schemas/public-boundary-declaration.schema.json +73 -0
  148. package/adapters/cursor/src/schemas/report-binding.schema.json +42 -0
  149. package/adapters/cursor/src/schemas/report-model.schema.json +308 -0
  150. package/adapters/cursor/src/schemas/skill-family-directory-verification-request.schema.json +100 -0
  151. package/adapters/cursor/src/schemas/skill-family-directory-verification-result.schema.json +193 -0
  152. package/adapters/cursor/src/schemas/source-authority-receipt.schema.json +31 -0
  153. package/adapters/cursor/src/schemas/state-event-envelope.schema.json +38 -0
  154. package/adapters/cursor/src/schemas/state-snapshot-metadata.schema.json +25 -0
  155. package/adapters/cursor/src/schemas/structured-scan-policy.schema.json +97 -0
  156. package/adapters/cursor/src/schemas/surface-scan-policy.schema.json +42 -0
  157. package/adapters/cursor/src/schemas/timeout-policy.schema.json +24 -0
  158. package/adapters/cursor/src/schemas/token-estimate-record.schema.json +75 -0
  159. package/adapters/cursor/src/schemas/token-estimate-result.schema.json +43 -0
  160. package/adapters/cursor/src/schemas/watchdog-termination-envelope.schema.json +156 -0
  161. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  162. package/adapters/kimi/bin/consumer-contract-vectors.json +7 -7
  163. package/adapters/kimi/bin/error-codes.json +1 -1
  164. package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
  165. package/adapters/kimi/bin/prebuild-manifest.json +8 -4
  166. package/adapters/kimi/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
  167. package/adapters/kimi/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
  168. package/adapters/kimi/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
  169. package/adapters/kimi/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
  170. package/adapters/kimi/bin/registry.json +1 -1
  171. package/adapters/kimi/bin/release-skill.bundle.mjs +4425 -1519
  172. package/adapters/kimi/bin/rules.json +1 -1
  173. package/adapters/kimi/data/hosts/cursor/host-descriptor.json +34 -0
  174. package/adapters/kimi/data/hosts/registry.json +1 -0
  175. package/adapters/kimi/schemas/release-plan.schema.json +255 -25
  176. package/adapters/kimi/schemas/release-project.schema.json +228 -26
  177. package/adapters/kimi/skills/release-finish/SKILL.md +46 -7
  178. package/adapters/kimi/skills/release-help/SKILL.md +49 -105
  179. package/adapters/kimi/skills/release-verify/SKILL.md +2 -2
  180. package/adapters/kimi/src/schemas/host-descriptor.schema.json +9 -2
  181. package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
  182. package/adapters/qoder/bin/consumer-contract-vectors.json +7 -7
  183. package/adapters/qoder/bin/error-codes.json +1 -1
  184. package/adapters/qoder/bin/foundation-resource-binding.json +1 -1
  185. package/adapters/qoder/bin/prebuild-manifest.json +8 -4
  186. package/adapters/qoder/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
  187. package/adapters/qoder/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
  188. package/adapters/qoder/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
  189. package/adapters/qoder/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
  190. package/adapters/qoder/bin/registry.json +1 -1
  191. package/adapters/qoder/bin/release-skill.bundle.mjs +4425 -1519
  192. package/adapters/qoder/bin/rules.json +1 -1
  193. package/adapters/qoder/data/hosts/cursor/host-descriptor.json +34 -0
  194. package/adapters/qoder/data/hosts/registry.json +1 -0
  195. package/adapters/qoder/schemas/release-plan.schema.json +255 -25
  196. package/adapters/qoder/schemas/release-project.schema.json +228 -26
  197. package/adapters/qoder/skills/release-finish/SKILL.md +46 -7
  198. package/adapters/qoder/skills/release-help/SKILL.md +49 -105
  199. package/adapters/qoder/skills/release-verify/SKILL.md +2 -2
  200. package/adapters/qoder/src/schemas/host-descriptor.schema.json +9 -2
  201. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  202. package/adapters/workbuddy/bin/consumer-contract-vectors.json +7 -7
  203. package/adapters/workbuddy/bin/error-codes.json +1 -1
  204. package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
  205. package/adapters/workbuddy/bin/prebuild-manifest.json +8 -4
  206. package/adapters/workbuddy/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
  207. package/adapters/workbuddy/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
  208. package/adapters/workbuddy/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
  209. package/adapters/workbuddy/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
  210. package/adapters/workbuddy/bin/registry.json +1 -1
  211. package/adapters/workbuddy/bin/release-skill.bundle.mjs +4425 -1519
  212. package/adapters/workbuddy/bin/rules.json +1 -1
  213. package/adapters/workbuddy/data/hosts/cursor/host-descriptor.json +34 -0
  214. package/adapters/workbuddy/data/hosts/registry.json +1 -0
  215. package/adapters/workbuddy/schemas/release-plan.schema.json +255 -25
  216. package/adapters/workbuddy/schemas/release-project.schema.json +228 -26
  217. package/adapters/workbuddy/skills/release-finish/SKILL.md +46 -7
  218. package/adapters/workbuddy/skills/release-help/SKILL.md +47 -103
  219. package/adapters/workbuddy/skills/release-verify/SKILL.md +2 -2
  220. package/adapters/workbuddy/src/schemas/host-descriptor.schema.json +9 -2
  221. package/bin/consumer-contract-vectors.json +7 -7
  222. package/bin/error-codes.json +1 -1
  223. package/bin/foundation-resource-binding.json +1 -1
  224. package/bin/prebuild-manifest.json +8 -4
  225. package/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
  226. package/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
  227. package/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
  228. package/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
  229. package/bin/registry.json +1 -1
  230. package/bin/release-skill-cli.mjs +115 -15
  231. package/bin/release-skill.bundle.mjs +4425 -1519
  232. package/bin/rules.json +1 -1
  233. package/data/hosts/cursor/host-descriptor.json +34 -0
  234. package/data/hosts/registry.json +1 -0
  235. package/package.json +7 -5
  236. package/platform-manifest.json +75 -9
  237. package/references/02-project-config.md +4 -0
  238. package/schemas/release-plan.schema.json +255 -25
  239. package/schemas/release-project.schema.json +228 -26
  240. package/scripts/sync-public-files.mjs +3 -22
  241. package/skills/release-finish/SKILL.md +46 -7
  242. package/skills/release-help/SKILL.md +47 -103
  243. package/skills/release-verify/SKILL.md +2 -2
  244. package/skills-src/release-finish/SKILL.md +46 -7
  245. package/skills-src/release-help/SKILL.md +47 -103
  246. package/skills-src/release-verify/SKILL.md +2 -2
  247. package/src/adapters/contract.mjs +1 -0
  248. package/src/adapters/plugin-marketplace.mjs +201 -9
  249. package/src/commands/post-release-finish.mjs +448 -0
  250. package/src/commands/post-release-local.mjs +312 -36
  251. package/src/commands/prepare.mjs +74 -9
  252. package/src/commands/publish.mjs +4 -1
  253. package/src/commands/ship.mjs +3 -0
  254. package/src/commands/verify.mjs +73 -0
  255. package/src/core/checkpoints.mjs +5 -2
  256. package/src/core/config.mjs +1 -1
  257. package/src/core/foundation-host-verification.mjs +168 -0
  258. package/src/core/plan.mjs +19 -1
  259. package/src/core/postpublish.mjs +9 -1
  260. package/src/core/run-retention.mjs +1 -1
  261. package/src/core/surface-host-bindings.mjs +1 -0
  262. package/src/core/verification-gates.mjs +1 -1
  263. package/src/platforms/registry.mjs +225 -16
  264. package/src/producers/build-adapters.mjs +21 -26
  265. package/src/producers/foundation-resource-projection.mjs +3 -3
  266. package/src/schemas/host-descriptor.schema.json +9 -2
@@ -220,6 +220,7 @@ const ACTION_TARGET_FIELDS = Object.freeze({
220
220
  'codex-marketplace-install': Object.freeze(['consumer', 'plugin', 'marketplace', 'repo', 'ref', 'version', 'entrySkill']),
221
221
  'kimi-marketplace-install': Object.freeze(['consumer', 'plugin', 'repo', 'ref', 'version', 'entrySkill']),
222
222
  'codebuddy-marketplace-install': Object.freeze(['consumer', 'plugin', 'repo', 'ref', 'version', 'entrySkill']),
223
+ 'qoder-marketplace-install': Object.freeze(['consumer', 'plugin', 'marketplace', 'repo', 'ref', 'version', 'entrySkill']),
223
224
  'set-default-branch': Object.freeze(['repo', 'oldBranch', 'newBranch', 'expectedNewBranchCommit']),
224
225
  });
225
226
 
@@ -705,6 +706,7 @@ export async function advanceShip(options = {}, injected = {}) {
705
706
  adapterRegistry: options.adapterRegistry,
706
707
  root,
707
708
  verificationGatesAuthorized: state.verificationGatesAuthorized === true,
709
+ cursorHostRuntime: options.cursorHostRuntime,
708
710
  });
709
711
  state = {
710
712
  ...state,
@@ -837,6 +839,7 @@ export async function advanceShip(options = {}, injected = {}) {
837
839
  try {
838
840
  postRelease = derivePostReleaseChecklist(finalPlan, {
839
841
  root,
842
+ planPath: state.planPath,
840
843
  statePath,
841
844
  unitIds: state.selectedUnitIds,
842
845
  runPath: localFinishRunPath,
@@ -72,6 +72,7 @@ import {
72
72
  isFoundationPluginVerificationEligible,
73
73
  verifyFrozenPluginWithFoundation,
74
74
  } from '../core/foundation-plugin-verification.mjs';
75
+ import { verifyFrozenCursorSkillWithFoundation } from '../core/foundation-host-verification.mjs';
75
76
  import { resolveUnitScopedPath } from '../snapshot/public-path.mjs';
76
77
  import { canonicalJson } from '../core/digest.mjs';
77
78
  import {
@@ -130,6 +131,53 @@ export const VERIFICATION_RESOLVED_TYPES = Object.freeze({
130
131
  NOT_REQUIRED_UNCHANGED: 'NOT_REQUIRED_UNCHANGED',
131
132
  });
132
133
 
134
+ /** Verify actionless Cursor distributions through their frozen host contract. */
135
+ export async function verifyCursorHostDistributions({
136
+ plan,
137
+ root,
138
+ cursorHostRuntime,
139
+ runHostVerificationFn,
140
+ clock = defaultClock,
141
+ evidence,
142
+ } = {}) {
143
+ const checks = [];
144
+ for (const unit of plan?.units ?? []) {
145
+ const cursorDistribution = (unit.distributions ?? []).find(
146
+ (distribution) => distribution.type === 'cursor-plugin',
147
+ );
148
+ if (!cursorDistribution) continue;
149
+ const result = await verifyFrozenCursorSkillWithFoundation({
150
+ plan,
151
+ unitId: unit.id,
152
+ root,
153
+ cursorHostRuntime,
154
+ ...(runHostVerificationFn ? { runHostVerificationFn } : {}),
155
+ clock,
156
+ });
157
+ checks.push({
158
+ actionId: `cursor-host-verification-${unit.id}`,
159
+ actionType: 'cursor-host-verification',
160
+ unitId: unit.id,
161
+ distributionType: cursorDistribution.type,
162
+ status: VERIFICATION_RESOLVED_TYPES.PASSED_AUTOMATIC,
163
+ observation: result,
164
+ });
165
+ await evidence?.append({
166
+ phase: 'verify-host',
167
+ unitId: unit.id,
168
+ distributionType: cursorDistribution.type,
169
+ status: VERIFICATION_RESOLVED_TYPES.PASSED_AUTOMATIC,
170
+ hostId: result.hostId,
171
+ driverId: result.driverId,
172
+ requestDigest: result.requestDigest,
173
+ payloadDigest: result.payloadDigest,
174
+ entrySkill: result.entrySkill,
175
+ observation: result,
176
+ });
177
+ }
178
+ return checks;
179
+ }
180
+
133
181
  /** Re-read every frozen subject tarball before consuming the public receipt. */
134
182
  export async function verifyPublicSourceAuthorityReceiptOffline({ plan, root }) {
135
183
  const descriptor = plan.publicSourceAuthorityReceipt;
@@ -179,6 +227,7 @@ const ADAPTER_ACTION_TYPE_MAP = {
179
227
  'codex-marketplace-install': 'codex-marketplace-install',
180
228
  'kimi-marketplace-install': 'kimi-marketplace-install',
181
229
  'codebuddy-marketplace-install': 'codebuddy-marketplace-install',
230
+ 'qoder-marketplace-install': 'qoder-marketplace-install',
182
231
  };
183
232
 
184
233
  // ---------------------------------------------------------------------------
@@ -349,6 +398,7 @@ async function collectMissingManualAttestations({
349
398
  const manual = actions.filter((action) => (
350
399
  action.type === 'kimi-marketplace-install'
351
400
  || action.type === 'codebuddy-marketplace-install'
401
+ || action.type === 'qoder-marketplace-install'
352
402
  ));
353
403
  if (manual.length === 0) return [];
354
404
 
@@ -1097,6 +1147,8 @@ export async function verifyRelease(options) {
1097
1147
  execFn,
1098
1148
  configPath: configPathOpt,
1099
1149
  runPluginVerificationFn,
1150
+ runHostVerificationFn,
1151
+ cursorHostRuntime,
1100
1152
  observeFirstReleaseMarketplaceHeadFn,
1101
1153
  fetchFirstReleaseMarketplaceIndexFn,
1102
1154
  } = options ?? {};
@@ -1576,6 +1628,20 @@ export async function verifyRelease(options) {
1576
1628
  const manualFollowUps = [];
1577
1629
  const foundationPluginVerificationReceipts = [];
1578
1630
 
1631
+ // Cursor has no marketplace action or publish checkpoint. Verify each
1632
+ // declared Cursor distribution directly from its frozen unit contract,
1633
+ // using explicit runtime roots that never enter the release run record.
1634
+ // Plans without Cursor distributions do not enter
1635
+ // this branch and retain their existing action-only behavior.
1636
+ adapterChecks.push(...await verifyCursorHostDistributions({
1637
+ plan,
1638
+ root,
1639
+ cursorHostRuntime,
1640
+ runHostVerificationFn,
1641
+ clock: clockFn,
1642
+ evidence,
1643
+ }));
1644
+
1579
1645
  // Every action is identity-bound and adapters receive read-only or
1580
1646
  // per-action isolated consumer paths. Run independent checks concurrently;
1581
1647
  // the evidence writer serializes append operations and result arrays are
@@ -1710,6 +1776,7 @@ export async function verifyRelease(options) {
1710
1776
  'codex-marketplace-install': 'codex-plugin',
1711
1777
  'kimi-marketplace-install': 'kimi-plugin',
1712
1778
  'codebuddy-marketplace-install': 'codebuddy-plugin',
1779
+ 'qoder-marketplace-install': 'qoder-plugin',
1713
1780
  };
1714
1781
  const dist = unit?.distributions?.find((d) => d.type === typeToDist[action.type]);
1715
1782
 
@@ -1896,6 +1963,8 @@ export async function verifyRelease(options) {
1896
1963
  ? 'codex-plugin'
1897
1964
  : action.type === 'codebuddy-marketplace-install'
1898
1965
  ? 'codebuddy-plugin'
1966
+ : action.type === 'qoder-marketplace-install'
1967
+ ? 'qoder-plugin'
1899
1968
  : 'kimi-plugin';
1900
1969
  const installPath = verifyResult.observation?.installPath;
1901
1970
  consumerGateResults.push(...await runConsumerVerificationGates({
@@ -1919,6 +1988,10 @@ export async function verifyRelease(options) {
1919
1988
  ? {
1920
1989
  HOME: resolve(runDir, 'consumers', `codebuddy-${action.parameters.plugin}`),
1921
1990
  }
1991
+ : action.type === 'qoder-marketplace-install'
1992
+ ? {
1993
+ HOME: resolve(runDir, 'consumers', `qoder-${action.parameters.plugin}`),
1994
+ }
1922
1995
  : {
1923
1996
  HOME: resolve(runDir, 'consumers', `kimi-${action.parameters.plugin}`),
1924
1997
  },
@@ -34,6 +34,7 @@ export const CHECKPOINT_ORDER = [
34
34
  'codex-marketplace-install',
35
35
  'kimi-marketplace-install',
36
36
  'codebuddy-marketplace-install',
37
+ 'qoder-marketplace-install',
37
38
  ];
38
39
 
39
40
  /**
@@ -54,6 +55,7 @@ export const ADAPTER_ACTION_TYPE_MAP = {
54
55
  'codex-marketplace-install': 'codex-marketplace-install',
55
56
  'kimi-marketplace-install': 'kimi-marketplace-install',
56
57
  'codebuddy-marketplace-install': 'codebuddy-marketplace-install',
58
+ 'qoder-marketplace-install': 'qoder-marketplace-install',
57
59
  };
58
60
 
59
61
  /**
@@ -68,7 +70,7 @@ export const ADAPTER_ACTION_TYPE_MAP = {
68
70
  * placed in Tier 1 only for conservative scheduling.
69
71
  * - Tier 2 `github-release` depends on Tier 1 `create-tag` (release `--verify-tag`).
70
72
  *
71
- * Marketplace actions (claude/codex/kimi/codebuddy-marketplace-install) are
73
+ * Marketplace actions (claude/codex/kimi/codebuddy/qoder-marketplace-install) are
72
74
  * included in the tier table for ADAPTER_ACTION_TYPE_MAP lookup but are
73
75
  * filtered out before tier grouping in both publish and reconcile. They are
74
76
  * recorded as DEFERRED with CONSUMER_VERIFICATION_DEFERRED reason and never
@@ -82,7 +84,7 @@ export const TIER_TABLE = [
82
84
  ['push-commit', 'push-snapshot'], // Tier 0
83
85
  ['set-default-branch', 'create-tag', 'npm-publish'], // Tier 1
84
86
  ['github-release', 'claude-marketplace-install', 'codex-marketplace-install'], // Tier 2
85
- ['kimi-marketplace-install', 'codebuddy-marketplace-install'], // Tier 3
87
+ ['kimi-marketplace-install', 'codebuddy-marketplace-install', 'qoder-marketplace-install'], // Tier 3
86
88
  ];
87
89
 
88
90
  /** Fast reverse lookup: action type -> tier index (-1 when unknown). */
@@ -170,6 +172,7 @@ export const MARKETPLACE_ACTION_TYPES = new Set([
170
172
  'codex-marketplace-install',
171
173
  'kimi-marketplace-install',
172
174
  'codebuddy-marketplace-install',
175
+ 'qoder-marketplace-install',
173
176
  ]);
174
177
 
175
178
  /**
@@ -401,7 +401,7 @@ export async function loadProjectConfig({ root, configPath } = {}) {
401
401
  for (const unit of config.releaseUnits) {
402
402
  if (!unit?.distributions) continue;
403
403
  for (const dist of unit.distributions) {
404
- if (dist.type === 'npm') continue;
404
+ if (dist.type === 'npm' || dist.type === 'cursor-plugin') continue;
405
405
  if (dist.marketplaceSourceType === undefined || dist.marketplaceSourceType === null) {
406
406
  dist.marketplaceSourceType = dist.marketplaceRepo ? 'standalone-index' : 'bundled-family';
407
407
  }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Release-domain mapping for one frozen Cursor Skill. Foundation owns request
3
+ * preparation, installation, process supervision and bound stream reads.
4
+ */
5
+ import { basename, join, isAbsolute } from 'node:path';
6
+ import { mkdir } from 'node:fs/promises';
7
+ import { prepareHostVerification, runHostVerification } from 'skill-family-engineering-kit';
8
+ import { canonicalJson } from 'skill-family-contracts';
9
+ import { createTemporaryWorkspace } from 'skill-family-harness-node';
10
+
11
+ import { verifyFrozenSnapshot } from '../snapshot/frozen.mjs';
12
+ import { canonicalPublicPath, publicPathCollisionKey } from '../snapshot/public-path.mjs';
13
+ import { CONSUMER_VERIFICATION_DEFERRED, POST_PUBLISH_VERIFY_FAILED, ReleaseError } from './errors.mjs';
14
+
15
+ function fail(message, details = {}) {
16
+ return new ReleaseError(POST_PUBLISH_VERIFY_FAILED, message, details);
17
+ }
18
+
19
+ /** Release-owned scenario policy, shared by freezing and consuming the contract. */
20
+ export function assertCursorHostScenario(scenario) {
21
+ const keys = ['workloadDocument', 'fixtureFiles', 'protectedWorkspaceFiles', 'platformManifest', 'effectivePrompt', 'expectedResult'];
22
+ if (!scenario || typeof scenario !== 'object' || Array.isArray(scenario)
23
+ || Object.keys(scenario).length !== keys.length || keys.some((key) => !Object.hasOwn(scenario, key))) {
24
+ throw fail('Cursor host verification requires the complete frozen scenario');
25
+ }
26
+ for (const key of ['workloadDocument', 'effectivePrompt', 'expectedResult']) {
27
+ if (typeof scenario[key] !== 'string' || !scenario[key].isWellFormed()
28
+ || (key !== 'expectedResult' && scenario[key].length === 0)
29
+ || (key === 'effectivePrompt' && scenario[key].includes('\0'))) {
30
+ throw fail('Cursor scenario requires valid UTF-8 text', { field: key });
31
+ }
32
+ }
33
+ if (!scenario.platformManifest || typeof scenario.platformManifest !== 'object' || Array.isArray(scenario.platformManifest)) {
34
+ throw fail('Cursor scenario platformManifest must be a JSON object');
35
+ }
36
+ canonicalJson(scenario.platformManifest);
37
+ for (const field of ['fixtureFiles', 'protectedWorkspaceFiles']) {
38
+ const files = scenario[field];
39
+ if (!Array.isArray(files) || files.length === 0) throw fail('Cursor scenario files must be non-empty', { field });
40
+ const paths = [];
41
+ for (const file of files) {
42
+ if (!file || Object.keys(file).length !== 2 || typeof file.content !== 'string' || !file.content.isWellFormed()) {
43
+ throw fail('Cursor scenario file requires path and UTF-8 content', { field });
44
+ }
45
+ let path;
46
+ try { path = canonicalPublicPath(file.path).path; } catch { throw fail('Cursor scenario file path is unsafe', { field }); }
47
+ const key = publicPathCollisionKey(path);
48
+ if (path !== file.path || key.split('/').includes('.cursor')
49
+ || paths.some((other) => other === key || other.startsWith(key + '/') || key.startsWith(other + '/'))) {
50
+ throw fail('Cursor scenario files conflict or target a Cursor control path', { field });
51
+ }
52
+ paths.push(key);
53
+ }
54
+ }
55
+ }
56
+
57
+ /** Consume only frozen domain facts and this invocation's explicit private roots. */
58
+ export async function verifyFrozenCursorSkillWithFoundation({
59
+ plan, unitId, root, cursorHostRuntime,
60
+ runHostVerificationFn = runHostVerification,
61
+ clock = () => new Date().toISOString(),
62
+ } = {}) {
63
+ const unit = (plan?.units ?? []).find((candidate) => candidate.id === unitId);
64
+ const distribution = unit?.distributions?.find((candidate) => candidate.type === 'cursor-plugin');
65
+ const contract = distribution?.hostVerificationContract;
66
+ if (!unit?.frozenSnapshot?.path || !unit.frozenSnapshot.commit || !unit.frozenSnapshot.manifestDigest) {
67
+ throw fail('Cursor host verification requires a frozen unit snapshot', { unitId });
68
+ }
69
+ if (contract?.contractVersion !== 1 || contract.hostId !== 'cursor'
70
+ || contract.entrySkill !== distribution.entrySkill
71
+ || contract.payloadDigest !== unit.frozenSnapshot.manifestDigest
72
+ || contract.manifestRelativePath !== 'skills/' + contract.entrySkill + '/SKILL.md') {
73
+ throw fail('Cursor host verification contract does not bind the frozen payload and entry Skill', { unitId });
74
+ }
75
+ assertCursorHostScenario(contract.scenario);
76
+ if (!cursorHostRuntime || Object.keys(cursorHostRuntime).length !== 2
77
+ || !['executableRoot', 'existingUserStateRoot'].every((key) =>
78
+ typeof cursorHostRuntime[key] === 'string' && isAbsolute(cursorHostRuntime[key]))) {
79
+ throw fail('Cursor host verification requires explicit absolute executableRoot and existingUserStateRoot', { unitId });
80
+ }
81
+ const frozen = await verifyFrozenSnapshot({ root, snapshotPath: unit.frozenSnapshot.path, expectedDigest: contract.payloadDigest });
82
+ const prefix = 'skills/' + contract.entrySkill + '/';
83
+ const members = frozen.entries.filter((entry) => entry.type === 'file' && entry.path.startsWith(prefix))
84
+ .map((entry) => entry.path.slice(prefix.length));
85
+ if (!members.includes('SKILL.md')) throw fail('Cursor frozen entry Skill is missing SKILL.md', { unitId });
86
+ const scenario = contract.scenario;
87
+ const owned = await createTemporaryWorkspace({ prefix: 'release-skill-cursor-' });
88
+ let retain = false;
89
+ let prepared;
90
+ let result;
91
+ try {
92
+ const repositoryRoot = await owned.resolve('repository');
93
+ const workspaceRoot = join(repositoryRoot, 'workspace');
94
+ const sessionRoot = await owned.resolve('session');
95
+ await mkdir(workspaceRoot, { recursive: true, mode: 0o700 });
96
+ await mkdir(sessionRoot, { mode: 0o700 });
97
+ for (const file of scenario.protectedWorkspaceFiles) {
98
+ await owned.writeFile('repository/workspace/' + file.path, file.content, { mode: 0o600 });
99
+ }
100
+ prepared = await prepareHostVerification({
101
+ hostId: 'cursor',
102
+ verificationSetId: plan.digest + ':' + unitId + ':cursor',
103
+ candidate: {
104
+ ref: unit.frozenSnapshot.commit,
105
+ manifest: canonicalJson({
106
+ contractVersion: contract.contractVersion, unitId, candidateRef: unit.frozenSnapshot.commit,
107
+ payloadDigest: contract.payloadDigest, entrySkill: contract.entrySkill,
108
+ manifestRelativePath: contract.manifestRelativePath,
109
+ }),
110
+ },
111
+ skill: { root: join(frozen.snapshotDir, 'skills', contract.entrySkill), entrySkill: contract.entrySkill, members },
112
+ workloadDocument: scenario.workloadDocument,
113
+ fixtureFiles: scenario.fixtureFiles,
114
+ workspace: { repositoryRoot, root: workspaceRoot, protectedMembers: scenario.protectedWorkspaceFiles.map((file) => file.path) },
115
+ platformManifest: canonicalJson(scenario.platformManifest),
116
+ effectivePrompt: scenario.effectivePrompt,
117
+ executable: { root: cursorHostRuntime.executableRoot, relPath: 'cursor-agent' },
118
+ existingUserStateRoot: cursorHostRuntime.existingUserStateRoot,
119
+ sessionRoot,
120
+ timeoutPolicy: {
121
+ schemaVersion: 1, kind: 'skill-family.timeout-policy',
122
+ maxSeconds: (distribution.timeoutMs ?? 300000) / 1000, killGraceSeconds: 1,
123
+ },
124
+ });
125
+ // An unexpected exception after dispatch cannot establish process termination.
126
+ retain = true;
127
+ result = await runHostVerificationFn(prepared);
128
+ retain = !['observed', 'failed', 'rejected'].includes(result?.status);
129
+ if (retain) {
130
+ throw new ReleaseError(CONSUMER_VERIFICATION_DEFERRED,
131
+ 'Cursor invocation is indeterminate; inspect the retained private scene before retrying with fresh runtime inputs', {
132
+ unitId,
133
+ requirements: [{ unitId, hostId: 'cursor', action: 'inspect-retained-cursor-scene', sceneId: basename(owned.root) }],
134
+ });
135
+ }
136
+ const stdout = result.streams ? await prepared.readInvocationStream(result, 'stdout') : null;
137
+ if (result.status !== 'observed') {
138
+ throw fail('Foundation Cursor invocation did not reach an observed result', { unitId, status: result.status, reason: result.reason });
139
+ }
140
+ if (!stdout) throw fail('Cursor invocation has no bound stdout evidence', { unitId });
141
+ let answer;
142
+ try { answer = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(stdout)); } catch {
143
+ throw fail('Cursor invocation did not return valid UTF-8 success JSON', { unitId });
144
+ }
145
+ if (answer?.type !== 'result' || answer.subtype !== 'success' || answer.is_error !== false
146
+ || typeof answer.result !== 'string' || answer.result !== scenario.expectedResult) {
147
+ throw fail('Cursor result does not match the frozen expected result', { unitId });
148
+ }
149
+ return {
150
+ planDigest: plan.digest, unitId, distributionType: 'cursor-plugin',
151
+ candidateRef: unit.frozenSnapshot.commit, payloadDigest: contract.payloadDigest,
152
+ manifestRelativePath: contract.manifestRelativePath, entrySkill: contract.entrySkill,
153
+ hostId: result.host.hostId, driverId: result.host.driverId, foundationStatus: result.status,
154
+ requestDigest: result.requestDigest, runtimeIdentities: result.runtimeIdentities,
155
+ execution: result.execution, snapshots: result.snapshots, streams: result.streams, observedAt: clock(),
156
+ };
157
+ } catch (error) {
158
+ if (error instanceof ReleaseError) throw error;
159
+ if (retain) {
160
+ throw new ReleaseError(CONSUMER_VERIFICATION_DEFERRED, 'Cursor invocation outcome is unknown; inspect the retained private scene', {
161
+ unitId, requirements: [{ unitId, hostId: 'cursor', action: 'inspect-retained-cursor-scene', sceneId: basename(owned.root) }],
162
+ });
163
+ }
164
+ throw fail('Foundation Cursor host verification failed closed', { unitId, foundationCode: error?.code ?? null });
165
+ } finally {
166
+ if (!retain) await owned.dispose();
167
+ }
168
+ }
package/src/core/plan.mjs CHANGED
@@ -413,7 +413,9 @@ function buildExpectedAdapterMap() {
413
413
  'create-tag': 'git-github',
414
414
  'github-release': 'github',
415
415
  'npm-publish': 'npm',
416
- ...Object.fromEntries(PLATFORMS.map((p) => [p.actionType, p.adapter])),
416
+ ...Object.fromEntries(PLATFORMS
417
+ .filter((platform) => platform.actionType !== null)
418
+ .map((platform) => [platform.actionType, platform.adapter])),
417
419
  'set-default-branch': 'git-github',
418
420
  };
419
421
  }
@@ -857,6 +859,22 @@ export function validatePlanActionCompleteness(plan, options = {}) {
857
859
  for (const platform of PLATFORMS) {
858
860
  const dist = distributions.find((d) => d.type === platform.distributionType);
859
861
  if (!dist) continue;
862
+ if (platform.actionType === null) {
863
+ if (!dist.entrySkill) {
864
+ failures.push(`unit "${unitId}": ${platform.distributionType} distribution requires entrySkill`);
865
+ }
866
+ const contract = dist.hostVerificationContract;
867
+ if (
868
+ !contract
869
+ || contract.hostId !== platform.id
870
+ || contract.entrySkill !== dist.entrySkill
871
+ || contract.manifestRelativePath !== `skills/${dist.entrySkill}/SKILL.md`
872
+ || contract.payloadDigest !== frozen?.manifestDigest
873
+ ) {
874
+ failures.push(`unit "${unitId}": ${platform.distributionType} distribution host verification contract does not match the frozen payload`);
875
+ }
876
+ continue;
877
+ }
860
878
  const plugin = dist.plugin;
861
879
  const marketplace = dist.marketplace;
862
880
  const entrySkill = dist.entrySkill;
@@ -30,6 +30,7 @@ import {
30
30
  resolvePresetRequiresApproval,
31
31
  } from './presets.mjs';
32
32
  import { checkGitRemoteUrl, describeGitRemoteUrlFailure } from './git-url-policy.mjs';
33
+ import { classifyPathInput } from 'skill-family-harness-node';
33
34
 
34
35
  /** Secret-ish environment variable denylist (R3 credential hygiene). */
35
36
  export const ENV_ALLOWLIST_DENYLIST = /TOKEN|SECRET|PASSWORD|PASSPHRASE|API_KEY|CREDENTIAL/i;
@@ -42,7 +43,7 @@ const SAFE_ID_RE = /^[a-z0-9][a-z0-9._-]*$/;
42
43
 
43
44
  /** Branch pattern (leading alphanumeric blocks option-like names). */
44
45
  const BRANCH_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/;
45
- const LOCAL_HOSTS = new Set(['claude', 'codex', 'kimi', 'codebuddy', 'workbuddy', 'qoder']);
46
+ const LOCAL_HOSTS = new Set(['claude', 'codex', 'kimi', 'codebuddy', 'workbuddy', 'qoder', 'cursor']);
46
47
  const HUB_HOST_RE = /^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/u;
47
48
  const HUB_REF_RE = /^refs\/heads\/[A-Za-z0-9][A-Za-z0-9._-]*(?:\/[A-Za-z0-9][A-Za-z0-9._-]*)*$/u;
48
49
 
@@ -500,6 +501,13 @@ export function validatePostPublishDeclaration(postPublish, options = {}) {
500
501
  if (hosts.has(host)) fail(`${unitLabel}localHostUpdate.hosts contains duplicate host "${host}"`);
501
502
  hosts.add(host);
502
503
  }
504
+ if (hosts.has('cursor')) {
505
+ if (!/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(localHostUpdate.plugin)) fail(`${unitLabel}Cursor plugin name must match the Cursor naming convention`);
506
+ const cursor = localHostUpdate.cursor;
507
+ if (!cursor || typeof cursor !== 'object' || Array.isArray(cursor)) fail(`${unitLabel}localHostUpdate.cursor is required`);
508
+ if (!classifyPathInput(cursor.sourcePath).ok) fail(`${unitLabel}localHostUpdate.cursor.sourcePath must be a safe relative path`);
509
+ if (cursor.dependencyInstall !== undefined && cursor.dependencyInstall !== 'npm-ci-ignore-scripts') fail(`${unitLabel}localHostUpdate.cursor.dependencyInstall is unsupported`);
510
+ } else if (localHostUpdate.cursor !== undefined) fail(`${unitLabel}localHostUpdate.cursor requires cursor in hosts`);
503
511
  const hub = localHostUpdate.hub;
504
512
  if (!hub || typeof hub !== 'object' || Array.isArray(hub)) {
505
513
  fail(`${unitLabel}localHostUpdate.hub must be a non-null object`);
@@ -67,7 +67,7 @@ function postVerificationComplete(run) {
67
67
  function publicationComplete(run) {
68
68
  return run.checkpoints.every((cp) => COMPLETED_CHECKPOINTS.has(cp.status)
69
69
  || (['failed', 'deferred'].includes(cp.status)
70
- && ['claude-marketplace-install', 'codex-marketplace-install', 'kimi-marketplace-install', 'codebuddy-marketplace-install'].includes(cp.actionType)));
70
+ && ['claude-marketplace-install', 'codex-marketplace-install', 'kimi-marketplace-install', 'codebuddy-marketplace-install', 'qoder-marketplace-install'].includes(cp.actionType)));
71
71
  }
72
72
 
73
73
  async function lineagePathsContained(run, runsRoot) {
@@ -91,6 +91,7 @@ export function normalizeGenericSkillsRel(skillsRaw) {
91
91
  const SKILLS_NORMALIZERS = {
92
92
  claude: normalizeGenericSkillsRel,
93
93
  codex: normalizeGenericSkillsRel,
94
+ qoder: normalizeGenericSkillsRel,
94
95
  kimi: normalizeKimiSkillsRel,
95
96
  codebuddy: normalizeCodeBuddySkillsRel,
96
97
  };
@@ -57,7 +57,7 @@ function validateGate(gate) {
57
57
  if (!gate.scope || typeof gate.scope.unit !== 'string') {
58
58
  throw gateError(gate, 'must declare scope.unit');
59
59
  }
60
- if (gate.phase === 'consumer-verify' && !['npm', 'claude-plugin', 'codex-plugin', 'kimi-plugin', 'codebuddy-plugin'].includes(gate.scope.distribution)) {
60
+ if (gate.phase === 'consumer-verify' && !['npm', 'claude-plugin', 'codex-plugin', 'kimi-plugin', 'codebuddy-plugin', 'qoder-plugin'].includes(gate.scope.distribution)) {
61
61
  throw gateError(gate, 'consumer-verify must declare a supported scope.distribution');
62
62
  }
63
63
  if (!Array.isArray(gate.command) || gate.command.length === 0 || gate.command.some((value) => typeof value !== 'string')) {