@mean-weasel/lineage 0.1.22 → 0.1.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.
Files changed (221) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/MANUAL_INSTALL_DOGFOOD.md +1 -0
  3. package/README.md +45 -5
  4. package/dist/cli/lineage-dev.js +612 -37
  5. package/dist/cli/lineage-dev.js.map +4 -4
  6. package/dist/cli/lineage-preview.js +612 -37
  7. package/dist/cli/lineage-preview.js.map +4 -4
  8. package/dist/cli/lineage.js +612 -37
  9. package/dist/cli/lineage.js.map +4 -4
  10. package/dist/docs-site/404.html +41 -0
  11. package/dist/docs-site/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js +1 -0
  12. package/dist/docs-site/_astro/Search.astro_astro_type_script_index_0_lang.x2pVooPA.js +1 -0
  13. package/dist/docs-site/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js +1 -0
  14. package/dist/docs-site/_astro/common.DarLW4Vf.css +3 -0
  15. package/dist/docs-site/_astro/ec.0vx5m.js +3 -0
  16. package/dist/docs-site/_astro/ec.w36nc.css +1 -0
  17. package/dist/docs-site/_astro/page.LAbJoB63.js +1 -0
  18. package/dist/docs-site/_astro/print.ehPL0gv-.css +1 -0
  19. package/dist/docs-site/_astro/starlight-toc.CTCQXEge.js +1 -0
  20. package/dist/docs-site/_astro/ui-core.DWIqd25E.js +2 -0
  21. package/dist/docs-site/concepts/agent-claims-handoffs/index.html +99 -0
  22. package/dist/docs-site/concepts/attempts-current-version/index.html +86 -0
  23. package/dist/docs-site/concepts/branches-vs-rerolls/index.html +114 -0
  24. package/dist/docs-site/concepts/projects-workspaces-assets/index.html +89 -0
  25. package/dist/docs-site/concepts/selections-next-variations/index.html +86 -0
  26. package/dist/docs-site/index.html +92 -0
  27. package/dist/docs-site/integrations/cloud-storage/index.html +92 -0
  28. package/dist/docs-site/integrations/image-generation/index.html +93 -0
  29. package/dist/docs-site/integrations/index.html +112 -0
  30. package/dist/docs-site/integrations/social-scheduling/index.html +89 -0
  31. package/dist/docs-site/operations/backup-recovery/index.html +87 -0
  32. package/dist/docs-site/operations/channels/index.html +97 -0
  33. package/dist/docs-site/operations/local-first-data/index.html +90 -0
  34. package/dist/docs-site/operations/profiles-database-identity/index.html +104 -0
  35. package/dist/docs-site/operations/troubleshooting/index.html +89 -0
  36. package/dist/docs-site/pagefind/fragment/en_1126abb.pf_fragment +0 -0
  37. package/dist/docs-site/pagefind/fragment/en_19b3f6c.pf_fragment +0 -0
  38. package/dist/docs-site/pagefind/fragment/en_31aa611.pf_fragment +0 -0
  39. package/dist/docs-site/pagefind/fragment/en_38817cc.pf_fragment +0 -0
  40. package/dist/docs-site/pagefind/fragment/en_4184ad1.pf_fragment +0 -0
  41. package/dist/docs-site/pagefind/fragment/en_41db543.pf_fragment +0 -0
  42. package/dist/docs-site/pagefind/fragment/en_469f895.pf_fragment +0 -0
  43. package/dist/docs-site/pagefind/fragment/en_56e45b6.pf_fragment +0 -0
  44. package/dist/docs-site/pagefind/fragment/en_56fe7ca.pf_fragment +0 -0
  45. package/dist/docs-site/pagefind/fragment/en_5be5e44.pf_fragment +0 -0
  46. package/dist/docs-site/pagefind/fragment/en_603c2be.pf_fragment +0 -0
  47. package/dist/docs-site/pagefind/fragment/en_66b3118.pf_fragment +0 -0
  48. package/dist/docs-site/pagefind/fragment/en_6a19c31.pf_fragment +0 -0
  49. package/dist/docs-site/pagefind/fragment/en_74e9307.pf_fragment +0 -0
  50. package/dist/docs-site/pagefind/fragment/en_761fab1.pf_fragment +0 -0
  51. package/dist/docs-site/pagefind/fragment/en_7c9d5a2.pf_fragment +0 -0
  52. package/dist/docs-site/pagefind/fragment/en_818744b.pf_fragment +0 -0
  53. package/dist/docs-site/pagefind/fragment/en_8269752.pf_fragment +0 -0
  54. package/dist/docs-site/pagefind/fragment/en_88b38c1.pf_fragment +0 -0
  55. package/dist/docs-site/pagefind/fragment/en_98d9b16.pf_fragment +0 -0
  56. package/dist/docs-site/pagefind/fragment/en_a3d636e.pf_fragment +0 -0
  57. package/dist/docs-site/pagefind/fragment/en_b15ecf0.pf_fragment +0 -0
  58. package/dist/docs-site/pagefind/fragment/en_bd3e194.pf_fragment +0 -0
  59. package/dist/docs-site/pagefind/fragment/en_bfb13e9.pf_fragment +0 -0
  60. package/dist/docs-site/pagefind/fragment/en_d0fca9e.pf_fragment +0 -0
  61. package/dist/docs-site/pagefind/fragment/en_d49ed00.pf_fragment +0 -0
  62. package/dist/docs-site/pagefind/fragment/en_e0786b9.pf_fragment +0 -0
  63. package/dist/docs-site/pagefind/fragment/en_e298dee.pf_fragment +0 -0
  64. package/dist/docs-site/pagefind/fragment/en_ee82be6.pf_fragment +0 -0
  65. package/dist/docs-site/pagefind/fragment/en_f4db9e2.pf_fragment +0 -0
  66. package/dist/docs-site/pagefind/fragment/en_f6179a8.pf_fragment +0 -0
  67. package/dist/docs-site/pagefind/index/en_7b1363c.pf_index +0 -0
  68. package/dist/docs-site/pagefind/pagefind-component-ui.css +1509 -0
  69. package/dist/docs-site/pagefind/pagefind-component-ui.js +56 -0
  70. package/dist/docs-site/pagefind/pagefind-entry.json +1 -0
  71. package/dist/docs-site/pagefind/pagefind-highlight.js +1070 -0
  72. package/dist/docs-site/pagefind/pagefind-modular-ui.css +214 -0
  73. package/dist/docs-site/pagefind/pagefind-modular-ui.js +8 -0
  74. package/dist/docs-site/pagefind/pagefind-ui.css +1 -0
  75. package/dist/docs-site/pagefind/pagefind-ui.js +2 -0
  76. package/dist/docs-site/pagefind/pagefind-worker.js +6 -0
  77. package/dist/docs-site/pagefind/pagefind.en_4e77743529.pf_meta +0 -0
  78. package/dist/docs-site/pagefind/pagefind.js +6 -0
  79. package/dist/docs-site/pagefind/wasm.en.pagefind +0 -0
  80. package/dist/docs-site/pagefind/wasm.unknown.pagefind +0 -0
  81. package/dist/docs-site/reference/cli/index.html +95 -0
  82. package/dist/docs-site/reference/interface-guide/index.html +86 -0
  83. package/dist/docs-site/reference/release-notes/index.html +80 -0
  84. package/dist/docs-site/reference/settings/index.html +86 -0
  85. package/dist/docs-site/reference/terminology/index.html +94 -0
  86. package/dist/docs-site/sitemap-0.xml +1 -0
  87. package/dist/docs-site/sitemap-index.xml +1 -0
  88. package/dist/docs-site/start-here/example-projects/index.html +88 -0
  89. package/dist/docs-site/start-here/first-workspace/index.html +91 -0
  90. package/dist/docs-site/start-here/installation-first-run/index.html +92 -0
  91. package/dist/docs-site/start-here/what-is-lineage/index.html +96 -0
  92. package/dist/docs-site/workflows/back-up-approved-assets/index.html +88 -0
  93. package/dist/docs-site/workflows/content-batches/index.html +88 -0
  94. package/dist/docs-site/workflows/continue-new-agent-session/index.html +89 -0
  95. package/dist/docs-site/workflows/create-grow-lineage/index.html +89 -0
  96. package/dist/docs-site/workflows/generate-import-variations/index.html +92 -0
  97. package/dist/docs-site/workflows/restore-earlier-attempt/index.html +87 -0
  98. package/dist/docs-site/workflows/review-approve-assets/index.html +87 -0
  99. package/dist/pages/.nojekyll +0 -0
  100. package/dist/pages/assets/agent-shared-state-DOfpTLiw.webp +0 -0
  101. package/dist/pages/assets/agent-to-canvas-DtIu_cPq.mp4 +0 -0
  102. package/dist/{web/assets/app-B-fAyNsU.css → pages/assets/app-CuBsKrS-.css} +1 -1
  103. package/dist/pages/assets/app-TEMX-pOn.js +50 -0
  104. package/dist/pages/assets/attempt-history-D6wdic6n.webp +0 -0
  105. package/dist/pages/assets/branching-tree-B8I5_S6D.png +0 -0
  106. package/dist/pages/assets/canvas-cli-DHBacq7r.png +0 -0
  107. package/dist/pages/assets/hero-agent-sync-D8ibW83E.mp4 +0 -0
  108. package/dist/pages/assets/hero-agent-sync-poster-zBB3KpbO.png +0 -0
  109. package/dist/pages/assets/hero-board-DG3ED4AW.webp +0 -0
  110. package/dist/pages/assets/hero-lineage-growth-Bsl9tiq-.mp4 +0 -0
  111. package/dist/pages/assets/hero-trace-connections-Bb52ABBc.mp4 +0 -0
  112. package/dist/pages/assets/human-selection-BdDBDE-I.webp +0 -0
  113. package/dist/pages/assets/human-to-agent-D1z92ZO9.mp4 +0 -0
  114. package/dist/pages/assets/jsx-runtime-DAFSxiwi.js +8 -0
  115. package/dist/pages/assets/landing-MwOFmHoi.js +2 -0
  116. package/dist/pages/assets/landing-wy4L-yBm.css +1 -0
  117. package/dist/pages/assets/reroll-history-DQcEH42R.mp4 +0 -0
  118. package/dist/pages/docs/404.html +41 -0
  119. package/dist/pages/docs/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js +1 -0
  120. package/dist/pages/docs/_astro/Search.astro_astro_type_script_index_0_lang.x2pVooPA.js +1 -0
  121. package/dist/pages/docs/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js +1 -0
  122. package/dist/pages/docs/_astro/common.DarLW4Vf.css +3 -0
  123. package/dist/pages/docs/_astro/ec.0vx5m.js +3 -0
  124. package/dist/pages/docs/_astro/ec.w36nc.css +1 -0
  125. package/dist/pages/docs/_astro/page.LAbJoB63.js +1 -0
  126. package/dist/pages/docs/_astro/print.ehPL0gv-.css +1 -0
  127. package/dist/pages/docs/_astro/starlight-toc.CTCQXEge.js +1 -0
  128. package/dist/pages/docs/_astro/ui-core.DWIqd25E.js +2 -0
  129. package/dist/pages/docs/concepts/agent-claims-handoffs/index.html +99 -0
  130. package/dist/pages/docs/concepts/attempts-current-version/index.html +86 -0
  131. package/dist/pages/docs/concepts/branches-vs-rerolls/index.html +114 -0
  132. package/dist/pages/docs/concepts/projects-workspaces-assets/index.html +89 -0
  133. package/dist/pages/docs/concepts/selections-next-variations/index.html +86 -0
  134. package/dist/pages/docs/index.html +92 -0
  135. package/dist/pages/docs/integrations/cloud-storage/index.html +92 -0
  136. package/dist/pages/docs/integrations/image-generation/index.html +93 -0
  137. package/dist/pages/docs/integrations/index.html +112 -0
  138. package/dist/pages/docs/integrations/social-scheduling/index.html +89 -0
  139. package/dist/pages/docs/operations/backup-recovery/index.html +87 -0
  140. package/dist/pages/docs/operations/channels/index.html +97 -0
  141. package/dist/pages/docs/operations/local-first-data/index.html +90 -0
  142. package/dist/pages/docs/operations/profiles-database-identity/index.html +104 -0
  143. package/dist/pages/docs/operations/troubleshooting/index.html +89 -0
  144. package/dist/pages/docs/pagefind/fragment/en_1126abb.pf_fragment +0 -0
  145. package/dist/pages/docs/pagefind/fragment/en_19b3f6c.pf_fragment +0 -0
  146. package/dist/pages/docs/pagefind/fragment/en_31aa611.pf_fragment +0 -0
  147. package/dist/pages/docs/pagefind/fragment/en_38817cc.pf_fragment +0 -0
  148. package/dist/pages/docs/pagefind/fragment/en_4184ad1.pf_fragment +0 -0
  149. package/dist/pages/docs/pagefind/fragment/en_41db543.pf_fragment +0 -0
  150. package/dist/pages/docs/pagefind/fragment/en_469f895.pf_fragment +0 -0
  151. package/dist/pages/docs/pagefind/fragment/en_56e45b6.pf_fragment +0 -0
  152. package/dist/pages/docs/pagefind/fragment/en_56fe7ca.pf_fragment +0 -0
  153. package/dist/pages/docs/pagefind/fragment/en_5be5e44.pf_fragment +0 -0
  154. package/dist/pages/docs/pagefind/fragment/en_603c2be.pf_fragment +0 -0
  155. package/dist/pages/docs/pagefind/fragment/en_66b3118.pf_fragment +0 -0
  156. package/dist/pages/docs/pagefind/fragment/en_6a19c31.pf_fragment +0 -0
  157. package/dist/pages/docs/pagefind/fragment/en_74e9307.pf_fragment +0 -0
  158. package/dist/pages/docs/pagefind/fragment/en_761fab1.pf_fragment +0 -0
  159. package/dist/pages/docs/pagefind/fragment/en_7c9d5a2.pf_fragment +0 -0
  160. package/dist/pages/docs/pagefind/fragment/en_818744b.pf_fragment +0 -0
  161. package/dist/pages/docs/pagefind/fragment/en_8269752.pf_fragment +0 -0
  162. package/dist/pages/docs/pagefind/fragment/en_88b38c1.pf_fragment +0 -0
  163. package/dist/pages/docs/pagefind/fragment/en_98d9b16.pf_fragment +0 -0
  164. package/dist/pages/docs/pagefind/fragment/en_a3d636e.pf_fragment +0 -0
  165. package/dist/pages/docs/pagefind/fragment/en_b15ecf0.pf_fragment +0 -0
  166. package/dist/pages/docs/pagefind/fragment/en_bd3e194.pf_fragment +0 -0
  167. package/dist/pages/docs/pagefind/fragment/en_bfb13e9.pf_fragment +0 -0
  168. package/dist/pages/docs/pagefind/fragment/en_d0fca9e.pf_fragment +0 -0
  169. package/dist/pages/docs/pagefind/fragment/en_d49ed00.pf_fragment +0 -0
  170. package/dist/pages/docs/pagefind/fragment/en_e0786b9.pf_fragment +0 -0
  171. package/dist/pages/docs/pagefind/fragment/en_e298dee.pf_fragment +0 -0
  172. package/dist/pages/docs/pagefind/fragment/en_ee82be6.pf_fragment +0 -0
  173. package/dist/pages/docs/pagefind/fragment/en_f4db9e2.pf_fragment +0 -0
  174. package/dist/pages/docs/pagefind/fragment/en_f6179a8.pf_fragment +0 -0
  175. package/dist/pages/docs/pagefind/index/en_7b1363c.pf_index +0 -0
  176. package/dist/pages/docs/pagefind/pagefind-component-ui.css +1509 -0
  177. package/dist/pages/docs/pagefind/pagefind-component-ui.js +56 -0
  178. package/dist/pages/docs/pagefind/pagefind-entry.json +1 -0
  179. package/dist/pages/docs/pagefind/pagefind-highlight.js +1070 -0
  180. package/dist/pages/docs/pagefind/pagefind-modular-ui.css +214 -0
  181. package/dist/pages/docs/pagefind/pagefind-modular-ui.js +8 -0
  182. package/dist/pages/docs/pagefind/pagefind-ui.css +1 -0
  183. package/dist/pages/docs/pagefind/pagefind-ui.js +2 -0
  184. package/dist/pages/docs/pagefind/pagefind-worker.js +6 -0
  185. package/dist/pages/docs/pagefind/pagefind.en_4e77743529.pf_meta +0 -0
  186. package/dist/pages/docs/pagefind/pagefind.js +6 -0
  187. package/dist/pages/docs/pagefind/wasm.en.pagefind +0 -0
  188. package/dist/pages/docs/pagefind/wasm.unknown.pagefind +0 -0
  189. package/dist/pages/docs/reference/cli/index.html +95 -0
  190. package/dist/pages/docs/reference/interface-guide/index.html +86 -0
  191. package/dist/pages/docs/reference/release-notes/index.html +80 -0
  192. package/dist/pages/docs/reference/settings/index.html +86 -0
  193. package/dist/pages/docs/reference/terminology/index.html +94 -0
  194. package/dist/pages/docs/sitemap-0.xml +1 -0
  195. package/dist/pages/docs/sitemap-index.xml +1 -0
  196. package/dist/pages/docs/start-here/example-projects/index.html +88 -0
  197. package/dist/pages/docs/start-here/first-workspace/index.html +91 -0
  198. package/dist/pages/docs/start-here/installation-first-run/index.html +92 -0
  199. package/dist/pages/docs/start-here/what-is-lineage/index.html +96 -0
  200. package/dist/pages/docs/workflows/back-up-approved-assets/index.html +88 -0
  201. package/dist/pages/docs/workflows/content-batches/index.html +88 -0
  202. package/dist/pages/docs/workflows/continue-new-agent-session/index.html +89 -0
  203. package/dist/pages/docs/workflows/create-grow-lineage/index.html +89 -0
  204. package/dist/pages/docs/workflows/generate-import-variations/index.html +92 -0
  205. package/dist/pages/docs/workflows/restore-earlier-attempt/index.html +87 -0
  206. package/dist/pages/docs/workflows/review-approve-assets/index.html +87 -0
  207. package/dist/pages/index.html +42 -0
  208. package/dist/pages/landing/index.html +42 -0
  209. package/dist/pages/lineage-showcase-cover-16x9.png +0 -0
  210. package/dist/runtime-build.json +5 -5
  211. package/dist/server.js +431 -46
  212. package/dist/server.js.map +4 -4
  213. package/dist/web/assets/app-CuBsKrS-.css +1 -0
  214. package/dist/web/assets/app-TEMX-pOn.js +50 -0
  215. package/dist/web/assets/landing-MwOFmHoi.js +2 -0
  216. package/dist/web/index.html +3 -3
  217. package/dist/web/landing/index.html +26 -3
  218. package/dist/web/lineage-showcase-cover-16x9.png +0 -0
  219. package/package.json +11 -2
  220. package/dist/web/assets/app-D-7R33Mc.js +0 -16
  221. package/dist/web/assets/landing-DIjBBL33.js +0 -2
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/lineageCli.ts
4
- import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
4
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
5
5
  import { randomUUID as randomUUID4 } from "node:crypto";
6
6
  import { homedir as homedir2, platform as platform2 } from "node:os";
7
- import { dirname as dirname6, join as join10, resolve as resolve8 } from "node:path";
7
+ import { dirname as dirname6, join as join10, resolve as resolve10 } from "node:path";
8
8
  import { spawn } from "node:child_process";
9
9
  import { fileURLToPath as fileURLToPath3 } from "node:url";
10
10
 
@@ -648,6 +648,7 @@ var lineageProfileDoctorSchemaVersion = "lineage.profile_doctor.v1";
648
648
  var lineageProfileCloneReceiptSchemaVersion = "lineage.profile_clone_receipt.v1";
649
649
  var lineageProfileAssetsCloneReceiptSchemaVersion = "lineage.profile_assets_clone_receipt.v1";
650
650
  var lineageProfileRuntimeRepinReceiptSchemaVersion = "lineage.profile_runtime_repin_receipt.v1";
651
+ var lineageProfileRuntimeUpgradeReceiptSchemaVersion = "lineage.profile_runtime_upgrade_receipt.v1";
651
652
 
652
653
  // src/server/nodeSqlite.ts
653
654
  import { createRequire } from "node:module";
@@ -848,7 +849,9 @@ function initializeLineageProfile(profileId, serviceOrigin, runtime, confirmWrit
848
849
  expected_runtime: {
849
850
  channel: runtime.channel,
850
851
  code_fingerprint: runtime.code.fingerprint.toLowerCase(),
851
- code_origin: runtime.code.origin
852
+ code_origin: runtime.code.origin,
853
+ ...runtime.gitSha ? { git_sha: runtime.gitSha } : {},
854
+ version: runtime.version
852
855
  },
853
856
  profile_id: profileId,
854
857
  schema_version: lineageProfileSchemaVersion,
@@ -914,7 +917,9 @@ function initializeLineageProfile(profileId, serviceOrigin, runtime, confirmWrit
914
917
  runtime: {
915
918
  channel: runtime.channel,
916
919
  code_fingerprint: runtime.code.fingerprint,
917
- code_origin: runtime.code.origin
920
+ code_origin: runtime.code.origin,
921
+ ...runtime.gitSha ? { git_sha: runtime.gitSha } : {},
922
+ version: runtime.version
918
923
  },
919
924
  schema_version: lineageProfileInitSchemaVersion,
920
925
  service_origin: profile.service_origin
@@ -950,6 +955,229 @@ function runtimeRepinInvariant(profile) {
950
955
  function sameFileIdentity(left, right) {
951
956
  return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
952
957
  }
958
+ function fsyncDirectory(path) {
959
+ const directoryFd = openSync(path, "r");
960
+ try {
961
+ fsyncSync(directoryFd);
962
+ } finally {
963
+ closeSync(directoryFd);
964
+ }
965
+ }
966
+ function parseStableVersion(value, label) {
967
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(value);
968
+ if (!match) throw new Error(`${label} must be a valid stable SemVer without a prerelease, found ${value}`);
969
+ return match.slice(1).map(BigInt);
970
+ }
971
+ function compareStableVersions(left, right) {
972
+ for (let index = 0; index < left.length; index += 1) {
973
+ if (left[index] !== right[index]) return left[index] < right[index] ? -1 : 1;
974
+ }
975
+ return 0;
976
+ }
977
+ function profileUpgradePreflight(profile, runtime) {
978
+ const doctor = doctorLineageProfile(profile.manifest_path, runtime);
979
+ const allowedRuntimeMismatches = /* @__PURE__ */ new Set(["runtime_code", "runtime_version", "runtime_git_sha"]);
980
+ const failures = doctor.checks.filter((check) => check.status === "fail" && !allowedRuntimeMismatches.has(check.id));
981
+ if (failures.length > 0) {
982
+ throw new Error(`Stable profile runtime upgrade requires a healthy production profile before changing its pin: ${failures.map((check) => `${check.id}: ${check.message}`).join("; ")}`);
983
+ }
984
+ }
985
+ function upgradeRuntimeIdentity(fingerprint, options = {}) {
986
+ return {
987
+ channel: "stable",
988
+ code_fingerprint: fingerprint,
989
+ code_origin: "package",
990
+ ...options.codeRoot ? { code_root: options.codeRoot } : {},
991
+ ...options.gitSha ? { git_sha: options.gitSha } : {},
992
+ ...options.version ? { version: options.version } : {}
993
+ };
994
+ }
995
+ function upgradeLineageStableProfileRuntime(selector, runtime, confirmWrite, testHooks = {}) {
996
+ if (!confirmWrite) throw new Error("Stable profile runtime upgrade requires --confirm-write");
997
+ if (runtime.channel !== "stable") throw new Error(`Stable profile runtime upgrade requires stable code, not ${runtime.channel}`);
998
+ if (!runtime.code?.verified) throw new Error("Stable profile runtime upgrade requires a verified package runtime");
999
+ if (runtime.code.channel !== "stable") throw new Error(`Stable profile runtime upgrade requires stable code identity, not ${runtime.code.channel}`);
1000
+ if (runtime.code.origin !== "package") throw new Error(`Stable profile runtime upgrade requires package code, not ${runtime.code.origin}`);
1001
+ if (!runtime.code.fingerprint || !/^[a-f0-9]{64}$/i.test(runtime.code.fingerprint)) {
1002
+ throw new Error("Stable profile runtime upgrade requires a valid executing code fingerprint");
1003
+ }
1004
+ if (runtime.code.package_version !== runtime.version) {
1005
+ throw new Error(`Stable profile runtime upgrade version ${runtime.version} does not match package version ${runtime.code.package_version || "missing"}`);
1006
+ }
1007
+ const targetVersion = parseStableVersion(runtime.version, "Stable profile runtime upgrade target version");
1008
+ const executingRoot = realpathSync(runtime.code.root);
1009
+ if (!statSync3(executingRoot).isDirectory()) {
1010
+ throw new Error(`Stable profile runtime upgrade code root is not a directory: ${executingRoot}`);
1011
+ }
1012
+ const profile = resolveLineageProfile(selector);
1013
+ if (profile.environment !== "production") {
1014
+ throw new Error(`Stable profile runtime upgrade requires a production profile, not ${profile.environment}`);
1015
+ }
1016
+ if (profile.expected_runtime?.channel !== "stable") {
1017
+ throw new Error("Stable profile runtime upgrade requires an existing stable channel pin");
1018
+ }
1019
+ if (profile.expected_runtime.code_origin !== "package") {
1020
+ throw new Error("Stable profile runtime upgrade requires an existing package origin pin");
1021
+ }
1022
+ const previousFingerprint = profile.expected_runtime.code_fingerprint;
1023
+ if (!previousFingerprint) throw new Error("Stable profile runtime upgrade requires an existing code fingerprint pin");
1024
+ const previousVersion = profile.expected_runtime.version;
1025
+ if (previousVersion) {
1026
+ const comparison = compareStableVersions(targetVersion, parseStableVersion(previousVersion, "Stable profile runtime upgrade source version"));
1027
+ if (previousFingerprint === runtime.code.fingerprint && previousVersion !== runtime.version) {
1028
+ throw new Error(`Stable profile runtime upgrade found one code fingerprint claiming conflicting versions ${previousVersion} and ${runtime.version}`);
1029
+ }
1030
+ if (comparison === 0 && previousFingerprint !== runtime.code.fingerprint) {
1031
+ throw new Error(`Stable profile runtime upgrade refuses same-version fingerprint change for ${runtime.version}`);
1032
+ }
1033
+ if (comparison < 0) {
1034
+ throw new Error(`Stable profile runtime upgrade refuses downgrade from ${previousVersion} to ${runtime.version}`);
1035
+ }
1036
+ if (comparison > 0 && previousFingerprint === runtime.code.fingerprint) {
1037
+ throw new Error(`Stable profile runtime upgrade requires a new code fingerprint for version ${runtime.version}`);
1038
+ }
1039
+ }
1040
+ if (previousFingerprint === runtime.code.fingerprint && profile.expected_runtime.git_sha && runtime.gitSha && profile.expected_runtime.git_sha !== runtime.gitSha) {
1041
+ throw new Error("Stable profile runtime upgrade found one code fingerprint claiming conflicting Git revisions");
1042
+ }
1043
+ profileUpgradePreflight(profile, runtime);
1044
+ const manifestPath = profile.manifest_path;
1045
+ const profileDirectory = dirname3(manifestPath);
1046
+ assertOwnerOnlyPath(profileDirectory, "profile directory");
1047
+ const beforeStats = assertOwnerOnlyPath(manifestPath, "manifest");
1048
+ const beforeText = readFileSync3(manifestPath, "utf8");
1049
+ const beforeHash = sha256(beforeText);
1050
+ let raw;
1051
+ try {
1052
+ raw = JSON.parse(beforeText);
1053
+ } catch (error) {
1054
+ throw new Error(`Could not parse profile manifest ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1055
+ }
1056
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("Profile manifest must be a JSON object");
1057
+ const nextExpectedRuntime = {
1058
+ channel: "stable",
1059
+ code_fingerprint: runtime.code.fingerprint.toLowerCase(),
1060
+ code_origin: "package",
1061
+ ...runtime.gitSha ? { git_sha: runtime.gitSha } : {},
1062
+ version: runtime.version
1063
+ };
1064
+ const afterText = `${JSON.stringify({ ...raw, expected_runtime: nextExpectedRuntime }, null, 2)}
1065
+ `;
1066
+ const afterHash = sha256(afterText);
1067
+ const normalizedTargetMatches = previousFingerprint === nextExpectedRuntime.code_fingerprint && previousVersion === nextExpectedRuntime.version && profile.expected_runtime.git_sha === nextExpectedRuntime.git_sha;
1068
+ const newRuntime = upgradeRuntimeIdentity(runtime.code.fingerprint, {
1069
+ codeRoot: executingRoot,
1070
+ gitSha: runtime.gitSha,
1071
+ version: runtime.version
1072
+ });
1073
+ const result = {
1074
+ asset_root: profile.asset_root,
1075
+ changed: !normalizedTargetMatches,
1076
+ database_path: profile.database_path,
1077
+ manifest_after_sha256: normalizedTargetMatches ? beforeHash : afterHash,
1078
+ manifest_before_sha256: beforeHash,
1079
+ manifest_path: manifestPath,
1080
+ new_runtime: newRuntime,
1081
+ post_upgrade_doctor_ok: true,
1082
+ previous_runtime: upgradeRuntimeIdentity(previousFingerprint, {
1083
+ gitSha: profile.expected_runtime.git_sha,
1084
+ version: previousVersion
1085
+ }),
1086
+ profile_fingerprint: profile.profile_fingerprint,
1087
+ profile_id: profile.profile_id,
1088
+ schema_version: lineageProfileRuntimeUpgradeReceiptSchemaVersion,
1089
+ service_origin: profile.service_origin,
1090
+ upgrade_kind: normalizedTargetMatches ? "idempotent" : previousVersion ? "versioned" : "legacy_unversioned"
1091
+ };
1092
+ if (!result.changed) {
1093
+ const doctor = doctorLineageProfile(manifestPath, runtime);
1094
+ if (!doctor.ok) throw new Error("Stable profile runtime upgrade idempotency check did not pass profile doctor");
1095
+ return result;
1096
+ }
1097
+ const operationId = randomUUID();
1098
+ const temporaryPath = join4(profileDirectory, `.profile.runtime-upgrade-${operationId}.tmp`);
1099
+ const rollbackPath = join4(profileDirectory, `.profile.runtime-upgrade-${operationId}.rollback`);
1100
+ let temporaryExists = false;
1101
+ let rollbackExists = false;
1102
+ let published = false;
1103
+ let preserveRollback = false;
1104
+ try {
1105
+ writeFileSync2(temporaryPath, afterText, { encoding: "utf8", flag: "wx", mode: 384 });
1106
+ temporaryExists = true;
1107
+ chmodSync2(temporaryPath, 384);
1108
+ const temporaryFd = openSync(temporaryPath, "r");
1109
+ try {
1110
+ fsyncSync(temporaryFd);
1111
+ } finally {
1112
+ closeSync(temporaryFd);
1113
+ }
1114
+ const preparedReplacement = resolveLineageProfile(temporaryPath);
1115
+ if (runtimeRepinInvariant(preparedReplacement) !== runtimeRepinInvariant(profile)) {
1116
+ throw new Error("Stable profile runtime upgrade would change immutable profile routing identity");
1117
+ }
1118
+ testHooks.beforePublish?.();
1119
+ const currentStats = assertOwnerOnlyPath(manifestPath, "manifest");
1120
+ const currentText = readFileSync3(manifestPath, "utf8");
1121
+ if (!sameFileIdentity(beforeStats, currentStats) || sha256(currentText) !== beforeHash) {
1122
+ throw new Error("Profile manifest changed while stable runtime upgrade was being prepared; refusing replacement");
1123
+ }
1124
+ linkSync(manifestPath, rollbackPath);
1125
+ rollbackExists = true;
1126
+ chmodSync2(rollbackPath, 384);
1127
+ const rollbackStats = assertOwnerOnlyPath(rollbackPath, "manifest");
1128
+ const linkedCurrentStats = assertOwnerOnlyPath(manifestPath, "manifest");
1129
+ if (!sameFileIdentity(rollbackStats, linkedCurrentStats) || readFileSync3(rollbackPath, "utf8") !== beforeText) {
1130
+ throw new Error("Stable profile runtime upgrade could not preserve an exact rollback preimage");
1131
+ }
1132
+ fsyncDirectory(profileDirectory);
1133
+ renameSync(temporaryPath, manifestPath);
1134
+ temporaryExists = false;
1135
+ published = true;
1136
+ fsyncDirectory(profileDirectory);
1137
+ testHooks.afterPublish?.();
1138
+ const replacement = resolveLineageProfile(manifestPath);
1139
+ if (runtimeRepinInvariant(replacement) !== runtimeRepinInvariant(profile)) {
1140
+ throw new Error("Stable profile runtime upgrade changed immutable profile routing identity");
1141
+ }
1142
+ if (readFileSync3(manifestPath, "utf8") !== afterText) {
1143
+ throw new Error("Stable profile runtime upgrade replacement bytes do not match the prepared manifest");
1144
+ }
1145
+ const doctor = doctorLineageProfile(manifestPath, runtime);
1146
+ if (!doctor.ok) {
1147
+ const failures = doctor.checks.filter((check) => check.status === "fail").map((check) => `${check.id}: ${check.message}`).join("; ");
1148
+ throw new Error(`Stable profile runtime upgrade failed post-upgrade profile doctor: ${failures}`);
1149
+ }
1150
+ rmSync(rollbackPath);
1151
+ rollbackExists = false;
1152
+ fsyncDirectory(profileDirectory);
1153
+ return result;
1154
+ } catch (error) {
1155
+ if (published && rollbackExists) {
1156
+ try {
1157
+ if (readFileSync3(manifestPath, "utf8") !== afterText) {
1158
+ throw new Error("published manifest no longer matches the prepared replacement", { cause: error });
1159
+ }
1160
+ renameSync(rollbackPath, manifestPath);
1161
+ rollbackExists = false;
1162
+ fsyncDirectory(profileDirectory);
1163
+ const restoredText = readFileSync3(manifestPath, "utf8");
1164
+ if (restoredText !== beforeText || sha256(restoredText) !== beforeHash) {
1165
+ throw new Error("restored manifest does not match the exact preimage", { cause: error });
1166
+ }
1167
+ } catch (rollbackError) {
1168
+ preserveRollback = true;
1169
+ throw new Error(
1170
+ `Stable profile runtime upgrade failed and automatic rollback could not be proven: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`,
1171
+ { cause: rollbackError }
1172
+ );
1173
+ }
1174
+ }
1175
+ throw error;
1176
+ } finally {
1177
+ if (temporaryExists) rmSync(temporaryPath, { force: true });
1178
+ if (rollbackExists && !preserveRollback) rmSync(rollbackPath, { force: true });
1179
+ }
1180
+ }
953
1181
  function repinLineageDevelopmentProfileRuntime(selector, checkoutRoot, runtime, confirmWrite) {
954
1182
  if (!confirmWrite) throw new Error("Profile runtime repin requires --confirm-write");
955
1183
  if (runtime.channel !== "dev") throw new Error(`Profile runtime repin requires dev code, not ${runtime.channel}`);
@@ -1824,6 +2052,21 @@ function lineageDb() {
1824
2052
  where status = 'pending';
1825
2053
  create index if not exists asset_reroll_requests_root_status
1826
2054
  on asset_reroll_requests(project_id, root_asset_id, status, created_at);
2055
+ create table if not exists asset_social_marks (
2056
+ id text primary key,
2057
+ project_id text not null references projects(id),
2058
+ root_asset_id text not null references assets(id),
2059
+ asset_id text not null references assets(id),
2060
+ notes text,
2061
+ marked_by text not null,
2062
+ marked_at text not null,
2063
+ unmarked_by text,
2064
+ unmarked_at text,
2065
+ updated_at text not null,
2066
+ unique(project_id, root_asset_id, asset_id)
2067
+ );
2068
+ create index if not exists asset_social_marks_root_active
2069
+ on asset_social_marks(project_id, root_asset_id, unmarked_at, marked_at);
1827
2070
  create table if not exists asset_reviews (
1828
2071
  asset_id text primary key references assets(id),
1829
2072
  review_state text not null check (review_state in ('unreviewed', 'approved', 'needs_revision', 'rejected', 'ignored')),
@@ -2810,7 +3053,7 @@ function validateAgentClaimForWrite(fields) {
2810
3053
  }
2811
3054
 
2812
3055
  // src/server/assetLineage.ts
2813
- import { join as join7 } from "node:path";
3056
+ import { isAbsolute as isAbsolute2, join as join7, resolve as resolve5 } from "node:path";
2814
3057
 
2815
3058
  // src/server/assetLineageSelection.ts
2816
3059
  function selectedRows(database, project, root) {
@@ -3515,6 +3758,11 @@ function collectAssets(project, source) {
3515
3758
  }
3516
3759
  return assets;
3517
3760
  }
3761
+ function absoluteLocalPath(localPath) {
3762
+ if (!localPath) return void 0;
3763
+ if (isAbsolute2(localPath)) return localPath;
3764
+ return resolve5(repoRoot, ".asset-scratch", localPath);
3765
+ }
3518
3766
  function upsertProject(database, project) {
3519
3767
  const timestamp = nowIso();
3520
3768
  database.prepare(`
@@ -3910,6 +4158,19 @@ function getLineageSnapshot(project, assetId) {
3910
4158
  const request = rerollRequestFrom(row);
3911
4159
  return [request.node_asset_id, withRerollTask(request, lineageTasksByNode.get(request.node_asset_id)?.reroll)];
3912
4160
  }));
4161
+ const hasSocialMarks = database.prepare("select 1 from sqlite_master where type = 'table' and name = 'asset_social_marks'").get() !== void 0;
4162
+ const socialMarkRows = ids.length > 0 && hasSocialMarks ? database.prepare(`select * from asset_social_marks where project_id = ? and root_asset_id = ? and unmarked_at is null and asset_id in (${placeholders})`).all(project, root, ...ids) : [];
4163
+ const socialMarksByNode = new Map(socialMarkRows.map((row) => [String(row.asset_id), {
4164
+ active: true,
4165
+ asset_id: String(row.asset_id),
4166
+ id: String(row.id),
4167
+ marked_at: String(row.marked_at),
4168
+ marked_by: String(row.marked_by),
4169
+ notes: rowString(row.notes),
4170
+ project_id: String(row.project_id),
4171
+ root_asset_id: String(row.root_asset_id),
4172
+ updated_at: String(row.updated_at)
4173
+ }]));
3913
4174
  const childIds = new Set(edges.map((edge) => edge.parent_asset_id));
3914
4175
  const selectedIds = new Set(selected.map((row) => row.asset_id));
3915
4176
  const selections = selected.map((row) => ({
@@ -3929,6 +4190,7 @@ function getLineageSnapshot(project, assetId) {
3929
4190
  const lineageTasks = lineageTasksByNode.get(row.asset_id);
3930
4191
  return {
3931
4192
  ...node,
4193
+ absolute_path: absoluteLocalPath(row.local_path),
3932
4194
  attempt_count: attempts.length,
3933
4195
  current_attempt: currentAttempt,
3934
4196
  is_latest: !childIds.has(row.asset_id),
@@ -3937,6 +4199,7 @@ function getLineageSnapshot(project, assetId) {
3937
4199
  preview_url: canPreviewLocally(row.media_type, previewPath) ? localPreviewUrl(project, previewPath) : void 0,
3938
4200
  reroll_request: rerollsByNode.get(row.asset_id),
3939
4201
  selection_note: nodeSelection?.notes,
4202
+ social_mark: socialMarksByNode.get(row.asset_id),
3940
4203
  user_selected: selectedIds.has(row.asset_id)
3941
4204
  };
3942
4205
  });
@@ -4359,9 +4622,246 @@ function linkSelectedLineageChild(project, fields) {
4359
4622
  };
4360
4623
  }
4361
4624
 
4625
+ // src/server/assetSocialMarks.ts
4626
+ import { accessSync, constants, existsSync as existsSync8 } from "node:fs";
4627
+ import { isAbsolute as isAbsolute3, resolve as resolve6 } from "node:path";
4628
+ function markFromRow(row) {
4629
+ return {
4630
+ active: row.unmarked_at === null,
4631
+ asset_id: row.asset_id,
4632
+ id: row.id,
4633
+ marked_at: row.marked_at,
4634
+ marked_by: row.marked_by,
4635
+ notes: row.notes || void 0,
4636
+ project_id: row.project_id,
4637
+ root_asset_id: row.root_asset_id,
4638
+ unmarked_at: row.unmarked_at || void 0,
4639
+ unmarked_by: row.unmarked_by || void 0,
4640
+ updated_at: row.updated_at
4641
+ };
4642
+ }
4643
+ function requireCanonicalSnapshot(project, rootAssetId) {
4644
+ const snapshot = getLineageSnapshot(project, rootAssetId);
4645
+ if (snapshot.root_asset_id !== rootAssetId) {
4646
+ throw new LineageError(`Asset ${rootAssetId} is not a canonical lineage canvas root`, 400);
4647
+ }
4648
+ return snapshot;
4649
+ }
4650
+ function resolveVisibleNode(snapshot, asset) {
4651
+ const exactId = snapshot.nodes.find((node) => node.asset_id === asset);
4652
+ if (exactId) return exactId;
4653
+ const title = asset.trim().toLocaleLowerCase();
4654
+ const matches = snapshot.nodes.filter((node) => node.title.trim().toLocaleLowerCase() === title);
4655
+ if (matches.length > 1) throw new LineageError(`Asset title "${asset}" matches multiple visible nodes; use an exact asset ID.`, 409);
4656
+ if (matches.length === 1) return matches[0];
4657
+ throw new LineageError(`Asset ${asset} is not visible in lineage canvas ${snapshot.root_asset_id}`, 404);
4658
+ }
4659
+ function rootChannel(snapshot) {
4660
+ return snapshot.nodes.find((node) => node.asset_id === snapshot.root_asset_id)?.channel;
4661
+ }
4662
+ function requireActor(actor, field) {
4663
+ const normalized = actor.trim();
4664
+ if (!normalized) throw new LineageError(`Social mark ${field} is required`);
4665
+ return normalized;
4666
+ }
4667
+ function socialMarkId(project, rootAssetId, assetId) {
4668
+ return `${project}:${rootAssetId}:social:${assetId}`;
4669
+ }
4670
+ function activeRow(project, rootAssetId, assetId) {
4671
+ const database = lineageDb();
4672
+ try {
4673
+ return database.prepare(`
4674
+ select * from asset_social_marks
4675
+ where project_id = ? and root_asset_id = ? and asset_id = ? and unmarked_at is null
4676
+ `).get(project, rootAssetId, assetId);
4677
+ } finally {
4678
+ database.close();
4679
+ }
4680
+ }
4681
+ function mutationResponse(project, rootAssetId, active, mark, dryRun) {
4682
+ return {
4683
+ active,
4684
+ ...dryRun ? { dryRun } : {},
4685
+ ...mark ? { mark } : {},
4686
+ ok: true,
4687
+ snapshot: getLineageSnapshot(project, rootAssetId)
4688
+ };
4689
+ }
4690
+ function markAssetSocial(project, fields) {
4691
+ const snapshot = requireCanonicalSnapshot(project, fields.rootAssetId);
4692
+ const node = resolveVisibleNode(snapshot, fields.asset);
4693
+ const markedBy = requireActor(fields.markedBy, "markedBy");
4694
+ requireLineageWorkspaceClaimForWrite({
4695
+ channel: rootChannel(snapshot),
4696
+ claimToken: fields.claimToken,
4697
+ confirmWrite: fields.confirmWrite,
4698
+ project,
4699
+ rootAssetId: fields.rootAssetId,
4700
+ writeKind: "social_mark"
4701
+ });
4702
+ const existing = activeRow(project, fields.rootAssetId, node.asset_id);
4703
+ if (existing) return mutationResponse(project, fields.rootAssetId, true, markFromRow(existing), fields.confirmWrite ? void 0 : true);
4704
+ const timestamp = nowIso();
4705
+ const preview = {
4706
+ active: true,
4707
+ asset_id: node.asset_id,
4708
+ id: socialMarkId(project, fields.rootAssetId, node.asset_id),
4709
+ marked_at: timestamp,
4710
+ marked_by: markedBy,
4711
+ notes: fields.notes?.trim() || void 0,
4712
+ project_id: project,
4713
+ root_asset_id: fields.rootAssetId,
4714
+ updated_at: timestamp
4715
+ };
4716
+ if (!fields.confirmWrite) return mutationResponse(project, fields.rootAssetId, true, preview, true);
4717
+ const database = lineageDb();
4718
+ try {
4719
+ database.prepare(`
4720
+ insert into asset_social_marks (
4721
+ id, project_id, root_asset_id, asset_id, notes, marked_by, marked_at,
4722
+ unmarked_by, unmarked_at, updated_at
4723
+ ) values (?, ?, ?, ?, ?, ?, ?, null, null, ?)
4724
+ on conflict(project_id, root_asset_id, asset_id) do update set
4725
+ notes = excluded.notes,
4726
+ marked_by = excluded.marked_by,
4727
+ marked_at = excluded.marked_at,
4728
+ unmarked_by = null,
4729
+ unmarked_at = null,
4730
+ updated_at = excluded.updated_at
4731
+ `).run(
4732
+ preview.id,
4733
+ project,
4734
+ fields.rootAssetId,
4735
+ node.asset_id,
4736
+ preview.notes || null,
4737
+ markedBy,
4738
+ timestamp,
4739
+ timestamp
4740
+ );
4741
+ } finally {
4742
+ database.close();
4743
+ }
4744
+ return mutationResponse(project, fields.rootAssetId, true, markFromRow(activeRow(project, fields.rootAssetId, node.asset_id)));
4745
+ }
4746
+ function unmarkAssetSocial(project, fields) {
4747
+ const snapshot = requireCanonicalSnapshot(project, fields.rootAssetId);
4748
+ const node = resolveVisibleNode(snapshot, fields.asset);
4749
+ const unmarkedBy = requireActor(fields.unmarkedBy, "unmarkedBy");
4750
+ requireLineageWorkspaceClaimForWrite({
4751
+ channel: rootChannel(snapshot),
4752
+ claimToken: fields.claimToken,
4753
+ confirmWrite: fields.confirmWrite,
4754
+ project,
4755
+ rootAssetId: fields.rootAssetId,
4756
+ writeKind: "social_unmark"
4757
+ });
4758
+ const existing = activeRow(project, fields.rootAssetId, node.asset_id);
4759
+ if (!existing) return mutationResponse(project, fields.rootAssetId, false, void 0, fields.confirmWrite ? void 0 : true);
4760
+ const timestamp = nowIso();
4761
+ const preview = markFromRow({
4762
+ ...existing,
4763
+ unmarked_at: timestamp,
4764
+ unmarked_by: unmarkedBy,
4765
+ updated_at: timestamp
4766
+ });
4767
+ if (!fields.confirmWrite) return mutationResponse(project, fields.rootAssetId, false, preview, true);
4768
+ const database = lineageDb();
4769
+ try {
4770
+ database.prepare(`
4771
+ update asset_social_marks
4772
+ set unmarked_by = ?, unmarked_at = ?, updated_at = ?
4773
+ where project_id = ? and root_asset_id = ? and asset_id = ? and unmarked_at is null
4774
+ `).run(unmarkedBy, timestamp, timestamp, project, fields.rootAssetId, node.asset_id);
4775
+ } finally {
4776
+ database.close();
4777
+ }
4778
+ return mutationResponse(project, fields.rootAssetId, false, preview);
4779
+ }
4780
+ function absoluteLocalPath2(reference) {
4781
+ if (!reference) return void 0;
4782
+ if (isAbsolute3(reference)) return reference;
4783
+ const repoRelative = resolve6(repoRoot, reference);
4784
+ return existsSync8(repoRelative) || reference === ".asset-scratch" || reference.startsWith(".asset-scratch/") ? repoRelative : resolve6(repoRoot, ".asset-scratch", reference);
4785
+ }
4786
+ function isReadable(path) {
4787
+ try {
4788
+ accessSync(path, constants.R_OK);
4789
+ return true;
4790
+ } catch {
4791
+ return false;
4792
+ }
4793
+ }
4794
+ function listItem(project, rootAssetId, node, row) {
4795
+ const relativePath = node.current_attempt?.file_path || node.local_path;
4796
+ const absolutePath = absoluteLocalPath2(relativePath);
4797
+ const exists = absolutePath ? existsSync8(absolutePath) : false;
4798
+ const warnings = [];
4799
+ if (absolutePath && !exists) warnings.push(`Asset ${node.asset_id} local file is missing: ${absolutePath}`);
4800
+ if (absolutePath && exists && !isReadable(absolutePath)) {
4801
+ warnings.push(`Asset ${node.asset_id} local file is unreadable: ${absolutePath}. Check file permissions before handoff.`);
4802
+ }
4803
+ if (!absolutePath && node.s3_key) {
4804
+ warnings.push(`Asset ${node.asset_id} is only available in non-local storage; create a readable local copy before handoff.`);
4805
+ }
4806
+ if (!absolutePath && !node.s3_key) warnings.push(`Asset ${node.asset_id} has no local file or storage key.`);
4807
+ const base = `--project ${shellQuote(project)} --root ${shellQuote(rootAssetId)} --asset ${shellQuote(node.asset_id)}`;
4808
+ return {
4809
+ ...markFromRow(row),
4810
+ checksum_sha256: node.current_attempt?.checksum_sha256 || node.checksum_sha256,
4811
+ commands: {
4812
+ mark: lineageCliCommand(`social mark ${base} --confirm-write`),
4813
+ unmark: lineageCliCommand(`social unmark ${base} --confirm-write`)
4814
+ },
4815
+ current_attempt: node.current_attempt,
4816
+ local: {
4817
+ absolute_path: absolutePath,
4818
+ exists,
4819
+ relative_path: relativePath
4820
+ },
4821
+ media_type: node.media_type,
4822
+ s3: node.s3_key ? { key: node.s3_key } : void 0,
4823
+ source: node.source,
4824
+ title: node.title,
4825
+ warnings
4826
+ };
4827
+ }
4828
+ function listAssetSocialMarks(project, rootAssetId) {
4829
+ const snapshot = requireCanonicalSnapshot(project, rootAssetId);
4830
+ const database = lineageDb();
4831
+ let rows;
4832
+ try {
4833
+ const hasSocialMarks = database.prepare("select 1 from sqlite_master where type = 'table' and name = 'asset_social_marks'").get() !== void 0;
4834
+ rows = hasSocialMarks ? database.prepare(`
4835
+ select * from asset_social_marks
4836
+ where project_id = ? and root_asset_id = ? and unmarked_at is null
4837
+ order by marked_at, asset_id
4838
+ `).all(project, rootAssetId) : [];
4839
+ } finally {
4840
+ database.close();
4841
+ }
4842
+ const nodesById = new Map(snapshot.nodes.map((node) => [node.asset_id, node]));
4843
+ return {
4844
+ commands: {
4845
+ mark: lineageCliCommand(`social mark --project ${shellQuote(project)} --root ${shellQuote(rootAssetId)} --asset <asset-id> --confirm-write`)
4846
+ },
4847
+ fetchedAt: nowIso(),
4848
+ marks: rows.flatMap((row) => {
4849
+ const node = nodesById.get(row.asset_id);
4850
+ return node ? [listItem(project, rootAssetId, node, row)] : [];
4851
+ }),
4852
+ project,
4853
+ root_asset_id: rootAssetId,
4854
+ schema_version: "lineage.social_marks.v1",
4855
+ workspace: {
4856
+ id: lineageWorkspaceId(project, rootAssetId),
4857
+ root_asset_id: rootAssetId
4858
+ }
4859
+ };
4860
+ }
4861
+
4362
4862
  // src/server/generationReceipts.ts
4363
- import { existsSync as existsSync8, realpathSync as realpathSync2, statSync as statSync4 } from "node:fs";
4364
- import { relative as relative3, resolve as resolve5 } from "node:path";
4863
+ import { existsSync as existsSync9, realpathSync as realpathSync2, statSync as statSync4 } from "node:fs";
4864
+ import { relative as relative3, resolve as resolve7 } from "node:path";
4365
4865
  import { randomUUID as randomUUID3 } from "node:crypto";
4366
4866
 
4367
4867
  // src/shared/generationOutputManifest.ts
@@ -4896,8 +5396,8 @@ function isPathInside(child, parent) {
4896
5396
  return Boolean(rel) && !rel.startsWith("..") && !rel.startsWith("/");
4897
5397
  }
4898
5398
  function scratchCandidate(file) {
4899
- const scratchRoot = resolve5(repoRoot, ".asset-scratch");
4900
- const candidate = file.startsWith(".asset-scratch/") || resolve5(file).startsWith(scratchRoot) ? resolve5(repoRoot, file) : resolve5(scratchRoot, file);
5399
+ const scratchRoot = resolve7(repoRoot, ".asset-scratch");
5400
+ const candidate = file.startsWith(".asset-scratch/") || resolve7(file).startsWith(scratchRoot) ? resolve7(repoRoot, file) : resolve7(scratchRoot, file);
4901
5401
  if (!isPathInside(candidate, scratchRoot)) throw new GenerationReceiptError(`Import file must be under .asset-scratch: ${file}`);
4902
5402
  return { candidate, scratchRoot };
4903
5403
  }
@@ -4907,7 +5407,7 @@ function resolveScratchManifestPath(file) {
4907
5407
  }
4908
5408
  function resolveScratchFile(file) {
4909
5409
  const { candidate, scratchRoot } = scratchCandidate(file);
4910
- if (!existsSync8(candidate)) throw new GenerationReceiptError(`Missing import file: ${file}`, 404);
5410
+ if (!existsSync9(candidate)) throw new GenerationReceiptError(`Missing import file: ${file}`, 404);
4911
5411
  const realScratchRoot = realpathSync2(scratchRoot);
4912
5412
  const realCandidate = realpathSync2(candidate);
4913
5413
  if (!isPathInside(realCandidate, realScratchRoot)) throw new GenerationReceiptError(`Import file must be under .asset-scratch: ${file}`);
@@ -5151,8 +5651,8 @@ function importImageRerollOutput(project = defaultProject, fields) {
5151
5651
 
5152
5652
  // src/server/lineageSelectionPacket.ts
5153
5653
  import { createHash as createHash5 } from "node:crypto";
5154
- import { existsSync as existsSync9, statSync as statSync5 } from "node:fs";
5155
- import { isAbsolute as isAbsolute2, resolve as resolve6 } from "node:path";
5654
+ import { existsSync as existsSync10, statSync as statSync5 } from "node:fs";
5655
+ import { isAbsolute as isAbsolute4, resolve as resolve8 } from "node:path";
5156
5656
  var LineageSelectionPacketError = class extends Error {
5157
5657
  constructor(message, warnings = [], errors = [], diagnostics = []) {
5158
5658
  super(message);
@@ -5198,13 +5698,13 @@ function resolveWorkspace(project, options) {
5198
5698
  }
5199
5699
  function resolveLocalReference(reference) {
5200
5700
  if (!reference) return void 0;
5201
- if (isAbsolute2(reference)) return reference;
5202
- const repoRelative = resolve6(repoRoot, reference);
5203
- if (existsSync9(repoRelative)) return repoRelative;
5204
- return resolve6(repoRoot, ".asset-scratch", reference);
5701
+ if (isAbsolute4(reference)) return reference;
5702
+ const repoRelative = resolve8(repoRoot, reference);
5703
+ if (existsSync10(repoRelative)) return repoRelative;
5704
+ return resolve8(repoRoot, ".asset-scratch", reference);
5205
5705
  }
5206
5706
  function fileSize(path) {
5207
- if (!path || !existsSync9(path)) return void 0;
5707
+ if (!path || !existsSync10(path)) return void 0;
5208
5708
  try {
5209
5709
  return statSync5(path).size;
5210
5710
  } catch {
@@ -5232,7 +5732,7 @@ function currentAttemptFor(node) {
5232
5732
  function assetForNode(node, catalogAsset, warnings) {
5233
5733
  const localReference = catalogAsset?.local?.absolute_path || node.current_attempt?.file_path || node.local_path || catalogAsset?.local?.relative_path;
5234
5734
  const absolutePath = catalogAsset?.local?.absolute_path || resolveLocalReference(localReference);
5235
- const localExists = absolutePath ? existsSync9(absolutePath) : false;
5735
+ const localExists = absolutePath ? existsSync10(absolutePath) : false;
5236
5736
  const hasLocalClaim = Boolean(absolutePath || node.local_path || catalogAsset?.local?.relative_path);
5237
5737
  const s3Key = catalogAsset?.s3?.key || node.s3_key;
5238
5738
  const hasS3 = Boolean(s3Key);
@@ -5389,7 +5889,7 @@ function assetForNodeV2(node, visibleCatalogAsset, catalogById, warnings, errors
5389
5889
  const mediaCatalogAsset = currentAttemptCatalogAsset || (isVisibleNodeAttempt ? visibleCatalogAsset : void 0);
5390
5890
  const localReference = currentAttempt.file_path || mediaCatalogAsset?.local?.absolute_path || mediaCatalogAsset?.local?.relative_path || (isVisibleNodeAttempt ? node.local_path : void 0);
5391
5891
  const absolutePath = resolveLocalReference(localReference);
5392
- const localExists = absolutePath ? existsSync9(absolutePath) : false;
5892
+ const localExists = absolutePath ? existsSync10(absolutePath) : false;
5393
5893
  const hasLocalClaim = Boolean(localReference);
5394
5894
  const s3Key = mediaCatalogAsset?.s3?.key || (isVisibleNodeAttempt ? node.s3_key : void 0);
5395
5895
  const hasS3 = Boolean(s3Key);
@@ -5655,8 +6155,8 @@ function getLineageSelectionPacket(project, options = {}) {
5655
6155
  // src/server/runtimeInfo.ts
5656
6156
  import { createHash as createHash6 } from "node:crypto";
5657
6157
  import { spawnSync as spawnSync2 } from "node:child_process";
5658
- import { existsSync as existsSync10, lstatSync as lstatSync3, readFileSync as readFileSync5, readdirSync as readdirSync3, readlinkSync, realpathSync as realpathSync3, statSync as statSync6 } from "node:fs";
5659
- import { dirname as dirname5, isAbsolute as isAbsolute3, join as join9, resolve as resolve7 } from "node:path";
6158
+ import { existsSync as existsSync11, lstatSync as lstatSync3, readFileSync as readFileSync5, readdirSync as readdirSync3, readlinkSync, realpathSync as realpathSync3, statSync as statSync6 } from "node:fs";
6159
+ import { dirname as dirname5, isAbsolute as isAbsolute5, join as join9, resolve as resolve9 } from "node:path";
5660
6160
  import { fileURLToPath as fileURLToPath2 } from "node:url";
5661
6161
 
5662
6162
  // src/shared/runtimeInfoTypes.ts
@@ -5675,7 +6175,7 @@ function isLineagePackageRoot(root) {
5675
6175
  }
5676
6176
  function executingCodeRoot() {
5677
6177
  const moduleDirectory = dirname5(fileURLToPath2(import.meta.url));
5678
- const candidates = [resolve7(moduleDirectory, "../.."), resolve7(moduleDirectory, "..")];
6178
+ const candidates = [resolve9(moduleDirectory, "../.."), resolve9(moduleDirectory, "..")];
5679
6179
  const root = candidates.find(isLineagePackageRoot);
5680
6180
  if (!root) throw new Error(`Unable to derive Lineage code root from executing module ${fileURLToPath2(import.meta.url)}`);
5681
6181
  return canonicalRoot(root);
@@ -5688,7 +6188,7 @@ function canonicalRoot(root) {
5688
6188
  try {
5689
6189
  return realpathSync3(root);
5690
6190
  } catch {
5691
- return resolve7(root);
6191
+ return resolve9(root);
5692
6192
  }
5693
6193
  }
5694
6194
  function packageInfo(root = codeRoot) {
@@ -5788,7 +6288,7 @@ function readInstallReceipt(path, errors) {
5788
6288
  return void 0;
5789
6289
  }
5790
6290
  let receipt;
5791
- const receiptPath = resolve7(path);
6291
+ const receiptPath = resolve9(path);
5792
6292
  try {
5793
6293
  receipt = JSON.parse(readFileSync5(receiptPath, "utf8"));
5794
6294
  } catch (error) {
@@ -5796,7 +6296,7 @@ function readInstallReceipt(path, errors) {
5796
6296
  return void 0;
5797
6297
  }
5798
6298
  if (receipt.schema_version !== lineageRuntimeInstallSchemaVersion) errors.push(`Unsupported runtime install receipt schema: ${String(receipt.schema_version)}`);
5799
- if (!isAbsolute3(receipt.package_root || "")) errors.push("Runtime install receipt package_root must be absolute");
6299
+ if (!isAbsolute5(receipt.package_root || "")) errors.push("Runtime install receipt package_root must be absolute");
5800
6300
  if (!receipt.package_integrity?.startsWith("sha512-")) errors.push("Runtime install receipt package integrity is invalid");
5801
6301
  if (receipt.package_source !== "registry" && receipt.package_source !== "local") errors.push("Runtime install receipt package source is invalid");
5802
6302
  if (typeof receipt.package_spec !== "string" || !receipt.package_spec) errors.push("Runtime install receipt package spec is invalid");
@@ -5877,8 +6377,8 @@ function packageCodeIdentity(root, channel, info, receiptPath) {
5877
6377
  function getLineageCodeIdentity(channel, options = {}) {
5878
6378
  const root = canonicalRoot(options.root || codeRoot);
5879
6379
  const info = packageInfo(root);
5880
- if (existsSync10(join9(root, ".git"))) return checkoutCodeIdentity(root, channel, info.version);
5881
- if (existsSync10(join9(root, "package.json"))) {
6380
+ if (existsSync11(join9(root, ".git"))) return checkoutCodeIdentity(root, channel, info.version);
6381
+ if (existsSync11(join9(root, "package.json"))) {
5882
6382
  return packageCodeIdentity(root, channel, info, options.receiptPath ?? process.env.LINEAGE_RUNTIME_RECEIPT);
5883
6383
  }
5884
6384
  const errors = [`Code root has neither checkout metadata nor package.json: ${root}`];
@@ -5917,7 +6417,7 @@ function getLineageRuntimeInfo(options = {}) {
5917
6417
  const dbPath = options.dbPath || lineageDbPath();
5918
6418
  const channel = normalizeRuntimeChannel(options.channel || process.env.LINEAGE_CHANNEL || process.env.LINEAGE_RELEASE_CHANNEL);
5919
6419
  const code = options.code || getLineageCodeIdentity(channel);
5920
- const databaseInfo = { exists: existsSync10(dbPath), path: dbPath };
6420
+ const databaseInfo = { exists: existsSync11(dbPath), path: dbPath };
5921
6421
  const schema = { migration_keys: [] };
5922
6422
  const serviceInstanceId = process.env.LINEAGE_SERVICE_INSTANCE_ID;
5923
6423
  const launcherPid = process.env.LINEAGE_LAUNCHER_PID ? Number(process.env.LINEAGE_LAUNCHER_PID) : void 0;
@@ -6074,7 +6574,7 @@ var signalExitCodes = {
6074
6574
  SIGTERM: 143
6075
6575
  };
6076
6576
  function packageRoot2() {
6077
- return resolve8(dirname6(fileURLToPath3(import.meta.url)), "..", "..");
6577
+ return resolve10(dirname6(fileURLToPath3(import.meta.url)), "..", "..");
6078
6578
  }
6079
6579
  function packageVersion() {
6080
6580
  try {
@@ -6113,7 +6613,7 @@ function readOptions(args, name) {
6113
6613
  }
6114
6614
  function readJsonFile(path, label) {
6115
6615
  try {
6116
- return JSON.parse(readFileSync6(resolve8(path), "utf8"));
6616
+ return JSON.parse(readFileSync6(resolve10(path), "utf8"));
6117
6617
  } catch (error) {
6118
6618
  throw new Error(`${label} must be a readable JSON file: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
6119
6619
  }
@@ -6157,7 +6657,7 @@ function resolveStartOptions(config, args) {
6157
6657
  };
6158
6658
  }
6159
6659
  function resolveCliAssetRoot(args) {
6160
- return resolve8(readOption(args, "--asset-root") || process.env.LINEAGE_ASSET_ROOT || packageRoot);
6660
+ return resolve10(readOption(args, "--asset-root") || process.env.LINEAGE_ASSET_ROOT || packageRoot);
6161
6661
  }
6162
6662
  function configureCliAssetRoot(args) {
6163
6663
  const assetRoot = resolveCliAssetRoot(args);
@@ -6189,10 +6689,10 @@ function prepareCliProfile(config, args) {
6189
6689
  if (hasOption(args, "--db")) throw new Error("A named profile cannot be combined with --db");
6190
6690
  if (hasOption(args, "--asset-root")) throw new Error("A named profile cannot be combined with --asset-root");
6191
6691
  const profile = resolveLineageProfile(selector);
6192
- if (process.env.LINEAGE_DB && resolve8(process.env.LINEAGE_DB) !== profile.database_path) {
6692
+ if (process.env.LINEAGE_DB && resolve10(process.env.LINEAGE_DB) !== profile.database_path) {
6193
6693
  throw new Error(`Profile ${profile.profile_id} database_path conflicts with LINEAGE_DB`);
6194
6694
  }
6195
- if (process.env.LINEAGE_ASSET_ROOT && resolve8(process.env.LINEAGE_ASSET_ROOT) !== profile.asset_root) {
6695
+ if (process.env.LINEAGE_ASSET_ROOT && resolve10(process.env.LINEAGE_ASSET_ROOT) !== profile.asset_root) {
6196
6696
  throw new Error(`Profile ${profile.profile_id} asset_root conflicts with LINEAGE_ASSET_ROOT`);
6197
6697
  }
6198
6698
  assertProfileChannel(profile, config.channel);
@@ -6221,6 +6721,7 @@ Usage:
6221
6721
  ${config.binName} profile clone --source-db <snapshot-source> --target-profile <id-or-manifest> --confirm-write [--json]
6222
6722
  ${config.binName} profile clone-assets --source-asset-root <path> --target-profile <id-or-manifest> --confirm-write [--json]
6223
6723
  ${config.binName} profile repin-runtime --profile <development-profile> --checkout-root <path> --confirm-write [--json]
6724
+ ${config.binName} profile upgrade-runtime --profile <production-profile> --confirm-write [--json]
6224
6725
  ${config.binName} runtime info [--json]
6225
6726
  ${config.binName} runtime doctor [--json]
6226
6727
  ${config.binName} next [--project <project>] [--root <asset-id>] [--db <path>] [--json]
@@ -6237,6 +6738,9 @@ Usage:
6237
6738
  ${config.binName} reroll cancel --root <asset-id> --target <asset-id> [--project <project>] [--confirm-write] [--db <path>] [--json]
6238
6739
  ${config.binName} reroll plan --root <asset-id> --target <asset-id> --prompt <text> [--project <project>] [--db <path>] [--json]
6239
6740
  ${config.binName} reroll import --job-id <job-id> --file <scratch-file> --confirm-write [--project <project>] [--db <path>] [--json]
6741
+ ${config.binName} social list --project <project> --root <asset-id> [--db <path>] [--json]
6742
+ ${config.binName} social mark --project <project> --root <asset-id> --asset <asset-id-or-exact-title> --confirm-write [--actor <actor>] [--notes <text>] [--claim-token <claim-id.secret>] [--db <path>] [--json]
6743
+ ${config.binName} social unmark --project <project> --root <asset-id> --asset <asset-id-or-exact-title> --confirm-write [--actor <actor>] [--claim-token <claim-id.secret>] [--db <path>] [--json]
6240
6744
  ${config.binName} tasks list --root <asset-id> [--project <project>] [--db <path>] [--json]
6241
6745
  ${config.binName} tasks inspect --task <task-id> [--project <project>] [--db <path>] [--json]
6242
6746
  ${config.binName} tasks claim --task <task-id> --agent-name <name> [--project <project>] [--db <path>] [--json]
@@ -6293,7 +6797,7 @@ function lineageServiceIdentityErrors(runtime, expected) {
6293
6797
  if (runtime.channel !== expected.channel) errors.push(`channel ${runtime.channel} != ${expected.channel}`);
6294
6798
  if (!runtime.code?.verified) errors.push("service code identity is not verified");
6295
6799
  if (runtime.code?.fingerprint !== expected.code_fingerprint) errors.push(`code fingerprint ${runtime.code?.fingerprint || "missing"} != ${expected.code_fingerprint}`);
6296
- if (resolve8(runtime.database.path) !== resolve8(expected.database_path)) errors.push(`database ${runtime.database.path} != ${expected.database_path}`);
6800
+ if (resolve10(runtime.database.path) !== resolve10(expected.database_path)) errors.push(`database ${runtime.database.path} != ${expected.database_path}`);
6297
6801
  if (runtime.service?.instance_id !== expected.instance_id) errors.push(`service instance ${runtime.service?.instance_id || "missing"} != ${expected.instance_id}`);
6298
6802
  if (runtime.service?.launcher_pid !== expected.launcher_pid) errors.push(`launcher pid ${runtime.service?.launcher_pid || "missing"} != ${expected.launcher_pid}`);
6299
6803
  if (expected.profile) {
@@ -6388,7 +6892,7 @@ function runLineageDataCommand(command, args) {
6388
6892
  });
6389
6893
  const out = readOption(args, "--out");
6390
6894
  if (out) {
6391
- const outPath = resolve8(out);
6895
+ const outPath = resolve10(out);
6392
6896
  mkdirSync6(dirname6(outPath), { recursive: true });
6393
6897
  writeFileSync4(outPath, `${JSON.stringify(packet, null, 2)}
6394
6898
  `);
@@ -6499,6 +7003,35 @@ function runLineageDataCommand(command, args) {
6499
7003
  }
6500
7004
  throw new Error(`Unknown reroll command: ${subcommand}`);
6501
7005
  }
7006
+ if (command === "social") {
7007
+ const subcommand = positionalArgs(args)[0] || "";
7008
+ const explicitProject = readOption(args, "--project");
7009
+ const asset = readOption(args, "--asset");
7010
+ if (!explicitProject) throw new Error(`lineage social ${subcommand || "command"} requires --project`);
7011
+ if (!options.rootAssetId) throw new Error(`lineage social ${subcommand || "command"} requires --root`);
7012
+ if (subcommand === "list") return listAssetSocialMarks(explicitProject, options.rootAssetId);
7013
+ if (!asset) throw new Error(`lineage social ${subcommand || "command"} requires --asset`);
7014
+ if (subcommand === "mark") {
7015
+ return markAssetSocial(explicitProject, {
7016
+ asset,
7017
+ claimToken: options.claimToken,
7018
+ confirmWrite: options.confirmWrite,
7019
+ markedBy: readOption(args, "--actor") || "agent",
7020
+ notes: readOption(args, "--notes"),
7021
+ rootAssetId: options.rootAssetId
7022
+ });
7023
+ }
7024
+ if (subcommand === "unmark") {
7025
+ return unmarkAssetSocial(explicitProject, {
7026
+ asset,
7027
+ claimToken: options.claimToken,
7028
+ confirmWrite: options.confirmWrite,
7029
+ rootAssetId: options.rootAssetId,
7030
+ unmarkedBy: readOption(args, "--actor") || "agent"
7031
+ });
7032
+ }
7033
+ throw new Error(`Unknown social command: ${subcommand}`);
7034
+ }
6502
7035
  if (command === "tasks") {
6503
7036
  const subcommand = positionalArgs(args)[0] || "";
6504
7037
  const taskId = readOption(args, "--task");
@@ -6628,6 +7161,20 @@ function printDataResult(command, result, json) {
6628
7161
  return;
6629
7162
  }
6630
7163
  }
7164
+ if (command === "social" && result && typeof result === "object") {
7165
+ if ("marks" in result) {
7166
+ const listed = result;
7167
+ console.log(`${listed.marks.length} social-marked asset(s)`);
7168
+ for (const mark of listed.marks) {
7169
+ console.log(`${mark.asset_id}: ${mark.title}`);
7170
+ for (const warning of mark.warnings || []) console.log(`Warning: ${warning}`);
7171
+ }
7172
+ return;
7173
+ }
7174
+ const mutation = result;
7175
+ console.log(`${mutation.dryRun ? "Dry run: " : ""}${mutation.active ? "Marked" : "Unmarked"} ${mutation.mark?.asset_id || "asset"} for social`);
7176
+ return;
7177
+ }
6631
7178
  if (command === "tasks" && result && typeof result === "object") {
6632
7179
  if ("tasks" in result) {
6633
7180
  const listed = result;
@@ -6716,6 +7263,30 @@ function runLineageProfileCommand(config, command, args) {
6716
7263
  writerLease.release();
6717
7264
  }
6718
7265
  }
7266
+ if (command === "upgrade-runtime") {
7267
+ if (!args.includes("--confirm-write")) throw new Error("Profile runtime upgrade requires --confirm-write");
7268
+ for (const option of [
7269
+ "--checkout-root",
7270
+ "--code-fingerprint",
7271
+ "--code-root",
7272
+ "--from-code-fingerprint",
7273
+ "--git-sha",
7274
+ "--runtime-receipt",
7275
+ "--runtime-root",
7276
+ "--to-code-fingerprint",
7277
+ "--to-version",
7278
+ "--version"
7279
+ ]) {
7280
+ if (hasOption(args, option)) throw new Error(`Profile runtime upgrade derives its target from the executing stable package and does not accept ${option}`);
7281
+ }
7282
+ const profile = resolveLineageProfile(selector);
7283
+ const writerLease = acquireProfileWriterLease(profile, config.channel, "cli");
7284
+ try {
7285
+ return upgradeLineageStableProfileRuntime(selector, runtimeIdentity, true);
7286
+ } finally {
7287
+ writerLease.release();
7288
+ }
7289
+ }
6719
7290
  if (command === "bind") {
6720
7291
  if (!args.includes("--confirm-write")) throw new Error("Profile bind requires --confirm-write");
6721
7292
  const profile = resolveLineageProfile(selector);
@@ -6747,6 +7318,9 @@ function printProfileResult(result, json) {
6747
7318
  } else if (result.schema_version === "lineage.profile_runtime_repin_receipt.v1") {
6748
7319
  console.log(`${result.changed ? "Repinned" : "Already pinned"} ${result.profile_id} to checkout ${result.checkout_root}`);
6749
7320
  console.log(`Code fingerprint: ${result.new_code_fingerprint}`);
7321
+ } else if (result.schema_version === "lineage.profile_runtime_upgrade_receipt.v1") {
7322
+ console.log(`${result.changed ? "Upgraded" : "Already pinned"} ${result.profile_id} to stable ${result.new_runtime.version}`);
7323
+ console.log(`Code fingerprint: ${result.new_runtime.code_fingerprint}`);
6750
7324
  } else {
6751
7325
  console.log(`Cloned ${result.files_copied} referenced asset file(s) into ${result.asset_root}`);
6752
7326
  console.log(`Receipt: ${result.receipt_path}`);
@@ -6781,6 +7355,7 @@ function lineageCliRequiresWriterLease(command, args) {
6781
7355
  const subcommand = positions[0] || "";
6782
7356
  if (command === "generate") return positions[0] !== "image" || positions[1] !== "inspect";
6783
7357
  if (command === "reroll") return subcommand !== "list";
7358
+ if (command === "social") return subcommand !== "list";
6784
7359
  if (command === "tasks") return subcommand !== "list" && subcommand !== "inspect";
6785
7360
  if (command === "db") return subcommand !== "info";
6786
7361
  if (command === "agent") return subcommand !== "status" && subcommand !== "graph" && subcommand !== "inspect";
@@ -6954,7 +7529,7 @@ function start(config, args) {
6954
7529
  process.exit(1);
6955
7530
  }
6956
7531
  const serverPath = join10(packageRoot2(), "dist", "server.js");
6957
- if (!existsSync11(serverPath)) {
7532
+ if (!existsSync12(serverPath)) {
6958
7533
  const message = `Missing bundled server at ${serverPath}. Run npm run build before using ${config.binName} start from a source checkout.`;
6959
7534
  if (options.json) console.error(JSON.stringify({ ok: false, error: message }, null, 2));
6960
7535
  else console.error(`${config.binName}: ${message}`);
@@ -7118,7 +7693,7 @@ async function runLineageCli(config, args = process.argv.slice(2)) {
7118
7693
  else console.error(`${config.binName}: ${message2}`);
7119
7694
  process.exit(1);
7120
7695
  }
7121
- if (command === "next" || command === "brief" || command === "inspect" || command === "selection" || command === "link-child" || command === "generate" || command === "reroll" || command === "tasks") {
7696
+ if (command === "next" || command === "brief" || command === "inspect" || command === "selection" || command === "link-child" || command === "generate" || command === "reroll" || command === "social" || command === "tasks") {
7122
7697
  const commandArgs = normalizedArgs.slice(1);
7123
7698
  const json2 = commandArgs.includes("--json");
7124
7699
  try {