release-skill 0.9.36 → 0.9.38
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/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.cursor-plugin/plugin.json +1 -1
- package/.kimi-plugin/plugin.json +1 -1
- package/.qoder-plugin/plugin.json +1 -1
- package/CHANGELOG.md +56 -0
- package/INSTALL.md +2 -2
- package/INSTALL.zh-CN.md +2 -2
- package/README.md +48 -14
- package/README.zh-CN.md +42 -14
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +2020 -1457
- package/adapters/claude/schemas/release-plan.schema.json +1 -0
- package/adapters/claude/schemas/test-intake.schema.json +1 -0
- package/adapters/claude/skills/release-config/SKILL.md +36 -41
- package/adapters/claude/skills/release-docs/SKILL.md +38 -35
- package/adapters/claude/skills/release-help/SKILL.md +59 -54
- package/adapters/claude/skills/release-marketplace/SKILL.md +38 -46
- package/adapters/claude/skills/release-prepare/SKILL.md +38 -27
- package/adapters/claude/skills/release-publish/SKILL.md +38 -44
- package/adapters/claude/skills/release-reconcile/SKILL.md +14 -0
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +2020 -1457
- package/adapters/codex/schemas/release-plan.schema.json +1 -0
- package/adapters/codex/schemas/test-intake.schema.json +1 -0
- package/adapters/codex/skills/release-config/SKILL.md +36 -41
- package/adapters/codex/skills/release-docs/SKILL.md +38 -35
- package/adapters/codex/skills/release-help/SKILL.md +59 -54
- package/adapters/codex/skills/release-marketplace/SKILL.md +38 -46
- package/adapters/codex/skills/release-prepare/SKILL.md +38 -27
- package/adapters/codex/skills/release-publish/SKILL.md +38 -44
- package/adapters/codex/skills/release-reconcile/SKILL.md +14 -0
- package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
- package/adapters/cursor/bin/release-skill.bundle.mjs +2020 -1457
- package/adapters/cursor/schemas/release-plan.schema.json +1 -0
- package/adapters/cursor/schemas/test-intake.schema.json +1 -0
- package/adapters/cursor/skills/release-config/SKILL.md +36 -41
- package/adapters/cursor/skills/release-docs/SKILL.md +38 -35
- package/adapters/cursor/skills/release-help/SKILL.md +59 -54
- package/adapters/cursor/skills/release-marketplace/SKILL.md +38 -46
- package/adapters/cursor/skills/release-prepare/SKILL.md +38 -27
- package/adapters/cursor/skills/release-publish/SKILL.md +38 -44
- package/adapters/cursor/skills/release-reconcile/SKILL.md +14 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +2020 -1457
- package/adapters/kimi/schemas/release-plan.schema.json +1 -0
- package/adapters/kimi/schemas/test-intake.schema.json +1 -0
- package/adapters/kimi/skills/release-config/SKILL.md +36 -41
- package/adapters/kimi/skills/release-docs/SKILL.md +38 -35
- package/adapters/kimi/skills/release-help/SKILL.md +59 -54
- package/adapters/kimi/skills/release-marketplace/SKILL.md +38 -46
- package/adapters/kimi/skills/release-prepare/SKILL.md +38 -27
- package/adapters/kimi/skills/release-publish/SKILL.md +38 -44
- package/adapters/kimi/skills/release-reconcile/SKILL.md +14 -0
- package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
- package/adapters/qoder/bin/release-skill.bundle.mjs +2020 -1457
- package/adapters/qoder/schemas/release-plan.schema.json +1 -0
- package/adapters/qoder/schemas/test-intake.schema.json +1 -0
- package/adapters/qoder/skills/release-config/SKILL.md +36 -41
- package/adapters/qoder/skills/release-docs/SKILL.md +38 -35
- package/adapters/qoder/skills/release-help/SKILL.md +59 -54
- package/adapters/qoder/skills/release-marketplace/SKILL.md +38 -46
- package/adapters/qoder/skills/release-prepare/SKILL.md +38 -27
- package/adapters/qoder/skills/release-publish/SKILL.md +38 -44
- package/adapters/qoder/skills/release-reconcile/SKILL.md +14 -0
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +2020 -1457
- package/adapters/workbuddy/schemas/release-plan.schema.json +1 -0
- package/adapters/workbuddy/schemas/test-intake.schema.json +1 -0
- package/adapters/workbuddy/skills/release-config/SKILL.md +36 -41
- package/adapters/workbuddy/skills/release-docs/SKILL.md +38 -35
- package/adapters/workbuddy/skills/release-help/SKILL.md +59 -54
- package/adapters/workbuddy/skills/release-marketplace/SKILL.md +38 -46
- package/adapters/workbuddy/skills/release-prepare/SKILL.md +38 -27
- package/adapters/workbuddy/skills/release-publish/SKILL.md +38 -44
- package/adapters/workbuddy/skills/release-reconcile/SKILL.md +14 -0
- package/bin/release-skill-cli.mjs +5 -0
- package/bin/release-skill.bundle.mjs +2020 -1457
- package/package.json +1 -1
- package/platform-manifest.json +4 -4
- package/references/01-state-machine.md +26 -0
- package/references/02-project-config.md +108 -7
- package/schemas/release-plan.schema.json +1 -0
- package/schemas/test-intake.schema.json +1 -0
- package/skills/release-config/SKILL.md +36 -41
- package/skills/release-docs/SKILL.md +38 -35
- package/skills/release-help/SKILL.md +59 -54
- package/skills/release-marketplace/SKILL.md +38 -46
- package/skills/release-prepare/SKILL.md +38 -27
- package/skills/release-publish/SKILL.md +38 -44
- package/skills/release-reconcile/SKILL.md +14 -0
- package/skills-src/release-config/SKILL.md +36 -41
- package/skills-src/release-docs/SKILL.md +38 -35
- package/skills-src/release-help/SKILL.md +59 -54
- package/skills-src/release-marketplace/SKILL.md +38 -46
- package/skills-src/release-prepare/SKILL.md +38 -27
- package/skills-src/release-publish/SKILL.md +38 -44
- package/skills-src/release-reconcile/SKILL.md +14 -0
- package/src/commands/approve.mjs +33 -24
- package/src/commands/prepare.mjs +9 -2
- package/src/commands/publish.mjs +10 -2
- package/src/commands/reconcile.mjs +2 -0
- package/src/commands/route.mjs +2 -159
- package/src/commands/verify.mjs +123 -17
- package/src/core/plan-replacement.mjs +371 -0
- package/src/core/pre-authority-failure.mjs +241 -0
- package/src/core/skill-resource-closure.mjs +1 -1
- package/src/core/test-candidate.mjs +15 -67
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFormalPlans, bindReplacement, canonicalFormalPlanPath } from './plan-replacement.mjs';
|
|
1
2
|
/**
|
|
2
3
|
* One test-intake candidate: exclusive save, strict read, and the no-git
|
|
3
4
|
* handoff decision shared by prepare and ship.
|
|
@@ -281,68 +282,6 @@ function sameTargetVersions(left, right) {
|
|
|
281
282
|
*
|
|
282
283
|
* @param {string} root
|
|
283
284
|
*/
|
|
284
|
-
async function readFormalPlans(root) {
|
|
285
|
-
const plansPath = resolve(root, '.release-skill', 'plans');
|
|
286
|
-
let names;
|
|
287
|
-
try {
|
|
288
|
-
names = await readdir(plansPath);
|
|
289
|
-
} catch (err) {
|
|
290
|
-
if (err?.code === 'ENOENT') {
|
|
291
|
-
try {
|
|
292
|
-
await lstat(plansPath);
|
|
293
|
-
} catch (statError) {
|
|
294
|
-
if (statError?.code === 'ENOENT') return [];
|
|
295
|
-
throw new ReleaseError(CONFIG_INVALID, 'formal test acceptance plans could not be read', {
|
|
296
|
-
reason: 'TEST_CANDIDATE_AUTHORITY_INVALID',
|
|
297
|
-
field: 'plansPath',
|
|
298
|
-
cause: statError?.code ?? 'unknown',
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
throw new ReleaseError(CONFIG_INVALID, 'formal test acceptance plans could not be read', {
|
|
303
|
-
reason: 'TEST_CANDIDATE_AUTHORITY_INVALID',
|
|
304
|
-
field: 'plansPath',
|
|
305
|
-
cause: err?.code ?? 'unknown',
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
const plans = [];
|
|
309
|
-
for (const name of names) {
|
|
310
|
-
if (!name.endsWith('.json')) continue;
|
|
311
|
-
const rel = `.release-skill/plans/${name}`;
|
|
312
|
-
let receipt;
|
|
313
|
-
try {
|
|
314
|
-
receipt = await readFileStrict(root, rel, { encoding: 'utf8' });
|
|
315
|
-
} catch (err) {
|
|
316
|
-
const cause = harnessKind(err) ?? 'unknown';
|
|
317
|
-
throw new ReleaseError(CONFIG_INVALID, 'formal test acceptance plan could not be read', {
|
|
318
|
-
reason: 'TEST_CANDIDATE_AUTHORITY_INVALID',
|
|
319
|
-
field: 'planPath',
|
|
320
|
-
path: rel,
|
|
321
|
-
cause,
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
let plan;
|
|
325
|
-
try {
|
|
326
|
-
plan = JSON.parse(receipt.content);
|
|
327
|
-
} catch {
|
|
328
|
-
throw new ReleaseError(CONFIG_INVALID, 'formal test acceptance plan is not valid JSON', {
|
|
329
|
-
reason: 'TEST_CANDIDATE_AUTHORITY_INVALID',
|
|
330
|
-
field: 'planPath',
|
|
331
|
-
path: rel,
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
plans.push({
|
|
335
|
-
planPath: rel,
|
|
336
|
-
planDigest: typeof plan?.digest === 'string' ? plan.digest : name.slice(0, -'.json'.length),
|
|
337
|
-
status: plan?.status,
|
|
338
|
-
subjectDigest: typeof plan?.testAcceptance?.subjectDigest === 'string'
|
|
339
|
-
? plan.testAcceptance.subjectDigest
|
|
340
|
-
: null,
|
|
341
|
-
targetVersions: targetVersionsOf(plan),
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
return plans;
|
|
345
|
-
}
|
|
346
285
|
|
|
347
286
|
async function sameAuthorityFile(left, right) {
|
|
348
287
|
const identity = async (value) => {
|
|
@@ -432,10 +371,11 @@ export async function decideTestCandidateHandoff({
|
|
|
432
371
|
expectedSubjectDigest,
|
|
433
372
|
configDigest,
|
|
434
373
|
extraDrifts = [],
|
|
374
|
+
replaceUnpublishedPlan,
|
|
435
375
|
}) {
|
|
436
376
|
if (!config?.testIntake) {
|
|
437
|
-
if (candidatePath) {
|
|
438
|
-
throw new ReleaseError(CONFIG_INVALID, 'candidate path requires testIntake', {
|
|
377
|
+
if (candidatePath || replaceUnpublishedPlan !== undefined) {
|
|
378
|
+
throw new ReleaseError(CONFIG_INVALID, 'candidate or replacement path requires testIntake', {
|
|
439
379
|
reason: 'TEST_CANDIDATE_WITHOUT_INTAKE',
|
|
440
380
|
field: 'candidatePath',
|
|
441
381
|
});
|
|
@@ -449,7 +389,8 @@ export async function decideTestCandidateHandoff({
|
|
|
449
389
|
});
|
|
450
390
|
}
|
|
451
391
|
if (!candidatePath) {
|
|
452
|
-
const
|
|
392
|
+
const bound = await bindReplacement({ root, prepared, replaceUnpublishedPlan, resultFile: requirements.resultFile });
|
|
393
|
+
const saved = await writeTestCandidate({ root, runDir, prepared: bound, requirements, prepareOptions });
|
|
453
394
|
const subjectDigest = computePlanDigest(saved.candidate.prepared);
|
|
454
395
|
return {
|
|
455
396
|
kind: 'save-candidate',
|
|
@@ -483,6 +424,9 @@ export async function decideTestCandidateHandoff({
|
|
|
483
424
|
});
|
|
484
425
|
}
|
|
485
426
|
|
|
427
|
+
if (replaceUnpublishedPlan !== undefined && canonicalFormalPlanPath(root, replaceUnpublishedPlan) !== `.release-skill/plans/${loaded.candidate.prepared.replacesPlanDigest}.json`) {
|
|
428
|
+
throw new ReleaseError(GATE_FAILED, 'replacement selection differs from frozen candidate');
|
|
429
|
+
}
|
|
486
430
|
const options = loaded.candidate.prepareOptions;
|
|
487
431
|
const drifts = [...extraDrifts];
|
|
488
432
|
if (loaded.candidate.writerVersion !== WRITER_VERSION) drifts.push('writerVersion');
|
|
@@ -510,10 +454,13 @@ export async function decideTestCandidateHandoff({
|
|
|
510
454
|
});
|
|
511
455
|
}
|
|
512
456
|
|
|
513
|
-
const formalPlans = await readFormalPlans(root);
|
|
457
|
+
const formalPlans = await readFormalPlans(root, loaded.candidate.prepared);
|
|
514
458
|
const targetVersions = targetVersionsOf(loaded.candidate.prepared);
|
|
459
|
+
const predecessor = loaded.candidate.prepared.replacesPlanDigest;
|
|
460
|
+
const retired = new Set(formalPlans.map((entry) => entry.plan.replacesPlanDigest).filter(Boolean));
|
|
461
|
+
if (predecessor) retired.add(predecessor);
|
|
515
462
|
const differentBasis = formalPlans.filter((plan) => (
|
|
516
|
-
sameTargetVersions(plan.targetVersions, targetVersions) && plan.subjectDigest !== subjectDigest
|
|
463
|
+
!retired.has(plan.planDigest) && sameTargetVersions(plan.targetVersions, targetVersions) && plan.subjectDigest !== subjectDigest
|
|
517
464
|
));
|
|
518
465
|
if (differentBasis.length > 0) {
|
|
519
466
|
throw new ReleaseError(GATE_FAILED, 'a different formal acceptance basis already exists for this target', {
|
|
@@ -543,6 +490,7 @@ export async function decideTestCandidateHandoff({
|
|
|
543
490
|
};
|
|
544
491
|
}
|
|
545
492
|
|
|
493
|
+
await bindReplacement({ root, prepared: loaded.candidate.prepared, replaceUnpublishedPlan, resultFile: requirements.resultFile });
|
|
546
494
|
const acceptance = await acceptTestResults({
|
|
547
495
|
root,
|
|
548
496
|
requirements,
|