@gitroll/tacitt-cli 0.6.9 → 0.6.11

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 (57) hide show
  1. package/README.md +43 -4
  2. package/dist/{agent-setup-CODlN6zl.mjs → agent-setup-DdfF3zch.mjs} +2 -2
  3. package/dist/agent-setup-worker.mjs +5 -5
  4. package/dist/cli.mjs +35 -12
  5. package/dist/codex-title-worker.mjs +1 -1
  6. package/dist/configured-preview-worker.mjs +5 -5
  7. package/dist/configured-session-worker.mjs +9 -7
  8. package/dist/{frame-Dj5Zs6UW.mjs → frame-Bq2zRh-i.mjs} +1 -1
  9. package/dist/{heap-Dt3-d27v.mjs → heap-CV8Eca8V.mjs} +217 -13
  10. package/dist/inspection-worker.mjs +4 -4
  11. package/dist/{job-BQXssW46.mjs → job-BKsifhyv.mjs} +38 -18
  12. package/dist/{locate-BWkblijS.mjs → locate-Cx52Zqxv.mjs} +204 -20
  13. package/dist/{model-CSx2dP_P.mjs → model-C1UXkHh9.mjs} +1 -1
  14. package/dist/operation-executor.mjs +3 -3
  15. package/dist/preview-worker.mjs +1 -1
  16. package/dist/privacy-validator.mjs +29 -2
  17. package/dist/{query-CfhcS4gd.mjs → query-AMZerUh8.mjs} +712 -215
  18. package/dist/{run-xlOaKCk8.mjs → run-CaPktPhH.mjs} +4 -4
  19. package/dist/{src-DzKuzGVH.mjs → src-D1iONla3.mjs} +2215 -1613
  20. package/dist/{src-V9GHLZQs.mjs → src-DlVbGB32.mjs} +74 -37
  21. package/dist/{src-BgsEnMsy.mjs → src-wZx_v0Ih.mjs} +297 -207
  22. package/dist/store-database-worker.mjs +1 -1
  23. package/dist/web/assets/index-CYl7eDUu.js +172 -0
  24. package/dist/web/assets/index-DzOozqms.css +1 -0
  25. package/dist/web/index.html +2 -2
  26. package/package.json +1 -1
  27. package/skills/tacitt/INDEX.md +6 -6
  28. package/skills/tacitt/SKILL.md +11 -11
  29. package/skills/tacitt/references/INDEX.md +3 -3
  30. package/skills/tacitt/references/b-method.md +2 -2
  31. package/skills/tacitt/references/bootstrap.md +2 -2
  32. package/skills/tacitt/references/classification.md +15 -5
  33. package/skills/tacitt/references/commands.md +37 -30
  34. package/skills/tacitt/references/journey.md +14 -6
  35. package/skills/tacitt/references/parallel-review.md +27 -33
  36. package/skills/tacitt/references/send.md +35 -9
  37. package/skills/tacitt/scripts/INDEX.md +37 -37
  38. package/skills/tacitt/scripts/answer.mjs +73 -15
  39. package/skills/tacitt/scripts/journey-classification-metadata.mjs +75 -26
  40. package/skills/tacitt/scripts/journey-classification-progress.mjs +1 -2
  41. package/skills/tacitt/scripts/journey-classification-validation.d.mts +13 -0
  42. package/skills/tacitt/scripts/journey-classification-validation.mjs +38 -0
  43. package/skills/tacitt/scripts/journey-classification.mjs +6 -1
  44. package/skills/tacitt/scripts/journey-consolidation.mjs +1 -0
  45. package/skills/tacitt/scripts/journey-scan-timing.d.mts +1 -0
  46. package/skills/tacitt/scripts/journey-scan-timing.mjs +19 -4
  47. package/skills/tacitt/scripts/journey-state.d.mts +15 -1
  48. package/skills/tacitt/scripts/journey-state.mjs +104 -5
  49. package/skills/tacitt/scripts/journey-tasks.d.mts +1 -0
  50. package/skills/tacitt/scripts/journey-tasks.mjs +13 -5
  51. package/skills/tacitt/scripts/journey.d.mts +46 -28
  52. package/skills/tacitt/scripts/journey.mjs +57 -8
  53. package/skills/tacitt/scripts/privacy-validator.mjs +178 -12
  54. package/skills/tacitt/scripts/review.d.mts +7 -6
  55. package/skills/tacitt/scripts/review.mjs +576 -147
  56. package/dist/web/assets/index-9UfCT_2C.js +0 -174
  57. package/dist/web/assets/index-CYJBq1px.css +0 -1
package/README.md CHANGED
@@ -15,18 +15,57 @@ CLI version, preserves verified previous release assets for existing tasks, and
15
15
  reports unmanaged-file collisions. Installation alone starts no scan and resets
16
16
  no source logs, state or existing exports.
17
17
 
18
- For the standalone Manual browser interface, run:
18
+ Tacitt has three surfaces: the Skill, the Skill panel that the Skill opens, and
19
+ the CLI commands the Skill runs (`tacitt --help` lists them). Running `tacitt`
20
+ with no command prints how to start the Skill.
21
+
22
+ For a local export without Send, run these CLI commands in order. `tacitt` stands for
23
+ `npx --yes @gitroll/tacitt-cli@latest`. Each command prints JSON. Take `runId`,
24
+ `revision`, `settingsVersion` and `dictionaryRevision` from the previous command's output,
25
+ because each step raises the revision. `slots` picks workspace ZIPs by position. The
26
+ `echo ... | tacitt ... --input -` form works in sh and PowerShell; in cmd.exe, save the JSON
27
+ to a file and pass `--input FILE`. Manual upload to Passport from the CLI is not available yet.
19
28
 
20
29
  ```sh
21
- npx --yes @gitroll/tacitt-cli@latest
30
+ tacitt workflow.startDiscovery # operationId
31
+ tacitt operation.wait <operationId>
32
+ tacitt workflow.getCurrent # runId, revision
33
+ echo '{"protocolVersion":1,"runId":"<runId>","expectedRevision":<revision>}' | tacitt workflow.chooseMethod --input -
34
+ echo '{"protocolVersion":1,"runId":"<runId>","expectedRevision":<revision>,"expectedSettingsVersion":<settingsVersion>,"mode":"configured","changes":[]}' | tacitt processing.batch --input -
35
+ echo '{"protocolVersion":1,"runId":"<runId>","expectedRevision":<revision>}' | tacitt workflow.reviewPending --input -
36
+ echo '{"protocolVersion":1,"runId":"<runId>","expectedRevision":<revision>,"expectedDictionaryRevision":"<dictionaryRevision>"}' | tacitt workflow.package --input - --async
37
+ tacitt operation.wait <operationId>
38
+ tacitt delivery.list # result id
39
+ echo '{"resultId":"<id>","slots":[0],"artifact":"primary","output":"export.zip"}' | tacitt delivery.issue --input -
40
+ ```
41
+
42
+ The `processing.batch` step applies the configured cleanup and redaction. Without it, the
43
+ export is the original session content with no redaction or anonymization.
44
+
45
+ Global sensitive terms and the bundle library are set through the CLI only.
46
+ `sensitiveTerms.replace` replaces the whole list; take `revision` from `sensitiveTerms.get`
47
+ (`"empty"` before the first save). `bundleLibrary.save` takes effect at the next CLI start.
48
+
49
+ ```sh
50
+ tacitt sensitiveTerms.get
51
+ echo '{"expectedRevision":"<revision>","terms":["Acme Corp","project-x"]}' | tacitt sensitiveTerms.replace --input -
52
+ tacitt bundleLibrary.get
53
+ echo '{"directory":"/absolute/path/to/bundles"}' | tacitt bundleLibrary.save --input -
54
+ ```
55
+
56
+ The `echo '...' |` lines work in sh and PowerShell. In cmd.exe, drop the single quotes and
57
+ put `|` right after the JSON:
58
+
59
+ ```bat
60
+ echo {"expectedRevision":"<revision>","terms":["Acme Corp","project-x"]}| tacitt sensitiveTerms.replace --input -
22
61
  ```
23
62
 
24
63
  The native Skill presents decisions in a focused panel. Source consent precedes
25
64
  classification and privacy analysis. The current model provider receives frozen
26
65
  original main conversations and retained tool results before privacy masking;
27
66
  this workflow is not local-only. Send separately authorizes packaging/upload of
28
- the selected reviewed material to the signed-in Tacitt account. Use Manual for
29
- standalone local export. Keep session data and panel capability URLs private.
67
+ the selected reviewed material to the signed-in Tacitt account. Keep session data
68
+ and panel capability URLs private.
30
69
 
31
70
  State from incompatible previews is not silently migrated. Keep the pinned CLI
32
71
  for old tasks and use a separate state directory when required; installing the
@@ -1,5 +1,5 @@
1
- import "./locate-BWkblijS.mjs";
2
- import "./src-DzKuzGVH.mjs";
1
+ import "./locate-Cx52Zqxv.mjs";
2
+ import "./src-D1iONla3.mjs";
3
3
  import { z } from "zod";
4
4
  import { Worker } from "node:worker_threads";
5
5
  //#region apps/cli/src/agent-setup/index.ts
@@ -1,8 +1,8 @@
1
- import { J as listAgentSetup } from "./src-V9GHLZQs.mjs";
2
- import { Dn as createStaticProcessor, dn as workspaceSchema, ln as sessionSchema } from "./locate-BWkblijS.mjs";
3
- import { l as createNativeAdapters, u as createGitNameSeedProvider } from "./src-BgsEnMsy.mjs";
4
- import { mi as effectiveTreatmentSchema, ot as maximumAgentSetupSessions } from "./src-DzKuzGVH.mjs";
5
- import "./agent-setup-CODlN6zl.mjs";
1
+ import { J as listAgentSetup } from "./src-DlVbGB32.mjs";
2
+ import { Nn as createStaticProcessor, _n as workspaceSchema, hn as sessionSchema } from "./locate-Cx52Zqxv.mjs";
3
+ import { l as createNativeAdapters, u as createGitNameSeedProvider } from "./src-wZx_v0Ih.mjs";
4
+ import { bi as effectiveTreatmentSchema, ct as maximumAgentSetupSessions } from "./src-D1iONla3.mjs";
5
+ import "./agent-setup-DdfF3zch.mjs";
6
6
  import { z } from "zod";
7
7
  import { parentPort, workerData } from "node:worker_threads";
8
8
  //#region apps/cli/src/agent-setup/worker.ts
package/dist/cli.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { _ as permissionDiagnostic, a as readAgentPresence, d as guardDetachedStartup, f as createRenderActivity, g as longCommands, h as businessCommands, i as invoke, l as awaitingPassport, m as parseOptions, n as readOperation, o as createRuntime, p as helpText, r as waitOperation, s as executorEventSchema, t as cancelOperation, u as serveDetached, v as startupFailure, y as traceStartup } from "./query-CfhcS4gd.mjs";
3
- import { nt as makePrivateDirectory, rt as openPrivateFile } from "./src-V9GHLZQs.mjs";
4
- import { A as createExecutionPort, I as inBackgroundStatusPoll, j as startHost, k as readStateDump } from "./job-BQXssW46.mjs";
5
- import { $t as safeField, Qt as safeCode, Xt as ApplicationError } from "./locate-BWkblijS.mjs";
6
- import { $n as reviewTransportBytes, ct as resolveFailureLocation, tt as readWorkspaceOutcomes, ui as stateSchema } from "./src-DzKuzGVH.mjs";
7
- import { _t as isCoordinationBusy } from "./heap-Dt3-d27v.mjs";
2
+ import { _ as longCommands, a as readAgentPresence, b as traceStartup, d as serveDetached, f as guardDetachedStartup, g as businessCommands, h as parseOptions, i as invoke, l as awaitingPassport, m as helpText, n as readOperation, o as createRuntime, p as createRenderActivity, r as waitOperation, s as executorEventSchema, t as cancelOperation, u as hostDrives, v as permissionDiagnostic, y as startupFailure } from "./query-AMZerUh8.mjs";
3
+ import { it as openPrivateFile, rt as makePrivateDirectory } from "./src-DlVbGB32.mjs";
4
+ import { A as createExecutionPort, I as inBackgroundStatusPoll, j as startHost, k as readStateDump } from "./job-BKsifhyv.mjs";
5
+ import { an as safeField, in as safeCode, nn as ApplicationError } from "./locate-Cx52Zqxv.mjs";
6
+ import { gi as stateSchema, rt as readWorkspaceOutcomes, ut as resolveFailureLocation, xn as reviewTransportBytes } from "./src-D1iONla3.mjs";
7
+ import { vt as isCoordinationBusy } from "./heap-CV8Eca8V.mjs";
8
8
  import { constants, createReadStream, write } from "node:fs";
9
9
  import { z } from "zod";
10
10
  import { lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, writeFile } from "node:fs/promises";
@@ -309,14 +309,23 @@ async function serve(options, journeyId) {
309
309
  const ended = journey?.phase === "completed" || journey?.abandonedAt !== void 0;
310
310
  const send = (await inBackgroundStatusPoll(async () => journeys?.get({})))?.journeyId === journeyId ? (await journeySend?.get({ journeyId }))?.send : null;
311
311
  const waiting = Boolean(send && awaitingPassport(send));
312
+ const driving = Boolean(send && hostDrives(send));
312
313
  const now = Date.now();
313
314
  endedAt = ended ? endedAt ?? now : void 0;
314
315
  settledAt = ended && !waiting ? settledAt ?? now : void 0;
315
316
  if (endedAt !== void 0 && hostFinished(now, endedAt, settledAt)) return scheduleStop();
316
- if (waiting && send) await journeySend?.advance({
317
- journeyId,
318
- sendId: send.id
319
- }, hostLife.signal);
317
+ if (driving && send && journeySend) {
318
+ let previous = send;
319
+ while (!hostLife.signal.aborted) {
320
+ const result = await journeySend.advance({
321
+ journeyId,
322
+ sendId: previous.id
323
+ }, hostLife.signal);
324
+ if (result.status !== "applied" || !result.send || !hostDrives(result.send) || result.send.revision === previous.revision) break;
325
+ if ((await inBackgroundStatusPoll(async () => journeys?.get({})))?.journeyId !== journeyId) break;
326
+ previous = result.send;
327
+ }
328
+ }
320
329
  })().catch(() => {}).finally(() => {
321
330
  following = false;
322
331
  });
@@ -990,6 +999,7 @@ const writeJson = (value) => new Promise((resolve, reject) => {
990
999
  /** Procedures the review helper sends; each runs exactly as its own `--input` command would. */
991
1000
  const streamProcedures = /* @__PURE__ */ new Set([
992
1001
  "journey.get",
1002
+ "journey.respond",
993
1003
  "journey.remoteWorkers",
994
1004
  "journey.prepare",
995
1005
  "journey.catalog",
@@ -1003,6 +1013,7 @@ const streamProcedures = /* @__PURE__ */ new Set([
1003
1013
  "journey.read",
1004
1014
  "review.guidance",
1005
1015
  "review.status",
1016
+ "review.reopenEmpty",
1006
1017
  "review.terms"
1007
1018
  ]);
1008
1019
  const request = z.object({
@@ -1050,11 +1061,17 @@ async function reviewStream(options) {
1050
1061
  const call = request.parse(JSON.parse(line));
1051
1062
  id = call.id;
1052
1063
  if (!streamProcedures.has(call.procedure)) throw new ApplicationError("INVALID_ARGUMENTS");
1064
+ if (call.procedure === "journey.respond" && !z.object({ action: z.object({ kind: z.enum(["review-interrupted", "review-reader-closed"]) }) }).safeParse(call.input).success) throw new ApplicationError("INVALID_ARGUMENTS");
1053
1065
  const result = await invoke(runtime, call.procedure, call.input ?? null, abort.signal);
1066
+ const output = call.procedure === "journey.respond" ? z.object({ status: z.enum([
1067
+ "applied",
1068
+ "replayed",
1069
+ "stale"
1070
+ ]) }).parse(result) : result;
1054
1071
  await writeJson({
1055
1072
  id,
1056
1073
  ok: true,
1057
- result
1074
+ result: output
1058
1075
  });
1059
1076
  } catch (error) {
1060
1077
  runtime.diagnostics.onFailure("command", error);
@@ -1296,6 +1313,8 @@ async function business(options, args) {
1296
1313
  //#endregion
1297
1314
  //#region apps/cli/src/cli.ts
1298
1315
  const stateDumpSchema = z.object({ state: z.unknown() });
1316
+ /** Printed by bare `tacitt`. Owner approved this text on 2026-11-12 (ADR 0278). */
1317
+ const skillPointer = "Tacitt runs from the Skill. In Claude Code, run /tacitt. In Codex, run $tacitt.\nTo install the Skill: npx --yes @gitroll/tacitt-cli@latest skill.install\n";
1299
1318
  async function main() {
1300
1319
  traceStartup("entry-ready");
1301
1320
  process.umask(63);
@@ -1359,7 +1378,11 @@ async function main() {
1359
1378
  return;
1360
1379
  }
1361
1380
  if (options.command) await business(options, process.argv.slice(2));
1362
- else await serve(options);
1381
+ else if (process.env["CI"] === "true" && process.env["SESS_PILOT_TEST_MANUAL_HOST"] === "1") await serve({
1382
+ ...options,
1383
+ "no-open": true
1384
+ });
1385
+ else process.stdout.write(skillPointer);
1363
1386
  }
1364
1387
  main().catch(async (error) => {
1365
1388
  if (process.argv.slice(2).find((argument) => businessCommands.some((name) => name === argument))) {
@@ -1,4 +1,4 @@
1
- import { i as errorCode } from "./model-CSx2dP_P.mjs";
1
+ import { i as errorCode } from "./model-C1UXkHh9.mjs";
2
2
  import { a as titleHintLimits, i as encodeTitleError, o as titleInputSchema, t as assertTitleHeap } from "./heap-1FYhGds4.mjs";
3
3
  import { stat } from "node:fs/promises";
4
4
  import { isAbsolute, join } from "node:path";
@@ -1,8 +1,8 @@
1
- import { Y as createConfiguredPreviewStream } from "./src-V9GHLZQs.mjs";
2
- import { Dn as createStaticProcessor, dn as workspaceSchema, ln as sessionSchema } from "./locate-BWkblijS.mjs";
3
- import { l as createNativeAdapters, u as createGitNameSeedProvider } from "./src-BgsEnMsy.mjs";
4
- import { mi as effectiveTreatmentSchema } from "./src-DzKuzGVH.mjs";
5
- import { a as classifyRecord, n as assertPreviewHeap, t as runPreview } from "./run-xlOaKCk8.mjs";
1
+ import { Y as createConfiguredPreviewStream } from "./src-DlVbGB32.mjs";
2
+ import { Nn as createStaticProcessor, _n as workspaceSchema, hn as sessionSchema } from "./locate-Cx52Zqxv.mjs";
3
+ import { l as createNativeAdapters, u as createGitNameSeedProvider } from "./src-wZx_v0Ih.mjs";
4
+ import { bi as effectiveTreatmentSchema } from "./src-D1iONla3.mjs";
5
+ import { a as classifyRecord, n as assertPreviewHeap, t as runPreview } from "./run-CaPktPhH.mjs";
6
6
  import { z } from "zod";
7
7
  import { parentPort, workerData } from "node:worker_threads";
8
8
  //#region apps/cli/src/preview/run.ts
@@ -1,8 +1,8 @@
1
- import { et as safeError, h as captureConfiguredSessionJob, j as runConfiguredSessionJob, u as runConfiguredScanJob, z as gitSeedsProvider } from "./src-V9GHLZQs.mjs";
2
- import { d as runJourneyBaseJob, i as runJourneyOutputVerifyJob, n as runJourneyOutputJob, r as runJourneyOutputShardJob } from "./job-BQXssW46.mjs";
3
- import { Dn as createStaticProcessor, Qt as safeCode } from "./locate-BWkblijS.mjs";
4
- import { l as createNativeAdapters } from "./src-BgsEnMsy.mjs";
5
- import "./src-DzKuzGVH.mjs";
1
+ import { h as captureConfiguredSessionJob, j as runConfiguredSessionJob, tt as safeError, u as runConfiguredScanJob, z as gitSeedsProvider } from "./src-DlVbGB32.mjs";
2
+ import { d as runJourneyBaseJob, i as runJourneyOutputVerifyJob, n as runJourneyOutputJob, r as runJourneyOutputShardJob } from "./job-BKsifhyv.mjs";
3
+ import { Nn as createStaticProcessor, br as processingSite, in as safeCode } from "./locate-Cx52Zqxv.mjs";
4
+ import { l as createNativeAdapters } from "./src-wZx_v0Ih.mjs";
5
+ import "./src-D1iONla3.mjs";
6
6
  import { parentPort } from "node:worker_threads";
7
7
  //#region apps/cli/src/configured-session-worker.ts
8
8
  const port = parentPort;
@@ -46,7 +46,8 @@ function reportFailure(error) {
46
46
  post({
47
47
  kind: "error",
48
48
  code,
49
- ...sourceId === void 0 ? {} : { sourceId }
49
+ ...sourceId === void 0 ? {} : { sourceId },
50
+ ...processingSite(error)
50
51
  });
51
52
  }
52
53
  function processingFor(job) {
@@ -100,7 +101,8 @@ port?.on("message", async (message) => {
100
101
  } catch (error) {
101
102
  post({
102
103
  kind: "error",
103
- code: safeCode(error)
104
+ code: safeCode(error),
105
+ ...processingSite(error)
104
106
  });
105
107
  }
106
108
  else if (isJourneyOutputShardRequest(message)) post({
@@ -1,4 +1,4 @@
1
- import { ur as ProcessingError } from "./locate-BWkblijS.mjs";
1
+ import { gr as ProcessingError } from "./locate-Cx52Zqxv.mjs";
2
2
  //#region packages/processing/src/compression/frame.ts
3
3
  function createZstdFrameCheck(fail = () => {
4
4
  throw new ProcessingError("PROCESSING_INVALID_RECORD");
@@ -1,6 +1,6 @@
1
- import { S as readPrivateReviewPayload, at as windowsOwnerOnly, it as restrictToOwner, rt as openPrivateFile, tt as exclusiveLock, x as writePrivateReviewPayload } from "./src-V9GHLZQs.mjs";
2
- import { At as journeyTermSchema, It as termTypeSchema, Mt as reviewTermsResultSchema, Xt as ApplicationError, _ as semanticFindingSchema, b as validateBReview, c as textDigest, d as reviewAlignmentAudit, dt as nativeJourneySchema, er as isStaticCategory, ft as journeyGuidanceSchema, g as validateReviewAlignment, gt as journeyWorkSuggestionSchema, ht as journeyDescribeSchema, i as reviewBatchSchema, it as journeyDigestSchema, jt as reviewTermsInputSchema, kt as privacyTermSchema, l as validateReview, m as reviewAlignmentSchema, nn as reviewFamilies, o as reviewDigest, ot as journeyIdSchema, r as reviewBatchChunks, s as reviewSubmitSchema, sn as fileIdentitySchema, tn as familySelected, tr as staticCategories, u as findingReferenceKey, v as bContextEvidence, wn as createDetectionSummary } from "./locate-BWkblijS.mjs";
3
- import { $r as isReadingComplete, $t as projectPreparationProgress, Ar as applyCardAnswers, Bn as confirmedHideTerms, Cn as confirmedHideDeltaSchema, En as settleConfirmedHides, Et as settleCorrections, Ft as journeyBatchReadSchema, G as validateInspectionClaim, Gn as retireWorkDescription, Gt as journeyContextSchema, H as validateInspectionBatches, Ht as newJourneyWork, Ir as mergePrivacyTerms, It as journeyReviewStorageQuerySchema, J as validateInspectionInputs, Jt as hidePublishSchema, K as validateInspectionFence, Kn as workDecisionDigest, Kt as hideBeginSchema, Ln as confirmedHideGroupIds, Lt as applyClassificationProposal, Mt as validateAttemptAppend, Nt as journeyGuidance, Ot as obsoleteJourney, Pn as bMaskedHits, Q as inspectionInputs, Qn as reviewRepairResourceSchema, Qt as journeyPolicyRespondSchema, Rn as confirmedHideKey, Rt as classificationCommitSchema, Un as maskedHitsStale, V as validateInspectionBatch, Vn as dictionaryHides, Wn as syncTermGroups, Wr as journeySendSchema, Wt as journeyContextQuerySchema, X as validateInspectionContribution, Xn as reviewCandidateResourceSchema, Xt as journeySearchSaveSchema, Y as validateInspectionQuery, Yt as journeySearchResultSchema, Z as validateDetectionSummary, Zt as searchScopeDigest, _n as sessionUsefulnessInputSchema, an as reviewAttemptResourceSchema, bn as journeyCommitSchema, br as preparationStartSchema, c as reconstructBFields, d as resolveGroupSplit, dr as validateScanExclusion, dt as manualUploadBindingSchema, ei as isSelectedWork, en as journeyResourceQuerySchema, er as bReviewDigest, fn as sessionRereadResourceSchema, g as semanticGroupKey, gn as sessionReviewStatusSchema, gr as journeyPreparationProgressSchema, gt as candidateSortAvailable, h as hideScopeDigest, hn as sessionReviewResourceSchema, hr as journeyInventorySchema, in as sortingApplySchema, ir as scanCanonicalResourceSchema, jt as attemptBudgetCodePoints, l as chooseCheapPlacement, ln as sessionReconcileResultSchema, mn as sessionReviewQuerySchema, mr as journeyInventoryReadSchema, mt as manualUploadSchema, ni as selectedWorks, nn as journeyIsolateSchema, o as bRereadDigest, on as reviewAttemptsQuerySchema, or as scanSessionResourceSchema, q as validateInspectionHead, qn as workPrivacyDecisions, qt as hideFinishSchema, ri as withReadingProgress, rn as journeyPlaceSchema, rr as scanAliasResourceSchema, s as reconcileBSession, sn as reviewAttemptsResultSchema, sr as scanGitSeedsId, t as scanBatchContextDigest, ti as isSkippedWork, tn as journeyResourceSchema, tr as mergeBRepair, u as reviseGroupMembership, ui as stateSchema, un as sessionReconciliationResourceSchema, v as projectJourneyInventory, vn as sessionUsefulnessResourceSchema, vr as preparationBeginSchema, x as bReviewScope, xn as journeyEventSchema, xr as validatePreparationFacts, y as preparationScope, yn as journeyCommitResultSchema, yr as preparationSettlementSchema, zr as staticSessionId } from "./src-DzKuzGVH.mjs";
1
+ import { S as readPrivateReviewPayload, at as restrictToOwner, it as openPrivateFile, nt as exclusiveLock, ot as windowsOwnerOnly, x as writePrivateReviewPayload } from "./src-DlVbGB32.mjs";
2
+ import { An as createDetectionSummary, Ft as emptyReviewCorrectionSchema, Ht as termTypeSchema, It as journeyTermSchema, Lt as reviewTermsInputSchema, Nt as privacyTermSchema, Pt as emptyReviewCorrectionResultSchema, Rt as reviewTermsResultSchema, S as validateBReview, a as reviewBatchChunks, b as bContextEvidence, bt as journeyWorkSuggestionSchema, c as reviewDigest, cn as reviewFamilies, d as validateReview, f as findingReferenceKey, g as reviewAlignmentSchema, gt as journeyGuidanceSchema, l as reviewSubmitSchema, lt as journeyIdSchema, mt as nativeJourneySchema, nn as ApplicationError, o as reviewBatchSchema, or as isStaticCategory, p as reviewAlignmentAudit, pn as fileIdentitySchema, sn as familySelected, sr as staticCategories, st as journeyDigestSchema, u as textDigest, v as validateReviewAlignment, y as semanticFindingSchema, yt as journeyDescribeSchema } from "./locate-Cx52Zqxv.mjs";
3
+ import { $ as validateDetectionSummary, $t as journeyResourceSchema, At as obsoleteJourney, Bn as journeySearchResultSchema, Cn as mergeBRepair, Cr as preparationStartSchema, En as scanCanonicalResourceSchema, Er as validatePreparationFacts, Fr as applyCardAnswers, Ft as journeyGuidance, Gn as journeyEventSchema, Gr as staticSessionId, Gt as journeyContextQuerySchema, Hn as searchScopeDigest, Hr as mergePrivacyTerms, In as journeyInventoryReadSchema, It as journeyBatchReadSchema, J as validateInspectionFence, Jt as hideFinishSchema, Kt as journeyContextSchema, Ln as journeyInventorySchema, Lt as journeyReviewStorageQuerySchema, Nn as validateScanExclusion, Nt as attemptBudgetCodePoints, On as scanSessionResourceSchema, Ot as settleCorrections, Pt as validateAttemptAppend, Q as validateInspectionContribution, Qt as journeyResourceQuerySchema, Rn as journeyPreparationProgressSchema, Rt as applyClassificationProposal, Sn as bReviewDigest, Sr as preparationSettlementSchema, Tn as scanAliasResourceSchema, Tr as preparationUpdateSchema, U as validateInspectionBatch, Un as journeyCommitResultSchema, Ut as newJourneyWork, Vn as journeySearchSaveSchema, W as validateInspectionBatches, Wn as journeyCommitSchema, X as validateInspectionInputs, Xn as settleConfirmedHides, Xr as journeySendSchema, Xt as journeyPolicyRespondSchema, Y as validateInspectionHead, Yt as hidePublishSchema, Z as validateInspectionQuery, Zt as projectPreparationProgress, _r as workPrivacyDecisions, ai as isReadingComplete, an as reviewAttemptsResultSchema, bn as reviewRepairResourceSchema, br as preparationBeginSchema, c as reconstructBFields, ci as selectedWorks, cn as sessionReconciliationResourceSchema, cr as confirmedHideKey, d as resolveGroupSplit, dr as dictionaryHides, en as journeyIsolateSchema, et as inspectionInputs, fn as sessionReviewQuerySchema, g as semanticGroupKey, gi as stateSchema, gn as sessionUsefulnessResourceSchema, gr as workDecisionDigest, gt as manualUploadSchema, h as hideScopeDigest, hn as sessionUsefulnessInputSchema, hr as retireWorkDescription, in as reviewAttemptsQuerySchema, ir as bMaskedHits, kn as scanGitSeedsId, l as chooseCheapPlacement, li as withReadingProgress, mn as sessionReviewStatusSchema, mr as syncTermGroups, nn as sortingApplySchema, o as bRereadDigest, oi as isSelectedWork, pn as sessionReviewResourceSchema, pr as maskedHitsStale, pt as manualUploadBindingSchema, q as validateInspectionClaim, qn as confirmedHideDeltaSchema, qt as hideBeginSchema, rn as reviewAttemptResourceSchema, s as reconcileBSession, si as isSkippedWork, sn as sessionReconcileResultSchema, sr as confirmedHideGroupIds, t as scanBatchContextDigest, tn as journeyPlaceSchema, u as reviseGroupMembership, un as sessionRereadResourceSchema, ur as confirmedHideTerms, v as projectJourneyInventory, vn as reviewCandidateResourceSchema, vt as candidateSortAvailable, wr as preparationStateSchema, x as bReviewScope, xr as preparationPermissionSchema, y as preparationScope, zt as classificationCommitSchema } from "./src-D1iONla3.mjs";
4
4
  import { constants } from "node:fs";
5
5
  import { z } from "zod";
6
6
  import { copyFile, link, lstat, mkdir, mkdtemp, open, readFile, realpath, rm, unlink } from "node:fs/promises";
@@ -454,6 +454,23 @@ function commitJourney(db, input) {
454
454
  };
455
455
  }
456
456
  if (input.state.journeyId !== input.event.journeyId) throw new ApplicationError("INVALID_ARGUMENTS");
457
+ if (input.closedReader) {
458
+ const hold = input.closedReader;
459
+ const unread = db.prepare(`SELECT 1 FROM journey_batches
460
+ WHERE id=? AND journey_id=? AND work_id=? AND input_revision=? AND review_ref_json IS NULL`);
461
+ if (hold.batchIds.some((id) => !unread.get(id, input.state.journeyId, hold.workId, hold.inputRevision))) throw new ApplicationError("REVISION_CONFLICT");
462
+ const save = db.prepare("INSERT OR IGNORE INTO journey_resources VALUES(?,?,?,?)");
463
+ for (const batchId of hold.batchIds) {
464
+ const fence = {
465
+ kind: "closed-reader",
466
+ journeyId: input.state.journeyId,
467
+ id: batchId + ":" + hold.packId,
468
+ packId: hold.packId,
469
+ batchId
470
+ };
471
+ save.run(fence.journeyId, fence.kind, fence.id, JSON.stringify(fence));
472
+ }
473
+ }
457
474
  if (input.state.abandonedAt && current?.journeyId === input.state.journeyId && !current.abandonedAt) {
458
475
  if (reachedPassport(db, current.journeyId)) throw new ApplicationError("INVALID_TRANSITION");
459
476
  db.prepare("DELETE FROM journey_current_send WHERE journey_id=?").run(current.journeyId);
@@ -2265,6 +2282,14 @@ function putSessionUsefulness(db, value) {
2265
2282
  const privateReviewHash = (value) => textDigest(JSON.stringify(value));
2266
2283
  //#endregion
2267
2284
  //#region packages/local-host/src/store/journey/terms/aliases.ts
2285
+ function scanAliasRequestKey(alias, requestKey) {
2286
+ const digest = textDigest(JSON.stringify([
2287
+ "scan-alias-v1",
2288
+ alias,
2289
+ requestKey
2290
+ ]));
2291
+ return `${digest.slice(0, 8)}-${digest.slice(8, 12)}-4${digest.slice(13, 16)}-8${digest.slice(17, 20)}-${digest.slice(20, 32)}`;
2292
+ }
2268
2293
  const scanAliasQuerySchema = z.object({ journeyId: z.uuid() }).strict();
2269
2294
  const scanAliasSelectionSchema = z.array(z.object({
2270
2295
  alias: scanAliasResourceSchema,
@@ -2288,7 +2313,8 @@ function selectScanAliases(db, input) {
2288
2313
  requireActiveBatch(state, canonical.header);
2289
2314
  if (canonical.header.digest !== alias.canonicalBatchDigest || target.header.digest !== alias.targetBatchDigest || canonical.header.workId !== target.header.workId || target.header.id !== alias.targetBatchId) throw new ApplicationError("CORRUPTED_STATE");
2290
2315
  if (!canonical.termPack) return [];
2291
- const packs = db.prepare(`SELECT p.payload_ref_json,p.request_hash,r.request_hash AS receipt_hash FROM journey_term_pack_batches m JOIN journey_term_packs p ON p.request_key=m.request_key LEFT JOIN journey_receipts r ON r.request_key=p.request_key AND r.journey_id=p.journey_id WHERE m.batch_id=? AND p.journey_id=? ORDER BY p.rowid`).all(canonical.header.id, input.journeyId).map((pack) => {
2316
+ const packs = db.prepare(`SELECT p.payload_ref_json,p.request_hash,r.request_hash AS receipt_hash FROM journey_term_pack_batches m JOIN journey_term_packs p ON p.request_key=m.request_key LEFT JOIN journey_receipts r ON r.request_key=p.request_key AND r.journey_id=p.journey_id WHERE m.batch_id=? AND p.journey_id=?
2317
+ AND NOT EXISTS (SELECT 1 FROM journey_resources w WHERE w.journey_id=p.journey_id AND w.kind='withdrawn-term-pack' AND w.id=m.batch_id||':'||json_extract(p.payload_ref_json,'$.packId')) ORDER BY p.rowid`).all(canonical.header.id, input.journeyId).map((pack) => {
2292
2318
  if (typeof pack["payload_ref_json"] !== "string" || typeof pack["receipt_hash"] !== "string") throw new ApplicationError("CORRUPTED_STATE");
2293
2319
  const reference = termPackReferenceSchema.parse(JSON.parse(pack["payload_ref_json"]));
2294
2320
  if (reference.requestHash !== pack["request_hash"] || reference.requestHash !== pack["receipt_hash"]) throw new ApplicationError("CORRUPTED_STATE");
@@ -2307,6 +2333,107 @@ function selectScanAliases(db, input) {
2307
2333
  });
2308
2334
  }
2309
2335
  //#endregion
2336
+ //#region packages/local-host/src/store/journey/terms/reopen.ts
2337
+ /** A permanent fence for an accepted pack whose empty coverage was withdrawn. */
2338
+ function withdrawnTermPack(db, journeyId, batchId, packId) {
2339
+ return Boolean(db.prepare("SELECT 1 FROM journey_resources WHERE journey_id=? AND kind='withdrawn-term-pack' AND id=?").get(journeyId, batchId + ":" + packId));
2340
+ }
2341
+ function reopenEmptyReview(db, input) {
2342
+ const state = requireReviewState(db, input.journeyId);
2343
+ if (state.scope.status !== "confirmed" || ![
2344
+ "processing",
2345
+ "review",
2346
+ "ready"
2347
+ ].includes(state.phase) || db.prepare("SELECT 1 FROM journey_current_send WHERE journey_id=?").get(input.journeyId)) throw new ApplicationError("INVALID_TRANSITION");
2348
+ const row = db.prepare("SELECT * FROM journey_batches WHERE journey_id=? AND id=?").get(input.journeyId, input.batchId);
2349
+ if (!row) throw new ApplicationError("UNKNOWN_ITEM");
2350
+ const batch = parseStoredBatch(row);
2351
+ const work = requireActiveBatch(state, batch.header);
2352
+ if (work.access === "drop" || batch.header.digest !== input.batchDigest) throw new ApplicationError("REVISION_CONFLICT");
2353
+ if (input.kind === "apply" && withdrawnTermPack(db, input.journeyId, input.batchId, input.expectedPackId)) return {
2354
+ status: "replayed",
2355
+ packId: input.expectedPackId
2356
+ };
2357
+ const ref = batch.termPack;
2358
+ if (!ref || input.kind === "apply" && ref.packId !== input.expectedPackId) throw new ApplicationError("REVISION_CONFLICT");
2359
+ if (db.prepare("SELECT 1 FROM journey_resources WHERE journey_id=? AND kind='scan-alias' AND id=?").get(input.journeyId, input.batchId)) throw new ApplicationError("INVALID_SELECTION");
2360
+ const aliases = db.prepare(`SELECT b.*,a.json AS alias_json FROM journey_batches b ${activeBatchJoin}
2361
+ JOIN journey_resources a ON a.journey_id=b.journey_id AND a.kind='scan-alias' AND a.id=b.id
2362
+ WHERE b.journey_id=? AND json_extract(a.json,'$.canonicalBatchId')=?`).all(input.journeyId, input.batchId).map((row) => {
2363
+ if (typeof row["alias_json"] !== "string") throw new ApplicationError("CORRUPTED_STATE");
2364
+ const alias = scanAliasResourceSchema.parse(JSON.parse(row["alias_json"]));
2365
+ const stored = parseStoredBatch(row);
2366
+ const packId = scanAliasRequestKey(alias, ref.idempotencyKey);
2367
+ if (stored.termPack && stored.termPack.packId !== packId) throw new ApplicationError("REVISION_CONFLICT");
2368
+ return {
2369
+ batch: stored,
2370
+ packId
2371
+ };
2372
+ });
2373
+ const attempts = [{
2374
+ batch,
2375
+ packId: ref.packId
2376
+ }, ...aliases];
2377
+ const targets = attempts.map((a) => a.batch).filter((b) => b.termPack);
2378
+ for (const target of targets) {
2379
+ const pointer = target.termPack;
2380
+ if (!pointer || target.header.workId !== work.id) throw new ApplicationError("CORRUPTED_STATE");
2381
+ const receipt = db.prepare(`SELECT p.request_hash, r.request_hash AS confirmed_hash, json_extract(p.json,'$.terms.kept') AS kept
2382
+ FROM journey_term_packs p JOIN journey_receipts r ON r.journey_id=p.journey_id AND r.request_key=p.request_key
2383
+ WHERE p.journey_id=? AND p.request_key=?`).get(input.journeyId, pointer.idempotencyKey);
2384
+ if (!receipt || receipt["request_hash"] !== pointer.requestHash || receipt["confirmed_hash"] !== pointer.requestHash) throw new ApplicationError("CORRUPTED_STATE");
2385
+ if (receipt["kept"] !== 0 || db.prepare("SELECT 1 FROM journey_term_occurrences WHERE journey_id=? AND batch_id=? LIMIT 1").get(input.journeyId, target.header.id)) throw new ApplicationError("INVALID_SELECTION");
2386
+ }
2387
+ if (input.kind === "inspect") return {
2388
+ status: "eligible",
2389
+ packId: ref.packId
2390
+ };
2391
+ if (work.completedBatches < targets.length) throw new ApplicationError("CORRUPTED_STATE");
2392
+ for (const { batch: target, packId } of attempts) {
2393
+ const id = target.header.id + ":" + packId;
2394
+ db.prepare("INSERT INTO journey_resources VALUES(?,?,?,?)").run(input.journeyId, "withdrawn-term-pack", id, JSON.stringify({
2395
+ kind: "withdrawn-term-pack",
2396
+ id,
2397
+ journeyId: input.journeyId,
2398
+ batchId: target.header.id,
2399
+ packId
2400
+ }));
2401
+ db.prepare("UPDATE journey_batches SET review_ref_json=NULL,review_sequence=NULL WHERE id=?").run(target.header.id);
2402
+ }
2403
+ if (commitJourney(db, {
2404
+ state: {
2405
+ ...state,
2406
+ readingComplete: false,
2407
+ phase: "processing",
2408
+ privacy: {
2409
+ ...state.privacy,
2410
+ packageApproval: null,
2411
+ confirmedAnswersRevision: null
2412
+ },
2413
+ works: state.works.map((item) => item.id === work.id ? {
2414
+ ...item,
2415
+ status: "reading",
2416
+ revision: item.revision + 1,
2417
+ completedBatches: item.completedBatches - targets.length,
2418
+ artifact: null
2419
+ } : item)
2420
+ },
2421
+ expectedRevision: state.revision,
2422
+ requestKey: input.idempotencyKey,
2423
+ requestHash: textDigest(JSON.stringify(input)),
2424
+ event: {
2425
+ journeyId: input.journeyId,
2426
+ at: (/* @__PURE__ */ new Date()).toISOString(),
2427
+ kind: "batch",
2428
+ source: "conversation"
2429
+ }
2430
+ }).status !== "applied") throw new ApplicationError("REVISION_CONFLICT");
2431
+ return {
2432
+ status: "reopened",
2433
+ packId: ref.packId
2434
+ };
2435
+ }
2436
+ //#endregion
2310
2437
  //#region packages/local-host/src/store/journey/finding-groups.ts
2311
2438
  function findingPreference(db, key) {
2312
2439
  const choice = db.prepare("SELECT choice FROM journey_preferences WHERE group_key=?").get(key)?.["choice"];
@@ -2442,6 +2569,7 @@ function selectTerms(db, query) {
2442
2569
  const batches = input.units.map((unit) => {
2443
2570
  const row = db.prepare("SELECT * FROM journey_batches WHERE journey_id=? AND id=?").get(input.journeyId, unit.batchId);
2444
2571
  if (!row) throw new ApplicationError("UNKNOWN_ITEM");
2572
+ if (withdrawnTermPack(db, input.journeyId, unit.batchId, input.packId)) throw new ApplicationError("REVISION_CONFLICT");
2445
2573
  const batch = parseStoredBatch(row);
2446
2574
  const alias = db.prepare("SELECT json FROM journey_resources WHERE journey_id=? AND kind='scan-alias' AND id=?").get(input.journeyId, unit.batchId);
2447
2575
  if (alias) {
@@ -2449,7 +2577,8 @@ function selectTerms(db, query) {
2449
2577
  const source = db.prepare("SELECT b.review_ref_json FROM journey_batches b WHERE b.journey_id=? AND b.id=json_extract(?,'$.canonicalBatchId')").get(input.journeyId, alias["json"]);
2450
2578
  if (!source || source["review_ref_json"] === null) throw new ApplicationError("SCOPE_NOT_READY");
2451
2579
  }
2452
- requireActiveBatch(state, batch.header);
2580
+ const work = requireActiveBatch(state, batch.header);
2581
+ if (work.reviewInterruptions?.some((hold) => hold.inputRevision === work.inputRevision && hold.batchIds.includes(unit.batchId)) || db.prepare("SELECT 1 FROM journey_resources WHERE journey_id=? AND kind='closed-reader' AND id=?").get(state.journeyId, unit.batchId + ":" + input.packId)) throw new ApplicationError("REVISION_CONFLICT");
2453
2582
  if (batch.header.digest !== unit.batchDigest) throw new ApplicationError("REVISION_CONFLICT");
2454
2583
  return batch;
2455
2584
  });
@@ -2613,7 +2742,8 @@ const termReuseSelectionSchema = z.object({
2613
2742
  */
2614
2743
  function selectTermReuse(db, input) {
2615
2744
  const state = requireReviewState(db, input.journeyId);
2616
- const targets = db.prepare(`SELECT b.* FROM journey_batches b ${activeBatchJoin} WHERE b.journey_id=? AND b.work_id=? AND b.review_ref_json IS NULL ${notScanAlias} AND json_extract(b.header_json,'\$.unit.sessionId')=? ORDER BY b.rowid`).all(input.journeyId, input.workId, input.sessionId).map(parseStoredBatch).filter((batch) => isLegacyBatch(db, batch.header));
2745
+ const targets = db.prepare(`SELECT b.* FROM journey_batches b ${activeBatchJoin} WHERE b.journey_id=? AND b.work_id=? AND b.review_ref_json IS NULL ${notScanAlias}
2746
+ AND NOT EXISTS (SELECT 1 FROM journey_resources wr WHERE wr.journey_id=b.journey_id AND wr.kind='withdrawn-term-pack' AND json_extract(wr.json,'$.batchId')=b.id) AND json_extract(b.header_json,'\$.unit.sessionId')=? ORDER BY b.rowid`).all(input.journeyId, input.workId, input.sessionId).map(parseStoredBatch).filter((batch) => isLegacyBatch(db, batch.header));
2617
2747
  if (!targets.length) return {
2618
2748
  targets,
2619
2749
  origins: []
@@ -3557,7 +3687,7 @@ function readInventoryFamilies(db, state) {
3557
3687
  //#endregion
3558
3688
  //#region packages/local-host/src/store/journey/resources.ts
3559
3689
  function saveJourneyResource(db, resource) {
3560
- if (resource.kind === "scan-canonical" || resource.kind === "scan-alias" || resource.kind === "b-attempt" || resource.kind === "preparation-start" || resource.kind === "preparation-settlement" || resource.kind === "b-usefulness" || resource.kind === "b-candidate" || resource.kind === "b-repair" || resource.kind === "b-session" || resource.kind === "b-reread" || resource.kind === "b-reconciliation" || resource.kind === "correction-report") throw new ApplicationError("INVALID_TRANSITION");
3690
+ if (resource.kind === "closed-reader" || resource.kind === "withdrawn-term-pack" || resource.kind === "scan-canonical" || resource.kind === "scan-alias" || resource.kind === "b-attempt" || resource.kind === "preparation-start" || resource.kind === "preparation-settlement" || resource.kind === "b-usefulness" || resource.kind === "b-candidate" || resource.kind === "b-repair" || resource.kind === "b-session" || resource.kind === "b-reread" || resource.kind === "b-reconciliation" || resource.kind === "correction-report") throw new ApplicationError("INVALID_TRANSITION");
3561
3691
  const state = readJourney(db, resource.journeyId);
3562
3692
  if (!state?.consent) throw new ApplicationError("CONFIRMATION_REQUIRED");
3563
3693
  if (state.pendingHideId && (resource.kind === "base" || resource.kind === "scan-session" || resource.kind === "static-session" || resource.kind === "output")) throw new ApplicationError("SCOPE_NOT_READY");
@@ -3644,6 +3774,64 @@ function readJourneyResources(db, input) {
3644
3774
  }
3645
3775
  //#endregion
3646
3776
  //#region packages/local-host/src/store/journey/preparation.ts
3777
+ function projection(fields) {
3778
+ const sql = `SELECT state_json ->> '$.formatVersion' AS format, ${fields.map((key) => `state_json -> '$.${key}' AS "${key}"`).join(",")} FROM journey_runs WHERE id=?`;
3779
+ return (db, journeyId) => {
3780
+ const row = db.prepare(sql).get(journeyId);
3781
+ if (!row) return null;
3782
+ if (row["format"] !== 10) throw new StoreError("OBSOLETE_SCHEMA", "Stored Journey preparation format is obsolete.");
3783
+ const result = {};
3784
+ for (const key of fields) {
3785
+ const value = row[key];
3786
+ if (typeof value === "string") result[key] = JSON.parse(value);
3787
+ }
3788
+ return result;
3789
+ };
3790
+ }
3791
+ const selectPermission = projection(preparationPermissionSchema.keyof().options);
3792
+ const selectState = projection(preparationStateSchema.keyof().options);
3793
+ function readPreparationPermission(db, journeyId) {
3794
+ return preparationPermissionSchema.nullable().parse(selectPermission(db, journeyId));
3795
+ }
3796
+ function readPreparationState(db, journeyId) {
3797
+ return preparationStateSchema.nullable().parse(selectState(db, journeyId));
3798
+ }
3799
+ /** Merge the small set of changed Work rows into the current state under its revision fence.
3800
+ * The normal commit owns its receipts, input generations, hide and correction invalidation. */
3801
+ function updatePreparation(db, input) {
3802
+ const current = readJourney(db);
3803
+ if (!current || current.journeyId !== input.journeyId) throw new ApplicationError("REVISION_CONFLICT");
3804
+ const replacements = new Map(input.works.map((work) => [work.id, work]));
3805
+ if (current.revision === input.expectedRevision && (replacements.size !== input.works.length || input.works.some((work) => !current.works.some((item) => item.id === work.id)))) throw new ApplicationError("INVALID_ARGUMENTS");
3806
+ const result = commitJourney(db, {
3807
+ expectedRevision: input.expectedRevision,
3808
+ requestKey: input.requestKey,
3809
+ requestHash: textDigest(JSON.stringify(input)),
3810
+ event: {
3811
+ journeyId: input.journeyId,
3812
+ kind: input.kind,
3813
+ source: "system",
3814
+ at: input.at
3815
+ },
3816
+ state: {
3817
+ ...current,
3818
+ works: current.works.map((work) => replacements.get(work.id) ?? work),
3819
+ phase: input.phase,
3820
+ readingComplete: input.readingComplete,
3821
+ readingStartedAt: input.readingStartedAt,
3822
+ privacy: input.clearPackageApproval ? {
3823
+ ...current.privacy,
3824
+ packageApproval: null
3825
+ } : current.privacy
3826
+ }
3827
+ });
3828
+ const state = readPreparationState(db, result.state.journeyId);
3829
+ if (!state) throw new ApplicationError("CORRUPTED_STATE");
3830
+ return {
3831
+ status: result.status,
3832
+ state
3833
+ };
3834
+ }
3647
3835
  function readPreparationReceipts(db, journeyId) {
3648
3836
  const starts = [], settlements = [];
3649
3837
  for (const row of db.prepare("SELECT kind,id,json FROM journey_resources WHERE journey_id=? AND kind IN ('preparation-start','preparation-settlement')").all(journeyId)) {
@@ -4571,9 +4759,11 @@ function isolateJourneyRoots(db, input) {
4571
4759
  const access = {
4572
4760
  access: parent.access,
4573
4761
  accessOrigin: parent.accessOrigin,
4574
- accessConfirmed: parent.accessConfirmed
4762
+ accessConfirmed: parent.accessConfirmed,
4763
+ ...parent.accessChoice ? { accessChoice: parent.accessChoice } : {},
4764
+ ...parent.accountAccessConflict === void 0 ? {} : { accountAccessConflict: parent.accountAccessConflict }
4575
4765
  };
4576
- const sameAccess = (work) => work.access === access.access && work.accessOrigin === access.accessOrigin && work.accessConfirmed === access.accessConfirmed;
4766
+ const sameAccess = (work) => work.access === access.access && work.accessOrigin === access.accessOrigin && work.accessConfirmed === access.accessConfirmed && work.accessChoice?.channel === access.accessChoice?.channel && work.accessChoice?.at === access.accessChoice?.at && work.accountAccessConflict === access.accountAccessConflict;
4577
4767
  const targetId = Array.from({ length: state.works.length + 1 }, (_, ordinal) => isolatedWorkId(parent.id, ordinal)).find((id) => {
4578
4768
  const work = state.works.find((item) => item.id === id);
4579
4769
  return !work || sameAccess(work);
@@ -4689,7 +4879,7 @@ function finishJourneyDescription(db, packet) {
4689
4879
  id: input.idempotencyKey
4690
4880
  })[0];
4691
4881
  if (original?.kind !== "work-description" || original.appliedRequestHash !== requestHash || original.preparedWorkRevision !== work.revision || original.snapshotId !== state.snapshotId || original.workId !== work.id || original.inputRevision !== work.inputRevision || original.explicitTitleId !== work.explicitTitleId || original.decisionDigest !== input.decisionDigest || original.confirmedHideRevision !== (state.confirmedHideRevision ?? 0)) throw new ApplicationError("REVISION_CONFLICT");
4692
- const preserveAccess = state.scope.status === "confirmed" || work.accessOrigin === "explicit" || work.accessOrigin === "delegate";
4882
+ const preserveAccess = state.scope.status === "confirmed" || work.accessOrigin === "explicit" || work.accessOrigin === "account" || work.accessOrigin === "delegate";
4693
4883
  const modelAccess = input.checkpoint === "complete" ? input.suggest : null;
4694
4884
  const keepAccess = preserveAccess || modelAccess === null || modelAccess === "drop";
4695
4885
  return commitJourney(db, {
@@ -4856,7 +5046,10 @@ function admitScanSession(db, input) {
4856
5046
  const state = readJourney(db, input.resource.journeyId);
4857
5047
  if (!state) throw new ApplicationError("UNKNOWN_ITEM");
4858
5048
  return {
4859
- state,
5049
+ state: preparationStateSchema.strip().parse({
5050
+ ...state,
5051
+ works: state.works.filter((work) => work.id === input.resource.workId)
5052
+ }),
4860
5053
  priors: input.headers.map((header) => admitReviewBatch(db, header))
4861
5054
  };
4862
5055
  }
@@ -6882,13 +7075,24 @@ const operations = {
6882
7075
  "journey.batch.put": define(true, batchPublicationSchema, putStoredBatch, nothing),
6883
7076
  "journey.batch.admit": define(false, batchHeaderSchema, admitReviewBatch, (value) => storedBatchSchema.nullable().parse(value)),
6884
7077
  "journey.scan.admit": define(true, scanAdmissionSchema, admitScanSession, (value) => z.object({
6885
- state: nativeJourneySchema,
7078
+ state: preparationStateSchema,
6886
7079
  priors: z.array(storedBatchSchema.nullable())
6887
7080
  }).strict().parse(value)),
6888
7081
  "journey.scan.put": define(true, scanBatchesSchema, putScanBatches, nothing),
6889
7082
  "journey.batch.read": define(false, journeyBatchReadSchema, selectReviewBatch, (value) => batchSelectionSchema.parse(value)),
6890
7083
  "journey.review.attempts": define(false, reviewAttemptsQuerySchema, readReviewAttempts, (value) => reviewAttemptsResultSchema.parse(value)),
7084
+ "journey.preparation.state": define(false, z.object({ journeyId: z.uuid() }), (db, { journeyId }) => readPreparationState(db, journeyId), (value) => preparationStateSchema.nullable().parse(value)),
7085
+ "journey.preparation.permission": define(false, z.object({ journeyId: z.uuid() }), (db, { journeyId }) => readPreparationPermission(db, journeyId), (value) => preparationPermissionSchema.nullable().parse(value)),
7086
+ "journey.preparation.update": define(true, preparationUpdateSchema, updatePreparation, (value) => z.object({
7087
+ status: z.enum([
7088
+ "applied",
7089
+ "stale",
7090
+ "replayed"
7091
+ ]),
7092
+ state: preparationStateSchema
7093
+ }).strict().parse(value)),
6891
7094
  "journey.current": define(false, z.object({ journeyId: z.uuid().optional() }), (db, input) => readJourney(db, input.journeyId), (value) => nativeJourneySchema.nullable().parse(value)),
7095
+ "journey.terms.reopen-empty": define(true, emptyReviewCorrectionSchema, reopenEmptyReview, (value) => emptyReviewCorrectionResultSchema.parse(value)),
6892
7096
  "journey.terms.select": define(false, termsQuerySchema, selectTerms, (value) => termsSelectionSchema.parse(value)),
6893
7097
  "journey.terms.finish": define(true, termsFinishSchema, finishTerms, (value) => reviewTermsResultSchema.parse(value)),
6894
7098
  "journey.terms.read": define(false, journeyTermsQuerySchema, readJourneyTerms, (value) => z.array(journeyTermSchema).parse(value)),
@@ -7410,4 +7614,4 @@ function assertStoreHeap(limitBytes = getHeapStatistics().heap_size_limit) {
7410
7614
  return limitBytes;
7411
7615
  }
7412
7616
  //#endregion
7413
- export { checkVersion as $, resourceSelectionQuerySchema as A, termReuseQuerySchema as B, loadAcceptedReview as C, aggregateUsefulness as D, loadPrivateResource as E, validateUnitCandidate as F, requireActiveBatch as G, batchHeader as H, reuseQuerySchema as I, validateDatabase as J, initializeAndClaim as K, batchQuery as L, evaluateUnitEvidence as M, sameCandidateRequest as N, sessionUsefulnessRecommendation as O, validateCandidateAttempt as P, validateState as Q, isPrivateResourceKind as R, validateSessionBindings as S, loadBatch as T, createJourneyPayloadFiles as U, privateReviewHash as V, validateBatchBody as W, readBundleDirectory as X, withDatabase as Y, readStoredState as Z, loadReconciliation as _, isCoordinationBusy as _t, decodeError as a, hasResetMarker as at, validatePreparationSelection as b, dropsAnchor as c, privatePath as ct, parseDelivery as d, stateDirectory as dt, schemaVersion as et, validateCandidate as f, syncDirectory as ft, loadPreparedBodies as g, errorCode as gt, loadPreparation as h, StoreError as ht, createExecutor as i, finishReset as it, inspectionQuerySchema as j, sessionQuery as k, replySchema as l, readResetMarker as lt, loadStatus as m, withFileCheckTimings as mt, releaseInheritedHeapFlags as n, createDatabaseFile as nt, ProtocolError as o, inspectStateFiles as ot, classificationJourneyCommit as p, withStateSnapshot as pt, ownership as q, storeHeapLimits as r, databaseName as rt, allowedReplies as s, present as st, assertStoreHeap as t, beginReset as tt, operations as u, recheckPrivatePath as ut, loadRereadBodies as v, isSqliteCorruption as vt, loadAuthenticatedReview as w, validateSessionReconciliation as x, loadSessionBodies as y, privateResourceHeader as z };
7617
+ export { validateState as $, resourceSelectionQuerySchema as A, termReuseQuerySchema as B, loadAcceptedReview as C, aggregateUsefulness as D, loadPrivateResource as E, validateUnitCandidate as F, validateBatchBody as G, privateReviewHash as H, reuseQuerySchema as I, ownership as J, requireActiveBatch as K, batchQuery as L, evaluateUnitEvidence as M, sameCandidateRequest as N, sessionUsefulnessRecommendation as O, validateCandidateAttempt as P, readStoredState as Q, isPrivateResourceKind as R, validateSessionBindings as S, loadBatch as T, batchHeader as U, scanAliasRequestKey as V, createJourneyPayloadFiles as W, withDatabase as X, validateDatabase as Y, readBundleDirectory as Z, loadReconciliation as _, errorCode as _t, decodeError as a, finishReset as at, validatePreparationSelection as b, dropsAnchor as c, present as ct, parseDelivery as d, recheckPrivatePath as dt, checkVersion as et, validateCandidate as f, stateDirectory as ft, loadPreparedBodies as g, StoreError as gt, loadPreparation as h, withFileCheckTimings as ht, createExecutor as i, databaseName as it, inspectionQuerySchema as j, sessionQuery as k, replySchema as l, privatePath as lt, loadStatus as m, withStateSnapshot as mt, releaseInheritedHeapFlags as n, beginReset as nt, ProtocolError as o, hasResetMarker as ot, classificationJourneyCommit as p, syncDirectory as pt, initializeAndClaim as q, storeHeapLimits as r, createDatabaseFile as rt, allowedReplies as s, inspectStateFiles as st, assertStoreHeap as t, schemaVersion as tt, operations as u, readResetMarker as ut, loadRereadBodies as v, isCoordinationBusy as vt, loadAuthenticatedReview as w, validateSessionReconciliation as x, loadSessionBodies as y, isSqliteCorruption as yt, privateResourceHeader as z };