release-skill 0.9.22 → 0.9.24

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 (220) 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 +1 -1
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/.qoder-plugin/plugin.json +1 -1
  7. package/CHANGELOG.md +61 -2
  8. package/INSTALL.md +4 -4
  9. package/INSTALL.zh-CN.md +4 -4
  10. package/README.md +53 -32
  11. package/README.zh-CN.md +44 -27
  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/registry.json +50 -1
  17. package/adapters/claude/bin/release-skill.bundle.mjs +2680 -2180
  18. package/adapters/claude/bin/rules.json +1 -1
  19. package/adapters/claude/skills/release-assess/SKILL.md +17 -3
  20. package/adapters/claude/skills/release-config/SKILL.md +6 -0
  21. package/adapters/claude/skills/release-docs/SKILL.md +6 -0
  22. package/adapters/claude/skills/release-help/SKILL.md +7 -2
  23. package/adapters/claude/skills/release-marketplace/SKILL.md +6 -0
  24. package/adapters/claude/skills/release-prepare/SKILL.md +8 -2
  25. package/adapters/claude/skills/release-publish/SKILL.md +7 -1
  26. package/adapters/claude/skills/release-reconcile/SKILL.md +6 -0
  27. package/adapters/claude/skills/release-setup/SKILL.md +9 -1
  28. package/adapters/claude/skills/release-verify/SKILL.md +6 -0
  29. package/adapters/claude/src/schemas/file-set-apply-request.schema.json +78 -0
  30. package/adapters/claude/src/schemas/file-set-event-payload.schema.json +55 -0
  31. package/adapters/claude/src/schemas/file-set-prune-request.schema.json +17 -0
  32. package/adapters/claude/src/schemas/file-set-recovery-request.schema.json +29 -0
  33. package/adapters/claude/src/schemas/file-set-result.schema.json +79 -0
  34. package/adapters/claude/src/schemas/professional-conclusion.schema.json +132 -0
  35. package/adapters/claude/src/schemas/project-manifest.schema.json +91 -0
  36. package/adapters/claude/src/schemas/state-store-recovery-observation.schema.json +129 -0
  37. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  38. package/adapters/codex/bin/consumer-contract-vectors.json +7 -7
  39. package/adapters/codex/bin/error-codes.json +1 -1
  40. package/adapters/codex/bin/foundation-resource-binding.json +1 -1
  41. package/adapters/codex/bin/registry.json +50 -1
  42. package/adapters/codex/bin/release-skill.bundle.mjs +2680 -2180
  43. package/adapters/codex/bin/rules.json +1 -1
  44. package/adapters/codex/skills/release-assess/SKILL.md +17 -3
  45. package/adapters/codex/skills/release-config/SKILL.md +6 -0
  46. package/adapters/codex/skills/release-docs/SKILL.md +6 -0
  47. package/adapters/codex/skills/release-help/SKILL.md +9 -3
  48. package/adapters/codex/skills/release-marketplace/SKILL.md +6 -0
  49. package/adapters/codex/skills/release-prepare/SKILL.md +8 -2
  50. package/adapters/codex/skills/release-publish/SKILL.md +7 -1
  51. package/adapters/codex/skills/release-reconcile/SKILL.md +6 -0
  52. package/adapters/codex/skills/release-setup/SKILL.md +9 -1
  53. package/adapters/codex/skills/release-verify/SKILL.md +6 -0
  54. package/adapters/codex/src/schemas/file-set-apply-request.schema.json +78 -0
  55. package/adapters/codex/src/schemas/file-set-event-payload.schema.json +55 -0
  56. package/adapters/codex/src/schemas/file-set-prune-request.schema.json +17 -0
  57. package/adapters/codex/src/schemas/file-set-recovery-request.schema.json +29 -0
  58. package/adapters/codex/src/schemas/file-set-result.schema.json +79 -0
  59. package/adapters/codex/src/schemas/professional-conclusion.schema.json +132 -0
  60. package/adapters/codex/src/schemas/project-manifest.schema.json +91 -0
  61. package/adapters/codex/src/schemas/state-store-recovery-observation.schema.json +129 -0
  62. package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
  63. package/adapters/cursor/bin/consumer-contract-vectors.json +7 -7
  64. package/adapters/cursor/bin/error-codes.json +1 -1
  65. package/adapters/cursor/bin/foundation-resource-binding.json +1 -1
  66. package/adapters/cursor/bin/registry.json +50 -1
  67. package/adapters/cursor/bin/release-skill.bundle.mjs +2680 -2180
  68. package/adapters/cursor/bin/rules.json +1 -1
  69. package/adapters/cursor/skills/release-assess/SKILL.md +17 -3
  70. package/adapters/cursor/skills/release-config/SKILL.md +6 -0
  71. package/adapters/cursor/skills/release-docs/SKILL.md +6 -0
  72. package/adapters/cursor/skills/release-help/SKILL.md +9 -3
  73. package/adapters/cursor/skills/release-marketplace/SKILL.md +6 -0
  74. package/adapters/cursor/skills/release-prepare/SKILL.md +8 -2
  75. package/adapters/cursor/skills/release-publish/SKILL.md +7 -1
  76. package/adapters/cursor/skills/release-reconcile/SKILL.md +6 -0
  77. package/adapters/cursor/skills/release-setup/SKILL.md +9 -1
  78. package/adapters/cursor/skills/release-verify/SKILL.md +6 -0
  79. package/adapters/cursor/src/schemas/file-set-apply-request.schema.json +78 -0
  80. package/adapters/cursor/src/schemas/file-set-event-payload.schema.json +55 -0
  81. package/adapters/cursor/src/schemas/file-set-prune-request.schema.json +17 -0
  82. package/adapters/cursor/src/schemas/file-set-recovery-request.schema.json +29 -0
  83. package/adapters/cursor/src/schemas/file-set-result.schema.json +79 -0
  84. package/adapters/cursor/src/schemas/professional-conclusion.schema.json +132 -0
  85. package/adapters/cursor/src/schemas/project-manifest.schema.json +91 -0
  86. package/adapters/cursor/src/schemas/state-store-recovery-observation.schema.json +129 -0
  87. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  88. package/adapters/kimi/bin/consumer-contract-vectors.json +7 -7
  89. package/adapters/kimi/bin/error-codes.json +1 -1
  90. package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
  91. package/adapters/kimi/bin/registry.json +50 -1
  92. package/adapters/kimi/bin/release-skill.bundle.mjs +2680 -2180
  93. package/adapters/kimi/bin/rules.json +1 -1
  94. package/adapters/kimi/skills/release-assess/SKILL.md +17 -3
  95. package/adapters/kimi/skills/release-config/SKILL.md +6 -0
  96. package/adapters/kimi/skills/release-docs/SKILL.md +6 -0
  97. package/adapters/kimi/skills/release-help/SKILL.md +9 -3
  98. package/adapters/kimi/skills/release-marketplace/SKILL.md +6 -0
  99. package/adapters/kimi/skills/release-prepare/SKILL.md +8 -2
  100. package/adapters/kimi/skills/release-publish/SKILL.md +7 -1
  101. package/adapters/kimi/skills/release-reconcile/SKILL.md +6 -0
  102. package/adapters/kimi/skills/release-setup/SKILL.md +9 -1
  103. package/adapters/kimi/skills/release-verify/SKILL.md +6 -0
  104. package/adapters/kimi/src/schemas/file-set-apply-request.schema.json +78 -0
  105. package/adapters/kimi/src/schemas/file-set-event-payload.schema.json +55 -0
  106. package/adapters/kimi/src/schemas/file-set-prune-request.schema.json +17 -0
  107. package/adapters/kimi/src/schemas/file-set-recovery-request.schema.json +29 -0
  108. package/adapters/kimi/src/schemas/file-set-result.schema.json +79 -0
  109. package/adapters/kimi/src/schemas/professional-conclusion.schema.json +132 -0
  110. package/adapters/kimi/src/schemas/project-manifest.schema.json +91 -0
  111. package/adapters/kimi/src/schemas/state-store-recovery-observation.schema.json +129 -0
  112. package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
  113. package/adapters/qoder/bin/consumer-contract-vectors.json +7 -7
  114. package/adapters/qoder/bin/error-codes.json +1 -1
  115. package/adapters/qoder/bin/foundation-resource-binding.json +1 -1
  116. package/adapters/qoder/bin/registry.json +50 -1
  117. package/adapters/qoder/bin/release-skill.bundle.mjs +2680 -2180
  118. package/adapters/qoder/bin/rules.json +1 -1
  119. package/adapters/qoder/skills/release-assess/SKILL.md +17 -3
  120. package/adapters/qoder/skills/release-config/SKILL.md +6 -0
  121. package/adapters/qoder/skills/release-docs/SKILL.md +6 -0
  122. package/adapters/qoder/skills/release-help/SKILL.md +9 -3
  123. package/adapters/qoder/skills/release-marketplace/SKILL.md +6 -0
  124. package/adapters/qoder/skills/release-prepare/SKILL.md +8 -2
  125. package/adapters/qoder/skills/release-publish/SKILL.md +7 -1
  126. package/adapters/qoder/skills/release-reconcile/SKILL.md +6 -0
  127. package/adapters/qoder/skills/release-setup/SKILL.md +9 -1
  128. package/adapters/qoder/skills/release-verify/SKILL.md +6 -0
  129. package/adapters/qoder/src/schemas/file-set-apply-request.schema.json +78 -0
  130. package/adapters/qoder/src/schemas/file-set-event-payload.schema.json +55 -0
  131. package/adapters/qoder/src/schemas/file-set-prune-request.schema.json +17 -0
  132. package/adapters/qoder/src/schemas/file-set-recovery-request.schema.json +29 -0
  133. package/adapters/qoder/src/schemas/file-set-result.schema.json +79 -0
  134. package/adapters/qoder/src/schemas/professional-conclusion.schema.json +132 -0
  135. package/adapters/qoder/src/schemas/project-manifest.schema.json +91 -0
  136. package/adapters/qoder/src/schemas/state-store-recovery-observation.schema.json +129 -0
  137. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  138. package/adapters/workbuddy/bin/consumer-contract-vectors.json +7 -7
  139. package/adapters/workbuddy/bin/error-codes.json +1 -1
  140. package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
  141. package/adapters/workbuddy/bin/registry.json +50 -1
  142. package/adapters/workbuddy/bin/release-skill.bundle.mjs +2680 -2180
  143. package/adapters/workbuddy/bin/rules.json +1 -1
  144. package/adapters/workbuddy/skills/release-assess/SKILL.md +17 -3
  145. package/adapters/workbuddy/skills/release-config/SKILL.md +6 -0
  146. package/adapters/workbuddy/skills/release-docs/SKILL.md +6 -0
  147. package/adapters/workbuddy/skills/release-help/SKILL.md +7 -2
  148. package/adapters/workbuddy/skills/release-marketplace/SKILL.md +6 -0
  149. package/adapters/workbuddy/skills/release-prepare/SKILL.md +8 -2
  150. package/adapters/workbuddy/skills/release-publish/SKILL.md +7 -1
  151. package/adapters/workbuddy/skills/release-reconcile/SKILL.md +6 -0
  152. package/adapters/workbuddy/skills/release-setup/SKILL.md +9 -1
  153. package/adapters/workbuddy/skills/release-verify/SKILL.md +6 -0
  154. package/adapters/workbuddy/src/schemas/file-set-apply-request.schema.json +78 -0
  155. package/adapters/workbuddy/src/schemas/file-set-event-payload.schema.json +55 -0
  156. package/adapters/workbuddy/src/schemas/file-set-prune-request.schema.json +17 -0
  157. package/adapters/workbuddy/src/schemas/file-set-recovery-request.schema.json +29 -0
  158. package/adapters/workbuddy/src/schemas/file-set-result.schema.json +79 -0
  159. package/adapters/workbuddy/src/schemas/professional-conclusion.schema.json +132 -0
  160. package/adapters/workbuddy/src/schemas/project-manifest.schema.json +91 -0
  161. package/adapters/workbuddy/src/schemas/state-store-recovery-observation.schema.json +129 -0
  162. package/bin/consumer-contract-vectors.json +7 -7
  163. package/bin/error-codes.json +1 -1
  164. package/bin/foundation-resource-binding.json +1 -1
  165. package/bin/registry.json +50 -1
  166. package/bin/release-skill-cli.mjs +80 -5
  167. package/bin/release-skill.bundle.mjs +2680 -2180
  168. package/bin/rules.json +1 -1
  169. package/package.json +4 -4
  170. package/platform-manifest.json +4 -4
  171. package/references/05-evidence-and-errors.md +2 -2
  172. package/scripts/build-bundle.mjs +2 -1
  173. package/scripts/sync-public-files.mjs +1 -1
  174. package/skills/release-assess/SKILL.md +17 -3
  175. package/skills/release-config/SKILL.md +6 -0
  176. package/skills/release-docs/SKILL.md +6 -0
  177. package/skills/release-help/SKILL.md +7 -2
  178. package/skills/release-marketplace/SKILL.md +6 -0
  179. package/skills/release-prepare/SKILL.md +8 -2
  180. package/skills/release-publish/SKILL.md +7 -1
  181. package/skills/release-reconcile/SKILL.md +6 -0
  182. package/skills/release-setup/SKILL.md +9 -1
  183. package/skills/release-verify/SKILL.md +6 -0
  184. package/skills-src/release-assess/SKILL.md +17 -3
  185. package/skills-src/release-config/SKILL.md +6 -0
  186. package/skills-src/release-docs/SKILL.md +6 -0
  187. package/skills-src/release-help/SKILL.md +7 -2
  188. package/skills-src/release-marketplace/SKILL.md +6 -0
  189. package/skills-src/release-prepare/SKILL.md +8 -2
  190. package/skills-src/release-publish/SKILL.md +7 -1
  191. package/skills-src/release-reconcile/SKILL.md +6 -0
  192. package/skills-src/release-setup/SKILL.md +9 -1
  193. package/skills-src/release-verify/SKILL.md +6 -0
  194. package/src/adapters/plugin-marketplace.mjs +7 -12
  195. package/src/artifacts/producer-registry.mjs +4 -3
  196. package/src/commands/assess.mjs +23 -3
  197. package/src/commands/post-release-local.mjs +2 -2
  198. package/src/commands/prepare.mjs +5 -30
  199. package/src/commands/professional-proof.mjs +506 -0
  200. package/src/commands/setup.mjs +16 -5
  201. package/src/commands/verify-records.mjs +14 -0
  202. package/src/core/baseline.mjs +9 -1
  203. package/src/core/derived-artifact-gates.mjs +38 -9
  204. package/src/core/redact.mjs +12 -5
  205. package/src/core/source-authority.mjs +28 -3
  206. package/src/npm/npm-entry-closure.mjs +71 -9
  207. package/src/producers/build-adapters.mjs +18 -5
  208. package/src/producers/foundation-resource-projection.mjs +3 -3
  209. package/src/producers/render-public-assets.mjs +2 -1
  210. package/src/producers/sync-skills.mjs +2 -1
  211. package/src/schemas/file-set-apply-request.schema.json +78 -0
  212. package/src/schemas/file-set-event-payload.schema.json +55 -0
  213. package/src/schemas/file-set-prune-request.schema.json +17 -0
  214. package/src/schemas/file-set-recovery-request.schema.json +29 -0
  215. package/src/schemas/file-set-result.schema.json +79 -0
  216. package/src/schemas/professional-conclusion.schema.json +132 -0
  217. package/src/schemas/project-manifest.schema.json +91 -0
  218. package/src/schemas/state-store-recovery-observation.schema.json +129 -0
  219. package/src/snapshot/frozen.mjs +19 -14
  220. package/src/core/run-retention.mjs +0 -227
@@ -1567,7 +1567,7 @@ set expectedTag $env(RELEASE_SKILL_KIMI_EXPECTED_TAG)
1567
1567
  set promptPattern {(?:(?:^|\\r|\\n)> (?:\\r*\\n|$)|(?:^|\\r|\\n)(?:(?:\\033\\[[0-9;?]*[ -/]*[@-~])|\\033\\][^\\x07]*\\x07|[ \\t])*│[^\\r\\n]*>[^\\r\\n]*│(?:(?:\\033\\[[0-9;?]*[ -/]*[@-~])|\\033\\][^\\x07]*\\x07|[ \\t])*(?:\\r*\\n|$))}
1568
1568
 
1569
1569
  proc cleanScreen {value} {
1570
- regsub -all {\\033\\[[0-9;?]*[ -/]*[@-~]} $value {} value
1570
+ regsub -all {\\033\\[[0-9;:<=>?]*[ -/]*[@-~]} $value {} value
1571
1571
  regsub -all {\\033\\][^\\x07]*\\x07} $value {} value
1572
1572
  regsub -all {\\r} $value {} value
1573
1573
  return $value
@@ -1646,7 +1646,7 @@ proc readDirectoryTrustDialog {prefix state timeoutCode unknownCode eofCode} {
1646
1646
  set framed [regexp -nocase {(^|\\n)[ \\t]*─{8,}[ \\t]*\\n[ \\t]*Trust this folder\\?} $cleanedPrefix]
1647
1647
  if {$framed} {
1648
1648
  expect {
1649
- -re {(^|\\r|\\n)[ \\t]*─{8,}[ \\t]*\\r*\\n} {
1649
+ -re {(^|\\r|\\n)[^\\r\\n]*─{8,}[^\\r\\n]*(\\r*\\n|$)} {
1650
1650
  append dialogBuffer $expect_out(buffer)
1651
1651
  }
1652
1652
  timeout { failTimeout $state $timeoutCode $unknownCode }
@@ -78,7 +78,6 @@ import { acquireProjectLock } from '../artifacts/project-lock.mjs';
78
78
  import { observePreviousPublicBaseline } from '../core/previous-public-baseline.mjs';
79
79
  import { verifyFrozenNpmTarballContract } from '../adapters/npm.mjs';
80
80
  import { createProductionPrepareRunDir } from '../core/run.mjs';
81
- import { cleanupRunRetention } from '../core/run-retention.mjs';
82
81
  import {
83
82
  PLATFORMS,
84
83
  normalizeHostId,
@@ -279,7 +278,9 @@ import { boundedOutputTail } from '../core/bounded-output.mjs';
279
278
  * already captures child stdout/stderr on non-zero exit; on failure this
280
279
  * layer writes bounded tails into the hooks evidence event AND echoes them to
281
280
  * the current process' stderr, so a failing hook is diagnosable on the
282
- * terminal without opening evidence.jsonl. Success events carry no tails.
281
+ * terminal without opening evidence.jsonl. Fresh success and non-zero exit
282
+ * events also retain the returned full streams through the evidence writer's
283
+ * redaction. Success events carry no tails; cache hits have no fresh streams.
283
284
  * Exit-code semantics are unchanged.
284
285
  *
285
286
  * @param {object} config - The loaded project config.
@@ -413,6 +414,7 @@ export async function runDeclaredHooks(config, root, evidence, hookFn = runHook,
413
414
  // streams instead of the noisy compiler prelude at the beginning.
414
415
  stdoutTail,
415
416
  stderrTail,
417
+ details: { stdout: result.stdout, stderr: result.stderr },
416
418
  ...selectionField,
417
419
  });
418
420
  throw new ReleaseError(
@@ -448,6 +450,7 @@ export async function runDeclaredHooks(config, root, evidence, hookFn = runHook,
448
450
  status: 'completed',
449
451
  hookName: name,
450
452
  exitCode: 0,
453
+ details: { stdout: result.stdout, stderr: result.stderr },
451
454
  ...selectionField,
452
455
  });
453
456
  records.push({ name, completed: true, cached: false, testSelection: selectionField.testSelection });
@@ -3404,34 +3407,6 @@ export async function prepareRelease(options) {
3404
3407
  const evidence = createEvidenceWriter({ runDir, command: 'prepare', clock });
3405
3408
 
3406
3409
  try {
3407
- // R14-01: production prepare performs one best-effort retention pass only
3408
- // after the current run/evidence authority exists and before high-cost
3409
- // config, snapshot, and hook work begins. It never blocks prepare.
3410
- if (production) {
3411
- try {
3412
- const retention = await (options.runRetentionFn ?? cleanupRunRetention)({
3413
- releaseDir,
3414
- currentRunDir: runDir,
3415
- });
3416
- await evidence.append({
3417
- phase: 'run-retention',
3418
- status: 'completed',
3419
- scanned: retention.scanned,
3420
- deleted: retention.deleted,
3421
- errors: retention.errors,
3422
- });
3423
- } catch (error) {
3424
- await evidence.append({
3425
- phase: 'run-retention',
3426
- status: 'warning',
3427
- scanned: 0,
3428
- deleted: 0,
3429
- errors: 1,
3430
- error: { code: error?.code ?? 'RUN_RETENTION_FAILED', message: error?.message ?? String(error) },
3431
- });
3432
- }
3433
- }
3434
-
3435
3410
  // --- Step 1: Load and validate config ---
3436
3411
  await evidence.append({ phase: 'config', status: 'started' });
3437
3412
 
@@ -0,0 +1,506 @@
1
+ /**
2
+ * Family projection and read of Foundation professional-conclusion files.
3
+ *
4
+ * Domain mapping stays here. Path containment, exclusive publication, and
5
+ * schema validation are Foundation 0.22.0 calls, not local copies.
6
+ */
7
+
8
+ import { readFileSync, realpathSync } from 'node:fs';
9
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
10
+
11
+ import { findSchemaByObject, validateDocument } from 'skill-family-contracts';
12
+ import {
13
+ HARNESS_ERROR_KINDS,
14
+ publishFileExclusive,
15
+ readFileStrict,
16
+ resolveContained,
17
+ } from 'skill-family-harness-node';
18
+
19
+ import {
20
+ GATE_FAILED,
21
+ PATH_UNSAFE,
22
+ ReleaseError,
23
+ STRUCTURE_INVALID,
24
+ } from '../core/errors.mjs';
25
+ import { PKG_ROOT } from '../core/pkg-root.mjs';
26
+
27
+ export const RELEASE_PROVIDER_ID = 'release-skill';
28
+
29
+ export const PROVIDER_ENTRY = Object.freeze({
30
+ ADOPTION: 'setup --assess-adoption',
31
+ ASSESS: 'assess --offline',
32
+ RECORDS: 'verify-records',
33
+ });
34
+
35
+ export const READ_EXIT_CODES = Object.freeze({
36
+ pass: 0,
37
+ not_pass: 1,
38
+ unavailable: 2,
39
+ });
40
+
41
+ const PASS_CODES = Object.freeze({
42
+ [PROVIDER_ENTRY.ADOPTION]: Object.freeze(['ADOPTED', 'ADOPTED_WITH_SUGGESTIONS']),
43
+ [PROVIDER_ENTRY.ASSESS]: Object.freeze(['ASSESSED']),
44
+ [PROVIDER_ENTRY.RECORDS]: Object.freeze(['CONSISTENT']),
45
+ });
46
+
47
+ const NOT_PASS_CODES = Object.freeze({
48
+ [PROVIDER_ENTRY.ADOPTION]: Object.freeze(['PARTIALLY_ADOPTED', 'NOT_CONFIGURED']),
49
+ [PROVIDER_ENTRY.ASSESS]: Object.freeze(['NEEDS_INPUT', 'BLOCKED']),
50
+ [PROVIDER_ENTRY.RECORDS]: Object.freeze(['CONTRADICTED', 'INSUFFICIENT']),
51
+ });
52
+
53
+ const INCOMPLETE_RECORD_CODES = Object.freeze([
54
+ 'INPUT_MISSING',
55
+ 'FORMAT_UNSUPPORTED',
56
+ 'HISTORICAL_TIME_MISSING',
57
+ ]);
58
+
59
+ function providerVersion() {
60
+ // Bundled adapters ship no package.json. Reuse the build-time identity
61
+ // already injected as __bundlePkg; source mode still reads package.json.
62
+ if (typeof __bundlePkg !== 'undefined' && __bundlePkg && typeof __bundlePkg.version === 'string' && __bundlePkg.version.length > 0) {
63
+ return __bundlePkg.version;
64
+ }
65
+ const pkg = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8'));
66
+ if (typeof pkg.version !== 'string' || pkg.version.length === 0) {
67
+ throw new ReleaseError(STRUCTURE_INVALID, 'package.json version is required for professional-proof provider.version');
68
+ }
69
+ return pkg.version;
70
+ }
71
+
72
+ function professionalSchemaRegistration() {
73
+ return findSchemaByObject('professional-conclusion');
74
+ }
75
+
76
+ function validateProfessionalConclusion(document) {
77
+ const registration = professionalSchemaRegistration();
78
+ if (!registration) {
79
+ return {
80
+ valid: false,
81
+ errorCode: 'SFC1002',
82
+ errors: [{ message: 'professional-conclusion schema is not registered in the installed skill-family-contracts' }],
83
+ };
84
+ }
85
+ return validateDocument(document, {
86
+ schemaId: registration.$id,
87
+ dialect: registration.dialect,
88
+ });
89
+ }
90
+
91
+ function assertValidConclusion(document) {
92
+ const result = validateProfessionalConclusion(document);
93
+ if (!result.valid) {
94
+ throw new ReleaseError(
95
+ STRUCTURE_INVALID,
96
+ 'professional conclusion failed Foundation schema validation',
97
+ { errorCode: result.errorCode, errors: result.errors },
98
+ );
99
+ }
100
+ return result;
101
+ }
102
+
103
+ function canonicalOutputPath(filePath) {
104
+ const absolute = resolve(filePath);
105
+ try {
106
+ return realpathSync(absolute);
107
+ } catch {
108
+ const parts = [basename(absolute)];
109
+ let cursor = dirname(absolute);
110
+ while (true) {
111
+ try {
112
+ return join(realpathSync(cursor), ...parts);
113
+ } catch {
114
+ const parent = dirname(cursor);
115
+ if (parent === cursor) return absolute;
116
+ parts.unshift(basename(cursor));
117
+ cursor = parent;
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ export function assertConclusionOutputOption(conclusionOutput, { conflictPath } = {}) {
124
+ if (conclusionOutput == null || conclusionOutput === '') return;
125
+ if (typeof conclusionOutput !== 'string' || !isAbsolute(conclusionOutput)) {
126
+ throw new ReleaseError(PATH_UNSAFE, '--conclusion-output must be an absolute file path');
127
+ }
128
+ if (conflictPath != null && conflictPath !== '') {
129
+ if (canonicalOutputPath(conclusionOutput) === canonicalOutputPath(conflictPath)) {
130
+ throw new ReleaseError(
131
+ PATH_UNSAFE,
132
+ 'assess --output and --conclusion-output must not resolve to the same path',
133
+ );
134
+ }
135
+ }
136
+ }
137
+
138
+ function nativeDetails(nativeResult) {
139
+ if (!nativeResult || typeof nativeResult !== 'object' || Array.isArray(nativeResult)) {
140
+ return { native: nativeResult ?? null };
141
+ }
142
+ const { conclusionPath: _conclusionPath, ...rest } = nativeResult;
143
+ return rest;
144
+ }
145
+
146
+ function nativeSummary(nativeResult, code) {
147
+ if (typeof nativeResult?.summary === 'string' && nativeResult.summary.trim().length > 0) {
148
+ return nativeResult.summary;
149
+ }
150
+ if (code === 'CONSISTENT') {
151
+ return '历史记录一致;不表示发布已经成功。';
152
+ }
153
+ if (code === 'INSUFFICIENT') {
154
+ return '提供的历史记录不足以完成一致性判断。';
155
+ }
156
+ if (code === 'CONTRADICTED') {
157
+ return '提供的历史记录互相矛盾。';
158
+ }
159
+ if (typeof code === 'string' && code.length > 0) {
160
+ return `release-skill 原生状态 ${code}`;
161
+ }
162
+ return 'release-skill 专业检查已返回领域结果。';
163
+ }
164
+
165
+ function adoptionScope(nativeResult) {
166
+ const limitations = [];
167
+ for (const item of nativeResult?.unobserved ?? []) {
168
+ if (typeof item?.note === 'string' && item.note.length > 0) limitations.push(item.note);
169
+ else if (typeof item?.field === 'string' && item.field.length > 0) limitations.push(item.field);
170
+ }
171
+ const configLoadFailed = nativeResult?.status === 'PARTIALLY_ADOPTED'
172
+ && nativeResult?.configDigest == null;
173
+ if (configLoadFailed) {
174
+ limitations.push('subsequent local adoption checks were not run because configuration is invalid');
175
+ return {
176
+ checked: ['setup --assess-adoption:config'],
177
+ limitations,
178
+ completion: 'partial',
179
+ };
180
+ }
181
+ return {
182
+ checked: ['setup --assess-adoption'],
183
+ limitations,
184
+ completion: 'complete',
185
+ };
186
+ }
187
+
188
+ function assessScope(nativeResult) {
189
+ const hasConfig = Boolean(nativeResult?.configDigest) || nativeResult?.status === 'ASSESSED';
190
+ const configOnly = !hasConfig && nativeResult?.status === 'NEEDS_INPUT';
191
+ if (configOnly) {
192
+ return {
193
+ checked: ['config'],
194
+ limitations: ['topology and remaining local checks were not run because configuration is invalid or missing'],
195
+ completion: 'partial',
196
+ };
197
+ }
198
+ const limitations = [];
199
+ if (nativeResult?.offline !== false) {
200
+ limitations.push('remote-prerequisites skipped (--offline)');
201
+ }
202
+ return {
203
+ checked: [
204
+ 'config',
205
+ 'topology',
206
+ 'common-docs',
207
+ 'plugin-manifests',
208
+ 'package-metadata',
209
+ 'readme-structure',
210
+ ],
211
+ limitations,
212
+ completion: 'complete',
213
+ };
214
+ }
215
+
216
+ function recordInputPresent(digest) {
217
+ return typeof digest?.bytesSha256 === 'string' && digest.bytesSha256.length > 0;
218
+ }
219
+
220
+ function recordsScope(nativeResult) {
221
+ const findings = nativeResult?.findings ?? [];
222
+ const limitations = [];
223
+ for (const finding of findings) {
224
+ if (INCOMPLETE_RECORD_CODES.includes(finding?.code) && typeof finding.message === 'string' && finding.message.length > 0) {
225
+ limitations.push(finding.message);
226
+ }
227
+ }
228
+ const digests = nativeResult?.digests ?? {};
229
+ const checked = [];
230
+ if (recordInputPresent(digests.plan)) checked.push('explicit-plan');
231
+ if (recordInputPresent(digests.approval)) checked.push('explicit-approval');
232
+ if (recordInputPresent(digests.targetRun)) checked.push('explicit-target-run');
233
+ const sourceDigests = Array.isArray(digests.sourceRuns) ? digests.sourceRuns : [];
234
+ const sourcePresent = sourceDigests.some((digest) => recordInputPresent(digest));
235
+ if (sourcePresent || (checked.length > 0 && limitations.length === 0)) {
236
+ checked.push('explicit-source-runs');
237
+ }
238
+ if (checked.length === 0) {
239
+ if (limitations.length === 0) {
240
+ limitations.push('required historical records were not supplied');
241
+ }
242
+ return { checked, limitations, completion: 'not-performed' };
243
+ }
244
+ if (limitations.length > 0) {
245
+ return { checked, limitations, completion: 'partial' };
246
+ }
247
+ return { checked, limitations, completion: 'complete' };
248
+ }
249
+
250
+ function scopeFor(entry, nativeResult) {
251
+ if (entry === PROVIDER_ENTRY.ADOPTION) return adoptionScope(nativeResult);
252
+ if (entry === PROVIDER_ENTRY.ASSESS) return assessScope(nativeResult);
253
+ if (entry === PROVIDER_ENTRY.RECORDS) return recordsScope(nativeResult);
254
+ return {
255
+ checked: [],
256
+ limitations: ['unknown family entry'],
257
+ completion: 'not-performed',
258
+ };
259
+ }
260
+
261
+ export function projectReleaseConclusion({
262
+ entry,
263
+ nativeResult,
264
+ subjectRef,
265
+ subjectRevision,
266
+ } = {}) {
267
+ const code = typeof nativeResult?.status === 'string' && nativeResult.status.length > 0
268
+ ? nativeResult.status
269
+ : 'UNKNOWN';
270
+ const scope = scopeFor(entry, nativeResult);
271
+ const subject = { ref: typeof subjectRef === 'string' && subjectRef.length > 0 ? subjectRef : 'unspecified-subject' };
272
+ if (typeof subjectRevision === 'string' && subjectRevision.length > 0) {
273
+ subject.revision = subjectRevision;
274
+ }
275
+ return {
276
+ schemaVersion: 1,
277
+ kind: 'skill-family.professional-conclusion',
278
+ provider: {
279
+ id: RELEASE_PROVIDER_ID,
280
+ version: providerVersion(),
281
+ entry,
282
+ },
283
+ subject,
284
+ scope: {
285
+ checked: scope.checked,
286
+ limitations: scope.limitations,
287
+ },
288
+ outcome: {
289
+ completion: scope.completion,
290
+ code,
291
+ summary: nativeSummary(nativeResult, code),
292
+ },
293
+ details: nativeDetails(nativeResult),
294
+ };
295
+ }
296
+
297
+ export function interpretReleaseConclusion(document) {
298
+ const provider = document?.provider && typeof document.provider === 'object'
299
+ ? {
300
+ id: document.provider.id,
301
+ version: document.provider.version,
302
+ entry: document.provider.entry,
303
+ }
304
+ : null;
305
+ const entry = provider?.entry;
306
+ const code = document?.outcome?.code;
307
+ const completion = document?.outcome?.completion;
308
+ const passCodes = typeof entry === 'string' && Object.hasOwn(PASS_CODES, entry)
309
+ ? PASS_CODES[entry]
310
+ : undefined;
311
+ const notPassCodes = typeof entry === 'string' && Object.hasOwn(NOT_PASS_CODES, entry)
312
+ ? NOT_PASS_CODES[entry]
313
+ : undefined;
314
+
315
+ if (provider?.id && provider.id !== RELEASE_PROVIDER_ID) {
316
+ return {
317
+ status: 'unavailable',
318
+ reason: '证明不属于 release-skill',
319
+ provider,
320
+ };
321
+ }
322
+ if (!entry || !passCodes || !notPassCodes) {
323
+ return {
324
+ status: 'unavailable',
325
+ reason: '未知入口,无法解释本族领域码',
326
+ provider,
327
+ };
328
+ }
329
+ if (typeof code !== 'string' || code.length === 0) {
330
+ return {
331
+ status: 'unavailable',
332
+ reason: '证明缺少原生领域码',
333
+ provider,
334
+ };
335
+ }
336
+ if (passCodes.includes(code)) {
337
+ if (completion !== 'complete') {
338
+ return {
339
+ status: 'not_pass',
340
+ reason: '声明范围尚未完成,不能把原生通过码当成专业通过',
341
+ provider,
342
+ };
343
+ }
344
+ if (entry === PROVIDER_ENTRY.RECORDS) {
345
+ return {
346
+ status: 'pass',
347
+ reason: '历史记录一致;不表示发布已经成功。historicalTerminalStatus 保持原值。',
348
+ provider,
349
+ };
350
+ }
351
+ if (entry === PROVIDER_ENTRY.ASSESS) {
352
+ return {
353
+ status: 'pass',
354
+ reason: '静态准备度检查完成;不表示已经发布。',
355
+ provider,
356
+ };
357
+ }
358
+ return {
359
+ status: 'pass',
360
+ reason: code === 'ADOPTED_WITH_SUGGESTIONS'
361
+ ? '已接入,建议保留在原证明中。'
362
+ : '接入检查通过。',
363
+ provider,
364
+ };
365
+ }
366
+ if (notPassCodes.includes(code)) {
367
+ if (entry === PROVIDER_ENTRY.RECORDS) {
368
+ return {
369
+ status: 'not_pass',
370
+ reason: code === 'INSUFFICIENT'
371
+ ? '历史记录不足,不能据此声称发布成功。'
372
+ : '历史记录互相矛盾。',
373
+ provider,
374
+ };
375
+ }
376
+ return {
377
+ status: 'not_pass',
378
+ reason: `原生状态 ${code} 表示证明记载的结果未通过。`,
379
+ provider,
380
+ };
381
+ }
382
+ return {
383
+ status: 'unavailable',
384
+ reason: `未知同版本领域码 ${code}`,
385
+ provider,
386
+ };
387
+ }
388
+
389
+ async function publishExclusiveAbsolute(absolutePath, bytes) {
390
+ const root = dirname(absolutePath);
391
+ const relPath = basename(absolutePath);
392
+ if (!relPath || relPath === '.' || relPath === '..') {
393
+ throw new ReleaseError(PATH_UNSAFE, '--conclusion-output must name a file');
394
+ }
395
+ await resolveContained(root, relPath);
396
+ try {
397
+ return await publishFileExclusive(root, relPath, bytes);
398
+ } catch (cause) {
399
+ const kind = cause?.details?.kind;
400
+ if (kind === HARNESS_ERROR_KINDS.EXCLUSIVE_PUBLISH_CONFLICT) {
401
+ throw new ReleaseError(
402
+ GATE_FAILED,
403
+ 'professional conclusion already exists; exclusive create refused to overwrite',
404
+ { kind },
405
+ );
406
+ }
407
+ throw new ReleaseError(
408
+ GATE_FAILED,
409
+ `professional conclusion exclusive write failed: ${cause?.message ?? 'unknown'}`,
410
+ { kind },
411
+ );
412
+ }
413
+ }
414
+
415
+ export async function attachProfessionalConclusion(nativeResult, {
416
+ entry,
417
+ subjectRef,
418
+ subjectRevision,
419
+ conclusionOutput,
420
+ conflictPath,
421
+ } = {}) {
422
+ if (conclusionOutput == null || conclusionOutput === '') {
423
+ return nativeResult;
424
+ }
425
+ assertConclusionOutputOption(conclusionOutput, { conflictPath });
426
+ const conclusion = projectReleaseConclusion({
427
+ entry,
428
+ nativeResult,
429
+ subjectRef,
430
+ subjectRevision,
431
+ });
432
+ assertValidConclusion(conclusion);
433
+ const published = await publishExclusiveAbsolute(
434
+ resolve(conclusionOutput),
435
+ `${JSON.stringify(conclusion, null, 2)}\n`,
436
+ );
437
+ return { ...nativeResult, conclusionPath: published.path };
438
+ }
439
+
440
+ function unavailableRead(reason, extra = {}) {
441
+ return {
442
+ status: 'unavailable',
443
+ reason,
444
+ provider: extra.provider ?? null,
445
+ conclusion: extra.conclusion ?? null,
446
+ };
447
+ }
448
+
449
+ function extractProvider(document) {
450
+ const provider = document?.provider;
451
+ if (!provider || typeof provider !== 'object' || Array.isArray(provider)) return null;
452
+ const extracted = {};
453
+ if (typeof provider.id === 'string') extracted.id = provider.id;
454
+ if (typeof provider.version === 'string') extracted.version = provider.version;
455
+ if (typeof provider.entry === 'string') extracted.entry = provider.entry;
456
+ return Object.keys(extracted).length > 0 ? extracted : null;
457
+ }
458
+
459
+ export async function readProfessionalProof({
460
+ proofRoot,
461
+ proofPath,
462
+ expectedProviderId = RELEASE_PROVIDER_ID,
463
+ } = {}) {
464
+ if (typeof proofRoot !== 'string' || proofRoot.length === 0 || typeof proofPath !== 'string' || proofPath.length === 0) {
465
+ return unavailableRead('proofRoot 与 proof 均为必填');
466
+ }
467
+
468
+ let receipt;
469
+ try {
470
+ await resolveContained(proofRoot, proofPath);
471
+ receipt = await readFileStrict(proofRoot, proofPath, { encoding: 'utf8' });
472
+ } catch (cause) {
473
+ return unavailableRead(`无法读取证明: ${cause?.message ?? 'unknown'}`);
474
+ }
475
+
476
+ let document;
477
+ try {
478
+ document = JSON.parse(receipt.content);
479
+ } catch {
480
+ return unavailableRead('证明不是合法 JSON');
481
+ }
482
+
483
+ const schema = validateProfessionalConclusion(document);
484
+ const provider = extractProvider(document);
485
+ if (!schema.valid) {
486
+ if (provider?.id && provider.id !== expectedProviderId) {
487
+ return unavailableRead('证明不属于 release-skill', { provider, conclusion: document });
488
+ }
489
+ return unavailableRead(
490
+ `证明未通过 professional-conclusion 结构校验(${schema.errorCode ?? 'invalid'})`,
491
+ { provider },
492
+ );
493
+ }
494
+
495
+ if (provider?.id !== expectedProviderId) {
496
+ return unavailableRead('证明不属于 release-skill', { provider, conclusion: document });
497
+ }
498
+
499
+ const mapped = interpretReleaseConclusion(document);
500
+ return {
501
+ status: mapped.status,
502
+ reason: mapped.reason,
503
+ provider: mapped.provider,
504
+ conclusion: document,
505
+ };
506
+ }
@@ -57,6 +57,11 @@ import {
57
57
  checkPluginManifests,
58
58
  checkReadmeStructure,
59
59
  } from './assess.mjs';
60
+ import {
61
+ PROVIDER_ENTRY,
62
+ assertConclusionOutputOption,
63
+ attachProfessionalConclusion,
64
+ } from './professional-proof.mjs';
60
65
 
61
66
  const execFile = promisify(execFileCb);
62
67
  const SKIP_DIRS = new Set([
@@ -2855,13 +2860,19 @@ async function reportConfigLoadError(root, configPath, error) {
2855
2860
  * @param {string} options.root - Absolute project root.
2856
2861
  * @returns {Promise<Object>} Adoption report.
2857
2862
  */
2858
- export async function assessAdoption({ root } = {}) {
2863
+ export async function assessAdoption({ root, conclusionOutput } = {}) {
2859
2864
  if (!root || typeof root !== 'string' || !isAbsolute(root)) {
2860
2865
  throw setupError(CONFIG_INVALID, 'assessment root must be an absolute path');
2861
2866
  }
2867
+ assertConclusionOutputOption(conclusionOutput);
2862
2868
  const rootReal = await realpath(root).catch((error) => {
2863
2869
  throw setupError(CONFIG_INVALID, `cannot resolve assessment root: ${error.message}`);
2864
2870
  });
2871
+ const finish = (report) => attachProfessionalConclusion(report, {
2872
+ entry: PROVIDER_ENTRY.ADOPTION,
2873
+ subjectRef: rootReal,
2874
+ conclusionOutput,
2875
+ });
2865
2876
  const configPath = join(rootReal, '.release-skill', 'project.yaml');
2866
2877
 
2867
2878
  let configExists = false;
@@ -2872,7 +2883,7 @@ export async function assessAdoption({ root } = {}) {
2872
2883
  if (error.code !== 'ENOENT') throw error;
2873
2884
  }
2874
2885
  if (!configExists) {
2875
- return reportNotConfigured(rootReal, configPath);
2886
+ return finish(await reportNotConfigured(rootReal, configPath));
2876
2887
  }
2877
2888
 
2878
2889
  let loaded;
@@ -2880,7 +2891,7 @@ export async function assessAdoption({ root } = {}) {
2880
2891
  loaded = await loadProjectConfig({ root: rootReal });
2881
2892
  } catch (error) {
2882
2893
  if (error.code !== CONFIG_INVALID) throw error;
2883
- return reportConfigLoadError(rootReal, configPath, error);
2894
+ return finish(await reportConfigLoadError(rootReal, configPath, error));
2884
2895
  }
2885
2896
  const { config, configDigest } = loaded;
2886
2897
 
@@ -3227,7 +3238,7 @@ export async function assessAdoption({ root } = {}) {
3227
3238
  ? '修复全部必选缺口后重新运行 release-skill setup --assess-adoption。'
3228
3239
  : null;
3229
3240
 
3230
- return buildAssessmentReport({
3241
+ return finish(buildAssessmentReport({
3231
3242
  config,
3232
3243
  configPath,
3233
3244
  configDigest,
@@ -3237,5 +3248,5 @@ export async function assessAdoption({ root } = {}) {
3237
3248
  gateSuggestions,
3238
3249
  gateDiagnostics,
3239
3250
  next,
3240
- });
3251
+ }));
3241
3252
  }
@@ -465,3 +465,17 @@ export function verifyReleaseRecords(request = {}) {
465
465
  findings: findings.map(({ _outcome, ...finding }) => finding),
466
466
  };
467
467
  }
468
+
469
+ /**
470
+ * Optional exclusive professional-conclusion output. The domain verifier stays
471
+ * pure; this hook loads the family projector only when a path is supplied.
472
+ */
473
+ export async function attachVerifyRecordsConclusion(result, conclusionOutput, subjectRef) {
474
+ if (conclusionOutput == null || conclusionOutput === '') return result;
475
+ const { attachProfessionalConclusion, PROVIDER_ENTRY } = await import('./professional-proof.mjs');
476
+ return attachProfessionalConclusion(result, {
477
+ entry: PROVIDER_ENTRY.RECORDS,
478
+ subjectRef,
479
+ conclusionOutput,
480
+ });
481
+ }
@@ -292,7 +292,15 @@ async function computeWorkspaceDigest(root) {
292
292
  * @returns {Promise<{ gitHead: string, gitTreeHash: string, workspaceDigest: string, statusEntries: string[], capturedAt: string }>}
293
293
  */
294
294
  export async function captureBaseline(root) {
295
- const opts = { cwd: root, shell: false, encoding: 'utf8' };
295
+ // Same 64 MiB bound as computeWorkspaceDigest: `git status --porcelain=v1
296
+ // -z --untracked-files=all` lists every untracked file and exceeds Node's
297
+ // default 1 MiB execFile maxBuffer once that listing grows past ~1 MiB.
298
+ const opts = {
299
+ cwd: root,
300
+ shell: false,
301
+ encoding: 'utf8',
302
+ maxBuffer: 64 * 1024 * 1024,
303
+ };
296
304
 
297
305
  const [{ stdout: headOut }, { stdout: treeOut }, { stdout: statusOut }] =
298
306
  await Promise.all([