@lmzhen/dsh-evolution-core 0.1.0-rc.68 → 0.1.0-rc.69

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.
package/lib/index.js CHANGED
@@ -67,11 +67,14 @@ function nodeEvolutionIo() {
67
67
  * the holder pid it carries (rc.66): a LIVE holder is never stolen, so a
68
68
  * slow writer no longer loses its lock to a peer at the 5s mark (the
69
69
  * takeover is the only best-effort surface; the retry budget fails loud —
70
- * rc.65 — instead of ever proceeding unlocked).
70
+ * rc.65 — instead of ever proceeding unlocked). Budget = 40 * 50ms (~2s,
71
+ * rc.69): 8-writer contention bursts on a loaded CI runner exceed 10
72
+ * attempts (500ms), and a fail-loud throw was observed instead of a clean
73
+ * serialization.
71
74
  */
72
75
  const withWriteLock = async (path, task) => {
73
76
  const lock = `${path}.lock`;
74
- for (let attempt = 0; attempt < 10; attempt += 1) try {
77
+ for (let attempt = 0; attempt < 40; attempt += 1) try {
75
78
  await writeFile(lock, String(process.pid), { flag: "wx" });
76
79
  try {
77
80
  return await task();
@@ -735,8 +738,13 @@ function eventsFile(home) {
735
738
  function isEventRecord(event) {
736
739
  return typeof event === "object" && event !== null && typeof event.seq === "number";
737
740
  }
738
- function parseEventList(raw) {
739
- if (raw === null) return [];
741
+ /**
742
+ * Parse an event log body. A missing file, a whitespace-only file (rc.69:
743
+ * rebuildable, NOT malformed) or a corrupt one reads as empty; corrupt content
744
+ * is still refused on append, never overwritten.
745
+ */
746
+ function parseEvolutionEvents(raw) {
747
+ if (raw === null || raw.trim() === "") return [];
740
748
  try {
741
749
  const parsed = JSON.parse(raw);
742
750
  if (!Array.isArray(parsed.events)) return [];
@@ -754,12 +762,12 @@ function parseEventList(raw) {
754
762
  async function appendEvolutionEvent(io, path, event) {
755
763
  let assigned = 0;
756
764
  await transactIo(io, path, (current) => {
757
- if (current !== null) try {
765
+ if (current !== null && current.trim() !== "") try {
758
766
  JSON.parse(current);
759
767
  } catch {
760
768
  return Promise.resolve(current);
761
769
  }
762
- const events = parseEventList(current);
770
+ const events = parseEvolutionEvents(current);
763
771
  const maxSeq = events.reduce((max, entry) => Math.max(max, entry.seq), 0);
764
772
  const record = {
765
773
  ...event,
@@ -775,10 +783,11 @@ async function appendEvolutionEvent(io, path, event) {
775
783
  if (assigned === 0) throw new Error(`evolution event log is malformed and was not touched: ${path}`);
776
784
  return assigned;
777
785
  }
778
- /** Read the event log; a missing file reads as empty, malformed is flagged. */
786
+ /** Read the event log; a missing/whitespace-only file reads as empty,
787
+ * corrupt content is flagged (and refused on append). */
779
788
  async function readEvolutionEvents(io, path) {
780
789
  const raw = await io.readText(path);
781
- if (raw === null) return {
790
+ if (raw === null || raw.trim() === "") return {
782
791
  events: [],
783
792
  malformed: false
784
793
  };
@@ -3116,4 +3125,4 @@ function evolutionHome(env = process.env) {
3116
3125
  return join(env.DSH_HOME ?? join(homedir(), ".dsh"), "evolution");
3117
3126
  }
3118
3127
  //#endregion
3119
- export { AUTHORING_DESCRIPTION_BAR, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EVENT_LOG_VERSION, EvolutionGateSet, LOW_QUALITY_THRESHOLD, MAX_DESCRIPTION_LENGTH, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MUTATIONS_FILE_VERSION, MemoryStore, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, QUALITY_WEIGHTS, SKILLS_GUIDANCE, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, advanceReview, appendEvolutionEvent, applyCuratorFields, authoringFeedback, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, computeDedupGroups, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, createGateSet, emptyRecord, evaluateThreat, eventsFile, evolutionHome, evolutionIoAdapter, foldCuratorFields, foldTurn, getRecord, latestActivityAt, lifecycleCandidate, loadMutations, loadSuppressedNames, loadUsage, markAgentCreated, memoryRoot, mutateUsage, mutationsFile, nodeEvolutionIo, normalizeUsageRecord, observeEvent, parseCuratorNominations, parseFrontmatter, readEvolutionEvents, recordMutation, relatedSkillNames, renderCuratorReportMarkdown, resolveOrigins, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, transactIo, updateSuppressedNames, usageFile, validateFrontmatter, verifyPromptBundle };
3128
+ export { AUTHORING_DESCRIPTION_BAR, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EVENT_LOG_VERSION, EvolutionGateSet, LOW_QUALITY_THRESHOLD, MAX_DESCRIPTION_LENGTH, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MUTATIONS_FILE_VERSION, MemoryStore, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, QUALITY_WEIGHTS, SKILLS_GUIDANCE, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, advanceReview, appendEvolutionEvent, applyCuratorFields, authoringFeedback, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, computeDedupGroups, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, createGateSet, emptyRecord, evaluateThreat, eventsFile, evolutionHome, evolutionIoAdapter, foldCuratorFields, foldTurn, getRecord, latestActivityAt, lifecycleCandidate, loadMutations, loadSuppressedNames, loadUsage, markAgentCreated, memoryRoot, mutateUsage, mutationsFile, nodeEvolutionIo, normalizeUsageRecord, observeEvent, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, readEvolutionEvents, recordMutation, relatedSkillNames, renderCuratorReportMarkdown, resolveOrigins, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, transactIo, updateSuppressedNames, usageFile, validateFrontmatter, verifyPromptBundle };
@@ -26,6 +26,12 @@ export interface EvolutionEvent {
26
26
  request?: string | undefined;
27
27
  }
28
28
  export declare function eventsFile(home: string): string;
29
+ /**
30
+ * Parse an event log body. A missing file, a whitespace-only file (rc.69:
31
+ * rebuildable, NOT malformed) or a corrupt one reads as empty; corrupt content
32
+ * is still refused on append, never overwritten.
33
+ */
34
+ export declare function parseEvolutionEvents(raw: string | null): EvolutionEvent[];
29
35
  /**
30
36
  * Append one event under the write lock (rc.68): `seq` = current max + 1
31
37
  * computed inside the transact, so two processes appending concurrently never
@@ -38,6 +44,7 @@ export interface EventLogRead {
38
44
  /** True when the file existed but could not be parsed (loss case; never overwritten). */
39
45
  malformed: boolean;
40
46
  }
41
- /** Read the event log; a missing file reads as empty, malformed is flagged. */
47
+ /** Read the event log; a missing/whitespace-only file reads as empty,
48
+ * corrupt content is flagged (and refused on append). */
42
49
  export declare function readEvolutionEvents(io: EvolutionIoLike, path: string): Promise<EventLogRead>;
43
50
  //# sourceMappingURL=evolution-events.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-core",
3
3
  "description": "Shared stores, prompts, signals and lifecycle logic for the dsh-evolution plugin family (community build)",
4
- "version": "0.1.0-rc.68",
4
+ "version": "0.1.0-rc.69",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },