agentera 3.0.0-dev.32 → 3.0.0-dev.39

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 (196) hide show
  1. package/bundle/CHANGELOG.md +75 -5
  2. package/bundle/references/adapters/package-publication.json +43 -0
  3. package/bundle/references/adapters/package-surface-characterization.md +12 -0
  4. package/bundle/references/analysis/evidence-tier-authority.yaml +23 -21
  5. package/bundle/references/analysis/verification-policy.yaml +1 -0
  6. package/bundle/references/artifacts/artifact-registry-interface-model.yaml +11 -2
  7. package/bundle/references/artifacts/glossary-entry-contract.yaml +1241 -0
  8. package/bundle/references/artifacts/state-storage-authority.yaml +149 -80
  9. package/bundle/references/cli/agent-ready-state-contract.yaml +84 -2
  10. package/bundle/references/cli/audience-namespace-cli-migration.yaml +12 -1
  11. package/bundle/references/cli/prime-consumer-compatibility.yaml +7 -1
  12. package/bundle/skills/agentera/SKILL.md +26 -26
  13. package/bundle/skills/agentera/capabilities/audit/schemas/validation.yaml +26 -8
  14. package/bundle/skills/agentera/capabilities/build/schemas/artifacts.yaml +21 -4
  15. package/bundle/skills/agentera/capabilities/build/schemas/exit.yaml +5 -4
  16. package/bundle/skills/agentera/capabilities/build/schemas/validation.yaml +67 -3
  17. package/bundle/skills/agentera/capabilities/discuss/schemas/artifacts.yaml +3 -2
  18. package/bundle/skills/agentera/capabilities/discuss/schemas/validation.yaml +64 -0
  19. package/bundle/skills/agentera/capabilities/plan/schemas/artifacts.yaml +4 -1
  20. package/bundle/skills/agentera/capabilities/plan/schemas/validation.yaml +30 -0
  21. package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +5 -2
  22. package/bundle/skills/agentera/capabilities/profile/schemas/exit.yaml +3 -2
  23. package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +30 -0
  24. package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +82 -0
  25. package/bundle/skills/agentera/schemas/artifacts/plan.yaml +19 -4
  26. package/bundle/skills/agentera/schemas/artifacts/progress.yaml +92 -0
  27. package/dist/analytics/extractCorpus/evidenceTiers.js +46 -3
  28. package/dist/analytics/extractCorpus/evidenceTiers.js.map +1 -1
  29. package/dist/analytics/extractCorpus/tierReader.js +1 -1
  30. package/dist/analytics/extractCorpus/tierReader.js.map +1 -1
  31. package/dist/analytics/glossaryAdviceResolution.js +167 -0
  32. package/dist/analytics/glossaryAdviceResolution.js.map +1 -0
  33. package/dist/analytics/glossaryInputAcquisition.js +320 -0
  34. package/dist/analytics/glossaryInputAcquisition.js.map +1 -0
  35. package/dist/analytics/personalGlossaryAdmission.js +134 -0
  36. package/dist/analytics/personalGlossaryAdmission.js.map +1 -0
  37. package/dist/analytics/personalGlossaryProfile.js +219 -0
  38. package/dist/analytics/personalGlossaryProfile.js.map +1 -0
  39. package/dist/audit/terminologyDrift.js +282 -0
  40. package/dist/audit/terminologyDrift.js.map +1 -0
  41. package/dist/capabilities/audit/instructions.js +2 -1
  42. package/dist/capabilities/audit/instructions.js.map +1 -1
  43. package/dist/capabilities/build/instructions.js +41 -2
  44. package/dist/capabilities/build/instructions.js.map +1 -1
  45. package/dist/capabilities/discuss/instructions.js +14 -1
  46. package/dist/capabilities/discuss/instructions.js.map +1 -1
  47. package/dist/capabilities/index.js +1 -1
  48. package/dist/capabilities/index.js.map +1 -1
  49. package/dist/capabilities/orchestrate/instructions.js +2 -2
  50. package/dist/capabilities/plan/instructions.js +19 -2
  51. package/dist/capabilities/plan/instructions.js.map +1 -1
  52. package/dist/capabilities/profile/instructions.js +67 -1
  53. package/dist/capabilities/profile/instructions.js.map +1 -1
  54. package/dist/cli/capabilityContext/bespoke.js +4 -3
  55. package/dist/cli/capabilityContext/bespoke.js.map +1 -1
  56. package/dist/cli/capabilityContext/build.js +77 -24
  57. package/dist/cli/capabilityContext/build.js.map +1 -1
  58. package/dist/cli/capabilityContext/closeout.js +2 -2
  59. package/dist/cli/capabilityContext/closeout.js.map +1 -1
  60. package/dist/cli/capabilityContext/evidence.js +2 -6
  61. package/dist/cli/capabilityContext/evidence.js.map +1 -1
  62. package/dist/cli/capabilityContext/planState.js +59 -78
  63. package/dist/cli/capabilityContext/planState.js.map +1 -1
  64. package/dist/cli/capabilityContext/projectVerification.js +410 -0
  65. package/dist/cli/capabilityContext/projectVerification.js.map +1 -0
  66. package/dist/cli/capabilityContext/startup.js +21 -4
  67. package/dist/cli/capabilityContext/startup.js.map +1 -1
  68. package/dist/cli/commands/glossaryAdvice.js +139 -0
  69. package/dist/cli/commands/glossaryAdvice.js.map +1 -0
  70. package/dist/cli/commands/personalGlossary.js +177 -0
  71. package/dist/cli/commands/personalGlossary.js.map +1 -0
  72. package/dist/cli/commands/prime/buildExecutionRequest.js +165 -0
  73. package/dist/cli/commands/prime/buildExecutionRequest.js.map +1 -0
  74. package/dist/cli/commands/prime/collectEntityOrientation.js +18 -2
  75. package/dist/cli/commands/prime/collectEntityOrientation.js.map +1 -1
  76. package/dist/cli/commands/prime/collectOrientationState.js +7 -1
  77. package/dist/cli/commands/prime/collectOrientationState.js.map +1 -1
  78. package/dist/cli/commands/prime/orientationOutput.js +12 -2
  79. package/dist/cli/commands/prime/orientationOutput.js.map +1 -1
  80. package/dist/cli/commands/prime.js +37 -2
  81. package/dist/cli/commands/prime.js.map +1 -1
  82. package/dist/cli/commands/profileGrounding.js +77 -0
  83. package/dist/cli/commands/profileGrounding.js.map +1 -0
  84. package/dist/cli/commands/query.js +14 -4
  85. package/dist/cli/commands/query.js.map +1 -1
  86. package/dist/cli/commands/schema.js +10 -0
  87. package/dist/cli/commands/schema.js.map +1 -1
  88. package/dist/cli/commands/state/write.js +4 -1
  89. package/dist/cli/commands/state/write.js.map +1 -1
  90. package/dist/cli/dispatch/lifecycle.js +45 -0
  91. package/dist/cli/dispatch/lifecycle.js.map +1 -1
  92. package/dist/cli/dispatch/prime.js +22 -1
  93. package/dist/cli/dispatch/prime.js.map +1 -1
  94. package/dist/cli/dispatch/shared.js +1 -1
  95. package/dist/cli/dispatch/shared.js.map +1 -1
  96. package/dist/cli/help.js +23 -1
  97. package/dist/cli/help.js.map +1 -1
  98. package/dist/cli/migrationRequired.js +2 -0
  99. package/dist/cli/migrationRequired.js.map +1 -1
  100. package/dist/cli/orientation/attention.js +3 -1
  101. package/dist/cli/orientation/attention.js.map +1 -1
  102. package/dist/cli/orientation.js +2 -2
  103. package/dist/cli/orientation.js.map +1 -1
  104. package/dist/registries/artifactProtocolIds.js +2 -0
  105. package/dist/registries/artifactProtocolIds.js.map +1 -1
  106. package/dist/registries/artifactRegistry.js +2 -0
  107. package/dist/registries/artifactRegistry.js.map +1 -1
  108. package/dist/registries/evidenceTierContract.js +26 -16
  109. package/dist/registries/evidenceTierContract.js.map +1 -1
  110. package/dist/registries/glossaryAdviceContract.js +79 -0
  111. package/dist/registries/glossaryAdviceContract.js.map +1 -0
  112. package/dist/registries/glossaryCaveatContract.js +68 -0
  113. package/dist/registries/glossaryCaveatContract.js.map +1 -0
  114. package/dist/registries/glossaryConsumerContractValidation.js +792 -0
  115. package/dist/registries/glossaryConsumerContractValidation.js.map +1 -0
  116. package/dist/registries/glossaryEntryContract.js +747 -0
  117. package/dist/registries/glossaryEntryContract.js.map +1 -0
  118. package/dist/registries/glossaryTermIdentity.js +18 -0
  119. package/dist/registries/glossaryTermIdentity.js.map +1 -0
  120. package/dist/registries/glossaryTermOccurrence.js +21 -0
  121. package/dist/registries/glossaryTermOccurrence.js.map +1 -0
  122. package/dist/state/changelog.js +222 -0
  123. package/dist/state/changelog.js.map +1 -0
  124. package/dist/state/decisionEntities.js +18 -11
  125. package/dist/state/decisionEntities.js.map +1 -1
  126. package/dist/state/decisionMigrationProvenance.js +84 -1
  127. package/dist/state/decisionMigrationProvenance.js.map +1 -1
  128. package/dist/state/decisionOverlay.js +9 -32
  129. package/dist/state/decisionOverlay.js.map +1 -1
  130. package/dist/state/decisionRevision.js +69 -444
  131. package/dist/state/decisionRevision.js.map +1 -1
  132. package/dist/state/decisionRevisionMigration.js +90 -0
  133. package/dist/state/decisionRevisionMigration.js.map +1 -0
  134. package/dist/state/directRetrieval.js +3 -52
  135. package/dist/state/directRetrieval.js.map +1 -1
  136. package/dist/state/entityCutover.js +6 -6
  137. package/dist/state/entityCutover.js.map +1 -1
  138. package/dist/state/entityGlossaryAuthority.js +14 -0
  139. package/dist/state/entityGlossaryAuthority.js.map +1 -0
  140. package/dist/state/entityMigrationIdentity.js +6 -0
  141. package/dist/state/entityMigrationIdentity.js.map +1 -0
  142. package/dist/state/entityMigrationPreview.js +14 -23
  143. package/dist/state/entityMigrationPreview.js.map +1 -1
  144. package/dist/state/entityPublicationContext.js +374 -71
  145. package/dist/state/entityPublicationContext.js.map +1 -1
  146. package/dist/state/entityStorage.js +45 -29
  147. package/dist/state/entityStorage.js.map +1 -1
  148. package/dist/state/healthEntities.js +2 -4
  149. package/dist/state/healthEntities.js.map +1 -1
  150. package/dist/state/listRetrieval.js +7 -38
  151. package/dist/state/listRetrieval.js.map +1 -1
  152. package/dist/state/migrationSourceBinding.js +1 -1
  153. package/dist/state/migrationSourceBinding.js.map +1 -1
  154. package/dist/state/objectiveExperimentEntities.js +9 -9
  155. package/dist/state/objectiveExperimentEntities.js.map +1 -1
  156. package/dist/state/planEntities.js +57 -16
  157. package/dist/state/planEntities.js.map +1 -1
  158. package/dist/state/progressEntities.js +177 -39
  159. package/dist/state/progressEntities.js.map +1 -1
  160. package/dist/state/progressGlossaryCaveat.js +169 -0
  161. package/dist/state/progressGlossaryCaveat.js.map +1 -0
  162. package/dist/state/progressPublicationOrder.js +20 -0
  163. package/dist/state/progressPublicationOrder.js.map +1 -0
  164. package/dist/state/safeProjectFile.js +4 -1
  165. package/dist/state/safeProjectFile.js.map +1 -1
  166. package/dist/state/startupProjection.js +2 -70
  167. package/dist/state/startupProjection.js.map +1 -1
  168. package/dist/state/stateStorageAuthority.js +48 -0
  169. package/dist/state/stateStorageAuthority.js.map +1 -0
  170. package/dist/state/summarySourceRowAuthority.js +1 -1
  171. package/dist/state/summarySourceRowAuthority.js.map +1 -1
  172. package/dist/state/todoDocsEntities.js +8 -9
  173. package/dist/state/todoDocsEntities.js.map +1 -1
  174. package/dist/state/write/explain.js +101 -92
  175. package/dist/state/write/explain.js.map +1 -1
  176. package/dist/state/write/glossaryPublication.js +264 -0
  177. package/dist/state/write/glossaryPublication.js.map +1 -0
  178. package/dist/state/write/index.js +1 -0
  179. package/dist/state/write/index.js.map +1 -1
  180. package/dist/state/write/operations.js +25 -27
  181. package/dist/state/write/operations.js.map +1 -1
  182. package/dist/state/write/planEvaluation.js +6 -2
  183. package/dist/state/write/planEvaluation.js.map +1 -1
  184. package/dist/state/write/transaction.js +7 -4
  185. package/dist/state/write/transaction.js.map +1 -1
  186. package/dist/upgrade/upgradeOrchestrator.js +15 -10
  187. package/dist/upgrade/upgradeOrchestrator.js.map +1 -1
  188. package/dist/validate/crossCapability.js +3 -0
  189. package/dist/validate/crossCapability.js.map +1 -1
  190. package/dist/validate/v1LegacyCruft.js +141 -0
  191. package/dist/validate/v1LegacyCruft.js.map +1 -0
  192. package/dist/validate/vocabularyAuthority.js +2 -0
  193. package/dist/validate/vocabularyAuthority.js.map +1 -1
  194. package/package.json +4 -3
  195. package/dist/state/decisionRevisionPublication.js +0 -532
  196. package/dist/state/decisionRevisionPublication.js.map +0 -1
@@ -0,0 +1,747 @@
1
+ import path from "node:path";
2
+ import { resolveSourceRoot } from "../core/sourceRoot.js";
3
+ import { loadYamlMappingFile } from "../core/yaml.js";
4
+ import { validateConsumerBoundary, validateConsumerEvidenceOwner, } from "./glossaryConsumerContractValidation.js";
5
+ export class GlossaryEntryBoundError extends Error {
6
+ }
7
+ const DEFERRED_CAPABILITIES = ["profile", "audit", "discuss", "plan", "build"];
8
+ export function glossaryEntryAuthorityPath(root = resolveSourceRoot()) {
9
+ return path.join(root, "references", "artifacts", "glossary-entry-contract.yaml");
10
+ }
11
+ function mapping(value) {
12
+ return value !== null && typeof value === "object" && !Array.isArray(value)
13
+ ? value
14
+ : null;
15
+ }
16
+ function strings(value) {
17
+ return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : [];
18
+ }
19
+ function contract(pathname) {
20
+ return loadYamlMappingFile(pathname);
21
+ }
22
+ export function personalGlossaryAdmissionContract(pathname = glossaryEntryAuthorityPath()) {
23
+ const authority = contract(pathname);
24
+ const personal = mapping(mapping(authority.ownership_contracts)?.personal);
25
+ const admission = mapping(personal?.admission);
26
+ return {
27
+ explicitSignalTypes: strings(mapping(admission?.explicit)?.candidate_signal_types),
28
+ inferredSignalTypes: strings(mapping(admission?.inferred)?.candidate_signal_types),
29
+ inferredSourceKinds: strings(mapping(admission?.inferred)?.source_kinds),
30
+ insufficientRecovery: typeof admission?.insufficient_recovery === "string"
31
+ ? admission.insufficient_recovery.trim()
32
+ : "",
33
+ };
34
+ }
35
+ export function confirmedVariantGuardContract(pathname = glossaryEntryAuthorityPath()) {
36
+ const authority = contract(pathname);
37
+ const project = mapping(mapping(authority.ownership_contracts)?.project);
38
+ const guard = mapping(project?.confirmed_variant_guard);
39
+ return { excludedDirectories: strings(guard?.excluded_directory_names) };
40
+ }
41
+ export function personalGlossaryOutputContract(pathname = glossaryEntryAuthorityPath()) {
42
+ const authority = contract(pathname);
43
+ const personal = mapping(mapping(authority.ownership_contracts)?.personal);
44
+ const output = mapping(personal?.profile_output);
45
+ const command = mapping(output?.command);
46
+ const request = mapping(command?.request);
47
+ const section = mapping(output?.section);
48
+ return {
49
+ command: typeof command?.canonical === "string" ? command.canonical : "",
50
+ requestSchemaVersion: typeof request?.schema_version === "string" ? request.schema_version : "",
51
+ sectionSchemaVersion: typeof section?.document_schema_version === "string" ? section.document_schema_version : "",
52
+ outputStatuses: strings(command?.output_statuses),
53
+ };
54
+ }
55
+ export function personalProfileGroundingContract(pathname = glossaryEntryAuthorityPath()) {
56
+ const grounding = mapping(mapping(contract(pathname).consumer_boundary)?.profile_grounding);
57
+ return {
58
+ command: typeof grounding?.command === "string" ? grounding.command : "",
59
+ schemaVersion: typeof grounding?.schema_version === "string" ? grounding.schema_version : "",
60
+ maxProfileUtf8Bytes: typeof grounding?.max_profile_utf8_bytes === "number" ? grounding.max_profile_utf8_bytes : 0,
61
+ };
62
+ }
63
+ export function glossaryConsumerContract(pathname = glossaryEntryAuthorityPath()) {
64
+ const consumer = mapping(contract(pathname).consumer_boundary);
65
+ const implementation = mapping(consumer?.implementation);
66
+ const integrations = mapping(implementation?.capability_integrations);
67
+ const matrix = mapping(consumer?.outcome_matrix);
68
+ const refresh = mapping(consumer?.refresh_events);
69
+ const disclosure = mapping(consumer?.disclosure);
70
+ const transient = mapping(disclosure?.transient_advice);
71
+ const durable = mapping(disclosure?.durable_surfaces);
72
+ const caveat = mapping(consumer?.autonomous_caveat);
73
+ const lifecycle = mapping(caveat?.lifecycle);
74
+ const gate = mapping(consumer?.downstream_gate);
75
+ return {
76
+ contractStatus: typeof consumer?.contract_status === "string" ? consumer.contract_status : "",
77
+ implementation: Object.fromEntries(Object.entries(implementation ?? {}).filter((entry) => typeof entry[1] === "string")),
78
+ capabilityIntegrations: Object.fromEntries(Object.entries(integrations ?? {}).filter((entry) => typeof entry[1] === "string")),
79
+ outcomes: Object.keys(matrix ?? {}),
80
+ refreshRequired: strings(refresh?.required),
81
+ refreshNotRequired: strings(refresh?.not_required),
82
+ transientAllowed: strings(transient?.allowed),
83
+ durableAllowed: strings(durable?.allowed),
84
+ caveatOwner: typeof caveat?.durable_owner === "string" ? caveat.durable_owner : "",
85
+ caveatChannel: typeof caveat?.durable_channel === "string" ? caveat.durable_channel : "",
86
+ caveatEvents: ["current", "resolved", "superseded"].filter((event) => nonEmpty(lifecycle?.[event])),
87
+ caveatExpiration: typeof lifecycle?.expiration === "string" ? lifecycle.expiration : "",
88
+ downstreamGateStatus: typeof gate?.status === "string" ? gate.status : "",
89
+ };
90
+ }
91
+ export function glossaryAcquisitionContract(pathname = glossaryEntryAuthorityPath()) {
92
+ const acquisition = mapping(mapping(contract(pathname).consumer_boundary)?.acquisition);
93
+ const bounds = mapping(acquisition?.bounds);
94
+ const availability = mapping(acquisition?.availability);
95
+ const output = mapping(acquisition?.output);
96
+ return {
97
+ maxSourceUtf8Bytes: typeof bounds?.max_source_utf8_bytes === "number" ? bounds.max_source_utf8_bytes : 0,
98
+ maxEntries: typeof bounds?.max_entries === "number" ? bounds.max_entries : 0,
99
+ availabilityStates: strings(availability?.states),
100
+ outputEntryFields: strings(output?.entry_fields),
101
+ };
102
+ }
103
+ function sameStrings(actual, expected) {
104
+ return JSON.stringify(strings(actual)) === JSON.stringify(expected);
105
+ }
106
+ function nonEmpty(value) {
107
+ return typeof value === "string" && value.trim().length > 0;
108
+ }
109
+ export function isSafeProjectSourcePath(value) {
110
+ return (typeof value === "string" &&
111
+ value.length > 0 &&
112
+ !path.posix.isAbsolute(value) &&
113
+ !path.win32.isAbsolute(value) &&
114
+ !value.split(/[\\/]/).includes(".."));
115
+ }
116
+ function isIsoCalendarDate(value) {
117
+ if (typeof value !== "string")
118
+ return false;
119
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
120
+ if (!match)
121
+ return false;
122
+ const [, year, month, day] = match.map(Number);
123
+ const date = new Date(`${value}T00:00:00.000Z`);
124
+ return (!Number.isNaN(date.getTime()) &&
125
+ date.getUTCFullYear() === year &&
126
+ date.getUTCMonth() + 1 === month &&
127
+ date.getUTCDate() === day);
128
+ }
129
+ export function validateGlossaryEntryContract(pathname = glossaryEntryAuthorityPath()) {
130
+ let authority;
131
+ try {
132
+ authority = contract(pathname);
133
+ }
134
+ catch (error) {
135
+ return [`glossary-entry-contract.yaml: ${error.message}`];
136
+ }
137
+ const errors = [];
138
+ if (authority.schema_version !== "agentera.glossaryEntryContract.v1") {
139
+ errors.push("schema_version must be agentera.glossaryEntryContract.v1");
140
+ }
141
+ const occurrence = mapping(authority.term_occurrence);
142
+ if (occurrence?.runtime !==
143
+ "packages/cli/src/registries/glossaryTermOccurrence.ts#containsGlossaryTerm" ||
144
+ !sameStrings(occurrence?.consumers, [
145
+ "audit_evidence",
146
+ "project_publication_revalidation",
147
+ "confirmed_variant_guard",
148
+ ]) ||
149
+ occurrence?.comparison !== "exact_case_sensitive_literal" ||
150
+ occurrence?.normalization !== "none" ||
151
+ occurrence?.identifier_continuation !== "unicode_ID_Continue_plus_dollar_ZWNJ_ZWJ" ||
152
+ !nonEmpty(occurrence?.rule)) {
153
+ errors.push("term_occurrence must define one exact-case escaped literal matcher with ECMAScript identifier-continuation boundaries");
154
+ }
155
+ const primitive = mapping(authority.shared_primitive);
156
+ const required = strings(primitive?.required_fields);
157
+ const expected = ["term", "meaning", "confidence", "permanence", "temporal", "provenance"];
158
+ if (!sameStrings(required, expected)) {
159
+ errors.push("shared_primitive.required_fields must define the canonical entry shape once");
160
+ }
161
+ const fields = mapping(primitive?.fields);
162
+ for (const field of expected) {
163
+ if (!mapping(fields?.[field]))
164
+ errors.push(`shared_primitive.fields.${field} is required`);
165
+ }
166
+ for (const field of ["term", "meaning"]) {
167
+ if (mapping(fields?.[field])?.type !== "non_empty_string") {
168
+ errors.push(`shared_primitive.fields.${field}.type must be non_empty_string`);
169
+ }
170
+ }
171
+ const confidence = mapping(fields?.confidence);
172
+ if (confidence?.type !== "integer" ||
173
+ confidence?.range_from !== "skills/agentera/protocol.yaml#CONFIDENCE_SCALE") {
174
+ errors.push("confidence must derive integer bounds from protocol CONFIDENCE_SCALE");
175
+ }
176
+ const permanence = mapping(fields?.permanence);
177
+ if (permanence?.type !== "enum" ||
178
+ !sameStrings(permanence?.values, ["stable", "durable", "situational"])) {
179
+ errors.push("permanence must use the existing profile permanence classes");
180
+ }
181
+ if (!nonEmpty(permanence?.classification_rule)) {
182
+ errors.push("permanence classification must remain independent from confidence");
183
+ }
184
+ const temporal = mapping(fields?.temporal);
185
+ if (temporal?.type !== "object" ||
186
+ temporal?.field_type !== "iso_date" ||
187
+ !sameStrings(temporal?.required_fields, ["observed_at", "last_confirmed_at"])) {
188
+ errors.push("temporal metadata must include observed_at and last_confirmed_at");
189
+ }
190
+ const provenance = mapping(fields?.provenance);
191
+ if (provenance?.type !== "discriminated_union" ||
192
+ provenance?.discriminator !== "kind" ||
193
+ provenance?.evidence_field !== "evidence" ||
194
+ provenance?.variants_from !== "provenance_variants") {
195
+ errors.push("provenance must derive its kind and evidence variants from provenance_variants");
196
+ }
197
+ const variants = mapping(authority.provenance_variants);
198
+ const variantExpectations = {
199
+ personal_explicit_definition: {
200
+ owner: "personal",
201
+ evidence_count: 1,
202
+ required_evidence_fields: ["source_id", "evidence_anchor", "signal_type"],
203
+ allowed_signal_types: ["correction", "decision", "instruction"],
204
+ },
205
+ personal_inferred_usage: {
206
+ owner: "personal",
207
+ evidence_count: 2,
208
+ required_evidence_fields: ["source_id", "evidence_anchor", "source_kind"],
209
+ allowed_source_kinds: ["instruction_document", "project_config_signal"],
210
+ },
211
+ project_file: {
212
+ owner: "project",
213
+ evidence_count: 1,
214
+ required_evidence_fields: ["source_path", "source_record_sha256"],
215
+ },
216
+ };
217
+ for (const [kind, expectedVariant] of Object.entries(variantExpectations)) {
218
+ const variant = mapping(variants?.[kind]);
219
+ if (!variant) {
220
+ errors.push(`provenance variant ${kind} is required`);
221
+ continue;
222
+ }
223
+ if (variant.owner !== expectedVariant.owner)
224
+ errors.push(`${kind}.owner must be ${expectedVariant.owner}`);
225
+ if (variant.evidence_count !== expectedVariant.evidence_count) {
226
+ errors.push(`${kind}.evidence_count must be ${expectedVariant.evidence_count}`);
227
+ }
228
+ if (!sameStrings(variant.required_evidence_fields, expectedVariant.required_evidence_fields)) {
229
+ errors.push(`${kind}.required_evidence_fields must match its provenance variant`);
230
+ }
231
+ if (variant.additional_evidence_fields !== "forbidden") {
232
+ errors.push(`${kind}.additional_evidence_fields must be forbidden`);
233
+ }
234
+ for (const allowedField of ["allowed_signal_types", "allowed_source_kinds"]) {
235
+ if (allowedField in expectedVariant &&
236
+ !sameStrings(variant[allowedField], expectedVariant[allowedField])) {
237
+ errors.push(`${kind}.${allowedField} must preserve the admitted evidence classes`);
238
+ }
239
+ }
240
+ if (!nonEmpty(variant.resolution_rule))
241
+ errors.push(`${kind}.resolution_rule is required`);
242
+ }
243
+ const ownership = mapping(authority.ownership_contracts);
244
+ const personal = mapping(ownership?.personal);
245
+ if (personal?.scope !== "user")
246
+ errors.push("personal ownership scope must be user");
247
+ if (!sameStrings(personal?.allowed_provenance, [
248
+ "personal_explicit_definition",
249
+ "personal_inferred_usage",
250
+ ])) {
251
+ errors.push("personal ownership must admit only personal provenance variants");
252
+ }
253
+ const personalInput = mapping(personal?.input);
254
+ const excluded = strings(personalInput?.excluded_categories);
255
+ for (const category of [
256
+ "project_glossary_artifact_identity",
257
+ "project_glossary_path",
258
+ "project_glossary_record",
259
+ "project_file_provenance",
260
+ ]) {
261
+ if (!excluded.includes(category))
262
+ errors.push(`personal input must exclude ${category}`);
263
+ }
264
+ if (personalInput?.tier !== "signal")
265
+ errors.push("personal input must use the bounded signal tier");
266
+ if (personalInput?.authority !== "references/analysis/evidence-tier-authority.yaml") {
267
+ errors.push("personal input must use evidence-tier authority");
268
+ }
269
+ if (!nonEmpty(personalInput?.bounded_rule)) {
270
+ errors.push("personal input bounded_rule is required");
271
+ }
272
+ const personalImplementation = mapping(personal?.implementation);
273
+ const admission = mapping(personal?.admission);
274
+ if (personalImplementation?.status !== "active_partial" ||
275
+ !sameStrings(personalImplementation?.active, [
276
+ "bounded_admission",
277
+ "explicit_classification",
278
+ "inferred_evidence_check",
279
+ "profile_full_rendering",
280
+ "profile_persistence",
281
+ ]) ||
282
+ !sameStrings(personalImplementation?.inactive, ["lookup"])) {
283
+ errors.push("personal rendering and persistence must be active_partial while lookup remains inactive");
284
+ }
285
+ if (!sameStrings(mapping(admission?.explicit)?.candidate_signal_types, [
286
+ "correction",
287
+ "decision",
288
+ ]) ||
289
+ !sameStrings(mapping(admission?.inferred)?.candidate_signal_types, [
290
+ "instruction",
291
+ "configuration",
292
+ ]) ||
293
+ !sameStrings(mapping(admission?.inferred)?.source_kinds, [
294
+ "instruction_document",
295
+ "project_config_signal",
296
+ ]) ||
297
+ !nonEmpty(admission?.read_rule) ||
298
+ !nonEmpty(admission?.insufficient_recovery) ||
299
+ !nonEmpty(admission?.isolation_rule)) {
300
+ errors.push("personal admission must declare bounded classifier, recovery, and isolation rules");
301
+ }
302
+ const decay = mapping(personal?.retention_and_decay);
303
+ if (decay?.authority !== "packages/cli/src/capabilities/profile/instructions.ts#Profile-format") {
304
+ errors.push("personal decay must delegate to the existing profile decay authority");
305
+ }
306
+ if (mapping(authority.authority)?.confidence !== "skills/agentera/protocol.yaml#CONFIDENCE_SCALE") {
307
+ errors.push("contract confidence authority must be protocol CONFIDENCE_SCALE");
308
+ }
309
+ for (const field of ["retention", "confidence", "permanence"]) {
310
+ if (!nonEmpty(decay?.[field]))
311
+ errors.push(`personal retention_and_decay.${field} is required`);
312
+ }
313
+ const profileOutput = mapping(personal?.profile_output);
314
+ const profileSection = mapping(profileOutput?.section);
315
+ const mergeIdentity = mapping(profileOutput?.merge_identity);
316
+ const profileCommand = mapping(profileOutput?.command);
317
+ const profileRequest = mapping(profileCommand?.request);
318
+ if (profileOutput?.owner !== "profile_full" ||
319
+ profileOutput?.lifecycle_callable !==
320
+ "packages/cli/src/analytics/personalGlossaryProfile.ts#updatePersonalGlossaryProfile" ||
321
+ profileCommand?.canonical !== "agentera report profile-glossary" ||
322
+ profileCommand?.namespace !== "report" ||
323
+ profileCommand?.input_flag !== "--input" ||
324
+ profileCommand?.stdin_value !== "-" ||
325
+ profileCommand?.format !== "json" ||
326
+ profileCommand?.dry_run_flag !== "--dry-run" ||
327
+ profileCommand?.project_checkout !== "not_required" ||
328
+ profileRequest?.schema_version !== "agentera.personalGlossaryUpdateRequest.v1" ||
329
+ !sameStrings(profileRequest?.required_fields, [
330
+ "schema_version",
331
+ "profile_path",
332
+ "as_of",
333
+ "fresh_entries",
334
+ "retained_history",
335
+ ]) ||
336
+ profileRequest?.additional_fields !== "forbidden" ||
337
+ !nonEmpty(profileRequest?.profile_path_rule) ||
338
+ !nonEmpty(profileRequest?.retained_history_shape) ||
339
+ !sameStrings(profileCommand?.output_statuses, [
340
+ "changed",
341
+ "unchanged_replay",
342
+ "dry_run_candidate",
343
+ ]) ||
344
+ !nonEmpty(profileCommand?.rule) ||
345
+ profileSection?.heading !== "## Glossary" ||
346
+ profileSection?.start_marker !== "<!-- agentera:personal-glossary:start -->" ||
347
+ profileSection?.end_marker !== "<!-- agentera:personal-glossary:end -->" ||
348
+ profileSection?.encoding !== "deterministic_json_fence" ||
349
+ profileSection?.document_schema_version !== "agentera.personalGlossarySection.v1" ||
350
+ profileSection?.entry_shape !== "shared_primitive" ||
351
+ !sameStrings(profileSection?.lifecycle_metadata, ["as_of", "confidence_basis"]) ||
352
+ !nonEmpty(profileSection?.boundary_rule) ||
353
+ mergeIdentity?.normalization !== "unicode_caseless_exact_no_normalization" ||
354
+ !nonEmpty(mergeIdentity?.rule) ||
355
+ !nonEmpty(profileOutput?.refresh_rule) ||
356
+ !nonEmpty(profileOutput?.decay_rule) ||
357
+ !nonEmpty(profileOutput?.isolation_rule)) {
358
+ errors.push("personal profile output must define deterministic isolated rendering and lifecycle rules");
359
+ }
360
+ const project = mapping(ownership?.project);
361
+ const projectInput = mapping(project?.input);
362
+ const projectImplementation = mapping(project?.implementation);
363
+ const proposalDigest = mapping(project?.proposal_digest);
364
+ const proposalValidation = mapping(project?.proposal_validation);
365
+ const entryDerivation = mapping(project?.entry_derivation);
366
+ const publication = mapping(project?.publication);
367
+ const confirmedVariantGuard = mapping(project?.confirmed_variant_guard);
368
+ const publicationRequest = mapping(publication?.request);
369
+ const persistedDocument = mapping(publication?.persisted_document);
370
+ const terminologySetIdentity = mapping(persistedDocument?.terminology_set_identity);
371
+ if (project?.scope !== "repository" ||
372
+ !sameStrings(project?.allowed_provenance, ["project_file"])) {
373
+ errors.push("project ownership must be repository-scoped project_file provenance");
374
+ }
375
+ if (projectImplementation?.status !== "active" ||
376
+ !sameStrings(projectImplementation?.active, [
377
+ "audit_proposal_digest",
378
+ "build_publication",
379
+ "confirmed_variant_guard",
380
+ ]) ||
381
+ !sameStrings(projectImplementation?.inactive, [
382
+ "lookup",
383
+ "precedence",
384
+ "semantic_equivalence_review",
385
+ ]) ||
386
+ proposalDigest?.algorithm !== "sha256" ||
387
+ proposalDigest?.encoding !== "lowercase_hex" ||
388
+ !nonEmpty(proposalDigest?.canonicalization)) {
389
+ errors.push("project glossary digest and build publication must remain active under the declared consumer boundary");
390
+ }
391
+ if (confirmedVariantGuard?.owner !==
392
+ "packages/cli/src/validate/v1LegacyCruft.ts#scanPost30CruftViolations" ||
393
+ confirmedVariantGuard?.validation_surface !== "packages/cli/test/cli/v1LegacyCruft.test.ts" ||
394
+ confirmedVariantGuard?.loader !==
395
+ "packages/cli/src/state/write/glossaryPublication.ts#loadProjectGlossaryDocument" ||
396
+ confirmedVariantGuard?.matching !== "exact_case_sensitive_boundary_aware_literal" ||
397
+ !nonEmpty(confirmedVariantGuard?.source_rule) ||
398
+ !nonEmpty(confirmedVariantGuard?.approved_evidence_rule) ||
399
+ !nonEmpty(confirmedVariantGuard?.failure_rule) ||
400
+ !sameStrings(confirmedVariantGuard?.exclusions, [
401
+ "generated_output",
402
+ "vendor_and_dependency_state",
403
+ "cache_state",
404
+ "repository_metadata",
405
+ "historical_agentera_state",
406
+ "project_glossary_document",
407
+ "unrelated_agentera_state",
408
+ ]) ||
409
+ !sameStrings(confirmedVariantGuard?.excluded_directory_names, [
410
+ ".agentera",
411
+ ".agentera-generated",
412
+ ".cache",
413
+ ".git",
414
+ ".next",
415
+ ".pnpm",
416
+ ".turbo",
417
+ ".venv",
418
+ ".vite",
419
+ "build",
420
+ "bundle",
421
+ "coverage",
422
+ "dist",
423
+ "node_modules",
424
+ "target",
425
+ "vendor",
426
+ ]) ||
427
+ !nonEmpty(confirmedVariantGuard?.exclusion_rule)) {
428
+ errors.push("confirmed variant guard must reuse validated project glossary pairs and bounded legacy-cruft exclusions");
429
+ }
430
+ if (proposalValidation?.owner !== "audit" ||
431
+ proposalValidation?.shared_runtime !==
432
+ "packages/cli/src/audit/terminologyDrift.ts#validateTerminologyProposal" ||
433
+ !sameStrings(proposalValidation?.required_rules, [
434
+ "safe_distinct_evidence_identities_per_term",
435
+ "unicode_caseless_exact_unique_term_identities",
436
+ "best_supported_canonical_term_with_lexicographic_tie_break",
437
+ "authority_confidence_floor_and_range",
438
+ "confidence_below_70_forces_info_severity",
439
+ "canonical_evidence_and_variant_order",
440
+ "canonical_proposal_digest",
441
+ ]) ||
442
+ !nonEmpty(proposalValidation?.publication_rule)) {
443
+ errors.push("project publication must reuse the Audit-owned canonical proposal validator");
444
+ }
445
+ if (entryDerivation?.term !== "proposal.proposed_canonical_term" ||
446
+ entryDerivation?.meaning !== "proposal.concept" ||
447
+ entryDerivation?.confidence !== "proposal.confidence" ||
448
+ entryDerivation?.permanence !== "stable" ||
449
+ entryDerivation?.["provenance.kind"] !== "project_file" ||
450
+ !nonEmpty(entryDerivation?.["provenance.evidence"]) ||
451
+ !nonEmpty(entryDerivation?.rejection_rule)) {
452
+ errors.push("project entry derivation must produce only the shared project_file primitive");
453
+ }
454
+ if (publication?.owner !== "build" ||
455
+ publication?.command !== "agentera state glossary publish --input REQUEST --format json" ||
456
+ publicationRequest?.schema_version !== "agentera.glossaryPublicationRequest.v1" ||
457
+ !sameStrings(publicationRequest?.required_fields, [
458
+ "schema_version",
459
+ "proposal",
460
+ "confirmation",
461
+ ]) ||
462
+ persistedDocument?.schema_version !== "agentera.projectGlossary.v1" ||
463
+ !sameStrings(persistedDocument?.required_fields, ["schema_version", "approvals", "entries"]) ||
464
+ terminologySetIdentity?.normalization !== "unicode_caseless_exact_no_normalization" ||
465
+ !sameStrings(terminologySetIdentity?.members, [
466
+ "paired_entry.term",
467
+ "paired_approval.proposal.variants.term",
468
+ ]) ||
469
+ terminologySetIdentity?.uniqueness !== "global_across_complete_document" ||
470
+ !nonEmpty(terminologySetIdentity?.rule) ||
471
+ !nonEmpty(publication?.transaction) ||
472
+ !nonEmpty(publication?.merge_and_replay)) {
473
+ errors.push("project publication must define one versioned atomic build-owned request and document");
474
+ }
475
+ if (projectInput?.authority !== "repository_files" ||
476
+ !nonEmpty(projectInput?.bounded_rule) ||
477
+ !sameStrings(projectInput?.excluded_categories, [
478
+ "personal_history_record",
479
+ "personal_history_anchor",
480
+ ])) {
481
+ errors.push("project input must be bounded repository-file evidence excluding personal history");
482
+ }
483
+ const consumer = mapping(authority.consumer_boundary);
484
+ errors.push(...validateConsumerBoundary(consumer));
485
+ errors.push(...validateConsumerEvidenceOwner(consumer));
486
+ const profileGrounding = mapping(consumer?.profile_grounding);
487
+ if (!sameStrings(consumer?.forbidden_persisted_entry_fields, [
488
+ "precedence",
489
+ "collision",
490
+ "review",
491
+ ]) ||
492
+ profileGrounding?.implementation !== "active_exclusion_only" ||
493
+ !sameStrings(profileGrounding?.capabilities, ["discuss", "plan", "build"]) ||
494
+ profileGrounding?.command !== "agentera report profile-grounding --format json" ||
495
+ profileGrounding?.schema_version !== "agentera.profileGrounding.v1" ||
496
+ profileGrounding?.parser !==
497
+ "packages/cli/src/analytics/personalGlossaryProfile.ts#personalProfileGrounding" ||
498
+ profileGrounding?.max_profile_utf8_bytes !== 65536 ||
499
+ profileGrounding?.raw_profile_read !== "forbidden" ||
500
+ !nonEmpty(profileGrounding?.content_rule) ||
501
+ !nonEmpty(profileGrounding?.failure_rule)) {
502
+ errors.push("sanitized profile grounding must continue excluding the owned glossary while consumer acquisition remains separate");
503
+ }
504
+ const exactCollision = mapping(consumer?.exact_collision);
505
+ if (exactCollision?.behavior !== "project_precedence_at_consumption" ||
506
+ exactCollision?.persistence !== "forbidden" ||
507
+ exactCollision?.personal_entry_suppression !== "forbidden") {
508
+ errors.push("exact collisions must defer project precedence to consumption without persistence or suppression");
509
+ }
510
+ const inferred = mapping(consumer?.inferred_semantic_equivalence);
511
+ if (inferred?.behavior !== "user_review" ||
512
+ inferred?.automatic_merge !== "forbidden" ||
513
+ inferred?.suppression !== "forbidden" ||
514
+ inferred?.precedence !== "forbidden") {
515
+ errors.push("inferred equivalence must defer to user review without merge, suppression, or precedence");
516
+ }
517
+ const capabilities = mapping(authority.deferred_capability_contracts);
518
+ const profile = mapping(capabilities?.profile);
519
+ const profileContracts = mapping(profile?.contracts);
520
+ if (profile?.implementation !== "active_partial" ||
521
+ !sameStrings(profile?.capabilities, ["profile"]) ||
522
+ !sameStrings(profile?.active_behavior, [
523
+ "ownership_contracts.personal.admission",
524
+ "ownership_contracts.personal.profile_output",
525
+ ]) ||
526
+ !sameStrings(profile?.inactive_behavior, ["lookup"]) ||
527
+ profileContracts?.admission !== "ownership_contracts.personal.input" ||
528
+ profileContracts?.provenance !== "ownership_contracts.personal.allowed_provenance" ||
529
+ profileContracts?.confidence !== "shared_primitive.fields.confidence" ||
530
+ profileContracts?.retention_and_decay !== "ownership_contracts.personal.retention_and_decay" ||
531
+ !sameStrings(profile?.forbidden_current_claims, ["lookup", "project_glossary_consumption"])) {
532
+ errors.push("profile glossary rendering and persistence must be active while Profile itself performs no consumer lookup");
533
+ }
534
+ const audit = mapping(capabilities?.audit);
535
+ const findingFamily = mapping(audit?.finding_family);
536
+ const proposalOutput = mapping(audit?.proposal_output);
537
+ const auditInputs = mapping(audit?.inputs);
538
+ if (audit?.implementation !== "active_partial" ||
539
+ !sameStrings(audit?.capabilities, ["audit"]) ||
540
+ audit?.active_behavior !== "terminology_drift_finding_generation" ||
541
+ findingFamily?.status !== "implemented" ||
542
+ findingFamily?.mutation !== "forbidden" ||
543
+ findingFamily?.evidence !== "ownership_contracts.project.input" ||
544
+ findingFamily?.confidence !== "skills/agentera/protocol.yaml#CONFIDENCE_SCALE" ||
545
+ findingFamily?.filtering !== "skills/agentera/capabilities/audit/schemas/validation.yaml" ||
546
+ !nonEmpty(findingFamily?.canonical_proposal_rule) ||
547
+ !nonEmpty(findingFamily?.personal_comparison_rule) ||
548
+ proposalOutput?.implementation !== "active" ||
549
+ proposalOutput?.intended_output !== "read_only_terminology_drift_finding" ||
550
+ proposalOutput?.digest !== "ownership_contracts.project.proposal_digest" ||
551
+ auditInputs?.personal_history !== "ownership_contracts.personal.input" ||
552
+ auditInputs?.project_file !== "ownership_contracts.project.input" ||
553
+ auditInputs?.project_file_history_classification !== "forbidden" ||
554
+ !sameStrings(audit?.forbidden_current_claims, [
555
+ "persistence",
556
+ "approval",
557
+ "docs_mapping_mutation",
558
+ "lookup",
559
+ ])) {
560
+ errors.push("audit findings and proposal digests must be active and read-only");
561
+ }
562
+ const buildPublication = mapping(capabilities?.build_publication);
563
+ if (buildPublication?.implementation !== "active" ||
564
+ !sameStrings(buildPublication?.capabilities, ["build"]) ||
565
+ buildPublication?.active_behavior !== "ownership_contracts.project.publication" ||
566
+ buildPublication?.output !== "skills/agentera/schemas/artifacts/glossary.yaml" ||
567
+ !sameStrings(buildPublication?.inactive_behavior, [
568
+ "lookup",
569
+ "precedence",
570
+ "semantic_equivalence_review",
571
+ "personal_profile_mutation",
572
+ "docs_mapping_mutation",
573
+ ])) {
574
+ errors.push("build must own only active typed glossary publication");
575
+ }
576
+ const consumers = mapping(capabilities?.consumers);
577
+ const consumerImplementations = mapping(consumers?.implementation);
578
+ if (consumerImplementations?.build !== "active" ||
579
+ consumerImplementations?.discuss !== "active" ||
580
+ consumerImplementations?.plan !== "active" ||
581
+ consumerImplementations?.prime !== "active" ||
582
+ !sameStrings(consumers?.capabilities, ["discuss", "plan", "build", "prime"]) ||
583
+ consumers?.behavior !== "consumer_boundary" ||
584
+ !sameStrings(consumers?.forbidden_current_claims, [])) {
585
+ errors.push("build, discuss, plan, and prime glossary consumption must be active");
586
+ }
587
+ return errors;
588
+ }
589
+ export function validateGlossaryCapabilityImplementationClaim(capability, claimedImplementation, pathname = glossaryEntryAuthorityPath()) {
590
+ if (!DEFERRED_CAPABILITIES.includes(capability)) {
591
+ return [`${capability} is not an affected glossary capability`];
592
+ }
593
+ const authority = contract(pathname);
594
+ const declarations = mapping(authority.deferred_capability_contracts);
595
+ const declaration = Object.values(declarations ?? {})
596
+ .map(mapping)
597
+ .find((candidate) => strings(candidate?.capabilities).includes(capability));
598
+ if (!declaration)
599
+ return [`${capability} has no deferred glossary declaration`];
600
+ const declaredImplementation = mapping(declaration.implementation)?.[capability] ?? declaration.implementation;
601
+ if (claimedImplementation !== declaredImplementation) {
602
+ return [
603
+ `${capability} glossary behavior is ${String(declaredImplementation)}; ${claimedImplementation} is a false implementation claim`,
604
+ ];
605
+ }
606
+ return [];
607
+ }
608
+ function requiredEntryShape(entry, authority) {
609
+ const errors = [];
610
+ const primitive = mapping(authority.shared_primitive);
611
+ const fields = mapping(primitive?.fields);
612
+ const consumer = mapping(authority.consumer_boundary);
613
+ const forbidden = strings(consumer?.forbidden_persisted_entry_fields).filter((field) => field in entry);
614
+ if (forbidden.length > 0) {
615
+ errors.push(`entry contains forbidden persisted fields: ${forbidden.join(", ")}`);
616
+ }
617
+ const requiredFields = strings(primitive?.required_fields);
618
+ const additional = Object.keys(entry).filter((field) => !requiredFields.includes(field));
619
+ if (additional.length > 0)
620
+ errors.push(`entry contains fields outside the shared primitive: ${additional.join(", ")}`);
621
+ for (const field of requiredFields) {
622
+ if (!(field in entry))
623
+ errors.push(`${field} is required`);
624
+ }
625
+ if (typeof entry.term !== "string" || entry.term.trim() === "")
626
+ errors.push("term must be a non-empty string");
627
+ if (typeof entry.meaning !== "string" || entry.meaning.trim() === "")
628
+ errors.push("meaning must be a non-empty string");
629
+ if (!Number.isInteger(entry.confidence) ||
630
+ Number(entry.confidence) < 0 ||
631
+ Number(entry.confidence) > 100) {
632
+ errors.push("confidence must be an integer from protocol CS1-CS5");
633
+ }
634
+ if (!strings(mapping(fields?.permanence)?.values).includes(String(entry.permanence))) {
635
+ errors.push("permanence must be an existing profile permanence class");
636
+ }
637
+ const temporal = mapping(entry.temporal);
638
+ for (const field of strings(mapping(fields?.temporal)?.required_fields)) {
639
+ if (!isIsoCalendarDate(temporal?.[field])) {
640
+ errors.push(`temporal.${field} must be an ISO date`);
641
+ }
642
+ }
643
+ return errors;
644
+ }
645
+ function evidenceShape(evidence, requiredFields, kind, index) {
646
+ const errors = [];
647
+ const missing = requiredFields.filter((field) => !(field in evidence));
648
+ const forbidden = Object.keys(evidence).filter((field) => !requiredFields.includes(field));
649
+ if (missing.length > 0) {
650
+ errors.push(`provenance.evidence[${index}] is missing ${kind} fields: ${missing.join(", ")}`);
651
+ }
652
+ if (forbidden.length > 0) {
653
+ errors.push(`provenance.evidence[${index}] contains fields forbidden for ${kind}: ${forbidden.join(", ")}`);
654
+ }
655
+ return errors;
656
+ }
657
+ function validateHistoryEvidence(evidence, context, index) {
658
+ const errors = [];
659
+ const sourceId = typeof evidence.source_id === "string" ? evidence.source_id : "";
660
+ const anchor = typeof evidence.evidence_anchor === "string" ? evidence.evidence_anchor : "";
661
+ const retained = context.retainedHistory.get(anchor);
662
+ if (!sourceId || !anchor)
663
+ errors.push(`provenance.evidence[${index}] requires source_id and evidence_anchor`);
664
+ if (!retained || retained.sourceId !== sourceId) {
665
+ errors.push(`provenance.evidence[${index}].evidence_anchor must resolve to its retained source_id`);
666
+ }
667
+ return errors;
668
+ }
669
+ export function validateGlossaryEntry(entry, owner, context = { retainedHistory: new Map() }, pathname = glossaryEntryAuthorityPath()) {
670
+ const authorityErrors = validateGlossaryEntryContract(pathname);
671
+ if (authorityErrors.length > 0) {
672
+ return authorityErrors.map((error) => `glossary authority invalid: ${error}`);
673
+ }
674
+ const authority = contract(pathname);
675
+ const variants = mapping(authority.provenance_variants);
676
+ const ownership = mapping(authority.ownership_contracts);
677
+ const ownerContract = mapping(ownership[owner]);
678
+ const errors = requiredEntryShape(entry, authority);
679
+ const provenance = mapping(entry.provenance);
680
+ const kind = provenance?.kind;
681
+ const evidence = Array.isArray(provenance?.evidence) ? provenance.evidence.map(mapping) : [];
682
+ if (!kind || !strings(ownerContract.allowed_provenance).includes(kind)) {
683
+ errors.push(owner === "personal"
684
+ ? "personal entries admit only bounded personal-history provenance"
685
+ : "project entries admit only repository-file provenance");
686
+ return errors;
687
+ }
688
+ const variant = mapping(variants[kind]);
689
+ const requiredEvidenceFields = strings(variant.required_evidence_fields);
690
+ for (const [index, item] of evidence.entries()) {
691
+ if (item)
692
+ errors.push(...evidenceShape(item, requiredEvidenceFields, kind, index));
693
+ }
694
+ const evidenceCount = Number(variant.evidence_count);
695
+ if (kind === "personal_explicit_definition") {
696
+ if (evidence.length !== evidenceCount || evidence[0] === null) {
697
+ errors.push("explicit personal definitions require exactly one retained record");
698
+ }
699
+ else {
700
+ errors.push(...validateHistoryEvidence(evidence[0], context, 0));
701
+ const retained = context.retainedHistory.get(String(evidence[0].evidence_anchor));
702
+ if (!retained ||
703
+ evidence[0].signal_type !== retained.signalType ||
704
+ !strings(variant.allowed_signal_types).includes(retained.signalType)) {
705
+ errors.push("explicit personal definition evidence has an inadmissible signal type");
706
+ }
707
+ }
708
+ }
709
+ if (kind === "personal_inferred_usage") {
710
+ if (evidence.length !== evidenceCount || evidence.some((item) => item === null)) {
711
+ errors.push("inferred personal usage requires exactly two retained records");
712
+ }
713
+ else {
714
+ for (const [index, item] of evidence.entries()) {
715
+ errors.push(...validateHistoryEvidence(item, context, index));
716
+ const retained = context.retainedHistory.get(String(item.evidence_anchor));
717
+ if (!retained ||
718
+ item.source_kind !== retained.sourceKind ||
719
+ !strings(variant.allowed_source_kinds).includes(retained.sourceKind)) {
720
+ errors.push(`provenance.evidence[${index}] has an inadmissible inferred source kind`);
721
+ }
722
+ }
723
+ const sourceIds = evidence.map((item) => String(item?.source_id));
724
+ const anchors = evidence.map((item) => String(item?.evidence_anchor));
725
+ if (new Set(sourceIds).size !== 2 || new Set(anchors).size !== 2) {
726
+ errors.push("inferred personal usage requires two distinct retained identities and anchors");
727
+ }
728
+ }
729
+ }
730
+ if (kind === "project_file") {
731
+ if (evidence.length !== evidenceCount || evidence[0] === null) {
732
+ errors.push("project file provenance requires exactly one source record");
733
+ }
734
+ else {
735
+ const sourcePath = evidence[0].source_path;
736
+ const digest = evidence[0].source_record_sha256;
737
+ if (!isSafeProjectSourcePath(sourcePath)) {
738
+ errors.push("project source_path must be safe and project-relative");
739
+ }
740
+ if (typeof digest !== "string" || !/^[a-f0-9]{64}$/.test(digest)) {
741
+ errors.push("project source_record_sha256 must be a lowercase SHA-256 digest");
742
+ }
743
+ }
744
+ }
745
+ return errors;
746
+ }
747
+ //# sourceMappingURL=glossaryEntryContract.js.map