@patronage/software-factory 1.0.0-alpha.3 → 1.0.0-alpha.5

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/README.md CHANGED
@@ -37,7 +37,7 @@ A stacked release train has one terminal release-owning slice. Interior slices d
37
37
  A stable cut has two phases. Phase 1 is the verified package candidate: steps 1 through 4 below, ending in the attended npm publish, tag, and GitHub release. Phase 2 is post-publication stable-doc convergence: steps 5 and 6, a normal admitted docs PR followed by the read-only convergence assertion from a clean `main`. Stable docs must never claim an unpublished version, so they cannot land in the phase-1 change. Prerelease behavior is unchanged: prerelease notes may land before publish while stable docs keep naming the prior stable version.
38
38
 
39
39
  1. Bump `package.json` to valid SemVer. The assertion derives its expected stable or prerelease channel from the version; it accepts no channel flag.
40
- 2. Publish to npm per the [repository operations runbook](../docs/repository-operations.md#publishing) (attended, hand-cut, `pnpm publish` only). A stable version uses `pnpm --filter @patronage/software-factory publish`, which assigns npm `latest`. A prerelease must use `pnpm --filter @patronage/software-factory publish --tag next`; bare `pnpm publish` defaults to `latest` and would move it before the post-hoc assertion can reject the release.
40
+ 2. Publish to npm per the [repository operations runbook](../docs/repository-operations.md#publishing) (attended, hand-cut, `pnpm publish` only). Both factory packages pin `publishConfig.tag: next`, so a bare `pnpm --filter @patronage/software-factory publish` lands on `next` and cannot move `latest`. A prerelease is therefore the bare command; a stable version must say it out loud: `pnpm --filter @patronage/software-factory publish --tag latest`. Forgetting the flag on a stable cut leaves `latest` untouched for the convergence assertion to flag the safe failure direction.
41
41
  3. Cut the `software-factory-vX.Y.Z` tag and push it.
42
42
  4. Cut the GitHub release for that tag, with notes covering features, deletions, breaking changes, and closed issues. Mark it as a prerelease exactly when its SemVer version has a prerelease component. When the release changes the profile `schemaVersion`, state it in the notes — the profile schema version is a separate axis from the package version.
43
43
  5. Converge the stable docs after publication. Update affected `software-factory-docs/` pages through a normal admitted docs PR. Stable docs must state the current stable version. Prerelease docs may record the candidate and its migration notes, but must not represent it as the current stable release.
package/dist/index.d.ts CHANGED
@@ -1252,8 +1252,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1252
1252
  reviewedPatchId: z.ZodOptional<z.ZodString>;
1253
1253
  status: z.ZodEnum<{
1254
1254
  blocked: "blocked";
1255
- stale: "stale";
1256
1255
  "not-required": "not-required";
1256
+ stale: "stale";
1257
1257
  current: "current";
1258
1258
  missing: "missing";
1259
1259
  }>;
@@ -1264,8 +1264,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1264
1264
  reviewedPatchId: z.ZodOptional<z.ZodString>;
1265
1265
  status: z.ZodEnum<{
1266
1266
  blocked: "blocked";
1267
- stale: "stale";
1268
1267
  "not-required": "not-required";
1268
+ stale: "stale";
1269
1269
  current: "current";
1270
1270
  missing: "missing";
1271
1271
  }>;
@@ -1779,8 +1779,8 @@ declare const loadEvidenceEnvelopes: (cwd: string) => LoadedEvidenceEnvelope[];
1779
1779
  declare const REVIEW_STATUS_VALUES: readonly ["not-required", "current", "stale", "missing", "blocked"];
1780
1780
  declare const reviewStatusSchema: z.ZodEnum<{
1781
1781
  blocked: "blocked";
1782
- stale: "stale";
1783
1782
  "not-required": "not-required";
1783
+ stale: "stale";
1784
1784
  current: "current";
1785
1785
  missing: "missing";
1786
1786
  }>;
@@ -2187,9 +2187,9 @@ declare const retroEnvelopeV1Schema: z.ZodObject<{
2187
2187
  kind: z.ZodLiteral<"retro-envelope">;
2188
2188
  outcome: z.ZodOptional<z.ZodObject<{
2189
2189
  status: z.ZodEnum<{
2190
+ blocked: "blocked";
2190
2191
  success: "success";
2191
2192
  fail: "fail";
2192
- blocked: "blocked";
2193
2193
  "ship-with-followups": "ship-with-followups";
2194
2194
  }>;
2195
2195
  verdict: z.ZodOptional<z.ZodString>;
@@ -4076,13 +4076,13 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4076
4076
  reviews: {
4077
4077
  correctness: {
4078
4078
  required: boolean;
4079
- status: "blocked" | "stale" | "not-required" | "current" | "missing";
4079
+ status: "blocked" | "not-required" | "stale" | "current" | "missing";
4080
4080
  reviewedHeadSha?: string | undefined;
4081
4081
  reviewedPatchId?: string | undefined;
4082
4082
  };
4083
4083
  security?: {
4084
4084
  required: boolean;
4085
- status: "blocked" | "stale" | "not-required" | "current" | "missing";
4085
+ status: "blocked" | "not-required" | "stale" | "current" | "missing";
4086
4086
  reviewedHeadSha?: string | undefined;
4087
4087
  reviewedPatchId?: string | undefined;
4088
4088
  } | undefined;
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { appendFileSync, constants, cpSync, createReadStream, existsSync, mkdirS
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { Command, InvalidArgumentError } from "commander";
6
6
  import path from "node:path";
7
- import crypto, { createHash, createSign, randomUUID } from "node:crypto";
7
+ import crypto, { createHash, randomUUID } from "node:crypto";
8
8
  import { z } from "zod";
9
9
  import { execFileSync, spawnSync } from "node:child_process";
10
10
  import { link, lstat, mkdir, open, readFile, readdir, realpath, rename, stat, unlink } from "node:fs/promises";
@@ -16,8 +16,9 @@ import { Worker } from "node:worker_threads";
16
16
  import picomatch from "picomatch";
17
17
  import { parse } from "yaml";
18
18
  import { promisify } from "node:util";
19
+ import { GitHubApiError, mintInstallationToken } from "@patronage/factory-ci";
19
20
  //#region package.json
20
- var version = "1.0.0-alpha.3";
21
+ var version = "1.0.0-alpha.5";
21
22
  //#endregion
22
23
  //#region src/review-rungs.ts
23
24
  const EVIDENCE_REVIEW_RUNGS$1 = [
@@ -7673,31 +7674,6 @@ const defaultSleep = async (ms) => {
7673
7674
  const { setTimeout: delay } = await import("node:timers/promises");
7674
7675
  await delay(ms);
7675
7676
  };
7676
- const base64url = (value) => Buffer.from(value).toString("base64url");
7677
- function appJwt(config, now) {
7678
- const issuedAt = Math.floor(now / 1e3) - 60;
7679
- const unsigned = `${base64url(JSON.stringify({
7680
- alg: "RS256",
7681
- typ: "JWT"
7682
- }))}.${base64url(JSON.stringify({
7683
- exp: issuedAt + 600,
7684
- iat: issuedAt,
7685
- iss: config.appId
7686
- }))}`;
7687
- const signer = createSign("RSA-SHA256");
7688
- signer.update(unsigned);
7689
- signer.end();
7690
- return `${unsigned}.${signer.sign(readFileSync(config.privateKeyPath), "base64url")}`;
7691
- }
7692
- /** Carries the HTTP status so a caller can tell a retryable failure apart. */
7693
- var GitHubApiError = class extends Error {
7694
- status;
7695
- constructor(status, statusText) {
7696
- super(`GitHub API ${status} ${statusText}`);
7697
- this.name = "GitHubApiError";
7698
- this.status = status;
7699
- }
7700
- };
7701
7677
  /**
7702
7678
  * Worth another attempt: 422 is what GitHub answers for a head SHA it has not
7703
7679
  * seen yet (the whole reason the retry exists), 5xx and rate limiting are
@@ -7752,24 +7728,24 @@ async function githubJson(request, url, init, timeoutMs = GITHUB_PUBLISH_TIMEOUT
7752
7728
  if (!response.ok) throw new GitHubApiError(response.status, response.statusText);
7753
7729
  return await response.json();
7754
7730
  }
7755
- async function installationToken(repository, config, request, now, timeoutMs = GITHUB_PUBLISH_TIMEOUT_MS) {
7756
- const jwt = appJwt(config, now);
7757
- let { installationId } = config;
7758
- if (installationId === void 0) {
7759
- const installation = await githubJson(request, `https://api.github.com/repos/${repository.owner}/${repository.repo}/installation`, {
7760
- headers: { Authorization: `Bearer ${jwt}` },
7761
- method: "GET"
7762
- }, timeoutMs);
7763
- if (typeof installation.id !== "number") throw new TypeError("GitHub App installation response omitted id");
7764
- installationId = installation.id;
7765
- }
7766
- const token = await githubJson(request, `https://api.github.com/app/installations/${installationId}/access_tokens`, {
7767
- headers: { Authorization: `Bearer ${jwt}` },
7768
- method: "POST"
7769
- }, timeoutMs);
7770
- if (typeof token.token !== "string") throw new TypeError("GitHub App token response omitted token");
7771
- return token.token;
7772
- }
7731
+ /**
7732
+ * Mint an installation token for the Patronage Factory App.
7733
+ *
7734
+ * The mechanism app JWT, installation lookup, token exchange — lives in
7735
+ * `@patronage/factory-ci` (#617), because paitronage's proof-comment publisher
7736
+ * had grown a second copy of it. What stays here is what is this repository's:
7737
+ * where the credentials come from (`user-config`), and the publish timeout the
7738
+ * rest of these calls are bound by. Nothing is cached, as before.
7739
+ */
7740
+ const installationToken = (repository, config, request, now, timeoutMs = GITHUB_PUBLISH_TIMEOUT_MS) => mintInstallationToken({
7741
+ credentials: config,
7742
+ owner: repository.owner,
7743
+ repo: repository.repo
7744
+ }, {
7745
+ fetch: request,
7746
+ now: () => now,
7747
+ timeoutMs
7748
+ });
7773
7749
  function verifyOutput(proof) {
7774
7750
  const value = proof;
7775
7751
  const commands = value.executedCommands?.length ?? 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patronage/software-factory",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.5",
4
4
  "description": "Shared Patronage software factory CLI and project-profile validation tools",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,13 +29,15 @@
29
29
  }
30
30
  },
31
31
  "publishConfig": {
32
- "access": "public"
32
+ "access": "public",
33
+ "tag": "next"
33
34
  },
34
35
  "dependencies": {
35
36
  "commander": "14.0.3",
36
37
  "picomatch": "^4.0.5",
37
38
  "yaml": "^2.9.0",
38
- "zod": "4.4.3"
39
+ "zod": "4.4.3",
40
+ "@patronage/factory-ci": "1.0.0-alpha.5"
39
41
  },
40
42
  "devDependencies": {
41
43
  "@types/node": "24.13.3",