@gitroll/tacitt-cli 0.6.12 → 0.6.13

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 (38) hide show
  1. package/dist/{agent-setup-B12VbvN_.mjs → agent-setup-Doy_AnPY.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-B3He21G8.mjs → frame-BPbypzmt.mjs} +1 -1
  8. package/dist/{heap-Blrat--4.mjs → heap-DCS_WXIA.mjs} +7 -3
  9. package/dist/inspection-worker.mjs +4 -4
  10. package/dist/{job-CBuIMTQ0.mjs → job--zxwgaPy.mjs} +135 -36
  11. package/dist/{locate-D-ZaHpI0.mjs → locate-PLEyLYW-.mjs} +287 -236
  12. package/dist/{model-DR9odhWP.mjs → model-CyXvsWLz.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-BYDcNKy2.mjs → query-BC7MIaWc.mjs} +335 -115
  17. package/dist/{run-DQ9xyKSt.mjs → run-Dod7Vykp.mjs} +6 -6
  18. package/dist/{src-DeNJ8vOD.mjs → src-CP-K2PBv.mjs} +1779 -1686
  19. package/dist/{src-Do-qzuMn.mjs → src-CttNuxOn.mjs} +590 -542
  20. package/dist/{src-CKQlTA2Z.mjs → src-dL6y2mcu.mjs} +180 -30
  21. package/dist/store-database-worker.mjs +1 -1
  22. package/package.json +1 -1
  23. package/skills/tacitt/SKILL.md +3 -3
  24. package/skills/tacitt/references/b-method.md +4 -2
  25. package/skills/tacitt/references/bootstrap.md +1 -1
  26. package/skills/tacitt/references/commands.md +10 -4
  27. package/skills/tacitt/references/journey.md +3 -3
  28. package/skills/tacitt/references/parallel-review.md +22 -9
  29. package/skills/tacitt/references/remote-workers.md +6 -2
  30. package/skills/tacitt/scripts/INDEX.md +4 -4
  31. package/skills/tacitt/scripts/answer.mjs +12 -1
  32. package/skills/tacitt/scripts/describe.mjs +3 -2
  33. package/skills/tacitt/scripts/journey-classification.mjs +1 -0
  34. package/skills/tacitt/scripts/journey-scan-timing.mjs +3 -2
  35. package/skills/tacitt/scripts/journey.mjs +1 -1
  36. package/skills/tacitt/scripts/privacy-validator.mjs +84 -33
  37. package/skills/tacitt/scripts/review.d.mts +1 -0
  38. package/skills/tacitt/scripts/review.mjs +296 -35
@@ -9170,6 +9170,8 @@ const decisionCardSchema = decisionCardInputSchema.extend({ answer: z.object({
9170
9170
  rule: z.string().max(4001),
9171
9171
  additionalRule: z.string().max(2e3),
9172
9172
  remember: z.boolean(),
9173
+ /** Where the answer came from; only the person's panel Hide is remembered (issue #126). */
9174
+ source: z.enum(["conversation", "panel"]).optional(),
9173
9175
  excludedTermKeys: z.array(z.string().min(1)).max(2e4).optional()
9174
9176
  }).strict().nullable() }).strict();
9175
9177
  const journeyPrivacySchema = z.object({
@@ -9691,6 +9693,11 @@ const nativeJourneySchema = z.object({
9691
9693
  /** ADR 0086: per-value Hide or Keep over the term types' answers; absent means none. */
9692
9694
  valueOverrides: z.array(journeyValueOverrideSchema).max(1e4).optional(),
9693
9695
  /**
9696
+ * Values the person hid in earlier journeys on this machine, frozen at formation (issue #126).
9697
+ * Private: never in a view, reader guidance or anything sent.
9698
+ */
9699
+ rememberedHides: z.array(z.string().min(1).max(1024)).max(1e5).optional(),
9700
+ /**
9694
9701
  * ADR 0086: the snapshot is read by dictionary review. A snapshot without it was read by B
9695
9702
  * review, whose evidence this release no longer renders, so it is obsolete.
9696
9703
  */
@@ -9815,61 +9822,6 @@ const bCategorySchema = z.enum([
9815
9822
  "other-identifying-information"
9816
9823
  ]);
9817
9824
  //#endregion
9818
- //#region packages/application/src/journey/b/submission.ts
9819
- const bOccurrenceSchema = bAnchorSchema.extend({
9820
- decision: z.enum([
9821
- "sensitive",
9822
- "retained",
9823
- "uncertain"
9824
- ]),
9825
- reason: z.string().min(1).max(1e3),
9826
- classification: bClassificationSchema.nullable()
9827
- }).strict();
9828
- const bTermSchema = z.object({
9829
- term: z.string().min(1).max(1024),
9830
- category: bCategorySchema,
9831
- occurrences: z.array(bOccurrenceSchema).min(1)
9832
- }).strict();
9833
- const bRetainedGroupSchema = bAnchorSchema.omit({
9834
- start: true,
9835
- end: true
9836
- }).extend({
9837
- starts: z.array(bCountSchema).min(1),
9838
- reason: z.string().min(1).max(1e3)
9839
- }).strict();
9840
- const bRepresentationSchema = bAnchorSchema.extend({
9841
- category: bCategorySchema,
9842
- classification: bClassificationSchema,
9843
- reason: z.string().min(1).max(1e3)
9844
- }).strict();
9845
- const bCoverageSchema = z.object({
9846
- fieldId: bIdSchema,
9847
- fieldHash: journeyDigestSchema,
9848
- start: bCountSchema,
9849
- end: bCountSchema,
9850
- status: z.enum(["reviewed", "incomplete"])
9851
- }).strict();
9852
- const bSessionDecisionSchema = z.object({
9853
- action: z.enum([
9854
- "local_redaction",
9855
- "exclude_session",
9856
- "uncertain"
9857
- ]),
9858
- reason: z.string().min(1).max(1e3),
9859
- evidence: z.array(bAnchorSchema)
9860
- }).strict();
9861
- const bReviewSubmissionSchema = z.object({
9862
- method: z.literal(bMethod),
9863
- unitId: journeyDigestSchema,
9864
- unitDigest: journeyDigestSchema,
9865
- terms: z.array(bTermSchema),
9866
- retainedSourceGroups: z.array(bRetainedGroupSchema),
9867
- representationLimits: z.array(bRepresentationSchema),
9868
- uncertainOccurrences: z.array(bAnchorSchema),
9869
- coverage: z.array(bCoverageSchema).min(1),
9870
- sessionDecision: bSessionDecisionSchema
9871
- }).strict();
9872
- //#endregion
9873
9825
  //#region packages/application/src/journey/b/text.ts
9874
9826
  const bDigest = (value) => createHash("sha256").update(value).digest("hex");
9875
9827
  function codePointLength(text) {
@@ -10030,6 +9982,61 @@ function bEvidenceReader(unit, visible = unit.fragments) {
10030
9982
  };
10031
9983
  }
10032
9984
  //#endregion
9985
+ //#region packages/application/src/journey/b/submission.ts
9986
+ const bOccurrenceSchema = bAnchorSchema.extend({
9987
+ decision: z.enum([
9988
+ "sensitive",
9989
+ "retained",
9990
+ "uncertain"
9991
+ ]),
9992
+ reason: z.string().min(1).max(1e3),
9993
+ classification: bClassificationSchema.nullable()
9994
+ }).strict();
9995
+ const bTermSchema = z.object({
9996
+ term: z.string().min(1).max(1024),
9997
+ category: bCategorySchema,
9998
+ occurrences: z.array(bOccurrenceSchema).min(1)
9999
+ }).strict();
10000
+ const bRetainedGroupSchema = bAnchorSchema.omit({
10001
+ start: true,
10002
+ end: true
10003
+ }).extend({
10004
+ starts: z.array(bCountSchema).min(1),
10005
+ reason: z.string().min(1).max(1e3)
10006
+ }).strict();
10007
+ const bRepresentationSchema = bAnchorSchema.extend({
10008
+ category: bCategorySchema,
10009
+ classification: bClassificationSchema,
10010
+ reason: z.string().min(1).max(1e3)
10011
+ }).strict();
10012
+ const bCoverageSchema = z.object({
10013
+ fieldId: bIdSchema,
10014
+ fieldHash: journeyDigestSchema,
10015
+ start: bCountSchema,
10016
+ end: bCountSchema,
10017
+ status: z.enum(["reviewed", "incomplete"])
10018
+ }).strict();
10019
+ const bSessionDecisionSchema = z.object({
10020
+ action: z.enum([
10021
+ "local_redaction",
10022
+ "exclude_session",
10023
+ "uncertain"
10024
+ ]),
10025
+ reason: z.string().min(1).max(1e3),
10026
+ evidence: z.array(bAnchorSchema)
10027
+ }).strict();
10028
+ const bReviewSubmissionSchema = z.object({
10029
+ method: z.literal(bMethod),
10030
+ unitId: journeyDigestSchema,
10031
+ unitDigest: journeyDigestSchema,
10032
+ terms: z.array(bTermSchema),
10033
+ retainedSourceGroups: z.array(bRetainedGroupSchema),
10034
+ representationLimits: z.array(bRepresentationSchema),
10035
+ uncertainOccurrences: z.array(bAnchorSchema),
10036
+ coverage: z.array(bCoverageSchema).min(1),
10037
+ sessionDecision: bSessionDecisionSchema
10038
+ }).strict();
10039
+ //#endregion
10033
10040
  //#region packages/application/src/journey/b/result.ts
10034
10041
  function bIssue(code, anchor) {
10035
10042
  return {
@@ -10075,6 +10082,152 @@ function unownedBObligations(parts) {
10075
10082
  return parts.filter((part) => !part.owned && !owned.some((other) => bProtects(other, part))).map((part) => bIssue("UNOWNED_SENSITIVE_CONTENT", part));
10076
10083
  }
10077
10084
  //#endregion
10085
+ //#region packages/application/src/journey/b/ranges.ts
10086
+ /** Invocation-local coverage of already validated code-point ranges. */
10087
+ function indexBRanges(parts) {
10088
+ const ranges = [];
10089
+ const ordered = parts.map(({ start, end }) => ({
10090
+ start,
10091
+ end
10092
+ }));
10093
+ ordered.sort((left, right) => left.start - right.start);
10094
+ for (const part of ordered) {
10095
+ const last = ranges.at(-1);
10096
+ if (last && part.start <= last.end) last.end = Math.max(last.end, part.end);
10097
+ else ranges.push(part);
10098
+ }
10099
+ const firstOverlap = (start) => {
10100
+ let low = 0;
10101
+ let high = ranges.length;
10102
+ while (low < high) {
10103
+ const middle = Math.floor((low + high) / 2);
10104
+ if ((ranges[middle]?.end ?? Infinity) <= start) low = middle + 1;
10105
+ else high = middle;
10106
+ }
10107
+ return low;
10108
+ };
10109
+ return {
10110
+ relation(query) {
10111
+ const part = ranges[firstOverlap(query.start)];
10112
+ return {
10113
+ covers: Boolean(part && part.start <= query.start && part.end >= query.end),
10114
+ overlaps: Boolean(part && part.start < query.end)
10115
+ };
10116
+ },
10117
+ uncovered(query) {
10118
+ const result = [];
10119
+ let cursor = query.start;
10120
+ for (let index = firstOverlap(cursor); index < ranges.length; index += 1) {
10121
+ const part = ranges[index];
10122
+ if (!part || part.start >= query.end) break;
10123
+ if (cursor < part.start) result.push({
10124
+ start: cursor,
10125
+ end: part.start
10126
+ });
10127
+ cursor = Math.max(cursor, part.end);
10128
+ if (cursor >= query.end) break;
10129
+ }
10130
+ if (cursor < query.end) result.push({
10131
+ start: cursor,
10132
+ end: query.end
10133
+ });
10134
+ return result;
10135
+ }
10136
+ };
10137
+ }
10138
+ //#endregion
10139
+ //#region packages/application/src/journey/b/literals.ts
10140
+ function bLiteralAdmitted(value) {
10141
+ return admitGitName(value, 1)?.value === value;
10142
+ }
10143
+ function bLiterals(evidence) {
10144
+ const value = evidence.exactQuote.normalize("NFC");
10145
+ if (bLiteralAdmitted(value)) return [{
10146
+ ...evidence,
10147
+ term: value
10148
+ }];
10149
+ return literalPieces(evidence.exactQuote).map((part) => ({
10150
+ ...evidence,
10151
+ start: evidence.start + part.start,
10152
+ end: evidence.start + part.end,
10153
+ utf16Start: evidence.utf16Start + part.utf16Start,
10154
+ utf16End: evidence.utf16Start + part.utf16End,
10155
+ exactQuote: part.exactQuote,
10156
+ term: part.term
10157
+ }));
10158
+ }
10159
+ /** The same admitted NFC terms for hidden text that is not one B evidence anchor. */
10160
+ function bLiteralTerms(text) {
10161
+ const value = text.normalize("NFC");
10162
+ return bLiteralAdmitted(value) ? [value] : literalPieces(text).map((part) => part.term);
10163
+ }
10164
+ /** Bounded line pieces of a nonrepresentable quote, in quote-relative coordinates. */
10165
+ function literalPieces(quote) {
10166
+ const chars = Array.from(quote);
10167
+ const index = createCodePointIndex(quote);
10168
+ const parts = [];
10169
+ let cursor = 0;
10170
+ while (cursor < chars.length) {
10171
+ while (cursor < chars.length && /\s/u.test(chars[cursor] ?? "")) cursor += 1;
10172
+ if (cursor === chars.length) break;
10173
+ const newline = chars.indexOf("\n", cursor);
10174
+ const lineEnd = newline < 0 ? chars.length : newline;
10175
+ let end = Math.min(lineEnd, cursor + 256);
10176
+ if (end < lineEnd) {
10177
+ let soft = end;
10178
+ while (soft > cursor + 12 && !/\s/u.test(chars[soft - 1] ?? "")) soft -= 1;
10179
+ if (soft > cursor + 12) end = soft;
10180
+ }
10181
+ while (end > cursor && /\s/u.test(chars[end - 1] ?? "")) end -= 1;
10182
+ if (end <= cursor) {
10183
+ cursor += 1;
10184
+ continue;
10185
+ }
10186
+ const exactQuote = index.slice(cursor, end);
10187
+ const term = exactQuote.normalize("NFC");
10188
+ if (end - cursor >= 12 && bLiteralAdmitted(term)) parts.push({
10189
+ start: cursor,
10190
+ end,
10191
+ utf16Start: index.offset(cursor),
10192
+ utf16End: index.offset(end),
10193
+ exactQuote,
10194
+ term
10195
+ });
10196
+ cursor = end;
10197
+ }
10198
+ return parts;
10199
+ }
10200
+ function uncoveredBObligations(sensitive, literals) {
10201
+ if (sensitive.length === 0) return [];
10202
+ const fields = new Map([...Map.groupBy(literals, (part) => part.fieldId)].map(([fieldId, parts]) => [fieldId, indexBRanges(parts)]));
10203
+ return sensitive.flatMap((evidence) => (fields.get(evidence.fieldId)?.uncovered(evidence) ?? [{
10204
+ start: evidence.start,
10205
+ end: evidence.end
10206
+ }]).filter((range) => /\S/u.test(codePointSlice(evidence.exactQuote, range.start - evidence.start, range.end - evidence.start))).map((range) => ({
10207
+ fieldId: evidence.fieldId,
10208
+ ...range
10209
+ })));
10210
+ }
10211
+ //#endregion
10212
+ //#region packages/application/src/journey/review/findings.ts
10213
+ const semanticFindingSchema = z.object({
10214
+ id: journeyIdSchema,
10215
+ chunkId: journeyIdSchema,
10216
+ start: z.number().int().nonnegative(),
10217
+ end: z.number().int().nonnegative(),
10218
+ spanHash: journeyDigestSchema,
10219
+ kind: journeyKindSchema,
10220
+ class: z.enum([
10221
+ "certain",
10222
+ "gray",
10223
+ "clear"
10224
+ ]),
10225
+ label: z.string().trim().min(1).max(120),
10226
+ groupKey: z.string().trim().min(1).max(120).nullable(),
10227
+ condition: z.string().max(1e3),
10228
+ defaultHide: z.number().min(0).max(1)
10229
+ }).strict();
10230
+ //#endregion
10078
10231
  //#region packages/application/src/journey/b/duplicates.ts
10079
10232
  /** At most this many fields bind to one analysed occurrence, so one field's bindings fit a batch. */
10080
10233
  const bBindingCap = 2e3;
@@ -10203,133 +10356,6 @@ function expandBDuplicates(unit, review) {
10203
10356
  };
10204
10357
  }
10205
10358
  //#endregion
10206
- //#region packages/application/src/journey/b/ranges.ts
10207
- /** Invocation-local coverage of already validated code-point ranges. */
10208
- function indexBRanges(parts) {
10209
- const ranges = [];
10210
- const ordered = parts.map(({ start, end }) => ({
10211
- start,
10212
- end
10213
- }));
10214
- ordered.sort((left, right) => left.start - right.start);
10215
- for (const part of ordered) {
10216
- const last = ranges.at(-1);
10217
- if (last && part.start <= last.end) last.end = Math.max(last.end, part.end);
10218
- else ranges.push(part);
10219
- }
10220
- const firstOverlap = (start) => {
10221
- let low = 0;
10222
- let high = ranges.length;
10223
- while (low < high) {
10224
- const middle = Math.floor((low + high) / 2);
10225
- if ((ranges[middle]?.end ?? Infinity) <= start) low = middle + 1;
10226
- else high = middle;
10227
- }
10228
- return low;
10229
- };
10230
- return {
10231
- relation(query) {
10232
- const part = ranges[firstOverlap(query.start)];
10233
- return {
10234
- covers: Boolean(part && part.start <= query.start && part.end >= query.end),
10235
- overlaps: Boolean(part && part.start < query.end)
10236
- };
10237
- },
10238
- uncovered(query) {
10239
- const result = [];
10240
- let cursor = query.start;
10241
- for (let index = firstOverlap(cursor); index < ranges.length; index += 1) {
10242
- const part = ranges[index];
10243
- if (!part || part.start >= query.end) break;
10244
- if (cursor < part.start) result.push({
10245
- start: cursor,
10246
- end: part.start
10247
- });
10248
- cursor = Math.max(cursor, part.end);
10249
- if (cursor >= query.end) break;
10250
- }
10251
- if (cursor < query.end) result.push({
10252
- start: cursor,
10253
- end: query.end
10254
- });
10255
- return result;
10256
- }
10257
- };
10258
- }
10259
- //#endregion
10260
- //#region packages/application/src/journey/b/literals.ts
10261
- function bLiteralAdmitted(value) {
10262
- return admitGitName(value, 1)?.value === value;
10263
- }
10264
- function bLiterals(evidence) {
10265
- const value = evidence.exactQuote.normalize("NFC");
10266
- if (bLiteralAdmitted(value)) return [{
10267
- ...evidence,
10268
- term: value
10269
- }];
10270
- return literalPieces(evidence.exactQuote).map((part) => ({
10271
- ...evidence,
10272
- start: evidence.start + part.start,
10273
- end: evidence.start + part.end,
10274
- utf16Start: evidence.utf16Start + part.utf16Start,
10275
- utf16End: evidence.utf16Start + part.utf16End,
10276
- exactQuote: part.exactQuote,
10277
- term: part.term
10278
- }));
10279
- }
10280
- /** The same admitted NFC terms for hidden text that is not one B evidence anchor. */
10281
- function bLiteralTerms(text) {
10282
- const value = text.normalize("NFC");
10283
- return bLiteralAdmitted(value) ? [value] : literalPieces(text).map((part) => part.term);
10284
- }
10285
- /** Bounded line pieces of a nonrepresentable quote, in quote-relative coordinates. */
10286
- function literalPieces(quote) {
10287
- const chars = Array.from(quote);
10288
- const index = createCodePointIndex(quote);
10289
- const parts = [];
10290
- let cursor = 0;
10291
- while (cursor < chars.length) {
10292
- while (cursor < chars.length && /\s/u.test(chars[cursor] ?? "")) cursor += 1;
10293
- if (cursor === chars.length) break;
10294
- const newline = chars.indexOf("\n", cursor);
10295
- const lineEnd = newline < 0 ? chars.length : newline;
10296
- let end = Math.min(lineEnd, cursor + 256);
10297
- if (end < lineEnd) {
10298
- let soft = end;
10299
- while (soft > cursor + 12 && !/\s/u.test(chars[soft - 1] ?? "")) soft -= 1;
10300
- if (soft > cursor + 12) end = soft;
10301
- }
10302
- while (end > cursor && /\s/u.test(chars[end - 1] ?? "")) end -= 1;
10303
- if (end <= cursor) {
10304
- cursor += 1;
10305
- continue;
10306
- }
10307
- const exactQuote = index.slice(cursor, end);
10308
- const term = exactQuote.normalize("NFC");
10309
- if (end - cursor >= 12 && bLiteralAdmitted(term)) parts.push({
10310
- start: cursor,
10311
- end,
10312
- utf16Start: index.offset(cursor),
10313
- utf16End: index.offset(end),
10314
- exactQuote,
10315
- term
10316
- });
10317
- cursor = end;
10318
- }
10319
- return parts;
10320
- }
10321
- function uncoveredBObligations(sensitive, literals) {
10322
- if (sensitive.length === 0) return [];
10323
- const fields = new Map([...Map.groupBy(literals, (part) => part.fieldId)].map(([fieldId, parts]) => [fieldId, indexBRanges(parts)]));
10324
- return sensitive.flatMap((evidence) => (fields.get(evidence.fieldId)?.uncovered(evidence) ?? [{
10325
- start: evidence.start,
10326
- end: evidence.end
10327
- }]).filter((range) => /\S/u.test(codePointSlice(evidence.exactQuote, range.start - evidence.start, range.end - evidence.start))).map((range) => ({
10328
- fieldId: evidence.fieldId,
10329
- ...range
10330
- })));
10331
- }
10332
- //#endregion
10333
10359
  //#region packages/application/src/journey/b/units.ts
10334
10360
  function validateBFields(fields) {
10335
10361
  const ids = /* @__PURE__ */ new Set();
@@ -10400,7 +10426,7 @@ function createBAnalysisUnits(input) {
10400
10426
  };
10401
10427
  });
10402
10428
  if (total === 0) return [];
10403
- const coreSize = total <= 8e4 ? 8e4 : 64e3;
10429
+ const coreSize = total <= 4e4 ? 4e4 : 24e3;
10404
10430
  const cores = input.cores ?? Array.from({ length: Math.ceil(total / coreSize) }, (_, ordinal) => Math.min(coreSize, total - ordinal * coreSize));
10405
10431
  if (cores.reduce((sum, core) => sum + core, 0) !== total) throw new ApplicationError("INCOMPLETE_REVIEW");
10406
10432
  const totalUnits = cores.length;
@@ -10566,25 +10592,6 @@ function bContextEvidence(unit, raw) {
10566
10592
  ].map(read).find((part) => !part.owned);
10567
10593
  }
10568
10594
  //#endregion
10569
- //#region packages/application/src/journey/review/findings.ts
10570
- const semanticFindingSchema = z.object({
10571
- id: journeyIdSchema,
10572
- chunkId: journeyIdSchema,
10573
- start: z.number().int().nonnegative(),
10574
- end: z.number().int().nonnegative(),
10575
- spanHash: journeyDigestSchema,
10576
- kind: journeyKindSchema,
10577
- class: z.enum([
10578
- "certain",
10579
- "gray",
10580
- "clear"
10581
- ]),
10582
- label: z.string().trim().min(1).max(120),
10583
- groupKey: z.string().trim().min(1).max(120).nullable(),
10584
- condition: z.string().max(1e3),
10585
- defaultHide: z.number().min(0).max(1)
10586
- }).strict();
10587
- //#endregion
10588
10595
  //#region packages/application/src/journey/review/alignment.ts
10589
10596
  const referenceSchema = z.object({
10590
10597
  batchId: journeyIdSchema,
@@ -10904,43 +10911,82 @@ function matchesReadReceipts(batch, attemptId, receipts) {
10904
10911
  }
10905
10912
  //#endregion
10906
10913
  //#region packages/application/src/journey/terms/locate.ts
10907
- /** NFC core text of each analysed field, contiguous core chunks joined, with its Work. */
10908
- function coreTexts(batches) {
10909
- const texts = [];
10914
+ /**
10915
+ * Issue #140: per batch and field, contiguous core and context chunks in offset order, never
10916
+ * joined across fields, field revisions or gaps. A core boundary maps to the NFC length of the
10917
+ * text before it.
10918
+ */
10919
+ function* termSearchRuns(batches) {
10910
10920
  for (const batch of batches) {
10911
- const fields = Map.groupBy(batch.chunks.filter((chunk) => chunk.core), (chunk) => JSON.stringify([chunk.fieldId, chunk.fieldHash]));
10912
- for (const chunks of fields.values()) {
10913
- let end = -1, text = "";
10914
- for (const chunk of chunks.toSorted((a, b) => a.offset - b.offset)) {
10915
- if (chunk.offset !== end && text) {
10916
- texts.push({
10917
- workId: batch.workId,
10918
- text: text.normalize("NFC")
10919
- });
10920
- text = "";
10921
+ const fields = Map.groupBy(batch.chunks, (chunk) => JSON.stringify([chunk.fieldId, chunk.fieldHash]));
10922
+ for (const group of fields.values()) {
10923
+ let run = [];
10924
+ const runs = [run];
10925
+ for (const chunk of group.toSorted((a, b) => a.offset - b.offset)) {
10926
+ const last = run.at(-1);
10927
+ if (last && last.offset + last.text.length !== chunk.offset) runs.push(run = []);
10928
+ run.push(chunk);
10929
+ }
10930
+ for (const chunks of runs) {
10931
+ if (!chunks.some((chunk) => chunk.core)) continue;
10932
+ const raw = chunks.map((chunk) => chunk.text).join("");
10933
+ const text = raw.normalize("NFC");
10934
+ const at = (end) => Math.min(text.length, raw.slice(0, end).normalize("NFC").length);
10935
+ const cores = [];
10936
+ let offset = 0;
10937
+ for (const chunk of chunks) {
10938
+ const start = offset;
10939
+ offset += chunk.text.length;
10940
+ if (!chunk.core) continue;
10941
+ const last = cores.at(-1);
10942
+ if (last && last[1] === start) last[1] = offset;
10943
+ else cores.push([start, offset]);
10921
10944
  }
10922
- text += chunk.text;
10923
- end = chunk.offset + chunk.text.length;
10945
+ yield {
10946
+ batch,
10947
+ chunks,
10948
+ text,
10949
+ cores: cores.map(([start, end]) => [at(start), at(end)])
10950
+ };
10924
10951
  }
10925
- if (text) texts.push({
10926
- workId: batch.workId,
10927
- text: text.normalize("NFC")
10928
- });
10929
10952
  }
10930
10953
  }
10931
- return texts;
10932
10954
  }
10933
- function occurrences(text, term) {
10934
- let count = 0;
10935
- for (let at = text.indexOf(term); at >= 0; at = text.indexOf(term, at + term.length)) count++;
10936
- return count;
10955
+ /**
10956
+ * Occurrences of `value` in a run: `owned` counts non-overlapping occurrences that start in a
10957
+ * core range; `first` is the first occurrence overlapping a core range, or -1. An occurrence that
10958
+ * starts outside the core is skipped by one position only, so it never hides an owned one.
10959
+ */
10960
+ function runOccurrences(run, value) {
10961
+ let owned = 0, first = -1;
10962
+ if (!value) return {
10963
+ owned,
10964
+ first
10965
+ };
10966
+ for (let at = run.text.indexOf(value); at >= 0;) {
10967
+ const end = at + value.length;
10968
+ if (run.cores.some(([start, stop]) => at >= start && at < stop)) {
10969
+ owned++;
10970
+ if (first < 0) first = at;
10971
+ at = run.text.indexOf(value, end);
10972
+ continue;
10973
+ }
10974
+ if (first < 0 && run.cores.some(([start, stop]) => at < stop && end > start)) first = at;
10975
+ at = run.text.indexOf(value, at + 1);
10976
+ }
10977
+ return {
10978
+ owned,
10979
+ first
10980
+ };
10937
10981
  }
10938
10982
  /**
10939
- * ADR 0086: a value is kept only if it occurs verbatim (NFC, case-sensitive) in a core chunk of
10940
- * the pack's units; its complete admitted NFC value becomes one dictionary entry (ADR 0088).
10983
+ * ADR 0086, refined by issue #140: a value is kept only if it occurs verbatim (NFC,
10984
+ * case-sensitive) in a pack field's contiguous core and context text, overlapping a core chunk;
10985
+ * only occurrences that start in a core chunk are counted. Its complete admitted NFC value
10986
+ * becomes one dictionary entry (ADR 0088).
10941
10987
  */
10942
10988
  function locateTerms(batches, terms) {
10943
- const texts = coreTexts(batches);
10989
+ const runs = [...termSearchRuns(batches)];
10944
10990
  const located = /* @__PURE__ */ new Map();
10945
10991
  const unlocatedIndices = [];
10946
10992
  let kept = 0, unlocated = 0;
@@ -10953,20 +10999,25 @@ function locateTerms(batches, terms) {
10953
10999
  }];
10954
11000
  }));
10955
11001
  for (const { type, value, index } of unique.values()) {
10956
- if (value.includes("█") || admitCustomTerm(value)?.value !== value || !texts.some((item) => item.text.includes(value))) {
11002
+ let count = 0;
11003
+ let workId;
11004
+ if (!value.includes("█") && admitCustomTerm(value)?.value === value) for (const run of runs) {
11005
+ const { owned, first } = runOccurrences(run, value);
11006
+ if (first < 0) continue;
11007
+ count += owned;
11008
+ workId ??= run.batch.workId;
11009
+ }
11010
+ if (workId === void 0) {
10957
11011
  unlocated++;
10958
11012
  unlocatedIndices.push(index);
10959
11013
  continue;
10960
11014
  }
10961
11015
  kept++;
10962
- const holders = texts.filter((item) => item.text.includes(value));
10963
- const first = holders[0];
10964
- if (!first) continue;
10965
11016
  located.set(JSON.stringify([type, value]), {
10966
11017
  type,
10967
11018
  value,
10968
- count: holders.reduce((sum, item) => sum + occurrences(item.text, value), 0),
10969
- workId: first.workId
11019
+ count,
11020
+ workId
10970
11021
  });
10971
11022
  }
10972
11023
  return {
@@ -10977,4 +11028,4 @@ function locateTerms(batches, terms) {
10977
11028
  };
10978
11029
  }
10979
11030
  //#endregion
10980
- export { bTermSchema as $, buildPlan as $n, profileHasChoices as $t, indexBRanges as A, createDetectionSummary as An, packageConfirmActionSchema as At, unownedBObligations as B, cleanupImplementationVersion as Bn, termOverrideValueSchema as Bt, createBAnalysisUnits as C, reviewRoles as Cn, hidePlanResourceSchema as Ct, bLiteralTerms as D, bindInspection as Dn, cardsSubmitSchema as Dt, bLiteralAdmitted as E, createSessionPreparation as En, cardsConfirmActionSchema as Et, bUnitFragments as F, staticOptions as Fn, emptyReviewCorrectionSchema as Ft, codePointSlice as G, nativeField as Gn, filterInventoryByPeriod as Gt, bEvidenceReader as H, nativeParserVersion as Hn, termTypeSchema as Ht, bAdoptedObligations as I, createReplacementKey as In, journeyTermSchema as It, utf16Offset as J, transformRecords as Jn, journeyProfileAddressActionSchema as Jt, createCodePointIndex as K, nativeItems as Kn, sessionPeriodSchema as Kt, bClassificationKey as L, sessionNonce as Ln, reviewTermsInputSchema as Lt, bDuplicateMirror as M, walkNativeFields as Mn, privacyTermKey as Mt, bDuplicateShift as N, createStaticProcessor as Nn, privacyTermSchema as Nt, bLiterals as O, currentDetectorVersion as On, decisionCardInputSchema as Ot, bLegacyBindings as P, staticDefaults as Pn, emptyReviewCorrectionResultSchema as Pt, bSessionDecisionSchema as Q, orderProfile as Qn, passportAddressSchema as Qt, bIssue as R, projectNativeRecord as Rn, reviewTermsResultSchema as Rt, validateBReview as S, mainReviewRole as Sn, hidePlanInputSchema as St, validateBUnit as T, renderNative as Tn, cardAnswerActionSchema as Tt, bDigest as U, redactionImplementationVersion as Un, termTypes as Ut, bAnchorKey as V, entityDictionaryVersion as Vn, termTypeInfo as Vt, codePointLength as W, nativeBoolean as Wn, deselectOutsidePeriod as Wt, bRetainedGroupSchema as X, retainedMainTextNodes as Xn, mergeOwnerProfile as Xt, bRepresentationSchema as Y, frameRecords as Yn, journeyProfileLinksActionSchema as Yt, bReviewSubmissionSchema as Z, validateProfile as Zn, ownerProfileSchema as Zt, reviewFindingsDigest as _, workspaceSchema as _n, declaredVersion as _r, journeyInstructionInputSchema as _t, reviewBatchChunks as a, safeField as an, detectStatic as ar, journeyAccessSchema as at, bContextEvidence as b, processNativeRecord as bn, processingSite as br, journeyWorkSuggestionSchema as bt, reviewDigest as c, reviewFamilies as cn, jsonSpans as cr, journeyExceptionSchema as ct, validateReview as d, observeSource as dn, admitCustomTerm as dr, journeyPreferenceScopeSchema as dt, respondProfileLinks as en, validatePlan as er, bAnchorSchema as et, findingReferenceKey as f, sourceSnapshotId as fn, admitGitName as fr, journeyProjectTypeSchema as ft, reviewAlignmentSchema as g, sourcePartSchema as gn, ProcessingError as gr, journeyGuidanceSchema as gt, reviewAlignmentInputSchema as h, sessionSchema as hn, defaultDetectionLimits as hr, reviewInterruptionSchema as ht, compileBFindings as i, safeCode as in, resolveLimits as ir, consentedWithoutAccount as it, bBindingCap as j, gapReasons as jn, privacyResolveSchema as jt, uncoveredBObligations as k, inspectionSemanticVersions as kn, emptyJourneyPrivacy as kt, reviewSubmitSchema as l, sourceGraph as ln, agentContextLocation as lr, journeyIdSchema as lt, reviewAlignmentAuditSchema as m, inventorySchema as mn, scalarBefore as mr, nativeJourneySchema as mt, matchesReadReceipts as n, ApplicationError as nn, defaultLimits as nr, bIdSchema as nt, reviewBatchSchema as o, familyNeedsReview as on, isStaticCategory as or, journeyChoiceSchema as ot, reviewAlignmentAudit as p, fileIdentitySchema as pn, scalarAfter as pr, journeyWorkTypeSchema as pt, subtractBRanges as q, nativeText as qn, emptyJourneyProfile as qt, reviewReadPages as r, UnfinishedJourneyError as rn, recordPointerLimits as rr, bMethod as rt, reviewChunkSchema as s, familySelected as sn, staticCategories as sr, journeyDigestSchema as st, locateTerms as t, classificationTitleMaskSchema as tn, copyData as tr, bCountSchema as tt, textDigest as u, sourceKey as un, createEntityMatcher as ur, journeyKindSchema as ut, validateReviewAlignment as v, nativeSourceIdentitySchema as vn, fail as vr, delegatedDecisionSchema as vt, validateBFields as w, createSentenceContext as wn, journeyConfigurationSchema as wt, validateBClassification as x, projectNativeReviewInput as xn, siteFields as xr, hideConfirmSchema as xt, semanticFindingSchema as y, withMandatoryCustomTerms as yn, isProcessingCode as yr, journeyDescribeSchema as yt, bOverlaps as z, parseNativeRecord as zn, termAssessmentSchema as zt };
11031
+ export { createCodePointIndex as $, validateProfile as $n, ownerProfileSchema as $t, bLegacyBindings as A, currentDetectorVersion as An, decisionCardInputSchema as At, bIssue as B, projectNativeRecord as Bn, reviewTermsResultSchema as Bt, validateBReview as C, projectNativeReviewInput as Cn, siteFields as Cr, hideConfirmSchema as Ct, bBindingCap as D, renderNative as Dn, cardAnswerActionSchema as Dt, validateBUnit as E, createSentenceContext as En, journeyConfigurationSchema as Et, bLiterals as F, createStaticProcessor as Fn, privacyTermSchema as Ft, bReviewSubmissionSchema as G, redactionImplementationVersion as Gn, termTypes as Gt, unownedBObligations as H, cleanupImplementationVersion as Hn, termOverrideValueSchema as Ht, uncoveredBObligations as I, staticDefaults as In, emptyReviewCorrectionResultSchema as It, bAnchorKey as J, nativeItems as Jn, sessionPeriodSchema as Jt, bSessionDecisionSchema as K, nativeBoolean as Kn, deselectOutsidePeriod as Kt, indexBRanges as L, staticOptions as Ln, emptyReviewCorrectionSchema as Lt, semanticFindingSchema as M, createDetectionSummary as Mn, packageConfirmActionSchema as Mt, bLiteralAdmitted as N, gapReasons as Nn, privacyResolveSchema as Nt, bDuplicateMirror as O, createSessionPreparation as On, cardsConfirmActionSchema as Ot, bLiteralTerms as P, walkNativeFields as Pn, privacyTermKey as Pt, codePointSlice as Q, retainedMainTextNodes as Qn, mergeOwnerProfile as Qt, bAdoptedObligations as R, createReplacementKey as Rn, journeyTermSchema as Rt, validateBClassification as S, processNativeRecord as Sn, processingSite as Sr, journeyWorkSuggestionSchema as St, validateBFields as T, reviewRoles as Tn, hidePlanResourceSchema as Tt, bRepresentationSchema as U, entityDictionaryVersion as Un, termTypeInfo as Ut, bOverlaps as V, parseNativeRecord as Vn, termAssessmentSchema as Vt, bRetainedGroupSchema as W, nativeParserVersion as Wn, termTypeSchema as Wt, bDigest as X, transformRecords as Xn, journeyProfileAddressActionSchema as Xt, bEvidenceReader as Y, nativeText as Yn, emptyJourneyProfile as Yt, codePointLength as Z, frameRecords as Zn, journeyProfileLinksActionSchema as Zt, reviewAlignmentInputSchema as _, sessionSchema as _n, defaultDetectionLimits as _r, reviewInterruptionSchema as _t, reviewReadPages as a, UnfinishedJourneyError as an, recordPointerLimits as ar, bMethod as at, validateReviewAlignment as b, nativeSourceIdentitySchema as bn, fail as br, delegatedDecisionSchema as bt, reviewBatchSchema as c, familyNeedsReview as cn, isStaticCategory as cr, journeyChoiceSchema as ct, reviewSubmitSchema as d, sourceGraph as dn, agentContextLocation as dr, journeyIdSchema as dt, passportAddressSchema as en, orderProfile as er, subtractBRanges as et, textDigest as f, sourceKey as fn, createEntityMatcher as fr, journeyKindSchema as ft, reviewAlignmentAuditSchema as g, inventorySchema as gn, scalarBefore as gr, nativeJourneySchema as gt, reviewAlignmentAudit as h, fileIdentitySchema as hn, scalarAfter as hr, journeyWorkTypeSchema as ht, matchesReadReceipts as i, ApplicationError as in, defaultLimits as ir, bIdSchema as it, bUnitFragments as j, inspectionSemanticVersions as jn, emptyJourneyPrivacy as jt, bDuplicateShift as k, bindInspection as kn, cardsSubmitSchema as kt, reviewChunkSchema as l, familySelected as ln, staticCategories as lr, journeyDigestSchema as lt, findingReferenceKey as m, sourceSnapshotId as mn, admitGitName as mr, journeyProjectTypeSchema as mt, runOccurrences as n, respondProfileLinks as nn, validatePlan as nr, bAnchorSchema as nt, compileBFindings as o, safeCode as on, resolveLimits as or, consentedWithoutAccount as ot, validateReview as p, observeSource as pn, admitCustomTerm as pr, journeyPreferenceScopeSchema as pt, bTermSchema as q, nativeField as qn, filterInventoryByPeriod as qt, termSearchRuns as r, classificationTitleMaskSchema as rn, copyData as rr, bCountSchema as rt, reviewBatchChunks as s, safeField as sn, detectStatic as sr, journeyAccessSchema as st, locateTerms as t, profileHasChoices as tn, buildPlan as tr, utf16Offset as tt, reviewDigest as u, reviewFamilies as un, jsonSpans as ur, journeyExceptionSchema as ut, reviewAlignmentSchema as v, sourcePartSchema as vn, ProcessingError as vr, journeyGuidanceSchema as vt, createBAnalysisUnits as w, mainReviewRole as wn, hidePlanInputSchema as wt, bContextEvidence as x, withMandatoryCustomTerms as xn, isProcessingCode as xr, journeyDescribeSchema as xt, reviewFindingsDigest as y, workspaceSchema as yn, declaredVersion as yr, journeyInstructionInputSchema as yt, bClassificationKey as z, sessionNonce as zn, reviewTermsInputSchema as zt };
@@ -1,4 +1,4 @@
1
- import "./src-Do-qzuMn.mjs";
1
+ import "./src-CttNuxOn.mjs";
2
2
  //#region packages/harnesses/src/discovery/model.ts
3
3
  const discoveryBounds = {
4
4
  entries: 1e5,
@@ -1,6 +1,6 @@
1
- import { c as executorRequestSchema, g as longCommands, i as invoke, m as parseOptions, n as readOperation, o as createRuntime, r as waitOperation, v as startupFailure } from "./query-BYDcNKy2.mjs";
2
- import { an as safeField, in as safeCode, nn as ApplicationError } from "./locate-D-ZaHpI0.mjs";
3
- import "./src-Do-qzuMn.mjs";
1
+ import { c as executorRequestSchema, g as longCommands, i as invoke, m as parseOptions, n as readOperation, o as createRuntime, r as waitOperation, v as startupFailure } from "./query-BC7MIaWc.mjs";
2
+ import { in as ApplicationError, on as safeCode, sn as safeField } from "./locate-PLEyLYW-.mjs";
3
+ import "./src-CttNuxOn.mjs";
4
4
  import { z } from "zod";
5
5
  import { setTimeout } from "node:timers/promises";
6
6
  //#region apps/cli/src/operation/executor.ts
@@ -1,4 +1,4 @@
1
- import { n as assertPreviewHeap, o as previewInputSchema, t as runPreview } from "./run-DQ9xyKSt.mjs";
1
+ import { n as assertPreviewHeap, o as previewInputSchema, t as runPreview } from "./run-Dod7Vykp.mjs";
2
2
  import { parentPort, workerData } from "node:worker_threads";
3
3
  //#region packages/harnesses/src/preview/worker/index.ts
4
4
  function notify(message) {
@@ -1,4 +1,4 @@
1
- import { Mt as privacyTermKey, Ot as decisionCardInputSchema, jt as privacyResolveSchema, r as reviewReadPages, t as locateTerms } from "./locate-D-ZaHpI0.mjs";
1
+ import { At as decisionCardInputSchema, Nt as privacyResolveSchema, Pt as privacyTermKey, a as reviewReadPages, t as locateTerms } from "./locate-PLEyLYW-.mjs";
2
2
  import { z } from "zod";
3
3
  //#region apps/cli/src/skill-install/privacy-validator.ts
4
4
  const sourceReferenceSchema = z.object({