@treeseed/sdk 0.12.42 → 0.12.43

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.
@@ -8,6 +8,7 @@ import { resolveTreeseedWorkflowState, type TreeseedWorkflowStatusOptions } from
8
8
  import { type TreeseedReconcileResult } from '../reconcile/index.js';
9
9
  import { type TreeseedWorkflowRunCommand, type TreeseedWorkflowRunJournal } from './runs.js';
10
10
  import { type TreeseedWorkflowMode } from './session.js';
11
+ import { type TreeseedManagedRepository } from '../operations/services/managed-repositories.js';
11
12
  import type { TreeseedCloseInput, TreeseedCiInput, TreeseedConfigInput, TreeseedDestroyInput, TreeseedExportInput, TreeseedReleaseCandidateInput, TreeseedReleaseInput, TreeseedRecoverInput, TreeseedResumeInput, TreeseedSaveInput, TreeseedStageInput, TreeseedSwitchInput, TreeseedTaskBranchMetadata, TreeseedUpdateInput, TreeseedWorkflowContext, TreeseedWorkflowCiMode, TreeseedWorkflowDevInput, TreeseedWorkflowExecutionMode, TreeseedWorkflowOperationId, TreeseedWorkflowResult, TreeseedWorkflowWorktreeMode, TreeseedReleaseCandidateMode, TreeseedProofInput } from '../workflow.js';
12
13
  type WorkflowWrite = NonNullable<TreeseedWorkflowContext['write']>;
13
14
  type WorkflowStatePayload = ReturnType<typeof resolveTreeseedWorkflowState>;
@@ -845,6 +846,7 @@ type StageRepoPlan = {
845
846
  name: string;
846
847
  path: string;
847
848
  kind: 'root' | 'managed';
849
+ repoKind?: TreeseedManagedRepository['kind'];
848
850
  sourceBranch: string;
849
851
  targetBranch: typeof STAGING_BRANCH;
850
852
  remoteSourceExists: boolean;
@@ -957,6 +959,12 @@ export declare function workflowRelease(helpers: WorkflowOperationHelpers, input
957
959
  sceneArtifacts: "full" | "screenshots";
958
960
  releaseImageRefs: Record<string, string>;
959
961
  localCleanup: import("../workflow-support.js").TreeseedLocalCleanupReport | null;
962
+ releaseHelperRepos: {
963
+ name: string;
964
+ kind: import("../operations/services/managed-repositories.js").TreeseedManagedRepositoryKind;
965
+ path: string;
966
+ remote: string | null;
967
+ }[];
960
968
  autoResumeCandidate: {
961
969
  runId: string;
962
970
  branch: string | null;
@@ -1045,6 +1053,30 @@ export declare function workflowRelease(helpers: WorkflowOperationHelpers, input
1045
1053
  workspaceUnlink: import("../operations/services/workspace-dependency-mode.js").WorkspaceDependencyModeReport;
1046
1054
  };
1047
1055
  packageReleases: Record<string, unknown>[];
1056
+ managedHelperReleases: {
1057
+ status: string;
1058
+ repos: {
1059
+ name: string;
1060
+ kind: import("../operations/services/managed-repositories.js").TreeseedManagedRepositoryKind;
1061
+ path: string;
1062
+ stagingHead: string;
1063
+ promotion: {
1064
+ targetBranch: string;
1065
+ expectedBefore: string | null;
1066
+ commitSha: string;
1067
+ pushed: boolean;
1068
+ verified: boolean;
1069
+ };
1070
+ backMerge: {
1071
+ status: string;
1072
+ merged: boolean;
1073
+ repoName: string;
1074
+ sourceBranch: string;
1075
+ targetBranch: string;
1076
+ commitSha: string;
1077
+ };
1078
+ }[];
1079
+ };
1048
1080
  rootRelease: {
1049
1081
  name: string;
1050
1082
  version: string;
@@ -1248,6 +1280,12 @@ export declare function workflowRelease(helpers: WorkflowOperationHelpers, input
1248
1280
  sceneArtifacts: "full" | "screenshots";
1249
1281
  releaseImageRefs: Record<string, string>;
1250
1282
  localCleanup: import("../workflow-support.js").TreeseedLocalCleanupReport | null;
1283
+ releaseHelperRepos: {
1284
+ name: string;
1285
+ kind: import("../operations/services/managed-repositories.js").TreeseedManagedRepositoryKind;
1286
+ path: string;
1287
+ remote: string | null;
1288
+ }[];
1251
1289
  freshArchivedRuns: never[];
1252
1290
  autoResumeCandidate: {
1253
1291
  runId: string;
@@ -2265,6 +2265,28 @@ function backMergeProductionIntoStaging(repoDir, repoName, message) {
2265
2265
  commitSha: headCommit(repoDir)
2266
2266
  };
2267
2267
  }
2268
+ function releaseHelperRepoToProduction(repo) {
2269
+ syncBranchWithOrigin(repo.dir, STAGING_BRANCH);
2270
+ if (!remoteBranchExists(repo.dir, STAGING_BRANCH)) {
2271
+ throw new Error(`${repo.name} has no origin/${STAGING_BRANCH} branch to release.`);
2272
+ }
2273
+ const stagingHead = remoteHeadCommit(repo.dir, STAGING_BRANCH);
2274
+ const promotion = promoteCommitToProductionBranch(repo.dir, stagingHead);
2275
+ const backMerge = backMergeProductionIntoStaging(repo.dir, repo.name, releaseAdminMessage({
2276
+ subject: `release: back-merge ${PRODUCTION_BRANCH} into ${STAGING_BRANCH}`,
2277
+ version: null,
2278
+ sourceRef: PRODUCTION_BRANCH,
2279
+ targetRef: STAGING_BRANCH
2280
+ }));
2281
+ return {
2282
+ name: repo.name,
2283
+ kind: repo.kind,
2284
+ path: repo.relativeDir,
2285
+ stagingHead,
2286
+ promotion,
2287
+ backMerge
2288
+ };
2289
+ }
2268
2290
  function backMergeRootProductionIntoStaging(root, syncPackageStagingHeads, options = {}) {
2269
2291
  const gitRoot = repoRoot(root);
2270
2292
  const commits = releaseHistoryCommits(gitRoot, STAGING_BRANCH, `origin/${PRODUCTION_BRANCH}`);
@@ -2309,6 +2331,27 @@ function releasePlanStableDependencyVersionMap(plannedRelease) {
2309
2331
  const stableDependencyVersions = plannedRelease.stableDependencyVersions && typeof plannedRelease.stableDependencyVersions === "object" && !Array.isArray(plannedRelease.stableDependencyVersions) ? plannedRelease.stableDependencyVersions : {};
2310
2332
  return new Map(Object.entries(stableDependencyVersions).map(([name, version]) => [name, String(version)]));
2311
2333
  }
2334
+ function collectReleaseHelperRepoBlockers(root) {
2335
+ const blockers = [];
2336
+ for (const repo of checkedOutReleaseHelperRepos(root)) {
2337
+ const branch = currentBranch(repo.dir) || null;
2338
+ if (hasMeaningfulChanges(repo.dir)) {
2339
+ blockers.push(`${repo.name} has uncommitted changes.`);
2340
+ }
2341
+ if (branch !== STAGING_BRANCH) {
2342
+ blockers.push(`${repo.name} is on ${branch ?? "(detached)"} instead of ${STAGING_BRANCH}.`);
2343
+ }
2344
+ try {
2345
+ originRemoteUrl(repo.dir);
2346
+ } catch {
2347
+ blockers.push(`${repo.name} has no readable origin remote.`);
2348
+ }
2349
+ if (!remoteBranchExists(repo.dir, STAGING_BRANCH)) {
2350
+ blockers.push(`${repo.name} has no origin/${STAGING_BRANCH} branch.`);
2351
+ }
2352
+ }
2353
+ return blockers;
2354
+ }
2312
2355
  function releasePlanPackageSelection(value) {
2313
2356
  const record = value && typeof value === "object" ? value : {};
2314
2357
  return {
@@ -5043,16 +5086,38 @@ function writeStageCandidateManifest(root, runId, manifest) {
5043
5086
  }
5044
5087
  return manifest;
5045
5088
  }
5046
- function checkedOutStagePackageRepos(root) {
5047
- return checkedOutManagedWorkflowRepos(root).filter((repo) => repo.kind === "package");
5089
+ function dedupeManagedReposByRemote(repos) {
5090
+ const seen = /* @__PURE__ */ new Set();
5091
+ const deduped = [];
5092
+ for (const repo of repos) {
5093
+ const key = repo.remoteUrl ? `remote:${repo.remoteUrl}` : `path:${repo.dir}`;
5094
+ if (seen.has(key)) continue;
5095
+ seen.add(key);
5096
+ deduped.push(repo);
5097
+ }
5098
+ return deduped;
5099
+ }
5100
+ function checkedOutStagePromotionRepos(root) {
5101
+ return dedupeManagedReposByRemote(checkedOutManagedWorkflowRepos(root).filter((repo) => repo.kind === "package" || repo.kind === "template" || repo.kind === "fixture"));
5102
+ }
5103
+ function checkedOutReleaseHelperRepos(root) {
5104
+ return dedupeManagedReposByRemote(checkedOutManagedWorkflowRepos(root).filter((repo) => repo.kind === "template" || repo.kind === "fixture"));
5105
+ }
5106
+ function syncAllCheckedOutReleaseHelperRepos(root, branchName) {
5107
+ for (const repo of checkedOutManagedWorkflowRepos(root).filter((entry) => entry.kind === "template" || entry.kind === "fixture")) {
5108
+ if (remoteBranchExists(repo.dir, branchName)) {
5109
+ syncBranchWithOrigin(repo.dir, branchName);
5110
+ }
5111
+ }
5048
5112
  }
5049
5113
  function buildStagePromotionPlan(root, branchName, input) {
5050
5114
  const gitRoot = repoRoot(root);
5051
5115
  const repos = [
5052
- ...checkedOutStagePackageRepos(root).map((repo) => ({
5116
+ ...checkedOutStagePromotionRepos(root).map((repo) => ({
5053
5117
  name: repo.name,
5054
5118
  path: repo.dir,
5055
5119
  kind: "managed",
5120
+ repoKind: repo.kind,
5056
5121
  sourceBranch: branchName,
5057
5122
  targetBranch: STAGING_BRANCH,
5058
5123
  remoteSourceExists: remoteBranchExists(repo.dir, branchName),
@@ -5149,6 +5214,7 @@ function createStageCandidateManifest(root, runId, branchName, plan, verificatio
5149
5214
  packages: packageRepos.map((repo) => ({
5150
5215
  name: repo.name,
5151
5216
  path: repo.path,
5217
+ repoKind: repo.repoKind,
5152
5218
  commit: headCommit(repo.path),
5153
5219
  remote: (() => {
5154
5220
  try {
@@ -5165,7 +5231,7 @@ function createStageCandidateManifest(root, runId, branchName, plan, verificatio
5165
5231
  }
5166
5232
  function cleanupStageSourceBranches(root, branchName, manifest) {
5167
5233
  const results = [];
5168
- for (const repo of checkedOutStagePackageRepos(root)) {
5234
+ for (const repo of checkedOutStagePromotionRepos(root)) {
5169
5235
  const manifestRepo = manifest.packages.find((entry) => entry.name === repo.name);
5170
5236
  if (!manifestRepo) continue;
5171
5237
  const remoteDeleted = deleteRemoteBranch(repo.dir, branchName);
@@ -5320,7 +5386,7 @@ ${currentBlockers.map((entry) => `- ${entry}`).join("\n")}`, {
5320
5386
  const mergeDown = await executeJournalStep(root, workflowRun.runId, "merge-staging-down", () => {
5321
5387
  const results = [];
5322
5388
  try {
5323
- for (const repo of checkedOutStagePackageRepos(root)) {
5389
+ for (const repo of checkedOutStagePromotionRepos(root)) {
5324
5390
  if (!remoteBranchExists(repo.dir, featureBranch)) {
5325
5391
  results.push({ name: repo.name, path: repo.dir, skipped: true, reason: "remote-branch-missing" });
5326
5392
  continue;
@@ -5622,6 +5688,7 @@ async function workflowRelease(helpers, input) {
5622
5688
  const rootRepo = createWorkspaceRootRepoReport(root);
5623
5689
  traceReleaseStartup("inspect package repos");
5624
5690
  const packageReports = createWorkspacePackageReports(root);
5691
+ const releaseHelperRepos = checkedOutReleaseHelperRepos(root);
5625
5692
  const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? null;
5626
5693
  traceReleaseStartup("check auto resume");
5627
5694
  const autoResumeRun = executionMode === "execute" && !explicitResumeRunId && input.fresh !== true ? findAutoResumableReleaseRun(root, session.branchName, rootRepo, packageReports, { archiveStale: false }) : null;
@@ -5653,6 +5720,7 @@ async function workflowRelease(helpers, input) {
5653
5720
  level,
5654
5721
  repairVersionLine: effectiveInput.repairVersionLine === true
5655
5722
  });
5723
+ blockers.push(...collectReleaseHelperRepoBlockers(root));
5656
5724
  const selectedVersions = releasePlanVersionMap(plannedRelease.plannedVersions);
5657
5725
  const releaseImageVersions = productionReleaseImageRefVersions(root, selectedVersions);
5658
5726
  const releaseImageRefs = productionReleaseImageRefEnv(releaseImageVersions);
@@ -5673,6 +5741,12 @@ async function workflowRelease(helpers, input) {
5673
5741
  sceneArtifacts: normalizeSceneArtifactsMode(effectiveInput.sceneArtifacts),
5674
5742
  releaseImageRefs,
5675
5743
  localCleanup,
5744
+ releaseHelperRepos: releaseHelperRepos.map((repo) => ({
5745
+ name: repo.name,
5746
+ kind: repo.kind,
5747
+ path: repo.relativeDir,
5748
+ remote: repo.remoteUrl
5749
+ })),
5676
5750
  freshArchivedRuns: [],
5677
5751
  autoResumeCandidate: planAutoResumeRun ? {
5678
5752
  runId: planAutoResumeRun.runId,
@@ -5740,6 +5814,14 @@ ${blockers.join("\n")}`, {
5740
5814
  resumable: true
5741
5815
  };
5742
5816
  }),
5817
+ {
5818
+ id: "release-helper-repos",
5819
+ description: "Promote starter templates and shared fixture repositories from staging to production",
5820
+ repoName: rootRepo.name,
5821
+ repoPath: rootRepo.path,
5822
+ branch: STAGING_BRANCH,
5823
+ resumable: true
5824
+ },
5743
5825
  { id: "verify-published-artifacts", description: "Verify immutable registry artifacts exist after publish workflows", repoName: rootRepo.name, repoPath: rootRepo.path, branch: PRODUCTION_BRANCH, resumable: true },
5744
5826
  { id: "production-package-deploy-workflows", description: "Wait for production package deploy workflows before live verification", repoName: rootRepo.name, repoPath: rootRepo.path, branch: PRODUCTION_BRANCH, resumable: true },
5745
5827
  { id: "production-hosting", description: "Reconcile and live-verify production hosted resources before root deploy", repoName: rootRepo.name, repoPath: rootRepo.path, branch: PRODUCTION_BRANCH, resumable: true },
@@ -5861,6 +5943,11 @@ ${rendered}`);
5861
5943
  });
5862
5944
  packageReleases.push(packageRelease);
5863
5945
  }
5946
+ const managedHelperReleases = await executeJournalStep(root, workflowRun.runId, "release-helper-repos", () => {
5947
+ const releases = releaseHelperRepos.map((repo) => releaseHelperRepoToProduction(repo));
5948
+ syncAllCheckedOutReleaseHelperRepos(root, STAGING_BRANCH);
5949
+ return { status: "completed", repos: releases };
5950
+ });
5864
5951
  const publishedArtifacts = await executeJournalStep(root, workflowRun.runId, "verify-published-artifacts", () => verifyPublishedReleaseArtifacts(selectedVersions));
5865
5952
  const productionPackageDeployWorkflows = await executeJournalStep(root, workflowRun.runId, "production-package-deploy-workflows", () => {
5866
5953
  const deployGates = productionPackageDeployGates(root, allVersions);
@@ -5950,6 +6037,7 @@ ${rendered}`);
5950
6037
  workspaceUnlink,
5951
6038
  releaseMetadata,
5952
6039
  packageReleases,
6040
+ managedHelperReleases,
5953
6041
  rootRelease,
5954
6042
  publishWait: publishWait.workflowGates,
5955
6043
  publishedArtifacts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.12.42",
3
+ "version": "0.12.43",
4
4
  "description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {