instar 1.3.888 → 1.3.890

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 (180) hide show
  1. package/dashboard/glance.js +1 -0
  2. package/dashboard/index.html +72 -0
  3. package/dashboard/preferences-learning.js +87 -5
  4. package/dist/commands/init.js +3 -0
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/commands/server.d.ts.map +1 -1
  7. package/dist/commands/server.js +112 -0
  8. package/dist/commands/server.js.map +1 -1
  9. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  10. package/dist/config/ConfigDefaults.js +29 -0
  11. package/dist/config/ConfigDefaults.js.map +1 -1
  12. package/dist/core/BackupManager.d.ts.map +1 -1
  13. package/dist/core/BackupManager.js +31 -15
  14. package/dist/core/BackupManager.js.map +1 -1
  15. package/dist/core/ClassReviewReplicatedStore.d.ts +27 -0
  16. package/dist/core/ClassReviewReplicatedStore.d.ts.map +1 -0
  17. package/dist/core/ClassReviewReplicatedStore.js +153 -0
  18. package/dist/core/ClassReviewReplicatedStore.js.map +1 -0
  19. package/dist/core/CoherenceJournal.d.ts +1 -1
  20. package/dist/core/CoherenceJournal.d.ts.map +1 -1
  21. package/dist/core/CoherenceJournal.js +9 -2
  22. package/dist/core/CoherenceJournal.js.map +1 -1
  23. package/dist/core/InitiativeTracker.d.ts +7 -0
  24. package/dist/core/InitiativeTracker.d.ts.map +1 -1
  25. package/dist/core/InitiativeTracker.js +17 -0
  26. package/dist/core/InitiativeTracker.js.map +1 -1
  27. package/dist/core/PostUpdateMigrator.d.ts +2 -1
  28. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  29. package/dist/core/PostUpdateMigrator.js +153 -0
  30. package/dist/core/PostUpdateMigrator.js.map +1 -1
  31. package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
  32. package/dist/core/WriteDomainRegistry.js +32 -0
  33. package/dist/core/WriteDomainRegistry.js.map +1 -1
  34. package/dist/core/canonicalPipelineRegistry.d.ts +73 -0
  35. package/dist/core/canonicalPipelineRegistry.d.ts.map +1 -0
  36. package/dist/core/canonicalPipelineRegistry.js +56 -0
  37. package/dist/core/canonicalPipelineRegistry.js.map +1 -0
  38. package/dist/core/canonicalPipelineRuntimeEvidence.d.ts +13 -0
  39. package/dist/core/canonicalPipelineRuntimeEvidence.d.ts.map +1 -0
  40. package/dist/core/canonicalPipelineRuntimeEvidence.js +15 -0
  41. package/dist/core/canonicalPipelineRuntimeEvidence.js.map +1 -0
  42. package/dist/core/componentCategories.d.ts.map +1 -1
  43. package/dist/core/componentCategories.js +3 -0
  44. package/dist/core/componentCategories.js.map +1 -1
  45. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  46. package/dist/core/devGatedFeatures.js +36 -0
  47. package/dist/core/devGatedFeatures.js.map +1 -1
  48. package/dist/core/machineCoherenceManifest.d.ts +2 -2
  49. package/dist/core/machineCoherenceManifest.d.ts.map +1 -1
  50. package/dist/core/machineCoherenceManifest.js +2 -1
  51. package/dist/core/machineCoherenceManifest.js.map +1 -1
  52. package/dist/core/types.d.ts +36 -1
  53. package/dist/core/types.d.ts.map +1 -1
  54. package/dist/core/types.js.map +1 -1
  55. package/dist/core/version-skew.d.ts +1 -1
  56. package/dist/core/version-skew.d.ts.map +1 -1
  57. package/dist/data/llmBenchCoverage.d.ts.map +1 -1
  58. package/dist/data/llmBenchCoverage.js +20 -0
  59. package/dist/data/llmBenchCoverage.js.map +1 -1
  60. package/dist/data/provenanceCoverage.d.ts +6 -0
  61. package/dist/data/provenanceCoverage.d.ts.map +1 -1
  62. package/dist/data/provenanceCoverage.js +30 -0
  63. package/dist/data/provenanceCoverage.js.map +1 -1
  64. package/dist/feedback-factory/drain/FeedbackConsumerPromotionStore.d.ts +18 -0
  65. package/dist/feedback-factory/drain/FeedbackConsumerPromotionStore.d.ts.map +1 -0
  66. package/dist/feedback-factory/drain/FeedbackConsumerPromotionStore.js +51 -0
  67. package/dist/feedback-factory/drain/FeedbackConsumerPromotionStore.js.map +1 -0
  68. package/dist/feedback-factory/drain/FeedbackDrainBackupCadence.d.ts +20 -0
  69. package/dist/feedback-factory/drain/FeedbackDrainBackupCadence.d.ts.map +1 -0
  70. package/dist/feedback-factory/drain/FeedbackDrainBackupCadence.js +28 -0
  71. package/dist/feedback-factory/drain/FeedbackDrainBackupCadence.js.map +1 -0
  72. package/dist/feedback-factory/drain/FeedbackDrainPosture.d.ts +14 -0
  73. package/dist/feedback-factory/drain/FeedbackDrainPosture.d.ts.map +1 -0
  74. package/dist/feedback-factory/drain/FeedbackDrainPosture.js +15 -0
  75. package/dist/feedback-factory/drain/FeedbackDrainPosture.js.map +1 -0
  76. package/dist/feedback-factory/drain/FeedbackDrainService.d.ts +121 -0
  77. package/dist/feedback-factory/drain/FeedbackDrainService.d.ts.map +1 -0
  78. package/dist/feedback-factory/drain/FeedbackDrainService.js +370 -0
  79. package/dist/feedback-factory/drain/FeedbackDrainService.js.map +1 -0
  80. package/dist/feedback-factory/drain/FeedbackDrainStore.d.ts +369 -0
  81. package/dist/feedback-factory/drain/FeedbackDrainStore.d.ts.map +1 -0
  82. package/dist/feedback-factory/drain/FeedbackDrainStore.js +1125 -0
  83. package/dist/feedback-factory/drain/FeedbackDrainStore.js.map +1 -0
  84. package/dist/feedback-factory/drain/FeedbackDrainTickProxy.d.ts +67 -0
  85. package/dist/feedback-factory/drain/FeedbackDrainTickProxy.d.ts.map +1 -0
  86. package/dist/feedback-factory/drain/FeedbackDrainTickProxy.js +151 -0
  87. package/dist/feedback-factory/drain/FeedbackDrainTickProxy.js.map +1 -0
  88. package/dist/feedback-factory/drain/FeedbackFactoryGeneratedDefaults.d.ts +32 -0
  89. package/dist/feedback-factory/drain/FeedbackFactoryGeneratedDefaults.d.ts.map +1 -0
  90. package/dist/feedback-factory/drain/FeedbackFactoryGeneratedDefaults.js +47 -0
  91. package/dist/feedback-factory/drain/FeedbackFactoryGeneratedDefaults.js.map +1 -0
  92. package/dist/feedback-factory/drain/FeedbackInitiativeConsumer.d.ts +33 -0
  93. package/dist/feedback-factory/drain/FeedbackInitiativeConsumer.d.ts.map +1 -0
  94. package/dist/feedback-factory/drain/FeedbackInitiativeConsumer.js +71 -0
  95. package/dist/feedback-factory/drain/FeedbackInitiativeConsumer.js.map +1 -0
  96. package/dist/feedback-factory/drain/FeedbackReadinessArbiter.d.ts +36 -0
  97. package/dist/feedback-factory/drain/FeedbackReadinessArbiter.d.ts.map +1 -0
  98. package/dist/feedback-factory/drain/FeedbackReadinessArbiter.js +153 -0
  99. package/dist/feedback-factory/drain/FeedbackReadinessArbiter.js.map +1 -0
  100. package/dist/feedback-factory/drain/index.d.ts +2 -0
  101. package/dist/feedback-factory/drain/index.d.ts.map +1 -0
  102. package/dist/feedback-factory/drain/index.js +2 -0
  103. package/dist/feedback-factory/drain/index.js.map +1 -0
  104. package/dist/feedback-factory/processing/FeedbackProcessingService.d.ts +14 -0
  105. package/dist/feedback-factory/processing/FeedbackProcessingService.d.ts.map +1 -1
  106. package/dist/feedback-factory/processing/FeedbackProcessingService.js +25 -4
  107. package/dist/feedback-factory/processing/FeedbackProcessingService.js.map +1 -1
  108. package/dist/feedback-factory/store/FeedbackSourceGenerations.d.ts +39 -0
  109. package/dist/feedback-factory/store/FeedbackSourceGenerations.d.ts.map +1 -0
  110. package/dist/feedback-factory/store/FeedbackSourceGenerations.js +288 -0
  111. package/dist/feedback-factory/store/FeedbackSourceGenerations.js.map +1 -0
  112. package/dist/feedback-factory/store/JsonlFeedbackStore.d.ts +26 -6
  113. package/dist/feedback-factory/store/JsonlFeedbackStore.d.ts.map +1 -1
  114. package/dist/feedback-factory/store/JsonlFeedbackStore.js +139 -25
  115. package/dist/feedback-factory/store/JsonlFeedbackStore.js.map +1 -1
  116. package/dist/monitoring/ClaimClauseArbiter.d.ts +54 -0
  117. package/dist/monitoring/ClaimClauseArbiter.d.ts.map +1 -0
  118. package/dist/monitoring/ClaimClauseArbiter.js +163 -0
  119. package/dist/monitoring/ClaimClauseArbiter.js.map +1 -0
  120. package/dist/monitoring/ClassReviewStore.d.ts +152 -0
  121. package/dist/monitoring/ClassReviewStore.d.ts.map +1 -0
  122. package/dist/monitoring/ClassReviewStore.js +431 -0
  123. package/dist/monitoring/ClassReviewStore.js.map +1 -0
  124. package/dist/monitoring/CommitmentTracker.d.ts +5 -0
  125. package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
  126. package/dist/monitoring/CommitmentTracker.js +2 -0
  127. package/dist/monitoring/CommitmentTracker.js.map +1 -1
  128. package/dist/monitoring/CompletionClaimVerifier.d.ts +79 -0
  129. package/dist/monitoring/CompletionClaimVerifier.d.ts.map +1 -0
  130. package/dist/monitoring/CompletionClaimVerifier.js +281 -0
  131. package/dist/monitoring/CompletionClaimVerifier.js.map +1 -0
  132. package/dist/monitoring/CorrectionCaptureLoop.d.ts +6 -1
  133. package/dist/monitoring/CorrectionCaptureLoop.d.ts.map +1 -1
  134. package/dist/monitoring/CorrectionCaptureLoop.js +10 -0
  135. package/dist/monitoring/CorrectionCaptureLoop.js.map +1 -1
  136. package/dist/monitoring/CorrectionClassReview.d.ts +93 -0
  137. package/dist/monitoring/CorrectionClassReview.d.ts.map +1 -0
  138. package/dist/monitoring/CorrectionClassReview.js +280 -0
  139. package/dist/monitoring/CorrectionClassReview.js.map +1 -0
  140. package/dist/monitoring/CorrectionInstanceFixGate.d.ts +20 -0
  141. package/dist/monitoring/CorrectionInstanceFixGate.d.ts.map +1 -0
  142. package/dist/monitoring/CorrectionInstanceFixGate.js +30 -0
  143. package/dist/monitoring/CorrectionInstanceFixGate.js.map +1 -0
  144. package/dist/monitoring/TurnEvidence.d.ts +38 -0
  145. package/dist/monitoring/TurnEvidence.d.ts.map +1 -0
  146. package/dist/monitoring/TurnEvidence.js +208 -0
  147. package/dist/monitoring/TurnEvidence.js.map +1 -0
  148. package/dist/monitoring/guardManifest.d.ts.map +1 -1
  149. package/dist/monitoring/guardManifest.js +26 -0
  150. package/dist/monitoring/guardManifest.js.map +1 -1
  151. package/dist/scaffold/templates.d.ts.map +1 -1
  152. package/dist/scaffold/templates.js +2 -0
  153. package/dist/scaffold/templates.js.map +1 -1
  154. package/dist/server/AgentServer.d.ts +9 -0
  155. package/dist/server/AgentServer.d.ts.map +1 -1
  156. package/dist/server/AgentServer.js +304 -2
  157. package/dist/server/AgentServer.js.map +1 -1
  158. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  159. package/dist/server/CapabilityIndex.js +16 -0
  160. package/dist/server/CapabilityIndex.js.map +1 -1
  161. package/dist/server/routes.d.ts +34 -0
  162. package/dist/server/routes.d.ts.map +1 -1
  163. package/dist/server/routes.js +616 -8
  164. package/dist/server/routes.js.map +1 -1
  165. package/dist/testing/selfActionRegistry.d.ts.map +1 -1
  166. package/dist/testing/selfActionRegistry.js +32 -0
  167. package/dist/testing/selfActionRegistry.js.map +1 -1
  168. package/package.json +5 -2
  169. package/scripts/lint-canonical-pipeline-completeness.mjs +265 -0
  170. package/src/data/builtin-manifest.json +66 -66
  171. package/src/data/llmBenchCoverage.ts +20 -0
  172. package/src/data/provenanceCoverage.ts +36 -0
  173. package/src/scaffold/templates/jobs/instar/correction-class-review-backstop.md +23 -0
  174. package/src/scaffold/templates/jobs/instar/feedback-factory-process.md +13 -12
  175. package/src/scaffold/templates.ts +2 -0
  176. package/src/templates/hooks/settings-template.json +10 -0
  177. package/upgrades/1.3.889.md +26 -0
  178. package/upgrades/1.3.890.md +19 -0
  179. package/upgrades/side-effects/NEXT.md +57 -68
  180. package/upgrades/side-effects/feedback-factory-operating-drain.md +110 -0
@@ -46,6 +46,7 @@ export const GLANCE_ADOPTED_TABS = [
46
46
  'machines', 'systems', 'spend', 'routing-map', // Phase 3 (the jargon belt); 'systems' is the Health tab
47
47
  // Phase 4 — the sweep: every remaining data-summary view on the floor.
48
48
  'pr-pipeline', 'tokens', 'llm-activity', 'secrets', 'resources', 'initiatives',
49
+ 'feedback-drain', // operated summary reviewed against the F10/F11 floor
49
50
  ];
50
51
 
51
52
  export const GLANCE_GRANDFATHERED = [
@@ -2942,6 +2942,7 @@
2942
2942
  <button class="tab" data-tab="threadline" onclick="switchTab('threadline')">Threadline</button>
2943
2943
  <button class="tab" data-tab="integrated-being" onclick="switchTab('integrated-being')">Integrated-Being</button>
2944
2944
  <button class="tab" data-tab="process-health" onclick="switchTab('process-health')">Process Health</button>
2945
+ <button class="tab" data-tab="feedback-drain" onclick="switchTab('feedback-drain')">Feedback Drain</button>
2945
2946
  <button class="tab" data-tab="preferences-learning" onclick="switchTab('preferences-learning')">Preferences</button>
2946
2947
  </div>
2947
2948
  <div class="tab-group" role="group" aria-label="Content & Access">
@@ -3575,6 +3576,18 @@
3575
3576
  </details>
3576
3577
  </div>
3577
3578
 
3579
+ <div id="feedbackDrainPanel" class="ph-root" style="display:none;flex-direction:column;padding:28px;gap:20px;overflow-y:auto">
3580
+ <div style="display:flex;justify-content:space-between;align-items:baseline">
3581
+ <h2 class="ph-title">Feedback Drain</h2>
3582
+ <div id="fdStamp" class="ph-stamp" aria-live="polite"></div>
3583
+ </div>
3584
+ <p class="ph-intro">The operated path from accepted feedback through agent readiness and durable Initiative handoff.</p>
3585
+ <div id="fdPosture" class="ph-headline-wrap" aria-live="polite"></div>
3586
+ <section class="ph-section"><h3 class="ph-h">Backlog</h3><div id="fdBacklog" class="ph-detail-body"></div></section>
3587
+ <section class="ph-section"><h3 class="ph-h">Progress</h3><div id="fdProgress" class="ph-detail-body"></div></section>
3588
+ <section class="ph-section"><h3 class="ph-h">Source integrity</h3><div id="fdSource" class="ph-detail-body"></div></section>
3589
+ </div>
3590
+
3578
3591
  <!-- subscriptions tab (Subscription & Auth Standard P2.2 — live quota + pending logins) -->
3579
3592
  <style>
3580
3593
  .sub-account { border:1px solid var(--border,#2a2a2a); border-radius:10px; padding:14px 16px; margin-bottom:12px; }
@@ -3743,6 +3756,16 @@
3743
3756
  <p class="ph-h-sub">Patterns I've distilled from moments you corrected me. The exact words are never stored — just a short, scrubbed summary.</p>
3744
3757
  <div id="plCorrections"></div>
3745
3758
  </section>
3759
+ <section class="ph-section">
3760
+ <h3 class="ph-h">Class reviews and outcomes</h3>
3761
+ <p class="ph-h-sub">The standard and process question each correction opened, with their independent durable outcomes.</p>
3762
+ <div id="plClassReviews"></div>
3763
+ </section>
3764
+ <section class="ph-section">
3765
+ <h3 class="ph-h">Verify-before-done observations</h3>
3766
+ <p class="ph-h-sub">Advisory evidence checks from recent completion claims. This never blocks a response.</p>
3767
+ <div id="plCompletionAudit"></div>
3768
+ </section>
3746
3769
  </div>
3747
3770
 
3748
3771
  <!-- machines tab (Multi-Machine Session Pool §L2) -->
@@ -5270,6 +5293,13 @@
5270
5293
  onActivate: () => { if (typeof startProcessHealth === 'function') startProcessHealth(); },
5271
5294
  onDeactivate: () => { if (typeof stopProcessHealth === 'function') stopProcessHealth(); },
5272
5295
  },
5296
+ {
5297
+ id: 'feedback-drain',
5298
+ panels: ['feedbackDrainPanel'],
5299
+ display: ['flex'],
5300
+ onActivate: () => { if (typeof startFeedbackDrain === 'function') startFeedbackDrain(); },
5301
+ onDeactivate: () => { if (typeof stopFeedbackDrain === 'function') stopFeedbackDrain(); },
5302
+ },
5273
5303
  {
5274
5304
  id: 'subscriptions',
5275
5305
  panels: ['subscriptionsPanel'],
@@ -8812,6 +8842,8 @@
8812
8842
  headline: document.getElementById('plHeadline'),
8813
8843
  preferences: document.getElementById('plPreferences'),
8814
8844
  corrections: document.getElementById('plCorrections'),
8845
+ classReviews: document.getElementById('plClassReviews'),
8846
+ completionAudit: document.getElementById('plCompletionAudit'),
8815
8847
  stamp: document.getElementById('plStamp'),
8816
8848
  };
8817
8849
  // Raw fetch (NOT apiFetch) so the controller sees 503 itself.
@@ -8827,17 +8859,57 @@
8827
8859
  if (__plController) __plController.stop();
8828
8860
  }
8829
8861
 
8862
+ // Feedback Factory operated-drain read surface. Closed status DTO only;
8863
+ // dynamic values are assigned with textContent and never interpreted as HTML.
8864
+ let __fdTimer = null;
8865
+ const fdText = (id, value) => { const node = document.getElementById(id); if (node) node.textContent = String(value ?? '—'); };
8866
+ async function loadFeedbackDrain() {
8867
+ try {
8868
+ const response = await fetch('/feedback-factory/drain/status', { headers: { 'Authorization': `Bearer ${token}` } });
8869
+ const data = await response.json();
8870
+ const posture = data.posture || { state: 'unavailable', reason: 'initialization-failure' };
8871
+ fdText('fdPosture', `${posture.state}: ${posture.reason}`);
8872
+ if (!response.ok) {
8873
+ fdText('fdBacklog', 'The drain is not accepting work in this posture.');
8874
+ fdText('fdProgress', 'No operated run is active.');
8875
+ fdText('fdSource', 'Source integrity is unavailable until initialization succeeds.');
8876
+ } else {
8877
+ const drain = data.drain || {}; const readiness = drain.readiness || {}; const work = drain.work || {};
8878
+ fdText('fdBacklog', `collecting ${readiness.collecting || 0}; ready ${readiness.ready || 0}; queued ${work.queued || 0}; retryable ${work.retryable || 0}; held ${(readiness.held || 0) + (work.held || 0)}; dead-lettered ${work['dead-lettered'] || 0}`);
8879
+ const run = data.lastRun;
8880
+ fdText('fdProgress', run ? `last run ${run.state}; completed ${work.completed || 0}; oldest queued ${drain.oldestQueuedAgeMs == null ? 'none' : Math.round(drain.oldestQueuedAgeMs / 1000) + 's'}` : 'No drain run has been admitted yet.');
8881
+ fdText('fdSource', `byte lag ${drain.sourceProjectionLagBytes || 0}; generation lag ${drain.sourceGenerationLag || 0}; checksum conflicts ${drain.sourceChecksumConflicts || 0}`);
8882
+ }
8883
+ fdText('fdStamp', `updated ${new Date().toLocaleTimeString()}`);
8884
+ } catch {
8885
+ fdText('fdPosture', 'unavailable: status request failed');
8886
+ fdText('fdStamp', '');
8887
+ }
8888
+ }
8889
+ function startFeedbackDrain() {
8890
+ stopFeedbackDrain();
8891
+ void loadFeedbackDrain();
8892
+ __fdTimer = setInterval(() => { void loadFeedbackDrain(); }, 15000);
8893
+ }
8894
+ function stopFeedbackDrain() {
8895
+ if (__fdTimer) clearInterval(__fdTimer);
8896
+ __fdTimer = null;
8897
+ }
8898
+
8830
8899
  // Handle tab visibility — close SSE when tab is backgrounded
8831
8900
  document.addEventListener('visibilitychange', () => {
8832
8901
  if (document.hidden) {
8833
8902
  disconnectJobsSSE();
8834
8903
  if (currentTab === 'process-health') stopProcessHealth();
8904
+ if (currentTab === 'feedback-drain') stopFeedbackDrain();
8835
8905
  if (currentTab === 'preferences-learning') stopPreferencesLearning();
8836
8906
  } else if (currentTab === 'jobs') {
8837
8907
  connectJobsSSE();
8838
8908
  loadJobs(); // Resync on return
8839
8909
  } else if (currentTab === 'process-health') {
8840
8910
  startProcessHealth(); // resume on return: one immediate refresh + re-arm
8911
+ } else if (currentTab === 'feedback-drain') {
8912
+ startFeedbackDrain();
8841
8913
  } else if (currentTab === 'preferences-learning') {
8842
8914
  startPreferencesLearning(); // resume on return
8843
8915
  }
@@ -227,6 +227,81 @@ export function renderCorrections(doc, target, body, now = Date.now()) {
227
227
  }
228
228
  }
229
229
 
230
+ export function renderClassReviews(doc, target, body) {
231
+ if (!target) return;
232
+ target.replaceChildren();
233
+ if (body && body.disabled) {
234
+ target.appendChild(el(doc, 'p', 'ph-empty', 'Class review is still dark on this agent.'));
235
+ return;
236
+ }
237
+ const records = body && Array.isArray(body.records) ? body.records.slice(0, 20) : [];
238
+ if (!records.length) { target.appendChild(el(doc, 'p', 'ph-empty', 'No class reviews yet.')); return; }
239
+ for (const record of records) {
240
+ const item = doc.createElement('details');
241
+ item.setAttribute('class', 'ph-item');
242
+ const heading = doc.createElement('summary');
243
+ heading.setAttribute('class', 'ph-item-summary');
244
+ heading.appendChild(el(doc, 'div', 'ph-item-text', `${friendlyReviewValue(record.standardReview?.verdict || record.fillState, 'Review pending')} · ${friendlyReviewValue(record.processReview?.verdict, 'Process review pending')}`));
245
+ item.appendChild(heading);
246
+ const detail = el(doc, 'div', 'ph-item-body');
247
+ detail.appendChild(labeledRow(doc, 'Lifecycle', friendlyReviewValue(record.reviewLifecycle, 'Open')));
248
+ detail.appendChild(labeledRow(doc, 'Standard outcome', friendlyReviewValue(record.standardOutcome, 'Proposed')));
249
+ detail.appendChild(labeledRow(doc, 'Process outcome', friendlyReviewValue(record.processOutcome, 'Proposed')));
250
+ item.appendChild(detail);
251
+ target.appendChild(item);
252
+ }
253
+ }
254
+
255
+ export function renderCompletionAudit(doc, target, body) {
256
+ if (!target) return;
257
+ target.replaceChildren();
258
+ if (body && body.disabled) { target.appendChild(el(doc, 'p', 'ph-empty', 'Verify-before-done is still dark on this agent.')); return; }
259
+ const records = body && Array.isArray(body.records) ? body.records.slice(-20).reverse() : [];
260
+ if (!records.length) { target.appendChild(el(doc, 'p', 'ph-empty', 'No completion observations yet.')); return; }
261
+ for (const record of records) {
262
+ const item = doc.createElement('details');
263
+ item.setAttribute('class', 'ph-item');
264
+ const heading = doc.createElement('summary');
265
+ heading.setAttribute('class', 'ph-item-summary');
266
+ heading.appendChild(el(doc, 'div', 'ph-item-text', friendlyReviewValue(record.verdict, 'Observed')));
267
+ item.appendChild(heading);
268
+ const detail = el(doc, 'div', 'ph-item-body');
269
+ detail.appendChild(labeledRow(doc, 'Action', friendlyActionKind(record.actionKind)));
270
+ detail.appendChild(labeledRow(doc, 'Blocked', 'No — advisory only'));
271
+ item.appendChild(detail);
272
+ target.appendChild(item);
273
+ }
274
+ }
275
+
276
+ const REVIEW_VALUE_LABELS = Object.freeze({
277
+ pending: 'Review pending', filled: 'Review complete', 'dead-lettered': 'Needs a manual retry',
278
+ covered: 'Covered by an existing standard', 'needs-upgrade': 'Needs a standard upgrade',
279
+ 'new-standard-needed': 'Needs a new standard', 'not-applicable': 'No class change needed',
280
+ 'process-gap': 'Process gap found', open: 'Open', parked: 'Parked for follow-up',
281
+ resolved: 'Resolved', superseded: 'Combined into another review', reopened: 'Reopened',
282
+ proposed: 'Proposed', ratified: 'Approved', shipped: 'Shipped', rejected: 'Declined',
283
+ deferred: 'Parked for follow-up', 'expired-unreviewed': 'Awaiting overdue review',
284
+ 'no-action': 'No action needed', observed: 'Observed', corroborated: 'Evidence found',
285
+ 'uncorroborated-contradicted': 'Evidence contradicts this claim',
286
+ 'uncorroborated-unknown': 'Evidence was not found', 'not-eligible': 'Not eligible for this check',
287
+ 'not-evaluated': 'Not evaluated',
288
+ });
289
+
290
+ export function friendlyReviewValue(value, fallback = 'Status unavailable') {
291
+ return typeof value === 'string' && REVIEW_VALUE_LABELS[value]
292
+ ? REVIEW_VALUE_LABELS[value]
293
+ : fallback;
294
+ }
295
+
296
+ const ACTION_KIND_LABELS = Object.freeze({
297
+ sent: 'Message sent', deployed: 'Deployment', 'handed-off': 'Handoff', committed: 'Commit',
298
+ pushed: 'Push', merged: 'Merge', restarted: 'Restart', fixed: 'Fix', other: 'Other action',
299
+ });
300
+
301
+ export function friendlyActionKind(value) {
302
+ return typeof value === 'string' && ACTION_KIND_LABELS[value] ? ACTION_KIND_LABELS[value] : 'Other action';
303
+ }
304
+
230
305
  /**
231
306
  * Disabled (503) state. Pinned copy + operator hint as PLAIN PROSE — no
232
307
  * `<code>`, no monospace, no config-key string (Dashboard Standard).
@@ -245,7 +320,7 @@ export function renderDisabled(doc, els) {
245
320
  det.appendChild(el(doc, 'p', 'ph-operator-hint', 'You can switch it on in this agent’s settings.'));
246
321
  els.headline.appendChild(det);
247
322
  }
248
- for (const k of ['preferences', 'corrections']) {
323
+ for (const k of ['preferences', 'corrections', 'classReviews', 'completionAudit']) {
249
324
  if (els[k]) els[k].replaceChildren();
250
325
  }
251
326
  if (els.stamp) els.stamp.textContent = '';
@@ -255,6 +330,8 @@ export function renderDisabled(doc, els) {
255
330
  const URLS = {
256
331
  prefs: '/preferences/session-context',
257
332
  corrections: '/corrections?limit=20',
333
+ classReviews: '/class-reviews?limit=20',
334
+ completionAudit: '/completion-claim/audit?limit=20',
258
335
  };
259
336
 
260
337
  /**
@@ -280,10 +357,10 @@ export function createController(opts) {
280
357
  timerId: null,
281
358
  inFlight: null,
282
359
  active: false,
283
- snapshot: { prefs: null, corrections: null },
360
+ snapshot: { prefs: null, corrections: null, classReviews: null, completionAudit: null },
284
361
  last200At: 0,
285
362
  consecutiveFailedTicks: 0,
286
- renderedSig: { headline: null, preferences: null, corrections: null, disabled: false },
363
+ renderedSig: { headline: null, preferences: null, corrections: null, classReviews: null, completionAudit: null, disabled: false },
287
364
  };
288
365
 
289
366
  async function fetchOne(key, controller) {
@@ -303,7 +380,7 @@ export function createController(opts) {
303
380
  state.inFlight = controller;
304
381
  let results;
305
382
  try {
306
- results = await Promise.all(['prefs', 'corrections'].map((k) => fetchOne(k, controller)));
383
+ results = await Promise.all(['prefs', 'corrections', 'classReviews', 'completionAudit'].map((k) => fetchOne(k, controller)));
307
384
  } catch {
308
385
  if (controller.signal && controller.signal.aborted) return;
309
386
  state.inFlight = null;
@@ -315,7 +392,7 @@ export function createController(opts) {
315
392
  if (controller.signal && controller.signal.aborted) return;
316
393
  state.inFlight = null;
317
394
  // Feature OFF (503 on EITHER endpoint) → pinned disabled copy.
318
- if (results.some((r) => r.status === 503)) {
395
+ if (results.some((r) => ['prefs', 'corrections'].includes(r.key) && r.status === 503)) {
319
396
  state.consecutiveFailedTicks = 0;
320
397
  if (state.renderedSig.disabled !== true) {
321
398
  renderDisabled(doc, els);
@@ -327,6 +404,7 @@ export function createController(opts) {
327
404
  state.renderedSig.disabled = false;
328
405
  for (const r of results) {
329
406
  if (r.status === 200) { state.snapshot[r.key] = r.body; }
407
+ else if (r.status === 503) { state.snapshot[r.key] = { disabled: true }; }
330
408
  }
331
409
  state.consecutiveFailedTicks = 0;
332
410
  state.last200At = now();
@@ -350,11 +428,15 @@ export function createController(opts) {
350
428
  const stale = headlineStale();
351
429
  const prefs = state.snapshot.prefs;
352
430
  const corrections = state.snapshot.corrections;
431
+ const classReviews = state.snapshot.classReviews;
432
+ const completionAudit = state.snapshot.completionAudit;
353
433
  const prefCount = prefs && Number.isFinite(prefs.count) ? prefs.count : 0;
354
434
 
355
435
  sectionRender('headline', sig(stale, prefCount), () => renderHeadline(doc, els.headline, { prefCount, stale }));
356
436
  sectionRender('preferences', sig(prefs), () => renderPreferences(doc, els.preferences, prefs));
357
437
  sectionRender('corrections', sig(corrections), () => renderCorrections(doc, els.corrections, corrections, now()));
438
+ sectionRender('classReviews', sig(classReviews), () => renderClassReviews(doc, els.classReviews, classReviews));
439
+ sectionRender('completionAudit', sig(completionAudit), () => renderCompletionAudit(doc, els.completionAudit, completionAudit));
358
440
 
359
441
  if (els.stamp) {
360
442
  const ageS = state.last200At ? Math.max(0, Math.round((now() - state.last200At) / 1000)) : null;
@@ -4136,6 +4136,9 @@ fi
4136
4136
  // Unjustified Stop Gate router — Stop hook that calls the server-side gate.
4137
4137
  // Shadow-mode by default; enforcement is controlled server-side.
4138
4138
  fs.writeFileSync(path.join(hooksDir, 'stop-gate-router.js'), migrator.getHookContent('stop-gate-router'), { mode: 0o755 });
4139
+ // Verify-Before-Done observer — structural client-side TurnEvidence, always
4140
+ // installed; fleet-dark/dev-dry-run gating occurs inside the hook + route.
4141
+ fs.writeFileSync(path.join(hooksDir, 'completion-claim-observe.js'), migrator.getHookContent('completion-claim-observe'), { mode: 0o755 });
4139
4142
  // Hook event reporter — posts hook events to the Instar server for observability
4140
4143
  // and session resumption (claudeSessionId). Uses command hooks because Claude Code
4141
4144
  // HTTP hooks (type: "http") silently fail to fire as of v2.1.78.