@kungfu-tech/buildchain 3.0.7-alpha.0 → 3.0.7

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.
Files changed (67) hide show
  1. package/actions/promote-buildchain-ref/README.md +10 -0
  2. package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
  3. package/contracts/engineering-housekeeper-v1.schema.json +143 -0
  4. package/contracts/fixtures/engineering-housekeeper-v1/cases.json +68 -0
  5. package/dist/site/buildchain-contract.json +24 -24
  6. package/dist/site/buildchain-site.json +91 -30
  7. package/dist/site/capability-registry.json +3 -3
  8. package/dist/site/controller-registry.json +6 -2
  9. package/dist/site/kfd-claims.json +122 -11
  10. package/dist/site/kfd-upstream-aggregate.json +1 -1
  11. package/dist/site/manual-registry.json +8 -7
  12. package/dist/site/node-api-registry.json +683 -105
  13. package/dist/site/page-registry.json +80 -19
  14. package/dist/site/public-surface-audit.json +98 -7
  15. package/dist/site/publication-authority-registry.json +81 -1
  16. package/dist/site/publication-registry.json +4 -4
  17. package/dist/site/release-provenance.json +2 -0
  18. package/dist/site/site-manifest.json +11 -11
  19. package/dist/site/workflow-registry.json +119 -2
  20. package/docs/MAP.md +1 -0
  21. package/docs/auditable-demo.md +2 -2
  22. package/docs/dev-delivery-warrant.md +49 -4
  23. package/docs/engineering-housekeeper.md +138 -0
  24. package/docs/lifecycle-protocol.md +4 -2
  25. package/docs/node-api-reference.md +277 -212
  26. package/docs/release-governance.md +17 -2
  27. package/docs/release-tail-provider-plane.md +1 -1
  28. package/docs/reusable-build-surface.md +11 -0
  29. package/package.json +4 -1
  30. package/packages/core/artifact-signing.js +61 -0
  31. package/packages/core/buildchain-config.js +66 -6
  32. package/packages/core/buildchain-publication-authority.js +4 -0
  33. package/packages/core/controller-evidence.js +2 -1
  34. package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
  35. package/packages/core/dev-delivery-warrant-shadow.js +502 -0
  36. package/packages/core/dev-delivery-warrant.js +15 -6
  37. package/packages/core/diagnostics.js +8 -3
  38. package/packages/core/engineering-housekeeper-github-client.js +222 -0
  39. package/packages/core/engineering-housekeeper-github.js +501 -0
  40. package/packages/core/engineering-housekeeper.js +259 -0
  41. package/packages/core/index.js +3 -0
  42. package/packages/core/kfd-gate.js +45 -15
  43. package/packages/core/publication-rehearsal-runtime.js +13 -1
  44. package/packages/core/release-passport.js +130 -20
  45. package/scripts/assemble-self-publication-admission.mjs +1 -1
  46. package/scripts/audit-publication-control-plane.mjs +1 -1
  47. package/scripts/auditable-demo-bundle-verification.mjs +2 -3
  48. package/scripts/auditable-demo-platform.mjs +2 -2
  49. package/scripts/auditable-demo-renditions.mjs +1 -1
  50. package/scripts/auditable-demo.mjs +2 -2
  51. package/scripts/build-contract-core.mjs +8 -3
  52. package/scripts/build-standalone-binary.mjs +14 -3
  53. package/scripts/check-inventory.mjs +3 -1
  54. package/scripts/dev-delivery-warrant.mjs +31 -4
  55. package/scripts/dev-pr-auto-merge.mjs +30 -4
  56. package/scripts/dev-pr-delivery-warrant.mjs +50 -0
  57. package/scripts/engineering-housekeeper-workflow.mjs +394 -0
  58. package/scripts/generate-site-bundle.mjs +23 -4
  59. package/scripts/inspect-artifact-signing-requests.mjs +6 -0
  60. package/scripts/materialize-self-release-candidate-version.mjs +6 -0
  61. package/scripts/publication-commit-evidence.mjs +69 -23
  62. package/scripts/release-candidate-resolver.mjs +16 -10
  63. package/scripts/resume-from-candidate-run.mjs +123 -9
  64. package/scripts/seal-artifact-signing-requests.mjs +6 -0
  65. package/scripts/site-capability-metadata.mjs +2 -0
  66. package/scripts/web-surface-core.mjs +8 -2
  67. package/scripts/workflow-call-contract.mjs +1 -1
@@ -610,6 +610,12 @@ The reusable caller supports `off`, read-only `shadow`, and fail-closed
610
610
  `required` rollout modes. GitHub Merge Queue remains the final protected-ref
611
611
  authority in every mode.
612
612
 
613
+ Bounded-concurrency experiments use the separate effect-disabled Warrant
614
+ shadow planner. It may evaluate at most two fully bound lanes from one exact
615
+ observation, but it cannot mint a second production Warrant or mutate GitHub.
616
+ Its aggregate threshold decision is qualification evidence for a later rollout
617
+ change, not authority to change the live single-flight policy.
618
+
613
619
  The canonical consumer required check context is `check / check`, matching the
614
620
  reusable workflow call plus its `check` job. Buildchain's own `Verify` workflow
615
621
  emits the repository-local context `check`, so Buildchain self-promotion,
@@ -785,6 +791,12 @@ active semver dev line, so consumers do not pin patrol to a stale minor branch.
785
791
  The separate workflow names keep consumer schedules readable and stable while
786
792
  Buildchain adds new checks behind the cadence wrappers.
787
793
 
794
+ Branch and pull-request residue uses the separate
795
+ [`Engineering Housekeeper`](engineering-housekeeper.md) contract. Its scheduled
796
+ callers are report-only and read-only by default; apply requires a manual,
797
+ explicit two-part gate, exact provider-state revalidation, scoped job
798
+ permissions, and rooted plan/report/receipt evidence.
799
+
788
800
  ## Package-Manager Adapters
789
801
 
790
802
  Old ABV assumed JavaScript repositories with root version state and often
@@ -816,8 +828,11 @@ declare version-state files and lifecycle commands without pretending every
816
828
  project is a Node workspace. Supported version files include JSON, TOML, and
817
829
  regex-based files such as `CMakeLists.txt` or `conanfile.py`.
818
830
 
819
- The promotion action consumes `version.files`, optional anchored/manual
820
- `version.derived_files`, and `lifecycle.verify`.
831
+ The promotion action consumes `version.files`, optional
832
+ `version.derived_files`, and `lifecycle.verify`. Semver and anchored/manual
833
+ repositories may both declare lifecycle-regenerated tracked outputs as derived
834
+ files; anchored/manual repositories additionally bind them into their committed
835
+ version witnesses.
821
836
  The verify stage runs after generated version-state changes are applied locally
822
837
  and before any release refs move. If `verification-command` is passed directly
823
838
  to the action, that explicit command overrides `lifecycle.verify`.
@@ -95,7 +95,7 @@ Buildchain self-release calls the same public reusable workflow coordinate as a
95
95
  consumer:
96
96
 
97
97
  ```text
98
- kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@9a0cdf8d84aacf8c7daaac82efa43d1b34696a03
98
+ kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@769b221bad7a6b9104afad4c2628d9dca396ab0f
99
99
  ```
100
100
 
101
101
  The caller pins the public router and runtime to the same exact implementation
@@ -683,6 +683,17 @@ For a declared macOS `archive`, the authority safely extracts the sealed
683
683
  container, signs and verifies every Mach-O payload, signs Mach-O payloads inside
684
684
  embedded Python wheels, rebuilds each affected wheel's PEP 427 `RECORD`, and
685
685
  recreates the original zip or tar.gz before returning the exact final bytes.
686
+ Archives whose executable hosts a JIT runtime can additionally request the
687
+ Buildchain-owned `entitlements_profile = "jit-executable-v1"` and exact paths,
688
+ for example `entitlements_paths = ["product/runtime/python/bin/python3"]`.
689
+ Paths are relative to the extracted archive root. The authority
690
+ then attaches only `com.apple.security.cs.allow-jit` to the exact executable
691
+ Mach-O files sealed in `entitlements_paths`, leaves every other executable and
692
+ library without exception entitlements, and records the profile, paths, and
693
+ entitled executable count in provider evidence. Consumer-provided entitlement
694
+ files and wildcard target paths are unsupported. The profile is valid only for
695
+ Apple `archive` requests and fails closed on an unsafe, missing,
696
+ non-executable, duplicate, or unsealed target path.
686
697
  Windows `pe` and `binary` artifacts
687
698
  resolve to timestamped native `windows-authenticode`; Windows PE never falls
688
699
  back to a detached signature. Linux and other non-native binary files,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "3.0.7-alpha.0",
3
+ "version": "3.0.7",
4
4
  "private": false,
5
5
  "description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
6
6
  "repository": "https://github.com/kungfu-systems/buildchain",
@@ -49,6 +49,8 @@
49
49
  "./publication-control-plane-audit": "./packages/core/publication-control-plane-audit.js",
50
50
  "./buildchain-publication-authority": "./packages/core/buildchain-publication-authority.js",
51
51
  "./github-governance-authority": "./packages/core/github-governance-authority.js",
52
+ "./engineering-housekeeper": "./packages/core/engineering-housekeeper.js",
53
+ "./engineering-housekeeper-github": "./packages/core/engineering-housekeeper-github.js",
52
54
  "./kfd-gate": "./packages/core/kfd-gate.js",
53
55
  "./release-candidate": "./packages/core/release-candidate.js",
54
56
  "./release-candidate-recovery": "./packages/core/release-candidate-recovery.js",
@@ -137,6 +139,7 @@
137
139
  },
138
140
  "dependencies": {
139
141
  "@kungfu-tech/kfd": "1.0.0-alpha.53",
142
+ "ajv": "8.20.0",
140
143
  "smol-toml": "1.7.0"
141
144
  }
142
145
  }
@@ -9,6 +9,7 @@ export const ARTIFACT_SIGNING_AUTHORITY_CONTRACT =
9
9
 
10
10
  const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/u;
11
11
  const SOURCE_SHA_PATTERN = /^[0-9a-f]{40}$/u;
12
+ const ENTITLEMENTS_PROFILES = new Set(["none", "jit-executable-v1"]);
12
13
  const FORBIDDEN_CREDENTIAL_KEYS =
13
14
  /(?:certificate|password|private.?key|secret|token|notary|issuer|team.?id|environment)/iu;
14
15
 
@@ -88,6 +89,64 @@ function exactSourceSha(value, label) {
88
89
  return normalized;
89
90
  }
90
91
 
92
+ function entitlementsPaths(value) {
93
+ if (value === undefined) return [];
94
+ if (!Array.isArray(value)) {
95
+ throw new Error("signing entitlements paths must be an array");
96
+ }
97
+ const paths = value.map((entry, index) => {
98
+ const normalized = nonEmptyString(
99
+ entry,
100
+ `signing entitlements paths[${index}]`,
101
+ );
102
+ const parts = normalized.split("/");
103
+ if (
104
+ normalized.startsWith("/") ||
105
+ normalized.includes("\\") ||
106
+ normalized.includes(",") ||
107
+ parts.some((part) => part === "" || part === "." || part === "..")
108
+ ) {
109
+ throw new Error(
110
+ `signing entitlements paths[${index}] must be a safe archive-relative path`,
111
+ );
112
+ }
113
+ return normalized;
114
+ });
115
+ if (new Set(paths).size !== paths.length) {
116
+ throw new Error("signing entitlements paths must not contain duplicates");
117
+ }
118
+ return paths;
119
+ }
120
+
121
+ function signingEntitlements(profileId, kind, signature) {
122
+ const profile = String(signature.entitlementsProfile || "none").trim();
123
+ const paths = entitlementsPaths(signature.entitlementsPaths);
124
+ if (!ENTITLEMENTS_PROFILES.has(profile)) {
125
+ throw new Error(
126
+ `unsupported signing entitlements profile: ${profile || "<empty>"}`,
127
+ );
128
+ }
129
+ if (
130
+ profile !== "none" &&
131
+ (profileId !== "apple-developer-id" || kind !== "archive")
132
+ ) {
133
+ throw new Error(
134
+ `signing entitlements profile ${profile} requires an Apple archive`,
135
+ );
136
+ }
137
+ if (
138
+ (profile === "none" && paths.length !== 0) ||
139
+ (profile !== "none" && paths.length === 0)
140
+ ) {
141
+ throw new Error(
142
+ "signing entitlements paths must be non-empty exactly when an entitlements profile is enabled",
143
+ );
144
+ }
145
+ return profile === "none"
146
+ ? {}
147
+ : { entitlementsProfile: profile, entitlementsPaths: paths };
148
+ }
149
+
91
150
  function assertNoCredentialMaterial(value, path = "request") {
92
151
  if (!value || typeof value !== "object") return;
93
152
  for (const [key, child] of Object.entries(value)) {
@@ -191,6 +250,7 @@ export function createArtifactSigningRequest({
191
250
  platform,
192
251
  artifactKind: kind,
193
252
  });
253
+ const entitlementIntent = signingEntitlements(profile.id, kind, signature);
194
254
  const request = {
195
255
  schemaVersion: 1,
196
256
  contract: ARTIFACT_SIGNING_REQUEST_CONTRACT,
@@ -253,6 +313,7 @@ export function createArtifactSigningRequest({
253
313
  profile: profile.id,
254
314
  provider: profile.provider,
255
315
  semantics: profile.semantics,
316
+ ...entitlementIntent,
256
317
  },
257
318
  delivery: {
258
319
  mode: nonEmptyString(
@@ -61,6 +61,10 @@ const SUPPORTED_SIGNING_PROFILES = new Set([
61
61
  "windows-authenticode",
62
62
  "detached-signature-v1",
63
63
  ]);
64
+ const SUPPORTED_SIGNING_ENTITLEMENTS_PROFILES = new Set([
65
+ "none",
66
+ "jit-executable-v1",
67
+ ]);
64
68
  const SUPPORTED_SIGNING_ARTIFACT_KINDS = new Set([
65
69
  "auto", "binary", "archive", "blob", "directory", "mach-o", "app-bundle",
66
70
  "framework-bundle", "plugin-bundle", "xpc-bundle", "dylib", "pkg", "dmg",
@@ -99,6 +103,27 @@ function normalizeStringArray(value, label) {
99
103
  return value.map((entry, index) => assertString(entry, `${label}[${index}]`));
100
104
  }
101
105
 
106
+ function normalizeSigningEntitlementsPaths(value, label) {
107
+ const paths = normalizeStringArray(value, label);
108
+ for (const [index, entry] of paths.entries()) {
109
+ const parts = entry.split("/");
110
+ if (
111
+ entry.startsWith("/") ||
112
+ entry.includes("\\") ||
113
+ entry.includes(",") ||
114
+ parts.some((part) => part === "" || part === "." || part === "..")
115
+ ) {
116
+ throw new Error(
117
+ `${label}[${index}] must be a safe archive-relative path`,
118
+ );
119
+ }
120
+ }
121
+ if (new Set(paths).size !== paths.length) {
122
+ throw new Error(`${label} must not contain duplicate paths`);
123
+ }
124
+ return paths;
125
+ }
126
+
102
127
  function getByDottedKey(target, key) {
103
128
  return String(key)
104
129
  .split(".")
@@ -362,7 +387,16 @@ function normalizeSigningSection(signing) {
362
387
  const artifacts = signing.artifacts.map((artifact, index) => {
363
388
  const label = `signing.artifacts[${index}]`;
364
389
  assertPlainObject(artifact, label);
365
- const allowedKeys = new Set(["id", "path", "profile", "kind", "platforms", "required"]);
390
+ const allowedKeys = new Set([
391
+ "id",
392
+ "path",
393
+ "profile",
394
+ "entitlements_profile",
395
+ "entitlements_paths",
396
+ "kind",
397
+ "platforms",
398
+ "required",
399
+ ]);
366
400
  for (const key of Object.keys(artifact)) {
367
401
  if (!allowedKeys.has(key)) {
368
402
  throw new Error(`${label}.${key} is not supported; declare desired signature state only`);
@@ -377,10 +411,41 @@ function normalizeSigningSection(signing) {
377
411
  if (!SUPPORTED_SIGNING_ARTIFACT_KINDS.has(kind)) {
378
412
  throw new Error(`${label}.kind is not a supported signing artifact kind`);
379
413
  }
414
+ const entitlementsProfile =
415
+ artifact.entitlements_profile === undefined
416
+ ? "none"
417
+ : assertString(
418
+ artifact.entitlements_profile,
419
+ `${label}.entitlements_profile`,
420
+ );
421
+ if (!SUPPORTED_SIGNING_ENTITLEMENTS_PROFILES.has(entitlementsProfile)) {
422
+ throw new Error(
423
+ `${label}.entitlements_profile must be one of none or jit-executable-v1`,
424
+ );
425
+ }
426
+ if (entitlementsProfile !== "none" && kind !== "archive") {
427
+ throw new Error(
428
+ `${label}.entitlements_profile ${entitlementsProfile} requires kind = "archive"`,
429
+ );
430
+ }
431
+ const entitlementsPaths = normalizeSigningEntitlementsPaths(
432
+ artifact.entitlements_paths,
433
+ `${label}.entitlements_paths`,
434
+ );
435
+ if (
436
+ (entitlementsProfile === "none" && entitlementsPaths.length !== 0) ||
437
+ (entitlementsProfile !== "none" && entitlementsPaths.length === 0)
438
+ ) {
439
+ throw new Error(
440
+ `${label}.entitlements_paths must be non-empty exactly when entitlements_profile is enabled`,
441
+ );
442
+ }
380
443
  return {
381
444
  id: artifact.id === undefined ? artifactPath : assertString(artifact.id, `${label}.id`),
382
445
  path: artifactPath,
383
446
  profile,
447
+ entitlementsProfile,
448
+ entitlementsPaths,
384
449
  kind,
385
450
  platforms: normalizeStringArray(artifact.platforms, `${label}.platforms`),
386
451
  required: optionalBoolean(artifact.required, true),
@@ -1193,11 +1258,6 @@ function validateAnchoredDerivedVersionMaterialConfig(config) {
1193
1258
  if (derivedFiles.length === 0) {
1194
1259
  return;
1195
1260
  }
1196
- if (config.version.strategy !== "anchored" || config.version.next !== "manual") {
1197
- throw new Error(
1198
- "version.derived_files requires version.strategy = \"anchored\" and version.next = \"manual\"",
1199
- );
1200
- }
1201
1261
  const versionState =
1202
1262
  config.lifecycle?.["version-state"] ||
1203
1263
  config.lifecycle?.version_state;
@@ -37,6 +37,10 @@ const DESCRIPTORS = Object.freeze([
37
37
  [".github/workflows/dev-delivery-warrant-cancel.yml", "governance-write"],
38
38
  [".github/workflows/dev-merge-queue-governance.yml", "governance-write"],
39
39
  [".github/workflows/dev-pr-auto-merge.yml", "governance-write"],
40
+ [".github/workflows/engineering-housekeeper-daily.yml", "governance-write"],
41
+ [".github/workflows/engineering-housekeeper-monthly.yml", "governance-write"],
42
+ [".github/workflows/engineering-housekeeper-weekly.yml", "governance-write"],
43
+ [".github/workflows/engineering-housekeeper.yml", "governance-write"],
40
44
  [".github/workflows/github-governance-audit.yml", "governance-write"],
41
45
  [".github/workflows/github-artifact-attestation.yml", "evidence-publication", true, ["github-artifact-attestation"], "oidc", "consumer-defined", "caller-bound", "caller-bound"],
42
46
  [".github/workflows/npm-publish.yml", "dry-run-only"],
@@ -34,13 +34,14 @@ const CONTROLLER_SPECS = [
34
34
  "resolve-runtime",
35
35
  "resolve-source",
36
36
  "anchored-release-preflight",
37
+ "pre-build-contracts",
37
38
  "build",
38
39
  "signing-finalization",
39
40
  "credential-island",
40
41
  "verify",
41
42
  "aggregate",
42
43
  ],
43
- optionalStages: ["anchored-release-preflight", "credential-island"],
44
+ optionalStages: ["anchored-release-preflight", "pre-build-contracts", "credential-island"],
44
45
  evidence: [
45
46
  "platform-manifests",
46
47
  "build-summary",
@@ -6,6 +6,7 @@ const TERMINAL_STATES = new Set(["merged", "terminal-failure", "dequeued", "canc
6
6
  const QUEUED_CANCELLATION_EVENTS = new Map([
7
7
  ["closed", "cancelled"],
8
8
  ["cancelled", "cancelled"],
9
+ ["synchronize", "cancelled"],
9
10
  ["dequeued", "dequeued"],
10
11
  ]);
11
12