@lmzhen/dsh-evolution-core 0.3.50 → 0.3.52

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.
@@ -68,23 +68,6 @@ export interface EvolutionEvent {
68
68
  } | undefined;
69
69
  }
70
70
  export declare function eventsFile(home: string): string;
71
- /**
72
- * Parse an event log body. A missing file, a whitespace-only file (rc.69:
73
- * rebuildable, NOT malformed) or a corrupt one reads as empty; corrupt content
74
- * is still refused on append, never overwritten.
75
- *
76
- * This reader is **v1-only** (F-338): a body carrying a `version` other than
77
- * `EVENT_LOG_VERSION` is a future-format log this reader cannot interpret, so
78
- * it reads as an EMPTY timeline rather than being mis-parsed as v1. The read
79
- * side never overwrites it on its own — `appendEvolutionEvent` rejects a
80
- * version mismatch up front and preserves the original bytes, so a newer log
81
- * is never silently downgraded here.
82
- *
83
- * Per-entry normalization (rc.70 F-1): entries without a numeric `seq` are
84
- * skipped here and dropped at the next append — valid entries survive, the
85
- * damaged record is the only loss (self-heal semantics, matching the usage
86
- * sidecar's per-field normalization on read).
87
- */
88
71
  export declare function parseEvolutionEvents(raw: string | null): EvolutionEvent[];
89
72
  /**
90
73
  * List the numeric archives under the log's directory, sorted ascending by
@@ -2,9 +2,11 @@
2
2
  * Shared library for the dsh-evolution plugin family.
3
3
  *
4
4
  * Pure stores, prompts, signals, lifecycle logic, threat scanning, IO seam
5
- * types, and session-event augmentations. This package owns no Cordis plugin
6
- * entry of its own; consumers import named exports from the package root so
7
- * published npm bundles never depend on source subpaths.
5
+ * types, and session-event augmentations. This package ships no runtime
6
+ * plugin beyond the invariant companion entry (`./invariant` the subpath
7
+ * the host auto-assembles to register this package's no-op invariant);
8
+ * consumers import named exports from the package root so published npm
9
+ * bundles never depend on source subpaths.
8
10
  * @module @lmzhen/dsh-evolution-core
9
11
  */
10
12
  export * from './curator.ts';
package/lib/types/io.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * Every evolution package passes `ctx.evolutionIo.provider()`; standalone
5
5
  * consumers (and the core's own tests) can use `nodeEvolutionIo`.
6
6
  */
7
+ import { open } from 'node:fs/promises';
7
8
  export interface EvolutionIoLike {
8
9
  readText(path: string): Promise<string | null>;
9
10
  writeText(path: string, content: string): Promise<void>;
@@ -42,16 +43,22 @@ export interface EvolutionIoLike {
42
43
  /**
43
44
  * Optional mtime-generation probe (0.3.18, E-71): the path's mtime in
44
45
  * milliseconds since epoch, or `null` when unknown (unsupported backend,
45
- * missing path, stat failure). Consumers use it as a cheap invalidation
46
- * stamp for a cached directory listing; a backend without it keeps
47
- * event-driven invalidation only.
46
+ * missing path, stat failure). Intended as a cheap invalidation stamp for a
47
+ * cached directory listing; a backend without it keeps event-driven
48
+ * invalidation only. V9-07 (0.3.51): as of this release NO in-tree consumer
49
+ * calls it — skill-catalog invalidation is event-driven
50
+ * (`evolution/skill-mutated` / `evolution/skills-refresh`). The probe stays
51
+ * as a backend contract extension point; document it here before wiring a
52
+ * consumer.
48
53
  */
49
54
  mtime?(this: void, path: string): Promise<number | null>;
50
55
  }
51
56
  /**
52
57
  * Run `task` inside `io.transact` when the backend provides it; otherwise fall
53
58
  * back to a plain read → task → write/remove sequence (no cross-process lock —
54
- * callers keep their single-process serialize chain as the second layer).
59
+ * callers keep their single-process serialize chain as the second layer). A
60
+ * byte-identical task result skips the write (C-07 — parity with the
61
+ * node backend's V5-03 short-circuit).
55
62
  */
56
63
  export declare function transactIo(io: EvolutionIoLike, path: string, task: (current: string | null) => string | null | Promise<string | null>): Promise<void>;
57
64
  /** Lazy adapter over an IO provider registry, shared by every evolution consumer. */
@@ -70,9 +77,12 @@ export declare function evolutionIoAdapter(provider: () => EvolutionIoLike): Evo
70
77
  export declare const pendingSelfCleanup: Map<string, string>;
71
78
  /**
72
79
  * Retry a rename that a peer is temporarily holding on Windows (EPERM/EBUSY):
73
- * a short 50ms backoff, at most 3 retries (~150ms budget), matching the
74
- * write-lock cadence. A non-transient code surfaces immediately. `fn` is the
75
- * rename primitive, injectable for deterministic tests.
80
+ * exponential backoff (50ms doubling, capped at 800ms) with a ~2s total
81
+ * budget, matching the write-lock cadence (C-28 the old 3x50ms
82
+ * budget turned a transient antivirus hold into a permanent write failure).
83
+ * A non-transient code surfaces immediately; a persistent EPERM/EBUSY
84
+ * rethrows with a pointer at the usual causes instead of a bare errno.
85
+ * `fn` is the rename primitive, injectable for deterministic tests.
76
86
  *
77
87
  * @param tmp - the source path to rename.
78
88
  * @param target - the destination path.
@@ -80,5 +90,28 @@ export declare const pendingSelfCleanup: Map<string, string>;
80
90
  * @returns a promise that resolves once the rename succeeds.
81
91
  */
82
92
  export declare function renameWithRetry(tmp: string, target: string, fn?: (from: string, to: string) => Promise<void>): Promise<void>;
83
- export declare function nodeEvolutionIo(): EvolutionIoLike;
93
+ /**
94
+ * V10-06 (P1-1): the crash-durable tmp half of the upstream storage-json
95
+ * `writeAtomic` protocol (storage-json/src/atomic.ts:24-40): exclusive-create
96
+ * a same-directory tmp (`wx` — never clobbers), write, `handle.sync()`,
97
+ * close, then hand the tmp to `commitTmp` for the rename. Without the fsync a
98
+ * power loss could land the rename (metadata) before the data blocks and
99
+ * leave an empty/truncated target — which the state-json E-9 quarantine then
100
+ * amplifies into a permanent fail-loud. The tmp name keeps the
101
+ * `<target>.<pid>.<rand>.tmp` shape so sweepStaleTmps keeps matching.
102
+ * Mode parity note: no explicit mode is passed (upstream uses 0o600) — this
103
+ * seam also writes operator-editable skill/memory files, so the previous
104
+ * `writeFile` default (0o666 & ~umask) is deliberately preserved.
105
+ * `openImpl` is injectable so the sync-failure regression test can drive a
106
+ * failing `handle.sync()` deterministically.
107
+ */
108
+ export declare function writeDurableTmp(target: string, content: string, openImpl?: typeof open): Promise<string>;
109
+ /**
110
+ * Build the Node IO backend. `lockAttempts` scales the write-lock retry budget
111
+ * (attempts × 50ms); the default 40 (~2s, rc.69) covers production contention,
112
+ * while contention TESTS on a loaded runner may raise it (e.g. 240 ≈ 12s) —
113
+ * V10-06 integration: full-suite parallel load made 32-way takeover bursts
114
+ * exceed the default budget and fail loud.
115
+ */
116
+ export declare function nodeEvolutionIo(lockAttempts?: number): EvolutionIoLike;
84
117
  //# sourceMappingURL=io.d.ts.map
@@ -25,6 +25,11 @@ export interface MemoryStoreOptions {
25
25
  root?: string;
26
26
  maxConsolidationFailures?: number;
27
27
  io?: EvolutionIoLike;
28
+ /** V10-03 (P2-18): deployment-declared benign pattern labels (ScanOptions.
29
+ * excludeLabels) applied to every threat check of this store (writes and
30
+ * the renderContext filter). Default empty — the strict ANY-hit-blocks
31
+ * policy is unchanged; deploy configs opt in. */
32
+ threatExemptLabels?: readonly string[];
28
33
  }
29
34
  export type { EvolutionIoLike };
30
35
  export declare class MemoryStore {
@@ -34,6 +39,8 @@ export declare class MemoryStore {
34
39
  readonly root: string;
35
40
  private readonly maxFailures;
36
41
  private readonly io;
42
+ /** V10-03 (P2-18): see MemoryStoreOptions.threatExemptLabels. */
43
+ private readonly threatExemptLabels;
37
44
  /** V6-16 (0.3.37): same-process RMW serialization (the SkillLibrary queue) —
38
45
  * on a backend WITHOUT a transact lock two concurrent callers compute on the
39
46
  * same old content and the last rename wins, silently dropping one op's
@@ -43,6 +50,12 @@ export declare class MemoryStore {
43
50
  private failureCount;
44
51
  private lastFailureAt;
45
52
  constructor(options?: MemoryStoreOptions);
53
+ /** V10-03 (P2-18): ScanOptions shared by every threat check of this store —
54
+ * the constructor's exempt labels, empty by default (behavior unchanged). */
55
+ private threatScanOptions;
56
+ /** V10-03 (P2-18): the strict-scan write gate. A block message names the hit
57
+ * label (scanMemoryThreats already embeds it) plus the self-heal hint. */
58
+ private memoryThreatBlock;
46
59
  limitFor(target: MemoryTarget): number;
47
60
  /**
48
61
  * Read-guard probe: `{ size, limit }` when the on-disk file exceeds
@@ -67,7 +80,12 @@ export declare class MemoryStore {
67
80
  * never loaded just to back it up. Failure to back up does not change the
68
81
  * refusal semantics. V8-23⑫ (0.3.49): ONE fixed backup name per target —
69
82
  * a fresh refusal overwrites it (the previous timestamped names accumulated
70
- * per drift incident with no retention policy).
83
+ * per drift incident with no retention policy). V9-08 (0.3.51) declares the
84
+ * two failure shapes: (1) the pre-copy remove of the previous `.bak` fails —
85
+ * harmless, because the copy contract is overwrite (`cp force`);
86
+ * (2) the copy itself fails (disk/backend) — returns `null` and the refusal
87
+ * message simply carries no backup suffix; the refusal semantics and the
88
+ * on-disk file are untouched either way.
71
89
  */
72
90
  private backupFile;
73
91
  /**
@@ -3,8 +3,8 @@
3
3
  * changes semantically: the bundle digest is the fail-closed signal for
4
4
  * review workers, so a stale id across deployments must be distinguishable.
5
5
  */
6
- export declare const PROMPT_BUNDLE_VERSION = 14;
7
- export declare const PROMPT_BUNDLE_ID = "dsh-evolution@14";
6
+ export declare const PROMPT_BUNDLE_VERSION = 15;
7
+ export declare const PROMPT_BUNDLE_ID = "dsh-evolution@15";
8
8
  export declare const MEMORY_REVIEW_PROMPT = "[Auto-review \u2014 Memory]\nReview the conversation above and consider saving to memory if appropriate.\n\nFocus on:\n1. Has the user revealed things about themselves \u2014 persona, desires, preferences, or personal details worth remembering?\n2. Has the user expressed expectations about how you should behave, their work style, or ways they want you to operate?\n\nIf something stands out, save it using the memory tool.\nIf nothing is worth saving, just say \"Nothing to save.\" and stop.";
9
9
  export declare const SKILL_REVIEW_PROMPT = "[Auto-review \u2014 Skills]\nReview the conversation above and update the skill library. Be ACTIVE \u2014 most sessions produce at least one skill update, even if small. A pass that does nothing is a missed learning opportunity, not a neutral outcome.\n\nTarget shape of the library: CLASS-LEVEL skills, each with a rich SKILL.md and a references/ directory for session-specific detail. Not a long flat list of narrow one-session-one-skill entries. This shapes HOW you update, not WHETHER you update.\n\nSignals to look for (any one of these warrants action):\n \u2022 User corrected your style, tone, format, legibility, or verbosity. Frustration signals like 'stop doing X', 'this is too verbose', 'don't format like this', 'why are you explaining', 'just give me the answer', 'you always do Y and I hate it', or an explicit 'remember this' are FIRST-CLASS skill signals, not just memory signals. Update the relevant skill(s) to embed the preference so the next session starts already knowing.\n \u2022 User corrected your workflow, approach, or sequence of steps. Encode the correction as a pitfall or explicit step in the skill that governs that class of task.\n \u2022 Non-trivial technique, fix, workaround, debugging path, or tool-usage pattern emerged that a future session would benefit from. Capture it.\n \u2022 A skill that got loaded or consulted this session turned out to be wrong, missing a step, or outdated. Patch it NOW.\n\nRead-before-write (enforced by this channel): update, patch, delete, or write support files ONLY into skills you loaded or read in THIS session \u2014 ops on unread skills are dropped; CREATE of a brand-new umbrella is the only exception.\n\nPreference order \u2014 prefer the earliest action that fits, but do pick one when a signal above fired:\n 1. UPDATE A CURRENTLY-LOADED SKILL. Look back through the conversation for skills the user loaded or you read. If any of them covers the territory of the new learning, PATCH that one first. It is the skill that was in play, so it's the right one to extend.\n 2. UPDATE AN EXISTING UMBRELLA. If no loaded skill fits but an existing class-level skill does, patch it. Add a subsection, a pitfall, or broaden a trigger.\n 3. ADD A SUPPORT FILE under an existing umbrella. Skills can be packaged with three kinds of support files \u2014 use the right directory per kind:\n \u2022 references/<topic>.md \u2014 session-specific detail (error transcripts, reproduction recipes, provider quirks) AND condensed knowledge banks: quoted research, API docs, external authoritative excerpts, or domain notes you found while working on the problem. Write it concise and for the value of the task, not as a full mirror of upstream docs.\n \u2022 templates/<name>.<ext> \u2014 starter files meant to be copied and modified (boilerplate configs, scaffolding, a known-good example the agent can reproduce with modifications).\n \u2022 scripts/<name>.<ext> \u2014 statically re-runnable actions the skill can invoke directly (verification scripts, fixture generators, deterministic probes, anything the agent should run rather than hand-type each time).\n Add support files via skill_manage action=write_file with file_path starting 'references/', 'templates/', or 'scripts/'. The umbrella's SKILL.md should gain a one-line pointer to any new support file so future agents know it exists.\n 4. RESTRUCTURE a loaded skill whose body grew log-like \u2014 rc/sha/date-dense sections, session-detail spirals, or a fat body with no support files. Use skill_manage action=restructure with restructure: [{\"heading\": \"<the exact ## heading text>\", \"to_file\": \"references/<topic>.md\"}] \u2014 the ENTIRE ## section (from that heading to the next heading) moves into the support file and its position becomes a pointer line. The skill's name and directory never change. Only propose headings that exist verbatim in the body; never invent one, and never restructure a healthy small skill.\n 5. CREATE A NEW CLASS-LEVEL UMBRELLA SKILL when no existing skill covers the class. The name MUST be at the class level. The name MUST NOT be a specific PR number, error string, feature codename, library-alone name, or 'fix-X / debug-Y / audit-Z-today' session artifact. If the proposed name only makes sense for today's task, it's wrong \u2014 fall back to (1), (2), or (3).\n\nUser-preference embedding (important): when the user expressed a style/format/workflow preference, the update belongs in the SKILL.md body, not just in memory. Memory captures 'who the user is and what the current situation and state of your operations are'; skills capture 'how to do this class of task for this user'. When they complain about how you handled a task, the skill that governs that task needs to carry the lesson.\n\nIf you notice two existing skills that overlap, note it in your reply \u2014 the background curator handles consolidation at scale.\n\nTwo-tier deposition discipline (DSH addition, same spirit as the umbrella rule): before writing, classify the knowledge:\n \u2022 PATTERN (reusable \u2014 symptom \u2192 mechanism \u2192 fix \u2192 verification, still valuable next session) belongs in the SKILL.md body.\n \u2022 LOG (one-off \u2014 commit SHAs, npm/profile states, what this release changed, this session's process narrative) belongs in a references/ file, never the body. Body density IS reuse rate. Keep new entries tight: a pattern fits in 2-8 physical lines; prefer changing the current-state pointer over appending history.\n\nProtected skills (DO NOT edit these):\n \u2022 Bundled skills (shipped with the platform).\n \u2022 Hub-installed skills (installed from a hub).\nPinned skills are read-only to THIS background review pass \u2014 the pinned write guard refuses background changes, so this pass may not update them. They also cannot be archived by any writer (the foreground included): remove the .pinned marker first. Foreground and delegated-subagent update/patch writes to pinned skills remain allowed.\nIf the only skills that need updating are protected, say 'Nothing to save.' and stop.\n\nDo NOT capture (these become persistent self-imposed constraints that bite you later when the environment changes):\n \u2022 Environment-dependent failures: missing binaries, fresh-install errors, post-migration path mismatches, 'command not found', unconfigured credentials, uninstalled packages. The user can fix these \u2014 they are not durable rules.\n \u2022 Negative claims about tools or features ('browser tools do not work', 'X tool is broken', 'cannot use Y'). These harden into refusals the agent cites against itself for months after the actual problem was fixed.\n \u2022 Session-specific transient errors that resolved before the conversation ended. If retrying worked, the lesson is the retry pattern, not the original failure.\n \u2022 One-off task narratives. A user asking 'summarize today's market' or 'analyze this PR' is not a class of work that warrants a skill.\n\nIf a tool failed because of setup state, capture the FIX (install command, config step, env var to set) under an existing setup or troubleshooting skill \u2014 never 'this tool does not work' as a standalone constraint.\n\n'Nothing to save.' is a real option but should NOT be the default. If the session ran smoothly with no corrections and produced no new technique, just say 'Nothing to save.' and stop. Otherwise, act.";
10
10
  export declare const COMBINED_REVIEW_PROMPT = "[Auto-review]\nReview the conversation above and update two things:\n\n**Memory**: who the user is. Did the user reveal persona, desires, preferences, personal details, or expectations about how you should behave? Save facts about the user and durable preferences with the memory tool.\n\n**Skills**: how to do this class of task. Be ACTIVE \u2014 most sessions produce at least one skill update. A pass that does nothing is a missed learning opportunity, not a neutral outcome.\n\nTarget shape of the skill library: CLASS-LEVEL skills with a rich SKILL.md and a references/ directory for session-specific detail. Not a long flat list of narrow one-session-one-skill entries.\n\nSignals that warrant a skill update (any one is enough):\n \u2022 User corrected your style, tone, format, legibility, verbosity, or approach. Frustration is a FIRST-CLASS skill signal, not just a memory signal. 'stop doing X', 'don't format like this', 'I hate when you Y' \u2014 embed the lesson in the skill that governs that task so the next session starts fixed.\n \u2022 Non-trivial technique, fix, workaround, or debugging path emerged.\n \u2022 A skill that was loaded or consulted turned out wrong, missing, or outdated \u2014 patch it now.\n\nRead-before-write (enforced by this channel): update, patch, delete, or write support files ONLY into skills you loaded or read in THIS session \u2014 ops on unread skills are dropped; CREATE of a brand-new umbrella is the only exception.\n\nPreference order for skills \u2014 pick the earliest that fits:\n 1. UPDATE A CURRENTLY-LOADED SKILL. Check what skills were loaded or read in the conversation. If one of them covers the learning, PATCH it first. It was in play; it's the right place.\n 2. UPDATE AN EXISTING UMBRELLA. Patch it.\n 3. ADD A SUPPORT FILE under an existing umbrella via skill_manage action=write_file. Three kinds: references/<topic>.md for session-specific detail OR condensed knowledge banks (quoted research, API docs excerpts, domain notes) written concise and task-focused; templates/<name>.<ext> for starter files meant to be copied and modified; scripts/<name>.<ext> for statically re-runnable actions (verification, fixture generators, probes). Add a one-line pointer in SKILL.md so future agents find them.\n 4. RESTRUCTURE a loaded skill whose body grew log-like (rc/sha/date-dense sections, session-detail spirals, fat body with no support files) via skill_manage action=restructure with restructure: [{\"heading\": \"<the exact ## heading text>\", \"to_file\": \"references/<topic>.md\"}] \u2014 the ENTIRE ## section moves into the support file and its position becomes a pointer line; the skill's name and directory never change. Only propose headings that exist verbatim in the body.\n 5. CREATE A NEW CLASS-LEVEL UMBRELLA when nothing exists. Name at the class level \u2014 NOT a PR number, error string, codename, library-alone name, or 'fix-X / debug-Y' session artifact. If the name only fits today's task, fall back to (1), (2), or (3).\n\nTwo-tier deposition discipline (DSH addition): classify before writing \u2014 PATTERN (symptom \u2192 mechanism \u2192 fix \u2192 verification) goes in the SKILL.md body; LOG (commit SHAs, npm/profile states, this release's change list, this session's narrative) goes in a references/ file. Body density IS reuse rate; a pattern fits in 2-8 physical lines.\n\nUser-preference embedding: when the user complains about how you handled a task, update the skill that governs that task \u2014 memory alone isn't enough. Memory says 'who the user is and what the current situation and state of your operations are'; skills say 'how to do this class of task for this user'. Both should carry user-preference lessons when relevant.\n\nIf you notice overlapping existing skills, mention it \u2014 the background curator handles consolidation.\n\nProtected skills (DO NOT edit these):\n \u2022 Bundled skills (shipped with the platform).\n \u2022 Hub-installed skills (installed from a hub).\nPinned skills are read-only to THIS background review pass \u2014 the pinned write guard refuses background changes, so this pass may not update them. They also cannot be archived by any writer (the foreground included): remove the .pinned marker first. Foreground and delegated-subagent update/patch writes to pinned skills remain allowed.\nIf the only skills that need updating are protected, say 'Nothing to save.' and stop.\n\nDo NOT capture as skills (these become persistent self-imposed constraints that bite you later when the environment changes):\n \u2022 Environment-dependent failures: missing binaries, fresh-install errors, post-migration path mismatches, 'command not found', unconfigured credentials, uninstalled packages. The user can fix these \u2014 they are not durable rules.\n \u2022 Negative claims about tools or features ('browser tools do not work', 'X tool is broken', 'cannot use Y'). These harden into refusals the agent cites against itself for months after the actual problem was fixed.\n \u2022 Session-specific transient errors that resolved before the conversation ended. If retrying worked, the lesson is the retry pattern, not the original failure.\n \u2022 One-off task narratives. A user asking 'summarize today's market' or 'analyze this PR' is not a class of work that warrants a skill.\n\nIf a tool failed because of setup state, capture the FIX (install command, config step, env var to set) under an existing setup or troubleshooting skill \u2014 never 'this tool does not work' as a standalone constraint.\n\nAct on whichever of the two dimensions has real signal. If genuinely nothing stands out on either, say 'Nothing to save.' and stop \u2014 but don't reach for that conclusion as a default.";
@@ -19,6 +19,18 @@ export declare const COMPLETION_SKILL_REVIEW_PROMPT = "[Auto-review \u2014 Skill
19
19
  * compare the two heads (011 mismatch protocol).
20
20
  */
21
21
  export declare const MAINTAIN_PROMPT = "<<<MAINTAIN_PROMPT v={bundle_version} sig={joint_signature}>>>\n\n## \u89D2\u8272\n\u4F60\u662F\u6280\u80FD\u5E93\u7684**\u5916\u90E8\u5BA1\u8BA1\u8005**\uFF1A\u53EA\u8BFB\u3001\u53EA\u8F93\u51FA\u8BA1\u5212\u3001\u4ECE\u4E0D\u6267\u884C\uFF08\u6267\u884C\u7531\u7528\u6237\u547D\u4EE4\u4E0E\u5BA1\u6279\u5B8C\u6210\uFF09\u3002\n\n## 1. \u8F93\u5165\u5951\u7EA6\uFF08\u51B2\u7A81\u65F6\u4EE5\u6B64\u4E3A\u51C6\uFF09\n\u673A\u68B0\u4E8B\u5B9E\u5757 <<<MECHANICAL_FACTS v={signals_version} sig={joint_signature}>>>\uFF08\u4E0B\u65B9\uFF0C\u4EE5 <<<END FACTS>>> \u95ED\u5408\uFF09\u662F\u552F\u4E00\u8BC1\u636E\u6765\u6E90\u3002\n- verdict \u4EC5\u4E09\u503C\uFF1Apass=\u672A\u8D8A\u9608 / over=\u8D8A\u9608 / unknown=\u672A\u68C0\u6D4B\u3002\n- over \u662F\u4E8B\u5B9E\u4F4D\u7F6E\uFF0C\u4E0D\u662F\u8FDD\u89C4\u7ED3\u8BBA\uFF1B\u6CA1\u6709\u6761\u6B3E\u5BF9\u5E94\u7684\u4E8B\u5B9E\uFF0C\u4E0D\u4EA7\u751F\u5EFA\u8BAE\u3002\n- unknown \u2260 pass\uFF1B\u5F15\u7528 unknown \u4FE1\u53F7\u7684\u6761\u76EE\u5FC5\u987B needs_human:true\u3002\n- \u4E8B\u5B9E\u53EA\u8BFB\uFF1A\u4E0D\u6539\u5199\u3001\u4E0D\u8865\u5199\u3001\u4E0D\u628A\u4E8B\u5B9E\"\u7FFB\u8BD1\"\u6210\u88C1\u51B3\u3002\n- \u4E24\u5904 sig \u4E0D\u4E00\u81F4\u6216\u4EFB\u4E00\u7F3A\u5931 \u2192 \u53EA\u8F93\u51FA MISMATCH + \u4E24\u4FA7\u7248\u672C\u53F7\uFF0C\u7981\u6B62\u8F93\u51FA\u8BA1\u5212\u3002\n\n## 2. \u4FE1\u53F7\u2192\u6761\u6B3E\u6620\u5C04\uFF08\u6BCF\u6761 over \u5FC5\u987B\u843D\u5230\u6761\u6B3E\uFF1B\u65E0\u4E00\u9057\u6F0F\uFF09\n{signal:dedup_group}\u2192A1 \u00B7 {signal:narrow_name}\u2192A2 \u00B7 {signal:prefix_cluster}\u2192A3 \u00B7 {signal:stamp_density}\u4E0E{signal:body_size}\u2192B1 \u00B7 {signal:pointer_missing}\u2192B2 \u00B7 {signal:dup_heading}\u2192B3 \u00B7 {signal:overlong_line}\u2192B4 \u00B7 {signal:description_chars}\u2192B5 \u00B7 {signal:usage_observed}/{signal:quality_low}\u2192\u95E8\u63A7\uFF08\u6821\u9A8C\u5668\u5BF9 quality_low=unknown \u7684\u6280\u80FD\u5F3A\u5236 needs_human\uFF0C\u6A21\u677F\u4FA7\u4E0D\u91CD\u590D\uFF09\n\n## 3. \u5B8C\u6574\u6027\u5951\u7EA6\uFF08\u6821\u9A8C\u5668\u673A\u68B0\u6267\u884C\uFF09\n\u4E8B\u5B9E\u5757\u4E2D\u6BCF\u6761 over \u4FE1\u53F7\u5FC5\u987B\u6EE1\u8DB3\u5176\u4E00\uFF1A\u6210\u4E3A\u67D0\u6761\u5EFA\u8BAE\u7684 evidence\uFF0C\u6216\u5728 notes \u4E2D\u8BF4\u660E\"\u5DF2\u5BA1\u00B7\u65E0\u6761\u6B3E\u5BF9\u5E94\u00B7\u4E0D\u52A8\u4F5C\"\u3002\u7981\u6B62\u9759\u9ED8\u7701\u7565\uFF1B\u5148\u9010\u4FE1\u53F7\u6838\u5BF9\u518D\u8F93\u51FA\u3002\n\n## 4. \u5DE5\u4F5C\u6D41\u7A0B\uFF08\u6309\u5E8F\u6267\u884C\uFF0C\u4E0D\u5F97\u8DF3\u6B65\uFF09\n\u2460 \u901A\u8BFB\u4E8B\u5B9E\u5757 \u2192 \u2461 \u5BF9\u6BCF\u4E2A\u5019\u9009\u6280\u80FD\u7528 skill \u8BFB\u6B63\u6587\uFF08B1/B2/B4 \u5FC5\u8BFB\uFF1B**\u8BFB\u53D6\u5931\u8D25\u5FC5\u987B\u62A5\u544A\u5DE5\u5177\u8FD4\u56DE\u7684\u4E8B\u5B9E**\uFF08\u9519\u8BEF\u4FE1\u606F/\u65E0\u5BF9\u5E94\u6761\u76EE\uFF09\uFF0C\u7981\u6B62\u7528\"\u65E0\u6CD5\u8BFB\u53D6\"\u542B\u7CCA\u7ED5\u8FC7\uFF09\u2192 \u2462 maintenance_probe \u6309\u9700\u6DF1\u6316 \u2192 \u2463 \u9010\u4FE1\u53F7\u8FC7 \u00A73 \u5B8C\u6574\u6027 \u2192 \u2464 \u8F93\u51FA\u8BA1\u5212\u3002\n\n## 5. \u68C0\u67E5\u6E05\u5355\uFF08\u4FE1\u53F7 \u2192 \u8BED\u4E49\u5224\u5B9A \u2192 \u8F93\u51FA\u5F62\u6001\uFF09\nA. \u57DF\u00B7\u788E\u7247\u5316\n- A1 {signal:dedup_group}=over\uFF1A\u5224\u8FD1\u91CD\u590D\u7EC4\u662F\u5426\u540C\u4F1E\u53EF\u5408\u5E76\uFF1B\u662F\u2192relationship-level consolidate\uFF1B\u5426\u2192\u4E0D\u8F93\u51FA\u3002\n- A2 {signal:narrow_name}=over\uFF1A\u5224\u5426\"\u4EC5\u5BF9\u4ECA\u65E5\u4EFB\u52A1\u6210\u7ACB\"\uFF1B\u6210\u7ACB\u2192\u6539\u540D/\u5F52\u6863\u5EFA\u8BAE\uFF1B\u5185\u90E8\u4EE3\u53F7\uFF08\u683C\u5F0F\u5408\u89C4\u8BED\u4E49\u7A84\uFF09\u2192conf\u22640.4+needs_human\u3002\n- A3 {signal:prefix_cluster}=over\uFF1A\u5224\u7C07\u5185\u662F\u5426\u540C\u4F1E\uFF1B\u975E\u540C\u4F1E\u2192notes \u63D0\u57DF\u5212\u5206\u89C2\u5BDF\uFF0C\u4E0D\u5F3A\u5236\u5EFA\u4F1E\u3002\n\nB. \u5C42\u00B7\u5206\u5C42\u9519\u4F4D\n- B1 {signal:stamp_density}\uFF08\u9608\u503C {signal:stamp_density.threshold}\uFF09\u6216 {signal:body_size}\uFF08\u9608\u503C {signal:body_size.threshold}\uFF09=over\uFF1A\u6309**\u4E09\u95EE\u5224\u636E**\u5224\u951A/\u6B8B\u7559\u2014\u2014\u2460 \u8BE5\u7F16\u53F7/\u65F6\u95F4\u6233\u662F\u5426\u88AB\u5E93\u5185\u5176\u4ED6\u6587\u4EF6\u5F15\u7528\uFF1F\u2461 \u9664\"\u4F55\u65F6\u4EA7\u751F/\u4E3A\u4F55\u5B58\u5728\"\u5916\u662F\u5426\u8FD8\u627F\u8F7D\u4FE1\u606F\uFF1F\u2462 \u5220\u9664\u662F\u5426\u5F71\u54CD\u4EFB\u4F55\u8DE8\u6587\u6863\u68C0\u7D22\uFF1F\uFF08\u2460\u662F\u4E14\u2462\u662F\u2192\u951A\uFF1B\u5426\u5219\u2192\u6B8B\u7559\u5019\u9009\uFF0C\u4EBA\u5BA1\uFF09\u3002\u951A\u2192\u5141\u8BB8\u4FDD\u7559 + needs_human + semantic_reasoning \u5199\u4E09\u95EE\u7ED3\u679C\uFF1B**\u951A\u4E0D\u4F7F\u7528 is_override**\uFF08is_override \u4EC5\u7528\u4E8E \u00A77 \u7533\u8BC9\uFF1B\u951A\u662F B1 \u7684\u6B63\u5E38\u88C1\u51B3\u8DEF\u5F84\uFF09\uFF1B\u6B8B\u7559\u2192restructure \u5EFA\u8BAE\uFF08movable headings \u9010\u5B57\u5F15\u7528\uFF09\u3002**\u951A\u2260\u53EF\u8BFB\uFF1A\u5355\u884C >4000 \u5B57\u7B26\u5373\u4F7F\u5728\u951A\u7C7B\u4E5F\u5FC5\u987B\u62C6\u5206\u3002**\n- B2 {signal:pointer_missing}=over\uFF1A\u8BFB\u652F\u6301\u6587\u4EF6\u540E\u5224\u6027\u8D28\u2014\u2014\u53EF\u590D\u7528\u6A21\u5F0F\u2192\u4E0A\u79FB\u6B63\u6587\uFF1B\u4F1A\u8BDD\u4E13\u5C5E\u5B9E\u5F55\u2192\u4FDD\u7559+\u8865\u6307\u9488\uFF1B\u5F62\u6001=patch \u6307\u5F15\u3002**\u672A\u8BFB\u5185\u5BB9\u4EC5\u51ED\u6587\u4EF6\u540D \u2192 conf\u22640.4 \u4E14\u63AA\u8F9E\"\u5148\u4EBA\u5DE5\u786E\u8BA4\u518D\u6267\u884C\"\u3002** **\u7F3A\u5931\u6307\u9488=\u652F\u6301\u6587\u4EF6\u5B58\u5728\u3001\u6B63\u6587\u65E0\u5F15\u7528\uFF08\u5355\u5411\u8BED\u4E49\uFF09\uFF0Cfinding \u8868\u8FF0\u52FF\u53CD\u5411\u3002**\n- B3 {signal:dup_heading}=over\uFF1A\u5220\u9664\u591A\u4F59\u6807\u9898\u884C\uFF08\u4FDD\u7559\u4E00\u4EFD\uFF09\uFF0Cpatch \u6307\u5F15\u3002\n- B4 {signal:overlong_line}=over\uFF1A>1500 \u62C6\u884C\uFF1B>4000 \u5224\u5B9A\u53EF\u8BFB\u6027\u5371\u673A\uFF08\u5185\u5BB9\u5408\u6CD5\u4E5F\u62C6\uFF09\uFF1Bpatch \u6307\u5F15\u3002**finding \u5FC5\u987B\u7ED9\u5168\u91CF\u53E3\u5F84\uFF1A\u5171 N \u884C\u8D85\u9650\uFF0C\u5176\u4E2D >4000 \u7684\u9010\u884C\u5217\u51FA\u3002**\n- B5 {signal:description_chars}=over\uFF1A\u5148\u5224**\u6027\u8D28**\u4E09\u5206\u7C7B\u2014\u2014\u4E8B\u4EF6\u6027\u627F\u8BFA\uFF08\u5355\u6B21\u6545\u969C/incident \u5199\u5165\u5143\u6570\u636E\uFF09\u2192\u88C1\u526A\u5EFA\u8BAE\uFF1B\u53D9\u4E8B\u6027\u81EA\u6211\u63CF\u8FF0\u2192\u538B\u7F29\u5EFA\u8BAE\uFF1B\u4E30\u5BCC\u4F46\u5408\u89C4\uFF08\u5B8C\u6574\u7528\u4F8B\u8FB9\u754C\uFF09\u2192\u4FDD\u7559 + is_override + override_reason=\"\u5408\u6CD5\u5BC6\u5EA6\"\u3002**\u5206\u7C7B\u7279\u5F81**\uFF1A\u542B\"\u6062\u590D/\u4FEE\u590D\u67D0\u6B21\u4E8B\u6545\u3001\u65E5\u671F\u5FEB\u7167\"\u7C7B\u4E00\u6B21\u6027\u63AA\u8F9E\u2192\u4E8B\u4EF6\u6027\u627F\u8BFA\uFF1B\"\u52A8\u8BCD+\u5BF9\u8C61\"\u5F0F\u4EFB\u52A1\u8BF4\u660E\u2192\u53D9\u4E8B\u6027\uFF1B\u679A\u4E3E\u5B8C\u6574\u7528\u4F8B\u8FB9\u754C\u4E14\u4E0D\u53EF\u62C6\u5206\u2192\u4E30\u5BCC\u5408\u89C4\u3002**\u7B2C\u4E09\u7C7B\u95E8\u69DB\uFF08\u9ED8\u8BA4\u4ECE\u4E25\uFF0C\u534A\u673A\u68B0\uFF09**\uFF1A\u5148\u81EA\u884C\u8BD5\u5199\u4E00\u4E2A \u226460 \u5B57\u538B\u7F29\u65B9\u6848\u2014\u2014\u80FD\u4FDD\u7559\u5168\u90E8\u8DEF\u7531\u5173\u952E\u9879\uFF08\u89E6\u53D1\u8BCD+\u57DF\uFF09\u2192 \u4E0D\u53EF\u5224\u7B2C\u4E09\u7C7B\uFF08\u6309\u538B\u7F29\u5EFA\u8BAE\uFF09\uFF1B\u53EA\u6709\u8BD5\u5199\u5931\u8D25\uFF08\u5728 semantic_reasoning \u5217\u51FA\u8BD5\u5199\u65B9\u6848\u4E0E\u5177\u4F53\u5931\u8D25\u70B9\uFF09\u624D\u53EF\u5224\u4E30\u5BCC\u5408\u89C4\u3002\u63CF\u8FF0\u6587\u672C\u53EF\u89C1\uFF08probe desc-text \u6216\u6B63\u6587 frontmatter\uFF09\u65F6\u4ECD\u987B\u4E09\u5206\u7C7B\uFF1B\u4EC5\u957F\u5EA6\u53EF\u89C1 \u2192 conf\u22640.4\u3002semantic_reasoning \u5FC5\u5199\u4E09\u5206\u7C7B\u4E4B\u4E00\u3002\n\nD. \u5E93\u00B7\u6574\u5408\u7EAA\u5F8B\uFF08\u8BA1\u5212\u5F62\u6001\u7EA6\u675F\uFF09\n- D1 \u540C\u7C7B\u95EE\u9898\u591A\u5904\u51FA\u73B0\u2192\u5408\u6210\u4E00\u6761 relationship-level \u5EFA\u8BAE\uFF0C\u4E0D\u9010\u9879\u8F93\u51FA\u3002\n- D2 \u7ED3\u6784\u7C7B\u4F18\u5148\u7EA7\u9AD8\u4E8E\u5185\u5BB9\u7C7B\uFF1B\u5F71\u54CD\u9762 library-level > relationship-level > skill-level\u3002\n\n## 6. \u8F93\u51FA\u5951\u7EA6\uFF08\u6821\u9A8C\u5668\u673A\u68B0\u6267\u884C\uFF09\n{verdict: \"issues\" | \"no_issues\",\n plan: [{ kind: \"skill-level\"|\"relationship-level\"|\"library-level\", names: [str],\n rule: \"A1\"|\"B2\"|..., evidence: [{signal, value}],\n finding: \"<\u4E00\u53E5\u4E8B\u5B9E\u63CF\u8FF0\uFF1A\u5F15\u7528\u4FE1\u53F7 id \u4E0E\u503C\uFF1B\u96F6\u88C1\u51B3\u52A8\u8BCD>\",\n recommendation: \"<\u552F\u4E00\u5141\u8BB8\u7684'\u5E94'\u53E5\uFF1A\u5EFA\u8BAE\u52A8\u4F5C+\u7406\u7531+\u6267\u884C\u5F62\u6001\uFF08\u547D\u4EE4/patch \u6307\u5F15\uFF09>\",\n semantic_reasoning: \"<\u8BED\u4E49\u5224\u636E\uFF1B\u542B LLM \u63A8\u65AD\u65F6 confidence\u22640.4>\",\n impact: \"better|worse|neutral\", impact_reason: \"<\u76F8\u5BF9'\u4E0D\u52A8'\u7684\u51C0\u5F71\u54CD>\",\n reversibility: \"archive|restructure|patch|rename|none\", undo_path: \"<\u4E00\u6B65\u64A4\u9500\u65B9\u5F0F>\",\n confidence: float, needs_human: bool, is_override: bool,\n override_reason: \"<\u4EC5 is_override>\" }],\n notes: [str]}\n- verdict=no_issues \u21D2 plan=[]\uFF08\u4E0D\u5141\u8BB8\u7A7A plan \u4E4B\u5916\u7684\"\u65E0\u95EE\u9898\"\u8868\u8FF0\uFF09\u3002\n- **confidence \u964D\u6863\u89C4\u5219\uFF08\u673A\u68B0\uFF09**\uFF1A\u6761\u6B3E\u5168\u90E8\u7531\u673A\u68B0\u8BC1\u636E\u652F\u6491 \u2192 0.6\u20130.9\uFF1B\u6BCF\u542B\u4E00\u9879\u8BED\u4E49\u63A8\u65AD\uFF08\u662F\u5426\u951A/\u662F\u5426\u540C\u4F1E/\u6027\u8D28\u5F52\u7C7B\uFF09\u2192 \u4E0A\u9650 0.4\u3002\n- needs_human = (confidence < 0.6) OR (\u4E0D\u53EF\u9006) OR (is_override) OR (\u5F15\u7528 unknown \u4FE1\u53F7)\u3002\n- \u8BED\u8A00\uFF1Afinding/recommendation/notes \u4E0E\u5E93\u6B63\u6587\u8BED\u8A00\u4E00\u81F4\uFF08\u4E0D\u81EA\u8BA2\u8BED\u8A00\uFF09\uFF1B\u5B57\u6BB5\u540D/\u4FE1\u53F7 id/\u679A\u4E3E\u4FDD\u7559\u82F1\u6587\u3002\n- **\u63D0\u4EA4\u524D\u81EA\u67E5\uFF08\u9010\u9879\u5BF9\u7167\uFF0C\u4E0D\u8BB8\u8DF3\u8FC7\uFF09**\uFF1A\u2460 verdict \u4E0E plan \u4E00\u81F4 \u2461 \u6BCF\u6761 evidence \u5728\u4E8B\u5B9E\u5757 \u2462 undo_path \u975E\u7A7A\uFF08\u4E0D\u53EF\u9006=n/a\uFF09\u2463 confidence \u542B\u63A8\u65AD\u22640.4 \u2464 finding \u65E0\"\u5E94\"\u5B57 \u2465 \u00A73 \u5B8C\u6574\u6027\u5951\u7EA6\u6EE1\u8DB3\u3002\n\n## 7. \u88C1\u51B3\u7EAA\u5F8B\n- finding \u7981\u6B62\"\u5E94\u5F53\"\u53E5\u5F0F\uFF1Brecommendation \u662F\u552F\u4E00\"\u5E94\"\u53E5\uFF0C\u53E5\u677F\uFF1A\u5EFA\u8BAE\u5BF9 {names} \u6267\u884C {\u52A8\u4F5C}\uFF08\u5F62\u6001\uFF1A{\u547D\u4EE4/patch \u6307\u5F15}\uFF09\uFF0C\u7406\u7531\uFF1A{\u7406\u7531}\u3002\n- **\u5BA1\u67E5\u8005\u89C6\u89D2**\uFF1A\u5148\u5BF9\u6BCF\u4E2A\u4FE1\u53F7\u72EC\u7ACB\u521D\u5224\uFF0C\u518D\u4E0E\u6B63\u6587\u5BF9\u7167\uFF1B\u88AB\u5BA1\u5BF9\u8C61\u7684\u81EA\u6211\u58F0\u660E\u53EA\u4F5C\u7EBF\u7D22\u4E0D\u4F5C\u4F9D\u636E\uFF1B**\u81EA\u5C5E/\u7EF4\u62A4\u8005\u6280\u80FD\u4E00\u5F8B\u4ECE\u4E25\u53E3\u5F84**\uFF08\u4F5C\u8005\u58F0\u660E\"\u8FD9\u662F\u951A\"\u4E0D\u6784\u6210\u8C41\u514D\uFF09\u3002\n- \u7533\u8BC9\uFF1A\u673A\u68B0\u9608\u503C\u4E0E\u8BED\u4E49\u5224\u65AD\u51B2\u7A81\u2192is_override:true + override_reason + needs_human:true\uFF0C\u4E0D\u5F97\u9759\u9ED8\u7ED5\u8FC7\u3002\n- \u4E0D\u52A8\u4F5C\u5408\u6CD5\uFF1Averdict=no_issues \u662F\u5408\u6CD5\u8F93\u51FA\uFF1B\u8FDE\u7EED\u7A7A\u62A5\u544A=\u4FE1\u53F7\u5B9A\u4E49\u95EE\u9898\uFF0C\u4E0D\u662F\"\u66F4\u79EF\u6781\"\u7684\u7406\u7531\u3002\n- \u9519\u8BEF\u6210\u672C\uFF1Arename \u5FC5\u987B needs_human:true\uFF1B\u53EF\u9006\u52A8\u4F5C\uFF08archive/restructure \u4E24\u9636\u6BB5\uFF09\u53EF needs_human:false \u4F46 undo_path \u5FC5\u586B\u3002\n- \u4E0D\u505A\uFF1A\u4E0D\u5EFA\u8BAE\u5220\u9664\uFF08\u53EA\u5EFA\u8BAE archive\uFF09\uFF1B\u4E0D\u63D0\u5347\u5185\u5BB9\u8D28\u91CF\uFF08\u7ED3\u6784\u5BA1\u67E5\u53EA flag \u4F4D\u7F6E/\u5F52\u5C5E/\u5206\u5C42\uFF09\uFF1Bprotected \u96C6\uFF08bundled/hub/pinned\uFF09\u5185 0 \u5EFA\u8BAE\u3002\n\n## 8. \u6CDB\u5316\n- \u4FE1\u53F7\u96C6\u5F00\u653E\uFF1A\u4E8B\u5B9E\u5757\u542B\u3001\u00A75 \u672A\u5217\u7684\u4FE1\u53F7 \u2192 notes \u63D0\"\u8BE5\u4FE1\u53F7\u503C\u5F97\u65B0\u589E\u6761\u6B3E\"\uFF0C\u7981\u6B62\u89E3\u91CA\u4E3A\u5DF2\u77E5\u95EE\u9898\u3002\n- \u5E93\u89C4\u6A21\u65E0\u5173\uFF1A\u5224\u636E\u662F\u4E8B\u5B9E\u4E0E\u6761\u6B3E\uFF0C\u4E0D\u662F\u5E93\u4F53\u91CF\u5370\u8C61\u3002\n- \u4FE1\u53F7\u673A\u5236\u7591\u95EE\uFF08\u9608\u503C/\u68C0\u6D4B\u539F\u7406\uFF09\u2192 needs_human\uFF0C\u4E0D\u731C\u6D4B\u673A\u5236\u3002";
22
+ /**
23
+ * One-line output instruction appended after the facts block in the maintain
24
+ * subagent's prompt (persona carries the template, the prompt carries facts +
25
+ * this instruction — one copy of the template in the model input, 011 v11
26
+ * P3-4). F-16: this text used to be hardcoded in evolution-maintenance
27
+ * orchestrate — a second model-facing prompt living OUTSIDE the bundle digest.
28
+ * It now rides PROMPT_BUNDLE so the digest integrity check covers every
29
+ * maintenance prompt. Adding the entry changes the bundle digest (the intended
30
+ * fail-closed signal); PROMPT_BUNDLE_VERSION itself is owned by the core test
31
+ * pin and stays untouched in this batch.
32
+ */
33
+ export declare const MAINTAIN_OUTPUT_INSTRUCTION = "\u6309\u6A21\u677F\u5951\u7EA6\u8F93\u51FA JSON \u7EF4\u62A4\u8BA1\u5212\uFF08verdict/plan/notes\uFF09\uFF1B\u9664 skill \u5DE5\u5177\u4E0E\u7EF4\u62A4\u6A21\u677F\u5916\u4F60\u65E0\u5176\u4ED6\u5DE5\u5177\u3002";
22
34
  /**
23
35
  * System-prompt guidance section (Hermes `SKILLS_GUIDANCE`, DSH-adapted).
24
36
  * Registered as a system-prompt section by tool-skill-manage (it mounts
@@ -2,7 +2,7 @@
2
2
  * Deterministic review signal gate.
3
3
  *
4
4
  * Scans a DSH session event log for durable learning signals before any LLM
5
- * is spent. `turn/end` calls `observeTurn`; the returned review kind is
5
+ * is spent. `turn/end` calls `observeEvent`; the returned review kind is
6
6
  * accumulated until a configured interval fires.
7
7
  */
8
8
  import type { Session, SessionEvent } from '@deepseek-ai/dsh-session';
@@ -31,6 +31,12 @@ export declare const DEFAULT_HEALTH_THRESHOLDS: SkillHealthThresholds;
31
31
  * (`defaced`, `feedback`) are not counted as commit shas; the ISO branch
32
32
  * accepts a UTC `Z`, a numeric UTC offset (`+08:00`), or no timezone at all —
33
33
  * non-UTC timestamps used to escape detection (log-like content missed).
34
+ *
35
+ * Known boundary, recorded not fixed (C-22, v10 audit): the hex branch also
36
+ * matches 7-40 char PURE-DIGIT strings (order numbers etc.), so stamp density
37
+ * can be over-reported for such content. The misjudgment direction is "extra
38
+ * stamp hits" only — never a miss — and no real incident exists; revisit only
39
+ * if curator decisions are actually distorted (v10 deferral ledger).
34
40
  */
35
41
  export declare const HEALTH_STAMP_RE: RegExp;
36
42
  /**
@@ -188,6 +188,13 @@ export interface FrontmatterNormalizeResult {
188
188
  * rewritten block no longer parses, or a rewritten value's parsed content
189
189
  * differs from the original, the rewrite is rolled back and reported in
190
190
  * `issues` (fail-loud, never a silent value corruption — P3-4).
191
+ *
192
+ * V10-02 (P2-3): the rewrite decision is PER LINE — each entry parses its own
193
+ * value, so a duplicated key can never route one entry's unsafe value into a
194
+ * different line's rewrite (the old key→Map lookup rewrote the FIRST (safe)
195
+ * line with the SECOND line's quoted value, and the last-wins YAML reader
196
+ * masked the damage). A duplicated key is itself invalid input and is
197
+ * reported in `issues` (the write path refuses) instead of being rewritten.
191
198
  */
192
199
  export declare function normalizeFrontmatter(content: string): FrontmatterNormalizeResult;
193
200
  /**
@@ -236,7 +243,10 @@ export declare class SkillLibrary {
236
243
  * one skill never interleave their read-modify-write (the cross-process layer
237
244
  * is the IO backend's transact lock; this chain is the second layer). */
238
245
  private readonly serial;
239
- constructor(root?: string, io?: EvolutionIoLike, limits?: SkillLimits, onMutation?: (event: EvolutionSkillMutatedEvent) => void, transact?: typeof transactIo);
246
+ /** V10-03 (P2-18): see the constructor's threatExemptLabels. Empty by
247
+ * default — the strict ANY-hit-blocks policy is unchanged. */
248
+ private readonly threatExemptLabels;
249
+ constructor(root?: string, io?: EvolutionIoLike, limits?: SkillLimits, onMutation?: (event: EvolutionSkillMutatedEvent) => void, transact?: typeof transactIo, threatExemptLabels?: readonly string[]);
240
250
  /**
241
251
  * Run one single-file read-modify-write for a mutator. When `transact` was
242
252
  * injected the read and the write run inside it (cross-process atomicity);
@@ -249,6 +259,13 @@ export declare class SkillLibrary {
249
259
  private runSingleWrite;
250
260
  /** Notify the mutation observer after a successful write; observers must never fail the mutation. */
251
261
  private notifyMutation;
262
+ /** V10-03 (P2-18): ScanOptions shared by every write-path threat check —
263
+ * the constructor's exempt labels, empty by default (behavior unchanged). */
264
+ private threatScanOptions;
265
+ /** V10-03 (P2-18): the strict-scan write gate. A block message names the hit
266
+ * label (scanContentThreats already embeds it) plus the self-heal hint, so a
267
+ * false-positive rewrite direction is actionable instead of a dead end. */
268
+ private contentThreatBlock;
252
269
  list(): Promise<SkillSummary[]>;
253
270
  read(rawName: string): Promise<string | null>;
254
271
  /**
@@ -267,7 +284,7 @@ export declare class SkillLibrary {
267
284
  private badName;
268
285
  writeProtection(rawName: string, origin?: WriteOrigin): Promise<string | null>;
269
286
  deleteProtection(rawName: string, options?: {
270
- allowBundled?: boolean;
287
+ allowBundled?: boolean | undefined;
271
288
  }): Promise<string | null>;
272
289
  isManaged(rawName: string): Promise<boolean>;
273
290
  /** Whether the skill carries the bundled marker (curator prune-builtins eligibility). */
@@ -1,6 +1,14 @@
1
1
  /**
2
- * Evolution home path helper: `$DSH_HOME/evolution` for plugin-owned sidecar
3
- * state (reports, activity store, feedback file, state-domain data).
2
+ * Evolution home path helpers: the DSH root and `$DSH_HOME/evolution` for
3
+ * plugin-owned sidecar state (reports, activity store, feedback file,
4
+ * state-domain data).
5
+ *
6
+ * C-10: PATH HELPERS ONLY — despite the file name there is no store
7
+ * here. Durable evolution state lives in the state stack (evolution-state over
8
+ * evolution-state-json / -domain); skills and memories live in skill-store.ts
9
+ * / memory-store.ts. The file name is kept deliberately: renaming it would
10
+ * touch every family import for zero behavior change, and the audit records
11
+ * the mismatch as known naming debt.
4
12
  */
5
13
  /**
6
14
  * DSH home root: `$DSH_HOME` or `~/.dsh`. Single source of the empty-string
@@ -9,6 +17,12 @@
9
17
  * V8-06 (0.3.47) extends the guard to whitespace (upstream home-paths:
10
18
  * `trim().length > 0` is the adoption test — `DSH_HOME=" "` must not produce
11
19
  * a sidecar under a relative "." path).
20
+ * C-11: the adoption test and the RETURNED value now come from the
21
+ * SAME trimmed source — the old form tested `trim()` but returned the raw
22
+ * value, so `DSH_HOME=" /x "` was accepted AND persisted with literal spaces.
23
+ * Known tradeoff vs upstream `resolveDshHome`: `~` is NOT expanded here —
24
+ * documented as a deliberate difference in the v10 audit; revisit only if a
25
+ * real deployment needs it.
12
26
  */
13
27
  export declare function evolutionRoot(env?: NodeJS.ProcessEnv): string;
14
28
  /** Evolution home path helper: `$DSH_HOME/evolution` for plugin-owned sidecar
@@ -48,4 +48,13 @@ export declare function evaluateThreat(text: string, scope?: ThreatScope, maxSca
48
48
  export declare function scanMemoryThreats(text: string, maxScanChars?: number, options?: ScanOptions): string | null;
49
49
  /** User-facing block message for skill content writes. */
50
50
  export declare function scanContentThreats(text: string, maxScanChars?: number, options?: ScanOptions): string | null;
51
+ /**
52
+ * V10-03 (P2-18): suffix the SkillLibrary/MemoryStore write gates append to a
53
+ * block message — the hit label is already embedded by scanContentThreats /
54
+ * scanMemoryThreats, this names the deployable self-heal path so the model
55
+ * (or operator) can allowlist a known-benign label. The evolution-threat tool
56
+ * channel deliberately does NOT append it: that channel has no
57
+ * threatExemptLabels option to advertise.
58
+ */
59
+ export declare const THREAT_EXEMPT_HINT = " If this is a legitimate false positive, the deployment can allow its label via the threatExemptLabels store option.";
51
60
  //# sourceMappingURL=threats.d.ts.map
package/package.json CHANGED
@@ -1,14 +1,14 @@
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.3.50",
4
+ "version": "0.3.52",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/lmzhen/dsh-evolution.git",
11
- "directory": "packages/dsh-evolution-core"
11
+ "directory": "packages/evolution-core"
12
12
  },
13
13
  "type": "module",
14
14
  "main": "lib/index.js",