@yemi33/minions 0.1.2414 → 0.1.2416
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.
package/dashboard/js/refresh.js
CHANGED
|
@@ -185,7 +185,7 @@ const RENDER_VERSIONS = {
|
|
|
185
185
|
// Bumped to 4 by W-mqrdavob (Agents table: per-agent charter editor column).
|
|
186
186
|
// Bumped to 5 by W-mr0qs0vw (Worker Pool: CC session idle-timeout field).
|
|
187
187
|
// Bumped to 6 for the fleet ACP pool opt-in default and warning copy.
|
|
188
|
-
settings:
|
|
188
|
+
settings: 7,
|
|
189
189
|
};
|
|
190
190
|
const _sectionCache = {};
|
|
191
191
|
const _lastValueByKey = {};
|
package/dashboard/js/settings.js
CHANGED
|
@@ -713,6 +713,13 @@ async function openSettings() {
|
|
|
713
713
|
// non-dispatch toggles that historically lived under Auto-fix; placed
|
|
714
714
|
// adjacent to Auto-fix so operators following the legacy mental model find
|
|
715
715
|
// the moved flags quickly.
|
|
716
|
+
// W-mryz3r7h — a section may opt into a collapsible rail fold by declaring a
|
|
717
|
+
// `group`. Grouped sections render as an indented child list beneath a fold
|
|
718
|
+
// header (see RAIL_GROUPS + the railHtml builder below); their PANES stay in
|
|
719
|
+
// the content area unchanged. Copilot/Claude Tuning + Budget + the former
|
|
720
|
+
// standalone Advanced pane now live under the "Advanced" fold (collapsed by
|
|
721
|
+
// default). The former standalone Advanced leaf is nested here too so there is
|
|
722
|
+
// only ever one thing labeled "Advanced".
|
|
716
723
|
const sections = [
|
|
717
724
|
{ id: 'runtime', label: 'Agents Runtime & Models', featured: true, html: paneRuntime },
|
|
718
725
|
{ id: 'autofix', label: 'Auto-fix & Review Loop', featured: true, html: paneAutoFix },
|
|
@@ -723,15 +730,19 @@ async function openSettings() {
|
|
|
723
730
|
{ id: 'polling', label: 'Polling', html: panePolling },
|
|
724
731
|
{ id: 'concurrency', label: 'Concurrency & Timeouts', html: paneConcurrency },
|
|
725
732
|
{ id: 'worktree', label: 'Worker Pool & Worktrees', html: paneWorktree },
|
|
726
|
-
{ id: 'copilot', label: 'Copilot Tuning', html: paneCopilot },
|
|
727
|
-
{ id: 'claude', label: 'Claude Tuning', html: paneClaude },
|
|
728
733
|
{ id: 'harness', label: 'Runtime Harness', html: paneHarness },
|
|
729
|
-
{ id: 'budget', label: 'Budget', html: paneBudget },
|
|
730
734
|
{ id: 'maxturns', label: 'Max Turns', html: paneMaxTurns },
|
|
731
735
|
{ id: 'features', label: 'Feature Flags', html: paneFeatures },
|
|
732
|
-
{ id: '
|
|
736
|
+
{ id: 'copilot', label: 'Copilot Tuning', html: paneCopilot, group: 'advanced' },
|
|
737
|
+
{ id: 'claude', label: 'Claude Tuning', html: paneClaude, group: 'advanced' },
|
|
738
|
+
{ id: 'budget', label: 'Budget', html: paneBudget, group: 'advanced' },
|
|
739
|
+
{ id: 'advanced', label: 'Advanced', html: paneAdvanced, group: 'advanced' },
|
|
733
740
|
];
|
|
734
741
|
|
|
742
|
+
// Rail fold registry: group id → display label. Sections opt in via `group`.
|
|
743
|
+
const RAIL_GROUPS = { advanced: { label: 'Advanced' } };
|
|
744
|
+
const ADV_FOLD_KEY = 'minions.settings.advancedFoldOpen';
|
|
745
|
+
|
|
735
746
|
// localStorage-persisted active tab. Falls back to the first section when no
|
|
736
747
|
// saved value or the saved value points at a section we no longer render.
|
|
737
748
|
const SAVED_TAB_KEY = 'minions.settings.activeTab';
|
|
@@ -741,12 +752,41 @@ async function openSettings() {
|
|
|
741
752
|
if (saved && sections.some(s => s.id === saved)) activeTab = saved;
|
|
742
753
|
} catch { /* localStorage may be blocked in some embeds */ }
|
|
743
754
|
|
|
744
|
-
|
|
745
|
-
|
|
755
|
+
// Compute the Advanced fold's initial expanded state. Default (no saved
|
|
756
|
+
// value) is COLLAPSED. Persisted choice survives reloads. If the restored
|
|
757
|
+
// active tab lives inside the fold, force it open so the active rail button
|
|
758
|
+
// is visible on load.
|
|
759
|
+
let advancedFoldOpen = false;
|
|
760
|
+
try { advancedFoldOpen = localStorage.getItem(ADV_FOLD_KEY) === '1'; } catch { /* localStorage may be blocked in some embeds */ }
|
|
761
|
+
if (sections.some(s => s.id === activeTab && s.group === 'advanced')) advancedFoldOpen = true;
|
|
762
|
+
const foldStates = { advanced: advancedFoldOpen };
|
|
763
|
+
|
|
764
|
+
const railBtnHtml = (s, child) =>
|
|
765
|
+
'<button class="settings-rail-btn' + (s.featured ? ' featured' : '') + (child ? ' settings-rail-btn--child' : '') + (s.id === activeTab ? ' active' : '') + '" data-settings-tab="' + s.id + '" type="button">' +
|
|
746
766
|
'<span class="rail-label">' + escHtml(s.label) + '</span>' +
|
|
747
767
|
'<span class="match-count" style="display:none">0</span>' +
|
|
748
|
-
'</button>'
|
|
749
|
-
|
|
768
|
+
'</button>';
|
|
769
|
+
|
|
770
|
+
// Walk the sections in order. Leaves render directly; the first grouped
|
|
771
|
+
// section emits the whole fold (header + indented child list), and later
|
|
772
|
+
// members of the same group are skipped (they were rendered with the header).
|
|
773
|
+
const renderedGroups = new Set();
|
|
774
|
+
const railHtml = sections.map(function(s) {
|
|
775
|
+
if (!s.group) return railBtnHtml(s, false);
|
|
776
|
+
if (renderedGroups.has(s.group)) return '';
|
|
777
|
+
renderedGroups.add(s.group);
|
|
778
|
+
const members = sections.filter(x => x.group === s.group);
|
|
779
|
+
const open = !!foldStates[s.group];
|
|
780
|
+
const label = (RAIL_GROUPS[s.group] && RAIL_GROUPS[s.group].label) || s.group;
|
|
781
|
+
return '<button class="settings-rail-fold' + (open ? ' open' : '') + '" data-settings-fold="' + s.group + '" type="button" aria-expanded="' + (open ? 'true' : 'false') + '">' +
|
|
782
|
+
'<span class="fold-chevron" aria-hidden="true"></span>' +
|
|
783
|
+
'<span class="rail-label">' + escHtml(label) + '</span>' +
|
|
784
|
+
'<span class="match-count" style="display:none">0</span>' +
|
|
785
|
+
'</button>' +
|
|
786
|
+
'<div class="settings-rail-group' + (open ? ' open' : '') + '" data-settings-group="' + s.group + '" role="group" aria-label="' + escHtml(label) + '">' +
|
|
787
|
+
members.map(m => railBtnHtml(m, true)).join('') +
|
|
788
|
+
'</div>';
|
|
789
|
+
}).join('');
|
|
750
790
|
|
|
751
791
|
const panesHtml = sections.map(s =>
|
|
752
792
|
'<div class="settings-pane' + (s.id === activeTab ? ' active' : '') + '" data-settings-pane="' + s.id + '">' +
|
|
@@ -806,6 +846,20 @@ async function openSettings() {
|
|
|
806
846
|
});
|
|
807
847
|
});
|
|
808
848
|
|
|
849
|
+
// ── Wire up fold headers (expand/collapse the Advanced group) ───────────
|
|
850
|
+
// <button> headers are keyboard-operable natively (Enter/Space fire click).
|
|
851
|
+
// The user's choice is persisted so it survives reloads; the render-time
|
|
852
|
+
// default is collapsed unless the active tab lives inside the fold.
|
|
853
|
+
body.querySelectorAll('[data-settings-fold]').forEach(function(btn) {
|
|
854
|
+
btn.addEventListener('click', function() {
|
|
855
|
+
const gid = btn.getAttribute('data-settings-fold');
|
|
856
|
+
const open = _toggleSettingsFold(gid);
|
|
857
|
+
if (gid === 'advanced') {
|
|
858
|
+
try { localStorage.setItem(ADV_FOLD_KEY, open ? '1' : '0'); } catch { /* ignore */ }
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
|
|
809
863
|
// ── Cross-tab search box ───────────────────────────────────────────────
|
|
810
864
|
// Filters every [data-search] row across every pane. Empty query restores
|
|
811
865
|
// the active-tab view; non-empty query shows ALL panes with matches and
|
|
@@ -1257,6 +1311,32 @@ function _selectSettingsTab(id) {
|
|
|
1257
1311
|
body.querySelectorAll('[data-settings-pane]').forEach(function(pane) {
|
|
1258
1312
|
pane.classList.toggle('active', pane.getAttribute('data-settings-pane') === id);
|
|
1259
1313
|
});
|
|
1314
|
+
// If the selected tab lives inside a collapsed fold, expand the fold so its
|
|
1315
|
+
// rail button is visible. The pane renders regardless of fold state.
|
|
1316
|
+
const activeBtn = body.querySelector('[data-settings-tab="' + id + '"]');
|
|
1317
|
+
const group = activeBtn && activeBtn.closest('[data-settings-group]');
|
|
1318
|
+
if (group) {
|
|
1319
|
+
group.classList.add('open');
|
|
1320
|
+
const gid = group.getAttribute('data-settings-group');
|
|
1321
|
+
const fold = body.querySelector('[data-settings-fold="' + gid + '"]');
|
|
1322
|
+
if (fold) { fold.classList.add('open'); fold.setAttribute('aria-expanded', 'true'); }
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// Expand/collapse a rail fold. Returns the resulting open state (or undefined
|
|
1327
|
+
// if the fold isn't mounted). `forceState` (boolean) sets an explicit state;
|
|
1328
|
+
// omitting it toggles. Keeps the header's aria-expanded + chevron in sync.
|
|
1329
|
+
function _toggleSettingsFold(groupId, forceState) {
|
|
1330
|
+
const body = document.getElementById('modal-body');
|
|
1331
|
+
if (!body) return undefined;
|
|
1332
|
+
const fold = body.querySelector('[data-settings-fold="' + groupId + '"]');
|
|
1333
|
+
const group = body.querySelector('[data-settings-group="' + groupId + '"]');
|
|
1334
|
+
if (!fold || !group) return undefined;
|
|
1335
|
+
const open = typeof forceState === 'boolean' ? forceState : !group.classList.contains('open');
|
|
1336
|
+
group.classList.toggle('open', open);
|
|
1337
|
+
fold.classList.toggle('open', open);
|
|
1338
|
+
fold.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1339
|
+
return open;
|
|
1260
1340
|
}
|
|
1261
1341
|
|
|
1262
1342
|
function _applySettingsSearch(rawQuery) {
|
|
@@ -1281,6 +1361,25 @@ function _applySettingsSearch(rawQuery) {
|
|
|
1281
1361
|
const badge = btn.querySelector('.match-count');
|
|
1282
1362
|
if (badge) badge.style.display = 'none';
|
|
1283
1363
|
});
|
|
1364
|
+
// Restore each fold's resting state: persisted collapsed default, but kept
|
|
1365
|
+
// open if an active child tab lives inside it (so the visible pane's rail
|
|
1366
|
+
// button isn't hidden). Clears any search-driven fold badge/no-match.
|
|
1367
|
+
body.querySelectorAll('[data-settings-fold]').forEach(function(fold) {
|
|
1368
|
+
const gid = fold.getAttribute('data-settings-fold');
|
|
1369
|
+
const group = body.querySelector('[data-settings-group="' + gid + '"]');
|
|
1370
|
+
if (!group) return;
|
|
1371
|
+
fold.classList.remove('no-match');
|
|
1372
|
+
const foldBadge = fold.querySelector('.match-count');
|
|
1373
|
+
if (foldBadge) foldBadge.style.display = 'none';
|
|
1374
|
+
let open = false;
|
|
1375
|
+
if (gid === 'advanced') {
|
|
1376
|
+
try { open = localStorage.getItem('minions.settings.advancedFoldOpen') === '1'; } catch { /* ignore */ }
|
|
1377
|
+
}
|
|
1378
|
+
if (group.querySelector('[data-settings-tab].active')) open = true;
|
|
1379
|
+
group.classList.toggle('open', open);
|
|
1380
|
+
fold.classList.toggle('open', open);
|
|
1381
|
+
fold.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1382
|
+
});
|
|
1284
1383
|
return;
|
|
1285
1384
|
}
|
|
1286
1385
|
|
|
@@ -1311,6 +1410,32 @@ function _applySettingsSearch(rawQuery) {
|
|
|
1311
1410
|
}
|
|
1312
1411
|
}
|
|
1313
1412
|
});
|
|
1413
|
+
|
|
1414
|
+
// Fold handling during search: expand any fold that has a matching child so
|
|
1415
|
+
// results aren't hidden behind a collapsed header, aggregate the child match
|
|
1416
|
+
// counts onto the fold header badge, and dim folds with zero matches. Runs
|
|
1417
|
+
// AFTER the per-pane loop so child rail badges are already computed.
|
|
1418
|
+
body.querySelectorAll('[data-settings-fold]').forEach(function(fold) {
|
|
1419
|
+
const gid = fold.getAttribute('data-settings-fold');
|
|
1420
|
+
const group = body.querySelector('[data-settings-group="' + gid + '"]');
|
|
1421
|
+
if (!group) return;
|
|
1422
|
+
let total = 0;
|
|
1423
|
+
let anyMatch = false;
|
|
1424
|
+
group.querySelectorAll('[data-settings-tab]').forEach(function(cb) {
|
|
1425
|
+
if (!cb.classList.contains('no-match')) anyMatch = true;
|
|
1426
|
+
const badge = cb.querySelector('.match-count');
|
|
1427
|
+
if (badge && badge.style.display !== 'none') total += Number(badge.textContent) || 0;
|
|
1428
|
+
});
|
|
1429
|
+
group.classList.toggle('open', anyMatch);
|
|
1430
|
+
fold.classList.toggle('open', anyMatch);
|
|
1431
|
+
fold.setAttribute('aria-expanded', anyMatch ? 'true' : 'false');
|
|
1432
|
+
fold.classList.toggle('no-match', !anyMatch);
|
|
1433
|
+
const foldBadge = fold.querySelector('.match-count');
|
|
1434
|
+
if (foldBadge) {
|
|
1435
|
+
foldBadge.textContent = String(total);
|
|
1436
|
+
foldBadge.style.display = total > 0 ? 'inline-block' : 'none';
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1314
1439
|
}
|
|
1315
1440
|
|
|
1316
1441
|
async function saveSettings() {
|
|
@@ -1682,7 +1807,7 @@ async function removeProject(name) {
|
|
|
1682
1807
|
}
|
|
1683
1808
|
}
|
|
1684
1809
|
|
|
1685
|
-
window.MinionsSettings = { openSettings, saveSettings, addProject, removeProject, resetSettingsToDefaults, _selectSettingsTab, _applySettingsSearch };
|
|
1810
|
+
window.MinionsSettings = { openSettings, saveSettings, addProject, removeProject, resetSettingsToDefaults, _selectSettingsTab, _applySettingsSearch, _toggleSettingsFold };
|
|
1686
1811
|
|
|
1687
1812
|
// ── Settings embed mode (Slim UX parity — W-mrkpke07000i8ed5) ───────────
|
|
1688
1813
|
// The Slim UX Settings modal embeds this LITERAL classic Settings surface in
|
package/dashboard/styles.css
CHANGED
|
@@ -1308,6 +1308,19 @@
|
|
|
1308
1308
|
.settings-rail-btn.active { color: var(--blue); border-left-color: var(--blue); background: var(--surface); font-weight: 600; }
|
|
1309
1309
|
.settings-rail-btn.no-match { opacity: 0.3; }
|
|
1310
1310
|
.settings-rail-btn .match-count { font-size: var(--text-xs); color: var(--blue); background: var(--bg); border-radius: var(--radius-xl); padding: 1px 6px; min-width: 18px; text-align: center; }
|
|
1311
|
+
/* W-mryz3r7h — collapsible rail fold ("Advanced"). The fold header is a real
|
|
1312
|
+
<button> (keyboard-operable); child rail buttons live in a container that
|
|
1313
|
+
is hidden unless the fold is .open. Children are indented under the header. */
|
|
1314
|
+
.settings-rail-fold { display: flex; align-items: center; gap: var(--space-2); width: 100%; padding: var(--space-3) var(--space-6); background: transparent; border: none; border-left: 3px solid transparent; color: var(--muted); font-size: var(--text-md); font-weight: 400; font-family: inherit; cursor: pointer; text-align: left; transition: all var(--transition-fast); }
|
|
1315
|
+
.settings-rail-fold:hover { color: var(--text); background: var(--surface); }
|
|
1316
|
+
.settings-rail-fold.no-match { opacity: 0.3; }
|
|
1317
|
+
.settings-rail-fold .rail-label { flex: 1; }
|
|
1318
|
+
.settings-rail-fold .match-count { font-size: var(--text-xs); color: var(--blue); background: var(--bg); border-radius: var(--radius-xl); padding: 1px 6px; min-width: 18px; text-align: center; }
|
|
1319
|
+
.settings-rail-fold .fold-chevron::before { content: '\25B8'; display: inline-block; font-size: var(--text-xs); color: var(--muted); transition: transform var(--transition-fast); }
|
|
1320
|
+
.settings-rail-fold.open .fold-chevron::before { transform: rotate(90deg); }
|
|
1321
|
+
.settings-rail-group { display: none; }
|
|
1322
|
+
.settings-rail-group.open { display: block; }
|
|
1323
|
+
.settings-rail-btn--child { padding-left: var(--space-9); }
|
|
1311
1324
|
.settings-content { flex: 1; overflow-y: auto; padding: var(--space-7) var(--space-8); min-width: 0; }
|
|
1312
1325
|
.settings-pane { display: none; }
|
|
1313
1326
|
.settings-pane.active { display: block; }
|
package/docs/command-center.md
CHANGED
|
@@ -77,6 +77,12 @@ Because that bypass can spend tens of seconds starting a one-off process before
|
|
|
77
77
|
|
|
78
78
|
**No new `engine.*` flag.** The limits above are module-level constants in `dashboard.js` (`CC_IMAGE_MAX_COUNT`, `CC_IMAGE_MAX_DECODED_BYTES`, `CC_IMAGE_MIME_ALLOWLIST`), not config keys; `engine/shared.js` `ENGINE_DEFAULTS` was not touched. Per CLAUDE.md Best Practice #9 (Settings parity), no Settings toggle is added because no new `engine.*` flag was introduced. Runtime selection that determines whether images are accepted is the existing `engine.ccCli` / `ccModel` override, which already has a Settings control.
|
|
79
79
|
|
|
80
|
+
## Operator-checkout write authority (interactive turns)
|
|
81
|
+
|
|
82
|
+
Interactive Command Center turns MAY use `Edit`/`Write`/`Bash` to change files and run mutating git commands (e.g. a clean `git pull --ff-only origin main`, a requested `git checkout -- <file>`, a one-off file edit) inside a configured project's `localPath` — but **only** to carry out a change the human explicitly asked to make in that checkout, acting as the operator's hands. The authoritative rule text lives in `prompts/cc-system.md` → "Operator checkouts — write only on an explicit user request" and is regression-tested in `test/unit.test.js` ("cc-system prompt permits explicit-request CC edits to operator checkouts with guardrails").
|
|
83
|
+
|
|
84
|
+
Guardrails preserved: no unsolicited writes (question/inspection-only asks stay read-only), requested scope only (never revert/stash/reset/overwrite the operator's unrelated uncommitted changes, no destructive commands like `git reset --hard` / `git clean -fd` unless explicitly named), and dispatch is still preferred for Medium/Large feature work that should land as a reviewed PR. This authority is **CC-only**: it does not change agent dispatch, which always runs in engine-owned isolated worktrees (`worktree` mode) or the guarded live-checkout path (`live`/`hybrid` mode). Worktree ownership, wipe guards, and live-checkout safety are unchanged.
|
|
85
|
+
|
|
80
86
|
## Create-PR flow — checkout-mode-aware (PR #387)
|
|
81
87
|
|
|
82
88
|
The "Create PR" chip offered by CC after a local edit follows the **same checkout pattern** as a normal dispatch for the project (`shared.resolveCheckoutMode`).
|
|
@@ -229,7 +229,9 @@ The core invariant holds end-to-end through restore: **the engine only ever swit
|
|
|
229
229
|
|
|
230
230
|
## Create-PR chip safety (CC-initiated, separate from the dispatch path)
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
The **"Create PR"** chip is only for pre-existing operator changes and never fabricates edits itself. In worktree mode, `prepareCreatePrWorktree` creates a fresh branch from `origin/<mainBranch>` (falling back to local main when no remote exists), applies only the uncommitted diff/untracked files, and leaves the source checkout unchanged. Topic commits from the operator's current branch cannot leak into the PR. Live-mode Create-PR remains a deliberate in-place flow and retains the base-branch protections below.
|
|
233
|
+
|
|
234
|
+
(Note: interactive Command Center turns may make direct, explicitly-requested edits to `localPath` — including a requested clean `git pull --ff-only origin main` — as the operator's hands; see `prompts/cc-system.md` → "Operator checkouts — write only on an explicit user request". That interactive authority is separate from this Create-PR chip flow and does not change the dispatch-path or agent-worktree ownership contracts on this page.)
|
|
233
235
|
|
|
234
236
|
**The bug it fixes (W-mr3jbpru).** The old live-mode chip message keyed its branch instruction off `git rev-parse --abbrev-ref HEAD` (the *current* branch) and only told the agent to branch off cleanly **if** that current branch already happened to be the project's main branch. If the checkout was on any other branch, the message told the agent to commit the new changes **directly onto that branch** — silently carrying all of that branch's unrelated commits into the new PR. There was no engine-side verification that the current branch was clean or at parity with main; it was trusted blindly. A user hit exactly this: a Create-PR chip PR ended up built on top of a leftover topic branch and carried unrelated diffs.
|
|
235
237
|
|
|
@@ -15,8 +15,10 @@ Lifecycle keeps the cross-cutting invariants; the detail lives here.
|
|
|
15
15
|
|
|
16
16
|
For `checkoutMode: "worktree"`, `project.localPath` is an operator-owned source
|
|
17
17
|
checkout, not an execution directory. Project-bound read-only and mutating
|
|
18
|
-
dispatches both run in engine-owned worktrees; Command Center
|
|
19
|
-
|
|
18
|
+
dispatches both run in engine-owned worktrees; interactive Command Center turns
|
|
19
|
+
may write to `localPath` only on an explicit user request (never as a dispatch or
|
|
20
|
+
background action — see `prompts/cc-system.md`); Create-PR staging copies existing
|
|
21
|
+
operator changes without resetting or deleting
|
|
20
22
|
the source; restart restore requires explicit live dispatch provenance plus
|
|
21
23
|
current live configuration. Branchless project dispatches receive detached
|
|
22
24
|
worktrees, and path containment resolves symlinks/junctions before accepting a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2416",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|
package/prompts/cc-system.md
CHANGED
|
@@ -89,17 +89,19 @@ If you start a small task and discover it's actually Medium (3+ files, more tool
|
|
|
89
89
|
|
|
90
90
|
When genuinely in doubt about the size, delegate — agents have isolated worktrees, full tool access, durable work-item tracking, and no turn limits.
|
|
91
91
|
|
|
92
|
-
###
|
|
93
|
-
|
|
92
|
+
### Operator checkouts — write only on an explicit user request
|
|
93
|
+
A configured project's `localPath` is the **human operator's own working tree**. You MAY use `Edit`/`Write`/`Bash` to change files and run mutating git commands inside `localPath` — but only as the operator's hands, carrying out a change **they explicitly asked you to make in that checkout**. This includes ordinary requested git operations (e.g. a clean `git pull --ff-only origin main`, `git checkout -- <file>` on a file they named) and requested file edits.
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
Guardrails (these still bind, regardless of checkout mode or task size):
|
|
96
|
+
- **Explicit request only — never unsolicited.** If the human only asked a question, asked you to inspect, or asked you to dispatch, stay read-only. Do not edit or run mutating commands in `localPath` on your own initiative. This is the one place the direct-handling override does **not** apply: absent an explicit ask to change that checkout, treat it as read-only.
|
|
97
|
+
- **Requested scope only — never their unrelated work.** Change exactly what was asked. Never revert, stash, reset, discard, or overwrite the operator's unrelated uncommitted changes, and never run destructive commands (`git reset --hard`, `git clean -fd`, `git checkout .`, branch/force-push/delete operations) unless the human explicitly named that exact destructive action.
|
|
98
|
+
- **Prefer dispatch for real feature work.** For Medium/Large coding work — new features, multi-file refactors, anything that should land as a reviewed PR — still **dispatch a work item** (`POST /api/work-items`, `type: "implement"` or `"fix"`) so the engine runs it in an isolated worktree with the normal review/build loop. Direct `localPath` writes are for the small, explicit "do this here, now" asks: a quick fix, a requested `git pull`, a one-off edit.
|
|
99
|
+
- **CC-only authority — dispatch isolation is unchanged.** This permission covers interactive Command Center turns only. It does **not** loosen how the engine dispatches agents: dispatched agents still run in engine-owned isolated worktrees (`worktree` mode) or the guarded live-checkout path (`live`/`hybrid` mode), and worktree ownership, wipe guards, and live-checkout safety are untouched. Do not hand-edit `localPath` to work around a dirty-tree or busy-dispatch gate — dispatch instead.
|
|
96
100
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Read-only inspection is still fine: viewing files and `git status`/`git log`/`git diff` are allowed. Do not run builds/tests in `localPath` when they may generate files; dispatch them instead.
|
|
101
|
+
Read-only inspection never needs a request: viewing files and `git status`/`git log`/`git diff` are always fine.
|
|
100
102
|
|
|
101
103
|
### Create a PR from pre-existing operator changes
|
|
102
|
-
Use this
|
|
104
|
+
Use this when the human asks to open a PR from changes that **already exist** in a configured worktree-mode project's operator checkout — so the PR is built from a clean copy and the operator's original tree is left untouched.
|
|
103
105
|
|
|
104
106
|
First call:
|
|
105
107
|
```
|
|
@@ -355,7 +357,7 @@ Every configured project has an effective **checkout mode** — surfaced in your
|
|
|
355
357
|
- **`live`** — agents run **in-place** inside the project's `localPath` (no worktree). The engine caps this to **one mutating dispatch at a time** per project. Dirty-tree behavior follows the configured auto-stash/reset policy (auto-stash defaults on); with recovery disabled, the item remains pending until the tree is clean. Use only when worktrees are unworkable.
|
|
356
358
|
- **`hybrid`** — `live` **plus** a `liveValidation: { type, autoDispatch }` block. Work items not listed in `type` use isolated worktrees; listed live-capable canonical work-item types run in-place. The normal validation type is `test`. `build-and-test` is a playbook name, not a work-item type. `type` accepts one string or an array. When `autoDispatch:true`, `test` must be included; the engine creates one PR-targeted `test` WI with the `build-and-test` playbook after each eligible coding WI completes.
|
|
357
359
|
|
|
358
|
-
**CC
|
|
360
|
+
**CC may write into a configured project's `localPath` only on an explicit user request** (see "Operator checkouts — write only on an explicit user request"). Dispatched agents never do — the engine owns their isolated-worktree or guarded-live path. Read-only inspection remains fine.
|
|
359
361
|
|
|
360
362
|
**When to recommend hybrid:** the project's build/test/validation genuinely cannot run in an isolated worktree (it needs the real checkout — submodules, a `repo`-managed tree, an emulator/dev-server bound to `localPath`, deep-path tooling), **but** you still want coding agents to work in parallel rather than serialize through the single live checkout. If the *whole* workflow must run on the real tree, use plain `live`. If nothing needs the real tree, stay on `worktree`.
|
|
361
363
|
|