[object Object]

← back to Agent Cabinet

Approve auto-commits cabinet.yaml (path-limited, local, Steve+Claude co-author); commit hash surfaced in toast

2f295b3a5e692bafe69ec0e7944e17e4fda4c493 · 2026-06-16 15:50:29 -0700 · SteveStudio2

Files touched

Diff

commit 2f295b3a5e692bafe69ec0e7944e17e4fda4c493
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Tue Jun 16 15:50:29 2026 -0700

    Approve auto-commits cabinet.yaml (path-limited, local, Steve+Claude co-author); commit hash surfaced in toast
---
 pyramid.html |  2 +-
 server.js    | 32 ++++++++++++++++++++++++++------
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/pyramid.html b/pyramid.html
index 29889dd..c721e57 100644
--- a/pyramid.html
+++ b/pyramid.html
@@ -507,7 +507,7 @@ async function decide(key,action){
     if(!r.ok){ toast('failed: '+(r.error||'?')); return; }
     SUG_STATE=r.decisions||SUG_STATE; drawCross();
     const g=SUGGEST.find(x=>sKey(x)===key)||{};
-    toast(action==='approve'?(r.cabinet&&r.cabinet.already?'already in cabinet.yaml':'✓ approved — added under '+offName(g.to)+' in cabinet.yaml'):action==='remove'?'✕ suggestion removed':'↺ decision reset');
+    toast(action==='approve'?(r.cabinet&&r.cabinet.already?'already in cabinet.yaml':('✓ approved — committed '+(r.commit||'(no hash)')+' · added under '+offName(g.to))):action==='remove'?'✕ suggestion removed':'↺ decision reset');
     if(document.getElementById('dr-title').textContent==='Cross-officer analysis') showAnalysis();
     else if(action==='remove') closePanel();
     else openSuggestion(key);
diff --git a/server.js b/server.js
index d484df7..3a54b99 100644
--- a/server.js
+++ b/server.js
@@ -95,6 +95,21 @@ function applyToCabinet(s) {
   return { added: true };
 }
 
+// Auto-commit ONLY cabinet.yaml (path-limited, so other dirty files aren't swept
+// in). Local commit, no push. Author = Steve, with Claude co-author trailer.
+function gitCommitCabinet(s, cb) {
+  const msg = 'cabinet: approve cross-officer ' + s.kind + ' ' + s.id + ' under ' + s.to +
+    ' (from ' + s.from + ') via pyramid\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>';
+  execFile('git', ['-C', ROOT, 'add', 'cabinet.yaml'], (e1) => {
+    if (e1) return cb(e1);
+    execFile('git', ['-C', ROOT, '-c', 'user.name=Steve Abrams', '-c', 'user.email=steve@designerwallcoverings.com',
+      'commit', '-m', msg, '--', 'cabinet.yaml'], { timeout: 15000 }, (e2) => {
+      if (e2) return cb(e2);
+      execFile('git', ['-C', ROOT, 'rev-parse', '--short', 'HEAD'], (e3, out) => cb(e3, (out || '').trim()));
+    });
+  });
+}
+
 function parseYaml(text) {
   // Tiny YAML subset parser — good enough for cabinet.yaml's shape.
   // Produces {president, cabinet:[{vp, domain, triggers:[], directors:[{skill?,subagent?,owns?}]}]}
@@ -592,12 +607,17 @@ const server = http.createServer((req, res) => {
     if (!s) return J({ ok: false, error: 'unknown suggestion' });
     if (!['approve', 'remove', 'reset'].includes(action)) return J({ ok: false, error: 'bad action' });
     const st = loadSugState(); st.decisions = st.decisions || {};
-    let cab;
-    if (action === 'approve') { st.decisions[key] = 'approved'; cab = applyToCabinet(s); }
-    else if (action === 'remove') { st.decisions[key] = 'removed'; }
-    else { delete st.decisions[key]; }
-    saveSugState(st);
-    return J({ ok: true, action, decisions: st.decisions, cabinet: cab });
+    if (action === 'remove') { st.decisions[key] = 'removed'; saveSugState(st); return J({ ok: true, action, decisions: st.decisions }); }
+    if (action === 'reset')  { delete st.decisions[key];      saveSugState(st); return J({ ok: true, action, decisions: st.decisions }); }
+    // approve: record, write into cabinet.yaml, then auto-commit (only if newly added)
+    st.decisions[key] = 'approved'; saveSugState(st);
+    const cab = applyToCabinet(s);
+    if (cab && cab.added) {
+      gitCommitCabinet(s, (gerr, hash) => J({ ok: true, action, decisions: st.decisions, cabinet: cab, commit: gerr ? null : hash, gitError: gerr ? String(gerr) : undefined }));
+    } else {
+      J({ ok: true, action, decisions: st.decisions, cabinet: cab });
+    }
+    return;
   }
 
   if (req.url === '/cabinet.yaml') {

← d87e58a Pyramid: clickable suggestion lines — click a dotted arrow t  ·  back to Agent Cabinet  ·  Suggestion lines truly clickable: amber ◆ handle at each lin e46f975 →