harnery 0.37.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dist/commander.d.ts +9 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +7 -2
  4. package/dist/commands/admission.d.ts +21 -0
  5. package/dist/commands/admission.d.ts.map +1 -0
  6. package/dist/commands/admission.js +565 -0
  7. package/dist/commands/agents.d.ts +7 -0
  8. package/dist/commands/agents.d.ts.map +1 -1
  9. package/dist/commands/agents.js +61 -1
  10. package/dist/commands/artifacts.d.ts.map +1 -1
  11. package/dist/commands/artifacts.js +48 -2
  12. package/dist/commands/browse-ai.d.ts +2 -2
  13. package/dist/commands/browse-ai.d.ts.map +1 -1
  14. package/dist/commands/browse-ai.js +6 -4
  15. package/dist/commands/browse.d.ts.map +1 -1
  16. package/dist/commands/browse.js +349 -21
  17. package/dist/commands/fetch.js +1 -0
  18. package/dist/commands/qa-record.d.ts +144 -0
  19. package/dist/commands/qa-record.d.ts.map +1 -0
  20. package/dist/commands/qa-record.js +0 -0
  21. package/dist/commands/qa-run.d.ts +7 -3
  22. package/dist/commands/qa-run.d.ts.map +1 -1
  23. package/dist/commands/qa-run.js +268 -13
  24. package/dist/commands/qa-status.d.ts +71 -0
  25. package/dist/commands/qa-status.d.ts.map +1 -0
  26. package/dist/commands/qa-status.js +490 -0
  27. package/dist/commands/qa-verify.d.ts +40 -0
  28. package/dist/commands/qa-verify.d.ts.map +1 -0
  29. package/dist/commands/qa-verify.js +180 -0
  30. package/dist/commands/review-pack.d.ts +4 -0
  31. package/dist/commands/review-pack.d.ts.map +1 -0
  32. package/dist/commands/review-pack.js +1001 -0
  33. package/dist/core/agents/qa-signal.d.ts +111 -0
  34. package/dist/core/agents/qa-signal.d.ts.map +1 -0
  35. package/dist/core/agents/qa-signal.js +231 -0
  36. package/dist/core/agents/session-name-display.d.ts +20 -5
  37. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  38. package/dist/core/agents/session-name-display.js +67 -7
  39. package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
  40. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  41. package/dist/core/agents/state/heartbeat-writer.d.ts +11 -0
  42. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  43. package/dist/core/agents/state/heartbeat-writer.js +17 -0
  44. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  45. package/dist/core/agents/state/live-coordination-view.js +1 -0
  46. package/dist/core/agents/state/live-coordination-writer.js +5 -0
  47. package/dist/core/artifacts/constants.d.ts +1 -1
  48. package/dist/core/artifacts/constants.js +1 -1
  49. package/dist/core/artifacts/index.d.ts +57 -6
  50. package/dist/core/artifacts/index.d.ts.map +1 -1
  51. package/dist/core/artifacts/index.js +265 -10
  52. package/dist/core/config.d.ts +8 -0
  53. package/dist/core/config.d.ts.map +1 -1
  54. package/dist/core/config.js +16 -0
  55. package/dist/core/diagnostics/bundle.d.ts +16 -0
  56. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  57. package/dist/core/diagnostics/bundle.js +101 -7
  58. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  59. package/dist/core/events/v3/bootstrap.js +10 -0
  60. package/dist/core/events/v3/coordination-view.d.ts +3 -0
  61. package/dist/core/events/v3/coordination-view.d.ts.map +1 -1
  62. package/dist/core/events/v3/coordination-view.js +69 -8
  63. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  64. package/dist/core/events/v3/producers/intake.js +9 -2
  65. package/dist/core/events/v3/producers/recorder.d.ts +23 -0
  66. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  67. package/dist/core/events/v3/producers/recorder.js +264 -18
  68. package/dist/core/hooks/cli.js +120 -27
  69. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  70. package/dist/core/hooks/resolve/transcript.js +10 -3
  71. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  72. package/dist/core/hooks/session-name-presence.js +4 -1
  73. package/dist/core/qa-artifacts.d.ts +20 -0
  74. package/dist/core/qa-artifacts.d.ts.map +1 -0
  75. package/dist/core/qa-artifacts.js +110 -0
  76. package/dist/core/resources/contract.d.ts +6 -0
  77. package/dist/core/resources/contract.d.ts.map +1 -1
  78. package/dist/core/resources/sampler.d.ts +7 -0
  79. package/dist/core/resources/sampler.d.ts.map +1 -1
  80. package/dist/core/resources/sampler.js +106 -5
  81. package/dist/lib/admission.d.ts +71 -0
  82. package/dist/lib/admission.d.ts.map +1 -0
  83. package/dist/lib/admission.js +264 -0
  84. package/dist/lib/agent-browser/client.d.ts +1 -1
  85. package/dist/lib/agent-browser/client.d.ts.map +1 -1
  86. package/dist/lib/agent-browser/client.js +1 -5
  87. package/dist/lib/browser/capture-fidelity.d.ts +39 -0
  88. package/dist/lib/browser/capture-fidelity.d.ts.map +1 -0
  89. package/dist/lib/browser/capture-fidelity.js +84 -0
  90. package/dist/lib/browser/client.d.ts +41 -1
  91. package/dist/lib/browser/client.d.ts.map +1 -1
  92. package/dist/lib/browser/client.js +167 -9
  93. package/dist/lib/browser/critique.d.ts +38 -1
  94. package/dist/lib/browser/critique.d.ts.map +1 -1
  95. package/dist/lib/browser/critique.js +34 -6
  96. package/dist/lib/browser/index.d.ts +4 -2
  97. package/dist/lib/browser/index.d.ts.map +1 -1
  98. package/dist/lib/browser/index.js +2 -0
  99. package/dist/lib/browser/page-review-judge.d.ts +64 -0
  100. package/dist/lib/browser/page-review-judge.d.ts.map +1 -0
  101. package/dist/lib/browser/page-review-judge.js +270 -0
  102. package/dist/lib/browser/page-review-pack.d.ts +613 -0
  103. package/dist/lib/browser/page-review-pack.d.ts.map +1 -0
  104. package/dist/lib/browser/page-review-pack.js +1751 -0
  105. package/dist/lib/browser/qa-run-contracts.d.ts +214 -10
  106. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
  107. package/dist/lib/browser/qa-run-contracts.js +136 -1
  108. package/dist/lib/browser/qa-run.d.ts +100 -10
  109. package/dist/lib/browser/qa-run.d.ts.map +1 -1
  110. package/dist/lib/browser/qa-run.js +768 -169
  111. package/dist/lib/browser/request-diagnostics.d.ts +13 -0
  112. package/dist/lib/browser/request-diagnostics.d.ts.map +1 -0
  113. package/dist/lib/browser/request-diagnostics.js +18 -0
  114. package/dist/lib/browser/tiling.d.ts +19 -0
  115. package/dist/lib/browser/tiling.d.ts.map +1 -1
  116. package/dist/lib/browser/tiling.js +28 -0
  117. package/dist/lib/cookies/client.d.ts +9 -0
  118. package/dist/lib/cookies/client.d.ts.map +1 -1
  119. package/dist/lib/cookies/client.js +197 -44
  120. package/dist/lib/cookies/extra.d.ts +18 -0
  121. package/dist/lib/cookies/extra.d.ts.map +1 -0
  122. package/dist/lib/cookies/extra.js +14 -0
  123. package/dist/lib/cookies/index.d.ts +2 -1
  124. package/dist/lib/cookies/index.d.ts.map +1 -1
  125. package/dist/lib/cookies/index.js +2 -1
  126. package/dist/lib/durable-job.d.ts +124 -0
  127. package/dist/lib/durable-job.d.ts.map +1 -0
  128. package/dist/lib/durable-job.js +296 -0
  129. package/dist/lib/http/client.d.ts +7 -1
  130. package/dist/lib/http/client.d.ts.map +1 -1
  131. package/dist/lib/http/client.js +2 -0
  132. package/dist/lib/instructions/templates.d.ts.map +1 -1
  133. package/dist/lib/instructions/templates.js +5 -2
  134. package/package.json +8 -2
  135. package/src/commander.ts +50 -2
  136. package/src/commands/admission.ts +699 -0
  137. package/src/commands/agents.ts +87 -1
  138. package/src/commands/artifacts.ts +97 -21
  139. package/src/commands/browse-ai.ts +10 -5
  140. package/src/commands/browse.ts +481 -20
  141. package/src/commands/fetch.ts +1 -0
  142. package/src/commands/qa-record.ts +682 -0
  143. package/src/commands/qa-run.ts +335 -16
  144. package/src/commands/qa-status.ts +608 -0
  145. package/src/commands/qa-verify.ts +238 -0
  146. package/src/commands/review-pack.ts +1281 -0
  147. package/src/core/agents/qa-signal.ts +261 -0
  148. package/src/core/agents/session-name-display.ts +78 -7
  149. package/src/core/agents/state/heartbeat-reader.ts +7 -0
  150. package/src/core/agents/state/heartbeat-writer.ts +23 -0
  151. package/src/core/agents/state/live-coordination-view.ts +1 -0
  152. package/src/core/agents/state/live-coordination-writer.ts +5 -0
  153. package/src/core/artifacts/constants.ts +1 -1
  154. package/src/core/artifacts/index.ts +370 -21
  155. package/src/core/config.ts +23 -0
  156. package/src/core/diagnostics/bundle.ts +119 -11
  157. package/src/core/events/v3/bootstrap.ts +10 -0
  158. package/src/core/events/v3/coordination-view.ts +100 -11
  159. package/src/core/events/v3/producers/intake.ts +9 -2
  160. package/src/core/events/v3/producers/recorder.ts +312 -18
  161. package/src/core/hooks/cli.ts +140 -32
  162. package/src/core/hooks/resolve/transcript.ts +10 -3
  163. package/src/core/hooks/session-name-presence.ts +6 -1
  164. package/src/core/qa-artifacts.ts +126 -0
  165. package/src/core/resources/contract.ts +7 -0
  166. package/src/core/resources/sampler.ts +138 -6
  167. package/src/lib/admission.ts +347 -0
  168. package/src/lib/agent-browser/client.ts +2 -10
  169. package/src/lib/browser/capture-fidelity.ts +98 -0
  170. package/src/lib/browser/client.ts +206 -10
  171. package/src/lib/browser/critique.ts +62 -7
  172. package/src/lib/browser/index.ts +36 -0
  173. package/src/lib/browser/page-review-judge.ts +360 -0
  174. package/src/lib/browser/page-review-pack.ts +2384 -0
  175. package/src/lib/browser/qa-run-contracts.ts +366 -3
  176. package/src/lib/browser/qa-run.ts +868 -190
  177. package/src/lib/browser/request-diagnostics.ts +27 -0
  178. package/src/lib/browser/tiling.ts +32 -0
  179. package/src/lib/cookies/client.ts +228 -42
  180. package/src/lib/cookies/extra.ts +28 -0
  181. package/src/lib/cookies/index.ts +2 -0
  182. package/src/lib/durable-job.ts +407 -0
  183. package/src/lib/http/client.ts +13 -1
  184. package/src/lib/instructions/templates.ts +5 -2
@@ -14,8 +14,9 @@ export interface ArtifactActor {
14
14
  session_id?: string;
15
15
  name?: string;
16
16
  }
17
- export interface ArtifactManifestV1 {
17
+ export interface ArtifactManifestV2 {
18
18
  schema_version: typeof ARTIFACT_SCHEMA_VERSION;
19
+ holds: ArtifactHold[];
19
20
  artifact_id: string;
20
21
  slug: string;
21
22
  purpose: string;
@@ -30,7 +31,24 @@ export interface ArtifactManifestV1 {
30
31
  released_by?: ArtifactActor;
31
32
  oversize_acknowledged?: boolean;
32
33
  }
33
- export type ArtifactClassification = "managed-active" | "managed-current" | "managed-expired" | "managed-oversize" | "managed-over-budget" | "managed-tracked" | "invalid-manifest" | "unmanaged" | "symlink" | "unknown";
34
+ export interface ArtifactHold {
35
+ id: string;
36
+ reason: string;
37
+ set_by: ArtifactActor;
38
+ set_at: string;
39
+ }
40
+ export interface ArtifactHoldInput {
41
+ id: string;
42
+ reason: string;
43
+ }
44
+ export declare function artifactCapabilities(): {
45
+ readonly schema_version: 2;
46
+ readonly holds: true;
47
+ readonly atomic_create_holds: true;
48
+ readonly owner_scoped_unhold: true;
49
+ readonly explicit_v1_migration: true;
50
+ };
51
+ export type ArtifactClassification = "managed-held" | "managed-active" | "managed-current" | "managed-expired" | "managed-oversize" | "managed-over-budget" | "managed-tracked" | "invalid-manifest" | "unmanaged" | "symlink" | "unknown";
34
52
  export interface ArtifactInventoryEntry {
35
53
  name: string;
36
54
  path: string;
@@ -51,7 +69,12 @@ export interface ArtifactCreateInput {
51
69
  slug: string;
52
70
  purpose: string;
53
71
  retentionDays: number;
72
+ /** Sub-day retention. When set, it replaces `retentionDays` for this unit
73
+ * (a page review pack expires in minutes, not days). 1 to 5,256,000. */
74
+ retentionMinutes?: number;
54
75
  actor?: ArtifactActor;
76
+ /** Holds are persisted with the first manifest; a valid actor is required. */
77
+ holds?: ArtifactHoldInput[];
55
78
  now?: Date;
56
79
  id?: string;
57
80
  big?: boolean;
@@ -73,12 +96,20 @@ export interface ArtifactAdoptionResult {
73
96
  adopted_path: string | null;
74
97
  adopted_directories: number;
75
98
  }
99
+ interface ParsedManifest {
100
+ ok: true;
101
+ manifest: ArtifactManifestV2;
102
+ }
103
+ interface ManifestError {
104
+ ok: false;
105
+ reason: string;
106
+ }
76
107
  export declare function artifactsRoot(repoRoot: string): string;
77
108
  /** Public config-aware default for embedding hosts that create artifact units. */
78
109
  export declare function configuredArtifactRetentionDays(repoRoot: string): number;
79
110
  export declare function createArtifact(repoRoot: string, input: ArtifactCreateInput): {
80
111
  path: string;
81
- manifest: ArtifactManifestV1;
112
+ manifest: ArtifactManifestV2;
82
113
  };
83
114
  export declare function inventoryArtifacts(repoRoot: string, opts?: {
84
115
  now?: Date;
@@ -89,10 +120,18 @@ export declare function showArtifact(repoRoot: string, ref: string, opts?: {
89
120
  freshnessSeconds?: number;
90
121
  }): {
91
122
  entry: ArtifactInventoryEntry;
92
- manifest: ArtifactManifestV1;
123
+ manifest: ArtifactManifestV2;
93
124
  };
94
- export declare function renewArtifact(repoRoot: string, ref: string, days: number, reason: string, input?: ArtifactMutationInput): ArtifactManifestV1;
95
- export declare function releaseArtifact(repoRoot: string, ref: string, input?: ArtifactMutationInput): ArtifactManifestV1;
125
+ export declare function renewArtifact(repoRoot: string, ref: string, days: number, reason: string, input?: ArtifactMutationInput): ArtifactManifestV2;
126
+ export declare function releaseArtifact(repoRoot: string, ref: string, input?: ArtifactMutationInput): ArtifactManifestV2;
127
+ export declare function holdArtifact(repoRoot: string, ref: string, input: ArtifactHoldInput & {
128
+ actor: ArtifactActor;
129
+ now?: Date;
130
+ }): ArtifactManifestV2;
131
+ export declare function unholdArtifact(repoRoot: string, ref: string, id: string, input: {
132
+ actor: ArtifactActor;
133
+ now?: Date;
134
+ }): ArtifactManifestV2;
96
135
  /** Adopt untracked loose files and legacy directories without changing directory paths. */
97
136
  export declare function adoptUnmanagedArtifactFiles(repoRoot: string, input: {
98
137
  yes?: boolean;
@@ -129,4 +168,16 @@ export declare function autoCleanArtifacts(repoRoot: string, opts?: {
129
168
  now?: Date;
130
169
  }): ArtifactAutoCleanResult;
131
170
  export declare function resolveArtifactRef(repoRoot: string, ref: string): string;
171
+ /** Validate only the current schema. Legacy conversion belongs to migrateArtifacts. */
172
+ export declare function parseArtifactManifest(value: unknown): ParsedManifest | ManifestError;
173
+ export interface ArtifactMigrationEntry {
174
+ path: string;
175
+ action: "keep" | "would-migrate" | "migrated";
176
+ reason: string;
177
+ preimage_path?: string;
178
+ }
179
+ /** Explicit, bounded v1 cutover. Preview never writes; every applied unit keeps its exact preimage. */
180
+ export declare function migrateArtifacts(repoRoot: string, opts?: {
181
+ yes?: boolean;
182
+ }): ArtifactMigrationEntry[];
132
183
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/artifacts/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/artifacts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyBH,OAAO,EAAqB,uBAAuB,EAAiB,MAAM,gBAAgB,CAAC;AAE3F,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3F,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,OAAO,uBAAuB,CAAC;IAC/C,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,SAAS,EAAE;QACT,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,oBAAoB;;;;;;EAQnC;AAED,MAAM,MAAM,sBAAsB,GAC9B,cAAc,GACd,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,kBAAkB,GAClB,qBAAqB,GACrB,iBAAiB,GACjB,kBAAkB,GAClB,WAAW,GACX,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,sBAAsB,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,qBAAqB,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB;4EACwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,CAAC,CAAC;IAC7F,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,UAAU,aAAa;IACrB,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,kFAAkF;AAClF,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,mBAAmB,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,kBAAkB,CAAA;CAAE,CAEhD;AAgDD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,IAAI,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GACnD,sBAAsB,EAAE,CAyB1B;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,IAAI,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GACnD;IAAE,KAAK,EAAE,sBAAsB,CAAC;IAAC,QAAQ,EAAE,kBAAkB,CAAA;CAAE,CAOjE;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,qBAA0B,GAChC,kBAAkB,CAIpB;AA6BD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,qBAA0B,GAChC,kBAAkB,CAEpB;AAqBD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,iBAAiB,GAAG;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,CAAA;CAAE,GAC9D,kBAAkB,CAoBpB;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,CAAA;CAAE,GAC1C,kBAAkB,CAmBpB;AAED,2FAA2F;AAC3F,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;IACL,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,GACA,sBAAsB,CAIxB;AAwGD,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,sBAAsB,EAAE,CAgB1B;AAwDD,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,IAAI,CAAA;CAAO,GACxB,uBAAuB,CAgCzB;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAoBxE;AAiLD,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,aAAa,CA8CpF;AAiBD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,UAAU,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,uGAAuG;AACvG,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3B,sBAAsB,EAAE,CA+E1B"}
@@ -8,13 +8,22 @@
8
8
  * expired, inactive, untracked managed unit is deletable.
9
9
  */
10
10
  import { spawnSync } from "node:child_process";
11
- import { randomUUID } from "node:crypto";
12
- import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
11
+ import { createHash, randomUUID } from "node:crypto";
12
+ import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmdirSync, rmSync, writeFileSync, } from "node:fs";
13
13
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
14
14
  import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
15
15
  import { artifactAutoCleanEnabled, artifactAutoCleanIntervalHours, artifactDefaultRetentionDays, artifactMaxBytes, artifactMaxUnitBytes, } from "../config.js";
16
16
  import { ARTIFACT_MANIFEST, ARTIFACT_SCHEMA_VERSION, ARTIFACTS_DIR } from "./constants.js";
17
17
  export { ARTIFACT_MANIFEST, ARTIFACT_SCHEMA_VERSION, ARTIFACTS_DIR } from "./constants.js";
18
+ export function artifactCapabilities() {
19
+ return {
20
+ schema_version: ARTIFACT_SCHEMA_VERSION,
21
+ holds: true,
22
+ atomic_create_holds: true,
23
+ owner_scoped_unhold: true,
24
+ explicit_v1_migration: true,
25
+ };
26
+ }
18
27
  export function artifactsRoot(repoRoot) {
19
28
  return join(resolve(repoRoot), ARTIFACTS_DIR);
20
29
  }
@@ -23,6 +32,9 @@ export function configuredArtifactRetentionDays(repoRoot) {
23
32
  return artifactDefaultRetentionDays(repoRoot);
24
33
  }
25
34
  export function createArtifact(repoRoot, input) {
35
+ return withArtifactLock(repoRoot, () => createArtifactUnlocked(repoRoot, input));
36
+ }
37
+ function createArtifactUnlocked(repoRoot, input) {
26
38
  const now = input.now ?? new Date();
27
39
  assertValidDate(now, "now");
28
40
  const slug = normalizeSlug(input.slug);
@@ -31,11 +43,17 @@ export function createArtifact(repoRoot, input) {
31
43
  const purpose = input.purpose.trim();
32
44
  if (!purpose)
33
45
  throw new Error("purpose must not be empty");
34
- const retentionDays = positiveDays(input.retentionDays);
46
+ const expiresAt = input.retentionMinutes !== undefined
47
+ ? addMinutes(now, positiveMinutes(input.retentionMinutes))
48
+ : addDays(now, positiveDays(input.retentionDays));
35
49
  const artifactId = input.id ?? randomUUID();
36
50
  if (!isSafeId(artifactId)) {
37
51
  throw new Error("artifact id must use ASCII letters, digits, hyphens, or underscores");
38
52
  }
53
+ const holds = (input.holds ?? []).map((hold) => makeHold(hold, input.actor, now));
54
+ if (new Set(holds.map((hold) => hold.id)).size !== holds.length) {
55
+ throw new Error("duplicate hold id");
56
+ }
39
57
  const root = artifactsRoot(repoRoot);
40
58
  mkdirSync(root, { recursive: true });
41
59
  const date = now.toISOString().slice(0, 10);
@@ -43,13 +61,14 @@ export function createArtifact(repoRoot, input) {
43
61
  mkdirSync(path);
44
62
  const manifest = {
45
63
  schema_version: ARTIFACT_SCHEMA_VERSION,
64
+ holds,
46
65
  artifact_id: artifactId,
47
66
  slug,
48
67
  purpose,
49
68
  created_at: now.toISOString(),
50
69
  created_by: input.actor,
51
70
  retention: {
52
- expires_at: addDays(now, retentionDays).toISOString(),
71
+ expires_at: expiresAt.toISOString(),
53
72
  },
54
73
  ...(input.big ? { oversize_acknowledged: true } : {}),
55
74
  };
@@ -89,6 +108,9 @@ export function showArtifact(repoRoot, ref, opts = {}) {
89
108
  return { entry, manifest: parsed.manifest };
90
109
  }
91
110
  export function renewArtifact(repoRoot, ref, days, reason, input = {}) {
111
+ return withArtifactLock(repoRoot, () => renewArtifactUnlocked(repoRoot, ref, days, reason, input));
112
+ }
113
+ function renewArtifactUnlocked(repoRoot, ref, days, reason, input) {
92
114
  const now = input.now ?? new Date();
93
115
  assertValidDate(now, "now");
94
116
  const retentionDays = positiveDays(days);
@@ -111,6 +133,9 @@ export function renewArtifact(repoRoot, ref, days, reason, input = {}) {
111
133
  return manifest;
112
134
  }
113
135
  export function releaseArtifact(repoRoot, ref, input = {}) {
136
+ return withArtifactLock(repoRoot, () => releaseArtifactUnlocked(repoRoot, ref, input));
137
+ }
138
+ function releaseArtifactUnlocked(repoRoot, ref, input) {
114
139
  const now = input.now ?? new Date();
115
140
  assertValidDate(now, "now");
116
141
  const path = resolveArtifactRef(repoRoot, ref);
@@ -125,8 +150,57 @@ export function releaseArtifact(repoRoot, ref, input = {}) {
125
150
  atomicWriteManifest(path, manifest);
126
151
  return manifest;
127
152
  }
153
+ export function holdArtifact(repoRoot, ref, input) {
154
+ const hold = makeHold(input, input.actor, input.now ?? new Date());
155
+ return withArtifactLock(repoRoot, () => {
156
+ const path = resolveArtifactRef(repoRoot, ref);
157
+ const parsed = readManifest(path);
158
+ if (!parsed.ok)
159
+ throw new Error(parsed.reason);
160
+ const previous = parsed.manifest.holds.find((item) => item.id === hold.id);
161
+ if (previous) {
162
+ if (previous.set_by.instance_id !== input.actor.instance_id ||
163
+ previous.reason !== hold.reason) {
164
+ throw new Error("hold id already exists with a different owner or reason");
165
+ }
166
+ return parsed.manifest;
167
+ }
168
+ const manifest = { ...parsed.manifest, holds: [...parsed.manifest.holds, hold] };
169
+ atomicWriteManifest(path, manifest);
170
+ return manifest;
171
+ });
172
+ }
173
+ export function unholdArtifact(repoRoot, ref, id, input) {
174
+ if (!validHoldId(id))
175
+ throw new Error("invalid hold id");
176
+ if (!validActor(input.actor))
177
+ throw new Error("a valid hold actor is required");
178
+ return withArtifactLock(repoRoot, () => {
179
+ const path = resolveArtifactRef(repoRoot, ref);
180
+ const parsed = readManifest(path);
181
+ if (!parsed.ok)
182
+ throw new Error(parsed.reason);
183
+ const hold = parsed.manifest.holds.find((item) => item.id === id);
184
+ if (!hold)
185
+ return parsed.manifest;
186
+ if (hold.set_by.instance_id !== input.actor.instance_id) {
187
+ throw new Error("only the hold owner may remove this hold");
188
+ }
189
+ const manifest = {
190
+ ...parsed.manifest,
191
+ holds: parsed.manifest.holds.filter((item) => item.id !== id),
192
+ };
193
+ atomicWriteManifest(path, manifest);
194
+ return manifest;
195
+ });
196
+ }
128
197
  /** Adopt untracked loose files and legacy directories without changing directory paths. */
129
198
  export function adoptUnmanagedArtifactFiles(repoRoot, input) {
199
+ if (input.yes)
200
+ return withArtifactLock(repoRoot, () => adoptUnmanagedArtifactFilesUnlocked(repoRoot, input));
201
+ return adoptUnmanagedArtifactFilesUnlocked(repoRoot, input);
202
+ }
203
+ function adoptUnmanagedArtifactFilesUnlocked(repoRoot, input) {
130
204
  const now = input.now ?? new Date();
131
205
  const candidates = [];
132
206
  for (const row of inventoryArtifacts(repoRoot, { now })) {
@@ -185,7 +259,7 @@ export function adoptUnmanagedArtifactFiles(repoRoot, input) {
185
259
  }
186
260
  const files = candidates.filter((candidate) => candidate.kind === "file");
187
261
  const created = files.length
188
- ? createArtifact(repoRoot, {
262
+ ? createArtifactUnlocked(repoRoot, {
189
263
  slug: "adopted-unmanaged",
190
264
  purpose: input.purpose,
191
265
  retentionDays: input.retentionDays,
@@ -201,6 +275,7 @@ export function adoptUnmanagedArtifactFiles(repoRoot, input) {
201
275
  for (const candidate of directories) {
202
276
  atomicWriteManifest(candidate.path, {
203
277
  schema_version: ARTIFACT_SCHEMA_VERSION,
278
+ holds: [],
204
279
  artifact_id: randomUUID(),
205
280
  slug: normalizeSlug(candidate.name),
206
281
  purpose: `${input.purpose}: ${candidate.name}`,
@@ -218,6 +293,23 @@ export function adoptUnmanagedArtifactFiles(repoRoot, input) {
218
293
  };
219
294
  }
220
295
  export function cleanArtifacts(repoRoot, opts = {}) {
296
+ if (!opts.yes)
297
+ return inventoryArtifacts(repoRoot, opts);
298
+ try {
299
+ return withArtifactLock(repoRoot, () => cleanArtifactsUnlocked(repoRoot, opts));
300
+ }
301
+ catch (error) {
302
+ return inventoryArtifacts(repoRoot, opts).map((row) => row.action === "would-delete"
303
+ ? {
304
+ ...row,
305
+ classification: "unknown",
306
+ action: "keep",
307
+ reason: errorMessage("cleanup refused", error),
308
+ }
309
+ : row);
310
+ }
311
+ }
312
+ function cleanArtifactsUnlocked(repoRoot, opts) {
221
313
  const now = opts.now ?? new Date();
222
314
  const freshnessSeconds = opts.freshnessSeconds ?? 600;
223
315
  const rows = inventoryArtifacts(repoRoot, { now, freshnessSeconds });
@@ -369,6 +461,14 @@ function classifyArtifactPath(repoRoot, path, now, freshnessSeconds) {
369
461
  owner_instance_id: manifest.created_by?.instance_id ?? null,
370
462
  oversize_acknowledged: manifest.oversize_acknowledged === true,
371
463
  });
464
+ if (manifest.holds.length > 0) {
465
+ return {
466
+ ...base,
467
+ classification: "managed-held",
468
+ reason: `held: ${manifest.holds.map((hold) => hold.id).join(", ")}`,
469
+ action: "keep",
470
+ };
471
+ }
372
472
  if (base.bytes === null || lastModifiedMs === null) {
373
473
  return {
374
474
  ...base,
@@ -452,11 +552,24 @@ function readManifest(path) {
452
552
  }
453
553
  let value;
454
554
  try {
555
+ const unit = lstatSync(path);
556
+ const stat = lstatSync(manifestPath);
557
+ if (!unit.isDirectory() ||
558
+ unit.isSymbolicLink() ||
559
+ !stat.isFile() ||
560
+ stat.isSymbolicLink() ||
561
+ stat.size > 1024 * 1024) {
562
+ return { ok: false, reason: "manifest must be a bounded regular file in a direct directory" };
563
+ }
455
564
  value = JSON.parse(readFileSync(manifestPath, "utf8"));
456
565
  }
457
566
  catch (error) {
458
567
  return { ok: false, reason: errorMessage("manifest is unreadable", error) };
459
568
  }
569
+ return parseArtifactManifest(value);
570
+ }
571
+ /** Validate only the current schema. Legacy conversion belongs to migrateArtifacts. */
572
+ export function parseArtifactManifest(value) {
460
573
  if (!value || typeof value !== "object" || Array.isArray(value)) {
461
574
  return { ok: false, reason: "manifest must be a JSON object" };
462
575
  }
@@ -464,6 +577,11 @@ function readManifest(path) {
464
577
  if (m.schema_version !== ARTIFACT_SCHEMA_VERSION) {
465
578
  return { ok: false, reason: `unsupported schema_version ${String(m.schema_version)}` };
466
579
  }
580
+ if (!Array.isArray(m.holds) ||
581
+ m.holds.some((hold) => !validHold(hold)) ||
582
+ new Set(m.holds.map((hold) => hold.id)).size !== m.holds.length) {
583
+ return { ok: false, reason: "invalid holds" };
584
+ }
467
585
  if (!isSafeId(m.artifact_id))
468
586
  return { ok: false, reason: "invalid artifact_id" };
469
587
  if (typeof m.slug !== "string" || !m.slug || normalizeSlug(m.slug) !== m.slug) {
@@ -501,11 +619,139 @@ function readManifest(path) {
501
619
  function atomicWriteManifest(path, manifest) {
502
620
  const target = join(path, ARTIFACT_MANIFEST);
503
621
  const tmp = `${target}.tmp.${process.pid}.${randomUUID().slice(0, 8)}`;
504
- writeFileSync(tmp, `${JSON.stringify(manifest, null, 2)}\n`, {
505
- encoding: "utf8",
506
- mode: 0o600,
507
- });
508
- renameSync(tmp, target);
622
+ try {
623
+ writeFileSync(tmp, `${JSON.stringify(manifest, null, 2)}\n`, {
624
+ encoding: "utf8",
625
+ mode: 0o600,
626
+ flag: "wx",
627
+ });
628
+ renameSync(tmp, target);
629
+ }
630
+ finally {
631
+ rmSync(tmp, { force: true });
632
+ }
633
+ }
634
+ /** Explicit, bounded v1 cutover. Preview never writes; every applied unit keeps its exact preimage. */
635
+ export function migrateArtifacts(repoRoot, opts = {}) {
636
+ const migrate = () => {
637
+ const root = artifactsRoot(repoRoot);
638
+ if (!existsSync(root))
639
+ return [];
640
+ return readdirSync(root)
641
+ .sort()
642
+ .map((name) => {
643
+ const path = join(root, name);
644
+ try {
645
+ const stat = lstatSync(path);
646
+ if (stat.isSymbolicLink() || !stat.isDirectory())
647
+ throw new Error("not a direct directory");
648
+ if (containsTrackedPath(repoRoot, path))
649
+ throw new Error("artifact contains tracked files");
650
+ const target = join(path, ARTIFACT_MANIFEST);
651
+ const manifestStat = lstatSync(target);
652
+ if (!manifestStat.isFile() ||
653
+ manifestStat.isSymbolicLink() ||
654
+ manifestStat.size > 1024 * 1024) {
655
+ throw new Error("manifest is not a bounded regular file");
656
+ }
657
+ const preimage = readFileSync(target, "utf8");
658
+ const legacy = JSON.parse(preimage);
659
+ if (legacy?.schema_version !== 1) {
660
+ return {
661
+ path,
662
+ action: "keep",
663
+ reason: `schema_version ${String(legacy?.schema_version)} is not a migration source`,
664
+ };
665
+ }
666
+ if (Object.hasOwn(legacy, "holds"))
667
+ throw new Error("v1 manifest unexpectedly contains holds");
668
+ const parsed = parseArtifactManifest({
669
+ ...legacy,
670
+ schema_version: ARTIFACT_SCHEMA_VERSION,
671
+ holds: [],
672
+ });
673
+ if (!parsed.ok)
674
+ throw new Error(parsed.reason);
675
+ const digest = createHash("sha256").update(preimage).digest("hex");
676
+ const preimagePath = join(resolve(repoRoot), ".harnery/artifact-migrations", `${digest}.v1.json`);
677
+ if (!opts.yes)
678
+ return {
679
+ path,
680
+ action: "would-migrate",
681
+ reason: "valid v1 manifest",
682
+ preimage_path: preimagePath,
683
+ };
684
+ mkdirSync(dirname(preimagePath), { recursive: true });
685
+ try {
686
+ writeFileSync(preimagePath, preimage, { flag: "wx", mode: 0o600 });
687
+ }
688
+ catch (error) {
689
+ if (error.code !== "EEXIST" ||
690
+ lstatSync(preimagePath).isSymbolicLink() ||
691
+ readFileSync(preimagePath, "utf8") !== preimage)
692
+ throw error;
693
+ }
694
+ if (readFileSync(target, "utf8") !== preimage)
695
+ throw new Error("manifest changed before migration");
696
+ atomicWriteManifest(path, parsed.manifest);
697
+ return {
698
+ path,
699
+ action: "migrated",
700
+ reason: "v1 preimage preserved; identity and retention unchanged",
701
+ preimage_path: preimagePath,
702
+ };
703
+ }
704
+ catch (error) {
705
+ return { path, action: "keep", reason: errorMessage("migration refused", error) };
706
+ }
707
+ });
708
+ };
709
+ return opts.yes ? withArtifactLock(repoRoot, migrate) : migrate();
710
+ }
711
+ /** A persistent directory lock fails closed on contention or a crashed owner. */
712
+ function withArtifactLock(repoRoot, action) {
713
+ const lock = join(resolve(repoRoot), ".harnery/artifacts-mutation.lock");
714
+ mkdirSync(dirname(lock), { recursive: true });
715
+ try {
716
+ mkdirSync(lock);
717
+ }
718
+ catch (error) {
719
+ throw new Error(errorMessage("artifact store mutation lock unavailable; retry after its owner finishes", error));
720
+ }
721
+ try {
722
+ return action();
723
+ }
724
+ finally {
725
+ rmdirSync(lock);
726
+ }
727
+ }
728
+ function validHoldId(value) {
729
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/.test(value);
730
+ }
731
+ function validHold(value) {
732
+ if (!value || typeof value !== "object" || Array.isArray(value))
733
+ return false;
734
+ const hold = value;
735
+ return (validHoldId(hold.id) &&
736
+ typeof hold.reason === "string" &&
737
+ !!hold.reason.trim() &&
738
+ validActor(hold.set_by) &&
739
+ validIso(hold.set_at));
740
+ }
741
+ function makeHold(input, actor, now) {
742
+ assertValidDate(now, "now");
743
+ if (!validActor(actor))
744
+ throw new Error("a valid hold actor is required");
745
+ if (!validHoldId(input.id))
746
+ throw new Error("invalid hold id");
747
+ if (typeof input.reason !== "string" || !input.reason.trim())
748
+ throw new Error("hold reason must not be empty");
749
+ return {
750
+ id: input.id,
751
+ reason: input.reason.trim(),
752
+ set_by: { ...actor },
753
+ set_at: now.toISOString(),
754
+ };
509
755
  }
510
756
  function ownerLiveness(repoRoot, instanceId, now, freshnessSeconds) {
511
757
  try {
@@ -621,6 +867,15 @@ function positiveDays(value) {
621
867
  function addDays(date, days) {
622
868
  return new Date(date.getTime() + days * 24 * 60 * 60 * 1000);
623
869
  }
870
+ function positiveMinutes(value) {
871
+ if (!Number.isInteger(value) || value <= 0 || value > 3650 * 24 * 60) {
872
+ throw new Error("retention minutes must be between 1 and 5256000");
873
+ }
874
+ return value;
875
+ }
876
+ function addMinutes(date, minutes) {
877
+ return new Date(date.getTime() + minutes * 60 * 1000);
878
+ }
624
879
  function isSafeId(value) {
625
880
  return (typeof value === "string" &&
626
881
  value.length >= 8 &&
@@ -220,6 +220,14 @@ export declare function artifactMaxUnitBytes(coordRoot?: string | null): number;
220
220
  * `artifacts clean --yes`.
221
221
  */
222
222
  export declare function artifactAutoCleanEnabled(coordRoot?: string | null): boolean;
223
+ /**
224
+ * Whether `qa-run` and `review-pack create` sweep expired page review packs
225
+ * out of the artifact store before starting. Precedence:
226
+ * `HARNERY_REVIEW_PACK_AUTO_CLEAN` (1/true enables, 0/false disables) ->
227
+ * `review_pack.auto_clean` -> disabled. The sweep deletes only packs whose
228
+ * manifest says `managed: true` with `retention.expires_at` in the past.
229
+ */
230
+ export declare function reviewPackAutoCleanEnabled(coordRoot?: string | null): boolean;
223
231
  /**
224
232
  * Sweep cadence in hours. Env-only (`HARNERY_ARTIFACT_AUTO_CLEAN_INTERVAL_HOURS`,
225
233
  * mainly for tests): the cadence is an implementation detail, not policy, and
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
@@ -582,6 +582,22 @@ export function artifactAutoCleanEnabled(coordRoot) {
582
582
  return true;
583
583
  return readConfig(root).artifacts?.auto_clean !== false;
584
584
  }
585
+ /**
586
+ * Whether `qa-run` and `review-pack create` sweep expired page review packs
587
+ * out of the artifact store before starting. Precedence:
588
+ * `HARNERY_REVIEW_PACK_AUTO_CLEAN` (1/true enables, 0/false disables) ->
589
+ * `review_pack.auto_clean` -> disabled. The sweep deletes only packs whose
590
+ * manifest says `managed: true` with `retention.expires_at` in the past.
591
+ */
592
+ export function reviewPackAutoCleanEnabled(coordRoot) {
593
+ const env = coordEnv("REVIEW_PACK_AUTO_CLEAN");
594
+ if (env !== undefined)
595
+ return env === "1" || env.toLowerCase() === "true";
596
+ const root = coordRoot ?? findCoordRoot();
597
+ if (!root)
598
+ return false;
599
+ return readConfig(root).review_pack?.auto_clean === true;
600
+ }
585
601
  /**
586
602
  * Sweep cadence in hours. Env-only (`HARNERY_ARTIFACT_AUTO_CLEAN_INTERVAL_HOURS`,
587
603
  * mainly for tests): the cadence is an implementation detail, not policy, and
@@ -25,11 +25,27 @@ export interface DiagnosticBundleListRow {
25
25
  finding_id?: string;
26
26
  error?: string;
27
27
  }
28
+ export interface DiagnosticBundleCandidateRow {
29
+ artifact_id: string;
30
+ created_at: string;
31
+ expires_at: string | null;
32
+ classification: ArtifactInventoryEntry["classification"];
33
+ selectable: boolean;
34
+ captured_at?: string;
35
+ finding_id?: string;
36
+ error?: string;
37
+ }
28
38
  export declare function captureDiagnosticBundle(repoRootRaw: string, input?: CaptureDiagnosticBundleInput): CapturedDiagnosticBundle;
29
39
  export declare function validateDiagnosticBundle(repoRootRaw: string, ref: string): ValidatedDiagnosticBundle;
30
40
  /** Frozen web readers accept an opaque artifact id, never a filesystem path. */
31
41
  export declare function readFrozenDiagnosticBundle(repoRoot: string, artifactId: string): ValidatedDiagnosticBundle;
32
42
  export declare function showDiagnosticBundle(repoRoot: string, ref: string): ValidatedDiagnosticBundle;
33
43
  export declare function listDiagnosticBundles(repoRoot: string): DiagnosticBundleListRow[];
44
+ /**
45
+ * Lists managed bundle candidates without opening their bounded payloads.
46
+ * Selection is not authority: every show, replay, and comparison operation
47
+ * still performs complete digest and schema validation before returning data.
48
+ */
49
+ export declare function listDiagnosticBundleCandidates(repoRoot: string): DiagnosticBundleCandidateRow[];
34
50
  export declare function replayDiagnosticBundle(repoRoot: string, ref: string, now?: Date): DiagnosticReplayResult;
35
51
  //# sourceMappingURL=bundle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../src/core/diagnostics/bundle.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAwBnF,OAAO,EAUL,KAAK,wBAAwB,EAG7B,KAAK,sBAAsB,EAE3B,KAAK,iBAAiB,EAEtB,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AA0BvB,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,wBAAwB,CAAC;IACnC,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,4BAAiC,GACvC,wBAAwB,CAqG1B;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,GACV,yBAAyB,CA+C3B;AAED,gFAAgF;AAChF,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,yBAAyB,CAK3B;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,yBAAyB,CAE7F;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,uBAAuB,EAAE,CA0BjF;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,OAAa,GACf,sBAAsB,CAgBxB"}
1
+ {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../src/core/diagnostics/bundle.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,aAAa,EACb,sBAAsB,EAEvB,MAAM,uBAAuB,CAAC;AAwB/B,OAAO,EAUL,KAAK,wBAAwB,EAG7B,KAAK,sBAAsB,EAE3B,KAAK,iBAAiB,EAEtB,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AA0BvB,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,wBAAwB,CAAC;IACnC,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IACzD,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,4BAAiC,GACvC,wBAAwB,CAqG1B;AAkCD,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,GACV,yBAAyB,CAsB3B;AAED,gFAAgF;AAChF,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,yBAAyB,CAK3B;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,yBAAyB,CAE7F;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,uBAAuB,EAAE,CA0BjF;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,4BAA4B,EAAE,CA4C/F;AAqCD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,OAAa,GACf,sBAAsB,CAgBxB"}