release-skill 0.8.0 → 0.8.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 +34 -7
- package/INSTALL.md +2 -2
- package/INSTALL.zh-CN.md +2 -2
- package/README.md +34 -26
- package/README.zh-CN.md +21 -17
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/error-codes.json +1 -1
- package/adapters/claude/bin/foundation-resource-binding.json +1 -1
- package/adapters/claude/bin/prebuild-manifest.json +8 -4
- package/adapters/claude/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/claude/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/claude/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/claude/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/claude/bin/registry.json +22 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +4904 -3079
- package/adapters/claude/bin/rules.json +1 -1
- package/adapters/claude/schemas/release-plan.schema.json +3 -2
- package/adapters/claude/schemas/release-run.schema.json +35 -1
- package/adapters/claude/src/schemas/filesystem-tree-observation.schema.json +55 -0
- package/adapters/claude/src/schemas/host-verification-result.schema.json +2 -2
- package/adapters/claude/src/schemas/plugin-verification-request.schema.json +374 -0
- package/adapters/claude/src/schemas/plugin-verification-result.schema.json +799 -0
- package/adapters/claude/src/schemas/watchdog-termination-envelope.schema.json +16 -0
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/error-codes.json +1 -1
- package/adapters/codex/bin/foundation-resource-binding.json +1 -1
- package/adapters/codex/bin/prebuild-manifest.json +8 -4
- package/adapters/codex/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/codex/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/codex/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/codex/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/codex/bin/registry.json +22 -1
- package/adapters/codex/bin/release-skill.bundle.mjs +4904 -3079
- package/adapters/codex/bin/rules.json +1 -1
- package/adapters/codex/schemas/release-plan.schema.json +3 -2
- package/adapters/codex/schemas/release-run.schema.json +35 -1
- package/adapters/codex/src/schemas/filesystem-tree-observation.schema.json +55 -0
- package/adapters/codex/src/schemas/host-verification-result.schema.json +2 -2
- package/adapters/codex/src/schemas/plugin-verification-request.schema.json +374 -0
- package/adapters/codex/src/schemas/plugin-verification-result.schema.json +799 -0
- package/adapters/codex/src/schemas/watchdog-termination-envelope.schema.json +16 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/error-codes.json +1 -1
- package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
- package/adapters/kimi/bin/prebuild-manifest.json +8 -4
- package/adapters/kimi/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/kimi/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/kimi/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/kimi/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/kimi/bin/registry.json +22 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +4904 -3079
- package/adapters/kimi/bin/rules.json +1 -1
- package/adapters/kimi/schemas/release-plan.schema.json +3 -2
- package/adapters/kimi/schemas/release-run.schema.json +35 -1
- package/adapters/kimi/src/schemas/filesystem-tree-observation.schema.json +55 -0
- package/adapters/kimi/src/schemas/host-verification-result.schema.json +2 -2
- package/adapters/kimi/src/schemas/plugin-verification-request.schema.json +374 -0
- package/adapters/kimi/src/schemas/plugin-verification-result.schema.json +799 -0
- package/adapters/kimi/src/schemas/watchdog-termination-envelope.schema.json +16 -0
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/error-codes.json +1 -1
- package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
- package/adapters/workbuddy/bin/prebuild-manifest.json +8 -4
- package/adapters/workbuddy/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/workbuddy/bin/registry.json +22 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +4904 -3079
- package/adapters/workbuddy/bin/rules.json +1 -1
- package/adapters/workbuddy/schemas/release-plan.schema.json +3 -2
- package/adapters/workbuddy/schemas/release-run.schema.json +35 -1
- package/adapters/workbuddy/src/schemas/filesystem-tree-observation.schema.json +55 -0
- package/adapters/workbuddy/src/schemas/host-verification-result.schema.json +2 -2
- package/adapters/workbuddy/src/schemas/plugin-verification-request.schema.json +374 -0
- package/adapters/workbuddy/src/schemas/plugin-verification-result.schema.json +799 -0
- package/adapters/workbuddy/src/schemas/watchdog-termination-envelope.schema.json +16 -0
- package/bin/error-codes.json +1 -1
- package/bin/foundation-resource-binding.json +1 -1
- package/bin/prebuild-manifest.json +8 -4
- package/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/bin/registry.json +22 -1
- package/bin/release-skill.bundle.mjs +4904 -3079
- package/bin/rules.json +1 -1
- package/package.json +4 -4
- package/platform-manifest.json +2 -2
- package/schemas/release-plan.schema.json +3 -2
- package/schemas/release-run.schema.json +35 -1
- package/src/commands/prepare.mjs +15 -4
- package/src/commands/verify.mjs +62 -10
- package/src/core/foundation-plugin-verification.mjs +247 -0
- package/src/core/skill-resource-closure.mjs +8 -4
- package/src/producers/foundation-resource-projection.mjs +3 -3
- package/src/schemas/filesystem-tree-observation.schema.json +55 -0
- package/src/schemas/host-verification-result.schema.json +2 -2
- package/src/schemas/plugin-verification-request.schema.json +374 -0
- package/src/schemas/plugin-verification-result.schema.json +799 -0
- package/src/schemas/watchdog-termination-envelope.schema.json +16 -0
package/bin/rules.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "release-skill",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "广州市风荷科技有限公司"
|
|
@@ -79,9 +79,9 @@
|
|
|
79
79
|
"ajv-formats": "3.0.1",
|
|
80
80
|
"libnpmpublish": "11.2.0",
|
|
81
81
|
"npm-registry-fetch": "18.0.2",
|
|
82
|
-
"skill-family-contracts": "0.
|
|
83
|
-
"skill-family-engineering-kit": "0.
|
|
84
|
-
"skill-family-harness-node": "0.
|
|
82
|
+
"skill-family-contracts": "0.13.0",
|
|
83
|
+
"skill-family-engineering-kit": "0.13.0",
|
|
84
|
+
"skill-family-harness-node": "0.13.0",
|
|
85
85
|
"yaml": "2.9.0"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
package/platform-manifest.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
3
|
"platformId": "release-skill",
|
|
4
4
|
"platformName": "release-skill Plugin Platform",
|
|
5
|
-
"version": "0.8.
|
|
5
|
+
"version": "0.8.1",
|
|
6
6
|
"logicalRoot": "skills-src/",
|
|
7
7
|
"logicalRootDigest": "3142e4454f192de6bb3de09141dfb7798a16acd3b8de820a70a2bf185f82fd38",
|
|
8
8
|
"projectionDigest": "43e29d004faac5800912a95abc013690e72f5cfd3b42e33cbc28fef39a39bfa3",
|
|
9
|
-
"projectionId": "release-skill:platform:0.8.
|
|
9
|
+
"projectionId": "release-skill:platform:0.8.1",
|
|
10
10
|
"manifestRequired": true,
|
|
11
11
|
"projectionSources": {
|
|
12
12
|
"skills-src-root": {
|
|
@@ -1051,9 +1051,10 @@
|
|
|
1051
1051
|
"humanConsumersStrategy": {
|
|
1052
1052
|
"type": "string",
|
|
1053
1053
|
"enum": [
|
|
1054
|
-
"manualFollowUps"
|
|
1054
|
+
"manualFollowUps",
|
|
1055
|
+
"foundationPayloadThenManualFollowUps"
|
|
1055
1056
|
],
|
|
1056
|
-
"description": "Human consumer strategy marker. '
|
|
1057
|
+
"description": "Human consumer strategy marker. 'foundationPayloadThenManualFollowUps': verify first observes the complete frozen plugin through Foundation, then preserves non-blocking Kimi/CodeBuddy marketplace/invocation follow-ups. 'manualFollowUps': legacy 0.8 behavior without the Foundation payload observation. Absent: legacy attestation closed loop."
|
|
1057
1058
|
},
|
|
1058
1059
|
"workflowKind": {
|
|
1059
1060
|
"type": "string",
|
|
@@ -530,7 +530,7 @@
|
|
|
530
530
|
},
|
|
531
531
|
"manualFollowUps": {
|
|
532
532
|
"type": "array",
|
|
533
|
-
"description": "Non-blocking
|
|
533
|
+
"description": "Non-blocking marketplace installation and invocation follow-ups for Kimi/CodeBuddy. Under foundationPayloadThenManualFollowUps, the frozen payload was observed first but these real-host tasks remain unverified.",
|
|
534
534
|
"items": {
|
|
535
535
|
"type": "object",
|
|
536
536
|
"required": [
|
|
@@ -571,6 +571,40 @@
|
|
|
571
571
|
}
|
|
572
572
|
}
|
|
573
573
|
},
|
|
574
|
+
"foundationPluginVerificationReceipts": {
|
|
575
|
+
"type": "array",
|
|
576
|
+
"description": "Foundation complete-plugin local payload observations bound to verify actions. These receipts do not prove marketplace installation or host invocation.",
|
|
577
|
+
"items": {
|
|
578
|
+
"type": "object",
|
|
579
|
+
"additionalProperties": false,
|
|
580
|
+
"required": [
|
|
581
|
+
"planDigest",
|
|
582
|
+
"actionId",
|
|
583
|
+
"unitId",
|
|
584
|
+
"requestDigest",
|
|
585
|
+
"hostId",
|
|
586
|
+
"goal",
|
|
587
|
+
"sourceType",
|
|
588
|
+
"foundationStatus",
|
|
589
|
+
"payloadMatches",
|
|
590
|
+
"observationDigest",
|
|
591
|
+
"observedAt"
|
|
592
|
+
],
|
|
593
|
+
"properties": {
|
|
594
|
+
"planDigest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
595
|
+
"actionId": { "type": "string", "minLength": 1 },
|
|
596
|
+
"unitId": { "type": "string", "minLength": 1 },
|
|
597
|
+
"requestDigest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
598
|
+
"hostId": { "enum": ["kimi-code", "workbuddy"] },
|
|
599
|
+
"goal": { "const": "install-only" },
|
|
600
|
+
"sourceType": { "const": "local-staged" },
|
|
601
|
+
"foundationStatus": { "const": "observed" },
|
|
602
|
+
"payloadMatches": { "const": true },
|
|
603
|
+
"observationDigest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
604
|
+
"observedAt": { "type": "string", "format": "date-time" }
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
},
|
|
574
608
|
"skillResourceClosureReceipts": {
|
|
575
609
|
"type": "array",
|
|
576
610
|
"description": "Per-surface skill resource closure verification receipts from verify phase",
|
package/src/commands/prepare.mjs
CHANGED
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
import { ReleaseError, GATE_FAILED, CONFIG_INVALID, CONFIG_MISSING, FORBIDDEN_CONTENT_DETECTED, RELEASE_DOCS_STALE, DIRTY_SOURCE_INPUT, BUNDLE_STALE } from '../core/errors.mjs';
|
|
59
59
|
import { assertBundleFreshness } from '../core/bundle-freshness.mjs';
|
|
60
60
|
import { assertAdapterFreshness, assertSelfBootstrapFacts } from '../core/derived-artifact-gates.mjs';
|
|
61
|
+
import { isFoundationPluginVerificationEligible } from '../core/foundation-plugin-verification.mjs';
|
|
61
62
|
import { PKG_ROOT } from '../core/pkg-root.mjs';
|
|
62
63
|
import { writeFrozenMarker, FROZEN_MARKER_FILENAME } from '../core/frozen-marker.mjs';
|
|
63
64
|
import {
|
|
@@ -4235,11 +4236,21 @@ export async function prepareRelease(options) {
|
|
|
4235
4236
|
});
|
|
4236
4237
|
}
|
|
4237
4238
|
|
|
4238
|
-
// Detect human consumer platforms (Kimi/CodeBuddy) in the plan.
|
|
4239
|
-
//
|
|
4240
|
-
|
|
4239
|
+
// Detect human consumer platforms (Kimi/CodeBuddy) in the plan. Foundation
|
|
4240
|
+
// 0.13 can observe their complete local payload only when production has
|
|
4241
|
+
// frozen a bundled-family source. Standalone marketplace sources remain on
|
|
4242
|
+
// the legacy manual-follow-up path because Foundation exposes no public
|
|
4243
|
+
// Kimi/WorkBuddy public-channel driver in this release.
|
|
4244
|
+
const humanConsumerActions = externalActions.filter(
|
|
4241
4245
|
(a) => a.type === 'kimi-marketplace-install' || a.type === 'codebuddy-marketplace-install',
|
|
4242
4246
|
);
|
|
4247
|
+
const humanConsumersStrategy = humanConsumerActions.length === 0
|
|
4248
|
+
? null
|
|
4249
|
+
: humanConsumerActions.some((action) => (
|
|
4250
|
+
isFoundationPluginVerificationEligible({ action, units })
|
|
4251
|
+
))
|
|
4252
|
+
? 'foundationPayloadThenManualFollowUps'
|
|
4253
|
+
: 'manualFollowUps';
|
|
4243
4254
|
|
|
4244
4255
|
// --- Fold the validated postPublish declaration into the frozen plan ---
|
|
4245
4256
|
// Bindings: tag (tagTemplate rendered at the resolved target version —
|
|
@@ -4331,7 +4342,7 @@ export async function prepareRelease(options) {
|
|
|
4331
4342
|
: {}),
|
|
4332
4343
|
verificationGates: config.verificationGates ?? [],
|
|
4333
4344
|
snapshotDigest: overallSnapshotDigest,
|
|
4334
|
-
...(
|
|
4345
|
+
...(humanConsumersStrategy ? { humanConsumersStrategy } : {}),
|
|
4335
4346
|
...(production ? {
|
|
4336
4347
|
production: {
|
|
4337
4348
|
mode: 'github-npm-v1',
|
package/src/commands/verify.mjs
CHANGED
|
@@ -62,6 +62,10 @@ import {
|
|
|
62
62
|
import { loadProjectConfig } from '../core/config.mjs';
|
|
63
63
|
import { assertTransition, PUBLISHED, VERIFIED } from '../core/state-machine.mjs';
|
|
64
64
|
import { clearFrozenMarker } from '../core/frozen-marker.mjs';
|
|
65
|
+
import {
|
|
66
|
+
isFoundationPluginVerificationEligible,
|
|
67
|
+
verifyFrozenPluginWithFoundation,
|
|
68
|
+
} from '../core/foundation-plugin-verification.mjs';
|
|
65
69
|
import { resolveUnitScopedPath } from '../snapshot/public-path.mjs';
|
|
66
70
|
import {
|
|
67
71
|
normalizeRegistry,
|
|
@@ -944,6 +948,7 @@ export async function verifyRelease(options) {
|
|
|
944
948
|
previousVerifyRun,
|
|
945
949
|
execFn,
|
|
946
950
|
configPath: configPathOpt,
|
|
951
|
+
runPluginVerificationFn,
|
|
947
952
|
} = options ?? {};
|
|
948
953
|
|
|
949
954
|
const clockFn = typeof clockOpt === 'function' ? clockOpt : defaultClock;
|
|
@@ -1351,10 +1356,15 @@ export async function verifyRelease(options) {
|
|
|
1351
1356
|
}
|
|
1352
1357
|
}
|
|
1353
1358
|
|
|
1354
|
-
//
|
|
1355
|
-
//
|
|
1356
|
-
//
|
|
1357
|
-
const isHumanConsumerFollowUp =
|
|
1359
|
+
// Kimi/CodeBuddy remain non-blocking post-release manual tasks. New plans
|
|
1360
|
+
// additionally require a Foundation observation of the complete frozen
|
|
1361
|
+
// payload before the manual marketplace/invocation follow-up is emitted.
|
|
1362
|
+
const isHumanConsumerFollowUp = [
|
|
1363
|
+
'manualFollowUps',
|
|
1364
|
+
'foundationPayloadThenManualFollowUps',
|
|
1365
|
+
].includes(plan.humanConsumersStrategy);
|
|
1366
|
+
const requiresFoundationPayloadObservation = plan.humanConsumersStrategy
|
|
1367
|
+
=== 'foundationPayloadThenManualFollowUps';
|
|
1358
1368
|
|
|
1359
1369
|
const missingManualAttestations = isHumanConsumerFollowUp
|
|
1360
1370
|
? []
|
|
@@ -1382,6 +1392,7 @@ export async function verifyRelease(options) {
|
|
|
1382
1392
|
|
|
1383
1393
|
// Collect non-blocking manual follow-up tasks for human consumer platforms
|
|
1384
1394
|
const manualFollowUps = [];
|
|
1395
|
+
const foundationPluginVerificationReceipts = [];
|
|
1385
1396
|
|
|
1386
1397
|
// Every action is identity-bound and adapters receive read-only or
|
|
1387
1398
|
// per-action isolated consumer paths. Run independent checks concurrently;
|
|
@@ -1414,13 +1425,40 @@ export async function verifyRelease(options) {
|
|
|
1414
1425
|
}
|
|
1415
1426
|
|
|
1416
1427
|
if (isMarketplaceAction(action.type)) {
|
|
1417
|
-
//
|
|
1418
|
-
//
|
|
1419
|
-
//
|
|
1428
|
+
// Both human-consumer strategies keep marketplace installation and
|
|
1429
|
+
// invocation as non-blocking manual follow-ups. The new strategy first
|
|
1430
|
+
// requires Foundation to observe the identity-bound frozen payload.
|
|
1420
1431
|
const isHumanConsumerPlatform = action.type === 'kimi-marketplace-install'
|
|
1421
1432
|
|| action.type === 'codebuddy-marketplace-install';
|
|
1422
1433
|
if (isHumanConsumerFollowUp && isHumanConsumerPlatform) {
|
|
1423
1434
|
const platform = action.type === 'kimi-marketplace-install' ? 'kimi' : 'codebuddy';
|
|
1435
|
+
const observesFoundationPayload = requiresFoundationPayloadObservation
|
|
1436
|
+
&& isFoundationPluginVerificationEligible({ action, units: plan.units });
|
|
1437
|
+
if (observesFoundationPayload) {
|
|
1438
|
+
const actionRunDir = join(runDir, 'foundation-plugin-verification', action.id);
|
|
1439
|
+
await mkdir(join(actionRunDir, 'install'), { recursive: true, mode: 0o700 });
|
|
1440
|
+
await mkdir(join(actionRunDir, 'temporary'), { recursive: true, mode: 0o700 });
|
|
1441
|
+
await mkdir(join(actionRunDir, 'evidence'), { recursive: true, mode: 0o700 });
|
|
1442
|
+
const receipt = await verifyFrozenPluginWithFoundation({
|
|
1443
|
+
plan,
|
|
1444
|
+
action,
|
|
1445
|
+
root,
|
|
1446
|
+
runDir: actionRunDir,
|
|
1447
|
+
...(runPluginVerificationFn ? { runPluginVerificationFn } : {}),
|
|
1448
|
+
clock: clockFn,
|
|
1449
|
+
});
|
|
1450
|
+
foundationPluginVerificationReceipts.push(receipt);
|
|
1451
|
+
await evidence.append({
|
|
1452
|
+
phase: 'verify-marketplace',
|
|
1453
|
+
actionId: action.id,
|
|
1454
|
+
actionType: action.type,
|
|
1455
|
+
status: 'FOUNDATION_PAYLOAD_OBSERVED',
|
|
1456
|
+
requestDigest: receipt.requestDigest,
|
|
1457
|
+
hostId: receipt.hostId,
|
|
1458
|
+
payloadMatches: receipt.payloadMatches,
|
|
1459
|
+
observationDigest: receipt.observationDigest,
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1424
1462
|
manualFollowUps.push({
|
|
1425
1463
|
actionId: action.id,
|
|
1426
1464
|
platform,
|
|
@@ -1428,20 +1466,26 @@ export async function verifyRelease(options) {
|
|
|
1428
1466
|
version: action.parameters?.version,
|
|
1429
1467
|
unitId: action.unitId,
|
|
1430
1468
|
verifiedBySystem: false,
|
|
1431
|
-
reason:
|
|
1469
|
+
reason: observesFoundationPayload
|
|
1470
|
+
? 'complete frozen payload observed by Foundation; marketplace installation and host invocation remain manual'
|
|
1471
|
+
: 'human consumer platform — installation is a manual post-release task; not verified by system',
|
|
1432
1472
|
});
|
|
1433
1473
|
adapterChecks.push({
|
|
1434
1474
|
actionId: action.id,
|
|
1435
1475
|
actionType: action.type,
|
|
1436
1476
|
status: 'SKIPPED',
|
|
1437
|
-
reason:
|
|
1477
|
+
reason: observesFoundationPayload
|
|
1478
|
+
? 'foundation-payload-observed-manual-follow-up'
|
|
1479
|
+
: 'manual-follow-up',
|
|
1438
1480
|
});
|
|
1439
1481
|
await evidence.append({
|
|
1440
1482
|
phase: 'verify-marketplace',
|
|
1441
1483
|
actionId: action.id,
|
|
1442
1484
|
actionType: action.type,
|
|
1443
1485
|
status: 'SKIPPED',
|
|
1444
|
-
reason:
|
|
1486
|
+
reason: observesFoundationPayload
|
|
1487
|
+
? 'foundation-payload-observed-manual-follow-up'
|
|
1488
|
+
: 'manual-follow-up',
|
|
1445
1489
|
});
|
|
1446
1490
|
return;
|
|
1447
1491
|
}
|
|
@@ -1727,6 +1771,8 @@ export async function verifyRelease(options) {
|
|
|
1727
1771
|
}));
|
|
1728
1772
|
adapterChecks.sort((a, b) => a.actionId.localeCompare(b.actionId));
|
|
1729
1773
|
consumerVerificationReceipts.sort((a, b) => a.actionId.localeCompare(b.actionId));
|
|
1774
|
+
foundationPluginVerificationReceipts.sort((a, b) => a.actionId.localeCompare(b.actionId));
|
|
1775
|
+
manualFollowUps.sort((a, b) => a.actionId.localeCompare(b.actionId));
|
|
1730
1776
|
consumerGateResults.sort((a, b) => a.id.localeCompare(b.id));
|
|
1731
1777
|
const rejectedAction = actionResults.find((result) => result.status === 'rejected');
|
|
1732
1778
|
if (rejectedAction) throw rejectedAction.reason;
|
|
@@ -2034,6 +2080,9 @@ export async function verifyRelease(options) {
|
|
|
2034
2080
|
gateResults: consumerGateResults,
|
|
2035
2081
|
consumerVerificationReceipts,
|
|
2036
2082
|
skillResourceClosureReceipts,
|
|
2083
|
+
...(foundationPluginVerificationReceipts.length > 0
|
|
2084
|
+
? { foundationPluginVerificationReceipts }
|
|
2085
|
+
: {}),
|
|
2037
2086
|
...(manualFollowUps.length > 0 ? { manualFollowUps } : {}),
|
|
2038
2087
|
startedAt: clockFn(),
|
|
2039
2088
|
finishedAt: clockFn(),
|
|
@@ -2132,6 +2181,9 @@ export async function verifyRelease(options) {
|
|
|
2132
2181
|
recoveryActionCode: null,
|
|
2133
2182
|
smokeTest,
|
|
2134
2183
|
gateResults: consumerGateResults,
|
|
2184
|
+
...(foundationPluginVerificationReceipts.length > 0
|
|
2185
|
+
? { foundationPluginVerificationReceipts }
|
|
2186
|
+
: {}),
|
|
2135
2187
|
...(manualFollowUps.length > 0 ? { manualFollowUps } : {}),
|
|
2136
2188
|
...(baselineAdvance ? { baselineAdvance } : {}),
|
|
2137
2189
|
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release-domain thin adapter for Foundation 0.13 complete-plugin observation.
|
|
3
|
+
*
|
|
4
|
+
* The frozen snapshot remains the only payload authority. This module derives
|
|
5
|
+
* transient Foundation inputs from that tree, calls the released Kit API, and
|
|
6
|
+
* returns only the fields needed to bind the mechanism observation to a
|
|
7
|
+
* release action. It does not prove a marketplace install or host invocation.
|
|
8
|
+
*
|
|
9
|
+
* @module core/foundation-plugin-verification
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
|
|
14
|
+
import { digestDocument } from 'skill-family-contracts';
|
|
15
|
+
import {
|
|
16
|
+
bundledHostProfilesRoot,
|
|
17
|
+
describeHost,
|
|
18
|
+
runPluginVerification,
|
|
19
|
+
} from 'skill-family-engineering-kit';
|
|
20
|
+
import {
|
|
21
|
+
createFilesystemRootBinding,
|
|
22
|
+
readFileBound,
|
|
23
|
+
} from 'skill-family-harness-node';
|
|
24
|
+
|
|
25
|
+
import { getPlatform, normalizeHostId } from '../platforms/registry.mjs';
|
|
26
|
+
import { verifyFrozenSnapshot } from '../snapshot/frozen.mjs';
|
|
27
|
+
import { ReleaseError, POST_PUBLISH_VERIFY_FAILED } from './errors.mjs';
|
|
28
|
+
|
|
29
|
+
// Foundation's public plugin-verification request freezes the built-in driver
|
|
30
|
+
// contract at 1.0.0. The real API integration test intentionally exercises
|
|
31
|
+
// this value so an upstream contract change fails closed during adoption.
|
|
32
|
+
const FOUNDATION_PLUGIN_DRIVER_VERSION = '1.0.0';
|
|
33
|
+
|
|
34
|
+
function fail(message, details = {}) {
|
|
35
|
+
return new ReleaseError(POST_PUBLISH_VERIFY_FAILED, message, details);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function projectSourceMembers(entries) {
|
|
39
|
+
const directories = new Set();
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
const parts = entry.path.split('/');
|
|
42
|
+
for (let count = 1; count < parts.length; count += 1) {
|
|
43
|
+
directories.add(parts.slice(0, count).join('/'));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [
|
|
47
|
+
...[...directories].map((path) => ({ path, type: 'directory' })),
|
|
48
|
+
...entries.map((entry) => ({
|
|
49
|
+
path: entry.path,
|
|
50
|
+
type: 'file',
|
|
51
|
+
sha256: entry.contentDigest,
|
|
52
|
+
bytes: entry.size,
|
|
53
|
+
executable: (entry.mode & 0o111) !== 0,
|
|
54
|
+
})),
|
|
55
|
+
].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Return whether one human-consumer action has the complete frozen identity
|
|
60
|
+
* required by Foundation's local-staged plugin verification.
|
|
61
|
+
*
|
|
62
|
+
* This predicate is deliberately plan-derived. It does not persist a second
|
|
63
|
+
* action list, and the verifier repeats the same checks before every call.
|
|
64
|
+
*/
|
|
65
|
+
export function isFoundationPluginVerificationEligible({ action, units = [] } = {}) {
|
|
66
|
+
const platformId = action?.parameters?.consumer;
|
|
67
|
+
const expectedType = platformId === 'kimi'
|
|
68
|
+
? 'kimi-marketplace-install'
|
|
69
|
+
: platformId === 'codebuddy'
|
|
70
|
+
? 'codebuddy-marketplace-install'
|
|
71
|
+
: null;
|
|
72
|
+
if (!expectedType || action?.type !== expectedType) return false;
|
|
73
|
+
|
|
74
|
+
const unit = units.find((candidate) => candidate.id === action.unitId);
|
|
75
|
+
const snapshot = unit?.frozenSnapshot;
|
|
76
|
+
const sourceDescriptor = action.parameters?.sourceDescriptor;
|
|
77
|
+
if (
|
|
78
|
+
!snapshot?.path
|
|
79
|
+
|| !snapshot.commit
|
|
80
|
+
|| !snapshot.manifestDigest
|
|
81
|
+
|| sourceDescriptor?.form !== 'bundled-family'
|
|
82
|
+
|| sourceDescriptor.pluginSubpath !== '.'
|
|
83
|
+
|| sourceDescriptor.commit !== snapshot.commit
|
|
84
|
+
|| sourceDescriptor.payloadDigest !== snapshot.manifestDigest
|
|
85
|
+
|| action.parameters?.sourceCommit !== snapshot.commit
|
|
86
|
+
|| action.parameters?.snapshotPath !== snapshot.path
|
|
87
|
+
|| action.parameters?.manifestDigest !== snapshot.manifestDigest
|
|
88
|
+
) return false;
|
|
89
|
+
|
|
90
|
+
const platform = getPlatform(platformId);
|
|
91
|
+
return (unit.distributions ?? []).some((distribution) => (
|
|
92
|
+
distribution.type === platform.distributionType
|
|
93
|
+
&& typeof distribution.installationContract?.manifestRelativePath === 'string'
|
|
94
|
+
&& distribution.installationContract.manifestRelativePath.length > 0
|
|
95
|
+
));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function assertResultMatchesRequest(result, request) {
|
|
99
|
+
if (
|
|
100
|
+
result?.status !== 'observed'
|
|
101
|
+
|| result.requestDigest !== digestDocument(request)
|
|
102
|
+
|| result.goal !== request.goal
|
|
103
|
+
|| digestDocument(result.source) !== digestDocument(request.source)
|
|
104
|
+
|| digestDocument(result.host) !== digestDocument(request.host)
|
|
105
|
+
|| result.facts?.install?.payloadMatches !== true
|
|
106
|
+
|| result.facts.install.sourceMode !== 'local-staged'
|
|
107
|
+
|| result.facts.install.retained !== true
|
|
108
|
+
|| result.facts.invocation !== null
|
|
109
|
+
) {
|
|
110
|
+
throw fail('Foundation complete-plugin observation did not satisfy the frozen local payload contract', {
|
|
111
|
+
foundationStatus: result?.status ?? null,
|
|
112
|
+
foundationReason: result?.reason ?? null,
|
|
113
|
+
payloadMatches: result?.facts?.install?.payloadMatches ?? null,
|
|
114
|
+
requiredActions: result?.requiredActions ?? [],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Observe one frozen Kimi or CodeBuddy plugin payload through Foundation.
|
|
121
|
+
*
|
|
122
|
+
* @param {object} input
|
|
123
|
+
* @param {object} input.plan
|
|
124
|
+
* @param {object} input.action
|
|
125
|
+
* @param {string} input.root
|
|
126
|
+
* @param {string} input.runDir - Fresh per-action directory containing empty
|
|
127
|
+
* install, temporary and evidence children.
|
|
128
|
+
* @param {Function} [input.runPluginVerificationFn]
|
|
129
|
+
* @param {Function} [input.clock]
|
|
130
|
+
* @returns {Promise<object>} local payload-observation receipt
|
|
131
|
+
*/
|
|
132
|
+
export async function verifyFrozenPluginWithFoundation({
|
|
133
|
+
plan,
|
|
134
|
+
action,
|
|
135
|
+
root,
|
|
136
|
+
runDir,
|
|
137
|
+
runPluginVerificationFn = runPluginVerification,
|
|
138
|
+
clock = () => new Date().toISOString(),
|
|
139
|
+
} = {}) {
|
|
140
|
+
const unit = (plan?.units ?? []).find((candidate) => candidate.id === action?.unitId);
|
|
141
|
+
if (!unit?.frozenSnapshot?.path || !unit.frozenSnapshot.commit) {
|
|
142
|
+
throw fail('Foundation plugin observation requires a frozen unit snapshot', {
|
|
143
|
+
actionId: action?.id,
|
|
144
|
+
unitId: action?.unitId,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!isFoundationPluginVerificationEligible({ action, units: plan.units })) {
|
|
149
|
+
throw fail('Foundation local plugin observation requires one identity-bound bundled-family payload', {
|
|
150
|
+
actionId: action?.id,
|
|
151
|
+
sourceForm: action?.parameters?.sourceDescriptor?.form ?? null,
|
|
152
|
+
pluginSubpath: action?.parameters?.sourceDescriptor?.pluginSubpath ?? null,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const platformId = action?.parameters?.consumer;
|
|
157
|
+
const platform = getPlatform(platformId);
|
|
158
|
+
if (!['kimi', 'codebuddy'].includes(platform.id)) {
|
|
159
|
+
throw fail('Foundation local plugin observation is not configured for this release platform', {
|
|
160
|
+
actionId: action?.id,
|
|
161
|
+
platform: platform.id,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const sourceDescriptor = action.parameters.sourceDescriptor;
|
|
165
|
+
const distribution = (unit.distributions ?? []).find(
|
|
166
|
+
(candidate) => candidate.type === platform.distributionType,
|
|
167
|
+
);
|
|
168
|
+
const manifestRelativePath = distribution?.installationContract?.manifestRelativePath;
|
|
169
|
+
if (!manifestRelativePath) {
|
|
170
|
+
throw fail('Foundation plugin observation requires the frozen installation manifest path', {
|
|
171
|
+
actionId: action.id,
|
|
172
|
+
distributionType: platform.distributionType,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const frozen = await verifyFrozenSnapshot({
|
|
177
|
+
root,
|
|
178
|
+
snapshotPath: unit.frozenSnapshot.path,
|
|
179
|
+
expectedDigest: unit.frozenSnapshot.manifestDigest,
|
|
180
|
+
});
|
|
181
|
+
const sourceRoot = frozen.snapshotDir;
|
|
182
|
+
const sourceMembers = projectSourceMembers(frozen.entries);
|
|
183
|
+
const sourceManifest = frozen.entries.find((entry) => entry.path === manifestRelativePath);
|
|
184
|
+
if (!sourceManifest) {
|
|
185
|
+
throw fail('Foundation plugin observation manifest is not part of the frozen payload', {
|
|
186
|
+
actionId: action.id,
|
|
187
|
+
manifestRelativePath,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const hostsRoot = bundledHostProfilesRoot();
|
|
192
|
+
const hostId = await normalizeHostId(platform.buildAdapter.name);
|
|
193
|
+
const descriptor = await describeHost({ hostId, hostsRoot });
|
|
194
|
+
const hostsBinding = await createFilesystemRootBinding(hostsRoot);
|
|
195
|
+
const descriptorFile = await readFileBound(hostsRoot, `${hostId}/host-descriptor.json`, {
|
|
196
|
+
rootBinding: hostsBinding,
|
|
197
|
+
});
|
|
198
|
+
if (!descriptor.verification?.driverId) {
|
|
199
|
+
throw fail('Foundation host has no complete-plugin verification driver', { hostId });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const request = {
|
|
203
|
+
schemaVersion: 1,
|
|
204
|
+
kind: 'skill-family.plugin-verification-request',
|
|
205
|
+
operation: 'plugin-verification',
|
|
206
|
+
verificationSetId: `${plan.digest}:${action.id}`,
|
|
207
|
+
goal: 'install-only',
|
|
208
|
+
source: {
|
|
209
|
+
type: 'local-staged',
|
|
210
|
+
candidateRef: sourceDescriptor.commit,
|
|
211
|
+
sourceManifestSha256: sourceManifest.contentDigest,
|
|
212
|
+
membersDigest: digestDocument(sourceMembers),
|
|
213
|
+
},
|
|
214
|
+
host: {
|
|
215
|
+
hostId,
|
|
216
|
+
descriptorSha256: descriptorFile.sha256,
|
|
217
|
+
driverId: descriptor.verification.driverId,
|
|
218
|
+
driverVersion: FOUNDATION_PLUGIN_DRIVER_VERSION,
|
|
219
|
+
},
|
|
220
|
+
install: { mode: 'fresh-tree' },
|
|
221
|
+
};
|
|
222
|
+
const bindings = {
|
|
223
|
+
sourceRoot,
|
|
224
|
+
sourceManifestRelPath: manifestRelativePath,
|
|
225
|
+
sourceMembers,
|
|
226
|
+
installContainerRoot: join(runDir, 'install'),
|
|
227
|
+
temporaryRoot: join(runDir, 'temporary'),
|
|
228
|
+
privateEvidenceRoot: join(runDir, 'evidence'),
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const result = await runPluginVerificationFn({ request, bindings, hostsRoot });
|
|
232
|
+
assertResultMatchesRequest(result, request);
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
planDigest: plan.digest,
|
|
236
|
+
actionId: action.id,
|
|
237
|
+
unitId: unit.id,
|
|
238
|
+
requestDigest: result.requestDigest,
|
|
239
|
+
hostId,
|
|
240
|
+
goal: request.goal,
|
|
241
|
+
sourceType: request.source.type,
|
|
242
|
+
foundationStatus: result.status,
|
|
243
|
+
payloadMatches: result.facts.install.payloadMatches,
|
|
244
|
+
observationDigest: result.facts.install.observationDigest,
|
|
245
|
+
observedAt: clock(),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
@@ -376,10 +376,14 @@ export function evaluateConsumerSkillResourceClosureReceipts(plan, receipts) {
|
|
|
376
376
|
'kimi-plugin',
|
|
377
377
|
'codebuddy-plugin',
|
|
378
378
|
]);
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
|
|
379
|
+
// Kimi/CodeBuddy remain non-blocking manual marketplace/invocation tasks in
|
|
380
|
+
// both strategy generations. The newer strategy adds a Foundation frozen
|
|
381
|
+
// payload observation but still does not establish an installed host
|
|
382
|
+
// surface, so these distributions stay outside this receipt set.
|
|
383
|
+
const humanConsumerTypes = [
|
|
384
|
+
'manualFollowUps',
|
|
385
|
+
'foundationPayloadThenManualFollowUps',
|
|
386
|
+
].includes(plan.humanConsumersStrategy)
|
|
383
387
|
? new Set(['kimi-plugin', 'codebuddy-plugin'])
|
|
384
388
|
: new Set();
|
|
385
389
|
const expectedKeys = [];
|
|
@@ -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.13.0',
|
|
59
|
+
'skill-family-engineering-kit': '0.13.0',
|
|
60
|
+
'skill-family-harness-node': '0.13.0',
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
export const BINDING_RECORD_PATH = 'bin/foundation-resource-binding.json';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://contracts.skill-family.example/v1/filesystem-tree-observation.json",
|
|
4
|
+
"title": "Filesystem tree observation",
|
|
5
|
+
"description": "A complete same-pass fact observation of a bound filesystem tree (FND-DES-014 section 6.1). Private data only: it is returned by the public read-only harness entry, but never enters a public plugin verification result. The record keeps the original root binding so consumers re-verify the same bound root and never re-bind a replacement. Members are canonical relative paths sorted by Unicode code point; directories carry identity/type/permission facts only; file bytes come from the same native descriptor that supplies statMode.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schemaVersion", "kind", "rootBinding", "members", "membersDigest"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schemaVersion": { "const": 1 },
|
|
11
|
+
"kind": { "const": "skill-family.filesystem-tree-observation" },
|
|
12
|
+
"rootBinding": { "$ref": "https://contracts.skill-family.example/v1/filesystem-root-binding.json" },
|
|
13
|
+
"members": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"uniqueItems": true,
|
|
16
|
+
"items": { "$ref": "#/$defs/member" }
|
|
17
|
+
},
|
|
18
|
+
"membersDigest": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"$defs": {
|
|
24
|
+
"statMode": { "type": "integer", "minimum": 0, "maximum": 65535 },
|
|
25
|
+
"member": {
|
|
26
|
+
"oneOf": [
|
|
27
|
+
{ "$ref": "#/$defs/fileMember" },
|
|
28
|
+
{ "$ref": "#/$defs/directoryMember" }
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"fileMember": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"required": ["path", "type", "sha256", "bytes", "statMode", "contentBase64"],
|
|
35
|
+
"properties": {
|
|
36
|
+
"path": { "type": "string", "minLength": 1 },
|
|
37
|
+
"type": { "const": "file" },
|
|
38
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
39
|
+
"bytes": { "type": "integer", "minimum": 0 },
|
|
40
|
+
"statMode": { "$ref": "#/$defs/statMode" },
|
|
41
|
+
"contentBase64": { "type": "string" }
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"directoryMember": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"required": ["path", "type", "statMode"],
|
|
48
|
+
"properties": {
|
|
49
|
+
"path": { "type": "string", "minLength": 1 },
|
|
50
|
+
"type": { "const": "directory" },
|
|
51
|
+
"statMode": { "$ref": "#/$defs/statMode" }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -103,8 +103,8 @@
|
|
|
103
103
|
"spawned": { "type": "boolean" },
|
|
104
104
|
"exitStatus": { "type": ["integer", "null"], "minimum": 0 },
|
|
105
105
|
"processStatus": { "type": ["string", "null"], "enum": ["PLANNED", "STARTING", "RUNNING", "EXITED", "FAILED_TO_START", "TIMED_OUT", "TERMINATED", null] },
|
|
106
|
-
"terminationReason": { "$ref": "https://contracts.skill-family.example/v1/watchdog-termination-envelope.json#/properties/terminationReason" },
|
|
107
|
-
"watchdogReason": { "$ref": "https://contracts.skill-family.example/v1/watchdog-termination-envelope.json#/properties/watchdogReason" },
|
|
106
|
+
"terminationReason": { "$ref": "https://contracts.skill-family.example/v1/watchdog-termination-envelope.json#/properties/terminationReason", "not": { "const": "output_limit_exceeded" } },
|
|
107
|
+
"watchdogReason": { "$ref": "https://contracts.skill-family.example/v1/watchdog-termination-envelope.json#/properties/watchdogReason", "not": { "const": "output_limit_exceeded" } },
|
|
108
108
|
"runnerModelOverrideAbsent": { "type": "boolean" }
|
|
109
109
|
}
|
|
110
110
|
},
|