dsh-weiwen-law 0.2.2 → 0.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-weiwen-law",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "唯稳律(Weiwen's Law)通用因果引擎(白箱呈现)—— DeepSeek Harness (Cordis) 插件实现",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -258,12 +258,24 @@ export function parseCapability(input) {
258
258
  const USE_LEDGER_CAP = 20;
259
259
  const _uses = new Map(); // key -> {count, log:[{at,action,targets}]}
260
260
 
261
- /** 证的稳定键:有 id 用 id;无 id 用内容指纹(同内容=同证,计数合并) */
261
+ /**
262
+ * 证的稳定键:**内容指纹恒参与**,`id` 只作可读前缀。
263
+ * ⇒ 同 id 同内容 ⇒ 同键(就是同一张证,计数当然合并);
264
+ * 同 id 换了内容(改作用域/改次数/改到期)⇒ 新键 ⇒ **计数从 0 起**。
265
+ *
266
+ * 🔴 为什么不能"有 id 就只认 id"(0.2.2 实测踩到,真装探针复现):
267
+ * 宿主习惯用固定 id(`xiaod-001`)重签新证,若键只认 id ⇒ 旧证的用量被新证继承
268
+ * ⇒ **新证一签发就显示"已用尽"** ⇒ 全量转 review。
269
+ * 方向虽然是"更严"(不会误放行),但它把「替换不累积」这条硬约束在计数维度上打穿了:
270
+ * 换掉的授权不该还挂着旧账。⇒ 键必须含内容,替换才是真替换。
271
+ */
262
272
  export function capabilityKey(cap) {
263
273
  if (!cap) return '';
264
- if (cap.id) return 'id:' + cap.id;
265
- return 'anon:' + cap.actions.join(',') + '|' + cap.scope.map((s) => s.kind + ':' + s.value).join(',')
266
- + '|' + String(cap.maxUses) + '|' + String(cap.expiresAt);
274
+ const body = [...cap.actions].sort().join(',')
275
+ + '|' + cap.scope.map((s) => s.kind + ':' + s.value).sort().join(',')
276
+ + '|' + String(cap.maxUses)
277
+ + '|' + String(cap.expiresAt);
278
+ return (cap.id ? 'id:' + cap.id : 'anon') + '#' + body;
267
279
  }
268
280
 
269
281
  /** 已用次数 */
@@ -257,4 +257,16 @@ test('证是当前有效量(替换不累积):不同证各自计数,不
257
257
  const y = cap({ id: null, maxUses: 1 });
258
258
  consume(x, { action: 'write', targets: [] });
259
259
  assert.equal(usesOf(y), 1);
260
+ // 🔴 同 id 换内容 ⇒ 计数从 0 起(替换不累积在**计数维度**上的落点)
261
+ // 病灶(0.2.2 真装探针实测):键只认 id ⇒ 宿主固定 id 重签 ⇒ 新证一签发就"已用尽"⇒ 全量 review。
262
+ // 方向虽更严,但换掉的授权还挂着旧账 ⇒ 替换语义被打穿,且会让人误判"证不管用"。
263
+ resetCapabilityUsage();
264
+ const oldOne = cap({ id: 'same', maxUses: 1 });
265
+ consume(oldOne, { action: 'write', targets: [] });
266
+ assert.equal(usesOf(oldOne), 1);
267
+ const renewed = cap({ id: 'same', maxUses: 2 }); // 同 id、改了次数
268
+ assert.equal(usesOf(renewed), 0, '同 id 换内容必须是新证,不得继承旧计数');
269
+ assert.notEqual(capabilityKey(oldOne), capabilityKey(renewed));
270
+ // 同 id 同内容仍是同一张证(重复解析/重复下发不得变成新证逃过计数)
271
+ assert.equal(capabilityKey(cap({ id: 'same', maxUses: 2 })), capabilityKey(renewed));
260
272
  });