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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ The format is based on Keep a Changelog and follows Semantic Versioning.
- **`charter score` counts a thin pointer as substantive when `.ai/` is** (`#318`, part 3) β€” score numbers in JSON change, and only upward. A pointer `CLAUDE.md`, `.cursorrules` or `AGENTS.md` used to score as "thin" (βˆ’5, and "Expand `CLAUDE.md` beyond a thin pointer") however much its `.ai/` held. It is now substantive when its text is, or when the file `adf compile` would render for it is (compiled in memory; nothing is written). Hand-authored and compiled files are scored on their text as before, so no score goes down. `signals.agentConfig` adds `mode`, `source` and `substantiveVia` per file. Recommendations no longer tell you to edit a pointer or compiled `CLAUDE.md`; a pointer with little to route to gets "Add repo commands and guardrails to `.ai/core.adf` (or run: charter adf populate)".
- **`charter adf migrate` without `--source` skips a file recorded `unmanaged`** (`#318`, part 3) β€” naming the file with `--source` still migrates it, and now changes its entry to `pointer` before the pointer is written (back to `unmanaged` if the migration fails), so doctor reports no drift afterwards.
- **`adf compile --check` names `charter adf vendor restore <file>` instead of `git checkout -- <file>`** (`#318`, part 3) β€” for a missing recorded pointer, and for a recorded pointer that now holds another mode's content.
- **A legacy-keyed `AGENTS.md` gets a specific refusal from `adf compile` and `adf vendor set`** (`#302`, `#318` part 6) β€” when `AGENTS.md` is recorded under the legacy name `agents.md`, `adf compile --target agents|all --write --mode compiled` used to report the generic `refused-record`. It now reports `refused-legacy-spelling` (new `targets[].status` value, exit 1), renders nothing and leaves the record alone. Without `--mode`, the skip or refusal remedy becomes `charter adf vendor rename-legacy && charter adf compile --target agents --write --mode compiled`. `adf vendor set AGENTS.md compiled` exits 1 with a message naming the rename instead of "no compile target writes it". Doctor's `legacy-spelling` row now names `charter adf vendor rename-legacy`, and sets `remedy` to it.

### Added

- **`charter adf vendor rename-legacy`, and doctor rows for a tracked lowercase `agents.md`** (`#302`, `#318` part 6) β€” repos bootstrapped before `#297` track `agents.md`, which nothing renamed. `rename-legacy` moves the `VENDORS` key first (`agents.md: <mode>` becomes `AGENTS.md: <mode>`, or a same-mode duplicate `agents.md` line is removed through a new `removeManifestBullet` and the same re-parse guard), then renames in two steps through `agents.md.charter-rename`, which works on case-insensitive filesystems. **Index effect:** when git tracks `agents.md`, both steps are `git mv`, so the command leaves a staged rename (`R agents.md -> AGENTS.md`) and stages nothing else. `manifest.adf` is left unstaged, and the output names `git add .ai/manifest.adf && git commit` (with `AGENTS.md` in the `git add` when its title line was rewritten), plus a note that `git commit` also records anything already staged. An untracked file, or a directory outside git, is renamed on disk only. **Content effect:** the only edit is a first line that is exactly `# agents.md`, rewritten to `# AGENTS.md` and left unstaged. Mentions of `agents.md` elsewhere are listed, never edited. It refuses (exit 1, nothing changed) when both spellings are tracked or are two different files (it never merges or deletes), when `agents.md` has uncommitted changes, when the temp name exists, when the two keys are recorded with different modes or `agents.md` is listed twice, when the git index cannot be read inside a work tree, or when it is run below the repository root. The index is read with plain exact paths, so exported `GIT_*_PATHSPECS` variables cannot hide a tracked `agents.md`. If a rename step fails, the completed step is undone and the record put back. `--dry-run` writes nothing, and `--format json` prints one result. Nothing calls it automatically: bootstrap, tidy, compile and the hook never rename. `charter doctor`'s `adf vendor mode` check adds three INFO rows read from the git index only: `legacy-tracked` (names `rename-legacy`), `legacy-both-distinct` (merge into `AGENTS.md`, then `git rm agents.md`) and `legacy-both-one-file` (`git rm --cached agents.md`). They never fail `--ci`.

- **`charter adf tidy --source` is repeatable, and `--print-paths` lists the files tidy writes** (`#320`) β€” each `--source` is processed in argument order, and two names for one file (`AGENTS.md` and `agents.md` on a case-insensitive filesystem, or a hard link) are tidied once, under the first name. A `--source` with no value is now a usage error (exit 2); it used to tidy every vendor file silently. `--print-paths` prints one `vendor <path>` or `module <path>` line per file tidy writes (or would write, under `--dry-run`) and nothing else on stdout; warnings go to stderr. It cannot be combined with `--format json` (exit 2). JSON output adds `paths` (`vendor` and `modules`); `modulesModified` is unchanged. A single `--source` gives the same output as before.
- **`charter adf vendor restore <file>`** (`#318`, part 3) β€” rewrites one vendor file as its thin pointer template (`CLAUDE.md` gets the hybrid template with its module index, as bootstrap writes it), carrying `## Environment` and other retained sections over verbatim. It runs only for a file in `pointer` mode, or an absent file with no entry, and refuses (exit 1, nothing written) whenever content would be lost: hand-authored content, content `adf tidy` would extract, any line of a pointer that is not from a Charter template and that the new pointer would not carry (an edited title, an extra note, edited `## Session Start` text), your own sections in a compiled file, or a compiled file that differs from what `.ai/` compiles to now (checked in memory). It is the fix doctor names for a recorded pointer that now holds compiled output. A file with no entry gets `pointer` recorded first; a failed file write puts the manifest back and is reported as `write-failed` with `writeError`. `--dry-run` and `--format json` are supported.
- **`charter adf migrate` registers the modules it routes content into** (`#239`, completes the migrate half of `#241`) β€” migrate could create `infra.adf`, `frontend.adf` or `core.adf` without adding them to `.ai/manifest.adf` or the `CLAUDE.md` module index, so no agent knew to load them and `charter doctor` failed until someone wired them by hand. Each target module that is not already in the manifest is now registered after the module merge and before the pointer is written, so the hybrid `CLAUDE.md` index includes it. A source other than `CLAUDE.md` that adds a module refreshes that index afterwards. `core.adf` and `state.adf` go to `DEFAULT_LOAD`, through the same classification `doctor` checks. Other modules go to `ON_DEMAND` with up to six triggers inferred from the headings of the items routed there. The heading word the router matched counts double, a named module's curated triggers come first, words that already trigger another module are dropped, and the lowercase module stem is the fallback. To supply that heading word, `@stackbilt/adf`'s `RoutingTrace` gains an optional `headingKeyword`. `classifyElement` now always sets `routingTrace`, even without a trigger map, in which case `candidateScores` is `{}`. That is an additive change, but code that read a defined `routingTrace` as "content routing ran" should check `candidateScores` instead. Registration is append-only. New bullets are inserted as text after the section's last item, in that item's indentation and bullet style, and a missing section is appended at the end of the file. Comments, HTML comments, tables, section order, blank lines and CRLF or LF line endings are left byte-for-byte. Before writing, the edited manifest is re-parsed and must equal the old one plus exactly the new entries; otherwise nothing is written and a warning is printed. An existing entry is never rewritten, moved or given new triggers, and a re-run writes nothing. An existing entry is reported as it stands, in its real section with its triggers as spelled. A section header with no items counts as an empty list and is filled. `core.adf` under `ON_DEMAND` is left in place with a warning naming the move. A missing `manifest.adf` warns to run `charter adf init`, an unparseable one warns to fix it or run `charter doctor`, and in both cases registration is skipped. The plan prints a `REGISTER` line per module, and `--format json` adds `sources[].registrations[]` (`module`, `loadPolicy`, `triggers`, `triggerSources`, `reason`, `status`, `skipReason`). `--dry-run` writes nothing and reports each planned module once across sources. As a known gap, it does not simulate an earlier source's new triggers when routing a later one. Bootstrap's two orphan writers now use the same shared writer, which also removes a line-splice ON_DEMAND writer that could register a module twice. That brings three behavior changes. Orphan triggers are now the lowercase stem (`Observability.adf` gets `observability`). A `DEFAULT_LOAD` that is not a list is now skipped with a warning, where it used to have a raw `DEFAULT_LOAD:` block appended. And with `--format json`, writer warnings now appear in the phase 3 step's `warnings`. A bootstrap `--yes` run registers migrate's modules in phase 4, so a re-run finds no orphans. `charter adf create` keeps its own upsert writer, and the stale `charter adf register` hint in bootstrap is tracked separately in `#347`.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ charter adf compile --target all --write # Re-render compiled-mode CLAUDE.md, AG
charter adf compile --target all --write --mode compiled # Switch pointer files to compiled output
charter adf vendor status # Show each vendor file's mode (pointer, compiled, unmanaged)
charter adf vendor restore CLAUDE.md # Rewrite one pointer from its template (refuses if content would be lost)
charter adf vendor rename-legacy # Rename a pre-#297 agents.md to AGENTS.md (stages a git mv; moves its VENDORS entry)
charter adf patch <file> --ops <json> # Apply structured patch operations to an ADF file
charter adf sync --check # Verify files match lock
charter adf fmt .ai/core.adf --write # Reformat to canonical form
Expand Down
Loading
Loading