@codyswann/lisa 4.19.0 → 4.21.0

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 (133) hide show
  1. package/all/copy-contents/.gitattributes +2 -1
  2. package/all/create-only/.lisa/PROJECT_LEARNINGS.md +1 -1
  3. package/dist/core/learnings-alias.d.ts +26 -45
  4. package/dist/core/learnings-alias.d.ts.map +1 -1
  5. package/dist/core/learnings-alias.js +29 -48
  6. package/dist/core/learnings-alias.js.map +1 -1
  7. package/dist/core/learnings-budget-check.d.ts +2 -1
  8. package/dist/core/learnings-budget-check.d.ts.map +1 -1
  9. package/dist/core/learnings-budget-check.js +13 -11
  10. package/dist/core/learnings-budget-check.js.map +1 -1
  11. package/dist/core/learnings-contract.d.ts +27 -10
  12. package/dist/core/learnings-contract.d.ts.map +1 -1
  13. package/dist/core/learnings-contract.js +37 -9
  14. package/dist/core/learnings-contract.js.map +1 -1
  15. package/dist/core/learnings-document.d.ts +20 -1
  16. package/dist/core/learnings-document.d.ts.map +1 -1
  17. package/dist/core/learnings-document.js +81 -14
  18. package/dist/core/learnings-document.js.map +1 -1
  19. package/dist/core/learnings-entry.d.ts +11 -1
  20. package/dist/core/learnings-entry.d.ts.map +1 -1
  21. package/dist/core/learnings-entry.js +98 -24
  22. package/dist/core/learnings-entry.js.map +1 -1
  23. package/dist/core/learnings-merge-driver.d.ts +4 -2
  24. package/dist/core/learnings-merge-driver.d.ts.map +1 -1
  25. package/dist/core/learnings-merge-driver.js +6 -3
  26. package/dist/core/learnings-merge-driver.js.map +1 -1
  27. package/dist/core/learnings-merge.d.ts.map +1 -1
  28. package/dist/core/learnings-merge.js +96 -10
  29. package/dist/core/learnings-merge.js.map +1 -1
  30. package/dist/core/learnings-overflow.d.ts.map +1 -1
  31. package/dist/core/learnings-overflow.js +72 -3
  32. package/dist/core/learnings-overflow.js.map +1 -1
  33. package/dist/core/learnings-supersede.d.ts +56 -0
  34. package/dist/core/learnings-supersede.d.ts.map +1 -0
  35. package/dist/core/learnings-supersede.js +199 -0
  36. package/dist/core/learnings-supersede.js.map +1 -0
  37. package/dist/core/learnings-writer.d.ts +9 -7
  38. package/dist/core/learnings-writer.d.ts.map +1 -1
  39. package/dist/core/learnings-writer.js +31 -58
  40. package/dist/core/learnings-writer.js.map +1 -1
  41. package/dist/core/learnings.d.ts +3 -0
  42. package/dist/core/learnings.d.ts.map +1 -1
  43. package/dist/core/learnings.js +3 -0
  44. package/dist/core/learnings.js.map +1 -1
  45. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  46. package/dist/core/upstream-evidence-manifest.js +24 -7
  47. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  48. package/package.json +3 -2
  49. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa/.codex-plugin/skills/lisa-debrief-apply/SKILL.md +5 -4
  52. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +2 -2
  53. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  54. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  55. package/plugins/lisa/agents/learner.md +7 -6
  56. package/plugins/lisa/rules/reference/project-learnings.md +34 -1
  57. package/plugins/lisa/skills/lisa-debrief-apply/SKILL.md +5 -4
  58. package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +2 -2
  59. package/plugins/lisa/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  60. package/plugins/lisa/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  61. package/plugins/lisa-agy/agents/learner.md +7 -6
  62. package/plugins/lisa-agy/plugin.json +1 -1
  63. package/plugins/lisa-agy/skills/lisa-debrief-apply/SKILL.md +5 -4
  64. package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +2 -2
  65. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  66. package/plugins/lisa-agy/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  67. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  69. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  70. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-copilot/agents/learner.agent.md +7 -6
  74. package/plugins/lisa-copilot/rules/reference/project-learnings.md +34 -1
  75. package/plugins/lisa-copilot/skills/lisa-debrief-apply/SKILL.md +5 -4
  76. package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +2 -2
  77. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  78. package/plugins/lisa-copilot/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  79. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-cursor/agents/learner.md +7 -6
  81. package/plugins/lisa-cursor/rules/project-learnings-reference.mdc +34 -1
  82. package/plugins/lisa-cursor/skills/lisa-debrief-apply/SKILL.md +5 -4
  83. package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +2 -2
  84. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  85. package/plugins/lisa-cursor/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  86. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  88. package/plugins/lisa-expo-agy/plugin.json +1 -1
  89. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  93. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  94. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  98. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  99. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  103. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  104. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  108. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  109. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  113. package/plugins/lisa-rails-agy/plugin.json +1 -1
  114. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  116. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  117. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  118. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  119. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  120. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  122. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  123. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  124. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  126. package/plugins/src/base/agents/learner.md +7 -6
  127. package/plugins/src/base/rules/reference/project-learnings.md +34 -1
  128. package/plugins/src/base/skills/lisa-debrief-apply/SKILL.md +5 -4
  129. package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +2 -2
  130. package/plugins/src/base/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  131. package/plugins/src/base/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  132. package/scripts/verify-learner-frontmatter-built.mjs +1 -1
  133. package/scripts/verify-packed-learnings-contract.mjs +268 -0
@@ -27,8 +27,11 @@
27
27
  * Per id, against the base: if only one side changed it, that side wins
28
28
  * (including a removal). If both sides made the SAME change, it is not a
29
29
  * conflict. If both sides changed it differently, the merge FAILS rather than
30
- * guessing — with one narrow exception: when the only differing field is
31
- * `last_confirmed`, the later date wins, because that field is a monotonic
30
+ * guessing — with two information-preserving exceptions. First, two distinct
31
+ * fingerprint successors of the same base stamp are a concurrent stable-id
32
+ * fork: both survive deterministically, with the lower fingerprint retaining
33
+ * the stable id and the other keyed by its fingerprint. Second, when the only
34
+ * differing field is `last_confirmed`, the later date wins, because that field is a monotonic
32
35
  * "this rule demonstrably applied again" stamp and taking the later one loses
33
36
  * no information. A confirmation bump never beats a content edit; that is a
34
37
  * genuine conflict, because preferring the later timestamp would silently
@@ -42,6 +45,7 @@
42
45
  */
43
46
  import { LEARNINGS_CONTRACT, } from "./learnings-contract.js";
44
47
  import { assertDocumentBudget, parseLearningsFile, renderLearningsFile, } from "./learnings-document.js";
48
+ import { validateLearningEntry } from "./learnings-entry.js";
45
49
  /**
46
50
  * Merge two divergent ledger versions against their common ancestor.
47
51
  * @param base - Merge-base document, or undefined when the file is new on both sides
@@ -100,16 +104,32 @@ function resolveAllIds(base, ours, theirs) {
100
104
  reason: `Both branches changed learning ${conflictingIds.map(id => `'${id}'`).join(", ")} differently; recompact by hand to keep the intended content`,
101
105
  };
102
106
  }
103
- return {
104
- kind: "entries",
105
- entries: resolutions
106
- .filter((resolution) => resolution !== "absent" && resolution !== "conflict")
107
- .sort((left, right) => left.id.localeCompare(right.id)),
108
- };
107
+ const unsortedEntries = resolutions.flatMap(resolution => resolution === "absent" || resolution === "conflict"
108
+ ? []
109
+ : Array.isArray(resolution)
110
+ ? resolution
111
+ : [resolution]);
112
+ const entries = sortLearningEntries(unsortedEntries);
113
+ const duplicateId = findDuplicate(entries.map(entry => entry.id));
114
+ if (duplicateId !== undefined) {
115
+ return {
116
+ kind: "conflict",
117
+ reason: `Concurrent learning fork would collide with live id '${duplicateId}'; recompact by hand to preserve both entries`,
118
+ };
119
+ }
120
+ const duplicateFingerprint = findDuplicate(entries.map(entry => entry.fingerprint));
121
+ if (duplicateFingerprint !== undefined) {
122
+ return {
123
+ kind: "conflict",
124
+ reason: `Merged project learnings contain duplicate fingerprint '${duplicateFingerprint}'; recompact by hand rather than guessing which identical capture wins`,
125
+ };
126
+ }
127
+ return { kind: "entries", entries };
109
128
  }
110
129
  /**
111
130
  * Resolve one id with standard three-way semantics.
112
131
  * @param base - Ancestor version, if any
132
+ * @param base - Common ancestor version, if any
113
133
  * @param ours - Current-branch version, if any
114
134
  * @param theirs - Incoming-branch version, if any
115
135
  * @returns The winning entry, `absent` when both sides removed it, or `conflict`
@@ -126,27 +146,93 @@ function resolveOneId(base, ours, theirs) {
126
146
  if (sameEntry(base, theirs)) {
127
147
  return ours ?? "absent";
128
148
  }
129
- return resolveDivergentEdit(ours, theirs);
149
+ return resolveDivergentEdit(base, ours, theirs);
130
150
  }
131
151
  /**
132
152
  * Resolve the one divergence that is safe to auto-merge: both branches only
133
153
  * re-confirmed the same entry, so the later monotonic stamp wins.
154
+ * @param base - Common ancestor version, if any
134
155
  * @param ours - Current-branch version, if any
135
156
  * @param theirs - Incoming-branch version, if any
136
157
  * @returns The later-confirmed entry, or `conflict`
137
158
  */
138
- function resolveDivergentEdit(ours, theirs) {
159
+ function resolveDivergentEdit(base, ours, theirs) {
139
160
  if (ours === undefined || theirs === undefined) {
140
161
  // One side edited the entry while the other superseded it. Dropping the
141
162
  // edit or resurrecting the removal are both information loss.
142
163
  return "conflict";
143
164
  }
165
+ const fork = forkConcurrentStableRewrite(base, ours, theirs);
166
+ if (fork !== undefined) {
167
+ return fork;
168
+ }
144
169
  // Neutralize only `last_confirmed`: if the entries match once that field is
145
170
  // aligned, it was the sole difference and taking the later stamp loses nothing.
146
171
  return sameEntry({ ...ours, last_confirmed: theirs.last_confirmed }, theirs)
147
172
  ? pickLaterConfirmation(ours, theirs)
148
173
  : "conflict";
149
174
  }
175
+ /**
176
+ * Preserve both successors when two branches legitimately rewrote the same
177
+ * stable identity from the same fingerprint snapshot.
178
+ *
179
+ * The lower fingerprint keeps the stable public id; the other successor is
180
+ * re-keyed to its own globally unique fingerprint. This ordering is independent
181
+ * of ours/theirs orientation, so reversing merge sides produces identical
182
+ * bytes. A confirmation-versus-rewrite is not a fork: both successors must
183
+ * carry new, distinct fingerprints relative to the base.
184
+ * @param base - Common ancestor entry
185
+ * @param ours - Current-branch successor
186
+ * @param theirs - Incoming-branch successor
187
+ * @returns Two deterministic survivors, or undefined when this is not a fork
188
+ */
189
+ function forkConcurrentStableRewrite(base, ours, theirs) {
190
+ if (base === undefined ||
191
+ ours.id !== base.id ||
192
+ theirs.id !== base.id ||
193
+ ours.fingerprint === base.fingerprint ||
194
+ theirs.fingerprint === base.fingerprint ||
195
+ ours.fingerprint === theirs.fingerprint) {
196
+ return undefined;
197
+ }
198
+ const [primary, forked] = compareTokens(ours.fingerprint, theirs.fingerprint) <= 0
199
+ ? [ours, theirs]
200
+ : [theirs, ours];
201
+ return [
202
+ primary,
203
+ validateLearningEntry({ ...forked, id: forked.fingerprint }),
204
+ ];
205
+ }
206
+ /**
207
+ * Find the first repeated token in encounter order.
208
+ * @param values - Stable tokens to inspect
209
+ * @returns First duplicate, when one exists
210
+ */
211
+ function findDuplicate(values) {
212
+ return values.find((value, index) => values.indexOf(value) !== index);
213
+ }
214
+ /**
215
+ * Compare stable tokens without locale dependence.
216
+ * @param left - First token
217
+ * @param right - Second token
218
+ * @returns Negative, zero, or positive ordering result
219
+ */
220
+ function compareTokens(left, right) {
221
+ return left === right ? 0 : left < right ? -1 : 1;
222
+ }
223
+ /**
224
+ * Sort entries by id without mutating a caller-owned or local array.
225
+ * @param entries - Entries in arbitrary order
226
+ * @returns New deterministically ordered array
227
+ */
228
+ function sortLearningEntries(entries) {
229
+ return entries.reduce((ordered, entry) => {
230
+ const insertion = ordered.findIndex(current => compareTokens(entry.id, current.id) < 0);
231
+ return insertion === -1
232
+ ? [...ordered, entry]
233
+ : [...ordered.slice(0, insertion), entry, ...ordered.slice(insertion)];
234
+ }, []);
235
+ }
150
236
  /**
151
237
  * Choose the later of two confirmation stamps.
152
238
  * @param ours - Current-branch version
@@ -1 +1 @@
1
- {"version":3,"file":"learnings-merge.js","sourceRoot":"","sources":["../../src/core/learnings-merge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"learnings-merge.js","sourceRoot":"","sources":["../../src/core/learnings-merge.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"learnings-overflow.d.ts","sourceRoot":"","sources":["../../src/core/learnings-overflow.ts"],"names":[],"mappings":"AAkDA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,oBAAoB,EAIrB,MAAM,yBAAyB,CAAC;AAgBjC;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAI1E;AA6CD,6DAA6D;AAC7D,MAAM,WAAW,yBAAyB;IACxC,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,yBAAyB,CAAC,CAIpC;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,SAAS,MAAM,EAAE,GACrB,OAAO,CAAC,sBAAsB,CAAC,CAwBjC;AA0BD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,KAAK,CAAC,CAyBhB"}
1
+ {"version":3,"file":"learnings-overflow.d.ts","sourceRoot":"","sources":["../../src/core/learnings-overflow.ts"],"names":[],"mappings":"AAkDA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,oBAAoB,EAIrB,MAAM,yBAAyB,CAAC;AAiBjC;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAI1E;AA6CD,6DAA6D;AAC7D,MAAM,WAAW,yBAAyB;IACxC,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,yBAAyB,CAAC,CAIpC;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,SAAS,MAAM,EAAE,GACrB,OAAO,CAAC,sBAAsB,CAAC,CAwBjC;AAoHD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,KAAK,CAAC,CA0BhB"}
@@ -23,7 +23,7 @@
23
23
  * Being tracked means it has the very concurrent-writer problem the ledger just
24
24
  * had (CodySwannGT/lisa#1995), which is exactly why it reuses the ledger's
25
25
  * canonical document format verbatim: one `.gitattributes` line binds it to the
26
- * SAME union-by-id merge driver, `parseLearningsFile` gives it the SAME
26
+ * SAME id-and-fingerprint merge driver, `parseLearningsFile` gives it the SAME
27
27
  * conflict-marker guard, and `checkLearningsBudget` can gate it in CI unchanged.
28
28
  * A bespoke format would have needed all of that written a second time.
29
29
  *
@@ -49,6 +49,7 @@ import * as fse from "fs-extra";
49
49
  import * as path from "node:path";
50
50
  import { writeFileAtomically } from "../utils/atomic-file-write.js";
51
51
  import { LearningsBudgetError, assertDocumentBudget, parseLearningsFile, renderLearningsFile, } from "./learnings-document.js";
52
+ import { validateLearningEntry } from "./learnings-entry.js";
52
53
  import { assertLearningsUnchanged, assertSafeLearningParents, readExistingLearnings, resolveSafeLearningTarget, } from "./learnings-file-safety.js";
53
54
  import { withFileTargetLock } from "./learnings-lock.js";
54
55
  import { readProjectConfig, resolveProjectLearningsFile, } from "./project-config.js";
@@ -156,6 +157,10 @@ export async function drainLearningsOverflow(projectRoot, ids) {
156
157
  async function publishOverflow(root, target, entries, preImage) {
157
158
  const rendered = renderLearningsFile(entries);
158
159
  assertDocumentBudget(rendered, entries.length, "Learnings overflow");
160
+ // Budget checks alone do not prove identity uniqueness or exact schema.
161
+ // Reparse the COMPLETE candidate while the overflow lock is held, before a
162
+ // temporary file can be renamed over the last known-good document.
163
+ parseLearningsFile(rendered);
159
164
  await fse.ensureDir(path.dirname(target));
160
165
  await writeFileAtomically(target, rendered, {
161
166
  beforeRename: async () => {
@@ -164,6 +169,69 @@ async function publishOverflow(root, target, entries, preImage) {
164
169
  },
165
170
  });
166
171
  }
172
+ /**
173
+ * Resolve an incoming capture against overflow identity without losing either
174
+ * version. Exact byte-equivalent identity is the only idempotent case. When a
175
+ * distinct fingerprint arrives under an occupied stable id, the fingerprint
176
+ * becomes its deterministic fork id, matching the merge driver's concurrent
177
+ * stable-id policy. Every other id/fingerprint collision fails before publish.
178
+ * @param entries - Validated overflow entries read under its lock
179
+ * @param entry - Validated dropped capture
180
+ * @returns Complete candidate entries, or undefined for an exact replay
181
+ */
182
+ function resolveOverflowEntries(entries, entry) {
183
+ const byId = entries.find(current => current.id === entry.id);
184
+ const byFingerprint = entries.find(current => current.fingerprint === entry.fingerprint);
185
+ if (byId !== undefined && byFingerprint === byId) {
186
+ return resolveExactOverflowReplay(byId, entry);
187
+ }
188
+ if (byFingerprint !== undefined) {
189
+ throw new Error(`Duplicate learning fingerprint '${entry.fingerprint}' in the overflow`);
190
+ }
191
+ if (byId === undefined) {
192
+ return [...entries, entry];
193
+ }
194
+ // A self-identifying version (`id === fingerprint`) owns that stable id. For
195
+ // two rewritten versions, the lower fingerprint owns it, exactly as in the
196
+ // merge driver. The rule is independent of lock-arrival order.
197
+ const primary = selectOverflowPrimary(byId, entry);
198
+ const forked = primary === byId ? entry : byId;
199
+ if (entries.some(current => current.id === forked.fingerprint)) {
200
+ throw new Error(`Cannot preserve learning '${entry.id}': deterministic fork id '${forked.fingerprint}' already exists in the overflow`);
201
+ }
202
+ const rekeyed = validateLearningEntry({
203
+ ...forked,
204
+ id: forked.fingerprint,
205
+ });
206
+ return entries.flatMap(current => current === byId ? [primary, rekeyed] : [current]);
207
+ }
208
+ /**
209
+ * Accept only a byte-identical replay of one occupied id/fingerprint pair.
210
+ * @param current - Existing overflow identity
211
+ * @param incoming - Incoming entry with the same id and fingerprint
212
+ * @returns Undefined for an exact idempotent replay
213
+ */
214
+ function resolveExactOverflowReplay(current, incoming) {
215
+ if (JSON.stringify(current) === JSON.stringify(incoming)) {
216
+ return undefined;
217
+ }
218
+ throw new Error(`Duplicate learning identity '${incoming.id}' has different content in the overflow`);
219
+ }
220
+ /**
221
+ * Pick the version that owns a colliding stable id independently of arrival.
222
+ * @param current - Version already in the overflow
223
+ * @param incoming - Distinct version arriving under the same id
224
+ * @returns Stable-id owner
225
+ */
226
+ function selectOverflowPrimary(current, incoming) {
227
+ if (current.id === current.fingerprint) {
228
+ return current;
229
+ }
230
+ if (incoming.id === incoming.fingerprint) {
231
+ return incoming;
232
+ }
233
+ return incoming.fingerprint < current.fingerprint ? incoming : current;
234
+ }
167
235
  /**
168
236
  * Preserve a capture the ledger had no budget for, then re-raise the breach.
169
237
  *
@@ -188,11 +256,12 @@ export async function preserveDroppedLearning(projectRoot, entry, breach) {
188
256
  // Re-establish containment after the lock wait; see drainLearningsOverflow.
189
257
  await assertSafeLearningParents(root, path.dirname(target));
190
258
  const { entries, image } = await readOverflowEntries(target);
191
- if (entries.some(current => current.id === entry.id)) {
259
+ const next = resolveOverflowEntries(entries, entry);
260
+ if (next === undefined) {
192
261
  return true;
193
262
  }
194
263
  try {
195
- await publishOverflow(root, target, [...entries, entry], image);
264
+ await publishOverflow(root, target, next, image);
196
265
  return true;
197
266
  }
198
267
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"learnings-overflow.js","sourceRoot":"","sources":["../../src/core/learnings-overflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEpE,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAE7B,wEAAwE;AACxE,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAAC,aAAqB;IAChE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7E,OAAO,GAAG,IAAI,GAAG,eAAe,GAAG,SAAS,EAAE,CAAC;AACjD,CAAC;AAQD;;;;GAIG;AACH,KAAK,UAAU,qBAAqB,CAClC,WAAmB;IAEnB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,4BAA4B,CAC3C,2BAA2B,CAAC,MAAM,CAAC,CACpC,CAAC;IACF,OAAO,yBAAyB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAUD;;;;;GAKG;AACH,KAAK,UAAU,mBAAmB,CAAC,MAAc;IAC/C,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACrD,OAAO;QACL,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC;QACnE,KAAK,EAAE,QAAQ;KAChB,CAAC;AACJ,CAAC;AAUD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,WAAmB;IAEnB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACtD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AACnC,CAAC;AAcD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,WAAmB,EACnB,GAAsB;IAEtB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAClE,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,OAAO,kBAAkB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QAC3C,oEAAoE;QACpE,kEAAkE;QAClE,kEAAkE;QAClE,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,OAAO;aACpB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;aACxC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACnE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;QACD,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,OAAO;YACP,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC1D,SAAS,EAAE,QAAQ,CAAC,MAAM;SAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,eAAe,CAC5B,IAAY,EACZ,MAAc,EACd,OAAiC,EACjC,QAA4B;IAE5B,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC9C,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACrE,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,MAAM,mBAAmB,CAAC,MAAM,EAAE,QAAQ,EAAE;QAC1C,YAAY,EAAE,KAAK,IAAI,EAAE;YACvB,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAC5D,MAAM,wBAAwB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnD,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAmB,EACnB,KAAoB,EACpB,MAA4B;IAE5B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAClE,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QAC5D,4EAA4E;QAC5E,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC7D,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACH,MAAM,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;YAChE,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;gBAC1C,OAAO,KAAK,CAAC;YACf,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,oBAAoB,CAC5B,SAAS;QACP,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,mCAAmC,MAAM,oFAAoF;QAChJ,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,sBAAsB,MAAM,4GAA4G,CAC9J,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,GAAsB;IACjD,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAC1D,CAAC;QACD,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"learnings-overflow.js","sourceRoot":"","sources":["../../src/core/learnings-overflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEpE,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAE7B,wEAAwE;AACxE,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAAC,aAAqB;IAChE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7E,OAAO,GAAG,IAAI,GAAG,eAAe,GAAG,SAAS,EAAE,CAAC;AACjD,CAAC;AAQD;;;;GAIG;AACH,KAAK,UAAU,qBAAqB,CAClC,WAAmB;IAEnB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,4BAA4B,CAC3C,2BAA2B,CAAC,MAAM,CAAC,CACpC,CAAC;IACF,OAAO,yBAAyB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAUD;;;;;GAKG;AACH,KAAK,UAAU,mBAAmB,CAAC,MAAc;IAC/C,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACrD,OAAO;QACL,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC;QACnE,KAAK,EAAE,QAAQ;KAChB,CAAC;AACJ,CAAC;AAUD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,WAAmB;IAEnB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACtD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AACnC,CAAC;AAcD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,WAAmB,EACnB,GAAsB;IAEtB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAClE,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,OAAO,kBAAkB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QAC3C,oEAAoE;QACpE,kEAAkE;QAClE,kEAAkE;QAClE,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,OAAO;aACpB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;aACxC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACnE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;QACD,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,OAAO;YACP,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC1D,SAAS,EAAE,QAAQ,CAAC,MAAM;SAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,eAAe,CAC5B,IAAY,EACZ,MAAc,EACd,OAAiC,EACjC,QAA4B;IAE5B,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC9C,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACrE,wEAAwE;IACxE,2EAA2E;IAC3E,mEAAmE;IACnE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,MAAM,mBAAmB,CAAC,MAAM,EAAE,QAAQ,EAAE;QAC1C,YAAY,EAAE,KAAK,IAAI,EAAE;YACvB,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAC5D,MAAM,wBAAwB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnD,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,sBAAsB,CAC7B,OAAiC,EACjC,KAAoB;IAEpB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9D,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAChC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,CACrD,CAAC;IACF,IAAI,IAAI,KAAK,SAAS,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QACjD,OAAO,0BAA0B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,mCAAmC,KAAK,CAAC,WAAW,mBAAmB,CACxE,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,+DAA+D;IAC/D,MAAM,OAAO,GAAG,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CACb,6BAA6B,KAAK,CAAC,EAAE,6BAA6B,MAAM,CAAC,WAAW,kCAAkC,CACvH,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,qBAAqB,CAAC;QACpC,GAAG,MAAM;QACT,EAAE,EAAE,MAAM,CAAC,WAAW;KACvB,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAC/B,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAClD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,0BAA0B,CACjC,OAAsB,EACtB,QAAuB;IAEvB,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,IAAI,KAAK,CACb,gCAAgC,QAAQ,CAAC,EAAE,yCAAyC,CACrF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,qBAAqB,CAC5B,OAAsB,EACtB,QAAuB;IAEvB,IAAI,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC;QACvC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,QAAQ,CAAC,EAAE,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,QAAQ,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;AACzE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAmB,EACnB,KAAoB,EACpB,MAA4B;IAE5B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAClE,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QAC5D,4EAA4E;QAC5E,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,sBAAsB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACpD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACH,MAAM,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YACjD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;gBAC1C,OAAO,KAAK,CAAC;YACf,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,oBAAoB,CAC5B,SAAS;QACP,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,mCAAmC,MAAM,oFAAoF;QAChJ,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,sBAAsB,MAAM,4GAA4G,CAC9J,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,GAAsB;IACjD,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAC1D,CAAC;QACD,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file learnings-supersede.ts
3
+ * @description Plans exact stamped supersedes without mutating shared state
4
+ * @module core/learnings-supersede
5
+ */
6
+ import { type LearningEntry } from "./learnings-contract.js";
7
+ /** Exact identity and content version a caller observed before consolidation. */
8
+ export interface LearningEntryStamp {
9
+ /** Stable public identity expected in the locked ledger. */
10
+ readonly id: string;
11
+ /** Content fingerprint expected at that identity. */
12
+ readonly fingerprint: string;
13
+ }
14
+ /** Why an observed supersede stamp no longer authorizes replacement. */
15
+ export type StaleSupersedeReason = "absent" | "fingerprint-mismatch";
16
+ /** One target whose locked state differs from the caller's snapshot. */
17
+ export interface StaleSupersedeTarget {
18
+ /** Exact identity and fingerprint the caller observed. */
19
+ readonly expected: LearningEntryStamp;
20
+ /** Current fingerprint; absent when the identity no longer exists. */
21
+ readonly actualFingerprint?: string;
22
+ /** Whether the identity disappeared or now names different content. */
23
+ readonly reason: StaleSupersedeReason;
24
+ }
25
+ /** Immutable plan produced from the state read inside the writer lock. */
26
+ export interface LearningSupersedePlan {
27
+ /** Entry to append or replace, carrying the stable primary id when exact. */
28
+ readonly entry: LearningEntry;
29
+ /** Existing entries retained byte-for-byte. */
30
+ readonly retained: readonly LearningEntry[];
31
+ /** Exact targets removed together; empty on any stale target. */
32
+ readonly removed: readonly LearningEntry[];
33
+ /** Stale targets that forced a safe append instead of replacement. */
34
+ readonly stale: readonly StaleSupersedeTarget[];
35
+ }
36
+ /**
37
+ * Validate caller-supplied version stamps before any filesystem work.
38
+ * @param stamps - Possibly untrusted supersede option
39
+ * @returns Frozen, id-unique stamps in caller order
40
+ */
41
+ export declare function validateLearningEntryStamps(stamps: readonly LearningEntryStamp[] | undefined): readonly LearningEntryStamp[];
42
+ /**
43
+ * Plan an all-or-nothing stamped replacement against locked current state.
44
+ *
45
+ * Fingerprint collision is checked first because it is the durable dedupe
46
+ * boundary. An exact target set is removed together and the lexicographically
47
+ * first target id becomes the stable primary. Any missing or changed target
48
+ * makes the whole request a safe append: no target is removed and no alias is
49
+ * claimed.
50
+ * @param entries - Current validated entries read inside the lock
51
+ * @param candidate - New validated content composed by the caller
52
+ * @param expected - Validated version stamps from the caller's snapshot
53
+ * @returns Deterministic append-or-replace plan
54
+ */
55
+ export declare function planLearningSupersede(entries: readonly LearningEntry[], candidate: LearningEntry, expected: readonly LearningEntryStamp[]): LearningSupersedePlan;
56
+ //# sourceMappingURL=learnings-supersede.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learnings-supersede.d.ts","sourceRoot":"","sources":["../../src/core/learnings-supersede.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,yBAAyB,CAAC;AAOjC,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,wEAAwE;AACxE,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,sBAAsB,CAAC;AAErE,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,sEAAsE;IACtE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;CACvC;AAED,0EAA0E;AAC1E,MAAM,WAAW,qBAAqB;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACjD;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,kBAAkB,EAAE,GAAG,SAAS,GAChD,SAAS,kBAAkB,EAAE,CAc/B;AAuDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GACtC,qBAAqB,CAgCvB"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * @file learnings-supersede.ts
3
+ * @description Plans exact stamped supersedes without mutating shared state
4
+ * @module core/learnings-supersede
5
+ */
6
+ import { LEARNINGS_CONTRACT, MAX_STABLE_TOKEN_BYTES, } from "./learnings-contract.js";
7
+ import { validateLearningEntry } from "./learnings-entry.js";
8
+ const STABLE_TOKEN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
9
+ const EXACT_STAMP_ERROR = "Invalid supersede stamp: expected exactly {id, fingerprint}";
10
+ /**
11
+ * Validate caller-supplied version stamps before any filesystem work.
12
+ * @param stamps - Possibly untrusted supersede option
13
+ * @returns Frozen, id-unique stamps in caller order
14
+ */
15
+ export function validateLearningEntryStamps(stamps) {
16
+ if (stamps === undefined) {
17
+ return [];
18
+ }
19
+ if (!Array.isArray(stamps)) {
20
+ throw new Error("Invalid supersede option: expected learning {id, fingerprint} stamps");
21
+ }
22
+ const validated = validateStampArray(stamps);
23
+ if (new Set(validated.map(stamp => stamp.id)).size !== validated.length) {
24
+ throw new Error("Invalid supersede option: duplicate stamped ids");
25
+ }
26
+ return Object.freeze(validated);
27
+ }
28
+ /**
29
+ * Read a dense, expando-free supersede array entirely through own descriptors.
30
+ * Native iteration methods invoke indexed getters and skip sparse holes, which
31
+ * lets hostile containers change the target set while validation is running.
32
+ * The ledger cap bounds descriptor traversal before any member is inspected.
33
+ * @param stamps - Array already established by Array.isArray
34
+ * @returns Frozen validated stamps in exact index order
35
+ */
36
+ function validateStampArray(stamps) {
37
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(stamps, "length");
38
+ if (lengthDescriptor === undefined ||
39
+ !("value" in lengthDescriptor) ||
40
+ typeof lengthDescriptor.value !== "number" ||
41
+ !Number.isSafeInteger(lengthDescriptor.value) ||
42
+ lengthDescriptor.value < 0) {
43
+ throw new Error("Invalid supersede array: expected a dense array");
44
+ }
45
+ const length = lengthDescriptor.value;
46
+ if (length > LEARNINGS_CONTRACT.maxEntries) {
47
+ throw new Error(`Invalid supersede array exceeds maxEntries ${LEARNINGS_CONTRACT.maxEntries}`);
48
+ }
49
+ const expectedKeys = new Set([
50
+ "length",
51
+ ...Array.from({ length }, (_unused, index) => String(index)),
52
+ ]);
53
+ const ownKeys = Reflect.ownKeys(stamps);
54
+ if (ownKeys.length !== expectedKeys.size ||
55
+ ownKeys.some(key => typeof key !== "string" || !expectedKeys.has(key))) {
56
+ throw new Error("Invalid supersede array: expected dense indexed values without extra or symbol fields");
57
+ }
58
+ return Object.freeze(Array.from({ length }, (_unused, index) => {
59
+ const descriptor = Object.getOwnPropertyDescriptor(stamps, String(index));
60
+ if (descriptor === undefined || !("value" in descriptor)) {
61
+ throw new Error("Invalid supersede array: indexed accessors are not allowed");
62
+ }
63
+ return validateStamp(descriptor.value);
64
+ }));
65
+ }
66
+ /**
67
+ * Plan an all-or-nothing stamped replacement against locked current state.
68
+ *
69
+ * Fingerprint collision is checked first because it is the durable dedupe
70
+ * boundary. An exact target set is removed together and the lexicographically
71
+ * first target id becomes the stable primary. Any missing or changed target
72
+ * makes the whole request a safe append: no target is removed and no alias is
73
+ * claimed.
74
+ * @param entries - Current validated entries read inside the lock
75
+ * @param candidate - New validated content composed by the caller
76
+ * @param expected - Validated version stamps from the caller's snapshot
77
+ * @returns Deterministic append-or-replace plan
78
+ */
79
+ export function planLearningSupersede(entries, candidate, expected) {
80
+ const stale = expected.flatMap(stamp => staleTarget(entries, stamp));
81
+ const targetIds = new Set(expected.map(stamp => stamp.id));
82
+ const removed = sortLearningEntries(entries.filter(entry => targetIds.has(entry.id)));
83
+ const primary = removed[0];
84
+ const retained = entries.filter(entry => !targetIds.has(entry.id));
85
+ if (entries.some(entry => entry.fingerprint === candidate.fingerprint)) {
86
+ throw new Error(`Duplicate learning fingerprint: ${candidate.fingerprint}`);
87
+ }
88
+ if (stale.length > 0 || expected.length === 0) {
89
+ if (entries.some(entry => entry.id === candidate.id)) {
90
+ throw new Error(`Duplicate learning id: ${candidate.id}`);
91
+ }
92
+ return {
93
+ entry: candidate,
94
+ retained: entries,
95
+ removed: [],
96
+ stale,
97
+ };
98
+ }
99
+ if (primary === undefined) {
100
+ throw new Error("Invalid supersede plan: exact target set was empty");
101
+ }
102
+ return {
103
+ entry: validateLearningEntry({ ...candidate, id: primary.id }),
104
+ retained,
105
+ removed,
106
+ stale: [],
107
+ };
108
+ }
109
+ /**
110
+ * Validate one inert, exact two-field stamp.
111
+ * @param candidate - Untrusted stamp value
112
+ * @returns Frozen exact stamp
113
+ */
114
+ function validateStamp(candidate) {
115
+ if (candidate === null ||
116
+ typeof candidate !== "object" ||
117
+ Array.isArray(candidate)) {
118
+ throw new Error(EXACT_STAMP_ERROR);
119
+ }
120
+ const keys = Reflect.ownKeys(candidate);
121
+ if (keys.length !== 2 ||
122
+ keys.some(key => typeof key !== "string" || (key !== "id" && key !== "fingerprint")) ||
123
+ !Object.hasOwn(candidate, "id") ||
124
+ !Object.hasOwn(candidate, "fingerprint")) {
125
+ throw new Error(EXACT_STAMP_ERROR);
126
+ }
127
+ const idDescriptor = Object.getOwnPropertyDescriptor(candidate, "id");
128
+ const fingerprintDescriptor = Object.getOwnPropertyDescriptor(candidate, "fingerprint");
129
+ if (idDescriptor === undefined || fingerprintDescriptor === undefined) {
130
+ throw new Error(EXACT_STAMP_ERROR);
131
+ }
132
+ const id = readStampToken(idDescriptor, "id");
133
+ const fingerprint = readStampToken(fingerprintDescriptor, "fingerprint");
134
+ return Object.freeze({ id, fingerprint });
135
+ }
136
+ /**
137
+ * Read one accessor-free stable token from a stamp descriptor.
138
+ * @param descriptor - Own field descriptor
139
+ * @param field - Stamp field being validated
140
+ * @returns Validated stable token
141
+ */
142
+ function readStampToken(descriptor, field) {
143
+ if (!("value" in descriptor) || typeof descriptor.value !== "string") {
144
+ throw new Error(`Invalid supersede stamp ${field}: expected a data string`);
145
+ }
146
+ const value = descriptor.value;
147
+ if (Buffer.byteLength(value, "utf8") > MAX_STABLE_TOKEN_BYTES) {
148
+ throw new Error(`Invalid supersede stamp ${field}: ${field} exceeds max stable token bytes ${MAX_STABLE_TOKEN_BYTES}`);
149
+ }
150
+ if (value.trim() !== value || !STABLE_TOKEN.test(value)) {
151
+ throw new Error(`Invalid supersede stamp ${field}: expected a stable token`);
152
+ }
153
+ return value;
154
+ }
155
+ /**
156
+ * Describe a target that no longer matches its exact stamp.
157
+ * @param entries - Current entries inside the writer lock
158
+ * @param expected - Caller-observed target stamp
159
+ * @returns Empty for an exact match, otherwise one stale diagnosis
160
+ */
161
+ function staleTarget(entries, expected) {
162
+ const actual = entries.find(entry => entry.id === expected.id);
163
+ if (actual === undefined) {
164
+ return [{ expected, reason: "absent" }];
165
+ }
166
+ if (actual.fingerprint !== expected.fingerprint) {
167
+ return [
168
+ {
169
+ expected,
170
+ actualFingerprint: actual.fingerprint,
171
+ reason: "fingerprint-mismatch",
172
+ },
173
+ ];
174
+ }
175
+ return [];
176
+ }
177
+ /**
178
+ * Compare tokens for locale-independent primary selection.
179
+ * @param left - First token
180
+ * @param right - Second token
181
+ * @returns Negative, zero, or positive ordering result
182
+ */
183
+ function compareTokens(left, right) {
184
+ return left === right ? 0 : left < right ? -1 : 1;
185
+ }
186
+ /**
187
+ * Sort entries by stable id without mutating shared state.
188
+ * @param entries - Entries in arbitrary order
189
+ * @returns New deterministically ordered array
190
+ */
191
+ function sortLearningEntries(entries) {
192
+ return entries.reduce((ordered, entry) => {
193
+ const insertion = ordered.findIndex(current => compareTokens(entry.id, current.id) < 0);
194
+ return insertion === -1
195
+ ? [...ordered, entry]
196
+ : [...ordered.slice(0, insertion), entry, ...ordered.slice(insertion)];
197
+ }, []);
198
+ }
199
+ //# sourceMappingURL=learnings-supersede.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learnings-supersede.js","sourceRoot":"","sources":["../../src/core/learnings-supersede.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
+ import { type LearningEntryStamp, type StaleSupersedeTarget } from "./learnings-supersede.js";
1
2
  /** Write-time consolidation options for {@link persistConsolidatedLearning}. */
2
3
  export interface ConsolidatedLearningOptions {
3
- /** Ids of existing entries the new entry merges or replaces. */
4
- readonly supersede?: readonly string[];
4
+ /** Exact versions of existing entries the new entry merges or replaces. */
5
+ readonly supersede?: readonly LearningEntryStamp[];
6
+ /** Called when any target no longer matches its observed version stamp. */
7
+ readonly onStaleSupersede?: (targets: readonly StaleSupersedeTarget[]) => void;
5
8
  /**
6
- * Called when a supersede target was already absent at write time — almost
7
- * always because a concurrent pass consolidated it first. The write still
8
- * succeeds; this is a diagnostic, not a failure hook.
9
+ * Compatibility diagnostic for targets that are absent (not merely changed).
10
+ * @deprecated Use {@link onStaleSupersede}, which distinguishes both cases.
9
11
  */
10
12
  readonly onAbsentSupersede?: (ids: readonly string[]) => void;
11
13
  /**
@@ -21,7 +23,7 @@ export interface ConsolidatedLearningOptions {
21
23
  * entry point; consolidation-aware writers use
22
24
  * {@link persistConsolidatedLearning} instead.
23
25
  * @param projectRoot - Absolute path to the host project root
24
- * @param candidate - Untrusted seven-field learning entry
26
+ * @param candidate - Untrusted eight-field learning entry
25
27
  * @returns Absolute path to the persisted learnings file
26
28
  */
27
29
  export declare function persistLearningEntry(projectRoot: string, candidate: unknown): Promise<string>;
@@ -34,7 +36,7 @@ export declare function persistLearningEntry(projectRoot: string, candidate: unk
34
36
  * consolidation. Without `supersede` this is exactly the append-only
35
37
  * {@link persistLearningEntry} behavior, including the duplicate-id throw.
36
38
  * @param projectRoot - Absolute path to the host project root
37
- * @param candidate - Untrusted seven-field learning entry
39
+ * @param candidate - Untrusted eight-field learning entry
38
40
  * @param options - Optional consolidation directives
39
41
  * @returns Absolute path to the persisted learnings file
40
42
  */