@gitroll/tacitt-cli 0.6.14 → 0.6.15

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 (37) hide show
  1. package/dist/{agent-setup-CEPQfjBv.mjs → agent-setup-Bv73EZ2b.mjs} +2 -2
  2. package/dist/agent-setup-worker.mjs +5 -5
  3. package/dist/cli.mjs +10 -10
  4. package/dist/codex-title-worker.mjs +1 -1
  5. package/dist/configured-preview-worker.mjs +5 -5
  6. package/dist/configured-session-worker.mjs +5 -5
  7. package/dist/{frame-CRm99088.mjs → frame-0v_mLdvG.mjs} +1 -1
  8. package/dist/{heap-Bh9tySJp.mjs → heap-aWke8lrK.mjs} +3 -3
  9. package/dist/inspection-worker.mjs +4 -4
  10. package/dist/{job-CsWKWcM4.mjs → job-ILnqW1ge.mjs} +4 -4
  11. package/dist/{locate-CP2t26j2.mjs → locate-LpCex6Xj.mjs} +5 -94
  12. package/dist/{model-ChHuVKoB.mjs → model-BEWsfkIU.mjs} +1 -1
  13. package/dist/operation-executor.mjs +3 -3
  14. package/dist/preview-worker.mjs +1 -1
  15. package/dist/privacy-validator.mjs +1 -1
  16. package/dist/{query-D28k3XOr.mjs → query-BX8QwaBi.mjs} +93 -9
  17. package/dist/{run-CiJmV__5.mjs → run-P_uZgSYa.mjs} +4 -4
  18. package/dist/{src-B3il9x5F.mjs → src-CHy3-siB.mjs} +59 -26
  19. package/dist/{src-CWMrGXJK.mjs → src-ikYI-1c5.mjs} +4 -4
  20. package/dist/{src-CmvEJj46.mjs → src-sERffIem.mjs} +8 -31
  21. package/dist/store-database-worker.mjs +1 -1
  22. package/package.json +1 -1
  23. package/skills/tacitt/SKILL.md +1 -1
  24. package/skills/tacitt/references/bootstrap.md +1 -1
  25. package/skills/tacitt/references/classification.md +2 -0
  26. package/skills/tacitt/references/commands.md +5 -4
  27. package/skills/tacitt/references/journey.md +2 -2
  28. package/skills/tacitt/references/parallel-review.md +3 -3
  29. package/skills/tacitt/scripts/INDEX.md +4 -4
  30. package/skills/tacitt/scripts/journey-classification-progress.mjs +6 -0
  31. package/skills/tacitt/scripts/journey-classification-tasks.mjs +9 -1
  32. package/skills/tacitt/scripts/journey-classification-validation.d.mts +14 -1
  33. package/skills/tacitt/scripts/journey-classification-validation.mjs +45 -0
  34. package/skills/tacitt/scripts/journey-consolidation-submit.mjs +22 -5
  35. package/skills/tacitt/scripts/journey-scan-timing.mjs +1 -1
  36. package/skills/tacitt/scripts/journey.mjs +15 -7
  37. package/skills/tacitt/scripts/review.mjs +2 -2
@@ -1,5 +1,5 @@
1
- import { $ as createCodePointIndex, A as bLegacyBindings, B as bIssue, Bt as reviewTermsResultSchema, C as validateBReview, Ct as hideConfirmSchema, Dt as cardAnswerActionSchema, E as validateBUnit, Et as journeyConfigurationSchema, F as bLiterals, G as bReviewSubmissionSchema, Gt as termTypes, H as unownedBObligations, Ht as termOverrideValueSchema, I as uncoveredBObligations, In as createStaticProcessor, J as bAnchorKey, Jt as sessionPeriodSchema, K as bSessionDecisionSchema, Kt as deselectOutsidePeriod, L as indexBRanges, Ln as staticDefaults, Lt as emptyReviewCorrectionSchema, M as semanticFindingSchema, Mn as inspectionSemanticVersions, Mt as packageConfirmActionSchema, N as bLiteralAdmitted, Nt as privacyResolveSchema, O as bDuplicateMirror, Ot as cardsConfirmActionSchema, P as bLiteralTerms, Pn as gapReasons, Pt as privacyTermKey, Q as codePointSlice, R as bAdoptedObligations, Rn as staticOptions, S as validateBClassification, T as validateBFields, Tt as hidePlanResourceSchema, U as bRepresentationSchema, Un as cleanupImplementationVersion, Ut as termTypeInfo, V as bOverlaps, Vt as termAssessmentSchema, W as bRetainedGroupSchema, Wn as entityDictionaryVersion, Wt as termTypeSchema, X as bDigest, Xt as journeyProfileAddressActionSchema, Y as bEvidenceReader, Yt as emptyJourneyProfile, Z as codePointLength, Zt as journeyProfileLinksActionSchema, _ as reviewAlignmentInputSchema, _n as inventorySchema, _r as scalarBefore, _t as reviewInterruptionSchema, an as ApplicationError, at as bMethod, b as validateReviewAlignment, bt as delegatedDecisionSchema, c as reviewBatchSchema, cr as detectStatic, ct as journeyChoiceSchema, d as reviewSubmitSchema, dn as reviewFamilies, dt as journeyIdSchema, er as validateProfile, et as subtractBRanges, f as textDigest, fn as sourceGraph, ft as journeyKindSchema, g as reviewAlignmentAuditSchema, gn as fileIdentitySchema, gr as scalarAfter, gt as nativeJourneySchema, hn as sourceSnapshotId, ht as journeyWorkTypeSchema, in as classificationTitleMaskSchema, ir as copyData, it as bIdSchema, j as bUnitFragments, jn as currentDetectorVersion, jt as emptyJourneyPrivacy, kt as cardsSubmitSchema, l as reviewChunkSchema, ln as familyNeedsReview, lt as journeyDigestSchema, m as findingReferenceKey, mn as observeSource, mt as journeyProjectTypeSchema, nn as respondProfileLinks, nt as bAnchorSchema, o as compileBFindings, on as UnfinishedJourneyError, ot as consentedWithoutAccount, p as validateReview, pn as sourceKey, pr as createEntityMatcher, pt as journeyPreferenceScopeSchema, q as bTermSchema, rr as validatePlan, rt as bCountSchema, s as reviewBatchChunks, sn as safeCode, st as journeyAccessSchema, tr as orderProfile, u as reviewDigest, un as familySelected, ur as staticCategories, ut as journeyExceptionSchema, v as reviewAlignmentSchema, vr as defaultDetectionLimits, vt as journeyGuidanceSchema, w as createBAnalysisUnits, wt as hidePlanInputSchema, xn as nativeSourceIdentitySchema, xt as journeyDescribeSchema, y as reviewFindingsDigest, yn as sourcePartSchema, yt as journeyInstructionInputSchema, zt as reviewTermsInputSchema } from "./locate-CP2t26j2.mjs";
2
- import { ZodError, z } from "zod";
1
+ import { $ as createCodePointIndex, A as bLegacyBindings, B as bIssue, Bt as reviewTermsResultSchema, C as validateBReview, Ct as hideConfirmSchema, Dt as cardAnswerActionSchema, E as validateBUnit, Et as journeyConfigurationSchema, F as bLiterals, G as bReviewSubmissionSchema, Gt as termTypes, H as unownedBObligations, Ht as termOverrideValueSchema, I as uncoveredBObligations, In as createStaticProcessor, J as bAnchorKey, Jt as sessionPeriodSchema, K as bSessionDecisionSchema, Kt as deselectOutsidePeriod, L as indexBRanges, Ln as staticDefaults, Lt as emptyReviewCorrectionSchema, M as semanticFindingSchema, Mn as inspectionSemanticVersions, Mt as packageConfirmActionSchema, N as bLiteralAdmitted, Nt as privacyResolveSchema, O as bDuplicateMirror, Ot as cardsConfirmActionSchema, P as bLiteralTerms, Pn as gapReasons, Pt as privacyTermKey, Q as codePointSlice, R as bAdoptedObligations, Rn as staticOptions, S as validateBClassification, T as validateBFields, Tt as hidePlanResourceSchema, U as bRepresentationSchema, Un as cleanupImplementationVersion, Ut as termTypeInfo, V as bOverlaps, Vt as termAssessmentSchema, W as bRetainedGroupSchema, Wn as entityDictionaryVersion, Wt as termTypeSchema, X as bDigest, Xt as journeyProfileAddressActionSchema, Y as bEvidenceReader, Yt as emptyJourneyProfile, Z as codePointLength, Zt as journeyProfileLinksActionSchema, _ as reviewAlignmentInputSchema, _n as inventorySchema, _r as scalarBefore, _t as reviewInterruptionSchema, an as ApplicationError, at as bMethod, b as validateReviewAlignment, bt as delegatedDecisionSchema, c as reviewBatchSchema, cr as detectStatic, ct as journeyChoiceSchema, d as reviewSubmitSchema, dn as reviewFamilies, dt as journeyIdSchema, er as validateProfile, et as subtractBRanges, f as textDigest, fn as sourceGraph, ft as journeyKindSchema, g as reviewAlignmentAuditSchema, gn as fileIdentitySchema, gr as scalarAfter, gt as nativeJourneySchema, hn as sourceSnapshotId, ht as journeyWorkTypeSchema, in as classificationTitleMaskSchema, ir as copyData, it as bIdSchema, j as bUnitFragments, jn as currentDetectorVersion, jt as emptyJourneyPrivacy, kt as cardsSubmitSchema, l as reviewChunkSchema, ln as familyNeedsReview, lt as journeyDigestSchema, m as findingReferenceKey, mn as observeSource, mt as journeyProjectTypeSchema, nn as respondProfileLinks, nt as bAnchorSchema, o as compileBFindings, on as UnfinishedJourneyError, ot as consentedWithoutAccount, p as validateReview, pn as sourceKey, pr as createEntityMatcher, pt as journeyPreferenceScopeSchema, q as bTermSchema, rr as validatePlan, rt as bCountSchema, s as reviewBatchChunks, sn as safeCode, st as journeyAccessSchema, tr as orderProfile, u as reviewDigest, un as familySelected, ur as staticCategories, ut as journeyExceptionSchema, v as reviewAlignmentSchema, vr as defaultDetectionLimits, vt as journeyGuidanceSchema, w as createBAnalysisUnits, wt as hidePlanInputSchema, xn as nativeSourceIdentitySchema, xt as journeyDescribeSchema, y as reviewFindingsDigest, yn as sourcePartSchema, yt as journeyInstructionInputSchema, zt as reviewTermsInputSchema } from "./locate-LpCex6Xj.mjs";
2
+ import { z } from "zod";
3
3
  import { createHash, createHmac, createSecretKey, randomUUID } from "node:crypto";
4
4
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
5
5
  //#region packages/application/src/processing/model.ts
@@ -2321,7 +2321,12 @@ const classificationKnownProjectSchema = z.object({
2321
2321
  repositoryName: safeText,
2322
2322
  workspaceIds: z.array(z.string().min(1).max(128)).min(1),
2323
2323
  rootSessionIds: z.array(z.string().min(1).max(128)),
2324
- titles: z.array(safeText).max(5)
2324
+ titles: z.array(safeText).max(5),
2325
+ /**
2326
+ * ADR 0276 slice H: the one rebuilt account Work whose roots share this identity, given only
2327
+ * when the account has never listed a key of these roots. The Skill joins them to it.
2328
+ */
2329
+ accountWorkId: journeyIdSchema.optional()
2325
2330
  }).strict();
2326
2331
  const classificationAssignmentSchema = z.object({
2327
2332
  rootSessionId: z.string().min(1).max(128),
@@ -3097,20 +3102,33 @@ const journeyClassificationReadSchema = z.object({
3097
3102
  /** Classification-only prefix; omitted callers retain full capture verification. */
3098
3103
  limit: z.number().int().min(1).max(8).optional()
3099
3104
  }).strict();
3105
+ const classificationRootsSchema = z.array(z.string().min(1).max(128)).min(1).max(1e4);
3106
+ /** A new Work the classifier names and describes. */
3107
+ const classificationProjectItemSchema = z.object({
3108
+ projectName: z.string().trim().min(1).max(240),
3109
+ description: z.string().trim().min(1).max(1e3),
3110
+ projectType: journeyProjectTypeSchema,
3111
+ rootSessionIds: classificationRootsSchema,
3112
+ recommendation: z.object({
3113
+ access: journeyAccessSchema,
3114
+ reason: z.string().trim().min(1).max(200)
3115
+ }).strict()
3116
+ }).strict();
3117
+ /**
3118
+ * ADR 0276 slice H: unplaced roots the account has never listed join one rebuilt account Work,
3119
+ * which keeps its id, title, access and marker. It carries no description, project type or
3120
+ * recommendation (Q6).
3121
+ */
3122
+ const classificationAccountJoinSchema = z.object({
3123
+ accountWorkId: journeyIdSchema,
3124
+ rootSessionIds: classificationRootsSchema
3125
+ }).strict();
3126
+ const isClassificationAccountJoin = (item) => "accountWorkId" in item;
3100
3127
  const journeyClassificationSubmitSchema = z.object({
3101
3128
  journeyId: journeyIdSchema,
3102
3129
  expectedRevision: count$10,
3103
3130
  idempotencyKey: journeyIdSchema,
3104
- items: z.array(z.object({
3105
- projectName: z.string().trim().min(1).max(240),
3106
- description: z.string().trim().min(1).max(1e3),
3107
- projectType: journeyProjectTypeSchema,
3108
- rootSessionIds: z.array(z.string().min(1).max(128)).min(1).max(1e4),
3109
- recommendation: z.object({
3110
- access: journeyAccessSchema,
3111
- reason: z.string().trim().min(1).max(200)
3112
- }).strict()
3113
- }).strict()).min(1).max(1e3),
3131
+ items: z.array(z.union([classificationProjectItemSchema, classificationAccountJoinSchema])).min(1).max(1e3),
3114
3132
  /**
3115
3133
  * E2: roots classification has not grouped yet. They wait in one unread Work while the
3116
3134
  * listed Works are confirmed and read; a later submission without them completes it.
@@ -5580,10 +5598,13 @@ const classificationCommitSchema = z.object({
5580
5598
  accountSubject: z.string().min(1).max(128).optional(),
5581
5599
  /** A failed or mismatched late read (ADR 0277 section 5). */
5582
5600
  accountRead: z.enum(["signed-out", "unavailable"]).optional(),
5583
- items: z.array(journeyClassificationSubmitSchema.shape.items.element.extend({
5601
+ /** Empty when every classified root joins a rebuilt account Work. */
5602
+ items: z.array(classificationProjectItemSchema.extend({
5584
5603
  workId: journeyIdSchema,
5585
5604
  workspaceIds: workspaces
5586
- })).min(1).max(1e3),
5605
+ })).max(1e3),
5606
+ /** ADR 0276 slice H: roots added to a rebuilt account Work, one entry per Work. */
5607
+ accountJoins: z.array(classificationAccountJoinSchema.extend({ workspaceIds: workspaces })).max(1e3).optional(),
5587
5608
  pending: z.object({
5588
5609
  workId: journeyIdSchema,
5589
5610
  rootSessionIds: z.array(z.string().min(1).max(128)).min(1).max(1e4),
@@ -5610,10 +5631,30 @@ function classificationPartition(state) {
5610
5631
  function sameAccessRecord(work, other) {
5611
5632
  return work.access === other?.access && work.accessOrigin === other.accessOrigin && work.accessConfirmed === other.accessConfirmed && work.accessChoice?.channel === other.accessChoice?.channel && work.accessChoice?.at === other.accessChoice?.at && work.accountAccessConflict === other.accountAccessConflict;
5612
5633
  }
5634
+ /**
5635
+ * ADR 0276 slice H: the rebuilt Work takes the joined roots and is read again with them. Its
5636
+ * access value never changes. Neither an account proof nor a person's Public choice covered the
5637
+ * new sessions, so Public becomes a suggestion again (D5, agent default); Private stays confirmed.
5638
+ */
5639
+ function joinedAccountWork(work, join) {
5640
+ const scan = resetWorkScan(work);
5641
+ const { accessChoice: _choice, ...unchosen } = scan;
5642
+ return {
5643
+ ...work.access === "public" ? {
5644
+ ...unchosen,
5645
+ accessConfirmed: false
5646
+ } : scan,
5647
+ rootSessionIds: [...work.rootSessionIds, ...join.rootSessionIds],
5648
+ sourceWorkspaceIds: [.../* @__PURE__ */ new Set([...work.sourceWorkspaceIds, ...join.workspaceIds])]
5649
+ };
5650
+ }
5613
5651
  /** The proposal's Works built from the given state's current Works (E2). */
5614
5652
  function classificationWorks(state, plan) {
5615
5653
  const late = plan.late;
5616
- const works = state.works.filter((work) => work.accountWork);
5654
+ const works = state.works.filter((work) => work.accountWork).map((work) => {
5655
+ const join = plan.accountJoins?.find((item) => item.accountWorkId === work.id);
5656
+ return join ? joinedAccountWork(work, join) : work;
5657
+ });
5617
5658
  for (const item of plan.items) {
5618
5659
  const existing = state.works.find((work) => work.placement !== "classifying" && !work.accountWork && work.rootSessionIds.length === item.rootSessionIds.length && work.rootSessionIds.every((root) => item.rootSessionIds.includes(root)));
5619
5660
  const owners = late ? state.works.filter((work) => work.placement === "placed" && work.rootSessionIds.some((root) => item.rootSessionIds.includes(root))) : [];
@@ -13820,8 +13861,7 @@ function reconcileBSession(fields, units, reviews, signal = new AbortController(
13820
13861
  validateBFields(fields);
13821
13862
  const first = units[0];
13822
13863
  if (!first) throw new ApplicationError("INCOMPLETE_REVIEW");
13823
- const rebuild = (lineContext) => createBAnalysisUnits({
13824
- lineContext,
13864
+ const expected = createBAnalysisUnits({
13825
13865
  sessionId: first.sessionId,
13826
13866
  snapshotId: first.snapshotId,
13827
13867
  inputRevision: first.inputRevision,
@@ -13831,13 +13871,6 @@ function reconcileBSession(fields, units, reviews, signal = new AbortController(
13831
13871
  cores: units.toSorted((left, right) => left.ordinal - right.ordinal).map((unit) => unit.coreCodePoints)
13832
13872
  });
13833
13873
  const byId = new Map(units.map((unit) => [unit.id, unit]));
13834
- let current;
13835
- try {
13836
- current = rebuild(true);
13837
- } catch (error) {
13838
- if (!(error instanceof ZodError) && !(error instanceof ApplicationError && error.code === "INCOMPLETE_REVIEW")) throw error;
13839
- }
13840
- const expected = current?.every((unit) => byId.get(unit.id)?.digest === unit.digest) ? current : rebuild(false);
13841
13874
  if (byId.size !== units.length || expected.length !== units.length || expected.some((unit) => byId.get(unit.id)?.digest !== unit.digest)) throw new ApplicationError("INCOMPLETE_REVIEW");
13842
13875
  const seen = /* @__PURE__ */ new Set();
13843
13876
  const validated = reviews.map((review) => {
@@ -14369,4 +14402,4 @@ function equivalenceDuplicates(batch) {
14369
14402
  return bindings.sort();
14370
14403
  }
14371
14404
  //#endregion
14372
- export { validateDetectionSummary as $, hideKeyId as $n, sendReceiptSchema as $r, journeyPlaceSchema as $t, renewBeforeMs as A, selectionSchema as Ai, scanExclusionResourceSchema as An, nativeStartSchema as Ar, validateAttemptAppend as At, createApplication as B, selectFrozenConfiguration as Bi, journeySearchResultSchema as Bn, sendsOnNextAdvance as Br, newJourneyWork as Bt, rebindReviewOverlay as C, bMaskedHits as Ci, scanAliasResourceSchema as Cn, assertJourneyPrivacyReady as Cr, sendActionSchema as Ct, staticallyHiddenFindings as D, commandSchema as Di, scanGitSeedsId as Dn, nativeEventsSchema as Dr, sendStartSchema as Dt, guardedFindingText as E, replacementMarkerPattern as Ei, scanSessionResourceSchema as En, rememberedHideValues as Er, sendRequestSchema as Et, readAccountAvatarUrl as F, recommendParallelism as Fi, journeyInventoryReadSchema as Fn, hostDrives as Fr, classificationCommitSchema as Ft, validateInspectionRequest as G, journeyEventSchema as Gn, staticReviewDigest as Gr, hideFinishSchema as Gt, inspectionStartSchema as H, searchScopeDigest as Hn, nativeAdapterVersions as Hr, journeyContextQuerySchema as Ht, storedCredentialSchema as I, approvedTreatmentSchema as Ii, journeyInventorySchema as In, itemAtPassport as Ir, classificationPartition as It, validateInspectionFence as J, readConfirmedHideChange as Jn, agentSourcePathSegment as Jr, sendOutputQuerySchema as Jt, inspectionQuerySchema as K, confirmedHideAdditions as Kn, staticSessionId as Kr, hidePublishSchema as Kt, sourceIdentityEvidence as L, effectiveTreatmentSchema as Li, journeyPreparationProgressSchema as Ln, obsoleteJourney as Lr, classificationWorks as Lt, deviceAuthorizationSchema as M, producerVersion as Mi, resolveSourceSelection as Mn, advanceStepsSend as Mr, journeyBatchReadSchema as Mt, deviceProfileScopeSchema as N, stateSchema as Ni, projectPreparationProgress as Nn, awaitingPassport as Nr, journeyReviewStorageQuerySchema as Nt, createAccount as O, packageCommandSchema as Oi, scanGitSeedsResource as On, nativeGetSchema as Or, settleCorrections as Ot, profileUrlMatchesIdentity as P, sourceChangePolicy as Pi, journeyInventoryQuerySchema as Pn, currentJourneyAdapters as Pr, applyClassificationProposal as Pt, validateInspectionContribution as Q, addHideIndexTokens as Qn, sendArtifactSchema as Qr, journeyIsolateSchema as Qt, withSourceEvidence as R, defaultScopedSettings as Ri, accountReadStop as Rn, sendOwesPassport as Rr, retireScanGeneration as Rt, rebindReviewAlignment as S, bExceptionRanges as Si, planBRepair as Sn, applyCardAnswers as Sr, replaceSensitiveTermsSchema as St, reviewBatchReuse as T, bWordBounded as Ti, scanPreparationDigest as Tn, reconcileReaderKeeps as Tr, sendPrepareSchema as Tt, validateInspectionBatch as U, journeyCommitResultSchema as Un, mergePrivacyTerms as Ur, journeyContextSchema as Ut, inspectionCancelSchema as V, staticCatalogRef as Vi, journeySearchSaveSchema as Vn, metadataProcessingVersion as Vr, reviewCompileSchema as Vt, validateInspectionBatches as W, journeyCommitSchema as Wn, hasCurrentStaticReview as Wr, hideBeginSchema as Wt, validateInspectionInputs as X, settleConfirmedHides as Xn, agentSourcesSchema as Xr, journeyResourceQuerySchema as Xt, validateInspectionHead as Y, renderedHideValue as Yn, agentSourceSchema as Yr, sendOutputSchema as Yt, validateInspectionQuery as Z, termHideAdditions as Zn, journeySendSchema as Zr, journeyResourceSchema as Zt, changeJourney as _, dictionaryHides as _i, reviewRepairInputSchema as _n, journeyPreviewSchema as _r, manualUploadStartSchema as _t, bCrossSegmentReviewSchema as a, isClassifying as ai, sessionReconcileResultSchema as an, workCorrectionBinding as ar, appendSensitiveTerms as at, isGenericPublicTerm as b, syncTermGroups as bi, bReviewDigest as bn, displayedScopeDigest as br, maximumTermsFileBytes as bt, reconstructBFields as c, isSelectedWork as ci, sessionRereadResourceSchema as cn, preparationPermissionSchema as cr, maximumAgentSetupSessions as ct, resolveGroupSplit as d, unfinishedPackagingCode as di, sessionReviewResourceSchema as dn, preparationStateSchema as dr, validateFailureLocation as dt, sendReplaceResultSchema as ei, sortingApplySchema as en, maximumHideIndexTokens as er, inspectionInputs as et, selectedValueMembers as f, withReadingProgress as fi, sessionReviewStatusSchema as fn, preparationUpdateSchema as fr, manualUploadActionSchema as ft, semanticGroupKey as g, confirmedHideTerms as gi, reviewCandidateResourceSchema as gn, journeyFindingsSchema as gr, manualUploadSchema as gt, hideScopeDigest as h, confirmedHideKinds as hi, reviewCandidateInputSchema as hn, journeyFindingImpactSchema as hr, manualUploadPrepareSchema as ht, validateBCrossSegmentReview as i, hasCompleteWorkDescription as ii, sessionReconcileInputSchema as in, workPrivacyDecisions as ir, reviewInventory as it, accountProfileSchema as j, gitMetadataBudgetBytes as ji, validateScanExclusion as jn, retryReviewActionSchema as jr, journeyGuidance as jt, renew as k, selectionBatchSchema as ki, verifiedScanGitSeeds as kn, nativeRespondSchema as kr, attemptBudgetCodePoints as kt, chooseCheapPlacement as l, isSkippedWork as li, sessionReviewInputSchema as ln, preparationSettlementSchema as lr, artifactFailureDetailSchema as lt, activeJourneyTerms as m, confirmedHideKey as mi, sessionUsefulnessResourceSchema as mn, journeyClassificationSessionsSchema as mr, manualUploadChoiceSchema as mt, scanBatchEquivalenceDigest as n, sendSessionUploadSchema as ni, reviewAttemptsQuerySchema as nn, retireWorkDescription as nr, settingsQuerySchema as nt, bRereadDigest as o, isIncludedWork as oi, sessionReconciliationResourceSchema as on, emptyPreparationFacts as or, classifyAgentSetup as ot, splitSelectorValues as p, confirmedHideGroupIds as pi, sessionUsefulnessInputSchema as pn, validatePreparationFacts as pr, manualUploadBindingSchema as pt, validateInspectionClaim as q, confirmedHideDeltaSchema as qn, staticSessionResourceSchema as qr, journeyPolicyRespondSchema as qt, locateTermEvidence as r, sendSettingsSchema as ri, reviewAttemptsResultSchema as rn, workDecisionDigest as rr, readWorkspaceOutcomes as rt, reconcileBSession as s, isReadingComplete as si, sessionRereadInputSchema as sn, preparationBeginSchema as sr, defaultAgentSetupInclusion as st, scanBatchContextDigest as t, sendResolutionSchema as ti, reviewAttemptResourceSchema as tn, packHideIndex as tr, settingsBatchSchema as tt, reviseGroupMembership as u, selectedWorks as ui, sessionReviewQuerySchema as un, preparationStartSchema as ur, resolveFailureLocation as ut, projectJourneyInventory as v, journeyConfirmedHides as vi, reviewRepairResourceSchema as vn, journeyRenderInputSchema as vr, candidateSortAvailable as vt, rebindReviewSubmit as w, bTermMatcher as wi, scanCanonicalResourceSchema as wn, privacyDecisionDigest as wr, sendChoiceSchema as wt, bReviewScope as x, bContainment as xi, mergeBRepair as xn, requestedScope as xr, normalizeSensitiveTerms as xt, preparationScope as y, maskedHitsStale as yi, reviewTransportBytes as yn, journeyRenderSchema as yr, emptyTermsRevision as yt, agentSetupKey as z, freezeConfiguration as zi, applyClassification as zn, sendReachedPassport as zr, isolatedWorkId as zt };
14405
+ export { validateDetectionSummary as $, hideKeyId as $n, sendArtifactSchema as $r, journeyPlaceSchema as $t, renewBeforeMs as A, selectionBatchSchema as Ai, scanExclusionResourceSchema as An, nativeRespondSchema as Ar, validateAttemptAppend as At, createApplication as B, freezeConfiguration as Bi, journeySearchResultSchema as Bn, sendReachedPassport as Br, newJourneyWork as Bt, rebindReviewOverlay as C, bExceptionRanges as Ci, scanAliasResourceSchema as Cn, applyCardAnswers as Cr, sendActionSchema as Ct, staticallyHiddenFindings as D, replacementMarkerPattern as Di, scanGitSeedsId as Dn, rememberedHideValues as Dr, sendStartSchema as Dt, guardedFindingText as E, bWordBounded as Ei, scanSessionResourceSchema as En, reconcileReaderKeeps as Er, sendRequestSchema as Et, readAccountAvatarUrl as F, sourceChangePolicy as Fi, journeyInventoryReadSchema as Fn, currentJourneyAdapters as Fr, classificationCommitSchema as Ft, validateInspectionRequest as G, journeyEventSchema as Gn, hasCurrentStaticReview as Gr, hideFinishSchema as Gt, inspectionStartSchema as H, staticCatalogRef as Hi, searchScopeDigest as Hn, metadataProcessingVersion as Hr, journeyContextQuerySchema as Ht, storedCredentialSchema as I, recommendParallelism as Ii, journeyInventorySchema as In, hostDrives as Ir, classificationPartition as It, validateInspectionFence as J, readConfirmedHideChange as Jn, staticSessionResourceSchema as Jr, sendOutputQuerySchema as Jt, inspectionQuerySchema as K, confirmedHideAdditions as Kn, staticReviewDigest as Kr, hidePublishSchema as Kt, sourceIdentityEvidence as L, approvedTreatmentSchema as Li, journeyPreparationProgressSchema as Ln, itemAtPassport as Lr, classificationWorks as Lt, deviceAuthorizationSchema as M, gitMetadataBudgetBytes as Mi, resolveSourceSelection as Mn, retryReviewActionSchema as Mr, journeyBatchReadSchema as Mt, deviceProfileScopeSchema as N, producerVersion as Ni, projectPreparationProgress as Nn, advanceStepsSend as Nr, journeyReviewStorageQuerySchema as Nt, createAccount as O, commandSchema as Oi, scanGitSeedsResource as On, nativeEventsSchema as Or, settleCorrections as Ot, profileUrlMatchesIdentity as P, stateSchema as Pi, journeyInventoryQuerySchema as Pn, awaitingPassport as Pr, applyClassificationProposal as Pt, validateInspectionContribution as Q, addHideIndexTokens as Qn, journeySendSchema as Qr, journeyIsolateSchema as Qt, withSourceEvidence as R, effectiveTreatmentSchema as Ri, accountReadStop as Rn, obsoleteJourney as Rr, retireScanGeneration as Rt, rebindReviewAlignment as S, bContainment as Si, planBRepair as Sn, requestedScope as Sr, replaceSensitiveTermsSchema as St, reviewBatchReuse as T, bTermMatcher as Ti, scanPreparationDigest as Tn, privacyDecisionDigest as Tr, sendPrepareSchema as Tt, validateInspectionBatch as U, journeyCommitResultSchema as Un, nativeAdapterVersions as Ur, journeyContextSchema as Ut, inspectionCancelSchema as V, selectFrozenConfiguration as Vi, journeySearchSaveSchema as Vn, sendsOnNextAdvance as Vr, reviewCompileSchema as Vt, validateInspectionBatches as W, journeyCommitSchema as Wn, mergePrivacyTerms as Wr, hideBeginSchema as Wt, validateInspectionInputs as X, settleConfirmedHides as Xn, agentSourceSchema as Xr, journeyResourceQuerySchema as Xt, validateInspectionHead as Y, renderedHideValue as Yn, agentSourcePathSegment as Yr, sendOutputSchema as Yt, validateInspectionQuery as Z, termHideAdditions as Zn, agentSourcesSchema as Zr, journeyResourceSchema as Zt, changeJourney as _, confirmedHideTerms as _i, reviewRepairInputSchema as _n, journeyFindingsSchema as _r, manualUploadStartSchema as _t, bCrossSegmentReviewSchema as a, hasCompleteWorkDescription as ai, sessionReconcileResultSchema as an, workCorrectionBinding as ar, appendSensitiveTerms as at, isGenericPublicTerm as b, maskedHitsStale as bi, bReviewDigest as bn, journeyRenderSchema as br, maximumTermsFileBytes as bt, reconstructBFields as c, isReadingComplete as ci, sessionRereadResourceSchema as cn, preparationPermissionSchema as cr, maximumAgentSetupSessions as ct, resolveGroupSplit as d, selectedWorks as di, sessionReviewResourceSchema as dn, preparationStateSchema as dr, validateFailureLocation as dt, sendReceiptSchema as ei, sortingApplySchema as en, maximumHideIndexTokens as er, inspectionInputs as et, selectedValueMembers as f, unfinishedPackagingCode as fi, sessionReviewStatusSchema as fn, preparationUpdateSchema as fr, manualUploadActionSchema as ft, semanticGroupKey as g, confirmedHideKinds as gi, reviewCandidateResourceSchema as gn, journeyFindingImpactSchema as gr, manualUploadSchema as gt, hideScopeDigest as h, confirmedHideKey as hi, reviewCandidateInputSchema as hn, journeyClassificationSessionsSchema as hr, manualUploadPrepareSchema as ht, validateBCrossSegmentReview as i, sendSettingsSchema as ii, sessionReconcileInputSchema as in, workPrivacyDecisions as ir, reviewInventory as it, accountProfileSchema as j, selectionSchema as ji, validateScanExclusion as jn, nativeStartSchema as jr, journeyGuidance as jt, renew as k, packageCommandSchema as ki, verifiedScanGitSeeds as kn, nativeGetSchema as kr, attemptBudgetCodePoints as kt, chooseCheapPlacement as l, isSelectedWork as li, sessionReviewInputSchema as ln, preparationSettlementSchema as lr, artifactFailureDetailSchema as lt, activeJourneyTerms as m, confirmedHideGroupIds as mi, sessionUsefulnessResourceSchema as mn, isClassificationAccountJoin as mr, manualUploadChoiceSchema as mt, scanBatchEquivalenceDigest as n, sendResolutionSchema as ni, reviewAttemptsQuerySchema as nn, retireWorkDescription as nr, settingsQuerySchema as nt, bRereadDigest as o, isClassifying as oi, sessionReconciliationResourceSchema as on, emptyPreparationFacts as or, classifyAgentSetup as ot, splitSelectorValues as p, withReadingProgress as pi, sessionUsefulnessInputSchema as pn, validatePreparationFacts as pr, manualUploadBindingSchema as pt, validateInspectionClaim as q, confirmedHideDeltaSchema as qn, staticSessionId as qr, journeyPolicyRespondSchema as qt, locateTermEvidence as r, sendSessionUploadSchema as ri, reviewAttemptsResultSchema as rn, workDecisionDigest as rr, readWorkspaceOutcomes as rt, reconcileBSession as s, isIncludedWork as si, sessionRereadInputSchema as sn, preparationBeginSchema as sr, defaultAgentSetupInclusion as st, scanBatchContextDigest as t, sendReplaceResultSchema as ti, reviewAttemptResourceSchema as tn, packHideIndex as tr, settingsBatchSchema as tt, reviseGroupMembership as u, isSkippedWork as ui, sessionReviewQuerySchema as un, preparationStartSchema as ur, resolveFailureLocation as ut, projectJourneyInventory as v, dictionaryHides as vi, reviewRepairResourceSchema as vn, journeyPreviewSchema as vr, candidateSortAvailable as vt, rebindReviewSubmit as w, bMaskedHits as wi, scanCanonicalResourceSchema as wn, assertJourneyPrivacyReady as wr, sendChoiceSchema as wt, bReviewScope as x, syncTermGroups as xi, mergeBRepair as xn, displayedScopeDigest as xr, normalizeSensitiveTerms as xt, preparationScope as y, journeyConfirmedHides as yi, reviewTransportBytes as yn, journeyRenderInputSchema as yr, emptyTermsRevision as yt, agentSetupKey as z, defaultScopedSettings as zi, applyClassification as zn, sendOwesPassport as zr, isolatedWorkId as zt };
@@ -1,7 +1,7 @@
1
- import { $n as retainedMainTextNodes, Cr as processingSite, Fn as walkNativeFields, Hn as parseNativeRecord, Jn as nativeField, Xn as nativeText, Yn as nativeItems, ar as defaultLimits, br as declaredVersion, hn as sourceSnapshotId, mn as observeSource, qn as nativeBoolean, yr as ProcessingError } from "./locate-CP2t26j2.mjs";
2
- import { Hr as nativeAdapterVersions, it as reviewInventory, ji as gitMetadataBudgetBytes } from "./src-B3il9x5F.mjs";
3
- import { a as isRecord, i as errorCode, n as checkDiscoveryAbort, o as unresolvedMetadata, r as discoveryBounds, t as DiscoveryWarnings } from "./model-ChHuVKoB.mjs";
4
- import { c as previewLimits, d as checkOpenSource, f as checkSource, h as openSource, i as pageFits, l as previewSourceVersion, m as noSymlinks, p as fileSnapshot, r as previewHeapLimits, s as previewMessageSchema, u as createZstdFrameCheck } from "./run-CiJmV__5.mjs";
1
+ import { $n as retainedMainTextNodes, Cr as processingSite, Fn as walkNativeFields, Hn as parseNativeRecord, Jn as nativeField, Xn as nativeText, Yn as nativeItems, ar as defaultLimits, br as declaredVersion, hn as sourceSnapshotId, mn as observeSource, qn as nativeBoolean, yr as ProcessingError } from "./locate-LpCex6Xj.mjs";
2
+ import { Mi as gitMetadataBudgetBytes, Ur as nativeAdapterVersions, it as reviewInventory } from "./src-CHy3-siB.mjs";
3
+ import { a as isRecord, i as errorCode, n as checkDiscoveryAbort, o as unresolvedMetadata, r as discoveryBounds, t as DiscoveryWarnings } from "./model-BEWsfkIU.mjs";
4
+ import { c as previewLimits, d as checkOpenSource, f as checkSource, h as openSource, i as pageFits, l as previewSourceVersion, m as noSymlinks, p as fileSnapshot, r as previewHeapLimits, s as previewMessageSchema, u as createZstdFrameCheck } from "./run-P_uZgSYa.mjs";
5
5
  import { a as titleHintLimits, n as titleHeapLimits, o as titleInputSchema, r as decodeTitleError, s as titleReplySchema } from "./heap-1FYhGds4.mjs";
6
6
  import { constants } from "node:fs";
7
7
  import { access, constants as constants$1, lstat, open, opendir, readFile, readdir, realpath, stat } from "node:fs/promises";
@@ -1,6 +1,6 @@
1
- import { $n as retainedMainTextNodes, An as bindInspection, Bn as sessionNonce, Cn as processNativeRecord, Cr as processingSite, D as bBindingCap, Dn as createSentenceContext, En as reviewRoles, Fn as walkNativeFields, Gn as nativeParserVersion, Hn as parseNativeRecord, Jn as nativeField, Kn as redactionImplementationVersion, Nn as createDetectionSummary, On as renderNative, Qn as frameRecords, Sn as withMandatoryCustomTerms, Sr as isProcessingCode, Tn as mainReviewRole, Un as cleanupImplementationVersion, Vn as projectNativeRecord, Wn as entityDictionaryVersion, X as bDigest, Xn as nativeText, Yn as nativeItems, Z as codePointLength, Zn as transformRecords, an as ApplicationError, ar as defaultLimits, cr as detectStatic, dn as reviewFamilies, dr as jsonSpans, er as validateProfile, f as textDigest, fr as agentContextLocation, hn as sourceSnapshotId, k as bDuplicateShift, kn as createSessionPreparation, nr as buildPlan, or as recordPointerLimits, pn as sourceKey, pr as createEntityMatcher, rr as validatePlan, sn as safeCode, sr as resolveLimits, u as reviewDigest, un as familySelected, vr as defaultDetectionLimits, w as createBAnalysisUnits, wn as projectNativeReviewInput, wr as siteFields, xn as nativeSourceIdentitySchema, xr as fail$1, yr as ProcessingError, zn as createReplacementKey } from "./locate-CP2t26j2.mjs";
2
- import { t as createZstdFrameCheck } from "./frame-CRm99088.mjs";
3
- import { Ei as replacementMarkerPattern, Ii as approvedTreatmentSchema, Jr as agentSourcePathSegment, L as sourceIdentityEvidence, Mn as resolveSourceSelection, Pi as sourceChangePolicy, R as withSourceEvidence, Si as bExceptionRanges, Ti as bWordBounded, Vi as staticCatalogRef, Vr as metadataProcessingVersion, Yr as agentSourceSchema, ot as classifyAgentSetup, st as defaultAgentSetupInclusion, xi as bContainment, z as agentSetupKey } from "./src-B3il9x5F.mjs";
1
+ import { $n as retainedMainTextNodes, An as bindInspection, Bn as sessionNonce, Cn as processNativeRecord, Cr as processingSite, D as bBindingCap, Dn as createSentenceContext, En as reviewRoles, Fn as walkNativeFields, Gn as nativeParserVersion, Hn as parseNativeRecord, Jn as nativeField, Kn as redactionImplementationVersion, Nn as createDetectionSummary, On as renderNative, Qn as frameRecords, Sn as withMandatoryCustomTerms, Sr as isProcessingCode, Tn as mainReviewRole, Un as cleanupImplementationVersion, Vn as projectNativeRecord, Wn as entityDictionaryVersion, X as bDigest, Xn as nativeText, Yn as nativeItems, Z as codePointLength, Zn as transformRecords, an as ApplicationError, ar as defaultLimits, cr as detectStatic, dn as reviewFamilies, dr as jsonSpans, er as validateProfile, f as textDigest, fr as agentContextLocation, hn as sourceSnapshotId, k as bDuplicateShift, kn as createSessionPreparation, nr as buildPlan, or as recordPointerLimits, pn as sourceKey, pr as createEntityMatcher, rr as validatePlan, sn as safeCode, sr as resolveLimits, u as reviewDigest, un as familySelected, vr as defaultDetectionLimits, w as createBAnalysisUnits, wn as projectNativeReviewInput, wr as siteFields, xn as nativeSourceIdentitySchema, xr as fail$1, yr as ProcessingError, zn as createReplacementKey } from "./locate-LpCex6Xj.mjs";
2
+ import { t as createZstdFrameCheck } from "./frame-0v_mLdvG.mjs";
3
+ import { Ci as bExceptionRanges, Di as replacementMarkerPattern, Ei as bWordBounded, Fi as sourceChangePolicy, Hi as staticCatalogRef, Hr as metadataProcessingVersion, L as sourceIdentityEvidence, Li as approvedTreatmentSchema, Mn as resolveSourceSelection, R as withSourceEvidence, Si as bContainment, Xr as agentSourceSchema, Yr as agentSourcePathSegment, ot as classifyAgentSetup, st as defaultAgentSetupInclusion, z as agentSetupKey } from "./src-CHy3-siB.mjs";
4
4
  import { constants } from "node:fs";
5
5
  import { link, lstat, mkdir, open, readFile, readdir, realpath, rm, rmdir, stat, unlink } from "node:fs/promises";
6
6
  import { execFile, fork, spawn } from "node:child_process";
@@ -16623,32 +16623,10 @@ function reviewBatchBody(context, unit, ordinal) {
16623
16623
  ...duplicates.length ? { duplicates } : {}
16624
16624
  };
16625
16625
  }
16626
- /** Transport refinement keeps line/record boundaries and the hard long-line fallback. */
16627
- function split(unit, parts) {
16628
- if (parts === 1) return [unit.coreCodePoints];
16629
- const target = Math.ceil(unit.coreCodePoints / parts);
16630
- const lengths = [];
16631
- let core = 0;
16632
- const take = (length) => {
16633
- if (core && core + length > target) {
16634
- lengths.push(core);
16635
- core = 0;
16636
- }
16637
- core += length;
16638
- };
16639
- for (const fragment of unit.fragments.filter((part) => part.core)) {
16640
- let line = 0;
16641
- for (const scalar of fragment.text) {
16642
- line++;
16643
- if (scalar === "\n" || line === 64e3) {
16644
- take(line);
16645
- line = 0;
16646
- }
16647
- }
16648
- if (line) take(line);
16649
- }
16650
- if (core) lengths.push(core);
16651
- return lengths;
16626
+ /** Near-equal cores that together keep the original core. */
16627
+ function split(core, parts) {
16628
+ const count = Math.min(core, parts);
16629
+ return Array.from({ length: count }, (_, index) => Math.floor(core / count) + (index < core % count ? 1 : 0));
16652
16630
  }
16653
16631
  function units(context, fields, ordinal, deduplicate, cores) {
16654
16632
  return createBAnalysisUnits({
@@ -16657,7 +16635,6 @@ function units(context, fields, ordinal, deduplicate, cores) {
16657
16635
  inputRevision: context.inputRevision,
16658
16636
  fields,
16659
16637
  deduplicate,
16660
- lineContext: true,
16661
16638
  ...cores ? { cores } : {}
16662
16639
  }).map((unit, index) => {
16663
16640
  const body = reviewBatchBody(context, unit, ordinal + index);
@@ -16677,7 +16654,7 @@ function settle(context, fields, ordinal, deduplicate) {
16677
16654
  let bodies = units(context, fields, ordinal, deduplicate);
16678
16655
  for (let round = 0; bodies.some((item) => !item.fits); round += 1) {
16679
16656
  if (bodies.some(({ body, fits }) => !fits && new Set(body.unit.fragments.filter((part) => part.core).map((part) => part.fieldId)).size === 1) || round === 4) return void 0;
16680
- bodies = units(context, fields, ordinal, deduplicate, bodies.flatMap(({ body, fits, load }) => split(body.unit, fits ? 1 : Math.ceil(load / .8))));
16657
+ bodies = units(context, fields, ordinal, deduplicate, bodies.flatMap(({ body, fits, load }) => split(body.unit.coreCodePoints, fits ? 1 : Math.ceil(load / .8))));
16681
16658
  }
16682
16659
  return bodies;
16683
16660
  }
@@ -1,4 +1,4 @@
1
- import { i as createExecutor, t as assertStoreHeap } from "./heap-Bh9tySJp.mjs";
1
+ import { i as createExecutor, t as assertStoreHeap } from "./heap-aWke8lrK.mjs";
2
2
  import { parentPort } from "node:worker_threads";
3
3
  //#region packages/local-host/src/store/worker/entry.ts
4
4
  if (!parentPort) throw new Error("STORE_WORKER_REQUIRED");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitroll/tacitt-cli",
3
- "version": "0.6.14",
3
+ "version": "0.6.15",
4
4
  "description": "Private local session review and export for coding agents",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -14,7 +14,7 @@ Prepare the [exact paired CLI](references/bootstrap.md), then follow the [classi
14
14
 
15
15
  Before initializing the run, inspect the native host's actually available subagent models. Select the newest available supported native fast-tier model at explicit `low` reasoning, using fresh context for a newly delegated task; use the SAME exact model and effort for classification, bounded evidence, keyword/privacy scanning, internal card drafting and final resolution. Do not inherit the main agent's model or effort. On the currently documented Codex host this is `gpt-6-luna` with `reasoning_effort: "low"`; resolve availability again on a later host rather than assuming this dated example is universal. Use fresh-context children and pass the model/effort in the native spawn tool arguments, not just in a prompt. If the host cannot set the required profile, report that capability failure; do not silently switch model, raise effort or claim compliance.
16
16
 
17
- Initialize the shared phase/review run with `--model EXACT_NATIVE_MODEL --effort low`. Keep at most twenty-four native child agents active across all phases, further limited by actual available slots; a delegated coordinator also occupies a slot. Launch ready assignments up to available capacity together when the host supports parallel native spawns. Otherwise issue consecutive native spawns without waiting for results between launches. Every helper response returns the pinned `agentProfile`; use it for dispatch and record the actual spawn receipts and available slots. Helpers record requested settings, not proof that a model executed them. No global client configuration changes or extra API provider are needed.
17
+ Initialize the shared phase/review run with `--model EXACT_NATIVE_MODEL --effort low`. Keep at most forty-eight native child agents active across all phases, further limited by actual available slots; a delegated coordinator also occupies a slot. Launch ready assignments up to available capacity together when the host supports parallel native spawns. Otherwise issue consecutive native spawns without waiting for results between launches. Every helper response returns the pinned `agentProfile`; use it for dispatch and record the actual spawn receipts and available slots. Helpers record requested settings, not proof that a model executed them. No global client configuration changes or extra API provider are needed.
18
18
 
19
19
  ## Coordinator ownership
20
20
 
@@ -110,7 +110,7 @@ the intended Skill; do not report native-client discovery without observing it.
110
110
 
111
111
  ## Client concurrency setup
112
112
 
113
- The installer offers one default-No terminal question to save a setting of twenty-four
113
+ The installer offers one default-No terminal question to save a setting of forty-eight
114
114
  concurrent subagents for the selected clients. It explains speed, concurrent usage,
115
115
  the user/project scope and restart. Explicit `--configure-agents` (JSON
116
116
  `configureAgents:true`) authorizes the same change; `--skip-agent-config` declines.
@@ -73,6 +73,8 @@ Publish accepted completed-root progress at helper checkpoints while refilling s
73
73
 
74
74
  After scope confirmation, one item may hold roots of one published Work, plus roots still being grouped. It may join several published Works only when they have the same visibility record, as a Work and the blocked Work split from it do. Otherwise the helper stops with `CLASSIFICATION_LATE_SEVERAL_WORKS` before commit (CLI would refuse with `INVALID_SELECTION`) and prints one line per refused item, such as `items[0] joins Works <id>, <id>`, because the joined roots would take the first Work's visibility, Public included. Keep each published Work's roots in its own item, and keep any other root pending or in a new item.
75
75
 
76
+ A rebuilt profile Work (`accountWork: true`, shown with its id and profile title) is a join target only through the catalog: a known project whose catalog entry carries `accountWorkId` is submitted by the helper as `{accountWorkId, rootSessionIds}`, with no description, project type or recommendation, and gets no description task (ADR 0276 slice H). Never name a rebuilt Work yourself and never put its roots in an item. The helper checks joins before commit and stops with `CLASSIFICATION_ACCOUNT_WORK_JOIN`, printing `items[<index>] joins Works <id>` per refused item; CLI also refuses a joined session the profile already lists (`INVALID_SELECTION`). Do not retry such a refusal by hand: report it and stop the classification step. Joining keeps the Work's title and visibility; a Public profile Work becomes a Public suggestion again for the person to confirm.
77
+
76
78
  ## One-shot workflow acceptance
77
79
 
78
80
  The human-approved acceptance bar is a complete Skill workflow with useful
@@ -69,7 +69,8 @@ When every session is already on the profile, left out or held back, `journey.pr
69
69
  the counts once and that nothing is left to send this time, then end the turn. Running `/tacitt`
70
70
  again reads the profile again.
71
71
  Works with `accountWork: true` are the person's profile Works rebuilt with their profile title;
72
- classification never groups or describes them.
72
+ classification never groups or describes them. A new session whose project already has such a
73
+ Work joins it instead of forming another (ADR 0276 slice H): see the classification reference.
73
74
  When no selected Work is left (every one skipped or Dropped), the helper phase is
74
75
  `nothing-to-send`: there is no Send, and `await` waits for a Retry or a move out of Drop.
75
76
  When the person asks to retry, respond
@@ -185,11 +186,11 @@ exhausted command budget or unknown write stops refill. Known repairs and partia
185
186
  `next-packs --agents FREE_SLOTS` remains available. Refill observes current input
186
187
  and guidance again; accepted results remain bound to existing source receipts.
187
188
 
188
- For native orchestration, use automatic refill only when all twenty-four host slots are free with no pending launches. While any child is live, wait on native completion and pass the current numeric free count. An answer can appear before its worker exits.
189
+ For native orchestration, use automatic refill only when all forty-eight host slots are free with no pending launches. While any child is live, wait on native completion and pass the current numeric free count. An answer can appear before its worker exits.
189
190
 
190
191
  Aggregate `ingest` defaults to automatic refill. `review.mjs ingest --run PRIVATE_RUN --agents auto --wait-ms 30000`
191
192
  ingests published answers, then reserves packs for every free slot, where free
192
- slots are twenty-four minus outstanding scan packs, unacknowledged lost children,
193
+ slots are forty-eight minus outstanding scan packs, unacknowledged lost children,
193
194
  active card children and due-card reservations (reported as `freeSlots`). When nothing was ingested or reserved, it waits up to `--wait-ms`
194
195
  (bounded by half the command budget) for a published answer, rechecking journey
195
196
  input every few seconds, and returns as soon as something changes. Within that wait
@@ -211,7 +212,7 @@ within confirmed available native capacity and calls again, until `next` is
211
212
  AI agent slots and static CPU workers are separate resources: background static
212
213
  processing (Track B), its progress and the core count never reduce `freeSlots`.
213
214
  Auto mode subtracts `--card-agents N` active card children as well as outstanding
214
- privacy packs and unacknowledged lost children from twenty-four. When a card batch
215
+ privacy packs and unacknowledged lost children from forty-eight. When a card batch
215
216
  is due, `--planned-card-agents N` (default one) reserves upcoming card children
216
217
  before scan refill; `cardBatch.plannedAgents` reports the reservation available
217
218
  within capacity. Dispatch at most that count in the same turn as scan packs and
@@ -27,9 +27,9 @@ A concise stage card explains the current next step and Public/Private/Drop mean
27
27
 
28
28
  ## Scan
29
29
 
30
- Run `journey.prepare` after scope confirmation to perform static processing only for selected Works; while grouping continues it waits and also prepares the Works the complete proposal publishes or extends. Use the compact `result:"receipt"` response for this owned preparation. Keep this operation running and use `review.mjs next-packs` as each session publishes authenticated static receipts and review input, without waiting for the remaining sessions in its Work or creating ZIPs. Dispatch printed prompts verbatim, ingest completed answers and refill available slots within the shared twenty-four-child ceiling across phases. The helper saves task files and repeatable batched requests; do not replace it with custom scripts.
30
+ Run `journey.prepare` after scope confirmation to perform static processing only for selected Works; while grouping continues it waits and also prepares the Works the complete proposal publishes or extends. Use the compact `result:"receipt"` response for this owned preparation. Keep this operation running and use `review.mjs next-packs` as each session publishes authenticated static receipts and review input, without waiting for the remaining sessions in its Work or creating ZIPs. Dispatch printed prompts verbatim, ingest completed answers and refill available slots within the shared forty-eight-child ceiling across phases. The helper saves task files and repeatable batched requests; do not replace it with custom scripts.
31
31
 
32
- At the first ready unit and each completion, run the [shared dispatch loop](parallel-review.md#dispatch-while-stages-overlap). Pass the actual free count to `review.mjs ingest --run PRIVATE_RUN --agents FREE_SLOTS --card-agents ACTIVE_CARDS` while any child is live or the host supplies fewer than twenty-four slots. Automatic capacity is for a verified empty pool with all twenty-four slots free. Spawn one fresh native child per returned `packs[].prompt` using the pinned `agentProfile`; then check both queues again. When the only runnable work is already in flight, wait for a native completion for up to 30 seconds, then recount free host slots before ingesting. Background static processing uses separate CPU workers. Follow returned `ingested[].prompt`, `retryPrompt` or repair instructions before calling again. Bind every launched reader and report verified closure through the [reader recovery contract](parallel-review.md#recovery). Lost readers retain their slots until Main confirms closure. Accepted writes and pending repairs keep their recovery authority. A confirmed technical failure may release the remaining batches for a fresh attempt. Refused units remain visibly incomplete and receive no new reader. Launch independent work first, then handle ongoing repair. When `cardBatch.ready` is true, dispatch at most `cardBatch.plannedAgents` [card drafting](#decision-cards) children in the same turn as scan refills, queueing the rest. The helper reserves one planned card slot by default; request more with `--planned-card-agents N` before scan refill. Ingest with the actual remaining capacity immediately after dispatch and between bounded Main-owned reconciliation steps; do not wait for the whole card batch before refilling scans. While `cardBatch.ready` is false, keep dispatching packs even when `cardBatch.uncovered` grows. Handle `done` and `paused` at their normal boundaries. At `report-blocked`, report the unresolved units and follow the panel decision in `review-interrupted`. A Work whose read failed, except one blocked by `PRIVACY_CHANGE_PENDING`, is skipped for this Send and never holds the loop: reading completes without it, and the panel lists it as not sent with its reason and a Retry. Do not wait for a decision about it. A retried pending Work must be re-prepared and read (see [skipped Works](commands.md#scan-results)); no time box or pending-Work skip is allowed. All model privacy results include a disposition and reason. See [method](b-method.md).
32
+ At the first ready unit and each completion, run the [shared dispatch loop](parallel-review.md#dispatch-while-stages-overlap). Pass the actual free count to `review.mjs ingest --run PRIVATE_RUN --agents FREE_SLOTS --card-agents ACTIVE_CARDS` while any child is live or the host supplies fewer than forty-eight slots. Automatic capacity is for a verified empty pool with all forty-eight slots free. Spawn one fresh native child per returned `packs[].prompt` using the pinned `agentProfile`; then check both queues again. When the only runnable work is already in flight, wait for a native completion for up to 30 seconds, then recount free host slots before ingesting. Background static processing uses separate CPU workers. Follow returned `ingested[].prompt`, `retryPrompt` or repair instructions before calling again. Bind every launched reader and report verified closure through the [reader recovery contract](parallel-review.md#recovery). Lost readers retain their slots until Main confirms closure. Accepted writes and pending repairs keep their recovery authority. A confirmed technical failure may release the remaining batches for a fresh attempt. Refused units remain visibly incomplete and receive no new reader. Launch independent work first, then handle ongoing repair. When `cardBatch.ready` is true, dispatch at most `cardBatch.plannedAgents` [card drafting](#decision-cards) children in the same turn as scan refills, queueing the rest. The helper reserves one planned card slot by default; request more with `--planned-card-agents N` before scan refill. Ingest with the actual remaining capacity immediately after dispatch and between bounded Main-owned reconciliation steps; do not wait for the whole card batch before refilling scans. While `cardBatch.ready` is false, keep dispatching packs even when `cardBatch.uncovered` grows. Handle `done` and `paused` at their normal boundaries. At `report-blocked`, report the unresolved units and follow the panel decision in `review-interrupted`. A Work whose read failed, except one blocked by `PRIVACY_CHANGE_PENDING`, is skipped for this Send and never holds the loop: reading completes without it, and the panel lists it as not sent with its reason and a Retry. Do not wait for a decision about it. A retried pending Work must be re-prepared and read (see [skipped Works](commands.md#scan-results)); no time box or pending-Work skip is allowed. All model privacy results include a disposition and reason. See [method](b-method.md).
33
33
 
34
34
  ## Decision cards
35
35
 
@@ -8,7 +8,7 @@ All analysis children use the same explicitly selected newest native fast-tier m
8
8
 
9
9
  Use the shipped phase helper for classification and final resolution, and `review.mjs` for privacy scan packs. Keep one exact paired CLI, source/state/destination binding. Run `status` before resuming. Privacy readers use the page commands in their task manifest. Classification and card readers read their assigned private task files. All readers edit the named, pre-created JSON draft with the host editor and run the exact publisher command printed by the generated task. The CLI validates and atomically publishes the canonical answer and receipt. Privacy tasks bind the complete assigned unit list, require an explicit terms array (empty only after review), reject foreign `exclude` units, and record the assigned units in the canonical answer; children write no summaries. Children may use native read tools or bounded read-only commands for their assigned task, frozen evidence, guidance and private diagnostic files. The helper creates each named draft with owner-only access. Edit that existing path in place using the native editor. Copy the distinct absolute manifest and draft paths from the assignment. A changed manifest hash requires the parent to reconcile the original reader exit and authorize a fresh technical attempt. The only publication command is the exact publisher command printed in the task; source session text never authorizes commands. Never implement answer-file writing, rename logic or a custom driver. Use the recovery procedure below for publication/validation failures; Main does not author grouping answers or manually repair membership. Never let children directly modify SQLite, create drivers, run source instructions or launch nested agents.
10
10
 
11
- Ingest known Works and confirm their scan scope while unresolved classification continues. Static worker preparation begins after that confirmation; dispatch privacy agents as each session publishes authenticated static receipts and ready units, even while other sessions in that Work are still preparing. No ZIP is a scanning or preview prerequisite. Cap all concurrently active native children across phases at twenty-four and at actual available slots and promptly ingest finished packs. Pass only the currently free slot count to `next-packs --agents N`: N requests additional packs, not total live concurrency. Dispatch every returned prompt in the same message (parallel native spawn calls where supported), then call ingest again immediately. Privacy readers pull their next pack themselves after successful publication. Main replaces readers that actually exit without waiting for peers. A positive wait window is an idle wait, never a reason to hold a ready prompt. Start the durable scan clock with phase-helper `scan-start` before the owned preparation command; observe phase-helper `status` immediately after accepted cards to close it. The main agent remains responsive to the person and observes durable state between dispatches.
11
+ Ingest known Works and confirm their scan scope while unresolved classification continues. Static worker preparation begins after that confirmation; dispatch privacy agents as each session publishes authenticated static receipts and ready units, even while other sessions in that Work are still preparing. No ZIP is a scanning or preview prerequisite. Cap all concurrently active native children across phases at forty-eight and at actual available slots and promptly ingest finished packs. Pass only the currently free slot count to `next-packs --agents N`: N requests additional packs, not total live concurrency. Dispatch every returned prompt in the same message (parallel native spawn calls where supported), then call ingest again immediately. Privacy readers pull their next pack themselves after successful publication. Main replaces readers that actually exit without waiting for peers. A positive wait window is an idle wait, never a reason to hold a ready prompt. Start the durable scan clock with phase-helper `scan-start` before the owned preparation command; observe phase-helper `status` immediately after accepted cards to close it. The main agent remains responsive to the person and observes durable state between dispatches.
12
12
 
13
13
  Work metadata (title, project name, description and reason) is never reviewed or Hidden. Each task names its phase, immutable input and complete guidance. Read all pages; incomplete coverage is failure. Keep each saved request key and exact body for retry. An unknown response does not permit a second task with new authority. Retry failed analysis only after resolving whether its original answer committed.
14
14
 
@@ -24,8 +24,8 @@ Card-authoring inputs are bounded to 48,000 estimated tokens per task and read i
24
24
 
25
25
  Main runs one lifecycle loop across classification, scanning and card work. It launches the initial privacy pool together; persistent privacy readers pull successive assignments without Main dispatch between packs. Main binds any missing native receipts, replaces actual exits and handles cards. On each native completion, new input or preparation milestone, check receipts and ready tasks. Keep the existing classification priority for unresolved roots, requested evidence and reconciliation. Admit known Works as soon as their metadata is published. Continue launching ready privacy packs while grouping or its repairs run.
26
26
 
27
- 1. Count native capacity across the whole tree, including a delegated Main, other live children and reservations awaiting launch. Use the smaller of the host's available slots and the space left under twenty-four. Maintain one owner per task; publication alone does not prove child exit. Static preparation uses separate CPU workers.
28
- 2. After allocating slots to other stage tasks, pass the remaining free count to `review.mjs ingest --agents FREE_SLOTS --card-agents ACTIVE_CARDS`. This single call records batched reader events, ingests and refills. Pass `--reader-starts JSON` only for native spawn receipts not already self-bound by readers, and `--reader-exits JSON` for confirmed exits since the last call; starts are applied before exits. Each array contains objects with `packId`, `readerId` (the native ID), optional `turn`, and, for exits, `result` (`published`, `retry` or `refused`). Use zero for ingestion only. Bare aggregate `ingest` and `--agents auto` infer capacity; use them only when all twenty-four native slots are confirmed free and no launch is outstanding. While any child is live, use a bounded native completion wait, then recount and pass the numeric free count. Never infer a free slot from the configured maximum alone.
27
+ 1. Count native capacity across the whole tree, including a delegated Main, other live children and reservations awaiting launch. Use the smaller of the host's available slots and the space left under forty-eight. Maintain one owner per task; publication alone does not prove child exit. Static preparation uses separate CPU workers.
28
+ 2. After allocating slots to other stage tasks, pass the remaining free count to `review.mjs ingest --agents FREE_SLOTS --card-agents ACTIVE_CARDS`. This single call records batched reader events, ingests and refills. Pass `--reader-starts JSON` only for native spawn receipts not already self-bound by readers, and `--reader-exits JSON` for confirmed exits since the last call; starts are applied before exits. Each array contains objects with `packId`, `readerId` (the native ID), optional `turn`, and, for exits, `result` (`published`, `retry` or `refused`). Use zero for ingestion only. Bare aggregate `ingest` and `--agents auto` infer capacity; use them only when all forty-eight native slots are confirmed free and no launch is outstanding. While any child is live, use a bounded native completion wait, then recount and pass the numeric free count. Never infer a free slot from the configured maximum alone.
29
29
  3. For a due batch, request ready card capacity with `--planned-card-agents N`. The default reserves one slot. Open the due batch promptly. Once it is open the helper stops reserving for a new batch. If it produces fewer unowned drafts than planned, run ingest again immediately with the actual remaining capacity. When card drafts and privacy packs are both ready, advance both in the same dispatch when capacity permits. Keep request counts within the ready card backlog and give surplus capacity to ready work in the other stage. Launch at most `cardBatch.plannedAgents` card children alongside every returned privacy pack. On an already-open card batch, count its queued drafts explicitly before assigning remaining slots to scans. Recompute from the host at the next completion.
30
30
  4. If the complete global card catalog needs substantial analysis while other work is ready, assign one fresh pinned child the read-only comparison of all candidates against `existingCards`. Give it only the task's bounded catalog, existing policies and output schema. Ask for a proposal referencing candidate IDs, without canonical publication, CLI mutations or panel actions. Count this child in capacity. Main keeps refilling, reviews the complete catalog and proposal in bounded portions between dispatches, verifies every mapping and policy, then solely publishes the global answer and ingests it. For an extend-only batch already ready for ingestion, ingest immediately.
31
31
  5. Dispatch unowned ready work before tracing diagnostics, writing timing reports or waiting. Spawn all returned privacy packs and planned card children in the same message, using parallel native spawn calls where supported. Readers bind their own actual native ID with the generated `reader-start` as their first command when the host exposes self identity. Collect any remaining spawn receipts and all confirmed exits into the next ingest call; do not spend a separate coordinator turn per child start or exit. Run ingest again without waiting after the dispatch batch and when children exit. When no stage child is active and owned preparation is the only producer, pass the positive actual host budget and `--wait-ms 30000` to the same ingest call. Repeated probes inside that wait reuse one authenticated CLI runtime. When stage children are active, wait up to 30 seconds for the first change; return to both queues immediately.
@@ -16,11 +16,11 @@ journey-classification-metadata.d.mts | Declaration | Types generated AI metadat
16
16
  journey-classification-metadata.mjs | Runtime | Validates global reconciliation, reduces oversized Works from all evidence slices and creates hash-bound, token-bounded AI metadata tasks for new Works and Unsorted.
17
17
  journey-classification-progress.mjs | Runtime | Accepted-answer counters and fenced progress checkpoints.
18
18
  journey-classification-tasks.d.mts | Declaration | Types initial classification routing and catalog/evidence task generation for helper consumers and tests.
19
- journey-classification-tasks.mjs | Runtime | Original-root routing, capacity-balanced bounded tasks and unresolved-first ready ordering with stable assignments.
19
+ journey-classification-tasks.mjs | Runtime | Original-root routing, capacity-balanced bounded tasks and unresolved-first ready ordering with stable assignments; known projects the catalog ties to a rebuilt Work become joins, not tasks.
20
20
  journey-classification-validation.d.mts | Declaration | Shared classification validator contracts.
21
- journey-classification-validation.mjs | Runtime | Shared root judgment, stable target identity, fixed project metadata, consolidation validation, target-preserving assignment expansion and the late check that an item joins only placed Works with one access record (ADR 0274).
21
+ journey-classification-validation.mjs | Runtime | Shared root judgment, stable target identity, fixed project metadata, consolidation validation, target-preserving assignment expansion and the late check that an item joins only placed Works with one access record (ADR 0274), and the account Work join check (ADR 0276 slice H).
22
22
  journey-classification.mjs | Runtime | Batched known-project metadata, unknown-root grouping and streamed four-row evidence, versioned checkpoints and generation progress.
23
- journey-consolidation-submit.mjs | Runtime | Expands candidate assignments, applies per-root overrides, combines small topics into Unsorted, attaches known projects and checks exact root coverage.
23
+ journey-consolidation-submit.mjs | Runtime | Expands candidate assignments, applies per-root overrides, combines small topics into Unsorted, attaches known projects, adds account Work join items (ADR 0276 slice H) and checks exact root coverage.
24
24
  journey-consolidation.d.mts | Declaration | Consolidation helper contract for bounded evidence regression tests.
25
25
  journey-consolidation.mjs | Runtime | Builds proposed-name and stable-target candidate buckets, persists complete reconciliation input, and returns compact dispatch descriptors.
26
26
  journey-panel-host.d.mts | Declaration | Types the detached-host handoff for helper tests.
@@ -38,7 +38,7 @@ local-cli.mjs | Read-only package verifier | Validates an explicit installed CLI
38
38
  review-stream-client.d.mts | Type declarations | Types the fixed request/shutdown limits, private lifecycle callbacks and review-stream client methods.
39
39
  review-stream-client.mjs | Runtime | Bounds requests and owned-child shutdown, permits retry only for explicit pre-request startup busy, and preserves unknown lost-write outcomes for receipt replay.
40
40
  review.d.mts | Type declarations | Types the review helper's exported task rendering, answer reading, per-Work progress and entry functions for tests.
41
- review.mjs | Runtime | Session-ready scanning, ready-pack-first twenty-four-slot refill, 30000-token pack target, manifest-derived page byte capture, default reader pull with native self-binding, host-capability fallback and replayable slot transfer, blocked-Work exclusion, largest-Work queue ordering, isolated retries, batched native reader and turn receipts with launch reservations, bounded reissue and visible interruption recovery, planned card capacity, outcome-reported preparation recovery and read-only status, metadata-gated questions, Track B-gated final preview and hash-bound answer recovery.
41
+ review.mjs | Runtime | Session-ready scanning, ready-pack-first forty-eight-slot refill, 30000-token pack target, manifest-derived page byte capture, default reader pull with native self-binding, host-capability fallback and replayable slot transfer, blocked-Work exclusion, largest-Work queue ordering, isolated retries, batched native reader and turn receipts with launch reservations, bounded reissue and visible interruption recovery, planned card capacity, outcome-reported preparation recovery and read-only status, metadata-gated questions, Track B-gated final preview and hash-bound answer recovery.
42
42
  verify-cli.mjs | Verification entry | Checks a managed local receipt and its current package bytes before the Skill starts a journey.
43
43
  -->
44
44
 
@@ -64,6 +64,12 @@ export async function publishClassificationProgress(
64
64
  const knownKeys = new Set(classification.projects.flatMap((project) => project.rootKeys))
65
65
  const aiCompleted = new Set()
66
66
  const completed = new Set()
67
+ // Roots joining a rebuilt account Work are grouped by routing alone (ADR 0276 slice H).
68
+ for (const join of classification.accountJoins ?? [])
69
+ for (const key of join.rootKeys) {
70
+ knownKeys.add(key)
71
+ completed.add(key)
72
+ }
67
73
  const latestJudgments = new Set()
68
74
  for (const item of manifest.tasks) {
69
75
  if (item.evidenceRound > 0 && !(await optional(item.answer))) continue
@@ -116,9 +116,17 @@ async function initialize(directory, manifest, view, call) {
116
116
  })
117
117
  if (projectRootKeys.size + unknown.length !== roots.length)
118
118
  throw new Error('CLASSIFICATION_ROUTING_COVERAGE_INVALID')
119
+ // ADR 0276 slice H: a known project the catalog ties to a rebuilt account Work joins it. It is
120
+ // neither described nor offered to the classifier as a target.
121
+ const accountJoins = projects.flatMap((project) =>
122
+ project.metadata.accountWorkId
123
+ ? [{ accountWorkId: project.metadata.accountWorkId, rootKeys: project.rootKeys }]
124
+ : [],
125
+ )
119
126
  manifest.classification = {
120
127
  roots,
121
- projects,
128
+ projects: projects.filter((project) => !project.metadata.accountWorkId),
129
+ accountJoins,
122
130
  unknown,
123
131
  routingAssignments: assignments,
124
132
  evidence: [],
@@ -18,8 +18,21 @@ export function validateConsolidationAnswer(
18
18
  projectCatalog: unknown[]
19
19
  },
20
20
  ): { code: string | null; details: string[] }
21
+ export function validateAccountWorkJoins(
22
+ items: ReadonlyArray<{ rootSessionIds: readonly string[]; accountWorkId?: string | undefined }>,
23
+ works: ReadonlyArray<{
24
+ id: string
25
+ placement?: string
26
+ rootSessionIds: readonly string[]
27
+ accountWork?: boolean
28
+ }>,
29
+ confirmed: boolean,
30
+ ): void
21
31
  export function validateLatePlacedWorks(
22
- items: ReadonlyArray<{ projectName: string; rootSessionIds: readonly string[] }>,
32
+ items: ReadonlyArray<
33
+ | { projectName: string; rootSessionIds: readonly string[] }
34
+ | { accountWorkId: string; rootSessionIds: readonly string[] }
35
+ >,
23
36
  works: ReadonlyArray<{
24
37
  id: string
25
38
  placement?: string
@@ -257,6 +257,8 @@ export function validateLatePlacedWorks(items, works) {
257
257
  const details = []
258
258
  const conflicts = []
259
259
  items.forEach((item, index) => {
260
+ // Account Work joins are checked by `validateAccountWorkJoins`.
261
+ if (Object.hasOwn(item, 'accountWorkId')) return
260
262
  const owners = placed.filter((work) =>
261
263
  work.rootSessionIds.some((root) => item.rootSessionIds.includes(root)),
262
264
  )
@@ -276,3 +278,46 @@ export function validateLatePlacedWorks(items, works) {
276
278
  conflicts,
277
279
  })
278
280
  }
281
+
282
+ /**
283
+ * ADR 0276 slice H, mirroring `createClassificationProposal` in the CLI, which stays
284
+ * authoritative: a join item has exactly `accountWorkId` and `rootSessionIds` and names a rebuilt
285
+ * Work; after scope confirmation it takes only roots still being grouped; no item takes a root of
286
+ * a rebuilt Work. Whether the account lists a joined root's keys only the CLI can check.
287
+ */
288
+ export function validateAccountWorkJoins(items, works, confirmed) {
289
+ const rebuilt = works.filter((work) => work.accountWork)
290
+ const grouping = new Set(
291
+ works.flatMap((work) => (work.placement === 'classifying' ? work.rootSessionIds : [])),
292
+ )
293
+ const details = []
294
+ const conflicts = []
295
+ items.forEach((item, index) => {
296
+ const holders = rebuilt.filter((work) =>
297
+ work.rootSessionIds.some((root) => item.rootSessionIds.includes(root)),
298
+ )
299
+ const join = Object.hasOwn(item, 'accountWorkId')
300
+ const problem = holders.length
301
+ ? 'takes roots of a rebuilt Work'
302
+ : !join
303
+ ? null
304
+ : Object.keys(item).some((field) => !['accountWorkId', 'rootSessionIds'].includes(field))
305
+ ? 'joins with fields other than accountWorkId and rootSessionIds'
306
+ : !rebuilt.some((work) => work.id === item.accountWorkId)
307
+ ? 'names a Work that is not a rebuilt account Work'
308
+ : confirmed && item.rootSessionIds.some((root) => !grouping.has(root))
309
+ ? 'joins roots that are no longer being grouped'
310
+ : null
311
+ if (!problem) return
312
+ const workIds = holders.length ? holders.map((work) => work.id) : [item.accountWorkId]
313
+ conflicts.push({ item: index, workIds })
314
+ details.push(`items[${index}] ${problem}`)
315
+ })
316
+ // `conflicts` carries only item indexes and Work ids, so the helper may print it to stderr.
317
+ if (details.length)
318
+ throw Object.assign(new Error('CLASSIFICATION_ACCOUNT_WORK_JOIN'), {
319
+ code: 'CLASSIFICATION_ACCOUNT_WORK_JOIN',
320
+ details,
321
+ conflicts,
322
+ })
323
+ }