release-skill 0.9.4 → 0.9.6

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 (59) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codebuddy-plugin/plugin.json +1 -1
  4. package/.codex-plugin/plugin.json +2 -2
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/CHANGELOG.md +56 -0
  7. package/INSTALL.md +2 -2
  8. package/INSTALL.zh-CN.md +2 -2
  9. package/README.md +21 -17
  10. package/README.zh-CN.md +18 -16
  11. package/adapters/claude/.claude-plugin/marketplace.json +1 -1
  12. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  13. package/adapters/claude/bin/release-skill.bundle.mjs +1036 -174
  14. package/adapters/claude/schemas/release-plan.schema.json +36 -0
  15. package/adapters/claude/schemas/release-project.schema.json +31 -0
  16. package/adapters/claude/skills/release-finish/SKILL.md +5 -5
  17. package/adapters/claude/skills/release-help/SKILL.md +7 -3
  18. package/adapters/claude/skills/release-verify/SKILL.md +14 -1
  19. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  20. package/adapters/codex/bin/release-skill.bundle.mjs +1036 -174
  21. package/adapters/codex/schemas/release-plan.schema.json +36 -0
  22. package/adapters/codex/schemas/release-project.schema.json +31 -0
  23. package/adapters/codex/skills/release-finish/SKILL.md +5 -5
  24. package/adapters/codex/skills/release-help/SKILL.md +7 -3
  25. package/adapters/codex/skills/release-verify/SKILL.md +14 -1
  26. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  27. package/adapters/kimi/bin/release-skill.bundle.mjs +1036 -174
  28. package/adapters/kimi/schemas/release-plan.schema.json +36 -0
  29. package/adapters/kimi/schemas/release-project.schema.json +31 -0
  30. package/adapters/kimi/skills/release-finish/SKILL.md +5 -5
  31. package/adapters/kimi/skills/release-help/SKILL.md +7 -3
  32. package/adapters/kimi/skills/release-verify/SKILL.md +14 -1
  33. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  34. package/adapters/workbuddy/bin/release-skill.bundle.mjs +1036 -174
  35. package/adapters/workbuddy/schemas/release-plan.schema.json +36 -0
  36. package/adapters/workbuddy/schemas/release-project.schema.json +31 -0
  37. package/adapters/workbuddy/skills/release-finish/SKILL.md +5 -5
  38. package/adapters/workbuddy/skills/release-help/SKILL.md +7 -3
  39. package/adapters/workbuddy/skills/release-verify/SKILL.md +14 -1
  40. package/bin/release-skill-cli.mjs +217 -26
  41. package/bin/release-skill.bundle.mjs +1036 -174
  42. package/package.json +1 -1
  43. package/platform-manifest.json +4 -4
  44. package/schemas/release-plan.schema.json +36 -0
  45. package/schemas/release-project.schema.json +31 -0
  46. package/skills/release-finish/SKILL.md +5 -5
  47. package/skills/release-help/SKILL.md +7 -3
  48. package/skills/release-verify/SKILL.md +14 -1
  49. package/skills-src/release-finish/SKILL.md +5 -5
  50. package/skills-src/release-help/SKILL.md +7 -3
  51. package/skills-src/release-verify/SKILL.md +14 -1
  52. package/src/adapters/plugin-marketplace.mjs +54 -5
  53. package/src/commands/post-release-local.mjs +204 -10
  54. package/src/commands/prepare.mjs +178 -9
  55. package/src/commands/ship.mjs +26 -11
  56. package/src/commands/verify.mjs +175 -2
  57. package/src/core/plan.mjs +45 -1
  58. package/src/platforms/registry.mjs +97 -0
  59. package/src/readme/contract.mjs +23 -3
@@ -9,9 +9,9 @@ const __bundlePkgRoot = __bundleResolve(__bundleDirname(__bundleFileURLToPath(im
9
9
  // Provide a real require() for CJS packages bundled into ESM (e.g. yaml, ajv).
10
10
  const __bundleRealRequire = __bundleCreateRequire(import.meta.url);
11
11
  // Package identity injected at build time — closure-independent --version probe.
12
- const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.4"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.6"});
13
13
  // Build-time source digest for the BUNDLE_STALE freshness gate (see above).
14
- const __bundleSourceDigest = "f9cf3c6e981293ac4d26c870433f0d8cad33e8a838e48d0d9c027f2cefe8bec6";
14
+ const __bundleSourceDigest = "8fd8e6c5f7796efe9e1f7153382b50c74d78a4b5f836f872837ff2f11e4daeb5";
15
15
 
16
16
  var __create = Object.create;
17
17
  var __defProp = Object.defineProperty;
@@ -19972,8 +19972,8 @@ var require_graceful_fs = __commonJS({
19972
19972
  fs2.createReadStream = createReadStream2;
19973
19973
  fs2.createWriteStream = createWriteStream;
19974
19974
  var fs$readFile = fs2.readFile;
19975
- fs2.readFile = readFile56;
19976
- function readFile56(path40, options, cb) {
19975
+ fs2.readFile = readFile57;
19976
+ function readFile57(path40, options, cb) {
19977
19977
  if (typeof options === "function")
19978
19978
  cb = options, options = null;
19979
19979
  return go$readFile(path40, options, cb);
@@ -19989,7 +19989,7 @@ var require_graceful_fs = __commonJS({
19989
19989
  }
19990
19990
  __name(go$readFile, "go$readFile");
19991
19991
  }
19992
- __name(readFile56, "readFile");
19992
+ __name(readFile57, "readFile");
19993
19993
  var fs$writeFile = fs2.writeFile;
19994
19994
  fs2.writeFile = writeFile16;
19995
19995
  function writeFile16(path40, data, options, cb) {
@@ -41768,6 +41768,91 @@ function getPlatform(id) {
41768
41768
  if (!platform) throw new Error(`unknown platform: ${id}`);
41769
41769
  return platform;
41770
41770
  }
41771
+ function standaloneIdentityCore(platformOrId, fields) {
41772
+ const platform = typeof platformOrId === "string" ? getPlatform(platformOrId) : platformOrId;
41773
+ const id = platform?.id;
41774
+ if (id !== "claude" && id !== "codex") {
41775
+ throw new Error(`standalone-index install identity is unsupported for platform "${id ?? "<unknown>"}"`);
41776
+ }
41777
+ const { name, source, version } = fields;
41778
+ if (typeof name !== "string" || name.length === 0 || !source || typeof source !== "object" || Array.isArray(source) || typeof source.source !== "string" || typeof source.ref !== "string" || source.ref.length === 0 || typeof source.sha !== "string" || !STANDALONE_SHA_RE.test(source.sha)) {
41779
+ throw new Error(`${id} standalone-index install identity has invalid owned fields`);
41780
+ }
41781
+ if (id === "claude") {
41782
+ if (source.source !== "github" || typeof source.repo !== "string" || source.repo.length === 0 || typeof version !== "string" || version.length === 0) {
41783
+ throw new Error("Claude standalone-index install identity requires github repo, ref, sha, and version");
41784
+ }
41785
+ return {
41786
+ name,
41787
+ source: { source: "github", repo: source.repo, ref: source.ref, sha: source.sha },
41788
+ version
41789
+ };
41790
+ }
41791
+ if (source.source !== "url" || typeof source.url !== "string" || source.url.length === 0) {
41792
+ throw new Error("Codex standalone-index install identity requires url, ref, and sha");
41793
+ }
41794
+ return {
41795
+ name,
41796
+ source: { source: "url", url: source.url, ref: source.ref, sha: source.sha }
41797
+ };
41798
+ }
41799
+ function buildExpectedStandaloneIndexInstallIdentity(platformOrId, frozenIdentity = {}) {
41800
+ const platform = typeof platformOrId === "string" ? getPlatform(platformOrId) : platformOrId;
41801
+ const id = platform?.id;
41802
+ const { name, repo, tag, sha, version } = frozenIdentity;
41803
+ if (typeof repo !== "string" || repo.length === 0 || typeof tag !== "string" || tag.length === 0) {
41804
+ throw new Error("standalone-index expected identity requires a plugin repository and tag");
41805
+ }
41806
+ if (id === "claude") {
41807
+ return standaloneIdentityCore(platform, {
41808
+ name,
41809
+ version,
41810
+ source: { source: "github", repo, ref: tag, sha }
41811
+ });
41812
+ }
41813
+ if (id === "codex") {
41814
+ return standaloneIdentityCore(platform, {
41815
+ name,
41816
+ source: { source: "url", url: `https://github.com/${repo}.git`, ref: `refs/tags/${tag}`, sha }
41817
+ });
41818
+ }
41819
+ return standaloneIdentityCore(platform, { name, source: {}, version });
41820
+ }
41821
+ function projectObservedStandaloneIndexInstallIdentity(platformOrId, entry) {
41822
+ const platform = typeof platformOrId === "string" ? getPlatform(platformOrId) : platformOrId;
41823
+ const id = platform?.id;
41824
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
41825
+ throw new Error("standalone-index observed identity requires an entry object");
41826
+ }
41827
+ const source = entry.source;
41828
+ if (!source || typeof source !== "object" || Array.isArray(source)) {
41829
+ throw new Error("standalone-index observed identity requires a source object");
41830
+ }
41831
+ if (id === "claude") {
41832
+ return standaloneIdentityCore(platform, {
41833
+ name: entry.name,
41834
+ version: entry.version,
41835
+ source: {
41836
+ source: source.source,
41837
+ repo: source.repo,
41838
+ ref: source.ref,
41839
+ sha: source.sha
41840
+ }
41841
+ });
41842
+ }
41843
+ if (id === "codex") {
41844
+ return standaloneIdentityCore(platform, {
41845
+ name: entry.name,
41846
+ source: {
41847
+ source: source.source,
41848
+ url: source.url,
41849
+ ref: source.ref,
41850
+ sha: source.sha
41851
+ }
41852
+ });
41853
+ }
41854
+ return standaloneIdentityCore(platform, { name: entry.name, source });
41855
+ }
41771
41856
  function resolveSkillProjectionSurfaceHost(surfaceId, registry = PLATFORMS) {
41772
41857
  const platform = registry.find((item) => item.skillProjectionSurface === surfaceId);
41773
41858
  return platform?.buildAdapter?.name ?? null;
@@ -41982,7 +42067,7 @@ async function normalizeHostId(hostId, { fallback = false } = {}) {
41982
42067
  throw err;
41983
42068
  }
41984
42069
  }
41985
- var CLAUDE, CODEX, KIMI, CODEBUDDY, PLATFORMS, VALID_DISTRIBUTION_TYPES, VALID_ACTION_TYPES, VALID_SOURCE_FORMS, VALID_MARKETPLACE_REF_FORMS, VALID_LIST_OUTPUTS, VALID_CLI_OUTPUTS, VALID_ENTRY_VERSION_BINDING, VALID_INSTALL_METHODS, VALID_REF_STRENGTHS, VALID_OUTPUT_PROTOCOLS, VALID_IDENTITY_EVIDENCE, VALID_DEGRADATION_POLICIES, SKILL_PROJECTION_SURFACE_PATTERN, cachedHostsRoot, normalizedHostIds;
42070
+ var CLAUDE, CODEX, KIMI, CODEBUDDY, PLATFORMS, VALID_DISTRIBUTION_TYPES, VALID_ACTION_TYPES, VALID_SOURCE_FORMS, VALID_MARKETPLACE_REF_FORMS, VALID_LIST_OUTPUTS, VALID_CLI_OUTPUTS, VALID_ENTRY_VERSION_BINDING, VALID_INSTALL_METHODS, VALID_REF_STRENGTHS, VALID_OUTPUT_PROTOCOLS, VALID_IDENTITY_EVIDENCE, VALID_DEGRADATION_POLICIES, SKILL_PROJECTION_SURFACE_PATTERN, STANDALONE_SHA_RE, cachedHostsRoot, normalizedHostIds;
41986
42071
  var init_registry2 = __esm({
41987
42072
  async "src/platforms/registry.mjs"() {
41988
42073
  init_src3();
@@ -42269,6 +42354,10 @@ var init_registry2 = __esm({
42269
42354
  VALID_DEGRADATION_POLICIES = /* @__PURE__ */ new Set(["block", "human-attestation", "human-attestation-with-fallback"]);
42270
42355
  SKILL_PROJECTION_SURFACE_PATTERN = /^platforms\/[a-z0-9]+(?:-[a-z0-9]+)*$/u;
42271
42356
  __name(getPlatform, "getPlatform");
42357
+ STANDALONE_SHA_RE = /^[0-9a-f]{40}$/u;
42358
+ __name(standaloneIdentityCore, "standaloneIdentityCore");
42359
+ __name(buildExpectedStandaloneIndexInstallIdentity, "buildExpectedStandaloneIndexInstallIdentity");
42360
+ __name(projectObservedStandaloneIndexInstallIdentity, "projectObservedStandaloneIndexInstallIdentity");
42272
42361
  __name(resolveSkillProjectionSurfaceHost, "resolveSkillProjectionSurfaceHost");
42273
42362
  __name(assertRegistry, "assertRegistry");
42274
42363
  assertRegistry();
@@ -43160,6 +43249,7 @@ function validatePlanActionCompleteness(plan, options = {}) {
43160
43249
  }
43161
43250
  if (production && externalMarketplace && dist.marketplaceSourceType === "standalone-index") {
43162
43251
  const params = action.parameters;
43252
+ const bootstrap = dist.firstReleaseBootstrap === "manual-index-checkpoint";
43163
43253
  if (params) {
43164
43254
  if (!params.marketplaceIndexPath) {
43165
43255
  failures.push(
@@ -43176,6 +43266,38 @@ function validatePlanActionCompleteness(plan, options = {}) {
43176
43266
  `unit "${unitId}", action "${action.id}": parameters.selectedEntry is required for production standalone-index`
43177
43267
  );
43178
43268
  }
43269
+ if (bootstrap) {
43270
+ if (params.firstReleaseBootstrap !== "manual-index-checkpoint") {
43271
+ failures.push(
43272
+ `unit "${unitId}", action "${action.id}": parameters.firstReleaseBootstrap must be manual-index-checkpoint for a bootstrap distribution`
43273
+ );
43274
+ }
43275
+ if (params.marketplaceIndexSha !== null) {
43276
+ failures.push(
43277
+ `unit "${unitId}", action "${action.id}": parameters.marketplaceIndexSha must remain null until the manual index checkpoint`
43278
+ );
43279
+ }
43280
+ let expectedEntry = null;
43281
+ try {
43282
+ expectedEntry = buildExpectedStandaloneIndexInstallIdentity(platform, {
43283
+ name: plugin,
43284
+ version: targetVersion,
43285
+ repo: publicRepo,
43286
+ tag: expectedTag,
43287
+ sha: frozen?.commit
43288
+ });
43289
+ } catch {
43290
+ }
43291
+ if (!expectedEntry || canonicalJson2(params.selectedEntry) !== canonicalJson2(expectedEntry)) {
43292
+ failures.push(
43293
+ `unit "${unitId}", action "${action.id}": bootstrap selectedEntry does not match the frozen plugin repository, tag, commit, and version`
43294
+ );
43295
+ }
43296
+ } else if (params.firstReleaseBootstrap !== void 0 || params.marketplaceIndexSha !== void 0) {
43297
+ failures.push(
43298
+ `unit "${unitId}", action "${action.id}": bootstrap-only fields are not allowed without firstReleaseBootstrap`
43299
+ );
43300
+ }
43179
43301
  }
43180
43302
  }
43181
43303
  if (!externalMarketplace && dist.marketplaceSourceType === "bundled-family") {
@@ -43205,6 +43327,14 @@ function validatePlanActionCompleteness(plan, options = {}) {
43205
43327
  _checkRequired(action, "expected.ref", action.expected?.ref, action.parameters?.ref, unitId, failures);
43206
43328
  _checkRequired(action, "expected.marketplaceLocation", action.expected?.marketplaceLocation, "external", unitId, failures);
43207
43329
  _checkRequired(action, "expected.marketplaceCommitSha", action.expected?.marketplaceCommitSha, action.parameters?.marketplaceCommitSha, unitId, failures);
43330
+ if (dist.firstReleaseBootstrap === "manual-index-checkpoint") {
43331
+ _checkRequired(action, "expected.firstReleaseBootstrap", action.expected?.firstReleaseBootstrap, "manual-index-checkpoint", unitId, failures);
43332
+ if (action.expected?.marketplaceIndexSha !== null) {
43333
+ failures.push(
43334
+ `unit "${unitId}", action "${action.id}": expected.marketplaceIndexSha must be null until the manual index checkpoint`
43335
+ );
43336
+ }
43337
+ }
43208
43338
  } else {
43209
43339
  _checkRequired(action, "expected.repo", action.expected?.repo, publicRepo, unitId, failures);
43210
43340
  _checkRequired(action, "expected.ref", action.expected?.ref, expectedTag, unitId, failures);
@@ -59992,7 +60122,7 @@ var require_entry_index = __commonJS({
59992
60122
  var {
59993
60123
  appendFile,
59994
60124
  mkdir: mkdir35,
59995
- readFile: readFile56,
60125
+ readFile: readFile57,
59996
60126
  readdir: readdir34,
59997
60127
  rm: rm19,
59998
60128
  writeFile: writeFile16
@@ -60184,7 +60314,7 @@ ${hashEntry(stringified)} ${stringified}`);
60184
60314
  __name(ls, "ls");
60185
60315
  module.exports.bucketEntries = bucketEntries;
60186
60316
  async function bucketEntries(bucket, filter) {
60187
- const data = await readFile56(bucket, "utf8");
60317
+ const data = await readFile57(bucket, "utf8");
60188
60318
  return _bucketEntries(data, filter);
60189
60319
  }
60190
60320
  __name(bucketEntries, "bucketEntries");
@@ -64494,7 +64624,7 @@ var require_verify = __commonJS({
64494
64624
  "use strict";
64495
64625
  var {
64496
64626
  mkdir: mkdir35,
64497
- readFile: readFile56,
64627
+ readFile: readFile57,
64498
64628
  rm: rm19,
64499
64629
  stat: stat22,
64500
64630
  truncate,
@@ -64721,7 +64851,7 @@ var require_verify = __commonJS({
64721
64851
  __name(writeVerifile, "writeVerifile");
64722
64852
  module.exports.lastRun = lastRun;
64723
64853
  async function lastRun(cache) {
64724
- const data = await readFile56(path40.join(cache, "_lastverified"), { encoding: "utf8" });
64854
+ const data = await readFile57(path40.join(cache, "_lastverified"), { encoding: "utf8" });
64725
64855
  return /* @__PURE__ */ new Date(+data);
64726
64856
  }
64727
64857
  __name(lastRun, "lastRun");
@@ -76643,11 +76773,11 @@ var require_normalize = __commonJS({
76643
76773
  // ../../node_modules/.pnpm/@npmcli+package-json@7.0.5/node_modules/@npmcli/package-json/lib/read-package.js
76644
76774
  var require_read_package = __commonJS({
76645
76775
  "../../node_modules/.pnpm/@npmcli+package-json@7.0.5/node_modules/@npmcli/package-json/lib/read-package.js"(exports, module) {
76646
- var { readFile: readFile56 } = __require("fs/promises");
76776
+ var { readFile: readFile57 } = __require("fs/promises");
76647
76777
  var parseJSON = require_lib33();
76648
76778
  async function read(filename) {
76649
76779
  try {
76650
- const data = await readFile56(filename, "utf8");
76780
+ const data = await readFile57(filename, "utf8");
76651
76781
  return data;
76652
76782
  } catch (err) {
76653
76783
  err.message = `Could not read package.json: ${err}`;
@@ -76780,7 +76910,7 @@ var require_sort2 = __commonJS({
76780
76910
  // ../../node_modules/.pnpm/@npmcli+package-json@7.0.5/node_modules/@npmcli/package-json/lib/index.js
76781
76911
  var require_lib40 = __commonJS({
76782
76912
  "../../node_modules/.pnpm/@npmcli+package-json@7.0.5/node_modules/@npmcli/package-json/lib/index.js"(exports, module) {
76783
- var { readFile: readFile56, writeFile: writeFile16 } = __require("node:fs/promises");
76913
+ var { readFile: readFile57, writeFile: writeFile16 } = __require("node:fs/promises");
76784
76914
  var { resolve: resolve42 } = __require("node:path");
76785
76915
  var parseJSON = require_lib33();
76786
76916
  var updateDeps = require_update_dependencies();
@@ -76906,7 +77036,7 @@ var require_lib40 = __commonJS({
76906
77036
  const indexFile = resolve42(this.path, "index.js");
76907
77037
  let indexFileContent;
76908
77038
  try {
76909
- indexFileContent = await readFile56(indexFile, "utf8");
77039
+ indexFileContent = await readFile57(indexFile, "utf8");
76910
77040
  } catch (err) {
76911
77041
  throw parseErr;
76912
77042
  }
@@ -89207,7 +89337,7 @@ var require_dist16 = __commonJS({
89207
89337
  var require_provenance = __commonJS({
89208
89338
  "../../node_modules/.pnpm/libnpmpublish@11.2.0/node_modules/libnpmpublish/lib/provenance.js"(exports, module) {
89209
89339
  var sigstore = require_dist16();
89210
- var { readFile: readFile56 } = __require("node:fs/promises");
89340
+ var { readFile: readFile57 } = __require("node:fs/promises");
89211
89341
  var ci = require_ci_info();
89212
89342
  var { env } = process;
89213
89343
  var INTOTO_PAYLOAD_TYPE = "application/vnd.in-toto+json";
@@ -89399,7 +89529,7 @@ var require_provenance = __commonJS({
89399
89529
  var verifyProvenance = /* @__PURE__ */ __name(async (subject, provenancePath) => {
89400
89530
  let provenanceBundle;
89401
89531
  try {
89402
- provenanceBundle = JSON.parse(await readFile56(provenancePath));
89532
+ provenanceBundle = JSON.parse(await readFile57(provenancePath));
89403
89533
  } catch (err) {
89404
89534
  err.message = `Invalid provenance provided: ${err.message}`;
89405
89535
  throw err;
@@ -99484,7 +99614,7 @@ var require_entry_index2 = __commonJS({
99484
99614
  var {
99485
99615
  appendFile,
99486
99616
  mkdir: mkdir35,
99487
- readFile: readFile56,
99617
+ readFile: readFile57,
99488
99618
  readdir: readdir34,
99489
99619
  rm: rm19,
99490
99620
  writeFile: writeFile16
@@ -99676,7 +99806,7 @@ ${hashEntry(stringified)} ${stringified}`);
99676
99806
  __name(ls, "ls");
99677
99807
  module.exports.bucketEntries = bucketEntries;
99678
99808
  async function bucketEntries(bucket, filter) {
99679
- const data = await readFile56(bucket, "utf8");
99809
+ const data = await readFile57(bucket, "utf8");
99680
99810
  return _bucketEntries(data, filter);
99681
99811
  }
99682
99812
  __name(bucketEntries, "bucketEntries");
@@ -105365,7 +105495,7 @@ var require_verify3 = __commonJS({
105365
105495
  "use strict";
105366
105496
  var {
105367
105497
  mkdir: mkdir35,
105368
- readFile: readFile56,
105498
+ readFile: readFile57,
105369
105499
  rm: rm19,
105370
105500
  stat: stat22,
105371
105501
  truncate,
@@ -105592,7 +105722,7 @@ var require_verify3 = __commonJS({
105592
105722
  __name(writeVerifile, "writeVerifile");
105593
105723
  module.exports.lastRun = lastRun;
105594
105724
  async function lastRun(cache) {
105595
- const data = await readFile56(path40.join(cache, "_lastverified"), { encoding: "utf8" });
105725
+ const data = await readFile57(path40.join(cache, "_lastverified"), { encoding: "utf8" });
105596
105726
  return /* @__PURE__ */ new Date(+data);
105597
105727
  }
105598
105728
  __name(lastRun, "lastRun");
@@ -109510,6 +109640,39 @@ function transportPayload(entries) {
109510
109640
  contentDigest
109511
109641
  }));
109512
109642
  }
109643
+ function foundationPayloadMembers(observation) {
109644
+ return observation.members.map((member) => {
109645
+ if (member.type === "file") {
109646
+ return {
109647
+ path: member.path,
109648
+ type: member.type,
109649
+ mode: member.statMode & ~146,
109650
+ size: member.bytes,
109651
+ contentDigest: member.sha256
109652
+ };
109653
+ }
109654
+ if (member.type === "directory") {
109655
+ return { path: member.path, type: member.type, mode: member.statMode };
109656
+ }
109657
+ return {
109658
+ path: member.path,
109659
+ type: member.type,
109660
+ mode: member.statMode,
109661
+ size: member.bytes,
109662
+ targetBase64: member.targetBase64
109663
+ };
109664
+ });
109665
+ }
109666
+ async function observeMarketplaceInstallTree(installPath) {
109667
+ const canonicalInstallPath = await realpath22(installPath);
109668
+ const rootBinding = await createFilesystemRootBinding(canonicalInstallPath);
109669
+ const observation = await observeFilesystemTree({
109670
+ root: canonicalInstallPath,
109671
+ rootBinding,
109672
+ symlinkPolicy: { mode: "record" }
109673
+ });
109674
+ return foundationPayloadMembers(observation);
109675
+ }
109513
109676
  function resolveMarketplaceRoot(platform, marketplaceIndexPath) {
109514
109677
  const defaultMarketplace = platform.manifestPaths.marketplace;
109515
109678
  if (!defaultMarketplace) {
@@ -109739,10 +109902,10 @@ async function verifyInstalledMarketplacePayload(action, context, installPath, c
109739
109902
  throw new Error(`unsupported marketplace payload contract: ${JSON.stringify(payloadContract)}`);
109740
109903
  }
109741
109904
  if (payloadContract === PAYLOAD_CONTRACT_DECLARED_MANIFEST || payloadContract === PAYLOAD_CONTRACT_EXTERNAL_MARKETPLACE) {
109742
- const installedSnapshot2 = await computeFrozenSnapshot(installPath);
109905
+ const installedMembers = await observeMarketplaceInstallTree(installPath);
109743
109906
  const authorityPayload = transportPayload(authorityEntries);
109744
109907
  const installedByPath = new Map(
109745
- transportPayload(installedSnapshot2.entries).map((entry) => [entry.path, entry])
109908
+ installedMembers.map((entry) => [entry.path, entry])
109746
109909
  );
109747
109910
  const conflicts = [];
109748
109911
  for (const authorityEntry of authorityPayload) {
@@ -109767,11 +109930,19 @@ async function verifyInstalledMarketplacePayload(action, context, installPath, c
109767
109930
  );
109768
109931
  }
109769
109932
  const authorityPaths = new Set(authorityPayload.map((entry) => entry.path));
109770
- const extraPaths = installedSnapshot2.entries.map((entry) => entry.path).filter((path40) => !authorityPaths.has(path40));
109933
+ const extraMembers = installedMembers.filter((entry) => entry.type !== "directory" && !authorityPaths.has(entry.path));
109934
+ const extraPaths = extraMembers.map((entry) => entry.path);
109771
109935
  const extraInstalledPaths = extraPaths.slice(0, EXTRA_INSTALLED_PATHS_CAP);
109936
+ const extraInstalledLinks = extraMembers.filter((entry) => entry.type === "symlink").slice(0, EXTRA_INSTALLED_PATHS_CAP).map((entry) => ({
109937
+ path: entry.path,
109938
+ targetBase64: entry.targetBase64,
109939
+ bytes: entry.size,
109940
+ statMode: entry.mode
109941
+ }));
109772
109942
  return {
109773
109943
  manifestDigest: action.manifestDigest,
109774
109944
  extraInstalledPaths,
109945
+ ...extraInstalledLinks.length > 0 ? { extraInstalledLinks } : {},
109775
109946
  ...extraPaths.length > EXTRA_INSTALLED_PATHS_CAP ? { extraInstalledPathsTotal: extraPaths.length } : {}
109776
109947
  };
109777
109948
  }
@@ -109787,6 +109958,7 @@ function extraInstalledPathsAudit(binding) {
109787
109958
  if (!binding || !Array.isArray(binding.extraInstalledPaths)) return {};
109788
109959
  return {
109789
109960
  extraInstalledPaths: binding.extraInstalledPaths,
109961
+ ...Array.isArray(binding.extraInstalledLinks) && binding.extraInstalledLinks.length > 0 ? { extraInstalledLinks: binding.extraInstalledLinks } : {},
109790
109962
  ...binding.extraInstalledPathsTotal !== void 0 ? { extraInstalledPathsTotal: binding.extraInstalledPathsTotal } : {}
109791
109963
  };
109792
109964
  }
@@ -112190,6 +112362,7 @@ var init_plugin_marketplace = __esm({
112190
112362
  async "src/adapters/plugin-marketplace.mjs"() {
112191
112363
  init_contract();
112192
112364
  init_installation_contract();
112365
+ init_src2();
112193
112366
  init_frozen();
112194
112367
  await init_registry2();
112195
112368
  await init_kimi();
@@ -112202,6 +112375,8 @@ var init_plugin_marketplace = __esm({
112202
112375
  PAYLOAD_CONTRACT_EXTERNAL_MARKETPLACE = "external-marketplace-v1";
112203
112376
  EXTRA_INSTALLED_PATHS_CAP = 200;
112204
112377
  PAYLOAD_CONFLICT_REPORT_CAP = 10;
112378
+ __name(foundationPayloadMembers, "foundationPayloadMembers");
112379
+ __name(observeMarketplaceInstallTree, "observeMarketplaceInstallTree");
112205
112380
  CONSUMER_INSTALL_RECIPE_VERSION = "consumer-install-v1";
112206
112381
  __name(resolveMarketplaceRoot, "resolveMarketplaceRoot");
112207
112382
  __name(extractDeclaredPluginSource, "extractDeclaredPluginSource");
@@ -112391,6 +112566,136 @@ async function verifyPublicSourceAuthorityReceiptOffline({ plan, root }) {
112391
112566
  function defaultClock() {
112392
112567
  return (/* @__PURE__ */ new Date()).toISOString();
112393
112568
  }
112569
+ function parseMarketplaceHead(stdout) {
112570
+ if (typeof stdout !== "string") return null;
112571
+ let sha = null;
112572
+ let defaultBranch = null;
112573
+ for (const line of stdout.trim().split("\n").filter(Boolean)) {
112574
+ const tab = line.indexOf(" ");
112575
+ if (tab < 0) continue;
112576
+ const left = line.slice(0, tab);
112577
+ const right = line.slice(tab + 1);
112578
+ if (right !== "HEAD") continue;
112579
+ if (left.startsWith("ref: refs/heads/")) defaultBranch = left.slice("ref: refs/heads/".length);
112580
+ else if (EXTERNAL_MARKETPLACE_SHA_RE2.test(left)) sha = left;
112581
+ }
112582
+ return sha && defaultBranch ? { sha, defaultBranch } : null;
112583
+ }
112584
+ async function defaultObserveMarketplaceHead(repo, { githubHost = "github.com" } = {}) {
112585
+ try {
112586
+ const { stdout } = await execFile6("git", [
112587
+ "ls-remote",
112588
+ "--symref",
112589
+ `https://${githubHost}/${repo}.git`,
112590
+ "HEAD"
112591
+ ], { shell: false, encoding: "utf8", timeout: 3e4 });
112592
+ const parsed = parseMarketplaceHead(stdout);
112593
+ return parsed ? { status: "observed", ...parsed } : { status: "unknown", error: "could not resolve marketplace HEAD" };
112594
+ } catch (error) {
112595
+ return { status: "unknown", error: error.message };
112596
+ }
112597
+ }
112598
+ function decodeMarketplaceIndex(content) {
112599
+ if (typeof content !== "string") return null;
112600
+ try {
112601
+ return JSON.parse(Buffer.from(content.replace(/\s/g, ""), "base64").toString("utf8"));
112602
+ } catch {
112603
+ return null;
112604
+ }
112605
+ }
112606
+ async function defaultFetchMarketplaceIndex(repo, path40, ref, { githubHost = "github.com" } = {}) {
112607
+ try {
112608
+ const { stdout } = await execFile6("gh", [
112609
+ "api",
112610
+ `repos/${repo}/contents/${path40}?ref=${ref}`,
112611
+ "--jq",
112612
+ ".content"
112613
+ ], {
112614
+ shell: false,
112615
+ encoding: "utf8",
112616
+ timeout: 3e4,
112617
+ env: { ...process.env, GH_HOST: githubHost }
112618
+ });
112619
+ const index = decodeMarketplaceIndex(stdout);
112620
+ return index && typeof index === "object" ? { status: "fetched", index } : { status: "unknown", error: "could not decode marketplace index" };
112621
+ } catch (error) {
112622
+ return { status: "unknown", error: error.message };
112623
+ }
112624
+ }
112625
+ async function verifyFirstReleaseBootstrapIndexes({
112626
+ actions,
112627
+ plan,
112628
+ evidence,
112629
+ observeHeadFn,
112630
+ fetchIndexFn
112631
+ }) {
112632
+ const bindings = /* @__PURE__ */ new Map();
112633
+ for (const action of actions) {
112634
+ if (action.parameters?.firstReleaseBootstrap !== FIRST_RELEASE_BOOTSTRAP_MODE) continue;
112635
+ const params = action.parameters;
112636
+ const githubHost = params.githubHost ?? "github.com";
112637
+ const failDeferred = /* @__PURE__ */ __name(async (reason, observed2 = null) => {
112638
+ await evidence.append({
112639
+ phase: "verify-marketplace-bootstrap-index",
112640
+ actionId: action.id,
112641
+ actionType: action.type,
112642
+ status: "needs-input",
112643
+ reason,
112644
+ ...observed2 ? { observed: observed2 } : {}
112645
+ });
112646
+ throw new ReleaseError(
112647
+ CONSUMER_VERIFICATION_DEFERRED,
112648
+ `first-release bootstrap marketplace index is not an exact match for action "${action.id}"; complete the manual index checkpoint and retry verify with the same plan`,
112649
+ { actionId: action.id, reason, observed: observed2, nextState: "NEEDS_MANUAL_ATTESTATIONS" }
112650
+ );
112651
+ }, "failDeferred");
112652
+ const observed = await observeHeadFn(params.repo, { githubHost });
112653
+ if (observed?.status !== "observed" || !EXTERNAL_MARKETPLACE_SHA_RE2.test(observed.sha ?? "") || !observed.defaultBranch) {
112654
+ await failDeferred(`could not observe current marketplace HEAD: ${observed?.error ?? "unknown"}`, observed);
112655
+ }
112656
+ const path40 = params.marketplaceIndexPath;
112657
+ const fetched = await fetchIndexFn(params.repo, path40, observed.sha, { githubHost });
112658
+ if (fetched?.status !== "fetched" || !fetched.index || typeof fetched.index !== "object") {
112659
+ await failDeferred(`could not read marketplace index at ${observed.sha}: ${fetched?.error ?? "unknown"}`, observed);
112660
+ }
112661
+ const index = fetched.index;
112662
+ if (index.name !== params.marketplaceName) {
112663
+ await failDeferred(`marketplace name mismatch: expected ${params.marketplaceName}, got ${index.name}`, { ...observed, name: index.name });
112664
+ }
112665
+ if (!Array.isArray(index.plugins)) {
112666
+ await failDeferred("marketplace index plugins must be an array", { ...observed, pluginsType: typeof index.plugins });
112667
+ }
112668
+ const entries = index.plugins.filter((entry) => entry && entry.name === params.plugin);
112669
+ let projectedEntry = null;
112670
+ if (entries.length === 1) {
112671
+ try {
112672
+ projectedEntry = projectObservedStandaloneIndexInstallIdentity(params.consumer, entries[0]);
112673
+ } catch {
112674
+ projectedEntry = null;
112675
+ }
112676
+ }
112677
+ if (entries.length !== 1 || !projectedEntry || canonicalJson2(projectedEntry) !== canonicalJson2(params.selectedEntry)) {
112678
+ await failDeferred("marketplace selectedEntry is missing, duplicated, or does not exactly match the plan-bound expected entry", {
112679
+ ...observed,
112680
+ entryCount: entries.length,
112681
+ selectedEntry: projectedEntry ?? entries[0] ?? null
112682
+ });
112683
+ }
112684
+ bindings.set(action.id, {
112685
+ marketplaceIndexSha: observed.sha,
112686
+ marketplaceIndexRef: action.type === "claude-marketplace-install" ? observed.defaultBranch : observed.sha
112687
+ });
112688
+ await evidence.append({
112689
+ phase: "verify-marketplace-bootstrap-index",
112690
+ actionId: action.id,
112691
+ actionType: action.type,
112692
+ status: "observed",
112693
+ marketplaceIndexSha: observed.sha,
112694
+ marketplaceIndexRef: action.type === "claude-marketplace-install" ? observed.defaultBranch : observed.sha
112695
+ });
112696
+ }
112697
+ return bindings;
112698
+ }
112394
112699
  function isValidDigest2(digest3) {
112395
112700
  return typeof digest3 === "string" && /^[a-f0-9]{64}$/.test(digest3);
112396
112701
  }
@@ -112925,7 +113230,9 @@ async function verifyRelease(options) {
112925
113230
  previousVerifyRun,
112926
113231
  execFn,
112927
113232
  configPath: configPathOpt,
112928
- runPluginVerificationFn
113233
+ runPluginVerificationFn,
113234
+ observeFirstReleaseMarketplaceHeadFn,
113235
+ fetchFirstReleaseMarketplaceIndexFn
112929
113236
  } = options ?? {};
112930
113237
  const clockFn = typeof clockOpt === "function" ? clockOpt : defaultClock;
112931
113238
  if (!sourceRunPath) {
@@ -113139,6 +113446,13 @@ async function verifyRelease(options) {
113139
113446
  const consumerGateResults = [];
113140
113447
  const consumerVerificationReceipts = [];
113141
113448
  const actions = plan.externalActions ?? [];
113449
+ const bootstrapIndexBindings = await verifyFirstReleaseBootstrapIndexes({
113450
+ actions,
113451
+ plan,
113452
+ evidence,
113453
+ observeHeadFn: observeFirstReleaseMarketplaceHeadFn ?? defaultObserveMarketplaceHead,
113454
+ fetchIndexFn: fetchFirstReleaseMarketplaceIndexFn ?? defaultFetchMarketplaceIndex
113455
+ });
113142
113456
  const trustedVerifyRuns = [];
113143
113457
  const planDir = dirname10(planPath);
113144
113458
  const releaseDir = basename9(planDir) === "plans" ? dirname10(planDir) : planDir;
@@ -113335,10 +113649,21 @@ async function verifyRelease(options) {
113335
113649
  root,
113336
113650
  runDir
113337
113651
  };
113652
+ const bootstrapBinding = bootstrapIndexBindings.get(action.id) ?? null;
113338
113653
  const actionInput = {
113339
113654
  actionType: adapterActionType,
113340
113655
  ...action.parameters
113341
113656
  };
113657
+ if (bootstrapBinding) {
113658
+ actionInput.marketplaceCommitSha = bootstrapBinding.marketplaceIndexSha;
113659
+ actionInput.marketplaceIndexSha = bootstrapBinding.marketplaceIndexSha;
113660
+ actionInput.ref = bootstrapBinding.marketplaceIndexRef;
113661
+ actionInput.sourceDescriptor = {
113662
+ ...actionInput.sourceDescriptor,
113663
+ marketplaceCommitSha: bootstrapBinding.marketplaceIndexSha,
113664
+ ref: bootstrapBinding.marketplaceIndexRef
113665
+ };
113666
+ }
113342
113667
  const unit = (plan.units ?? []).find((u) => u.id === action.unitId);
113343
113668
  const typeToDist = {
113344
113669
  "claude-marketplace-install": "claude-plugin",
@@ -113392,6 +113717,7 @@ async function verifyRelease(options) {
113392
113717
  actionType: action.type,
113393
113718
  status: VERIFICATION_RESOLVED_TYPES.NOT_REQUIRED_UNCHANGED,
113394
113719
  installationContractDigest: currentDigest,
113720
+ ...bootstrapBinding ? { marketplaceIndexSha: bootstrapBinding.marketplaceIndexSha, marketplaceIndexRef: bootstrapBinding.marketplaceIndexRef } : {},
113395
113721
  reason: "\u5B89\u88C5\u5951\u7EA6\u6458\u8981\u672A\u53D8\u5316\uFF0C\u8DF3\u8FC7\u9A8C\u8BC1"
113396
113722
  });
113397
113723
  consumerVerificationReceipts.push({
@@ -113409,7 +113735,8 @@ async function verifyRelease(options) {
113409
113735
  actionId: action.id,
113410
113736
  actionType: action.type,
113411
113737
  status: VERIFICATION_RESOLVED_TYPES.NOT_REQUIRED_UNCHANGED,
113412
- installationContractDigest: currentDigest
113738
+ installationContractDigest: currentDigest,
113739
+ ...bootstrapBinding ? { marketplaceIndexSha: bootstrapBinding.marketplaceIndexSha, marketplaceIndexRef: bootstrapBinding.marketplaceIndexRef } : {}
113413
113740
  });
113414
113741
  return;
113415
113742
  }
@@ -113428,8 +113755,14 @@ async function verifyRelease(options) {
113428
113755
  { actionId: action.id }
113429
113756
  );
113430
113757
  }
113758
+ const expectedInput = bootstrapBinding ? {
113759
+ ...action.expected,
113760
+ marketplaceCommitSha: bootstrapBinding.marketplaceIndexSha,
113761
+ marketplaceIndexSha: bootstrapBinding.marketplaceIndexSha,
113762
+ ref: bootstrapBinding.marketplaceIndexRef
113763
+ } : action.expected;
113431
113764
  const verifyResult = await adapter.verify(
113432
- { ...actionInput, expected: action.expected },
113765
+ { ...actionInput, expected: expectedInput },
113433
113766
  marketplaceContext
113434
113767
  );
113435
113768
  const isHumanConfirmed = verifyResult.observation?.humanConfirmed === true;
@@ -113440,6 +113773,7 @@ async function verifyRelease(options) {
113440
113773
  status: resolvedStatus,
113441
113774
  observation: verifyResult.observation,
113442
113775
  error: verifyResult.error,
113776
+ ...bootstrapBinding ? { marketplaceIndexSha: bootstrapBinding.marketplaceIndexSha, marketplaceIndexRef: bootstrapBinding.marketplaceIndexRef } : {},
113443
113777
  ...dist?.installationContractDigest ? { installationContractDigest: dist.installationContractDigest } : {}
113444
113778
  };
113445
113779
  adapterChecks.push(check);
@@ -113459,7 +113793,8 @@ async function verifyRelease(options) {
113459
113793
  phase: "verify-marketplace",
113460
113794
  actionId: action.id,
113461
113795
  actionType: action.type,
113462
- status: check.status
113796
+ status: check.status,
113797
+ ...bootstrapBinding ? { marketplaceIndexSha: bootstrapBinding.marketplaceIndexSha, marketplaceIndexRef: bootstrapBinding.marketplaceIndexRef } : {}
113463
113798
  });
113464
113799
  if (check.status === "FAILED") {
113465
113800
  throw new ReleaseError(
@@ -113899,7 +114234,7 @@ async function verifyRelease(options) {
113899
114234
  throw err;
113900
114235
  }
113901
114236
  }
113902
- var execFile6, VERIFICATION_RESOLVED_TYPES, ADAPTER_ACTION_TYPE_MAP2, defaultNpmExecutor;
114237
+ var execFile6, VERIFICATION_RESOLVED_TYPES, ADAPTER_ACTION_TYPE_MAP2, FIRST_RELEASE_BOOTSTRAP_MODE, EXTERNAL_MARKETPLACE_SHA_RE2, defaultNpmExecutor;
113903
114238
  var init_verify = __esm({
113904
114239
  async "src/commands/verify.mjs"() {
113905
114240
  init_src2();
@@ -113918,6 +114253,7 @@ var init_verify = __esm({
113918
114253
  init_frozen_marker();
113919
114254
  await init_foundation_plugin_verification();
113920
114255
  init_public_path();
114256
+ init_digest();
113921
114257
  init_npm();
113922
114258
  init_verification_gates();
113923
114259
  await init_skill_resource_closure();
@@ -113948,6 +114284,13 @@ var init_verify = __esm({
113948
114284
  "codebuddy-marketplace-install": "codebuddy-marketplace-install"
113949
114285
  };
113950
114286
  __name(defaultClock, "defaultClock");
114287
+ FIRST_RELEASE_BOOTSTRAP_MODE = "manual-index-checkpoint";
114288
+ EXTERNAL_MARKETPLACE_SHA_RE2 = /^[0-9a-f]{40}$/;
114289
+ __name(parseMarketplaceHead, "parseMarketplaceHead");
114290
+ __name(defaultObserveMarketplaceHead, "defaultObserveMarketplaceHead");
114291
+ __name(decodeMarketplaceIndex, "decodeMarketplaceIndex");
114292
+ __name(defaultFetchMarketplaceIndex, "defaultFetchMarketplaceIndex");
114293
+ __name(verifyFirstReleaseBootstrapIndexes, "verifyFirstReleaseBootstrapIndexes");
113951
114294
  __name(isValidDigest2, "isValidDigest");
113952
114295
  __name(collectMissingManualAttestations, "collectMissingManualAttestations");
113953
114296
  __name(matchesSubset2, "matchesSubset");
@@ -121372,6 +121715,17 @@ function extractManifestSkillNames(skills) {
121372
121715
  function findSkillNames(content, manifestSkillNames) {
121373
121716
  return manifestSkillNames.filter((name) => content.includes(name));
121374
121717
  }
121718
+ function hasInstallCommand(content) {
121719
+ if (/npm\s+install|npx\s+release-skill|npm\s+i\s+release-skill/i.test(content)) {
121720
+ return true;
121721
+ }
121722
+ const codeBlocks = [
121723
+ ...[...content.matchAll(/```[^\n]*\n([\s\S]*?)```/g)].map((match) => match[1]),
121724
+ ...[...content.matchAll(/`([^`\n]+)`/g)].map((match) => match[1])
121725
+ ];
121726
+ const pluginInstall = /^(?:[$>]\s*)?(?:codex\s+plugin\s+add|claude\s+plugin\s+install)\s+\S+/i;
121727
+ return codeBlocks.some((block) => block.split("\n").some((line) => pluginInstall.test(line.trim())));
121728
+ }
121375
121729
  async function evaluateReadme({ snapshotDir, pluginManifest }) {
121376
121730
  const findings = [];
121377
121731
  const enPath = path39.join(snapshotDir, "README.md");
@@ -121412,7 +121766,7 @@ async function evaluateReadme({ snapshotDir, pluginManifest }) {
121412
121766
  safeFirstCommand: enMarkers.has("safe-first-command")
121413
121767
  };
121414
121768
  const readabilityChecks = {
121415
- hasInstall: enContent ? /npm\s+install|npx\s+release-skill|npm\s+i\s+release-skill/i.test(enContent) : false,
121769
+ hasInstall: enContent ? hasInstallCommand(enContent) : false,
121416
121770
  hasMinimalExample: enContent ? /```[\s\S]*?(release-skill|assess|prepare|help)[\s\S]*?```/i.test(enContent) : false,
121417
121771
  hasFailureDiagnosis: enContent ? /CONFIG_INVALID|PARTIAL|GATE_FAILED|if.*fail|如果.*失败|故障|troubleshoot/i.test(enContent) : false,
121418
121772
  hasNextSteps: enContent ? /next.*step|下一步|后续|see.*also|参阅|进一步/i.test(enContent) : false
@@ -121472,6 +121826,7 @@ var init_contract2 = __esm({
121472
121826
  __name(resolveSkillName, "resolveSkillName");
121473
121827
  __name(extractManifestSkillNames, "extractManifestSkillNames");
121474
121828
  __name(findSkillNames, "findSkillNames");
121829
+ __name(hasInstallCommand, "hasInstallCommand");
121475
121830
  __name(evaluateReadme, "evaluateReadme");
121476
121831
  }
121477
121832
  });
@@ -127335,6 +127690,7 @@ __export(prepare_exports, {
127335
127690
  decodeExternalMarketplaceIndex: () => decodeExternalMarketplaceIndex,
127336
127691
  observeOriginAhead: () => observeOriginAhead,
127337
127692
  parseExternalMarketplaceLsRemote: () => parseExternalMarketplaceLsRemote,
127693
+ parseFirstReleasePluginRepositoryRefs: () => parseFirstReleasePluginRepositoryRefs,
127338
127694
  prepareRelease: () => prepareRelease,
127339
127695
  readHeadCommitTimestamp: () => readHeadCommitTimestamp,
127340
127696
  readLatestFrozenPlan: () => readLatestFrozenPlan,
@@ -128204,7 +128560,7 @@ function parseExternalMarketplaceLsRemote(stdout) {
128204
128560
  if (right !== "HEAD") continue;
128205
128561
  if (left.startsWith("ref: refs/heads/")) {
128206
128562
  defaultBranch = left.slice("ref: refs/heads/".length);
128207
- } else if (EXTERNAL_MARKETPLACE_SHA_RE2.test(left)) {
128563
+ } else if (EXTERNAL_MARKETPLACE_SHA_RE3.test(left)) {
128208
128564
  sha = left;
128209
128565
  }
128210
128566
  }
@@ -128259,13 +128615,74 @@ async function defaultFetchExternalMarketplaceIndex(repo, manifestPath, ref, { g
128259
128615
  return { status: "unknown", error: error.message };
128260
128616
  }
128261
128617
  }
128618
+ function parseFirstReleasePluginRepositoryRefs(stdout, tag) {
128619
+ if (typeof stdout !== "string" || typeof tag !== "string" || tag.length === 0) {
128620
+ return { hasRefs: false, tagExists: false };
128621
+ }
128622
+ const refs = [];
128623
+ let hasRefs = false;
128624
+ for (const line of stdout.trim().split("\n").filter(Boolean)) {
128625
+ const tabIndex = line.indexOf(" ");
128626
+ if (tabIndex < 0) continue;
128627
+ const sha = line.slice(0, tabIndex);
128628
+ const ref = line.slice(tabIndex + 1);
128629
+ if (EXTERNAL_MARKETPLACE_SHA_RE3.test(sha)) {
128630
+ hasRefs = true;
128631
+ if (ref && ref !== "HEAD") refs.push(ref);
128632
+ }
128633
+ }
128634
+ const target = `refs/tags/${tag}`;
128635
+ return {
128636
+ hasRefs,
128637
+ tagExists: refs.includes(target) || refs.includes(`${target}^{}`)
128638
+ };
128639
+ }
128640
+ async function defaultObserveFirstReleasePluginRepository(repo, tag, { githubHost = "github.com" } = {}) {
128641
+ const url = `https://${githubHost}/${repo}.git`;
128642
+ let repositoryRefs;
128643
+ try {
128644
+ const { stdout } = await execFile9(
128645
+ "git",
128646
+ ["ls-remote", "--symref", url],
128647
+ { shell: false, encoding: "utf8", timeout: 3e4 }
128648
+ );
128649
+ repositoryRefs = parseFirstReleasePluginRepositoryRefs(String(stdout ?? ""), tag);
128650
+ } catch (error) {
128651
+ return { status: "unknown", error: error.message };
128652
+ }
128653
+ let releaseExists = false;
128654
+ try {
128655
+ await execFile9(
128656
+ "gh",
128657
+ ["api", `repos/${repo}/releases/tags/${tag}`],
128658
+ {
128659
+ shell: false,
128660
+ encoding: "utf8",
128661
+ timeout: 3e4,
128662
+ env: { ...process.env, GH_HOST: githubHost }
128663
+ }
128664
+ );
128665
+ releaseExists = true;
128666
+ } catch (error) {
128667
+ const diagnostic = `${error?.stderr ?? ""} ${error?.message ?? ""}`;
128668
+ if (!/\b404\b|not found/i.test(diagnostic)) {
128669
+ return { status: "unknown", error: error.message };
128670
+ }
128671
+ }
128672
+ return { status: repositoryRefs.hasRefs ? "non-empty" : "empty", tagExists: repositoryRefs.tagExists, releaseExists };
128673
+ }
128674
+ function isFirstReleaseBootstrap(dist) {
128675
+ return dist?.firstReleaseBootstrap === FIRST_RELEASE_BOOTSTRAP_MODE2;
128676
+ }
128262
128677
  async function resolveExternalMarketplaceFreezes({
128263
128678
  unitResults,
128264
128679
  resolvedVersions,
128265
128680
  offline,
128266
128681
  evidence,
128267
128682
  observeHeadFn,
128268
- fetchIndexFn
128683
+ fetchIndexFn,
128684
+ productionAssets = null,
128685
+ observePluginRepositoryFn = defaultObserveFirstReleasePluginRepository
128269
128686
  }) {
128270
128687
  const freezes = /* @__PURE__ */ new Map();
128271
128688
  for (let index = 0; index < unitResults.length; index += 1) {
@@ -128273,8 +128690,24 @@ async function resolveExternalMarketplaceFreezes({
128273
128690
  const version = resolvedVersions[index];
128274
128691
  const githubHost = unit.production?.githubHost ?? "github.com";
128275
128692
  for (const dist of unit.distributions ?? []) {
128693
+ if (isFirstReleaseBootstrap(dist) && dist.marketplaceSourceType !== "standalone-index") {
128694
+ throw new ReleaseError(
128695
+ GATE_FAILED,
128696
+ `unit "${unit.id}" ${dist.type} first-release bootstrap requires marketplaceSourceType standalone-index`,
128697
+ { unitId: unit.id, distributionType: dist.type }
128698
+ );
128699
+ }
128276
128700
  if (dist.marketplaceSourceType !== "standalone-index") continue;
128277
- if (!dist.marketplaceRepo) continue;
128701
+ if (!dist.marketplaceRepo) {
128702
+ if (isFirstReleaseBootstrap(dist)) {
128703
+ throw new ReleaseError(
128704
+ GATE_FAILED,
128705
+ `unit "${unit.id}" ${dist.type} first-release bootstrap requires marketplaceRepo`,
128706
+ { unitId: unit.id, distributionType: dist.type }
128707
+ );
128708
+ }
128709
+ continue;
128710
+ }
128278
128711
  const platform = PLATFORMS.find((p) => p.distributionType === dist.type);
128279
128712
  if (!platform) {
128280
128713
  throw new ReleaseError(
@@ -128290,8 +128723,15 @@ async function resolveExternalMarketplaceFreezes({
128290
128723
  { unitId: unit.id, marketplaceSourceType: dist.marketplaceSourceType }
128291
128724
  );
128292
128725
  }
128726
+ if (isFirstReleaseBootstrap(dist) && dist.type !== "claude-plugin" && dist.type !== "codex-plugin") {
128727
+ throw new ReleaseError(
128728
+ GATE_FAILED,
128729
+ `unit "${unit.id}" ${dist.type} first-release bootstrap is supported only for claude-plugin or codex-plugin standalone-index distributions`,
128730
+ { unitId: unit.id, distributionType: dist.type }
128731
+ );
128732
+ }
128293
128733
  const observed = await observeHeadFn(dist.marketplaceRepo, { githubHost });
128294
- if (observed.status !== "observed" || !EXTERNAL_MARKETPLACE_SHA_RE2.test(observed.sha ?? "") || !observed.defaultBranch) {
128734
+ if (observed.status !== "observed" || !EXTERNAL_MARKETPLACE_SHA_RE3.test(observed.sha ?? "") || !observed.defaultBranch) {
128295
128735
  throw new ReleaseError(
128296
128736
  GATE_FAILED,
128297
128737
  `unit "${unit.id}" could not freeze external marketplace "${dist.marketplaceRepo}" HEAD: ${observed.error ?? "unknown"}`,
@@ -128323,21 +128763,61 @@ async function resolveExternalMarketplaceFreezes({
128323
128763
  { unitId: unit.id, marketplaceRepo: dist.marketplaceRepo }
128324
128764
  );
128325
128765
  }
128326
- const pluginEntries = Array.isArray(marketplaceIndex.plugins) ? marketplaceIndex.plugins.filter((entry) => entry && entry.name === dist.plugin) : [];
128327
- if (pluginEntries.length !== 1) {
128766
+ if (!Array.isArray(marketplaceIndex.plugins)) {
128767
+ throw new ReleaseError(
128768
+ GATE_FAILED,
128769
+ `unit "${unit.id}" ${dist.type} external marketplace index plugins must be an array`,
128770
+ { unitId: unit.id, marketplaceRepo: dist.marketplaceRepo }
128771
+ );
128772
+ }
128773
+ const pluginEntries = marketplaceIndex.plugins.filter((entry) => entry && entry.name === dist.plugin);
128774
+ if (isFirstReleaseBootstrap(dist) && pluginEntries.length > 0) {
128775
+ throw new ReleaseError(
128776
+ GATE_FAILED,
128777
+ `unit "${unit.id}" first-release bootstrap requires the marketplace entry to be absent, found ${pluginEntries.length} matching entries`,
128778
+ { unitId: unit.id, marketplaceRepo: dist.marketplaceRepo }
128779
+ );
128780
+ }
128781
+ if (pluginEntries.length !== 1 && !isFirstReleaseBootstrap(dist)) {
128328
128782
  throw new ReleaseError(
128329
128783
  GATE_FAILED,
128330
128784
  `unit "${unit.id}" external marketplace index must contain exactly one plugin entry named "${dist.plugin}", found ${pluginEntries.length}`,
128331
128785
  { unitId: unit.id, marketplaceRepo: dist.marketplaceRepo }
128332
128786
  );
128333
128787
  }
128334
- if (platform.marketplaceEntryCarriesVersion && pluginEntries[0].version !== version) {
128788
+ let selectedEntry = pluginEntries[0];
128789
+ if (!isFirstReleaseBootstrap(dist) && platform.marketplaceEntryCarriesVersion && selectedEntry.version !== version) {
128335
128790
  throw new ReleaseError(
128336
128791
  GATE_FAILED,
128337
128792
  `unit "${unit.id}" external marketplace index entry version "${pluginEntries[0].version}" does not match target version "${version}"`,
128338
128793
  { unitId: unit.id, marketplaceRepo: dist.marketplaceRepo }
128339
128794
  );
128340
128795
  }
128796
+ if (isFirstReleaseBootstrap(dist)) {
128797
+ const asset = productionAssets?.[index];
128798
+ if (!asset || !EXTERNAL_MARKETPLACE_SHA_RE3.test(asset.commit ?? "") || !asset.tag) {
128799
+ throw new ReleaseError(
128800
+ GATE_FAILED,
128801
+ `unit "${unit.id}" first-release bootstrap requires a frozen plugin tag and commit`,
128802
+ { unitId: unit.id, distributionType: dist.type }
128803
+ );
128804
+ }
128805
+ const pluginState = await observePluginRepositoryFn(unit.publicRepo, asset.tag, { githubHost });
128806
+ if (pluginState?.status !== "empty" || pluginState.tagExists !== false || pluginState.releaseExists !== false) {
128807
+ throw new ReleaseError(
128808
+ GATE_FAILED,
128809
+ `unit "${unit.id}" first-release bootstrap requires an empty plugin repository without the target tag or release`,
128810
+ { unitId: unit.id, publicRepo: unit.publicRepo, tag: asset.tag, observed: pluginState }
128811
+ );
128812
+ }
128813
+ selectedEntry = buildExpectedStandaloneIndexInstallIdentity(platform, {
128814
+ name: dist.plugin,
128815
+ repo: unit.publicRepo,
128816
+ tag: asset.tag,
128817
+ sha: asset.commit,
128818
+ version
128819
+ });
128820
+ }
128341
128821
  const marketplaceRef = platform.marketplaceRefForm === "name" ? observed.defaultBranch : sha;
128342
128822
  freezes.set(`${unit.id} ${dist.type}`, {
128343
128823
  // 向后兼容字段(buildExternalActions 使用 repo / ref / marketplace)
@@ -128350,7 +128830,8 @@ async function resolveExternalMarketplaceFreezes({
128350
128830
  marketplaceRef,
128351
128831
  marketplaceIndexPath: manifestPath,
128352
128832
  marketplaceName: marketplaceIndex.name,
128353
- selectedEntry: pluginEntries[0]
128833
+ selectedEntry,
128834
+ ...isFirstReleaseBootstrap(dist) ? { firstReleaseBootstrap: FIRST_RELEASE_BOOTSTRAP_MODE2, marketplaceIndexSha: null } : {}
128354
128835
  });
128355
128836
  await evidence.append({
128356
128837
  phase: "external-marketplace-freeze",
@@ -128362,7 +128843,8 @@ async function resolveExternalMarketplaceFreezes({
128362
128843
  marketplaceRef,
128363
128844
  marketplaceIndexPath: manifestPath,
128364
128845
  marketplaceName: marketplaceIndex.name,
128365
- selectedEntry: pluginEntries[0],
128846
+ selectedEntry,
128847
+ ...isFirstReleaseBootstrap(dist) ? { firstReleaseBootstrap: FIRST_RELEASE_BOOTSTRAP_MODE2, marketplaceIndexSha: null } : {},
128366
128848
  defaultBranch: observed.defaultBranch
128367
128849
  });
128368
128850
  }
@@ -128697,7 +129179,11 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets, e
128697
129179
  ...externalMarketplace && freeze ? {
128698
129180
  marketplaceIndexPath: freeze.marketplaceIndexPath,
128699
129181
  marketplaceName: freeze.marketplaceName,
128700
- selectedEntry: freeze.selectedEntry
129182
+ selectedEntry: freeze.selectedEntry,
129183
+ ...freeze.firstReleaseBootstrap ? {
129184
+ firstReleaseBootstrap: freeze.firstReleaseBootstrap,
129185
+ marketplaceIndexSha: null
129186
+ } : {}
128701
129187
  } : {}
128702
129188
  },
128703
129189
  expected: {
@@ -128712,7 +129198,11 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets, e
128712
129198
  entrySkill: dist.entrySkill,
128713
129199
  entrySkillFound: true,
128714
129200
  manifestDigest: asset.manifestDigest,
128715
- ...externalMarketplace ? { marketplaceLocation: "external", marketplaceCommitSha: freeze.marketplaceCommitSha } : {}
129201
+ ...externalMarketplace ? { marketplaceLocation: "external", marketplaceCommitSha: freeze.marketplaceCommitSha } : {},
129202
+ ...externalMarketplace && freeze?.firstReleaseBootstrap ? {
129203
+ firstReleaseBootstrap: freeze.firstReleaseBootstrap,
129204
+ marketplaceIndexSha: null
129205
+ } : {}
128716
129206
  },
128717
129207
  status: "PENDING"
128718
129208
  });
@@ -130340,13 +130830,26 @@ async function prepareRelease(options) {
130340
130830
  skipSkillResourceClosure,
130341
130831
  workflow
130342
130832
  });
130833
+ for (const { unit } of unitResults) {
130834
+ for (const dist of unit.distributions ?? []) {
130835
+ if (isFirstReleaseBootstrap(dist) && (!production || offline)) {
130836
+ throw new ReleaseError(
130837
+ GATE_FAILED,
130838
+ `unit "${unit.id}" ${dist.type} first-release bootstrap requires online production prepare`,
130839
+ { unitId: unit.id, distributionType: dist.type }
130840
+ );
130841
+ }
130842
+ }
130843
+ }
130343
130844
  const externalMarketplaceFreezes = production ? await resolveExternalMarketplaceFreezes({
130344
130845
  unitResults,
130345
130846
  resolvedVersions,
130346
130847
  offline,
130347
130848
  evidence,
130348
130849
  observeHeadFn: options.observeExternalMarketplaceHeadFn ?? defaultObserveExternalMarketplaceHead,
130349
- fetchIndexFn: options.fetchExternalMarketplaceIndexFn ?? defaultFetchExternalMarketplaceIndex
130850
+ fetchIndexFn: options.fetchExternalMarketplaceIndexFn ?? defaultFetchExternalMarketplaceIndex,
130851
+ productionAssets,
130852
+ observePluginRepositoryFn: options.observeFirstReleasePluginRepositoryFn ?? defaultObserveFirstReleasePluginRepository
130350
130853
  }) : /* @__PURE__ */ new Map();
130351
130854
  const units = await Promise.all(unitResults.map(async ({ unit, manifest }, idx) => {
130352
130855
  const unitVersion = resolvedVersions[idx];
@@ -130426,6 +130929,10 @@ async function prepareRelease(options) {
130426
130929
  frozenDist.marketplaceIndexPath = freeze.marketplaceIndexPath;
130427
130930
  frozenDist.marketplaceName = freeze.marketplaceName;
130428
130931
  frozenDist.selectedEntry = freeze.selectedEntry;
130932
+ if (freeze.firstReleaseBootstrap) {
130933
+ frozenDist.firstReleaseBootstrap = freeze.firstReleaseBootstrap;
130934
+ frozenDist.marketplaceIndexSha = null;
130935
+ }
130429
130936
  }
130430
130937
  }
130431
130938
  return frozenDist;
@@ -130795,7 +131302,7 @@ async function prepareRelease(options) {
130795
131302
  await lock.release();
130796
131303
  }
130797
131304
  }
130798
- var execFile9, CONSUMER_INSTALL_RECIPE_VERSION2, HOOK_EXECUTION_ORDER, EXTERNAL_MARKETPLACE_SHA_RE2;
131305
+ var execFile9, CONSUMER_INSTALL_RECIPE_VERSION2, FIRST_RELEASE_BOOTSTRAP_MODE2, HOOK_EXECUTION_ORDER, EXTERNAL_MARKETPLACE_SHA_RE3;
130799
131306
  var init_prepare = __esm({
130800
131307
  async "src/commands/prepare.mjs"() {
130801
131308
  init_src2();
@@ -130836,6 +131343,7 @@ var init_prepare = __esm({
130836
131343
  init_bounded_output();
130837
131344
  execFile9 = promisify12(execFileCb12);
130838
131345
  CONSUMER_INSTALL_RECIPE_VERSION2 = "consumer-install-v1";
131346
+ FIRST_RELEASE_BOOTSTRAP_MODE2 = "manual-index-checkpoint";
130839
131347
  __name(resolveUnitVersion, "resolveUnitVersion");
130840
131348
  __name(resolveAllUnitVersions, "resolveAllUnitVersions");
130841
131349
  HOOK_EXECUTION_ORDER = Object.freeze(["lint", "docs", "build", "test", "typecheck"]);
@@ -130854,11 +131362,14 @@ var init_prepare = __esm({
130854
131362
  __name(enumerateFrozenTagPaths, "enumerateFrozenTagPaths");
130855
131363
  __name(collectPostPublishCommandCandidates, "collectPostPublishCommandCandidates");
130856
131364
  __name(assertPrivateExecutionDeclarations, "assertPrivateExecutionDeclarations");
130857
- EXTERNAL_MARKETPLACE_SHA_RE2 = /^[0-9a-f]{40}$/;
131365
+ EXTERNAL_MARKETPLACE_SHA_RE3 = /^[0-9a-f]{40}$/;
130858
131366
  __name(parseExternalMarketplaceLsRemote, "parseExternalMarketplaceLsRemote");
130859
131367
  __name(decodeExternalMarketplaceIndex, "decodeExternalMarketplaceIndex");
130860
131368
  __name(defaultObserveExternalMarketplaceHead, "defaultObserveExternalMarketplaceHead");
130861
131369
  __name(defaultFetchExternalMarketplaceIndex, "defaultFetchExternalMarketplaceIndex");
131370
+ __name(parseFirstReleasePluginRepositoryRefs, "parseFirstReleasePluginRepositoryRefs");
131371
+ __name(defaultObserveFirstReleasePluginRepository, "defaultObserveFirstReleasePluginRepository");
131372
+ __name(isFirstReleaseBootstrap, "isFirstReleaseBootstrap");
130862
131373
  __name(resolveExternalMarketplaceFreezes, "resolveExternalMarketplaceFreezes");
130863
131374
  __name(buildExternalActions, "buildExternalActions");
130864
131375
  __name(computeSourceAuthorityClosure, "computeSourceAuthorityClosure");
@@ -130948,12 +131459,13 @@ var init_hooks2 = __esm({
130948
131459
  // src/commands/post-release-local.mjs
130949
131460
  var post_release_local_exports = {};
130950
131461
  __export(post_release_local_exports, {
131462
+ assertLocalFinishRun: () => assertLocalFinishRun,
130951
131463
  assertVerifiedReleaseRun: () => assertVerifiedReleaseRun,
130952
131464
  derivePostReleaseChecklist: () => derivePostReleaseChecklist,
130953
131465
  unavailablePostReleaseChecklist: () => unavailablePostReleaseChecklist,
130954
131466
  updateLocalHostPlugins: () => updateLocalHostPlugins
130955
131467
  });
130956
- import { access as access2 } from "node:fs/promises";
131468
+ import { access as access2, readFile as readFile36 } from "node:fs/promises";
130957
131469
  import { homedir } from "node:os";
130958
131470
  import { join as join27, relative as relative27 } from "node:path";
130959
131471
  function attachFoundationFailure(error, { envelope, stdout }) {
@@ -131047,13 +131559,35 @@ function assertExecutableTarget(target) {
131047
131559
  throw new Error(`local host update requires the frozen public commit for unit ${target.unitId}`);
131048
131560
  }
131049
131561
  }
131050
- function derivePostReleaseChecklist(plan) {
131562
+ function buildShipNextStep({ root, statePath, unitIds }) {
131563
+ const argv = ["release-skill", "ship"];
131564
+ if (typeof root === "string" && root.length > 0) argv.push("--root", root);
131565
+ if (typeof statePath === "string" && statePath.length > 0) argv.push("--state", statePath);
131566
+ if (Array.isArray(unitIds) && unitIds.length > 0) {
131567
+ for (const unitId of unitIds) argv.push("--unit", unitId);
131568
+ }
131569
+ return {
131570
+ code: "COMPLETE_POST_VERIFY",
131571
+ message: "Complete the postVerify phase with ship before running post-release.",
131572
+ argv
131573
+ };
131574
+ }
131575
+ function derivePostReleaseChecklist(plan, {
131576
+ runPath,
131577
+ root,
131578
+ statePath,
131579
+ unitIds,
131580
+ postVerifyComplete = false
131581
+ } = {}) {
131051
131582
  if (!plan || typeof plan !== "object" || typeof plan.digest !== "string") {
131052
131583
  throw new Error("a frozen release plan with digest is required");
131053
131584
  }
131054
131585
  const units = plan.units ?? [];
131055
131586
  const uncovered = units.filter((unit) => !BRANCH_ACTION_INCLUDED.has(unit.productionConfig?.branchStrategy));
131056
131587
  const targets = pluginTargets(plan);
131588
+ const hasPendingPostVerify = postVerifyHooks(plan).length > 0 && !postVerifyComplete && targets.length > 0;
131589
+ const hasStatePath = typeof statePath === "string" && statePath.length > 0;
131590
+ const selectedUnitIds = Array.isArray(unitIds) ? unitIds : void 0;
131057
131591
  return {
131058
131592
  command: "post-release",
131059
131593
  status: "AWAITING_USER_DECISION",
@@ -131069,13 +131603,143 @@ function derivePostReleaseChecklist(plan) {
131069
131603
  }))
131070
131604
  },
131071
131605
  localHostUpdate: {
131072
- promptRequired: targets.length > 0,
131073
- available: targets.length > 0,
131606
+ promptRequired: targets.length > 0 && !hasPendingPostVerify,
131607
+ available: targets.length > 0 && !hasPendingPostVerify,
131608
+ ...!hasPendingPostVerify && runPath ? { runPath } : {},
131609
+ ...hasPendingPostVerify ? {
131610
+ ...hasStatePath ? { nextSteps: [buildShipNextStep({ root, statePath, unitIds: selectedUnitIds })] } : {}
131611
+ } : {},
131074
131612
  hosts: [...new Set(targets.map((target) => target.host))].sort(),
131075
131613
  targets
131076
131614
  }
131077
131615
  };
131078
131616
  }
131617
+ function postVerifyHooks(plan) {
131618
+ if (plan?.planVersion === void 0) return [];
131619
+ return normalizePostPublishView(plan).flatMap((declaration) => (declaration.hooks ?? []).filter((hook) => hook.phase === "postVerify").map((hook) => ({
131620
+ actionId: postPublishActionId({ planVersion: plan.planVersion, unitId: declaration.unitId, localId: hook.id }),
131621
+ hook,
131622
+ unitId: declaration.unitId
131623
+ })));
131624
+ }
131625
+ function localFinishEvidenceError(message, { cause, root, statePath, unitIds } = {}) {
131626
+ const error = new Error(`local host update evidence is not ready: ${message}; next step: obtain approval if required, then complete the postVerify phase with ship before rerunning post-release`);
131627
+ error.code = "LOCAL_FINISH_EVIDENCE_NOT_READY";
131628
+ const hasStatePath = typeof statePath === "string" && statePath.length > 0;
131629
+ error.details = {
131630
+ cause: {
131631
+ code: cause?.code ?? "LOCAL_FINISH_EVIDENCE_NOT_READY",
131632
+ message: cause?.message ?? message
131633
+ },
131634
+ ...hasStatePath ? {
131635
+ nextSteps: [buildShipNextStep({ root, statePath, unitIds })]
131636
+ } : {}
131637
+ };
131638
+ return error;
131639
+ }
131640
+ async function updateLocalHostPlugins({
131641
+ plan: _suppliedPlan,
131642
+ planPath,
131643
+ runPath,
131644
+ runRecord: _suppliedRunRecord,
131645
+ production: _suppliedProduction,
131646
+ root = process.cwd(),
131647
+ statePath,
131648
+ unitIds,
131649
+ ...options
131650
+ } = {}) {
131651
+ if (!planPath || !runPath) {
131652
+ throw localFinishEvidenceError(
131653
+ "planPath and runPath are required before local host updates",
131654
+ { root, statePath, unitIds }
131655
+ );
131656
+ }
131657
+ let plan;
131658
+ let runRecord;
131659
+ try {
131660
+ plan = JSON.parse(await readFile36(planPath, "utf8"));
131661
+ runRecord = await loadRun(runPath, {
131662
+ requireDigest: true,
131663
+ authorityPlanPath: planPath
131664
+ });
131665
+ } catch (cause) {
131666
+ throw localFinishEvidenceError(cause.message, { cause, root, statePath, unitIds });
131667
+ }
131668
+ await assertLocalFinishRun({
131669
+ plan,
131670
+ planPath,
131671
+ runPath,
131672
+ runRecord,
131673
+ production: Boolean(plan.production),
131674
+ root,
131675
+ statePath,
131676
+ unitIds
131677
+ });
131678
+ return updateLocalHostPluginsInternal({
131679
+ plan,
131680
+ root,
131681
+ ...options
131682
+ });
131683
+ }
131684
+ async function assertLocalFinishRun({
131685
+ plan,
131686
+ planPath,
131687
+ runPath,
131688
+ runRecord,
131689
+ production = false,
131690
+ root,
131691
+ statePath,
131692
+ unitIds
131693
+ } = {}) {
131694
+ const hooks = postVerifyHooks(plan);
131695
+ if (hooks.length === 0) {
131696
+ try {
131697
+ await validateRunLineage(runRecord, { plan, planPath, runPath, production });
131698
+ assertVerifiedReleaseRun(plan, runRecord);
131699
+ } catch (cause) {
131700
+ throw localFinishEvidenceError(cause.message, { cause, root, statePath, unitIds, plan });
131701
+ }
131702
+ return { runPath, phase: "verify" };
131703
+ }
131704
+ if (runRecord?.command !== "postverify" || runRecord?.status !== "DISTRIBUTED") {
131705
+ throw localFinishEvidenceError("the plan declares postVerify hooks, so the supplied verify run cannot authorize local-finish", { root, statePath, unitIds, plan });
131706
+ }
131707
+ if (runRecord.planDigest !== plan?.digest) {
131708
+ throw localFinishEvidenceError("postVerify run is not bound to the frozen plan", { root, statePath, unitIds, plan });
131709
+ }
131710
+ if (typeof runRecord.sourceRunPath !== "string" || typeof runRecord.sourceRunId !== "string" || typeof runRecord.sourceRunDigest !== "string") {
131711
+ throw localFinishEvidenceError("completed postVerify run has incomplete verify-run lineage", { root, statePath, unitIds, plan });
131712
+ }
131713
+ const checkpoints = Array.isArray(runRecord.checkpoints) ? runRecord.checkpoints : null;
131714
+ const expectedIds = hooks.map(({ actionId }) => actionId);
131715
+ const actualIds = checkpoints?.map((checkpoint) => checkpoint?.actionId) ?? [];
131716
+ if (!checkpoints || actualIds.length !== expectedIds.length || new Set(actualIds).size !== actualIds.length || actualIds.some((id) => !expectedIds.includes(id)) || expectedIds.some((id) => !actualIds.includes(id)) || checkpoints.some((checkpoint) => checkpoint.actionType !== "postpublish-hook" || !["succeeded", "NO_CHANGE"].includes(checkpoint.status))) {
131717
+ throw localFinishEvidenceError("postVerify checkpoints must match each declared hook exactly and be succeeded or NO_CHANGE", { root, statePath, unitIds, plan });
131718
+ }
131719
+ let sourceRun;
131720
+ try {
131721
+ sourceRun = await loadRun(runRecord.sourceRunPath, {
131722
+ requireDigest: true,
131723
+ authorityPlanPath: planPath
131724
+ });
131725
+ } catch (cause) {
131726
+ throw localFinishEvidenceError(cause.message, { cause, root, statePath, unitIds, plan });
131727
+ }
131728
+ if (sourceRun.command !== "verify" || sourceRun.status !== "VERIFIED" || sourceRun.runId !== runRecord.sourceRunId || sourceRun.runDigest !== runRecord.sourceRunDigest || sourceRun.planDigest !== plan.digest) {
131729
+ throw localFinishEvidenceError("postVerify lineage does not point to the same-plan VERIFIED run", { root, statePath, unitIds, plan });
131730
+ }
131731
+ try {
131732
+ await validateRunLineage(sourceRun, {
131733
+ plan,
131734
+ planPath,
131735
+ runPath: runRecord.sourceRunPath,
131736
+ production
131737
+ });
131738
+ } catch (cause) {
131739
+ throw localFinishEvidenceError(cause.message, { cause, root, statePath, unitIds, plan });
131740
+ }
131741
+ return { runPath, sourceRunPath: runRecord.sourceRunPath, phase: "postverify" };
131742
+ }
131079
131743
  function unavailablePostReleaseChecklist(plan, error) {
131080
131744
  return {
131081
131745
  command: "post-release",
@@ -131101,7 +131765,11 @@ function hostEnvironment(host, { kimiHome } = {}) {
131101
131765
  env.HOME ??= homedir();
131102
131766
  env.PATH ??= "/usr/bin:/bin";
131103
131767
  env.GIT_TERMINAL_PROMPT = "0";
131104
- if (host === "workbuddy") env.CODEBUDDY_CONFIG_DIR = join27(env.HOME, ".workbuddy");
131768
+ if (host === "codebuddy") env.CODEBUDDY_CONFIG_DIR = join27(env.HOME, ".codebuddy");
131769
+ if (host === "workbuddy") {
131770
+ env.CODEBUDDY_CONFIG_DIR = join27(env.HOME, ".workbuddy");
131771
+ env.WORKBUDDY_CONFIG_DIR = join27(env.HOME, ".workbuddy");
131772
+ }
131105
131773
  if (host === "kimi" && kimiHome) env.KIMI_CONFIG_DIR = kimiHome;
131106
131774
  return env;
131107
131775
  }
@@ -131152,12 +131820,17 @@ async function commandAvailable(command2, host, run6) {
131152
131820
  }
131153
131821
  }
131154
131822
  async function defaultDetect(host, run6 = defaultRun) {
131155
- if (host === "codebuddy" || host === "workbuddy") {
131156
- for (const command2 of ["codebuddy", "cbc", CODEBUDDY_MACOS_PATH]) {
131823
+ if (host === "codebuddy") {
131824
+ for (const command2 of ["codebuddy", "cbc"]) {
131157
131825
  if (await commandAvailable(command2, host, run6)) return { available: true, command: command2 };
131158
131826
  }
131159
131827
  return { available: false, reason: "CodeBuddy/WorkBuddy CLI not found" };
131160
131828
  }
131829
+ if (host === "workbuddy") {
131830
+ if (process.platform !== "darwin") return { available: false, status: "SKIPPED_UNSUPPORTED_PLATFORM", reason: "WorkBuddy local update is supported only on macOS" };
131831
+ if (await commandAvailable(CODEBUDDY_MACOS_PATH, host, run6)) return { available: true, command: CODEBUDDY_MACOS_PATH };
131832
+ return { available: false, reason: "WorkBuddy embedded CLI not found" };
131833
+ }
131161
131834
  if (host === "kimi") {
131162
131835
  if (!await commandAvailable("kimi", host, run6)) return { available: false, reason: "kimi CLI not found" };
131163
131836
  try {
@@ -131690,7 +132363,7 @@ function failedHostResult(target, error) {
131690
132363
  ...publicDetails !== void 0 ? { details: publicDetails } : {}
131691
132364
  };
131692
132365
  }
131693
- async function updateLocalHostPlugins({
132366
+ async function updateLocalHostPluginsInternal({
131694
132367
  plan,
131695
132368
  root = process.cwd(),
131696
132369
  confirmPlanDigest,
@@ -131701,7 +132374,7 @@ async function updateLocalHostPlugins({
131701
132374
  verifyInstalledPayload = verifyInstalledMarketplacePayload
131702
132375
  } = {}) {
131703
132376
  const effectiveKimiHome = kimiHome ?? process.env.KIMI_CONFIG_DIR ?? join27(homedir(), ".kimi-code");
131704
- const checklist = derivePostReleaseChecklist(plan);
132377
+ const checklist = derivePostReleaseChecklist(plan, { postVerifyComplete: true });
131705
132378
  if (confirmPlanDigest !== plan.digest) {
131706
132379
  throw new Error("plan digest confirmation does not match the frozen release plan");
131707
132380
  }
@@ -131718,7 +132391,7 @@ async function updateLocalHostPlugins({
131718
132391
  results.push({
131719
132392
  host: target.host,
131720
132393
  unitId: target.unitId,
131721
- status: "SKIPPED_NOT_INSTALLED",
132394
+ status: detected?.status ?? "SKIPPED_NOT_INSTALLED",
131722
132395
  reason: detected?.reason ?? "host unavailable"
131723
132396
  });
131724
132397
  continue;
@@ -131753,6 +132426,8 @@ var init_post_release_local = __esm({
131753
132426
  await init_registry2();
131754
132427
  init_codebuddy();
131755
132428
  await init_plugin_marketplace();
132429
+ init_postpublish();
132430
+ await init_run();
131756
132431
  HOSTS_BY_ACTION = Object.freeze({
131757
132432
  "claude-marketplace-install": ["claude"],
131758
132433
  "codex-marketplace-install": ["codex"],
@@ -131787,7 +132462,9 @@ var init_post_release_local = __esm({
131787
132462
  "NODE_EXTRA_CA_CERTS",
131788
132463
  "CLAUDE_CONFIG_DIR",
131789
132464
  "CODEX_HOME",
131790
- "KIMI_CONFIG_DIR"
132465
+ "KIMI_CONFIG_DIR",
132466
+ "CODEBUDDY_CONFIG_DIR",
132467
+ "WORKBUDDY_CONFIG_DIR"
131791
132468
  ]);
131792
132469
  CODEBUDDY_PLUGIN_LIST_ARGS = Object.freeze(["plugin", "list", "--json"]);
131793
132470
  CODEBUDDY_MARKETPLACE_LIST_ARGS = Object.freeze(["plugin", "marketplace", "list"]);
@@ -131795,7 +132472,12 @@ var init_post_release_local = __esm({
131795
132472
  __name(actionTarget, "actionTarget");
131796
132473
  __name(pluginTargets, "pluginTargets");
131797
132474
  __name(assertExecutableTarget, "assertExecutableTarget");
132475
+ __name(buildShipNextStep, "buildShipNextStep");
131798
132476
  __name(derivePostReleaseChecklist, "derivePostReleaseChecklist");
132477
+ __name(postVerifyHooks, "postVerifyHooks");
132478
+ __name(localFinishEvidenceError, "localFinishEvidenceError");
132479
+ __name(updateLocalHostPlugins, "updateLocalHostPlugins");
132480
+ __name(assertLocalFinishRun, "assertLocalFinishRun");
131799
132481
  __name(unavailablePostReleaseChecklist, "unavailablePostReleaseChecklist");
131800
132482
  __name(assertVerifiedReleaseRun, "assertVerifiedReleaseRun");
131801
132483
  __name(hostEnvironment, "hostEnvironment");
@@ -131825,7 +132507,7 @@ var init_post_release_local = __esm({
131825
132507
  __name(runKimiUpdate, "runKimiUpdate");
131826
132508
  __name(aggregateStatus, "aggregateStatus");
131827
132509
  __name(failedHostResult, "failedHostResult");
131828
- __name(updateLocalHostPlugins, "updateLocalHostPlugins");
132510
+ __name(updateLocalHostPluginsInternal, "updateLocalHostPluginsInternal");
131829
132511
  }
131830
132512
  });
131831
132513
 
@@ -131835,7 +132517,7 @@ __export(approve_exports, {
131835
132517
  approvePlan: () => approvePlan,
131836
132518
  approvePostPublishHook: () => approvePostPublishHook
131837
132519
  });
131838
- import { readFile as readFile36, writeFile as writeFile8 } from "node:fs/promises";
132520
+ import { readFile as readFile37, writeFile as writeFile8 } from "node:fs/promises";
131839
132521
  import { resolve as resolve33, dirname as dirname17, basename as basename13 } from "node:path";
131840
132522
  function defaultClock2() {
131841
132523
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -131859,7 +132541,7 @@ async function approvePlan(options) {
131859
132541
  }
131860
132542
  let planRaw;
131861
132543
  try {
131862
- planRaw = await readFile36(planPath, "utf8");
132544
+ planRaw = await readFile37(planPath, "utf8");
131863
132545
  } catch (err) {
131864
132546
  throw new ReleaseError(
131865
132547
  GATE_FAILED,
@@ -131987,7 +132669,7 @@ async function approvePlan(options) {
131987
132669
  await writeFile8(immutableApprovalPath, json, { encoding: "utf8", flag: "wx", mode: 384 });
131988
132670
  } catch (error) {
131989
132671
  if (error.code !== "EEXIST") throw error;
131990
- const existing = await readFile36(immutableApprovalPath, "utf8");
132672
+ const existing = await readFile37(immutableApprovalPath, "utf8");
131991
132673
  if (existing !== json) {
131992
132674
  throw new ReleaseError(
131993
132675
  GATE_FAILED,
@@ -132038,7 +132720,7 @@ async function approvePostPublishHook(options) {
132038
132720
  }
132039
132721
  let planRaw;
132040
132722
  try {
132041
- planRaw = await readFile36(planPath, "utf8");
132723
+ planRaw = await readFile37(planPath, "utf8");
132042
132724
  } catch (err) {
132043
132725
  throw new ReleaseError(GATE_FAILED, `cannot read release plan: ${err.message}`, { planPath, cause: err.code });
132044
132726
  }
@@ -132098,7 +132780,7 @@ async function approvePostPublishHook(options) {
132098
132780
  await writeFile8(immutableApprovalPath, json, { encoding: "utf8", flag: "wx", mode: 384 });
132099
132781
  } catch (error) {
132100
132782
  if (error.code !== "EEXIST") throw error;
132101
- const existing = await readFile36(immutableApprovalPath, "utf8");
132783
+ const existing = await readFile37(immutableApprovalPath, "utf8");
132102
132784
  if (existing !== json) {
132103
132785
  throw new ReleaseError(
132104
132786
  GATE_FAILED,
@@ -132249,7 +132931,7 @@ __export(publish_exports, {
132249
132931
  classifyPreObservation: () => classifyPreObservation,
132250
132932
  publishRelease: () => publishRelease
132251
132933
  });
132252
- import { readFile as readFile37, mkdir as mkdir22 } from "node:fs/promises";
132934
+ import { readFile as readFile38, mkdir as mkdir22 } from "node:fs/promises";
132253
132935
  import { isAbsolute as isAbsolute23, join as join28, relative as relative28 } from "node:path";
132254
132936
  function assertInsideAssetRoot(assetRoot, candidate, label) {
132255
132937
  const rel = relative28(assetRoot, candidate);
@@ -132482,7 +133164,7 @@ async function publishRelease(options) {
132482
133164
  const captureBaselineActual = typeof captureBaselineFn === "function" ? captureBaselineFn : captureBaseline;
132483
133165
  let planRaw;
132484
133166
  try {
132485
- planRaw = await readFile37(planPath, "utf8");
133167
+ planRaw = await readFile38(planPath, "utf8");
132486
133168
  } catch (err) {
132487
133169
  throw new ReleaseError(GATE_FAILED, `cannot read release plan: ${err.message}`, { planPath, cause: err.code });
132488
133170
  }
@@ -132673,7 +133355,7 @@ async function publishRelease(options) {
132673
133355
  await evidence.append({ phase: "safety-gate", gate: "approval-load", status: "started" });
132674
133356
  let approvalRaw;
132675
133357
  try {
132676
- approvalRaw = await readFile37(approvalPath, "utf8");
133358
+ approvalRaw = await readFile38(approvalPath, "utf8");
132677
133359
  } catch (err) {
132678
133360
  throw new ReleaseError(
132679
133361
  GATE_FAILED,
@@ -133310,7 +133992,7 @@ var reconcile_exports = {};
133310
133992
  __export(reconcile_exports, {
133311
133993
  reconcileRelease: () => reconcileRelease
133312
133994
  });
133313
- import { readFile as readFile38, mkdir as mkdir23 } from "node:fs/promises";
133995
+ import { readFile as readFile39, mkdir as mkdir23 } from "node:fs/promises";
133314
133996
  import { join as join29 } from "node:path";
133315
133997
  function defaultClock5() {
133316
133998
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -133339,7 +134021,7 @@ async function reconcileRelease(options) {
133339
134021
  }
133340
134022
  let planRaw;
133341
134023
  try {
133342
- planRaw = await readFile38(planPath, "utf8");
134024
+ planRaw = await readFile39(planPath, "utf8");
133343
134025
  } catch (err) {
133344
134026
  throw new ReleaseError(GATE_FAILED, `cannot read release plan: ${err.message}`, { planPath, cause: err.code });
133345
134027
  }
@@ -133462,7 +134144,7 @@ async function reconcileRelease(options) {
133462
134144
  { sourceRunId: sourceRun.runId }
133463
134145
  );
133464
134146
  }
133465
- const sourceApprovalRaw = await readFile38(consumedApprovalPath, "utf8").catch((error) => {
134147
+ const sourceApprovalRaw = await readFile39(consumedApprovalPath, "utf8").catch((error) => {
133466
134148
  throw new ReleaseError(GATE_FAILED, "source run approval authority is unavailable", {
133467
134149
  sourceRunId: sourceRun.runId,
133468
134150
  cause: error.code
@@ -133624,7 +134306,7 @@ async function reconcileRelease(options) {
133624
134306
  if (approvalPath) {
133625
134307
  let approvalRaw;
133626
134308
  try {
133627
- approvalRaw = await readFile38(approvalPath, "utf8");
134309
+ approvalRaw = await readFile39(approvalPath, "utf8");
133628
134310
  } catch (err) {
133629
134311
  throw new ReleaseError(
133630
134312
  GATE_FAILED,
@@ -135090,7 +135772,7 @@ var init_notify_handoff = __esm({
135090
135772
  // src/core/proposal-inbox.mjs
135091
135773
  import { execFile as execFileCb14 } from "node:child_process";
135092
135774
  import { promisify as promisify14 } from "node:util";
135093
- import { mkdir as mkdir26, mkdtemp as mkdtemp8, readFile as readFile39, rm as rm12, writeFile as writeFile11 } from "node:fs/promises";
135775
+ import { mkdir as mkdir26, mkdtemp as mkdtemp8, readFile as readFile40, rm as rm12, writeFile as writeFile11 } from "node:fs/promises";
135094
135776
  import { tmpdir as tmpdir6 } from "node:os";
135095
135777
  import { dirname as dirname19, join as join32 } from "node:path";
135096
135778
  function defaultExec2(command2, args2, options = {}) {
@@ -135270,7 +135952,7 @@ async function observeProposalInboxGitPush(params) {
135270
135952
  const targetPath2 = join32(cloneDir, proposalPath);
135271
135953
  let existing = null;
135272
135954
  try {
135273
- existing = await readFile39(targetPath2, "utf8");
135955
+ existing = await readFile40(targetPath2, "utf8");
135274
135956
  } catch {
135275
135957
  existing = null;
135276
135958
  }
@@ -135431,7 +136113,7 @@ async function deliverProposalLocalFile(params) {
135431
136113
  const targetPath2 = join32(workspaceRealpath, proposalPath);
135432
136114
  let existing = null;
135433
136115
  try {
135434
- existing = await readFile39(targetPath2, "utf8");
136116
+ existing = await readFile40(targetPath2, "utf8");
135435
136117
  } catch {
135436
136118
  existing = null;
135437
136119
  }
@@ -135928,7 +136610,7 @@ var init_preset_gitwrite = __esm({
135928
136610
  });
135929
136611
 
135930
136612
  // src/core/marketplace-registry-entry.mjs
135931
- import { readFile as readFile40 } from "node:fs/promises";
136613
+ import { readFile as readFile41 } from "node:fs/promises";
135932
136614
  import { join as join34 } from "node:path";
135933
136615
  function updateRegistryEntry(registry, params) {
135934
136616
  const { entryKey, fieldsFromPlan, contextProjection } = params ?? {};
@@ -135994,7 +136676,7 @@ async function executeMarketplaceRegistryEntryHook(params) {
135994
136676
  const absoluteRegistry = join34(worktree, registryPath);
135995
136677
  let raw;
135996
136678
  try {
135997
- raw = await readFile40(absoluteRegistry, "utf8");
136679
+ raw = await readFile41(absoluteRegistry, "utf8");
135998
136680
  } catch {
135999
136681
  throw new ReleaseError(
136000
136682
  REMOTE_CONFLICT,
@@ -136046,7 +136728,7 @@ var init_marketplace_registry_entry = __esm({
136046
136728
  });
136047
136729
 
136048
136730
  // src/core/docs-refresh-preset.mjs
136049
- import { mkdir as mkdir27, readFile as readFile41, writeFile as writeFile12 } from "node:fs/promises";
136731
+ import { mkdir as mkdir27, readFile as readFile42, writeFile as writeFile12 } from "node:fs/promises";
136050
136732
  import { dirname as dirname20, isAbsolute as isAbsolute24, join as join35, relative as relative29, resolve as resolve34 } from "node:path";
136051
136733
  function resolvePayloadSource(payloadDir, from) {
136052
136734
  const sourcePath = resolve34(payloadDir, from);
@@ -136084,7 +136766,7 @@ async function executeDocsRefreshHook(params) {
136084
136766
  const mutate = /* @__PURE__ */ __name(async (worktree) => {
136085
136767
  for (const mapping of mappings) {
136086
136768
  const sourcePath = resolvePayloadSource(payloadDir, mapping.from);
136087
- let content = await readFile41(sourcePath).catch(() => null);
136769
+ let content = await readFile42(sourcePath).catch(() => null);
136088
136770
  if (content === null) {
136089
136771
  throw new ReleaseError(
136090
136772
  GATE_FAILED,
@@ -136301,7 +136983,7 @@ __export(distribute_exports, {
136301
136983
  });
136302
136984
  import { execFile as execFileCb16 } from "node:child_process";
136303
136985
  import { promisify as promisify16 } from "node:util";
136304
- import { realpath as realpath32, lstat as lstat43, mkdir as mkdir28, readFile as readFile42, rm as rm15 } from "node:fs/promises";
136986
+ import { realpath as realpath32, lstat as lstat43, mkdir as mkdir28, readFile as readFile43, rm as rm15 } from "node:fs/promises";
136305
136987
  import { mkdtemp as mkdtemp10 } from "node:fs/promises";
136306
136988
  import { tmpdir as tmpdir8 } from "node:os";
136307
136989
  import { isAbsolute as isAbsolute25, join as join36, relative as relative30, resolve as resolve35 } from "node:path";
@@ -136400,7 +137082,7 @@ async function distributeRelease(options) {
136400
137082
  const hookRunner = typeof runHookFn === "function" ? runHookFn : runHook;
136401
137083
  let planRaw;
136402
137084
  try {
136403
- planRaw = await readFile42(planPath, "utf8");
137085
+ planRaw = await readFile43(planPath, "utf8");
136404
137086
  } catch (err) {
136405
137087
  throw new ReleaseError(GATE_FAILED, `cannot read release plan: ${err.message}`, { planPath, cause: err.code });
136406
137088
  }
@@ -136515,7 +137197,7 @@ async function distributeRelease(options) {
136515
137197
  await evidence.append({ phase: "safety-gate", gate: "approval-load", status: "started" });
136516
137198
  let approvalRaw;
136517
137199
  try {
136518
- approvalRaw = await readFile42(approvalPath, "utf8");
137200
+ approvalRaw = await readFile43(approvalPath, "utf8");
136519
137201
  } catch (err) {
136520
137202
  throw new ReleaseError(GATE_FAILED, `cannot read approval record: ${err.message}`, { approvalPath, cause: err.code });
136521
137203
  }
@@ -136627,7 +137309,7 @@ async function distributeRelease(options) {
136627
137309
  for (const hookApprovalPath of hookApprovalPaths) {
136628
137310
  let hookApprovalRaw;
136629
137311
  try {
136630
- hookApprovalRaw = await readFile42(hookApprovalPath, "utf8");
137312
+ hookApprovalRaw = await readFile43(hookApprovalPath, "utf8");
136631
137313
  } catch (err) {
136632
137314
  await failBlocked(new ReleaseError(
136633
137315
  GATE_FAILED,
@@ -137732,7 +138414,7 @@ __export(postverify_exports, {
137732
138414
  });
137733
138415
  import { execFile as execFileCb17 } from "node:child_process";
137734
138416
  import { promisify as promisify17 } from "node:util";
137735
- import { mkdir as mkdir29, readFile as readFile43, realpath as realpath33, rm as rm16 } from "node:fs/promises";
138417
+ import { mkdir as mkdir29, readFile as readFile44, realpath as realpath33, rm as rm16 } from "node:fs/promises";
137736
138418
  import { mkdtemp as mkdtemp11 } from "node:fs/promises";
137737
138419
  import { tmpdir as tmpdir9 } from "node:os";
137738
138420
  import { join as join37 } from "node:path";
@@ -137765,7 +138447,7 @@ async function postVerifyRelease(options) {
137765
138447
  const hookRunner = typeof runHookFn === "function" ? runHookFn : runHook;
137766
138448
  let planRaw;
137767
138449
  try {
137768
- planRaw = await readFile43(planPath, "utf8");
138450
+ planRaw = await readFile44(planPath, "utf8");
137769
138451
  } catch (err) {
137770
138452
  throw new ReleaseError(GATE_FAILED, `cannot read release plan: ${err.message}`, { planPath, cause: err.code });
137771
138453
  }
@@ -137799,7 +138481,7 @@ async function postVerifyRelease(options) {
137799
138481
  }
137800
138482
  let approvalRaw;
137801
138483
  try {
137802
- approvalRaw = await readFile43(approvalPath, "utf8");
138484
+ approvalRaw = await readFile44(approvalPath, "utf8");
137803
138485
  } catch (err) {
137804
138486
  throw new ReleaseError(GATE_FAILED, `cannot read approval record: ${err.message}`, { approvalPath, cause: err.code });
137805
138487
  }
@@ -137872,16 +138554,16 @@ async function postVerifyRelease(options) {
137872
138554
  validatePostPublishDeclaration(declaration, { unitId: declaration.unitId });
137873
138555
  await verifyExecutionBundle({ planPath, postPublish: declaration });
137874
138556
  const declaredHooks = declaration.hooks ?? [];
137875
- const postVerifyHooks2 = declaredHooks.filter((hook) => (hook.phase ?? "distribute") === "postVerify");
137876
- prepared.push({ declaration, postVerifyHooks: postVerifyHooks2 });
138557
+ const postVerifyHooks3 = declaredHooks.filter((hook) => (hook.phase ?? "distribute") === "postVerify");
138558
+ prepared.push({ declaration, postVerifyHooks: postVerifyHooks3 });
137877
138559
  }
137878
- const postVerifyHooks = prepared.flatMap((prep) => prep.postVerifyHooks);
138560
+ const postVerifyHooks2 = prepared.flatMap((prep) => prep.postVerifyHooks);
137879
138561
  const approvedHookIds = /* @__PURE__ */ new Set();
137880
138562
  const hookApprovalPaths = postpublishApprovalPaths ?? [];
137881
138563
  for (const hookApprovalPath of hookApprovalPaths) {
137882
138564
  let hookApprovalRaw;
137883
138565
  try {
137884
- hookApprovalRaw = await readFile43(hookApprovalPath, "utf8");
138566
+ hookApprovalRaw = await readFile44(hookApprovalPath, "utf8");
137885
138567
  } catch (err) {
137886
138568
  throw new ReleaseError(
137887
138569
  GATE_FAILED,
@@ -138036,7 +138718,7 @@ async function postVerifyRelease(options) {
138036
138718
  status: "started",
138037
138719
  sourceVerifyRunId: verifyRun.runId,
138038
138720
  sourcePublishRunId: publishRun.runId,
138039
- hookCount: postVerifyHooks.length,
138721
+ hookCount: postVerifyHooks2.length,
138040
138722
  dryRun: dryRun === true
138041
138723
  });
138042
138724
  await snapshot(DISTRIBUTING2);
@@ -138516,7 +139198,7 @@ var release_metadata_exports = {};
138516
139198
  __export(release_metadata_exports, {
138517
139199
  updatePreviousPublicBaselines: () => updatePreviousPublicBaselines
138518
139200
  });
138519
- import { lstat as lstat44, readFile as readFile44 } from "node:fs/promises";
139201
+ import { lstat as lstat44, readFile as readFile45 } from "node:fs/promises";
138520
139202
  import { resolve as resolve36 } from "node:path";
138521
139203
  function assertOid(value, label) {
138522
139204
  if (typeof value !== "string" || !/^[a-f0-9]{40,64}$/.test(value)) {
@@ -138532,8 +139214,8 @@ async function updatePreviousPublicBaselines(options = {}) {
138532
139214
  let configStat;
138533
139215
  try {
138534
139216
  [plan, configRaw, configStat] = await Promise.all([
138535
- readFile44(planPath, "utf8").then(JSON.parse),
138536
- readFile44(configPath, "utf8"),
139217
+ readFile45(planPath, "utf8").then(JSON.parse),
139218
+ readFile45(configPath, "utf8"),
138537
139219
  lstat44(configPath)
138538
139220
  ]);
138539
139221
  } catch (error) {
@@ -138618,7 +139300,7 @@ var ship_exports = {};
138618
139300
  __export(ship_exports, {
138619
139301
  advanceShip: () => advanceShip
138620
139302
  });
138621
- import { readFile as readFile45, lstat as lstat45, mkdir as mkdir30 } from "node:fs/promises";
139303
+ import { readFile as readFile46, lstat as lstat45, mkdir as mkdir30 } from "node:fs/promises";
138622
139304
  import { basename as basename14, dirname as dirname21, resolve as resolve37 } from "node:path";
138623
139305
  function normalizeNewShipUnitIds(releaseUnits, requestedUnitIds) {
138624
139306
  if (requestedUnitIds === void 0) return void 0;
@@ -138674,7 +139356,7 @@ function assertFrozenPlanUnitScope({ statePath, stateUnitIds, expectedUnitIds, p
138674
139356
  }
138675
139357
  async function readFrozenPlanForUnitScope(planPath, statePath, stateUnitIds) {
138676
139358
  try {
138677
- return JSON.parse(await readFile45(planPath, "utf8"));
139359
+ return JSON.parse(await readFile46(planPath, "utf8"));
138678
139360
  } catch (error) {
138679
139361
  throw unitScopeMismatch(
138680
139362
  statePath,
@@ -138702,7 +139384,7 @@ async function readState(path40) {
138702
139384
  if (!stat22.isFile() || stat22.isSymbolicLink()) {
138703
139385
  throw new Error("ship state must be a regular non-symlink file");
138704
139386
  }
138705
- const state = JSON.parse(await readFile45(path40, "utf8"));
139387
+ const state = JSON.parse(await readFile46(path40, "utf8"));
138706
139388
  const { stateDigest, ...body } = state;
138707
139389
  if (typeof stateDigest !== "string" || stateDigest !== sha256Hex(canonicalJson2(body)) || body.statePath !== path40) {
138708
139390
  throw new Error("ship state digest or authority path does not match");
@@ -138751,7 +139433,8 @@ async function defaultDependencies() {
138751
139433
  readMaxApprovalMs: /* @__PURE__ */ __name(() => MAX_APPROVAL_MS, "readMaxApprovalMs")
138752
139434
  };
138753
139435
  }
138754
- function publicState2(state) {
139436
+ function publicState2(state, { postRelease } = {}) {
139437
+ const visiblePostRelease = postRelease === void 0 ? state.postRelease : postRelease;
138755
139438
  return {
138756
139439
  command: "ship",
138757
139440
  status: state.status,
@@ -138776,7 +139459,7 @@ function publicState2(state) {
138776
139459
  ...state.requirements ? { requirements: state.requirements } : {},
138777
139460
  ...state.manualFollowUps ? { manualFollowUps: state.manualFollowUps } : {},
138778
139461
  ...state.metadataUpdate ? { metadataUpdate: state.metadataUpdate } : {},
138779
- ...state.postRelease ? { postRelease: state.postRelease } : {},
139462
+ ...visiblePostRelease ? { postRelease: visiblePostRelease } : {},
138780
139463
  externalActionsIncludedInPlanApproval: true,
138781
139464
  verificationGatesIncludedInPlanApproval: true,
138782
139465
  postPublishCheckpointApprovalsIncludedInPlanApproval: false
@@ -138800,7 +139483,7 @@ function projectActionTarget(action) {
138800
139483
  }
138801
139484
  async function buildApprovalSummary(planPath, maxApprovalMs = MAX_APPROVAL_MS) {
138802
139485
  try {
138803
- const plan = JSON.parse(await readFile45(planPath, "utf8"));
139486
+ const plan = JSON.parse(await readFile46(planPath, "utf8"));
138804
139487
  const approvalWindowHours = deriveApprovalWindowHours(maxApprovalMs);
138805
139488
  const actions = (plan.externalActions ?? []).map((action) => ({
138806
139489
  id: action.id,
@@ -138865,19 +139548,19 @@ async function buildApprovalSummary(planPath, maxApprovalMs = MAX_APPROVAL_MS) {
138865
139548
  );
138866
139549
  }
138867
139550
  }
138868
- async function allUnclosedPostVerifyHooksUngated(state, postVerifyHooks, planVersion) {
138869
- if (!Array.isArray(postVerifyHooks) || postVerifyHooks.length === 0) return false;
138870
- let candidates = postVerifyHooks;
139551
+ async function allUnclosedPostVerifyHooksUngated(state, postVerifyHooks2, planVersion) {
139552
+ if (!Array.isArray(postVerifyHooks2) || postVerifyHooks2.length === 0) return false;
139553
+ let candidates = postVerifyHooks2;
138871
139554
  if (state.postVerify?.runPath) {
138872
139555
  try {
138873
- const record = JSON.parse(await readFile45(state.postVerify.runPath, "utf8"));
139556
+ const record = JSON.parse(await readFile46(state.postVerify.runPath, "utf8"));
138874
139557
  const checkpoints = Array.isArray(record.checkpoints) ? record.checkpoints : [];
138875
139558
  const closedIds = new Set(
138876
139559
  checkpoints.filter((cp4) => cp4?.actionType === "postpublish-hook" && (cp4.status === "succeeded" || cp4.status === "NO_CHANGE")).map((cp4) => cp4.actionId)
138877
139560
  );
138878
- candidates = postVerifyHooks.filter(({ hook, unitId }) => !closedIds.has(postPublishActionId({ planVersion, unitId, localId: hook.id })));
139561
+ candidates = postVerifyHooks2.filter(({ hook, unitId }) => !closedIds.has(postPublishActionId({ planVersion, unitId, localId: hook.id })));
138879
139562
  } catch {
138880
- candidates = postVerifyHooks;
139563
+ candidates = postVerifyHooks2;
138881
139564
  }
138882
139565
  }
138883
139566
  if (candidates.length === 0) return false;
@@ -139021,7 +139704,7 @@ async function advanceShip(options = {}, injected = {}) {
139021
139704
  });
139022
139705
  let frozenPlan = null;
139023
139706
  if (Array.isArray(state.selectedUnitIds) || deps.preflightGitTransports) {
139024
- frozenPlan = Array.isArray(state.selectedUnitIds) ? await readFrozenPlanForUnitScope(prepared.planPath, statePath, state.selectedUnitIds) : JSON.parse(await readFile45(prepared.planPath, "utf8"));
139707
+ frozenPlan = Array.isArray(state.selectedUnitIds) ? await readFrozenPlanForUnitScope(prepared.planPath, statePath, state.selectedUnitIds) : JSON.parse(await readFile46(prepared.planPath, "utf8"));
139025
139708
  }
139026
139709
  if (Array.isArray(state.selectedUnitIds)) {
139027
139710
  assertFrozenPlanUnitScope({
@@ -139118,7 +139801,7 @@ async function advanceShip(options = {}, injected = {}) {
139118
139801
  }
139119
139802
  let postVerifyRanThisCall = false;
139120
139803
  if (state.status === "PUBLISHED" || state.status === "NEEDS_MANUAL_ATTESTATIONS") {
139121
- const plan = JSON.parse(await readFile45(state.planPath, "utf8"));
139804
+ const plan = JSON.parse(await readFile46(state.planPath, "utf8"));
139122
139805
  const hasDistributeWork = normalizePostPublishView(plan).some((declaration) => (declaration.targets?.length ?? 0) > 0 || (declaration.hooks?.length ?? 0) > 0);
139123
139806
  const needsDistribution = hasDistributeWork;
139124
139807
  if (needsDistribution && deps.distributeRelease) {
@@ -139173,15 +139856,6 @@ async function advanceShip(options = {}, injected = {}) {
139173
139856
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
139174
139857
  };
139175
139858
  await writeJsonAtomic(statePath, state);
139176
- if (verified.status === "VERIFIED") {
139177
- try {
139178
- state.postRelease = derivePostReleaseChecklist(plan);
139179
- } catch (error) {
139180
- state.postRelease = unavailablePostReleaseChecklist(plan, error);
139181
- }
139182
- state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
139183
- await writeJsonAtomic(statePath, state);
139184
- }
139185
139859
  } catch (error) {
139186
139860
  if (error?.code !== CONSUMER_VERIFICATION_DEFERRED) throw error;
139187
139861
  state = {
@@ -139192,8 +139866,8 @@ async function advanceShip(options = {}, injected = {}) {
139192
139866
  };
139193
139867
  await writeJsonAtomic(statePath, state);
139194
139868
  }
139195
- const postVerifyHooks = normalizePostPublishView(plan).flatMap((declaration) => declaration.hooks ?? []).filter((hook) => hook.phase === "postVerify");
139196
- if (state.status === "VERIFIED" && postVerifyHooks.length > 0 && deps.postVerifyRelease) {
139869
+ const postVerifyHooks2 = normalizePostPublishView(plan).flatMap((declaration) => declaration.hooks ?? []).filter((hook) => hook.phase === "postVerify");
139870
+ if (state.status === "VERIFIED" && postVerifyHooks2.length > 0 && deps.postVerifyRelease) {
139197
139871
  postVerifyRanThisCall = true;
139198
139872
  let postVerifyOutcome;
139199
139873
  try {
@@ -139227,7 +139901,7 @@ async function advanceShip(options = {}, injected = {}) {
139227
139901
  }
139228
139902
  const reentryApprovalPaths = options.postpublishApprovalPaths ?? [];
139229
139903
  if (state.status === "VERIFIED" && deps.postVerifyRelease && !postVerifyRanThisCall && (!state.postVerify || state.postVerify.status !== "DISTRIBUTED")) {
139230
- const reentryPlan = JSON.parse(await readFile45(state.planPath, "utf8"));
139904
+ const reentryPlan = JSON.parse(await readFile46(state.planPath, "utf8"));
139231
139905
  const reentryPostVerifyHooks = normalizePostPublishView(reentryPlan).flatMap((declaration) => (declaration.hooks ?? []).map((hook) => ({ hook, unitId: declaration.unitId }))).filter(({ hook }) => hook.phase === "postVerify");
139232
139906
  const approvallessRetryAllowed = reentryApprovalPaths.length === 0 && await allUnclosedPostVerifyHooksUngated(state, reentryPostVerifyHooks, reentryPlan.planVersion);
139233
139907
  if (reentryPostVerifyHooks.length > 0 && (reentryApprovalPaths.length > 0 || approvallessRetryAllowed)) {
@@ -139261,6 +139935,24 @@ async function advanceShip(options = {}, injected = {}) {
139261
139935
  await writeJsonAtomic(statePath, state);
139262
139936
  }
139263
139937
  }
139938
+ if (state.status === "VERIFIED") {
139939
+ const finalPlan = JSON.parse(await readFile46(state.planPath, "utf8"));
139940
+ const hasPostVerify = normalizePostPublishView(finalPlan).some((declaration) => (declaration.hooks ?? []).some((hook) => hook.phase === "postVerify"));
139941
+ const localFinishRunPath = hasPostVerify ? state.postVerify?.status === "DISTRIBUTED" ? state.postVerify.runPath : void 0 : state.verifyRunPath;
139942
+ let postRelease;
139943
+ try {
139944
+ postRelease = derivePostReleaseChecklist(finalPlan, {
139945
+ root,
139946
+ statePath,
139947
+ unitIds: state.selectedUnitIds,
139948
+ runPath: localFinishRunPath,
139949
+ postVerifyComplete: !hasPostVerify || state.postVerify?.status === "DISTRIBUTED"
139950
+ });
139951
+ } catch (error) {
139952
+ postRelease = unavailablePostReleaseChecklist(finalPlan, error);
139953
+ }
139954
+ return publicState2(state, { postRelease });
139955
+ }
139264
139956
  return publicState2(state);
139265
139957
  }
139266
139958
  var HOUR_MS, ACTION_TARGET_FIELDS;
@@ -140232,7 +140924,7 @@ var attest_exports = {};
140232
140924
  __export(attest_exports, {
140233
140925
  recordManualAttestation: () => recordManualAttestation
140234
140926
  });
140235
- import { readFile as readFile46 } from "node:fs/promises";
140927
+ import { readFile as readFile47 } from "node:fs/promises";
140236
140928
  import { resolve as resolve38, join as join39 } from "node:path";
140237
140929
  async function validateInstalledConsumerClosure({
140238
140930
  root,
@@ -140250,7 +140942,7 @@ async function validateInstalledConsumerClosure({
140250
140942
  ]);
140251
140943
  let plan;
140252
140944
  try {
140253
- plan = JSON.parse(await readFile46(planPath, "utf8"));
140945
+ plan = JSON.parse(await readFile47(planPath, "utf8"));
140254
140946
  } catch (error) {
140255
140947
  throw new ReleaseError(
140256
140948
  GATE_FAILED,
@@ -140324,7 +141016,7 @@ async function recordManualAttestation(options = {}, injected = {}) {
140324
141016
  const requirementPath = join39(authorityDir, descriptor.requirementFile);
140325
141017
  let requirement;
140326
141018
  try {
140327
- requirement = JSON.parse(await readFile46(requirementPath, "utf8"));
141019
+ requirement = JSON.parse(await readFile47(requirementPath, "utf8"));
140328
141020
  } catch (error) {
140329
141021
  throw new ReleaseError(
140330
141022
  GATE_FAILED,
@@ -140408,7 +141100,7 @@ var init_attest = __esm({
140408
141100
  });
140409
141101
 
140410
141102
  // src/artifacts/policy.mjs
140411
- import { readFile as readFile47 } from "node:fs/promises";
141103
+ import { readFile as readFile48 } from "node:fs/promises";
140412
141104
  import { join as join40 } from "node:path";
140413
141105
  import { createHash as createHash19 } from "node:crypto";
140414
141106
  function validateArtifactPolicy(policy) {
@@ -140508,7 +141200,7 @@ async function parseSafeYamlWithinRoot(root, policyPath) {
140508
141200
  const fullPath = join40(root, policyPath);
140509
141201
  let content;
140510
141202
  try {
140511
- content = await readFile47(fullPath, "utf8");
141203
+ content = await readFile48(fullPath, "utf8");
140512
141204
  } catch (err) {
140513
141205
  throw new ReleaseError(
140514
141206
  ARTIFACT_POLICY_INVALID,
@@ -140619,7 +141311,7 @@ var init_policy = __esm({
140619
141311
  });
140620
141312
 
140621
141313
  // src/artifacts/entry.mjs
140622
- import { lstat as lstat46, readdir as readdir30, readFile as readFile48 } from "node:fs/promises";
141314
+ import { lstat as lstat46, readdir as readdir30, readFile as readFile49 } from "node:fs/promises";
140623
141315
  import { join as join41 } from "node:path";
140624
141316
  import { promisify as promisify21 } from "node:util";
140625
141317
  import { execFile as execFile14 } from "node:child_process";
@@ -140664,7 +141356,7 @@ async function enumerateTreeEntries(root, dirPath, relBase) {
140664
141356
  const subEntries = await enumerateTreeEntries(root, absPath, relPath);
140665
141357
  entries.push(...subEntries);
140666
141358
  } else {
140667
- const content = await readFile48(absPath);
141359
+ const content = await readFile49(absPath);
140668
141360
  entries.push(
140669
141361
  Object.freeze({
140670
141362
  path: relPath,
@@ -140727,7 +141419,7 @@ async function readEntry({ root, path: path40, source = "worktree" } = {}) {
140727
141419
  { path: path40, nlink: st.nlink }
140728
141420
  );
140729
141421
  }
140730
- const content = await readFile48(absPath);
141422
+ const content = await readFile49(absPath);
140731
141423
  return Object.freeze({
140732
141424
  kind: "regular",
140733
141425
  path: path40,
@@ -140991,7 +141683,7 @@ var init_graph = __esm({
140991
141683
  });
140992
141684
 
140993
141685
  // src/artifacts/producer-registry.mjs
140994
- import { readFile as readFile49, readdir as readdir31, stat as stat19, mkdir as mkdir32, writeFile as writeFile14, rm as rm18 } from "node:fs/promises";
141686
+ import { readFile as readFile50, readdir as readdir31, stat as stat19, mkdir as mkdir32, writeFile as writeFile14, rm as rm18 } from "node:fs/promises";
140995
141687
  import { join as join42 } from "node:path";
140996
141688
  import { mkdtemp as mkdtemp13 } from "node:fs/promises";
140997
141689
  import { tmpdir as tmpdir11 } from "node:os";
@@ -141023,7 +141715,7 @@ async function createBuiltInProducerRegistry() {
141023
141715
  const lockfilePath = join42(new URL("../../..", import.meta.url).pathname, "pnpm-lock.yaml");
141024
141716
  let lockfileBytes;
141025
141717
  try {
141026
- lockfileBytes = await readFile49(lockfilePath);
141718
+ lockfileBytes = await readFile50(lockfilePath);
141027
141719
  } catch {
141028
141720
  lockfileBytes = Buffer.from("");
141029
141721
  }
@@ -141040,7 +141732,7 @@ async function createBuiltInProducerRegistry() {
141040
141732
  const allModuleBytes = await Promise.all(
141041
141733
  allModulePaths.map(async (p) => {
141042
141734
  try {
141043
- return await readFile49(p);
141735
+ return await readFile50(p);
141044
141736
  } catch {
141045
141737
  return Buffer.from("");
141046
141738
  }
@@ -141066,7 +141758,7 @@ async function readDirEntries(dirPath, relBase = "") {
141066
141758
  if (st.isDirectory()) {
141067
141759
  entries.push(...await readDirEntries(absPath, relPath));
141068
141760
  } else {
141069
- const content = await readFile49(absPath);
141761
+ const content = await readFile50(absPath);
141070
141762
  entries.push(Object.freeze({
141071
141763
  path: relPath,
141072
141764
  type: "blob",
@@ -141593,7 +142285,7 @@ var init_state = __esm({
141593
142285
  });
141594
142286
 
141595
142287
  // src/artifacts/artifact-plan.mjs
141596
- import { writeFile as writeFile15, mkdir as mkdir33, readFile as readFile50, rename as rename5, open as open14 } from "node:fs/promises";
142288
+ import { writeFile as writeFile15, mkdir as mkdir33, readFile as readFile51, rename as rename5, open as open14 } from "node:fs/promises";
141597
142289
  import { dirname as dirname23 } from "node:path";
141598
142290
  function assemblePlan({
141599
142291
  operation,
@@ -142282,7 +142974,7 @@ var init_adoption = __esm({
142282
142974
  // src/artifacts/inspect.mjs
142283
142975
  import { promisify as promisify24 } from "node:util";
142284
142976
  import { execFile as execFile17 } from "node:child_process";
142285
- import { readdir as readdir32, stat as stat20, readFile as readFile51 } from "node:fs/promises";
142977
+ import { readdir as readdir32, stat as stat20, readFile as readFile52 } from "node:fs/promises";
142286
142978
  import { join as join43 } from "node:path";
142287
142979
  async function hasNestedGitRoots(root) {
142288
142980
  try {
@@ -142554,7 +143246,7 @@ var init_inspect = __esm({
142554
143246
  });
142555
143247
 
142556
143248
  // src/artifacts/resolution.mjs
142557
- import { mkdir as mkdir34, open as open15, readFile as readFile52, stat as stat21, lstat as lstat47, chmod as chmod6 } from "node:fs/promises";
143249
+ import { mkdir as mkdir34, open as open15, readFile as readFile53, stat as stat21, lstat as lstat47, chmod as chmod6 } from "node:fs/promises";
142558
143250
  import { join as join44, resolve as resolve39, relative as relative31, isAbsolute as isAbsolute27, basename as basename15 } from "node:path";
142559
143251
  function decodeBuffer(value, label) {
142560
143252
  if (value == null) return null;
@@ -142886,7 +143578,7 @@ async function submitResolution({
142886
143578
  async function readAndValidateResolvedFile(resolvedPath) {
142887
143579
  let content;
142888
143580
  try {
142889
- content = await readFile52(resolvedPath);
143581
+ content = await readFile53(resolvedPath);
142890
143582
  } catch (err) {
142891
143583
  throw new ReleaseError(MISSING_PARAMETERS, `cannot read resolved file: ${err.message}`, { resolvedPath, cause: err.code });
142892
143584
  }
@@ -143054,7 +143746,7 @@ var artifacts_exports = {};
143054
143746
  __export(artifacts_exports, {
143055
143747
  runArtifactsCommand: () => runArtifactsCommand
143056
143748
  });
143057
- import { readFile as readFile53 } from "node:fs/promises";
143749
+ import { readFile as readFile54 } from "node:fs/promises";
143058
143750
  import { join as join45 } from "node:path";
143059
143751
  async function runArtifactsCommand({ subcommand, args: args2, root } = {}) {
143060
143752
  if (!VALID_SUBCOMMANDS.has(subcommand)) {
@@ -143183,7 +143875,7 @@ async function handleAdopt({ args: args2, root }) {
143183
143875
  { subcommand: "adopt" }
143184
143876
  );
143185
143877
  }
143186
- const planRaw = await readFile53(planPath, "utf8");
143878
+ const planRaw = await readFile54(planPath, "utf8");
143187
143879
  const plan = JSON.parse(planRaw);
143188
143880
  if (expectedDigest && plan.planDigest !== expectedDigest) {
143189
143881
  throw new ReleaseError(
@@ -143198,7 +143890,7 @@ async function handleAdopt({ args: args2, root }) {
143198
143890
  if (artifact.path) {
143199
143891
  const entry = await readEntry({ root, path: artifact.path, source: "worktree" });
143200
143892
  if (entry.kind === "regular") {
143201
- const bytes = await readFile53(join45(root, artifact.path));
143893
+ const bytes = await readFile54(join45(root, artifact.path));
143202
143894
  currentEntries.set(artifact.id, Object.freeze({ ...entry, bytes, content: bytes }));
143203
143895
  } else {
143204
143896
  currentEntries.set(artifact.id, entry);
@@ -143263,7 +143955,7 @@ async function handleBootstrap({ args: args2, root }) {
143263
143955
  { subcommand: "bootstrap" }
143264
143956
  );
143265
143957
  }
143266
- const planRaw = await readFile53(planPath, "utf8");
143958
+ const planRaw = await readFile54(planPath, "utf8");
143267
143959
  const adoptionPlan = JSON.parse(planRaw);
143268
143960
  const currentEntries = /* @__PURE__ */ new Map();
143269
143961
  for (const id of new Set((adoptionPlan.protectedHunks ?? []).map((h) => h.artifactId))) {
@@ -143275,12 +143967,12 @@ async function handleBootstrap({ args: args2, root }) {
143275
143967
  if (entry.kind !== "regular") {
143276
143968
  throw new ReleaseError("PLAN_STALE", `artifact is no longer a regular file: ${id}`, { id });
143277
143969
  }
143278
- const bytes = await readFile53(join45(root, artifactPath));
143970
+ const bytes = await readFile54(join45(root, artifactPath));
143279
143971
  currentEntries.set(id, Object.freeze({ ...entry, bytes, content: bytes }));
143280
143972
  }
143281
143973
  let replacementBytes;
143282
143974
  if (action === "replace" && replacementPath) {
143283
- replacementBytes = await readFile53(replacementPath);
143975
+ replacementBytes = await readFile54(replacementPath);
143284
143976
  }
143285
143977
  const updated = await discardBootstrapHunk({
143286
143978
  adoptionPlan,
@@ -143318,7 +144010,7 @@ async function handleResolve({ args: args2, root }) {
143318
144010
  { subcommand: "resolve" }
143319
144011
  );
143320
144012
  }
143321
- const planRaw = await readFile53(planPath, "utf8");
144013
+ const planRaw = await readFile54(planPath, "utf8");
143322
144014
  const plan = JSON.parse(planRaw);
143323
144015
  if (plan.planDigest !== expectedDigest) {
143324
144016
  throw new ReleaseError(
@@ -144136,7 +144828,7 @@ __export(route_exports, {
144136
144828
  resolvePreviousReleaseCommit: () => resolvePreviousReleaseCommit
144137
144829
  });
144138
144830
  import { execFileSync } from "node:child_process";
144139
- import { lstat as lstat48, readdir as readdir33, readFile as readFile54 } from "node:fs/promises";
144831
+ import { lstat as lstat48, readdir as readdir33, readFile as readFile55 } from "node:fs/promises";
144140
144832
  import { relative as relative32, resolve as resolve40, basename as basename16, dirname as dirname24, join as join46 } from "node:path";
144141
144833
  function classifyPath(path40) {
144142
144834
  if (typeof path40 !== "string" || path40.length === 0) return "ignore";
@@ -144223,7 +144915,7 @@ async function resolvePreviousReleaseCommit(root) {
144223
144915
  if (!file.endsWith(".json")) continue;
144224
144916
  let plan;
144225
144917
  try {
144226
- plan = JSON.parse(await readFile54(resolve40(plansDir, file), "utf8"));
144918
+ plan = JSON.parse(await readFile55(resolve40(plansDir, file), "utf8"));
144227
144919
  } catch {
144228
144920
  continue;
144229
144921
  }
@@ -144349,7 +145041,7 @@ async function readRunRouting(root, options = {}) {
144349
145041
  let authorityMissing = false;
144350
145042
  let markerlessRunDebris = false;
144351
145043
  try {
144352
- await readFile54(runPath);
145044
+ await readFile55(runPath);
144353
145045
  authorityExists = true;
144354
145046
  } catch (error) {
144355
145047
  if (error.code === "ENOENT") {
@@ -144504,7 +145196,7 @@ async function isMarkerlessRunDebris(runDirectory) {
144504
145196
  const evidenceFiles = await readDirectory(join46(runDirectory, "evidence", evidenceEntry.name));
144505
145197
  if (!evidenceFiles || evidenceFiles.length !== 1 || evidenceFiles[0].name !== "release-skill-install-evidence.json" || !evidenceFiles[0].isFile() || evidenceFiles[0].isSymbolicLink()) return false;
144506
145198
  try {
144507
- const evidenceRecord = JSON.parse(await readFile54(
145199
+ const evidenceRecord = JSON.parse(await readFile55(
144508
145200
  join46(runDirectory, "evidence", evidenceEntry.name, evidenceFiles[0].name),
144509
145201
  "utf8"
144510
145202
  ));
@@ -144589,8 +145281,8 @@ async function isProvenPreAuthorityFailure(runDir, runName) {
144589
145281
  let summary;
144590
145282
  let events;
144591
145283
  try {
144592
- summary = JSON.parse(await readFile54(summaryPath, "utf8"));
144593
- events = (await readFile54(evidencePath, "utf8")).trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
145284
+ summary = JSON.parse(await readFile55(summaryPath, "utf8"));
145285
+ events = (await readFile55(evidencePath, "utf8")).trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
144594
145286
  } catch {
144595
145287
  return false;
144596
145288
  }
@@ -144675,7 +145367,7 @@ async function readLegacyPostverifyRecovery(runPath) {
144675
145367
  if (run6.command !== "postverify" || run6.status !== "DISTRIBUTED") {
144676
145368
  throw new ReleaseError(GATE_FAILED, "legacy postverify status is not terminal DISTRIBUTED");
144677
145369
  }
144678
- const planRaw = await readFile54(run6.planPath, "utf8");
145370
+ const planRaw = await readFile55(run6.planPath, "utf8");
144679
145371
  const plan = JSON.parse(planRaw);
144680
145372
  validatePlan(plan);
144681
145373
  if (!plan.digest || plan.digest !== computePlanDigest(plan)) {
@@ -144706,7 +145398,7 @@ async function readLegacyPostverifyRecovery(runPath) {
144706
145398
  runPath: run6.sourceRunPath,
144707
145399
  production
144708
145400
  });
144709
- const summary = JSON.parse(await readFile54(join46(resolve40(runPath, ".."), "summary.json"), "utf8"));
145401
+ const summary = JSON.parse(await readFile55(join46(resolve40(runPath, ".."), "summary.json"), "utf8"));
144710
145402
  if (!summary || summary.status !== run6.status) {
144711
145403
  throw new ReleaseError(GATE_FAILED, "legacy postverify summary status does not match its sealed run status");
144712
145404
  }
@@ -145080,7 +145772,7 @@ Workflow Profiles:
145080
145772
  });
145081
145773
 
145082
145774
  // bin/release-skill-cli.mjs
145083
- import { readFile as readFile55 } from "node:fs/promises";
145775
+ import { readFile as readFile56 } from "node:fs/promises";
145084
145776
  import { basename as basename17, dirname as dirname25, join as join47, resolve as resolve41 } from "node:path";
145085
145777
  import { execFile as execFileCb22 } from "node:child_process";
145086
145778
  import { promisify as promisify26 } from "node:util";
@@ -145132,7 +145824,7 @@ guardOutputStream(process.stdout);
145132
145824
  guardOutputStream(process.stderr);
145133
145825
  registerPathRedactor(redactSensitivePaths);
145134
145826
  var execFile19 = promisify26(execFileCb22);
145135
- var COMMANDS = /* @__PURE__ */ new Set(["help", "setup", "assess", "prepare", "approve", "publish", "reconcile", "verify", "ship", "post-release", "attest", "hooks", "artifacts", "docs", "distribute", "route", "lineage"]);
145827
+ var COMMANDS = /* @__PURE__ */ new Set(["help", "setup", "assess", "prepare", "approve", "publish", "reconcile", "verify", "postverify", "ship", "post-release", "attest", "hooks", "artifacts", "docs", "distribute", "route", "lineage"]);
145136
145828
  function flushOutputStream(stream) {
145137
145829
  if (!stream?.writable || stream.destroyed || stream.writableEnded || outputStreamStates.get(stream)?.failed) {
145138
145830
  return Promise.resolve();
@@ -145163,6 +145855,27 @@ async function exitAfterFlush(exitCode) {
145163
145855
  process.exit(exitCode);
145164
145856
  }
145165
145857
  __name(exitAfterFlush, "exitAfterFlush");
145858
+ function publicErrorDetails(error) {
145859
+ const details = error?.details;
145860
+ if (!details || typeof details !== "object" || Array.isArray(details)) return {};
145861
+ const output = {};
145862
+ if (details.cause && typeof details.cause === "object" && !Array.isArray(details.cause)) {
145863
+ const cause = {};
145864
+ if (typeof details.cause.code === "string") cause.code = details.cause.code;
145865
+ if (typeof details.cause.message === "string") cause.message = details.cause.message;
145866
+ if (Object.keys(cause).length > 0) output.cause = cause;
145867
+ }
145868
+ if (Array.isArray(details.nextSteps)) {
145869
+ const nextSteps = details.nextSteps.filter((step) => step && typeof step === "object" && !Array.isArray(step)).map((step) => ({
145870
+ ...typeof step.code === "string" ? { code: step.code } : {},
145871
+ ...typeof step.message === "string" ? { message: step.message } : {},
145872
+ ...Array.isArray(step.argv) && step.argv.every((arg) => typeof arg === "string") ? { argv: [...step.argv] } : {}
145873
+ })).filter((step) => Object.keys(step).length > 0);
145874
+ if (nextSteps.length > 0) output.nextSteps = nextSteps;
145875
+ }
145876
+ return output;
145877
+ }
145878
+ __name(publicErrorDetails, "publicErrorDetails");
145166
145879
  async function checkDependency(command2, versionArgs = ["--version"]) {
145167
145880
  try {
145168
145881
  const { stdout } = await execFile19(command2, versionArgs, {
@@ -145302,6 +146015,11 @@ function getCapabilityMaturity() {
145302
146015
  mode: "fresh consumer verification (protocol-tested; no OS/network sandbox)",
145303
146016
  description: "Recheck remote state, exact npm installation, CLI help, and automated Claude/Codex installs before VERIFIED; Kimi/CodeBuddy remain unverified manual follow-ups"
145304
146017
  },
146018
+ postverify: {
146019
+ available: true,
146020
+ mode: "independent postVerify execution",
146021
+ description: "Run approved postVerify hooks from a VERIFIED run in an independent run; never reads or writes ship state"
146022
+ },
145305
146023
  distribute: {
145306
146024
  available: true,
145307
146025
  mode: "controlled production (protocol-tested; no OS/network sandbox)",
@@ -145335,6 +146053,7 @@ Commands:
145335
146053
  publish Publish frozen GitHub/npm artifacts after approval
145336
146054
  reconcile Resume PARTIAL state from evidence; conflicts require a human
145337
146055
  verify Fresh remote and consumer verification; only this reaches VERIFIED
146056
+ postverify Run approved postVerify hooks from a VERIFIED run in an independent run
145338
146057
  ship Resume one durable prepare -> approve -> publish -> verify flow; completes a parked
145339
146058
  postVerify hook once its checkpoint approval is provided (--hook-approval)
145340
146059
  post-release Inspect optional local finishing work after VERIFIED; update installed host plugins
@@ -145349,10 +146068,10 @@ Commands:
145349
146068
 
145350
146069
  Options:
145351
146070
  --root <path> Project root directory (default: cwd)
145352
- --plan <path> Path to the release plan file
146071
+ --plan <path> Path to the release plan file (required for approve/publish/reconcile/verify/postverify)
145353
146072
  --run <path> Path to the release run file, or a run directory whose release-run.json
145354
- is resolved automatically (required for reconcile/verify)
145355
- --approval <path> Path to the approval record
146073
+ is resolved automatically (required for reconcile/verify/postverify)
146074
+ --approval <path> Path to the release-level approval record (required for publish/postverify)
145356
146075
  --production Prepare immutable Git/npm production artifacts
145357
146076
  --output <path> Override prepare/approve output path (non-production only)
145358
146077
  --run-dir <path> Override prepare run directory; production requires one direct child of .release-skill/runs
@@ -145389,7 +146108,7 @@ Options:
145389
146108
  --install-path <path> Actual managed plugin path when closure verification requires it
145390
146109
  --install-channel <desktop|cli> CodeBuddy installation channel when required
145391
146110
  --approve Approve the ship plan (boolean; plan digest is auto-resolved)
145392
- --hook-approval <path> Checkpoint approval for a requiresApproval postPublish hook (ship/distribute; repeatable)
146111
+ --hook-approval <path> Checkpoint approval for one requiresApproval hook (ship/distribute/postverify; repeatable)
145393
146112
  --state <path> Override the durable ship state file
145394
146113
  --update-local-hosts Update installed plugins for selected local hosts after VERIFIED
145395
146114
  --hosts <ids> Comma-separated local hosts for post-release update. No local host is updated unless --hosts contains at least one id
@@ -145871,9 +146590,15 @@ if (command === "ship") {
145871
146590
  } else {
145872
146591
  console.log("Post-release: branch advancement was already included; skip the merge question.");
145873
146592
  }
145874
- if (result2.postRelease.localHostUpdate?.promptRequired) {
146593
+ const localHostUpdate = result2.postRelease.localHostUpdate;
146594
+ if (localHostUpdate?.available === true && typeof localHostUpdate.runPath === "string") {
145875
146595
  console.log(`Post-release: ask whether to update local host plugins (${result2.postRelease.localHostUpdate.hosts.join(", ")}).`);
145876
- console.log(`Post-release command: release-skill post-release --plan ${result2.planPath} --run ${result2.verifyRunPath}`);
146596
+ console.log(`Post-release command: release-skill post-release --plan ${result2.planPath} --run ${localHostUpdate.runPath}`);
146597
+ console.log("Choose --hosts before adding --update-local-hosts to perform a local update.");
146598
+ } else {
146599
+ for (const step of localHostUpdate?.nextSteps ?? []) {
146600
+ console.log(`Next [${step.code}] ${step.message} (${step.argv.join(" ")})`);
146601
+ }
145877
146602
  }
145878
146603
  }
145879
146604
  for (const followUp of result2.manualFollowUps ?? []) {
@@ -146217,55 +146942,68 @@ if (command === "post-release") {
146217
146942
  const planPath = value("--plan") ? resolve41(value("--plan")) : void 0;
146218
146943
  const runPath = value("--run") ? resolve41(value("--run")) : void 0;
146219
146944
  if (!planPath || !runPath) {
146220
- const message = "post-release requires --plan <path> and --run <verified-run-path>";
146945
+ const message = "post-release requires --plan <path> and --run <verify-or-postverify-run-path>";
146221
146946
  if (hasJson) console.log(JSON.stringify({ error: "MISSING_PARAMETERS", message, exitCode: 1 }));
146222
146947
  else console.error(`Error: ${message}`);
146223
146948
  await exitAfterFlush(1);
146224
146949
  }
146225
146950
  try {
146226
146951
  const {
146227
- assertVerifiedReleaseRun: assertVerifiedReleaseRun2,
146228
146952
  derivePostReleaseChecklist: derivePostReleaseChecklist2,
146229
146953
  updateLocalHostPlugins: updateLocalHostPlugins2
146230
146954
  } = await init_post_release_local().then(() => post_release_local_exports);
146231
146955
  const { validatePlan: validatePlan2 } = await init_plan().then(() => plan_exports);
146232
146956
  const {
146233
146957
  loadRun: loadRun2,
146234
- resolveRunPath: resolveRunPath2,
146235
- validateRunLineage: validateRunLineage2
146958
+ resolveRunPath: resolveRunPath2
146236
146959
  } = await init_run().then(() => run_exports);
146237
- const plan = JSON.parse(await readFile55(planPath, "utf8"));
146960
+ const plan = JSON.parse(await readFile56(planPath, "utf8"));
146238
146961
  validatePlan2(plan);
146962
+ const updateRequested = args.includes("--update-local-hosts");
146239
146963
  const resolvedRunPath = await resolveRunPath2(runPath);
146240
146964
  const runRecord = await loadRun2(resolvedRunPath, {
146241
146965
  requireDigest: true,
146242
146966
  authorityPlanPath: planPath
146243
146967
  });
146244
- await validateRunLineage2(runRecord, {
146245
- plan,
146246
- planPath,
146247
- runPath: resolvedRunPath,
146248
- production: Boolean(plan.production)
146249
- });
146250
- assertVerifiedReleaseRun2(plan, runRecord);
146251
- const updateRequested = args.includes("--update-local-hosts");
146968
+ if (!updateRequested) {
146969
+ const { assertLocalFinishRun: assertLocalFinishRun2 } = await init_post_release_local().then(() => post_release_local_exports);
146970
+ await assertLocalFinishRun2({
146971
+ plan,
146972
+ planPath,
146973
+ runPath: resolvedRunPath,
146974
+ runRecord,
146975
+ production: Boolean(plan.production),
146976
+ root: resolve41(value("--root") ?? process.cwd())
146977
+ });
146978
+ }
146252
146979
  const hostsIndex = args.indexOf("--hosts");
146253
146980
  const rawHosts = hostsIndex !== -1 && args[hostsIndex + 1] && !args[hostsIndex + 1].startsWith("--") ? args[hostsIndex + 1] : "";
146254
146981
  const selectedHosts = rawHosts.split(",").map((host) => host.trim()).filter(Boolean);
146255
146982
  const result2 = updateRequested ? await updateLocalHostPlugins2({
146256
- plan,
146983
+ planPath,
146984
+ runPath: resolvedRunPath,
146257
146985
  root: resolve41(value("--root") ?? process.cwd()),
146258
146986
  confirmPlanDigest: value("--confirm-plan"),
146259
146987
  selectedHosts
146260
- }) : derivePostReleaseChecklist2(plan);
146988
+ }) : derivePostReleaseChecklist2(plan, {
146989
+ runPath: runRecord.command === "postverify" && runRecord.status === "DISTRIBUTED" ? resolvedRunPath : runRecord.command === "verify" ? resolvedRunPath : void 0,
146990
+ root: resolve41(value("--root") ?? process.cwd()),
146991
+ postVerifyComplete: runRecord.command === "postverify" && runRecord.status === "DISTRIBUTED"
146992
+ });
146261
146993
  if (hasJson) {
146262
146994
  console.log(JSON.stringify(result2, null, 2));
146263
146995
  } else if (!updateRequested) {
146264
146996
  console.log(`Post-release status: ${result2.status}`);
146265
146997
  if (result2.merge.promptRequired) console.log("Ask whether the user wants to merge the remaining branch.");
146266
146998
  else console.log("Branch advancement was already included in the release workflow; skip the merge question.");
146267
- if (result2.localHostUpdate.promptRequired) {
146999
+ if (result2.localHostUpdate.available === true && typeof result2.localHostUpdate.runPath === "string") {
146268
147000
  console.log(`Ask whether to update local host plugins: ${result2.localHostUpdate.hosts.join(", ")}`);
147001
+ console.log(`Post-release command: release-skill post-release --plan ${planPath} --run ${result2.localHostUpdate.runPath}`);
147002
+ console.log("Choose --hosts before adding --update-local-hosts to perform a local update.");
147003
+ } else {
147004
+ for (const step of result2.localHostUpdate.nextSteps ?? []) {
147005
+ console.log(`Next [${step.code}] ${step.message} (${step.argv.join(" ")})`);
147006
+ }
146269
147007
  }
146270
147008
  } else {
146271
147009
  console.log(`Local host update: ${result2.status}`);
@@ -146281,9 +147019,15 @@ if (command === "post-release") {
146281
147019
  console.log(JSON.stringify({
146282
147020
  error: err.code ?? "POST_RELEASE_FAILED",
146283
147021
  message: err.message,
147022
+ details: publicErrorDetails(err),
146284
147023
  exitCode: err.exitCode ?? 1
146285
147024
  }));
146286
- } else console.error(`Error: ${err.message}`);
147025
+ } else {
147026
+ console.error(`Error: ${err.message}`);
147027
+ for (const step of err.details?.nextSteps ?? []) {
147028
+ console.error(`Next [${step.code}] ${step.message} (${step.argv.join(" ")})`);
147029
+ }
147030
+ }
146287
147031
  await exitAfterFlush(err.exitCode ?? 1);
146288
147032
  }
146289
147033
  }
@@ -146330,9 +147074,9 @@ if (command === "verify") {
146330
147074
  derivePostReleaseChecklist: derivePostReleaseChecklist2,
146331
147075
  unavailablePostReleaseChecklist: unavailablePostReleaseChecklist2
146332
147076
  } = await init_post_release_local().then(() => post_release_local_exports);
146333
- const plan = JSON.parse(await readFile55(planPath, "utf8"));
147077
+ const plan = JSON.parse(await readFile56(planPath, "utf8"));
146334
147078
  try {
146335
- result2.postRelease = derivePostReleaseChecklist2(plan);
147079
+ result2.postRelease = derivePostReleaseChecklist2(plan, { root, runPath: result2.runPath });
146336
147080
  } catch (error) {
146337
147081
  result2.postRelease = unavailablePostReleaseChecklist2(plan, error);
146338
147082
  }
@@ -146357,8 +147101,14 @@ if (command === "verify") {
146357
147101
  } else if (result2.postRelease?.merge.promptRequired) {
146358
147102
  console.log("Post-release: ask whether to merge the remaining branch.");
146359
147103
  }
146360
- if (result2.postRelease?.localHostUpdate.promptRequired) {
147104
+ if (result2.postRelease?.localHostUpdate?.available === true && typeof result2.postRelease.localHostUpdate.runPath === "string") {
146361
147105
  console.log(`Post-release: ask whether to update local host plugins (${result2.postRelease.localHostUpdate.hosts.join(", ")}).`);
147106
+ console.log(`Post-release command: release-skill post-release --plan ${planPath} --run ${result2.postRelease.localHostUpdate.runPath}`);
147107
+ console.log("Choose --hosts before adding --update-local-hosts to perform a local update.");
147108
+ } else {
147109
+ for (const step of result2.postRelease?.localHostUpdate?.nextSteps ?? []) {
147110
+ console.log(`Next [${step.code}] ${step.message} (${step.argv.join(" ")})`);
147111
+ }
146362
147112
  }
146363
147113
  }
146364
147114
  await exitAfterFlush(result2.status === "VERIFIED" ? 0 : 1);
@@ -146367,6 +147117,7 @@ if (command === "verify") {
146367
147117
  const errOutput = {
146368
147118
  error: err.code ?? "UNKNOWN_ERROR",
146369
147119
  message: err.message,
147120
+ details: err.details ?? {},
146370
147121
  exitCode: err.exitCode ?? 1
146371
147122
  };
146372
147123
  console.log(JSON.stringify(errOutput));
@@ -146376,6 +147127,117 @@ if (command === "verify") {
146376
147127
  await exitAfterFlush(err.exitCode ?? 1);
146377
147128
  }
146378
147129
  }
147130
+ if (command === "postverify") {
147131
+ if (args.includes("--help") || args.includes("-h")) {
147132
+ const helpText = `release-skill postverify - Run approved postVerify hooks from a VERIFIED run
147133
+
147134
+ Usage:
147135
+ release-skill postverify [--root <path>] --plan <path> --approval <path> --run <path> [options]
147136
+
147137
+ Required options:
147138
+ --plan <path> Path to the release plan file
147139
+ --approval <path> Path to the release-level approval record
147140
+ --run <path> Path to the VERIFIED verify run file or directory
147141
+
147142
+ Optional options:
147143
+ --root <path> Project root directory (default: cwd)
147144
+ --hook-approval <path> Checkpoint approval for a requiresApproval postVerify hook (repeatable)
147145
+ --json Output results as JSON
147146
+ -h, --help Show this help message and exit
147147
+
147148
+ This command creates an independent postVerify run and never reads or writes ship state.`;
147149
+ if (hasJson) {
147150
+ console.log(JSON.stringify({
147151
+ command: "postverify",
147152
+ status: "HELP",
147153
+ usage: "release-skill postverify [--root <path>] --plan <path> --approval <path> --run <path> [options]",
147154
+ options: {
147155
+ "--root": "Project root directory (default: cwd)",
147156
+ "--plan": "Path to the release plan file (required)",
147157
+ "--approval": "Path to the release-level approval record (required)",
147158
+ "--run": "Path to the VERIFIED verify run file or directory (required)",
147159
+ "--hook-approval": "Checkpoint approval for a requiresApproval postVerify hook (repeatable)",
147160
+ "--json": "Output results as JSON",
147161
+ "-h, --help": "Show this help message and exit"
147162
+ },
147163
+ message: "Creates an independent postVerify run and never reads or writes ship state."
147164
+ }, null, 2));
147165
+ } else {
147166
+ console.log(helpText);
147167
+ }
147168
+ await exitAfterFlush(0);
147169
+ }
147170
+ let malformedValuedFlag;
147171
+ const value = /* @__PURE__ */ __name((flag) => {
147172
+ let firstValue;
147173
+ for (let i = 0; i < args.length; i += 1) {
147174
+ if (args[i] !== flag) continue;
147175
+ const next = args[i + 1];
147176
+ if (!next || next.startsWith("-")) {
147177
+ malformedValuedFlag ??= flag;
147178
+ continue;
147179
+ }
147180
+ firstValue ??= next;
147181
+ }
147182
+ return firstValue;
147183
+ }, "value");
147184
+ const root = resolve41(value("--root") ?? process.cwd());
147185
+ const planPath = value("--plan");
147186
+ const approvalPath = value("--approval");
147187
+ const runPath = value("--run");
147188
+ const postpublishApprovalPaths = [];
147189
+ for (let i = 0; i < args.length; i += 1) {
147190
+ if (args[i] === "--hook-approval") {
147191
+ const next = args[i + 1];
147192
+ if (!next || next.startsWith("-")) {
147193
+ malformedValuedFlag ??= "--hook-approval";
147194
+ } else {
147195
+ postpublishApprovalPaths.push(resolve41(next));
147196
+ }
147197
+ }
147198
+ }
147199
+ if (malformedValuedFlag || !planPath || !approvalPath || !runPath) {
147200
+ const msg = "postverify requires --plan <path>, --approval <path>, and --run <path>";
147201
+ if (hasJson) {
147202
+ console.log(JSON.stringify({ error: MISSING_PARAMETERS, message: msg, exitCode: EXIT_CODE_MAP[MISSING_PARAMETERS] }));
147203
+ } else {
147204
+ console.error(`Error: ${msg}`);
147205
+ }
147206
+ await exitAfterFlush(EXIT_CODE_MAP[MISSING_PARAMETERS]);
147207
+ }
147208
+ try {
147209
+ const { postVerifyRelease: postVerifyRelease2 } = await init_postverify().then(() => postverify_exports);
147210
+ const result2 = await postVerifyRelease2({
147211
+ planPath: resolve41(planPath),
147212
+ approvalPath: resolve41(approvalPath),
147213
+ sourceRunPath: resolve41(runPath),
147214
+ root,
147215
+ ...postpublishApprovalPaths.length > 0 ? { postpublishApprovalPaths } : {}
147216
+ });
147217
+ if (hasJson) {
147218
+ console.log(JSON.stringify(result2, null, 2));
147219
+ } else {
147220
+ console.log(`PostVerify status: ${result2.status}`);
147221
+ for (const cp4 of result2.checkpoints ?? []) {
147222
+ console.log(` ${cp4.actionId}: ${cp4.status}`);
147223
+ }
147224
+ if (result2.runPath) console.log(`PostVerify run: ${result2.runPath}`);
147225
+ }
147226
+ await exitAfterFlush(result2.status === "DISTRIBUTED" ? 0 : 1);
147227
+ } catch (err) {
147228
+ if (hasJson) {
147229
+ console.log(JSON.stringify({
147230
+ error: err.code ?? "UNKNOWN_ERROR",
147231
+ message: err.message,
147232
+ details: err.details ?? {},
147233
+ exitCode: err.exitCode ?? 1
147234
+ }));
147235
+ } else {
147236
+ console.error(`Error: ${err.message}`);
147237
+ }
147238
+ await exitAfterFlush(err.exitCode ?? 1);
147239
+ }
147240
+ }
146379
147241
  if (command === "publish") {
146380
147242
  const rootIdx = args.indexOf("--root");
146381
147243
  const rawRoot = rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : process.cwd();