claude-code-session-manager 0.95.0 → 0.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/assets/{DataModel-DZK9QZpV.js → DataModel-T0D00BpK.js} +1 -1
  2. package/dist/assets/History-DuAovTr7.js +2 -0
  3. package/dist/assets/{Hooks-CmK6l0uf.js → Hooks-CgP9jsAN.js} +3 -3
  4. package/dist/assets/{HostBilko-D8TIx-Bx.js → HostBilko-DHfz3Mxa.js} +1 -1
  5. package/dist/assets/{Library-DTxpOjx8.js → Library--poVP-d1.js} +1 -1
  6. package/dist/assets/{MarkdownEditor-BPJyrevq.js → MarkdownEditor-C56-Pc4c.js} +1 -1
  7. package/dist/assets/{McpServers-B_rvIYDs.js → McpServers-D-ykP4Vi.js} +2 -2
  8. package/dist/assets/Memory-BrkJjRL8.js +8 -0
  9. package/dist/assets/{Permissions-DhMMp27n.js → Permissions-BMpAaxJu.js} +3 -3
  10. package/dist/assets/Plugins-OuEe5S_T.js +2 -0
  11. package/dist/assets/{ProvenanceBadge-ptlItdCK.js → ProvenanceBadge-4eNbDYe8.js} +1 -1
  12. package/dist/assets/{SaveBar-CSbNSl2W.js → SaveBar-BQ8y9Lkv.js} +1 -1
  13. package/dist/assets/Scheduler-DcMT5sT_.js +16 -0
  14. package/dist/assets/{ScopeSwitcher-BJof-GWH.js → ScopeSwitcher-XdxtVKP-.js} +1 -1
  15. package/dist/assets/Settings-FdaPUq2t.js +3 -0
  16. package/dist/assets/{SkillReferenceGraph-CGl4B89-.js → SkillReferenceGraph-CJzvvc0_.js} +1 -1
  17. package/dist/assets/Skills-Wvm9OE1e.js +3 -0
  18. package/dist/assets/{SystemPrompt-DKj-q39J.js → SystemPrompt--LYw_69j.js} +1 -1
  19. package/dist/assets/TagLibrary-Be6HbMMx.js +1 -0
  20. package/dist/assets/{TiptapBody-BbcJL9az.js → TiptapBody-DzFlWMgO.js} +1 -1
  21. package/dist/assets/{Toggle-DAslvnnv.js → Toggle-BxtAFE1i.js} +1 -1
  22. package/dist/assets/index-XkrLB67D.js +3079 -0
  23. package/dist/assets/{settingsSchema-pzp0TgvN.js → settingsSchema-BIvDZ1Cj.js} +1 -1
  24. package/dist/index.html +1 -1
  25. package/package.json +8 -2
  26. package/plugins/CLAUDE.md +3 -3
  27. package/plugins/session-manager-dev/skills/develop/SKILL.md +83 -67
  28. package/plugins/session-manager-dev/skills/develop/standards.md +5 -11
  29. package/plugins/session-manager-dev/skills/requesting-code-review/SKILL.md +5 -15
  30. package/scripts/README.md +1 -0
  31. package/scripts/hooks/guard-destructive-git.cjs +9 -374
  32. package/scripts/hooks/guard-inline-implementation.cjs +9 -128
  33. package/scripts/hooks/guard-prd-writes.cjs +9 -74
  34. package/scripts/hooks/guard-self-schedule.cjs +9 -61
  35. package/scripts/hooks/lib/guard-destructive-git-policy.cjs +410 -0
  36. package/scripts/hooks/lib/guard-inline-implementation-policy.cjs +159 -0
  37. package/scripts/hooks/lib/guard-prd-writes-policy.cjs +100 -0
  38. package/scripts/hooks/lib/guard-self-schedule-policy.cjs +89 -0
  39. package/scripts/scheduler-mcp-server.cjs +8 -1
  40. package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +59 -0
  41. package/src/main/__tests__/exchanges.test.cjs +41 -96
  42. package/src/main/__tests__/exchangesPromptId.test.cjs +16 -25
  43. package/src/main/__tests__/files-reject-credentials.test.cjs +1 -1
  44. package/src/main/__tests__/health-credentials.test.cjs +81 -0
  45. package/src/main/__tests__/memoryAggregate.test.cjs +40 -1
  46. package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +1 -1
  47. package/src/main/__tests__/planValidator.test.cjs +71 -0
  48. package/src/main/__tests__/prdCreate.test.cjs +3 -1
  49. package/src/main/__tests__/prdSizing.test.cjs +106 -0
  50. package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +46 -0
  51. package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +10 -0
  52. package/src/main/__tests__/scheduler-looks-done.test.cjs +40 -0
  53. package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +81 -0
  54. package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +6 -1
  55. package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +110 -0
  56. package/src/main/__tests__/seedAgentPersonas.test.cjs +19 -1
  57. package/src/main/__tests__/seedValidatorPersona.test.cjs +116 -0
  58. package/src/main/__tests__/telemetryClient.test.cjs +2 -2
  59. package/src/main/__tests__/telemetryContract.test.cjs +2 -2
  60. package/src/main/__tests__/usageSingleFlight.test.cjs +10 -0
  61. package/src/main/__tests__/validationSentinels.test.cjs +84 -0
  62. package/src/main/build-info.json +4 -4
  63. package/src/main/config.cjs +26 -14
  64. package/src/main/exchanges.cjs +26 -21
  65. package/src/main/health.cjs +66 -4
  66. package/src/main/index.cjs +1 -1
  67. package/src/main/ipcSchemas.cjs +5 -1
  68. package/src/main/lib/__tests__/crashTelemetry.test.cjs +1 -1
  69. package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +115 -0
  70. package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +85 -27
  71. package/src/main/lib/__tests__/gitWorktree.test.cjs +21 -27
  72. package/src/main/lib/__tests__/guardShims.test.cjs +10 -3
  73. package/src/main/lib/__tests__/loadGate.test.cjs +44 -1
  74. package/src/main/lib/__tests__/opsOwnership.test.cjs +14 -0
  75. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +4 -4
  76. package/src/main/lib/__tests__/telemetryConsent.test.cjs +2 -2
  77. package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +1 -1
  78. package/src/main/lib/agentModelResolve.cjs +1 -0
  79. package/src/main/lib/credentials.cjs +62 -0
  80. package/src/main/lib/cwdClassify.cjs +15 -0
  81. package/src/main/lib/definitionOfDone.cjs +9 -13
  82. package/src/main/lib/effectiveModelInfo.cjs +2 -9
  83. package/src/main/lib/epicWorktreeProjectConfig.cjs +75 -31
  84. package/src/main/lib/loadGate.cjs +29 -5
  85. package/src/main/lib/opsOwnership.cjs +23 -0
  86. package/src/main/lib/planValidator.cjs +28 -0
  87. package/src/main/lib/prdCreate.cjs +8 -4
  88. package/src/main/lib/prdSizing.cjs +83 -0
  89. package/src/main/lib/runLogRetention.cjs +2 -8
  90. package/src/main/lib/telemetryBacklog.cjs +9 -3
  91. package/src/main/lib/telemetryClient.cjs +12 -3
  92. package/src/main/lib/telemetrySettings.cjs +8 -2
  93. package/src/main/lib/validationSentinels.cjs +29 -0
  94. package/src/main/memoryAggregate.cjs +36 -11
  95. package/src/main/otelSettings.cjs +8 -2
  96. package/src/main/scheduler.cjs +215 -31
  97. package/src/main/seedAgentPersonas.cjs +1 -1
  98. package/src/main/sessionsStore.cjs +8 -2
  99. package/src/main/supervisor.cjs +3 -3
  100. package/src/main/templates/PRD_AUTHORING.md +9 -9
  101. package/src/main/voiceSettings.cjs +8 -2
  102. package/src/preload/api.d.ts +3 -3
  103. package/src/preload/index.cjs +5 -5
  104. package/src/seed/agents/dev-lead.md +24 -15
  105. package/src/seed/agents/validator.md +33 -0
  106. package/dist/assets/History-BDyJUGtR.js +0 -2
  107. package/dist/assets/Memory-B-9GlHqe.js +0 -8
  108. package/dist/assets/Plugins-DKfAlmMY.js +0 -2
  109. package/dist/assets/Scheduler--rn1QBYc.js +0 -16
  110. package/dist/assets/Settings-Dsx6XZP5.js +0 -3
  111. package/dist/assets/Skills-D522Zfol.js +0 -3
  112. package/dist/assets/TagLibrary-BH7CG7-6.js +0 -1
  113. package/dist/assets/index-Yf8e7VCB.js +0 -3079
@@ -1 +1 @@
1
- import{az as b,ak as n,S as N,aO as O,ax as K}from"./index-Yf8e7VCB.js";function T({node:e,targetScope:r,onOverride:s,schema:a,rootLabel:m}){const[f,x]=b.useState(""),[u,o]=b.useState(!0),g=[],d=e.kind==="object"&&Object.keys(e.children).length===0,y=((a==null?void 0:a.rootKeys())??[]).length>0;return n.jsxs("div",{className:"h-full flex flex-col",children:[n.jsxs("div",{className:"shrink-0 border-b border-line bg-bg-elev px-3 py-1.5 flex items-center gap-2 text-xs",children:[n.jsxs("span",{className:"text-fg-faint",children:["merged · overrides → ",n.jsx("span",{className:"text-fg-dim",children:N[r].label})]}),y&&n.jsxs("label",{className:"flex items-center gap-1 text-fg-faint cursor-pointer",children:[n.jsx("input",{type:"checkbox",checked:u,onChange:t=>o(t.target.checked),className:"accent-accent"}),"show unset keys"]}),n.jsx("div",{className:"flex-1"}),n.jsx("input",{value:f,onChange:t=>x(t.target.value),placeholder:"filter keys or descriptions…",className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg w-56"})]}),n.jsx("div",{className:"flex-1 overflow-auto px-3 py-2 font-mono text-xs",children:d&&!u?n.jsx("div",{className:"text-fg-faint italic px-2 py-4",children:"no settings resolved — all scope files empty or missing"}):n.jsx(E,{node:e,path:g,label:m??"(root)",targetScope:r,onOverride:s,filter:f,depth:0,schema:a,showGhosts:u,defaultOpen:!0})})]})}function E(e){return e.node.kind==="object"?n.jsx(P,{...e,node:e.node}):e.node.kind==="array"?n.jsx(J,{...e,node:e.node}):n.jsx(R,{...e,node:e.node})}function A(e,r){return r.trim()?e.toLowerCase().includes(r.toLowerCase()):!0}function w(e,r,s){return!!(!s.trim()||e.some(a=>A(a,s))||r!=null&&r.description&&A(r.description,s))}function P({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u,showGhosts:o,defaultOpen:g}){const[d,h]=b.useState(g??x<2),y=(u==null?void 0:u.at(r))??null,t=new Set(Object.keys(e.children)),i=o?(y==null?void 0:y.knownChildren)??[]:[],c=Array.from(new Set([...t,...i])).sort(),l=b.useMemo(()=>f.trim()?c.filter(p=>{const v=(u==null?void 0:u.at([...r,p]))??null;return w([...r,p],v,f)?!0:t.has(p)?L(e.children[p],[...r,p],f,u):!1}):c,[c.join(","),f,e,r.join("."),u]);if(f.trim()&&l.length===0&&!w(r,y,f))return null;if(r.length===0)return n.jsx("div",{children:l.map(p=>j(p))});return n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:x*14},onClick:()=>h(!d),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:d?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" {",t.size,i.length>t.size?`/${new Set([...t,...i]).size}`:"","}"]}),(y==null?void 0:y.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:y.description,children:["— ",y.description]})]}),d&&l.map(p=>j(p))]});function j(p){if(t.has(p))return n.jsx(E,{node:e.children[p],path:[...r,p],label:p,targetScope:a,onOverride:m,filter:f,depth:x+1,schema:u,showGhosts:o},p);const v=(u==null?void 0:u.at([...r,p]))??null;return v?n.jsx(I,{path:[...r,p],label:p,info:v,targetScope:a,onSet:m,depth:x+1,filter:f},p):null}}function L(e,r,s,a){const m=(a==null?void 0:a.at(r))??null;if(w(r,m,s))return!0;if(e.kind!=="object")return!1;for(const f of Object.keys(e.children))if(L(e.children[f],[...r,f],s,a))return!0;return!1}function J({node:e,path:r,label:s,filter:a,depth:m,schema:f}){const[x,u]=b.useState(!0),o=(f==null?void 0:f.at(r))??null;return w(r,o,a)?n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:m*14},onClick:()=>u(!x),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:x?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" [",e.items.length,"]"]}),n.jsx(S,{tone:"warn",children:"accumulated"}),(o==null?void 0:o.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:o.description,children:["— ",o.description]})]}),x&&e.items.map((g,d)=>n.jsxs("div",{className:"flex items-start gap-2 py-0.5",style:{paddingLeft:(m+1)*14},children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0"}),n.jsxs("span",{className:"text-fg-faint",children:[d,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-xl",children:k(g.value)}),n.jsx(C,{scope:g.fromScope})]},d))]}):null}function R({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u}){const[o,g]=b.useState(!1),d=(u==null?void 0:u.at(r))??null;if(!w(r,d,f))return null;const h=e.winner!==a,y=e.shadowed.length>0;return n.jsxs("div",{children:[n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded",style:{paddingLeft:x*14},children:[n.jsx("span",{className:`w-3 shrink-0 ${y?"text-fg-faint cursor-pointer":""}`,onClick:()=>y&&g(!o),children:y?o?"▾":"▸":""}),n.jsxs("span",{className:"text-fg",children:[s,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-md",children:k(e.value)}),n.jsx(C,{scope:e.winner}),(d==null?void 0:d.deprecated)&&n.jsx(S,{tone:"warn",children:"deprecated"}),(d==null?void 0:d.default)!==void 0&&_(e.value,d.default)&&n.jsx(S,{tone:"dim",children:"= default"}),e.typeMismatch&&n.jsx("span",{className:"text-yellow-500/80",title:"shadowed scopes have a different value type",children:"⚠"}),(d==null?void 0:d.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:d.description,children:["— ",d.description]}),n.jsx("div",{className:"flex-1"}),(d==null?void 0:d.enum)&&n.jsx(F,{values:d.enum}),n.jsx("button",{disabled:!h,onClick:()=>m(r,e.value),className:`opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border rounded transition-opacity ${h?"border-accent/50 text-accent hover:bg-accent hover:text-bg":"border-line text-fg-faint cursor-not-allowed"}`,title:h?`copy into ${N[a].label} draft`:`already winning in ${N[a].label}`,children:"override"})]}),o&&e.shadowed.map((t,i)=>n.jsxs("div",{className:"flex items-center gap-2 py-0.5 text-fg-faint line-through",style:{paddingLeft:(x+1)*14+12},children:[n.jsx("span",{children:"shadowed:"}),n.jsx("span",{className:"truncate max-w-xl",children:k(t.value)}),n.jsx(C,{scope:t.scope,dim:!0})]},i))]})}function I({path:e,label:r,info:s,targetScope:a,onSet:m,depth:f,filter:x}){if(!w(e,s,x))return null;const u=s.default!==void 0?s.default:M(s.type),o=Array.isArray(s.type)?s.type.join("|"):s.type??"?";return n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded opacity-60",style:{paddingLeft:f*14},children:[n.jsx("span",{className:"w-3 shrink-0"}),n.jsx("span",{className:"text-fg-dim",children:r}),n.jsx(S,{tone:"dim",children:o}),s.required&&n.jsx(S,{tone:"warn",children:"required"}),s.default!==void 0&&n.jsxs("span",{className:"text-fg-faint",children:["default: ",k(s.default)]}),s.description&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:s.description,children:["— ",s.description]}),n.jsx("div",{className:"flex-1"}),s.enum&&n.jsx(F,{values:s.enum}),n.jsx("button",{onClick:()=>m(e,u),className:"opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border border-accent/50 rounded text-accent hover:bg-accent hover:text-bg transition-opacity",title:`write to ${N[a].label} draft`,children:"set"})]})}function _(e,r){return e===r?!0:typeof e!=typeof r?!1:e&&r&&typeof e=="object"?JSON.stringify(e)===JSON.stringify(r):!1}function M(e){switch(Array.isArray(e)?e[0]:e){case"boolean":return!1;case"integer":case"number":return 0;case"string":return"";case"array":return[];case"object":return{};case"null":return null;default:return null}}function k(e){if(typeof e=="string")return JSON.stringify(e);if(e===null)return"null";if(typeof e=="object"){const r=JSON.stringify(e);return r.length>100?r.slice(0,100)+"…":r}return String(e)}function C({scope:e,dim:r}){const s=e==="user"?"bg-bg-hi text-fg-dim border-line":e==="project"?"bg-blue-950/30 text-blue-400/80 border-blue-900/40":"bg-purple-950/30 text-purple-400/80 border-purple-900/40";return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s} ${r?"opacity-60":""}`,children:N[e].label})}function S({children:e,tone:r="default"}){const s={default:"bg-bg-hi text-fg-dim border-line",warn:"bg-yellow-950/30 text-yellow-500/80 border-yellow-900/40",dim:"bg-bg-elev text-fg-faint border-line"}[r];return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s}`,children:e})}function F({values:e}){const r=e.map(s=>String(s)).join(" | ");return n.jsxs("span",{className:"text-[10px] text-fg-faint truncate max-w-xs",title:`allowed: ${r}`,children:["∈ ",r.length>30?r.slice(0,30)+"…":r]})}function G(e,r,s){const a=O(o=>o.files),m=O(o=>o.loadJson),f=O(o=>o.watchFile),x=O(o=>o.unwatchFile),u=b.useMemo(()=>{const o={};for(const g of e.scopes){const d=e.resolve(g,r??"",s);d&&(o[g]=d)}return o},[e,r,s]);return b.useEffect(()=>{const o=Object.values(u).filter(Boolean);return o.forEach(g=>{a[g]||m(g),f(g)}),()=>{o.forEach(g=>x(g))}},[JSON.stringify(u)]),u}function z(e){const r=e??{},s={...r.$defs??{},...r.definitions??{}},a=t=>{if(!t)return{};if(!t.$ref)return x(t);const i=/^#\/(\$defs|definitions)\/(.+)$/.exec(t.$ref);if(!i)return x(t);const c=s[i[2]];return c?a({...c,...u(t)}):x(t)},m=new Set(["__proto__","constructor","prototype"]),f=t=>{if(!t)return{};const i={};for(const[c,l]of Object.entries(t))m.has(c)||(i[c]=l);return i},x=t=>{if(!t.allOf||t.allOf.length===0)return t;const i={...t};delete i.allOf;for(const c of t.allOf){const l=a(c);Object.assign(i,{properties:{...f(i.properties),...f(l.properties)},required:B([...i.required??[],...l.required??[]])}),!i.description&&l.description&&(i.description=l.description),i.default===void 0&&l.default!==void 0&&(i.default=l.default),!i.type&&l.type&&(i.type=l.type)}return i},u=t=>{const{$ref:i,...c}=t;return c},o=(t,i)=>{let c=a(t);for(const l of i){if(!c)return null;if(/^\d+$/.test(l)){const v=Array.isArray(c.items)?c.items[Number(l)]:c.items;c=v?a(v):null;continue}const j=c.properties;if(j&&!m.has(l)&&l in j){c=a(j[l]);continue}const p=c.additionalProperties;if(p&&typeof p=="object"){c=a(p);continue}return null}return c},g=(t,i,c)=>{if(!t)return null;const l={};if(t.description&&(l.description=t.description),t.type&&(l.type=t.type),t.default!==void 0&&(l.default=t.default),t.enum&&(l.enum=t.enum),t.const!==void 0&&(l.const=t.const),t.deprecated&&(l.deprecated=!0),t.examples&&(l.examples=t.examples),t.properties&&(l.knownChildren=Object.keys(t.properties)),c&&(i!=null&&i.includes(c))&&(l.required=!0),t.items&&!Array.isArray(t.items)){const j=a(t.items),p=g(j,void 0,null);p&&(l.itemsInfo=p)}return(t.additionalProperties===!0||typeof t.additionalProperties=="object")&&(l.open=!0),l},d=t=>i=>{if(i.length===0)return g(a(t),void 0,null);const c=o(t,i.slice(0,-1));if(!c)return null;const l=i[i.length-1],j=o(c,[l]);return g(j,c.required,l)},h=t=>{const i=a(t);return Object.keys(i.properties??{})},y=t=>({at:d(t),rootKeys:()=>h(t),rootedAt:i=>{const c=o(t,i);return y(c??{})}});return y(r)}function B(e){return Array.from(new Set(e))}let $=null;function V(){return $||($=z(K)),$}export{T as E,V as s,G as u};
1
+ import{az as b,ak as n,S as N,aR as k,ax as K}from"./index-XkrLB67D.js";function T({node:e,targetScope:r,onOverride:s,schema:a,rootLabel:m}){const[f,x]=b.useState(""),[u,o]=b.useState(!0),g=[],d=e.kind==="object"&&Object.keys(e.children).length===0,y=((a==null?void 0:a.rootKeys())??[]).length>0;return n.jsxs("div",{className:"h-full flex flex-col",children:[n.jsxs("div",{className:"shrink-0 border-b border-line bg-bg-elev px-3 py-1.5 flex items-center gap-2 text-xs",children:[n.jsxs("span",{className:"text-fg-faint",children:["merged · overrides → ",n.jsx("span",{className:"text-fg-dim",children:N[r].label})]}),y&&n.jsxs("label",{className:"flex items-center gap-1 text-fg-faint cursor-pointer",children:[n.jsx("input",{type:"checkbox",checked:u,onChange:t=>o(t.target.checked),className:"accent-accent"}),"show unset keys"]}),n.jsx("div",{className:"flex-1"}),n.jsx("input",{value:f,onChange:t=>x(t.target.value),placeholder:"filter keys or descriptions…",className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg w-56"})]}),n.jsx("div",{className:"flex-1 overflow-auto px-3 py-2 font-mono text-xs",children:d&&!u?n.jsx("div",{className:"text-fg-faint italic px-2 py-4",children:"no settings resolved — all scope files empty or missing"}):n.jsx(E,{node:e,path:g,label:m??"(root)",targetScope:r,onOverride:s,filter:f,depth:0,schema:a,showGhosts:u,defaultOpen:!0})})]})}function E(e){return e.node.kind==="object"?n.jsx(P,{...e,node:e.node}):e.node.kind==="array"?n.jsx(R,{...e,node:e.node}):n.jsx(J,{...e,node:e.node})}function A(e,r){return r.trim()?e.toLowerCase().includes(r.toLowerCase()):!0}function w(e,r,s){return!!(!s.trim()||e.some(a=>A(a,s))||r!=null&&r.description&&A(r.description,s))}function P({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u,showGhosts:o,defaultOpen:g}){const[d,h]=b.useState(g??x<2),y=(u==null?void 0:u.at(r))??null,t=new Set(Object.keys(e.children)),i=o?(y==null?void 0:y.knownChildren)??[]:[],c=Array.from(new Set([...t,...i])).sort(),l=b.useMemo(()=>f.trim()?c.filter(p=>{const v=(u==null?void 0:u.at([...r,p]))??null;return w([...r,p],v,f)?!0:t.has(p)?L(e.children[p],[...r,p],f,u):!1}):c,[c.join(","),f,e,r.join("."),u]);if(f.trim()&&l.length===0&&!w(r,y,f))return null;if(r.length===0)return n.jsx("div",{children:l.map(p=>j(p))});return n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:x*14},onClick:()=>h(!d),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:d?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" {",t.size,i.length>t.size?`/${new Set([...t,...i]).size}`:"","}"]}),(y==null?void 0:y.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:y.description,children:["— ",y.description]})]}),d&&l.map(p=>j(p))]});function j(p){if(t.has(p))return n.jsx(E,{node:e.children[p],path:[...r,p],label:p,targetScope:a,onOverride:m,filter:f,depth:x+1,schema:u,showGhosts:o},p);const v=(u==null?void 0:u.at([...r,p]))??null;return v?n.jsx(I,{path:[...r,p],label:p,info:v,targetScope:a,onSet:m,depth:x+1,filter:f},p):null}}function L(e,r,s,a){const m=(a==null?void 0:a.at(r))??null;if(w(r,m,s))return!0;if(e.kind!=="object")return!1;for(const f of Object.keys(e.children))if(L(e.children[f],[...r,f],s,a))return!0;return!1}function R({node:e,path:r,label:s,filter:a,depth:m,schema:f}){const[x,u]=b.useState(!0),o=(f==null?void 0:f.at(r))??null;return w(r,o,a)?n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:m*14},onClick:()=>u(!x),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:x?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" [",e.items.length,"]"]}),n.jsx(S,{tone:"warn",children:"accumulated"}),(o==null?void 0:o.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:o.description,children:["— ",o.description]})]}),x&&e.items.map((g,d)=>n.jsxs("div",{className:"flex items-start gap-2 py-0.5",style:{paddingLeft:(m+1)*14},children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0"}),n.jsxs("span",{className:"text-fg-faint",children:[d,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-xl",children:O(g.value)}),n.jsx(C,{scope:g.fromScope})]},d))]}):null}function J({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u}){const[o,g]=b.useState(!1),d=(u==null?void 0:u.at(r))??null;if(!w(r,d,f))return null;const h=e.winner!==a,y=e.shadowed.length>0;return n.jsxs("div",{children:[n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded",style:{paddingLeft:x*14},children:[n.jsx("span",{className:`w-3 shrink-0 ${y?"text-fg-faint cursor-pointer":""}`,onClick:()=>y&&g(!o),children:y?o?"▾":"▸":""}),n.jsxs("span",{className:"text-fg",children:[s,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-md",children:O(e.value)}),n.jsx(C,{scope:e.winner}),(d==null?void 0:d.deprecated)&&n.jsx(S,{tone:"warn",children:"deprecated"}),(d==null?void 0:d.default)!==void 0&&_(e.value,d.default)&&n.jsx(S,{tone:"dim",children:"= default"}),e.typeMismatch&&n.jsx("span",{className:"text-yellow-500/80",title:"shadowed scopes have a different value type",children:"⚠"}),(d==null?void 0:d.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:d.description,children:["— ",d.description]}),n.jsx("div",{className:"flex-1"}),(d==null?void 0:d.enum)&&n.jsx(F,{values:d.enum}),n.jsx("button",{disabled:!h,onClick:()=>m(r,e.value),className:`opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border rounded transition-opacity ${h?"border-accent/50 text-accent hover:bg-accent hover:text-bg":"border-line text-fg-faint cursor-not-allowed"}`,title:h?`copy into ${N[a].label} draft`:`already winning in ${N[a].label}`,children:"override"})]}),o&&e.shadowed.map((t,i)=>n.jsxs("div",{className:"flex items-center gap-2 py-0.5 text-fg-faint line-through",style:{paddingLeft:(x+1)*14+12},children:[n.jsx("span",{children:"shadowed:"}),n.jsx("span",{className:"truncate max-w-xl",children:O(t.value)}),n.jsx(C,{scope:t.scope,dim:!0})]},i))]})}function I({path:e,label:r,info:s,targetScope:a,onSet:m,depth:f,filter:x}){if(!w(e,s,x))return null;const u=s.default!==void 0?s.default:M(s.type),o=Array.isArray(s.type)?s.type.join("|"):s.type??"?";return n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded opacity-60",style:{paddingLeft:f*14},children:[n.jsx("span",{className:"w-3 shrink-0"}),n.jsx("span",{className:"text-fg-dim",children:r}),n.jsx(S,{tone:"dim",children:o}),s.required&&n.jsx(S,{tone:"warn",children:"required"}),s.default!==void 0&&n.jsxs("span",{className:"text-fg-faint",children:["default: ",O(s.default)]}),s.description&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:s.description,children:["— ",s.description]}),n.jsx("div",{className:"flex-1"}),s.enum&&n.jsx(F,{values:s.enum}),n.jsx("button",{onClick:()=>m(e,u),className:"opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border border-accent/50 rounded text-accent hover:bg-accent hover:text-bg transition-opacity",title:`write to ${N[a].label} draft`,children:"set"})]})}function _(e,r){return e===r?!0:typeof e!=typeof r?!1:e&&r&&typeof e=="object"?JSON.stringify(e)===JSON.stringify(r):!1}function M(e){switch(Array.isArray(e)?e[0]:e){case"boolean":return!1;case"integer":case"number":return 0;case"string":return"";case"array":return[];case"object":return{};case"null":return null;default:return null}}function O(e){if(typeof e=="string")return JSON.stringify(e);if(e===null)return"null";if(typeof e=="object"){const r=JSON.stringify(e);return r.length>100?r.slice(0,100)+"…":r}return String(e)}function C({scope:e,dim:r}){const s=e==="user"?"bg-bg-hi text-fg-dim border-line":e==="project"?"bg-blue-950/30 text-blue-400/80 border-blue-900/40":"bg-purple-950/30 text-purple-400/80 border-purple-900/40";return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s} ${r?"opacity-60":""}`,children:N[e].label})}function S({children:e,tone:r="default"}){const s={default:"bg-bg-hi text-fg-dim border-line",warn:"bg-yellow-950/30 text-yellow-500/80 border-yellow-900/40",dim:"bg-bg-elev text-fg-faint border-line"}[r];return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s}`,children:e})}function F({values:e}){const r=e.map(s=>String(s)).join(" | ");return n.jsxs("span",{className:"text-[10px] text-fg-faint truncate max-w-xs",title:`allowed: ${r}`,children:["∈ ",r.length>30?r.slice(0,30)+"…":r]})}function G(e,r,s){const a=k(o=>o.files),m=k(o=>o.loadJson),f=k(o=>o.watchFile),x=k(o=>o.unwatchFile),u=b.useMemo(()=>{const o={};for(const g of e.scopes){const d=e.resolve(g,r??"",s);d&&(o[g]=d)}return o},[e,r,s]);return b.useEffect(()=>{const o=Object.values(u).filter(Boolean);return o.forEach(g=>{a[g]||m(g),f(g)}),()=>{o.forEach(g=>x(g))}},[JSON.stringify(u)]),u}function z(e){const r=e??{},s={...r.$defs??{},...r.definitions??{}},a=t=>{if(!t)return{};if(!t.$ref)return x(t);const i=/^#\/(\$defs|definitions)\/(.+)$/.exec(t.$ref);if(!i)return x(t);const c=s[i[2]];return c?a({...c,...u(t)}):x(t)},m=new Set(["__proto__","constructor","prototype"]),f=t=>{if(!t)return{};const i={};for(const[c,l]of Object.entries(t))m.has(c)||(i[c]=l);return i},x=t=>{if(!t.allOf||t.allOf.length===0)return t;const i={...t};delete i.allOf;for(const c of t.allOf){const l=a(c);Object.assign(i,{properties:{...f(i.properties),...f(l.properties)},required:B([...i.required??[],...l.required??[]])}),!i.description&&l.description&&(i.description=l.description),i.default===void 0&&l.default!==void 0&&(i.default=l.default),!i.type&&l.type&&(i.type=l.type)}return i},u=t=>{const{$ref:i,...c}=t;return c},o=(t,i)=>{let c=a(t);for(const l of i){if(!c)return null;if(/^\d+$/.test(l)){const v=Array.isArray(c.items)?c.items[Number(l)]:c.items;c=v?a(v):null;continue}const j=c.properties;if(j&&!m.has(l)&&l in j){c=a(j[l]);continue}const p=c.additionalProperties;if(p&&typeof p=="object"){c=a(p);continue}return null}return c},g=(t,i,c)=>{if(!t)return null;const l={};if(t.description&&(l.description=t.description),t.type&&(l.type=t.type),t.default!==void 0&&(l.default=t.default),t.enum&&(l.enum=t.enum),t.const!==void 0&&(l.const=t.const),t.deprecated&&(l.deprecated=!0),t.examples&&(l.examples=t.examples),t.properties&&(l.knownChildren=Object.keys(t.properties)),c&&(i!=null&&i.includes(c))&&(l.required=!0),t.items&&!Array.isArray(t.items)){const j=a(t.items),p=g(j,void 0,null);p&&(l.itemsInfo=p)}return(t.additionalProperties===!0||typeof t.additionalProperties=="object")&&(l.open=!0),l},d=t=>i=>{if(i.length===0)return g(a(t),void 0,null);const c=o(t,i.slice(0,-1));if(!c)return null;const l=i[i.length-1],j=o(c,[l]);return g(j,c.required,l)},h=t=>{const i=a(t);return Object.keys(i.properties??{})},y=t=>({at:d(t),rootKeys:()=>h(t),rootedAt:i=>{const c=o(t,i);return y(c??{})}});return y(r)}function B(e){return Array.from(new Set(e))}let $=null;function V(){return $||($=z(K)),$}export{T as E,V as s,G as u};
package/dist/index.html CHANGED
@@ -7,7 +7,7 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
9
  <link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;1,6..72,400&family=Geist:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
10
- <script type="module" crossorigin src="./assets/index-Yf8e7VCB.js"></script>
10
+ <script type="module" crossorigin src="./assets/index-XkrLB67D.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="./assets/monaco-editor-BW5C4Iv1.js">
12
12
  <link rel="stylesheet" crossorigin href="./assets/monaco-editor-BTnBOi8r.css">
13
13
  <link rel="stylesheet" crossorigin href="./assets/index-CKH5Uxik.css">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-session-manager",
3
- "version": "0.95.0",
3
+ "version": "0.96.0",
4
4
  "description": "Local cockpit for the Claude Code CLI — multi-tab terminal, full config surface, scheduler, voice dictation, and live observability.",
5
5
  "type": "module",
6
6
  "main": "src/main/index.cjs",
@@ -20,6 +20,10 @@
20
20
  "scripts/hooks/guard-destructive-git.cjs",
21
21
  "scripts/hooks/guard-inline-implementation.cjs",
22
22
  "scripts/hooks/guard-self-schedule.cjs",
23
+ "scripts/hooks/lib/guard-destructive-git-policy.cjs",
24
+ "scripts/hooks/lib/guard-self-schedule-policy.cjs",
25
+ "scripts/hooks/lib/guard-prd-writes-policy.cjs",
26
+ "scripts/hooks/lib/guard-inline-implementation-policy.cjs",
23
27
  "src/main/",
24
28
  "src/preload/",
25
29
  "src/seed/",
@@ -39,6 +43,7 @@
39
43
  "prepack": "node scripts/write-build-info.cjs",
40
44
  "prepublishOnly": "node scripts/write-build-info.cjs && vite build",
41
45
  "test:unit": "vitest run",
46
+ "test:unit:stability": "bash scripts/test-unit-stability.sh",
42
47
  "bench": "node scripts/bench/main-bench.cjs",
43
48
  "bench:renderer": "vitest run --config vitest.bench.config.ts",
44
49
  "test:e2e": "xvfb-run -a playwright test",
@@ -58,7 +63,8 @@
58
63
  "lint:any": "node scripts/check-explicit-any.cjs",
59
64
  "lint:main-ts-check": "node scripts/check-main-ts-check.cjs",
60
65
  "lint:filenames": "node scripts/check-tracked-filenames.cjs",
61
- "lint": "npm run lint:selectors && npm run lint:hooks && npm run lint:unregistered-tests && npm run lint:docs && npm run lint:paths && npm run lint:any && npm run lint:main-ts-check && npm run lint:filenames",
66
+ "lint:renderer-storage": "node scripts/check-renderer-storage.cjs",
67
+ "lint": "npm run lint:selectors && npm run lint:hooks && npm run lint:unregistered-tests && npm run lint:docs && npm run lint:paths && npm run lint:any && npm run lint:main-ts-check && npm run lint:filenames && npm run lint:renderer-storage",
62
68
  "sync:settings-schema": "node scripts/sync-settings-schema.cjs"
63
69
  },
64
70
  "keywords": [
package/plugins/CLAUDE.md CHANGED
@@ -49,9 +49,9 @@ for live sibling consumers.
49
49
  - Do not confuse this with `.claude/` at the repo root — this repo's own unshipped local dev
50
50
  config. `.claude/agents/*.md` are per-project OVERLAYS that win over
51
51
  `~/.claude/agents/*.md` (Claude Code's own precedence, per `src/main/agentLibrary.cjs`);
52
- `src/seed/agents/` is the shipped source for exactly the three personas
53
- `src/main/seedAgentPersonas.cjs` copies into `~/.claude/agents/` on first boot (`architect`,
54
- `dev-lead`, `project-home-builder`); `builder` and `bilko-host-publisher` have no seed source
52
+ `src/seed/agents/` is the shipped source for the four personas
53
+ `src/main/seedAgentPersonas.cjs` copies into `~/.claude/agents/` on first boot (see its
54
+ `PERSONAS` list); `builder` and `bilko-host-publisher` have no seed source
55
55
  and exist only as this repo's overlays. Tracked on purpose: `.claude/settings.json` (guard
56
56
  hooks) plus the `builder.md`/`project-home-builder.md` overlays (auditable edits) —
57
57
  `bilko-host-publisher.md` is untracked despite being in use; nothing else under `.claude/` is.
@@ -3,7 +3,8 @@ name: develop
3
3
  description: >-
4
4
  Lead a software-development task by analyzing it from multiple angles (positive path, edge
5
5
  cases, interaction effects, integration, UI validation) and decomposing it into a series of
6
- self-contained PRDs — either a handful of independent small PRDs, or a 3-5 PRD evolving chain
6
+ self-contained PRDs, sized as <10-minute Sonnet-executable work-items — either a handful of
7
+ independent small PRDs, or a 3-5 PRD evolving chain
7
8
  with sub-tasked acceptance criteria for larger asks — queued for the session-manager
8
9
  scheduler, each pointing the headless executor at the engineering standards file to read at
9
10
  runtime — then track those PRDs to completion, verify them against their acceptance criteria,
@@ -200,6 +201,23 @@ can't load skills.
200
201
  completeness pass instead of extending the chain further; a chain that long is a sign the
201
202
  original decomposition was wrong, not that it needs one more link. A chain does not relax
202
203
  the ~15-min/30-min-ceiling sizing below — each link is still individually small.
204
+
205
+ **Every plan ends with one `validate` PRD.** After the work-item PRDs are written, author
206
+ exactly one more through `scheduler_create_prd`: `agentType: "validator"`, `tag: "build"`,
207
+ `estimateMinutes: 10`, `dependsOn` = every other slug in this plan, slug
208
+ `validate-<short-plan-name>`. Its Goal lists the plan's PRD slugs and titles; its Acceptance
209
+ criteria give, per PRD, where its file lives
210
+ (`<cwd>/session-manager-operations/scheduler/epics/<epic-id>/prds/<NN>-<slug>.md` while queued,
211
+ the sibling `prds-archived/` once terminal — locate by slug in either) and end with the
212
+ review-record path to write and commit:
213
+ `session-manager-operations/reviews/validation/<epic-id>/<validate-slug>.md`.
214
+ Implementation notes: "Work as the validator persona — the procedure is your system prompt."
215
+ While a plan has a pending validator, the scheduler suppresses both the per-PRD validation
216
+ prompt into this session and the in-run `/code-review` steps for its work-items — one review,
217
+ once, at the end. If `scheduler_create_prd` rejects `agentType: "validator"` (persona not
218
+ installed on this machine yet), say so and fall back to today's per-PRD validation; do not
219
+ hand-write the file.
220
+
203
221
  - **Sub-tasked Acceptance Criteria** (either shape, when a single PRD legitimately spans more
204
222
  than one concern dimension from step 3 — e.g. it has both core-functionality and
205
223
  edge-case/interaction-effect checks): group the `# Acceptance criteria` checklist under
@@ -355,9 +373,9 @@ can't load skills.
355
373
 
356
374
  - [ ] <each line is a verifiable check the executor can run after building>
357
375
  - [ ] <include explicit file paths, function names, expected behavior>
358
- - [ ] a bounded test command passes, e.g. `timeout 300 npm run typecheck` / `pytest -x` /
359
- `cargo check` (the run-before-done / never-end-on-red rule lives in standards.md →
360
- Execution discipline; the AC just has to name the command).
376
+ - [ ] exactly one gate line — a bounded command or an `&&` chain of at most two, e.g.
377
+ `timeout 300 npm run typecheck && timeout 300 npx vitest run <file>` (the run-before-done
378
+ rule lives in standards.md; the AC just names the command).
361
379
 
362
380
  # Implementation notes
363
381
 
@@ -383,17 +401,31 @@ can't load skills.
383
401
  "the design we discussed"; if a PRD depends on another PRD's output, say so in
384
402
  `# Implementation notes` AND give it a higher `NN` so it queues after.
385
403
 
386
- **Scope sizing — keep it SMALL (data-driven, 2026-06).** Across 400+ real runs the median
387
- PRD finishes in **~7 minutes**, p90 **~21 min**, p99 **~66 min** — yet authored
388
- `estimateMinutes` ran 5–8× too high. Oversized scoping anchors PRDs too big and pushes them
389
- into the rare >60-min tail where ~100% of true hangs live (deploy poll-loops, unbounded e2e
390
- suites). Target ~15 minutes of wall-clock work per PRD — **hard ceiling ~30 min; if you
391
- project more, SPLIT** into sequential `NN` PRDs and document the dependency in each. Set
392
- `estimateMinutes` realistically: **p50≈8, p90≈21** — don't write 60/90, it's almost always
393
- wrong and hides real outliers. Each execution costs ~$0.50–$2; smaller PRDs = smaller blast
394
- radius when a run is rate-limited, timed out, or killed. **`rateLimited` exit-1 is NOT a
395
- failure** — it's the scheduler's designed auto-pause; the job auto-resumes at the next
396
- window reset. Don't add retry logic for it.
404
+ **Work-item shape — every PRD must be executable by a Sonnet-class executor in under 10
405
+ minutes (2026-09 calibration: wall p50 7.8 min, 60% of runs ≤ 10 min; authored estimates ran
406
+ 4× high).**
407
+ - **One change-set per PRD**: ≤ 3 files edited, ≤ 1 new file, exactly ONE gate line (a
408
+ bounded command or an `&&` chain of at most two). If a PRD needs a new shared helper AND
409
+ its first consumer, that is two PRDs (`primitive` → `wire`).
410
+ - **≤ 6 AC lines**: each behavior line names file + symbol + the observable result; exactly
411
+ one tests line naming the test file and the test names; exactly one gate line. No
412
+ open-ended lines ("grep X and update whatever depends on it") — resolve the list yourself
413
+ while authoring and name the files.
414
+ - **Implementation notes are a recipe, not prose**: `Read first:` (≤ 4 files, with line
415
+ ranges), `Steps:` (numbered, each naming the file and the function/signature), `Do not
416
+ touch:` (files a sibling PRD owns). Quote a signature rather than describing it.
417
+ - **`estimateMinutes` ≤ 10 target, 15 ceiling** — project more, split. (The scheduler's kill
418
+ budget floors at 45 min regardless, so a low estimate never starves a run.)
419
+ - **Decomposition types** — name one per PRD in its first Goal sentence, and chain in this
420
+ order when several apply:
421
+ - `primitive` — one new helper/module + its unit test, no call sites.
422
+ - `wire` — adopt an existing primitive at named call sites, no logic change.
423
+ - `behavior` — one function's logic change + the test that pins it.
424
+ - `migration` — mechanical rename/move/config change, no logic; gate is typecheck/lint.
425
+ - `doc` — text only; gate is `lint:docs` or the doc's own test.
426
+ - `validate` — the plan's trailing validation PRD (see Phase 2).
427
+ - `scheduler_create_prd` returns `warnings[]` when a PRD exceeds these limits — fix the PRD
428
+ before confirming it to the user; never queue a warned PRD silently.
397
429
 
398
430
  5. **Emit each PRD.** If you used `scheduler_create_prd`, this step is already done — the tool
399
431
  wrote the file to the canonical path with the standards pointer included; skip to step 5.
@@ -414,64 +446,48 @@ can't load skills.
414
446
 
415
447
  6. **Confirm to the user**, per emitted PRD: filename, chosen `NN` + rationale
416
448
  (parallel-with-X / serial-after-Y), `cwd`, and an ETA + token-cost ballpark. Note they can
417
- "Run now" in the SchedulePanel or wait for `when-available` polling.
418
-
419
- ## Phase 2 — Track to completion (reusable tail)
420
-
421
- The queued PRDs run headlessly and can take a while. Don't fire-and-forget, and don't block —
422
- hand off to a recurring check. An approved proposal delegates to this exact phase, so it is the
423
- single definition of "tracked to done" for both entry paths.
424
-
425
- 7. **Watch the scheduler every ~30 min.** Start a 30-minute monitoring loop (`/loop 30m` over
426
- this watch step, or a `ScheduleWakeup` at 1800s if self-pacing) scoped to the PRD ids you
427
- emitted. On each tick, read the scheduler's job status (queue + run history under
428
- `~/.claude/session-manager/scheduled-plans/`, or the SchedulePanel) and branch:
429
- - **Still queued / running, within its window** — leave it; re-check next tick.
430
- - **Failed / errored / `needs_review` / timed out / killed by the watchdog or supervisor /
431
- overran its estimate badly** — STOP waiting and surface it now: which PRD, the failure
432
- signal, the relevant log tail, and the likely cause (a stuck poll-loop or post-AC overrun
433
- per `PRD_AUTHORING.md`). Don't silently retry forever. For `needs_review`, the scheduler
434
- auto-files a Root Cause Analysis into the target project's feedback inbox
435
- (`rcaFeedbackHook`, filename `<date>-rca-<slug>-<runId>.md`) — reference that file in
436
- your report rather than re-deriving the analysis, and let the approving user fold its
437
- prevention hint back into future PRD authoring. A `rateLimited` exit-1 is the
438
- scheduler's benign auto-pause (auto-resumes next window) — keep waiting, don't escalate.
439
- - **All PRDs completed successfully** — go to step 8.
440
-
441
- 8. **Gate: definition of done** (same for both entry paths). Once the code has landed:
442
- - **Verify live against each PRD's acceptance criteria** — run the health check, hit the
443
- endpoint, show before/after. The headless run asserted its own test command; this is the
444
- interactive confirmation it actually does what was asked.
445
- - **Route to the specialist that actually matches what changed** — not always the generic
446
- reviewer. This environment has dedicated agents that sit unused unless explicitly called;
447
- match the PRD's surface to the right one before calling a major/risky change done:
448
- - Touches an API's request/response shape, REST/GraphQL contract, or endpoint design →
449
- dispatch `api-designer` (Agent tool).
450
- - Touches auth, input handling, secrets, or data storage → dispatch `security-auditor` in
451
- addition to the mandatory security AC the PRD's own execution already required — the
452
- auditor catches what the executor's self-check might miss.
453
- - Is a structural refactor (no behavior change intended) → dispatch `refactorer`.
454
- - Is performance-sensitive or touches a hot path → dispatch `perf-profiler`.
455
- - Adds or updates a dependency → dispatch `dependency-auditor`.
456
- - Touches a database schema, migration, or table design → **no specialist exists for this
457
- in this environment today.** Don't silently let the generic code-reviewer stand in for a
458
- schema review it isn't specialized for — say so explicitly in your report ("schema change,
459
- no dedicated reviewer available, manual review recommended") rather than implying coverage
460
- that isn't there.
461
- - Anything else, or a **major feature/risky change** not covered above — dispatch the
462
- generic `requesting-code-review` skill (`code-reviewer` agent) as the default.
463
- Fix Critical/Important findings from whichever specialist(s) ran before calling it done.
464
- - **Report back**: what landed, PRD/commit refs, which specialist(s) reviewed it (or the
465
- explicit "no specialist available" note), verification result, anything left open.
449
+ "Run now" in the SchedulePanel or wait for `when-available` polling. Name the plan's validate
450
+ PRD and state that no per-PRD validation will be requested from this session.
451
+
452
+ ## Phase 2 — Validation runs as its own job; this session only decides
453
+
454
+ Every plan queued in Phase 1 ends with a `validate` PRD (agentType `validator`) whose
455
+ `dependsOn` lists every other slug in the plan, so the scheduler runs it exactly once, after the
456
+ last work-item lands. That job — not this session — re-runs each PRD's gate, checks every
457
+ acceptance criterion against the tree, reviews the plan's combined diff (`/code-review`,
458
+ `/security-review`), commits a review record, and ends with `VALIDATION: <slug>
459
+ VERIFIED|REFUTED` per PRD. The scheduler appends one verdict event per PRD to this Epic (the
460
+ traffic light reads them) plus one check-in for the validator itself.
461
+
462
+ 7. **Do not poll and do not re-verify per PRD.** Completed work-items arrive here as check-in
463
+ events only; leave them alone. There is no 30-minute loop to start and no `ScheduleWakeup` to
464
+ arm. Act on exactly three signals:
465
+ - **A work-item parks `needs_review` / `failed`** — read the scheduler's auto-filed RCA
466
+ (`<date>-rca-<slug>-<runId>.md` in the project's feedback inbox) and decide: fix the PRD
467
+ (`scheduler_update_prd`) and `scheduler_reset_job`, or `scheduler_archive_prd` it. The
468
+ plan's validator waits behind it. A `rateLimited` exit is the scheduler's benign auto-pause
469
+ — not a signal.
470
+ - **The validator's check-in arrives** — read its review record. Every PRD VERIFIED and no
471
+ Critical/Important finding → the plan is done; report what landed (slugs, commits, record
472
+ path). Any REFUTED PRD or Critical/Important finding → queue a fix wave: one `behavior`/
473
+ `wire` PRD per finding (recipe format, ≤ 10 min each), no `dependsOn`, plus a new trailing
474
+ `validate` PRD for the wave. Do not fix inline.
475
+ - **The validator itself parks** — treat it like any parked job; its RCA says why the
476
+ procedure could not run.
477
+
478
+ 8. **Definition of done** = the plan's latest validator reported every PRD VERIFIED with no open
479
+ Critical/Important finding, and its review record is committed. Report: what landed (PRD
480
+ slugs, commits), the record path, findings deferred as Minor, anything left open. A plan with
481
+ a REFUTED PRD is never "done with caveats" — it gets a fix wave or an explicit human decision
482
+ to stop.
466
483
 
467
484
  ## References (reuse, don't duplicate)
468
485
 
469
486
  - `~/.claude/session-manager/scheduled-plans/PRD_AUTHORING.md` — the §1–§10 safety rules.
470
487
  - `standards.md` beside this file — the engineering + execution-discipline rules. Every PRD points the executor at its absolute path (see "Standards" above) rather than embedding a copy.
471
488
  - `test-driven-development`, `systematic-debugging` — interactive dev sessions.
472
- - `requesting-code-review` — the Phase-2 default review gate; `api-designer`, `security-auditor`,
473
- `refactorer`, `perf-profiler`, `dependency-auditor` — specialist agents routed to by surface
474
- area (see step 8) rather than always defaulting to the generic reviewer.
489
+ - `validator` persona (src/seed/agents/validator.md) — the plan-level review point for scheduled
490
+ work; `requesting-code-review` — interactive sessions reviewing their own inline work only.
475
491
 
476
492
  ## Notes
477
493
 
@@ -1,16 +1,10 @@
1
1
  # Engineering standards
2
2
 
3
- > Single source of truth for the developer guidance that used to live in the global
4
- > `~/.claude/CLAUDE.md`. Consumers: the `/develop` skill reads it while planning and
5
- > inlines it **verbatim** into every PRD it emits (under an `## Engineering standards`
6
- > heading); the `/prd` command points here for the execution-discipline rules so a
7
- > directly-authored PRD carries the same block. The headless `claude -p` executor sees no
8
- > skills and no conversation — inlining this is the only way these rules reach it. Edit
9
- > here once; every call site updates.
10
- >
11
- > The **Execution discipline** section below is the executor-facing core — it is the part
12
- > that MUST appear in every PRD body. The rest (Performance, Debugging, API reuse, TDD)
13
- > guides authoring and interactive work.
3
+ > Single source of truth for the engineering rationale behind every scheduled PRD.
4
+ > `scheduler_create_prd` (src/main/lib/prdCreate.cjs) appends a one-line pointer to this file's
5
+ > absolute path to every PRD — never a copy — and the executor's ordered run contract lives in
6
+ > the `dev-lead` persona it is launched as. Read a section here when a contract line needs its
7
+ > reasoning; edit here once and every PRD sees it.
14
8
 
15
9
  ## Performance
16
10
 
@@ -76,21 +76,11 @@ You: [Fix progress indicators]
76
76
 
77
77
  ## Integration with /develop
78
78
 
79
- This is the **default** reviewer at `/develop`'s Phase 2 done-gate (step 8) — but not the only
80
- one. `/develop` routes to a matching specialist first when a PRD's surface calls for it
81
- (`api-designer` for API-contract changes, `security-auditor` for auth/input/data, `refactorer`
82
- for structural refactors, `perf-profiler` for hot-path changes, `dependency-auditor` for
83
- dependency bumps) and only falls back to this generic reviewer for everything else or when no
84
- specialist fits. Don't treat this skill as covering ground those specialists already own.
85
-
86
- Since this project's actual unit of work is a scheduler PRD (not a live multi-task session), the
87
- review point is per-PRD, at its own commit range — `BASE_SHA`/`HEAD_SHA` above are that PRD's
88
- commit(s), not an arbitrary task boundary from a different workflow shape. Review:
89
- - **After a PRD completes**, as part of `/develop`'s definition-of-done gate (step 8) — every
90
- major/risky PRD, before calling it done.
91
- - **When a PRD comes back `needs_review`** — read the scheduler's auto-filed RCA first
92
- (`rcaFeedbackHook`'s `<date>-rca-<slug>-<runId>.md`), then use this review to confirm the fix
93
- if one gets queued.
79
+ For scheduled work, review happens once per plan in the trailing `validate` PRD (persona
80
+ `validator`), which runs `/code-review` and `/security-review` over the plan's combined diff and
81
+ reports per-PRD VERIFIED/REFUTED verdicts. This skill is for an interactive session reviewing its
82
+ own inline work. Never dispatch a `code-reviewer` subagent from a headless run — it has no next
83
+ turn to collect the result.
94
84
 
95
85
  ## Red Flags
96
86
 
package/scripts/README.md CHANGED
@@ -15,6 +15,7 @@ scripts/lib was moved into src/main/lib (2026-09); nothing under src/ may requir
15
15
  | check-doc-hierarchy.cjs | `npm run lint:docs` (in `lint`) | no |
16
16
  | check-explicit-any.cjs | `npm run lint:any` (in `lint`) — bans explicit `any`/`@ts-ignore` in renderer+preload; per-line `// lint-allow-any: <reason>` | no |
17
17
  | check-main-ts-check.cjs | `npm run lint:main-ts-check` (in `lint`) — `tsconfig.main.json` `include` ⇔ `// @ts-check` first line, both directions; explicit list only, `checkJs` false | no |
18
+ | check-renderer-storage.cjs | `npm run lint:renderer-storage` (in `lint`) — ratchet-bans `localStorage.`/`sessionStorage.` in `src/renderer` outside an explicit ALLOWLIST (Epic stateless-ui-review) | no |
18
19
  | check-tracked-filenames.cjs | `npm run lint:filenames` (in `lint`) — fails on tracked paths with Windows-illegal chars, trailing dot/space, or reserved device names | no |
19
20
  | check-unregistered-tests.cjs | `npm run lint:unregistered-tests` (in `lint`) | no |
20
21
  | check-unstable-selectors.cjs | `npm run lint:selectors` (in `lint`) | no |