agentera 3.0.0-dev.72 → 3.0.0-dev.74

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 (92) hide show
  1. package/README.md +111 -0
  2. package/bundle/.agentera-build-source.json +8 -0
  3. package/bundle/CHANGELOG.md +17 -17
  4. package/bundle/extract-corpus-parity.json +1 -1
  5. package/bundle/references/adapters/package-publication.json +49 -53
  6. package/bundle/references/adapters/package-registry.yaml +5 -1
  7. package/bundle/references/adapters/package-surface-characterization.md +6 -4
  8. package/bundle/references/analysis/evidence-tier-authority.yaml +45 -4
  9. package/bundle/references/analysis/personal-glossary-evaluation-authority.yaml +175 -0
  10. package/bundle/references/analysis/personal-glossary-evaluation-corpus.yaml +291 -0
  11. package/bundle/references/analysis/personal-glossary-holdout.yaml +192 -0
  12. package/bundle/references/artifacts/glossary-entry-contract.yaml +1533 -35
  13. package/bundle/references/cli/update-channels.yaml +2 -2
  14. package/bundle/references/cli/vocabulary.md +1 -1
  15. package/bundle/references/meta/retained-reference-authority.yaml +15 -0
  16. package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +11 -7
  17. package/bundle/skills/agentera/capabilities/profile/schemas/exit.yaml +6 -3
  18. package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +13 -7
  19. package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +5 -0
  20. package/bundle/skills/agentera/schemas/artifacts/plan.yaml +7 -0
  21. package/dist/.agentera-build-source.json +8 -0
  22. package/dist/analytics/extractCorpus/copilotSessions.js +13 -4
  23. package/dist/analytics/extractCorpus/core.js +223 -3
  24. package/dist/analytics/extractCorpus/corpus.js +18 -1
  25. package/dist/analytics/extractCorpus/cursorSessions.js +22 -4
  26. package/dist/analytics/extractCorpus/evidenceTiers.js +123 -17
  27. package/dist/analytics/extractCorpus/filesystemSources.js +4 -0
  28. package/dist/analytics/extractCorpus/index.js +2 -2
  29. package/dist/analytics/extractCorpus/jsonlSessions.js +52 -3
  30. package/dist/analytics/extractCorpus/sqliteSessions.js +12 -3
  31. package/dist/analytics/personalGlossaryAdmission.js +26 -46
  32. package/dist/analytics/personalGlossaryCandidateProjection.js +692 -0
  33. package/dist/analytics/personalGlossaryCandidateProjectionExcerpts.js +144 -0
  34. package/dist/analytics/personalGlossaryCandidateReadView.js +23 -0
  35. package/dist/analytics/personalGlossaryCurrentGeneration.js +25 -0
  36. package/dist/analytics/personalGlossaryDecision.js +203 -0
  37. package/dist/analytics/personalGlossaryExplicit.js +788 -0
  38. package/dist/analytics/personalGlossaryExplicitMining.js +285 -0
  39. package/dist/analytics/personalGlossaryExplicitScope.js +41 -0
  40. package/dist/analytics/personalGlossaryExplicitSegments.js +418 -0
  41. package/dist/analytics/personalGlossaryExplicitTypes.js +26 -0
  42. package/dist/analytics/personalGlossaryLexicalClassifier.js +175 -0
  43. package/dist/analytics/personalGlossaryProfile.js +13 -3
  44. package/dist/analytics/personalGlossaryRecurrence.js +618 -0
  45. package/dist/analytics/personalGlossaryReviewRecordStorage.js +428 -0
  46. package/dist/analytics/personalGlossaryReviewRecords.js +332 -0
  47. package/dist/capabilities/index.js +5 -1
  48. package/dist/capabilities/profile/instructions.js +50 -15
  49. package/dist/cli/commands/personalGlossaryCandidateReads.js +744 -0
  50. package/dist/cli/commands/personalGlossaryDecision.js +349 -0
  51. package/dist/cli/commands/personalGlossaryPublish.js +484 -0
  52. package/dist/cli/commands/personalGlossaryReviewRecordReads.js +209 -0
  53. package/dist/cli/commands/personalGlossaryReviewRecords.js +623 -0
  54. package/dist/cli/commands/prime/briefOrientation.js +118 -19
  55. package/dist/cli/commands/report.js +18 -2
  56. package/dist/cli/commands/schema.js +179 -0
  57. package/dist/cli/commands/verify.js +51 -9
  58. package/dist/cli/dispatch/lifecycle.js +51 -6
  59. package/dist/cli/help.js +48 -2
  60. package/dist/cli/migrationRequired.js +4 -1
  61. package/dist/core/atomicWriter.js +4 -1
  62. package/dist/core/developmentInvocation.js +4 -1
  63. package/dist/core/yaml.js +53 -1
  64. package/dist/eval/glossaryEvaluation.js +751 -0
  65. package/dist/eval/glossaryEvaluationProcess.js +29 -0
  66. package/dist/eval/glossaryEvaluationRunner.js +571 -0
  67. package/dist/eval/glossaryEvaluationSuccessReport.js +297 -0
  68. package/dist/registries/activationTuples.js +31 -3
  69. package/dist/registries/evidenceTierContract.js +4 -0
  70. package/dist/registries/explicitSegmentGrammarContract.js +619 -0
  71. package/dist/registries/glossaryCandidateContractPaths.js +6 -0
  72. package/dist/registries/glossaryCandidateContracts.js +820 -0
  73. package/dist/registries/glossaryCandidateDecisionAuthority.js +119 -0
  74. package/dist/registries/glossaryCandidateDecisionContract.js +62 -0
  75. package/dist/registries/glossaryCandidateProjectionAuthority.js +197 -0
  76. package/dist/registries/glossaryCandidateProjectionContract.js +104 -0
  77. package/dist/registries/glossaryEntryContract.js +105 -103
  78. package/dist/registries/glossaryEntryTemporal.js +12 -0
  79. package/dist/registries/glossaryMiningAuthority.js +764 -0
  80. package/dist/registries/glossaryProfileFullContract.js +24 -0
  81. package/dist/registries/glossaryReviewRecordsAuthority.js +426 -0
  82. package/dist/registries/glossaryReviewRecordsContract.js +129 -0
  83. package/dist/registries/glossaryTermIdentity.js +122 -0
  84. package/dist/registries/packagePublication.js +3 -3
  85. package/dist/registries/personalGlossaryContracts.js +1 -0
  86. package/dist/release/releaseMetadata.js +14 -7
  87. package/dist/upgrade/npxPlatformStatus.js +2 -1
  88. package/dist/upgrade/projectIntegration.js +7 -1
  89. package/dist/validate/activationArtifactEvidence.js +18 -4
  90. package/dist/validate/activationEvidenceManifest.js +101 -22
  91. package/package.json +2 -4
  92. package/dist/cli/commands/personalGlossary.js +0 -176
@@ -0,0 +1,764 @@
1
+ import { createHash } from "node:crypto";
2
+ import { validatePersonalCandidateProjectionAuthority } from "./glossaryCandidateProjectionAuthority.js";
3
+ import { validatePersonalGlossaryReviewRecordsAuthority } from "./glossaryReviewRecordsAuthority.js";
4
+ import { validateExplicitSegmentGrammar } from "./explicitSegmentGrammarContract.js";
5
+ export const PERSONAL_GLOSSARY_MINING_POLICY_VERSION = "agentera.personalGlossaryMiningPolicy.v1";
6
+ function mapping(value) {
7
+ return value !== null && typeof value === "object" && !Array.isArray(value)
8
+ ? value
9
+ : null;
10
+ }
11
+ function strings(value) {
12
+ return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : [];
13
+ }
14
+ function sameStrings(actual, expected) {
15
+ return JSON.stringify(strings(actual)) === JSON.stringify(expected);
16
+ }
17
+ function nonEmpty(value) {
18
+ return typeof value === "string" && value.trim().length > 0;
19
+ }
20
+ function isLowerSha256(value) {
21
+ return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
22
+ }
23
+ function compareUnicodeStrings(left, right) {
24
+ const leftScalars = [...left];
25
+ const rightScalars = [...right];
26
+ for (let index = 0; index < Math.min(leftScalars.length, rightScalars.length); index += 1) {
27
+ const leftCodePoint = leftScalars[index].codePointAt(0);
28
+ const rightCodePoint = rightScalars[index].codePointAt(0);
29
+ if (leftCodePoint !== rightCodePoint)
30
+ return leftCodePoint - rightCodePoint;
31
+ }
32
+ return leftScalars.length - rightScalars.length;
33
+ }
34
+ function sha256Utf8(value) {
35
+ return createHash("sha256").update(value, "utf8").digest("hex");
36
+ }
37
+ export function glossaryEvidenceSetDigest(generation, anchors) {
38
+ if (!nonEmpty(generation))
39
+ throw new TypeError("generation must be a non-empty string");
40
+ if (anchors.length === 0 ||
41
+ anchors.some((anchor) => !nonEmpty(anchor)) ||
42
+ new Set(anchors).size !== anchors.length) {
43
+ throw new TypeError("qualifying evidence anchors must be non-empty and unique");
44
+ }
45
+ return sha256Utf8(JSON.stringify({
46
+ schema_version: "agentera.personalGlossaryEvidenceSet.v1",
47
+ generation,
48
+ anchors: [...anchors].sort(compareUnicodeStrings),
49
+ }));
50
+ }
51
+ /** Apply the contract-owned ordered consent discriminator without reading history. */
52
+ export function classifyPersonalMiningConsent(input) {
53
+ if (!["absent", "valid", "stale"].includes(input.consentStatus) ||
54
+ !["absent", "current", "stale"].includes(input.generationStatus) ||
55
+ !["complete", "cap_selected", "truncated", "flagged_incomplete"].includes(input.coverageStatus) ||
56
+ typeof input.signalTierBounded !== "boolean") {
57
+ throw new TypeError("consent discriminator input is invalid");
58
+ }
59
+ if (input.consentStatus === "absent" || input.generationStatus === "absent")
60
+ return "absent";
61
+ if (input.consentStatus === "stale" || input.generationStatus === "stale")
62
+ return "stale";
63
+ if (input.coverageStatus !== "complete")
64
+ return "degraded";
65
+ return "present";
66
+ }
67
+ export { PERSONAL_REVIEW_DISPOSITIONS, personalReviewApprovalReceiptDigest, personalReviewApprovalReplayStatus, personalReviewDispositionLifecycle, projectPersonalReviewRetention, validatePersonalReviewApprovalReceipt, } from "./glossaryReviewRecordsAuthority.js";
68
+ export function validatePersonalMiningAuthority(authority) {
69
+ const errors = [];
70
+ const mining = mapping(authority.personal_mining_authority);
71
+ const replacement = mapping(mining?.replacement);
72
+ const explicitDiscovery = mapping(mining?.explicit_discovery);
73
+ const explicitGrammar = mapping(explicitDiscovery?.grammar);
74
+ const adjacentDirectReference = mapping(explicitDiscovery?.adjacent_direct_reference_exclusion);
75
+ const adjacentDirectionalBinding = mapping(adjacentDirectReference?.binding);
76
+ const explicitForms = Array.isArray(explicitGrammar?.forms)
77
+ ? explicitGrammar.forms.map(mapping).filter((value) => value !== null)
78
+ : [];
79
+ const form = (id) => explicitForms.find((value) => value.id === id) ?? {};
80
+ if (mining?.status !== "active_partial" ||
81
+ !nonEmpty(mining?.implementation_boundary) ||
82
+ !sameStrings(mining?.preserved_decisions, ["prmnztjytv", "mcsmqqitnm", "zyvgyvjnsv"]) ||
83
+ replacement?.shared_primitive !== "shared_primitive" ||
84
+ replacement?.personal_owner !== "ownership_contracts.personal" ||
85
+ replacement?.project_owner !== "ownership_contracts.project" ||
86
+ replacement?.consumer_precedence !== "consumer_boundary.primary_selection" ||
87
+ replacement?.project_publication !== "ownership_contracts.project.publication" ||
88
+ !sameStrings(replacement?.allowed_effects, ["personal_profile_output"]) ||
89
+ !sameStrings(replacement?.forbidden_effects, [
90
+ "project_glossary_read",
91
+ "project_glossary_mutation",
92
+ "project_publication_replacement",
93
+ "consumer_precedence_change",
94
+ "personal_project_merge",
95
+ ]) ||
96
+ !nonEmpty(replacement?.rule)) {
97
+ errors.push("personal_mining_authority replacement must preserve shared primitive, isolation, consumer precedence, and Build project publication");
98
+ }
99
+ if (explicitDiscovery?.status !== "active" ||
100
+ explicitDiscovery?.runtime !==
101
+ "packages/cli/src/analytics/personalGlossaryExplicit.ts#discoverExplicitGlossaryCues" ||
102
+ explicitDiscovery?.mining_runtime !==
103
+ "packages/cli/src/analytics/personalGlossaryExplicitMining.ts#mineExplicitGlossaryCandidates" ||
104
+ explicitDiscovery?.input !== "bounded_signal_tier_plus_direct_single_anchor_resolution" ||
105
+ explicitDiscovery?.output !== "evidence_capsule_plus_exact_source_spans_only" ||
106
+ explicitDiscovery?.scope_window !== "current_sentence_and_one_adjacent_sentence_each_side" ||
107
+ explicitDiscovery?.scope_binding !==
108
+ "explicit_scope_marker_must_contain_the_term_or_be_a_direct_qualifier" ||
109
+ explicitDiscovery?.span_encoding !== "utf8_byte_offsets" ||
110
+ explicitDiscovery?.strings !== "preserve_exact_source_bytes_without_unicode_normalization" ||
111
+ !sameStrings(adjacentDirectReference?.references, [
112
+ "this_definition",
113
+ "this_term",
114
+ "this_meaning",
115
+ "following_definition",
116
+ "following_term",
117
+ "following_meaning",
118
+ ]) ||
119
+ !sameStrings(adjacentDirectReference?.qualifiers, [
120
+ "question",
121
+ "example",
122
+ "future",
123
+ "hypothetical",
124
+ ]) ||
125
+ adjacentDirectionalBinding?.preceding_sentence !== "following_reference_only" ||
126
+ adjacentDirectionalBinding?.following_sentence !== "this_reference_only" ||
127
+ adjacentDirectionalBinding?.exact_literal_term !== "either_direction" ||
128
+ !sameStrings(explicitForms.map((value) => String(value.id)), [
129
+ "quoted_means",
130
+ "definition_list_colon",
131
+ "acronym_stands_for",
132
+ "acronym_parenthetical",
133
+ "by_i_mean",
134
+ "use_for",
135
+ "use_to_mean",
136
+ "refers_to",
137
+ "clarification_prefer_to_mean",
138
+ "correction_means",
139
+ ]) ||
140
+ form("quoted_means").syntax !== "quoted_or_code_delimited_term_means_meaning" ||
141
+ form("definition_list_colon").syntax !== "standalone_safe_definition_list_term_colon_meaning" ||
142
+ form("definition_list_colon").prefix !==
143
+ "sentence_or_line_start_with_optional_markdown_list_bullet" ||
144
+ !sameStrings(form("definition_list_colon").unmarked_terms, [
145
+ "quoted_or_code_delimited_term",
146
+ "uppercase_acronym_with_exact_expansion_initials",
147
+ ]) ||
148
+ !sameStrings(form("definition_list_colon").markers, ["definition", "term"]) ||
149
+ form("definition_list_colon").marked_term !== "bounded_nonempty_term" ||
150
+ form("definition_list_colon").bare_multiword_term_without_marker !== "reject" ||
151
+ form("definition_list_colon").meaning_start !== "nonempty_same_physical_line" ||
152
+ form("definition_list_colon").continuation !==
153
+ "after_nonempty_inline_meaning_within_maximum_meaning_bound_only" ||
154
+ form("definition_list_colon").empty_marker_structure !==
155
+ "reject_immediately_following_colon_line" ||
156
+ form("acronym_stands_for").syntax !== "bounded_acronym_stands_for_expansion" ||
157
+ form("acronym_parenthetical").syntax !== "bounded_acronym_parenthesized_expansion_or_reverse" ||
158
+ form("by_i_mean").syntax !== "by_term_i_mean_meaning" ||
159
+ form("use_for").syntax !== "use_term_for_meaning" ||
160
+ form("use_to_mean").syntax !== "use_term_to_mean_meaning" ||
161
+ form("refers_to").syntax !== "term_refers_to_meaning" ||
162
+ form("clarification_prefer_to_mean").syntax !== "to_clarify_i_prefer_term_to_mean_meaning" ||
163
+ form("correction_means").syntax !==
164
+ "approved_correction_prefix_quoted_or_code_term_means_meaning" ||
165
+ form("acronym_stands_for").validation !== "expansion_initials_must_match_acronym_exactly" ||
166
+ form("acronym_parenthetical").validation !== "expansion_initials_must_match_acronym_exactly" ||
167
+ Number(mapping(explicitGrammar?.bounds)?.maximum_term_utf8_bytes) !== 256 ||
168
+ Number(mapping(explicitGrammar?.bounds)?.maximum_meaning_utf8_bytes) !== 4096 ||
169
+ Number(mapping(explicitGrammar?.bounds)?.maximum_acronym_utf8_bytes) !== 32 ||
170
+ Number(mapping(explicitGrammar?.bounds)?.maximum_acronym_words) !== 32) {
171
+ errors.push("personal_mining_authority explicit discovery must declare the active ten-form bounded grammar");
172
+ }
173
+ errors.push(...validateExplicitSegmentGrammar(explicitGrammar));
174
+ const explicitSourceProvenance = mapping(mapping(authority.provenance_variants)?.personal_explicit_definition);
175
+ const explicitSourceFields = mapping(explicitSourceProvenance?.source_provenance);
176
+ if (!sameStrings(explicitSourceFields?.required_fields, [
177
+ "source_id",
178
+ "evidence_anchor",
179
+ "source_kind",
180
+ "signal_type",
181
+ "origin_id",
182
+ "content_fingerprint",
183
+ "author_class",
184
+ "session_id",
185
+ "project_id",
186
+ ]) ||
187
+ !sameStrings(explicitSourceFields?.allowed_source_kinds, ["conversation_turn"]) ||
188
+ explicitSourceFields?.identity_fields_must_match_full_record !== true) {
189
+ errors.push("personal_explicit_definition source provenance must require complete conversation identity");
190
+ }
191
+ const termIdentity = mapping(mining?.term_identity);
192
+ const equality = mapping(termIdentity?.equality);
193
+ const stable = mapping(termIdentity?.stable_term_identity);
194
+ const revision = mapping(termIdentity?.candidate_revision);
195
+ if (equality?.runtime !==
196
+ "packages/cli/src/registries/glossaryTermIdentity.ts#unicodeCaselessExact" ||
197
+ equality?.semantics !== "unicode_caseless_exact_no_normalization" ||
198
+ !nonEmpty(equality?.rule) ||
199
+ stable?.schema_version !== "agentera.personalGlossaryTermIdentity.v1" ||
200
+ stable?.runtime !==
201
+ "packages/cli/src/registries/glossaryTermIdentity.ts#stableGlossaryTermIdentity" ||
202
+ stable?.algorithm !== "sha256" ||
203
+ stable?.encoding !== "lowercase_hex" ||
204
+ stable?.canonical_input !== "ecmascript_simple_case_fold_key" ||
205
+ stable?.no_unicode_normalization !== true ||
206
+ !nonEmpty(stable?.key_rule) ||
207
+ !nonEmpty(stable?.invariant) ||
208
+ revision?.schema_version !== "agentera.personalGlossaryCandidateRevision.v1" ||
209
+ revision?.runtime !==
210
+ "packages/cli/src/registries/glossaryTermIdentity.ts#glossaryCandidateRevision" ||
211
+ revision?.algorithm !== "sha256" ||
212
+ revision?.encoding !== "lowercase_hex" ||
213
+ !sameStrings(revision?.bound_fields, [
214
+ "stable_term_identity",
215
+ "meaning",
216
+ "scope",
217
+ "evidence",
218
+ "policy_version",
219
+ "generation",
220
+ ]) ||
221
+ revision?.policy_version !== PERSONAL_GLOSSARY_MINING_POLICY_VERSION ||
222
+ revision?.generation !== "content_addressed_candidate_projection_generation" ||
223
+ revision?.evidence !== "complete_source_id_anchor_identity_set" ||
224
+ !nonEmpty(revision?.canonicalization) ||
225
+ !nonEmpty(revision?.replay_rule)) {
226
+ errors.push("personal_mining_authority term identity must preserve Unicode caseless-exact no-normalization semantics and stable identity runtime");
227
+ }
228
+ const provenance = mapping(mining?.provenance);
229
+ const evolution = mapping(provenance?.evolution);
230
+ const provenanceVariants = mapping(authority.provenance_variants);
231
+ const conversationVariant = mapping(provenanceVariants?.personal_inferred_conversation);
232
+ const distinctness = mapping(conversationVariant?.distinctness);
233
+ const expectedEvidence = mapping(conversationVariant?.expected_evidence);
234
+ const setDigest = mapping(expectedEvidence?.set_digest);
235
+ if (provenance?.existing_inferred_variant !== "provenance_variants.personal_inferred_usage" ||
236
+ provenance?.conversation_variant !== "provenance_variants.personal_inferred_conversation" ||
237
+ evolution?.status !== "explicit_append_only_extension" ||
238
+ evolution?.shared_primitive !== "unchanged" ||
239
+ !nonEmpty(evolution?.existing_variant_rule) ||
240
+ !nonEmpty(evolution?.conversation_rule) ||
241
+ !nonEmpty(evolution?.audit_rule) ||
242
+ conversationVariant?.evidence_count !== "variable" ||
243
+ conversationVariant?.minimum_evidence_count !== 3 ||
244
+ !sameStrings(conversationVariant?.required_evidence_fields, [
245
+ "source_id",
246
+ "evidence_anchor",
247
+ "source_kind",
248
+ "signal_type",
249
+ "session_id",
250
+ "project_id",
251
+ "content_fingerprint",
252
+ "author_class",
253
+ ]) ||
254
+ conversationVariant?.additional_evidence_fields !== "forbidden" ||
255
+ !sameStrings(conversationVariant?.allowed_signal_types, [
256
+ "correction",
257
+ "decision",
258
+ "question",
259
+ "instruction",
260
+ "configuration",
261
+ ]) ||
262
+ !sameStrings(conversationVariant?.allowed_source_kinds, ["conversation_turn"]) ||
263
+ !sameStrings(conversationVariant?.allowed_author_classes, ["user"]) ||
264
+ distinctness?.source_ids !== 3 ||
265
+ distinctness?.evidence_anchors !== 3 ||
266
+ distinctness?.session_ids !== 2 ||
267
+ distinctness?.project_ids !== 2 ||
268
+ distinctness?.content_fingerprints !== 3 ||
269
+ conversationVariant?.completeness_field !== "provenance.evidence_complete" ||
270
+ conversationVariant?.completeness_value !== true ||
271
+ conversationVariant?.completeness_authority !== "expected_qualifying_anchor_set_exact_match" ||
272
+ expectedEvidence?.binding !== "generation_bound" ||
273
+ !sameStrings(expectedEvidence?.context_fields, [
274
+ "generation",
275
+ "qualifying_evidence_anchors",
276
+ "qualifying_evidence_set_sha256",
277
+ ]) ||
278
+ expectedEvidence?.exact_set !== "required" ||
279
+ setDigest?.algorithm !== "sha256" ||
280
+ setDigest?.encoding !== "lowercase_hex" ||
281
+ !nonEmpty(setDigest?.canonicalization) ||
282
+ !nonEmpty(expectedEvidence?.duplicate_rule) ||
283
+ conversationVariant?.admission !== "review_only") {
284
+ errors.push("personal_mining_authority provenance must append explicit complete conversation evidence without altering the two-record variant");
285
+ }
286
+ const consent = mapping(mining?.consent_lifecycle);
287
+ const consentStates = mapping(consent?.states);
288
+ const present = mapping(consentStates?.present);
289
+ const absent = mapping(consentStates?.absent);
290
+ const stale = mapping(consentStates?.stale);
291
+ const degraded = mapping(consentStates?.degraded);
292
+ const recovery = mapping(consent?.recovery);
293
+ const discriminator = mapping(consent?.discriminator);
294
+ const discriminatorValues = mapping(discriminator?.values);
295
+ const discriminatorPredicates = mapping(discriminator?.predicates);
296
+ if (consent?.authority !== "references/analysis/evidence-tier-authority.yaml" ||
297
+ consent?.operation !== "Profile_Full" ||
298
+ consent?.policy !== "reuse_existing_generation" ||
299
+ consent?.refresh !== "forbidden_during_profile_full" ||
300
+ !nonEmpty(consent?.reuse) ||
301
+ !sameStrings(discriminator?.inputs, [
302
+ "consent_status",
303
+ "generation_status",
304
+ "coverage_status",
305
+ "signal_tier_bounded",
306
+ ]) ||
307
+ !sameStrings(discriminator?.order, ["absent", "stale", "degraded", "present"]) ||
308
+ !sameStrings(discriminatorValues?.consent_status, ["absent", "valid", "stale"]) ||
309
+ !sameStrings(discriminatorValues?.generation_status, ["absent", "current", "stale"]) ||
310
+ !sameStrings(discriminatorValues?.coverage_status, [
311
+ "complete",
312
+ "cap_selected",
313
+ "truncated",
314
+ "flagged_incomplete",
315
+ ]) ||
316
+ JSON.stringify(discriminatorValues?.signal_tier_bounded) !== JSON.stringify([true, false]) ||
317
+ discriminatorPredicates?.absent !== "consent_absent_or_generation_absent" ||
318
+ discriminatorPredicates?.stale !== "after_absent_consent_or_generation_stale" ||
319
+ discriminatorPredicates?.degraded !==
320
+ "valid_current_actual_cap_truncation_or_flagged_incomplete" ||
321
+ discriminatorPredicates?.present !== "valid_current_complete_any_boundedness" ||
322
+ !nonEmpty(discriminator?.exclusivity) ||
323
+ !String(discriminator?.exclusivity).includes("first match") ||
324
+ !nonEmpty(discriminator?.boundedness_rule) ||
325
+ !String(discriminator?.boundedness_rule).includes("inherently bounded") ||
326
+ present?.condition !== "valid_current_complete_any_boundedness" ||
327
+ present?.action !== "reuse_generation_once_for_bounded_mining" ||
328
+ present?.result !== "mining_authorized" ||
329
+ absent?.condition !== "consent_absent_or_generation_absent" ||
330
+ absent?.action !== "skip_mining_without_read_or_refresh" ||
331
+ absent?.result !== "degraded_consent_required" ||
332
+ stale?.condition !== "after_absent_consent_or_generation_stale" ||
333
+ stale?.action !== "reject_generation_for_new_mining_and_preserve_existing_entries" ||
334
+ stale?.result !== "degraded_refresh_required" ||
335
+ degraded?.condition !== "valid_current_actual_cap_truncation_or_flagged_incomplete" ||
336
+ degraded?.action !== "reuse_only_as_degraded_evidence_without_automatic_admission" ||
337
+ degraded?.result !== "degraded_coverage" ||
338
+ recovery?.command !== "npx -y agentera@next report refresh --consent local-history" ||
339
+ recovery?.explicit_consent !== "required" ||
340
+ recovery?.after_refresh !== "retry_profile_full_with_the_new_generation" ||
341
+ !sameStrings(recovery?.forbidden, [
342
+ "live_extraction_without_consent",
343
+ "silent_refresh",
344
+ "stale_generation_reuse",
345
+ ]) ||
346
+ !nonEmpty(recovery?.rule)) {
347
+ errors.push("personal_mining_authority consent lifecycle must reuse existing generations and define present, absent, stale, and degraded recovery");
348
+ }
349
+ const profileFull = mapping(mining?.profile_full);
350
+ const existingGeneration = mapping(profileFull?.existing_generation);
351
+ const deterministicAdmission = mapping(profileFull?.deterministic_admission);
352
+ const review = mapping(profileFull?.review);
353
+ const publication = mapping(profileFull?.publication);
354
+ const output = mapping(profileFull?.output);
355
+ const candidateRetrieval = mapping(mining?.candidate_retrieval);
356
+ const candidateList = mapping(candidateRetrieval?.list);
357
+ if (profileFull?.status !== "active" ||
358
+ profileFull?.entrypoint !== "Profile_Full" ||
359
+ profileFull?.base_profile_order !== "write_base_profile_before_personal_glossary" ||
360
+ existingGeneration?.source !== "existing_consent_bound_user_local_candidate_projection" ||
361
+ existingGeneration?.generation_count !== 1 ||
362
+ !sameStrings(existingGeneration?.binding_fields, [
363
+ "generation",
364
+ "policy_version",
365
+ "candidate_projection_sha256",
366
+ ]) ||
367
+ existingGeneration?.list_limit !== candidateList?.default_limit ||
368
+ existingGeneration?.continuation !== "forbidden" ||
369
+ existingGeneration?.degraded !==
370
+ "explicit_refresh_required_without_candidate_classification_or_publication" ||
371
+ !sameStrings(existingGeneration?.forbidden_effects, [
372
+ "history_refresh",
373
+ "history_collection",
374
+ "candidate_discovery",
375
+ "candidate_projection_write",
376
+ "new_generation",
377
+ "project_glossary_read",
378
+ ]) ||
379
+ existingGeneration?.unavailable !== "preserve_base_profile_and_owned_section" ||
380
+ deterministicAdmission?.host_classification !== "semantic_evidence_not_admission_authority" ||
381
+ deterministicAdmission?.command !== "candidate_contracts.layers.cli_decision" ||
382
+ deterministicAdmission?.automatic !== "explicit_current_authorized_only" ||
383
+ deterministicAdmission?.inferred !== "review_or_abstain" ||
384
+ review?.queue_before_question !== true ||
385
+ review?.question_channel_maximum !== 3 ||
386
+ review?.no_question_channel !== "durable_queue" ||
387
+ review?.output !== "bounded_review_cards_and_summary" ||
388
+ publication?.command !== "ownership_contracts.personal.profile_output" ||
389
+ publication?.automatic !== "explicit_current_authorized_only" ||
390
+ publication?.no_decision_synthesis !== true ||
391
+ publication?.failure !== "preserve_base_profile_and_owned_section" ||
392
+ output?.profile_path !== "canonical_user_profile_authority" ||
393
+ output?.terminal !== "compact_bounded_summary") {
394
+ errors.push("personal_mining_authority Profile Full integration must reuse one existing consent-bound generation, preserve base output, bound review, and publish only explicit authorization");
395
+ }
396
+ const privacy = mapping(mining?.privacy);
397
+ const storage = mapping(privacy?.storage);
398
+ const storageFilesystem = mapping(storage?.filesystem);
399
+ const contentExclusion = mapping(privacy?.content_exclusion);
400
+ const excerpts = mapping(privacy?.excerpts);
401
+ const reviews = mapping(privacy?.reviews);
402
+ const authentication = mapping(reviews?.authentication);
403
+ const correction = mapping(authentication?.correction);
404
+ const signature = mapping(authentication?.signature);
405
+ const freshness = mapping(authentication?.freshness);
406
+ const replay = mapping(authentication?.replay);
407
+ const terminalDisposition = mapping(authentication?.terminal_disposition);
408
+ const retention = mapping(privacy?.retention);
409
+ const purge = mapping(privacy?.purge);
410
+ const purgeBoundaries = mapping(purge?.boundaries);
411
+ if (privacy?.owner !== "current_user" ||
412
+ privacy?.scope !== "user_local" ||
413
+ storage?.root !== "$AGENTERA_PROFILE_DIR/intermediate/personal-glossary" ||
414
+ storage?.project_storage !== "forbidden" ||
415
+ !sameStrings(storage?.records, [
416
+ "candidate_metadata",
417
+ "safe_excerpts",
418
+ "review_metadata",
419
+ "review_dispositions",
420
+ ]) ||
421
+ storage?.project_state !== "unchanged" ||
422
+ storageFilesystem?.authority !== "host_filesystem" ||
423
+ storageFilesystem?.configured_path !== "use_as_provided" ||
424
+ storageFilesystem?.outcomes !== "ordinary_permissions_and_io" ||
425
+ storageFilesystem?.same_user_path_manipulation !== "outside_agentera_threat_model" ||
426
+ storageFilesystem?.symlink_confinement !== "not_claimed" ||
427
+ !nonEmpty(storageFilesystem?.rule) ||
428
+ !nonEmpty(storage?.rule) ||
429
+ contentExclusion?.owner !== "glossary_content_policy" ||
430
+ contentExclusion?.classification !== "actual_secret_or_sensitive_value" ||
431
+ contentExclusion?.conceptual_terminology !== "eligible" ||
432
+ !sameStrings(contentExclusion?.candidate_fields, ["term", "meaning"]) ||
433
+ contentExclusion?.candidate_action !== "reject_before_projection" ||
434
+ contentExclusion?.candidate_reason !== "secret_content" ||
435
+ contentExclusion?.excerpt_action !== "omit_complete_excerpt_before_projection" ||
436
+ !sameStrings(contentExclusion?.secret_classes, [
437
+ "api_keys",
438
+ "access_tokens",
439
+ "passwords",
440
+ "cookies",
441
+ "private_keys",
442
+ "authorization_headers",
443
+ "personal_contact_data",
444
+ "session_identifiers",
445
+ "runtime_store_paths",
446
+ ]) ||
447
+ !nonEmpty(contentExclusion?.rule) ||
448
+ excerpts?.purpose !== "bounded_review_context_only" ||
449
+ excerpts?.max_utf8_bytes !== 500 ||
450
+ !sameStrings(excerpts?.forbidden_content, [
451
+ "raw_transcript",
452
+ "raw_tool_arguments",
453
+ "raw_runtime_store_paths",
454
+ "raw_session_identifiers",
455
+ "secret_or_sensitive_content",
456
+ "unrelated_conversation",
457
+ ]) ||
458
+ excerpts?.failure !== "omit_complete_excerpt_before_persistence" ||
459
+ reviews?.owner !== "current_user" ||
460
+ !sameStrings(reviews?.required_fields, [
461
+ "candidate_id",
462
+ "candidate_revision",
463
+ "semantic_fingerprint",
464
+ "generation",
465
+ "policy_version",
466
+ "disposition",
467
+ "disposed_at",
468
+ ]) ||
469
+ !sameStrings(reviews?.dispositions, ["accept", "correct", "reject", "defer"]) ||
470
+ reviews?.trusted_disposition_authority !== "current_user" ||
471
+ !nonEmpty(authentication?.proof) ||
472
+ !sameStrings(authentication?.binding_fields, [
473
+ "review_id",
474
+ "candidate_id",
475
+ "candidate_revision",
476
+ "candidate_projection_sha256",
477
+ "semantic_fingerprint",
478
+ "generation",
479
+ "policy_version",
480
+ "disposition",
481
+ "corrected_meaning",
482
+ "corrected_scope",
483
+ "disposed_at",
484
+ ]) ||
485
+ !sameStrings(authentication?.forbidden_principals, [
486
+ "agent",
487
+ "model",
488
+ "imported_record",
489
+ "generic_consent",
490
+ ]) ||
491
+ authentication?.receipt_schema_version !== "agentera.personalGlossaryReviewApproval.v1" ||
492
+ authentication?.issuer !== "agentera-local-host" ||
493
+ authentication?.subject !== "current_user" ||
494
+ authentication?.trusted_channel !== "agentera-local-host-ipc" ||
495
+ !sameStrings(authentication?.required_fields, [
496
+ "schema_version",
497
+ "issuer",
498
+ "subject",
499
+ "trusted_channel",
500
+ "review_id",
501
+ "candidate_id",
502
+ "candidate_revision",
503
+ "candidate_projection_sha256",
504
+ "semantic_fingerprint",
505
+ "generation",
506
+ "policy_version",
507
+ "disposition",
508
+ "corrected_meaning",
509
+ "corrected_scope",
510
+ "disposed_at",
511
+ "expires_at",
512
+ "nonce",
513
+ "signature",
514
+ ]) ||
515
+ signature?.algorithm !== "ed25519" ||
516
+ signature?.encoding !== "base64url" ||
517
+ signature?.key_source !== "user_local_trusted_host_key" ||
518
+ !sameStrings(signature?.signed_fields, [
519
+ "schema_version",
520
+ "issuer",
521
+ "subject",
522
+ "trusted_channel",
523
+ "review_id",
524
+ "candidate_id",
525
+ "candidate_revision",
526
+ "candidate_projection_sha256",
527
+ "semantic_fingerprint",
528
+ "generation",
529
+ "policy_version",
530
+ "disposition",
531
+ "corrected_meaning",
532
+ "corrected_scope",
533
+ "disposed_at",
534
+ "expires_at",
535
+ "nonce",
536
+ ]) ||
537
+ signature?.verification !== "trusted_host_public_key_signature_check" ||
538
+ !sameStrings(correction?.correct_fields, ["corrected_meaning", "corrected_scope"]) ||
539
+ correction?.non_correct_values !== null ||
540
+ !sameStrings(correction?.corrected_scope_values, ["personal"]) ||
541
+ correction?.corrected_meaning_max_utf8_bytes !== 4_096 ||
542
+ !nonEmpty(correction?.rule) ||
543
+ freshness?.max_age_seconds !== 300 ||
544
+ freshness?.disposed_at !== "trusted_host_clock" ||
545
+ freshness?.expires_at_required !== true ||
546
+ freshness?.rule !== "disposed_at_must_not_be_future_and_expires_at_must_be_after_now" ||
547
+ replay?.nonce !== "required_unique_receipt_nonce" ||
548
+ replay?.index !== "user_local_consumed_receipt_digest_index" ||
549
+ replay?.exact_replay !== "no_op_only_when_nonce_and_receipt_digest_match" ||
550
+ replay?.conflicting_replay !== "reject_reused_nonce_with_changed_bindings_or_signature" ||
551
+ !sameStrings(terminalDisposition?.terminal, ["accept", "correct", "reject"]) ||
552
+ !sameStrings(terminalDisposition?.pending, ["defer"]) ||
553
+ retention?.pending_excerpt_days !== 30 ||
554
+ retention?.terminal_excerpt_days !== 0 ||
555
+ retention?.review_metadata_days_after_terminal !== 90 ||
556
+ retention?.accepted_entry !== "ownership_contracts.personal.retention_and_decay" ||
557
+ retention?.pending_review_metadata !== "retained_until_terminal_or_purge" ||
558
+ retention?.terminal_review_metadata !== "retained_for_90_days_after_disposition" ||
559
+ !nonEmpty(retention?.rule) ||
560
+ purge?.authority !== "current_user" ||
561
+ purge?.trigger !== "explicit_authenticated_user_request_or_retention_expiry" ||
562
+ !sameStrings(purge?.removes, ["candidate_metadata", "safe_excerpts", "review_metadata", "review_dispositions"]) ||
563
+ purge?.behavior !== "atomic_remove_user_local_records_before_next_read" ||
564
+ purge?.project_state !== "untouched" ||
565
+ purge?.profile_entry !== "not_implicitly_deleted" ||
566
+ purgeBoundaries?.pending !== "purge_removes_excerpt_and_metadata_immediately" ||
567
+ purgeBoundaries?.terminal !== "purge_removes_excerpt_and_metadata_before_next_read" ||
568
+ purgeBoundaries?.accepted_profile_entry !== "purge_does_not_implicitly_delete_profile_entry" ||
569
+ !nonEmpty(purge?.recovery)) {
570
+ errors.push("personal_mining_authority privacy must exclude sensitive content, use host-filesystem configured-path ownership, and remain authenticated, retained, and purgeable");
571
+ }
572
+ const admission = mapping(mining?.admission);
573
+ if (admission?.inferred_automatic_admission !== "disabled" ||
574
+ admission?.inferred_review !== "allowed" ||
575
+ admission?.quality_threshold !==
576
+ "references/analysis/personal-glossary-evaluation-authority.yaml#gates.release_authorizing_gate" ||
577
+ admission?.holdout !==
578
+ "references/analysis/personal-glossary-evaluation-authority.yaml#holdout" ||
579
+ !nonEmpty(admission?.rule)) {
580
+ errors.push("personal_mining_authority must keep inferred automatic admission disabled and bind the evaluation authority");
581
+ }
582
+ errors.push(...validatePersonalCandidateProjectionAuthority(authority));
583
+ errors.push(...validatePersonalGlossaryReviewRecordsAuthority(authority));
584
+ return errors;
585
+ }
586
+ export function validateProvenanceVariants(variants) {
587
+ const errors = [];
588
+ const expectations = {
589
+ personal_explicit_definition: {
590
+ owner: "personal",
591
+ evidence_count: 1,
592
+ required_evidence_fields: ["source_id", "evidence_anchor", "signal_type"],
593
+ allowed_signal_types: ["correction", "decision", "instruction"],
594
+ },
595
+ personal_inferred_usage: {
596
+ owner: "personal",
597
+ evidence_count: 2,
598
+ required_evidence_fields: ["source_id", "evidence_anchor", "source_kind"],
599
+ allowed_source_kinds: ["instruction_document", "project_config_signal"],
600
+ },
601
+ personal_inferred_conversation: {
602
+ owner: "personal",
603
+ evidence_count: "variable",
604
+ minimum_evidence_count: 3,
605
+ required_evidence_fields: [
606
+ "source_id",
607
+ "evidence_anchor",
608
+ "source_kind",
609
+ "signal_type",
610
+ "session_id",
611
+ "project_id",
612
+ "content_fingerprint",
613
+ "author_class",
614
+ ],
615
+ allowed_signal_types: ["correction", "decision", "question", "instruction", "configuration"],
616
+ allowed_source_kinds: ["conversation_turn"],
617
+ allowed_author_classes: ["user"],
618
+ },
619
+ project_file: {
620
+ owner: "project",
621
+ evidence_count: 1,
622
+ required_evidence_fields: ["source_path", "source_record_sha256"],
623
+ },
624
+ };
625
+ for (const [kind, expected] of Object.entries(expectations)) {
626
+ const variant = mapping(variants?.[kind]);
627
+ if (!variant) {
628
+ errors.push(`provenance variant ${kind} is required`);
629
+ continue;
630
+ }
631
+ if (variant.owner !== expected.owner)
632
+ errors.push(`${kind}.owner must be ${expected.owner}`);
633
+ if (variant.evidence_count !== expected.evidence_count) {
634
+ errors.push(`${kind}.evidence_count must be ${expected.evidence_count}`);
635
+ }
636
+ if ("minimum_evidence_count" in expected &&
637
+ variant.minimum_evidence_count !== expected.minimum_evidence_count) {
638
+ errors.push(`${kind}.minimum_evidence_count must be ${expected.minimum_evidence_count}`);
639
+ }
640
+ if (!sameStrings(variant.required_evidence_fields, expected.required_evidence_fields)) {
641
+ errors.push(`${kind}.required_evidence_fields must match its provenance variant`);
642
+ }
643
+ if (variant.additional_evidence_fields !== "forbidden") {
644
+ errors.push(`${kind}.additional_evidence_fields must be forbidden`);
645
+ }
646
+ for (const allowedField of [
647
+ "allowed_signal_types",
648
+ "allowed_source_kinds",
649
+ "allowed_author_classes",
650
+ ]) {
651
+ if (allowedField in expected &&
652
+ !sameStrings(variant[allowedField], expected[allowedField])) {
653
+ errors.push(`${kind}.${allowedField} must preserve the admitted evidence classes`);
654
+ }
655
+ }
656
+ if (!nonEmpty(variant.resolution_rule))
657
+ errors.push(`${kind}.resolution_rule is required`);
658
+ }
659
+ return errors;
660
+ }
661
+ export function validateHistoryEvidence(evidence, context, index) {
662
+ const errors = [];
663
+ const sourceId = typeof evidence.source_id === "string" ? evidence.source_id : "";
664
+ const anchor = typeof evidence.evidence_anchor === "string" ? evidence.evidence_anchor : "";
665
+ const retained = context.retainedHistory.get(anchor);
666
+ if (!sourceId || !anchor) {
667
+ errors.push(`provenance.evidence[${index}] requires source_id and evidence_anchor`);
668
+ }
669
+ if (!retained || retained.sourceId !== sourceId) {
670
+ errors.push(`provenance.evidence[${index}].evidence_anchor must resolve to its retained source_id`);
671
+ }
672
+ return errors;
673
+ }
674
+ function sameStringSet(left, right) {
675
+ return (left.length === right.length &&
676
+ new Set(left).size === left.length &&
677
+ new Set(right).size === right.length &&
678
+ left.every((value) => right.includes(value)));
679
+ }
680
+ function validateExpectedConversationEvidence(expectation, actualEvidence) {
681
+ const errors = [];
682
+ if (!expectation) {
683
+ errors.push("conversation inference requires generation-bound expected evidence");
684
+ return errors;
685
+ }
686
+ const expectedAnchors = [...expectation.qualifyingEvidenceAnchors];
687
+ if (!nonEmpty(expectation.generation) ||
688
+ expectedAnchors.length === 0 ||
689
+ expectedAnchors.some((anchor) => !nonEmpty(anchor)) ||
690
+ new Set(expectedAnchors).size !== expectedAnchors.length) {
691
+ errors.push("conversation inference expected evidence anchors are invalid");
692
+ return errors;
693
+ }
694
+ let expectedDigest = "";
695
+ try {
696
+ expectedDigest = glossaryEvidenceSetDigest(expectation.generation, expectedAnchors);
697
+ }
698
+ catch {
699
+ errors.push("conversation inference expected evidence digest cannot be computed");
700
+ }
701
+ if (expectation.qualifyingEvidenceSetSha256 !== expectedDigest) {
702
+ errors.push("conversation inference expected evidence digest is invalid");
703
+ }
704
+ const completeEvidence = actualEvidence.filter((item) => item !== null);
705
+ const actualAnchors = completeEvidence.map((item) => String(item.evidence_anchor));
706
+ const actualSourceIds = completeEvidence.map((item) => String(item.source_id));
707
+ const actualFingerprints = completeEvidence.map((item) => String(item.content_fingerprint));
708
+ if (new Set(actualAnchors).size !== actualAnchors.length ||
709
+ new Set(actualSourceIds).size !== actualSourceIds.length ||
710
+ new Set(actualFingerprints).size !== actualFingerprints.length) {
711
+ errors.push("conversation inference rejects duplicate source, anchor, or content identities");
712
+ }
713
+ if (!sameStringSet(actualAnchors, expectedAnchors)) {
714
+ errors.push("conversation inference evidence must exactly match the generation-bound anchor set");
715
+ }
716
+ return errors;
717
+ }
718
+ export function validateConversationProvenance(evidence, provenance, variant, context) {
719
+ const errors = [];
720
+ errors.push(...validateExpectedConversationEvidence(context.conversationEvidence, evidence));
721
+ const minimumEvidenceCount = Number(variant.minimum_evidence_count);
722
+ if (evidence.length < minimumEvidenceCount || evidence.some((item) => item === null)) {
723
+ errors.push("conversation inference requires at least three complete retained records");
724
+ return errors;
725
+ }
726
+ if (provenance?.evidence_complete !== true) {
727
+ errors.push("conversation inference requires complete provenance evidence");
728
+ }
729
+ for (const [index, item] of evidence.entries()) {
730
+ errors.push(...validateHistoryEvidence(item, context, index));
731
+ const retained = context.retainedHistory.get(String(item.evidence_anchor));
732
+ if (!nonEmpty(item.source_kind) ||
733
+ !nonEmpty(item.signal_type) ||
734
+ !nonEmpty(item.session_id) ||
735
+ !nonEmpty(item.project_id) ||
736
+ !isLowerSha256(item.content_fingerprint) ||
737
+ !nonEmpty(item.author_class) ||
738
+ !retained ||
739
+ item.source_kind !== retained.sourceKind ||
740
+ item.signal_type !== retained.signalType ||
741
+ item.session_id !== retained.sessionId ||
742
+ item.project_id !== retained.projectId ||
743
+ item.content_fingerprint !== retained.contentFingerprint ||
744
+ item.author_class !== retained.authorClass ||
745
+ !strings(variant.allowed_source_kinds).includes(String(item.source_kind)) ||
746
+ !strings(variant.allowed_signal_types).includes(String(item.signal_type)) ||
747
+ !strings(variant.allowed_author_classes).includes(String(item.author_class))) {
748
+ errors.push(`provenance.evidence[${index}] has inadmissible conversation provenance`);
749
+ }
750
+ }
751
+ const sourceIds = evidence.map((item) => String(item?.source_id));
752
+ const anchors = evidence.map((item) => String(item?.evidence_anchor));
753
+ const sessions = evidence.map((item) => String(item?.session_id));
754
+ const projects = evidence.map((item) => String(item?.project_id));
755
+ const fingerprints = evidence.map((item) => String(item?.content_fingerprint));
756
+ if (new Set(sourceIds).size < 3 ||
757
+ new Set(anchors).size < 3 ||
758
+ new Set(sessions).size < 2 ||
759
+ new Set(projects).size < 2 ||
760
+ new Set(fingerprints).size < 3) {
761
+ errors.push("conversation inference requires distinct source identities, anchors, sessions, projects, and content fingerprints");
762
+ }
763
+ return errors;
764
+ }