commitshow 0.4.1 → 0.4.2

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.
package/dist/lib/api.js CHANGED
@@ -161,13 +161,25 @@ export async function waitForPreviewSnapshot(projectId, since, timeoutMs = 180_0
161
161
  // Otherwise require last_analysis_at to have advanced.
162
162
  if (sinceMs === 0 || lastMs > sinceMs) {
163
163
  const snapshot = await fetchLatestSnapshot(projectId);
164
- return {
165
- project,
166
- snapshot,
167
- standing: null,
168
- is_preview: project.status === 'preview',
169
- cache_hit: false,
170
- };
164
+ // Race guard · analyze-project's concurrency claim-lock stamps
165
+ // projects.last_analysis_at = now() BEFORE the audit runs (see
166
+ // analyze-project line 4699+ · "datasette/2026-05-12 race"
167
+ // comment block · the lock serializes parallel audits). Without
168
+ // this guard the poll would return inside the claim-lock window
169
+ // with project.score_total = 0 (untouched initial) and
170
+ // snapshot = null · the user saw a 0/100 trophy on a fresh
171
+ // first-time audit. Wait for an actual snapshot whose
172
+ // created_at advances past the user's baseline.
173
+ const snapMs = snapshot?.created_at ? new Date(snapshot.created_at).getTime() : 0;
174
+ if (snapshot && snapMs > sinceMs) {
175
+ return {
176
+ project,
177
+ snapshot,
178
+ standing: null,
179
+ is_preview: project.status === 'preview',
180
+ cache_hit: false,
181
+ };
182
+ }
171
183
  }
172
184
  }
173
185
  await new Promise(r => setTimeout(r, intervalMs));
@@ -469,7 +469,15 @@ export function renderAudit(view) {
469
469
  // the calibrated total.
470
470
  const WALK_ON_AUDIT_MAX = 50;
471
471
  const isWalkOn = p.status === 'preview';
472
- const total = p.score_total ?? 0;
472
+ // Prefer snapshot.score_total over project.score_total defensive
473
+ // against the analyze-project claim-lock race (the lock stamps
474
+ // projects.last_analysis_at BEFORE the audit body runs · during that
475
+ // window project.score_total is still the 0 default while a freshly
476
+ // landed snapshot already carries the real number). The api.ts
477
+ // poller's race guard also ensures snapshot exists, but rendering
478
+ // also prefers snapshot here so any future call site that reads a
479
+ // fresh envelope without the guard still gets the truth.
480
+ const total = snapshot?.score_total ?? p.score_total ?? 0;
473
481
  const lines = [];
474
482
  // Big COMMIT.SHOW ANSI Shadow banner. Three-tier fallback by width:
475
483
  // · cols ≥ 99 → single-line "COMMIT.SHOW"
@@ -728,7 +736,10 @@ export function renderAudit(view) {
728
736
  // — same visual frame for share screenshots.
729
737
  const lockedBar = '─ audition unlocks ─';
730
738
  const auditDen = isWalkOn ? WALK_ON_AUDIT_MAX : 50;
731
- const auditScoreClamp = Math.min(p.score_auto ?? 0, auditDen);
739
+ // Prefer snapshot.score_auto over project.score_auto same claim-lock
740
+ // race rationale as the `total` line above.
741
+ const auditScore = snapshot?.score_auto ?? p.score_auto ?? 0;
742
+ const auditScoreClamp = Math.min(auditScore, auditDen);
732
743
  lines.push(' ' + ` Audit ${pad(`${auditScoreClamp}/${auditDen}`, 7)} ${scoreBar(auditScoreClamp, auditDen)}`);
733
744
  if (isWalkOn) {
734
745
  lines.push(' ' + ` Scout ${pad('—/30', 7)} ` + c.muted(lockedBar));
@@ -961,11 +972,18 @@ export function renderMarkdown(view) {
961
972
  lines.push(`- ${s}`);
962
973
  lines.push('');
963
974
  }
964
- lines.push(`## Score · ${p.score_total} / 100`);
975
+ // Prefer snapshot scores · race-safe vs analyze-project's claim-lock
976
+ // window where projects.score_* are still 0 while the snapshot already
977
+ // carries the real numbers. See renderAudit() comment block above.
978
+ const mdTotal = snapshot?.score_total ?? p.score_total;
979
+ const mdAudit = snapshot?.score_auto ?? p.score_auto;
980
+ const mdForecast = snapshot?.score_forecast ?? p.score_forecast;
981
+ const mdCommunity = snapshot?.score_community ?? p.score_community;
982
+ lines.push(`## Score · ${mdTotal} / 100`);
965
983
  lines.push('');
966
- lines.push(`- Audit: ${p.score_auto}/50`);
967
- lines.push(`- Scout: ${p.score_forecast}/30`);
968
- lines.push(`- Community: ${p.score_community}/20`);
984
+ lines.push(`- Audit: ${mdAudit}/50`);
985
+ lines.push(`- Scout: ${mdForecast}/30`);
986
+ lines.push(`- Community: ${mdCommunity}/20`);
969
987
  if (delta != null && delta !== 0) {
970
988
  lines.push(`- **Δ ${delta > 0 ? '+' : ''}${delta}** since last audit`);
971
989
  }
@@ -1025,9 +1043,16 @@ export function toAgentShape(view) {
1025
1043
  // pillar-only fallback (Brief slot excluded · base /45).
1026
1044
  const WALK_ON_AUDIT_MAX = 50;
1027
1045
  const isWalkOn = p.status === 'preview';
1028
- const walkOnTotal = isWalkOn ? (p.score_total ?? 0) : null;
1046
+ // Prefer snapshot scores · race-safe vs analyze-project's claim-lock
1047
+ // window (see renderAudit comment). Snapshot carries the canonical
1048
+ // numbers for this run · projects.score_* are denormalized later.
1049
+ const jTotal = snapshot?.score_total ?? p.score_total;
1050
+ const jAudit = snapshot?.score_auto ?? p.score_auto;
1051
+ const jForecast = snapshot?.score_forecast ?? p.score_forecast;
1052
+ const jCommunity = snapshot?.score_community ?? p.score_community;
1053
+ const walkOnTotal = isWalkOn ? (jTotal ?? 0) : null;
1029
1054
  const walkOnAuditNormalized = isWalkOn
1030
- ? Math.min(100, Math.round(((p.score_auto ?? 0) / WALK_ON_AUDIT_MAX) * 100))
1055
+ ? Math.min(100, Math.round(((jAudit ?? 0) / WALK_ON_AUDIT_MAX) * 100))
1031
1056
  : null;
1032
1057
  return {
1033
1058
  schema_version: '1',
@@ -1044,16 +1069,16 @@ export function toAgentShape(view) {
1044
1069
  },
1045
1070
  score: {
1046
1071
  track: isWalkOn ? 'walk_on' : 'league',
1047
- total: p.score_total,
1072
+ total: jTotal,
1048
1073
  total_max: 100,
1049
- audit: p.score_auto,
1074
+ audit: jAudit,
1050
1075
  audit_max: 50,
1051
- scout: p.score_forecast,
1076
+ scout: jForecast,
1052
1077
  scout_max: 30,
1053
- community: p.score_community,
1078
+ community: jCommunity,
1054
1079
  community_max: 20,
1055
1080
  delta_since_last: snapshot?.score_total_delta ?? null,
1056
- band: bandFor(p.score_total),
1081
+ band: bandFor(jTotal ?? 0),
1057
1082
  walk_on_total: walkOnTotal,
1058
1083
  walk_on_band: walkOnTotal != null ? bandFor(walkOnTotal) : null,
1059
1084
  walk_on_audit_normalized: walkOnAuditNormalized,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commitshow",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "commit.show CLI — audit any vibe-coded project from your terminal.",
5
5
  "type": "module",
6
6
  "bin": {