Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ title: system-config
category: reference
component: overview
status: active
version: 5.0.0
last_updated: 2026-07-26
version: 5.1.0
last_updated: 2026-08-13
tags: [overview, chezmoi, mise, zsh, mcp, vscode, policy, agents]
priority: critical
---
Expand Down Expand Up @@ -40,6 +40,7 @@ never committed — secrets live in 1Password and are resolved at launch.
| Terminal | 14 iTerm2 dynamic profiles + color presets | `iterm2/`, `scripts/install-iterm2-profiles.sh` |
| Editor | reviewed subset of VS Code user settings, profiles, extensions | `vscode/`, `scripts/sync-vscode.sh` |
| MCP | user-level server baseline across 7 hosts. For Claude Desktop this is the **managed `mcpServers` block only** — the app's `preferences`, `globalShortcut` and `coworkUserFilesPath` are preserved, and the app is a concurrent writer of the same file ([`docs/claude-macos-app.md`](docs/claude-macos-app.md)) | `scripts/mcp-servers.json`, `scripts/sync-mcp.sh` |
| Git identity | shared commit/push gate, runtime adapters, and a fail-open SessionStart mismatch announcer; fail-silent live hook registration remains operator-controlled | `home/dot_local/bin/executable_git-identity-*`, `docs/git-identity.md` |
| Agent permissions | root-owned unconditional deny floor for Claude Code | `macos/claude-code/` |
| Policy | canonical live policy consumed by a sibling repo | `policies/host-capability-substrate/` |
| Maintenance | system update orchestration, health reporting | `scripts/system-update.sh`, `ng-doctor` |
Expand All @@ -58,9 +59,11 @@ resolve server-side (§1, class "Account-managed") — both of which carry real
authorization and are invisible to every mechanism below. Enumeration there is
legibility, not governance.

1. **chezmoi** — `home/` is the source tree; 78 managed targets under `$HOME`.
Go templates, not Jinja2. Guard possibly absent map keys with `hasKey` before
reading them; `| default` cannot recover from `missingkey=error`.
1. **chezmoi** — `home/` is the source tree. Measure the current managed-target
inventory with `chezmoi managed`; do not copy its mutable total into general
architecture prose. Go templates, not Jinja2. Guard possibly absent map keys
with `hasKey` before reading them; `| default` cannot recover from
`missingkey=error`.
2. **Repo scripts** — `scripts/sync-mcp.sh`, `scripts/sync-vscode.sh`,
`scripts/install-iterm2-profiles.sh`, `scripts/mcp-usage-collector.sh`.
These write files chezmoi does not own, usually because the host application
Expand Down Expand Up @@ -124,13 +127,15 @@ Owned elsewhere, deliberately:
named templates
- `jq` shape checks on the MCP and VS Code JSON manifests
- `tests/system-update`, `tests/android`, `tests/vscode`, `tests/mcp-vscode`,
`tests/mcp-codex`, `tests/mcp-claude-desktop`
`tests/mcp-codex`, `tests/mcp-claude-desktop`, `tests/git-identity-gate`
- an isolated production-equivalent SessionStart launcher with a hostile outer
environment; no live agent configuration is read or changed
- `scripts/policy-lint.sh`, `scripts/check-instruction-bridge.sh`
- repo-contract greps (no fish surface, no stale path references)

**Known coverage gaps, stated rather than implied:** four of the ten suites —
`tests/git-identity-gate`, `tests/jr-style`, `tests/mise-config`,
`tests/policies` — are not invoked by the gate. `ng-doctor` reports 101 checks,
**Known coverage gaps, stated rather than implied:** three of the ten suites —
`tests/jr-style`, `tests/mise-config`, and `tests/policies` — are not invoked by
the gate. `ng-doctor` reports 101 checks,
a number of which are unimplemented skip stubs rather than assertions, so a
green summary is not by itself evidence of a healthy host. There is no secret or
PII scanner in the gate.
Expand Down
73 changes: 64 additions & 9 deletions docs/agentic-hook-enforcement.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ title: Agentic Hook Enforcement Surfaces
category: reference
component: agentic_hook_enforcement
status: active
version: 0.8.0
last_updated: 2026-08-01
version: 0.9.0
last_updated: 2026-08-13
tags: [agentic, hooks, git-identity, claude, codex, copilot, cursor, devin, enforcement]
priority: high
---
Expand All @@ -30,6 +30,12 @@ is actually installed here + the items still to verify.
> Codex/Cursor/Copilot were verified independently in a separate project. Devin
> Desktop is installed, but neither its Cascade hook nor a standalone Devin CLI
> hook is wired on this workstation.
>
> **Q1b is source-only in this change.** The shared Claude/Codex SessionStart
> announcer is built and covered by the synthetic suite, but it has not been
> deployed, appended to either live runtime config, reviewed for Codex trust, or
> live-accepted. All activation claims above remain specifically about the
> existing PreToolUse enforcement.

This is a hook-mechanics doc; it carries **no identity values** (those stay in
meta-inventory's internal registry — see the git-identity HARD RULES).
Expand All @@ -50,8 +56,10 @@ meta-inventory's internal registry — see the git-identity HARD RULES).
| Surface | Blocks? | Event / matcher | Input shape (command location) | Deny mechanism |
|---|---|---|---|---|
| Claude Code (CLI, Code tab, Cowork, remote) | yes | `PreToolUse` / `Bash` | `tool_name`, `tool_input.command`, top-level `cwd` | exit 2 + stderr |
| Claude Code session context | advisory | `SessionStart` | top-level absolute `cwd` | exit 0 + privacy-safe `systemMessage` / `hookSpecificOutput.additionalContext` |
| Claude **Chat** tab / Claude Desktop chat | no | — (MCP only) | — | — |
| Codex CLI | yes¹ | `PreToolUse` / `Bash` | `tool_name`, `tool_input.command`, `cwd` | exit 2 / `permissionDecision:"deny"` |
| Codex session context | advisory | `SessionStart` | top-level absolute `cwd` | exit 0 + privacy-safe `systemMessage` / `hookSpecificOutput.additionalContext` |
| Copilot CLI | yes² | `preToolUse` (or PascalCase `PreToolUse`) / `bash` | camelCase `toolName`/`toolArgs.command`; **or** PascalCase `tool_name`/`tool_input.command` | **exit 0 + stdout `{"permissionDecision":"deny"}`** (exit 2 ≠ block) |
| Cursor (agent) | partial³ | `beforeShellExecution` | top-level `command`, `cwd`, `workspace_roots` | `{"permission":"deny"}` |
| Devin Desktop (Cascade) | yes | `pre_run_command` | `agent_action_name`, `tool_info.command_line`, `tool_info.cwd` | exit 2 |
Expand Down Expand Up @@ -88,23 +96,36 @@ not registered as a workstation command. Its shell tool name is `exec`, not

## Adapter model

The engine is reused unchanged; only the per-runtime **adapter** differs. All
adapters share `git_identity_hooklib.py` (deployed to `~/.local/lib/`) for the
`shlex` command→repo resolver and the gate invocation. Deployed files:
The legacy gate policy and PreToolUse protocol are reused unchanged; only the
per-runtime **adapter** differs. Q1b adds a separate machine rendering of the
same evaluation for SessionStart. All enforcement adapters share
`git_identity_hooklib.py` (deployed to `~/.local/lib/`) for the `shlex`
command→repo resolver and the legacy gate invocation. Deployed files:

| File (`~/.local/bin/`) | Runtimes | Input | Deny |
|---|---|---|---|
| `git-identity-gate` | — (engine) | a repo path | exit 2 on mismatch |
| `git-identity-gate` | — (engine) | legacy repo path only; SessionStart loads callable `run_machine_v1()` outside the CLI grammar | legacy exit 2 unchanged; programmatic `confirmed_mismatch` only after strict registry qualification |
| `git-identity-pretooluse` | **Claude Code, Codex** | `tool_name:"Bash"`, `tool_input.command`, `cwd` | exit 2 |
| `git-identity-cursor` | **Cursor** | top-level `command`, `cwd` | stdout `{"permission":"deny"}` |
| `git-identity-copilot` | **Copilot** | PascalCase `tool_name`/`tool_input` or camelCase `toolName`/`toolArgs` | **exit 0** + stdout `{"permissionDecision":"deny"}` |
| `git-identity-sessionstart` | **Claude Code, Codex** | SessionStart JSON with top-level absolute `cwd` | **exit 0**; warning JSON only for a confirmed mismatch |

`git-identity-pretooluse` also covers **Devin CLI** input (matcher `exec`) once
that command is installed. **Devin Desktop Cascade** (`tool_info.command_line`)
needs a fifth adapter. Every adapter **fails open** on any error.

Tests: `tests/git-identity-gate/run.sh` (26 cases — engine, all adapter input
shapes, allow/block/advisory). Skips if the registry is absent.
Tests: `tests/git-identity-gate/run.sh` covers the engine, all enforcement
adapter input shapes, exact legacy compatibility, SessionStart
schema/privacy/fail-open behavior, machine-protocol qualification, a complete
production environment allowlist, environment poisoning, and real-gate
integration. The suite generates synthetic registries and temporary
repositories; it does not read the live private registry. The programmatic
machine entrypoint does not alter legacy CLI or PreToolUse decisions, argv,
statuses, or diagnostics. When `Q1B_PRODUCER_CHECKER` explicitly names the
producer checker, the differential receipt requires its normal success or
contract-failure completion marker and reports checker errors separately. With
that variable unset, the external differential is an explicit skip rather than
an inferred producer-acceptance result.

### Wiring (per-tool; current local state annotated, 2026-06-22)

Expand All @@ -117,6 +138,33 @@ shapes, allow/block/advisory). Skips if the registry is absent.
// {"version":1,"hooks":{"preToolUse":[{"type":"command","bash":"~/.local/bin/git-identity-copilot"}]}} [WIRED 2026-06-22 (~/.copilot/hooks/git-identity.json) + firing verified]
```

Q1b adds no tracked hook configuration. After targeted owner deployment, the
owner manually appends one native `SessionStart` command entry to each of
`~/.claude/settings.json` and `~/.codex/hooks.json`, using the exact sanitized
command shown separately for both runtimes in the Q1b runbook. That registration
command is the production entry boundary: `/usr/bin/env -i` clears the ambient
environment before `/usr/bin/python3` invokes Apple's Xcode shim;
`2>/dev/null || :` suppresses pre-main diagnostics and converts pre-main
interpreter failure into a silent status-zero allow without consuming valid
warning JSON on stdout. Directly executing the Python file is not equivalent
production evidence because hostile Apple developer-tool selectors can affect
the shim before Python code begins.

Once started, the announcer derives canonical `HOME` through the passwd API,
resolves `sys.executable`, and accepts the resolved interpreter only when the
target is a root-owned executable regular file with no group/world write bit.
It constructs a separate child environment containing only `HOME`,
`PATH=/usr/bin:/bin`, `LANG=C`, and `LC_ALL=C`, then uses the validated resolved
interpreter and a fixed `runpy` bootstrap to call the sibling gate's
`run_machine_v1()`. The direct gate CLI remains entirely legacy.

The append must leave both existing PreToolUse registrations and Claude's
existing divergence SessionStart entry byte-for-byte unchanged. The owner then
performs Codex trust review and fresh-session wrong-root/correct-root acceptance.
None of those runtime actions or outcomes is claimed by this source change; the
exact commands and receipt requirements are in
[`docs/git-identity.md`](./git-identity.md#q1b-sourceruntime-ownership-handoff).

## Backstop on GitHub.com (correction)

Custom **server-side `pre-receive` hooks are GitHub *Enterprise Server* only —
Expand Down Expand Up @@ -191,6 +239,10 @@ the agent only sees a **generic "blocked by a hook"** message, not the hook's

## Unresolved — verify before trusting (do not promote to "truth")

- **Q1b live activation:** source and synthetic tests are complete; targeted
deployment, the two manual SessionStart appends, Codex trust review, and live
wrong-root/correct-root acceptance remain owner work. Until those receipts
exist, do not claim the announcer is installed, trusted, or firing.
- **Copilot project MCP key**: `.mcp.json` / `.github/mcp.json` — top-level
`servers` (VS Code style) vs `mcpServers` (Copilot user-file style) is
unresolved; verify against the installed CLI.
Expand All @@ -207,7 +259,7 @@ the agent only sees a **generic "blocked by a hook"** message, not the hook's
## Staged plan (grounded in what's installed)

1. **Docs (this).** Verified matrix + corrections; unresolved items marked. ✅
2. **Tier-1 adapters** — built + tested (26-case suite): Claude/Codex
2. **Tier-1 adapters** — built + tested: Claude/Codex
(`git-identity-pretooluse`), Cursor (`git-identity-cursor`), Copilot
(`git-identity-copilot`) + shared `git_identity_hooklib.py`. ✅
3. **Tier-2 (document only):** Devin CLI and Devin Desktop Cascade (not wired).
Expand All @@ -217,6 +269,9 @@ the agent only sees a **generic "blocked by a hook"** message, not the hook's
(`copilot -p`, deny-via-stdout) — Codex/Cursor/Copilot in a separate project.
#2893/#2392 remain known-open Copilot caveats (guardrail, not boundary). Only
remaining open item: Cursor's crash/timeout fail-behavior.
5. **Q1b SessionStart announcement:** source + synthetic coverage done;
owner deployment, two native registrations, Codex trust review, and live
acceptance pending. The dual PreToolUse architecture is unchanged.

## Related

Expand Down
Loading
Loading