@galda/cli 0.10.104 → 0.10.105

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.
Files changed (2) hide show
  1. package/app/index.html +50 -28
  2. package/package.json +1 -1
package/app/index.html CHANGED
@@ -2235,6 +2235,19 @@
2235
2235
  /* Changed/Tests aligns with the request TEXT, not the number/chevron — same 18px
2236
2236
  the fold body below uses, so both sit under the same column (Masa 2026-07-29). */
2237
2237
  .redesign .rqit .rqev{font-size:12px;color:var(--ink3);margin:5px 0 0;padding-left:18px}
2238
+ /* Canonical requirement coverage: one compact ledger, not three competing
2239
+ interpretations. Status is text + a small rule/mark (no badge surface),
2240
+ and Manager checks stay visually distinct from worker claims. */
2241
+ .redesign .rqcov{display:flex;flex-direction:column;margin-top:2px}
2242
+ .redesign .rqcov-row{padding:11px 0;border-top:1px solid var(--hair)}
2243
+ .redesign .rqcov-row:first-child{padding-top:0;border-top:0}
2244
+ .redesign .rqcov-main{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:14px;align-items:baseline}
2245
+ .redesign .rqcov-text{font-size:13.5px;line-height:1.55;color:var(--ink)}
2246
+ .redesign .rqcov-state{font:600 10px/1.4 var(--mono);letter-spacing:.02em;color:var(--ink3);white-space:nowrap}
2247
+ .redesign .rqcov-state.verified{color:var(--green)}
2248
+ .redesign .rqcov-state.failed{color:var(--danger)}
2249
+ .redesign .rqcov-detail{margin-top:5px;font-size:11.5px;line-height:1.55;color:var(--ink3)}
2250
+ .redesign .rqcov-detail b{font-weight:600;color:var(--ink2)}
2238
2251
  /* A long ask folds with the SAME .lacc pulldown Activity/Conversation use — chevron on
2239
2252
  the peek line, body is the genuine rest (no duplicate, no trailing "…": Masa
2240
2253
  2026-07-29 flagged both the vertical anchor line and the "…" as unnecessary — the
@@ -9473,6 +9486,33 @@ function latestGoalRunForOutcome(goalId, tasks, outcome){
9473
9486
  note: prev.run?.note ? `${note} ${prev.run.note}` : note,
9474
9487
  };
9475
9488
  }
9489
+ function canonicalRequirementCoverage(goal, tasks){
9490
+ const requirements = Array.isArray(goal?.requirements) ? goal.requirements.filter((r) => r?.id && r?.text) : [];
9491
+ if (!requirements.length) {
9492
+ return (tasks || []).filter((t) => t.goalId === goal?.id && (t.detail || t.title) && (!t.reply || !t.replyFrom || t.replyFrom === 'you'))
9493
+ .sort((a, b) => (a.num ?? 0) - (b.num ?? 0))
9494
+ .map((t) => ({ id: `legacy-task-${t.id}`, text: String((t.reply ? t.detail : t.title) ?? t.title ?? '').replace(/\s+/g, ' ').trim(), coverage: 'unverified', legacy: true }));
9495
+ }
9496
+ const attemptOrder = new Map((goal.attempts || []).map((attempt, index) => [attempt.id, index]));
9497
+ const evidence = Array.isArray(goal.requirementEvidence) ? goal.requirementEvidence : [];
9498
+ return requirements.map((requirement) => {
9499
+ const rows = evidence.filter((row) => row?.requirementId === requirement.id)
9500
+ .sort((a, b) => (attemptOrder.get(a.attemptId) ?? -1) - (attemptOrder.get(b.attemptId) ?? -1));
9501
+ const latestAttemptId = rows.at(-1)?.attemptId;
9502
+ const latestRows = latestAttemptId ? rows.filter((row) => row.attemptId === latestAttemptId) : [];
9503
+ const statuses = latestRows.map((row) => row.coverage);
9504
+ const coverage = statuses.includes('failed') ? 'failed'
9505
+ : statuses.includes('verified') ? 'verified'
9506
+ : statuses.length && statuses.every((status) => status === 'not-applicable') ? 'not-applicable'
9507
+ : 'unverified';
9508
+ const claims = [...new Set(latestRows.map((row) => row.claim).filter(Boolean))];
9509
+ const workerEvidence = [...new Set(latestRows.map((row) => row.workerEvidence || row.evidence).filter(Boolean))];
9510
+ const checks = latestRows.flatMap((row) => Array.isArray(row.checks) ? row.checks : [])
9511
+ .filter((check) => Array.isArray(check.requirementIds) && check.requirementIds.includes(requirement.id));
9512
+ const checkFacts = [...new Set(checks.map((check) => `${check.kind}: ${check.status}`))];
9513
+ return { id: requirement.id, text: requirement.text, coverage, claims, workerEvidence, checkFacts, attemptId: latestAttemptId ?? null };
9514
+ });
9515
+ }
9476
9516
  // One digest-row → one See-all card item. Extracted from saRows (2026-07-24) so a single
9477
9517
  // "Needs you" goal can be opened as the SAME review card (saLedgerRows solo mode) — the card's
9478
9518
  // whole shape (Request/Result/Proof/Activity, empty sections auto-hidden) is reused verbatim.
@@ -9522,10 +9562,7 @@ function saRowFromDigest(r, gs, ts){
9522
9562
  // simply omit it, so the UI remains backward compatible during rollout.
9523
9563
  attempt: g?.reviewAttempt ?? null,
9524
9564
  unmappedChangedFiles: outcome?.unmappedChangedFiles ?? [],
9525
- requirementVerification: outcome?.requirementVerification ?? null,
9526
- requirementCoverage: ts.filter((t) => t.goalId === g?.id && (t.detail || t.title)).sort((a, b) => (a.num ?? 0) - (b.num ?? 0)).map((t) => ({ text: String((t.reply ? t.detail : t.title) ?? t.title ?? '').replace(/\s+/g, ' ').trim(), taskStatus: t.status, evidenceStatus: t.requirementEvidence?.requirements?.[0]?.status ?? null })),
9527
- requirementEvidence: outcome?.requirementEvidence?.requirements ?? [],
9528
- requirementCoverage: ts.filter((t) => t.goalId === g?.id && (t.detail || t.title) && (!t.reply || !t.replyFrom || t.replyFrom === 'you')).sort((a, b) => (a.num ?? 0) - (b.num ?? 0)).map((t) => ({ text: String((t.reply ? t.detail : t.title) ?? t.title ?? '').replace(/\s+/g, ' ').trim(), status: t.status })),
9565
+ requirementCoverage: canonicalRequirementCoverage(g, ts),
9529
9566
  did, siblings, risk: g?.risk ?? null,
9530
9567
  verificationInfraError: g?.verificationInfraError ?? r.verificationInfraError ?? null,
9531
9568
  prNote: g?.prNote ?? null,
@@ -10081,31 +10118,18 @@ function saItemHtml(it, i){
10081
10118
  const reqSec = reqStackRows
10082
10119
  ? `<section class="rsec rreq" data-reqmsgs="${gk}"><div class="rsec-h">Request</div><div class="rqlist">${reqStackRows}</div></section>`
10083
10120
  : '';
10084
- const reqVerify = it.requirementVerification;
10085
- const reqVerifyParts = [
10086
- reqVerify?.proof?.verified ? 'Independent proof recorded' : reqVerify?.proof ? 'Proof recorded' : '',
10087
- reqVerify?.run?.url ? `Open it: ${reqVerify.run.url}` : '',
10088
- Array.isArray(reqVerify?.changedFiles) && reqVerify.changedFiles.length ? `Files: ${reqVerify.changedFiles.join(' · ')}` : '',
10089
- ].filter(Boolean);
10090
- const reqVerifySec = reqVerifyParts.length ? `<section class="rsec"><div class="rsec-h">Requirement verification</div><p class="rbody v">${esc(reqVerifyParts.join(' · '))}</p></section>` : '';
10091
10121
  const coverage = (it.requirementCoverage || []).filter((r) => r.text);
10092
- // For one requirement copied from Request, its status is useful but the full
10093
- // sentence is not. Multiple or narrowed requirements retain their text.
10094
- const coverageIsSingleRequest = coverage.length === 1
10095
- && String(coverage[0].text).replace(/\s+/g, ' ').trim() === requestText;
10096
10122
  const coverageRows = coverage.map((r) => {
10097
- const status = r.evidenceStatus || r.taskStatus;
10098
- const tone = status === 'addressed' || status === 'done' ? 'ok' : status === 'partial' ? '' : 'dim';
10099
- return `<p class="rbody v"><b class="${tone}">${esc(status)}</b>${coverageIsSingleRequest ? '' : ` · ${esc(r.text)}`}</p>`;
10100
- }).join('');
10101
- const coverageSec = coverageRows ? `<section class="rsec"><div class="rsec-h">Requirement coverage</div>${coverageRows}</section>` : '';
10102
- // Worker-declared evidence is displayed verbatim as an auditable label, not
10103
- // promoted into a Manager verdict. Empty means the worker did not provide it.
10104
- const evidenceRows = (it.requirementEvidence || []).map((entry) => {
10105
- const tone = entry.status === 'addressed' ? 'ok' : entry.status === 'partial' ? '' : 'dim';
10106
- return `<p class="rbody v"><b class="${tone}">${esc(entry.status)}</b> · ${esc(entry.evidence || entry.id)}</p>`;
10123
+ const labels = { verified: 'Verified', failed: 'Failed', 'not-applicable': 'Not applicable', unverified: 'Not verified' };
10124
+ const details = [
10125
+ r.legacy ? 'Legacy item no canonical evidence was recorded' : '',
10126
+ r.checkFacts?.length ? `Manager: ${r.checkFacts.join(' · ')}` : '',
10127
+ r.claims?.length ? `Worker claim: ${r.claims.join(', ')}` : '',
10128
+ r.workerEvidence?.length ? r.workerEvidence.join(' · ') : '',
10129
+ ].filter(Boolean);
10130
+ return `<div class="rqcov-row" data-requirement-id="${esc(r.id)}"><div class="rqcov-main"><div class="rqcov-text">${esc(r.text)}</div><span class="rqcov-state ${esc(r.coverage)}">${esc(labels[r.coverage] || labels.unverified)}</span></div>${details.length ? `<div class="rqcov-detail">${details.map((detail, index) => index === 0 ? esc(detail) : `<br>${esc(detail)}`).join('')}</div>` : ''}</div>`;
10107
10131
  }).join('');
10108
- const evidenceSec = evidenceRows ? `<section class="rsec"><div class="rsec-h">Requirement evidence</div>${evidenceRows}</section>` : '';
10132
+ const coverageSec = coverageRows ? `<section class="rsec"><div class="rsec-h">Requirement coverage</div><div class="rqcov">${coverageRows}</div></section>` : '';
10109
10133
  // ── Open it (Masa 2026-07-22): a plain section in the card's own typography — the same
10110
10134
  // heading + body shape as Request and Result, not a chip in another vocabulary. The
10111
10135
  // address the worker declared IS the control (press it → the process starts on demand →
@@ -10355,8 +10379,6 @@ function saItemHtml(it, i){
10355
10379
  ${S('Open it', openBody + infraBody)}
10356
10380
  ${S('What to check', checkBody)}
10357
10381
  ${coverageSec}
10358
- ${evidenceSec}
10359
- ${reqVerifySec}
10360
10382
  ${S('Run state', interruptedBody)}
10361
10383
  ${S('PR', prRepairBody)}
10362
10384
  ${S('Also carries', carriesBody)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@galda/cli",
3
- "version": "0.10.104",
3
+ "version": "0.10.105",
4
4
  "type": "module",
5
5
  "description": "Galda - hand off work to Claude Code or Codex, get proof back. Runs on your existing subscription, no extra API cost.",
6
6
  "scripts": {