release-skill 0.2.3 → 0.2.4
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +1 -1
- package/CHANGELOG.md +22 -0
- package/CONTRIBUTING.md +27 -0
- package/INSTALL.md +95 -139
- package/INSTALL.zh-CN.md +70 -121
- package/README.md +264 -913
- package/README.zh-CN.md +222 -535
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +19054 -17573
- package/adapters/claude/schemas/release-plan.schema.json +137 -0
- package/adapters/claude/schemas/release-project.schema.json +93 -0
- package/adapters/claude/schemas/release-run.schema.json +70 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +19054 -17573
- package/adapters/codex/schemas/release-plan.schema.json +137 -0
- package/adapters/codex/schemas/release-project.schema.json +93 -0
- package/adapters/codex/schemas/release-run.schema.json +70 -2
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +19054 -17573
- package/adapters/kimi/schemas/release-plan.schema.json +137 -0
- package/adapters/kimi/schemas/release-project.schema.json +93 -0
- package/adapters/kimi/schemas/release-run.schema.json +70 -2
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +19054 -17573
- package/adapters/workbuddy/schemas/release-plan.schema.json +137 -0
- package/adapters/workbuddy/schemas/release-project.schema.json +93 -0
- package/adapters/workbuddy/schemas/release-run.schema.json +70 -2
- package/bin/release-skill.bundle.mjs +19054 -17573
- package/package.json +1 -1
- package/schemas/release-plan.schema.json +137 -0
- package/schemas/release-project.schema.json +93 -0
- package/schemas/release-run.schema.json +70 -2
- package/src/adapters/plugin-marketplace.mjs +1190 -435
- package/src/commands/prepare.mjs +380 -40
- package/src/commands/publish.mjs +107 -75
- package/src/commands/reconcile.mjs +92 -327
- package/src/commands/setup.mjs +148 -20
- package/src/commands/verify.mjs +304 -20
- package/src/core/baseline.mjs +8 -1
- package/src/core/checkpoints.mjs +50 -7
- package/src/core/config.mjs +15 -0
- package/src/core/errors.mjs +2 -0
- package/src/core/installation-contract.mjs +341 -0
- package/src/core/plan.mjs +129 -6
- package/src/platforms/codebuddy.mjs +193 -280
- package/src/platforms/codex.mjs +369 -0
- package/src/platforms/kimi.mjs +164 -119
- package/src/platforms/registry.mjs +24 -6
package/src/commands/publish.mjs
CHANGED
|
@@ -46,6 +46,8 @@ import {
|
|
|
46
46
|
TIER_TABLE,
|
|
47
47
|
groupActionsByTier,
|
|
48
48
|
sortActionsByCheckpointOrder,
|
|
49
|
+
isRemoteWriteAction,
|
|
50
|
+
isMarketplaceAction,
|
|
49
51
|
} from '../core/checkpoints.mjs';
|
|
50
52
|
import { appendRunState, createProductionRunDir, writeRunAtomic, resolveDefaultRunDir } from '../core/run.mjs';
|
|
51
53
|
import {
|
|
@@ -53,6 +55,7 @@ import {
|
|
|
53
55
|
GATE_FAILED,
|
|
54
56
|
BASELINE_CHANGED,
|
|
55
57
|
PARTIAL_RELEASE,
|
|
58
|
+
CONSUMER_VERIFICATION_DEFERRED,
|
|
56
59
|
} from '../core/errors.mjs';
|
|
57
60
|
import { assertTransition, PUBLISHING, PUBLISHED, PARTIAL } from '../core/state-machine.mjs';
|
|
58
61
|
import { matchObservation } from '../adapters/contract.mjs';
|
|
@@ -81,13 +84,6 @@ const ACTION_NOT_ALLOWED = 'ACTION_NOT_ALLOWED';
|
|
|
81
84
|
// CHECKPOINT_ORDER, ADAPTER_ACTION_TYPE_MAP and TIER_TABLE live in
|
|
82
85
|
// ../core/checkpoints.mjs (single source shared with reconcile.mjs; T3.1 §4.7).
|
|
83
86
|
|
|
84
|
-
const MARKETPLACE_TYPES = new Set([
|
|
85
|
-
'claude-marketplace-install',
|
|
86
|
-
'codex-marketplace-install',
|
|
87
|
-
'kimi-marketplace-install',
|
|
88
|
-
'codebuddy-marketplace-install',
|
|
89
|
-
]);
|
|
90
|
-
|
|
91
87
|
// ---------------------------------------------------------------------------
|
|
92
88
|
// Helpers
|
|
93
89
|
// ---------------------------------------------------------------------------
|
|
@@ -359,7 +355,7 @@ async function executeCheckpoint(action, adapterRegistry, context) {
|
|
|
359
355
|
// (T1.1 PROPAGATING handling). A present-but-mismatched
|
|
360
356
|
// observation (CONFLICTING) is never retried — it is an
|
|
361
357
|
// authoritative conflict that must fail closed for human review.
|
|
362
|
-
const observePolicy =
|
|
358
|
+
const observePolicy = isMarketplaceAction(planActionType)
|
|
363
359
|
? clampPolicyToTimeout(DEFAULT_OBSERVE_RETRY_POLICY, action.parameters?.timeoutMs)
|
|
364
360
|
: DEFAULT_OBSERVE_RETRY_POLICY;
|
|
365
361
|
const retryOutcome = await observeWithRetry({
|
|
@@ -892,21 +888,27 @@ export async function publishRelease(options) {
|
|
|
892
888
|
const orderedActions = sortActionsByCheckpointOrder(publishingPlan.externalActions);
|
|
893
889
|
|
|
894
890
|
// =======================================================================
|
|
895
|
-
//
|
|
891
|
+
// Partition: marketplace actions are deferred to verify phase.
|
|
892
|
+
// They get a stable CONSUMER_VERIFICATION_DEFERRED checkpoint immediately;
|
|
893
|
+
// they never call marketplace adapter preflight/execute/observe/verify.
|
|
894
|
+
// =======================================================================
|
|
895
|
+
const remoteWriteActions = orderedActions.filter((a) => !isMarketplaceAction(a.type));
|
|
896
|
+
|
|
897
|
+
// =======================================================================
|
|
898
|
+
// Safety Gate 10: Global preflight - validate all non-marketplace actions
|
|
899
|
+
// before any execute. Marketplace actions are deferred and skip preflight.
|
|
896
900
|
// =======================================================================
|
|
897
901
|
await evidence.append({ phase: 'safety-gate', gate: 'global-preflight', status: 'started' });
|
|
898
902
|
|
|
899
|
-
for (const action of
|
|
903
|
+
for (const action of remoteWriteActions) {
|
|
900
904
|
if (action.type === 'write-remote-identifier') continue;
|
|
901
905
|
|
|
902
906
|
const adapterActionType = ADAPTER_ACTION_TYPE_MAP[action.type];
|
|
903
907
|
if (!adapterActionType) continue;
|
|
904
908
|
|
|
905
909
|
const adapter = adapterRegistry.getAdapter(adapterActionType);
|
|
906
|
-
const isMarketplace = MARKETPLACE_TYPES.has(action.type);
|
|
907
910
|
const preflightContext = {
|
|
908
911
|
externalWritesAuthorized: false,
|
|
909
|
-
isolatedConsumerWritesAuthorized: isMarketplace,
|
|
910
912
|
plan: publishingPlan,
|
|
911
913
|
baseline: plan.baseline,
|
|
912
914
|
root,
|
|
@@ -918,15 +920,11 @@ export async function publishRelease(options) {
|
|
|
918
920
|
);
|
|
919
921
|
if (preflightResult.status === 'PREFLIGHT_FAILED') {
|
|
920
922
|
// T3.3 §4.6: an "occupied" preflight failure overlaps semantically with
|
|
921
|
-
// "already consistent".
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
//
|
|
925
|
-
|
|
926
|
-
// keeps the fail-closed GATE_FAILED. Marketplace preflights are local
|
|
927
|
-
// integrity checks, never remote occupation, so they are NEVER arbitrated
|
|
928
|
-
// -- a CONSISTENT observation must not bypass their completeness gate.
|
|
929
|
-
if (!isMarketplace && hasExpected(action.expected)) {
|
|
923
|
+
// "already consistent". Arbitrate with a single read-only pre-observe
|
|
924
|
+
// using the SAME classifier as the per-checkpoint path: CONSISTENT means
|
|
925
|
+
// the remote already satisfies the frozen plan, so let the action through
|
|
926
|
+
// to its SKIPPED path instead of failing the whole gate.
|
|
927
|
+
if (hasExpected(action.expected)) {
|
|
930
928
|
let arbitration;
|
|
931
929
|
try {
|
|
932
930
|
arbitration = await adapter.observe(
|
|
@@ -961,15 +959,27 @@ export async function publishRelease(options) {
|
|
|
961
959
|
await evidence.append({ phase: 'safety-gate', gate: 'global-preflight', status: 'passed' });
|
|
962
960
|
|
|
963
961
|
// =======================================================================
|
|
964
|
-
//
|
|
962
|
+
// Build checkpoints: marketplace actions get a deferred result immediately;
|
|
963
|
+
// non-marketplace actions start as PENDING and are executed by tiers.
|
|
965
964
|
// =======================================================================
|
|
966
965
|
const runPath = join(runDir, 'release-run.json');
|
|
967
|
-
const checkpoints = orderedActions.map((action) =>
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
966
|
+
const checkpoints = orderedActions.map((action) => {
|
|
967
|
+
if (isMarketplaceAction(action.type)) {
|
|
968
|
+
return {
|
|
969
|
+
actionId: action.id,
|
|
970
|
+
actionType: action.type,
|
|
971
|
+
status: 'DEFERRED',
|
|
972
|
+
phase: 'post-publish-verification',
|
|
973
|
+
reason: CONSUMER_VERIFICATION_DEFERRED,
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
return {
|
|
977
|
+
actionId: action.id,
|
|
978
|
+
actionType: action.type,
|
|
979
|
+
status: 'PENDING',
|
|
980
|
+
error: null,
|
|
981
|
+
};
|
|
982
|
+
});
|
|
973
983
|
|
|
974
984
|
const startedAt = clockFn();
|
|
975
985
|
const buildPersistedState = (status = PUBLISHING, finishedAt) => ({
|
|
@@ -987,10 +997,14 @@ export async function publishRelease(options) {
|
|
|
987
997
|
: checkpoint.status === 'FAILED' ? 'failed'
|
|
988
998
|
: checkpoint.status === 'UNCERTAIN' ? 'uncertain'
|
|
989
999
|
: checkpoint.status === 'SKIPPED' ? 'skipped'
|
|
1000
|
+
: checkpoint.status === 'DEFERRED' ? 'deferred'
|
|
990
1001
|
: 'pending',
|
|
991
1002
|
...(checkpoint.preObserve ? { preObserve: checkpoint.preObserve } : {}),
|
|
992
1003
|
...(checkpoint.postObserve ? { postObserve: checkpoint.postObserve } : {}),
|
|
993
1004
|
...(checkpoint.error ? { error: { code: 'GATE_FAILED', message: checkpoint.error } } : {}),
|
|
1005
|
+
...(checkpoint.reason === CONSUMER_VERIFICATION_DEFERRED
|
|
1006
|
+
? { reason: CONSUMER_VERIFICATION_DEFERRED, phase: checkpoint.phase ?? 'post-publish-verification' }
|
|
1007
|
+
: {}),
|
|
994
1008
|
})),
|
|
995
1009
|
startedAt,
|
|
996
1010
|
...(finishedAt ? { finishedAt } : {}),
|
|
@@ -1023,7 +1037,9 @@ export async function publishRelease(options) {
|
|
|
1023
1037
|
// order). appendRunState's seq chain (run.mjs) is unchanged.
|
|
1024
1038
|
// =======================================================================
|
|
1025
1039
|
const checkpointByActionId = new Map(checkpoints.map((cp) => [cp.actionId, cp]));
|
|
1026
|
-
|
|
1040
|
+
// Only non-marketplace actions participate in tier execution.
|
|
1041
|
+
// Marketplace actions are already deferred and excluded from tiers.
|
|
1042
|
+
const { tiers, unknown } = groupActionsByTier(remoteWriteActions);
|
|
1027
1043
|
let stopped = false;
|
|
1028
1044
|
|
|
1029
1045
|
// Fail closed on any action type the tier table does not recognize. Such a
|
|
@@ -1075,7 +1091,7 @@ export async function publishRelease(options) {
|
|
|
1075
1091
|
|
|
1076
1092
|
// --- Tier execute: concurrent, allSettled semantics (no fail-fast).
|
|
1077
1093
|
const results = await Promise.all(tierActions.map(async (action) => {
|
|
1078
|
-
const isMarketplace =
|
|
1094
|
+
const isMarketplace = isMarketplaceAction(action.type);
|
|
1079
1095
|
const actionContext = {
|
|
1080
1096
|
externalWritesAuthorized: !isMarketplace,
|
|
1081
1097
|
isolatedConsumerWritesAuthorized: isMarketplace,
|
|
@@ -1136,60 +1152,76 @@ export async function publishRelease(options) {
|
|
|
1136
1152
|
// consistency pass. Both the branch tip and default-branch name are bound
|
|
1137
1153
|
// in the frozen action expectations. A late change keeps the saga PARTIAL
|
|
1138
1154
|
// and must be resolved by reconcile/human review.
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1155
|
+
//
|
|
1156
|
+
// IMPORTANT: the final status determination must read the LATEST checkpoint
|
|
1157
|
+
// state AFTER this consistency check, not use any previously cached boolean.
|
|
1158
|
+
// A late consistency failure that changes a checkpoint from SUCCEEDED to
|
|
1159
|
+
// FAILED must cause PARTIAL, not stale PUBLISHED.
|
|
1160
|
+
{
|
|
1161
|
+
// Build fresh remote-write checkpoint view (reads latest state)
|
|
1162
|
+
const remoteWriteCps = checkpoints.filter((cp) => isRemoteWriteAction(cp.actionType));
|
|
1163
|
+
const allRemoteConsistent = remoteWriteCps.every(
|
|
1164
|
+
(cp) => cp.status === 'SUCCEEDED' || cp.status === 'SKIPPED',
|
|
1165
|
+
);
|
|
1166
|
+
if (allRemoteConsistent) {
|
|
1167
|
+
await evidence.append({ phase: 'safety-gate', gate: 'final-branch-consistency', status: 'started' });
|
|
1168
|
+
for (let index = 0; index < orderedActions.length; index += 1) {
|
|
1169
|
+
const action = orderedActions[index];
|
|
1170
|
+
if (!['push-snapshot', 'set-default-branch'].includes(action.type)) continue;
|
|
1171
|
+
const adapterActionType = ADAPTER_ACTION_TYPE_MAP[action.type];
|
|
1172
|
+
const adapter = adapterRegistry.getAdapter(adapterActionType);
|
|
1173
|
+
let observed;
|
|
1174
|
+
try {
|
|
1175
|
+
observed = await adapter.observe(
|
|
1176
|
+
{ actionType: adapterActionType, ...action.parameters, expected: action.expected },
|
|
1177
|
+
{ externalWritesAuthorized: false, plan: publishingPlan, baseline: plan.baseline, root, runDir },
|
|
1178
|
+
);
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
observed = { observation: null, error: error.message };
|
|
1181
|
+
}
|
|
1182
|
+
const observation = observed?.observation;
|
|
1183
|
+
const observable = observation && !(observed.error && Object.keys(observation).length === 0);
|
|
1184
|
+
const comparison = observable ? matchObservation(action.expected ?? {}, observation) : { matches: false, mismatches: [] };
|
|
1185
|
+
if (!comparison.matches) {
|
|
1186
|
+
checkpoints[index].status = observable ? 'FAILED' : 'UNCERTAIN';
|
|
1187
|
+
checkpoints[index].error = observable
|
|
1188
|
+
? `final branch consistency mismatch: ${comparison.mismatches.join('; ')}`
|
|
1189
|
+
: `final branch consistency unobservable: ${observed?.error ?? 'empty observation'}`;
|
|
1190
|
+
await evidence.append({
|
|
1191
|
+
phase: 'safety-gate',
|
|
1192
|
+
gate: 'final-branch-consistency',
|
|
1193
|
+
status: 'failed',
|
|
1194
|
+
actionId: action.id,
|
|
1195
|
+
error: checkpoints[index].error,
|
|
1196
|
+
});
|
|
1197
|
+
break;
|
|
1198
|
+
}
|
|
1154
1199
|
}
|
|
1155
|
-
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
checkpoints[index].status = observable ? 'FAILED' : 'UNCERTAIN';
|
|
1160
|
-
checkpoints[index].error = observable
|
|
1161
|
-
? `final branch consistency mismatch: ${comparison.mismatches.join('; ')}`
|
|
1162
|
-
: `final branch consistency unobservable: ${observed?.error ?? 'empty observation'}`;
|
|
1163
|
-
await evidence.append({
|
|
1164
|
-
phase: 'safety-gate',
|
|
1165
|
-
gate: 'final-branch-consistency',
|
|
1166
|
-
status: 'failed',
|
|
1167
|
-
actionId: action.id,
|
|
1168
|
-
error: checkpoints[index].error,
|
|
1169
|
-
});
|
|
1170
|
-
break;
|
|
1200
|
+
// Re-read fresh state after consistency check
|
|
1201
|
+
const freshRemoteCps = checkpoints.filter((cp) => isRemoteWriteAction(cp.actionType));
|
|
1202
|
+
if (freshRemoteCps.every((cp) => cp.status === 'SUCCEEDED' || cp.status === 'SKIPPED')) {
|
|
1203
|
+
await evidence.append({ phase: 'safety-gate', gate: 'final-branch-consistency', status: 'passed' });
|
|
1171
1204
|
}
|
|
1172
1205
|
}
|
|
1173
|
-
if (checkpoints.every((cp) => cp.status === 'SUCCEEDED' || cp.status === 'SKIPPED')) {
|
|
1174
|
-
await evidence.append({ phase: 'safety-gate', gate: 'final-branch-consistency', status: 'passed' });
|
|
1175
|
-
}
|
|
1176
1206
|
}
|
|
1177
1207
|
|
|
1178
|
-
// Determine overall status
|
|
1179
|
-
|
|
1180
|
-
//
|
|
1181
|
-
//
|
|
1182
|
-
|
|
1183
|
-
const
|
|
1208
|
+
// Determine overall status from the LATEST checkpoint state.
|
|
1209
|
+
// Never use a cached boolean computed before the consistency check.
|
|
1210
|
+
// 远端写入动作的状态决定整体发布状态
|
|
1211
|
+
// 市场安装动作的结果记录在 run 中,但不阻止 PUBLISHED
|
|
1212
|
+
const remoteWriteCheckpointsFinal = checkpoints.filter((cp) => isRemoteWriteAction(cp.actionType));
|
|
1213
|
+
const remoteWriteAllSucceeded = remoteWriteCheckpointsFinal.every(
|
|
1214
|
+
(cp) => cp.status === 'SUCCEEDED' || cp.status === 'SKIPPED',
|
|
1215
|
+
);
|
|
1216
|
+
const remoteWriteHasFailure = remoteWriteCheckpointsFinal.some(
|
|
1217
|
+
(cp) => cp.status === 'FAILED' || cp.status === 'UNCERTAIN',
|
|
1218
|
+
);
|
|
1184
1219
|
|
|
1185
1220
|
let overallStatus;
|
|
1186
|
-
if (
|
|
1221
|
+
if (remoteWriteAllSucceeded) {
|
|
1187
1222
|
overallStatus = PUBLISHED;
|
|
1188
1223
|
publishingPlan.status = PUBLISHED;
|
|
1189
|
-
} else if (
|
|
1190
|
-
// Once any execute was attempted, recovery must go through reconcile,
|
|
1191
|
-
// even when no success was observed. Re-running publish could duplicate
|
|
1192
|
-
// a write accepted just before a transport failure.
|
|
1224
|
+
} else if (remoteWriteHasFailure) {
|
|
1193
1225
|
overallStatus = PARTIAL;
|
|
1194
1226
|
publishingPlan.status = overallStatus;
|
|
1195
1227
|
} else {
|