@beastmode-develeap/beastmode 0.1.245 → 0.1.247

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__ = "20260514-100818-c27f4a2";</script>
18
+ <script>window.__BUILD_STAMP__ = "20260514-104557-2efe699";</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">
@@ -463,7 +463,7 @@ body {
463
463
  min-height: 0;
464
464
  }
465
465
  .page-dashboard-main { min-width: 0; }
466
- .page-dashboard-sidebar { min-width: 0; }
466
+ .page-dashboard-sidebar { min-width: 0; max-height: calc(100vh - 120px); overflow-y: auto; }
467
467
 
468
468
  .page-centered {
469
469
  max-width: 800px;
@@ -3172,16 +3172,11 @@ input[type="range"]::-webkit-slider-thumb {
3172
3172
  }
3173
3173
  .activity-list-wrapper {
3174
3174
  position: relative;
3175
+ max-height: 320px;
3176
+ overflow-y: auto;
3175
3177
  }
3176
3178
  .activity-list-wrapper::after {
3177
- content: '';
3178
- position: absolute;
3179
- bottom: 0;
3180
- left: 0;
3181
- right: 0;
3182
- height: 24px;
3183
- background: linear-gradient(transparent, var(--bg-card));
3184
- pointer-events: none;
3179
+ display: none;
3185
3180
  }
3186
3181
  /* Scenario × Environment Matrix */
3187
3182
  .scenario-matrix-wrapper {
@@ -4782,22 +4777,35 @@ function swimlaneColor(colorKey) {
4782
4777
  }
4783
4778
  window.swimlaneColor = swimlaneColor;
4784
4779
 
4780
+ const _FIXTURE_NAME_RE = /^\[FIXTURE\]|^\[BM-VERIFY\]|^test[-_\s]|^S\d+[\s_-]|\bfixture\b|^smoke[-_\s]test\b|test[-_\s]item/i;
4781
+ function _isFixtureItem(item) {
4782
+ if (!item) return false;
4783
+ if (item.task_type === 'test_fixture') return true;
4784
+ const extra = item.extra || {};
4785
+ if (extra && extra._bm_fixture) return true;
4786
+ if (_FIXTURE_NAME_RE.test(item.name || '')) return true;
4787
+ return false;
4788
+ }
4789
+ window._isFixtureItem = _isFixtureItem;
4790
+
4785
4791
  function computePipelineStats(items) {
4786
4792
  const config = window.PIPELINE_CONFIG || {};
4787
4793
  const swimlanes = config.swimlanes || [];
4788
4794
  const typeStages = config.type_stages || {};
4789
4795
 
4790
- const totalItems = items.length;
4791
- const doneItems = items.filter(i => i.status === 'Done').length;
4792
- const stuckItems = items.filter(i => i.status === 'Stuck').length;
4793
- const activeItems = items.filter(i =>
4796
+ const realItems = items.filter(i => !_isFixtureItem(i));
4797
+
4798
+ const totalItems = realItems.length;
4799
+ const doneItems = realItems.filter(i => i.status === 'Done').length;
4800
+ const stuckItems = realItems.filter(i => i.status === 'Stuck').length;
4801
+ const activeItems = realItems.filter(i =>
4794
4802
  i.status && i.status !== 'New' && i.status !== 'Done' && i.status !== 'Stuck' && i.status !== ''
4795
4803
  ).length;
4796
4804
  const globalProgress = totalItems > 0 ? doneItems / totalItems : 0;
4797
4805
 
4798
4806
  const swimlaneStats = swimlanes.map(lane => {
4799
4807
  const taskTypes = lane.taskTypes || lane.task_types || [];
4800
- const laneItems = items.filter(i => taskTypes.includes(i.task_type || 'code'));
4808
+ const laneItems = realItems.filter(i => taskTypes.includes(i.task_type || 'code'));
4801
4809
  const total = laneItems.length;
4802
4810
  const done = laneItems.filter(i => i.status === 'Done').length;
4803
4811
  const stuck = laneItems.filter(i => i.status === 'Stuck').length;
@@ -5575,9 +5583,28 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5575
5583
  const [envTimeline, setEnvTimeline] = useState(null);
5576
5584
  const [deployModal, setDeployModal] = useState(null);
5577
5585
  const [auditEvents, setAuditEvents] = useState([]);
5586
+ const [pipelineConfig, setPipelineConfig] = useState(null);
5587
+ const [statusError, setStatusError] = useState('');
5578
5588
  const sidebarRef = useRef(null);
5579
5589
  const topCommentRef = useRef(null);
5580
5590
 
5591
+ useEffect(() => {
5592
+ api('GET', '/api/pipeline-config')
5593
+ .then(data => setPipelineConfig(data))
5594
+ .catch(() => {});
5595
+ }, []);
5596
+
5597
+ const validStatuses = useMemo(() => {
5598
+ if (!pipelineConfig) return STATUSES;
5599
+ const typeStages = pipelineConfig.type_stages || {};
5600
+ const taskType = item.task_type || 'code';
5601
+ const stages = typeStages[taskType] || typeStages['code'] || STATUSES;
5602
+ const overlays = new Set(pipelineConfig.overlay_statuses || []);
5603
+ const allowed = new Set([...stages, ...overlays, 'Superseded']);
5604
+ if (item.status) allowed.add(item.status);
5605
+ return STATUSES.filter(s => allowed.has(s));
5606
+ }, [pipelineConfig, item.task_type, item.status]);
5607
+
5581
5608
  useEffect(() => {
5582
5609
  if (!item || !item.extra || !item.extra.current_env) {
5583
5610
  setEnvTimeline(null);
@@ -5806,6 +5833,7 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5806
5833
  disabled=${item.status === 'Superseded'}
5807
5834
  onChange=${async (e) => {
5808
5835
  const newStatus = e.target.value;
5836
+ setStatusError('');
5809
5837
  if (newStatus === 'Superseded') {
5810
5838
  e.target.value = item.status || 'New';
5811
5839
  setShowSupersededModal(true);
@@ -5814,10 +5842,20 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5814
5842
  try {
5815
5843
  await api('PATCH', '/api/board/items/' + item.id, { status: newStatus });
5816
5844
  onStatusChange();
5817
- } catch (err) { /* ignore */ }
5845
+ } catch (err) {
5846
+ e.target.value = item.status || 'New';
5847
+ const msg = (err && err.message) || 'Status change failed';
5848
+ setStatusError(msg);
5849
+ setTimeout(() => setStatusError(''), 5000);
5850
+ }
5818
5851
  }}>
5819
- ${STATUSES.map(s => html`<option value=${s} selected=${s === (item.status || 'New')}>${s}</option>`)}
5852
+ ${validStatuses.map(s => html`<option value=${s} selected=${s === (item.status || 'New')}>${s}</option>`)}
5820
5853
  </select>
5854
+ ${statusError && html`
5855
+ <div role="alert" style="color:var(--danger);font-size:11px;margin-top:4px;" data-testid="status-error">
5856
+ ${statusError}
5857
+ </div>
5858
+ `}
5821
5859
 
5822
5860
  <label>Priority</label>
5823
5861
  <select value=${item.priority || ''} onChange=${async (e) => {
@@ -7181,10 +7219,11 @@ function BoardPage({ selectedProject }) {
7181
7219
  `;
7182
7220
  }
7183
7221
 
7184
- const totalItems = swimlaneFilteredItems.length;
7185
- const activeItems = swimlaneFilteredItems.filter(i => i.status !== 'Done').length;
7186
- const doneItems = swimlaneFilteredItems.filter(i => i.status === 'Done').length;
7187
- const stuckItems = swimlaneFilteredItems.filter(i => i.status === 'Stuck').length;
7222
+ const realFilteredItems = swimlaneFilteredItems.filter(i => !_isFixtureItem(i));
7223
+ const totalItems = realFilteredItems.length;
7224
+ const activeItems = realFilteredItems.filter(i => i.status !== 'Done').length;
7225
+ const doneItems = realFilteredItems.filter(i => i.status === 'Done').length;
7226
+ const stuckItems = realFilteredItems.filter(i => i.status === 'Stuck').length;
7188
7227
  const progressPct = totalItems > 0 ? Math.round((doneItems / totalItems) * 100) : 0;
7189
7228
  const isFiltered = searchTerm.length >= 2 || activeFilterCount > 0;
7190
7229
 
@@ -1 +1 @@
1
- c27f4a2bafaeb0dc4bc9d5359b2b87c81f3f41bc
1
+ 2efe699f470b333750fc7e22e76e91d7581761c1
@@ -1 +1 @@
1
- 20260514-100818-c27f4a2
1
+ 20260514-104557-2efe699
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beastmode-develeap/beastmode",
3
- "version": "0.1.245",
3
+ "version": "0.1.247",
4
4
  "description": "BeastMode Dark Factory — turn intent into verified software",
5
5
  "type": "module",
6
6
  "bin": {