@beastmode-develeap/beastmode 0.1.187 → 0.1.189

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__ = "20260507-130556-55b1f3a";</script>
18
+ <script>window.__BUILD_STAMP__ = "20260507-140717-1212ed1";</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">
@@ -2979,10 +2979,14 @@ input[type="range"]::-webkit-slider-thumb {
2979
2979
  font-size: 13px;
2980
2980
  font-weight: 500;
2981
2981
  color: var(--text);
2982
- white-space: nowrap;
2982
+ display: -webkit-box;
2983
+ -webkit-line-clamp: 2;
2984
+ -webkit-box-orient: vertical;
2983
2985
  overflow: hidden;
2984
2986
  text-overflow: ellipsis;
2985
2987
  margin-bottom: 4px;
2988
+ word-break: break-word;
2989
+ line-height: 1.35;
2986
2990
  }
2987
2991
  .activity-meta {
2988
2992
  display: flex;
@@ -2993,6 +2997,8 @@ input[type="range"]::-webkit-slider-thumb {
2993
2997
  font-size: 11px;
2994
2998
  color: var(--text-muted);
2995
2999
  font-family: var(--font-mono);
3000
+ flex-shrink: 0;
3001
+ margin-left: auto;
2996
3002
  }
2997
3003
  .badge-sm {
2998
3004
  display: inline-block;
@@ -3004,6 +3010,100 @@ input[type="range"]::-webkit-slider-thumb {
3004
3010
  font-weight: 500;
3005
3011
  white-space: nowrap;
3006
3012
  }
3013
+ .activity-meta .badge-sm {
3014
+ max-width: 160px;
3015
+ overflow: hidden;
3016
+ text-overflow: ellipsis;
3017
+ }
3018
+ .activity-list-wrapper {
3019
+ position: relative;
3020
+ }
3021
+ .activity-list-wrapper::after {
3022
+ content: '';
3023
+ position: absolute;
3024
+ bottom: 0;
3025
+ left: 0;
3026
+ right: 0;
3027
+ height: 24px;
3028
+ background: linear-gradient(transparent, var(--bg-card));
3029
+ pointer-events: none;
3030
+ }
3031
+ /* Scenario × Environment Matrix */
3032
+ .scenario-matrix-wrapper {
3033
+ overflow-x: auto;
3034
+ border: 1px solid var(--border);
3035
+ border-radius: var(--radius-sm);
3036
+ background: var(--bg-card);
3037
+ }
3038
+ .scenario-matrix {
3039
+ width: 100%;
3040
+ font-size: 12px;
3041
+ border-collapse: collapse;
3042
+ }
3043
+ .scenario-matrix th {
3044
+ text-align: center;
3045
+ padding: 8px 10px;
3046
+ border-bottom: 1px solid var(--border);
3047
+ white-space: nowrap;
3048
+ background: var(--bg-input);
3049
+ font-weight: 600;
3050
+ }
3051
+ .scenario-matrix th.scenario-col {
3052
+ text-align: left;
3053
+ }
3054
+ .scenario-matrix td {
3055
+ padding: 6px 10px;
3056
+ border-bottom: 1px solid var(--border);
3057
+ text-align: center;
3058
+ vertical-align: middle;
3059
+ }
3060
+ .scenario-matrix tr:last-child td {
3061
+ border-bottom: none;
3062
+ }
3063
+ .scenario-matrix td.scenario-name-cell {
3064
+ text-align: left;
3065
+ white-space: nowrap;
3066
+ max-width: 220px;
3067
+ overflow: hidden;
3068
+ text-overflow: ellipsis;
3069
+ }
3070
+ .scenario-matrix-id {
3071
+ font-family: var(--font-mono);
3072
+ color: var(--accent);
3073
+ margin-right: 6px;
3074
+ }
3075
+ .scenario-matrix-included {
3076
+ color: var(--success);
3077
+ font-weight: 700;
3078
+ }
3079
+ .scenario-matrix-excluded {
3080
+ color: var(--text-muted);
3081
+ opacity: 0.4;
3082
+ }
3083
+ .scenario-matrix-severity {
3084
+ display: block;
3085
+ font-size: 10px;
3086
+ margin-top: 2px;
3087
+ }
3088
+ .scenario-matrix-severity.is-override {
3089
+ font-style: italic;
3090
+ }
3091
+ .scenario-matrix-toggle {
3092
+ display: flex;
3093
+ align-items: center;
3094
+ gap: 6px;
3095
+ cursor: pointer;
3096
+ user-select: none;
3097
+ }
3098
+ .scenario-matrix-toggle-icon {
3099
+ font-size: 10px;
3100
+ color: var(--text-muted);
3101
+ transition: transform 0.2s;
3102
+ display: inline-block;
3103
+ }
3104
+ .scenario-matrix-toggle-icon.is-open {
3105
+ transform: rotate(90deg);
3106
+ }
3007
3107
  .run-item {
3008
3108
  padding: 10px 12px;
3009
3109
  border-radius: var(--radius-sm);
@@ -4072,6 +4172,7 @@ function DashboardPage({ selectedProject, onProjectChange }) {
4072
4172
  <h3>Recent Activity</h3>
4073
4173
  <span class="badge badge-muted">${recentItems.length}</span>
4074
4174
  </div>
4175
+ <div class="activity-list-wrapper">
4075
4176
  <div class="activity-list">
4076
4177
  ${recentItems.map(item => html`
4077
4178
  <div class="activity-item" key=${item.id}>
@@ -4084,6 +4185,7 @@ function DashboardPage({ selectedProject, onProjectChange }) {
4084
4185
  `)}
4085
4186
  ${recentItems.length === 0 && html`<div class="empty-text" style="padding:12px;">No recent activity</div>`}
4086
4187
  </div>
4188
+ </div>
4087
4189
  </div>
4088
4190
 
4089
4191
  <div class="card">
@@ -4091,6 +4193,7 @@ function DashboardPage({ selectedProject, onProjectChange }) {
4091
4193
  <h3>Active Runs</h3>
4092
4194
  <span class="badge badge-muted">${activeRuns.length}</span>
4093
4195
  </div>
4196
+ <div class="activity-list-wrapper">
4094
4197
  <div class="activity-list">
4095
4198
  ${activeRuns.map(run => {
4096
4199
  const cp = run.checkpoint || {};
@@ -4114,6 +4217,7 @@ function DashboardPage({ selectedProject, onProjectChange }) {
4114
4217
  })}
4115
4218
  ${activeRuns.length === 0 && html`<div class="empty-text" style="padding:12px;">No active runs</div>`}
4116
4219
  </div>
4220
+ </div>
4117
4221
  </div>
4118
4222
  </div>
4119
4223
  </div>
@@ -6739,12 +6843,14 @@ function stageLabel(stage, extra) {
6739
6843
  return { label: stage, cls: 'badge-muted' };
6740
6844
  }
6741
6845
 
6742
- function RunDetail({ detail }) {
6846
+ function RunDetail({ detail, matrixData, matrixLoading, matrixError, matrixOpen, onToggleMatrix }) {
6743
6847
  const cp = detail.checkpoint || {};
6744
6848
  const stages = cp.completed_stages || [];
6745
6849
  const iterations = detail.iterations || [];
6746
6850
  const prUrl = cp.pr_url || '';
6747
6851
  const itemId = detail.manifest?.item_id || detail.manifest?.monday_item_id || '';
6852
+ const hasOverrides = matrixData && matrixData.scenarios.some(sc => sc.severity_overrides && Object.keys(sc.severity_overrides).length > 0);
6853
+ const sevColorFor = (sev) => sev === 'critical' ? 'var(--danger)' : sev === 'high' ? 'var(--warning)' : sev === 'medium' ? 'var(--accent)' : 'var(--text-muted)';
6748
6854
 
6749
6855
  return html`
6750
6856
  <div>
@@ -6806,6 +6912,68 @@ function RunDetail({ detail }) {
6806
6912
  </div>
6807
6913
  ` : null}
6808
6914
 
6915
+ <!-- Scenario × Environment Matrix -->
6916
+ <div style="margin-bottom:16px;" data-testid="scenario-env-matrix">
6917
+ <div class="scenario-matrix-toggle" onClick=${onToggleMatrix}>
6918
+ <span class=${'scenario-matrix-toggle-icon' + (matrixOpen ? ' is-open' : '')}>▶</span>
6919
+ <span style="font-size:12px;font-weight:600;color:var(--text-secondary);">Scenario × Environment Matrix</span>
6920
+ ${matrixData ? html`<span class="badge badge-muted" style="font-size:10px;">${matrixData.scenarios.length}</span>` : null}
6921
+ </div>
6922
+ ${matrixOpen ? html`
6923
+ <div style="margin-top:12px;">
6924
+ ${matrixLoading ? html`<div style="font-size:12px;color:var(--text-muted);padding:8px 0;">Loading matrix...</div>` : null}
6925
+ ${matrixError ? html`<div class="error-msg">${matrixError}</div>` : null}
6926
+ ${matrixData && matrixData.scenarios.length > 0 ? html`
6927
+ <div class="scenario-matrix-wrapper">
6928
+ <table class="scenario-matrix">
6929
+ <thead>
6930
+ <tr>
6931
+ <th class="scenario-col">Scenario</th>
6932
+ ${matrixData.environments.map(env => html`
6933
+ <th key=${env}><span class="badge-sm">${env}</span></th>
6934
+ `)}
6935
+ </tr>
6936
+ </thead>
6937
+ <tbody>
6938
+ ${matrixData.scenarios.map(sc => html`
6939
+ <tr key=${sc.id}>
6940
+ <td class="scenario-name-cell" title=${sc.title}>
6941
+ <span class="scenario-matrix-id">${sc.id}</span>${sc.title}
6942
+ </td>
6943
+ ${matrixData.environments.map(env => {
6944
+ const included = (sc.included_in || []).includes(env);
6945
+ const sev = (sc.effective_severity && sc.effective_severity[env]) || '';
6946
+ const isOverride = sc.severity_overrides && Object.prototype.hasOwnProperty.call(sc.severity_overrides, env);
6947
+ return html`
6948
+ <td key=${env}>
6949
+ ${included ? html`
6950
+ <span class="scenario-matrix-included" aria-label="included">✓</span>
6951
+ ${sev ? html`
6952
+ <span class=${'scenario-matrix-severity' + (isOverride ? ' is-override' : '')}
6953
+ style=${'color:' + sevColorFor(sev) + ';'}>
6954
+ ${sev}${isOverride ? '*' : ''}
6955
+ </span>
6956
+ ` : null}
6957
+ ` : html`<span class="scenario-matrix-excluded" aria-label="excluded">—</span>`}
6958
+ </td>
6959
+ `;
6960
+ })}
6961
+ </tr>
6962
+ `)}
6963
+ </tbody>
6964
+ </table>
6965
+ </div>
6966
+ ${hasOverrides ? html`
6967
+ <div style="font-size:10px;color:var(--text-muted);margin-top:6px;font-style:italic;">* severity overridden for this environment</div>
6968
+ ` : null}
6969
+ ` : null}
6970
+ ${matrixData && matrixData.scenarios.length === 0 && !matrixError ? html`
6971
+ <div style="font-size:12px;color:var(--text-muted);padding:8px 0;">No scenarios found for this run.</div>
6972
+ ` : null}
6973
+ </div>
6974
+ ` : null}
6975
+ </div>
6976
+
6809
6977
  <!-- Action Links -->
6810
6978
  <div style="display:flex;gap:8px;flex-wrap:wrap;">
6811
6979
  ${itemId ? html`
@@ -6829,6 +6997,10 @@ function RunsPage({ selectedProject }) {
6829
6997
  const [expandedId, setExpandedId] = useState(null);
6830
6998
  const [detail, setDetail] = useState(null);
6831
6999
  const [pinnedIds, setPinnedIds] = useState({});
7000
+ const [matrixData, setMatrixData] = useState(null);
7001
+ const [matrixLoading, setMatrixLoading] = useState(false);
7002
+ const [matrixError, setMatrixError] = useState(null);
7003
+ const [matrixOpen, setMatrixOpen] = useState(false);
6832
7004
 
6833
7005
  const fetchRuns = () => {
6834
7006
  api('GET', '/api/runs')
@@ -6883,12 +7055,27 @@ function RunsPage({ selectedProject }) {
6883
7055
  if (expandedId === id) {
6884
7056
  setExpandedId(null);
6885
7057
  setDetail(null);
7058
+ setMatrixData(null);
7059
+ setMatrixError(null);
7060
+ setMatrixOpen(false);
6886
7061
  return;
6887
7062
  }
6888
7063
  try {
6889
7064
  const data = await api('GET', '/api/runs/' + id);
6890
7065
  setDetail(data);
6891
7066
  setExpandedId(id);
7067
+ setMatrixData(null);
7068
+ setMatrixError(null);
7069
+ setMatrixLoading(true);
7070
+ const pid = data.manifest?.project_id || 'beastmode';
7071
+ api('GET', '/api/debug/scenario-env-matrix?run_id=' + encodeURIComponent(id) + '&project=' + encodeURIComponent(pid))
7072
+ .then(m => { setMatrixData(m); setMatrixLoading(false); })
7073
+ .catch(e => {
7074
+ const raw = e && e.message ? e.message : 'Failed to load matrix';
7075
+ const msg = /not found/i.test(raw) ? 'Scenario matrix not available for this run' : raw;
7076
+ setMatrixError(msg);
7077
+ setMatrixLoading(false);
7078
+ });
6892
7079
  } catch (e) { setError(e.message); }
6893
7080
  };
6894
7081
 
@@ -6901,7 +7088,14 @@ function RunsPage({ selectedProject }) {
6901
7088
  <p>Pipeline run history</p>
6902
7089
  </div>
6903
7090
 
6904
- ${error ? html`<div class="error-msg">${error}</div>` : null}
7091
+ ${error ? html`<div class="error-msg" data-testid="runs-error" style="display:flex;align-items:center;">
7092
+ <span style="flex:1;">${error}</span>
7093
+ <button
7094
+ aria-label="Dismiss error"
7095
+ data-testid="runs-error-dismiss"
7096
+ style="background:none;border:none;color:var(--danger);cursor:pointer;font-size:14px;padding:0 4px;line-height:1;margin-left:8px;"
7097
+ onClick=${() => setError(null)}>✕</button>
7098
+ </div>` : null}
6905
7099
 
6906
7100
  ${filteredRuns.length === 0
6907
7101
  ? html`<${EmptyState}
@@ -6965,7 +7159,13 @@ function RunsPage({ selectedProject }) {
6965
7159
  ${expandedId === run.id && detail ? html`
6966
7160
  <tr key=${run.id + '-detail'}>
6967
7161
  <td colspan="7" style="background:var(--surface-elevated);padding:20px;border-left:3px solid var(--accent);">
6968
- <${RunDetail} detail=${detail} />
7162
+ <${RunDetail}
7163
+ detail=${detail}
7164
+ matrixData=${matrixData}
7165
+ matrixLoading=${matrixLoading}
7166
+ matrixError=${matrixError}
7167
+ matrixOpen=${matrixOpen}
7168
+ onToggleMatrix=${() => setMatrixOpen(o => !o)} />
6969
7169
  </td>
6970
7170
  </tr>
6971
7171
  ` : null}
@@ -7021,6 +7221,9 @@ function EnvironmentRow({ env, health, onEdit, onDelete }) {
7021
7221
  : html`<span style="font-size:12px;color:var(--text-muted);">—</span>`}
7022
7222
  </td>
7023
7223
  <td style="font-size:12px;">${classesDisplay}</td>
7224
+ <td style="font-size:12px;color:var(--text-secondary);">
7225
+ ${env.last_deploy ? timeAgo(env.last_deploy) : html`<span style="color:var(--text-muted);">—</span>`}
7226
+ </td>
7024
7227
  <td style="text-align:center;">${env.auto_promote ? '✓' : '—'}</td>
7025
7228
  <td style="text-align:center;">${env.approval_required ? '✓' : '—'}</td>
7026
7229
  <td>
@@ -7051,6 +7254,7 @@ function EnvironmentsTable({ environments, healthStatuses, onEdit, onDelete }) {
7051
7254
  <th id="env-table-label">Name</th>
7052
7255
  <th>Health</th>
7053
7256
  <th>Scenario Classes</th>
7257
+ <th style="width:100px;">Last Deploy</th>
7054
7258
  <th style="width:60px;text-align:center;">Auto</th>
7055
7259
  <th style="width:60px;text-align:center;">Approval</th>
7056
7260
  <th>Promotes to</th>
@@ -1 +1 @@
1
- 55b1f3ac5a1c28532a083c98fab8b6321ff5598d
1
+ 1212ed162ede891b7a1d226aafb9cf88d90c02b0
@@ -1 +1 @@
1
- 20260507-130556-55b1f3a
1
+ 20260507-140717-1212ed1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beastmode-develeap/beastmode",
3
- "version": "0.1.187",
3
+ "version": "0.1.189",
4
4
  "description": "BeastMode Dark Factory — turn intent into verified software",
5
5
  "type": "module",
6
6
  "bin": {