@lmzhen/dsh-evolution-approval 0.3.79 → 0.3.80

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
@@ -134,6 +134,42 @@ var EvolutionApproval = class extends Service {
134
134
  async approve(id) {
135
135
  return await this.dedupe(`approve:${id}`, () => this.doApprove(id));
136
136
  }
137
+ /** S2-P2-22 (0.3.80): return an ORPHANED executing record to the pending
138
+ * window. "Orphaned" = status `executing` with no approve running in THIS
139
+ * process (the family single-instance claim rules out any other live
140
+ * process, so a claim that is not in this service's in-flight dedupe map
141
+ * belongs to a dead one). The stored `claimedBy` rides along as the seam's
142
+ * release credential, so no state-seam expansion is needed. The operator is
143
+ * expected to verify the effect first: re-approving replays the write
144
+ * deliberately (duplicates possible if the effect already landed);
145
+ * rejecting closes the record. */
146
+ async release(id) {
147
+ const executing = (await this.state().listPending("executing")).find((item) => item.id === id);
148
+ if (!executing) {
149
+ if ((await this.state().listPending("pending")).find((item) => item.id === id)) return {
150
+ ok: false,
151
+ message: `Pending write "${id}" is already in the pending window — approve or reject it.`
152
+ };
153
+ return {
154
+ ok: false,
155
+ message: `Pending write "${id}" is not in the executing window — nothing to release.`
156
+ };
157
+ }
158
+ if (this.inFlight.has(`approve:${id}`) || this.inFlight.has(`reject:${id}`)) return {
159
+ ok: false,
160
+ message: `Pending write "${id}" has an approve/reject RUNNING in this process — do not release it. Verify the effect instead; a completed approve resolves its own record.`
161
+ };
162
+ const claimId = typeof executing.claimedBy === "string" ? executing.claimedBy : "";
163
+ if (claimId === "") return {
164
+ ok: false,
165
+ message: `Pending write "${id}" carries no claim to release (unexpected record shape) — reject it instead.`
166
+ };
167
+ await this.state().releasePendingClaim(id, claimId);
168
+ return {
169
+ ok: true,
170
+ message: `Released "${id}" back to the pending window. VERIFY the effect first: approve re-runs the write deliberately (duplicates possible if the effect already landed); reject closes the record.`
171
+ };
172
+ }
137
173
  async reject(id) {
138
174
  return await this.dedupe(`reject:${id}`, async () => {
139
175
  const claimId = randomUUID();
@@ -95,6 +95,13 @@ export type ApprovalLike = {
95
95
  ok: boolean;
96
96
  message: string;
97
97
  }>;
98
+ /** S2-P2-22 (0.3.80): return an ORPHANED executing record (an approve that
99
+ * crashed mid-run) to the pending window, so it can be deliberately
100
+ * re-approved or rejected. Optional: only the real service implements it. */
101
+ release?(id: string): Promise<{
102
+ ok: boolean;
103
+ message: string;
104
+ }>;
98
105
  };
99
106
  /** The requesting session's effective policy (override ?? configured default);
100
107
  * undefined when the approval service is not mounted or no session is
@@ -156,6 +163,19 @@ export declare class EvolutionApproval extends Service {
156
163
  ok: boolean;
157
164
  message: string;
158
165
  }>;
166
+ /** S2-P2-22 (0.3.80): return an ORPHANED executing record to the pending
167
+ * window. "Orphaned" = status `executing` with no approve running in THIS
168
+ * process (the family single-instance claim rules out any other live
169
+ * process, so a claim that is not in this service's in-flight dedupe map
170
+ * belongs to a dead one). The stored `claimedBy` rides along as the seam's
171
+ * release credential, so no state-seam expansion is needed. The operator is
172
+ * expected to verify the effect first: re-approving replays the write
173
+ * deliberately (duplicates possible if the effect already landed);
174
+ * rejecting closes the record. */
175
+ release(id: string): Promise<{
176
+ ok: boolean;
177
+ message: string;
178
+ }>;
159
179
  reject(id: string): Promise<{
160
180
  ok: boolean;
161
181
  message: string;
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.79",
4
+ "version": "0.3.80",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,11 +30,11 @@
30
30
  },
31
31
  "peerDependencies": {
32
32
  "@deepseek-ai/cordis": "^4.0.1",
33
- "@lmzhen/dsh-evolution-state-storage": "^0.3.79",
34
- "@lmzhen/dsh-evolution-state": "^0.3.79"
33
+ "@lmzhen/dsh-evolution-state-storage": "^0.3.80",
34
+ "@lmzhen/dsh-evolution-state": "^0.3.80"
35
35
  },
36
36
  "devDependencies": {
37
- "@lmzhen/dsh-evolution-state-storage": "^0.3.79",
38
- "@lmzhen/dsh-evolution-state": "^0.3.79"
37
+ "@lmzhen/dsh-evolution-state-storage": "^0.3.80",
38
+ "@lmzhen/dsh-evolution-state": "^0.3.80"
39
39
  }
40
40
  }