release-skill 0.9.0 → 0.9.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.
- 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 +32 -0
- package/INSTALL.md +22 -9
- package/INSTALL.zh-CN.md +15 -8
- package/README.md +37 -30
- package/README.zh-CN.md +32 -27
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/consumer-contract-vectors.json +110 -0
- package/adapters/claude/bin/error-codes.json +8 -1
- package/adapters/claude/bin/foundation-resource-binding.json +1 -1
- package/adapters/claude/bin/registry.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +1719 -1291
- package/adapters/claude/bin/rules.json +1 -1
- package/adapters/claude/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/adapters/claude/skills/release-finish/SKILL.md +4 -4
- package/adapters/claude/skills/release-help/SKILL.md +3 -2
- package/adapters/claude/skills/release-prepare/SKILL.md +2 -2
- package/adapters/claude/skills/release-publish/SKILL.md +3 -1
- package/adapters/claude/src/schemas/migration-manifest.schema.json +271 -7
- package/adapters/claude/src/schemas/profile-adoption-declaration.schema.json +8 -0
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/consumer-contract-vectors.json +110 -0
- package/adapters/codex/bin/error-codes.json +8 -1
- package/adapters/codex/bin/foundation-resource-binding.json +1 -1
- package/adapters/codex/bin/registry.json +1 -1
- package/adapters/codex/bin/release-skill.bundle.mjs +1719 -1291
- package/adapters/codex/bin/rules.json +1 -1
- package/adapters/codex/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/adapters/codex/skills/release-finish/SKILL.md +4 -4
- package/adapters/codex/skills/release-help/SKILL.md +3 -2
- package/adapters/codex/skills/release-prepare/SKILL.md +2 -2
- package/adapters/codex/skills/release-publish/SKILL.md +3 -1
- package/adapters/codex/src/schemas/migration-manifest.schema.json +271 -7
- package/adapters/codex/src/schemas/profile-adoption-declaration.schema.json +8 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/consumer-contract-vectors.json +110 -0
- package/adapters/kimi/bin/error-codes.json +8 -1
- package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
- package/adapters/kimi/bin/registry.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +1719 -1291
- package/adapters/kimi/bin/rules.json +1 -1
- package/adapters/kimi/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/adapters/kimi/skills/release-finish/SKILL.md +4 -4
- package/adapters/kimi/skills/release-help/SKILL.md +3 -2
- package/adapters/kimi/skills/release-prepare/SKILL.md +2 -2
- package/adapters/kimi/skills/release-publish/SKILL.md +3 -1
- package/adapters/kimi/src/schemas/migration-manifest.schema.json +271 -7
- package/adapters/kimi/src/schemas/profile-adoption-declaration.schema.json +8 -0
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/consumer-contract-vectors.json +110 -0
- package/adapters/workbuddy/bin/error-codes.json +8 -1
- package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
- package/adapters/workbuddy/bin/registry.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +1719 -1291
- package/adapters/workbuddy/bin/rules.json +1 -1
- package/adapters/workbuddy/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/adapters/workbuddy/skills/release-finish/SKILL.md +4 -4
- package/adapters/workbuddy/skills/release-help/SKILL.md +3 -2
- package/adapters/workbuddy/skills/release-prepare/SKILL.md +2 -2
- package/adapters/workbuddy/skills/release-publish/SKILL.md +3 -1
- package/adapters/workbuddy/src/schemas/migration-manifest.schema.json +271 -7
- package/adapters/workbuddy/src/schemas/profile-adoption-declaration.schema.json +8 -0
- package/bin/consumer-contract-vectors.json +110 -0
- package/bin/error-codes.json +8 -1
- package/bin/foundation-resource-binding.json +1 -1
- package/bin/registry.json +1 -1
- package/bin/release-skill-cli.mjs +40 -4
- package/bin/release-skill.bundle.mjs +1719 -1291
- package/bin/rules.json +1 -1
- package/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/package.json +4 -4
- package/platform-manifest.json +5 -5
- package/skills/release-finish/SKILL.md +4 -4
- package/skills/release-help/SKILL.md +3 -2
- package/skills/release-prepare/SKILL.md +2 -2
- package/skills/release-publish/SKILL.md +3 -1
- package/skills-src/release-finish/SKILL.md +4 -4
- package/skills-src/release-help/SKILL.md +3 -2
- package/skills-src/release-prepare/SKILL.md +2 -2
- package/skills-src/release-publish/SKILL.md +3 -1
- package/src/commands/post-release-local.mjs +249 -58
- package/src/commands/prepare.mjs +39 -4
- package/src/commands/ship.mjs +124 -16
- package/src/core/derived-artifact-gates.mjs +32 -5
- package/src/producers/foundation-resource-projection.mjs +16 -3
- package/src/schemas/migration-manifest.schema.json +271 -7
- package/src/schemas/profile-adoption-declaration.schema.json +8 -0
package/src/commands/ship.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { readFile, lstat, mkdir } from 'node:fs/promises';
|
|
|
2
2
|
import { basename, dirname, resolve } from 'node:path';
|
|
3
3
|
import { publishFileOrReplace } from 'skill-family-harness-node';
|
|
4
4
|
|
|
5
|
+
import { MAX_APPROVAL_MS } from '../core/approval.mjs';
|
|
5
6
|
import { canonicalJson, sha256Hex } from '../core/digest.mjs';
|
|
6
7
|
import {
|
|
7
8
|
effectiveHookRequiresApproval,
|
|
@@ -88,6 +89,7 @@ async function defaultDependencies() {
|
|
|
88
89
|
postVerifyRelease: postverifyModule.postVerifyRelease,
|
|
89
90
|
preflightGitTransports: transportModule.preflightGitTransports,
|
|
90
91
|
updatePreviousPublicBaselines: metadataModule.updatePreviousPublicBaselines,
|
|
92
|
+
readMaxApprovalMs: () => MAX_APPROVAL_MS,
|
|
91
93
|
};
|
|
92
94
|
}
|
|
93
95
|
|
|
@@ -116,34 +118,135 @@ function publicState(state) {
|
|
|
116
118
|
...(state.manualFollowUps ? { manualFollowUps: state.manualFollowUps } : {}),
|
|
117
119
|
...(state.metadataUpdate ? { metadataUpdate: state.metadataUpdate } : {}),
|
|
118
120
|
...(state.postRelease ? { postRelease: state.postRelease } : {}),
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
externalActionsIncludedInPlanApproval: true,
|
|
122
|
+
verificationGatesIncludedInPlanApproval: true,
|
|
123
|
+
postPublishCheckpointApprovalsIncludedInPlanApproval: false,
|
|
121
124
|
};
|
|
122
125
|
}
|
|
123
126
|
|
|
127
|
+
const HOUR_MS = 60 * 60 * 1000;
|
|
128
|
+
|
|
129
|
+
const ACTION_TARGET_FIELDS = Object.freeze({
|
|
130
|
+
'push-commit': Object.freeze(['remote', 'branch']),
|
|
131
|
+
'push-snapshot': Object.freeze(['repo', 'branch', 'branchStrategy', 'commit']),
|
|
132
|
+
'create-tag': Object.freeze(['repo', 'tag', 'commit']),
|
|
133
|
+
'npm-publish': Object.freeze(['package', 'version', 'registry', 'tag', 'access', 'provenance']),
|
|
134
|
+
'github-release': Object.freeze(['repo', 'tag', 'name', 'commit']),
|
|
135
|
+
'claude-marketplace-install': Object.freeze(['consumer', 'plugin', 'marketplace', 'repo', 'ref', 'version', 'entrySkill']),
|
|
136
|
+
'codex-marketplace-install': Object.freeze(['consumer', 'plugin', 'marketplace', 'repo', 'ref', 'version', 'entrySkill']),
|
|
137
|
+
'kimi-marketplace-install': Object.freeze(['consumer', 'plugin', 'repo', 'ref', 'version', 'entrySkill']),
|
|
138
|
+
'codebuddy-marketplace-install': Object.freeze(['consumer', 'plugin', 'repo', 'ref', 'version', 'entrySkill']),
|
|
139
|
+
'set-default-branch': Object.freeze(['repo', 'oldBranch', 'newBranch', 'expectedNewBranchCommit']),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
function deriveApprovalWindowHours(maxApprovalMs) {
|
|
143
|
+
if (
|
|
144
|
+
!Number.isSafeInteger(maxApprovalMs)
|
|
145
|
+
|| maxApprovalMs <= 0
|
|
146
|
+
|| maxApprovalMs % HOUR_MS !== 0
|
|
147
|
+
) {
|
|
148
|
+
throw new ReleaseError(
|
|
149
|
+
GATE_FAILED,
|
|
150
|
+
'core approval window must be a positive whole number of hours',
|
|
151
|
+
{ maxApprovalMs },
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return maxApprovalMs / HOUR_MS;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function projectActionTarget(action) {
|
|
158
|
+
const parameters = action?.parameters ?? {};
|
|
159
|
+
return Object.fromEntries(
|
|
160
|
+
(ACTION_TARGET_FIELDS[action?.type] ?? [])
|
|
161
|
+
.filter((field) => parameters[field] !== undefined)
|
|
162
|
+
.map((field) => [field, parameters[field]]),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
124
166
|
/**
|
|
125
167
|
* Build a human-readable approval summary from the frozen plan.
|
|
126
|
-
* Lists
|
|
127
|
-
*
|
|
168
|
+
* Lists every publish target and every checkpoint approval separately. All
|
|
169
|
+
* fields are mechanically projected from the frozen plan; unreadable plans
|
|
170
|
+
* fail closed instead of producing an empty or partial summary.
|
|
128
171
|
*
|
|
129
172
|
* @param {string} planPath - Path to the frozen release plan.
|
|
130
173
|
* @returns {Promise<object>} The approval summary.
|
|
131
174
|
*/
|
|
132
|
-
async function buildApprovalSummary(planPath) {
|
|
175
|
+
async function buildApprovalSummary(planPath, maxApprovalMs = MAX_APPROVAL_MS) {
|
|
133
176
|
try {
|
|
134
177
|
const plan = JSON.parse(await readFile(planPath, 'utf8'));
|
|
135
|
-
const
|
|
136
|
-
id: unit.id,
|
|
137
|
-
targetVersion: unit.targetVersion ?? unit.version,
|
|
138
|
-
}));
|
|
178
|
+
const approvalWindowHours = deriveApprovalWindowHours(maxApprovalMs);
|
|
139
179
|
const actions = (plan.externalActions ?? []).map((action) => ({
|
|
140
180
|
id: action.id,
|
|
141
181
|
type: action.type,
|
|
142
182
|
unitId: action.unitId,
|
|
183
|
+
target: projectActionTarget(action),
|
|
143
184
|
}));
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
185
|
+
const units = (plan.units ?? []).map((unit) => {
|
|
186
|
+
const targetVersion = unit.targetVersion ?? unit.version;
|
|
187
|
+
const githubRelease = (plan.externalActions ?? [])
|
|
188
|
+
.find((action) => action.unitId === unit.id && action.type === 'github-release');
|
|
189
|
+
return {
|
|
190
|
+
id: unit.id,
|
|
191
|
+
targetVersion,
|
|
192
|
+
publicRepo: unit.publicRepo,
|
|
193
|
+
branch: unit.frozenSnapshot?.branch
|
|
194
|
+
?? unit.productionConfig?.branchTemplate
|
|
195
|
+
?.replaceAll('{tag}', unit.tagTemplate?.replace('{version}', targetVersion) ?? '')
|
|
196
|
+
.replaceAll('{version}', targetVersion)
|
|
197
|
+
.replaceAll('{unit}', unit.id),
|
|
198
|
+
branchStrategy: unit.frozenSnapshot?.branchStrategy ?? unit.productionConfig?.branchStrategy,
|
|
199
|
+
tag: unit.tagTemplate?.replace('{version}', targetVersion),
|
|
200
|
+
npm: (unit.distributions ?? [])
|
|
201
|
+
.filter((distribution) => distribution.type === 'npm')
|
|
202
|
+
.map((distribution) => ({
|
|
203
|
+
package: distribution.package,
|
|
204
|
+
version: targetVersion,
|
|
205
|
+
registry: distribution.registry,
|
|
206
|
+
distTag: distribution.tag,
|
|
207
|
+
access: distribution.access,
|
|
208
|
+
provenance: distribution.provenance,
|
|
209
|
+
})),
|
|
210
|
+
...(githubRelease ? {
|
|
211
|
+
githubRelease: {
|
|
212
|
+
repo: githubRelease.parameters?.repo ?? githubRelease.parameters?.publicRepo,
|
|
213
|
+
tag: githubRelease.parameters?.tag,
|
|
214
|
+
name: githubRelease.parameters?.name,
|
|
215
|
+
},
|
|
216
|
+
} : {}),
|
|
217
|
+
};
|
|
218
|
+
});
|
|
219
|
+
const postPublishCheckpointApprovals = normalizePostPublishView(plan)
|
|
220
|
+
.flatMap((declaration) => (declaration.hooks ?? [])
|
|
221
|
+
.filter((hook) => effectiveHookRequiresApproval(hook))
|
|
222
|
+
.map((hook) => ({
|
|
223
|
+
unitId: declaration.unitId,
|
|
224
|
+
hookId: hook.id,
|
|
225
|
+
actionId: postPublishActionId({
|
|
226
|
+
planVersion: plan.planVersion,
|
|
227
|
+
unitId: declaration.unitId,
|
|
228
|
+
localId: hook.id,
|
|
229
|
+
}),
|
|
230
|
+
phase: hook.phase ?? 'distribute',
|
|
231
|
+
...(hook.preset !== undefined ? { preset: hook.preset } : {}),
|
|
232
|
+
...(hook.config?.delivery !== undefined ? { delivery: hook.config.delivery } : {}),
|
|
233
|
+
requiresApproval: true,
|
|
234
|
+
includedInPlanApproval: false,
|
|
235
|
+
approvalWindowHours,
|
|
236
|
+
binding: { planDigest: plan.digest, hookId: hook.id },
|
|
237
|
+
})));
|
|
238
|
+
return {
|
|
239
|
+
units,
|
|
240
|
+
actions,
|
|
241
|
+
waivers: Array.isArray(plan.waivers) ? plan.waivers : [],
|
|
242
|
+
postPublishCheckpointApprovals,
|
|
243
|
+
};
|
|
244
|
+
} catch (error) {
|
|
245
|
+
throw new ReleaseError(
|
|
246
|
+
GATE_FAILED,
|
|
247
|
+
`cannot build ship approval summary from frozen plan: ${error.message}`,
|
|
248
|
+
{ planPath },
|
|
249
|
+
);
|
|
147
250
|
}
|
|
148
251
|
}
|
|
149
252
|
|
|
@@ -197,9 +300,11 @@ async function allUnclosedPostVerifyHooksUngated(state, postVerifyHooks, planVer
|
|
|
197
300
|
* resumes instead of reconstructing authority from terminal/chat output.
|
|
198
301
|
*
|
|
199
302
|
* New flow (v0.4+): ship directly runs configured hooks and verification gates
|
|
200
|
-
* without a separate hook authorization step.
|
|
201
|
-
*
|
|
202
|
-
*
|
|
303
|
+
* without a separate hook authorization step. Plan approval is the only
|
|
304
|
+
* normal release-level gate. A postPublish hook whose effective
|
|
305
|
+
* requiresApproval is true still needs its independent checkpoint approval.
|
|
306
|
+
* Kimi/CodeBuddy installations are non-blocking manual follow-up tasks when
|
|
307
|
+
* the plan declares humanConsumersStrategy: 'manualFollowUps'.
|
|
203
308
|
*/
|
|
204
309
|
export async function advanceShip(options = {}, injected = {}) {
|
|
205
310
|
const root = resolve(options.root ?? process.cwd());
|
|
@@ -268,7 +373,10 @@ export async function advanceShip(options = {}, injected = {}) {
|
|
|
268
373
|
transportPreflight = await deps.preflightGitTransports(frozenPlan);
|
|
269
374
|
process.env.RELEASE_SKILL_GIT_TRANSPORT = transportPreflight.transport;
|
|
270
375
|
}
|
|
271
|
-
const
|
|
376
|
+
const maxApprovalMs = typeof deps.readMaxApprovalMs === 'function'
|
|
377
|
+
? await deps.readMaxApprovalMs()
|
|
378
|
+
: MAX_APPROVAL_MS;
|
|
379
|
+
const approvalSummary = await buildApprovalSummary(prepared.planPath, maxApprovalMs);
|
|
272
380
|
state = {
|
|
273
381
|
...state,
|
|
274
382
|
status: 'NEEDS_PLAN_APPROVAL',
|
|
@@ -5,17 +5,20 @@
|
|
|
5
5
|
* In the 0.6.1 cycle two prepares burned the full ~80s test hook before
|
|
6
6
|
* surfacing drift that a sub-second check could have caught: adapter trees
|
|
7
7
|
* out of sync with skills-src/, and self-bootstrap fact pins still bound to
|
|
8
|
-
* the previous version. This module promotes those
|
|
9
|
-
* prepare's earliest stage, alongside the bundle
|
|
8
|
+
* the previous version. This module promotes those checks and the platform
|
|
9
|
+
* manifest freshness check to prepare's earliest stage, alongside the bundle
|
|
10
|
+
* freshness gate:
|
|
10
11
|
*
|
|
11
12
|
* - adapter gate: runs `scripts/build-adapters.mjs --check` (drift list,
|
|
12
13
|
* exit 1 on drift — the same supported check the scripts surface offers);
|
|
14
|
+
* - platform manifest gate: runs
|
|
15
|
+
* `scripts/generate-platform-manifest.mjs --check`;
|
|
13
16
|
* - fact-pin gate: runs the version fact pins of
|
|
14
17
|
* `test/release-docs-self-bootstrap.test.mjs` — exactly its hermetic
|
|
15
18
|
* section 1 (`[self-bootstrap 1*]`: byte-level version assertions plus the
|
|
16
19
|
* in-process read-only planner), scoped via `--test-name-pattern`.
|
|
17
20
|
*
|
|
18
|
-
*
|
|
21
|
+
* All three promote the CANONICAL check logic — the gates and the full pipeline
|
|
19
22
|
* can never disagree about what "in sync" means. The facts gate is scoped to
|
|
20
23
|
* the suite's hermetic fact-pin section deliberately: the suite's remaining
|
|
21
24
|
* sections shell out to npm/git and drive fixture prepares, which would make
|
|
@@ -88,6 +91,15 @@ const GATES = Object.freeze({
|
|
|
88
91
|
'Rebuild the existing adapters with: node scripts/build-adapters.mjs --apply ' +
|
|
89
92
|
`(or run the one-click derived-artifact sync from the workspace root: ${DERIVED_SYNC_COMMAND}).`,
|
|
90
93
|
}),
|
|
94
|
+
'platform-manifest': Object.freeze({
|
|
95
|
+
artifact: 'platform-manifest',
|
|
96
|
+
marker: join('scripts', 'generate-platform-manifest.mjs'),
|
|
97
|
+
argv: (pkgRoot) => [join(pkgRoot, 'scripts', 'generate-platform-manifest.mjs'), '--check'],
|
|
98
|
+
timeoutMs: 120000,
|
|
99
|
+
remediation:
|
|
100
|
+
'Regenerate the platform manifest with: node scripts/generate-platform-manifest.mjs ' +
|
|
101
|
+
`(or run the one-click derived-artifact sync from the workspace root: ${DERIVED_SYNC_COMMAND}).`,
|
|
102
|
+
}),
|
|
91
103
|
'self-bootstrap-facts': Object.freeze({
|
|
92
104
|
artifact: 'self-bootstrap-facts',
|
|
93
105
|
marker: join('test', 'release-docs-self-bootstrap.test.mjs'),
|
|
@@ -118,7 +130,7 @@ async function isFile(path) {
|
|
|
118
130
|
/**
|
|
119
131
|
* Run one derived-artifact check (pure decision, never throws).
|
|
120
132
|
*
|
|
121
|
-
* @param {'adapters' | 'self-bootstrap-facts'} kind - Gate to run.
|
|
133
|
+
* @param {'adapters' | 'platform-manifest' | 'self-bootstrap-facts'} kind - Gate to run.
|
|
122
134
|
* @param {string} pkgRoot - Absolute package root of the running checkout.
|
|
123
135
|
* @param {object} [options]
|
|
124
136
|
* @param {Function} [options.execFileFn] - execFile seam (tests).
|
|
@@ -202,6 +214,11 @@ export function checkAdapterFreshness(pkgRoot, options = {}) {
|
|
|
202
214
|
return checkDerivedArtifactGate('adapters', pkgRoot, options);
|
|
203
215
|
}
|
|
204
216
|
|
|
217
|
+
/** Platform manifest freshness decision (generate-platform-manifest --check). */
|
|
218
|
+
export function checkPlatformManifestFreshness(pkgRoot, options = {}) {
|
|
219
|
+
return checkDerivedArtifactGate('platform-manifest', pkgRoot, options);
|
|
220
|
+
}
|
|
221
|
+
|
|
205
222
|
/** Self-bootstrap fact-pin decision (single-file test). */
|
|
206
223
|
export function checkSelfBootstrapFacts(pkgRoot, options = {}) {
|
|
207
224
|
return checkDerivedArtifactGate('self-bootstrap-facts', pkgRoot, options);
|
|
@@ -215,7 +232,9 @@ async function assertGate(kind, pkgRoot, options = {}) {
|
|
|
215
232
|
const gate = GATES[kind];
|
|
216
233
|
const subject = kind === 'adapters'
|
|
217
234
|
? 'adapters/ is out of sync with its sources (build-adapters --check reported drift)'
|
|
218
|
-
:
|
|
235
|
+
: kind === 'platform-manifest'
|
|
236
|
+
? 'platform-manifest.json is out of sync with the current package tree (generate-platform-manifest --check reported drift)'
|
|
237
|
+
: 'the release-docs-self-bootstrap fact pins are stale (the hermetic fact-pin check failed)';
|
|
219
238
|
throw new ReleaseError(
|
|
220
239
|
DERIVED_ARTIFACT_STALE,
|
|
221
240
|
`${subject}. ${DERIVED_ARTIFACT_PREGATE_NOTE} ${gate.remediation}`,
|
|
@@ -243,6 +262,14 @@ export function assertAdapterFreshness(pkgRoot, options = {}) {
|
|
|
243
262
|
return assertGate('adapters', pkgRoot, options);
|
|
244
263
|
}
|
|
245
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Fail-closed platform manifest pre-gate used by prepare's earliest stage.
|
|
267
|
+
* Not-applicable layouts return quietly; drift throws DERIVED_ARTIFACT_STALE.
|
|
268
|
+
*/
|
|
269
|
+
export function assertPlatformManifestFreshness(pkgRoot, options = {}) {
|
|
270
|
+
return assertGate('platform-manifest', pkgRoot, options);
|
|
271
|
+
}
|
|
272
|
+
|
|
246
273
|
/**
|
|
247
274
|
* Fail-closed self-bootstrap fact-pin pre-gate used by prepare's earliest
|
|
248
275
|
* stage. Not-applicable layouts return quietly; drift throws
|
|
@@ -55,9 +55,9 @@ import { digestBytes, publishFileOrReplace, readFileStrict, withTemporaryWorkspa
|
|
|
55
55
|
import { canonicalJson } from '../core/digest.mjs';
|
|
56
56
|
|
|
57
57
|
export const FOUNDATION_PACKAGES = Object.freeze({
|
|
58
|
-
'skill-family-contracts': '0.
|
|
59
|
-
'skill-family-engineering-kit': '0.
|
|
60
|
-
'skill-family-harness-node': '0.
|
|
58
|
+
'skill-family-contracts': '0.14.0',
|
|
59
|
+
'skill-family-engineering-kit': '0.14.0',
|
|
60
|
+
'skill-family-harness-node': '0.14.0',
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
export const BINDING_RECORD_PATH = 'bin/foundation-resource-binding.json';
|
|
@@ -295,6 +295,19 @@ export async function computeFoundationProjection({ pkgRoot, bundleContent }) {
|
|
|
295
295
|
continue;
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
|
+
// Foundation 0.14 keeps this example behind a capability-selection
|
|
299
|
+
// branch that esbuild removes from the release-skill bundle. The
|
|
300
|
+
// source scanner still sees the directory URL in the marked module,
|
|
301
|
+
// so admit this one exact directory only when its path survives in
|
|
302
|
+
// the built bundle.
|
|
303
|
+
if (
|
|
304
|
+
pkg === 'skill-family-engineering-kit'
|
|
305
|
+
&& toPosix(relative(packages[pkg].root, sourceDir)) === 'data/examples/minimal-plugin-payload'
|
|
306
|
+
&& !bundleContent.includes('data/examples/minimal-plugin-payload')
|
|
307
|
+
) {
|
|
308
|
+
scopeOf(targetPosix);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
298
311
|
const files = await walkTree(sourceDir, `${pkg} directory read "${rel}"`);
|
|
299
312
|
const members = [];
|
|
300
313
|
for (const file of files) {
|
|
@@ -13,6 +13,138 @@
|
|
|
13
13
|
"kind": {
|
|
14
14
|
"const": "skill-family.migration-manifest"
|
|
15
15
|
},
|
|
16
|
+
"capabilityUseContext": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"required": ["scope", "locale"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"scope": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"enum": ["contracts", "harness", "engineering-kit", "profile", "all"]
|
|
24
|
+
},
|
|
25
|
+
"locale": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"enum": ["en", "zh-CN"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"capabilityUses": {
|
|
32
|
+
"type": "array",
|
|
33
|
+
"minItems": 1,
|
|
34
|
+
"maxItems": 200,
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"required": ["useId", "problemStatement", "constraints", "retainedGuarantees"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"useId": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
43
|
+
"maxLength": 80
|
|
44
|
+
},
|
|
45
|
+
"problemStatement": {
|
|
46
|
+
"allOf": [
|
|
47
|
+
{ "$ref": "#/$defs/trimmedCapabilityUseText" },
|
|
48
|
+
{ "type": "string", "maxLength": 1000 }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"constraints": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"maxItems": 20,
|
|
54
|
+
"uniqueItems": true,
|
|
55
|
+
"items": {
|
|
56
|
+
"allOf": [
|
|
57
|
+
{ "$ref": "#/$defs/trimmedCapabilityUseText" },
|
|
58
|
+
{ "type": "string", "maxLength": 500 }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"retainedGuarantees": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"minItems": 1,
|
|
65
|
+
"maxItems": 20,
|
|
66
|
+
"uniqueItems": true,
|
|
67
|
+
"items": {
|
|
68
|
+
"allOf": [
|
|
69
|
+
{ "$ref": "#/$defs/trimmedCapabilityUseText" },
|
|
70
|
+
{ "type": "string", "maxLength": 500 }
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"catalogFilters": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"maxItems": 20,
|
|
77
|
+
"uniqueItems": true,
|
|
78
|
+
"items": {
|
|
79
|
+
"allOf": [
|
|
80
|
+
{ "$ref": "#/$defs/trimmedCapabilityUseText" },
|
|
81
|
+
{ "type": "string", "maxLength": 200 }
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"capabilityDecisions": {
|
|
89
|
+
"type": "array",
|
|
90
|
+
"items": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"additionalProperties": false,
|
|
93
|
+
"required": ["useId", "disposition", "reason"],
|
|
94
|
+
"properties": {
|
|
95
|
+
"useId": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
98
|
+
"maxLength": 80
|
|
99
|
+
},
|
|
100
|
+
"disposition": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"enum": ["direct-adoption", "compatibility-layer", "keep-business", "foundation-gap"]
|
|
103
|
+
},
|
|
104
|
+
"targetCapability": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"pattern": "^foundation\\.[a-z0-9\\-]+(\\.[a-z0-9\\-]+)+$"
|
|
107
|
+
},
|
|
108
|
+
"reason": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"minLength": 1,
|
|
111
|
+
"maxLength": 1000
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"allOf": [
|
|
115
|
+
{
|
|
116
|
+
"if": {
|
|
117
|
+
"required": ["disposition"],
|
|
118
|
+
"properties": {
|
|
119
|
+
"disposition": {
|
|
120
|
+
"enum": ["direct-adoption", "compatibility-layer"]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"then": {
|
|
125
|
+
"required": ["targetCapability"],
|
|
126
|
+
"properties": { "targetCapability": { "type": "string" } }
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"if": {
|
|
131
|
+
"required": ["disposition"],
|
|
132
|
+
"properties": {
|
|
133
|
+
"disposition": {
|
|
134
|
+
"enum": ["keep-business", "foundation-gap"]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"then": {
|
|
139
|
+
"not": {
|
|
140
|
+
"required": ["targetCapability"],
|
|
141
|
+
"properties": { "targetCapability": { "type": "string" } }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
},
|
|
16
148
|
"manifestVersion": {
|
|
17
149
|
"type": "integer",
|
|
18
150
|
"minimum": 1,
|
|
@@ -31,11 +163,34 @@
|
|
|
31
163
|
"description": "Relative POSIX path of one legacy implementation. Shape only: absolute paths, traversal, UNC/drive forms and symlink escapes are rejected by harness containment at assessment time, fail-closed."
|
|
32
164
|
},
|
|
33
165
|
"replacedBy": {
|
|
34
|
-
"
|
|
35
|
-
|
|
166
|
+
"oneOf": [
|
|
167
|
+
{ "type": "string", "minLength": 1 },
|
|
168
|
+
{ "type": "null" }
|
|
169
|
+
],
|
|
36
170
|
"description": "The replacement facility (tool/command/module) that supersedes this legacy entry. An empty replacement is never a valid exit claim."
|
|
171
|
+
},
|
|
172
|
+
"useId": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
175
|
+
"maxLength": 80
|
|
176
|
+
},
|
|
177
|
+
"recoveryRef": {
|
|
178
|
+
"type": "string",
|
|
179
|
+
"minLength": 1
|
|
37
180
|
}
|
|
38
|
-
}
|
|
181
|
+
},
|
|
182
|
+
"allOf": [
|
|
183
|
+
{
|
|
184
|
+
"if": {
|
|
185
|
+
"required": ["replacedBy"],
|
|
186
|
+
"properties": { "replacedBy": { "type": "null" } }
|
|
187
|
+
},
|
|
188
|
+
"then": {
|
|
189
|
+
"required": ["useId"],
|
|
190
|
+
"properties": { "useId": { "type": "string" } }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
]
|
|
39
194
|
}
|
|
40
195
|
},
|
|
41
196
|
"legacyReferences": {
|
|
@@ -57,11 +212,30 @@
|
|
|
57
212
|
"description": "Exact literal text that must be absent from the file. No regex; byte-exact UTF-8 match."
|
|
58
213
|
},
|
|
59
214
|
"replacedBy": {
|
|
60
|
-
"
|
|
61
|
-
|
|
215
|
+
"oneOf": [
|
|
216
|
+
{ "type": "string", "minLength": 1 },
|
|
217
|
+
{ "type": "null" }
|
|
218
|
+
],
|
|
62
219
|
"description": "The replacement facility that supersedes this old reference. Empty is never valid."
|
|
220
|
+
},
|
|
221
|
+
"useId": {
|
|
222
|
+
"type": "string",
|
|
223
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
224
|
+
"maxLength": 80
|
|
63
225
|
}
|
|
64
|
-
}
|
|
226
|
+
},
|
|
227
|
+
"allOf": [
|
|
228
|
+
{
|
|
229
|
+
"if": {
|
|
230
|
+
"required": ["replacedBy"],
|
|
231
|
+
"properties": { "replacedBy": { "type": "null" } }
|
|
232
|
+
},
|
|
233
|
+
"then": {
|
|
234
|
+
"required": ["useId"],
|
|
235
|
+
"properties": { "useId": { "type": "string" } }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
]
|
|
65
239
|
}
|
|
66
240
|
},
|
|
67
241
|
"exceptions": {
|
|
@@ -144,5 +318,95 @@
|
|
|
144
318
|
}
|
|
145
319
|
}
|
|
146
320
|
}
|
|
147
|
-
}
|
|
321
|
+
},
|
|
322
|
+
"$defs": {
|
|
323
|
+
"trimmedCapabilityUseText": {
|
|
324
|
+
"type": "string",
|
|
325
|
+
"minLength": 1,
|
|
326
|
+
"pattern": "^(?!\\s)(?![\\s\\S]*\\s$)(?![\\s\\S]*\\u0000)[\\s\\S]+$"
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
"allOf": [
|
|
330
|
+
{
|
|
331
|
+
"if": {
|
|
332
|
+
"required": ["capabilityUses"],
|
|
333
|
+
"properties": { "capabilityUses": { "type": "array" } }
|
|
334
|
+
},
|
|
335
|
+
"then": {
|
|
336
|
+
"required": ["capabilityUseContext"],
|
|
337
|
+
"properties": { "capabilityUseContext": { "type": "object" } }
|
|
338
|
+
},
|
|
339
|
+
"else": {
|
|
340
|
+
"not": {
|
|
341
|
+
"anyOf": [
|
|
342
|
+
{
|
|
343
|
+
"required": ["capabilityUseContext"],
|
|
344
|
+
"properties": { "capabilityUseContext": { "type": "object" } }
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"required": ["capabilityDecisions"],
|
|
348
|
+
"properties": { "capabilityDecisions": { "type": "array" } }
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
"if": {
|
|
356
|
+
"required": ["capabilityDecisions"],
|
|
357
|
+
"properties": { "capabilityDecisions": { "type": "array" } }
|
|
358
|
+
},
|
|
359
|
+
"then": {
|
|
360
|
+
"required": ["capabilityUses", "capabilityUseContext"],
|
|
361
|
+
"properties": {
|
|
362
|
+
"capabilityUses": { "type": "array" },
|
|
363
|
+
"capabilityUseContext": { "type": "object" }
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"if": {
|
|
369
|
+
"required": ["legacyInfra"],
|
|
370
|
+
"properties": {
|
|
371
|
+
"legacyInfra": {
|
|
372
|
+
"type": "array",
|
|
373
|
+
"contains": {
|
|
374
|
+
"type": "object",
|
|
375
|
+
"required": ["useId"],
|
|
376
|
+
"properties": { "useId": { "type": "string" } }
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
"then": {
|
|
382
|
+
"required": ["capabilityUses", "capabilityUseContext"],
|
|
383
|
+
"properties": {
|
|
384
|
+
"capabilityUses": { "type": "array" },
|
|
385
|
+
"capabilityUseContext": { "type": "object" }
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"if": {
|
|
391
|
+
"required": ["legacyReferences"],
|
|
392
|
+
"properties": {
|
|
393
|
+
"legacyReferences": {
|
|
394
|
+
"type": "array",
|
|
395
|
+
"contains": {
|
|
396
|
+
"type": "object",
|
|
397
|
+
"required": ["useId"],
|
|
398
|
+
"properties": { "useId": { "type": "string" } }
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
"then": {
|
|
404
|
+
"required": ["capabilityUses", "capabilityUseContext"],
|
|
405
|
+
"properties": {
|
|
406
|
+
"capabilityUses": { "type": "array" },
|
|
407
|
+
"capabilityUseContext": { "type": "object" }
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
]
|
|
148
412
|
}
|
|
@@ -54,6 +54,14 @@
|
|
|
54
54
|
"stability": { "enum": ["stable", "candidate-only"] }
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
+
"capabilities": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"uniqueItems": true,
|
|
60
|
+
"items": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"pattern": "^foundation\\.[a-z0-9\\-]+(?:\\.[a-z0-9\\-]+)+$"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
57
65
|
"foundation_pin": {
|
|
58
66
|
"type": "object",
|
|
59
67
|
"additionalProperties": false,
|