release-skill 0.3.0 → 0.4.1

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 (81) hide show
  1. package/.claude-plugin/marketplace.json +6 -2
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codebuddy-plugin/plugin.json +2 -2
  4. package/.codex-plugin/plugin.json +4 -4
  5. package/.kimi-plugin/plugin.json +3 -3
  6. package/CHANGELOG.md +36 -3
  7. package/INSTALL.md +34 -110
  8. package/INSTALL.zh-CN.md +17 -83
  9. package/LICENSE +183 -18
  10. package/NOTICE +4 -2
  11. package/README.md +61 -75
  12. package/README.zh-CN.md +48 -63
  13. package/adapters/claude/.claude-plugin/marketplace.json +6 -2
  14. package/adapters/claude/.claude-plugin/plugin.json +2 -2
  15. package/adapters/claude/bin/release-skill.bundle.mjs +157 -231
  16. package/adapters/claude/schemas/release-plan.schema.json +7 -0
  17. package/adapters/claude/schemas/release-run.schema.json +43 -0
  18. package/adapters/claude/skills/release-help/SKILL.md +5 -5
  19. package/adapters/claude/skills/release-prepare/SKILL.md +6 -15
  20. package/adapters/claude/skills/release-publish/SKILL.md +5 -6
  21. package/adapters/claude/skills/release-reconcile/SKILL.md +2 -2
  22. package/adapters/claude/skills/release-setup/SKILL.md +6 -0
  23. package/adapters/claude/skills/release-verify/SKILL.md +4 -6
  24. package/adapters/codex/.codex-plugin/plugin.json +4 -4
  25. package/adapters/codex/bin/release-skill.bundle.mjs +157 -231
  26. package/adapters/codex/schemas/release-plan.schema.json +7 -0
  27. package/adapters/codex/schemas/release-run.schema.json +43 -0
  28. package/adapters/codex/skills/release-help/SKILL.md +5 -5
  29. package/adapters/codex/skills/release-prepare/SKILL.md +6 -15
  30. package/adapters/codex/skills/release-publish/SKILL.md +5 -6
  31. package/adapters/codex/skills/release-reconcile/SKILL.md +2 -2
  32. package/adapters/codex/skills/release-setup/SKILL.md +6 -0
  33. package/adapters/codex/skills/release-verify/SKILL.md +4 -6
  34. package/adapters/kimi/.kimi-plugin/plugin.json +3 -3
  35. package/adapters/kimi/bin/release-skill.bundle.mjs +157 -231
  36. package/adapters/kimi/schemas/release-plan.schema.json +7 -0
  37. package/adapters/kimi/schemas/release-run.schema.json +43 -0
  38. package/adapters/kimi/skills/release-help/SKILL.md +5 -5
  39. package/adapters/kimi/skills/release-prepare/SKILL.md +6 -15
  40. package/adapters/kimi/skills/release-publish/SKILL.md +5 -6
  41. package/adapters/kimi/skills/release-reconcile/SKILL.md +2 -2
  42. package/adapters/kimi/skills/release-setup/SKILL.md +6 -0
  43. package/adapters/kimi/skills/release-verify/SKILL.md +4 -6
  44. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +2 -2
  45. package/adapters/workbuddy/bin/release-skill.bundle.mjs +157 -231
  46. package/adapters/workbuddy/schemas/release-plan.schema.json +7 -0
  47. package/adapters/workbuddy/schemas/release-run.schema.json +43 -0
  48. package/adapters/workbuddy/skills/release-help/SKILL.md +5 -5
  49. package/adapters/workbuddy/skills/release-prepare/SKILL.md +6 -15
  50. package/adapters/workbuddy/skills/release-publish/SKILL.md +5 -6
  51. package/adapters/workbuddy/skills/release-reconcile/SKILL.md +2 -2
  52. package/adapters/workbuddy/skills/release-setup/SKILL.md +6 -0
  53. package/adapters/workbuddy/skills/release-verify/SKILL.md +4 -6
  54. package/bin/release-skill-cli.mjs +45 -48
  55. package/bin/release-skill.bundle.mjs +157 -231
  56. package/package.json +2 -2
  57. package/references/01-state-machine.md +1 -1
  58. package/references/06-adapter-contract.md +6 -5
  59. package/schemas/release-plan.schema.json +7 -0
  60. package/schemas/release-run.schema.json +43 -0
  61. package/skills/release-help/SKILL.md +5 -5
  62. package/skills/release-prepare/SKILL.md +6 -15
  63. package/skills/release-publish/SKILL.md +5 -6
  64. package/skills/release-reconcile/SKILL.md +2 -2
  65. package/skills/release-setup/SKILL.md +6 -0
  66. package/skills/release-verify/SKILL.md +4 -6
  67. package/skills-src/release-help/SKILL.md +5 -5
  68. package/skills-src/release-prepare/SKILL.md +6 -15
  69. package/skills-src/release-publish/SKILL.md +5 -6
  70. package/skills-src/release-reconcile/SKILL.md +2 -2
  71. package/skills-src/release-setup/SKILL.md +6 -0
  72. package/skills-src/release-verify/SKILL.md +4 -6
  73. package/src/adapters/npm.mjs +31 -0
  74. package/src/commands/approve.mjs +8 -6
  75. package/src/commands/hooks.mjs +4 -8
  76. package/src/commands/prepare.mjs +13 -50
  77. package/src/commands/publish.mjs +0 -8
  78. package/src/commands/reconcile.mjs +3 -37
  79. package/src/commands/ship.mjs +55 -77
  80. package/src/commands/verify.mjs +56 -19
  81. package/src/core/skill-resource-closure.mjs +7 -1
@@ -9,7 +9,7 @@ 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.3.0"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.4.1"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -24274,10 +24274,11 @@ function evaluateConsumerSkillResourceClosureReceipts(plan, receipts) {
24274
24274
  "kimi-plugin",
24275
24275
  "codebuddy-plugin"
24276
24276
  ]);
24277
+ const humanConsumerTypes = plan.humanConsumersStrategy === "manualFollowUps" ? /* @__PURE__ */ new Set(["kimi-plugin", "codebuddy-plugin"]) : /* @__PURE__ */ new Set();
24277
24278
  const expectedKeys = [];
24278
24279
  for (const unit of plan.units ?? []) {
24279
24280
  for (const distribution of unit.distributions ?? []) {
24280
- if (supported.has(distribution.type)) {
24281
+ if (supported.has(distribution.type) && !humanConsumerTypes.has(distribution.type)) {
24281
24282
  expectedKeys.push(`${unit.id}:${distribution.type}`);
24282
24283
  }
24283
24284
  }
@@ -40520,11 +40521,11 @@ var require_lib7 = __commonJS({
40520
40521
  stream.pipe(checker);
40521
40522
  stream.on("error", reject);
40522
40523
  checker.on("error", reject);
40523
- let verified2;
40524
+ let verified;
40524
40525
  checker.on("verified", (s) => {
40525
- verified2 = s;
40526
+ verified = s;
40526
40527
  });
40527
- checker.on("end", () => resolve33(verified2));
40528
+ checker.on("end", () => resolve33(verified));
40528
40529
  checker.resume();
40529
40530
  });
40530
40531
  }
@@ -64312,8 +64313,8 @@ var require_timestamp3 = __commonJS({
64312
64313
  verifySignature(key) {
64313
64314
  const signedAttrs = this.signedAttrsObj.toDER();
64314
64315
  signedAttrs[0] = 49;
64315
- const verified2 = crypto.verify(signedAttrs, key, this.signatureValue, this.signatureAlgorithm);
64316
- if (!verified2) {
64316
+ const verified = crypto.verify(signedAttrs, key, this.signatureValue, this.signatureAlgorithm);
64317
+ if (!verified) {
64317
64318
  throw new error_1.RFC3161TimestampVerificationError("signature verification failed");
64318
64319
  }
64319
64320
  }
@@ -69374,8 +69375,8 @@ var require_sct2 = __commonJS({
69374
69375
  logID: sct.logID,
69375
69376
  targetDate: sct.datetime
69376
69377
  });
69377
- const verified2 = validCTLogs.some((log) => sct.verify(preCert.buffer, log.publicKey));
69378
- if (!verified2) {
69378
+ const verified = validCTLogs.some((log) => sct.verify(preCert.buffer, log.publicKey));
69379
+ if (!verified) {
69379
69380
  throw new error_1.VerificationError({
69380
69381
  code: "CERTIFICATE_ERROR",
69381
69382
  message: "SCT verification failed"
@@ -69692,13 +69693,13 @@ var require_set2 = __commonJS({
69692
69693
  logID: entry.logId.keyId,
69693
69694
  targetDate: new Date(Number(entry.integratedTime) * 1e3)
69694
69695
  });
69695
- const verified2 = validTLogs.some((tlog) => {
69696
+ const verified = validTLogs.some((tlog) => {
69696
69697
  const payload = toVerificationPayload(entry);
69697
69698
  const data = Buffer.from(core_1.json.canonicalize(payload), "utf8");
69698
69699
  const signature = entry.inclusionPromise.signedEntryTimestamp;
69699
69700
  return core_1.crypto.verify(data, tlog.publicKey, signature);
69700
69701
  });
69701
- if (!verified2) {
69702
+ if (!verified) {
69702
69703
  throw new error_1.VerificationError({
69703
69704
  code: "TLOG_INCLUSION_PROMISE_ERROR",
69704
69705
  message: "inclusion promise could not be verified"
@@ -69736,7 +69737,7 @@ var require_tsa3 = __commonJS({
69736
69737
  serialNumber: timestamp.signerSerialNumber,
69737
69738
  issuer: timestamp.signerIssuer
69738
69739
  });
69739
- const verified2 = timestampAuthorities.some((ca) => {
69740
+ const verified = timestampAuthorities.some((ca) => {
69740
69741
  try {
69741
69742
  verifyTimestampForCA(timestamp, data, ca);
69742
69743
  return true;
@@ -69744,7 +69745,7 @@ var require_tsa3 = __commonJS({
69744
69745
  return false;
69745
69746
  }
69746
69747
  });
69747
- if (!verified2) {
69748
+ if (!verified) {
69748
69749
  throw new error_1.VerificationError({
69749
69750
  code: "TIMESTAMP_ERROR",
69750
69751
  message: "timestamp could not be verified"
@@ -71133,6 +71134,7 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71133
71134
  }
71134
71135
  let offset = 0;
71135
71136
  let manifest = null;
71137
+ let readme = null;
71136
71138
  while (offset + 512 <= data.length) {
71137
71139
  let allZero = true;
71138
71140
  for (let i = 0; i < 512; i++) {
@@ -71182,6 +71184,20 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71182
71184
  throw new Error(`tarball manifest version "${manifest.version}" does not match expected "${expected.version}"`);
71183
71185
  }
71184
71186
  }
71187
+ if (entryName.toLowerCase() === "package/readme.md") {
71188
+ if (readme) throw new Error("tarball contains conflicting package README entries");
71189
+ if (!(typeFlag === 0 || typeFlag === 48)) {
71190
+ throw new Error("tarball package README must be a regular file");
71191
+ }
71192
+ const bodyStart = offset + 512;
71193
+ const bodyEnd = bodyStart + size;
71194
+ if (bodyEnd > data.length) throw new Error("frozen npm tarball entry exceeds archive bounds");
71195
+ if (size > 10 * 1024 * 1024) throw new Error("tarball package README is unreasonably large");
71196
+ readme = {
71197
+ content: data.subarray(bodyStart, bodyEnd).toString("utf8"),
71198
+ filename: entryName.slice("package/".length)
71199
+ };
71200
+ }
71185
71201
  const nextOffset = offset + 512 + Math.ceil(size / 512) * 512;
71186
71202
  if (!Number.isSafeInteger(nextOffset) || nextOffset > data.length) {
71187
71203
  throw new Error("frozen npm tarball entry exceeds archive bounds");
@@ -71189,6 +71205,10 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71189
71205
  offset = nextOffset;
71190
71206
  }
71191
71207
  if (!manifest) throw new Error("tarball does not contain package/package.json");
71208
+ if (readme) {
71209
+ manifest.readme = readme.content;
71210
+ manifest.readmeFilename = readme.filename;
71211
+ }
71192
71212
  return manifest;
71193
71213
  }
71194
71214
  async function verifyFrozenNpmTarballIdentity(action, root) {
@@ -81249,39 +81269,16 @@ async function prepareRelease(options) {
81249
81269
  if (declaredHooks.length > 0) {
81250
81270
  await evidence.append({
81251
81271
  phase: "hook-authorization",
81252
- status: "started",
81272
+ status: "authorized",
81253
81273
  hookCount: declaredHooks.length,
81254
81274
  hooks: declaredHooks.map((h) => `${h.name}: ${h.executable} ${h.args.join(" ")}`)
81255
81275
  });
81256
- if (hooksAuthorized !== true) {
81257
- const hookList = declaredHooks.map((h) => ` - ${h.name}: executable="${h.executable}", args=[${h.args.join(", ")}], cwd="${h.cwd}"`).join("\n");
81258
- await evidence.append({
81259
- phase: "hook-authorization",
81260
- status: "denied",
81261
- reason: "hooks not explicitly authorized"
81262
- });
81263
- throw new ReleaseError(
81264
- GATE_FAILED,
81265
- `project declares ${declaredHooks.length} hook(s) that will be executed as arbitrary local processes.
81266
- These hooks are NOT sandboxed \u2014 they may write to the filesystem outside the project, access local credentials, or make network calls.
81267
- The following hooks will run:
81268
- ${hookList}
81269
-
81270
- To proceed, pass --acknowledge-hook-side-effects (CLI) or hooksAuthorized=true (API). Authorization means you accept hook side-effect risks; it does NOT make hooks safe.`,
81271
- { hookNames: declaredHooks.map((h) => h.name), hookCount: declaredHooks.length }
81272
- );
81273
- }
81274
- await evidence.append({
81275
- phase: "hook-authorization",
81276
- status: "authorized",
81277
- hookCount: declaredHooks.length
81278
- });
81279
81276
  }
81280
81277
  const declaredVerificationGates = config.verificationGates ?? [];
81281
81278
  if (declaredVerificationGates.length > 0) {
81282
81279
  await evidence.append({
81283
81280
  phase: "verification-gate-authorization",
81284
- status: "started",
81281
+ status: "authorized",
81285
81282
  gateCount: declaredVerificationGates.length,
81286
81283
  gates: declaredVerificationGates.map((gate) => ({
81287
81284
  id: gate.id,
@@ -81293,23 +81290,6 @@ To proceed, pass --acknowledge-hook-side-effects (CLI) or hooksAuthorized=true (
81293
81290
  cwd: gate.cwd
81294
81291
  }))
81295
81292
  });
81296
- if (verificationGatesAuthorized !== true) {
81297
- await evidence.append({
81298
- phase: "verification-gate-authorization",
81299
- status: "denied",
81300
- gateCount: declaredVerificationGates.length
81301
- });
81302
- throw new ReleaseError(
81303
- GATE_FAILED,
81304
- `project declares ${declaredVerificationGates.length} verification gate(s). They run local project commands without a network sandbox. To proceed, pass --acknowledge-gate-side-effects (CLI) or verificationGatesAuthorized=true (API).`,
81305
- { gateIds: declaredVerificationGates.map((gate) => gate.id) }
81306
- );
81307
- }
81308
- await evidence.append({
81309
- phase: "verification-gate-authorization",
81310
- status: "authorized",
81311
- gateCount: declaredVerificationGates.length
81312
- });
81313
81293
  }
81314
81294
  await evidence.append({ phase: "hooks", status: "started" });
81315
81295
  await runDeclaredHooks(config, realRoot, evidence, options.runHookFn ?? runHook, {
@@ -82080,6 +82060,9 @@ To proceed, pass --acknowledge-hook-side-effects (CLI) or hooksAuthorized=true (
82080
82060
  const frozenDistributionsMap = new Map(units.map((u) => [u.id, u.distributions]));
82081
82061
  const externalActions = buildExternalActions(unitResults, resolvedVersions, productionAssets, externalMarketplaceFreezes, frozenDistributionsMap);
82082
82062
  const overallSnapshotDigest = sha256Hex(snapshotDigests.join(":"));
82063
+ const hasHumanConsumerActions = externalActions.some(
82064
+ (a) => a.type === "kimi-marketplace-install" || a.type === "codebuddy-marketplace-install"
82065
+ );
82083
82066
  const plan = {
82084
82067
  planVersion: 2,
82085
82068
  status: "PREPARED",
@@ -82102,6 +82085,7 @@ To proceed, pass --acknowledge-hook-side-effects (CLI) or hooksAuthorized=true (
82102
82085
  },
82103
82086
  verificationGates: config.verificationGates ?? [],
82104
82087
  snapshotDigest: overallSnapshotDigest,
82088
+ ...hasHumanConsumerActions ? { humanConsumersStrategy: "manualFollowUps" } : {},
82105
82089
  ...production ? {
82106
82090
  production: {
82107
82091
  mode: "github-npm-v1",
@@ -82224,16 +82208,10 @@ import { resolve as resolve24 } from "node:path";
82224
82208
  async function validateDeclaredHooks(options = {}) {
82225
82209
  const {
82226
82210
  root = process.cwd(),
82227
- hooksAuthorized,
82211
+ hooksAuthorized: _hooksAuthorized,
82228
82212
  hookCache = true,
82229
82213
  runDir = resolve24(root, ".release-skill", "runs", `hooks-${Date.now()}`)
82230
82214
  } = options;
82231
- if (hooksAuthorized !== true) {
82232
- throw new ReleaseError(
82233
- GATE_FAILED,
82234
- "hooks validate executes declared project commands; pass --acknowledge-hook-side-effects"
82235
- );
82236
- }
82237
82215
  const { config, configDigest } = await loadProjectConfig({ root });
82238
82216
  await mkdir12(runDir, { recursive: true });
82239
82217
  const evidence = createEvidenceWriter({
@@ -82259,7 +82237,6 @@ var init_hooks2 = __esm({
82259
82237
  await init_config();
82260
82238
  init_evidence();
82261
82239
  await init_prepare();
82262
- init_errors();
82263
82240
  __name(validateDeclaredHooks, "validateDeclaredHooks");
82264
82241
  }
82265
82242
  });
@@ -82560,7 +82537,7 @@ function defaultClock() {
82560
82537
  async function approvePlan(options) {
82561
82538
  const {
82562
82539
  planPath,
82563
- expectedDigest,
82540
+ expectedDigest: expectedDigestInput,
82564
82541
  actor,
82565
82542
  expiresInMs = 24 * 60 * 60 * 1e3,
82566
82543
  // 24 hours
@@ -82571,9 +82548,6 @@ async function approvePlan(options) {
82571
82548
  if (!planPath || typeof planPath !== "string") {
82572
82549
  throw new ReleaseError(PLAN_DIGEST_MISMATCH, "planPath must be a non-empty string");
82573
82550
  }
82574
- if (!expectedDigest || typeof expectedDigest !== "string") {
82575
- throw new ReleaseError(PLAN_DIGEST_MISMATCH, "expectedDigest must be a non-empty string");
82576
- }
82577
82551
  if (!actor || typeof actor !== "string") {
82578
82552
  throw new ReleaseError(GATE_FAILED, "actor must be a non-empty string");
82579
82553
  }
@@ -82599,6 +82573,7 @@ async function approvePlan(options) {
82599
82573
  }
82600
82574
  const actualDigest = computePlanDigest(plan);
82601
82575
  assertImmutablePlanAuthority(planPath, plan);
82576
+ const expectedDigest = expectedDigestInput ?? actualDigest;
82602
82577
  if (actualDigest !== expectedDigest) {
82603
82578
  throw new ReleaseError(
82604
82579
  PLAN_DIGEST_MISMATCH,
@@ -83234,7 +83209,6 @@ async function publishRelease(options) {
83234
83209
  clock: clockOpt,
83235
83210
  captureBaselineFn,
83236
83211
  productionMode = false,
83237
- productionConfirmation,
83238
83212
  observePreviousPublicBaselineFn,
83239
83213
  observeRetrySleep
83240
83214
  } = options ?? {};
@@ -83291,13 +83265,6 @@ async function publishRelease(options) {
83291
83265
  if (!plan.production.assetRoot || plan.production.assetRoot === ".") {
83292
83266
  throw new ReleaseError(GATE_FAILED, "production plan requires a dedicated assetRoot");
83293
83267
  }
83294
- if (!productionConfirmation || productionConfirmation !== actualDigest) {
83295
- throw new ReleaseError(
83296
- GATE_FAILED,
83297
- "production confirmation must exactly match the current plan digest",
83298
- { planDigest: actualDigest }
83299
- );
83300
- }
83301
83268
  }
83302
83269
  await evidence.append({ phase: "safety-gate", gate: "action-completeness", status: "started" });
83303
83270
  const completenessResult = validatePlanActionCompleteness(plan);
@@ -84009,7 +83976,6 @@ async function reconcileRelease(options) {
84009
83976
  root = process.cwd(),
84010
83977
  clock: clockOpt,
84011
83978
  captureBaselineFn,
84012
- productionConfirmation,
84013
83979
  observePreviousPublicBaselineFn,
84014
83980
  observeRetrySleep
84015
83981
  } = options ?? {};
@@ -84179,21 +84145,6 @@ async function reconcileRelease(options) {
84179
84145
  { sourceRunId: sourceRun.runId, sourceRunStatus: sourceRun.status }
84180
84146
  );
84181
84147
  }
84182
- const planActionsById = new Map(
84183
- (plan.externalActions ?? []).map((action) => [action.id, action])
84184
- );
84185
- const hasNonMarketplaceRetryCandidate = sourceRun.checkpoints.some((checkpoint) => {
84186
- if (checkpoint.status === "succeeded") return false;
84187
- const action = planActionsById.get(checkpoint.actionId);
84188
- return action && !isMarketplaceAction(action.type);
84189
- });
84190
- if (plan.production?.mode === "github-npm-v1" && hasNonMarketplaceRetryCandidate && productionConfirmation !== actualDigest) {
84191
- throw new ReleaseError(
84192
- GATE_FAILED,
84193
- "production reconcile confirmation must exactly match the current plan digest before retry",
84194
- { planDigest: actualDigest }
84195
- );
84196
- }
84197
84148
  await evidence.append({
84198
84149
  phase: "safety-gate",
84199
84150
  gate: "source-run-load",
@@ -84622,13 +84573,6 @@ async function reconcileRelease(options) {
84622
84573
  }
84623
84574
  const nonMarketplaceRetries = actionsToRetry.filter((a) => !isMarketplaceAction(a.type));
84624
84575
  if (nonMarketplaceRetries.length > 0) {
84625
- if (plan.production?.mode === "github-npm-v1" && productionConfirmation !== actualDigest) {
84626
- throw new ReleaseError(
84627
- GATE_FAILED,
84628
- "production reconcile confirmation must exactly match the current plan digest before retry",
84629
- { planDigest: actualDigest, actionsToRetry: nonMarketplaceRetries.map((action) => action.id) }
84630
- );
84631
- }
84632
84576
  if (!approval) {
84633
84577
  throw new ReleaseError(
84634
84578
  GATE_FAILED,
@@ -85473,7 +85417,7 @@ async function verifyRelease(options) {
85473
85417
  runDir: runDirOpt,
85474
85418
  clock: clockOpt,
85475
85419
  npmExecutor,
85476
- verificationGatesAuthorized,
85420
+ verificationGatesAuthorized: _verificationGatesAuthorized,
85477
85421
  gateEnv,
85478
85422
  previousVerifyRun
85479
85423
  } = options ?? {};
@@ -85522,13 +85466,6 @@ async function verifyRelease(options) {
85522
85466
  await evidence.append({ phase: "verify", step: "plan-load", status: "started" });
85523
85467
  const consumerGates = (plan.verificationGates ?? []).filter((gate) => gate.phase === "consumer-verify");
85524
85468
  const configuredSmokeBins = (plan.units ?? []).flatMap((unit) => (unit.distributions ?? []).filter((distribution) => distribution.type === "npm" && distribution.smokeBin).map((distribution) => ({ unitId: unit.id, smokeBin: distribution.smokeBin })));
85525
- if ((consumerGates.length > 0 || configuredSmokeBins.length > 0) && verificationGatesAuthorized !== true) {
85526
- throw new ReleaseError(
85527
- GATE_FAILED,
85528
- `plan declares ${consumerGates.length} consumer verification gate(s) and ${configuredSmokeBins.length} npm CLI smoke process(es). They execute installed project code without an OS or network sandbox. To proceed, pass --acknowledge-gate-side-effects (CLI) or verificationGatesAuthorized=true (API).`,
85529
- { gateIds: consumerGates.map((gate) => gate.id), configuredSmokeBins }
85530
- );
85531
- }
85532
85469
  await evidence.append({ phase: "verify", step: "plan-load", status: "passed" });
85533
85470
  await evidence.append({ phase: "verify", step: "source-run-load", status: "started" });
85534
85471
  const sourceRun = await loadRun(sourceRunPath, {
@@ -85723,7 +85660,8 @@ async function verifyRelease(options) {
85723
85660
  }
85724
85661
  }
85725
85662
  }
85726
- const missingManualAttestations = await collectMissingManualAttestations({
85663
+ const isHumanConsumerFollowUp = plan.humanConsumersStrategy === "manualFollowUps";
85664
+ const missingManualAttestations = isHumanConsumerFollowUp ? [] : await collectMissingManualAttestations({
85727
85665
  actions,
85728
85666
  adapterRegistry,
85729
85667
  plan,
@@ -85744,6 +85682,7 @@ async function verifyRelease(options) {
85744
85682
  { requirements: missingManualAttestations }
85745
85683
  );
85746
85684
  }
85685
+ const manualFollowUps = [];
85747
85686
  const actionResults = await Promise.allSettled(actions.map(async (action) => {
85748
85687
  const adapterActionType = ADAPTER_ACTION_TYPE_MAP2[action.type];
85749
85688
  if (!adapterActionType) {
@@ -85766,6 +85705,33 @@ async function verifyRelease(options) {
85766
85705
  );
85767
85706
  }
85768
85707
  if (isMarketplaceAction(action.type)) {
85708
+ const isHumanConsumerPlatform = action.type === "kimi-marketplace-install" || action.type === "codebuddy-marketplace-install";
85709
+ if (isHumanConsumerFollowUp && isHumanConsumerPlatform) {
85710
+ const platform = action.type === "kimi-marketplace-install" ? "kimi" : "codebuddy";
85711
+ manualFollowUps.push({
85712
+ actionId: action.id,
85713
+ platform,
85714
+ plugin: action.parameters?.plugin,
85715
+ version: action.parameters?.version,
85716
+ unitId: action.unitId,
85717
+ verifiedBySystem: false,
85718
+ reason: "human consumer platform \u2014 installation is a manual post-release task; not verified by system"
85719
+ });
85720
+ adapterChecks.push({
85721
+ actionId: action.id,
85722
+ actionType: action.type,
85723
+ status: "SKIPPED",
85724
+ reason: "manual-follow-up"
85725
+ });
85726
+ await evidence.append({
85727
+ phase: "verify-marketplace",
85728
+ actionId: action.id,
85729
+ actionType: action.type,
85730
+ status: "SKIPPED",
85731
+ reason: "manual-follow-up"
85732
+ });
85733
+ return;
85734
+ }
85769
85735
  const marketplaceContext = {
85770
85736
  externalWritesAuthorized: false,
85771
85737
  isolatedConsumerWritesAuthorized: true,
@@ -86143,6 +86109,7 @@ async function verifyRelease(options) {
86143
86109
  gateResults: consumerGateResults,
86144
86110
  consumerVerificationReceipts,
86145
86111
  skillResourceClosureReceipts,
86112
+ ...manualFollowUps.length > 0 ? { manualFollowUps } : {},
86146
86113
  startedAt: clockFn(),
86147
86114
  finishedAt: clockFn()
86148
86115
  };
@@ -86163,7 +86130,8 @@ async function verifyRelease(options) {
86163
86130
  status: VERIFIED,
86164
86131
  adapterChecks,
86165
86132
  smokeTest,
86166
- gateResults: consumerGateResults
86133
+ gateResults: consumerGateResults,
86134
+ ...manualFollowUps.length > 0 ? { manualFollowUps } : {}
86167
86135
  };
86168
86136
  } catch (err) {
86169
86137
  await evidence.append({
@@ -86567,8 +86535,7 @@ function publicState(state) {
86567
86535
  status: state.status,
86568
86536
  statePath: state.statePath,
86569
86537
  targetVersion: state.targetVersion,
86570
- ...state.hookAuthorizationDigest ? {
86571
- hookAuthorizationDigest: state.hookAuthorizationDigest,
86538
+ ...state.hooks && state.hooks.length > 0 ? {
86572
86539
  hooks: state.hooks
86573
86540
  } : {},
86574
86541
  ...state.planPath ? {
@@ -86577,25 +86544,32 @@ function publicState(state) {
86577
86544
  evidenceDir: state.evidenceDir,
86578
86545
  warnings: state.warnings ?? []
86579
86546
  } : {},
86547
+ ...state.approvalSummary ? { approvalSummary: state.approvalSummary } : {},
86580
86548
  ...state.approvalPath ? { approvalPath: state.approvalPath } : {},
86581
86549
  ...state.sourceRunPath ? { sourceRunPath: state.sourceRunPath } : {},
86582
86550
  ...state.requirements ? { requirements: state.requirements } : {},
86551
+ ...state.manualFollowUps ? { manualFollowUps: state.manualFollowUps } : {},
86583
86552
  ...state.metadataUpdate ? { metadataUpdate: state.metadataUpdate } : {},
86584
- verificationGateAuthorizationIncludedInPlanApproval: state.status !== "NEEDS_HOOK_AUTHORIZATION",
86585
- postVerifyMetadataUpdateIncludedInPlanApproval: state.status !== "NEEDS_HOOK_AUTHORIZATION"
86553
+ verificationGateAuthorizationIncludedInPlanApproval: true,
86554
+ postVerifyMetadataUpdateIncludedInPlanApproval: true
86586
86555
  };
86587
86556
  }
86588
- function hookAuthority(loaded, targetVersion) {
86589
- const hooks = Object.keys(loaded.config.hooks ?? {}).sort();
86590
- return {
86591
- hooks,
86592
- digest: sha256Hex(canonicalJson({
86593
- kind: "release-skill-hook-authorization/v1",
86594
- configDigest: loaded.configDigest,
86595
- hooks: loaded.config.hooks ?? {},
86596
- targetVersion: targetVersion ?? null
86597
- }))
86598
- };
86557
+ async function buildApprovalSummary(planPath) {
86558
+ try {
86559
+ const plan = JSON.parse(await readFile27(planPath, "utf8"));
86560
+ const units = (plan.units ?? []).map((unit) => ({
86561
+ id: unit.id,
86562
+ targetVersion: unit.targetVersion ?? unit.version
86563
+ }));
86564
+ const actions = (plan.externalActions ?? []).map((action) => ({
86565
+ id: action.id,
86566
+ type: action.type,
86567
+ unitId: action.unitId
86568
+ }));
86569
+ return { units, actions };
86570
+ } catch {
86571
+ return { units: [], actions: [] };
86572
+ }
86599
86573
  }
86600
86574
  async function advanceShip(options = {}, injected = {}) {
86601
86575
  const root = resolve29(options.root ?? process.cwd());
@@ -86609,65 +86583,21 @@ async function advanceShip(options = {}, injected = {}) {
86609
86583
  }
86610
86584
  if (!state) {
86611
86585
  const loaded = await deps.loadProjectConfig({ root });
86612
- const authority = hookAuthority(loaded, options.targetVersion);
86613
- const hooks = authority.hooks;
86614
- const hookAuthorizationDigest = authority.digest;
86586
+ const hooks = Object.keys(loaded.config.hooks ?? {}).sort();
86615
86587
  state = {
86616
- schemaVersion: 1,
86588
+ schemaVersion: 2,
86617
86589
  root,
86618
86590
  statePath,
86619
86591
  targetVersion: options.targetVersion ?? null,
86620
86592
  configDigest: loaded.configDigest,
86621
86593
  hooks,
86622
- hookAuthorizationDigest,
86623
- status: hooks.length > 0 ? "NEEDS_HOOK_AUTHORIZATION" : "NEW",
86594
+ status: "NEW",
86624
86595
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
86625
86596
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
86626
86597
  };
86627
86598
  await writeJsonAtomic(statePath, state);
86628
- if (hooks.length > 0 && options.hookAuthorizationDigest !== hookAuthorizationDigest) {
86629
- return publicState(state);
86630
- }
86631
86599
  }
86632
86600
  if (state.status === "NEEDS_HOOK_AUTHORIZATION") {
86633
- const loaded = await deps.loadProjectConfig({ root });
86634
- const current = hookAuthority(loaded, state.targetVersion);
86635
- if (loaded.configDigest !== state.configDigest || current.digest !== state.hookAuthorizationDigest) {
86636
- state = {
86637
- ...state,
86638
- configDigest: loaded.configDigest,
86639
- hooks: current.hooks,
86640
- hookAuthorizationDigest: current.digest,
86641
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
86642
- };
86643
- await writeJsonAtomic(statePath, state);
86644
- if (verified.status === "VERIFIED" && deps.updatePreviousPublicBaselines) {
86645
- try {
86646
- state.metadataUpdate = await deps.updatePreviousPublicBaselines({
86647
- root,
86648
- planPath: state.planPath
86649
- });
86650
- } catch (error) {
86651
- state.metadataUpdate = {
86652
- status: "FAILED",
86653
- error: error.message
86654
- };
86655
- }
86656
- state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
86657
- await writeJsonAtomic(statePath, state);
86658
- }
86659
- return publicState(state);
86660
- }
86661
- if (options.hookAuthorizationDigest !== state.hookAuthorizationDigest) {
86662
- if (options.hookAuthorizationDigest) {
86663
- throw new ReleaseError(
86664
- PLAN_DIGEST_MISMATCH,
86665
- "hook authorization digest does not match the current config, hooks and target version"
86666
- );
86667
- }
86668
- return publicState(state);
86669
- }
86670
- state.hookAuthorizedBy = options.actor ?? null;
86671
86601
  state.status = "NEW";
86672
86602
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
86673
86603
  await writeJsonAtomic(statePath, state);
@@ -86675,17 +86605,15 @@ async function advanceShip(options = {}, injected = {}) {
86675
86605
  if (state.status === "NEW") {
86676
86606
  const loaded = await deps.loadProjectConfig({ root });
86677
86607
  if (loaded.configDigest !== state.configDigest) {
86678
- const current = hookAuthority(loaded, state.targetVersion);
86608
+ const hooks = Object.keys(loaded.config.hooks ?? {}).sort();
86679
86609
  state = {
86680
86610
  ...state,
86681
86611
  configDigest: loaded.configDigest,
86682
- hooks: current.hooks,
86683
- hookAuthorizationDigest: current.digest,
86684
- status: current.hooks.length > 0 ? "NEEDS_HOOK_AUTHORIZATION" : "NEW",
86612
+ hooks,
86613
+ status: "NEW",
86685
86614
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
86686
86615
  };
86687
86616
  await writeJsonAtomic(statePath, state);
86688
- if (state.status === "NEEDS_HOOK_AUTHORIZATION") return publicState(state);
86689
86617
  }
86690
86618
  const prepared = await deps.prepareRelease({
86691
86619
  root,
@@ -86693,7 +86621,7 @@ async function advanceShip(options = {}, injected = {}) {
86693
86621
  offline: false,
86694
86622
  production: true,
86695
86623
  hooksAuthorized: true,
86696
- verificationGatesAuthorized: false,
86624
+ verificationGatesAuthorized: true,
86697
86625
  hookCache: true
86698
86626
  });
86699
86627
  let transportPreflight = null;
@@ -86702,6 +86630,7 @@ async function advanceShip(options = {}, injected = {}) {
86702
86630
  transportPreflight = await deps.preflightGitTransports(frozenPlan);
86703
86631
  process.env.RELEASE_SKILL_GIT_TRANSPORT = transportPreflight.transport;
86704
86632
  }
86633
+ const approvalSummary = await buildApprovalSummary(prepared.planPath);
86705
86634
  state = {
86706
86635
  ...state,
86707
86636
  status: "NEEDS_PLAN_APPROVAL",
@@ -86709,6 +86638,7 @@ async function advanceShip(options = {}, injected = {}) {
86709
86638
  planDigest: prepared.planDigest,
86710
86639
  evidenceDir: prepared.evidenceDir,
86711
86640
  warnings: prepared.warnings,
86641
+ approvalSummary,
86712
86642
  ...transportPreflight ? {
86713
86643
  gitTransport: transportPreflight.transport,
86714
86644
  gitTransportPreflight: transportPreflight.repositories
@@ -86718,8 +86648,10 @@ async function advanceShip(options = {}, injected = {}) {
86718
86648
  await writeJsonAtomic(statePath, state);
86719
86649
  }
86720
86650
  if (state.status === "NEEDS_PLAN_APPROVAL") {
86721
- if (!options.planApprovalDigest) return publicState(state);
86722
- if (options.planApprovalDigest !== state.planDigest) {
86651
+ const approveRequested = options.approve === true || typeof options.planApprovalDigest === "string" && options.planApprovalDigest.length > 0;
86652
+ if (!approveRequested) return publicState(state);
86653
+ if (options.approve === true) {
86654
+ } else if (options.planApprovalDigest !== state.planDigest) {
86723
86655
  throw new ReleaseError(PLAN_DIGEST_MISMATCH, "ship plan approval digest does not match the frozen plan");
86724
86656
  }
86725
86657
  if (!options.actor) {
@@ -86751,8 +86683,7 @@ async function advanceShip(options = {}, injected = {}) {
86751
86683
  approvalPath: state.approvalPath,
86752
86684
  adapterRegistry: options.adapterRegistry,
86753
86685
  root,
86754
- productionMode: true,
86755
- productionConfirmation: state.planDigest
86686
+ productionMode: true
86756
86687
  });
86757
86688
  state = {
86758
86689
  ...state,
@@ -86768,8 +86699,7 @@ async function advanceShip(options = {}, injected = {}) {
86768
86699
  sourceRunPath: state.sourceRunPath,
86769
86700
  approvalPath: state.approvalPath,
86770
86701
  adapterRegistry: options.adapterRegistry,
86771
- root,
86772
- productionConfirmation: state.planDigest
86702
+ root
86773
86703
  });
86774
86704
  state = {
86775
86705
  ...state,
@@ -86781,7 +86711,7 @@ async function advanceShip(options = {}, injected = {}) {
86781
86711
  }
86782
86712
  if (state.status === "PUBLISHED" || state.status === "NEEDS_MANUAL_ATTESTATIONS") {
86783
86713
  try {
86784
- const verified2 = await deps.verifyRelease({
86714
+ const verified = await deps.verifyRelease({
86785
86715
  planPath: state.planPath,
86786
86716
  sourceRunPath: state.sourceRunPath,
86787
86717
  adapterRegistry: options.adapterRegistry,
@@ -86790,9 +86720,10 @@ async function advanceShip(options = {}, injected = {}) {
86790
86720
  });
86791
86721
  state = {
86792
86722
  ...state,
86793
- status: verified2.status,
86794
- verifyRunPath: verified2.runPath,
86723
+ status: verified.status,
86724
+ verifyRunPath: verified.runPath,
86795
86725
  requirements: void 0,
86726
+ manualFollowUps: verified.manualFollowUps ?? void 0,
86796
86727
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
86797
86728
  };
86798
86729
  await writeJsonAtomic(statePath, state);
@@ -86817,7 +86748,7 @@ var init_ship = __esm({
86817
86748
  __name(readState, "readState");
86818
86749
  __name(defaultDependencies, "defaultDependencies");
86819
86750
  __name(publicState, "publicState");
86820
- __name(hookAuthority, "hookAuthority");
86751
+ __name(buildApprovalSummary, "buildApprovalSummary");
86821
86752
  __name(advanceShip, "advanceShip");
86822
86753
  }
86823
86754
  });
@@ -90993,13 +90924,13 @@ async function performEnvironmentChecks() {
90993
90924
  checks.kimi = {
90994
90925
  ...kimiCheck,
90995
90926
  required: false,
90996
- usage: "\u4EC5\u5F53\u8BA1\u5212\u58F0\u660E kimi-plugin distribution \u65F6\u7528\u4E8E\u6D88\u8D39\u8005\u5B89\u88C5\u9A8C\u8BC1"
90927
+ usage: "\u4EC5\u4F9B\u53D1\u5E03\u540E\u7684\u4EBA\u5DE5\u5B89\u88C5\u4F7F\u7528\uFF1Brelease-skill \u4E0D\u6838\u9A8C Kimi \u5B89\u88C5"
90997
90928
  };
90998
90929
  const codebuddyCheck = await checkCodeBuddyDependency();
90999
90930
  checks.codebuddy = {
91000
90931
  ...codebuddyCheck,
91001
90932
  required: false,
91002
- usage: "\u4EC5\u5F53\u8BA1\u5212\u58F0\u660E codebuddy-plugin distribution \u65F6\u7528\u4E8E\u6D88\u8D39\u8005\u5B89\u88C5\u9A8C\u8BC1\uFF08\u4EBA\u5DE5 attestation \u95ED\u73AF\uFF1BCLI \u65E0\u6CD5\u9489\u6B7B\u51BB\u7ED3 ref\uFF0C\u5B89\u88C5\u7531\u4EBA\u5DE5\u5B8C\u6210\u5E76\u51FA\u5177 attestation\uFF09"
90933
+ usage: "\u4EC5\u4F9B\u53D1\u5E03\u540E\u7684\u4EBA\u5DE5\u5B89\u88C5\u4F7F\u7528\uFF1Brelease-skill \u4E0D\u6838\u9A8C CodeBuddy/WorkBuddy \u5B89\u88C5"
91003
90934
  };
91004
90935
  return checks;
91005
90936
  }
@@ -91019,7 +90950,7 @@ function getCapabilityMaturity() {
91019
90950
  prepare: {
91020
90951
  available: true,
91021
90952
  mode: "offline local writes",
91022
- description: "Freeze a release plan with snapshots and gates"
90953
+ description: "Freeze a release plan with snapshots and gates; the command invocation authorizes hook and gate execution"
91023
90954
  },
91024
90955
  docs: {
91025
90956
  available: true,
@@ -91029,7 +90960,7 @@ function getCapabilityMaturity() {
91029
90960
  publish: {
91030
90961
  available: true,
91031
90962
  mode: "controlled production (protocol-tested; no OS/network sandbox)",
91032
- description: "Publishes frozen GitHub/npm artifacts and runs configured Claude/Codex/Kimi/CodeBuddy consumer checkpoints (CodeBuddy via human attestation closed loop) with approval and exact digest confirmation"
90963
+ description: "Publishes frozen GitHub/npm artifacts after one readable-plan approval, runs automated Claude/Codex checkpoints, and emits non-blocking Kimi/CodeBuddy manual follow-ups"
91033
90964
  },
91034
90965
  reconcile: {
91035
90966
  available: true,
@@ -91039,7 +90970,7 @@ function getCapabilityMaturity() {
91039
90970
  verify: {
91040
90971
  available: true,
91041
90972
  mode: "fresh consumer verification (protocol-tested; no OS/network sandbox)",
91042
- description: "Recheck remote state, exact npm installation, CLI help, and configured Claude/Codex/Kimi/CodeBuddy installs (CodeBuddy via human attestation) before VERIFIED"
90973
+ description: "Recheck remote state, exact npm installation, CLI help, and automated Claude/Codex installs before VERIFIED; Kimi/CodeBuddy remain unverified manual follow-ups"
91043
90974
  }
91044
90975
  };
91045
90976
  }
@@ -91056,11 +90987,11 @@ Commands:
91056
90987
  assess Read-only assessment of project release readiness
91057
90988
  prepare Freeze a release plan (release-skill output to .release-skill/; hooks may do remote ops)
91058
90989
  approve Record local approval for a frozen release plan
91059
- publish Publish frozen GitHub/npm artifacts after approval and digest confirmation
90990
+ publish Publish frozen GitHub/npm artifacts after approval
91060
90991
  reconcile Resume PARTIAL state from evidence; conflicts require a human
91061
90992
  verify Fresh remote and consumer verification; only this reaches VERIFIED
91062
90993
  ship Resume one durable prepare -> approve -> publish -> verify flow
91063
- attest Record a generated Kimi/CodeBuddy manual result without editing JSON
90994
+ attest Legacy only: record a Kimi/CodeBuddy result for an old frozen plan
91064
90995
  hooks Run declared development hooks and populate reusable receipts
91065
90996
  artifacts Artifact status, inspect, update/apply, resolution, and diagnostics
91066
90997
  docs Refresh declared release documents (read-only dry-run by default)
@@ -91071,7 +91002,6 @@ Options:
91071
91002
  --run <path> Path to the release run file (required for reconcile/verify)
91072
91003
  --approval <path> Path to the approval record
91073
91004
  --production Prepare immutable Git/npm production artifacts
91074
- --confirm-production <digest> Confirm the exact production plan digest
91075
91005
  --output <path> Override prepare/approve output path (non-production only)
91076
91006
  --run-dir <path> Override prepare run directory; production requires one direct child of .release-skill/runs
91077
91007
  --answers <path> Human-reviewed setup answers JSON
@@ -91080,16 +91010,13 @@ Options:
91080
91010
  --unit <id> Release unit whose declared release documents are refreshed (docs refresh)
91081
91011
  --confirm-refresh <sha256:...> Confirm the exact dry-run refreshDigest before any document write
91082
91012
  --ack-local-document-write Acknowledge the explicit local release-document write (docs refresh --write)
91083
- --acknowledge-hook-side-effects Acknowledge unsandboxed legacy hook execution
91084
- --acknowledge-gate-side-effects Acknowledge unsandboxed local verification gate execution
91085
- --platform <id> Manual attestation platform: kimi or codebuddy
91013
+ --platform <id> Legacy attestation platform: kimi or codebuddy
91086
91014
  --plugin <id> Plugin id for a generated manual requirement
91087
91015
  --actor <name> Person confirming an approval or manual result
91088
91016
  --result <value> Manual result: passed or failed
91089
91017
  --install-path <path> Actual managed plugin path when closure verification requires it
91090
91018
  --install-channel <desktop|cli> CodeBuddy installation channel when required
91091
- --authorize-hooks <digest> Authorize the exact ship hook/config digest
91092
- --approve-plan <digest> Approve the exact ship plan and its verification gates
91019
+ --approve Approve the ship plan (boolean; plan digest is auto-resolved)
91093
91020
  --state <path> Override the durable ship state file
91094
91021
  --no-hook-cache Force every prepare hook to run in full; neither read nor write the hook cache
91095
91022
  --json Output results as JSON
@@ -91098,14 +91025,16 @@ Options:
91098
91025
 
91099
91026
  Safety:
91100
91027
  Safe default: help -> setup (when config is absent) -> assess -> prepare --offline -> human review.
91101
- Production happy end: prepare --production -> approve -> publish -> verify.
91028
+ Production happy end: ship --target-version <ver> -> ship --approve --actor <name>.
91029
+ The ship command runs configured hooks and gates automatically; the only human gate is plan approval.
91030
+ Kimi/CodeBuddy installations are non-blocking manual follow-up tasks (not verified by system).
91102
91031
  prepare copies current public files into a local snapshot; it does not rewrite source files.
91103
91032
  - Default mode is offline (release-skill pipeline does no remote writes)
91104
91033
  - prepare output goes to .release-skill/ directory only
91105
91034
  - User-configured hooks may write anywhere and perform remote operations
91106
91035
  - To ensure zero remote writes, disable hooks or audit them separately
91107
91036
  - docs refresh --write rewrites only declared README managed regions and the current CHANGELOG entry after exact refreshDigest confirmation; it never commits, pushes, tags, publishes, or installs.
91108
- - publish requires explicit approval and an exact plan-digest confirmation
91037
+ - publish requires explicit approval; plan digest is auto-read from the plan file
91109
91038
  - publish consumes frozen Git/npm artifacts, never the live workspace
91110
91039
  - existing remote objects and uncertain checks stop for human intervention
91111
91040
  - production-equivalent protocol sandbox is verified; a real remote canary is not
@@ -91176,8 +91105,8 @@ if (!command || command === "help") {
91176
91105
  conditionalConsumers: {
91177
91106
  claude: "\u58F0\u660E claude-plugin distribution \u65F6\u5FC5\u987B\u53EF\u7528",
91178
91107
  codex: "\u58F0\u660E codex-plugin distribution \u65F6\u5FC5\u987B\u53EF\u7528",
91179
- kimi: "\u58F0\u660E kimi-plugin distribution \u65F6\u5FC5\u987B\u53EF\u7528",
91180
- codebuddy: "\u58F0\u660E codebuddy-plugin distribution \u65F6\u7528\u4E8E\u4EBA\u5DE5 attestation \u95ED\u73AF\uFF08CLI \u65E0\u6CD5\u9489\u6B7B\u51BB\u7ED3 ref\uFF0C\u5B89\u88C5\u7531\u4EBA\u5DE5\u5B8C\u6210\u5E76\u51FA\u5177 attestation\uFF09"
91108
+ kimi: "\u53D1\u5E03\u540E\u4EBA\u5DE5\u5B89\u88C5\u5F85\u529E\uFF1B\u4E0D\u5F71\u54CD\u751F\u4EA7\u53D1\u5E03\u5C31\u7EEA\u5EA6\uFF0C\u7CFB\u7EDF\u4E0D\u6838\u9A8C",
91109
+ codebuddy: "\u53D1\u5E03\u540E\u4EBA\u5DE5\u5B89\u88C5\u5F85\u529E\uFF1B\u4E0D\u5F71\u54CD\u751F\u4EA7\u53D1\u5E03\u5C31\u7EEA\u5EA6\uFF0C\u7CFB\u7EDF\u4E0D\u6838\u9A8C"
91181
91110
  }
91182
91111
  }
91183
91112
  },
@@ -91186,12 +91115,12 @@ if (!command || command === "help") {
91186
91115
  maturity: {
91187
91116
  setup: "read-only by default; create-once requires answers plus exact setupDigest confirmation",
91188
91117
  assess: "read-only (default); --output writes local report",
91189
- prepare: "offline local writes; configured hooks/gates require their explicit side-effect acknowledgements",
91118
+ prepare: "offline local writes; the command invocation authorizes configured hook and gate execution",
91190
91119
  docs: "read-only dry-run by default; write requires --write, exact --confirm-refresh, and --ack-local-document-write; never commits, pushes, or publishes",
91191
91120
  onlinePrepare: "previous-public-baseline observation available; production mode freezes publish artifacts and fails closed on drift or unknown state",
91192
- publish: "GitHub/npm plus configured Claude/Codex/Kimi/CodeBuddy consumer checkpoints are protocol-tested without an OS/network sandbox (CodeBuddy via human attestation closed loop); approval and exact digest confirmation required",
91121
+ publish: "GitHub/npm plus automated Claude/Codex consumer checkpoints are protocol-tested without an OS/network sandbox; one approval is required and the internal plan digest is checked automatically",
91193
91122
  reconcile: "PARTIAL recovery is protocol-tested without an OS/network sandbox; remote conflicts require human intervention",
91194
- verify: "fresh exact npm and Claude/Codex/Kimi/CodeBuddy consumer installation checks are protocol-tested without an OS/network sandbox (CodeBuddy via human attestation); configured consumer processes require explicit acknowledgement; success reaches VERIFIED"
91123
+ verify: "fresh exact npm and Claude/Codex consumer installation checks are protocol-tested without an OS/network sandbox; Kimi/CodeBuddy are unverified manual follow-ups; command invocation authorizes configured gates"
91195
91124
  },
91196
91125
  recommendations: []
91197
91126
  };
@@ -91218,12 +91147,6 @@ if (!command || command === "help") {
91218
91147
  if (!checks.codex.available) {
91219
91148
  output.recommendations.push("Install Codex CLI before releasing a configured codex-plugin distribution");
91220
91149
  }
91221
- if (!checks.kimi.available) {
91222
- output.recommendations.push("Install Kimi Code CLI before releasing a configured kimi-plugin distribution");
91223
- }
91224
- if (!checks.codebuddy.available) {
91225
- output.recommendations.push("Install WorkBuddy (bundled codebuddy CLI) before releasing a configured codebuddy-plugin distribution; the install is a manual attestation closed loop because the CLI cannot pin a frozen ref");
91226
- }
91227
91150
  console.log(JSON.stringify(output, null, 2));
91228
91151
  process.exit(readiness.status === "READY" ? 0 : 1);
91229
91152
  } else {
@@ -91325,6 +91248,7 @@ if (command === "hooks") {
91325
91248
  const { validateDeclaredHooks: validateDeclaredHooks2 } = await init_hooks2().then(() => hooks_exports);
91326
91249
  const result = await validateDeclaredHooks2({
91327
91250
  root,
91251
+ // --acknowledge-hook-side-effects is accepted as a no-effect compatibility input
91328
91252
  hooksAuthorized: args.includes("--acknowledge-hook-side-effects"),
91329
91253
  hookCache: !args.includes("--no-hook-cache")
91330
91254
  });
@@ -91378,7 +91302,7 @@ if (command === "ship") {
91378
91302
  root,
91379
91303
  statePath: value("--state"),
91380
91304
  targetVersion: value("--target-version") ?? value("--version"),
91381
- hookAuthorizationDigest: value("--authorize-hooks"),
91305
+ approve: args.includes("--approve"),
91382
91306
  planApprovalDigest: value("--approve-plan"),
91383
91307
  actor: value("--actor"),
91384
91308
  adapterRegistry
@@ -91388,11 +91312,11 @@ if (command === "ship") {
91388
91312
  } else {
91389
91313
  console.log(`Ship status: ${result.status}`);
91390
91314
  console.log(`State: ${result.statePath}`);
91391
- if (result.hookAuthorizationDigest && result.status === "NEEDS_HOOK_AUTHORIZATION") {
91392
- console.log(`Authorize hooks: --authorize-hooks ${result.hookAuthorizationDigest}`);
91393
- }
91394
91315
  if (result.planDigest && result.status === "NEEDS_PLAN_APPROVAL") {
91395
- console.log(`Approve plan and verification gates: --approve-plan ${result.planDigest} --actor <person>`);
91316
+ console.log(`Approve plan: ship --approve --actor <person>`);
91317
+ }
91318
+ for (const followUp of result.manualFollowUps ?? []) {
91319
+ console.log(`Manual follow-up [${followUp.platform}] ${followUp.plugin}: not verified by system`);
91396
91320
  }
91397
91321
  for (const requirement of result.requirements ?? []) {
91398
91322
  console.log(`Manual requirement [${requirement.platform}]: ${requirement.requirementPath}`);
@@ -91542,8 +91466,8 @@ if (command === "approve") {
91542
91466
  const actor = actorIdx !== -1 && args[actorIdx + 1] ? args[actorIdx + 1] : void 0;
91543
91467
  const outputIdx = args.indexOf("--output");
91544
91468
  const outputPath = outputIdx !== -1 && args[outputIdx + 1] ? resolve32(args[outputIdx + 1]) : void 0;
91545
- if (!planPath || !expectedDigest || !actor) {
91546
- const msg = "approve requires --plan <path>, --digest <sha256>, and --actor <name>";
91469
+ if (!planPath || !actor) {
91470
+ const msg = "approve requires --plan <path> and --actor <name>";
91547
91471
  if (hasJson) {
91548
91472
  console.log(JSON.stringify({ error: "MISSING_PARAMETERS", message: msg, exitCode: 1 }));
91549
91473
  } else {
@@ -91553,11 +91477,19 @@ if (command === "approve") {
91553
91477
  }
91554
91478
  try {
91555
91479
  const { approvePlan: approvePlan2 } = await init_approve().then(() => approve_exports);
91480
+ const { computePlanDigest: computePlanDigest2 } = await init_plan().then(() => plan_exports);
91481
+ const { readFile: readFileFs } = await import("node:fs/promises");
91482
+ let resolvedDigest = expectedDigest;
91483
+ if (!resolvedDigest) {
91484
+ const planRaw = await readFileFs(resolve32(planPath), "utf8");
91485
+ const planObj = JSON.parse(planRaw);
91486
+ resolvedDigest = computePlanDigest2(planObj);
91487
+ }
91556
91488
  const resolvedPlanPath = resolve32(planPath);
91557
91489
  const planDir = dirname16(resolvedPlanPath);
91558
- const releaseDir = basename10(planDir) === "plans" && basename10(resolvedPlanPath) === `${expectedDigest}.json` ? dirname16(planDir) : planDir;
91490
+ const releaseDir = basename10(planDir) === "plans" && basename10(resolvedPlanPath) === `${resolvedDigest}.json` ? dirname16(planDir) : planDir;
91559
91491
  const approvalPath = outputPath ?? join30(releaseDir, "approval-record.json");
91560
- const record = await approvePlan2({ planPath, expectedDigest, actor, outputPath: approvalPath });
91492
+ const record = await approvePlan2({ planPath, expectedDigest: resolvedDigest, actor, outputPath: approvalPath });
91561
91493
  if (hasJson) {
91562
91494
  console.log(JSON.stringify(record, null, 2));
91563
91495
  } else {
@@ -91590,8 +91522,6 @@ if (command === "reconcile") {
91590
91522
  const runPath = runIdx !== -1 && args[runIdx + 1] ? resolve32(args[runIdx + 1]) : void 0;
91591
91523
  const approvalIdx = args.indexOf("--approval");
91592
91524
  const approvalPath = approvalIdx !== -1 && args[approvalIdx + 1] ? resolve32(args[approvalIdx + 1]) : void 0;
91593
- const confirmationIdx = args.indexOf("--confirm-production");
91594
- const productionConfirmation = confirmationIdx !== -1 && args[confirmationIdx + 1] ? args[confirmationIdx + 1] : void 0;
91595
91525
  if (!planPath || !runPath) {
91596
91526
  const msg = "reconcile requires --plan <path> and --run <path>";
91597
91527
  if (hasJson) {
@@ -91619,8 +91549,7 @@ if (command === "reconcile") {
91619
91549
  sourceRunPath: runPath,
91620
91550
  approvalPath,
91621
91551
  adapterRegistry: registry,
91622
- root,
91623
- productionConfirmation
91552
+ root
91624
91553
  });
91625
91554
  if (hasJson) {
91626
91555
  console.log(JSON.stringify(result, null, 2));
@@ -91713,10 +91642,8 @@ if (command === "publish") {
91713
91642
  const planPath = planIdx !== -1 && args[planIdx + 1] ? resolve32(args[planIdx + 1]) : void 0;
91714
91643
  const approvalIdx = args.indexOf("--approval");
91715
91644
  const approvalPath = approvalIdx !== -1 && args[approvalIdx + 1] ? resolve32(args[approvalIdx + 1]) : void 0;
91716
- const confirmationIdx = args.indexOf("--confirm-production");
91717
- const productionConfirmation = confirmationIdx !== -1 && args[confirmationIdx + 1] ? args[confirmationIdx + 1] : void 0;
91718
- if (!planPath || !approvalPath || !productionConfirmation) {
91719
- const msg = "publish requires --plan <path>, --approval <path>, and --confirm-production <plan-digest>";
91645
+ if (!planPath || !approvalPath) {
91646
+ const msg = "publish requires --plan <path> and --approval <path>";
91720
91647
  if (hasJson) {
91721
91648
  console.log(JSON.stringify({ error: "MISSING_PARAMETERS", message: msg, exitCode: 1 }));
91722
91649
  } else {
@@ -91742,8 +91669,7 @@ if (command === "publish") {
91742
91669
  approvalPath,
91743
91670
  adapterRegistry: registry,
91744
91671
  root,
91745
- productionMode: true,
91746
- productionConfirmation
91672
+ productionMode: true
91747
91673
  });
91748
91674
  if (hasJson) {
91749
91675
  console.log(JSON.stringify(result, null, 2));