skill-family-engineering-kit 0.7.0 → 0.8.0

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/package.json CHANGED
@@ -8,8 +8,8 @@
8
8
  "url": "https://github.com/ifoohoo/skill-family-engineering-kit/issues"
9
9
  },
10
10
  "dependencies": {
11
- "skill-family-contracts": "0.7.0",
12
- "skill-family-harness-node": "0.7.0"
11
+ "skill-family-contracts": "0.8.0",
12
+ "skill-family-harness-node": "0.8.0"
13
13
  },
14
14
  "description": "Build-time scaffold, adoption planning, projection, and checks.",
15
15
  "engines": {
@@ -48,7 +48,7 @@
48
48
  "url": "https://github.com/ifoohoo/skill-family-engineering-kit.git"
49
49
  },
50
50
  "type": "module",
51
- "version": "0.7.0",
51
+ "version": "0.8.0",
52
52
  "scripts": {
53
53
  "check": "node --test",
54
54
  "test": "node --test"
@@ -1,19 +1,28 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "kind": "skill-family.profile-extension-spi",
4
- "spiVersion": 2,
4
+ "spiVersion": 3,
5
5
  "status": "stable",
6
- "description": "Stable extension SPI for Skill Family profiles. A profile may only DECLARE its finite differences from the common core; it can never rewrite, patch, or inject core behavior. Enforcement is mechanical: a frozen pipeline of checks with stable result codes. SPI v2 adds two optional descriptor sections with machine verification: the Foundation adoption declaration (lightweight adoption proof replacing the deprecated adoption-lock concept, remediation handoff D-8) and self-tightening override declarations (remediation handoff D-6).",
7
- "baseContracts": "1.1.1",
6
+ "description": "Stable extension SPI for Skill Family profiles and project adoption declarations. A profile may only DECLARE its finite differences from the common core; a project profile declares its adopting workspace. Enforcement is mechanical with stable result codes.",
7
+ "baseContracts": "1.7.0",
8
8
  "descriptor": {
9
9
  "schemaId": "https://contracts.skill-family.example/v1/profile-descriptor.json",
10
10
  "object": "profile-descriptor",
11
11
  "dialect": "2020-12",
12
12
  "policy": "strict",
13
13
  "file": "profile.json",
14
- "note": "The descriptor is the only intake surface. It is plain validated data (additionalProperties: false at every level), so a profile cannot smuggle behavior in through the descriptor itself. Since SPI v2 the validator composes the two optional top-level sections `adoption` and `overrides` (defined by profiles/spi/profile-adoption.schema.json) onto the contracts profile-descriptor schema before validating; the contracts package stays untouched.",
14
+ "note": "The descriptor is the provider intake surface. It is plain validated data (additionalProperties: false at every level), so a profile cannot smuggle behavior in through the descriptor itself. SPI v3 also defines the separate project-profile intake; provider validation composes the optional top-level sections `adoption` and `overrides` onto the contracts profile-descriptor schema before validating; the contracts package stays untouched.",
15
15
  "extendedSchemaId": "https://spi.profiles.skill-family.example/v2/profile-descriptor-extended.json"
16
16
  },
17
+ "projectProfile": {
18
+ "schemaId": "https://contracts.skill-family.example/v1/project-profile.json",
19
+ "object": "project-profile",
20
+ "dialect": "2020-12",
21
+ "policy": "strict",
22
+ "file": "profile.json",
23
+ "note": "The project profile is the scaffolded workspace adoption declaration and is never accepted by verifyProfile.",
24
+ "errorCode": "SPE1008"
25
+ },
17
26
  "principles": [
18
27
  "declaration-only: differences are data declarations interpreted by the core's own fixed logic; a profile never supplies behavior that the core executes as core logic",
19
28
  "closed-core: no difference may add, override, or remove anything owned by the common core (contracts objects, registered protocols, kernel operations, core packages, kit commands)",
@@ -34,16 +43,21 @@
34
43
  "adoption-pin-digests",
35
44
  "overrides-policy"
36
45
  ],
46
+ "projectProfilePipeline": [
47
+ "project-profile-schema",
48
+ "adoption-pin-digests",
49
+ "overrides-policy"
50
+ ],
37
51
  "resultCodes": [
38
52
  {
39
53
  "code": "SPE0000",
40
54
  "name": "OK",
41
- "meaning": "The profile descriptor and all declared SPI resources satisfy every SPI rule; the profile is admitted as a declaration-only extension."
55
+ "meaning": "The provider descriptor or project profile and all applicable declarations satisfy every SPI rule; the input is admitted as a declaration-only extension or project adoption declaration."
42
56
  },
43
57
  {
44
58
  "code": "SPE1001",
45
59
  "name": "DESCRIPTOR_INVALID",
46
- "meaning": "The descriptor failed the profile-descriptor schema (since SPI v2 the SPI-composed extended schema that admits the optional adoption and overrides sections) under the declared dialect and strict policy, or could not be parsed as JSON. Adoption field completeness and pin format are schema-checked here. The observed contracts code (for example SFC1001) is carried in details.observedCode."
60
+ "meaning": "The descriptor failed the SPI-composed extended profile-descriptor schema under the declared dialect and strict policy, or could not be parsed as JSON. Adoption field completeness and pin format are schema-checked here. The observed contracts code (for example SFC1001) is carried in details.observedCode."
47
61
  },
48
62
  {
49
63
  "code": "SPE1002",
@@ -74,6 +88,11 @@
74
88
  "code": "SPE1007",
75
89
  "name": "OVERRIDE_POLICY_VIOLATION",
76
90
  "meaning": "An overrides entry violates the tightening-only policy: the rule id does not exist in the frozen rule baseline catalog, the parameter is not registered for that rule, the same rule/parameter pair is declared twice, or the numeric value does not strictly tighten the catalog baseline in the parameter's declared direction (relaxation and equality are both refused)."
91
+ },
92
+ {
93
+ "code": "SPE1008",
94
+ "name": "PROJECT_PROFILE_INVALID",
95
+ "meaning": "The project-level profile cannot be read, parsed, or validated against the project-profile contract. Pin and override failures retain SPE1006 and SPE1007."
77
96
  }
78
97
  ],
79
98
  "extensionPoints": [
@@ -24,7 +24,7 @@ const SPI_DEFINITION = JSON.parse(
24
24
  await readFile(new URL("./extension-spi.json", import.meta.url), "utf8"),
25
25
  );
26
26
 
27
- // SPI v2 type definitions for the optional `adoption` and `overrides`
27
+ // SPI v3 type definitions for the optional `adoption` and `overrides`
28
28
  // descriptor sections (remediation handoff D-8/D-6). The contracts package
29
29
  // stays untouched: the loader composes these $defs onto the contracts
30
30
  // profile-descriptor schema at load time and validates descriptors against
@@ -47,11 +47,24 @@ const BASE_DESCRIPTOR_SCHEMA = JSON.parse(
47
47
  ),
48
48
  );
49
49
 
50
+ const PROJECT_PROFILE_SCHEMA = JSON.parse(
51
+ await readFile(
52
+ new URL("./project-profile.schema.json", import.meta.url),
53
+ "utf8",
54
+ ),
55
+ );
56
+
57
+ export const PROJECT_PROFILE_SCHEMA_ID = PROJECT_PROFILE_SCHEMA.$id;
58
+
59
+ export function loadProjectProfileSchema() {
60
+ return structuredClone(PROJECT_PROFILE_SCHEMA);
61
+ }
62
+
50
63
  /** $id of the SPI-composed descriptor schema (distinct from the contracts $id on purpose). */
51
64
  export const EXTENDED_DESCRIPTOR_SCHEMA_ID = SPI_DEFINITION.descriptor.extendedSchemaId;
52
65
 
53
66
  /**
54
- * Composes the SPI v2 extended descriptor schema: the frozen contracts
67
+ * Composes the SPI v3 extended descriptor schema: the frozen contracts
55
68
  * profile-descriptor schema plus the optional top-level `adoption` and
56
69
  * `overrides` sections defined by profiles/spi/profile-adoption.schema.json.
57
70
  * Returns a fresh document on every call; callers never mutate it.
@@ -70,7 +83,7 @@ export function loadRuleBaselineCatalog() {
70
83
  return RULE_BASELINE_CATALOG;
71
84
  }
72
85
 
73
- /** Frozen SPI result codes: SPE0000 plus SPE1001..SPE1007 (SPI v2). */
86
+ /** Frozen SPI result codes: SPE0000 plus SPE1001..SPE1008. */
74
87
  export const SPI_RESULT_CODES = Object.freeze(
75
88
  Object.fromEntries(SPI_DEFINITION.resultCodes.map((entry) => [entry.code, entry.name])),
76
89
  );
@@ -283,7 +296,7 @@ export function assessOverridesPolicy(overrides) {
283
296
  * failing step decides the stable result code:
284
297
  *
285
298
  * SPE1001 descriptor invalid (schema/parse; carries observedCode). Since
286
- * SPI v2 the descriptor is validated against the SPI-composed
299
+ * SPI v3 the descriptor is validated against the SPI-composed
287
300
  * extended schema that admits the optional `adoption` and
288
301
  * `overrides` sections; adoption field completeness and pin
289
302
  * format are schema-checked here.
@@ -330,8 +343,8 @@ export async function verifyProfile({ profileRoot, descriptorRelPath } = {}) {
330
343
  }
331
344
  const validation = validateDocument(descriptor, {
332
345
  schema: loadExtendedDescriptorSchema(),
333
- dialect: SPI_DEFINITION.descriptor.dialect,
334
- policy: SPI_DEFINITION.descriptor.policy,
346
+ dialect: SPI_DEFINITION.projectProfile.dialect,
347
+ policy: SPI_DEFINITION.projectProfile.policy,
335
348
  });
336
349
  if (!validation.valid) {
337
350
  return result("SPE1001", {
@@ -459,7 +472,7 @@ export async function verifyProfile({ profileRoot, descriptorRelPath } = {}) {
459
472
  }
460
473
  const keys = Object.keys(exported).sort();
461
474
  const allowed = [...DECLARATION_SHAPE.allowedKeySet].sort();
462
- if (keys.join("") !== allowed.join("")) {
475
+ if (keys.join("\0") !== allowed.join("\0")) {
463
476
  return result("SPE1002", {
464
477
  profileId,
465
478
  reason: "declaration-shape-keys",
@@ -568,6 +581,71 @@ export async function verifyProfile({ profileRoot, descriptorRelPath } = {}) {
568
581
  });
569
582
  }
570
583
 
584
+ /**
585
+ * Verifies the project-level adoption declaration emitted by scaffold.
586
+ * This intake is deliberately separate from verifyProfile: a project profile
587
+ * declares the adopting workspace, while a descriptor declares a provider.
588
+ */
589
+ export async function verifyProjectProfile({ projectRoot, profileRelPath } = {}) {
590
+ if (typeof projectRoot !== "string" || projectRoot.length === 0) {
591
+ throw new TypeError("verifyProjectProfile: projectRoot must be a non-empty path string");
592
+ }
593
+ const relPath = profileRelPath ?? "profile.json";
594
+ const projectSteps = [];
595
+ let raw;
596
+ try {
597
+ raw = await readFileContained(projectRoot, relPath, { encoding: "utf8" });
598
+ } catch (cause) {
599
+ return result("SPE1008", { reason: "project-profile-unreadable", details: { kind: cause?.details?.kind ?? "unknown", profile: relPath }, steps: projectSteps });
600
+ }
601
+ let document;
602
+ try {
603
+ document = JSON.parse(raw);
604
+ } catch {
605
+ return result("SPE1008", { reason: "project-profile-parse-failed", details: { profile: relPath }, steps: projectSteps });
606
+ }
607
+ const validation = validateDocument(document, {
608
+ schema: loadProjectProfileSchema(),
609
+ dialect: SPI_DEFINITION.descriptor.dialect,
610
+ policy: SPI_DEFINITION.descriptor.policy,
611
+ });
612
+ if (!validation.valid) {
613
+ return result("SPE1008", {
614
+ reason: "project-profile-schema-invalid",
615
+ details: { observedCode: validation.errorCode, errors: validation.errors.slice(0, 5) },
616
+ steps: projectSteps,
617
+ });
618
+ }
619
+ projectSteps.push("project-profile-schema");
620
+ const projectProfile = validation.data;
621
+ let digestReport;
622
+ try {
623
+ digestReport = await verifyAdoptionDigests({ profileRoot: projectRoot, adoption: projectProfile.adoption });
624
+ } catch (cause) {
625
+ return result("SPE1008", { reason: "project-profile-adoption-invalid", details: { message: cause?.message ?? String(cause) }, steps: projectSteps });
626
+ }
627
+ if (!digestReport.ok) {
628
+ const failed = digestReport.results.find((entry) => entry.status !== "verified");
629
+ return result("SPE1006", {
630
+ reason: failed.status === "artifact-unreadable" ? "pin-artifact-unreadable" : "pin-digest-mismatch",
631
+ details: { package: failed.package, path: failed.path, declaredSha256: failed.declaredSha256, actualSha256: failed.actualSha256, containmentKind: failed.containmentKind ?? null },
632
+ steps: projectSteps,
633
+ });
634
+ }
635
+ projectSteps.push("adoption-pin-digests");
636
+ const assessment = assessOverridesPolicy(projectProfile.overrides);
637
+ if (!assessment.ok) {
638
+ const [finding] = assessment.findings;
639
+ return result("SPE1007", { reason: finding.kind, details: { ...finding }, steps: projectSteps });
640
+ }
641
+ projectSteps.push("overrides-policy");
642
+ return result("SPE0000", {
643
+ projectId: projectProfile.project.id,
644
+ details: { adoptionDeclared: true, overrides: projectProfile.overrides.length },
645
+ steps: projectSteps,
646
+ });
647
+ }
648
+
571
649
  /** The frozen SPI definition document. */
572
650
  export function loadSpiDefinition() {
573
651
  return SPI_DEFINITION;
@@ -2,119 +2,14 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://spi.profiles.skill-family.example/v2/profile-adoption.json",
4
4
  "title": "ProfileAdoptionAndOverridesSections",
5
- "description": "SPI v2 type definitions for the optional top-level `adoption` and `overrides` sections of a profile descriptor (skill-family.profile-descriptor). The adoption declaration is the lightweight adoption proof that replaces the deprecated adoption-lock concept (remediation handoff D-8); overrides carry self-tightening rule declarations that reuse the original rule ids with project-level numeric values (handoff D-6). profiles/spi/index.mjs composes these $defs onto the contracts profile-descriptor schema; the contracts package itself is not modified here.",
5
+ "description": "SPI v3 forwarding schema for provider descriptor adoption and overrides sections. Shape definitions remain owned by the Contracts profile-adoption-declaration schema.",
6
6
  "type": "object",
7
7
  "$defs": {
8
- "pinnedPackage": {
9
- "type": "object",
10
- "additionalProperties": false,
11
- "required": ["version", "path", "sha256"],
12
- "properties": {
13
- "version": {
14
- "type": "string",
15
- "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
16
- "description": "Exact pinned version of this Foundation package."
17
- },
18
- "path": {
19
- "type": "string",
20
- "pattern": "^[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*$",
21
- "description": "Relative POSIX path of the pinned artifact inside the profile write set; verifyProfile reads it through the containment layer and compares its real sha256 against the declared digest (GK-4 real-file discipline)."
22
- },
23
- "sha256": {
24
- "type": "string",
25
- "pattern": "^[0-9a-f]{64}$",
26
- "description": "Declared sha256 hex digest of the pinned artifact bytes."
27
- }
28
- }
29
- },
30
8
  "adoption": {
31
- "type": "object",
32
- "description": "Foundation adoption declaration (minimal D-8 field set). Presence is optional; when present every field is mandatory and mechanically verified by verifyProfile.",
33
- "additionalProperties": false,
34
- "required": ["foundation_profile", "foundation_pin", "adopted_at"],
35
- "properties": {
36
- "foundation_profile": {
37
- "type": "object",
38
- "additionalProperties": false,
39
- "required": ["id", "version", "stability"],
40
- "properties": {
41
- "id": {
42
- "type": "string",
43
- "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
44
- "description": "Stable kebab-case identifier of the adopted Foundation profile."
45
- },
46
- "version": {
47
- "type": "integer",
48
- "minimum": 1,
49
- "description": "Integer version of the adopted Foundation profile."
50
- },
51
- "stability": {
52
- "enum": ["stable", "candidate-only"],
53
- "description": "Stability class of the adopted Foundation profile at adoption time."
54
- }
55
- }
56
- },
57
- "foundation_pin": {
58
- "type": "object",
59
- "description": "Pin of all three Foundation packages (contracts, harness-node, engineering-kit) with exact versions and artifact sha256 digests.",
60
- "additionalProperties": false,
61
- "required": ["algorithm", "packages"],
62
- "properties": {
63
- "algorithm": {
64
- "const": "sha256",
65
- "description": "Digest algorithm; sha256 is the only admitted value."
66
- },
67
- "packages": {
68
- "type": "object",
69
- "additionalProperties": false,
70
- "required": [
71
- "skill-family-contracts",
72
- "skill-family-harness-node",
73
- "skill-family-engineering-kit"
74
- ],
75
- "properties": {
76
- "skill-family-contracts": { "$ref": "#/$defs/pinnedPackage" },
77
- "skill-family-harness-node": { "$ref": "#/$defs/pinnedPackage" },
78
- "skill-family-engineering-kit": { "$ref": "#/$defs/pinnedPackage" }
79
- }
80
- }
81
- }
82
- },
83
- "adopted_at": {
84
- "type": "string",
85
- "format": "date-time",
86
- "description": "RFC 3339 date-time at which the adoption took place."
87
- }
88
- }
9
+ "$ref": "https://contracts.skill-family.example/v1/profile-adoption-declaration.json#/$defs/adoption"
89
10
  },
90
11
  "overrides": {
91
- "type": "array",
92
- "description": "Self-tightening override declarations (handoff D-6): each entry reuses an existing rule id with a project-level numeric value. Overrides may only tighten the baseline, never relax it; every entry is machine-checked by verifyProfile against the frozen rule baseline catalog (profiles/spi/rule-baseline-catalog.json).",
93
- "items": {
94
- "type": "object",
95
- "additionalProperties": false,
96
- "required": ["ruleId", "parameter", "value"],
97
- "properties": {
98
- "ruleId": {
99
- "type": "string",
100
- "pattern": "^SFA-[A-Z0-9]+-[0-9]{3}$",
101
- "description": "Canonical rule id being tightened (no new rule id is minted; the original id is reused)."
102
- },
103
- "parameter": {
104
- "type": "string",
105
- "pattern": "^[a-zA-Z][a-zA-Z0-9]*$",
106
- "description": "Named numeric parameter of the rule, as registered in the rule baseline catalog."
107
- },
108
- "value": {
109
- "type": "number",
110
- "description": "Project-level override value; must strictly tighten the catalog baseline in the parameter's declared direction."
111
- },
112
- "note": {
113
- "type": "string",
114
- "description": "Optional human rationale; not consumed mechanically."
115
- }
116
- }
117
- }
12
+ "$ref": "https://contracts.skill-family.example/v1/profile-adoption-declaration.json#/$defs/overrides"
118
13
  }
119
14
  }
120
15
  }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://contracts.skill-family.example/v1/project-profile.json",
4
+ "title": "ProjectProfile",
5
+ "description": "Declares a project's Skill Family adoption and tightening overrides.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schemaVersion", "kind", "project", "adoption", "overrides"],
9
+ "properties": {
10
+ "schemaVersion": { "const": 1 },
11
+ "kind": { "const": "skill-family.project-profile" },
12
+ "project": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": ["id", "workspace"],
16
+ "properties": {
17
+ "id": { "type": "string", "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$" },
18
+ "workspace": { "type": "string", "minLength": 1 }
19
+ }
20
+ },
21
+ "adoption": { "$ref": "https://contracts.skill-family.example/v1/profile-adoption-declaration.json#/$defs/adoption" },
22
+ "overrides": { "$ref": "https://contracts.skill-family.example/v1/profile-adoption-declaration.json#/$defs/overrides" },
23
+ "note": { "type": "string" }
24
+ }
25
+ }
@@ -0,0 +1,19 @@
1
+ version: 0.8.0
2
+ date: 2026-08-21
3
+ locales:
4
+ en:
5
+ summary: Profile SPI v3 adds direct verification for scaffolded Project Profiles while descriptor verification remains stable.
6
+ changes:
7
+ added:
8
+ - Adds verifyProjectProfile({ projectRoot, profileRelPath? }) for skill-family.project-profile declarations.
9
+ - Keeps verifyProfile descriptor-only and reuses Contracts-owned adoption and overrides field definitions.
10
+ - Adds SPE1008 PROJECT_PROFILE_INVALID; SPE1006 and SPE1007 retain their existing meanings.
11
+ upgradeNotes: Project-root consumers must pin engineering-kit 0.8.0 and call verifyProjectProfile. Descriptor consumers continue to call verifyProfile; 0.7.0 remains available.
12
+ zh-CN:
13
+ summary: Profile SPI v3 新增 scaffold 项目 Profile 的直接校验,descriptor 校验保持兼容。
14
+ changes:
15
+ added:
16
+ - 新增 verifyProjectProfile({ projectRoot, profileRelPath? }),用于校验 skill-family.project-profile 声明。
17
+ - verifyProfile 继续只校验 descriptor,并复用 Contracts 拥有的 adoption 与 overrides 字段定义。
18
+ - 新增 SPE1008 PROJECT_PROFILE_INVALID;SPE1006 与 SPE1007 含义保持不变。
19
+ upgradeNotes: 项目根消费者必须锁定 engineering-kit 0.8.0 并调用 verifyProjectProfile;descriptor 消费者继续调用 verifyProfile,0.7.0 仍可使用。
@@ -54,12 +54,12 @@ async function readExistingBytes(rootAbs, relPath) {
54
54
  }
55
55
 
56
56
  /**
57
- * The per-adoption profile draft (SPI v2; remediation handoff C3/D-8).
57
+ * The per-adoption project profile draft (SPI v3; remediation handoff C3/D-8).
58
58
  *
59
59
  * Replaces the deprecated ten-field hand-off draft of the adoption-lock
60
60
  * era: the adoption-lock concept and its artifact form are abolished, and
61
61
  * the lightweight adoption proof is the profile.json descriptor's own
62
- * adoption declaration, machine-verified by verifyProfile (SPI v2):
62
+ * adoption declaration, machine-verified by verifyProjectProfile:
63
63
  * descriptor-schema completeness of the minimal adoption field set, GK-4
64
64
  * real-file digest discipline (SPE1006), and the tightening-only overrides
65
65
  * policy (SPE1007).
@@ -100,7 +100,8 @@ export function buildProfileDraft({ inputs, migrationManifest, writeSet, entries
100
100
  .map((pkg) => [pkg.name, pkg]),
101
101
  );
102
102
  const loadedVersions = Object.freeze({
103
- "skill-family-contracts": CONTRACTS_VERSION,
103
+ "skill-family-contracts": KIT_VERSION,
104
+ "skill-family-harness-node": KIT_VERSION,
104
105
  "skill-family-engineering-kit": KIT_VERSION,
105
106
  });
106
107
  const packages = {};
@@ -114,7 +115,7 @@ export function buildProfileDraft({ inputs, migrationManifest, writeSet, entries
114
115
  packages[name] = { version, path: null, sha256: null };
115
116
  }
116
117
 
117
- incompleteFields.push("descriptor.profile.version", "adoption.adopted_at");
118
+ incompleteFields.push("adoption.adopted_at");
118
119
 
119
120
  // Binding digests: the target file-set summary and the Foundation plan.
120
121
  const targetSetDigest = digestBytes(
@@ -124,31 +125,30 @@ export function buildProfileDraft({ inputs, migrationManifest, writeSet, entries
124
125
  Buffer.from(writeSet.map((item) => `${item.path}:${item.action}:${item.sha256}`).sort().join("\n"), "utf8"),
125
126
  );
126
127
 
127
- return {
128
- kind: "skill-family.profile-draft",
128
+ const projectProfile = {
129
129
  schemaVersion: 1,
130
- descriptorRelPath: "profile.json",
131
- descriptor: {
132
- schemaVersion: 1,
133
- kind: "skill-family.profile-descriptor",
134
- profile: { id: inputs.projectId, name: inputs.projectName, version: null },
135
- base: { contractsVersion: CONTRACTS_VERSION },
136
- differences: [],
137
- spi: [],
138
- adoption: {
139
- foundation_profile: foundationProfile,
140
- foundation_pin: { algorithm: "sha256", packages },
141
- adopted_at: null,
142
- },
143
- overrides: [],
130
+ kind: "skill-family.project-profile",
131
+ project: { id: inputs.projectId, workspace: `${inputs.projectId}-workspace` },
132
+ adoption: {
133
+ foundation_profile: foundationProfile,
134
+ foundation_pin: { algorithm: "sha256", packages },
135
+ adopted_at: null,
144
136
  },
137
+ overrides: [],
138
+ };
139
+
140
+ return {
141
+ kind: "skill-family.project-profile-draft",
142
+ schemaVersion: 1,
143
+ profileRelPath: "profile.json",
144
+ projectProfile,
145
145
  overridesGuidance:
146
- "overrides stays empty by default. An override is admissible only when it reuses an existing rule id with a project-level numeric value that strictly tightens the frozen rule baseline catalog in the parameter's declared direction (not-increase: strictly less; not-decrease: strictly greater). Equality and relaxation are refused (SPI v2 SPE1007); risk identification for misuse of tightening belongs to skill-failure-auditor, not to this mechanical check.",
146
+ "overrides stays empty by default. An override is admissible only when it reuses an existing rule id with a project-level numeric value that strictly tightens the frozen rule baseline catalog in the parameter's declared direction (not-increase: strictly less; not-decrease: strictly greater). Equality and relaxation are refused (SPE1007); risk identification for misuse of tightening belongs to skill-failure-auditor, not to this mechanical check.",
147
147
  incompleteFields,
148
148
  ready: incompleteFields.length === 0,
149
149
  binding: { targetSetDigest, foundationPlanDigest },
150
150
  policyNote:
151
- "adoption-lock is deprecated (remediation handoff D-8): the kit no longer produces adoption-lock-form artifacts. The adoption proof is this descriptor's adoption declaration, completed with real artifact paths and digests inside the profile write set and machine-verified by verifyProfile (SPI v2: SPE1006 digest discipline, SPE1007 tightening-only overrides).",
151
+ "adoption-lock is deprecated (remediation handoff D-8): the kit no longer produces adoption-lock-form artifacts. The adoption proof is this project's adoption declaration, completed with real artifact paths and digests inside the profile write set and machine-verified by verifyProjectProfile (SPE1006 digest discipline, SPE1007 tightening-only overrides).",
152
152
  };
153
153
  }
154
154
 
package/src/skeleton.mjs CHANGED
@@ -398,9 +398,9 @@ function renderProjectProfile({ projectId, profileId }) {
398
398
  foundation_pin: {
399
399
  algorithm: "sha256",
400
400
  packages: {
401
- "skill-family-contracts": { version: CONTRACTS_VERSION, path: null, sha256: null },
401
+ "skill-family-contracts": { version: KIT_VERSION, path: null, sha256: null },
402
402
  "skill-family-engineering-kit": { version: KIT_VERSION, path: null, sha256: null },
403
- "skill-family-harness-node": { version: null, path: null, sha256: null },
403
+ "skill-family-harness-node": { version: KIT_VERSION, path: null, sha256: null },
404
404
  },
405
405
  },
406
406
  adopted_at: null,
@@ -413,7 +413,7 @@ function renderProjectProfile({ projectId, profileId }) {
413
413
  note: "示例覆盖:项目自加严词元告警阈值(基线 2000,取值必须严格小于基线)。覆盖只允许更严、不允许放松;每条覆盖都是可机器校验的声明,按冻结的规则基线目录(rule-baseline-catalog.json)核验。",
414
414
  },
415
415
  ],
416
- note: "采用声明与自加严覆盖由项目自行维护;engineering kit 只种子化模板,永不覆盖。null 字段必须由项目以真实事实补齐后才构成有效声明:foundation_profile.version/stability 是人的决策;foundation_pin.packages.*.path/sha256 必须从项目写集内真实钉扎工件的字节计算(GK-4 摘要纪律);adopted_at 是完成采用的 RFC 3339 时间。本文件是项目级声明(kind skill-family.project-profile),不是 Profile SPI descriptor;adoption/overrides 段的形状与 SPI v2 权威定义(profiles/spi/profile-adoption.schema.json)逐字段一致。",
416
+ note: "采用声明与自加严覆盖由项目自行维护;engineering kit 只种子化模板,永不覆盖。null 字段必须由项目以真实事实补齐后才构成有效声明:foundation_profile.version/stability 是人的决策;foundation_pin.packages.*.path/sha256 必须从项目写集内真实钉扎工件的字节计算(GK-4 摘要纪律);adopted_at 是完成采用的 RFC 3339 时间。本文件是项目级声明(kind skill-family.project-profile),不是 Profile SPI descriptor;adoption/overrides 段的形状与 SPI v3 权威定义(Contracts profile-adoption-declaration 的 $defs)逐字段一致。",
417
417
  });
418
418
  }
419
419