@beastmode-develeap/beastmode 0.1.206 → 0.1.207

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.
@@ -15,7 +15,7 @@
15
15
  }
16
16
  </script>
17
17
  <!--BOARD_DATA-->
18
- <script>window.__BUILD_STAMP__ = "20260509-103000-fd3a02c";</script>
18
+ <script>window.__BUILD_STAMP__ = "20260509-122800-7350789";</script>
19
19
  <link rel="preconnect" href="https://fonts.googleapis.com">
20
20
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
21
21
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -1522,6 +1522,37 @@ input[type="range"]::-webkit-slider-thumb {
1522
1522
  margin: 0 -8px;
1523
1523
  padding: 8px;
1524
1524
  }
1525
+ /* Audit Trail — Superseded transition history (Story 6) */
1526
+ .audit-timeline { display: flex; flex-direction: column; gap: 8px; }
1527
+ .audit-entry {
1528
+ background: var(--bg-input);
1529
+ border-radius: 8px;
1530
+ padding: 10px 12px;
1531
+ border-left: 3px solid var(--accent);
1532
+ }
1533
+ .audit-entry-header {
1534
+ display: flex;
1535
+ justify-content: space-between;
1536
+ align-items: center;
1537
+ margin-bottom: 4px;
1538
+ }
1539
+ .audit-actor { font-weight: 600; font-size: 13px; }
1540
+ .audit-time { color: var(--text-muted); font-size: 12px; }
1541
+ .audit-entry-body { font-size: 13px; display: flex; flex-direction: column; gap: 4px; }
1542
+ .audit-from { color: var(--text-muted); }
1543
+ .audit-successor a, .audit-cascade a {
1544
+ color: var(--accent);
1545
+ text-decoration: none;
1546
+ }
1547
+ .audit-successor a:hover, .audit-cascade a:hover { text-decoration: underline; }
1548
+ .audit-reason {
1549
+ margin-top: 4px;
1550
+ padding: 6px 8px;
1551
+ background: var(--bg-secondary);
1552
+ border-radius: 4px;
1553
+ font-style: italic;
1554
+ color: var(--text-secondary);
1555
+ }
1525
1556
  .btn-env-deploy {
1526
1557
  display: inline-flex;
1527
1558
  align-items: center;
@@ -5172,7 +5203,7 @@ function SupersededModal({ item, onConfirm, onCancel }) {
5172
5203
 
5173
5204
  // ── Item Detail Sidebar ──
5174
5205
 
5175
- function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject }) {
5206
+ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, allItems, onSelectItem }) {
5176
5207
  const [updates, setUpdates] = useState([]);
5177
5208
  const [loadingUpdates, setLoadingUpdates] = useState(true);
5178
5209
  const [sortNewest, setSortNewest] = useState(true);
@@ -5187,6 +5218,7 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject }) {
5187
5218
  const [phaseData, setPhaseData] = useState([]);
5188
5219
  const [envTimeline, setEnvTimeline] = useState(null);
5189
5220
  const [deployModal, setDeployModal] = useState(null);
5221
+ const [auditEvents, setAuditEvents] = useState([]);
5190
5222
  const sidebarRef = useRef(null);
5191
5223
  const topCommentRef = useRef(null);
5192
5224
 
@@ -5208,6 +5240,25 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject }) {
5208
5240
  .catch(() => setEnvTimeline(null));
5209
5241
  }, [item && item.id, item && item.extra && item.extra.current_env]);
5210
5242
 
5243
+ // FR-4: Fetch SupersededTransition audit events for the selected item.
5244
+ // Only fires when the item is in Superseded status — non-Superseded items
5245
+ // never have audit entries and the section is hidden anyway (NFR-4).
5246
+ useEffect(() => {
5247
+ if (!item || item.status !== 'Superseded') {
5248
+ setAuditEvents([]);
5249
+ return;
5250
+ }
5251
+ const proj = selectedProject && selectedProject !== 'all' ? selectedProject : null;
5252
+ const qs = proj ? '?board=' + encodeURIComponent(proj) : '';
5253
+ const sep = qs ? '&' : '?';
5254
+ const url = '/api/events' + qs + sep + 'item_id=' + encodeURIComponent(item.id) +
5255
+ '&type=SupersededTransition';
5256
+ fetch(url)
5257
+ .then(r => r.ok ? r.json() : [])
5258
+ .then(events => setAuditEvents(Array.isArray(events) ? events : []))
5259
+ .catch(() => setAuditEvents([]));
5260
+ }, [item && item.id, item && item.status]);
5261
+
5211
5262
  // Cost summary fetch — keyed on item.id, refreshed alongside the
5212
5263
  // 10-second updates/attachments poll below. The api() helper only
5213
5264
  // auto-scopes /api/board/*, so append ?board=<proj> manually.
@@ -5535,6 +5586,49 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject }) {
5535
5586
  </div>
5536
5587
  </div>
5537
5588
  `}
5589
+ ${item && item.status === 'Superseded' && auditEvents.length > 0 && html`
5590
+ <div class="detail-section" style="padding:12px 24px 0;" data-testid="audit-trail-section">
5591
+ <h4 class="detail-section-title" style="margin:0 0 8px 0;font-size:13px;font-weight:600;">Audit Trail</h4>
5592
+ <div class="audit-timeline">
5593
+ ${auditEvents.map(ev => {
5594
+ const payload = (ev && ev.payload) || {};
5595
+ const isCascade = ev.actor === 'cascade';
5596
+ return html`
5597
+ <div class="audit-entry" key=${ev.id} data-testid="audit-entry">
5598
+ <div class="audit-entry-header">
5599
+ <span class="audit-actor" data-testid="audit-actor">${isCascade ? 'Auto-cascaded' : 'Status changed'}</span>
5600
+ <span class="audit-time" data-testid="audit-time">${timeAgo(ev.timestamp)}</span>
5601
+ </div>
5602
+ <div class="audit-entry-body">
5603
+ <span class="audit-from" data-testid="audit-from-status">From: <strong>${payload.from_status || ''}</strong></span>
5604
+ ${payload.superseded_by && html`
5605
+ <span class="audit-successor">
5606
+ Successor: <a href="#" data-testid="audit-successor-link" onClick=${(e) => {
5607
+ e.preventDefault();
5608
+ const target = allItems && allItems.find ? allItems.find(i => String(i.id) === String(payload.superseded_by)) : null;
5609
+ if (target && onSelectItem) onSelectItem(target);
5610
+ }}>#${payload.superseded_by}</a>
5611
+ </span>
5612
+ `}
5613
+ ${payload.cascade_source && html`
5614
+ <span class="audit-cascade">
5615
+ Cascaded from parent: <a href="#" data-testid="audit-cascade-source" onClick=${(e) => {
5616
+ e.preventDefault();
5617
+ const target = allItems && allItems.find ? allItems.find(i => String(i.id) === String(payload.cascade_source)) : null;
5618
+ if (target && onSelectItem) onSelectItem(target);
5619
+ }}>#${payload.cascade_source}</a>
5620
+ </span>
5621
+ `}
5622
+ ${payload.superseded_reason && html`
5623
+ <div class="audit-reason" data-testid="audit-reason">${payload.superseded_reason}</div>
5624
+ `}
5625
+ </div>
5626
+ </div>
5627
+ `;
5628
+ })}
5629
+ </div>
5630
+ </div>
5631
+ `}
5538
5632
  ${(loadingAttachments || attachments.length > 0) && html`
5539
5633
  <div style="padding:12px 24px 0;">
5540
5634
  <h4 style="margin:0 0 8px 0;font-size:13px;font-weight:600;">
@@ -6931,7 +7025,7 @@ function BoardPage({ selectedProject }) {
6931
7025
  `}
6932
7026
 
6933
7027
  ${showCreateDialog && html`<${CreateTaskDialog} onClose=${() => setShowCreateDialog(false)} onCreated=${fetchItems} />`}
6934
- ${selectedItem && html`<${ItemDetailSidebar} item=${selectedItem} selectedProject=${selectedProject} onClose=${() => setSelectedItem(null)} onStatusChange=${() => { fetchItems(); setSelectedItem(null); }} />`}
7028
+ ${selectedItem && html`<${ItemDetailSidebar} item=${selectedItem} selectedProject=${selectedProject} allItems=${items} onSelectItem=${setSelectedItem} onClose=${() => setSelectedItem(null)} onStatusChange=${() => { fetchItems(); setSelectedItem(null); }} />`}
6935
7029
  </div>
6936
7030
  `;
6937
7031
  }
@@ -1 +1 @@
1
- fd3a02ca51716afc9999e6cc4441295cc82987c0
1
+ 7350789bf2ce5bca2ca226ac0e7e8f656012caf4
@@ -1 +1 @@
1
- 20260509-103000-fd3a02c
1
+ 20260509-122800-7350789
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beastmode-develeap/beastmode",
3
- "version": "0.1.206",
3
+ "version": "0.1.207",
4
4
  "description": "BeastMode Dark Factory — turn intent into verified software",
5
5
  "type": "module",
6
6
  "bin": {