feat(router-gate): support git -C path for worktree dev
Shell resets cwd each call so a worktree cd does not persist; pointing git at the worktree dir is the cwd-independent way to commit there. classifyGitCommand now strips the leading working-dir flag before all checks, so the real subcommand is classified and all hard-patterns (hook-bypass, force-push, force-add, config-injection) plus the push-main-guard still apply. TDD: plus 6 tests; full tools suite 2003 GREEN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+11
-10
@@ -1,6 +1,6 @@
|
||||
# Brain Status (auto-generated)
|
||||
|
||||
Last updated: 2026-06-02T06:32:47.583Z
|
||||
Last updated: 2026-06-02T10:04:24.915Z
|
||||
|
||||
| Контролёр | Состояние | Детали |
|
||||
|---|---|---|
|
||||
@@ -8,13 +8,13 @@ Last updated: 2026-06-02T06:32:47.583Z
|
||||
| C2 Cross-ref consistency | ✅ | [cross-ref-checker] OK — 0 drift in 4 files |
|
||||
| C3 Observer-of-observer | ✅ | [observer-of-observer] OK — last read 0 week(s) ago |
|
||||
| C4 Сигнальный статус | ✅ | This file (self-reference) |
|
||||
| C5 Observer-coverage | ✅ | 130 episode(s) this month · Stop-hook + post-commit OK |
|
||||
| C5 Observer-coverage | ✅ | 137 episode(s) this month · Stop-hook + post-commit OK |
|
||||
| C6 Chain map sync | ✅ | [chain-map-checker] OK — 16 chains in sync |
|
||||
|
||||
## Метрики (информационные, не алерты)
|
||||
|
||||
- Observer evidence: 130 episodes this month, 0 observer_error markers, 6 PII matches before filter
|
||||
- Legacy v1 episodes (not in factor analysis): 130
|
||||
- Observer evidence: 137 episodes this month, 0 observer_error markers, 6 PII matches before filter
|
||||
- Legacy v1 episodes (not in factor analysis): 137
|
||||
- Last /brain-retro: 2 day(s) ago
|
||||
- Использование узлов: см. `/brain-retro` (раз в спринт). missed_activations: 0. **Неиспользованные узлы — не алерт, если профильной задачи не было** (Pravila §16.4 v1.36; capability-readiness; см. memory `feedback_brain_unused_tools_not_problem` — outside-repo memory store).
|
||||
|
||||
@@ -29,9 +29,9 @@ Baseline дисциплины роутера (этап 2 router discipline overh
|
||||
| analysis | 2 | 0.0% | 0.0% |
|
||||
| bugfix | 1 | 0.0% | 0.0% |
|
||||
|
||||
Router step distribution: 1: 74, 2: 51, 5: 4
|
||||
Router step distribution: 1: 81, 2: 51, 5: 4
|
||||
|
||||
Boundaries applied (ADR / границы): 1 of 129 эпизодов (0.8%).
|
||||
Boundaries applied (ADR / границы): 1 of 136 эпизодов (0.7%).
|
||||
|
||||
## Активные многоэтапные проекты
|
||||
|
||||
@@ -55,10 +55,10 @@ Long sessions correlate with discipline drift. Если % regulated просел
|
||||
|
||||
| Компонент | Токены (in/out) | USD |
|
||||
|---|---|---|
|
||||
| Classifier (Sonnet 4.6) | 10454/50220 | $0.78 |
|
||||
| Classifier (Sonnet 4.6) | 10473/50827 | $0.79 |
|
||||
| Self-assessment (Sonnet 4.6) | 0/0 | $0.00 |
|
||||
| Reviewer (Opus 4.7 + fallback) | 0/0 | $0.00 |
|
||||
| **Итого** | | **$0.78** |
|
||||
| **Итого** | | **$0.79** |
|
||||
|
||||
## Аномалии классификатора
|
||||
|
||||
@@ -71,7 +71,7 @@ Episodes since last run: 542 / threshold: 10
|
||||
|
||||
## Reviewer: субагент vs fallback
|
||||
|
||||
0 эпизодов проверено из 130.
|
||||
0 эпизодов проверено из 137.
|
||||
|
||||
## Reviewer findings
|
||||
|
||||
@@ -97,7 +97,8 @@ Episodes since last run: 542 / threshold: 10
|
||||
|
||||
| PID | Имя | CPU-время | Возраст |
|
||||
|---|---|---|---|
|
||||
| 10388 | Code | 2.57ч | 0.0ч |
|
||||
| 10388 | Code | 3.01ч | NaNч |
|
||||
| 3220 | MsMpEng | 1.13ч | 0.0ч |
|
||||
|
||||
⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий.
|
||||
|
||||
|
||||
@@ -187,14 +187,23 @@ const GIT_HARD_PATTERNS = [
|
||||
];
|
||||
|
||||
function gitSubcommand(command) {
|
||||
const m = normalizeCommand(command).match(/\bgit\s+(?:-c\s+\S+\s+)*([a-z][\w-]*)/);
|
||||
// Skip leading global flags `-c <val>` and `-C <path>`. `git -C <dir> <sub>` is the
|
||||
// cwd-independent way to operate on a worktree (the shell resets cwd each call), so the
|
||||
// real subcommand must be found after `-C`. `-C` (uppercase, working-dir) is case-distinct
|
||||
// from the blocked `-c` config-injection (GIT_HARD_PATTERNS still scans the full command).
|
||||
const m = normalizeCommand(command).match(
|
||||
/\bgit\s+(?:(?:-c\s+\S+|-C\s+(?:"[^"]*"|'[^']*'|\S+))\s+)*([a-z][\w-]*)/,
|
||||
);
|
||||
return m ? m[1] : null;
|
||||
}
|
||||
|
||||
export function classifyGitCommand(command, ctx = {}) {
|
||||
const norm = normalizeCommand(command);
|
||||
// Strip a leading `git -C <path>` (worktree-dir flag) so every rule below sees the real
|
||||
// subcommand+flags. Without this, position-anchored hard-patterns (--no-verify / --force /
|
||||
// add -f) and the push-main-guard would be bypassed by interposing `-C <dir>`.
|
||||
const norm = normalizeCommand(command).replace(/(\bgit)\s+-C\s+(?:"[^"]*"|'[^']*'|\S+)\s+/, '$1 ');
|
||||
if (!/\bgit\b/.test(norm)) return null;
|
||||
const sub = gitSubcommand(command);
|
||||
const sub = gitSubcommand(norm);
|
||||
if (!sub) return null;
|
||||
|
||||
// 1. git-hard — block безусловно
|
||||
|
||||
@@ -220,6 +220,28 @@ describe('classifyGitCommand — push main-guard (owner-authorized 2026-06-02 re
|
||||
});
|
||||
});
|
||||
|
||||
describe('classifyGitCommand — git -C <path> (worktree dev, 2026-06-02)', () => {
|
||||
const na = { approvedGitOps: [], now: 4_000_000 };
|
||||
// git -C points git at another working tree (cwd resets each shell call, so this is
|
||||
// the cwd-independent way to commit in a worktree). Classify by the REAL subcommand
|
||||
// after -C, with all hard-patterns / push-main-guard still applied to the full command.
|
||||
it.each([
|
||||
'git -C "C:\\моя\\проекты\\портал crm\\worktree-x" commit -m "y"',
|
||||
'git -C "C:\\моя\\проекты\\портал crm\\worktree-x" add app/foo.php',
|
||||
'git -C "/path/worktree-x" push origin feature-y',
|
||||
'git -C /repo status',
|
||||
])('classifies by real subcommand after -C: %s', (cmd) => {
|
||||
expect(classifyGitCommand(cmd, na).result).toBe('allow');
|
||||
});
|
||||
|
||||
it('still blocks push to main even with -C', () => {
|
||||
expect(classifyGitCommand('git -C /repo push origin main', na).result).toBe('block');
|
||||
});
|
||||
it('still blocks --no-verify even with -C', () => {
|
||||
expect(classifyGitCommand('git -C /repo commit --no-verify -m x', na).result).toBe('block');
|
||||
});
|
||||
});
|
||||
|
||||
describe('classifyGitCommand — git-hard (always block)', () => {
|
||||
it.each([
|
||||
'git push --force origin main',
|
||||
|
||||
Reference in New Issue
Block a user