@ashlr/hub 3.2.7 → 3.3.0

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 (149) hide show
  1. package/CHANGELOG.md +268 -0
  2. package/README.md +4 -1
  3. package/dist/build-identity.json +2 -2
  4. package/dist/cli/activation.d.ts +41 -0
  5. package/dist/cli/activation.js +485 -0
  6. package/dist/cli/activation.js.map +1 -0
  7. package/dist/cli/audit.js +18 -0
  8. package/dist/cli/audit.js.map +1 -1
  9. package/dist/cli/backlog.d.ts +0 -5
  10. package/dist/cli/backlog.js +18 -0
  11. package/dist/cli/backlog.js.map +1 -1
  12. package/dist/cli/comms.js +20 -0
  13. package/dist/cli/comms.js.map +1 -1
  14. package/dist/cli/completions.d.ts +6 -1
  15. package/dist/cli/completions.js +35 -11
  16. package/dist/cli/completions.js.map +1 -1
  17. package/dist/cli/daemon.js +10 -3
  18. package/dist/cli/daemon.js.map +1 -1
  19. package/dist/cli/demo.d.ts +0 -27
  20. package/dist/cli/demo.js +17 -0
  21. package/dist/cli/demo.js.map +1 -1
  22. package/dist/cli/help.js +19 -3
  23. package/dist/cli/help.js.map +1 -1
  24. package/dist/cli/inbox.d.ts +14 -2
  25. package/dist/cli/inbox.js +137 -23
  26. package/dist/cli/inbox.js.map +1 -1
  27. package/dist/cli/index.d.ts +1 -0
  28. package/dist/cli/index.js +10 -0
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/cli/onboard.d.ts +0 -13
  31. package/dist/cli/onboard.js +20 -0
  32. package/dist/cli/onboard.js.map +1 -1
  33. package/dist/cli/resources.js +15 -0
  34. package/dist/cli/resources.js.map +1 -1
  35. package/dist/cli/run.js +1 -1
  36. package/dist/cli/run.js.map +1 -1
  37. package/dist/cli/sandbox.js +43 -0
  38. package/dist/cli/sandbox.js.map +1 -1
  39. package/dist/cli/tui.d.ts +0 -6
  40. package/dist/cli/tui.js +14 -0
  41. package/dist/cli/tui.js.map +1 -1
  42. package/dist/core/config.js +70 -0
  43. package/dist/core/config.js.map +1 -1
  44. package/dist/core/daemon/activation-permit.d.ts +268 -33
  45. package/dist/core/daemon/activation-permit.js +1111 -106
  46. package/dist/core/daemon/activation-permit.js.map +1 -1
  47. package/dist/core/daemon/loop.d.ts +13 -1
  48. package/dist/core/daemon/loop.js +192 -9
  49. package/dist/core/daemon/loop.js.map +1 -1
  50. package/dist/core/daemon/runtime-activation-resident-start-protocol.d.ts +141 -0
  51. package/dist/core/daemon/runtime-activation-resident-start-protocol.js +462 -0
  52. package/dist/core/daemon/runtime-activation-resident-start-protocol.js.map +1 -0
  53. package/dist/core/daemon/runtime-activation-resident-start-runtime.d.ts +27 -0
  54. package/dist/core/daemon/runtime-activation-resident-start-runtime.js +28 -0
  55. package/dist/core/daemon/runtime-activation-resident-start-runtime.js.map +1 -0
  56. package/dist/core/daemon/runtime-activation-stopped-consumer.d.ts +139 -0
  57. package/dist/core/daemon/runtime-activation-stopped-consumer.js +995 -0
  58. package/dist/core/daemon/runtime-activation-stopped-consumer.js.map +1 -0
  59. package/dist/core/daemon/runtime-activation-stopped-runtime.d.ts +36 -0
  60. package/dist/core/daemon/runtime-activation-stopped-runtime.js +134 -0
  61. package/dist/core/daemon/runtime-activation-stopped-runtime.js.map +1 -0
  62. package/dist/core/daemon/runtime-activation-transaction.d.ts +41 -17
  63. package/dist/core/daemon/runtime-activation-transaction.js +80 -11
  64. package/dist/core/daemon/runtime-activation-transaction.js.map +1 -1
  65. package/dist/core/daemon/runtime-release-dependency-inventory.js +1 -0
  66. package/dist/core/daemon/runtime-release-dependency-inventory.js.map +1 -1
  67. package/dist/core/daemon/service-install-authority.d.ts +0 -4
  68. package/dist/core/daemon/service-install-authority.js +18 -3
  69. package/dist/core/daemon/service-install-authority.js.map +1 -1
  70. package/dist/core/dashboard.d.ts +10 -0
  71. package/dist/core/dashboard.js +103 -28
  72. package/dist/core/dashboard.js.map +1 -1
  73. package/dist/core/effective-config.js +7 -0
  74. package/dist/core/effective-config.js.map +1 -1
  75. package/dist/core/fleet/automerge-pass.js +33 -0
  76. package/dist/core/fleet/automerge-pass.js.map +1 -1
  77. package/dist/core/fleet/decisions-ledger.js +35 -12
  78. package/dist/core/fleet/decisions-ledger.js.map +1 -1
  79. package/dist/core/fleet/external-skill-git-capture.js +9 -1
  80. package/dist/core/fleet/external-skill-git-capture.js.map +1 -1
  81. package/dist/core/fleet/judge-decision-metadata.d.ts +8 -1
  82. package/dist/core/fleet/judge-decision-metadata.js +14 -1
  83. package/dist/core/fleet/judge-decision-metadata.js.map +1 -1
  84. package/dist/core/fleet/manager.d.ts +15 -1
  85. package/dist/core/fleet/manager.js +108 -30
  86. package/dist/core/fleet/manager.js.map +1 -1
  87. package/dist/core/fleet/post-merge-credit.d.ts +134 -9
  88. package/dist/core/fleet/post-merge-credit.js +350 -10
  89. package/dist/core/fleet/post-merge-credit.js.map +1 -1
  90. package/dist/core/fleet/self-improve.d.ts +34 -1
  91. package/dist/core/fleet/self-improve.js +106 -1
  92. package/dist/core/fleet/self-improve.js.map +1 -1
  93. package/dist/core/fleet/skill-library.d.ts +21 -2
  94. package/dist/core/fleet/skill-library.js +54 -12
  95. package/dist/core/fleet/skill-library.js.map +1 -1
  96. package/dist/core/fleet/status.js +1 -0
  97. package/dist/core/fleet/status.js.map +1 -1
  98. package/dist/core/fleet/worked-ledger.d.ts +22 -0
  99. package/dist/core/fleet/worked-ledger.js +32 -0
  100. package/dist/core/fleet/worked-ledger.js.map +1 -1
  101. package/dist/core/foundry/automerge-scope-policy.d.ts +2 -2
  102. package/dist/core/foundry/automerge-scope-policy.js +9 -2
  103. package/dist/core/foundry/automerge-scope-policy.js.map +1 -1
  104. package/dist/core/goals/store.js +18 -4
  105. package/dist/core/goals/store.js.map +1 -1
  106. package/dist/core/inbox/apply.js +5 -2
  107. package/dist/core/inbox/apply.js.map +1 -1
  108. package/dist/core/inbox/merge.d.ts +94 -1
  109. package/dist/core/inbox/merge.js +349 -16
  110. package/dist/core/inbox/merge.js.map +1 -1
  111. package/dist/core/integrations/github.d.ts +2 -0
  112. package/dist/core/integrations/github.js +2 -1
  113. package/dist/core/integrations/github.js.map +1 -1
  114. package/dist/core/learn/reflect.d.ts +42 -4
  115. package/dist/core/learn/reflect.js +56 -5
  116. package/dist/core/learn/reflect.js.map +1 -1
  117. package/dist/core/run/engines.d.ts +8 -0
  118. package/dist/core/run/engines.js +9 -1
  119. package/dist/core/run/engines.js.map +1 -1
  120. package/dist/core/run/learned-router.js +28 -3
  121. package/dist/core/run/learned-router.js.map +1 -1
  122. package/dist/core/run/orchestrator.d.ts +7 -2
  123. package/dist/core/run/orchestrator.js +86 -60
  124. package/dist/core/run/orchestrator.js.map +1 -1
  125. package/dist/core/swarm/rollback.d.ts +11 -0
  126. package/dist/core/swarm/rollback.js +54 -0
  127. package/dist/core/swarm/rollback.js.map +1 -1
  128. package/dist/core/swarm/runner.js +94 -8
  129. package/dist/core/swarm/runner.js.map +1 -1
  130. package/dist/core/types.d.ts +100 -1
  131. package/dist/core/types.js.map +1 -1
  132. package/dist/core/web/api.js +105 -7
  133. package/dist/core/web/api.js.map +1 -1
  134. package/dist/core/web/control.d.ts +20 -0
  135. package/dist/core/web/control.js +44 -32
  136. package/dist/core/web/control.js.map +1 -1
  137. package/dist/core/web/fleet-status-cache.d.ts +68 -0
  138. package/dist/core/web/fleet-status-cache.js +120 -0
  139. package/dist/core/web/fleet-status-cache.js.map +1 -0
  140. package/dist/core/web/public/next/assets/index-CY0U241s.css +1 -0
  141. package/dist/core/web/public/next/assets/index-Ct0mf8n4.js +69 -0
  142. package/dist/core/web/public/next/assets/index-Ct0mf8n4.js.map +1 -0
  143. package/dist/core/web/public/next/index.html +15 -0
  144. package/dist/release-dependency-inventory.json +1 -1
  145. package/docs/RUNTIME_ACTIVATION_AUTHORITY.md +126 -21
  146. package/docs/contracts/CONTRACT-M521.md +102 -0
  147. package/package.json +20 -3
  148. package/schema/config.schema.json +800 -5
  149. package/node_modules/which/CHANGELOG.md +0 -166
@@ -1,11 +1,56 @@
1
1
  /**
2
- * Positive post-merge learning authority.
2
+ * Positive post-merge learning authority — the release protocol.
3
3
  *
4
- * A realized merge is an immutable operational fact, not proof that the change
5
- * remained beneficial. Only the future purpose-built release protocol may mint
6
- * this label. Existing merge receipts and observation-only stability witnesses
7
- * deliberately do not satisfy it.
4
+ * A realized merge is an immutable operational fact, not proof that the
5
+ * change remained beneficial. This module is the ONLY place allowed to mint
6
+ * "post-merge credit release" authority, and it does so conservatively:
7
+ *
8
+ * 1. The proposal must carry an AUTHENTICATED realized-merge witness
9
+ * (authenticatedRealizedMergeOf — binds to this exact proposal via a
10
+ * local receipt HMAC or host reconciliation; shape alone is never
11
+ * authority).
12
+ * 2. At least MIN_POST_MERGE_OBSERVATION_WINDOW_MS must have elapsed since
13
+ * the merge was observed. The regression sentinel / outcome watcher
14
+ * (outcome-watcher.ts) sweeps merged proposals on a 6h throttle with a
15
+ * 7-day follow-up window; 14 days gives ~2x that follow-up window and
16
+ * dozens of scan cycles of headroom before we trust the silence.
17
+ * 3. The signed, observation-only post-merge-observations ledger
18
+ * (post-merge-observations.ts) must contain NO adverse record
19
+ * ('regressed' | 'reverted' | 'followed-up') for this exact
20
+ * (repo, proposalId, mergeCommit) event, and the ledger must have read
21
+ * back healthy/complete (a degraded read fails closed — we never treat
22
+ * "couldn't check" as "checked clean").
23
+ *
24
+ * That ledger is adverse-only (it never records a positive "we checked, it's
25
+ * clean" row), so eligibility here is inherently a "silence after a
26
+ * conservative window" heuristic, not a positive confirmation. That is a
27
+ * known, documented limitation — see sweepPostMergeCreditReleases' doc
28
+ * comment — not an oversight.
29
+ *
30
+ * MINTING: hasReleasedPostMergeCredit(labelBasis) verifies a self-describing,
31
+ * HMAC-signed token — NOT bare string equality against
32
+ * POST_MERGE_CREDIT_RELEASE_LABEL. This is deliberate: several downstream
33
+ * consumers persist labelBasis-shaped fields on structures that are, at least
34
+ * in tests and in caller-authored proposal/ledger data, fully attacker/
35
+ * test-controlled. A bare literal is trivially replayable; only a value
36
+ * carrying a valid local-key HMAC over the specific (repo, proposalId,
37
+ * mergeCommit) event can pass. isPostMergeCreditReleaseLabel() remains
38
+ * "structural recognition only" — matching the bare literal — for the
39
+ * existing defensive filters elsewhere that specifically reject the
40
+ * unauthenticated literal (e.g. skill-records.ts, decisions-ledger.ts).
8
41
  */
42
+ import { createHmac, timingSafeEqual } from 'node:crypto';
43
+ import { resolve } from 'node:path';
44
+ import { loadOrCreateKey, signSkillCardAttestation } from '../foundry/provenance.js';
45
+ import { authenticatedRealizedMergeOf } from '../inbox/realized-merge.js';
46
+ import { listProposalsDetailed } from '../inbox/store.js';
47
+ import { causalMetadataFromProposal } from '../learning/causal.js';
48
+ import { readDecisions, recordDecision } from './decisions-ledger.js';
49
+ import { postMergeObservationEventId, readPostMergeObservations } from './post-merge-observations.js';
50
+ import { skillCardContentHash } from './skill-attestation.js';
51
+ // ---------------------------------------------------------------------------
52
+ // Structural constants
53
+ // ---------------------------------------------------------------------------
9
54
  export const POST_MERGE_CREDIT_POLICY_VERSION = 'post-merge-credit-v1';
10
55
  export const POST_MERGE_CREDIT_RELEASE_LABEL = 'post-merge-credit-release-v1';
11
56
  /** Structural recognition only; this string is not release authority. */
@@ -13,11 +58,306 @@ export function isPostMergeCreditReleaseLabel(labelBasis) {
13
58
  return labelBasis === POST_MERGE_CREDIT_RELEASE_LABEL;
14
59
  }
15
60
  /**
16
- * No authenticated release verifier exists in this protocol version.
17
- * Deliberately return false even for the reserved label so raw ledgers,
18
- * injected dependencies, and caller-controlled metadata cannot mint credit.
61
+ * Minimum wall-clock time since the realized merge before credit MAY be
62
+ * released. See module doc for rationale (~2x the outcome-watcher follow-up
63
+ * window, dozens of throttled scan cycles of headroom).
64
+ */
65
+ export const MIN_POST_MERGE_OBSERVATION_WINDOW_MS = 14 * 24 * 60 * 60 * 1000;
66
+ /** Domain-separated HMAC payload prefix for minted credit tokens. */
67
+ const CREDIT_TOKEN_DOMAIN = 'ashlr:post-merge-credit-release:v1';
68
+ /**
69
+ * Both segments are truncated to 24 hex chars (96 bits each) — plenty for
70
+ * MAC-forgery infeasibility and event-identifier collision-resistance at
71
+ * fleet scale, and DELIBERATELY short for two independent reasons discovered
72
+ * empirically (see test/post-merge-credit-release.test.ts):
73
+ * 1. decisions-ledger.ts's generic secret-scrubber (util/scrub.ts) redacts
74
+ * any bare 64+ char hex run as a likely API key/hash — a 64-char segment
75
+ * would be silently corrupted the moment it passed through
76
+ * recordDecision().
77
+ * 2. recordDecision() -> sanitizeDecisionEntry() unconditionally ends with
78
+ * normalizeDecisionLearningFields() (learning/causal.ts), which re-runs
79
+ * causalMetadata() and applies boundedText(labelBasis, 80) — ANY
80
+ * labelBasis over 80 chars is silently truncated, with no way to opt
81
+ * out from a caller outside that module. The full token
82
+ * ("post-merge-credit-release-v1:" (30 chars) + eventRef + ':' + mac)
83
+ * must fit in 80 chars total, so each hex segment is capped at 24
84
+ * (30 + 24 + 1 + 24 = 79).
85
+ * Neither constraint is owned by this module; 24 hex chars satisfies both
86
+ * with margin.
87
+ */
88
+ const CREDIT_REF_HEX_CHARS = 24;
89
+ /** `post-merge-credit-release-v1:<event-ref-hex32>:<hmac-hex32>` */
90
+ const CREDIT_TOKEN_RE = new RegExp(`^post-merge-credit-release-v1:([a-f0-9]{${CREDIT_REF_HEX_CHARS}}):([a-f0-9]{${CREDIT_REF_HEX_CHARS}})$`);
91
+ /** Truncate a full sha256 eventId to the reference used inside the token. */
92
+ function creditEventRef(eventId) {
93
+ return eventId.slice(0, CREDIT_REF_HEX_CHARS);
94
+ }
95
+ function computeCreditMac(eventRef) {
96
+ try {
97
+ return createHmac('sha256', loadOrCreateKey())
98
+ .update(`${CREDIT_TOKEN_DOMAIN}:${eventRef}`)
99
+ .digest('hex')
100
+ .slice(0, CREDIT_REF_HEX_CHARS);
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ function equalHex(left, right) {
107
+ try {
108
+ const a = Buffer.from(left, 'hex');
109
+ const b = Buffer.from(right, 'hex');
110
+ return a.length === b.length && a.length > 0 && timingSafeEqual(a, b);
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ }
116
+ /**
117
+ * Verify that `labelBasis` is a validly-signed post-merge-credit release
118
+ * token minted by evaluatePostMergeCreditRelease. Pure, synchronous
119
+ * (host-local key read only), never throws. Deliberately rejects the bare
120
+ * POST_MERGE_CREDIT_RELEASE_LABEL literal and any other string that is not a
121
+ * correctly-formed, correctly-signed token — so raw ledgers, injected
122
+ * dependencies, and caller-controlled metadata cannot mint credit.
123
+ */
124
+ export function hasReleasedPostMergeCredit(labelBasis) {
125
+ try {
126
+ if (typeof labelBasis !== 'string')
127
+ return false;
128
+ const match = CREDIT_TOKEN_RE.exec(labelBasis);
129
+ if (!match)
130
+ return false;
131
+ const eventRef = match[1];
132
+ const mac = match[2];
133
+ const expected = computeCreditMac(eventRef);
134
+ if (!expected)
135
+ return false;
136
+ return equalHex(mac, expected);
137
+ }
138
+ catch {
139
+ return false;
140
+ }
141
+ }
142
+ /**
143
+ * Determine whether `proposal` is eligible for post-merge credit release
144
+ * RIGHT NOW, and mint the signed token when it is. Fail-closed on every
145
+ * ambiguous path (missing witness, unparseable timestamp, window not yet
146
+ * elapsed, degraded/incomplete observation-ledger read, any adverse
147
+ * observation for this exact merge event, signing failure). Never throws.
148
+ *
149
+ * This function does NOT persist anything — see sweepPostMergeCreditReleases
150
+ * for the write-back that records a minted label to the decisions ledger.
151
+ */
152
+ export function evaluatePostMergeCreditRelease(proposal, opts = {}) {
153
+ try {
154
+ if (!proposal || typeof proposal.id !== 'string' || proposal.id.length === 0) {
155
+ return { eligible: false, label: null, reason: 'no-authenticated-merge-witness' };
156
+ }
157
+ const evidence = authenticatedRealizedMergeOf(proposal);
158
+ if (!evidence || typeof proposal.repo !== 'string' || proposal.repo.length === 0) {
159
+ return { eligible: false, label: null, reason: 'no-authenticated-merge-witness' };
160
+ }
161
+ const observedAtIso = evidence.source === 'github-host'
162
+ ? evidence.reconciliation.observedAt
163
+ : evidence.observedAt;
164
+ const mergedAtMs = Date.parse(observedAtIso);
165
+ if (!Number.isFinite(mergedAtMs)) {
166
+ return { eligible: false, label: null, reason: 'invalid-merge-timestamp' };
167
+ }
168
+ const nowMs = opts.nowMs ?? Date.now();
169
+ if (nowMs < mergedAtMs || nowMs - mergedAtMs < MIN_POST_MERGE_OBSERVATION_WINDOW_MS) {
170
+ return { eligible: false, label: null, reason: 'observation-window-not-elapsed' };
171
+ }
172
+ let repoAbs;
173
+ try {
174
+ repoAbs = resolve(proposal.repo);
175
+ }
176
+ catch {
177
+ return { eligible: false, label: null, reason: 'no-authenticated-merge-witness' };
178
+ }
179
+ const eventId = postMergeObservationEventId({
180
+ repo: repoAbs,
181
+ proposalId: proposal.id,
182
+ mergeCommit: evidence.mergeCommitOid.toLowerCase(),
183
+ });
184
+ // Adverse-only ledger: absence of a row is the only "clean" signal it can
185
+ // ever give us. A degraded/incomplete read must NOT be treated as clean.
186
+ const read = readPostMergeObservations({ requireComplete: true });
187
+ if (read.sourceState === 'degraded' || !read.complete) {
188
+ return { eligible: false, label: null, reason: 'observation-ledger-degraded' };
189
+ }
190
+ if (read.observations.some((observation) => observation.eventId === eventId)) {
191
+ return { eligible: false, label: null, reason: 'adverse-outcome-observed' };
192
+ }
193
+ const eventRef = creditEventRef(eventId);
194
+ const mac = computeCreditMac(eventRef);
195
+ if (!mac)
196
+ return { eligible: false, label: null, reason: 'signing-unavailable' };
197
+ return {
198
+ eligible: true,
199
+ label: `${POST_MERGE_CREDIT_RELEASE_LABEL}:${eventRef}:${mac}`,
200
+ reason: 'clean-window-elapsed',
201
+ };
202
+ }
203
+ catch {
204
+ return { eligible: false, label: null, reason: 'error' };
205
+ }
206
+ }
207
+ // ---------------------------------------------------------------------------
208
+ // sweepPostMergeCreditReleases — periodic write-back
209
+ // ---------------------------------------------------------------------------
210
+ /**
211
+ * Bound on candidates inspected per sweep pass — mirrors outcome-watcher.ts's
212
+ * own bounding so a large backlog degrades gracefully instead of blocking.
213
+ */
214
+ const MAX_SWEEP_CANDIDATES = 200;
215
+ /**
216
+ * Sweep applied proposals for newly-eligible post-merge credit and record it
217
+ * to the decisions ledger (a new 'merged' row with the signed labelBasis
218
+ * token — decisions-ledger.ts's own newest-row-wins semantics let this
219
+ * supersede the plain merge-fact row written at merge time). Idempotent: a
220
+ * proposal that already carries a released decision row is left alone.
221
+ *
222
+ * KNOWN LIMITATION (by design, not a bug): the post-merge-observations ledger
223
+ * only ever records ADVERSE outcomes. This sweep therefore releases credit on
224
+ * "no adverse record after a conservative window", not on a positive
225
+ * "actively confirmed clean" signal — there is no such positive signal
226
+ * anywhere in the fleet today. If a repo's outcome-watcher coverage were
227
+ * somehow disabled for a specific proposal's repo (not enrolled, or the local
228
+ * clone was deleted), this sweep cannot detect that and would release credit
229
+ * on elapsed time alone. The MIN_POST_MERGE_OBSERVATION_WINDOW_MS margin
230
+ * exists specifically to bound that risk; closing it fully would require
231
+ * outcome-watcher.ts (unowned here) to also persist a positive "swept clean"
232
+ * marker per merge event.
233
+ *
234
+ * SCALING NOTE: unlike outcome-watcher.ts, this sweep has no persistent
235
+ * cursor — it re-scans applied proposals from the top of listProposalsDetailed
236
+ * every pass, bounded by MAX_SWEEP_CANDIDATES. At small-to-moderate applied-
237
+ * proposal counts this converges fine (already-released proposals are
238
+ * skipped instantly via the decisions-ledger dedup check, so the cap mostly
239
+ * matters for proposals still awaiting their window). At very large scale a
240
+ * persistent cursor (mirroring monitoring-cursor.ts) would be needed to
241
+ * guarantee every proposal eventually gets swept; out of scope here.
242
+ *
243
+ * Never throws.
244
+ */
245
+ export function sweepPostMergeCreditReleases(opts = {}) {
246
+ const result = {
247
+ scanned: 0, released: 0, skipped: 0, sourceComplete: true,
248
+ };
249
+ try {
250
+ const nowMs = opts.nowMs ?? Date.now();
251
+ let proposals;
252
+ if (typeof opts.listProposals === 'function') {
253
+ try {
254
+ proposals = opts.listProposals();
255
+ }
256
+ catch {
257
+ proposals = [];
258
+ }
259
+ }
260
+ else {
261
+ const read = listProposalsDetailed({ status: 'applied', requireComplete: true });
262
+ if (!read.complete || read.sourceState === 'degraded') {
263
+ result.sourceComplete = false;
264
+ return result;
265
+ }
266
+ proposals = read.proposals;
267
+ }
268
+ let inspected = 0;
269
+ for (const proposal of proposals) {
270
+ if (proposal.status !== 'applied')
271
+ continue;
272
+ if (inspected >= MAX_SWEEP_CANDIDATES) {
273
+ result.sourceComplete = false;
274
+ break;
275
+ }
276
+ inspected++;
277
+ result.scanned++;
278
+ try {
279
+ const existing = readDecisions({ proposalId: proposal.id, requireComplete: true });
280
+ const existingQuality = existing.sourceQuality;
281
+ if (existingQuality && (existingQuality.sourceState === 'degraded' || existingQuality.complete === false)) {
282
+ result.skipped++;
283
+ result.sourceComplete = false;
284
+ continue;
285
+ }
286
+ const alreadyReleased = existing.some((entry) => entry.action === 'merged' && hasReleasedPostMergeCredit(entry.labelBasis));
287
+ if (alreadyReleased)
288
+ continue;
289
+ const eligibility = evaluatePostMergeCreditRelease(proposal, { nowMs });
290
+ if (!eligibility.eligible || !eligibility.label) {
291
+ result.skipped++;
292
+ continue;
293
+ }
294
+ const ts = new Date(nowMs).toISOString();
295
+ recordDecision({
296
+ ts,
297
+ proposalId: proposal.id,
298
+ // NOTE: labelBasis is intentionally NOT routed through
299
+ // causalMetadataFromProposal's `input.labelBasis` — causalMetadata()
300
+ // (learning/causal.ts) applies boundedText(value, 80) to that field,
301
+ // which would silently truncate our signed token (>80 chars) into
302
+ // an unverifiable fragment. Set it directly below instead, after the
303
+ // spread, so the full token survives.
304
+ ...causalMetadataFromProposal(proposal, { ts, learningSource: 'outcome-record' }),
305
+ action: 'merged',
306
+ verdict: 'applied',
307
+ labelBasis: eligibility.label,
308
+ repo: proposal.repo ?? '',
309
+ engine: proposal.engineModel ?? '',
310
+ model: '',
311
+ });
312
+ result.released++;
313
+ }
314
+ catch {
315
+ result.skipped++;
316
+ }
317
+ }
318
+ return result;
319
+ }
320
+ catch {
321
+ return result;
322
+ }
323
+ }
324
+ // ---------------------------------------------------------------------------
325
+ // attestPostMergeCreditSkillCard — dedicated signing boundary
326
+ // ---------------------------------------------------------------------------
327
+ /**
328
+ * Sign a SkillCard that legitimately carries released post-merge credit.
329
+ *
330
+ * skill-attestation.ts's generic attestSkillCard() deliberately refuses any
331
+ * card for which hasReleasedPostMergeCredit(card.labelBasis) is true — its
332
+ * signature payload doesn't cover the credit-release claim, so it must not
333
+ * be the thing vouching for it. This module owns that proof, so it owns this
334
+ * signing boundary too: same crypto primitive (signSkillCardAttestation),
335
+ * same payload shape, so verifyAttestedSkillCard() on the read side still
336
+ * validates cards signed here — only the gating differs (this function signs
337
+ * ONLY when hasReleasedPostMergeCredit is true; attestSkillCard signs only
338
+ * when it is false).
339
+ *
340
+ * Never throws.
19
341
  */
20
- export function hasReleasedPostMergeCredit(_labelBasis) {
21
- return false;
342
+ export function attestPostMergeCreditSkillCard(card) {
343
+ try {
344
+ if (!hasReleasedPostMergeCredit(card.labelBasis))
345
+ return null;
346
+ const diffHash = card.verification?.diffHash;
347
+ if (!card.proposalId || !diffHash)
348
+ return null;
349
+ const contentHash = skillCardContentHash(card);
350
+ const attestation = signSkillCardAttestation({
351
+ contentHash,
352
+ skillId: card.skillId,
353
+ revision: card.revision,
354
+ proposalId: card.proposalId,
355
+ diffHash,
356
+ });
357
+ return attestation ? { ...card, contentHash, attestation } : null;
358
+ }
359
+ catch {
360
+ return null;
361
+ }
22
362
  }
23
363
  //# sourceMappingURL=post-merge-credit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"post-merge-credit.js","sourceRoot":"","sources":["../../../src/core/fleet/post-merge-credit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,sBAA+B,CAAC;AAChF,MAAM,CAAC,MAAM,+BAA+B,GAAG,8BAAuC,CAAC;AAEvF,yEAAyE;AACzE,MAAM,UAAU,6BAA6B,CAAC,UAAmB;IAC/D,OAAO,UAAU,KAAK,+BAA+B,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,WAAoB;IAC7D,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"post-merge-credit.js","sourceRoot":"","sources":["../../../src/core/fleet/post-merge-credit.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@
23
23
  * - TELEMETRY: a usage counter is appended to the decisions ledger under
24
24
  * action 'self-improve:written' for observability (no PII, no secrets).
25
25
  */
26
- import type { AshlrConfig, GenomeEntry } from '../types.js';
26
+ import type { AshlrConfig, DecisionEntry, GenomeEntry } from '../types.js';
27
27
  /** Hard cap on total characters injected from anti-playbook entries per run. */
28
28
  export declare const ANTI_PLAYBOOK_INJECT_CAP = 800;
29
29
  type RejectionVerdict = 'review' | 'noise' | 'harmful';
@@ -51,6 +51,39 @@ export declare function deriveLesson(verdict: RejectionVerdict, _reasoning: stri
51
51
  * @param cfg Fleet config.
52
52
  */
53
53
  export declare function learnFromRejection(proposalId: string, proposalTitle: string, verdict: string, reasoning: string, cfg: AshlrConfig): void;
54
+ export interface RejectionLearningSweepResult {
55
+ /** Distinct proposals with a rejection-shaped judge verdict inspected. */
56
+ scanned: number;
57
+ /** Proposals for which learnFromRejection actually wrote a lesson. */
58
+ written: number;
59
+ /** Proposals inspected but skipped (already written, or a transient error). */
60
+ skipped: number;
61
+ /** False when the decisions source was incomplete/degraded, or the
62
+ * candidate cap was hit — a hint the pass should be retried. */
63
+ sourceComplete: boolean;
64
+ }
65
+ /**
66
+ * Sweep the decisions ledger for judged rejection verdicts (review | noise |
67
+ * harmful) and call learnFromRejection() for each one that hasn't already
68
+ * produced a genome lesson.
69
+ *
70
+ * learnFromRejection's ONLY prior caller was automerge-pass.ts, reached
71
+ * exclusively from inside the auto-merge pass (cfg.foundry.autoMerge.enabled
72
+ * === true). With auto-merge off — the default — a judge verdict could still
73
+ * be recorded (manual `ashlr judge`, a shadow/observe-only judge pass, etc.)
74
+ * but the fleet never learned from it. This sweep reads the SAME
75
+ * decisions-ledger 'judged' rows regardless of what produced them, so a
76
+ * rejected or failed run teaches the fleet whether or not auto-merge is on.
77
+ *
78
+ * Idempotent: a proposalId that already has a 'self-improve:written'
79
+ * telemetry row is skipped. Bounded, fail-open-on-nothing-to-do,
80
+ * fail-closed-on-source-degradation (sourceComplete:false signals "retry
81
+ * later", never treated as "nothing to learn"). Never throws.
82
+ */
83
+ export declare function sweepRejectionLearning(cfg: AshlrConfig, opts?: {
84
+ sinceMs?: number;
85
+ listDecisions?: () => DecisionEntry[];
86
+ }): RejectionLearningSweepResult;
54
87
  /**
55
88
  * Filter a list of genome entries to anti-playbook entries suitable for
56
89
  * inject-time grounding. Applies:
@@ -24,7 +24,7 @@
24
24
  * action 'self-improve:written' for observability (no PII, no secrets).
25
25
  */
26
26
  import { appendHubEntry } from '../genome/store.js';
27
- import { recordDecision } from './decisions-ledger.js';
27
+ import { readDecisions, readDecisionsDetailed, recordDecision } from './decisions-ledger.js';
28
28
  // ---------------------------------------------------------------------------
29
29
  // Constants
30
30
  // ---------------------------------------------------------------------------
@@ -118,6 +118,111 @@ export function learnFromRejection(proposalId, proposalTitle, verdict, reasoning
118
118
  }
119
119
  }
120
120
  // ---------------------------------------------------------------------------
121
+ // Public: sweepRejectionLearning — auto-merge-independent reachability
122
+ // ---------------------------------------------------------------------------
123
+ /**
124
+ * Bound on judged-decision candidates inspected per sweep pass.
125
+ */
126
+ const MAX_REJECTION_SWEEP_CANDIDATES = 200;
127
+ /**
128
+ * Sweep the decisions ledger for judged rejection verdicts (review | noise |
129
+ * harmful) and call learnFromRejection() for each one that hasn't already
130
+ * produced a genome lesson.
131
+ *
132
+ * learnFromRejection's ONLY prior caller was automerge-pass.ts, reached
133
+ * exclusively from inside the auto-merge pass (cfg.foundry.autoMerge.enabled
134
+ * === true). With auto-merge off — the default — a judge verdict could still
135
+ * be recorded (manual `ashlr judge`, a shadow/observe-only judge pass, etc.)
136
+ * but the fleet never learned from it. This sweep reads the SAME
137
+ * decisions-ledger 'judged' rows regardless of what produced them, so a
138
+ * rejected or failed run teaches the fleet whether or not auto-merge is on.
139
+ *
140
+ * Idempotent: a proposalId that already has a 'self-improve:written'
141
+ * telemetry row is skipped. Bounded, fail-open-on-nothing-to-do,
142
+ * fail-closed-on-source-degradation (sourceComplete:false signals "retry
143
+ * later", never treated as "nothing to learn"). Never throws.
144
+ */
145
+ export function sweepRejectionLearning(cfg, opts = {}) {
146
+ const result = {
147
+ scanned: 0, written: 0, skipped: 0, sourceComplete: true,
148
+ };
149
+ try {
150
+ const foundry = cfg.foundry;
151
+ if (foundry?.['selfImprove'] === false)
152
+ return result;
153
+ let decisions;
154
+ if (typeof opts.listDecisions === 'function') {
155
+ try {
156
+ decisions = opts.listDecisions();
157
+ }
158
+ catch {
159
+ decisions = [];
160
+ }
161
+ }
162
+ else {
163
+ const read = readDecisionsDetailed({
164
+ requireComplete: true,
165
+ ...(opts.sinceMs !== undefined ? { sinceMs: opts.sinceMs } : {}),
166
+ });
167
+ if (!read.complete || read.sourceState === 'degraded') {
168
+ result.sourceComplete = false;
169
+ return result;
170
+ }
171
+ decisions = read.decisions;
172
+ }
173
+ // Newest judged verdict per proposalId — mirrors the isNewerDecision
174
+ // pattern used elsewhere (feedback.ts, quality-metrics.ts). The compare
175
+ // must run over EVERY judged verdict (not just rejection-shaped ones):
176
+ // if a proposal's latest verdict is 'ship', a stale earlier
177
+ // noise/harmful/review verdict must not be taught as a lesson — it was
178
+ // superseded.
179
+ const latestJudged = new Map();
180
+ for (const entry of decisions) {
181
+ if (entry.action !== 'judged')
182
+ continue;
183
+ const existing = latestJudged.get(entry.proposalId);
184
+ if (!existing || Date.parse(entry.ts) > Date.parse(existing.ts)) {
185
+ latestJudged.set(entry.proposalId, entry);
186
+ }
187
+ }
188
+ const latestRejection = new Map();
189
+ for (const [proposalId, entry] of latestJudged) {
190
+ const verdict = (entry.verdict ?? '').toLowerCase();
191
+ if (isRejection(verdict))
192
+ latestRejection.set(proposalId, entry);
193
+ }
194
+ let inspected = 0;
195
+ for (const [proposalId, entry] of latestRejection) {
196
+ if (inspected >= MAX_REJECTION_SWEEP_CANDIDATES) {
197
+ result.sourceComplete = false;
198
+ break;
199
+ }
200
+ inspected++;
201
+ result.scanned++;
202
+ try {
203
+ const existing = readDecisions({ proposalId, requireComplete: true });
204
+ const existingQuality = existing.sourceQuality;
205
+ if (existingQuality && (existingQuality.sourceState === 'degraded' || existingQuality.complete === false)) {
206
+ result.skipped++;
207
+ result.sourceComplete = false;
208
+ continue;
209
+ }
210
+ if (existing.some((row) => row.action === 'self-improve:written'))
211
+ continue;
212
+ learnFromRejection(proposalId, '', entry.verdict ?? 'review', '', cfg);
213
+ result.written++;
214
+ }
215
+ catch {
216
+ result.skipped++;
217
+ }
218
+ }
219
+ return result;
220
+ }
221
+ catch {
222
+ return result;
223
+ }
224
+ }
225
+ // ---------------------------------------------------------------------------
121
226
  // Curator: curateAntiPlaybooks
122
227
  // ---------------------------------------------------------------------------
123
228
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"self-improve.js","sourceRoot":"","sources":["../../../src/core/fleet/self-improve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;gDACgD;AAChD,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB,gFAAgF;AAChF,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C,gEAAgE;AAChE,MAAM,GAAG,GAAG,oBAAoB,CAAC;AASjC,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,SAAS,CAAC;AAC5D,CAAC;AAED,8EAA8E;AAC9E,kDAAkD;AAClD,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAyB,EACzB,UAAkB,EAClB,cAAsB;IAEtB,MAAM,eAAe,GAAqC;QACxD,MAAM,EAAE,oGAAoG;QAC5G,KAAK,EAAE,4GAA4G;QACnH,OAAO,EAAE,mHAAmH;KAC7H,CAAC;IACF,OAAO,8BAA8B,OAAO,MAAM,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAkB,EAClB,aAAqB,EACrB,OAAe,EACf,SAAiB,EACjB,GAAgB;IAEhB,wEAAwE;IACxE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,GAAG,CAAC,OAA8C,CAAC;QACnE,IAAI,OAAO,EAAE,CAAC,aAAa,CAAC,KAAK,KAAK;YAAE,OAAO;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,kCAAkC;IAClC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;QAAE,OAAO;IAElC,+BAA+B;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,8BAA8B,OAAO,EAAE,CAAC;QAEtD,cAAc,CAAC;YACb,KAAK;YACL,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,CAAC,GAAG,EAAE,WAAW,OAAO,EAAE,EAAE,YAAY,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACxE,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;IAED,yEAAyE;IACzE,IAAI,CAAC;QACH,cAAc,CAAC;YACb,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,UAAU;YACV,MAAM,EAAE,sBAAwE;YAChF,MAAM,EAAE,WAAW,OAAO,EAAE;YAC5B,IAAI,EAAE,EAAE;YACR,MAAM,EAAE,EAAE;YACV,KAAK,EAAE,EAAE;SAC8B,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAsB;IACxD,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAE/D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,UAAU,CAAC;QAEtD,kDAAkD;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACjC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxC,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC5B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAAE,OAAO,IAAI,CAAC,CAAC,wBAAwB;gBAC/D,OAAO,EAAE,IAAI,QAAQ,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,0BAA0B;QAC1B,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAClB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YACjC,OAAO,EAAE,GAAG,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;QAEH,mDAAmD;QACnD,MAAM,MAAM,GAAkB,EAAE,CAAC;QACjC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5C,IAAI,SAAS,GAAG,IAAI,GAAG,wBAAwB;gBAAE,MAAM;YACvD,SAAS,IAAI,IAAI,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"self-improve.js","sourceRoot":"","sources":["../../../src/core/fleet/self-improve.ts"],"names":[],"mappings":"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"}
@@ -69,7 +69,26 @@ export declare function distillWorkflow(proposal: Proposal): string;
69
69
  */
70
70
  export declare function learnFromApplied(proposal: Proposal, cfg: AshlrConfig): void;
71
71
  /**
72
- * Skill injection remains disabled until a distinct post-merge release proof
73
- * and verifier exist. Tags and generic genome persistence are not authority.
72
+ * Genome hub entries are unsigned free text — appendHubEntry has no
73
+ * authentication, and (per test/m243.skill-library.test.ts's "release
74
+ * verifier unavailable" suite) a forged entry carrying tags ['m243:skill',
75
+ * 'credit:released-v1'] must not be trusted into future-agent prompts just
76
+ * because it carries the right tag. There is no way to cryptographically
77
+ * verify a GenomeEntry's authenticity — genome rows carry no attestation
78
+ * field at all, unlike SkillCard (contentHash + attestation).
79
+ *
80
+ * Real positive-credit skill recall is NOT this function's job: it already
81
+ * exists, and already works, via skill-retrieval.ts's
82
+ * selectVerifiedSkills()/inspectVerifiedSkillCorpus() — those read the
83
+ * cryptographically-signed SkillCard corpus (skill-records.ts) and verify
84
+ * each card's attestation (verifyAttestedSkillCard) plus
85
+ * hasReleasedPostMergeCredit(card.labelBasis) before ever surfacing it. That
86
+ * pathway is what lights up once evaluatePostMergeCreditRelease() /
87
+ * attestPostMergeCreditSkillCard() start minting real signed cards — no
88
+ * change to this function was needed or safe to make.
89
+ *
90
+ * curateSkills() therefore stays a closed stub, matching curateAntiPlaybooks'
91
+ * signature for API symmetry with self-improve.ts but never promoting
92
+ * unsigned genome text to injected grounding. Pure; never throws.
74
93
  */
75
94
  export declare function curateSkills(_entries: GenomeEntry[]): GenomeEntry[];