Skip to content

Commit 9217968

Browse files
jacob-balslevclaude
andcommitted
fix(panel): renderCollected is tier-aware (QUALITY tier) so it serves both panels
The shared collected-view renderer only treated tier=mandatory as the grade-carrying core, so /boardmeeting quality-tier reviewers (GPT) rendered as [advisory] and were undercounted. Top-tier count now includes mandatory OR quality; per-row label shows MANDATORY/QUALITY/advisory by actual tier; summary reads "N MANDATORY (Opus + GPT)" for skill-audit-loop (byte-unchanged) and "N QUALITY" for boardmeeting. test-panel-progress.js 10->12 green. Closes the deviation the unify-TUI subagent flagged in a747ece. SYSTEM-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2b836bc commit 9217968

3 files changed

Lines changed: 33 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Skill Graph versions describe the contract at each checkpoint in the git history
2222

2323
### Fixed
2424

25+
- **`renderCollected` is now tier-aware so the shared collected TUI serves BOTH panels (2026-06-06T12:50+02:00).** The renderer (`lib/audit/panel-progress.js`, single-canonical, reused by `/boardmeeting` via the unify-TUI change) only treated `tier === 'mandatory'` as the grade-carrying core, so `/boardmeeting`'s `quality`-tier co-reviewer (GPT) rendered mislabeled as `[advisory]` and was undercounted — and quality-vs-advisory is load-bearing in boardmeeting synthesis. Fixed: the top-tier count now includes `mandatory` OR `quality`; the per-row label shows `MANDATORY` / `QUALITY` / `advisory` by actual tier; the summary line reads `N MANDATORY (Opus + GPT)` for the skill-audit-loop panel (byte-unchanged) and `N QUALITY` for the boardmeeting panel. Verified: boardmeeting case → `2 QUALITY · 1 advisory` with `[QUALITY]` rows; skill-audit-loop case identical to before. `test-panel-progress.js` +2 cases (10→12, both green). SYSTEM-only. (Closes the deviation the unify-TUI subagent flagged in `a747ece`.)
2526
- **Panel-enrich advisory delivery pivoted to TEXT-CAPTURE + the source-read-only fence series — first full live panel run landed (2026-06-06T).** Four SYSTEM fixes, each its own path-limited commit, hardened the in-session Agent-tool panel loop and were proven by the first end-to-end live run (`eval-driven-development`, 3 rounds × 8 models, Opus-curated, applied + committed to `jacob-balslev/skills` as `87f4a7f` — 248→528 lines; anti-loss + mandatory-coverage clean; lint clean; `subject: ai-engineering` kept over the GPT-5.5/Gemini-Flash subject misreads). (1) **`b680f11`** — `isolated-checkout.js::buildSeatbeltProfile` gained an optional `readOnlyRoots` set (emitted `(allow file-read* …)` before the read-write roots so a nested run-dir RW root wins by SBPL last-match; absent ⇒ byte-identical to the prior all-RW profile). The advisory fence now makes the skill SOURCE (skill-graph repo + skills library) **read-only** — a write EPERMs, so an advisory model physically cannot mutate a canonical `SKILL.md`; only the audit run-dir trees are writable. (2) **`ecd7df6`** — advisory `reviseProposal` delivery converted from the relative-file-write (the entry below) to **stdout TEXT-CAPTURE**, mirroring the propose path: sandboxed advisory models research under the read-only fence and EMIT the revised `SKILL.md` as reply text, captured via `extractEnrichedDoc`/`looksLikeSkillDoc` and written by us; frontier claude/codex keep native write-mode. **This supersedes the relative-write delivery recorded immediately below.** Convergence stays hash-authoritative; a text-capture reviser re-emits non-byte-identical text each round, so the `maxRounds` budget is the designed terminator. (3) **`431a34f`** — the Seatbelt deny anchor also EPERMed an lstat of the workspace-root NODE, which the **Gemini CLI** does at startup (`isWorkspaceHomeDir`), aborting it before any work; the fence now allows `file-read-metadata` on the workspace-root *literal* (readdir + private read/write stay denied — no private sibling leaks). (4) **`7c6becf`** — the panel hardcoded `osFenceActive:false` for codex, so under the outer Seatbelt codex started its OWN nested `-s workspace-write` sandbox; macOS denies nesting `sandbox_apply`, so codex (GPT-5.5, a MANDATORY frontier) silently wrote nothing and lost its revisions. The panel now passes `advisoryOsFence.active` → codex drops its inner sandbox (`--dangerously-bypass-approvals-and-sandbox`, SKI-169 Fix B). Tests: `test-isolated-checkout.js` 11→16 (+5, incl. live asserts: source read-OK / source write-DENIED / nested run-dir write-ALLOWED / workspace-root lstat ALLOWED / readdir DENIED); `test-panel-enrich-live-deps.js` 7→9 (+2 text-capture revise). Live-verified each fix (DeepSeek propose, Gemini probe, codex write probe, GPT-5.5 re-revise). Docs reconciled: `SKILL_AUDIT_LOOP.md` + `AGENTS.md` delivery contract (two tiers) + § "Kernel-fence interactions". SYSTEM-only; the CONTENT skill change is in the `skills` repo, not here.
2627
- **Panel-enrich `reviseProposal` now uses the sandbox-correct relative-write delivery for advisory models (2026-06-05T13:30Z).** _(Superseded 2026-06-06 by stdout text-capture in `ecd7df6` — see the entry above. Retained as the dated timeline of the unreleased window.)_ In the convergence loop, `reviseProposal` (`lib/audit/panel-enrich-live-deps.js`) is dispatched for EVERY alive proposal — frontier (claude/codex) AND advisory (gemini/opencode). It previously always instructed an **absolute** overwrite (`OVERWRITE the proposal file at <abs path>`) and dispatched with `cwd: skillGraphRoot`, which silently misdelivered for gemini (which writes relative to cwd → wrote into the skill-graph root, not the run dir) and risked opencode's `external_directory` rejection. It now mirrors `dispatchWriteProposal` (the propose path): sandboxed advisory CLIs get a RELATIVE-filename overwrite (`basename(ownProposalPath)`) into the dir holding their own proposal, with `cwd = gemini ? writeDir : skillGraphRoot` (opencode write-scope via `--dir`), and the current proposal + canonical body inlined so no external read is attempted; frontier claude/codex keep the absolute-path overwrite they handle natively (codex via `writableRoots`). Verified: 24/24 panel tests pass (`test-panel-enrich.js` 17 + `test-panel-enrich-live-deps.js` 7). Live note: gemini advisory delivery is code-correct but currently unauthenticated for non-interactive use (`~/.gemini/settings.json selectedType: gemini-api-key` with no key; AI Studio subscription needs an interactive `gemini /auth` OAuth login — `GEMINI_API_KEY` is deliberately NOT set, per the subscriptions-not-API rule). Advisory failures are best-effort and never block the loop; the certifying floor is the mandatory frontier pair (Opus + GPT-via-codex), both verified present. SYSTEM-only.
2728

lib/audit/panel-progress.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,25 @@ function renderCollected(status) {
7777
const done = s.done || 0;
7878
const total = s.total != null ? s.total : agents.length;
7979
const failed = s.failed || 0;
80-
const mand = agents.filter((a) => a.tier === 'mandatory').length;
81-
const adv = agents.length - mand;
80+
// Top tier = MANDATORY (skill-audit-loop panel) OR QUALITY (boardmeeting panel) — both are
81+
// the grade-carrying core, distinct from advisory/breadth. Count them together so the
82+
// shared renderer serves both panels correctly.
83+
const core = agents.filter((a) => a.tier === 'mandatory' || a.tier === 'quality').length;
84+
const adv = agents.length - core;
85+
const topLabel = agents.some((a) => a.tier === 'mandatory') ? 'MANDATORY' : 'QUALITY';
86+
const topSuffix = topLabel === 'MANDATORY' ? ' (Opus + GPT)' : '';
8287
const lines = [];
8388
lines.push(
8489
`⟳ Skill Audit Loop · ${s.skill || '?'}${s.phase || 'starting'}${done}/${total} done`
8590
+ (failed ? ` · ${failed} failed` : '')
8691
+ (s.elapsed_s != null ? ` │ ${fmtElapsed(s.elapsed_s * 1000)}` : '')
8792
+ (s.complete ? ' │ DONE' : ''),
8893
);
89-
lines.push(`${mand} MANDATORY (Opus + GPT) · ${adv} advisory (free)`);
94+
lines.push(`${core} ${topLabel}${topSuffix} · ${adv} advisory (free)`);
9095
agents.forEach((a, i) => {
9196
const branch = i === agents.length - 1 ? '└' : '├';
9297
const glyph = STATE_GLYPH[a.state] || '·';
93-
const tier = a.tier === 'mandatory' ? 'MANDATORY' : 'advisory';
98+
const tier = a.tier === 'mandatory' ? 'MANDATORY' : a.tier === 'quality' ? 'QUALITY' : 'advisory';
9499
const ph = a.phase ? ` ${a.phase}` : '';
95100
const el = (a.elapsed_s && ACTIVE_STATES.has(a.state)) ? ` ${fmtElapsed(a.elapsed_s * 1000)}` : '';
96101
lines.push(`${branch} ${glyph} ${resolveDisplayName(a.model)} [${tier}]${ph} · ${a.state}${el}`);

scripts/__tests__/test-panel-progress.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,29 @@ ok('tolerates a missing/empty status object', () => {
148148
const lines = renderCollected(null);
149149
assert.ok(Array.isArray(lines) && lines.length === 2, 'header lines even with no agents');
150150
});
151+
ok('renders the QUALITY tier (boardmeeting panel) distinctly from advisory', () => {
152+
const lines = renderCollected({
153+
skill: 'board', phase: 'review', done: 1, total: 3, failed: 1,
154+
agents: [
155+
{ model: 'opus', tier: 'quality', phase: 'review', state: 'reviewing' },
156+
{ model: 'codex-current', tier: 'quality', phase: 'review', state: 'running' },
157+
{ model: 'minimax', tier: 'advisory', phase: 'review', state: 'done' },
158+
],
159+
});
160+
assert.ok(lines[1].includes('2 QUALITY') && lines[1].includes('1 advisory'), 'quality counted as core, not advisory');
161+
assert.ok(lines[2].includes('[QUALITY]'), 'quality-tier row labeled QUALITY');
162+
assert.ok(!lines[2].includes('[advisory]'), 'quality row not mislabeled advisory');
163+
});
164+
ok('mandatory panel (skill-audit-loop) keeps its exact header', () => {
165+
const lines = renderCollected({
166+
skill: 'methodical', phase: 'propose', done: 1, total: 2,
167+
agents: [
168+
{ model: 'opus', tier: 'mandatory', phase: 'propose', state: 'proposed' },
169+
{ model: 'minimax', tier: 'advisory', phase: null, state: 'queued' },
170+
],
171+
});
172+
assert.ok(lines[1].includes('1 MANDATORY (Opus + GPT)'), 'mandatory header unchanged');
173+
});
151174

152175
// ── 4. helper ──
153176
console.log('4. helpers');

0 commit comments

Comments
 (0)