@beastmode-develeap/beastmode 0.1.204 → 0.1.205

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-102609-7aa0839";</script>
18
+ <script>window.__BUILD_STAMP__ = "20260509-102837-ac9ac7b";</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,37 +1522,6 @@ 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
- }
1556
1525
  .btn-env-deploy {
1557
1526
  display: inline-flex;
1558
1527
  align-items: center;
@@ -4291,7 +4260,7 @@ const STATUSES = [
4291
4260
  'Waiting for Infra Approval', 'Provisioning Infra', 'Infra Ready',
4292
4261
  'Approved & Merge to Main', 'Ready For Review',
4293
4262
  'Verifying Prod with Tests', 'Verifying Epic', 'Waiting for Epic Bugs', 'Awaiting Input',
4294
- 'Epic Breakdown Posted', 'Stuck', 'Done', 'Superseded',
4263
+ 'Epic Breakdown Posted', 'Stuck', 'Done',
4295
4264
  ];
4296
4265
 
4297
4266
  const KANBAN_COLUMNS = [
@@ -5126,88 +5095,12 @@ function DeployModal({ envName, refs, loading, item, selectedProject, onClose })
5126
5095
  `;
5127
5096
  }
5128
5097
 
5129
- // ── Superseded Modal ──
5130
-
5131
- function SupersededModal({ item, onConfirm, onCancel }) {
5132
- const [supersededBy, setSupersededBy] = useState('');
5133
- const [reason, setReason] = useState('');
5134
- const [error, setError] = useState('');
5135
- const [loading, setLoading] = useState(false);
5136
-
5137
- const isEpic = (item.task_type || '').toLowerCase() === 'epic';
5138
-
5139
- useEffect(() => {
5140
- const handleEsc = (e) => { if (e.key === 'Escape') onCancel(); };
5141
- document.addEventListener('keydown', handleEsc);
5142
- return () => document.removeEventListener('keydown', handleEsc);
5143
- }, []);
5144
-
5145
- const handleSubmit = async () => {
5146
- if (isEpic && !supersededBy.trim()) {
5147
- setError('Epics require a successor item ID (superseded_by).');
5148
- return;
5149
- }
5150
- if (isEpic && !reason.trim()) {
5151
- setError('Epics require a supersede reason.');
5152
- return;
5153
- }
5154
- setLoading(true);
5155
- setError('');
5156
- try {
5157
- const payload = { status: 'Superseded' };
5158
- const extra = {};
5159
- if (supersededBy.trim()) extra.superseded_by = supersededBy.trim();
5160
- if (reason.trim()) extra.superseded_reason = reason.trim();
5161
- if (Object.keys(extra).length > 0) payload.extra_columns = extra;
5162
- await api('PATCH', '/api/board/items/' + item.id, payload);
5163
- onConfirm();
5164
- } catch (err) {
5165
- setError(err.message || 'Failed to update status');
5166
- } finally {
5167
- setLoading(false);
5168
- }
5169
- };
5170
-
5171
- return html`
5172
- <div class="deploy-modal-overlay" data-testid="superseded-modal-overlay"
5173
- onClick=${(e) => { if (e.target === e.currentTarget) onCancel(); }}>
5174
- <div class="deploy-modal" data-testid="superseded-modal">
5175
- <h3>Mark as Superseded</h3>
5176
- <p style="font-size:13px;color:var(--text-muted);margin:0 0 12px;">
5177
- This action is permanent. Superseded items cannot be reactivated.
5178
- </p>
5179
- ${error && html`<div class="error-banner" style="margin-bottom:12px;">${error}</div>`}
5180
- <label>Successor item ID ${isEpic ? html`<span style="color:var(--danger);">*</span>` : '(optional)'}</label>
5181
- <input type="text" value=${supersededBy}
5182
- data-testid="superseded-by-input"
5183
- placeholder="e.g. 42"
5184
- onInput=${(e) => setSupersededBy(e.target.value)}
5185
- style="width:100%;padding:8px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text);font-size:13px;box-sizing:border-box;" />
5186
- <label>Reason ${isEpic ? html`<span style="color:var(--danger);">*</span>` : '(optional)'}</label>
5187
- <textarea value=${reason}
5188
- data-testid="superseded-reason-input"
5189
- placeholder="Why is this item being superseded?"
5190
- onInput=${(e) => setReason(e.target.value)}
5191
- style="width:100%;padding:8px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text);font-size:13px;box-sizing:border-box;resize:vertical;min-height:60px;" />
5192
- <div class="deploy-modal-actions">
5193
- <button onClick=${onCancel} disabled=${loading}>Cancel</button>
5194
- <button class="btn-primary" onClick=${handleSubmit} disabled=${loading}
5195
- data-testid="superseded-confirm-btn">
5196
- ${loading ? 'Updating...' : 'Mark Superseded'}
5197
- </button>
5198
- </div>
5199
- </div>
5200
- </div>
5201
- `;
5202
- }
5203
-
5204
5098
  // ── Item Detail Sidebar ──
5205
5099
 
5206
- function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, allItems, onSelectItem }) {
5100
+ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject }) {
5207
5101
  const [updates, setUpdates] = useState([]);
5208
5102
  const [loadingUpdates, setLoadingUpdates] = useState(true);
5209
5103
  const [sortNewest, setSortNewest] = useState(true);
5210
- const [showSupersededModal, setShowSupersededModal] = useState(false);
5211
5104
  // Attachments (Gap 8a — 2026-04-15): list every attachment the
5212
5105
  // board service has for this item, render image types as thumbnails
5213
5106
  // that open in a new tab when clicked.
@@ -5218,7 +5111,6 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5218
5111
  const [phaseData, setPhaseData] = useState([]);
5219
5112
  const [envTimeline, setEnvTimeline] = useState(null);
5220
5113
  const [deployModal, setDeployModal] = useState(null);
5221
- const [auditEvents, setAuditEvents] = useState([]);
5222
5114
  const sidebarRef = useRef(null);
5223
5115
  const topCommentRef = useRef(null);
5224
5116
 
@@ -5240,25 +5132,6 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5240
5132
  .catch(() => setEnvTimeline(null));
5241
5133
  }, [item && item.id, item && item.extra && item.extra.current_env]);
5242
5134
 
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
-
5262
5135
  // Cost summary fetch — keyed on item.id, refreshed alongside the
5263
5136
  // 10-second updates/attachments poll below. The api() helper only
5264
5137
  // auto-scopes /api/board/*, so append ?board=<proj> manually.
@@ -5427,17 +5300,9 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5427
5300
  </div>
5428
5301
  <div class="detail-fields">
5429
5302
  <label>Status</label>
5430
- <select value=${item.status || 'New'}
5431
- disabled=${item.status === 'Superseded'}
5432
- onChange=${async (e) => {
5433
- const newStatus = e.target.value;
5434
- if (newStatus === 'Superseded') {
5435
- e.target.value = item.status || 'New';
5436
- setShowSupersededModal(true);
5437
- return;
5438
- }
5303
+ <select value=${item.status || 'New'} onChange=${async (e) => {
5439
5304
  try {
5440
- await api('PATCH', '/api/board/items/' + item.id, { status: newStatus });
5305
+ await api('PATCH', '/api/board/items/' + item.id, { status: e.target.value });
5441
5306
  onStatusChange();
5442
5307
  } catch (err) { /* ignore */ }
5443
5308
  }}>
@@ -5586,49 +5451,6 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5586
5451
  </div>
5587
5452
  </div>
5588
5453
  `}
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
- `}
5632
5454
  ${(loadingAttachments || attachments.length > 0) && html`
5633
5455
  <div style="padding:12px 24px 0;">
5634
5456
  <h4 style="margin:0 0 8px 0;font-size:13px;font-weight:600;">
@@ -5685,11 +5507,6 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5685
5507
  selectedProject=${selectedProject}
5686
5508
  onClose=${() => setDeployModal(null)}
5687
5509
  />`}
5688
- ${showSupersededModal && html`<${SupersededModal}
5689
- item=${item}
5690
- onConfirm=${() => { setShowSupersededModal(false); onStatusChange(); }}
5691
- onCancel=${() => setShowSupersededModal(false)}
5692
- />`}
5693
5510
  `;
5694
5511
  }
5695
5512
 
@@ -6542,7 +6359,6 @@ function BoardPage({ selectedProject }) {
6542
6359
  e.preventDefault();
6543
6360
  e.currentTarget.classList.remove('drag-over', 'drag-over-invalid');
6544
6361
  e.currentTarget.removeAttribute('title');
6545
- if (status === 'Superseded') return;
6546
6362
  const di = dragInfoRef.current;
6547
6363
  if (!di) return;
6548
6364
  const validStages = (typeof getStagesForType === 'function')
@@ -7025,7 +6841,7 @@ function BoardPage({ selectedProject }) {
7025
6841
  `}
7026
6842
 
7027
6843
  ${showCreateDialog && html`<${CreateTaskDialog} onClose=${() => setShowCreateDialog(false)} onCreated=${fetchItems} />`}
7028
- ${selectedItem && html`<${ItemDetailSidebar} item=${selectedItem} selectedProject=${selectedProject} allItems=${items} onSelectItem=${setSelectedItem} onClose=${() => setSelectedItem(null)} onStatusChange=${() => { fetchItems(); setSelectedItem(null); }} />`}
6844
+ ${selectedItem && html`<${ItemDetailSidebar} item=${selectedItem} selectedProject=${selectedProject} onClose=${() => setSelectedItem(null)} onStatusChange=${() => { fetchItems(); setSelectedItem(null); }} />`}
7029
6845
  </div>
7030
6846
  `;
7031
6847
  }
@@ -1 +1 @@
1
- 7aa08391228bd7979262a65efcc7e0fa2df66453
1
+ ac9ac7bd53cf29439c9062ac8b847daa409df1ca
@@ -1 +1 @@
1
- 20260509-102609-7aa0839
1
+ 20260509-102837-ac9ac7b
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beastmode-develeap/beastmode",
3
- "version": "0.1.204",
3
+ "version": "0.1.205",
4
4
  "description": "BeastMode Dark Factory — turn intent into verified software",
5
5
  "type": "module",
6
6
  "bin": {