@lmzhen/dsh-evolution-approval 0.3.61 → 0.3.63

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
@@ -31,13 +31,19 @@ var EvolutionApproval = class extends Service {
31
31
  stageForeground: z.boolean().default(true)
32
32
  });
33
33
  enabled;
34
- stageForeground;
34
+ stageForegroundConfig;
35
+ /** P3 (v16): public read for staging pre-checks (learning-graph refuses a
36
+ * stage that no runner could replay only when foreground writes stage at
37
+ * all). Mirrors `this.stageForeground`. */
38
+ get stageForeground() {
39
+ return this.stageForegroundConfig;
40
+ }
35
41
  runners = /* @__PURE__ */ new Map();
36
42
  inFlight = /* @__PURE__ */ new Map();
37
43
  constructor(ctx, config = {}) {
38
44
  super(ctx, "evolutionApproval");
39
45
  this.enabled = config.enabled ?? false;
40
- this.stageForeground = config.stageForeground ?? true;
46
+ this.stageForegroundConfig = config.stageForeground ?? true;
41
47
  }
42
48
  state() {
43
49
  return this.ctx.evolutionState;
@@ -139,7 +145,7 @@ var EvolutionApproval = class extends Service {
139
145
  message: `Pending write "${id}" is not in the pending window (rotated or resolved).`
140
146
  };
141
147
  }
142
- const resolution = await this.state().tryResolvePending(id, "rejected");
148
+ const resolution = await this.state().tryResolvePending(id, "rejected", claimId);
143
149
  if (!resolution.applied || !resolution.record) {
144
150
  await this.state().releasePendingClaim(id, claimId);
145
151
  return {
@@ -195,10 +201,16 @@ var EvolutionApproval = class extends Service {
195
201
  const runner = this.runners.get(record.kind);
196
202
  if (!runner) {
197
203
  if (record.kind === "capability") {
198
- if (!(await this.state().tryResolvePending(id, "approved")).applied) return {
199
- ok: false,
200
- message: `Pending write "${id}" was already resolved.`
201
- };
204
+ if (!(await this.state().tryResolvePending(id, "approved", claimId)).applied) {
205
+ if ((await this.state().listPending("rejected")).find((item) => item.id === id)) return {
206
+ ok: false,
207
+ message: `Capability "${id}" was approved, but the record was resolved to "rejected" concurrently — no code ran and nothing needs replaying; verify before re-submitting.`
208
+ };
209
+ return {
210
+ ok: false,
211
+ message: `Pending write "${id}" was already resolved by another writer.`
212
+ };
213
+ }
202
214
  return {
203
215
  ok: true,
204
216
  message: "Capability approved for manual activation in Creator mode (no code was executed)."
@@ -227,10 +239,16 @@ var EvolutionApproval = class extends Service {
227
239
  message: "Replay runner failed; the pending write remains pending."
228
240
  };
229
241
  }
230
- if (!(await this.state().tryResolvePending(id, "approved")).applied) return {
231
- ok: false,
232
- message: `Pending write "${id}" was already resolved by another writer.`
233
- };
242
+ if (!(await this.state().tryResolvePending(id, "approved", claimId)).applied) {
243
+ if ((await this.state().listPending("rejected")).find((item) => item.id === id)) return {
244
+ ok: false,
245
+ message: `Approved write "${id}" was replayed, but the record was resolved to "rejected" concurrently — the effect has landed while the audit reads rejected. Verify the write and do NOT replay it.`
246
+ };
247
+ return {
248
+ ok: false,
249
+ message: `Pending write "${id}" was already resolved by another writer.`
250
+ };
251
+ }
234
252
  return {
235
253
  ok: true,
236
254
  message: `Approved ${record.kind} write "${id}" (${record.summary}).`
@@ -247,7 +265,7 @@ function normalizeSummary(input) {
247
265
  return `memory ${targetLabel === "memory" ? "" : `${targetLabel} `}batch of ${candidate.operations.length} operations`;
248
266
  }
249
267
  }
250
- if (input.kind === "skill" && /^skill delete /.test(trimmed)) return `${trimmed} (warning: archive)`;
268
+ if (input.kind === "skill" && /^(?:skill|graph)\s+delete\s/.test(trimmed)) return `${trimmed} (warning: archive)`;
251
269
  return trimmed;
252
270
  }
253
271
  //#endregion
@@ -66,6 +66,11 @@ export type ApprovalLike = {
66
66
  }>;
67
67
  hasRunner(kind: PendingKind): boolean;
68
68
  isEnabled?: boolean;
69
+ /** P3 (v16): whether foreground-origin writes stage (learning-graph's
70
+ * hasRunner pre-check reads it so it only refuses writes that would
71
+ * actually be staged). Optional: absent means "unknown" and callers must
72
+ * not pre-refuse on it. */
73
+ stageForeground?: boolean;
69
74
  registerRunner(kind: PendingKind, runner: WriteRunner): () => void;
70
75
  list(status?: PendingStatus): Promise<PendingRecord[]>;
71
76
  approve(id: string): Promise<{
@@ -106,7 +111,11 @@ export declare class EvolutionApproval extends Service {
106
111
  static inject: string[];
107
112
  static Config: Schema<Config>;
108
113
  private readonly enabled;
109
- private readonly stageForeground;
114
+ private readonly stageForegroundConfig;
115
+ /** P3 (v16): public read for staging pre-checks (learning-graph refuses a
116
+ * stage that no runner could replay only when foreground writes stage at
117
+ * all). Mirrors `this.stageForeground`. */
118
+ get stageForeground(): boolean;
110
119
  private readonly runners;
111
120
  private readonly inFlight;
112
121
  constructor(ctx: Context, config?: Config);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-approval",
3
3
  "description": "Stage/pending approval service for Hermes-style self-evolution writes (community build)",
4
- "version": "0.3.61",
4
+ "version": "0.3.63",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -35,15 +35,15 @@
35
35
  "peerDependencies": {
36
36
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
37
37
  "@deepseek-ai/cordis": "^4.0.1",
38
- "@lmzhen/dsh-evolution-state-storage": "^0.3.61",
39
- "@lmzhen/dsh-evolution-state": "^0.3.61"
38
+ "@lmzhen/dsh-evolution-state-storage": "^0.3.63",
39
+ "@lmzhen/dsh-evolution-state": "^0.3.63"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
43
- "@lmzhen/dsh-evolution-state-storage": "^0.3.61",
44
- "@lmzhen/dsh-evolution-state": "^0.3.61",
45
- "@lmzhen/dsh-evolution-io": "^0.3.61",
46
- "@lmzhen/dsh-evolution-io-node": "^0.3.61",
47
- "@lmzhen/dsh-evolution-state-json": "^0.3.61"
43
+ "@lmzhen/dsh-evolution-state-storage": "^0.3.63",
44
+ "@lmzhen/dsh-evolution-state": "^0.3.63",
45
+ "@lmzhen/dsh-evolution-io": "^0.3.63",
46
+ "@lmzhen/dsh-evolution-io-node": "^0.3.63",
47
+ "@lmzhen/dsh-evolution-state-json": "^0.3.63"
48
48
  }
49
49
  }