release-skill 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) 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 +23 -3
  7. package/INSTALL.md +34 -110
  8. package/INSTALL.zh-CN.md +17 -83
  9. package/README.md +42 -65
  10. package/README.zh-CN.md +37 -57
  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 +138 -231
  14. package/adapters/claude/schemas/release-plan.schema.json +7 -0
  15. package/adapters/claude/schemas/release-run.schema.json +43 -0
  16. package/adapters/claude/skills/release-help/SKILL.md +5 -5
  17. package/adapters/claude/skills/release-prepare/SKILL.md +6 -15
  18. package/adapters/claude/skills/release-publish/SKILL.md +5 -6
  19. package/adapters/claude/skills/release-reconcile/SKILL.md +2 -2
  20. package/adapters/claude/skills/release-verify/SKILL.md +4 -6
  21. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  22. package/adapters/codex/bin/release-skill.bundle.mjs +138 -231
  23. package/adapters/codex/schemas/release-plan.schema.json +7 -0
  24. package/adapters/codex/schemas/release-run.schema.json +43 -0
  25. package/adapters/codex/skills/release-help/SKILL.md +5 -5
  26. package/adapters/codex/skills/release-prepare/SKILL.md +6 -15
  27. package/adapters/codex/skills/release-publish/SKILL.md +5 -6
  28. package/adapters/codex/skills/release-reconcile/SKILL.md +2 -2
  29. package/adapters/codex/skills/release-verify/SKILL.md +4 -6
  30. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  31. package/adapters/kimi/bin/release-skill.bundle.mjs +138 -231
  32. package/adapters/kimi/schemas/release-plan.schema.json +7 -0
  33. package/adapters/kimi/schemas/release-run.schema.json +43 -0
  34. package/adapters/kimi/skills/release-help/SKILL.md +5 -5
  35. package/adapters/kimi/skills/release-prepare/SKILL.md +6 -15
  36. package/adapters/kimi/skills/release-publish/SKILL.md +5 -6
  37. package/adapters/kimi/skills/release-reconcile/SKILL.md +2 -2
  38. package/adapters/kimi/skills/release-verify/SKILL.md +4 -6
  39. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  40. package/adapters/workbuddy/bin/release-skill.bundle.mjs +138 -231
  41. package/adapters/workbuddy/schemas/release-plan.schema.json +7 -0
  42. package/adapters/workbuddy/schemas/release-run.schema.json +43 -0
  43. package/adapters/workbuddy/skills/release-help/SKILL.md +5 -5
  44. package/adapters/workbuddy/skills/release-prepare/SKILL.md +6 -15
  45. package/adapters/workbuddy/skills/release-publish/SKILL.md +5 -6
  46. package/adapters/workbuddy/skills/release-reconcile/SKILL.md +2 -2
  47. package/adapters/workbuddy/skills/release-verify/SKILL.md +4 -6
  48. package/bin/release-skill-cli.mjs +45 -48
  49. package/bin/release-skill.bundle.mjs +138 -231
  50. package/package.json +1 -1
  51. package/references/01-state-machine.md +1 -1
  52. package/references/06-adapter-contract.md +6 -5
  53. package/schemas/release-plan.schema.json +7 -0
  54. package/schemas/release-run.schema.json +43 -0
  55. package/skills/release-help/SKILL.md +5 -5
  56. package/skills/release-prepare/SKILL.md +6 -15
  57. package/skills/release-publish/SKILL.md +5 -6
  58. package/skills/release-reconcile/SKILL.md +2 -2
  59. package/skills/release-verify/SKILL.md +4 -6
  60. package/skills-src/release-help/SKILL.md +5 -5
  61. package/skills-src/release-prepare/SKILL.md +6 -15
  62. package/skills-src/release-publish/SKILL.md +5 -6
  63. package/skills-src/release-reconcile/SKILL.md +2 -2
  64. package/skills-src/release-verify/SKILL.md +4 -6
  65. package/src/commands/approve.mjs +8 -6
  66. package/src/commands/hooks.mjs +4 -8
  67. package/src/commands/prepare.mjs +13 -50
  68. package/src/commands/publish.mjs +0 -8
  69. package/src/commands/reconcile.mjs +3 -37
  70. package/src/commands/ship.mjs +55 -77
  71. package/src/commands/verify.mjs +56 -19
  72. 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.0"});
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"
@@ -81249,39 +81250,16 @@ async function prepareRelease(options) {
81249
81250
  if (declaredHooks.length > 0) {
81250
81251
  await evidence.append({
81251
81252
  phase: "hook-authorization",
81252
- status: "started",
81253
+ status: "authorized",
81253
81254
  hookCount: declaredHooks.length,
81254
81255
  hooks: declaredHooks.map((h) => `${h.name}: ${h.executable} ${h.args.join(" ")}`)
81255
81256
  });
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
81257
  }
81280
81258
  const declaredVerificationGates = config.verificationGates ?? [];
81281
81259
  if (declaredVerificationGates.length > 0) {
81282
81260
  await evidence.append({
81283
81261
  phase: "verification-gate-authorization",
81284
- status: "started",
81262
+ status: "authorized",
81285
81263
  gateCount: declaredVerificationGates.length,
81286
81264
  gates: declaredVerificationGates.map((gate) => ({
81287
81265
  id: gate.id,
@@ -81293,23 +81271,6 @@ To proceed, pass --acknowledge-hook-side-effects (CLI) or hooksAuthorized=true (
81293
81271
  cwd: gate.cwd
81294
81272
  }))
81295
81273
  });
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
81274
  }
81314
81275
  await evidence.append({ phase: "hooks", status: "started" });
81315
81276
  await runDeclaredHooks(config, realRoot, evidence, options.runHookFn ?? runHook, {
@@ -82080,6 +82041,9 @@ To proceed, pass --acknowledge-hook-side-effects (CLI) or hooksAuthorized=true (
82080
82041
  const frozenDistributionsMap = new Map(units.map((u) => [u.id, u.distributions]));
82081
82042
  const externalActions = buildExternalActions(unitResults, resolvedVersions, productionAssets, externalMarketplaceFreezes, frozenDistributionsMap);
82082
82043
  const overallSnapshotDigest = sha256Hex(snapshotDigests.join(":"));
82044
+ const hasHumanConsumerActions = externalActions.some(
82045
+ (a) => a.type === "kimi-marketplace-install" || a.type === "codebuddy-marketplace-install"
82046
+ );
82083
82047
  const plan = {
82084
82048
  planVersion: 2,
82085
82049
  status: "PREPARED",
@@ -82102,6 +82066,7 @@ To proceed, pass --acknowledge-hook-side-effects (CLI) or hooksAuthorized=true (
82102
82066
  },
82103
82067
  verificationGates: config.verificationGates ?? [],
82104
82068
  snapshotDigest: overallSnapshotDigest,
82069
+ ...hasHumanConsumerActions ? { humanConsumersStrategy: "manualFollowUps" } : {},
82105
82070
  ...production ? {
82106
82071
  production: {
82107
82072
  mode: "github-npm-v1",
@@ -82224,16 +82189,10 @@ import { resolve as resolve24 } from "node:path";
82224
82189
  async function validateDeclaredHooks(options = {}) {
82225
82190
  const {
82226
82191
  root = process.cwd(),
82227
- hooksAuthorized,
82192
+ hooksAuthorized: _hooksAuthorized,
82228
82193
  hookCache = true,
82229
82194
  runDir = resolve24(root, ".release-skill", "runs", `hooks-${Date.now()}`)
82230
82195
  } = 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
82196
  const { config, configDigest } = await loadProjectConfig({ root });
82238
82197
  await mkdir12(runDir, { recursive: true });
82239
82198
  const evidence = createEvidenceWriter({
@@ -82259,7 +82218,6 @@ var init_hooks2 = __esm({
82259
82218
  await init_config();
82260
82219
  init_evidence();
82261
82220
  await init_prepare();
82262
- init_errors();
82263
82221
  __name(validateDeclaredHooks, "validateDeclaredHooks");
82264
82222
  }
82265
82223
  });
@@ -82560,7 +82518,7 @@ function defaultClock() {
82560
82518
  async function approvePlan(options) {
82561
82519
  const {
82562
82520
  planPath,
82563
- expectedDigest,
82521
+ expectedDigest: expectedDigestInput,
82564
82522
  actor,
82565
82523
  expiresInMs = 24 * 60 * 60 * 1e3,
82566
82524
  // 24 hours
@@ -82571,9 +82529,6 @@ async function approvePlan(options) {
82571
82529
  if (!planPath || typeof planPath !== "string") {
82572
82530
  throw new ReleaseError(PLAN_DIGEST_MISMATCH, "planPath must be a non-empty string");
82573
82531
  }
82574
- if (!expectedDigest || typeof expectedDigest !== "string") {
82575
- throw new ReleaseError(PLAN_DIGEST_MISMATCH, "expectedDigest must be a non-empty string");
82576
- }
82577
82532
  if (!actor || typeof actor !== "string") {
82578
82533
  throw new ReleaseError(GATE_FAILED, "actor must be a non-empty string");
82579
82534
  }
@@ -82599,6 +82554,7 @@ async function approvePlan(options) {
82599
82554
  }
82600
82555
  const actualDigest = computePlanDigest(plan);
82601
82556
  assertImmutablePlanAuthority(planPath, plan);
82557
+ const expectedDigest = expectedDigestInput ?? actualDigest;
82602
82558
  if (actualDigest !== expectedDigest) {
82603
82559
  throw new ReleaseError(
82604
82560
  PLAN_DIGEST_MISMATCH,
@@ -83234,7 +83190,6 @@ async function publishRelease(options) {
83234
83190
  clock: clockOpt,
83235
83191
  captureBaselineFn,
83236
83192
  productionMode = false,
83237
- productionConfirmation,
83238
83193
  observePreviousPublicBaselineFn,
83239
83194
  observeRetrySleep
83240
83195
  } = options ?? {};
@@ -83291,13 +83246,6 @@ async function publishRelease(options) {
83291
83246
  if (!plan.production.assetRoot || plan.production.assetRoot === ".") {
83292
83247
  throw new ReleaseError(GATE_FAILED, "production plan requires a dedicated assetRoot");
83293
83248
  }
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
83249
  }
83302
83250
  await evidence.append({ phase: "safety-gate", gate: "action-completeness", status: "started" });
83303
83251
  const completenessResult = validatePlanActionCompleteness(plan);
@@ -84009,7 +83957,6 @@ async function reconcileRelease(options) {
84009
83957
  root = process.cwd(),
84010
83958
  clock: clockOpt,
84011
83959
  captureBaselineFn,
84012
- productionConfirmation,
84013
83960
  observePreviousPublicBaselineFn,
84014
83961
  observeRetrySleep
84015
83962
  } = options ?? {};
@@ -84179,21 +84126,6 @@ async function reconcileRelease(options) {
84179
84126
  { sourceRunId: sourceRun.runId, sourceRunStatus: sourceRun.status }
84180
84127
  );
84181
84128
  }
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
84129
  await evidence.append({
84198
84130
  phase: "safety-gate",
84199
84131
  gate: "source-run-load",
@@ -84622,13 +84554,6 @@ async function reconcileRelease(options) {
84622
84554
  }
84623
84555
  const nonMarketplaceRetries = actionsToRetry.filter((a) => !isMarketplaceAction(a.type));
84624
84556
  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
84557
  if (!approval) {
84633
84558
  throw new ReleaseError(
84634
84559
  GATE_FAILED,
@@ -85473,7 +85398,7 @@ async function verifyRelease(options) {
85473
85398
  runDir: runDirOpt,
85474
85399
  clock: clockOpt,
85475
85400
  npmExecutor,
85476
- verificationGatesAuthorized,
85401
+ verificationGatesAuthorized: _verificationGatesAuthorized,
85477
85402
  gateEnv,
85478
85403
  previousVerifyRun
85479
85404
  } = options ?? {};
@@ -85522,13 +85447,6 @@ async function verifyRelease(options) {
85522
85447
  await evidence.append({ phase: "verify", step: "plan-load", status: "started" });
85523
85448
  const consumerGates = (plan.verificationGates ?? []).filter((gate) => gate.phase === "consumer-verify");
85524
85449
  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
85450
  await evidence.append({ phase: "verify", step: "plan-load", status: "passed" });
85533
85451
  await evidence.append({ phase: "verify", step: "source-run-load", status: "started" });
85534
85452
  const sourceRun = await loadRun(sourceRunPath, {
@@ -85723,7 +85641,8 @@ async function verifyRelease(options) {
85723
85641
  }
85724
85642
  }
85725
85643
  }
85726
- const missingManualAttestations = await collectMissingManualAttestations({
85644
+ const isHumanConsumerFollowUp = plan.humanConsumersStrategy === "manualFollowUps";
85645
+ const missingManualAttestations = isHumanConsumerFollowUp ? [] : await collectMissingManualAttestations({
85727
85646
  actions,
85728
85647
  adapterRegistry,
85729
85648
  plan,
@@ -85744,6 +85663,7 @@ async function verifyRelease(options) {
85744
85663
  { requirements: missingManualAttestations }
85745
85664
  );
85746
85665
  }
85666
+ const manualFollowUps = [];
85747
85667
  const actionResults = await Promise.allSettled(actions.map(async (action) => {
85748
85668
  const adapterActionType = ADAPTER_ACTION_TYPE_MAP2[action.type];
85749
85669
  if (!adapterActionType) {
@@ -85766,6 +85686,33 @@ async function verifyRelease(options) {
85766
85686
  );
85767
85687
  }
85768
85688
  if (isMarketplaceAction(action.type)) {
85689
+ const isHumanConsumerPlatform = action.type === "kimi-marketplace-install" || action.type === "codebuddy-marketplace-install";
85690
+ if (isHumanConsumerFollowUp && isHumanConsumerPlatform) {
85691
+ const platform = action.type === "kimi-marketplace-install" ? "kimi" : "codebuddy";
85692
+ manualFollowUps.push({
85693
+ actionId: action.id,
85694
+ platform,
85695
+ plugin: action.parameters?.plugin,
85696
+ version: action.parameters?.version,
85697
+ unitId: action.unitId,
85698
+ verifiedBySystem: false,
85699
+ reason: "human consumer platform \u2014 installation is a manual post-release task; not verified by system"
85700
+ });
85701
+ adapterChecks.push({
85702
+ actionId: action.id,
85703
+ actionType: action.type,
85704
+ status: "SKIPPED",
85705
+ reason: "manual-follow-up"
85706
+ });
85707
+ await evidence.append({
85708
+ phase: "verify-marketplace",
85709
+ actionId: action.id,
85710
+ actionType: action.type,
85711
+ status: "SKIPPED",
85712
+ reason: "manual-follow-up"
85713
+ });
85714
+ return;
85715
+ }
85769
85716
  const marketplaceContext = {
85770
85717
  externalWritesAuthorized: false,
85771
85718
  isolatedConsumerWritesAuthorized: true,
@@ -86143,6 +86090,7 @@ async function verifyRelease(options) {
86143
86090
  gateResults: consumerGateResults,
86144
86091
  consumerVerificationReceipts,
86145
86092
  skillResourceClosureReceipts,
86093
+ ...manualFollowUps.length > 0 ? { manualFollowUps } : {},
86146
86094
  startedAt: clockFn(),
86147
86095
  finishedAt: clockFn()
86148
86096
  };
@@ -86163,7 +86111,8 @@ async function verifyRelease(options) {
86163
86111
  status: VERIFIED,
86164
86112
  adapterChecks,
86165
86113
  smokeTest,
86166
- gateResults: consumerGateResults
86114
+ gateResults: consumerGateResults,
86115
+ ...manualFollowUps.length > 0 ? { manualFollowUps } : {}
86167
86116
  };
86168
86117
  } catch (err) {
86169
86118
  await evidence.append({
@@ -86567,8 +86516,7 @@ function publicState(state) {
86567
86516
  status: state.status,
86568
86517
  statePath: state.statePath,
86569
86518
  targetVersion: state.targetVersion,
86570
- ...state.hookAuthorizationDigest ? {
86571
- hookAuthorizationDigest: state.hookAuthorizationDigest,
86519
+ ...state.hooks && state.hooks.length > 0 ? {
86572
86520
  hooks: state.hooks
86573
86521
  } : {},
86574
86522
  ...state.planPath ? {
@@ -86577,25 +86525,32 @@ function publicState(state) {
86577
86525
  evidenceDir: state.evidenceDir,
86578
86526
  warnings: state.warnings ?? []
86579
86527
  } : {},
86528
+ ...state.approvalSummary ? { approvalSummary: state.approvalSummary } : {},
86580
86529
  ...state.approvalPath ? { approvalPath: state.approvalPath } : {},
86581
86530
  ...state.sourceRunPath ? { sourceRunPath: state.sourceRunPath } : {},
86582
86531
  ...state.requirements ? { requirements: state.requirements } : {},
86532
+ ...state.manualFollowUps ? { manualFollowUps: state.manualFollowUps } : {},
86583
86533
  ...state.metadataUpdate ? { metadataUpdate: state.metadataUpdate } : {},
86584
- verificationGateAuthorizationIncludedInPlanApproval: state.status !== "NEEDS_HOOK_AUTHORIZATION",
86585
- postVerifyMetadataUpdateIncludedInPlanApproval: state.status !== "NEEDS_HOOK_AUTHORIZATION"
86534
+ verificationGateAuthorizationIncludedInPlanApproval: true,
86535
+ postVerifyMetadataUpdateIncludedInPlanApproval: true
86586
86536
  };
86587
86537
  }
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
- };
86538
+ async function buildApprovalSummary(planPath) {
86539
+ try {
86540
+ const plan = JSON.parse(await readFile27(planPath, "utf8"));
86541
+ const units = (plan.units ?? []).map((unit) => ({
86542
+ id: unit.id,
86543
+ targetVersion: unit.targetVersion ?? unit.version
86544
+ }));
86545
+ const actions = (plan.externalActions ?? []).map((action) => ({
86546
+ id: action.id,
86547
+ type: action.type,
86548
+ unitId: action.unitId
86549
+ }));
86550
+ return { units, actions };
86551
+ } catch {
86552
+ return { units: [], actions: [] };
86553
+ }
86599
86554
  }
86600
86555
  async function advanceShip(options = {}, injected = {}) {
86601
86556
  const root = resolve29(options.root ?? process.cwd());
@@ -86609,65 +86564,21 @@ async function advanceShip(options = {}, injected = {}) {
86609
86564
  }
86610
86565
  if (!state) {
86611
86566
  const loaded = await deps.loadProjectConfig({ root });
86612
- const authority = hookAuthority(loaded, options.targetVersion);
86613
- const hooks = authority.hooks;
86614
- const hookAuthorizationDigest = authority.digest;
86567
+ const hooks = Object.keys(loaded.config.hooks ?? {}).sort();
86615
86568
  state = {
86616
- schemaVersion: 1,
86569
+ schemaVersion: 2,
86617
86570
  root,
86618
86571
  statePath,
86619
86572
  targetVersion: options.targetVersion ?? null,
86620
86573
  configDigest: loaded.configDigest,
86621
86574
  hooks,
86622
- hookAuthorizationDigest,
86623
- status: hooks.length > 0 ? "NEEDS_HOOK_AUTHORIZATION" : "NEW",
86575
+ status: "NEW",
86624
86576
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
86625
86577
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
86626
86578
  };
86627
86579
  await writeJsonAtomic(statePath, state);
86628
- if (hooks.length > 0 && options.hookAuthorizationDigest !== hookAuthorizationDigest) {
86629
- return publicState(state);
86630
- }
86631
86580
  }
86632
86581
  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
86582
  state.status = "NEW";
86672
86583
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
86673
86584
  await writeJsonAtomic(statePath, state);
@@ -86675,17 +86586,15 @@ async function advanceShip(options = {}, injected = {}) {
86675
86586
  if (state.status === "NEW") {
86676
86587
  const loaded = await deps.loadProjectConfig({ root });
86677
86588
  if (loaded.configDigest !== state.configDigest) {
86678
- const current = hookAuthority(loaded, state.targetVersion);
86589
+ const hooks = Object.keys(loaded.config.hooks ?? {}).sort();
86679
86590
  state = {
86680
86591
  ...state,
86681
86592
  configDigest: loaded.configDigest,
86682
- hooks: current.hooks,
86683
- hookAuthorizationDigest: current.digest,
86684
- status: current.hooks.length > 0 ? "NEEDS_HOOK_AUTHORIZATION" : "NEW",
86593
+ hooks,
86594
+ status: "NEW",
86685
86595
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
86686
86596
  };
86687
86597
  await writeJsonAtomic(statePath, state);
86688
- if (state.status === "NEEDS_HOOK_AUTHORIZATION") return publicState(state);
86689
86598
  }
86690
86599
  const prepared = await deps.prepareRelease({
86691
86600
  root,
@@ -86693,7 +86602,7 @@ async function advanceShip(options = {}, injected = {}) {
86693
86602
  offline: false,
86694
86603
  production: true,
86695
86604
  hooksAuthorized: true,
86696
- verificationGatesAuthorized: false,
86605
+ verificationGatesAuthorized: true,
86697
86606
  hookCache: true
86698
86607
  });
86699
86608
  let transportPreflight = null;
@@ -86702,6 +86611,7 @@ async function advanceShip(options = {}, injected = {}) {
86702
86611
  transportPreflight = await deps.preflightGitTransports(frozenPlan);
86703
86612
  process.env.RELEASE_SKILL_GIT_TRANSPORT = transportPreflight.transport;
86704
86613
  }
86614
+ const approvalSummary = await buildApprovalSummary(prepared.planPath);
86705
86615
  state = {
86706
86616
  ...state,
86707
86617
  status: "NEEDS_PLAN_APPROVAL",
@@ -86709,6 +86619,7 @@ async function advanceShip(options = {}, injected = {}) {
86709
86619
  planDigest: prepared.planDigest,
86710
86620
  evidenceDir: prepared.evidenceDir,
86711
86621
  warnings: prepared.warnings,
86622
+ approvalSummary,
86712
86623
  ...transportPreflight ? {
86713
86624
  gitTransport: transportPreflight.transport,
86714
86625
  gitTransportPreflight: transportPreflight.repositories
@@ -86718,8 +86629,10 @@ async function advanceShip(options = {}, injected = {}) {
86718
86629
  await writeJsonAtomic(statePath, state);
86719
86630
  }
86720
86631
  if (state.status === "NEEDS_PLAN_APPROVAL") {
86721
- if (!options.planApprovalDigest) return publicState(state);
86722
- if (options.planApprovalDigest !== state.planDigest) {
86632
+ const approveRequested = options.approve === true || typeof options.planApprovalDigest === "string" && options.planApprovalDigest.length > 0;
86633
+ if (!approveRequested) return publicState(state);
86634
+ if (options.approve === true) {
86635
+ } else if (options.planApprovalDigest !== state.planDigest) {
86723
86636
  throw new ReleaseError(PLAN_DIGEST_MISMATCH, "ship plan approval digest does not match the frozen plan");
86724
86637
  }
86725
86638
  if (!options.actor) {
@@ -86751,8 +86664,7 @@ async function advanceShip(options = {}, injected = {}) {
86751
86664
  approvalPath: state.approvalPath,
86752
86665
  adapterRegistry: options.adapterRegistry,
86753
86666
  root,
86754
- productionMode: true,
86755
- productionConfirmation: state.planDigest
86667
+ productionMode: true
86756
86668
  });
86757
86669
  state = {
86758
86670
  ...state,
@@ -86768,8 +86680,7 @@ async function advanceShip(options = {}, injected = {}) {
86768
86680
  sourceRunPath: state.sourceRunPath,
86769
86681
  approvalPath: state.approvalPath,
86770
86682
  adapterRegistry: options.adapterRegistry,
86771
- root,
86772
- productionConfirmation: state.planDigest
86683
+ root
86773
86684
  });
86774
86685
  state = {
86775
86686
  ...state,
@@ -86781,7 +86692,7 @@ async function advanceShip(options = {}, injected = {}) {
86781
86692
  }
86782
86693
  if (state.status === "PUBLISHED" || state.status === "NEEDS_MANUAL_ATTESTATIONS") {
86783
86694
  try {
86784
- const verified2 = await deps.verifyRelease({
86695
+ const verified = await deps.verifyRelease({
86785
86696
  planPath: state.planPath,
86786
86697
  sourceRunPath: state.sourceRunPath,
86787
86698
  adapterRegistry: options.adapterRegistry,
@@ -86790,9 +86701,10 @@ async function advanceShip(options = {}, injected = {}) {
86790
86701
  });
86791
86702
  state = {
86792
86703
  ...state,
86793
- status: verified2.status,
86794
- verifyRunPath: verified2.runPath,
86704
+ status: verified.status,
86705
+ verifyRunPath: verified.runPath,
86795
86706
  requirements: void 0,
86707
+ manualFollowUps: verified.manualFollowUps ?? void 0,
86796
86708
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
86797
86709
  };
86798
86710
  await writeJsonAtomic(statePath, state);
@@ -86817,7 +86729,7 @@ var init_ship = __esm({
86817
86729
  __name(readState, "readState");
86818
86730
  __name(defaultDependencies, "defaultDependencies");
86819
86731
  __name(publicState, "publicState");
86820
- __name(hookAuthority, "hookAuthority");
86732
+ __name(buildApprovalSummary, "buildApprovalSummary");
86821
86733
  __name(advanceShip, "advanceShip");
86822
86734
  }
86823
86735
  });
@@ -90993,13 +90905,13 @@ async function performEnvironmentChecks() {
90993
90905
  checks.kimi = {
90994
90906
  ...kimiCheck,
90995
90907
  required: false,
90996
- usage: "\u4EC5\u5F53\u8BA1\u5212\u58F0\u660E kimi-plugin distribution \u65F6\u7528\u4E8E\u6D88\u8D39\u8005\u5B89\u88C5\u9A8C\u8BC1"
90908
+ usage: "\u4EC5\u4F9B\u53D1\u5E03\u540E\u7684\u4EBA\u5DE5\u5B89\u88C5\u4F7F\u7528\uFF1Brelease-skill \u4E0D\u6838\u9A8C Kimi \u5B89\u88C5"
90997
90909
  };
90998
90910
  const codebuddyCheck = await checkCodeBuddyDependency();
90999
90911
  checks.codebuddy = {
91000
90912
  ...codebuddyCheck,
91001
90913
  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"
90914
+ usage: "\u4EC5\u4F9B\u53D1\u5E03\u540E\u7684\u4EBA\u5DE5\u5B89\u88C5\u4F7F\u7528\uFF1Brelease-skill \u4E0D\u6838\u9A8C CodeBuddy/WorkBuddy \u5B89\u88C5"
91003
90915
  };
91004
90916
  return checks;
91005
90917
  }
@@ -91019,7 +90931,7 @@ function getCapabilityMaturity() {
91019
90931
  prepare: {
91020
90932
  available: true,
91021
90933
  mode: "offline local writes",
91022
- description: "Freeze a release plan with snapshots and gates"
90934
+ description: "Freeze a release plan with snapshots and gates; the command invocation authorizes hook and gate execution"
91023
90935
  },
91024
90936
  docs: {
91025
90937
  available: true,
@@ -91029,7 +90941,7 @@ function getCapabilityMaturity() {
91029
90941
  publish: {
91030
90942
  available: true,
91031
90943
  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"
90944
+ 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
90945
  },
91034
90946
  reconcile: {
91035
90947
  available: true,
@@ -91039,7 +90951,7 @@ function getCapabilityMaturity() {
91039
90951
  verify: {
91040
90952
  available: true,
91041
90953
  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"
90954
+ description: "Recheck remote state, exact npm installation, CLI help, and automated Claude/Codex installs before VERIFIED; Kimi/CodeBuddy remain unverified manual follow-ups"
91043
90955
  }
91044
90956
  };
91045
90957
  }
@@ -91056,11 +90968,11 @@ Commands:
91056
90968
  assess Read-only assessment of project release readiness
91057
90969
  prepare Freeze a release plan (release-skill output to .release-skill/; hooks may do remote ops)
91058
90970
  approve Record local approval for a frozen release plan
91059
- publish Publish frozen GitHub/npm artifacts after approval and digest confirmation
90971
+ publish Publish frozen GitHub/npm artifacts after approval
91060
90972
  reconcile Resume PARTIAL state from evidence; conflicts require a human
91061
90973
  verify Fresh remote and consumer verification; only this reaches VERIFIED
91062
90974
  ship Resume one durable prepare -> approve -> publish -> verify flow
91063
- attest Record a generated Kimi/CodeBuddy manual result without editing JSON
90975
+ attest Legacy only: record a Kimi/CodeBuddy result for an old frozen plan
91064
90976
  hooks Run declared development hooks and populate reusable receipts
91065
90977
  artifacts Artifact status, inspect, update/apply, resolution, and diagnostics
91066
90978
  docs Refresh declared release documents (read-only dry-run by default)
@@ -91071,7 +90983,6 @@ Options:
91071
90983
  --run <path> Path to the release run file (required for reconcile/verify)
91072
90984
  --approval <path> Path to the approval record
91073
90985
  --production Prepare immutable Git/npm production artifacts
91074
- --confirm-production <digest> Confirm the exact production plan digest
91075
90986
  --output <path> Override prepare/approve output path (non-production only)
91076
90987
  --run-dir <path> Override prepare run directory; production requires one direct child of .release-skill/runs
91077
90988
  --answers <path> Human-reviewed setup answers JSON
@@ -91080,16 +90991,13 @@ Options:
91080
90991
  --unit <id> Release unit whose declared release documents are refreshed (docs refresh)
91081
90992
  --confirm-refresh <sha256:...> Confirm the exact dry-run refreshDigest before any document write
91082
90993
  --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
90994
+ --platform <id> Legacy attestation platform: kimi or codebuddy
91086
90995
  --plugin <id> Plugin id for a generated manual requirement
91087
90996
  --actor <name> Person confirming an approval or manual result
91088
90997
  --result <value> Manual result: passed or failed
91089
90998
  --install-path <path> Actual managed plugin path when closure verification requires it
91090
90999
  --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
91000
+ --approve Approve the ship plan (boolean; plan digest is auto-resolved)
91093
91001
  --state <path> Override the durable ship state file
91094
91002
  --no-hook-cache Force every prepare hook to run in full; neither read nor write the hook cache
91095
91003
  --json Output results as JSON
@@ -91098,14 +91006,16 @@ Options:
91098
91006
 
91099
91007
  Safety:
91100
91008
  Safe default: help -> setup (when config is absent) -> assess -> prepare --offline -> human review.
91101
- Production happy end: prepare --production -> approve -> publish -> verify.
91009
+ Production happy end: ship --target-version <ver> -> ship --approve --actor <name>.
91010
+ The ship command runs configured hooks and gates automatically; the only human gate is plan approval.
91011
+ Kimi/CodeBuddy installations are non-blocking manual follow-up tasks (not verified by system).
91102
91012
  prepare copies current public files into a local snapshot; it does not rewrite source files.
91103
91013
  - Default mode is offline (release-skill pipeline does no remote writes)
91104
91014
  - prepare output goes to .release-skill/ directory only
91105
91015
  - User-configured hooks may write anywhere and perform remote operations
91106
91016
  - To ensure zero remote writes, disable hooks or audit them separately
91107
91017
  - 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
91018
+ - publish requires explicit approval; plan digest is auto-read from the plan file
91109
91019
  - publish consumes frozen Git/npm artifacts, never the live workspace
91110
91020
  - existing remote objects and uncertain checks stop for human intervention
91111
91021
  - production-equivalent protocol sandbox is verified; a real remote canary is not
@@ -91176,8 +91086,8 @@ if (!command || command === "help") {
91176
91086
  conditionalConsumers: {
91177
91087
  claude: "\u58F0\u660E claude-plugin distribution \u65F6\u5FC5\u987B\u53EF\u7528",
91178
91088
  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"
91089
+ 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",
91090
+ 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
91091
  }
91182
91092
  }
91183
91093
  },
@@ -91186,12 +91096,12 @@ if (!command || command === "help") {
91186
91096
  maturity: {
91187
91097
  setup: "read-only by default; create-once requires answers plus exact setupDigest confirmation",
91188
91098
  assess: "read-only (default); --output writes local report",
91189
- prepare: "offline local writes; configured hooks/gates require their explicit side-effect acknowledgements",
91099
+ prepare: "offline local writes; the command invocation authorizes configured hook and gate execution",
91190
91100
  docs: "read-only dry-run by default; write requires --write, exact --confirm-refresh, and --ack-local-document-write; never commits, pushes, or publishes",
91191
91101
  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",
91102
+ 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
91103
  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"
91104
+ 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
91105
  },
91196
91106
  recommendations: []
91197
91107
  };
@@ -91218,12 +91128,6 @@ if (!command || command === "help") {
91218
91128
  if (!checks.codex.available) {
91219
91129
  output.recommendations.push("Install Codex CLI before releasing a configured codex-plugin distribution");
91220
91130
  }
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
91131
  console.log(JSON.stringify(output, null, 2));
91228
91132
  process.exit(readiness.status === "READY" ? 0 : 1);
91229
91133
  } else {
@@ -91325,6 +91229,7 @@ if (command === "hooks") {
91325
91229
  const { validateDeclaredHooks: validateDeclaredHooks2 } = await init_hooks2().then(() => hooks_exports);
91326
91230
  const result = await validateDeclaredHooks2({
91327
91231
  root,
91232
+ // --acknowledge-hook-side-effects is accepted as a no-effect compatibility input
91328
91233
  hooksAuthorized: args.includes("--acknowledge-hook-side-effects"),
91329
91234
  hookCache: !args.includes("--no-hook-cache")
91330
91235
  });
@@ -91378,7 +91283,7 @@ if (command === "ship") {
91378
91283
  root,
91379
91284
  statePath: value("--state"),
91380
91285
  targetVersion: value("--target-version") ?? value("--version"),
91381
- hookAuthorizationDigest: value("--authorize-hooks"),
91286
+ approve: args.includes("--approve"),
91382
91287
  planApprovalDigest: value("--approve-plan"),
91383
91288
  actor: value("--actor"),
91384
91289
  adapterRegistry
@@ -91388,11 +91293,11 @@ if (command === "ship") {
91388
91293
  } else {
91389
91294
  console.log(`Ship status: ${result.status}`);
91390
91295
  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
91296
  if (result.planDigest && result.status === "NEEDS_PLAN_APPROVAL") {
91395
- console.log(`Approve plan and verification gates: --approve-plan ${result.planDigest} --actor <person>`);
91297
+ console.log(`Approve plan: ship --approve --actor <person>`);
91298
+ }
91299
+ for (const followUp of result.manualFollowUps ?? []) {
91300
+ console.log(`Manual follow-up [${followUp.platform}] ${followUp.plugin}: not verified by system`);
91396
91301
  }
91397
91302
  for (const requirement of result.requirements ?? []) {
91398
91303
  console.log(`Manual requirement [${requirement.platform}]: ${requirement.requirementPath}`);
@@ -91542,8 +91447,8 @@ if (command === "approve") {
91542
91447
  const actor = actorIdx !== -1 && args[actorIdx + 1] ? args[actorIdx + 1] : void 0;
91543
91448
  const outputIdx = args.indexOf("--output");
91544
91449
  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>";
91450
+ if (!planPath || !actor) {
91451
+ const msg = "approve requires --plan <path> and --actor <name>";
91547
91452
  if (hasJson) {
91548
91453
  console.log(JSON.stringify({ error: "MISSING_PARAMETERS", message: msg, exitCode: 1 }));
91549
91454
  } else {
@@ -91553,11 +91458,19 @@ if (command === "approve") {
91553
91458
  }
91554
91459
  try {
91555
91460
  const { approvePlan: approvePlan2 } = await init_approve().then(() => approve_exports);
91461
+ const { computePlanDigest: computePlanDigest2 } = await init_plan().then(() => plan_exports);
91462
+ const { readFile: readFileFs } = await import("node:fs/promises");
91463
+ let resolvedDigest = expectedDigest;
91464
+ if (!resolvedDigest) {
91465
+ const planRaw = await readFileFs(resolve32(planPath), "utf8");
91466
+ const planObj = JSON.parse(planRaw);
91467
+ resolvedDigest = computePlanDigest2(planObj);
91468
+ }
91556
91469
  const resolvedPlanPath = resolve32(planPath);
91557
91470
  const planDir = dirname16(resolvedPlanPath);
91558
- const releaseDir = basename10(planDir) === "plans" && basename10(resolvedPlanPath) === `${expectedDigest}.json` ? dirname16(planDir) : planDir;
91471
+ const releaseDir = basename10(planDir) === "plans" && basename10(resolvedPlanPath) === `${resolvedDigest}.json` ? dirname16(planDir) : planDir;
91559
91472
  const approvalPath = outputPath ?? join30(releaseDir, "approval-record.json");
91560
- const record = await approvePlan2({ planPath, expectedDigest, actor, outputPath: approvalPath });
91473
+ const record = await approvePlan2({ planPath, expectedDigest: resolvedDigest, actor, outputPath: approvalPath });
91561
91474
  if (hasJson) {
91562
91475
  console.log(JSON.stringify(record, null, 2));
91563
91476
  } else {
@@ -91590,8 +91503,6 @@ if (command === "reconcile") {
91590
91503
  const runPath = runIdx !== -1 && args[runIdx + 1] ? resolve32(args[runIdx + 1]) : void 0;
91591
91504
  const approvalIdx = args.indexOf("--approval");
91592
91505
  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
91506
  if (!planPath || !runPath) {
91596
91507
  const msg = "reconcile requires --plan <path> and --run <path>";
91597
91508
  if (hasJson) {
@@ -91619,8 +91530,7 @@ if (command === "reconcile") {
91619
91530
  sourceRunPath: runPath,
91620
91531
  approvalPath,
91621
91532
  adapterRegistry: registry,
91622
- root,
91623
- productionConfirmation
91533
+ root
91624
91534
  });
91625
91535
  if (hasJson) {
91626
91536
  console.log(JSON.stringify(result, null, 2));
@@ -91713,10 +91623,8 @@ if (command === "publish") {
91713
91623
  const planPath = planIdx !== -1 && args[planIdx + 1] ? resolve32(args[planIdx + 1]) : void 0;
91714
91624
  const approvalIdx = args.indexOf("--approval");
91715
91625
  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>";
91626
+ if (!planPath || !approvalPath) {
91627
+ const msg = "publish requires --plan <path> and --approval <path>";
91720
91628
  if (hasJson) {
91721
91629
  console.log(JSON.stringify({ error: "MISSING_PARAMETERS", message: msg, exitCode: 1 }));
91722
91630
  } else {
@@ -91742,8 +91650,7 @@ if (command === "publish") {
91742
91650
  approvalPath,
91743
91651
  adapterRegistry: registry,
91744
91652
  root,
91745
- productionMode: true,
91746
- productionConfirmation
91653
+ productionMode: true
91747
91654
  });
91748
91655
  if (hasJson) {
91749
91656
  console.log(JSON.stringify(result, null, 2));