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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The format is based on Keep a Changelog and follows Semantic Versioning.
### Fixed

- **The pre-commit hook's evidence gate blocks on vendor bloat only in files the commit includes** (`#357`) — re-run `charter hook install --pre-commit` to pick this up. After staged-only tidy (`#320`), the generated hook (no `verify:adf` script) still ran `charter doctor --adf-only --ci` over the whole working tree, so a commit could be refused for bloat it did not contain: an unstaged `GEMINI.md`, a partially staged vendor file the hook skips, or a vendor file skipped because a module tidy would write has unstaged or untracked work. The hook skips such files on purpose, and blocking on them was a regression from `#320`; the old hook only passed these commits by rewriting or staging files you had not staged. The gate now runs `charter doctor --adf-only --ci --staged --tidy-skipped "<files>"`. `--staged` limits the `adf vendor bloat` check to vendor files staged in the index; `--tidy-skipped` takes the staged files the tidy block skipped off that list. Bloat in the rest is reported in a new INFO row, `adf vendor bloat (not blocking)`, which names each file and why it does not block, and gives a per-file `adf tidy --source` command. A staged vendor file that tidy ran on and could not clean, or that was not tidied because tidy failed or `CHARTER_SKIP_TIDY=1` is set, still blocks. Every other doctor check still blocks as before, vendor-mode drift and a malformed `VENDORS` entry included. Without `--staged`, `charter doctor` is unchanged, so CI's `charter doctor --ci` still fails on all vendor bloat. The skip messages no longer claim the gate will block, and say that a plain `charter doctor --ci` still reports the file's bloat. The gate's failure output no longer has the "Blocked because … auto-tidy was skipped" line, so a failing gate no longer runs an extra `adf tidy --dry-run` for each skipped file. `--tidy-skipped` without `--staged` is a usage error (exit 2). An installed hook keeps the old gate until you reinstall, and `charter doctor` reports a `#320`-era hook with an INFO `pre-commit hook` row. With an older local CLI, the new hook's `--staged` and `--tidy-skipped` flags are ignored and the gate behaves as before.
- **The pre-commit hook's commit-scoped gate now also applies in repos with a `verify:adf` script, and runs that script with the repo's package manager** (`#357`) — re-run `charter hook install --pre-commit` to pick this up. The scoped gate above applied only when `package.json` had no `verify:adf` script. `charter setup` and `charter bootstrap` add one, so in most Node repos the hook ran `pnpm run verify:adf` instead, and that script's `charter doctor --adf-only --ci` still blocked a commit on bloat in vendor files it did not include. The hook also called `pnpm` by name, so the gate failed in npm and yarn repos on machines without pnpm. The hook now always runs the scoped doctor first, then `verify:adf` if the script exists, or `adf evidence` if it does not. This means a repo with a `verify:adf` script now gets `npx charter doctor --adf-only --ci --staged …` on every commit, even when its custom `verify:adf` leaves doctor out. It runs `verify:adf` with `pnpm` when there is a `pnpm-lock.yaml` and pnpm is installed, with `yarn` when there is a `yarn.lock` and yarn is installed, and with `npm` otherwise. While `verify:adf` runs, the hook sets `CHARTER_HOOK_SCOPED_DOCTOR` to a one-time token and writes the same token to its own file in the git directory, named after the token, which it deletes when it exits. Two commits running at once in one worktree therefore never delete each other's file. Doctor skips the `adf vendor bloat` check only when the variable and that file match. If the hook cannot write the file, it prints one `[pre-commit]` line saying so, and the doctor inside `verify:adf` checks every vendor file that time, as CI does. It then reports the check as an INFO row, `adf vendor bloat (checked by pre-commit hook)`, and prints a line saying so on stderr. Every other doctor check in `verify:adf` still blocks, and so do the script's other commands. Setting the variable without the file changes nothing, so a CI job that sets it by mistake still gets a full `charter doctor --ci`. `charter doctor` now also reports a hook installed with the earlier `#357` fix with an INFO `pre-commit hook` row, but only in a repo with a `verify:adf` script. Without one, that hook already runs the scoped gate.
- **`adf compile --write` no longer deletes your `## Environment` section from a compiled file** (`#303`) — the `#295` guard only covered pointer files. Re-compiling a file that already carried the compile banner silently dropped a user-added `## Environment` (or protocol) section with exit 0, and git was the only way back. Those sections are now kept in the retained block.
- **`charter bootstrap --force` no longer destroys a hand-authored `CLAUDE.md` before it can be migrated** (`#318`, part 3) — phase 3 overwrote every existing pointer-file name with the template under `--force`, before phase 4 (migrate) ran. Vendor files are not backed up (only `.ai/*.adf` go to `.ai/.backup/`), so phase 4 then found a pointer, migrated nothing, and the file's rules were gone with no `.pre-adf-migrate.bak`. The file is now skipped in phase 3, and with `--yes` phase 4 migrates it into `.ai/` and writes the backup (when it has no `VENDORS` entry).
- **`charter adf compile --check` and `charter adf tidy` read each vendor file's mode** (`#318`, part 2; `#302`) — `--check` compared every target byte-for-byte against compiled output, so a thin pointer was reported `[stale]` forever. Each target is now checked by its mode. A compiled file is `fresh` or `stale` as before. A pointer is `pointer-ok` when `adf tidy` would extract nothing from it, and `pointer-bloated` (exit 1, remedy `charter adf tidy --source <file>`) when it would. An unmanaged file is `skipped-unmanaged` (exit 0). A recorded `compiled` or `pointer` file that looks like another mode on disk is `mode-drift` (exit 1), with both fixes printed: `charter adf vendor set <file> <mode>` to keep the file, or the command that restores it. A missing file is `missing`, and a recorded pointer that is missing names `git checkout -- <file>` or `adf vendor set <file> unmanaged`. No remedy suggests `compile --write` for a pointer or unmanaged file. JSON keeps `stale` (which now also lists bloated and drifted files), `current`, `missing`, `upToDate` and `nextActions`, and adds `skipped` and `targets`. `adf tidy` processes a file only when it is a pointer: no compile banner, at least one pointer marker, and not recorded otherwise. The banner still wins whatever the record says (`#296`). A file recorded `compiled` is skipped with status `compiled`. Its warning lists the content-preserving fix first (`adf vendor set <file> pointer`, then `adf tidy --source <file>`) and says how many items regenerating would discard; `--check` mode-drift does the same. An `unmanaged` file (recorded, or hand-authored with no marker) now has status `unmanaged` instead of `not-found`, and its warning appears when it is recorded or named with `--source`. A recorded pointer with no marker is skipped rather than guessed at. File rows gain `vendorFile`, `mode`, `source` and `skipReason`. A `--source` path that is not a repo-root vendor name keeps the old marker logic. Tidy now finds files through the shared discovery helper, so `AGENTS.md` and `agents.md` that are one file on a case-insensitive filesystem are walked once, not twice. With no `SYNC` entries, `charter adf sync` now points to `charter adf vendor status` in text and in JSON `nextActions`, rather than ending at "Wrote empty lock file". Record entry problems are still reported only by `adf vendor status`. `doctor`, `score`, `bootstrap` and both pre-commit hooks are unchanged and are later parts of `#318`.
Expand Down
10 changes: 8 additions & 2 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ npx charter hook install --commit-msg --force # overwrite existing hooks
- Only the files tidy wrote that its dry run planned are re-staged, never `.ai/*.adf` as a whole. A gitignored module is written but not staged, and the hook says so. A file the hook cannot stage, or one the real run wrote beyond the plan, is named and left unstaged. Skip tidy with `CHARTER_SKIP_TIDY=1`.
- The tidy block never fails the commit: a tidy failure prints one line and the hook carries on.
- The evidence gate after it can. In the generated hook it runs `charter doctor --adf-only --ci --staged --tidy-skipped "<files>"` ([#357](https://github.com/Stackbilt-dev/charter/issues/357)). Vendor bloat blocks the commit only in a staged vendor file that the tidy block did not skip. A staged file that tidy ran on but could not clean still blocks, and so does one left untidied because tidy failed or `CHARTER_SKIP_TIDY=1` is set. Bloat in unstaged vendor files, and in files tidy skipped, shows as an INFO row. A planned-module skip (tidy would write into a module with unstaged or untracked work) skips tidy for every staged vendor file in that commit, so it lifts the bloat gate for all of them. Scoping is by file name: the bloat check still reads each vendor file in the working tree, not its staged content. Every other doctor check blocks as usual. CI's plain `charter doctor --ci` still fails on all vendor bloat. The skip message says how to tidy the file: stage or stash the unstaged work and commit again, or run `charter adf tidy --source <file>` and `git add` the result.
- A hook installed before [#320](https://github.com/Stackbilt-dev/charter/issues/320) re-stages all of `.ai/*.adf`. A hook installed before #357 blocks on vendor bloat outside the commit. Re-run `charter hook install --pre-commit` to replace either one (no `--force` needed for a Charter-managed hook). `charter doctor` reports an old hook as INFO.
- The scoped doctor runs in every repo, including one whose custom `verify:adf` leaves doctor out: such a repo now gets `npx charter doctor --adf-only --ci --staged …` on every commit. Next, if `package.json` has a `verify:adf` script (`charter setup` and `charter bootstrap` add one), the gate runs it; otherwise it runs `charter adf evidence --auto-measure --ci`. `verify:adf` runs with `pnpm` when the repo has a `pnpm-lock.yaml` and pnpm is installed, with `yarn` for a `yarn.lock` and yarn installed, and with `npm` otherwise, so pnpm is never required. The doctor inside `verify:adf` would check vendor bloat across the whole tree again. To prevent that, the hook sets `CHARTER_HOOK_SCOPED_DOCTOR` to a one-time token for that run and writes the token to its own file in the git directory (`charter-hook-scoped-doctor-<token>`, one per hook run, so overlapping commits in one worktree do not interfere), which it removes on exit. If the file cannot be written, the hook prints one `[pre-commit]` line saying so, and that time the doctor inside `verify:adf` checks every vendor file. When the two match, doctor reports `adf vendor bloat (checked by pre-commit hook)` as INFO and prints a line on stderr. All its other checks still block, and so do the script's other commands. The variable without the matching file does nothing, so `charter doctor --ci` in CI keeps full strictness even if the variable is set there. Run by hand, `npm run verify:adf` checks every vendor file, as CI does.
- A hook installed before [#320](https://github.com/Stackbilt-dev/charter/issues/320) re-stages all of `.ai/*.adf`. A hook installed before #357 blocks on vendor bloat outside the commit, and so, in a repo with a `verify:adf` script, does one from the first #357 fix, which also ran `pnpm run verify:adf` by name. Re-run `charter hook install --pre-commit` to replace any of them (no `--force` needed for a Charter-managed hook). `charter doctor` reports an old hook as INFO. It reports a hook from the first #357 fix only where `package.json` has a `verify:adf` script, since without one that hook already runs the scoped gate.
- `--force` — overwrite existing non-Charter hooks

Must specify at least one of `--commit-msg` or `--pre-commit`.
Expand Down Expand Up @@ -160,6 +161,7 @@ npx charter doctor --ci --format json # CI mode: exit 1 on warnings
- `--ci` — non-interactive, exits with policy violation code on warnings
- `--staged` — the pre-commit hook's gate ([#357](https://github.com/Stackbilt-dev/charter/issues/357)): the `adf vendor bloat` check counts only vendor files staged in the git index (staged deletions excluded). It selects files by name and still reads their working-tree content, not the staged blob. Bloat in other vendor files moves to an INFO row, `adf vendor bloat (not blocking)`, so it does not fail `--ci`. No other check is scoped. If the index cannot be read, every vendor file counts, as without the flag.
- `--tidy-skipped "<files>"` — with `--staged` only: space-separated vendor files the hook's tidy block skipped. Their bloat goes to the same INFO row. The value may be empty (`""`). Used without `--staged`, it is a usage error (exit 2).
- `CHARTER_HOOK_SCOPED_DOCTOR` (environment) — set only by the generated pre-commit hook while it runs `verify:adf`, after its own `--staged` doctor. Doctor honours it only when its value has the hook's token form (`<pid>-<seconds>`) and the file `charter-hook-scoped-doctor-<token>` in the git directory holds that token. It then reports the `adf vendor bloat` check as INFO (`adf vendor bloat (checked by pre-commit hook)`) and says so on stderr. No other check changes. The variable on its own (no matching file) has no effect, and `--staged` takes precedence over it.

The hook installed by `charter hook install --commit-msg` counts as a local
trailer gate: it rejects a commit with no trailer under a strict policy. A
Expand All @@ -174,7 +176,11 @@ tidy ([#320](https://github.com/Stackbilt-dev/charter/issues/320)). That hook re
all of `.ai/*.adf`; re-run `charter hook install --pre-commit`. The row also
appears for a hook from before the commit-scoped gate
([#357](https://github.com/Stackbilt-dev/charter/issues/357)), which blocks on vendor
bloat in files the commit does not include; the fix is the same. A current, foreign
bloat in files the commit does not include. It also appears for a hook from the
first #357 fix when `package.json` has a `verify:adf` script, because that hook's
gate stays unscoped there and runs the script with `pnpm` by name. Without the
script, that hook already runs the scoped gate, so it gets no row. The fix in
each case is the same. A current, foreign
or missing hook adds no row. It is INFO, not WARN, so `--ci` does not fail on upgrade.

#### Vendor file modes
Expand Down
Loading
Loading