769 lines
294 KiB
HTML
769 lines
294 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ja">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>MAESTRO 全機能カタログ & テストカバレッジ</title>
|
||
<style>
|
||
:root{
|
||
--bg:#f6f7f9; --card:#ffffff; --ink:#1f2733; --muted:#5b6675; --line:#e3e7ec;
|
||
--full:#1a7f37; --full-bg:#e6f4ea; --partial:#b3791a; --partial-bg:#fdf3e0;
|
||
--none:#c0362c; --none-bg:#fbe9e7; --other:#6b7280; --other-bg:#eef0f2;
|
||
--accent:#2563eb;
|
||
}
|
||
*{box-sizing:border-box}
|
||
body{margin:0;background:var(--bg);color:var(--ink);
|
||
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Hiragino Kaku Gothic ProN","Noto Sans JP",Meiryo,sans-serif;
|
||
line-height:1.6;font-size:14px;}
|
||
code,.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace;font-size:.86em;}
|
||
code{background:#eef1f4;border-radius:4px;padding:1px 5px;color:#243; word-break:break-word;}
|
||
.wrap{max-width:1480px;margin:0 auto;padding:24px 20px 80px;}
|
||
header.page{margin-bottom:8px;}
|
||
.stage-badge{display:inline-block;font-weight:700;font-size:12px;letter-spacing:.04em;
|
||
padding:5px 14px;border-radius:999px;background:#dbeafe;color:#1e40af;border:1px solid #bfdbfe;}
|
||
.stage-legend{display:inline-flex;gap:6px;margin-left:10px;font-size:11px;color:var(--muted);align-items:center;}
|
||
.stage-legend span{display:inline-flex;align-items:center;gap:4px;}
|
||
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;}
|
||
.dot.b{background:#3b82f6}.dot.y{background:#eab308}.dot.g{background:#22c55e}
|
||
h1{font-size:26px;margin:12px 0 4px;}
|
||
.sub{color:var(--muted);margin:0 0 6px;}
|
||
.links{margin:10px 0 22px;font-size:13px;}
|
||
.links a{color:var(--accent);text-decoration:none;margin-right:18px;}
|
||
.links a:hover{text-decoration:underline;}
|
||
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:20px 22px;margin:18px 0;
|
||
box-shadow:0 1px 2px rgba(20,30,50,.04);}
|
||
h2{font-size:18px;margin:0 0 14px;border-left:4px solid var(--accent);padding-left:10px;}
|
||
h3{font-size:15px;margin:18px 0 6px;color:#27313f;}
|
||
.stat-row{display:flex;flex-wrap:wrap;gap:14px;margin-bottom:18px;}
|
||
.stat{flex:1 1 150px;border:1px solid var(--line);border-radius:10px;padding:14px 16px;background:#fbfcfd;}
|
||
.stat .n{font-size:30px;font-weight:700;line-height:1;}
|
||
.stat .l{font-size:12px;color:var(--muted);margin-top:6px;}
|
||
.stat.full .n{color:var(--full)} .stat.partial .n{color:var(--partial)} .stat.none .n{color:var(--none)}
|
||
.chip{display:inline-block;font-weight:700;font-size:11px;padding:2px 9px;border-radius:999px;letter-spacing:.02em;white-space:nowrap;}
|
||
.cov-full{background:var(--full-bg);color:var(--full);}
|
||
.cov-partial{background:var(--partial-bg);color:var(--partial);}
|
||
.cov-none{background:var(--none-bg);color:var(--none);}
|
||
.cov-other{background:var(--other-bg);color:var(--other);}
|
||
table{border-collapse:collapse;width:100%;font-size:13px;}
|
||
.area-table th,.area-table td{border:1px solid var(--line);padding:8px 10px;text-align:left;}
|
||
.area-table th{background:#f0f3f7;font-weight:600;}
|
||
.area-table td.num{text-align:center;}
|
||
.area-table tr.total-row td{font-weight:700;background:#f7f9fc;}
|
||
.controls{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-bottom:14px;}
|
||
#search{flex:1 1 280px;min-width:220px;padding:9px 12px;border:1px solid var(--line);border-radius:8px;font-size:14px;}
|
||
.filt-btns{display:flex;gap:6px;flex-wrap:wrap;}
|
||
.fbtn{cursor:pointer;border:1px solid var(--line);background:#fff;border-radius:8px;padding:7px 14px;font-size:12px;font-weight:600;color:var(--muted);}
|
||
.fbtn.active{background:var(--accent);color:#fff;border-color:var(--accent);}
|
||
.fbtn[data-cov="FULL"].active{background:var(--full);border-color:var(--full);}
|
||
.fbtn[data-cov="PARTIAL"].active{background:var(--partial);border-color:var(--partial);}
|
||
.fbtn[data-cov="NONE"].active{background:var(--none);border-color:var(--none);}
|
||
#count{font-size:12px;color:var(--muted);margin-left:auto;}
|
||
.table-scroll{overflow-x:auto;border:1px solid var(--line);border-radius:10px;}
|
||
table.master{font-size:12.5px;}
|
||
table.master th,table.master td{padding:7px 9px;border-bottom:1px solid var(--line);vertical-align:top;text-align:left;}
|
||
table.master thead th{position:sticky;top:0;background:#eef2f7;z-index:2;font-weight:600;border-bottom:2px solid #d4dbe4;}
|
||
table.master tbody tr:hover{background:#f5f8fc;}
|
||
.c-id{white-space:nowrap;} .c-area{white-space:nowrap;color:var(--muted);}
|
||
.c-feature{font-weight:600;min-width:140px;}
|
||
.c-behavior{min-width:240px;color:#33404f;}
|
||
.c-source{min-width:150px;} .c-test{min-width:200px;color:#445;}
|
||
.c-cov{white-space:nowrap;text-align:center;} .c-gap{min-width:200px;color:#6a7686;}
|
||
.gap-area{margin-bottom:6px;}
|
||
ol.gap-list{margin:4px 0 14px;padding-left:22px;}
|
||
ol.gap-list li{margin-bottom:8px;}
|
||
.muted-note{font-size:12px;color:var(--muted);margin-top:6px;}
|
||
footer{margin-top:40px;color:var(--muted);font-size:12px;text-align:center;}
|
||
@media (max-width:680px){ h1{font-size:21px;} .wrap{padding:16px 12px 60px;} }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
|
||
<header class="page">
|
||
<span class="stage-badge">設計</span>
|
||
<span class="stage-legend">
|
||
<span><i class="dot b"></i>設計</span>
|
||
<span><i class="dot y"></i>実装</span>
|
||
<span><i class="dot g"></i>完成</span>
|
||
</span>
|
||
<h1>MAESTRO 全機能カタログ & テストカバレッジ</h1>
|
||
<p class="sub">8 領域・502 機能のインベントリと既存テストのカバレッジ一覧(TDD 機能テスト計画)</p>
|
||
<div class="links">
|
||
<a href="../superpowers/specs/2026-06-25-tdd-functional-tests-design.md">🔗 設計仕様 (design spec)</a>
|
||
<a href="#summary">サマリ</a>
|
||
<a href="#master">機能一覧</a>
|
||
<a href="#gaps">重要ギャップ</a>
|
||
</div>
|
||
</header>
|
||
|
||
<section class="card" id="summary">
|
||
<h2>サマリ</h2>
|
||
<div class="stat-row">
|
||
<div class="stat"><div class="n">502</div><div class="l">機能総数 (Total features)</div></div>
|
||
<div class="stat full"><div class="n">352</div><div class="l">FULL カバー済み</div></div>
|
||
<div class="stat partial"><div class="n">124</div><div class="l">PARTIAL 一部のみ</div></div>
|
||
<div class="stat none"><div class="n">26</div><div class="l">NONE 未カバー</div></div>
|
||
</div>
|
||
<table class="area-table">
|
||
<thead><tr><th>領域 (Area)</th><th>機能数</th><th>FULL</th><th>PARTIAL</th><th>NONE</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Engine core</td><td class="num">65</td><td class="num"><span class="chip cov-full">52</span></td><td class="num"><span class="chip cov-partial">9</span></td><td class="num"><span class="chip cov-none">4</span></td></tr>
|
||
<tr><td>Tools</td><td class="num">56</td><td class="num"><span class="chip cov-full">32</span></td><td class="num"><span class="chip cov-partial">22</span></td><td class="num"><span class="chip cov-none">2</span></td></tr>
|
||
<tr><td>Pieces</td><td class="num">32</td><td class="num"><span class="chip cov-full">18</span></td><td class="num"><span class="chip cov-partial">13</span></td><td class="num"><span class="chip cov-none">1</span></td></tr>
|
||
<tr><td>Reflection</td><td class="num">61</td><td class="num"><span class="chip cov-full">51</span></td><td class="num"><span class="chip cov-partial">10</span></td><td class="num"><span class="chip cov-none">0</span></td></tr>
|
||
<tr><td>Bridge core</td><td class="num">50</td><td class="num"><span class="chip cov-full">39</span></td><td class="num"><span class="chip cov-partial">9</span></td><td class="num"><span class="chip cov-none">2</span></td></tr>
|
||
<tr><td>Bridge auth & spaces</td><td class="num">66</td><td class="num"><span class="chip cov-full">48</span></td><td class="num"><span class="chip cov-partial">17</span></td><td class="num"><span class="chip cov-none">1</span></td></tr>
|
||
<tr><td>Services & DB</td><td class="num">87</td><td class="num"><span class="chip cov-full">69</span></td><td class="num"><span class="chip cov-partial">13</span></td><td class="num"><span class="chip cov-none">5</span></td></tr>
|
||
<tr><td>UI</td><td class="num">85</td><td class="num"><span class="chip cov-full">43</span></td><td class="num"><span class="chip cov-partial">31</span></td><td class="num"><span class="chip cov-none">11</span></td></tr>
|
||
<tr class="total-row"><td>合計</td><td class="num">502</td><td class="num"><span class="chip cov-full">352</span></td><td class="num"><span class="chip cov-partial">124</span></td><td class="num"><span class="chip cov-none">26</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="muted-note">本ページの数値は 8 つのインベントリ表をパースして算出した実数(502 件)。設計仕様 (design spec) の概算目標値は 483 件 (FULL 318 / PARTIAL 137 / NONE 29)。差分はインベントリ確定時の項目追加・分類更新による。</p>
|
||
</section>
|
||
|
||
<section class="card" id="master">
|
||
<h2>機能一覧 (Master feature table)</h2>
|
||
<div class="controls">
|
||
<input id="search" type="text" placeholder="検索: 機能名・ID・ソース・テスト… (live filter)">
|
||
<div class="filt-btns">
|
||
<button class="fbtn active" data-cov="ALL">すべて</button>
|
||
<button class="fbtn" data-cov="FULL">FULL</button>
|
||
<button class="fbtn" data-cov="PARTIAL">PARTIAL</button>
|
||
<button class="fbtn" data-cov="NONE">NONE</button>
|
||
</div>
|
||
<span id="count"></span>
|
||
</div>
|
||
<div class="table-scroll">
|
||
<table class="master" id="master-table">
|
||
<thead><tr>
|
||
<th>ID</th><th>領域</th><th>機能</th><th>挙動</th><th>ソース</th><th>既存テスト</th><th>カバレッジ</th><th>ギャップ</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-001</code></td><td class="c-area">Engine core</td><td class="c-feature">Parallel read-tool execution</td><td class="c-behavior">Consecutive PARALLEL_SAFE tools run via <code>Promise.all</code>; side-effecting tools act as a sequential barrier</td><td class="c-source"><code>agent-loop.ts (`canExecuteInParallel`, `PARALLEL_SAFE_TOOL_NAMES`, executeMovement)</code></td><td class="c-test">agent-loop.test.ts "runs consecutive safe tool calls in parallel"; "keeps side-effecting tools sequential as a barrier"; "executes regular tools before transition even if transition appears mid-batch"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-002</code></td><td class="c-area">Engine core</td><td class="c-feature"><code>transition</code> tool (non-terminal)</td><td class="c-behavior"><code>buildTransitionTool</code> enum = only <code>rules[].next</code>; <code>validateTransition</code> rejects targets outside rules; <code>processTransitionCalls</code> records lessons</td><td class="c-source"><code>agent-loop.ts (`buildTransitionTool`, `validateTransition`, `processTransitionCalls`)</code></td><td class="c-test">agent-loop.test.ts "non-terminal transition (movement-to-movement) still works"; "transition({next_step:…})"; "lets transition/complete win even if it shares a batch with a repeated call" (tool-loop)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap"><code>buildTransitionTool</code> / <code>validateTransition</code> exercised only indirectly via executeMovement; no unit test that an out-of-rules <code>next</code> is rejected in isolation</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-003</code></td><td class="c-area">Engine core</td><td class="c-feature"><code>complete</code> tool — status routing</td><td class="c-behavior">success→COMPLETE, aborted→ABORT, needs_user_input→ASK via <code>COMPLETE_STATUS_TO_NEXT</code> / <code>buildMovementResultFromComplete</code></td><td class="c-source"><code>agent-loop.ts (`buildCompleteTool`, `parseCompleteArgs`, `buildMovementResultFromComplete`)</code></td><td class="c-test">agent-loop.test.ts §7.1 "success status…becomes movement output"; "aborted status routes via ABORT next"; "needs_user_input routes via ASK next"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-004</code></td><td class="c-area">Engine core</td><td class="c-feature"><code>complete</code> arg validation</td><td class="c-behavior">success requires non-empty result; aborted requires abort_reason; needs_user_input requires missing_info; invalid → forced retry (no accumulatedText fallback)</td><td class="c-source"><code>agent-loop.ts (`parseCompleteArgs`, `validateCompleteArgs`)</code></td><td class="c-test">agent-loop.test.ts §7.1 "rejects success with empty result"; "rejects aborted without abort_reason"; (needs_user_input/missing_info path covered via routing test)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No explicit test that <code>needs_user_input</code> with empty <code>missing_info</code> is rejected (validateCompleteArgs branch); covered for success/aborted only</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-005</code></td><td class="c-area">Engine core</td><td class="c-feature">Multiple <code>complete</code> precedence</td><td class="c-behavior">0 completes → continue; >1 conflicting args → retry; >1 identical → first used</td><td class="c-source"><code>agent-loop.ts (complete dedup logic ~L1503-1535)</code></td><td class="c-test">agent-loop.test.ts §7.2 "invalid native complete forces retry"; "two native completes with conflicting args → retry"; "two native completes with identical args → first one used"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-006</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation-history integrity on retry</td><td class="c-behavior">Every <code>tool_use</code> id gets a matching <code>tool_result</code> even when complete is rejected</td><td class="c-source"><code>agent-loop.ts (retry path)</code></td><td class="c-test">agent-loop.test.ts §7.7 "all tool_use ids get a tool_result on retry"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-007</code></td><td class="c-area">Engine core</td><td class="c-feature">Tool-call loop detection (intra-movement)</td><td class="c-behavior">Identical tool-call batch repeated ≥ <code>maxToolLoopRepeats</code> (default 5) → ABORT; counter resets on arg change</td><td class="c-source"><code>agent-loop.ts (`DEFAULT_MAX_TOOL_LOOP_REPEATS`, `buildToolLoopAbortMessage`, consecutiveToolRepeats)</code></td><td class="c-test">agent-loop.tool-loop.test.ts (5 tests: limit abort, custom limit, under-limit, arg-change reset, transition wins)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-008</code></td><td class="c-area">Engine core</td><td class="c-feature">Loop-watchdog one-shot warning</td><td class="c-behavior">At <code>maxToolLoopRepeats-2</code> (min 2) inject a single <code>[loop watchdog]</code> reminder before aborting</td><td class="c-source"><code>agent-loop.ts (`warnAt`, `toolLoopWarned`, `injectLoopWarning`)</code></td><td class="c-test">(implicit in tool-loop tests' under-limit path)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No assertion that the watchdog warning message is actually injected exactly once before abort</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-009</code></td><td class="c-area">Engine core</td><td class="c-feature">Max-iterations abort</td><td class="c-behavior">Movement exceeding <code>safety.maxIterations</code> aborts with <code>buildMaxIterationsAbortMessage</code></td><td class="c-source"><code>agent-loop.ts (`buildMaxIterationsAbortMessage`)</code></td><td class="c-test">agent-loop.test.ts "aborts when maxIterations is exceeded"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-010</code></td><td class="c-area">Engine core</td><td class="c-feature">Text-only-response handling</td><td class="c-behavior">NON-terminal movement aborts after N text-only turns; STRICTLY-terminal (defaultNext COMPLETE, no rules) salvages prose as completion; counter resets on tool use</td><td class="c-source"><code>agent-loop.ts (`TEXT_ONLY_REMIND_EMPTY`, textOnlyOutcome)</code></td><td class="c-test">agent-loop.test.ts "aborts after text-only responses in a NON-terminal movement"; "salvages text-only prose…STRICTLY-terminal"; "does NOT salvage…still has rules"; "resets text-only counter when tool calls happen"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-011</code></td><td class="c-area">Engine core</td><td class="c-feature">Context-overflow forced transition (runtime usage)</td><td class="c-behavior">ContextManager threshold 95% → <code>force_transition</code> builds result via <code>movement.defaultNext</code>; falls to ABORT when defaultNext is terminal/absent</td><td class="c-source"><code>agent-loop.ts (`buildContextOverflowResult`, `buildForceTransitionResult`)</code></td><td class="c-test">agent-loop.test.ts "triggers force_transition when context manager signals exhaustion"; "fires onContextAction callback when context threshold crossed"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-012</code></td><td class="c-area">Engine core</td><td class="c-feature">Oversized initial prompt guard</td><td class="c-behavior">Prompt over budget pre-send → dedup→compact→summarize; aborts/force-transitions when still oversized</td><td class="c-source"><code>agent-loop.ts + context/prompt-guard.ts (`guardPromptBeforeSend`)</code></td><td class="c-test">agent-loop.test.ts "aborts when initial prompt is oversized and defaultNext is terminal"; "falls back to ABORT when oversized prompt has no defaultNext"; prompt-guard.test.ts (full stage matrix)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-013</code></td><td class="c-area">Engine core</td><td class="c-feature">Cancel signal handling</td><td class="c-behavior">Already-aborted cancelSignal returns ABORT immediately; mid-movement cancel returns ABORT "cancelled"</td><td class="c-source"><code>agent-loop.ts (cancelSignal checks)</code></td><td class="c-test">agent-loop.test.ts "returns ABORT immediately when cancelSignal is already aborted"</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Mid-iteration cancel (after work started) not asserted at agent-loop level; only the pre-loop case</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-014</code></td><td class="c-area">Engine core</td><td class="c-feature">ContextManager threshold ladder</td><td class="c-behavior">70%→warn, 85%→prompt, 95%→force_transition; each fires once; isExhausted ≥0.99</td><td class="c-source"><code>context-manager.ts (`ContextManager`)</code></td><td class="c-test">context-manager.test.ts (13 tests: each threshold, fire-once, defaults, limitTokens, isExhausted, hasUsageData, char fallback)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-015</code></td><td class="c-area">Engine core</td><td class="c-feature">Ollama context-limit autodetect</td><td class="c-behavior"><code>fetchOllamaContextLimit</code> prefers num_ctx > model_info.context_length; llama.cpp /props fallback; default on failure</td><td class="c-source"><code>context-manager.ts</code></td><td class="c-test">context-manager.test.ts (5 tests incl. num_ctx precedence, /props fallback, n_ctx absent)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-016</code></td><td class="c-area">Engine core</td><td class="c-feature">Tool catalog injection into system prompt</td><td class="c-behavior"><code>buildSystemPrompt</code> / <code>buildGlobalPreamble</code> auto-inject available-tools list + 1-line summaries; script section gated on Bash presence</td><td class="c-source"><code>agent-loop.ts (`buildGlobalPreamble`, `buildSystemPrompt`)</code></td><td class="c-test">agent-loop.test.ts "injects the script section when Bash is among the presented tools"; "omits…when Bash is not presented"; preamble byte-stability tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-017</code></td><td class="c-area">Engine core</td><td class="c-feature">Preamble/guidance split (job-stable vs movement)</td><td class="c-behavior"><code>buildGlobalPreamble</code> byte-identical across movements; <code>buildMovementGuidance</code> carries persona/instruction/transitions</td><td class="c-source"><code>agent-loop.ts</code></td><td class="c-test">agent-loop.test.ts "preamble is byte-identical across two different movements"; "guidance carries movement-specific instruction, persona, transitions"; "buildSystemPrompt wrapper still contains both halves"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-018</code></td><td class="c-area">Engine core</td><td class="c-feature">Progressive pressure (intra-movement revisit)</td><td class="c-behavior">visitCount==2 → caution; >=3 → warning injected into guidance</td><td class="c-source"><code>agent-loop.ts (`buildMovementGuidance` L672)</code></td><td class="c-test">agent-loop.test.ts "guidance injects progressive-pressure warning on revisit"</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Only the revisit-warning presence is asserted; the visitCount==2 (caution) vs >=3 (warning) distinction is not separately tested</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-019</code></td><td class="c-area">Engine core</td><td class="c-feature">Checklist watchdog</td><td class="c-behavior">One-shot reminder after 5 iterations with no checklist tool; suppressed if CreateChecklist used early</td><td class="c-source"><code>agent-loop.ts</code></td><td class="c-test">agent-loop.test.ts "injects a one-shot reminder after 5 iterations"; "does NOT fire when CreateChecklist is called early"; traceability "watchdog_fire"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-020</code></td><td class="c-area">Engine core</td><td class="c-feature">Cross-movement Read cache</td><td class="c-behavior">Cacheable read served to later movement; errors not cached; non-allowlisted tools (Bash) skipped</td><td class="c-source"><code>agent-loop.ts + context/tool-result-cache.ts</code></td><td class="c-test">agent-loop.test.ts "returns a cached Read result…"; "does not cache error results"; "skips caching tools outside the cacheable allowlist"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-021</code></td><td class="c-area">Engine core</td><td class="c-feature">Cache invalidation</td><td class="c-behavior">Edit/Write invalidate affected path; Grep entries all-evicted on edit; Bash conservatively invalidates all file entries; no invalidate on tool error</td><td class="c-source"><code>agent-loop.ts + context/invalidation.ts + tool-result-cache.ts</code></td><td class="c-test">agent-loop.test.ts Phase 2/4 (7 tests); invalidation.test.ts (5); tool-result-cache.test.ts (28)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-022</code></td><td class="c-area">Engine core</td><td class="c-feature">Cache key construction</td><td class="c-behavior">Deterministic v1-prefixed keys for Read/Grep/Glob/WebFetch/Office; URL scheme/host normalization; workspace isolation</td><td class="c-source"><code>context/cache-key.ts</code></td><td class="c-test">cache-key.test.ts (25); tool-result-cache.test.ts key tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-023</code></td><td class="c-area">Engine core</td><td class="c-feature">File-read dedup</td><td class="c-behavior">Older Reads of same file replaced with placeholder, most-recent kept; ignores non-Read tools; idempotent</td><td class="c-source"><code>context/file-read-dedup.ts</code></td><td class="c-test">file-read-dedup.test.ts (10)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-024</code></td><td class="c-area">Engine core</td><td class="c-feature">History summarization</td><td class="c-behavior"><code>summarizeHistory</code> replaces middle turns with summary, preserves tail; LLM-failure → summarized=false; update-mode with prior summary</td><td class="c-source"><code>context/history-compactor.ts</code></td><td class="c-test">history-compactor.test.ts (21: splitIntoTurns, buildSummaryPrompt, selectTailTurnStartIndex, summarizeHistory, summarizeForceTransition)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-025</code></td><td class="c-area">Engine core</td><td class="c-feature">Token estimation</td><td class="c-behavior">char→token by script class (ASCII/CJK/other), per-message overhead, image budget, tool serialization</td><td class="c-source"><code>context/token-estimate.ts</code></td><td class="c-test">token-estimate.test.ts (23)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-026</code></td><td class="c-area">Engine core</td><td class="c-feature">Prompt-guard staged compaction</td><td class="c-behavior">dedup→compact→summarize ladder; reserve-cap headroom; stage-3 skipped when disabled/no isolated LLM; ok:false when all fail</td><td class="c-source"><code>context/prompt-guard.ts</code></td><td class="c-test">prompt-guard.test.ts (27)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-027</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation class (seed/enter/persist)</td><td class="c-behavior">seed order (preamble+guidance+task); enterMovement appends guidance w/o reset; flush/loadFrom round-trip; rewrite truncates</td><td class="c-source"><code>context/conversation.ts</code></td><td class="c-test">conversation.test.ts (in-memory + persistence, 8)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-028</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation.replayableTurns</td><td class="c-behavior">Strips system + control-flow tool_calls (complete/transition) + dangling/orphan tool messages; keeps valid pairs</td><td class="c-source"><code>context/conversation.ts</code></td><td class="c-test">conversation.test.ts (7 replayableTurns cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-029</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation.seedContinuation / hasReplayableTranscript</td><td class="c-behavior">Builds [preamble,guidance,...priorTurns,user] & rewrites transcript; hasReplayableTranscript guards undefined/missing/system-only</td><td class="c-source"><code>context/conversation.ts</code></td><td class="c-test">conversation.test.ts (seedContinuation + 4 hasReplayableTranscript)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-030</code></td><td class="c-area">Engine core</td><td class="c-feature">Continuation seed in executeMovement</td><td class="c-behavior">priorTurns replayed & handoff block suppressed; plain seed when absent</td><td class="c-source"><code>agent-loop.ts</code></td><td class="c-test">agent-loop.test.ts "replays priorTurns and suppresses handoff block"; "without priorTurns, uses plain seed"; "with empty priorTurns array, behaves like plain seed"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-031</code></td><td class="c-area">Engine core</td><td class="c-feature">Atomic JSON persistence</td><td class="c-behavior">writeAtomicJson (tmp+rename, mkdir parents); readSafeJson returns missing/corrupt/wrong-version; quarantineCorruptFile</td><td class="c-source"><code>context/atomic-json.ts</code></td><td class="c-test">atomic-json.test.ts (11)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-032</code></td><td class="c-area">Engine core</td><td class="c-feature">Workspace path normalization</td><td class="c-behavior">Rejects absolute/backslash/drive/UNC/NUL/empty; canonicalizes <code>..</code>/<code>.</code>; prefixWorkspacePath join with traversal guard</td><td class="c-source"><code>context/path-normalize.ts</code></td><td class="c-test">path-normalize.test.ts (15)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-033</code></td><td class="c-area">Engine core</td><td class="c-feature">Piece loop guards (cross-movement)</td><td class="c-behavior"><code>enforceLoopGuards</code>: consecutive revisits > max_consecutive_revisits (default 4) → abort; counters reset on movement change</td><td class="c-source"><code>piece-runner.ts (`enforceLoopGuards`)</code></td><td class="c-test">piece-runner.test.ts "aborts when loop detection fires due to consecutive revisits"</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Per-movement <code>max_consecutive_revisits</code> override and the counter-reset-on-movement-change branch not separately asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-034</code></td><td class="c-area">Engine core</td><td class="c-feature">ASK limit fallback</td><td class="c-behavior">ASK result resolves <code>default_next</code> or first non-self/non-ASK/non-ABORT rule; aborts when no fallback; resolves COMPLETE default_next via ASK-limit fallback</td><td class="c-source"><code>piece-runner.ts (result.next==='ASK' branch)</code></td><td class="c-test">piece-runner.test.ts "falls back to default_next when ASK limit is reached"; "aborts when ASK limit reached and no fallback"; "resolves a COMPLETE default_next reached via the ASK-limit fallback"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-035</code></td><td class="c-area">Engine core</td><td class="c-feature">WAIT_SUBTASKS pause</td><td class="c-behavior">result.next==='WAIT_SUBTASKS' pauses piece run (waiting_subtasks)</td><td class="c-source"><code>piece-runner.ts (L1144)</code></td><td class="c-test">piece-runner.test.ts "does NOT write snapshot on waiting_subtasks (transient pause)"</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">The pause/resume return contract is only asserted via the snapshot-suppression side effect; no direct test of the waiting_subtasks PieceRunResult shape</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-036</code></td><td class="c-area">Engine core</td><td class="c-feature">SpawnSubTask depth-limit skip</td><td class="c-behavior">Movement requiring SpawnSubTask skipped at depth limit (incl. via shared_tools); resolves COMPLETE default_next on skip</td><td class="c-source"><code>piece-runner.ts (`pieceUsesSpawn`, spawn skip)</code></td><td class="c-test">piece-runner.test.ts "resolves a COMPLETE default_next when a SpawnSubTask movement is skipped at the depth limit"; "skips a SpawnSubTask movement at the depth limit even when…shared_tools"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="NONE" data-area="Engine core"><td class="c-id"><code>ENG-037</code></td><td class="c-area">Engine core</td><td class="c-feature">WAITING_HUMAN_BROWSER / _TOOL_REQUEST pauses</td><td class="c-behavior">Non-ASK human-wait sentinels pause the run</td><td class="c-source"><code>piece-runner.ts (L1159, L1176)</code></td><td class="c-test">(none found)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">No test exercises the WAITING_HUMAN_BROWSER or WAITING_HUMAN_TOOL_REQUEST branches</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-038</code></td><td class="c-area">Engine core</td><td class="c-feature">Review-feedback carry-forward</td><td class="c-behavior">verify movement output carried into later execute/process/plan/analyze; truncated per/combined caps; git status+diff appended after verify</td><td class="c-source"><code>piece-runner.ts (`buildInstructionWithFeedback`, `shouldCarryReviewFeedback`, truncate/trim)</code></td><td class="c-test">piece-runner.test.ts "carries cumulative verify feedback into later execute/analyze movements"; "appends safe git status and diff context after verify loops"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-039</code></td><td class="c-area">Engine core</td><td class="c-feature">Lessons accumulation & injection</td><td class="c-behavior">transition/complete lessons accumulated, capped (MAX_LESSONS_LENGTH 2000), injected into next movement; written to lessons.jsonl</td><td class="c-source"><code>piece-runner.ts (`buildLessonsContext`, `writeLessonLog`, lessonsAccumulator)</code></td><td class="c-test">(no direct test of buildLessonsContext/writeLessonLog; snapshot tests reference lessons)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Lessons capture appears in cancel-snapshot tests but the injection-into-next-movement and 2000-char trim logic is not directly asserted; writeLessonLog untested</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-040</code></td><td class="c-area">Engine core</td><td class="c-feature">loadPiece terminal-rule validation</td><td class="c-behavior">rejects rules[].next ∈ {COMPLETE,ABORT,ASK}; accepts default_next COMPLETE & WAIT_SUBTASKS sentinel; all bundled pieces valid</td><td class="c-source"><code>piece-runner.ts (`validatePieceDef`, `loadPiece`)</code></td><td class="c-test">piece-runner.test.ts (Phase 6b: 6 tests incl. all bundled pieces load)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-041</code></td><td class="c-area">Engine core</td><td class="c-feature">loadPiece multi-dir resolution</td><td class="c-behavior">custom dir(s) win over builtin; first dir wins on dup name; string|string[] forms</td><td class="c-source"><code>piece-runner.ts (`loadPiece`)</code></td><td class="c-test">piece-runner.test.ts "resolves from a list of custom dirs"; "first dir wins"; "string form still works"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-042</code></td><td class="c-area">Engine core</td><td class="c-feature">normalizeRequiredMcp</td><td class="c-behavior">retains valid slugs, drops invalid, undefined when absent, coerces non-array to []</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (4 required_mcp tests)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-043</code></td><td class="c-area">Engine core</td><td class="c-feature">normalizeSharedTools / mergeToolNames</td><td class="c-behavior">shared_tools sanitized; mergeToolNames unions shared+movement first-seen order, drops dups/non-strings</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (shared_tools 4 + mergeToolNames 3)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-044</code></td><td class="c-area">Engine core</td><td class="c-feature">validateAllowedSshConnections</td><td class="c-behavior">validates id format / wildcard / empty-deny; tolerates missing allowlist (A4 workspace-policy gating); reports offenders</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (allowed_ssh_connections 15 + loader-tolerance 1)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-045</code></td><td class="c-area">Engine core</td><td class="c-feature">Workspace tool-policy resolution & injection</td><td class="c-behavior">resolveWorkspaceTools (safe default, Bash/browser opt-in); runPiece injects workspaceTools overriding movement.allowed_tools; grantedTools unioned</td><td class="c-source"><code>piece-runner.ts (runPiece) + workspace-tool-policy.ts</code></td><td class="c-test">piece-runner.test.ts (resolveWorkspaceTools 5 + runPiece injection 4); workspace-tool-policy.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-046</code></td><td class="c-area">Engine core</td><td class="c-feature">runPiece max_movements default</td><td class="c-behavior">iterates when max_movements missing / 0 / negative</td><td class="c-source"><code>piece-runner.ts (runPiece)</code></td><td class="c-test">piece-runner.test.ts "still iterates when piece.max_movements is missing"; "…is 0 or negative"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-047</code></td><td class="c-area">Engine core</td><td class="c-feature">buildFollowupNotice</td><td class="c-behavior">detects follow-up when output/ or subtasks/ has non-hidden files; ignores dotfiles; empty for fresh ws</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (5 buildFollowupNotice tests)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-048</code></td><td class="c-area">Engine core</td><td class="c-feature">Cancel/terminal memory snapshots</td><td class="c-behavior">snapshot+meta-event on cancelled (pre/mid movement) and aborted; NOT on success or waiting_subtasks; v2 captures finalOutput/history/lessons w/ truncation</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (7 snapshot tests)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-049</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation continuity end-to-end</td><td class="c-behavior">movement 2 sees movement 1 history; transcript.jsonl written; continuation job replays prior turns & suppresses LIMIT-1 handoff</td><td class="c-source"><code>piece-runner.ts + conversation.ts</code></td><td class="c-test">piece-runner.conversation.test.ts (2)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-050</code></td><td class="c-area">Engine core</td><td class="c-feature">Piece classification prompt</td><td class="c-behavior"><code>buildClassificationPrompt</code> includes task+descriptions+files; biases toward chat; routes workspace-app</td><td class="c-source"><code>piece-classifier.ts</code></td><td class="c-test">piece-classifier.test.ts (buildClassificationPrompt 4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-051</code></td><td class="c-area">Engine core</td><td class="c-feature">Classification response parsing</td><td class="c-behavior"><code>parseClassificationResponse</code> extracts valid name, handles noise, prefers longest match, null on invalid/empty</td><td class="c-source"><code>piece-classifier.ts</code></td><td class="c-test">piece-classifier.test.ts (parseClassificationResponse 5)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="NONE" data-area="Engine core"><td class="c-id"><code>ENG-052</code></td><td class="c-area">Engine core</td><td class="c-feature"><code>classifyPiece</code> (async LLM orchestration)</td><td class="c-behavior">Builds prompt, calls LLM, parses + falls back to default piece</td><td class="c-source"><code>piece-classifier.ts (`classifyPiece`)</code></td><td class="c-test">(none)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">The end-to-end classify path (LLM call wiring, fallback when parse returns null, error handling) is untested; only its two pure helpers are</td></tr>
|
||
<tr data-cov="NONE" data-area="Engine core"><td class="c-id"><code>ENG-053</code></td><td class="c-area">Engine core</td><td class="c-feature">loadAllPieceTriggers</td><td class="c-behavior">Loads triggers/keywords from all pieces across dirs; custom wins on dup name; skips invalid pieces</td><td class="c-source"><code>piece-runner.ts (`loadAllPieceTriggers`)</code></td><td class="c-test">(none)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">No test; dir-precedence and skip-on-load-error branches uncovered</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-054</code></td><td class="c-area">Engine core</td><td class="c-feature">buildChecklistContext</td><td class="c-behavior">Builds checklist context block from logs root</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">(referenced in piece-runner.test.ts import only; no describe block)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Symbol imported in test file but no behavior assertion targets it specifically</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-055</code></td><td class="c-area">Engine core</td><td class="c-feature">buildLocalConversationContext</td><td class="c-behavior">Renders current instruction + recent-conversation + workspace files; interjection precedence; truncation; omitRecentConversation flag</td><td class="c-source"><code>local-context.ts</code></td><td class="c-test">local-context.test.ts (7)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-056</code></td><td class="c-area">Engine core</td><td class="c-feature">PieceCatalog caching/layering</td><td class="c-behavior">Built-ins + user pieces (custom wins); TTL cache; invalidate(userId); per-folder cache isolation</td><td class="c-source"><code>piece-catalog.ts</code></td><td class="c-test">piece-catalog.test.ts (7)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-057</code></td><td class="c-area">Engine core</td><td class="c-feature">stripThinkingTokens</td><td class="c-behavior">Strips <think>/<|thinking|>/gemma channel blocks; leaves unclosed intact; preserves unicode</td><td class="c-source"><code>strip-thinking.ts</code></td><td class="c-test">strip-thinking.test.ts (11) + agent-loop.test.ts (6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-058</code></td><td class="c-area">Engine core</td><td class="c-feature">prompt-coach scoring</td><td class="c-behavior">buildCoachMessages (source inclusion + truncation); normalizeCoachResult (clamp/defaults); runPromptCoach (normalize + error propagation)</td><td class="c-source"><code>prompt-coach.ts</code></td><td class="c-test">prompt-coach.test.ts (9)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-059</code></td><td class="c-area">Engine core</td><td class="c-feature">Console screen injection</td><td class="c-behavior">Injects SSH console screen each iteration when SshConsole* allowed + session exists; guards on missing taskId / no session / non-console piece; tail truncation</td><td class="c-source"><code>agent-loop.ts (buildSystemPrompt)</code></td><td class="c-test">agent-loop.test.ts (5 console) + agent-loop-console.test.ts (4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-060</code></td><td class="c-area">Engine core</td><td class="c-feature">Handoff block construction</td><td class="c-behavior">Static Continue block always; dynamic block only with handoffContext; null prevResult handled; long prevResult truncated head+tail</td><td class="c-source"><code>agent-loop.ts (buildSystemPrompt)</code></td><td class="c-test">agent-loop.test.ts (5 handoff tests)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-061</code></td><td class="c-area">Engine core</td><td class="c-feature">Per-user AGENTS.md / memory / working-dir injection</td><td class="c-behavior">Injects AGENTS.md + persistence protocol + Working Directory + approach/error-recovery sections, gated on userId/workspacePath</td><td class="c-source"><code>agent-loop.ts (buildSystemPrompt)</code></td><td class="c-test">agent-loop.user-agents.test.ts (9)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-062</code></td><td class="c-area">Engine core</td><td class="c-feature">Existing-workspace-files injection</td><td class="c-behavior">Injects existing input-files section when present, omits when empty</td><td class="c-source"><code>agent-loop.ts (buildSystemPrompt)</code></td><td class="c-test">agent-loop.test.ts (2)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-063</code></td><td class="c-area">Engine core</td><td class="c-feature">Traceability event emission</td><td class="c-behavior">movement_start/tool_call/result/movement_complete; cache_set/hit/invalidate; watchdog_fire; run_start/complete; followup_detected; shared runId</td><td class="c-source"><code>agent-loop.ts + piece-runner.ts</code></td><td class="c-test">agent-loop.test.ts (T-1, 5) + piece-runner.test.ts (T-2, 2)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-064</code></td><td class="c-area">Engine core</td><td class="c-feature">ownerId/userId defaulting</td><td class="c-behavior">Defaults to 'local'/synthetic when unowned; preserves real owner in auth mode</td><td class="c-source"><code>piece-runner.ts (runPiece)</code></td><td class="c-test">piece-runner.test.ts "defaults ownerId and userId to…"; "preserves a real ownerId/userId"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="NONE" data-area="Engine core"><td class="c-id"><code>ENG-065</code></td><td class="c-area">Engine core</td><td class="c-feature">Mission char-budget trimming</td><td class="c-behavior"><code>buildGlobalPreamble</code> trims mission text when total exceeds MISSION_TOTAL_CHAR_BUDGET</td><td class="c-source"><code>agent-loop.ts (L428 overflow trim)</code></td><td class="c-test">(none)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">The mission/preamble char-budget overflow trim is not asserted by any test</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-001</code></td><td class="c-area">Tools</td><td class="c-feature">Read</td><td class="c-behavior">Read file slice (offset/limit); delegates binary detection; rejects directories (EISDIR), enforces readonly/</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts (88 it: binary detector, readonly/ enforce, symlink escape)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">EISDIR/dir-reject path (MEMORY: prior crash) not explicitly named in describes — verify a dir-reject test exists</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-002</code></td><td class="c-area">Tools</td><td class="c-feature">Write</td><td class="c-behavior">Write to workspace; <code>assertWritable</code> blocks readonly/ + outside-workspace</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts (readonly/ enforcement, resolveAndGuard symlink escape)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-003</code></td><td class="c-area">Tools</td><td class="c-feature">Edit</td><td class="c-behavior">Exact-string replace; same write-guard path as Write</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Edit-specific match-fail / replace_all behavior not clearly enumerated in describes</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-004</code></td><td class="c-area">Tools</td><td class="c-feature">Bash</td><td class="c-behavior">Shell exec; AbortSignal propagation, path-scope guard, install-pattern blocking, unrestricted mode, env scrub, history log</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts (AbortSignal, checkBashPathScope, install rejection, bashUnrestricted, env scrub, history)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Strong coverage incl. cancel-traceability</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-005</code></td><td class="c-area">Tools</td><td class="c-feature">Glob</td><td class="c-behavior">Pattern file match</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts (core tools)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Few dedicated Glob assertions; edge cases (no-match, ignore) unclear</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-006</code></td><td class="c-area">Tools</td><td class="c-feature">Grep</td><td class="c-behavior">Content search</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same — limited Grep-specific cases</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-007</code></td><td class="c-area">Tools</td><td class="c-feature">WebSearch</td><td class="c-behavior">SearXNG search w/ fallback + history; query sanitize</td><td class="c-source"><code></code></td><td class="c-test">web.test.ts (sanitizeQuery, searchViaSearxng, fallback history, parseSearchResultsFromText)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-008</code></td><td class="c-area">Tools</td><td class="c-feature">WebFetch</td><td class="c-behavior">Fetch URL → readability text; SSRF guard; persistent context mgmt</td><td class="c-source"><code></code></td><td class="c-test">web.test.ts (web tools, persistent context), shared/ssrf.test.ts, shared/readability.test.ts, web.binary.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">SSRF tested at shared layer; binary handling covered</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-009</code></td><td class="c-area">Tools</td><td class="c-feature">DownloadFile</td><td class="c-behavior">Download to input/ or source/; source-library index.jsonl; raw log-only</td><td class="c-source"><code></code></td><td class="c-test">web.test.ts (source-library routing 2 it), raw-save</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Size-limit / large-file / redirect handling on download not asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-010</code></td><td class="c-area">Tools</td><td class="c-feature">ReadImage</td><td class="c-behavior">Read image → vision; usage tracking</td><td class="c-source"><code></code></td><td class="c-test">image.test.ts (13 it), image.vision-usage.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-011</code></td><td class="c-area">Tools</td><td class="c-feature">AnnotateImage</td><td class="c-behavior">Draw annotations on image</td><td class="c-source"><code></code></td><td class="c-test">image.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Annotation rendering correctness lightly covered</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-012</code></td><td class="c-area">Tools</td><td class="c-feature">ReadExcel</td><td class="c-behavior">Parse xlsx/xls/xlsm/xlsb; size limit 10MB; format-mismatch + CFB/HTML/CSV disguise rejection; optional styles</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts (format rejection 8 it, styles), excel-styles.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">**Size-limit (>10MB) enforcement path not tested** — only format mismatch</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-013</code></td><td class="c-area">Tools</td><td class="c-feature">ReadDocx</td><td class="c-behavior">Parse docx/docm; size limit 10MB</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Size-limit path untested; happy-path light</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-014</code></td><td class="c-area">Tools</td><td class="c-feature">ReadPdf</td><td class="c-behavior">Extract PDF text; query/grep mode (regex, case-insensitive, no-match, empty-query); size limit 10MB; OOXML-as-pdf rejection</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts (ReadPdf query mode 5 it, format rejection)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Size-limit path untested</td></tr>
|
||
<tr data-cov="NONE" data-area="Tools"><td class="c-id"><code>TOOL-015</code></td><td class="c-area">Tools</td><td class="c-feature">ReadPPTX</td><td class="c-behavior">Parse pptx/pptm; slide order resolution; notes; **ZIP-bomb detection (uncompressed >200MB)**; size limit 50MB</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">**ZIP-bomb detection AND PPTX size-limit have NO test** — highest-risk office gap</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-016</code></td><td class="c-area">Tools</td><td class="c-feature">ReadMsg</td><td class="c-behavior">Parse Outlook .msg</td><td class="c-source"><code></code></td><td class="c-test">msg.test.ts (41 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-017</code></td><td class="c-area">Tools</td><td class="c-feature">SplitExcelSheets</td><td class="c-behavior">Split workbook into per-sheet files</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Split correctness lightly covered</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-018</code></td><td class="c-area">Tools</td><td class="c-feature">SplitDocxSections</td><td class="c-behavior">Split docx into section files</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-019</code></td><td class="c-area">Tools</td><td class="c-feature">PdfToImages</td><td class="c-behavior">Render PDF pages to images; edit-not-allowed error, missing-file error, invalid page_range error</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts (3 error-path it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Error paths covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-020</code></td><td class="c-area">Tools</td><td class="c-feature">SQLite</td><td class="c-behavior">Run SQL on workspace .db; SELECT happy path, write guards (editAllowed), always-blocked DDL, PRAGMA, errors, path-traversal guard, large result sets</td><td class="c-source"><code></code></td><td class="c-test">data.test.ts (25 it across 9 describe)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Strong — guards + traversal + DDL block</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-021</code></td><td class="c-area">Tools</td><td class="c-feature">BatchReviewTextWithLLM</td><td class="c-behavior">Per-file isolated LLM review; output-path prefix enforcement</td><td class="c-source"><code></code></td><td class="c-test">review.test.ts (3 it)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Only 3 it; LLM-failure / partial-file handling not covered</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-022</code></td><td class="c-area">Tools</td><td class="c-feature">MergeReviewedResults</td><td class="c-behavior">Merge reviewed JSON → markdown; rejects outputs outside prefixes</td><td class="c-source"><code></code></td><td class="c-test">review.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Merge edge cases (malformed JSON) untested</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-023</code></td><td class="c-area">Tools</td><td class="c-feature">BrowseWeb</td><td class="c-behavior">Playwright browse + actions mode; URL validation, SSRF/localhost block, file-URL workspace containment, traversal reject, auth-expiry, download filename sanitize, unique-path, recording, source persist</td><td class="c-source"><code></code></td><td class="c-test">browser.test.ts (36 it), browser.runpageactions.test.ts, browser-frame-chain.e2e.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Validation/SSRF/recording very strong; live page-action correctness is e2e-gated (env-dependent)</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-024</code></td><td class="c-area">Tools</td><td class="c-feature">SpawnSubTask</td><td class="c-behavior">Spawn parallel subtask job</td><td class="c-source"><code></code></td><td class="c-test">orchestration.test.ts (8 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-025</code></td><td class="c-area">Tools</td><td class="c-feature">XSearch</td><td class="c-behavior">X/Twitter search</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts (18 it)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Live transaction-id breakage (MEMORY) means runtime failures not covered by unit tests</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-026</code></td><td class="c-area">Tools</td><td class="c-feature">XUserPosts</td><td class="c-behavior">X user timeline posts</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same upstream breakage caveat</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-027</code></td><td class="c-area">Tools</td><td class="c-feature">XPostDetail</td><td class="c-behavior">Single X post detail</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-028</code></td><td class="c-area">Tools</td><td class="c-feature">XFetchCardMedia</td><td class="c-behavior">Fetch X card media</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Likely thin coverage</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-029</code></td><td class="c-area">Tools</td><td class="c-feature">XTimeline</td><td class="c-behavior">X home/timeline</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Likely thin coverage</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-030</code></td><td class="c-area">Tools</td><td class="c-feature">SearchPlaces</td><td class="c-behavior">Maps place search</td><td class="c-source"><code></code></td><td class="c-test">maps.test.ts (27 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-031</code></td><td class="c-area">Tools</td><td class="c-feature">GetDirections</td><td class="c-behavior">Maps routing</td><td class="c-source"><code></code></td><td class="c-test">maps.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-032</code></td><td class="c-area">Tools</td><td class="c-feature">ReverseGeocode</td><td class="c-behavior">Coords → address</td><td class="c-source"><code></code></td><td class="c-test">maps.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-033</code></td><td class="c-area">Tools</td><td class="c-feature">GetYouTubeTranscript</td><td class="c-behavior">Fetch YouTube transcript</td><td class="c-source"><code></code></td><td class="c-test">youtube.test.ts (20 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-034</code></td><td class="c-area">Tools</td><td class="c-feature">SearchYouTube</td><td class="c-behavior">YouTube search</td><td class="c-source"><code></code></td><td class="c-test">youtube.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-035</code></td><td class="c-area">Tools</td><td class="c-feature">SearchAmazon</td><td class="c-behavior">Amazon product search</td><td class="c-source"><code></code></td><td class="c-test">amazon.test.ts (13 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-036</code></td><td class="c-area">Tools</td><td class="c-feature">TranscribeAudio</td><td class="c-behavior">Speech-to-text</td><td class="c-source"><code></code></td><td class="c-test">speech.test.ts (14 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-037</code></td><td class="c-area">Tools</td><td class="c-feature">CreateChecklist</td><td class="c-behavior">Create task checklist (META)</td><td class="c-source"><code></code></td><td class="c-test">checklist.test.ts (24 it incl. META_TOOL describe)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-038</code></td><td class="c-area">Tools</td><td class="c-feature">CheckItem</td><td class="c-behavior">Mark checklist item (META)</td><td class="c-source"><code></code></td><td class="c-test">checklist.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-039</code></td><td class="c-area">Tools</td><td class="c-feature">GetChecklist</td><td class="c-behavior">Read checklist (META)</td><td class="c-source"><code></code></td><td class="c-test">checklist.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-040</code></td><td class="c-area">Tools</td><td class="c-feature">ListPieces</td><td class="c-behavior">List available pieces</td><td class="c-source"><code></code></td><td class="c-test">pieces.test.ts (11 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-041</code></td><td class="c-area">Tools</td><td class="c-feature">GetPiece</td><td class="c-behavior">Read a piece YAML</td><td class="c-source"><code></code></td><td class="c-test">pieces.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-042</code></td><td class="c-area">Tools</td><td class="c-feature">CreatePiece</td><td class="c-behavior">Create custom piece (per-user fork)</td><td class="c-source"><code></code></td><td class="c-test">pieces.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Validation/lint reject paths (COMPLETE/ABORT in next) coverage unclear here</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-043</code></td><td class="c-area">Tools</td><td class="c-feature">UpdatePiece</td><td class="c-behavior">Update custom piece</td><td class="c-source"><code></code></td><td class="c-test">pieces.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-044</code></td><td class="c-area">Tools</td><td class="c-feature">InstallSkill</td><td class="c-behavior">Install skill from registry (META)</td><td class="c-source"><code></code></td><td class="c-test">skills.test.ts (26 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-045</code></td><td class="c-area">Tools</td><td class="c-feature">ReadSkill</td><td class="c-behavior">Read skill content (META, always available)</td><td class="c-source"><code></code></td><td class="c-test">skills.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-046</code></td><td class="c-area">Tools</td><td class="c-feature">ListSkills</td><td class="c-behavior">List skills (META)</td><td class="c-source"><code></code></td><td class="c-test">skills.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-047</code></td><td class="c-area">Tools</td><td class="c-feature">ReadToolDoc</td><td class="c-behavior">Read tool doc incl. MCP tools via cache (META, always available); name-required error, bad-MCP-name error</td><td class="c-source"><code></code></td><td class="c-test">docs.test.ts (10 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="NONE" data-area="Tools"><td class="c-id"><code>XCUT-001</code></td><td class="c-area">Tools</td><td class="c-feature">Dynamic tool loading + dispatch order</td><td class="c-behavior">index.ts <code>tryLoadModule</code> lazy-imports each module; <code>executeToolInner</code> dispatches in fixed order (mcp → web → image → data → office → review → x → orchestration → browser → maps → … → skills); failed loads silently skipped</td><td class="c-source"><code></code></td><td class="c-test">index.test.ts (1 it: SshConsole catalog)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">**Dispatch order, first-match-wins, and silent-skip-on-load-failure are essentially untested** (single catalog test)</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>XCUT-002</code></td><td class="c-area">Tools</td><td class="c-feature">META_TOOLS always-injected at dispatch</td><td class="c-behavior">index.ts keeps a LARGER META_TOOLS list (incl. RequestTool, Mission, UserFolder, AppDocs, Skills) injected into getToolDefs regardless of movement allowed_tools</td><td class="c-source"><code></code></td><td class="c-test">(indirect via tool-categories.test.ts subset)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">index.ts's runtime META injection (20 names) not directly asserted; only tool-categories' SUBSET set is tested</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-003</code></td><td class="c-area">Tools</td><td class="c-feature">tool-categories: name→category map</td><td class="c-behavior">first-write-wins map built from ALL_TOOL_DEFS('core') + MODULE_SPECS; memoised; listToolCategories; SENSITIVE_CATEGORIES={ssh,browser}; SENSITIVE_TOOLS={Bash}; META_TOOLS subset</td><td class="c-source"><code></code></td><td class="c-test">tool-categories.test.ts (12 it: META_TOOLS entries, sensitive sets, map)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-004</code></td><td class="c-area">Tools</td><td class="c-feature">RequestTool flow</td><td class="c-behavior">META tool; classifies request as already_available / available_not_allowed(requested) / unknown(hard-error); name+reason required; interactive→pending approval, non-interactive→auto_denied; records via ctx.recordToolRequest</td><td class="c-source"><code></code></td><td class="c-test">tool-request.test.ts (9 it), db/repository.tool-requests.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">All 3 classification branches + interactive/auto-deny covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-005</code></td><td class="c-area">Tools</td><td class="c-feature">workspace-tool-policy resolution</td><td class="c-behavior">resolveWorkspaceTools: safe categories default-on (minus disabledSafe), sensitive categories+tools opt-in only (enabledSensitive), META always unioned, Bash special-cased, <code>mcp__*</code> always carried; parseToolPolicy tolerant JSON</td><td class="c-source"><code></code></td><td class="c-test">workspace-tool-policy.test.ts, workspace-tool-policy.regression.test.ts, db/repository.tool-policy.test.ts, bridge/space-api.tool-policy.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Strong; regression test guards Bash toggle (MEMORY PR #653)</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>XCUT-006</code></td><td class="c-area">Tools</td><td class="c-feature">raw-save result logging</td><td class="c-behavior">saveRawData writes {logsRoot}/raw/<tool>-<ts>.txt + rawdata-history.jsonl for RAW_SAVE_TOOLS (web/x/youtube/amazon/speech/ms-learn); logRawDownload records DownloadFile path-only; errors swallowed to logger.warn</td><td class="c-source"><code></code></td><td class="c-test">raw-save.test.ts (5 it)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Helper, not a tool. Per-tool wiring (that each RAW_SAVE_TOOL actually triggers save) is not asserted end-to-end</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>XCUT-007</code></td><td class="c-area">Tools</td><td class="c-feature">structured-blocks</td><td class="c-behavior">Rich-UI structured-data save helper (no TOOL_DEFS)</td><td class="c-source"><code></code></td><td class="c-test">structured-blocks.test.ts (4 it)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Helper; basic coverage</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-008</code></td><td class="c-area">Tools</td><td class="c-feature">binary-detect shared helper</td><td class="c-behavior">Magic-byte/binary detection used by Read + WebFetch</td><td class="c-source"><code></code></td><td class="c-test">binary-detect.test.ts (26 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Well covered; underpins Read/WebFetch binary handling</td></tr>
|
||
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-009</code></td><td class="c-area">Tools</td><td class="c-feature">conflict-guard (persistent workspace)</td><td class="c-behavior">Detects concurrent-edit conflicts in shared space</td><td class="c-source"><code></code></td><td class="c-test">conflict-guard.test.ts (9 it), core.test.ts (conflict detection)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-001</code></td><td class="c-area">Pieces</td><td class="c-feature">chat</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test (as default-bias target); reflection/silent-fork & applier.fuzz use 'chat'; piece-runner all-load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No test asserts the single-movement respond contract or its 43-tool allowlist; only used as a name fixture + load-sweep</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-002</code></td><td class="c-area">Pieces</td><td class="c-feature">general</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test (<code>loadPiece('general')</code> line 401, used in review-prompt/shared_tools tests); pieces-api uses 'general' fixture; load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Loaded & used as a fixture for unrelated assertions (review prompts, SpawnSubTask skip), but its 4-movement decompose/verify loop is not asserted as a contract</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-003</code></td><td class="c-area">Pieces</td><td class="c-feature">research</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test <code>loadPiece('research')</code> (line 403, review-prompt structure assertion); load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Review-prompt/plan-aware structure asserted for it, but the dig/analyze/verify transition logic is not exercised</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-004</code></td><td class="c-area">Pieces</td><td class="c-feature">research-sub</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test; only schema-load smoke. Spawn-from-parent linkage untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-005</code></td><td class="c-area">Pieces</td><td class="c-feature">data-process</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test (longer-match-wins: data-process over general); load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Classifier disambiguation asserted; movement contract not</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-006</code></td><td class="c-area">Pieces</td><td class="c-feature">office-process</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test <code>loadPiece('office-process')</code> (line 402); piece-classifier fixture; load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Loaded for review-prompt assertion; 2-movement contract not exercised</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-007</code></td><td class="c-area">Pieces</td><td class="c-feature">slide</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test (chat vs slide routing fixture); load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Used as classifier fixture name; movement/tool contract untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-008</code></td><td class="c-area">Pieces</td><td class="c-feature">brainstorming</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test; schema-load smoke only</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-009</code></td><td class="c-area">Pieces</td><td class="c-feature">sns-research</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-010</code></td><td class="c-area">Pieces</td><td class="c-feature">x-ai-digest</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-011</code></td><td class="c-area">Pieces</td><td class="c-feature">piece-builder</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test (workspace-app routed AWAY from piece-builder); load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Only the negative-routing case touches it; its own contract untested</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PIECE-012</code></td><td class="c-area">Pieces</td><td class="c-feature">workspace-app</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test dedicated movement-structure assertions (build→verify, default_next checks, lines 542-557); piece-classifier routing test (line 41-52); workspace-app E2E harness (separate subsystem)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Movement structure + default_next + classifier routing all asserted; strongest-covered piece</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PIECE-013</code></td><td class="c-area">Pieces</td><td class="c-feature">ssh-console</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test dedicated assertion (interact default_next: COMPLETE, line 563-572); tools/ssh-console.test; ssh allowed_ssh_connections validation tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Movement + ssh-connection allowlist contract asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PIECE-014</code></td><td class="c-area">Pieces</td><td class="c-feature">ssh-ops</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test dedicated assertion (line 576+); ssh allowlist validation tests; load sweep</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Loaded + structurally asserted (execute/verify); ssh allowlist format validation covered. Lighter than ssh-console but real assertions exist</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-015</code></td><td class="c-area">Pieces</td><td class="c-feature">help</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-001</code></td><td class="c-area">Pieces</td><td class="c-feature">loadPiece (custom-dir priority + builtin fallback)</td><td class="c-behavior">Resolves piece by name across custom dirs (string|string[]), custom wins, fallback to builtin</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test "loadPiece multi-dir" (string|string[], per-user wins, first-dir wins, backward-compat); worker.test <code>loadPiece('local-piece')</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Multi-dir resolution well covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-002</code></td><td class="c-area">Pieces</td><td class="c-feature">validatePieceDef (Phase 6b terminal-next rejection)</td><td class="c-behavior">Throws if any <code>rules[].next</code> ∈ {COMPLETE,ABORT,ASK}; default_next exempt</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test "loadPiece terminal-rule validation (Phase 6b)": rejects COMPLETE/ABORT/ASK in rules[].next, accepts default_next: COMPLETE, accepts movement-to-movement + WAIT_SUBTASKS</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Core invariant directly asserted with error-message matching</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-003</code></td><td class="c-area">Pieces</td><td class="c-feature">default_next sentinel (engine-internal fallback)</td><td class="c-behavior">Used for context-overflow / ASK-limit / SpawnSubTask-skip; allows COMPLETE/ABORT/ASK</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test "falls back to default_next when ASK limit is reached", "resolves a COMPLETE default_next reached via ASK-limit fallback", SpawnSubTask-skip→default_next tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">All three sentinel paths exercised</td></tr>
|
||
<tr data-cov="NONE" data-area="Pieces"><td class="c-id"><code>PINFRA-004</code></td><td class="c-area">Pieces</td><td class="c-feature">lint-pieces.mjs (CI lint, terminal-next ban)</td><td class="c-behavior">Standalone script: hard-fails any piece with COMPLETE/ABORT/ASK in rules[].next</td><td class="c-source"><code></code></td><td class="c-test">**none** (no *.test for the script; runtime equivalent in validatePieceDef IS tested)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">Script itself has no test; logic duplicated/covered by PINFRA-002 but the CLI (file collection, exit codes, parse-error path) is untested</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-005</code></td><td class="c-area">Pieces</td><td class="c-feature">CreatePiece tool validation</td><td class="c-behavior">Rejects missing/0 max_movements, non-array shared_tools, bad rules[].next; writes to dirs[0] (per-user)</td><td class="c-source"><code></code></td><td class="c-test">tools/pieces.test "CreatePiece" block (missing max_movements, max_movements:0, valid write, shared_tools array/non-array)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Validation + write-target asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-006</code></td><td class="c-area">Pieces</td><td class="c-feature">UpdatePiece tool (built-in protection)</td><td class="c-behavior">Refuses overwriting built-in (isBuiltinOnly); allows update when custom override exists</td><td class="c-source"><code></code></td><td class="c-test">tools/pieces.test "UpdatePiece" block (refuses built-in overwrite regression, allows custom override)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Built-in immutability regression covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-007</code></td><td class="c-area">Pieces</td><td class="c-feature">rules[].next / default_next domain validation (tool path)</td><td class="c-behavior">validRuleNexts = movements ∪ WAIT_SUBTASKS; validDefaultNexts adds COMPLETE/ABORT/ASK</td><td class="c-source"><code></code></td><td class="c-test">Covered via CreatePiece tests + pieces-api "rejects rules[].next: COMPLETE" / "accepts default_next: COMPLETE"</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Both accept & reject branches asserted at API + tool level</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-008</code></td><td class="c-area">Pieces</td><td class="c-feature">ListPieces / GetPiece (custom+builtin merge, priority)</td><td class="c-behavior">Lists from per-user + global-custom + builtin; GetPiece prefers dirs[0]</td><td class="c-source"><code></code></td><td class="c-test">tools/pieces.test "customPiecesDir as array — P1-a regression" (ListPieces both dirs, GetPiece second-dir, GetPiece prefers per-user)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Multi-dir merge + priority covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-009</code></td><td class="c-area">Pieces</td><td class="c-feature">piece classifier — buildClassificationPrompt</td><td class="c-behavior">Builds LLM prompt from task + all piece descriptions + keyword hints; biases to chat; routes workspace-app</td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test "buildClassificationPrompt" (includes text/descriptions, file names, chat bias, workspace-app routing)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Prompt construction + routing bias asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-010</code></td><td class="c-area">Pieces</td><td class="c-feature">piece classifier — parseClassificationResponse</td><td class="c-behavior">Extracts valid piece name from noisy LLM output; longer-match-wins; null on invalid/empty</td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test "parseClassificationResponse" (valid, noisy, longer-match data-process>general, invalid→null, empty→null)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Parse edge cases covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-011</code></td><td class="c-area">Pieces</td><td class="c-feature">piece catalog (loadAllPieceTriggers / catalog merge)</td><td class="c-behavior">Enumerates all pieces' triggers; custom overrides builtin by name</td><td class="c-source"><code></code></td><td class="c-test">piece-catalog.test (builtin+custom merge, custom override wins, triggers exposed)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Catalog merge & override asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-012</code></td><td class="c-area">Pieces</td><td class="c-feature">pieces-api CRUD + per-user/builtin authz</td><td class="c-behavior">GET/PUT/POST/DELETE with per-user custom dirs, builtin immutability, cross-user authz, ?source=</td><td class="c-source"><code></code></td><td class="c-test">pieces-api.test — extensive: no-auth legacy + auth-aware (built-in 403, own-custom 200, cross-user 403, admin edit-but-not-delete builtin, duplicate-name reject, source resolution)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">One of the most thoroughly tested infra areas</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-013</code></td><td class="c-area">Pieces</td><td class="c-feature">SSH allowlist validation (allowed_ssh_connections)</td><td class="c-behavior">Validates UUID/wildcard/empty allowlist; rejects SshExec without allowlist & bad formats</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test (id-format reject, wildcard, multi-movement offenders) + pieces-api.test (SshExec w/o allowlist, UUID, wildcard, empty, bad format, non-array)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Both engine + API layers covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-014</code></td><td class="c-area">Pieces</td><td class="c-feature">shared_tools / required_mcp normalization</td><td class="c-behavior">Drops invalid entries, warns on SSH shared_tools w/o connections</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test imports normalizeRequiredMcp/normalizeSharedTools/mergeToolNames; tools/pieces + pieces-api shared_tools array/non-array tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Normalization + merge covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-015</code></td><td class="c-area">Pieces</td><td class="c-feature">custom/default piece namespace separation (reflection silent fork)</td><td class="c-behavior">Reflection forks built-in into data/users/{u}/pieces with forked_from_commit instead of mutating builtin</td><td class="c-source"><code></code></td><td class="c-test">reflection/silent-fork.test (silentFork chat/data-process into per-user dir)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Silent-fork path covered (reflection subsystem)</td></tr>
|
||
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-016</code></td><td class="c-area">Pieces</td><td class="c-feature">all-built-in-pieces load sweep</td><td class="c-behavior">Loops every pieces/*.yaml asserting loadPiece().not.toThrow()</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test (~line 531-537)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">This is what gives every piece PARTIAL; it validates schema but NOT each piece's movement/rule semantics</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PINFRA-017</code></td><td class="c-area">Pieces</td><td class="c-feature">per-user piece resolution in worker/scheduler/continue paths</td><td class="c-behavior">customPiecesDir threaded through worker, continue-job, classifier, pieces-api</td><td class="c-source"><code></code></td><td class="c-test">worker.test <code>loadPiece('local-piece', piecesDir, customPieceDirs)</code>; partly via pieces-api auth tests</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Worker path has one test; full propagation across scheduler/continue paths (per the known "no-auth local fallback" checklist) not comprehensively asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-001</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: enabled gate (default OFF)</td><td class="c-behavior"><code>maybeEnqueueReflection</code> returns early when <code>cfg.enabled=false</code></td><td class="c-source"><code>src/worker.ts (363)</code></td><td class="c-test">worker.reflection-enqueue.test.ts ("does nothing when enabled=false")</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">DEFAULT_REFLECTION default value (enabled:false) itself not asserted in a config test</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-002</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: no-recursion on reflection jobs</td><td class="c-behavior">returns early when <code>job.taskKind==='reflection'</code></td><td class="c-source"><code>src/worker.ts (371)</code></td><td class="c-test">worker.reflection-enqueue.test.ts ("does NOT recurse")</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-003</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: no-auth owner fallback to 'local'</td><td class="c-behavior"><code>ownerId ?? 'local'</code> for namespace + budget key</td><td class="c-source"><code>src/worker.ts (377)</code></td><td class="c-test">enqueue.test.ts (2 cases: fallback + budget keyed to 'local')</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-004</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: worker_required gate</td><td class="c-behavior">skip when <code>workerRequired</code> and no worker has role 'reflection'</td><td class="c-source"><code>src/worker.ts (379-388)</code></td><td class="c-test">enqueue.test.ts (3 cases: true+none/true+exists/false)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-005</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: per-user daily token budget cap</td><td class="c-behavior">skip when spent >= cap; cap=0 = unlimited; UTC-day window</td><td class="c-source"><code>src/worker.ts (392-405)</code></td><td class="c-test">enqueue.test.ts (at/under cap, cap=0, UTC 25h, independent users)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-006</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: space_id propagation (Space-as-Principal)</td><td class="c-behavior">carries job.spaceId into payload (null for personal)</td><td class="c-source"><code>src/worker.ts (407-420)</code></td><td class="c-test">enqueue.test.ts (carries space_id / null personal)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-007</code></td><td class="c-area">Reflection</td><td class="c-feature">Dispatch: reflection job routes to runReflectionJob</td><td class="c-behavior">worker picks task_kind='reflection', imports + runs runner, role-keyed routing</td><td class="c-source"><code>src/worker.ts (1129-1130, 2044-2073)</code></td><td class="c-test">worker.reflection-dispatch.test.ts (2 cases)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">dispatch test is small (~2 cases); gateway routing-key + 401-without-key path not asserted end-to-end</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-008</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: loadReflectionInputs (memory/activity/comments/feedback + observedRevisions)</td><td class="c-behavior">assembles inputs, builds bodyRevision CAS map</td><td class="c-source"><code>load-inputs.ts</code></td><td class="c-test">load-inputs.test.ts (~8 cases, CAS)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-009</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: forced submit_reflection tool_call (LLM)</td><td class="c-behavior">callReflectionLlm forces the tool, parses result, tokens</td><td class="c-source"><code>llm-client.ts</code></td><td class="c-test">llm-client.test.ts (~10 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-010</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: prompt construction</td><td class="c-behavior">buildSystemPrompt / buildUserPrompt</td><td class="c-source"><code>reflection-prompt.ts</code></td><td class="c-test">reflection-prompt.test.ts (~18 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-011</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: schema validation of LLM output</td><td class="c-behavior">reflection-schema parse of memory_changes/piece_changes</td><td class="c-source"><code>reflection-schema.ts</code></td><td class="c-test">reflection-schema.test.ts (~10 cases, all ops)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-012</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: end-to-end happy path + outcome + metric row</td><td class="c-behavior">runReflectionJob applies, snapshots, records metric</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts (~12 cases: applied/abstained/failed, snapshot, lock, space, budget)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-013</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: failure fallbacks record outcome='failed'</td><td class="c-behavior">loadInputs/LLM/apply throw → metric 'failed', return 'failed'</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts (failed)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only some of the 3 throw-sites (loadInputs/LLM/apply+snapshot) explicitly covered; writeSnapshot-throws-but-continue branch not clearly asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-014</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: Space-as-Principal storage resolution</td><td class="c-behavior">space job → data/spaces/{id}; personal → data/users/{userId}; attribution stays per-user</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts (space-principal), revisions.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-015</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_unknown_type</td><td class="c-behavior">type not in {user,feedback,project,reference}</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; applier.fuzz.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-016</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_bad_name</td><td class="c-behavior">name fails isValidMemoryName</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; applier.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-017</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_body_too_large</td><td class="c-behavior">body > maxBodyBytes (UTF-8)</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-018</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_injected_directive</td><td class="c-behavior">prompt-injection heuristics in body/description</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">only validator test hits it; not exercised through applier (low risk, static check)</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-019</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_missing_target</td><td class="c-behavior">non-add op missing merge_target OR target not in index</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; applier.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-020</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_name_collision</td><td class="c-behavior">add with already-existing name</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-021</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_target_piece_mismatch</td><td class="c-behavior">piece_changes.target_piece ≠ input.pieceName</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; applier.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-022</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_invalid_yaml</td><td class="c-behavior">piece new_yaml fails YAML parse</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-023</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_invalid_piece</td><td class="c-behavior">new_yaml parses but fails piece-lint</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-024</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_dangerous_piece</td><td class="c-behavior">COMPLETE/ABORT/ASK in rules[].next</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-025</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply CAS: rejected_stale_target</td><td class="c-behavior">merge_target body revision != snapshot → reject at write time</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">raised by applier (not static validator); covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-026</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply op: add</td><td class="c-behavior">upsertMemoryEntry for new name</td><td class="c-source"><code>applier.ts (applyOne)</code></td><td class="c-test">applier.test.ts; schema; runner</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-027</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply op: update (+CAS)</td><td class="c-behavior">upsert replacing body/desc/type, requires CAS pass</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (ops + CAS)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-028</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply op: merge_into (+CAS)</td><td class="c-behavior">read existing, append timestamped section, upsert merged</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-029</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply op: remove (+CAS)</td><td class="c-behavior">removeMemoryEntry (trash + index), requires CAS</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-030</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply: missing-target-on-disk at write time</td><td class="c-behavior">non-add op, target gone from disk → rejected_missing_target</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (rejected_missing_target)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-031</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply: 3-change hard cap</td><td class="c-behavior">memory_changes truncated to 3 with WARN</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts / fuzz</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">cap value 3 truncation likely covered by fuzz inputs but a dedicated ">3 dropped with WARN" assertion not confirmed</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-032</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply: applyOne write error → code='failed' on decision</td><td class="c-behavior">per-change try/catch records failed</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">—</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">the per-change applyOne throw path (decision.code='failed') not obviously asserted (distinct from runner-level 'failed')</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-033</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: abstained</td><td class="c-behavior">abstain_reason + no applied + no rejected</td><td class="c-source"><code>applier.ts (decideOutcome)</code></td><td class="c-test">applier.test.ts (abstained); runner</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-034</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: partial</td><td class="c-behavior">any applied AND any rejected/dropped</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (partial)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-035</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: applied</td><td class="c-behavior">any applied, none rejected</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (applied); runner</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-036</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: rejected</td><td class="c-behavior">any rejected/dropped, none applied</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (rejected); fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-037</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: fallback abstained</td><td class="c-behavior">empty input (0 changes, no piece)</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">the empty-input fallback branch (vs explicit abstain_reason) not clearly distinguished in tests</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-038</code></td><td class="c-area">Reflection</td><td class="c-feature">Piece write: cooldown gate (2 edits/24h)</td><td class="c-behavior">>=2 edits in window → {written:false, reason:'cooldown'} → pieceCooldownDropped</td><td class="c-source"><code>piece-writer.ts; applier.ts</code></td><td class="c-test">piece-writer.test.ts (cooldown); applier.test.ts (cooldown)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-039</code></td><td class="c-area">Reflection</td><td class="c-feature">Piece write: cooldown drop vs semantic reject distinction</td><td class="c-behavior">pieceCooldownDropped separate from pieceRejectCode in outcome</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts; piece-writer.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-040</code></td><td class="c-area">Reflection</td><td class="c-feature">Piece write: atomic temp+rename + catalog invalidate + recordPieceEdit</td><td class="c-behavior">writePiece tmp.{pid} → rename, repo.recordPieceEdit, catalog.invalidate</td><td class="c-source"><code>piece-writer.ts</code></td><td class="c-test">piece-writer.test.ts (~8 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">atomic-rename crash-safety (partial-write) not stress-tested</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-041</code></td><td class="c-area">Reflection</td><td class="c-feature">Silent fork of built-in piece</td><td class="c-behavior">builtin source → copy to data/users/{userId}/pieces with forked_from_commit/forked_at; idempotent; throws if source missing</td><td class="c-source"><code>silent-fork.ts</code></td><td class="c-test">silent-fork.test.ts (~9 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-042</code></td><td class="c-area">Reflection</td><td class="c-feature">Drift detection (forked_from_commit vs latest builtin commit)</td><td class="c-behavior">detectDrift returns drifted flag; graceful no-op without git</td><td class="c-source"><code>drift-detect.ts</code></td><td class="c-test">drift-detect.test.ts (~7 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-043</code></td><td class="c-area">Reflection</td><td class="c-feature">Snapshot write (.reflection-history/{ts}-{jobId} + index.jsonl append)</td><td class="c-behavior">writeSnapshot persists before/after files, appends index.jsonl atomically under lock</td><td class="c-source"><code>snapshot.ts; reflection-runner.ts</code></td><td class="c-test">snapshot.test.ts (~34 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-044</code></td><td class="c-area">Reflection</td><td class="c-feature">Snapshot: before/after memory file capture</td><td class="c-behavior">runner builds beforeFiles/afterFiles from accepted decisions (remove uses merge_target)</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts; snapshot.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">the remove-op before-file naming branch (merge_target vs name) in runner not specifically asserted</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-045</code></td><td class="c-area">Reflection</td><td class="c-feature">Snapshot: piece before/after YAML capture</td><td class="c-behavior">reads custom piece path pre/post for snapshot</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">piece YAML before/after capture lightly covered; existsSync(customPath) edge not asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-046</code></td><td class="c-area">Reflection</td><td class="c-feature">bodyRevision (SHA-1 of parsed body) consistency</td><td class="c-behavior">same helper for loader observedRevisions and applier CAS</td><td class="c-source"><code>revisions.ts</code></td><td class="c-test">revisions.test.ts (~8 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-047</code></td><td class="c-area">Reflection</td><td class="c-feature">Per-user file lock (withUserLock)</td><td class="c-behavior">serializes apply+snapshot critical section (lockfile)</td><td class="c-source"><code>user-lock.ts; applier.ts; reflection-runner.ts</code></td><td class="c-test">user-lock.test.ts; applier.test.ts (lock); runner</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">concurrent contention/torn-write scenario only indirectly assured</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-048</code></td><td class="c-area">Reflection</td><td class="c-feature">Activity log summarizer</td><td class="c-behavior">summarizes activity.log to bounded size for prompt</td><td class="c-source"><code>activity-summarizer.ts</code></td><td class="c-test">activity-summarizer.test.ts (~5 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-049</code></td><td class="c-area">Reflection</td><td class="c-feature">DB: reflection_metrics row (outcome applied/partial/abstained/rejected/failed)</td><td class="c-behavior">recordReflectionMetric inserts one row per reflection job</td><td class="c-source"><code>src/db/repository.ts (~4093-4127)</code></td><td class="c-test">reflection-runner.test.ts; migrate.reflection-columns.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-050</code></td><td class="c-area">Reflection</td><td class="c-feature">DB: reflection_piece_edits cooldown tracking</td><td class="c-behavior">recordPieceEdit insert + count-in-window query</td><td class="c-source"><code>src/db/repository.ts (~4068-4090)</code></td><td class="c-test">piece-writer.test.ts; migrate test</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-051</code></td><td class="c-area">Reflection</td><td class="c-feature">DB: aggregateReflectionMetrics (budget)</td><td class="c-behavior">sums tokens since UTC-day start for budget gate</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">enqueue.test.ts (budget)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-052</code></td><td class="c-area">Reflection</td><td class="c-feature">DB: task_kind / payload columns + reflection job creation</td><td class="c-behavior">migrate adds task_kind, payload; reflection job created with taskKind='reflection'</td><td class="c-source"><code>src/db/repository.ts; migrate.ts</code></td><td class="c-test">migrate.reflection-columns.test.ts (~23 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-053</code></td><td class="c-area">Reflection</td><td class="c-feature">API: GET /history (paged, owner-scoped, nextCursor)</td><td class="c-behavior">lists snapshot index entries, most-recent-first, cursor pagination</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts (~29 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-054</code></td><td class="c-area">Reflection</td><td class="c-feature">API: GET /history/:snapshotId (404 no-leak for non-owner/missing)</td><td class="c-behavior">detail or 404 (no existence leak)</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-055</code></td><td class="c-area">Reflection</td><td class="c-feature">API: POST /history/:snapshotId/revert (idempotent)</td><td class="c-behavior">revertSnapshotForUser; {reverted:true} first, {reverted:false} after</td><td class="c-source"><code>bridge/reflection-api.ts; snapshot.ts</code></td><td class="c-test">reflection-api.test.ts (revert); snapshot.test.ts (revert)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-056</code></td><td class="c-area">Reflection</td><td class="c-feature">API: space-aware store resolution + canEditInSpace authz</td><td class="c-behavior">resolves per-user vs per-space store; space history needs edit rights; 404 on mismatch</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts (space-principal)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">canEditInSpace negative path (member-without-edit-rights → 404) not clearly asserted as a distinct multiuser case</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-057</code></td><td class="c-area">Reflection</td><td class="c-feature">API: GET /metrics</td><td class="c-behavior">aggregated reflection metrics endpoint</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">endpoint exists; depth of assertion (per-outcome breakdown) unverified</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-058</code></td><td class="c-area">Reflection</td><td class="c-feature">API: GET /latest-for-task/:taskId (admin)</td><td class="c-behavior">latest snapshot for a task; null (not 404) when none; admin-gated</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">admin-gating + null-vs-404 distinction not confirmed asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-059</code></td><td class="c-area">Reflection</td><td class="c-feature">Retention: pruneOldSnapshots (age)</td><td class="c-behavior">delete snapshot dirs older than retentionDays; index.jsonl untouched</td><td class="c-source"><code>retention.ts</code></td><td class="c-test">retention.test.ts (~17 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-060</code></td><td class="c-area">Reflection</td><td class="c-feature">Retention: enforceDiskCap (oldest-first)</td><td class="c-behavior">prune oldest until under capBytes</td><td class="c-source"><code>retention.ts</code></td><td class="c-test">retention.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-061</code></td><td class="c-area">Reflection</td><td class="c-feature">Retention: runReflectionRetentionSweep (per-user + per-space)</td><td class="c-behavior">sweeps both data/users and data/spaces trees</td><td class="c-source"><code>retention.ts</code></td><td class="c-test">retention.test.ts (space-principal)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-001</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks</td><td class="c-behavior">List tasks filtered by <code>buildVisibilityWhere</code> (owner/admin/org/public/space members)</td><td class="c-source"><code>local-tasks-api.ts:123</code></td><td class="c-test">local-tasks-api.test.ts (visibility list: owner/admin/same-org/diff-org bystander)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-002</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks</td><td class="c-behavior">Create task+job; visibility enum validation, org-scope check, spaceId forces private, owner from req.user (or <code>local</code> no-auth)</td><td class="c-source"><code>local-tasks-api.ts:134</code></td><td class="c-test">local-tasks-api.test.ts (visibility, no-auth owner, runtime_dir, attachments, spaceId), spaces.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-003</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId</td><td class="c-behavior">Get one task; <code>canViewTask</code> visibility gate</td><td class="c-source"><code>local-tasks-api.ts:342</code></td><td class="c-test">space-view.test.ts (membership read gate), spaces.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-004</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/tool-requests</td><td class="c-behavior">List pending tool requests; canViewTask gate</td><td class="c-source"><code>local-tasks-api.ts:361</code></td><td class="c-test">local-tasks-api.test.ts:175 (lists pending)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">view-gate denial path not directly asserted on this route</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-005</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/tool-requests/:reqId/decide</td><td class="c-behavior">Approve/deny a parked tool request; write-gate (403 view-only / 404 non-member), re-queue parked job, 409 already-decided</td><td class="c-source"><code>local-tasks-api.ts:380</code></td><td class="c-test">local-tasks-api.test.ts:143 (approve/deny/409/invalid value/park-contract guard)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-006</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/local/tasks/:taskId/feedback</td><td class="c-behavior">Submit rating/feedback; owner-or-admin (404 to others even if public)</td><td class="c-source"><code>local-tasks-api.ts:418</code></td><td class="c-test">local-tasks-api.test.ts:740 (owner/admin/non-owner 404), spaces.test.ts:268</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="NONE" data-area="Bridge core"><td class="c-id"><code>APIC-007</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/local/tasks/:taskId/mission</td><td class="c-behavior">Set/update task mission text</td><td class="c-source"><code>local-tasks-api.ts:442</code></td><td class="c-test">none found (only auth.test.ts matches the word "mission")</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">No test exercises this route — authz + validation unverified</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-008</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/comments</td><td class="c-behavior">List comments; canViewTask gate</td><td class="c-source"><code>local-tasks-api.ts:474</code></td><td class="c-test">covered indirectly via comment-write-gate.test.ts setup; no dedicated read-gate assertion</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">GET read-gate not directly asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-009</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/comments</td><td class="c-behavior">Post comment (user message → spawns/reuses job); **comment write gate** (owner/admin/editor 201, viewer 403, non-member 404), attachment save, tool_request gate atomicity, idle-job reuse</td><td class="c-source"><code>local-tasks-api.ts:492</code></td><td class="c-test">comment-write-gate.test.ts (owner/admin/editor/viewer-403/non-member-404/personal-404), local-tasks-api.test.ts:816 (ownership, attachments, job reuse, terminal→fresh job)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-010</code></td><td class="c-area">Bridge core</td><td class="c-feature">PATCH /api/local/tasks/:taskId</td><td class="c-behavior">Update task (visibility change, etc.); owner/admin, org-scope validation</td><td class="c-source"><code>local-tasks-api.ts:623</code></td><td class="c-test">local-tasks-api.test.ts:451 (visibility change, invalid enum, org scope), spaces.test.ts:255 (non-member 404)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-011</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/regenerate-title</td><td class="c-behavior">Regenerate AI title; owner/admin only</td><td class="c-source"><code>local-tasks-api.ts:681</code></td><td class="c-test">spaces.test.ts:296 (owner regenerate, +line 318 case)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Only exercised in space context; non-owner-denied / no-generator paths not asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-012</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/evaluate-prompt</td><td class="c-behavior">Prompt-coach: evaluate an instruction via LLM</td><td class="c-source"><code>local-tasks-api.ts:717</code></td><td class="c-test">local-tasks-api.test.ts:1370 (no-generator, blank, happy paths)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-013</code></td><td class="c-area">Bridge core</td><td class="c-feature">DELETE /api/local/tasks/:taskId</td><td class="c-behavior">Delete task; owner-or-admin (404 to others even public)</td><td class="c-source"><code>local-tasks-api.ts:756</code></td><td class="c-test">local-tasks-api.test.ts:336 (owner/admin/non-owner-404), spaces.test.ts:69</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-014</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/cancel</td><td class="c-behavior">Cancel running job; write-gate (editor 200, viewer 403, non-member 404)</td><td class="c-source"><code>local-tasks-api.ts:778</code></td><td class="c-test">comment-write-gate.test.ts:117 (editor/viewer-403/non-member-404), spaces.test.ts:98</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-015</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/continue</td><td class="c-behavior">Spawn child job with new piece; 409 job_in_progress / no_previous_job, handoff comment, instruction persist</td><td class="c-source"><code>local-tasks-api.ts:818</code></td><td class="c-test">local-tasks-api.test.ts:1072 (happy/handoff/persist/409 in-progress/409 no-prev), spaces.test.ts:122 (space_id carry, non-member 404)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="NONE" data-area="Bridge core"><td class="c-id"><code>APIC-016</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/stream</td><td class="c-behavior">SSE stream of job events (tool calls, deltas)</td><td class="c-source"><code>local-tasks-api.ts:914</code></td><td class="c-test">none (<code>/stream</code> matches only in app-share/share/space-api tests)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">SSE event mapping, visibility gate, reconnection all untested</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-017</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/files</td><td class="c-behavior">List files in a section (input/output/logs/workspace); canViewTask gate; traversal 400</td><td class="c-source"><code>local-files-api.ts:41</code></td><td class="c-test">local-files-api.test.ts:74 (default section, subdir, unknown section 400, private-hidden, traversal 400, logs from runtime_dir + workspace_path fallback)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-018</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/files/content</td><td class="c-behavior">Serve file as text/plain; traversal 400, dir 400, missing 404, leading-slash strip</td><td class="c-source"><code>local-files-api.ts:86</code></td><td class="c-test">local-files-api.test.ts:152 (text, requires-path, 404 not 500, dir 400, traversal 400, abs-path strip)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-019</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/files/raw</td><td class="c-behavior">Serve raw bytes by extension; <code>trusted=1</code> drops CSP sandbox (owner-trust model), <code>setUntrustedFileResponseHeaders</code>, <code>ensurePathWithin</code> traversal guard (400)</td><td class="c-source"><code>local-files-api.ts:136</code></td><td class="c-test">local-files-api.test.ts:197 (type-by-ext, sandbox default, trusted for owner/admin/non-owner-shared-viewer/ownerless, no-user-still-sandboxed, no-auth, non-HTML stays sandboxed, traversal 400)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Strong coverage incl. CSP trust matrix</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-020</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/files/office-preview</td><td class="c-behavior">Excel→cells JSON / PPTX→slide PNGs; section/path validation 400, soffice missing → 503</td><td class="c-source"><code>local-files-api.ts:203</code></td><td class="c-test">office-preview.test.ts (file-type detect, spreadsheet preview incl. formula, soffice-missing throws) tests the *module*; the HTTP route itself not directly hit</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Module logic FULL; HTTP route (section enum 400, canViewTask gate, 503 mapping) not exercised end-to-end</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-021</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/local/tasks/:taskId/files/content</td><td class="c-behavior">Write/overwrite output file; owner/admin only, output-section-only, refuse while running, traversal 400, string-content validation</td><td class="c-source"><code>local-files-api.ts:240</code></td><td class="c-test">local-files-api.test.ts:290 (owner write, non-owner 404, admin, refuse-running, output-only, requires-string, traversal 400)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-022</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/files/upload</td><td class="c-behavior">Upload files to input/ (≤60mb); owner/admin, O_EXCL rename-on-collision, name sanitize, refuse-running, section/empty/traversal 400</td><td class="c-source"><code>local-files-api.ts:324</code></td><td class="c-test">local-files-api.test.ts:366 (input upload, subdir, collision rename, name sanitize, non-owner 404, admin, refuse-running, logs/workspace 400, empty 400, traversal 400)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-023</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/files/delete</td><td class="c-behavior">Delete files; owner/admin, idempotent skip-missing, skip-dirs, refuse-running, validate-all-first (no partial), traversal 400</td><td class="c-source"><code>local-files-api.ts:392</code></td><td class="c-test">local-files-api.test.ts:470 (delete, single path, skip-missing/dirs, non-owner 404, refuse-running, logs 400, empty 400, traversal 400, validate-all-first)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-024</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/files/download-zip</td><td class="c-behavior">Zip selected files; read-gated (non-owner of public OK), all sections, <code>safeZipEntryName</code> zip-slip protection, traversal 400, empty 400</td><td class="c-source"><code>local-files-api.ts:447</code></td><td class="c-test">local-files-api.test.ts:547 (owner zip, logs section, non-owner-public read, private 404, 404-nothing-matches, traversal 400, empty 400, zip-slip backslash, ..-strip)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-025</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/pieces</td><td class="c-behavior">List pieces (builtin + global-custom + caller's user-custom, priority-merged, no hiding)</td><td class="c-source"><code>pieces-api.ts:251</code></td><td class="c-test">pieces-api.test.ts (no-auth list, auth-aware merge, same-name no-hide, source tagging)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-026</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/pieces/:name</td><td class="c-behavior">Get one piece; <code>?source=</code> selector, invalid source → 400, priority resolution</td><td class="c-source"><code>pieces-api.ts:325</code></td><td class="c-test">pieces-api.test.ts (404 unknown, ?source=builtin, priority resolution, invalid-source typo 400)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-027</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/pieces/:name</td><td class="c-behavior">Update; builtin by non-admin 403, admin can edit builtin, own user-custom 200, others' 403, lint (rules[].next COMPLETE reject, SshExec allowlist, shared_tools)</td><td class="c-source"><code>pieces-api.ts:370</code></td><td class="c-test">pieces-api.test.ts (builtin-non-admin 403, own 200, other-user 403, admin builtin, ?source typo 400, lint rules)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-028</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/pieces</td><td class="c-behavior">Create; dup-name reject, builtin-name reject, no-auth→piecesDir vs user-custom, 503 when userPiecesRootDir unset for authed non-admin/admin, returns actual source</td><td class="c-source"><code>pieces-api.ts:430</code></td><td class="c-test">pieces-api.test.ts (create, dup reject, builtin-name reject, 503 fallback hole, source return, no-auth local user-custom)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-029</code></td><td class="c-area">Bridge core</td><td class="c-feature">DELETE /api/pieces/:name</td><td class="c-behavior">Delete; builtin non-deletable 403 (even admin), own user-custom 200, ?source typo 400 no-destructive-fallback</td><td class="c-source"><code>pieces-api.ts:490</code></td><td class="c-test">pieces-api.test.ts (builtin 403, own 200, admin-cannot-delete-builtin, ?source=user-custom target, typo 400)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-030</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/config</td><td class="c-behavior">Return v2 config + ETag; omits legacy provider/flat-storage keys; admin-only mount</td><td class="c-source"><code>config-api.ts:87</code></td><td class="c-test">config-api.test.ts (v2 shape+etag, omits legacy, exposes storage.*)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Note: sensitive-masking observed in tests as worker api_key exclusion (APIC-033), not on this raw GET</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-031</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/config</td><td class="c-behavior">Update with If-Match ETag optimistic lock → **409 on stale etag**; legacy-key reject 400; force config_version=2; YAML round-trip</td><td class="c-source"><code>config-api.ts:93</code></td><td class="c-test">config-api.test.ts (round-trip, force-version, legacy-key 400s ×6, storage round-trip, **409 stale etag**)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-032</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/config/reload</td><td class="c-behavior">Reload config from file (500 on parse error)</td><td class="c-source"><code>config-api.ts:117</code></td><td class="c-test">config-api.test.ts:208 (reload from file)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">500 error path not asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-033</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/workers</td><td class="c-behavior">List workers, allowlisted fields only (**no api_key leak**), synthesized default, proxy/proxyType exposed; 401 unauth</td><td class="c-source"><code>config-api.ts:126</code></td><td class="c-test">config-api.test.ts (default synth, allowlist fields, proxy no-key-leak, 401 unauth, 200 authed)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">This is the de-facto sensitive-masking test</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-034</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/workers/:workerId/backends</td><td class="c-behavior">Fetch /v1/models from proxy worker; 404 unknown, direct→empty, 60s cache, scheme/URL validation (no apiKey leak to fetch), 502 upstream fail, 401 unauth</td><td class="c-source"><code>config-api.ts:149</code></td><td class="c-test">config-api.test.ts (404, direct empty, fetch list, cache, file://+data: reject, malformed reject, 502, 401)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-035</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/tools</td><td class="c-behavior">Runtime tool catalog (builtin+meta+MCP+ssh w/ availability flags); <code>?legacy=1</code> flat array; 401 unauth</td><td class="c-source"><code>tools-api.ts:242</code></td><td class="c-test">tools-api.test.ts (catalog, source/category tags, meta scope, ssh avail/unavail, MCP authed/offline/no-user, 401, legacy flat, placeholder, TestWorkspaceApp)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-036</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/scheduled-tasks</td><td class="c-behavior">List; visibility filter, <code>?spaceId=</code> scope</td><td class="c-source"><code>scheduled-tasks-api.ts:105</code></td><td class="c-test">scheduled-tasks-api.test.ts (list, visibility filter owner/non-owner/admin, ?spaceId scope)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-037</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/scheduled-tasks/:id</td><td class="c-behavior">Get one scheduled task</td><td class="c-source"><code>scheduled-tasks-api.ts:120</code></td><td class="c-test">scheduled-tasks-api.test.ts (covered via list/manage setup)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No dedicated GET-by-id view-gate assertion</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-038</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/scheduled-tasks</td><td class="c-behavior">Create; visibility enum+org-scope validation, spaceId forces private + edit-rights fallback to NULL, no-auth synthetic <code>local</code> owner, requires body</td><td class="c-source"><code>scheduled-tasks-api.ts:133</code></td><td class="c-test">scheduled-tasks-api.test.ts (visibility, org reject, spaceId force-private/edit-fallback/see-not-edit-fallback, no-auth local, browserSessionProfile owner check, daily, require-body)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-039</code></td><td class="c-area">Bridge core</td><td class="c-feature">PATCH /api/scheduled-tasks/:id</td><td class="c-behavior">Update; <code>canManageSchedule</code> (owner/admin/space-editor); **non-owner may manage LIFECYCLE only (pause/resume/reschedule/visibility) — content edits owner-only (P3 confused-deputy guard)**; keeps space schedule private</td><td class="c-source"><code>scheduled-tasks-api.ts:267</code></td><td class="c-test">scheduled-tasks-api.test.ts (pause/resume, owner-or-admin 404 paths, admin any, space-editor manage, lost-edit-rights, keep-private)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Confused-deputy split (lifecycle vs content) is the known P3 design caveat</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-040</code></td><td class="c-area">Bridge core</td><td class="c-feature">DELETE /api/scheduled-tasks/:id</td><td class="c-behavior">Delete; owner/admin/space-editor (non-owner 404)</td><td class="c-source"><code>scheduled-tasks-api.ts:461</code></td><td class="c-test">scheduled-tasks-api.test.ts (owner, admin any, non-owner 404, space-editor survives-creator-leaving)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-041</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/scheduled-tasks/:id/trigger</td><td class="c-behavior">Manually trigger a run; canManageSchedule gate</td><td class="c-source"><code>scheduled-tasks-api.ts:480</code></td><td class="c-test">scheduled-tasks-api.test.ts:203 (creator who lost edit rights can no longer trigger)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Happy-path trigger spawning a run not directly asserted; only the denial path</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-042</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:id/subtasks/activities</td><td class="c-behavior">Aggregate subtask activity tree; canViewTask gate (404 hidden, public allowed)</td><td class="c-source"><code>subtask-activity-api.ts:28</code></td><td class="c-test">subtask-activity-api.test.ts (nested when waiting_subtasks, 404 not-found, 404 cannot-see, public allowed)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-043</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:id/subtasks/:jobId/activity</td><td class="c-behavior">Single subtask activity; 404 subtask-not-found</td><td class="c-source"><code>subtask-activity-api.ts:68</code></td><td class="c-test">subtask-activity-api.test.ts:211 (404 subtask not found)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">view-gate denial on this specific route not separately asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-044</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:id/subtasks/:jobId/files</td><td class="c-behavior">List subtask worktree files; canViewTask, <code>isJobWithinWorkspace</code> containment 404, 404 no-worktree</td><td class="c-source"><code>subtask-files-api.ts:12</code></td><td class="c-test">subtask-files-api.test.ts:67 (grouped listing, 400 bad id, 404 missing/private/no-worktree/outside-workspace)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-045</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:id/subtasks/:jobId/files/*</td><td class="c-behavior">Serve subtask file; <code>resolve</code>+<code>startsWith(base+sep)</code> traversal → 403, 404 missing, canViewTask, org-member allowed, sendFile</td><td class="c-source"><code>subtask-files-api.ts:55</code></td><td class="c-test">subtask-files-api.test.ts:121 (serve inside worktree, 404 missing, 404 cannot-see, org-member allowed)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">The 403 traversal branch (resolved≠base path-escape) is not directly asserted on this route</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-046</code></td><td class="c-area">Bridge core</td><td class="c-feature"><code>isJobWithinWorkspace</code></td><td class="c-behavior">Separator-bounded containment (rejects /12 vs /123 prefix sibling)</td><td class="c-source"><code>local-api-helpers.ts</code></td><td class="c-test">local-api-helpers.test.ts:8 (descendants, sibling-prefix reject, null/empty)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-047</code></td><td class="c-area">Bridge core</td><td class="c-feature"><code>ensurePathWithin</code> / <code>isPathEscapeError</code></td><td class="c-behavior">Path-traversal + **symlink-escape** hardening, sibling-prefix bypass guard</td><td class="c-source"><code>local-api-helpers.ts</code></td><td class="c-test">local-api-helpers.test.ts:92,106 (inside ok, traversal reject, sibling-prefix, symlink-escape, in-root resolve)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-048</code></td><td class="c-area">Bridge core</td><td class="c-feature"><code>safeZipEntryName</code></td><td class="c-behavior">Neutralizes .., backslash, Windows drive-letter in zip entry names (zip-slip)</td><td class="c-source"><code>local-api-helpers.ts</code></td><td class="c-test">local-api-helpers.test.ts:25 (plain path, never-absolute, drive-letter→C_)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-049</code></td><td class="c-area">Bridge core</td><td class="c-feature"><code>setUntrustedFileResponseHeaders</code></td><td class="c-behavior">nosniff + CSP sandbox on untrusted file responses</td><td class="c-source"><code>local-api-helpers.ts</code></td><td class="c-test">local-api-helpers.test.ts:78</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-050</code></td><td class="c-area">Bridge core</td><td class="c-feature">office-preview module (<code>getSpreadsheetPreview</code>/<code>getPresentationPreview</code>/<code>SofficeUnavailableError</code>)</td><td class="c-behavior">Excel cells incl. formula results, PPTX via soffice, throws when soffice absent (→503)</td><td class="c-source"><code>office-preview.ts</code></td><td class="c-test">office-preview.test.ts (file detect, spreadsheet+formula, soffice-missing throws)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Module-level only; see APIC-020 for the HTTP wiring gap</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-001</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">OAuth2 Google login (<code>GET /auth/google</code>, callback)</td><td class="c-behavior">Passport Google strategy, status gate (active→app, pending→/auth/pending)</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-002</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">OAuth2 Gitea login (<code>GET /auth/gitea</code>, callback)</td><td class="c-behavior">Passport OAuth2 Gitea; pending→admin auto-promote if adminEmails</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">callback strategy logic tested indirectly; live OAuth handshake not E2E</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-003</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Local auth login (<code>POST /auth/local</code>)</td><td class="c-behavior">username/password login w/ rate limiter</td><td class="c-source"><code>auth.ts, login-rate-limit.ts</code></td><td class="c-test">auth.local.test.ts, login-rate-limit.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-004</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Local signup (<code>POST /auth/local/signup</code>)</td><td class="c-behavior">self-register → pending status awaiting approval</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.local.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-005</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Login rate limiting</td><td class="c-behavior"><code>LoginRateLimiter</code> + per-IP throttle scope</td><td class="c-source"><code>login-rate-limit.ts, auth.ts</code></td><td class="c-test">login-rate-limit.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-006</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature"><code>requireAuth</code> middleware</td><td class="c-behavior">authenticated AND status==='active' else 401/redirect</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-007</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature"><code>requireAdmin</code> middleware</td><td class="c-behavior">role==='admin' else 401/403</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">exercised via admin routers; few direct unit cases</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-008</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature"><code>GET /auth/status</code> / <code>/login</code> / <code>/pending</code> / <code>/logout</code></td><td class="c-behavior">session state pages + logout</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">logout/pending HTML paths lightly covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-009</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Session store (persistent secret, survive restart)</td><td class="c-behavior">auto-gen session secret, short-secret warning</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.session-store.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-010</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Gitea orgs fetch (<code>/api/v1/user/orgs</code>)</td><td class="c-behavior">populates session.orgIds for 'org' visibility</td><td class="c-source"><code>auth.ts (`resolveOrgIds`)</code></td><td class="c-test">auth.test.ts, local-orgs.integration.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">fetch failure/network-error path not asserted</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-011</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Change password (<code>requireAuth</code> + current-password)</td><td class="c-behavior">requires current pw, 204 on success</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">wrong-current-pw rejection coverage thin</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-012</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Admin users CRUD (<code>GET/POST/PATCH/DELETE /api/admin/users</code>)</td><td class="c-behavior">**admin-only** (guard); create/approve/disable/set-password</td><td class="c-source"><code>admin-api.ts</code></td><td class="c-test">admin-api.test.ts, admin-api.local.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-013</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Admin orgs CRUD + members (<code>/api/admin/orgs*</code>)</td><td class="c-behavior">**admin-only**; org create/edit/delete, add/remove member</td><td class="c-source"><code>admin-api.ts</code></td><td class="c-test">admin-api.test.ts, local-orgs.integration.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-014</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">**No-auth admin passthrough**</td><td class="c-behavior">when authActive=false, admin guard → passthrough</td><td class="c-source"><code>admin-api.ts, branding-api.ts</code></td><td class="c-test">admin-api.local.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">branding adminGuard no-auth path not asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-015</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Task share create (<code>POST /api/local/tasks/:id/share</code>)</td><td class="c-behavior">**owner-or-admin only** (<code>checkTaskOwnership</code>) → token</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-016</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Task unshare (<code>DELETE .../share</code>)</td><td class="c-behavior">**owner-or-admin only**</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-017</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Public shared task read (<code>GET /api/shared/:token</code>)</td><td class="c-behavior">token-only (no auth); invalid/revoked token → 404</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-018</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Shared task comments/files/raw (<code>/api/shared/:token/...</code>)</td><td class="c-behavior">token-scoped read of files/comments/raw content</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">path-escape on <code>/files/content</code> lightly tested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-019</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Shared subtask activities/files (<code>/api/shared/:token/subtasks/*</code>)</td><td class="c-behavior">token-scoped subtask containment (<code>isJobWithinWorkspace</code> prefix guard)</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">wildcard <code>/subtasks/:jobId/files/*</code> traversal not directly asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-020</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Public app-share resolve (<code>GET /api/app-share/:token</code>)</td><td class="c-behavior">token→(spaceId,appName); revoked/invalid→404</td><td class="c-source"><code>app-share-api.ts</code></td><td class="c-test">app-share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-021</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">App-share file read (content/raw/list)</td><td class="c-behavior">**server-side path containment**: outside allowed route→403; escape→403</td><td class="c-source"><code>app-share-api.ts</code></td><td class="c-test">app-share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">strong path-escape/forbidden coverage</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-022</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">App-share is GET-only</td><td class="c-behavior">no write/delete endpoints (read-only public share)</td><td class="c-source"><code>app-share-api.ts</code></td><td class="c-test">app-share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-023</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space list (<code>GET /spaces/</code>)</td><td class="c-behavior">visibility-scoped list via repo viewer gate</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-024</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space get (<code>GET /spaces/:id</code>)</td><td class="c-behavior"><code>getSpace({viewer})</code> null→404 (private/org/public gate)</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-025</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space create (<code>POST /spaces/</code>)</td><td class="c-behavior">authenticated user creates space (owner)</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-026</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space update/archive (<code>PATCH /:id</code>, <code>POST /:id/archive</code>)</td><td class="c-behavior">**canManageSpace** (owner/admin/member-owner) else 403</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.test.ts, space-api.members.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-027</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space file list/content/raw/office-preview (<code>GET /:id/files*</code>)</td><td class="c-behavior">read gated by <code>getSpace({viewer})</code> visibility; not-found→404</td><td class="c-source"><code>space-api.ts, office-preview.ts</code></td><td class="c-test">space-api.test.ts, office-preview.test.ts, local-files-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">non-member-on-public read path not asserted per-endpoint</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-028</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space file upload/delete (<code>POST /:id/files/upload</code>,<code>/delete</code>)</td><td class="c-behavior">**canEditInSpace** (owner/admin/editor); viewer→403</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.write-gates.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">strong write-gate authz coverage (20 authz assertions)</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-029</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space file mkdir/move/download-zip (<code>POST /:id/files/...</code>)</td><td class="c-behavior">**canEditInSpace**; protected top-dirs blocked</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.write-gates.test.ts, space-api.write-endpoint.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-030</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space file write (<code>POST /:id/files/write</code>)</td><td class="c-behavior">**canEditInSpace** write gate</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.write-endpoint.test.ts, space-api.write-gates.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-031</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space calendar read (<code>GET /:id/calendar</code>, <code>/calendar/day</code>)</td><td class="c-behavior">visibility-gated read</td><td class="c-source"><code>space-api.ts, cross-calendar-api.ts</code></td><td class="c-test">space-api.calendar.test.ts, cross-calendar-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-032</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space calendar event CRUD (<code>POST/PATCH/DELETE /:id/calendar/events*</code>)</td><td class="c-behavior">edit gate; multi-day mirror</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.calendar.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">non-editor write rejection coverage lighter than file gates</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-033</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space members list (<code>GET /:id/members</code>)</td><td class="c-behavior">members visible; **email masked** unless admin/owner/self-member</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.members.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">PII masking explicitly tested</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-034</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space member add/role/remove (<code>POST/PATCH/DELETE /:id/members*</code>)</td><td class="c-behavior">**canManageSpace**; owner-add blocked(400); self-removal allowed; non-member→404</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.members.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">27 authz assertions</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-035</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space invite issue/get/revoke (<code>GET/POST/DELETE /:id/invite</code>)</td><td class="c-behavior">**canManageSpace**; no-auth→404</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.invite.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-036</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Invite preview + accept (<code>GET /invite/:token</code>, <code>POST /invite/:token/accept</code>)</td><td class="c-behavior">authed user joins via valid token; invalid/expired→404; no-auth→404</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.invite.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">maxUses/expiry validity via <code>isSpaceInviteValid</code></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-037</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">App share per-space (<code>GET/POST/DELETE /:id/apps/:app/share</code>)</td><td class="c-behavior">manage-gated issue/revoke of public app link</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.app-share.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">6 authz assertions; non-manager revoke path thin</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-038</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Space tool-policy (<code>GET /:id/tool-policy</code>, <code>PUT</code>)</td><td class="c-behavior">GET visibility-gated; **PUT canManageSpace**; sensitive (Bash/SSH/browser/MCP) opt-in</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.tool-policy.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only 9 authz assertions; Bash/sensitive write-path round-trip (PR #653 regression area) under-covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-039</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Browser captcha-pool (<code>GET/DELETE /captcha-pool</code>)</td><td class="c-behavior">captcha session pool read/clear</td><td class="c-source"><code>browser-api.ts</code></td><td class="c-test">browser-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-040</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Browser task-session (<code>GET/POST /task-session/:taskId*</code>)</td><td class="c-behavior">per-task browser session acquire/release</td><td class="c-source"><code>browser-api.ts</code></td><td class="c-test">browser-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-041</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Browser profile list (<code>GET /</code> )</td><td class="c-behavior">space-aware list; **decryptableByViewer** flag (DEK owner-bound)</td><td class="c-source"><code>browser-session-api.ts</code></td><td class="c-test">browser-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">non-owner sees but cannot decrypt — asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-042</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Browser profile create (<code>POST</code>)</td><td class="c-behavior">space ctx → **canEditInSpace**; personal → owner-scoped; spaceId ignored when no spaceAccess</td><td class="c-source"><code>browser-session-api.ts</code></td><td class="c-test">browser-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-043</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Browser profile delete (<code>DELETE /:id</code>)</td><td class="c-behavior">space editor/owner OR personal owner-only</td><td class="c-source"><code>browser-session-api.ts</code></td><td class="c-test">browser-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-044</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Browser interactive login session (<code>POST /profiles/:id/login</code>)</td><td class="c-behavior">spawn noVNC session for owner; DEK owner-bound</td><td class="c-source"><code>browser-session-api.ts, novnc-proxy.ts</code></td><td class="c-test">browser-session-api.test.ts, novnc-proxy.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">noVNC proxy authz/upgrade path lightly covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-045</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Console WS attach (<code>attachConsoleWs</code> upgrade)</td><td class="c-behavior"><code>decideAccess</code>: visibility gate + canWrite (owner OR admin)</td><td class="c-source"><code>console-ws-api.ts</code></td><td class="c-test">console-ws-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">decideAccess unit-tested</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-046</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Console status router (<code>createConsoleStatusRouter</code>)</td><td class="c-behavior">requireAuth; status poll for owner/admin</td><td class="c-source"><code>console-ws-api.ts</code></td><td class="c-test">console-ws-api.test.ts, console-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-047</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Console session router (<code>createConsoleSessionRouter</code>)</td><td class="c-behavior">requireAuth task-scoped session resolution</td><td class="c-source"><code>console-ws-api.ts</code></td><td class="c-test">console-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-048</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Console admin (<code>GET /ssh/console-sessions</code>, <code>POST .../:taskId/kill</code>)</td><td class="c-behavior">**requireAdmin** list/kill console sessions</td><td class="c-source"><code>console-admin-api.ts</code></td><td class="c-test">console-admin-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-049</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Gateway admin CRUD (<code>POST/PATCH/DELETE /:id</code>, rotate/revoke keys)</td><td class="c-behavior">**requireAdmin** backend + API-key mgmt</td><td class="c-source"><code>admin-gateway-api.ts</code></td><td class="c-test">admin-gateway-api.test.ts, .budget-rate.test.ts, .metric-labels.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">18 authz assertions; budget/rate/labels covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-050</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Gateway usage (<code>GET /:id/usage</code>)</td><td class="c-behavior">**requireAdmin** per-backend usage</td><td class="c-source"><code>admin-gateway-api.ts</code></td><td class="c-test">admin-gateway-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-051</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Gateway status (<code>GET /</code> status)</td><td class="c-behavior">non-admin status snapshot</td><td class="c-source"><code>admin-gateway-status-api.ts</code></td><td class="c-test">admin-gateway-status-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-052</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Gateway mount/proxy (<code>mountGateway</code>, classifyGatewayPath)</td><td class="c-behavior">LLM gateway request routing/classification middleware</td><td class="c-source"><code>gateway-mount.ts</code></td><td class="c-test">gateway-mount.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">path classification + config-equiv heavily tested; auth on proxied LLM calls (Bearer/key) not asserted in this file</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-053</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Notifications VAPID key (<code>GET /api/notifications/vapid-public-key</code>)</td><td class="c-behavior">requireAuth</td><td class="c-source"><code>notifications-api.ts</code></td><td class="c-test">notifications-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-054</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Notifications subscriptions (<code>GET/POST/DELETE /subscriptions</code>)</td><td class="c-behavior">requireAuth; web-push subscribe/unsubscribe</td><td class="c-source"><code>notifications-api.ts</code></td><td class="c-test">notifications-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-055</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Notifications preferences (<code>GET/PUT /preferences</code>)</td><td class="c-behavior">requireAuth pref read/write</td><td class="c-source"><code>notifications-api.ts</code></td><td class="c-test">notifications-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-056</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Notifications test/send (<code>POST</code> send endpoints)</td><td class="c-behavior">requireAuth push trigger</td><td class="c-source"><code>notifications-api.ts</code></td><td class="c-test">notifications-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">send/test push delivery path lightly asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-057</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Branding read (<code>GET /api/branding</code>)</td><td class="c-behavior">public branding config read</td><td class="c-source"><code>branding-api.ts</code></td><td class="c-test">branding-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-058</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Branding static serve (<code>/branding/*</code>)</td><td class="c-behavior">static asset serving</td><td class="c-source"><code>branding-api.ts</code></td><td class="c-test">branding-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">static traversal not asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-059</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Branding upload/delete (<code>POST/DELETE /api/branding/upload</code>)</td><td class="c-behavior">**adminGuard** (passthrough in no-auth)</td><td class="c-source"><code>branding-api.ts</code></td><td class="c-test">branding-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-060</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Security headers / HSTS opt-in</td><td class="c-behavior">HSTS default off (<code>max-age=0</code> clear), opt-in enable; 180d max-age</td><td class="c-source"><code>security-headers.ts</code></td><td class="c-test">security-headers.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">15 expects (no authz; correct)</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-061</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Server TLS listener</td><td class="c-behavior">HTTPS listener creation; cert handling</td><td class="c-source"><code>server-tls-listener.ts</code></td><td class="c-test">server-tls-listener.test.ts, server.tls.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-062</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Dashboard API (<code>createDashboardApi</code>)</td><td class="c-behavior">worker/node (GPU) status tabs</td><td class="c-source"><code>dashboard-api.ts</code></td><td class="c-test">dashboard-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">no explicit auth guard visible in router; visibility of dashboard data not gated/asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-063</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Dashboard workers</td><td class="c-behavior">worker status aggregation</td><td class="c-source"><code>dashboard-workers.ts</code></td><td class="c-test">dashboard-workers.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-064</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature"><code>buildVisibilityWhere</code> / <code>buildSpaceVisibilityWhere</code></td><td class="c-behavior">SQL WHERE for private/org/public</td><td class="c-source"><code>visibility.ts</code></td><td class="c-test">visibility.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">53 expects</td></tr>
|
||
<tr data-cov="FULL" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-065</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature"><code>canManageSpace</code> / <code>canEditInSpace</code> / <code>canEditEntity</code> / <code>canUserSeeTask</code></td><td class="c-behavior">role/owner/admin authz predicates</td><td class="c-source"><code>visibility.ts</code></td><td class="c-test">visibility.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">central predicates well unit-tested</td></tr>
|
||
<tr data-cov="NONE" data-area="Bridge auth & spaces"><td class="c-id"><code>APIS-066</code></td><td class="c-area">Bridge auth & spaces</td><td class="c-feature">Cross-space credential transfer (G3/#006)</td><td class="c-behavior">copy MCP/SSH creds between spaces, per-user token non-copy</td><td class="c-source"><code>(PR #642 — NOT in main)</code></td><td class="c-test">(n/a in main)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">**Not merged to main** — no endpoint present; out of scope for current tree</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>SVC-001</code></td><td class="c-area">Services & DB</td><td class="c-feature">Worker poll/claim loop</td><td class="c-behavior"><code>processNext</code>/<code>acquireJobOrRequeue</code> claim a queued job or requeue when issue lock held</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts ("requeues a claimed job when the issue lock is already held")</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">poll cadence, <code>pokePoll</code>, empty-queue path not directly asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-002</code></td><td class="c-area">Services & DB</td><td class="c-feature">Role / task_class matching</td><td class="c-behavior"><code>canClaimRole</code>/<code>supportsRole</code>/<code>getSupportedRoles</code> gate which jobs a worker claims</td><td class="c-source"><code>src/worker.ts, src/db/repository.ts (claimNextJob)</code></td><td class="c-test">repository.test.ts ("claims only jobs matching a healthy worker role set"; "does not let unhealthy workers claim queued jobs")</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">role-set matching + healthy filter covered at repo layer</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>SVC-003</code></td><td class="c-area">Services & DB</td><td class="c-feature">Title-only / reflection role filters</td><td class="c-behavior"><code>roles:['title']</code> skips agent jobs; <code>roles:['reflection']</code> runs reflection handler</td><td class="c-source"><code>src/worker.ts (handleReflectionJob, supportsRole)</code></td><td class="c-test">worker.reflection-dispatch.test.ts; worker.reflection-enqueue.test.ts (16)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">reflection enqueue + dispatch well covered; title-only worker skip path has no direct test</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>SVC-004</code></td><td class="c-area">Services & DB</td><td class="c-feature">Job lifecycle transitions</td><td class="c-behavior">queued→dispatching→running→succeeded/failed/waiting_human/waiting_subtasks via updateJob</td><td class="c-source"><code>src/worker.ts, src/db/repository.ts</code></td><td class="c-test">worker.test.ts; repository.test.ts; spaces-task-ops (cancel flips running→cancelled)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">terminal mapping covered (metrics test); waiting_human/waiting_subtasks transitions only indirectly exercised</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-005</code></td><td class="c-area">Services & DB</td><td class="c-feature">Retry / maxAttempts policy</td><td class="c-behavior">terminal vs transient classification; one recovery retry for max_iterations; give up on repeat</td><td class="c-source"><code>src/worker.ts (executeJob retry logic)</code></td><td class="c-test">worker.test.ts (5 retry cases incl. self-abort, max_iterations 1st/2nd hit, llm_error budget)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">strong branch coverage of terminal vs transient + attempt budget</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-006</code></td><td class="c-area">Services & DB</td><td class="c-feature">Retry handoff summary</td><td class="c-behavior"><code>buildRetryHandoffSummary</code>/<code>writeRetryHandoffSummary</code> carry lessons+diagnostics into retry</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts ("writes retry handoff summary"; "builds retry handoff summary from diagnostics and lessons")</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-007</code></td><td class="c-area">Services & DB</td><td class="c-feature">Deadline / hard-abort guard</td><td class="c-behavior"><code>shouldDeadlineAbort</code> aborts past deadline; job-guard timer force-aborts</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.job-guard.test.ts (8: before/after deadline, disabled=0, no re-abort, cancel mid-run, hard deadline)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-008</code></td><td class="c-area">Services & DB</td><td class="c-feature">Cancellation check</td><td class="c-behavior"><code>cancelCheck</code> returns true when DB job status is cancelled</td><td class="c-source"><code>src/worker.ts, repository.requestJobCancel</code></td><td class="c-test">worker.test.ts ("cancelCheck returns true when cancelled"); repository.test.ts (requestJobCancel 3 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">running→cancelled, queued no-op, unknown id all covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-009</code></td><td class="c-area">Services & DB</td><td class="c-feature">max_concurrency slot scheduling</td><td class="c-behavior">runs up to N concurrent, refills freed slot, single-flight when unset</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.concurrency.test.ts (5: concurrent, single-flight, waitForCompletion, decrement-on-throw)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-010</code></td><td class="c-area">Services & DB</td><td class="c-feature">Initialize / health probe</td><td class="c-behavior"><code>initialize</code> probes /api/tags + /v1/models, forwards Bearer, stays healthy on llama-server compat</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (3 initialize cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-011</code></td><td class="c-area">Services & DB</td><td class="c-feature">Unhealthy on LLM conn error</td><td class="c-behavior">requeues jobs + marks worker unhealthy on connection errors</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts ("requeues jobs and marks the worker unhealthy on LLM connection errors")</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-012</code></td><td class="c-area">Services & DB</td><td class="c-feature">resolveToolSpaceId (per-space MCP/SSH)</td><td class="c-behavior">NULL space + owner → personal space; keeps explicit; fails CLOSED on resolver error</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (5 resolveToolSpaceId cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">fail-closed sentinel asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-013</code></td><td class="c-area">Services & DB</td><td class="c-feature">Piece resolution for null-owner job</td><td class="c-behavior">no-auth job loads piece from data/users/local/pieces consistently</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (2 null-ownerId piece cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-014</code></td><td class="c-area">Services & DB</td><td class="c-feature">prepareJobWorkspace resolution</td><td class="c-behavior">uses stored workspacePath or falls back to legacy local/{taskId} + backfills</td><td class="c-source"><code>src/worker.ts, src/spaces/workspace-resolver.ts</code></td><td class="c-test">worker.test.ts (2 cases); workspace-resolver.test.ts (6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">persistent/ephemeral/personal + backfill covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-015</code></td><td class="c-area">Services & DB</td><td class="c-feature">maybeEnqueueReflection budget gate</td><td class="c-behavior">enqueue/skip by enabled flag, daily UTC budget cap, worker-required, per-user keying</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.reflection-enqueue.test.ts (16)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">strong: cap=0, 25h window, no-recursion, independent budgets</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>SVC-016</code></td><td class="c-area">Services & DB</td><td class="c-feature">resolveModel / role→model mapping</td><td class="c-behavior">selects backend model for the job role</td><td class="c-source"><code>src/worker.ts (resolveModel)</code></td><td class="c-test">worker/sticky-backend.test.ts (resolver follow-current); scheduling.test.ts (normalizeJobRole/parseUiRole)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">role parsing well covered; resolveModel's own fallback branches not directly tested</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-017</code></td><td class="c-area">Services & DB</td><td class="c-feature">Sticky backend resolver</td><td class="c-behavior">persists first backend, advances on CHANGE, retries on persist failure</td><td class="c-source"><code>src/worker.ts (createStickyBackendResolver)</code></td><td class="c-test">worker/sticky-backend.test.ts (6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-018</code></td><td class="c-area">Services & DB</td><td class="c-feature">Idle-routing yield</td><td class="c-behavior"><code>pickIdlerIndex</code> yields to strictly-idler serving sibling, ignores unhealthy/non-serving</td><td class="c-source"><code>src/worker.ts (findIdlerCompetitor)</code></td><td class="c-test">worker/idle-routing.test.ts (9)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="NONE" data-area="Services & DB"><td class="c-id"><code>SVC-019</code></td><td class="c-area">Services & DB</td><td class="c-feature">answerSubtaskAsk / subtask resume</td><td class="c-behavior">answers a subtask ASK; parent requeue when all subtasks done</td><td class="c-source"><code>src/worker.ts, repository.requeueParentJobIfAllSubtasksDone</code></td><td class="c-test">(none direct in scope)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">repo helper <code>requeueParentJobIfAllSubtasksDone</code> + worker <code>answerSubtaskAsk</code> untested</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-020</code></td><td class="c-area">Services & DB</td><td class="c-feature">Worker shutdown hooks</td><td class="c-behavior">installs + drains shutdown hooks in order, tolerates throwing hook</td><td class="c-source"><code>src/worker.ts / worker-bootstrap</code></td><td class="c-test">worker-bootstrap.test.ts (4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-021</code></td><td class="c-area">Services & DB</td><td class="c-feature">Worker metrics</td><td class="c-behavior">counters increment on labelled inc; terminal-status mapping complete</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.metrics.test.ts (3)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-022</code></td><td class="c-area">Services & DB</td><td class="c-feature">WorkerManager differential rebuild</td><td class="c-behavior">rebuild on config change: keep busy/unchanged worker, retire changed/removed without requeue, prune retired</td><td class="c-source"><code>src/worker-manager.ts</code></td><td class="c-test">worker-manager.test.ts (8)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">live-job protection + def-signature diff covered</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-023</code></td><td class="c-area">Services & DB</td><td class="c-feature">WorkerManager shutdown requeue</td><td class="c-behavior">requeues running jobs only on shutdown when worker fails to drain; not on clean shutdown</td><td class="c-source"><code>src/worker-manager.ts</code></td><td class="c-test">worker-manager.test.ts (2 shutdown cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="NONE" data-area="Services & DB"><td class="c-id"><code>SVC-024</code></td><td class="c-area">Services & DB</td><td class="c-feature">Worker dep injection setters</td><td class="c-behavior">setMcpDeps/setWorkerMetrics/setSkillCatalog/setPushService wire shared deps</td><td class="c-source"><code>src/worker-manager.ts</code></td><td class="c-test">(none direct)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">low-risk wiring; uncovered</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-025</code></td><td class="c-area">Services & DB</td><td class="c-feature">Scheduler cron conversion</td><td class="c-behavior"><code>convertToCron</code> maps daily/weekly/monthly→cron; passes cron through; once special-cased</td><td class="c-source"><code>src/scheduler.ts</code></td><td class="c-test">scheduler.test.ts (convertToCron 5 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-026</code></td><td class="c-area">Services & DB</td><td class="c-feature">calcNextRun / toSqliteDatetime</td><td class="c-behavior">next-run from cron; SQLite-compatible datetime; once→null</td><td class="c-source"><code>src/scheduler.ts</code></td><td class="c-test">scheduler.test.ts (calcNextRun 3 + toSqliteDatetime 2)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-027</code></td><td class="c-area">Services & DB</td><td class="c-feature">Scheduler skip-on-in-progress</td><td class="c-behavior">skips when prior job running/waiting_subtasks; starts new on waiting_human</td><td class="c-source"><code>src/scheduler.ts (IN_PROGRESS_STATUSES)</code></td><td class="c-test">scheduler.test.ts (4 skip cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">waiting_human-no-longer-blocks asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-028</code></td><td class="c-area">Services & DB</td><td class="c-feature">Schedule ownership inheritance</td><td class="c-behavior">propagates ownerId/visibility/scopeOrg + space_id to spawned local_task; NULL owner for system</td><td class="c-source"><code>src/scheduler.ts (executeScheduledTask)</code></td><td class="c-test">scheduler.test.ts (multiple ownership + space-bound vs personal cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-029</code></td><td class="c-area">Services & DB</td><td class="c-feature">Scheduled pieceName / classifier</td><td class="c-behavior">falls back to classifier when pieceName unset; preserves explicit pieceName</td><td class="c-source"><code>src/scheduler.ts</code></td><td class="c-test">scheduler.test.ts (pieceName cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>SVC-030</code></td><td class="c-area">Services & DB</td><td class="c-feature">Scheduled script (task_kind=script)</td><td class="c-behavior">runs browser-macro/script, writes logs to runtimeDir + output to space tree; failure paths; gate+allowlist</td><td class="c-source"><code>src/scheduler.ts (executeScriptScheduledTask)</code></td><td class="c-test">scheduler.test.ts (script cases incl. gate disabled, not-in-allowlist, audit row, missing script, null scriptName guard)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="NONE" data-area="Services & DB"><td class="c-id"><code>SVC-031</code></td><td class="c-area">Services & DB</td><td class="c-feature">executeById manual run</td><td class="c-behavior">runs a scheduled task on demand</td><td class="c-source"><code>src/scheduler.ts</code></td><td class="c-test">(none direct)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">manual-trigger path untested</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-001</code></td><td class="c-area">Services & DB</td><td class="c-feature">transformKeys snake↔camel</td><td class="c-behavior">toCamel/toSnake/toSnakeKeys round-trip incl. nested/arrays</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config.test.ts (toSnakeKeys 5 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-002</code></td><td class="c-area">Services & DB</td><td class="c-feature">loadConfig provider.retry</td><td class="c-behavior">loads from YAML or default; deprecated profiles→roles shim; proxy/proxyType defaults</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config.test.ts (provider.retry + roles/proxy cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-003</code></td><td class="c-area">Services & DB</td><td class="c-feature">validateConfig</td><td class="c-behavior">rejects bad concurrency/maxMovements/ask/maxStreamMinutes/maxJobMinutes/maxDepth/retry/worker fields/proxy type</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config.test.ts (~35 validateConfig cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">exhaustive boundary coverage</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>CFG-004</code></td><td class="c-area">Services & DB</td><td class="c-feature">Env overrides (config.ts)</td><td class="c-behavior">OLLAMA_BASE_URL/OLLAMA_MODEL/CONCURRENCY/WORKTREE_DIR/AAO_* override loaded config</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config.audit-regression.test.ts (OLLAMA_BASE_URL targets executed worker only, leaves persisted llm + extra workers)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only OLLAMA_BASE_URL asserted; CONCURRENCY/WORKTREE_DIR/OLLAMA_MODEL/AAO_* overrides untested</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-005</code></td><td class="c-area">Services & DB</td><td class="c-feature">normalizeConfig version handling</td><td class="c-behavior">v2 pass-through; missing→v1; unsupported version throws; null input→empty</td><td class="c-source"><code>src/config-normalize.ts</code></td><td class="c-test">config-normalize.test.ts (version handling, ~6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-006</code></td><td class="c-area">Services & DB</td><td class="c-feature">normalizeConfig v1→v2 llm</td><td class="c-behavior">proxy→connection_type, worker.model inheritance, single default worker, profiles→roles, retry/metrics map</td><td class="c-source"><code>src/config-normalize.ts</code></td><td class="c-test">config-normalize.test.ts (v1→v2 block, ~10)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-007</code></td><td class="c-area">Services & DB</td><td class="c-feature">config-normalizer storage mirror</td><td class="c-behavior">top-level flat keys ↔ storage.* both directions; storage.* precedence (#369); worktreeDir survival</td><td class="c-source"><code>src/config-normalize.ts, src/config.ts</code></td><td class="c-test">config-normalize.test.ts (storage migration + backwards-compat, ~8); config.audit-regression.test.ts (trash_retention_days takes effect)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">precedence trap (default-merge-before-normalize) regression-guarded</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-008</code></td><td class="c-area">Services & DB</td><td class="c-feature">Env-ref preservation</td><td class="c-behavior">${VAR} / env: prefix preserved verbatim through normalize</td><td class="c-source"><code>src/config-normalize.ts</code></td><td class="c-test">config-normalize.test.ts (3 env-ref cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-009</code></td><td class="c-area">Services & DB</td><td class="c-feature">browser.display_mode migration</td><td class="c-behavior">legacy captchaSolve=novnc → displayMode=novnc, key removed</td><td class="c-source"><code>src/config-normalize.ts (migrateBrowserDisplayMode)</code></td><td class="c-test">config-normalize.test.ts (display_mode case)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-010</code></td><td class="c-area">Services & DB</td><td class="c-feature">normalize fixtures (real configs)</td><td class="c-behavior">v1-single-ollama / multi-worker-proxy / gateway-with-keys / mcp-and-ssh normalize correctly</td><td class="c-source"><code>src/config-normalize.ts</code></td><td class="c-test">config-normalize.test.ts (4 fixture cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>CFG-011</code></td><td class="c-area">Services & DB</td><td class="c-feature">Auth config loading</td><td class="c-behavior">parses auth section snake_case + primary_provider; undefined when absent</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config-auth.test.ts (4)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">parsing covered; provider validation/exclusivity not asserted here</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-012</code></td><td class="c-area">Services & DB</td><td class="c-feature">ConfigManager read + ETag</td><td class="c-behavior">getConfig/getConfigForApi; etag from mtime; reloadFromFile</td><td class="c-source"><code>src/config-manager.ts</code></td><td class="c-test">config-manager.test.ts (load, etag, reload, masked-for-API)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-013</code></td><td class="c-area">Services & DB</td><td class="c-feature">ConfigManager write + CAS</td><td class="c-behavior">updateConfig writes YAML, rejects stale etag, creates file on fresh install, rejects unparseable</td><td class="c-source"><code>src/config-manager.ts</code></td><td class="c-test">config-manager.test.ts (update, stale-etag reject, fresh-install create, invalid YAML reject)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-014</code></td><td class="c-area">Services & DB</td><td class="c-feature">ConfigManager masking preservation</td><td class="c-behavior">masks apiKey (<code>********</code>); preserves masked field on update; matches workers/backends by id; drops mask when no prior key</td><td class="c-source"><code>src/config-manager.ts (mergeWithMaskPreservation)</code></td><td class="c-test">config-manager.test.ts (mask + preserve by-id for llm.workers + gateway.backends, drop-mask path)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-015</code></td><td class="c-area">Services & DB</td><td class="c-feature">ConfigManager change events</td><td class="c-behavior">emits config-changed on update</td><td class="c-source"><code>src/config-manager.ts</code></td><td class="c-test">config-manager.test.ts ("emits config-changed on update")</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="NONE" data-area="Services & DB"><td class="c-id"><code>CFG-016</code></td><td class="c-area">Services & DB</td><td class="c-feature">config-manager env overrides</td><td class="c-behavior">OLLAMA_*/WORKTREE_DIR/CONCURRENCY/DB_PATH applied at runtime read</td><td class="c-source"><code>src/config-manager.ts</code></td><td class="c-test">(none direct in config-manager.test.ts)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">DB_PATH + runtime env override path untested at config-manager layer</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-017</code></td><td class="c-area">Services & DB</td><td class="c-feature">Server TLS / listen-port config</td><td class="c-behavior">mergeServerConfig TLS defaults, http_redirect port normalize/collision, resolveListenPort + PORT env</td><td class="c-source"><code>src/server/config.ts</code></td><td class="c-test">server/config.test.ts (24)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">(PORT/LOG_LEVEL env live here, not config.ts)</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>CFG-018</code></td><td class="c-area">Services & DB</td><td class="c-feature">migrate-config CLI</td><td class="c-behavior">--help/unknown flag/missing file/dry-run/in-place+backup/already-v2/version-99</td><td class="c-source"><code>src/scripts/migrate-config.ts</code></td><td class="c-test">scripts/migrate-config.test.ts (8)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-001</code></td><td class="c-area">Services & DB</td><td class="c-feature">createJob / getJob round-trip</td><td class="c-behavior">persists job incl. space_id, runtime_dir, continued_from; subtask inherits parent space_id</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">spaces-resolution.test.ts; runtime-dir.test.ts; migrate.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>DB-002</code></td><td class="c-area">Services & DB</td><td class="c-feature">claimNextJob / retry / peek</td><td class="c-behavior">role+health-aware claim; claimNextRetryJob; peekNextClaimable</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (worker-aware scheduling 2)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">claimNextRetryJob + peekNextClaimable not individually asserted</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>DB-003</code></td><td class="c-area">Services & DB</td><td class="c-feature">requeueRunningJobs / recovery</td><td class="c-behavior">requeueRunningJobs, recoverOrphanedJobs, recoverStuckRunningJobs, resumeMcpWaitingJobs</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">(worker-manager shutdown exercises requeue indirectly)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">recoverOrphaned/recoverStuck/resumeMcp* have no direct unit test</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-004</code></td><td class="c-area">Services & DB</td><td class="c-feature">requestJobCancel</td><td class="c-behavior">running→cancelled; queued no-op; unknown id false</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (3)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-005</code></td><td class="c-area">Services & DB</td><td class="c-feature">createLocalTask / getLocalTask</td><td class="c-behavior">persists owner/visibility/space_id/runtime_dir; reads back</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts; repository-auth.test.ts; spaces-resolution.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-006</code></td><td class="c-area">Services & DB</td><td class="c-feature">listLocalTasks + latestJob/subtasks</td><td class="c-behavior">joins latest job, subtask info, ownerName/org name; ownerId filter</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (listLocalTasks suite); repository-auth.test.ts (ownerId filter)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-007</code></td><td class="c-area">Services & DB</td><td class="c-feature">deleteLocalTask whitelist</td><td class="c-behavior">deletes task+jobs; refuses when running; whitelists only ephemeral/local per-task dirs; protects shared space tree</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (4); spaces-task-ops.test.ts (~9 delete cases incl. symlink/trailing-slash/null-space)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">data-loss regression strongly guarded</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>DB-008</code></td><td class="c-area">Services & DB</td><td class="c-feature">local_task_comments</td><td class="c-behavior">addLocalTaskComment, listLocalTaskComments, getUninjected/markInjected, getLatestResultComment, attachments</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (Feedback/Share); migrate.test.ts (attachments column)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">comment inject/uninject + getLatestResultComment lifecycle not directly asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-009</code></td><td class="c-area">Services & DB</td><td class="c-feature">buildVisibilityWhere</td><td class="c-behavior">admin 1=1; owner/public; same-org branch; spaceColumn membership+owner OR; admin ignores spaceColumn</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">bridge/visibility.test.ts (33 incl. param-order, byte-identical back-compat)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">param ordering + admin-ignores-membership asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-010</code></td><td class="c-area">Services & DB</td><td class="c-feature">canManageSpace / canEditInSpace</td><td class="c-behavior">role-gated write checks (admin/owner/member roles, viewer read-only)</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">bridge/visibility.test.ts (canManage + canEdit suites)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-011</code></td><td class="c-area">Services & DB</td><td class="c-feature">Spaces CRUD</td><td class="c-behavior">createSpace/getSpace/listSpaces (visibility-filtered)/updateSpace/archiveSpace</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">spaces-repository.test.ts (12)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-012</code></td><td class="c-area">Services & DB</td><td class="c-feature">ensurePersonalSpace invariant</td><td class="c-behavior">exactly one private personal space/user, idempotent; never hard-deletable</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">spaces-repository.test.ts (ensurePersonalSpace + hardDelete invariant)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-013</code></td><td class="c-area">Services & DB</td><td class="c-feature">hardDeleteSpace crypto-shred</td><td class="c-behavior">shreds case space DEK on delete; never personal</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">spaces-repository.test.ts (crypto-shred case)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-014</code></td><td class="c-area">Services & DB</td><td class="c-feature">Space members CRUD + visibility</td><td class="c-behavior">add/list/getRole/updateRole/remove; membership-based read; non-member leak prevention; revocation</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.shared-space.test.ts (23)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">collaboration + leak + revocation all asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-015</code></td><td class="c-area">Services & DB</td><td class="c-feature">Personal space owner-only visibility</td><td class="c-behavior">even admin cannot see another user's personal space (list+get); no-auth synthetic owner path</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.personal-space-visibility.test.ts (8)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-016</code></td><td class="c-area">Services & DB</td><td class="c-feature">Per-space resource isolation</td><td class="c-behavior">space-A task sees only space-A MCP/SSH (not space-B/global); personal sees no case-space resources</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.spaces-resource-isolation.test.ts (4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-017</code></td><td class="c-area">Services & DB</td><td class="c-feature">Space tool_policy persistence</td><td class="c-behavior">default null; round-trips JSON; clear via null</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.tool-policy.test.ts (5)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-018</code></td><td class="c-area">Services & DB</td><td class="c-feature">Space invites</td><td class="c-behavior">create (no-expiry default), regenerate replaces, expiry validity, revoke, FK cascade</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.space-invites.test.ts (7)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>DB-019</code></td><td class="c-area">Services & DB</td><td class="c-feature">App share links</td><td class="c-behavior">create/get/revoke/resolveAppShareToken</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.app-share.test.ts (out-of-listed but present)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">present file not fully read; assume happy-path coverage</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-020</code></td><td class="c-area">Services & DB</td><td class="c-feature">Tool requests + grant overlay</td><td class="c-behavior">record/list newest-first, de-dup by job/task/piece, idempotent decide, granted-tools round-trip, per-piece aggregate</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.tool-requests.test.ts (7)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-021</code></td><td class="c-area">Services & DB</td><td class="c-feature">User CRUD + OAuth link</td><td class="c-behavior">createUser/getById/getByEmail/findOrCreateUserByOAuth (link by email)/listUsers/updateUser/deleteUser cascade</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository-auth.test.ts (17)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-022</code></td><td class="c-area">Services & DB</td><td class="c-feature">Local-auth credentials</td><td class="c-behavior">setLocalPassword/verify round-trip, per-user salt, createLocalUser email-takeover reject, upsertLocalSystemAdmin idempotent, delete refuses local user</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository-local-auth.test.ts (12)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-023</code></td><td class="c-area">Services & DB</td><td class="c-feature">Local orgs</td><td class="c-behavior">createLocalOrg (lorg: id), rename, member add/remove idempotent, listUserLocalOrgs, delete cascade + downgrade scoped resources</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository-local-orgs.test.ts (10)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-024</code></td><td class="c-area">Services & DB</td><td class="c-feature">ensureLocalUser DEK fix</td><td class="c-behavior">creates per-user row so DEK/SSH create succeeds; idempotent</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">ensure-local-user.test.ts (4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-025</code></td><td class="c-area">Services & DB</td><td class="c-feature">ScheduledTasks CRUD</td><td class="c-behavior">create/get/list/getDue/update/delete; space_id persisted (not dead-wired)</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (ScheduledTasks 5); spaces-resolution.test.ts (space_id round-trip)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-026</code></td><td class="c-area">Services & DB</td><td class="c-feature">Share token (local task)</td><td class="c-behavior">shareLocalTask token, idempotent, unshare clears, getByShareToken null on unknown/after-unshare</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (Share feature 6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>DB-027</code></td><td class="c-area">Services & DB</td><td class="c-feature">addAuditLog</td><td class="c-behavior">single audit_log insert helper</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">scheduler.test.ts (user_script_run audit row asserted via path)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">addAuditLog itself not unit-tested; only one consumer asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-028</code></td><td class="c-area">Services & DB</td><td class="c-feature">Gateway virtual keys</td><td class="c-behavior">find/list/revoke/delete/touchLastUsed + usage</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.gateway-keys.test.ts; gateway-usage.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">(gateway-adjacent; in repo file)</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-029</code></td><td class="c-area">Services & DB</td><td class="c-feature">LLM usage aggregation</td><td class="c-behavior">incrementLlmUsage (daily) + hourly + queryLlmUsageDaily + org map</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.llm-usage.test.ts; llm-usage-hourly.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>DB-030</code></td><td class="c-area">Services & DB</td><td class="c-feature">Push subscriptions / prefs</td><td class="c-behavior">upsert/list/get/delete/markSuccess/markFailure; notification prefs</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">(push-service.test.ts at higher layer)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">repo-level push CRUD not directly asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-031</code></td><td class="c-area">Services & DB</td><td class="c-feature">Calendar events</td><td class="c-behavior">getCalendarEvent/deleteCalendarEvent</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.calendar.test.ts; cross-calendar.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="NONE" data-area="Services & DB"><td class="c-id"><code>DB-032</code></td><td class="c-area">Services & DB</td><td class="c-feature">resumeToolRequestJob</td><td class="c-behavior">resumes a job waiting on a tool grant</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">(none direct)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">resume-after-grant path untested at repo layer</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-033</code></td><td class="c-area">Services & DB</td><td class="c-feature">Schema migrations (idempotent ALTER)</td><td class="c-behavior">runMigrations adds owner_id/continued_from/last_backend_id/attachments; idempotent</td><td class="c-source"><code>src/db/migrate.ts, schema.sql</code></td><td class="c-test">migrate.test.ts (16); migrate.space-id/ssh-space/mcp-space/reflection-columns/gateway-2b/privatize/rename tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">each major migration has its own test file</td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-034</code></td><td class="c-area">Services & DB</td><td class="c-feature">MCP table migrations</td><td class="c-behavior">creates mcp_servers/tokens/tools/oauth_pending idempotent; BLOB cols; auth_kind default; owner index</td><td class="c-source"><code>src/db/migrate.ts</code></td><td class="c-test">migrate.test.ts (MCP suite); migrate.mcp-auth-columns.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-035</code></td><td class="c-area">Services & DB</td><td class="c-feature">Space backfill migrations</td><td class="c-behavior">backfillMcpServer/Ssh/BrowserSession space_ids; privatize space rows; rename personal title</td><td class="c-source"><code>src/db/migrate.ts</code></td><td class="c-test">migrate.mcp-space/ssh-space/privatize-space-rows/rename-personal-space-title.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-036</code></td><td class="c-area">Services & DB</td><td class="c-feature">resolveTaskWorkspaceDir / dirs</td><td class="c-behavior">persistent→space files tree; ephemeral→throwaway; personal-space; ensureWorkspaceDirs creates subdirs</td><td class="c-source"><code>src/spaces/workspace-resolver.ts</code></td><td class="c-test">workspace-resolver.test.ts (6); spaces-resolution.test.ts (resolveTaskFolderContext)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
|
||
<tr data-cov="FULL" data-area="Services & DB"><td class="c-id"><code>DB-037</code></td><td class="c-area">Services & DB</td><td class="c-feature">Credential DEK encryption</td><td class="c-behavior">encrypt under SPACE DEK for space profile, OWNER DEK for personal; migration fallback; cross-DEK isolation; throws with no material</td><td class="c-source"><code>src/crypto/profile-dek.ts</code></td><td class="c-test">crypto/profile-dek.test.ts (6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">space-vs-owner DEK separation + non-decryptability asserted</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="Services & DB"><td class="c-id"><code>DB-038</code></td><td class="c-area">Services & DB</td><td class="c-feature">Branding storage</td><td class="c-behavior">branding config/assets read-write (gitignored data/branding)</td><td class="c-source"><code>src/bridge/branding-api.ts</code></td><td class="c-test">bridge/branding-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">API-layer test exists; storage-layer branch coverage unverified</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-001</code></td><td class="c-area">UI</td><td class="c-feature">Calendar month grid + bars</td><td class="c-behavior">month grid, week split, range/time fmt, multi-day bar layout</td><td class="c-source"><code>`lib/calendar.ts`</code></td><td class="c-test"><code>lib/calendar.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-002</code></td><td class="c-area">UI</td><td class="c-feature">Command palette</td><td class="c-behavior">build/filter/group commands, hotkey gate</td><td class="c-source"><code>`lib/command-palette.ts`</code></td><td class="c-test"><code>lib/command-palette.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-003</code></td><td class="c-area">UI</td><td class="c-feature">Cron → form state</td><td class="c-behavior">parse cron string into schedule fields</td><td class="c-source"><code>`lib/cronForm.ts`</code></td><td class="c-test"><code>lib/cronForm.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only <code>cronToFormState</code> exported/tested; reverse (form→cron) lives in <code>ScheduleFields.tsx</code> untested</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-004</code></td><td class="c-area">UI</td><td class="c-feature">File move resolution</td><td class="c-behavior">basename/parentDir, resolve drag-move plan (conflicts)</td><td class="c-source"><code>`lib/fileMove.ts`</code></td><td class="c-test"><code>lib/fileMove.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-005</code></td><td class="c-area">UI</td><td class="c-feature">File type categorization</td><td class="c-behavior">split name, category, color class</td><td class="c-source"><code>`lib/fileType.ts`</code></td><td class="c-test"><code>lib/fileType.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-006</code></td><td class="c-area">UI</td><td class="c-feature">File view sort/format</td><td class="c-behavior">sort entries, toggle sort, fmt timestamp/size</td><td class="c-source"><code>`lib/fileView.ts`</code></td><td class="c-test"><code>lib/fileView.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-007</code></td><td class="c-area">UI</td><td class="c-feature">Help docs frontmatter/render</td><td class="c-behavior">split/validate frontmatter, parse, slugify, render HTML, filter</td><td class="c-source"><code>`lib/help.ts`</code></td><td class="c-test"><code>lib/help.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-008</code></td><td class="c-area">UI</td><td class="c-feature">Live-workspace auto-focus</td><td class="c-behavior">decide auto-focus on new live task</td><td class="c-source"><code>`lib/live-workspace.ts`</code></td><td class="c-test"><code>lib/live-workspace.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-009</code></td><td class="c-area">UI</td><td class="c-feature">Memory summary/filter</td><td class="c-behavior">summarize + filter memory entries by type</td><td class="c-source"><code>`lib/memorySummary.ts`</code></td><td class="c-test"><code>lib/memorySummary.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-010</code></td><td class="c-area">UI</td><td class="c-feature">Notifications logic</td><td class="c-behavior">status→event map, shouldNotify, debounce, build options</td><td class="c-source"><code>`lib/notifications.ts`</code></td><td class="c-test"><code>lib/notifications.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"><code>createNotification</code>/permission are browser-API thin wrappers (not unit-covered, acceptable)</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-011</code></td><td class="c-area">UI</td><td class="c-feature">Output-path detection/linkify</td><td class="c-behavior">regex split text by output paths, linkify escaped HTML</td><td class="c-source"><code>`lib/output-path-detect.ts`</code></td><td class="c-test"><code>lib/output-path-detect.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-012</code></td><td class="c-area">UI</td><td class="c-feature">Owner display name</td><td class="c-behavior">derive owner label</td><td class="c-source"><code>`lib/owner.ts`</code></td><td class="c-test"><code>lib/owner.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-013</code></td><td class="c-area">UI</td><td class="c-feature">Public route matching</td><td class="c-behavior">match login/join/shared public routes</td><td class="c-source"><code>`lib/publicRoute.ts`</code></td><td class="c-test"><code>lib/publicRoute.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-014</code></td><td class="c-area">UI</td><td class="c-feature">Push subscribe helpers</td><td class="c-behavior">isPushSupported/iOS/PWA, base64→Uint8Array</td><td class="c-source"><code>`lib/push-subscribe.ts`</code></td><td class="c-test"><code>lib/push-subscribe.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-015</code></td><td class="c-area">UI</td><td class="c-feature">Shared view URLs</td><td class="c-behavior">shared image base + raw url builders</td><td class="c-source"><code>`lib/sharedView.ts`</code></td><td class="c-test"><code>lib/sharedView.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-016</code></td><td class="c-area">UI</td><td class="c-feature">Sharing scope preview</td><td class="c-behavior">visibility preview text + tooltip</td><td class="c-source"><code>`lib/sharingScope.ts`</code></td><td class="c-test"><code>lib/sharingScope.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-017</code></td><td class="c-area">UI</td><td class="c-feature">Space branding vars</td><td class="c-behavior">parse color, derive brand CSS vars</td><td class="c-source"><code>`lib/spaceBranding.ts`</code></td><td class="c-test"><code>lib/spaceBranding.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-018</code></td><td class="c-area">UI</td><td class="c-feature">Space rail sort</td><td class="c-behavior">order spaces for rail</td><td class="c-source"><code>`lib/spaceSort.ts`</code></td><td class="c-test"><code>lib/spaceSort.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-019</code></td><td class="c-area">UI</td><td class="c-feature">Space task filter/count</td><td class="c-behavior">filter tasks for space, count running</td><td class="c-source"><code>`lib/spaceTasks.ts`</code></td><td class="c-test"><code>lib/spaceTasks.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-020</code></td><td class="c-area">UI</td><td class="c-feature">Split pieces (custom/default)</td><td class="c-behavior">split + resolve piece options</td><td class="c-source"><code>`lib/splitPieces.ts`</code></td><td class="c-test"><code>lib/splitPieces.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-021</code></td><td class="c-area">UI</td><td class="c-feature">Streaming field extract</td><td class="c-behavior">extract a field from partial SSE stream</td><td class="c-source"><code>`lib/streamFieldExtract.ts`</code></td><td class="c-test"><code>lib/streamFieldExtract.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-022</code></td><td class="c-area">UI</td><td class="c-feature">Tab swipe physics</td><td class="c-behavior">axis lock, resist, commit, neighbor index</td><td class="c-source"><code>`lib/tab-swipe.ts`</code></td><td class="c-test"><code>lib/tab-swipe.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-023</code></td><td class="c-area">UI</td><td class="c-feature">Task list filter/sort/group</td><td class="c-behavior">query match, group-by-status, counts, filter+sort</td><td class="c-source"><code>`lib/taskFilter.ts`</code></td><td class="c-test"><code>lib/taskFilter.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-024</code></td><td class="c-area">UI</td><td class="c-feature">Task scope filter</td><td class="c-behavior">filter tasks by scope tab</td><td class="c-source"><code>`lib/taskScope.ts`</code></td><td class="c-test"><code>lib/taskScope.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-025</code></td><td class="c-area">UI</td><td class="c-feature">Theme pref + apply</td><td class="c-behavior">resolve/store/apply theme, system-dark, events</td><td class="c-source"><code>`lib/theme.ts` (+ `theme-css-parity.test.ts`)</code></td><td class="c-test"><code>lib/theme.test.ts</code>, <code>lib/theme-css-parity.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-026</code></td><td class="c-area">UI</td><td class="c-feature">Tool policy split/patch</td><td class="c-behavior">split categories, build policy patch, count enabled</td><td class="c-source"><code>`lib/toolPolicy.ts`</code></td><td class="c-test"><code>lib/toolPolicy.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">known Bash/sensitive-tools write-path gap (MEMORY: PR #653) — verify both category + sensitiveTools systems covered</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-027</code></td><td class="c-area">UI</td><td class="c-feature">Unsaved-changes guard</td><td class="c-behavior">detect unsaved, confirm discard, hook</td><td class="c-source"><code>`lib/unsavedGuard.ts`</code></td><td class="c-test"><code>lib/unsavedGuard.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap"><code>useUnsavedGuard</code> hook portion needs jsdom; pure parts covered</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-028</code></td><td class="c-area">UI</td><td class="c-feature">URL state (useUrlState)</td><td class="c-behavior">read/build search params, legacy ?page=tasks redirect</td><td class="c-source"><code>`lib/urlState.ts`</code></td><td class="c-test"><code>lib/urlState.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">hook wrapper <code>useUrlState.ts</code> itself untested (jsdom) but pure core covered</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-029</code></td><td class="c-area">UI</td><td class="c-feature">Misc utils + activity log parse</td><td class="c-behavior">relativeTime, status tones, previewable-by-type, parseActivityLog</td><td class="c-source"><code>`lib/utils.ts`</code></td><td class="c-test"><code>lib/utils.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-030</code></td><td class="c-area">UI</td><td class="c-feature">Workspace dir role</td><td class="c-behavior">classify input/output/logs/subtasks dir</td><td class="c-source"><code>`lib/workspaceDirs.ts`</code></td><td class="c-test"><code>lib/workspaceDirs.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-031</code></td><td class="c-area">UI</td><td class="c-feature">Pet state machine</td><td class="c-behavior">pet mood/animation state transitions</td><td class="c-source"><code>`lib/pets/petState.ts`</code></td><td class="c-test"><code>lib/pets/petState.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"><code>lib/pets/toolIconMap.ts</code> is a static map (no test, low value)</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-032</code></td><td class="c-area">UI</td><td class="c-feature">i18n workspace terminology</td><td class="c-behavior">locale keys use "ワークスペース" consistently</td><td class="c-source"><code>`i18n/index.ts`, `i18n/locales`</code></td><td class="c-test"><code>i18n/layout-workspace.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only layout/workspace terms checked; full key-parity across locales not asserted</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-033</code></td><td class="c-area">UI</td><td class="c-feature">Service worker push handler</td><td class="c-behavior">SW push event → notification payload</td><td class="c-source"><code>`sw/push-handler.ts`</code></td><td class="c-test"><code>sw/push-handler.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-034</code></td><td class="c-area">UI</td><td class="c-feature">App share URL builder</td><td class="c-behavior">build shareable app URL/token</td><td class="c-source"><code>`components/spaces/appShareUrl.ts`</code></td><td class="c-test"><code>components/spaces/appShareUrl.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-035</code></td><td class="c-area">UI</td><td class="c-feature">App↔iframe postMessage bridge</td><td class="c-behavior">validate/route postMessage between app iframe + host</td><td class="c-source"><code>`components/spaces/app-bridge.ts`</code></td><td class="c-test"><code>components/spaces/app-bridge.test.ts</code> (31 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">heaviest pure suite; strong</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-036</code></td><td class="c-area">UI</td><td class="c-feature">App file gateway (shared/auth)</td><td class="c-behavior">route file reads through gateway, prevent 401 drift</td><td class="c-source"><code>`components/spaces/app-file-gateway.ts`</code></td><td class="c-test"><code>components/spaces/app-file-gateway.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-037</code></td><td class="c-area">UI</td><td class="c-feature">Chat detail split logic</td><td class="c-behavior">split chat vs detail pane state</td><td class="c-source"><code>`components/spaces/ChatDetailSplit.tsx` (logic)</code></td><td class="c-test"><code>components/spaces/ChatDetailSplit.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">pure split logic tested; component render not</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-038</code></td><td class="c-area">UI</td><td class="c-feature">Space detail keying</td><td class="c-behavior">stable key/identity for space detail</td><td class="c-source"><code>`components/spaces/SpaceDetail.tsx` (logic)</code></td><td class="c-test"><code>components/spaces/spaceDetailKeying.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">keying logic only</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-039</code></td><td class="c-area">UI</td><td class="c-feature">App auto-approve gate</td><td class="c-behavior">decide auto-approve of app file ops</td><td class="c-source"><code>`components/spaces/AppRunner.tsx` (logic)</code></td><td class="c-test"><code>components/spaces/AppRunner.autoapprove.test.tsx</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only auto-approve branch; AppRunner iframe lifecycle untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-040</code></td><td class="c-area">UI</td><td class="c-feature">App-share API client</td><td class="c-behavior">app-share fetch client behavior</td><td class="c-source"><code>`src/api.ts` (app-share)</code></td><td class="c-test"><code>api.app-share.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only app-share endpoints; rest of <code>api.ts</code> untested</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-041</code></td><td class="c-area">UI</td><td class="c-feature">Harness gateway</td><td class="c-behavior">test-harness gateway (mounts real AppRunner)</td><td class="c-source"><code>`harness/`</code></td><td class="c-test"><code>harness/harness-gateway.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-042</code></td><td class="c-area">UI</td><td class="c-feature">Shared tabs (public view)</td><td class="c-behavior">which tabs show in SharedView/SharedAppView</td><td class="c-source"><code>`pages/SharedView.tsx`/`SharedAppView.tsx` (logic)</code></td><td class="c-test"><code>pages/sharedTabs.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">tab-selection logic only</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-043</code></td><td class="c-area">UI</td><td class="c-feature">Detail tab selection</td><td class="c-behavior">detail tab list / readonly gating</td><td class="c-source"><code>`components/detail/*` (logic)</code></td><td class="c-test"><code>detail/detailTabs.test.ts</code>, <code>detail/detail-readonly.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">tab + readonly logic; not full panels</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-044</code></td><td class="c-area">UI</td><td class="c-feature">Task data source resolution</td><td class="c-behavior">choose live vs shared data source for task</td><td class="c-source"><code>`components/detail/task-data-source.tsx` (logic)</code></td><td class="c-test"><code>detail/task-data-source.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-045</code></td><td class="c-area">UI</td><td class="c-feature">Console key handling</td><td class="c-behavior">terminal key → escape sequences</td><td class="c-source"><code>`detail/tabs/console/*`</code></td><td class="c-test"><code>detail/tabs/console/keys.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">key mapping only; TerminalView render/websocket untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-046</code></td><td class="c-area">UI</td><td class="c-feature">TopBar collapse logic</td><td class="c-behavior">which tabs collapse into overflow</td><td class="c-source"><code>`components/layout/TopBar.tsx` (logic)</code></td><td class="c-test"><code>layout/topbar-collapse.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">collapse math only</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-047</code></td><td class="c-area">UI</td><td class="c-feature">Settings sidebar sections</td><td class="c-behavior">section list/visibility logic</td><td class="c-source"><code>`components/settings/SettingsSidebar.tsx` (logic)</code></td><td class="c-test"><code>settings/settingsSidebar.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">section logic; per-form rendering untested</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-048</code></td><td class="c-area">UI</td><td class="c-feature">Rotating tips</td><td class="c-behavior">pick/rotate chat tip strings</td><td class="c-source"><code>`components/chat/RotatingTips.tsx` (logic)</code></td><td class="c-test"><code>chat/RotatingTips.tips.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-049</code></td><td class="c-area">UI</td><td class="c-feature">File drag-n-drop transfer</td><td class="c-behavior">build/read DataTransfer drag sources for files</td><td class="c-source"><code>`lib/fileDnd.ts` (`dragSources`, `readDragSources`)</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">pure, easy to test now; DnD bugs are user-visible</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-050</code></td><td class="c-area">UI</td><td class="c-feature">Files → base64</td><td class="c-behavior">convert File[] to base64 upload payload</td><td class="c-source"><code>`lib/fileBase64.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">uses FileReader (jsdom/node-FileReader needed); logic small but worth a test</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-051</code></td><td class="c-area">UI</td><td class="c-feature">Local date helpers</td><td class="c-behavior">localToday, tz offset, shiftDay</td><td class="c-source"><code>`lib/localDate.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">pure date math — high regression risk (tz), trivially testable</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-052</code></td><td class="c-area">UI</td><td class="c-feature">Space color</td><td class="c-behavior">derive deterministic space color</td><td class="c-source"><code>`lib/spaceColor.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">pure; deterministic-output test trivial</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-053</code></td><td class="c-area">UI</td><td class="c-feature">Polling/stale constants</td><td class="c-behavior">POLLING + STALE_TIME tables</td><td class="c-source"><code>`lib/constants.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">low value (constants) — skip unless invariants needed</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-054</code></td><td class="c-area">UI</td><td class="c-feature">Help content loader</td><td class="c-behavior">load+assemble help sections from raw md</td><td class="c-source"><code>`lib/help-content.ts` (`loadHelpSections`)</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap"><code>help.ts</code> parsing covered, but the loader/aggregation step is untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-055</code></td><td class="c-area">UI</td><td class="c-feature">FilePreview render (md/csv/jsonl/image/pdf)</td><td class="c-behavior">render each preview type; **relative-image rewrite** to <code>/api/local/tasks/:id/files/raw</code></td><td class="c-source"><code>`components/files/FilePreview.tsx` (`resolvedHref`, line ~320)</code></td><td class="c-test">none (only <code>lib/utils</code> <code>isPreviewable</code> flags)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">the relative-image rewrite is core, user-visible, untested. Could extract <code>resolveImageHref</code> to lib → sandbox-testable</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-056</code></td><td class="c-area">UI</td><td class="c-feature">FileBrowser (grid/list, sort, select, move)</td><td class="c-behavior">tile/detail views, multi-select, drag-move, breadcrumb</td><td class="c-source"><code>`components/files/FileBrowser.tsx` + `FileTileGrid/FileDetailList/FileBreadcrumb/MoveTargetDialog`</code></td><td class="c-test">e2e <code>spaces.spec.ts</code> (icon grid/upload/delete/multiselect/download)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">core flows covered by e2e; jsdom unit tests absent (sort/select logic is in tested libs UI-006/UI-004)</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-056b</code></td><td class="c-area">UI</td><td class="c-feature">useFileBrowser/useFileView/useFilePreview hooks</td><td class="c-behavior">data fetching + view state for files</td><td class="c-source"><code>`hooks/useFileBrowser.ts`, `useFileView.ts`, `useFilePreview.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">jsdom</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-057</code></td><td class="c-area">UI</td><td class="c-feature">ChatPane / ChatMessage / MovementGroup</td><td class="c-behavior">message stream, movement grouping, tool-calls section</td><td class="c-source"><code>`components/chat/*`</code></td><td class="c-test">e2e (inline chat create/open, cancel)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">rendering untested in jsdom; movement-group grouping logic not extracted to lib</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-058</code></td><td class="c-area">UI</td><td class="c-feature">Tool-request approval card</td><td class="c-behavior">inline Approve/Deny grants + resolves request</td><td class="c-source"><code>`components/chat/ToolRequestApproval.tsx`</code></td><td class="c-test">e2e <code>tool-request.spec.ts</code> (approve, deny)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">jsdom unit absent; e2e covers both branches</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-059</code></td><td class="c-area">UI</td><td class="c-feature">Settings ConfigForm + per-section forms</td><td class="c-behavior">edit each config.yaml section (Tools/LLM/Context/Safety/SSH/MCP/Gateway/Branding/Pets/Reflection/...)</td><td class="c-source"><code>`components/settings/*Form.tsx` (~45 files)</code></td><td class="c-test">settingsSidebar (sections), toolPolicy (UI-026)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">huge surface; only sidebar + tool-policy logic tested. Per-form validation/round-trip largely untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-060</code></td><td class="c-area">UI</td><td class="c-feature">Memory & Learning tab</td><td class="c-behavior">edit memory entries, reflection history + revert</td><td class="c-source"><code>`components/settings/MemoryLearningForm.tsx`, `ReflectionForm.tsx`</code></td><td class="c-test">e2e <code>spaces.spec.ts</code> (memory persists A vs B), memorySummary lib (UI-009)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">persistence covered by e2e; revert button + reflection-history list render untested</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-061</code></td><td class="c-area">UI</td><td class="c-feature">Usage dashboard</td><td class="c-behavior">per-user daily LLM usage charts</td><td class="c-source"><code>`components/usage/UsagePage.tsx`, `settings/GatewayKeyUsagePanel.tsx`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">jsdom; aggregation/format logic not extracted to lib</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-062</code></td><td class="c-area">UI</td><td class="c-feature">Worker/Node status widgets</td><td class="c-behavior">rail GPU panel, per-backend rows, worker pills</td><td class="c-source"><code>`components/dashboard/*Widget.tsx`, `hooks/useWorkerStatus/useNodeStatus`</code></td><td class="c-test">e2e <code>spaces.spec.ts</code> (GPU panel toggle, proxy per-backend rows)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">e2e smoke; widget render/empty-state untested in unit</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-063</code></td><td class="c-area">UI</td><td class="c-feature">Browser session panel + PiP</td><td class="c-behavior">live browser session view, picture-in-picture</td><td class="c-source"><code>`components/browser/*`, `hooks/usePictureInPicture.ts`</code></td><td class="c-test">e2e <code>spaces.spec.ts</code> (BROWSER panel groups render)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">PiP controller untested; panel render only via e2e</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-064</code></td><td class="c-area">UI</td><td class="c-feature">SSH config/console UI</td><td class="c-behavior">console terminal, grants, audit, key rotation</td><td class="c-source"><code>`components/settings/Ssh*.tsx`, `userfolder/Ssh*.tsx`, `useConsoleSession`</code></td><td class="c-test">none (types only <code>ssh-console-types.ts</code>)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">large untested surface; console session hook + websocket untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-065</code></td><td class="c-area">UI</td><td class="c-feature">Mobile edge-swipe / swipeable tabs</td><td class="c-behavior">edge swipe nav, tab swipe gestures</td><td class="c-source"><code>`hooks/useEdgeSwipe.ts`, `components/mobile/SwipeableTabs.tsx`</code></td><td class="c-test"><code>hooks/useEdgeSwipe.test.ts</code> (DOM — **fails in sandbox**), <code>lib/tab-swipe.ts</code> (UI-022)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">physics covered as lib; the DOM hook test exists but can't run here</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-066</code></td><td class="c-area">UI</td><td class="c-feature">Pets overlay + frame analysis</td><td class="c-behavior">pet sprite overlay, tool-spark, frame analysis</td><td class="c-source"><code>`components/pets/*`, `hooks/usePetFrameAnalysis/useActivePet`</code></td><td class="c-test"><code>lib/pets/petState.test.ts</code> (UI-031)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">state machine covered; overlay render + frame analysis untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-067</code></td><td class="c-area">UI</td><td class="c-feature">CreateTaskDialog + PromptCoach + ScheduleFields</td><td class="c-behavior">new chat/task dialog, prompt coaching, schedule builder</td><td class="c-source"><code>`components/create/*`</code></td><td class="c-test">e2e (+新規 opens dialog; ephemeral warning), cronForm lib (UI-003)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">dialog open + ephemeral warning via e2e; prompt-coach + form→cron untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-068</code></td><td class="c-area">UI</td><td class="c-feature">Monaco file editor (userfolder)</td><td class="c-behavior">edit AGENTS.md/MCP/skills/memory files</td><td class="c-source"><code>`components/userfolder/MonacoFileEditor.tsx` + panels</code></td><td class="c-test">e2e (AGENTS.md persistence, panels render)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">persistence via e2e; editor component untested in unit</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-069</code></td><td class="c-area">UI</td><td class="c-feature">Command palette UI</td><td class="c-behavior">open palette, fuzzy filter, execute command</td><td class="c-source"><code>`components/command/CommandPalette.tsx`</code></td><td class="c-test"><code>lib/command-palette.test.ts</code> (UI-002)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">all logic in lib (FULL); only the render/keyboard wiring is jsdom</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-070</code></td><td class="c-area">UI</td><td class="c-feature">Embed cards (X/Maps/YouTube/Amazon)</td><td class="c-behavior">render structured-block embeds + detail modals</td><td class="c-source"><code>`components/embed/*`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">jsdom; parsing of block data could be extracted to lib</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-071</code></td><td class="c-area">UI</td><td class="c-feature">Setup wizard</td><td class="c-behavior">first-run setup steps</td><td class="c-source"><code>`components/setup/SetupWizard.tsx`, `hooks/useSetupState`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">jsdom</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-072</code></td><td class="c-area">UI</td><td class="c-feature">Toast / notifications hooks</td><td class="c-behavior">toast queue, task notifications dispatch</td><td class="c-source"><code>`hooks/useToast.ts`, `useTaskNotifications.ts`</code></td><td class="c-test"><code>lib/notifications.test.ts</code> (UI-010)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">decision logic in lib; hook dispatch/debounce wiring untested</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-073</code></td><td class="c-area">UI</td><td class="c-feature">Spaces foundation + rail + detail tabs</td><td class="c-behavior">create case space, open detail, mobile rail↔detail</td><td class="c-source"><code>`components/spaces/*`</code></td><td class="c-test"><code>e2e/spaces.spec.ts</code> (37 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">most thorough e2e suite (chat CRUD, files, calendar, settings, apps, members, MCP isolation)</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-074</code></td><td class="c-area">UI</td><td class="c-feature">Cross-space calendar</td><td class="c-behavior">top-bar tab, grid, month nav, per-space dots</td><td class="c-source"><code>`components/spaces/CrossSpaceCalendar.tsx`</code></td><td class="c-test"><code>e2e/calendar.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-075</code></td><td class="c-area">UI</td><td class="c-feature">Tasks→workspace migration</td><td class="c-behavior">Tasks tab removed, personal WS auto-select, legacy redirect</td><td class="c-source"><code>`App.tsx`, `lib/urlState.ts`</code></td><td class="c-test"><code>e2e/tasks.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">also lib-covered (UI-028)</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-076</code></td><td class="c-area">UI</td><td class="c-feature">Sharing scope (multi-user)</td><td class="c-behavior">manager/member/stranger visibility (200/404), invite picker</td><td class="c-source"><code>space visibility</code></td><td class="c-test"><code>e2e-auth/sharing-scope.auth.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">requires auth seed</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-077</code></td><td class="c-area">UI</td><td class="c-feature">Shared space membership</td><td class="c-behavior">invite→member row+avatars, role change, removal</td><td class="c-source"><code>`components/spaces/SpaceMembersPanel.tsx`</code></td><td class="c-test"><code>e2e-auth/shared-space.auth.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-078</code></td><td class="c-area">UI</td><td class="c-feature">Space invite links</td><td class="c-behavior">join via token, invalid/revoked states</td><td class="c-source"><code>`components/spaces/JoinSpace.tsx`</code></td><td class="c-test"><code>e2e-auth/space-invite.auth.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-079</code></td><td class="c-area">UI</td><td class="c-feature">Workspace file input flow</td><td class="c-behavior">input/output folders show, next-action guidance, ephemeral warning</td><td class="c-source"><code>Files tab + create dialog</code></td><td class="c-test"><code>e2e-auth/workspace-file-input.auth.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">—</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-080</code></td><td class="c-area">UI</td><td class="c-feature">App-share public view</td><td class="c-behavior">login-free read-only shared app view</td><td class="c-source"><code>`pages/SharedAppView.tsx`, `SharedView.tsx`</code></td><td class="c-test">none (only lib <code>appShareUrl</code>/<code>sharedTabs</code>/<code>sharedView</code>)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">public viewer flow has no e2e; logic libs covered</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-081</code></td><td class="c-area">UI</td><td class="c-feature">Help page</td><td class="c-behavior">render help sections by category, search</td><td class="c-source"><code>`pages/HelpPage.tsx`</code></td><td class="c-test">none (lib <code>help.ts</code> FULL)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">rendering + search wiring not e2e/jsdom tested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-082</code></td><td class="c-area">UI</td><td class="c-feature">Pieces page (CRUD)</td><td class="c-behavior">list/edit custom vs default pieces, movement editor</td><td class="c-source"><code>`pages/PiecesPage.tsx`, `settings/PieceEditor.tsx`, `MovementForm.tsx`, `RulesTable.tsx`</code></td><td class="c-test">none (lib <code>splitPieces</code> FULL)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">piece editor / movement rules UI untested</td></tr>
|
||
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-083</code></td><td class="c-area">UI</td><td class="c-feature">Schedules page</td><td class="c-behavior">list/create scheduled tasks (5 types)</td><td class="c-source"><code>`pages/SchedulesPage.tsx`</code></td><td class="c-test">none (lib <code>cronForm</code> partial)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">schedule CRUD UI untested</td></tr>
|
||
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-084</code></td><td class="c-area">UI</td><td class="c-feature">Admin / users page</td><td class="c-behavior">user CRUD, captcha, org form</td><td class="c-source"><code>`pages/UsersPage.tsx`, `AdminCaptchaPage.tsx`, `admin/*`, `settings/OrgsForm.tsx`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">no UI tests for admin user management</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="card" id="gaps">
|
||
<h2>重要ギャップ (Top gaps)</h2>
|
||
<p class="muted-note">各領域インベントリの「Top gaps」を集約。最もリスクの高い未テスト挙動。</p>
|
||
<section class="gap-area"><h3>Engine core</h3>
|
||
<p>Highest-risk untested behaviors, ranked:</p>
|
||
<ol class="gap-list">
|
||
<li><strong>ENG-052 <code>classifyPiece</code> async path (NONE).</strong> Routing every task to a piece runs through this LLM call + fallback. Only the two pure helpers are tested; the wiring, the null-parse→default-piece fallback, and LLM-error handling have zero coverage. A regression here silently misroutes all tasks.</li>
|
||
<li><strong>ENG-037 WAITING_HUMAN_BROWSER / WAITING_HUMAN_TOOL_REQUEST pauses (NONE).</strong> Two human-wait sentinel branches in piece-runner have no test. A break leaves jobs hung or, worse, falsely completed — same failure class as the documented context-overflow <code>default_next</code> trap.</li>
|
||
<li><strong>ENG-039 Lessons injection + cap + lessons.jsonl (PARTIAL).</strong> Lessons appear in snapshot tests, but the core promise — accumulated lessons injected into the next movement's prompt and trimmed at 2000 chars — is not directly asserted, nor is <code>writeLessonLog</code>. Silent loss of cross-movement learning would be invisible.</li>
|
||
<li><strong>ENG-053 <code>loadAllPieceTriggers</code> (NONE).</strong> Trigger/keyword loading feeds the classifier hint layer; the custom-dir precedence and skip-on-invalid-piece branches are uncovered.</li>
|
||
<li><strong>ENG-033 cross-movement loop guard per-movement override + reset (PARTIAL) and ENG-013 mid-iteration cancel (PARTIAL).</strong> The default-revisit abort is tested, but per-movement <code>max_consecutive_revisits</code> and the counter-reset-on-movement-change branch are not — the exact knobs that prevent runaway loops. Likewise mid-work cancellation (vs pre-loop) is only covered via snapshot side effects.</li>
|
||
</ol>
|
||
</section>
|
||
<section class="gap-area"><h3>Tools</h3>
|
||
<ol class="gap-list">
|
||
<li><strong>ReadPPTX ZIP-bomb detection & PPTX size limit (TOOL-015) — NONE.</strong> <code>office.ts:2124-2130</code> rejects total-uncompressed > 200MB and <code>validateOfficeFile</code> enforces the 50MB file cap, but <code>office.test.ts</code> contains no test that crafts an over-limit / high-compression-ratio PPTX. This is the only declared anti-DoS guard in the tools layer and it is completely unverified. A malformed/regressed check would silently allow a decompression bomb.</li>
|
||
<li><strong>Office file-size-limit enforcement across ReadExcel/ReadDocx/ReadPdf (TOOL-012/013/014) — PARTIAL→NONE for the limit path.</strong> Tests thoroughly cover *format-mismatch* rejection (CFB/HTML/CSV/OOXML disguises) but never exercise <code>stats.size > maxSize</code> → warning emission. The <code>resolveMaxSize</code> config override (<code>tools.office_*_max_size_mb</code>) is also untested.</li>
|
||
<li><strong>index.ts dispatch order, first-match-wins, and silent-skip-on-load-failure (XCUT-001) — NONE.</strong> A single test checks the SshConsole catalog. The core invariant — that a tool name resolves to exactly one module in a fixed precedence order, and that a module failing to import is skipped without crashing dispatch — has no test. A duplicate tool name or an import regression would pass CI.</li>
|
||
<li><strong>index.ts runtime META_TOOLS injection (XCUT-002) — PARTIAL.</strong> The 20-name runtime META list in <code>index.ts:411</code> (RequestTool, MissionUpdate, all UserFolder/AppDocs tools, etc.) is what actually makes these always-available regardless of a movement's allowed_tools, yet only the smaller <code>tool-categories.ts</code> SUBSET is asserted. The two lists can drift (they already differ by design); nothing tests that the runtime injection list matches expectations.</li>
|
||
<li><strong>DownloadFile robustness (TOOL-009) — PARTIAL.</strong> Only source-vs-input routing is tested. No coverage for download size limits, redirect following, content-type handling, or failure modes — the same blast radius class as WebFetch but without WebFetch's SSRF/binary test depth.</li>
|
||
</ol>
|
||
</section>
|
||
<section class="gap-area"><h3>Pieces</h3>
|
||
<ol class="gap-list">
|
||
<li><strong>8 of 15 pieces have ZERO direct test beyond the schema-load sweep</strong> (research-sub, brainstorming, sns-research, x-ai-digest, help) and several more are only name-fixtures (chat, slide, data-process, piece-builder). No test asserts their movement/rule/transition contract or their allowed_tools allowlist. A piece could be silently broken (wrong tool name, dropped movement, bad transition graph that still parses) and only <code>loadPiece().not.toThrow()</code> would catch it — which it won't, since those are semantic not schema errors. Highest-value targets: research-sub (spawned, complex 3-movement loop), brainstorming, sns-research, x-ai-digest.</li>
|
||
<li><strong>No allowed_tools-vs-real-tool-registry consistency test.</strong> Pieces list tool names in <code>allowed_tools</code> as plain strings; nothing asserts every name in every piece resolves to an actual registered tool. A typo (or a renamed/removed tool) leaves a dead allowlist entry that silently never gets offered to the LLM — exactly the class of drift the maintenance-checklist warns about. This would be a cheap, high-leverage sweep test across all 15 pieces.</li>
|
||
<li><strong>lint-pieces.mjs (PINFRA-004) has no test.</strong> The CI gate script (file collection, exit codes 0/1/2, YAML parse-error path) is entirely untested. Its core rule is mirrored by validatePieceDef (tested), but the script's own CLI behavior — including its parse-error exit(2) and multi-file argument handling — could regress without notice.</li>
|
||
<li><strong>No transition-graph reachability / dangling-next validation for pieces.</strong> Validation only bans terminal values in rules[].next and (in the tool path) checks next ∈ movements∪WAIT_SUBTASKS. But file-backed <code>loadPiece</code>/<code>validatePieceDef</code> does NOT verify that every <code>rules[].next</code> points to an existing movement, nor that <code>initial_movement</code> exists, nor that every movement is reachable. A built-in YAML with a typo'd <code>next:</code> would load fine and only fail at runtime ("Movement not found"). No test guards this for the shipped pieces.</li>
|
||
<li><strong>Per-user piece resolution propagation (PINFRA-017) is only partially tested.</strong> The known "no-auth 'local' fallback" / "custom-default separation" checklists call out worker, continue-job, scheduler, classifier, and pieces-api as all needing custom-dir threading. Only the worker load path and pieces-api authz have tests; the scheduler-spawn and continue-job (resume after ASK/subtasks) paths resolving the correct per-user custom piece are not directly asserted — a regression here would silently fall back to built-in pieces for custom-piece users.</li>
|
||
</ol>
|
||
</section>
|
||
<section class="gap-area"><h3>Reflection</h3>
|
||
<ol class="gap-list">
|
||
<li><strong>REFL-032 — applyOne per-change write-failure path (<code>decision.code='failed'</code>)</strong>: when a memory write throws mid-loop, the applier records that single change as <code>failed</code> and continues. This distinct per-change failure branch (vs runner-level <code>outcome='failed'</code>) is not clearly asserted. It is the only place a memory mutation can partially fail under the lock, so it directly affects <code>partial</code>/<code>rejected</code> outcome correctness.</li>
|
||
<li><strong>REFL-056 — space authz negative path (<code>canEditInSpace</code> → 404)</strong>: the reflection-api space-store resolution depends on <code>canEditInSpace</code>. Tests cover the space-principal happy path, but a space *member without edit rights* hitting history/revert (must 404, no leak) is not asserted as a distinct multiuser case — exactly the kind of cross-principal authz gap that whole-branch reviews catch but per-unit tests miss.</li>
|
||
<li><strong>REFL-013 / REFL-044 / REFL-045 — runner snapshot-capture + non-fatal writeSnapshot failure</strong>: the runner's <code>writeSnapshot</code>-throws-but-record-metric-anyway branch, plus the before/after capture edge cases (remove-op uses <code>merge_target</code> for the before-file name; piece-YAML pre/post capture when <code>customPath</code> doesn't yet exist), are only lightly exercised. A torn snapshot here silently degrades the revert feature.</li>
|
||
<li><strong>REFL-007 — reflection dispatch / gateway routing</strong>: <code>worker.reflection-dispatch.test.ts</code> is small (~2 cases). The gateway role-keyed routing (<code>reflectionRoutingKey</code>, <code>roleFallback:'reflection'</code>) and the "send worker API key or gateway 401s" requirement are documented in code comments but not asserted end-to-end — a deploy-shaped risk (401 storms) that unit tests don't catch.</li>
|
||
<li><strong>REFL-031 / REFL-037 — boundary branches in applier</strong>: the ">3 memory_changes truncated with WARN" hard cap and the <code>decideOutcome</code> empty-input fallback-to-<code>abstained</code> (distinct from an explicit <code>abstain_reason</code>) lack dedicated assertions. Both are silent-correctness branches: a regression would mis-drop changes or mislabel outcomes without any test going red.</li>
|
||
</ol>
|
||
</section>
|
||
<section class="gap-area"><h3>Bridge core</h3>
|
||
<ol class="gap-list">
|
||
<li><strong>APIC-016 GET /api/local/tasks/:taskId/stream (NONE)</strong> — The SSE event stream (tool-call/delta mapping, visibility gate, the <code>toolBuf</code>/event-name reconstruction logic around line 976) has zero direct tests. This is the live job-progress feed the UI depends on; a regression here is invisible to CI.</li>
|
||
<li><strong>APIC-007 PUT /api/local/tasks/:taskId/mission (NONE)</strong> — Mission update route is completely untested: no authz (owner/admin vs viewer), no validation, no persistence assertion. Only superficial string match in an unrelated auth test.</li>
|
||
<li><strong>APIC-020 office-preview HTTP route (PARTIAL)</strong> — The module is well tested, but the <code>/files/office-preview</code> endpoint wiring is not: section-enum 400, <code>canViewTask</code> gate, and the SofficeUnavailableError→503 mapping are unexercised end-to-end (only <code>handleOfficePreviewError</code> would do the 503).</li>
|
||
<li><strong>APIC-045 subtask file 403 traversal branch (PARTIAL)</strong> — <code>GET /subtasks/:jobId/files/*</code> has the explicit <code>resolved !== base && !startsWith(base+sep)</code> → 403 escape guard, but no test drives a traversal payload to assert the 403. The containment helper is unit-tested (APIC-046) but the route-level enforcement is not.</li>
|
||
<li><strong>APIC-011 regenerate-title (PARTIAL)</strong> — Only exercised inside the spaces test in an owner-happy context; the owner/admin-only authz denial path and the missing-generator branch are not asserted. (Secondary: APIC-041 trigger happy-path, APIC-008 comments read-gate, and APIC-037/043 GET-by-id view gates are similar PARTIAL thin spots.)</li>
|
||
</ol>
|
||
</section>
|
||
<section class="gap-area"><h3>Bridge auth & spaces</h3>
|
||
<p>Highest risk first (authorization-weighted):</p>
|
||
<ol class="gap-list">
|
||
<li><strong>APIS-038 Space tool-policy write under-tested (HIGH).</strong> <code>PUT /:id/tool-policy</code> gates by <code>canManageSpace</code>, but only 9 authz assertions and — per MEMORY (PR #653) — the Bash/sensitive-tool write path (<code>buildPolicyPatch</code> only walked categories, missing the <code>SENSITIVE_TOOLS</code> separate channel) was a real save-loss bug. The read/validate/render/write round-trip for sensitive tools across both delivery systems is not asserted end-to-end. A regression here silently grants/denies Bash/SSH/browser/MCP. Add an integration test that PUTs a sensitive-tool policy as a non-manager (expect 403) and as owner (expect persisted both-channels).</li>
|
||
<li><strong>APIS-052 Gateway proxy auth boundary (HIGH).</strong> <code>mountGateway</code> proxies LLM traffic; classification and config-equivalence are well tested, but the <strong>authentication of proxied requests</strong> (Bearer/API-key acceptance/rejection, 401 on bad key) is not asserted in gateway-mount.test.ts. An unauthenticated path slipping through the gateway mount would expose backend LLM spend. Deploy-order note in MEMORY (gateway→client) hints this boundary is operationally fragile.</li>
|
||
<li><strong>APIS-062 Dashboard API has no visible auth guard (MEDIUM-HIGH).</strong> <code>createDashboardApi</code> router shows no <code>requireAuth</code>/<code>requireAdmin</code> in its route definitions and the test asserts 0 authz cases. Worker/node (GPU) status may leak infra topology to unauthenticated/non-admin users. Confirm guard is applied at mount site (server.ts) and add an authz assertion.</li>
|
||
<li><strong>APIS-019 Shared-subtask wildcard traversal (MEDIUM).</strong> <code>GET /api/shared/:token/subtasks/:jobId/files/*</code> is a public token route with a wildcard path segment; containment relies on <code>isJobWithinWorkspace</code> prefix guard. No direct test asserts path-traversal/prefix-collision rejection on this wildcard. This is exactly the class fixed previously (startsWith prefix collision) — add a traversal/escape test.</li>
|
||
<li><strong>APIS-014 No-auth admin passthrough breadth (MEDIUM).</strong> When <code>authActive=false</code>, admin guards downgrade to passthrough (admin-api covered; <strong>branding adminGuard no-auth path not asserted</strong>). If a deployment runs no-auth on a shared network, branding upload/delete is fully open. Document/assert the no-auth posture for every admin route, not just user CRUD.</li>
|
||
</ol>
|
||
<p>Lower-priority partials worth noting: APIS-018/APIS-027 (per-endpoint path-escape / non-member-on-public read not asserted), APIS-032 (calendar non-editor write rejection lighter than file gates), APIS-044 (noVNC proxy upgrade authz), APIS-056 (push send delivery).</p>
|
||
</section>
|
||
<section class="gap-area"><h3>Services & DB</h3>
|
||
<ol class="gap-list">
|
||
<li><strong>Job recovery & resume helpers untested (DB-003, DB-032, SVC-019).</strong> <code>recoverOrphanedJobs</code>, <code>recoverStuckRunningJobs</code>, <code>resumeMcpWaitingJobs</code>, <code>resumeToolRequestJob</code>, and <code>requeueParentJobIfAllSubtasksDone</code> have no direct unit tests. These run on worker restart / subtask completion and directly affect whether jobs get stuck or double-run — exactly the class of bug seen in the config-rebuild double-execution incident. Highest-risk gap because failures are silent (stuck or duplicated jobs in prod).</li>
|
||
<li><strong>Env override matrix only partially covered (CFG-004, CFG-016).</strong> Only <code>OLLAMA_BASE_URL</code> is asserted (in config.audit-regression). <code>CONCURRENCY</code>, <code>WORKTREE_DIR</code>, <code>OLLAMA_MODEL</code>, <code>DB_PATH</code>, and the <code>AAO_*</code> metrics vars have no test. Env overrides are a documented public interface and a known footgun (storage mirror precedence already bit the project in #368/#369).</li>
|
||
<li><strong><code>answerSubtaskAsk</code> / subtask ASK plumbing (SVC-019).</strong> The path where a parent worker answers a child subtask's ASK and the parent requeues on all-subtasks-done is untested end-to-end. This is core to the <code>waiting_subtasks</code>/<code>waiting_human</code> lifecycle and has tangled state.</li>
|
||
<li><strong><code>addAuditLog</code> not unit-tested (DB-027).</strong> Only one consumer (script run) is asserted indirectly. Audit log is a security/compliance surface; no test guards the row shape or that mutating ops actually write audit rows.</li>
|
||
<li><strong>Comment injection lifecycle (DB-008).</strong> <code>getUninjectedComments</code> / <code>markCommentsInjected</code> / <code>getLatestResultComment</code> drive what the agent sees from user comments and what the user sees as the final result — no direct assertion of the inject→mark→re-query cycle, despite being on the hot path for continuation jobs.</li>
|
||
</ol>
|
||
</section>
|
||
<section class="gap-area"><h3>UI</h3>
|
||
<ol class="gap-list">
|
||
<li><strong>UI-055 FilePreview relative-image rewrite</strong> (<code>resolveImageHref</code>/<code>resolvedHref</code>, FilePreview.tsx ~L320) — core, user-visible (broken images in markdown), and <strong>currently untested</strong>. *Sandbox-testable IF the href-rewrite function is extracted to <code>lib/</code> (pure string transform); today it's inline in a component so as-is it needs jsdom.*</li>
|
||
<li><strong>UI-059 Settings forms (~45 *Form.tsx)</strong> — largest untested surface; only <code>SettingsSidebar</code> section logic + <code>toolPolicy</code> are covered. Per-form validation/round-trip is jsdom/e2e. <strong>Mitigation that IS sandbox-testable:</strong> extract each form's validation/serialization into pure helpers (the established pattern in this repo) and unit-test those.</li>
|
||
<li><strong>UI-064 SSH UI (config/console/grants/audit/rotation)</strong> — large surface, only TS types tested, no behavior tests. Console session + websocket needs e2e; <strong>grant/audit list filtering + form validation are extractable to lib (sandbox-testable).</strong></li>
|
||
<li><strong>UI-049/UI-051/UI-052 untested pure libs</strong> (<code>fileDnd</code>, <code>localDate</code>, <code>spaceColor</code>) — <strong>immediately sandbox-testable</strong>, no extraction needed. <code>localDate</code> (tz math) is the highest regression risk of the three.</li>
|
||
<li><strong>UI-026 toolPolicy Bash/sensitive-tools write-path</strong> — known real bug class (MEMORY: PR #653 fixed a save gap where <code>buildPolicyPatch</code> only walked categories and missed <code>SENSITIVE_TOOLS</code>/Bash). Test exists but should assert <strong>both</strong> the category system and the separate sensitiveTools array across read/validate/render/write. <strong>Sandbox-testable now</strong> (pure).</li>
|
||
</ol>
|
||
</section>
|
||
</section>
|
||
|
||
<footer>
|
||
MAESTRO TDD 機能テスト計画 · 段階: 設計 · 更新日 2026-06-25 · 全 502 行
|
||
</footer>
|
||
|
||
</div>
|
||
<script>
|
||
(function(){
|
||
var search = document.getElementById('search');
|
||
var rows = Array.prototype.slice.call(document.querySelectorAll('#master-table tbody tr'));
|
||
var btns = Array.prototype.slice.call(document.querySelectorAll('.fbtn'));
|
||
var count = document.getElementById('count');
|
||
var covFilter = 'ALL';
|
||
var q = '';
|
||
// pre-cache lowercased text per row
|
||
rows.forEach(function(r){ r.__t = r.textContent.toLowerCase(); });
|
||
function apply(){
|
||
var shown = 0;
|
||
for(var i=0;i<rows.length;i++){
|
||
var r = rows[i];
|
||
var okCov = (covFilter === 'ALL') || (r.getAttribute('data-cov') === covFilter);
|
||
var okQ = !q || r.__t.indexOf(q) !== -1;
|
||
if(okCov && okQ){ r.style.display = ''; shown++; }
|
||
else { r.style.display = 'none'; }
|
||
}
|
||
count.textContent = shown + ' / ' + rows.length + ' 件表示';
|
||
}
|
||
search.addEventListener('input', function(){ q = search.value.trim().toLowerCase(); apply(); });
|
||
btns.forEach(function(b){
|
||
b.addEventListener('click', function(){
|
||
btns.forEach(function(x){ x.classList.remove('active'); });
|
||
b.classList.add('active');
|
||
covFilter = b.getAttribute('data-cov');
|
||
apply();
|
||
});
|
||
});
|
||
apply();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|