release-skill 0.9.22 → 0.9.24
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 +61 -2
- package/INSTALL.md +4 -4
- package/INSTALL.zh-CN.md +4 -4
- package/README.md +53 -32
- package/README.zh-CN.md +44 -27
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/consumer-contract-vectors.json +7 -7
- package/adapters/claude/bin/error-codes.json +1 -1
- package/adapters/claude/bin/foundation-resource-binding.json +1 -1
- package/adapters/claude/bin/registry.json +50 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +2680 -2180
- package/adapters/claude/bin/rules.json +1 -1
- package/adapters/claude/skills/release-assess/SKILL.md +17 -3
- package/adapters/claude/skills/release-config/SKILL.md +6 -0
- package/adapters/claude/skills/release-docs/SKILL.md +6 -0
- package/adapters/claude/skills/release-help/SKILL.md +7 -2
- package/adapters/claude/skills/release-marketplace/SKILL.md +6 -0
- package/adapters/claude/skills/release-prepare/SKILL.md +8 -2
- package/adapters/claude/skills/release-publish/SKILL.md +7 -1
- package/adapters/claude/skills/release-reconcile/SKILL.md +6 -0
- package/adapters/claude/skills/release-setup/SKILL.md +9 -1
- package/adapters/claude/skills/release-verify/SKILL.md +6 -0
- package/adapters/claude/src/schemas/file-set-apply-request.schema.json +78 -0
- package/adapters/claude/src/schemas/file-set-event-payload.schema.json +55 -0
- package/adapters/claude/src/schemas/file-set-prune-request.schema.json +17 -0
- package/adapters/claude/src/schemas/file-set-recovery-request.schema.json +29 -0
- package/adapters/claude/src/schemas/file-set-result.schema.json +79 -0
- package/adapters/claude/src/schemas/professional-conclusion.schema.json +132 -0
- package/adapters/claude/src/schemas/project-manifest.schema.json +91 -0
- package/adapters/claude/src/schemas/state-store-recovery-observation.schema.json +129 -0
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/consumer-contract-vectors.json +7 -7
- package/adapters/codex/bin/error-codes.json +1 -1
- package/adapters/codex/bin/foundation-resource-binding.json +1 -1
- package/adapters/codex/bin/registry.json +50 -1
- package/adapters/codex/bin/release-skill.bundle.mjs +2680 -2180
- package/adapters/codex/bin/rules.json +1 -1
- package/adapters/codex/skills/release-assess/SKILL.md +17 -3
- package/adapters/codex/skills/release-config/SKILL.md +6 -0
- package/adapters/codex/skills/release-docs/SKILL.md +6 -0
- package/adapters/codex/skills/release-help/SKILL.md +9 -3
- package/adapters/codex/skills/release-marketplace/SKILL.md +6 -0
- package/adapters/codex/skills/release-prepare/SKILL.md +8 -2
- package/adapters/codex/skills/release-publish/SKILL.md +7 -1
- package/adapters/codex/skills/release-reconcile/SKILL.md +6 -0
- package/adapters/codex/skills/release-setup/SKILL.md +9 -1
- package/adapters/codex/skills/release-verify/SKILL.md +6 -0
- package/adapters/codex/src/schemas/file-set-apply-request.schema.json +78 -0
- package/adapters/codex/src/schemas/file-set-event-payload.schema.json +55 -0
- package/adapters/codex/src/schemas/file-set-prune-request.schema.json +17 -0
- package/adapters/codex/src/schemas/file-set-recovery-request.schema.json +29 -0
- package/adapters/codex/src/schemas/file-set-result.schema.json +79 -0
- package/adapters/codex/src/schemas/professional-conclusion.schema.json +132 -0
- package/adapters/codex/src/schemas/project-manifest.schema.json +91 -0
- package/adapters/codex/src/schemas/state-store-recovery-observation.schema.json +129 -0
- package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
- package/adapters/cursor/bin/consumer-contract-vectors.json +7 -7
- package/adapters/cursor/bin/error-codes.json +1 -1
- package/adapters/cursor/bin/foundation-resource-binding.json +1 -1
- package/adapters/cursor/bin/registry.json +50 -1
- package/adapters/cursor/bin/release-skill.bundle.mjs +2680 -2180
- package/adapters/cursor/bin/rules.json +1 -1
- package/adapters/cursor/skills/release-assess/SKILL.md +17 -3
- package/adapters/cursor/skills/release-config/SKILL.md +6 -0
- package/adapters/cursor/skills/release-docs/SKILL.md +6 -0
- package/adapters/cursor/skills/release-help/SKILL.md +9 -3
- package/adapters/cursor/skills/release-marketplace/SKILL.md +6 -0
- package/adapters/cursor/skills/release-prepare/SKILL.md +8 -2
- package/adapters/cursor/skills/release-publish/SKILL.md +7 -1
- package/adapters/cursor/skills/release-reconcile/SKILL.md +6 -0
- package/adapters/cursor/skills/release-setup/SKILL.md +9 -1
- package/adapters/cursor/skills/release-verify/SKILL.md +6 -0
- package/adapters/cursor/src/schemas/file-set-apply-request.schema.json +78 -0
- package/adapters/cursor/src/schemas/file-set-event-payload.schema.json +55 -0
- package/adapters/cursor/src/schemas/file-set-prune-request.schema.json +17 -0
- package/adapters/cursor/src/schemas/file-set-recovery-request.schema.json +29 -0
- package/adapters/cursor/src/schemas/file-set-result.schema.json +79 -0
- package/adapters/cursor/src/schemas/professional-conclusion.schema.json +132 -0
- package/adapters/cursor/src/schemas/project-manifest.schema.json +91 -0
- package/adapters/cursor/src/schemas/state-store-recovery-observation.schema.json +129 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/consumer-contract-vectors.json +7 -7
- package/adapters/kimi/bin/error-codes.json +1 -1
- package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
- package/adapters/kimi/bin/registry.json +50 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +2680 -2180
- package/adapters/kimi/bin/rules.json +1 -1
- package/adapters/kimi/skills/release-assess/SKILL.md +17 -3
- package/adapters/kimi/skills/release-config/SKILL.md +6 -0
- package/adapters/kimi/skills/release-docs/SKILL.md +6 -0
- package/adapters/kimi/skills/release-help/SKILL.md +9 -3
- package/adapters/kimi/skills/release-marketplace/SKILL.md +6 -0
- package/adapters/kimi/skills/release-prepare/SKILL.md +8 -2
- package/adapters/kimi/skills/release-publish/SKILL.md +7 -1
- package/adapters/kimi/skills/release-reconcile/SKILL.md +6 -0
- package/adapters/kimi/skills/release-setup/SKILL.md +9 -1
- package/adapters/kimi/skills/release-verify/SKILL.md +6 -0
- package/adapters/kimi/src/schemas/file-set-apply-request.schema.json +78 -0
- package/adapters/kimi/src/schemas/file-set-event-payload.schema.json +55 -0
- package/adapters/kimi/src/schemas/file-set-prune-request.schema.json +17 -0
- package/adapters/kimi/src/schemas/file-set-recovery-request.schema.json +29 -0
- package/adapters/kimi/src/schemas/file-set-result.schema.json +79 -0
- package/adapters/kimi/src/schemas/professional-conclusion.schema.json +132 -0
- package/adapters/kimi/src/schemas/project-manifest.schema.json +91 -0
- package/adapters/kimi/src/schemas/state-store-recovery-observation.schema.json +129 -0
- package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
- package/adapters/qoder/bin/consumer-contract-vectors.json +7 -7
- package/adapters/qoder/bin/error-codes.json +1 -1
- package/adapters/qoder/bin/foundation-resource-binding.json +1 -1
- package/adapters/qoder/bin/registry.json +50 -1
- package/adapters/qoder/bin/release-skill.bundle.mjs +2680 -2180
- package/adapters/qoder/bin/rules.json +1 -1
- package/adapters/qoder/skills/release-assess/SKILL.md +17 -3
- package/adapters/qoder/skills/release-config/SKILL.md +6 -0
- package/adapters/qoder/skills/release-docs/SKILL.md +6 -0
- package/adapters/qoder/skills/release-help/SKILL.md +9 -3
- package/adapters/qoder/skills/release-marketplace/SKILL.md +6 -0
- package/adapters/qoder/skills/release-prepare/SKILL.md +8 -2
- package/adapters/qoder/skills/release-publish/SKILL.md +7 -1
- package/adapters/qoder/skills/release-reconcile/SKILL.md +6 -0
- package/adapters/qoder/skills/release-setup/SKILL.md +9 -1
- package/adapters/qoder/skills/release-verify/SKILL.md +6 -0
- package/adapters/qoder/src/schemas/file-set-apply-request.schema.json +78 -0
- package/adapters/qoder/src/schemas/file-set-event-payload.schema.json +55 -0
- package/adapters/qoder/src/schemas/file-set-prune-request.schema.json +17 -0
- package/adapters/qoder/src/schemas/file-set-recovery-request.schema.json +29 -0
- package/adapters/qoder/src/schemas/file-set-result.schema.json +79 -0
- package/adapters/qoder/src/schemas/professional-conclusion.schema.json +132 -0
- package/adapters/qoder/src/schemas/project-manifest.schema.json +91 -0
- package/adapters/qoder/src/schemas/state-store-recovery-observation.schema.json +129 -0
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/consumer-contract-vectors.json +7 -7
- package/adapters/workbuddy/bin/error-codes.json +1 -1
- package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
- package/adapters/workbuddy/bin/registry.json +50 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +2680 -2180
- package/adapters/workbuddy/bin/rules.json +1 -1
- package/adapters/workbuddy/skills/release-assess/SKILL.md +17 -3
- package/adapters/workbuddy/skills/release-config/SKILL.md +6 -0
- package/adapters/workbuddy/skills/release-docs/SKILL.md +6 -0
- package/adapters/workbuddy/skills/release-help/SKILL.md +7 -2
- package/adapters/workbuddy/skills/release-marketplace/SKILL.md +6 -0
- package/adapters/workbuddy/skills/release-prepare/SKILL.md +8 -2
- package/adapters/workbuddy/skills/release-publish/SKILL.md +7 -1
- package/adapters/workbuddy/skills/release-reconcile/SKILL.md +6 -0
- package/adapters/workbuddy/skills/release-setup/SKILL.md +9 -1
- package/adapters/workbuddy/skills/release-verify/SKILL.md +6 -0
- package/adapters/workbuddy/src/schemas/file-set-apply-request.schema.json +78 -0
- package/adapters/workbuddy/src/schemas/file-set-event-payload.schema.json +55 -0
- package/adapters/workbuddy/src/schemas/file-set-prune-request.schema.json +17 -0
- package/adapters/workbuddy/src/schemas/file-set-recovery-request.schema.json +29 -0
- package/adapters/workbuddy/src/schemas/file-set-result.schema.json +79 -0
- package/adapters/workbuddy/src/schemas/professional-conclusion.schema.json +132 -0
- package/adapters/workbuddy/src/schemas/project-manifest.schema.json +91 -0
- package/adapters/workbuddy/src/schemas/state-store-recovery-observation.schema.json +129 -0
- package/bin/consumer-contract-vectors.json +7 -7
- package/bin/error-codes.json +1 -1
- package/bin/foundation-resource-binding.json +1 -1
- package/bin/registry.json +50 -1
- package/bin/release-skill-cli.mjs +80 -5
- package/bin/release-skill.bundle.mjs +2680 -2180
- package/bin/rules.json +1 -1
- package/package.json +4 -4
- package/platform-manifest.json +4 -4
- package/references/05-evidence-and-errors.md +2 -2
- package/scripts/build-bundle.mjs +2 -1
- package/scripts/sync-public-files.mjs +1 -1
- package/skills/release-assess/SKILL.md +17 -3
- package/skills/release-config/SKILL.md +6 -0
- package/skills/release-docs/SKILL.md +6 -0
- package/skills/release-help/SKILL.md +7 -2
- package/skills/release-marketplace/SKILL.md +6 -0
- package/skills/release-prepare/SKILL.md +8 -2
- package/skills/release-publish/SKILL.md +7 -1
- package/skills/release-reconcile/SKILL.md +6 -0
- package/skills/release-setup/SKILL.md +9 -1
- package/skills/release-verify/SKILL.md +6 -0
- package/skills-src/release-assess/SKILL.md +17 -3
- package/skills-src/release-config/SKILL.md +6 -0
- package/skills-src/release-docs/SKILL.md +6 -0
- package/skills-src/release-help/SKILL.md +7 -2
- package/skills-src/release-marketplace/SKILL.md +6 -0
- package/skills-src/release-prepare/SKILL.md +8 -2
- package/skills-src/release-publish/SKILL.md +7 -1
- package/skills-src/release-reconcile/SKILL.md +6 -0
- package/skills-src/release-setup/SKILL.md +9 -1
- package/skills-src/release-verify/SKILL.md +6 -0
- package/src/adapters/plugin-marketplace.mjs +7 -12
- package/src/artifacts/producer-registry.mjs +4 -3
- package/src/commands/assess.mjs +23 -3
- package/src/commands/post-release-local.mjs +2 -2
- package/src/commands/prepare.mjs +5 -30
- package/src/commands/professional-proof.mjs +506 -0
- package/src/commands/setup.mjs +16 -5
- package/src/commands/verify-records.mjs +14 -0
- package/src/core/baseline.mjs +9 -1
- package/src/core/derived-artifact-gates.mjs +38 -9
- package/src/core/redact.mjs +12 -5
- package/src/core/source-authority.mjs +28 -3
- package/src/npm/npm-entry-closure.mjs +71 -9
- package/src/producers/build-adapters.mjs +18 -5
- package/src/producers/foundation-resource-projection.mjs +3 -3
- package/src/producers/render-public-assets.mjs +2 -1
- package/src/producers/sync-skills.mjs +2 -1
- package/src/schemas/file-set-apply-request.schema.json +78 -0
- package/src/schemas/file-set-event-payload.schema.json +55 -0
- package/src/schemas/file-set-prune-request.schema.json +17 -0
- package/src/schemas/file-set-recovery-request.schema.json +29 -0
- package/src/schemas/file-set-result.schema.json +79 -0
- package/src/schemas/professional-conclusion.schema.json +132 -0
- package/src/schemas/project-manifest.schema.json +91 -0
- package/src/schemas/state-store-recovery-observation.schema.json +129 -0
- package/src/snapshot/frozen.mjs +19 -14
- package/src/core/run-retention.mjs +0 -227
|
@@ -1567,7 +1567,7 @@ set expectedTag $env(RELEASE_SKILL_KIMI_EXPECTED_TAG)
|
|
|
1567
1567
|
set promptPattern {(?:(?:^|\\r|\\n)> (?:\\r*\\n|$)|(?:^|\\r|\\n)(?:(?:\\033\\[[0-9;?]*[ -/]*[@-~])|\\033\\][^\\x07]*\\x07|[ \\t])*│[^\\r\\n]*>[^\\r\\n]*│(?:(?:\\033\\[[0-9;?]*[ -/]*[@-~])|\\033\\][^\\x07]*\\x07|[ \\t])*(?:\\r*\\n|$))}
|
|
1568
1568
|
|
|
1569
1569
|
proc cleanScreen {value} {
|
|
1570
|
-
regsub -all {\\033\\[[0-9
|
|
1570
|
+
regsub -all {\\033\\[[0-9;:<=>?]*[ -/]*[@-~]} $value {} value
|
|
1571
1571
|
regsub -all {\\033\\][^\\x07]*\\x07} $value {} value
|
|
1572
1572
|
regsub -all {\\r} $value {} value
|
|
1573
1573
|
return $value
|
|
@@ -1646,7 +1646,7 @@ proc readDirectoryTrustDialog {prefix state timeoutCode unknownCode eofCode} {
|
|
|
1646
1646
|
set framed [regexp -nocase {(^|\\n)[ \\t]*─{8,}[ \\t]*\\n[ \\t]*Trust this folder\\?} $cleanedPrefix]
|
|
1647
1647
|
if {$framed} {
|
|
1648
1648
|
expect {
|
|
1649
|
-
-re {(^|\\r|\\n)[
|
|
1649
|
+
-re {(^|\\r|\\n)[^\\r\\n]*─{8,}[^\\r\\n]*(\\r*\\n|$)} {
|
|
1650
1650
|
append dialogBuffer $expect_out(buffer)
|
|
1651
1651
|
}
|
|
1652
1652
|
timeout { failTimeout $state $timeoutCode $unknownCode }
|
package/src/commands/prepare.mjs
CHANGED
|
@@ -78,7 +78,6 @@ import { acquireProjectLock } from '../artifacts/project-lock.mjs';
|
|
|
78
78
|
import { observePreviousPublicBaseline } from '../core/previous-public-baseline.mjs';
|
|
79
79
|
import { verifyFrozenNpmTarballContract } from '../adapters/npm.mjs';
|
|
80
80
|
import { createProductionPrepareRunDir } from '../core/run.mjs';
|
|
81
|
-
import { cleanupRunRetention } from '../core/run-retention.mjs';
|
|
82
81
|
import {
|
|
83
82
|
PLATFORMS,
|
|
84
83
|
normalizeHostId,
|
|
@@ -279,7 +278,9 @@ import { boundedOutputTail } from '../core/bounded-output.mjs';
|
|
|
279
278
|
* already captures child stdout/stderr on non-zero exit; on failure this
|
|
280
279
|
* layer writes bounded tails into the hooks evidence event AND echoes them to
|
|
281
280
|
* the current process' stderr, so a failing hook is diagnosable on the
|
|
282
|
-
* terminal without opening evidence.jsonl.
|
|
281
|
+
* terminal without opening evidence.jsonl. Fresh success and non-zero exit
|
|
282
|
+
* events also retain the returned full streams through the evidence writer's
|
|
283
|
+
* redaction. Success events carry no tails; cache hits have no fresh streams.
|
|
283
284
|
* Exit-code semantics are unchanged.
|
|
284
285
|
*
|
|
285
286
|
* @param {object} config - The loaded project config.
|
|
@@ -413,6 +414,7 @@ export async function runDeclaredHooks(config, root, evidence, hookFn = runHook,
|
|
|
413
414
|
// streams instead of the noisy compiler prelude at the beginning.
|
|
414
415
|
stdoutTail,
|
|
415
416
|
stderrTail,
|
|
417
|
+
details: { stdout: result.stdout, stderr: result.stderr },
|
|
416
418
|
...selectionField,
|
|
417
419
|
});
|
|
418
420
|
throw new ReleaseError(
|
|
@@ -448,6 +450,7 @@ export async function runDeclaredHooks(config, root, evidence, hookFn = runHook,
|
|
|
448
450
|
status: 'completed',
|
|
449
451
|
hookName: name,
|
|
450
452
|
exitCode: 0,
|
|
453
|
+
details: { stdout: result.stdout, stderr: result.stderr },
|
|
451
454
|
...selectionField,
|
|
452
455
|
});
|
|
453
456
|
records.push({ name, completed: true, cached: false, testSelection: selectionField.testSelection });
|
|
@@ -3404,34 +3407,6 @@ export async function prepareRelease(options) {
|
|
|
3404
3407
|
const evidence = createEvidenceWriter({ runDir, command: 'prepare', clock });
|
|
3405
3408
|
|
|
3406
3409
|
try {
|
|
3407
|
-
// R14-01: production prepare performs one best-effort retention pass only
|
|
3408
|
-
// after the current run/evidence authority exists and before high-cost
|
|
3409
|
-
// config, snapshot, and hook work begins. It never blocks prepare.
|
|
3410
|
-
if (production) {
|
|
3411
|
-
try {
|
|
3412
|
-
const retention = await (options.runRetentionFn ?? cleanupRunRetention)({
|
|
3413
|
-
releaseDir,
|
|
3414
|
-
currentRunDir: runDir,
|
|
3415
|
-
});
|
|
3416
|
-
await evidence.append({
|
|
3417
|
-
phase: 'run-retention',
|
|
3418
|
-
status: 'completed',
|
|
3419
|
-
scanned: retention.scanned,
|
|
3420
|
-
deleted: retention.deleted,
|
|
3421
|
-
errors: retention.errors,
|
|
3422
|
-
});
|
|
3423
|
-
} catch (error) {
|
|
3424
|
-
await evidence.append({
|
|
3425
|
-
phase: 'run-retention',
|
|
3426
|
-
status: 'warning',
|
|
3427
|
-
scanned: 0,
|
|
3428
|
-
deleted: 0,
|
|
3429
|
-
errors: 1,
|
|
3430
|
-
error: { code: error?.code ?? 'RUN_RETENTION_FAILED', message: error?.message ?? String(error) },
|
|
3431
|
-
});
|
|
3432
|
-
}
|
|
3433
|
-
}
|
|
3434
|
-
|
|
3435
3410
|
// --- Step 1: Load and validate config ---
|
|
3436
3411
|
await evidence.append({ phase: 'config', status: 'started' });
|
|
3437
3412
|
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family projection and read of Foundation professional-conclusion files.
|
|
3
|
+
*
|
|
4
|
+
* Domain mapping stays here. Path containment, exclusive publication, and
|
|
5
|
+
* schema validation are Foundation 0.22.0 calls, not local copies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
9
|
+
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
10
|
+
|
|
11
|
+
import { findSchemaByObject, validateDocument } from 'skill-family-contracts';
|
|
12
|
+
import {
|
|
13
|
+
HARNESS_ERROR_KINDS,
|
|
14
|
+
publishFileExclusive,
|
|
15
|
+
readFileStrict,
|
|
16
|
+
resolveContained,
|
|
17
|
+
} from 'skill-family-harness-node';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
GATE_FAILED,
|
|
21
|
+
PATH_UNSAFE,
|
|
22
|
+
ReleaseError,
|
|
23
|
+
STRUCTURE_INVALID,
|
|
24
|
+
} from '../core/errors.mjs';
|
|
25
|
+
import { PKG_ROOT } from '../core/pkg-root.mjs';
|
|
26
|
+
|
|
27
|
+
export const RELEASE_PROVIDER_ID = 'release-skill';
|
|
28
|
+
|
|
29
|
+
export const PROVIDER_ENTRY = Object.freeze({
|
|
30
|
+
ADOPTION: 'setup --assess-adoption',
|
|
31
|
+
ASSESS: 'assess --offline',
|
|
32
|
+
RECORDS: 'verify-records',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const READ_EXIT_CODES = Object.freeze({
|
|
36
|
+
pass: 0,
|
|
37
|
+
not_pass: 1,
|
|
38
|
+
unavailable: 2,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const PASS_CODES = Object.freeze({
|
|
42
|
+
[PROVIDER_ENTRY.ADOPTION]: Object.freeze(['ADOPTED', 'ADOPTED_WITH_SUGGESTIONS']),
|
|
43
|
+
[PROVIDER_ENTRY.ASSESS]: Object.freeze(['ASSESSED']),
|
|
44
|
+
[PROVIDER_ENTRY.RECORDS]: Object.freeze(['CONSISTENT']),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const NOT_PASS_CODES = Object.freeze({
|
|
48
|
+
[PROVIDER_ENTRY.ADOPTION]: Object.freeze(['PARTIALLY_ADOPTED', 'NOT_CONFIGURED']),
|
|
49
|
+
[PROVIDER_ENTRY.ASSESS]: Object.freeze(['NEEDS_INPUT', 'BLOCKED']),
|
|
50
|
+
[PROVIDER_ENTRY.RECORDS]: Object.freeze(['CONTRADICTED', 'INSUFFICIENT']),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const INCOMPLETE_RECORD_CODES = Object.freeze([
|
|
54
|
+
'INPUT_MISSING',
|
|
55
|
+
'FORMAT_UNSUPPORTED',
|
|
56
|
+
'HISTORICAL_TIME_MISSING',
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
function providerVersion() {
|
|
60
|
+
// Bundled adapters ship no package.json. Reuse the build-time identity
|
|
61
|
+
// already injected as __bundlePkg; source mode still reads package.json.
|
|
62
|
+
if (typeof __bundlePkg !== 'undefined' && __bundlePkg && typeof __bundlePkg.version === 'string' && __bundlePkg.version.length > 0) {
|
|
63
|
+
return __bundlePkg.version;
|
|
64
|
+
}
|
|
65
|
+
const pkg = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8'));
|
|
66
|
+
if (typeof pkg.version !== 'string' || pkg.version.length === 0) {
|
|
67
|
+
throw new ReleaseError(STRUCTURE_INVALID, 'package.json version is required for professional-proof provider.version');
|
|
68
|
+
}
|
|
69
|
+
return pkg.version;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function professionalSchemaRegistration() {
|
|
73
|
+
return findSchemaByObject('professional-conclusion');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validateProfessionalConclusion(document) {
|
|
77
|
+
const registration = professionalSchemaRegistration();
|
|
78
|
+
if (!registration) {
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
errorCode: 'SFC1002',
|
|
82
|
+
errors: [{ message: 'professional-conclusion schema is not registered in the installed skill-family-contracts' }],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return validateDocument(document, {
|
|
86
|
+
schemaId: registration.$id,
|
|
87
|
+
dialect: registration.dialect,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function assertValidConclusion(document) {
|
|
92
|
+
const result = validateProfessionalConclusion(document);
|
|
93
|
+
if (!result.valid) {
|
|
94
|
+
throw new ReleaseError(
|
|
95
|
+
STRUCTURE_INVALID,
|
|
96
|
+
'professional conclusion failed Foundation schema validation',
|
|
97
|
+
{ errorCode: result.errorCode, errors: result.errors },
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function canonicalOutputPath(filePath) {
|
|
104
|
+
const absolute = resolve(filePath);
|
|
105
|
+
try {
|
|
106
|
+
return realpathSync(absolute);
|
|
107
|
+
} catch {
|
|
108
|
+
const parts = [basename(absolute)];
|
|
109
|
+
let cursor = dirname(absolute);
|
|
110
|
+
while (true) {
|
|
111
|
+
try {
|
|
112
|
+
return join(realpathSync(cursor), ...parts);
|
|
113
|
+
} catch {
|
|
114
|
+
const parent = dirname(cursor);
|
|
115
|
+
if (parent === cursor) return absolute;
|
|
116
|
+
parts.unshift(basename(cursor));
|
|
117
|
+
cursor = parent;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function assertConclusionOutputOption(conclusionOutput, { conflictPath } = {}) {
|
|
124
|
+
if (conclusionOutput == null || conclusionOutput === '') return;
|
|
125
|
+
if (typeof conclusionOutput !== 'string' || !isAbsolute(conclusionOutput)) {
|
|
126
|
+
throw new ReleaseError(PATH_UNSAFE, '--conclusion-output must be an absolute file path');
|
|
127
|
+
}
|
|
128
|
+
if (conflictPath != null && conflictPath !== '') {
|
|
129
|
+
if (canonicalOutputPath(conclusionOutput) === canonicalOutputPath(conflictPath)) {
|
|
130
|
+
throw new ReleaseError(
|
|
131
|
+
PATH_UNSAFE,
|
|
132
|
+
'assess --output and --conclusion-output must not resolve to the same path',
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function nativeDetails(nativeResult) {
|
|
139
|
+
if (!nativeResult || typeof nativeResult !== 'object' || Array.isArray(nativeResult)) {
|
|
140
|
+
return { native: nativeResult ?? null };
|
|
141
|
+
}
|
|
142
|
+
const { conclusionPath: _conclusionPath, ...rest } = nativeResult;
|
|
143
|
+
return rest;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function nativeSummary(nativeResult, code) {
|
|
147
|
+
if (typeof nativeResult?.summary === 'string' && nativeResult.summary.trim().length > 0) {
|
|
148
|
+
return nativeResult.summary;
|
|
149
|
+
}
|
|
150
|
+
if (code === 'CONSISTENT') {
|
|
151
|
+
return '历史记录一致;不表示发布已经成功。';
|
|
152
|
+
}
|
|
153
|
+
if (code === 'INSUFFICIENT') {
|
|
154
|
+
return '提供的历史记录不足以完成一致性判断。';
|
|
155
|
+
}
|
|
156
|
+
if (code === 'CONTRADICTED') {
|
|
157
|
+
return '提供的历史记录互相矛盾。';
|
|
158
|
+
}
|
|
159
|
+
if (typeof code === 'string' && code.length > 0) {
|
|
160
|
+
return `release-skill 原生状态 ${code}`;
|
|
161
|
+
}
|
|
162
|
+
return 'release-skill 专业检查已返回领域结果。';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function adoptionScope(nativeResult) {
|
|
166
|
+
const limitations = [];
|
|
167
|
+
for (const item of nativeResult?.unobserved ?? []) {
|
|
168
|
+
if (typeof item?.note === 'string' && item.note.length > 0) limitations.push(item.note);
|
|
169
|
+
else if (typeof item?.field === 'string' && item.field.length > 0) limitations.push(item.field);
|
|
170
|
+
}
|
|
171
|
+
const configLoadFailed = nativeResult?.status === 'PARTIALLY_ADOPTED'
|
|
172
|
+
&& nativeResult?.configDigest == null;
|
|
173
|
+
if (configLoadFailed) {
|
|
174
|
+
limitations.push('subsequent local adoption checks were not run because configuration is invalid');
|
|
175
|
+
return {
|
|
176
|
+
checked: ['setup --assess-adoption:config'],
|
|
177
|
+
limitations,
|
|
178
|
+
completion: 'partial',
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
checked: ['setup --assess-adoption'],
|
|
183
|
+
limitations,
|
|
184
|
+
completion: 'complete',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function assessScope(nativeResult) {
|
|
189
|
+
const hasConfig = Boolean(nativeResult?.configDigest) || nativeResult?.status === 'ASSESSED';
|
|
190
|
+
const configOnly = !hasConfig && nativeResult?.status === 'NEEDS_INPUT';
|
|
191
|
+
if (configOnly) {
|
|
192
|
+
return {
|
|
193
|
+
checked: ['config'],
|
|
194
|
+
limitations: ['topology and remaining local checks were not run because configuration is invalid or missing'],
|
|
195
|
+
completion: 'partial',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const limitations = [];
|
|
199
|
+
if (nativeResult?.offline !== false) {
|
|
200
|
+
limitations.push('remote-prerequisites skipped (--offline)');
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
checked: [
|
|
204
|
+
'config',
|
|
205
|
+
'topology',
|
|
206
|
+
'common-docs',
|
|
207
|
+
'plugin-manifests',
|
|
208
|
+
'package-metadata',
|
|
209
|
+
'readme-structure',
|
|
210
|
+
],
|
|
211
|
+
limitations,
|
|
212
|
+
completion: 'complete',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function recordInputPresent(digest) {
|
|
217
|
+
return typeof digest?.bytesSha256 === 'string' && digest.bytesSha256.length > 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function recordsScope(nativeResult) {
|
|
221
|
+
const findings = nativeResult?.findings ?? [];
|
|
222
|
+
const limitations = [];
|
|
223
|
+
for (const finding of findings) {
|
|
224
|
+
if (INCOMPLETE_RECORD_CODES.includes(finding?.code) && typeof finding.message === 'string' && finding.message.length > 0) {
|
|
225
|
+
limitations.push(finding.message);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const digests = nativeResult?.digests ?? {};
|
|
229
|
+
const checked = [];
|
|
230
|
+
if (recordInputPresent(digests.plan)) checked.push('explicit-plan');
|
|
231
|
+
if (recordInputPresent(digests.approval)) checked.push('explicit-approval');
|
|
232
|
+
if (recordInputPresent(digests.targetRun)) checked.push('explicit-target-run');
|
|
233
|
+
const sourceDigests = Array.isArray(digests.sourceRuns) ? digests.sourceRuns : [];
|
|
234
|
+
const sourcePresent = sourceDigests.some((digest) => recordInputPresent(digest));
|
|
235
|
+
if (sourcePresent || (checked.length > 0 && limitations.length === 0)) {
|
|
236
|
+
checked.push('explicit-source-runs');
|
|
237
|
+
}
|
|
238
|
+
if (checked.length === 0) {
|
|
239
|
+
if (limitations.length === 0) {
|
|
240
|
+
limitations.push('required historical records were not supplied');
|
|
241
|
+
}
|
|
242
|
+
return { checked, limitations, completion: 'not-performed' };
|
|
243
|
+
}
|
|
244
|
+
if (limitations.length > 0) {
|
|
245
|
+
return { checked, limitations, completion: 'partial' };
|
|
246
|
+
}
|
|
247
|
+
return { checked, limitations, completion: 'complete' };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function scopeFor(entry, nativeResult) {
|
|
251
|
+
if (entry === PROVIDER_ENTRY.ADOPTION) return adoptionScope(nativeResult);
|
|
252
|
+
if (entry === PROVIDER_ENTRY.ASSESS) return assessScope(nativeResult);
|
|
253
|
+
if (entry === PROVIDER_ENTRY.RECORDS) return recordsScope(nativeResult);
|
|
254
|
+
return {
|
|
255
|
+
checked: [],
|
|
256
|
+
limitations: ['unknown family entry'],
|
|
257
|
+
completion: 'not-performed',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function projectReleaseConclusion({
|
|
262
|
+
entry,
|
|
263
|
+
nativeResult,
|
|
264
|
+
subjectRef,
|
|
265
|
+
subjectRevision,
|
|
266
|
+
} = {}) {
|
|
267
|
+
const code = typeof nativeResult?.status === 'string' && nativeResult.status.length > 0
|
|
268
|
+
? nativeResult.status
|
|
269
|
+
: 'UNKNOWN';
|
|
270
|
+
const scope = scopeFor(entry, nativeResult);
|
|
271
|
+
const subject = { ref: typeof subjectRef === 'string' && subjectRef.length > 0 ? subjectRef : 'unspecified-subject' };
|
|
272
|
+
if (typeof subjectRevision === 'string' && subjectRevision.length > 0) {
|
|
273
|
+
subject.revision = subjectRevision;
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
schemaVersion: 1,
|
|
277
|
+
kind: 'skill-family.professional-conclusion',
|
|
278
|
+
provider: {
|
|
279
|
+
id: RELEASE_PROVIDER_ID,
|
|
280
|
+
version: providerVersion(),
|
|
281
|
+
entry,
|
|
282
|
+
},
|
|
283
|
+
subject,
|
|
284
|
+
scope: {
|
|
285
|
+
checked: scope.checked,
|
|
286
|
+
limitations: scope.limitations,
|
|
287
|
+
},
|
|
288
|
+
outcome: {
|
|
289
|
+
completion: scope.completion,
|
|
290
|
+
code,
|
|
291
|
+
summary: nativeSummary(nativeResult, code),
|
|
292
|
+
},
|
|
293
|
+
details: nativeDetails(nativeResult),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function interpretReleaseConclusion(document) {
|
|
298
|
+
const provider = document?.provider && typeof document.provider === 'object'
|
|
299
|
+
? {
|
|
300
|
+
id: document.provider.id,
|
|
301
|
+
version: document.provider.version,
|
|
302
|
+
entry: document.provider.entry,
|
|
303
|
+
}
|
|
304
|
+
: null;
|
|
305
|
+
const entry = provider?.entry;
|
|
306
|
+
const code = document?.outcome?.code;
|
|
307
|
+
const completion = document?.outcome?.completion;
|
|
308
|
+
const passCodes = typeof entry === 'string' && Object.hasOwn(PASS_CODES, entry)
|
|
309
|
+
? PASS_CODES[entry]
|
|
310
|
+
: undefined;
|
|
311
|
+
const notPassCodes = typeof entry === 'string' && Object.hasOwn(NOT_PASS_CODES, entry)
|
|
312
|
+
? NOT_PASS_CODES[entry]
|
|
313
|
+
: undefined;
|
|
314
|
+
|
|
315
|
+
if (provider?.id && provider.id !== RELEASE_PROVIDER_ID) {
|
|
316
|
+
return {
|
|
317
|
+
status: 'unavailable',
|
|
318
|
+
reason: '证明不属于 release-skill',
|
|
319
|
+
provider,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (!entry || !passCodes || !notPassCodes) {
|
|
323
|
+
return {
|
|
324
|
+
status: 'unavailable',
|
|
325
|
+
reason: '未知入口,无法解释本族领域码',
|
|
326
|
+
provider,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (typeof code !== 'string' || code.length === 0) {
|
|
330
|
+
return {
|
|
331
|
+
status: 'unavailable',
|
|
332
|
+
reason: '证明缺少原生领域码',
|
|
333
|
+
provider,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (passCodes.includes(code)) {
|
|
337
|
+
if (completion !== 'complete') {
|
|
338
|
+
return {
|
|
339
|
+
status: 'not_pass',
|
|
340
|
+
reason: '声明范围尚未完成,不能把原生通过码当成专业通过',
|
|
341
|
+
provider,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (entry === PROVIDER_ENTRY.RECORDS) {
|
|
345
|
+
return {
|
|
346
|
+
status: 'pass',
|
|
347
|
+
reason: '历史记录一致;不表示发布已经成功。historicalTerminalStatus 保持原值。',
|
|
348
|
+
provider,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
if (entry === PROVIDER_ENTRY.ASSESS) {
|
|
352
|
+
return {
|
|
353
|
+
status: 'pass',
|
|
354
|
+
reason: '静态准备度检查完成;不表示已经发布。',
|
|
355
|
+
provider,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
status: 'pass',
|
|
360
|
+
reason: code === 'ADOPTED_WITH_SUGGESTIONS'
|
|
361
|
+
? '已接入,建议保留在原证明中。'
|
|
362
|
+
: '接入检查通过。',
|
|
363
|
+
provider,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
if (notPassCodes.includes(code)) {
|
|
367
|
+
if (entry === PROVIDER_ENTRY.RECORDS) {
|
|
368
|
+
return {
|
|
369
|
+
status: 'not_pass',
|
|
370
|
+
reason: code === 'INSUFFICIENT'
|
|
371
|
+
? '历史记录不足,不能据此声称发布成功。'
|
|
372
|
+
: '历史记录互相矛盾。',
|
|
373
|
+
provider,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
status: 'not_pass',
|
|
378
|
+
reason: `原生状态 ${code} 表示证明记载的结果未通过。`,
|
|
379
|
+
provider,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
status: 'unavailable',
|
|
384
|
+
reason: `未知同版本领域码 ${code}`,
|
|
385
|
+
provider,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function publishExclusiveAbsolute(absolutePath, bytes) {
|
|
390
|
+
const root = dirname(absolutePath);
|
|
391
|
+
const relPath = basename(absolutePath);
|
|
392
|
+
if (!relPath || relPath === '.' || relPath === '..') {
|
|
393
|
+
throw new ReleaseError(PATH_UNSAFE, '--conclusion-output must name a file');
|
|
394
|
+
}
|
|
395
|
+
await resolveContained(root, relPath);
|
|
396
|
+
try {
|
|
397
|
+
return await publishFileExclusive(root, relPath, bytes);
|
|
398
|
+
} catch (cause) {
|
|
399
|
+
const kind = cause?.details?.kind;
|
|
400
|
+
if (kind === HARNESS_ERROR_KINDS.EXCLUSIVE_PUBLISH_CONFLICT) {
|
|
401
|
+
throw new ReleaseError(
|
|
402
|
+
GATE_FAILED,
|
|
403
|
+
'professional conclusion already exists; exclusive create refused to overwrite',
|
|
404
|
+
{ kind },
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
throw new ReleaseError(
|
|
408
|
+
GATE_FAILED,
|
|
409
|
+
`professional conclusion exclusive write failed: ${cause?.message ?? 'unknown'}`,
|
|
410
|
+
{ kind },
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export async function attachProfessionalConclusion(nativeResult, {
|
|
416
|
+
entry,
|
|
417
|
+
subjectRef,
|
|
418
|
+
subjectRevision,
|
|
419
|
+
conclusionOutput,
|
|
420
|
+
conflictPath,
|
|
421
|
+
} = {}) {
|
|
422
|
+
if (conclusionOutput == null || conclusionOutput === '') {
|
|
423
|
+
return nativeResult;
|
|
424
|
+
}
|
|
425
|
+
assertConclusionOutputOption(conclusionOutput, { conflictPath });
|
|
426
|
+
const conclusion = projectReleaseConclusion({
|
|
427
|
+
entry,
|
|
428
|
+
nativeResult,
|
|
429
|
+
subjectRef,
|
|
430
|
+
subjectRevision,
|
|
431
|
+
});
|
|
432
|
+
assertValidConclusion(conclusion);
|
|
433
|
+
const published = await publishExclusiveAbsolute(
|
|
434
|
+
resolve(conclusionOutput),
|
|
435
|
+
`${JSON.stringify(conclusion, null, 2)}\n`,
|
|
436
|
+
);
|
|
437
|
+
return { ...nativeResult, conclusionPath: published.path };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function unavailableRead(reason, extra = {}) {
|
|
441
|
+
return {
|
|
442
|
+
status: 'unavailable',
|
|
443
|
+
reason,
|
|
444
|
+
provider: extra.provider ?? null,
|
|
445
|
+
conclusion: extra.conclusion ?? null,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function extractProvider(document) {
|
|
450
|
+
const provider = document?.provider;
|
|
451
|
+
if (!provider || typeof provider !== 'object' || Array.isArray(provider)) return null;
|
|
452
|
+
const extracted = {};
|
|
453
|
+
if (typeof provider.id === 'string') extracted.id = provider.id;
|
|
454
|
+
if (typeof provider.version === 'string') extracted.version = provider.version;
|
|
455
|
+
if (typeof provider.entry === 'string') extracted.entry = provider.entry;
|
|
456
|
+
return Object.keys(extracted).length > 0 ? extracted : null;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export async function readProfessionalProof({
|
|
460
|
+
proofRoot,
|
|
461
|
+
proofPath,
|
|
462
|
+
expectedProviderId = RELEASE_PROVIDER_ID,
|
|
463
|
+
} = {}) {
|
|
464
|
+
if (typeof proofRoot !== 'string' || proofRoot.length === 0 || typeof proofPath !== 'string' || proofPath.length === 0) {
|
|
465
|
+
return unavailableRead('proofRoot 与 proof 均为必填');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
let receipt;
|
|
469
|
+
try {
|
|
470
|
+
await resolveContained(proofRoot, proofPath);
|
|
471
|
+
receipt = await readFileStrict(proofRoot, proofPath, { encoding: 'utf8' });
|
|
472
|
+
} catch (cause) {
|
|
473
|
+
return unavailableRead(`无法读取证明: ${cause?.message ?? 'unknown'}`);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
let document;
|
|
477
|
+
try {
|
|
478
|
+
document = JSON.parse(receipt.content);
|
|
479
|
+
} catch {
|
|
480
|
+
return unavailableRead('证明不是合法 JSON');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const schema = validateProfessionalConclusion(document);
|
|
484
|
+
const provider = extractProvider(document);
|
|
485
|
+
if (!schema.valid) {
|
|
486
|
+
if (provider?.id && provider.id !== expectedProviderId) {
|
|
487
|
+
return unavailableRead('证明不属于 release-skill', { provider, conclusion: document });
|
|
488
|
+
}
|
|
489
|
+
return unavailableRead(
|
|
490
|
+
`证明未通过 professional-conclusion 结构校验(${schema.errorCode ?? 'invalid'})`,
|
|
491
|
+
{ provider },
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (provider?.id !== expectedProviderId) {
|
|
496
|
+
return unavailableRead('证明不属于 release-skill', { provider, conclusion: document });
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const mapped = interpretReleaseConclusion(document);
|
|
500
|
+
return {
|
|
501
|
+
status: mapped.status,
|
|
502
|
+
reason: mapped.reason,
|
|
503
|
+
provider: mapped.provider,
|
|
504
|
+
conclusion: document,
|
|
505
|
+
};
|
|
506
|
+
}
|
package/src/commands/setup.mjs
CHANGED
|
@@ -57,6 +57,11 @@ import {
|
|
|
57
57
|
checkPluginManifests,
|
|
58
58
|
checkReadmeStructure,
|
|
59
59
|
} from './assess.mjs';
|
|
60
|
+
import {
|
|
61
|
+
PROVIDER_ENTRY,
|
|
62
|
+
assertConclusionOutputOption,
|
|
63
|
+
attachProfessionalConclusion,
|
|
64
|
+
} from './professional-proof.mjs';
|
|
60
65
|
|
|
61
66
|
const execFile = promisify(execFileCb);
|
|
62
67
|
const SKIP_DIRS = new Set([
|
|
@@ -2855,13 +2860,19 @@ async function reportConfigLoadError(root, configPath, error) {
|
|
|
2855
2860
|
* @param {string} options.root - Absolute project root.
|
|
2856
2861
|
* @returns {Promise<Object>} Adoption report.
|
|
2857
2862
|
*/
|
|
2858
|
-
export async function assessAdoption({ root } = {}) {
|
|
2863
|
+
export async function assessAdoption({ root, conclusionOutput } = {}) {
|
|
2859
2864
|
if (!root || typeof root !== 'string' || !isAbsolute(root)) {
|
|
2860
2865
|
throw setupError(CONFIG_INVALID, 'assessment root must be an absolute path');
|
|
2861
2866
|
}
|
|
2867
|
+
assertConclusionOutputOption(conclusionOutput);
|
|
2862
2868
|
const rootReal = await realpath(root).catch((error) => {
|
|
2863
2869
|
throw setupError(CONFIG_INVALID, `cannot resolve assessment root: ${error.message}`);
|
|
2864
2870
|
});
|
|
2871
|
+
const finish = (report) => attachProfessionalConclusion(report, {
|
|
2872
|
+
entry: PROVIDER_ENTRY.ADOPTION,
|
|
2873
|
+
subjectRef: rootReal,
|
|
2874
|
+
conclusionOutput,
|
|
2875
|
+
});
|
|
2865
2876
|
const configPath = join(rootReal, '.release-skill', 'project.yaml');
|
|
2866
2877
|
|
|
2867
2878
|
let configExists = false;
|
|
@@ -2872,7 +2883,7 @@ export async function assessAdoption({ root } = {}) {
|
|
|
2872
2883
|
if (error.code !== 'ENOENT') throw error;
|
|
2873
2884
|
}
|
|
2874
2885
|
if (!configExists) {
|
|
2875
|
-
return reportNotConfigured(rootReal, configPath);
|
|
2886
|
+
return finish(await reportNotConfigured(rootReal, configPath));
|
|
2876
2887
|
}
|
|
2877
2888
|
|
|
2878
2889
|
let loaded;
|
|
@@ -2880,7 +2891,7 @@ export async function assessAdoption({ root } = {}) {
|
|
|
2880
2891
|
loaded = await loadProjectConfig({ root: rootReal });
|
|
2881
2892
|
} catch (error) {
|
|
2882
2893
|
if (error.code !== CONFIG_INVALID) throw error;
|
|
2883
|
-
return reportConfigLoadError(rootReal, configPath, error);
|
|
2894
|
+
return finish(await reportConfigLoadError(rootReal, configPath, error));
|
|
2884
2895
|
}
|
|
2885
2896
|
const { config, configDigest } = loaded;
|
|
2886
2897
|
|
|
@@ -3227,7 +3238,7 @@ export async function assessAdoption({ root } = {}) {
|
|
|
3227
3238
|
? '修复全部必选缺口后重新运行 release-skill setup --assess-adoption。'
|
|
3228
3239
|
: null;
|
|
3229
3240
|
|
|
3230
|
-
return buildAssessmentReport({
|
|
3241
|
+
return finish(buildAssessmentReport({
|
|
3231
3242
|
config,
|
|
3232
3243
|
configPath,
|
|
3233
3244
|
configDigest,
|
|
@@ -3237,5 +3248,5 @@ export async function assessAdoption({ root } = {}) {
|
|
|
3237
3248
|
gateSuggestions,
|
|
3238
3249
|
gateDiagnostics,
|
|
3239
3250
|
next,
|
|
3240
|
-
});
|
|
3251
|
+
}));
|
|
3241
3252
|
}
|
|
@@ -465,3 +465,17 @@ export function verifyReleaseRecords(request = {}) {
|
|
|
465
465
|
findings: findings.map(({ _outcome, ...finding }) => finding),
|
|
466
466
|
};
|
|
467
467
|
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Optional exclusive professional-conclusion output. The domain verifier stays
|
|
471
|
+
* pure; this hook loads the family projector only when a path is supplied.
|
|
472
|
+
*/
|
|
473
|
+
export async function attachVerifyRecordsConclusion(result, conclusionOutput, subjectRef) {
|
|
474
|
+
if (conclusionOutput == null || conclusionOutput === '') return result;
|
|
475
|
+
const { attachProfessionalConclusion, PROVIDER_ENTRY } = await import('./professional-proof.mjs');
|
|
476
|
+
return attachProfessionalConclusion(result, {
|
|
477
|
+
entry: PROVIDER_ENTRY.RECORDS,
|
|
478
|
+
subjectRef,
|
|
479
|
+
conclusionOutput,
|
|
480
|
+
});
|
|
481
|
+
}
|
package/src/core/baseline.mjs
CHANGED
|
@@ -292,7 +292,15 @@ async function computeWorkspaceDigest(root) {
|
|
|
292
292
|
* @returns {Promise<{ gitHead: string, gitTreeHash: string, workspaceDigest: string, statusEntries: string[], capturedAt: string }>}
|
|
293
293
|
*/
|
|
294
294
|
export async function captureBaseline(root) {
|
|
295
|
-
|
|
295
|
+
// Same 64 MiB bound as computeWorkspaceDigest: `git status --porcelain=v1
|
|
296
|
+
// -z --untracked-files=all` lists every untracked file and exceeds Node's
|
|
297
|
+
// default 1 MiB execFile maxBuffer once that listing grows past ~1 MiB.
|
|
298
|
+
const opts = {
|
|
299
|
+
cwd: root,
|
|
300
|
+
shell: false,
|
|
301
|
+
encoding: 'utf8',
|
|
302
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
303
|
+
};
|
|
296
304
|
|
|
297
305
|
const [{ stdout: headOut }, { stdout: treeOut }, { stdout: statusOut }] =
|
|
298
306
|
await Promise.all([
|