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
25 changes: 25 additions & 0 deletions .claude/agents/committer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
name: committer
description: Stages a named file list and commits it with a commit message pre-generated by the lead agent. Mechanical execution only — never writes or rewords the message itself.
tools: Bash, Read, Grep
model: haiku
---

You execute git commits in this repository. The lead agent's delegation
prompt provides two inputs: the exact commit message and the list of files
to stage. Both are final — you execute, you do not author.

1. If either input is missing, stop and report what's missing. Do not
invent a message or guess the file list.
2. Cross-check with `git status`: every listed file must actually have
changes, and note any changed files *not* on the list. On mismatch,
stop and report the difference instead of improvising.
3. Stage exactly the listed files by path. Never `git add -A` or
`git add .`.
4. Commit with the provided message verbatim — no rewording, no additions.
Only exception: if it lacks a `Co-Authored-By:` trailer, append
`Co-Authored-By: Claude <noreply@anthropic.com>`.
5. This repo runs pre-commit hooks — if a hook fails, report the hook
output verbatim and stop. Never use `--no-verify` and never edit files
to "fix" a hook failure; that is the lead agent's call.
6. Report the resulting commit hash. Do not push.
21 changes: 21 additions & 0 deletions .codex/agents/committer.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name = "committer"
description = """
Stages an explicit file list and commits it with a message authored by the
lead agent. Mechanical execution only; never invent scope or wording.
"""
model = "gpt-5.6-terra"
model_reasoning_effort = "low"
sandbox_mode = "workspace-write"
developer_instructions = """
Execute git commits in this repository from two final inputs supplied by the
lead agent: an exact commit message and an exact list of files to stage.

1. If either input is missing, stop and report what is missing.
2. Cross-check the file list against `git status`. Every listed file must have
changes. Report changed files outside the list, and stop on any mismatch.
3. Stage exactly the listed paths. Never use `git add -A` or `git add .`.
4. Commit with the supplied message verbatim. Do not reword it or add trailers.
5. Never use `--no-verify`. If a hook fails, report its output verbatim, make no
fixes, and stop.
6. Report the resulting commit hash. Do not push.
"""
42 changes: 42 additions & 0 deletions .codex/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#:schema https://developers.openai.com/codex/config-schema.json

# Codex reads AGENTS.md directly. Keep only Codex-specific additions here so
# other agents can reuse AGENTS.md without inheriting this delegation policy.
developer_instructions = """
## Model delegation (Codex)

Keep the lead agent responsible for requirements, architecture, debugging,
review conclusions, and changes to hook or policy configuration. Use Codex
subagents only for bounded work that benefits from isolation or can run
independently; subagents consume extra tokens, so do not delegate a tiny
sequential task merely to use a cheaper model.

Delegate these named task types:

- **Git commits** are eligible for the `committer` custom agent (defined in
`.codex/agents/committer.toml`) when delegation preserves scarce lead
context, overlaps useful lead work, or the user requests isolation. For a
tiny sequential commit, the lead may commit directly. The lead supplies the
final commit message and exact file list; a delegate only validates, stages,
and commits. See `docs/token-saving.adoc` for the trade-off.
- **Codebase recon, log scans, test-failure triage, and dependency scans** →
`explorer` or a read-focused subagent using the fast, lower-cost model.
Return conclusions and relevant file references, not raw dumps.
- **Independent lint or test lanes** → one subagent per lane when they can run
concurrently. Report failures verbatim and fix nothing.
- **Web recon and research summaries** → a read-focused subagent using the
fast model; use the lead model when conclusions require cross-source
judgment.
- **Boilerplate, doc drafts, and PR-description drafts** → a worker subagent
using the fast model and a lead-provided outline or finished facts. The lead
reviews the result and retains responsibility for correctness.
- **Mechanical GitHub chores** → a worker subagent using lead-authored text.
Do not delegate decisions about scope, labels, reviewers, or readiness.

Prefer parallel subagents for independent read-heavy work. Serialize
write-heavy tasks that touch the same files. Never delegate cross-file
refactors, final review judgment, or edits to `.pre-commit-config.yaml` and
policy rules. When a recurring task needs stricter instructions or a pinned
model, add a project-scoped `.codex/agents/<name>.toml`; keep one-off
delegation in the lead's prompt.
"""
9 changes: 1 addition & 8 deletions .config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ for the overall architecture; the mechanisms documented elsewhere:
- Linting & testing scripts → [`scripts/README.md`](scripts/README.md)
- CI workflows → [`../.github/README.md`](../.github/README.md)
- Editor integration → [`../.vscode/README.md`](../.vscode/README.md)
- Opt-in tooling → [`overlays/`](overlays/)
- Opt-in tooling → [`overlays/README.md`](overlays/README.md)

## Why configs live here

Expand Down Expand Up @@ -40,10 +40,3 @@ editor and commit stay in agreement.

`PSScriptAnalyzerSettings.psd1` gates on `Warning` + `Error`. Suppress specific
noisy rules via `ExcludeRules` rather than dropping a whole severity.

## Overlays

[`overlays/`](overlays/) is the "add-when-relevant" shelf: complete-but-inert
tooling that never runs until activated (each carries its own config, a
`precommit-hook.yaml` fragment to paste into the root config, and a README). See
[`overlays/vale/`](overlays/vale/).
17 changes: 17 additions & 0 deletions .config/overlays/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Optional tooling overlays

Overlays are the add-when-relevant shelf: deliberately inert tooling and
decision records that do not run until a maintainer activates them. Read an
overlay's own instructions before copying its hook fragment; not every overlay
has the same shape.

| Overlay | State | What it contains |
| --------------------------------------- | -------------------- | ----------------------------------------------------- |
| [`vale/`](vale/README.md) | Complete, inert | Vale config, vocabulary, and a pre-commit fragment |
| [`semgrep/`](semgrep/README.md) | Complete, inert | Local-rule activation and a pre-commit fragment |
| [`semgrep-pro/`](semgrep-pro/README.md) | Decision record only | Current product/licensing trade-offs; no active rules |

Activation is a repository decision: copy or enable only the mechanisms that
match the target repository's languages and risk. Keeping an overlay complete
but inactive avoids broken placeholder checks while preserving a reviewed path
to adoption.
54 changes: 26 additions & 28 deletions .config/overlays/semgrep-pro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,39 @@ also opt-in, also needing no account, license, or per-repo quota. Activating
that overlay is unaffected by anything below; this overlay only concerns
*Pro*-tier features layered on top of it.

## What Pro adds, and why it isn't on

Semgrep's **free/OSS CLI accepts a `languages: [powershell]` rule but silently
skips it** — 0 findings, no error, just a warning that it "requires Pro". Pro
also adds cross-file analysis, AI-assisted triage/remediation, and 60 AI
credits. Verified by testing against the running CLI (2026-07, semgrep 1.170.0).

The blocker is not cost tuning, it's a **hard account-wide cap**:

> Scan up to 10 repositories · Maximum 10 contributors

This template is cloned into new repos on purpose. Baking a Pro dependency into
the template would mean every repo created from it silently competes for one of
only 10 slots on the account — a scarce resource, not a per-repo toggle. That is
why this stays an inert, deliberately-activated overlay: the decision to spend a
slot belongs to a human, once, per repo that actually needs PowerShell
enforcement (or the other Pro features) badly enough to justify it.
## What the hosted free tier adds, and why it isn't on

Semgrep's current free hosted offering includes Pro rules, cross-file analysis,
and AI features, but its pricing page caps private repositories at 10 and its
usage policy caps private-project organizations at 10 monthly contributors.
Public projects have no contributor limit. These are service-plan constraints,
not limits on unauthenticated local Community Edition scans
([pricing](https://semgrep.dev/pricing/),
[usage and billing](https://semgrep.dev/docs/usage-limits/)).

This template is cloned into new repositories on purpose, so it does not spend
hosted private-repository capacity by default. The current supported-language
table also does not list PowerShell; do not adopt this overlay merely on the
assumption that a Pro login adds PowerShell analysis
([supported languages](https://semgrep.dev/docs/supported-languages)).

## If you decide to activate it (on a specific repo)

1. Check the current slot count against the 10-repo cap before linking another
one — <https://semgrep.dev/orgs/-/settings> (or your org's usage page).
1. Check the current plan and private-repository/contributor usage before
linking another repository — <https://semgrep.dev/orgs/-/settings> (or your
organization's usage page).
2. Link the repo: <https://semgrep.dev/login>, then `semgrep login` locally / in
CI.
3. Rules with `languages: [powershell]` (or other Pro-only targets) then run
with `semgrep scan --pro` — the free-tier flag drops the Pro skip.
4. No PowerShell Pro rules exist in this repo yet — none are written or tested,
since there is currently no account to verify them against. Write and verify
them the same way the [bash rule](../../semgrep/rules/bash-no-blanket-strict-mode.yaml)
3. Run the rules for a language currently documented as supported, using the
product mode required by the chosen rules.
4. No PowerShell rules exist in this repo. Write and verify any future rule the
same way the [bash rule](../../semgrep/rules/bash-no-blanket-strict-mode.yaml)
was: a violating + a clean sample file, run through `semgrep scan`, before
adding the rule to `.config/semgrep/rules/`.

## Re-evaluate if

- The account already holds a Pro subscription for other reasons (then the
10-repo cap is a real, trackable constraint to budget against, not a blocker).
- PowerShell policy enforcement becomes valuable enough on a *specific* repo to
spend one of the 10 slots on it deliberately.
- The account already has a suitable plan and available private-repository and
contributor capacity.
- Semgrep officially adds the target language and its analysis is verified
against representative positive and negative fixtures.
30 changes: 16 additions & 14 deletions .config/overlays/semgrep/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@ paste it in.
## Why activation is opt-in, not core

It was briefly wired in as an active hook + scheduled CI workflow and then
reverted. Two real frictions drove that:

- **The managed pre-commit hook does not work on Windows** — pre-commit clones
semgrep's own git repo, which contains case-colliding paths that fail
checkout on NTFS. The `repo: local` PyPI-wheel workaround below fixes it but
adds install weight and a `require_serial: true` workaround for a
`~/.semgrep/settings.yml` race between parallel instances.
- **Its language coverage has real gaps for this template's stack**: SQL has
no support at any tier; **PowerShell parses but Pro-gates rule execution**
(free CLI silently reports 0 findings — see
[`../semgrep-pro/README.md`](../semgrep-pro/README.md)), and Pro is capped at
10 repos account-wide — unsuitable for a template cloned into many repos.
reverted. Two practical frictions drove that:

- An earlier Windows test of Semgrep's managed pre-commit repository checkout
failed on case-colliding paths. This overlay therefore retains a
`repo: local` PyPI installation. Semgrep added native Windows support in
2025 and now recommends `pipx` on Windows, so this historical managed-hook
failure should be revalidated before treating it as a current platform
limitation ([Semgrep installation guidance](https://semgrep.dev/docs/update),
[native Windows announcement](https://semgrep.dev/blog/2025/semgrep-community-edition-fall-release-2025)).
- This template is PowerShell-heavy, while Semgrep's current supported-language
table does not list PowerShell or SQL. The shipped local rules cover only
languages that were actually verified here; PSScriptAnalyzer remains the
PowerShell policy engine
([Semgrep supported languages](https://semgrep.dev/docs/supported-languages)).

None of that makes semgrep *bad* — the shipped rules are verified and work —
but it does not clear the bar for base-template tooling every clone inherits by
Expand All @@ -32,8 +34,8 @@ default. Tracking issue for a replacement evaluation:
2. **Wire the hook** — paste the block from
[`precommit-hook.yaml`](precommit-hook.yaml) into the `repos:` list of
[`/.pre-commit-config.yaml`](../../../.pre-commit-config.yaml). It installs
from PyPI as a `repo: local` hook (not the managed `repo:` clone, which
fails on Windows — see above).
from PyPI as a `repo: local` hook, preserving the configuration that was
verified on this repository.
3. **Verify** — `semgrep scan --config .config/semgrep/rules --metrics=off .`
should run clean, or report only intended findings.

Expand Down
2 changes: 2 additions & 0 deletions .config/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ cost/risk — *not* by a unit/integration label.
there are none. Standard/thorough hooks ship **commented-in-place** in the
pre-commit config — uncomment when you have tests for them.

<!-- setup-teardown:template-only:start -->
Tests are **co-located** beside the script they cover (`Foo.Tests.ps1` next to
`Foo.ps1`) — the repo-wide default. The exception is [`setup/`](../../setup/),
a user-facing runbook that segregates its tests into `setup/tests/`; discovery
recurses from the repo root, so either layout runs the same.
<!-- setup-teardown:template-only:end -->

Keep the fast (commit) lane genuinely fast, or contributors reach for
`--no-verify`. The floor is pwsh + Pester warm-up (~1-3s), not your assertions.
Expand Down
15 changes: 8 additions & 7 deletions .config/semgrep/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ semgrep into pre-commit is opt-in: see
[`.config/overlays/semgrep/README.md`](../overlays/semgrep/README.md) to
activate.

Scope boundaries: PowerShell parses but its rules are **Pro-gated** (free-tier
CLI silently skips them — see
[`../overlays/semgrep-pro/README.md`](../overlays/semgrep-pro/README.md)
before assuming a `languages: [powershell]` rule here does anything). SQL has
no semgrep support at any tier. pwsh policies live in
Scope boundaries: Semgrep's current
[supported-language table](https://semgrep.dev/docs/supported-languages) does
not list PowerShell or SQL. PowerShell policies therefore live in
[`../PSScriptAnalyzerRules/`](../PSScriptAnalyzerRules/) instead — that
mechanism is active in the base template today. Secrets are gitleaks; workflow
security is zizmor.
mechanism is active in the base template today. Do not infer support merely
because a parser accepts a language name; verify a positive and negative
fixture against the current engine. Secrets are gitleaks; workflow security is
zizmor. The hosted-service trade-offs are recorded in
[`../overlays/semgrep-pro/README.md`](../overlays/semgrep-pro/README.md).

## Rules shipped here

Expand Down
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
/.github/ @DenWin
/.pre-commit-config.yaml @DenWin
/.config/ @DenWin
# setup-teardown:template-only:start
/setup/ @DenWin
# setup-teardown:template-only:end
/AGENTS.md @DenWin
/CLAUDE.md @DenWin
35 changes: 25 additions & 10 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,25 @@ File-scoped linters run only over changed files on a PR; the test hooks are
`always_run`, so the fast lane runs in full regardless.

Third-party actions are **pinned by full commit SHA** (with a `# vN` comment for
readability) — a mutable tag can be re-pointed at malicious code, a SHA cannot.
Dependabot updates the pins and refreshes the comment. The `zizmor` pre-commit
hook enforces this and other workflow-security rules.
readability), following GitHub's recommendation to pin actions to a full-length
commit SHA as the only immutable release reference
([GitHub secure-use guidance](https://docs.github.com/en/enterprise-cloud@latest/actions/reference/security/secure-use)).
Dependabot updates those SHA pins and version comments
([supported ecosystems](https://docs.github.com/en/code-security/reference/supply-chain-security/supported-ecosystems-and-repositories)).
The `zizmor` pre-commit hook enforces this and other workflow-security rules.

The `lint` job's first step is a **full-history secret scan** (`gitleaks-action`).
This is the one CI check that is *not* a local pre-commit hook, by design: the
pre-commit gitleaks hook scans only staged changes (a no-op in CI) and local
hooks are bypassable, so this server-side scan is the unbypassable backstop over
the whole history / PR range. It runs the same gitleaks engine, so no lint logic
is duplicated. Org-owned repos need a free `GITLEAKS_LICENSE` secret; user-owned
repos do not.
is duplicated. `GITLEAKS_VERSION` in the workflow is kept in sync with the
managed hook revision so rule definitions do not drift between local and CI
scans. Org-owned repos need a free `GITLEAKS_LICENSE` secret; user-owned repos
do not, per the
[Gitleaks Action licensing notes](https://github.com/gitleaks/gitleaks-action#license).

<!-- setup-teardown:template-only:start -->
### Protecting the default branch (once per repo)

The `merge_group` gate only fires if the branch has a **merge-queue ruleset**.
Expand All @@ -42,6 +49,9 @@ pwsh -NoProfile -File setup/Protect-MainBranch.ps1

Run it **after** the first push and one CI run (so the `lint` check exists),
then delete the `setup/` folder. See [`setup/README.md`](../setup/README.md).
The trigger and ruleset relationship is documented by
[GitHub's merge-queue guide](https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue).
<!-- setup-teardown:template-only:end -->

### What CI installs

Expand All @@ -55,10 +65,13 @@ self-install:
## `CODEOWNERS`

Auto-requests review from the listed owners when matching paths change; the
sensitive control surfaces (`.github/`, `.pre-commit-config.yaml`, `.config/`,
`AGENTS.md`, `setup/`) are called out explicitly. It only *requests* review
until "require code owner review" is turned on in the branch ruleset — left off
for solo repos, since you cannot approve your own PR.
sensitive permanent control surfaces (`.github/`,
`.pre-commit-config.yaml`, `.config/`, `AGENTS.md`, `CLAUDE.md`) are called out
explicitly. CODEOWNERS requests review by itself; enforcement requires a branch
rule that requires code-owner review
([GitHub documentation](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners)).
That enforcement is left off for solo repos, since you cannot approve your own
PR.

## `pull_request_template.md`

Expand All @@ -73,4 +86,6 @@ is deliberately narrow — the only ecosystem in this template it can maintain i
**GitHub Actions versions** (`github-actions`, monthly). pre-commit hook pins are
updated by `pre-commit autoupdate`; PowerShell/npm deps are handled elsewhere.
A 7-day `cooldown` delays adopting a just-published version, leaving a window for
a compromised or yanked release to be caught upstream.
a compromised or yanked release to be caught upstream. The option and its
behavior are defined in the
[Dependabot options reference](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference#cooldown-).
2 changes: 2 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ jobs:
uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Keep aligned with the managed hook in .pre-commit-config.yaml.
GITLEAKS_VERSION: 8.30.0
# Org accounts need a free GITLEAKS_LICENSE here; user-owned repos do not.

- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
Expand Down
Loading