@dzhechkov/harness-core 0.8.33 → 0.8.35

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.
Files changed (53) hide show
  1. package/.dz-manifest.json +52 -52
  2. package/README.md +228 -5
  3. package/dist/agentdb-index.d.ts +39 -7
  4. package/dist/agentdb-index.d.ts.map +1 -1
  5. package/dist/agentdb-index.js +217 -23
  6. package/dist/agentdb-index.js.map +1 -1
  7. package/dist/apply-leg.d.ts +197 -6
  8. package/dist/apply-leg.d.ts.map +1 -1
  9. package/dist/apply-leg.js +858 -46
  10. package/dist/apply-leg.js.map +1 -1
  11. package/dist/index.d.ts +7 -6
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +9 -4
  14. package/dist/index.js.map +1 -1
  15. package/dist/mutation-gate.d.ts +19 -0
  16. package/dist/mutation-gate.d.ts.map +1 -1
  17. package/dist/mutation-gate.js +37 -1
  18. package/dist/mutation-gate.js.map +1 -1
  19. package/dist/operations.d.ts +16 -1
  20. package/dist/operations.d.ts.map +1 -1
  21. package/dist/operations.js +115 -13
  22. package/dist/operations.js.map +1 -1
  23. package/dist/publish-sibling-drift.d.ts +72 -0
  24. package/dist/publish-sibling-drift.d.ts.map +1 -1
  25. package/dist/publish-sibling-drift.js +150 -4
  26. package/dist/publish-sibling-drift.js.map +1 -1
  27. package/dist/release.d.ts +72 -0
  28. package/dist/release.d.ts.map +1 -1
  29. package/dist/release.js +236 -19
  30. package/dist/release.js.map +1 -1
  31. package/dist/setup.d.ts.map +1 -1
  32. package/dist/setup.js +90 -14
  33. package/dist/setup.js.map +1 -1
  34. package/dist/skills.d.ts +87 -3
  35. package/dist/skills.d.ts.map +1 -1
  36. package/dist/skills.js +266 -15
  37. package/dist/skills.js.map +1 -1
  38. package/dist/vector-tier.d.ts +27 -2
  39. package/dist/vector-tier.d.ts.map +1 -1
  40. package/dist/vector-tier.js +117 -4
  41. package/dist/vector-tier.js.map +1 -1
  42. package/package.json +2 -2
  43. package/sbom.json +51 -51
  44. package/src/agentdb-index.ts +223 -24
  45. package/src/apply-leg.ts +875 -46
  46. package/src/index.ts +18 -2
  47. package/src/mutation-gate.ts +58 -2
  48. package/src/operations.ts +117 -14
  49. package/src/publish-sibling-drift.ts +209 -4
  50. package/src/release.ts +263 -17
  51. package/src/setup.ts +81 -16
  52. package/src/skills.ts +303 -14
  53. package/src/vector-tier.ts +157 -5
package/.dz-manifest.json CHANGED
@@ -9,7 +9,7 @@
9
9
  },
10
10
  {
11
11
  "path": "README.md",
12
- "sha256": "70cf5d858ca889a834345fe7c00f7312fc65477f5e5c3f6d2f4dd7d742ca2ed3"
12
+ "sha256": "37a3aab588b4b12b15d2d67a014abec49ae38615a1d28816310d5f2e6ad29f79"
13
13
  },
14
14
  {
15
15
  "path": "dist/__tests__/golden-baseline.test.d.ts",
@@ -29,19 +29,19 @@
29
29
  },
30
30
  {
31
31
  "path": "dist/agentdb-index.d.ts",
32
- "sha256": "69023233508c656035c8a27b95690bcfddab535d25dffb85f2041c4bfbf77f64"
32
+ "sha256": "179cd2dee305664b3c8365b4a8dcd64015b4e226b2753c6dd1e42769b4d0c96f"
33
33
  },
34
34
  {
35
35
  "path": "dist/agentdb-index.d.ts.map",
36
- "sha256": "965f21e2cb4c4f6ac8cf51b0238a64c3c44b0442ecc2ec198d67463bd7e76b31"
36
+ "sha256": "1e366365fadabe345c7d128da0250a4a21dc7e4924d1cc04f457fd38b40d3c72"
37
37
  },
38
38
  {
39
39
  "path": "dist/agentdb-index.js",
40
- "sha256": "2975c8ee201c4b6e7d78d0a7fc34e976d2d1c97692e6fce82c56417d90d397cd"
40
+ "sha256": "bbcee70ee0aa52d95960956a5692cff81d296e49083487c0bbfd3c3d93c94f9c"
41
41
  },
42
42
  {
43
43
  "path": "dist/agentdb-index.js.map",
44
- "sha256": "866790c8dcb6614538513927c389231e3617ec5c883470e1e15c19d1f3b2128b"
44
+ "sha256": "02208be0504446c12852720a17f99df8d64e69ba759a55f2ea4095ea19fa2f37"
45
45
  },
46
46
  {
47
47
  "path": "dist/agentdb-reindex-marker.d.ts",
@@ -125,19 +125,19 @@
125
125
  },
126
126
  {
127
127
  "path": "dist/apply-leg.d.ts",
128
- "sha256": "a3b35c162ce1059ec52e66250f4d5eb40ea80031bc6ba211cf2c239125da2dab"
128
+ "sha256": "9cc4cf39b4ef8479eba5745c767c60a8a331f777e19251ec9faf42530f16b7c7"
129
129
  },
130
130
  {
131
131
  "path": "dist/apply-leg.d.ts.map",
132
- "sha256": "a16afc919647db2ea568e29bf0e835df023a025716bf73e60ab5294300fee486"
132
+ "sha256": "5204913b80c8748926f20a1632658720b75336fcd7bdf15114ceb1707fde804c"
133
133
  },
134
134
  {
135
135
  "path": "dist/apply-leg.js",
136
- "sha256": "604ff5b789bf969728c174c560a0b463d64307f0120699feb28ed839d0e42afa"
136
+ "sha256": "f3e74e03e325569237340aace076e4e0507d12b48d47dcced7a836242ae13ac9"
137
137
  },
138
138
  {
139
139
  "path": "dist/apply-leg.js.map",
140
- "sha256": "78b6f40ca672f6ea40ce4d29c9ae9baab74c74252400a5e05540272a612201b1"
140
+ "sha256": "300050992853dba6e5cdd4dc54e66d77a506ac3750d81ed2d3da560086830e99"
141
141
  },
142
142
  {
143
143
  "path": "dist/apply.d.ts",
@@ -1037,19 +1037,19 @@
1037
1037
  },
1038
1038
  {
1039
1039
  "path": "dist/index.d.ts",
1040
- "sha256": "9268dcea6b76bcc172de258f26b263384be72751ecd80bf695d30fa956ce3255"
1040
+ "sha256": "5476546b291c2df0a0a4e3afd7b64900ada0b7ab26d400ae6fb6985853466c72"
1041
1041
  },
1042
1042
  {
1043
1043
  "path": "dist/index.d.ts.map",
1044
- "sha256": "525593dc52d74876f41071fc906828b3b1807d5b8d1bf0e570589b8f5e23a6cd"
1044
+ "sha256": "593859b38d79431517940bccf25f5e8ae079e142ac6cb66396b38421555a1d17"
1045
1045
  },
1046
1046
  {
1047
1047
  "path": "dist/index.js",
1048
- "sha256": "645544ee8ebb51df85c94f1150134c76885678ac0c54dfaaacfaa38090549ee6"
1048
+ "sha256": "a7264b451ffa98a04e54d23e83312abe3afe1f380b088e386841ea4325938596"
1049
1049
  },
1050
1050
  {
1051
1051
  "path": "dist/index.js.map",
1052
- "sha256": "0a45a791953cdecc4fb0bff3d39ea158f1d204ba5c3507783affd94fc38c559a"
1052
+ "sha256": "67861fde05a1927927ab1951738fbed427a2d016b22433603136041df21b3f0d"
1053
1053
  },
1054
1054
  {
1055
1055
  "path": "dist/integration-apply.d.ts",
@@ -1405,19 +1405,19 @@
1405
1405
  },
1406
1406
  {
1407
1407
  "path": "dist/mutation-gate.d.ts",
1408
- "sha256": "815a03f89f6cae322f4cb0d84ef58823bdb8fae9a18889cde68b8d6e878dc736"
1408
+ "sha256": "a77bd4063914f6dc09487990423e64c8186c5bc48b5285867852c905653605f4"
1409
1409
  },
1410
1410
  {
1411
1411
  "path": "dist/mutation-gate.d.ts.map",
1412
- "sha256": "de00794a601552c629e2a588de98b3a03a58c00ec73d87167dc8df430ae09a29"
1412
+ "sha256": "6e414fbaee4a6ed940620fb44cb8c39a2fd945e32a966f8141f1923ec242bed3"
1413
1413
  },
1414
1414
  {
1415
1415
  "path": "dist/mutation-gate.js",
1416
- "sha256": "88e9371ce15d5ead058a597dc49db25d2ef988479dcb278929559e851088206c"
1416
+ "sha256": "36dc04b58621a090dadaeefbd8d40e73196875f28a84f130f08bcad9b3ebcb53"
1417
1417
  },
1418
1418
  {
1419
1419
  "path": "dist/mutation-gate.js.map",
1420
- "sha256": "25379eb4fbe99792355a8164cab04f68585b9450ae3426109eefd0ee4b10da6b"
1420
+ "sha256": "64c71e44dd9635b4480f0e64c27cf510c2859890b03f1a00ce555b438e365090"
1421
1421
  },
1422
1422
  {
1423
1423
  "path": "dist/name-check.d.ts",
@@ -1485,19 +1485,19 @@
1485
1485
  },
1486
1486
  {
1487
1487
  "path": "dist/operations.d.ts",
1488
- "sha256": "7e2c8371b6ef88ab84ec325d54b0c89c1cb6825bc4b953d134f5fb36368038db"
1488
+ "sha256": "60bd3d2d8ddc18535260e2b790ee25e6ddb77eab597248f05948987d7d5a39eb"
1489
1489
  },
1490
1490
  {
1491
1491
  "path": "dist/operations.d.ts.map",
1492
- "sha256": "f147b33e2edd6766f4d8256f980315a1458f0408bb0683f5324ccee28e0b8223"
1492
+ "sha256": "f8021977e02820666664922176bca714047fc544c8b466f3956aae854e53dd53"
1493
1493
  },
1494
1494
  {
1495
1495
  "path": "dist/operations.js",
1496
- "sha256": "568d5ea2e70f3672deadf656384e0dedc8f02136e1aacfbff18b523ee0c90fa9"
1496
+ "sha256": "76105d806137ef947694e1cb10b2e8360411bd873e74386e87d933c99f53bbb3"
1497
1497
  },
1498
1498
  {
1499
1499
  "path": "dist/operations.js.map",
1500
- "sha256": "43e8044e64abc740d5db2486e149acdc943c58a046d9b0c94038e53c207fc0af"
1500
+ "sha256": "acbb9920848e738a286eff1f45ebdd3a9f5e3b09fec879567072573df15d171b"
1501
1501
  },
1502
1502
  {
1503
1503
  "path": "dist/package-skill-layouts.d.ts",
@@ -1677,19 +1677,19 @@
1677
1677
  },
1678
1678
  {
1679
1679
  "path": "dist/publish-sibling-drift.d.ts",
1680
- "sha256": "63e4bb439f1366114d21b9796489fec34308c34818bddeb6b69f590b8882c0fd"
1680
+ "sha256": "8e39c9f1d527ddaaa8b8655ee1e2fa0ee005926e25da8c3218cf0fb9cbed5502"
1681
1681
  },
1682
1682
  {
1683
1683
  "path": "dist/publish-sibling-drift.d.ts.map",
1684
- "sha256": "6c564150ddcb07ce2994c63764a0acb3d282bd702ed79dc906c886affc3e84e9"
1684
+ "sha256": "257237bac3a1d173113b4525ea19dbb4f7984a08ad017ebdd7af164ba6cc7cfe"
1685
1685
  },
1686
1686
  {
1687
1687
  "path": "dist/publish-sibling-drift.js",
1688
- "sha256": "b99fca72e63868d9177052aefe47390e9f5e08ff6869d65299dc0286c8bdfd18"
1688
+ "sha256": "16ac3f704998f295ed42c112277482d6adfb5d26e4c8a4c89781d24b63118f6f"
1689
1689
  },
1690
1690
  {
1691
1691
  "path": "dist/publish-sibling-drift.js.map",
1692
- "sha256": "504ecc5393016745aef5c60dfa9162a6531625a56b34ced46f4f8ee31d72bf11"
1692
+ "sha256": "8143d41cd1c706713fe6d9a742b71d9a28b3519a72072ca4683837ab54732d30"
1693
1693
  },
1694
1694
  {
1695
1695
  "path": "dist/publish-signing.d.ts",
@@ -1901,19 +1901,19 @@
1901
1901
  },
1902
1902
  {
1903
1903
  "path": "dist/release.d.ts",
1904
- "sha256": "cf5750b9c6d474c45cfd81b7cbb2f92a18b452fa1c069e4e3fbfc4dac06267d9"
1904
+ "sha256": "937161c359ffed77233d43ba2b5280cd8f34962baa1758daf8479805e23621ef"
1905
1905
  },
1906
1906
  {
1907
1907
  "path": "dist/release.d.ts.map",
1908
- "sha256": "6eaca079115921991149d68189ed8ef8c61be6efd12c426df8d091c1b39da5b9"
1908
+ "sha256": "6ce025a95be2b36246e6a8dbe3b324539e60d3f79febe1330071e51fced1282a"
1909
1909
  },
1910
1910
  {
1911
1911
  "path": "dist/release.js",
1912
- "sha256": "627e5efa90298701d01bee346790647f5688dc0f10c4727b348298490c35d219"
1912
+ "sha256": "0030839d3dfaa43dabed352703255a7f6f0e11c8e3db01abaa4341fddb57e211"
1913
1913
  },
1914
1914
  {
1915
1915
  "path": "dist/release.js.map",
1916
- "sha256": "a539f499cf28923856c6cb6f40417d67d662c6771b4df8fe4e7fe5ab9f2a7121"
1916
+ "sha256": "2f1cfa37c4f868859e6ea2b5aba4b896bd106e8ed1a9a8b5285b9ad30e52d896"
1917
1917
  },
1918
1918
  {
1919
1919
  "path": "dist/repo-boundary.d.ts",
@@ -2177,15 +2177,15 @@
2177
2177
  },
2178
2178
  {
2179
2179
  "path": "dist/setup.d.ts.map",
2180
- "sha256": "baefa9d4a927880e51b8dc9ec4b899e7625c029de82ed397501b32cb1d3e925a"
2180
+ "sha256": "3b8f5dc584a26b53be9f1e68cb8fffaaf68decbe1cf4b634f78c8ec0f3725b3d"
2181
2181
  },
2182
2182
  {
2183
2183
  "path": "dist/setup.js",
2184
- "sha256": "6f8cbb59ffc98b5f8722134b9b28b4d2bd5d847fe50edc38d2f539ebc9980151"
2184
+ "sha256": "8644f836c6236867d8f238cf48465506361647f11bc58b50134a7a862b7e711a"
2185
2185
  },
2186
2186
  {
2187
2187
  "path": "dist/setup.js.map",
2188
- "sha256": "f859ac7532e6b9d2232fbdbe6f7b3cb5e0a739842bc70024544c9263f78ec67e"
2188
+ "sha256": "3bafd2f8eb1c3305be28c42d59365dc58751d199b5e702b69c581c5df7b97560"
2189
2189
  },
2190
2190
  {
2191
2191
  "path": "dist/shell-veto-policy.d.ts",
@@ -2301,19 +2301,19 @@
2301
2301
  },
2302
2302
  {
2303
2303
  "path": "dist/skills.d.ts",
2304
- "sha256": "8b3b61f8d0bb9ff1a9426f8721cda72b6685a7097153f52e31b73854a570d9f1"
2304
+ "sha256": "e2ee030cc763d5e909855239129aad5275aa42c9128458320759d321174fd697"
2305
2305
  },
2306
2306
  {
2307
2307
  "path": "dist/skills.d.ts.map",
2308
- "sha256": "485f603831e9a2feea38f7a4c7ed0c45f530c1ff8a2ac1d5cd04b3498d40f465"
2308
+ "sha256": "857485e66830cc7a31e80a9a1b86614c5a02ceb074e7b209dbe62f12059f3198"
2309
2309
  },
2310
2310
  {
2311
2311
  "path": "dist/skills.js",
2312
- "sha256": "e72d99a4e16bf0c4d483e4b1b9aafee2c3a38a4861cddd8be81d20ccbe240613"
2312
+ "sha256": "9659576b23f1c2f47cf5f6f78857495e44c89fffa4fc2010f6ded703e126738d"
2313
2313
  },
2314
2314
  {
2315
2315
  "path": "dist/skills.js.map",
2316
- "sha256": "27188e499f4d04661dbb8ba6da4a8eb3d94fccd4171b3059c4e26334aae9f946"
2316
+ "sha256": "f51408fe56f037d19219a828a5adba28f2bbe05337c3042d4fa25888ab31101b"
2317
2317
  },
2318
2318
  {
2319
2319
  "path": "dist/slop-lint.d.ts",
@@ -2653,19 +2653,19 @@
2653
2653
  },
2654
2654
  {
2655
2655
  "path": "dist/vector-tier.d.ts",
2656
- "sha256": "289cc4a73d023af16d44ab00caa4c1cad2cbe6bd6720b6eddf932ef37e200a5b"
2656
+ "sha256": "cdf68fa28b6a7e78d0381a5b2d23647ae812fee004e31b4c8c3893a897fd6f6f"
2657
2657
  },
2658
2658
  {
2659
2659
  "path": "dist/vector-tier.d.ts.map",
2660
- "sha256": "52e4813a1af157a58ea91b7787c44c09dfc68e44b0baee315182467913c4dc08"
2660
+ "sha256": "8c504e223d4d78a29a24993078f4a90e771f56d6a62b310ab90b7ba44f1a6bd5"
2661
2661
  },
2662
2662
  {
2663
2663
  "path": "dist/vector-tier.js",
2664
- "sha256": "cf0d978a1e9bffb32a854d20a0a88a4daad7e852f12287db15ae1707c0db66a3"
2664
+ "sha256": "6554f0bd2566ac10e6c841732210daa2412028e520ce49acf197451024161382"
2665
2665
  },
2666
2666
  {
2667
2667
  "path": "dist/vector-tier.js.map",
2668
- "sha256": "f8db2ecd2284f2d5af15049443aa295c56fa3d67a2240bb4f08b884f36cf41bd"
2668
+ "sha256": "23de503688790bbe679dae6c72b3c77faa43a14d4d0fbc54946c2e35cfd2d543"
2669
2669
  },
2670
2670
  {
2671
2671
  "path": "dist/workflow-run-dispatch.d.ts",
@@ -2733,7 +2733,7 @@
2733
2733
  },
2734
2734
  {
2735
2735
  "path": "package.json",
2736
- "sha256": "a6f86350222413a151c6d4f73d9c80f4e2d3c21297000e2fe7b0708248d77724"
2736
+ "sha256": "273067dfe330063f7f449dea7480208e147a20eb7b554f921d1eb9ee33cb8ca4"
2737
2737
  },
2738
2738
  {
2739
2739
  "path": "src/__tests__/golden-baseline.test.ts",
@@ -2741,7 +2741,7 @@
2741
2741
  },
2742
2742
  {
2743
2743
  "path": "src/agentdb-index.ts",
2744
- "sha256": "49c55278320cf79d50463374cb4bd98702381eefc61aa81cf3889f117857f7d0"
2744
+ "sha256": "388fce3f17382373d5864dccc0c8685c1779d1101a490399ccbacb3bd6c1a2f5"
2745
2745
  },
2746
2746
  {
2747
2747
  "path": "src/agentdb-reindex-marker.ts",
@@ -2765,7 +2765,7 @@
2765
2765
  },
2766
2766
  {
2767
2767
  "path": "src/apply-leg.ts",
2768
- "sha256": "15feafe9dedd298f2d8f7815d2a100cedaf4864223affb80f8e8c34317e4b0f6"
2768
+ "sha256": "9391b6e0d9f6ef3f39662d4f3c89dbc173881584c6956ad5add2ebd54cdc85cf"
2769
2769
  },
2770
2770
  {
2771
2771
  "path": "src/apply.ts",
@@ -2997,7 +2997,7 @@
2997
2997
  },
2998
2998
  {
2999
2999
  "path": "src/index.ts",
3000
- "sha256": "307f2d574fd2d7a0f2b8ebe7a36047f0c3d8cade6e8fdda9e10e26d344914912"
3000
+ "sha256": "f248dbc9f0fc735dcfe11ae399c8f7c52b4053bb10e29234dc27c6c9581b7945"
3001
3001
  },
3002
3002
  {
3003
3003
  "path": "src/integration-apply.ts",
@@ -3093,7 +3093,7 @@
3093
3093
  },
3094
3094
  {
3095
3095
  "path": "src/mutation-gate.ts",
3096
- "sha256": "7a26f7f745ca6f8d0a3dcf9d32ff8b9acc9c37c106b411ff292690544ac1ddd5"
3096
+ "sha256": "0e777534524e5042bcdb8eef9c5f3d23c5fd0f94e2fe45a52623f3122a5dedef"
3097
3097
  },
3098
3098
  {
3099
3099
  "path": "src/name-check.ts",
@@ -3113,7 +3113,7 @@
3113
3113
  },
3114
3114
  {
3115
3115
  "path": "src/operations.ts",
3116
- "sha256": "fd66a40569fd53cbf0d621643ace1e4f89777e9634bcda24a730e971dc240ccd"
3116
+ "sha256": "8143fae14dbfcf0e87bf148add74e16e0ee8c2f8064fd2bfb0f90fbf1a21889b"
3117
3117
  },
3118
3118
  {
3119
3119
  "path": "src/package-skill-layouts.ts",
@@ -3161,7 +3161,7 @@
3161
3161
  },
3162
3162
  {
3163
3163
  "path": "src/publish-sibling-drift.ts",
3164
- "sha256": "2bd607ec044e2bca2a7336e10d69cee99db53e6f41b60bfa770711d756dd9116"
3164
+ "sha256": "5a7c2cffce4e62e4e859578b7014020670125e1ea68d4e04f2b1f6256c423b7a"
3165
3165
  },
3166
3166
  {
3167
3167
  "path": "src/publish-signing.ts",
@@ -3217,7 +3217,7 @@
3217
3217
  },
3218
3218
  {
3219
3219
  "path": "src/release.ts",
3220
- "sha256": "b7e248afaadc503642ef3b22eb86817e8ecb12744493c1780065de87772483a4"
3220
+ "sha256": "4e3390606a39f54b2a15db985a6e72a4f34c2ef59765c5124e1d64844d97447c"
3221
3221
  },
3222
3222
  {
3223
3223
  "path": "src/repo-boundary.ts",
@@ -3281,7 +3281,7 @@
3281
3281
  },
3282
3282
  {
3283
3283
  "path": "src/setup.ts",
3284
- "sha256": "795f796ad5093244807d5fd0c80478c89e28e3e3ef26540e8f636eb24d517038"
3284
+ "sha256": "0f44ef0d05c0ffabffe5e0f4fb4539b67fd72f62b42206d68bc98199ea718dcd"
3285
3285
  },
3286
3286
  {
3287
3287
  "path": "src/shell-veto-policy.ts",
@@ -3313,7 +3313,7 @@
3313
3313
  },
3314
3314
  {
3315
3315
  "path": "src/skills.ts",
3316
- "sha256": "cb1fc2b2f904e6c40ef7ba481c4ca2e9072b31302015134f4e636f67323c89d8"
3316
+ "sha256": "1dac38f95d956aa315554f6d7559e141a7acbebdab8c39d7f9880c3eae0f1f28"
3317
3317
  },
3318
3318
  {
3319
3319
  "path": "src/slop-lint.ts",
@@ -3405,7 +3405,7 @@
3405
3405
  },
3406
3406
  {
3407
3407
  "path": "src/vector-tier.ts",
3408
- "sha256": "f5b5187d2537f2d00c3f248676928d286e715e9984f67491aa1e02eb0f4b1b50"
3408
+ "sha256": "6607b712f768be25a140f224dc6c3c80d25e0e5144898fb819d488da7f0cd600"
3409
3409
  },
3410
3410
  {
3411
3411
  "path": "src/workflow-run-dispatch.ts",
@@ -3425,5 +3425,5 @@
3425
3425
  }
3426
3426
  ]
3427
3427
  },
3428
- "signature": "9lkZG7XpDHs9qtNmKiBJYt3PMKTOAhxXxEU3F4n2uOYQps/u6lY0vb50bdKsxMoZem8/R2qORHxYCU4kN+iYAQ=="
3428
+ "signature": "dNZha0vxyf5ob2PcHUsX1zU0BQwDvQbcrZ5oKyPYXcgGmJAjvPkXAPf4scWIz152ujHWLaetlKNujBxedF+CDA=="
3429
3429
  }
package/README.md CHANGED
@@ -210,7 +210,7 @@ explicit skills-only short circuit. `--no-verify` cannot authorize emission. A C
210
210
 
211
211
  | Module | Exports | Purpose |
212
212
  |---|---|---|
213
- | `skills` | `loadSkillFromDir`, `listSkills`, `listSkillsDetailed`, `describeSkillLoadFailure`, `formatSkillLoadFailures`, `formatSkillApplyFailures`, `discoverSkillIds` | Read skill directories into `CanonicalSkill` objects. **Two listing functions, deliberately:** `listSkills` THROWS on the first unloadable skill and always will — it is a published export, and silently turning it into a skip-and-collect function would downgrade every unknown third-party consumer from fail-closed to fail-silent without their consent (an incomplete catalogue reported as complete); a pinned regression test asserts it still throws. `listSkillsDetailed` is the total variant callers ask for BY NAME: it returns `{skills, failures}` with a per-id `try/catch`, so one unparseable `SKILL.md` never hides the ones after it (order-independence is the tested property — the offender first, middle or last yields the same counts). Every failure is NAMED — `describeSkillLoadFailure` is the single place a pathless parser throw becomes `{id, absolute path, verbatim reason, first line}`, because the parser is handed only TEXT and can never supply a path. `formatSkillLoadFailures` renders that list for stderr in one of two modes chosen by the CALLER (absolute paths for `dz list`/`dz sync`; relative-to-package for `dz install`, where a `node_modules/**` path is not actionable) |
213
+ | `skills` | `loadSkillFromDir`, `listSkills`, `listSkillsDetailed`, `describeSkillLoadFailure`, `formatSkillLoadFailures`, `formatSkillApplyFailures`, `discoverSkillIds`, `walkFiles`, `isSkillJunkFile`, `SKILL_JUNK_DIRS`, `SKILL_JUNK_FILES` | Read skill directories into `CanonicalSkill` objects. **Two listing functions, deliberately:** `listSkills` THROWS on the first unloadable skill and always will — it is a published export, and silently turning it into a skip-and-collect function would downgrade every unknown third-party consumer from fail-closed to fail-silent without their consent (an incomplete catalogue reported as complete); a pinned regression test asserts it still throws. `listSkillsDetailed` is the total variant callers ask for BY NAME: it returns `{skills, failures}` with a per-id `try/catch`, so one unparseable `SKILL.md` never hides the ones after it (order-independence is the tested property — the offender first, middle or last yields the same counts). Every failure is NAMED — `describeSkillLoadFailure` is the single place a pathless parser throw becomes `{id, absolute path, verbatim reason, first line}`, because the parser is handed only TEXT and can never supply a path. `formatSkillLoadFailures` renders that list for stderr in one of two modes chosen by the CALLER (absolute paths for `dz list`/`dz sync`; relative-to-package for `dz install`, where a `node_modules/**` path is not actionable). **Symlinks and junk (feature `skills-walk-symlinks-and-junk`):** `walkFiles`, the asset-discovery loop `loadSkillFromDir` and `getSkillInfo` both run on, resolves every symlink with `statSync` before deciding whether it names a file or a directory — a `Dirent` from `readdirSync` answers `false` to BOTH `isDirectory()` and `isFile()` for a symlink entry, so trusting those two checks alone silently drops every symlinked asset (MEASURED: 2 of 4 fixture assets vanished, exit 0, before this fix). A symlink whose target cannot be `stat`'d is a *broken symlink*; a directory (reached directly or through a symlink) whose `realpath` is already on the current ANCESTOR chain ends the walk there instead of recursing — the guard tracks the recursion path, not every directory ever visited, so two non-cyclic aliases of one directory (`alias1 -> shared`, `alias2 -> shared`) are both walked under their own logical paths (Codex r2, lead fix) (`walk-guards-cycles`, its own dedicated mutation entry as of fix-round 1 — the symlink-resolution mutation alone cannot prove the guard, because disabling symlink-following ALSO stops any cycle from ever being reached), which is what stops an `a -> ..` cycle from hanging. **Containment (fix-round 1, lead item AM-8):** a symlink is followed only when its RESOLVED target's real path lies within the skill directory's own real path — `assets/secret -> /etc/hostname`, or a relative `-> ../../..` that escapes upward, is refused with reason `'symlink escapes the skill directory'` and never bundled, whether the escaping target is a file or a directory; only a `..` path COMPONENT counts as an escape — a file legitimately named `..asset` is inside the root (Codex r2, lead fix). `SKILL.md` itself gets the same check BEFORE it is read: a `SKILL.md` that is a symlink escaping the skill directory makes the whole skill REFUSED with a named error (it is mandatory, so it cannot merely be skipped); an in-tree `SKILL.md` symlink still loads (Codex r2 CRITICAL, lead fix) (an escaping directory is not recursed into either — nothing beneath it is walked). **What counts as junk IS THE PUBLISHED CONTRACT** (fix-round 1 HIGH-1 — Codex's finding that this contradicts "never drops a legitimate skill asset" is REFUTED-BY-CONTRACT, not a bug: a skill cannot ship an asset under one of these exact names, on purpose or by accident, and that is the deliberate trade this design makes, not an oversight to be widened into content-sniffing): directories `__pycache__`, `node_modules`, `.git`, `__MACOSX`, `.pytest_cache`, `.mypy_cache` (`SKILL_JUNK_DIRS`); files named exactly `.DS_Store` or `Thumbs.db`, or matching `*.pyc`, `*.pyo`, `*.swp`, `*.swo`, or `.#*` (`SKILL_JUNK_FILES` + `isSkillJunkFile`) — a trailing `~` (editor backup) is deliberately NOT on the list: it is the one pattern a legitimate asset name can plausibly end with (`notes~`), and the list is conservative by contract — a false positive would silently drop a real asset (Codex r2, lead decision). This is NOT a whitelist — any other file (including a skill author's own `notes.local.txt`) is kept as a real asset; filtering someone else's files by name is not this list's job. Every junk entry, broken symlink, escaping symlink, and detected cycle is counted and NAMED, never silently dropped: `walkFiles` returns `{files, skipped}` where `skipped` is `{path, reason}[]` and `reason` NAMES the matched pattern (`'junk file (*.pyc)'`, `'junk directory (__pycache__)'`, not a bare `'junk file'` — fix-round 1 HIGH-1(b)), and `loadSkillFromDir` threads that list onto its `CanonicalSkill` result as an *optional* `skipped` field (present only when something was actually skipped, so every existing consumer that only reads the `CanonicalSkill` shape is unaffected). An unreadable directory (`readdirSync` throwing — fix-round 1 MEDIUM-3) is *also* a named `'unreadable directory (<errno>)'` skip, never a throw out of `loadSkillFromDir`. `dz install` sums the junk-tagged entries across the installed package's skills and prints one line — `skills: skipped N junk entr(y|ies) (…)` — only when N > 0; the count is ENTRIES, not files (a skipped junk directory is one entry regardless of how many files sit underneath it, since `walkFiles` never descends into it to count those), and a directory path in the list is shown with a trailing `/` (fix-round 1 MEDIUM-4) |
214
214
  | `apply` | `applyEmitResult` | Write an adapter `EmitResult` to disk — **additively** |
215
215
  | `repo-boundary` | `isRepoBoundary`, `RepoBoundaryIo` | A repository boundary is a `.git` directory with a real `HEAD` file or a worktree `gitdir:` redirect; an empty or unrelated `.git` entry is not a boundary, so `dz` run from a directory such as `/tmp` with a stray empty `.git` no longer treats it as a project root (and no longer creates a `.dz` store there). Named locks are unchanged: `<root>/.dz/locks/<name>.lock`, a pure function of the root. |
216
216
  | `targets` | `TARGETS`, `TargetName`, `isTargetName`, `resolveTargetName`, `TARGET_ALIASES`, `TARGET_NAMES_SORTED`, `formatTargetProblem`, `formatTargetAliasNote`, `normalizeTargetToken` | `--target` name → platform adapter, plus the resolution layer in front of it. `isTargetName`/`TARGETS`/`TARGET_NAMES` are UNCHANGED: `boundaries.json` names `isTargetName` as the scanned `--target` validation boundary, and every resolution ends in exactly that guard — the boundary is routed THROUGH, never relocated. `resolveTargetName` is total and pure, with fixed precedence: exact canonical → normalised canonical (case/padding/separators: `Claude_Code`, `claudecode`) → an explicit `TARGET_ALIASES` row → unique normalised prefix → Levenshtein ≤ 3 strictly better than the runner-up → nothing. **Aliases ACCEPT; prefix and Levenshtein only SUGGEST** — an alias row is an owner decision recorded in DATA (adding one is one line and zero control flow), while a fuzzy match is a guess, and installing to the wrong target on a guess is worse than one round-trip. An ambiguous prefix (`co` → `codex`/`copilot`) is terminal with NO suggestion, for the same reason. `formatTargetProblem` renders the two-line refusal, keeping the literal `--target must be one of:` substring that shipped assertions pin |
@@ -556,10 +556,13 @@ hook entry at all).
556
556
  `@dzhechkov/harness-core`, or the hub's own `harnessCoreDistDir()` for its own copies) — replacing
557
557
  a hard-coded `/usr/lib/node_modules/...` guess that failed on any other npm prefix (nvm,
558
558
  `/usr/local`, a differently-rooted global install).
559
- - `applyLegHookEntries()` — the exact `UserPromptSubmit`/`SessionStart` hook-registry entries
560
- `runSetup` merges into `.claude/settings.json` (a swallowed non-zero exit on the recall hook so a
561
- broken body never blocks a prompt; a detached `nohup` spawn for the daemon so `SessionStart` never
562
- waits on the ~1.5 s model load).
559
+ - `applyLegHookEntries(installRoot?)` — the exact `UserPromptSubmit`/`SessionStart` hook-registry
560
+ entries `runSetup` merges into `.claude/settings.json` (a swallowed non-zero exit on the recall
561
+ hook so a broken body never blocks a prompt; a detached `nohup` spawn for the daemon so
562
+ `SessionStart` never waits on the ~1.5 s model load). `installRoot` — an ABSOLUTE path — bakes both
563
+ commands as `node "<installRoot>/.claude/helpers/<file>" …`; omitting it (every zero-arg caller
564
+ before feature `apply-leg-install-root`) keeps the original `${CLAUDE_PROJECT_DIR:-.}`-relative
565
+ form. See "Install-root resolution" below for why the absolute form exists.
563
566
  - `applyLegStatus(root)` — the ONE measurement `dz doctor` and `dz parity` both read: do both helper
564
567
  files exist, at what version, and does `settings.json` actually reference them? Neither surface
565
568
  may declare the leg "installed" from a static capability table again (ADR-001 Decision 3) — a
@@ -604,6 +607,204 @@ instead of only ever checking the plain project path. The daemon also now prints
604
607
  (path N bytes)` and exits non-zero, never a silent "ready" for a socket that was never created
605
608
  (`APPLY_LEG_VERSION` bumped 3→4 for this and the resolver change).
606
609
 
610
+ ### One recall engine for hook and CLI (`hook-recall-hybrid-parity`, ADR-001, `APPLY_LEG_VERSION` 4→5)
611
+
612
+ The daemon's `op: recall` handler used to run its own brute-force cosine loop over the in-memory
613
+ mirror — a SECOND engine, diverging from `dz recall`'s `recallHybrid` (FTS5 lexical + semantic +
614
+ RRF). MEASURED (record 097ca040): 41.8% of taught lessons went unretrieved by either path over 48
615
+ days, and an exact lexical match at cosine 0.39 was silently dropped by the hook's cosine floor.
616
+
617
+ `embedDaemonSource(coreDistDir?)` now takes the SAME `coreDistDir` parameter `recallHookSource`
618
+ already had (default `null`, the hub's own portable marker) and inlines the SAME `loadCoreModule`
619
+ candidate-list pattern the hook uses, loading `index.js` from the resolved `CORE_DIST_DIR` to reach
620
+ `recallHybrid`/`patternRecordId`. `answerRecall(prompt, limit)` — the whole `op: recall` answer —
621
+ tries `hybridRecall` first: `core.recallHybrid(PROJECT, prompt, { limit, mode: 'hook',
622
+ deferExposures: true })`, raced via `Promise.race` against a `HOOK_RECALL_BUDGET_MS` timer (env,
623
+ default 500, always below the hook's own 800 ms socket timeout). On success the reply carries
624
+ `engine: 'hybrid'` and `hits[].score` normalized from the raw RRF sum into `[0,1]`
625
+ (`score / (2/(RRF_K+1))`, `RRF_K=60` — duplicated from `vector-tier.ts`'s own constant since this is
626
+ standalone generated text; `apply-leg-twins.test.ts` does not currently pin the two numerically
627
+ equal, only that both exist as literals — a numeric drift would need to be caught by the parity
628
+ test's own live assertions). On budget overrun / engine error / no resolvable core module, it falls
629
+ straight through to TODAY'S cosine leg (byte-identical) with `engine: 'cosine-fallback'` and a
630
+ `reason`.
631
+
632
+ `HybridRecallMode` in `vector-tier.ts` gained a fourth literal, `'hook'` — ranked identically to
633
+ `'hybrid'` (no semantic-weight change); its only role is to travel end to end for observability. The
634
+ ACTUAL mechanism that keeps a per-prompt recall from moving the lesson-bandit's exposure counters is
635
+ `deferExposures: true` plus never calling the returned `commitExposures(...)` — `recallHybrid`
636
+ already supported deferral for `dz recall --domain`'s own over-fetch-and-truncate case; the daemon is
637
+ simply a second caller of the same contract.
638
+
639
+ `pickEngine` (the one seam `recallHybrid`, `mirrorPatternsToVector`, `teachGuard` etc. all resolve
640
+ their engine through) now routes through `getOrOpenEngine(projectRoot)` instead of calling
641
+ `resolveVectorEngine` directly — a per-process cache keyed by `realpath(projectRoot)`, invalidated
642
+ whenever `.dz/agentdb.db`'s mtime, size, inode, OR write-generation counter (see "Store
643
+ write-generation counter" below) changes, so a long-lived caller (the daemon) pays the
644
+ `isPackageInstalled`/`probeNativeDep` walk once, not once per prompt. A short-lived CLI invocation is
645
+ unaffected (the cache is populated and discarded within one process either way — I-1 parity holds).
646
+ `getOrOpenEngine`'s second parameter is an injectable resolver (default `resolveVectorEngine`) purely
647
+ for spy-testability — two functions in the same ES module cannot be reliably intercepted by
648
+ `vi.spyOn` when one calls the other by its local name.
649
+
650
+ ### Install-root resolution (`apply-leg-install-root`, ADR-001, `APPLY_LEG_VERSION` 6→7)
651
+
652
+ Both generated files used to resolve their own store from `CLAUDE_PROJECT_DIR || cwd()` — the
653
+ SESSION's project, never the project the leg was actually installed into. A user-level install
654
+ (`dz setup --target claude-code --memory agentdb --project $HOME` — the owner's own layout, expecting
655
+ the leg everywhere `~/.claude/settings.json` is read) silently looked up a DIFFERENT project's `.dz/`
656
+ from every other session (issue #2, MEASURED on 0.8.25), and when `project === $HOME` the settings
657
+ command (`node "${CLAUDE_PROJECT_DIR:-.}/.claude/helpers/recall-hook.cjs"`) broke down to `Cannot
658
+ find module` from a foreign session, swallowed by `2>/dev/null || true`.
659
+
660
+ - **The hook and daemon now resolve `PROJECT` install-root-first.** `INSTALL_ROOT =
661
+ path.resolve(__dirname, '..', '..')` (the hook, `.cjs`) / `dirname(dirname(fileURLToPath(import.meta.url)))`
662
+ (the daemon, ESM) — the precedent is `claude-hooks-assets.ts`'s own `path.resolve(__dirname, '..',
663
+ '..')` for the destructive-guard hook. Order for the hook: `INSTALL_ROOT` (used when it owns a
664
+ `.dz/`) → `CLAUDE_PROJECT_DIR` → `cwd()`. Order for the daemon is the same shape but
665
+ `DZ_PROJECT_ROOT` stays the TOP override (an explicit project root always wins over the install
666
+ root) → `INSTALL_ROOT` → `cwd()`. The hook's existing `[dz-recall] engine=…` diagnostic line (stderr
667
+ only, never `additionalContext`) now also names `root=<path> (install|env|cwd)`.
668
+ - **`dz setup` now bakes an ABSOLUTE command.** `applyLegHookEntries(opts.projectRoot)` writes
669
+ `node "<installRoot>/.claude/helpers/recall-hook.cjs" …` instead of the
670
+ `${CLAUDE_PROJECT_DIR:-.}`-relative form — the deployed helper already bakes an absolute
671
+ `CORE_DIST_DIR`, so the relative command only masked that non-portability. `hookCommandInvokes` (and
672
+ therefore `applyLegStatus`) recognizes BOTH forms — a command is "ours" once the helper's full
673
+ `.claude/helpers/<file>` path follows a `node` invocation, whatever the prefix. A re-`dz setup` over
674
+ a pre-feature relative entry REPLACES it in place (same array position — `addIfMissing` in
675
+ `setup.ts` is now add-or-replace, never reorders), so an upgrade never leaves two entries for one
676
+ event.
677
+ - **Limits, named plainly.** One store per install: from a foreign project's session the hook injects
678
+ the INSTALL ROOT's lessons, not the session project's — that is the requested behavior (a
679
+ per-project store alongside a user-level one is a separate feature). The Codex host's own hook
680
+ (`codex-hooks-assets.ts:232`/`:373`) resolves its root from `payload.cwd || PWD || cwd()` — the
681
+ SAME class of weakness — and is deliberately left unfixed here (named, not silently patched); see
682
+ that file's own comments and the project backlog.
683
+
684
+ ### Store write-generation counter (`store-generation-counter`, `agentdb-index.ts`/`vector-tier.ts`)
685
+
686
+ `getOrOpenEngine`'s cache above invalidates on `.dz/agentdb.db`'s mtime/size/inode — AM-6 already
687
+ covers a temp+rename replace that preserves mtime (size or inode still differs), but a write of the
688
+ SAME byte length landing inside the same filesystem-mtime TICK, in place (no rename), could leave
689
+ all three signals coincidentally unchanged, serving the daemon a stale engine that never sees the
690
+ lesson `dz teach` just wrote. `indexPatternsToAgentdb` (`agentdb-index.ts` — the single write seam,
691
+ QR-6, every `dz teach`/consolidate/reindex/brain-mirror write) now bumps a sidecar counter file,
692
+ `<dbFile>.generation` (atomic tmp+`wx`+rename, next to the store itself so it travels with any copy),
693
+ on every successful write: `bumpStoreGeneration(projectRoot, dbPath?)` reads the current value via
694
+ `readStoreGeneration(projectRoot, dbPath?)` (missing/corrupt file degrades to `0` — the compatibility
695
+ floor for a store that predates this feature; a corrupt-but-numeric-looking value like `12junk` also
696
+ degrades to `0` — the parse is strict, `/^\d+$/`, not `Number.parseInt`'s leading-digits tolerance)
697
+ and writes `current + 1`. **Every exported store mutator bumps it** on its success path, not only
698
+ `indexPatternsToAgentdb`: `importVectorsToAgentdb`, `clearAgentdbQuarantine`, `deleteAgentdbByDzIds`,
699
+ `bumpAgentdbUses` and `reindexAgentdbRows` all call the same `bumpStoreGeneration` when they actually
700
+ changed a row (fix-round after independent Codex review, AM-1). The read-modify-write itself runs
701
+ under `withNamedLockSync(dirname(dbFile), 'store-generation', …)` (`named-lock.ts` — the repo's
702
+ advisory lock for a read-modify-write file store, `.claude/rules/cross-runtime-concurrency.md`; same
703
+ `dirname(dbFile)`-addressed pattern as `agentdb-reindex-marker.ts`'s `withAgentdbSnapshotLock`), with
704
+ the counter RE-READ from disk inside the lock — a bare read→compute→rename would let two concurrent
705
+ writers both publish the same `N+1` (one bump silently lost) or let a delayed writer overwrite a
706
+ later value with an earlier one (AM-2). Inside `indexPatternsToAgentdb`/`importVectorsToAgentdb` the
707
+ bump runs IMMEDIATELY after the row commit, BEFORE `writeEmbedManifest` — if the manifest write then
708
+ throws, the generation is already correct for the rows already on disk (AM-3). A write failure (a
709
+ jammed counter path, a full disk, an unresolvable path, or a lock that could not be acquired by its
710
+ deadline) is reported honestly on the index result (`generationBumped: false, generationReason`) but
711
+ NEVER fails the store write it accompanies, and `bumpStoreGeneration` itself never throws — telemetry
712
+ is not a gate (FR-4/AM-2/AM-4).
713
+
714
+ `getOrOpenEngine`'s cache-invalidation stat (`AgentdbDbStat`, `vector-tier.ts`) now carries
715
+ `generation` as a FOURTH independent signal alongside mtime/size/inode — a monotonically increasing
716
+ counter can never coincidentally match a stale cache entry the way mtime/size/inode occasionally can
717
+ on a coarse filesystem. Both public functions are exported from the package root
718
+ (`readStoreGeneration`, `bumpStoreGeneration`).
719
+
720
+ `dz doctor`'s "apply-leg alive (embed daemon)" check now sends one live `op: recall` probe
721
+ (`probeRecallEngine`, `operations.ts`, 1000 ms default — comfortably above the 500 ms production
722
+ budget default) when the socket exists, and appends `(engine: hybrid)` / `(engine: cosine-fallback)`
723
+ to the detail line on a successful reply. A non-listening path (every non-live doctor fixture in this
724
+ repo writes a plain file, never a real socket) fails the probe near-instantly, so every pre-existing
725
+ detail string is untouched.
726
+
727
+ **Honest NFR-1 finding.** MEASURED against the real 743-pattern production store on this machine,
728
+ 100 real prompts from `.dz/recall-usage.jsonl`, under the documented default budget: EVERY reply
729
+ fell back to `cosine-fallback` — `resolveAgentdbEmbedder` (`agentdb-index.ts`) reconstructs the
730
+ transformers pipeline on every call with no cross-call caching (measured standalone: 2–3.6 s/call,
731
+ no warm-up across repeats in one process), so a cold semantic leg routinely exceeds the 500 ms
732
+ budget. The p95/p50/reproducer script live in
733
+ `features/hook-recall-hybrid-parity/07_code_changes/change_manifest.md`. Fixing the embedder's own
734
+ cache is `agentdb-index.ts` work, outside this feature's touched files — named here as a follow-up,
735
+ not silently absorbed into a passing-looking number.
736
+
737
+ ### Green means injected, not merely present (`apply-leg-never-silent`, ADR-001, `APPLY_LEG_VERSION` 8→9)
738
+
739
+ Issue #2's second half: the recall hook exited 0 with NO stderr on every early-return path, and
740
+ `dz setup`'s own UserPromptSubmit command swallowed even a `Cannot find module` behind
741
+ `2>/dev/null || true` — a MEASURED state where `dz doctor` printed three green checks
742
+ (`apply-leg installed`, `apply-leg alive`, `memory hooks match config`) and `dz parity` printed
743
+ `✓ Self-learning … via UserPromptSubmit hook (auto recall)` while the leg injected nothing in every
744
+ session but one. Both instruments were reading FILE PRESENCE and STRUCTURAL WIRING as proof of
745
+ FUNCTION — the same class of defect ADR-001 Decision 3 already named for `applyLegStatus`, one layer
746
+ deeper.
747
+
748
+ - **The hook never exits silently now (FR-1).** Every early return in `main()` — `store-not-found`
749
+ (no `.dz/` under the resolved `PROJECT`), `socket-absent` (no daemon listening), `core-unavailable`
750
+ (`recall-hook-policy.js` unresolvable), `empty-prompt`, `no-hits` — prints exactly one line,
751
+ `[dz-recall] skipped reason=<reason> root=<path> (<source>) session=<path>`, on stderr before
752
+ returning. Exit code stays 0 — NEVER-BLOCK is unchanged; only the silence is gone.
753
+ - **`dz setup`'s own command no longer swallows that line (FR-2).** `applyLegHookEntries()`'s
754
+ UserPromptSubmit command dropped `2>/dev/null` (both the legacy relative form and the
755
+ `installRoot`-given absolute form); `|| true` stays, so a broken hook body still never fails a
756
+ prompt. **Where that line actually goes, MEASURED against the real Claude Code binary** (strings
757
+ extracted from `bin/claude.exe`, the `UserPromptSubmit` entry in its own hook-reference table):
758
+ `Exit code 0 - stdout shown to Claude` / `Exit code 2 - block processing, erase original prompt,
759
+ and show stderr to user only` / `Other exit codes - show stderr to user only` — on exit 0
760
+ (NEVER-BLOCK's exit code), stderr is named NOWHERE in that table. So the reason line is NOT for a
761
+ user watching Claude Code's own transcript (that channel does not exist for this hook on exit 0,
762
+ whatever "verbose mode" might suggest) — it is for the two readers who actually read a spawned
763
+ child's stderr directly: `probeApplyLeg`'s own `child_process` call below, and a human running the
764
+ hook by hand from a terminal.
765
+ - **A live, end-to-end probe replaces "files present" as the proof of function (FR-3/FR-4, ADR-001
766
+ Decision 1).** `probeApplyLeg(root, opts?)` — new export — spawns the REAL configured
767
+ UserPromptSubmit command (read back from `.claude/settings.json`, never reconstructed — a
768
+ reconstruction would silently stop testing the legacy relative form's own `${CLAUDE_PROJECT_DIR:-.}`
769
+ shell-expansion dependency) from a TEMPORARY cwd with `CLAUDE_PROJECT_DIR` pointing at that same
770
+ temp dir — the shape of a real session, never the project root itself. It writes a throwaway
771
+ "beacon" lesson into the lexical store via `recordPattern` (the same seam `dz teach` uses — no
772
+ embedding needed; the daemon's `recallHybrid` runs its LEXICAL leg synchronously and always, so an
773
+ exact-token beacon is found even under a starved hybrid budget) immediately before the probe and
774
+ removes it via `removePatternsByIds` in a `finally` — unconditionally, so a probe that throws,
775
+ times out, or never finds the leg alive still leaves the store exactly as it found it (proven by a
776
+ count-before == count-after test, not merely claimed). `ok: true` ONLY when the beacon's own token
777
+ comes back inside `additionalContext`; every other outcome is `ok: false` with a `reason` — taken
778
+ from the hook's own `[dz-recall] skipped reason=…` line when present (FR-1 feeding FR-3 directly),
779
+ else a best-effort description.
780
+ - `dz doctor` gains `apply-leg injects (live probe)`, evaluated whenever the existing
781
+ `apply-leg alive (embed daemon)` row's `applyLegWired` gate is true — green with the elapsed
782
+ time on success, red with the probe's `reason` on failure. A dedicated `try`/`catch`, separate
783
+ from the socket-alive check beside it: a probe failure must never suppress that already-useful
784
+ row, and vice versa.
785
+ - `dz parity`'s Self-learning cell now gates on `probeApplyLeg(cwd).ok`, not
786
+ `applyLegStatus(cwd).installed` alone — `computeParity` itself is untouched (FR-5 of the earlier
787
+ feature). A structurally-installed-but-silent leg reads `◐ … installed but silent: <reason>`,
788
+ never `✓ full`; the SAME `reason` `dz doctor`'s row prints, so the two instruments cannot
789
+ disagree about WHY a leg is dead, matching the fix-round-1 discipline `applyLegReasonMessage`
790
+ already established for `stale-version`/`unreadable`.
791
+ - `probeHookLiveness` (`operations.ts`) gained optional `cwd`/`env`/`timeoutMs` overrides (additive
792
+ — every pre-existing 2-arg call site, the Codex veto-hook liveness checks, is unaffected) and now
793
+ also returns `stdout` alongside `status`/`stderr`, reused by `probeApplyLeg` via a dynamic
794
+ `import()` rather than duplicating a `child_process` surface in `apply-leg.ts` (the core-boundary
795
+ IO ratchet stayed at its pinned `files:63 imports:69` — no new top-level IO import anywhere).
796
+ - `timeoutMs` defaults to 8000 ms. Measured (this environment, 2026-09-14/15): a
797
+ `store-not-found`/`socket-absent` probe returns in well under 200 ms; a live-daemon probe answers
798
+ in ~100-200 ms (matching ADR-001's own estimate) once warm. `dz doctor`/`dz parity` are
799
+ measurably slower by one probe's worth of wall time when the leg is wired — named here, not
800
+ hidden.
801
+ - **A limit, named plainly.** Under HEAVY concurrent load (this repo's own ~2700-test suite run in
802
+ one process), a live probe against a just-spawned daemon can occasionally exceed the hook's own
803
+ hardcoded 800 ms client-side socket timeout even when the daemon itself answers — a CPU-contention
804
+ flake, not a correctness defect; the daemon's own `HOOK_RECALL_BUDGET_MS` is independently
805
+ widenable, and `probeApplyLeg`'s `env` option exists for exactly this in tests. A real single
806
+ `dz doctor`/`dz parity` invocation never contends with 100+ concurrent test files.
807
+
607
808
  ## Run a plan without the Claude host
608
809
 
609
810
  `runWorkflow` (`workflow-run.ts`) is the PURE scheduler behind `dz workflow run`: it INTERPRETS a
@@ -812,6 +1013,28 @@ unchanged) from "the store file itself is unreadable" (corrupt file, permission
812
1013
  one `dz: <path> unreadable (<cause>) — falling back to the JSON store` line on stderr per process in
813
1014
  the second case, so a broken store no longer looks like plain "fewer lessons".
814
1015
 
1016
+ ## Embedder cache (`agentdb-index.ts` — `resolveAgentdbEmbedder`)
1017
+
1018
+ `resolveAgentdbEmbedder(projectRoot)` resolves agentdb's `EmbeddingService` and stands up the
1019
+ `@huggingface/transformers` pipeline behind it — MEASURED 2026-09-14 at 2-3.6s per call
1020
+ (`features/agentdb-embedder-cache/00_complexity_assessment.md`), because the model+dim for a
1021
+ project never changes within one process. It is now cached at module scope, keyed by
1022
+ `${agentdbDir}|${model}|${dim}` (so a `DZ_EMBED_MODEL`/`.dz/config.json` change — a different
1023
+ `resolveEmbedModel` result — gets its own entry rather than reusing a stale pipeline):
1024
+
1025
+ - Repeat calls for the same key return the **same object**, not a re-initialized one — MEASURED
1026
+ 2026-09-14 (`test/agentdb-embedder-cache.test.ts`, live model): cold call `2117ms`, warm call
1027
+ `1ms` (budget: ≤50ms).
1028
+ - The **promise** is cached, not the awaited result, so concurrent first callers for the same key
1029
+ join one in-flight initialization instead of racing two pipelines (`getAgentdbEmbedderCacheStats()`
1030
+ reports `initializations: 1` for two parallel first calls).
1031
+ - An `{error}` outcome (or a rejection) evicts its own cache entry immediately, so a failed init
1032
+ never "sticks" — the next call retries against the current config.
1033
+ - `resetAgentdbEmbedderCache()` clears the cache and the `initializations` counter; it exists for
1034
+ tests and future warm-start use only — `vector-tier.ts`/`backlog.ts` call sites are unaffected.
1035
+ - `getAgentdbEmbedderCacheStats()` returns `{ entries, initializations }` (`entries` = currently
1036
+ cached successful pipelines, `initializations` = pipelines actually started since the last reset).
1037
+
815
1038
  ## Consistent pre-reindex snapshot + rollback (`agentdb-snapshot.ts`)
816
1039
 
817
1040
  The pre-reindex snapshot `reindexAgentdbRows` takes before every reindex used to be a bare