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

### Changed

- **ON_DEMAND triggers inferred by `adf migrate` and `bootstrap` drop generic heading words and numbers** (`#349`). The router matches a trigger against each word of a task (exactly, or as a long enough prefix stem), so a heading such as `Release Workflow`, `Images and Components`, `Apps via Turborepo` or `Step 1` used to register triggers like `release`, `workflow`, `images`, `components`, `apps`, `via` or `1`, and the module then loaded for almost every task. `GENERIC_HEADING_WORDS` now covers the structure, process and filler words common in CLAUDE.md headings (`workflow`, `release`, `process`, `steps`, `setup`, `commands`, `files`, `code`, `apps`, `components`, `images`, `conventions`, `guide`, `via`, `using`, `when`, `how`, `all`, ...), task verbs (`add`, `fix`, `create`, `remove`, `implement`, `write`, `run`, `make`, ...) and generic nouns (`known`, `considerations`, `instructions`, `requirements`, `prerequisites`, `examples`, `support`, `engineering`): `How to Add a New Endpoint` gives `endpoint`. Domain words (`deployment`, `testing`, `database`, `auth`, `docker`, `api`, ...) are not on it. Numbers and version numbers (`1`, `2024`, `1.2`, `v2.1`, `v20.x`, `v2.0-beta`, `v2.0-beta-2`) are dropped; so is every all-digit token, including one such as `404`. A generic word that is part of the module's own file name is still used (`release` for `release.adf`). When nothing usable is left, the trigger is the module's file stem, as before. Only new registrations are affected; existing manifest entries are never rewritten.
- **On a case-insensitive filesystem, module paths are matched case-insensitively when registering modules in `manifest.adf`** (`#349`). There (Windows and macOS by default) `Infra.adf` and `infra.adf` are one file, so `adf migrate`, `bootstrap` and `adf init` no longer give it a second entry: a module listed under the other spelling is reported as already registered (`already registered as infra.adf under ON_DEMAND`; migrate's text output shows `[already registered as infra.adf]`, JSON adds `registeredAs`), and `bootstrap` no longer reports it as an unregistered module. Where two spellings are planned in one run, the module is registered once with triggers inferred from both. The filesystem is checked by looking up `manifest.adf` with its case swapped and comparing the two files' inode numbers (or, where the filesystem reports none, the directory listing), so a manifest spelled `MANIFEST.ADF` is detected too. On a case-sensitive filesystem (Linux), the two spellings are two files and the loader opens the exact name, so they are still registered separately. `charter doctor` checks a required `DEFAULT_LOAD` module (`core.adf`, `state.adf`) by its exact name, so on a case-insensitive filesystem a `DEFAULT_LOAD` entry spelled `Core.adf` is respelled in place to the name on disk (`core.adf`), changing only that token, instead of getting a second `core.adf` entry that `adf bundle` loaded as a second copy of the file (every constraint twice). JSON: `respelled: "written"` on that registration (`"planned"` under `--dry-run`, where the detail reads `would be respelled to core.adf`). If the edit cannot be made without changing anything else, the entry is left and a warning says `Core.adf and core.adf are the same file here; core.adf was not added again. Rename Core.adf to core.adf under DEFAULT_LOAD in …`. A `Core.adf` listed under `ON_DEMAND` gets the `listed under ON_DEMAND; not moved` warning (`Move Core.adf from ON_DEMAND to DEFAULT_LOAD as core.adf`) and no second entry.
- **`charter adf fmt` keeps the emoji form of the standard `CONSTRAINTS`, `FILES` and `TOOLS` decorations** (`#351`). These are written with a U+FE0F variation selector (`⚠️`, `🗂️`, `🛠️`), as every Charter scaffold writes `⚠️ CONSTRAINTS`. The parser drops the selector, and the formatter used to emit what it parsed, so `adf fmt --write` rewrote them to the text-style glyph (`⚠`) and `adf fmt --check` reported a freshly scaffolded file as non-canonical. The formatter now emits a section's standard decoration whenever the parsed one equals it apart from U+FE0F, whichever form the file had; any other decoration is emitted as parsed (still without U+FE0F, which the parser does not keep). **`adf fmt --write` output changes for files with a bare `⚠ CONSTRAINTS`, `🗂 FILES` or `🛠 TOOLS` header** (it now writes the U+FE0F form), and `adf fmt --check` reports such files as non-canonical until rewritten. The other commands that re-render a whole `.adf` file through the formatter (`adf tidy`, `adf migrate`, `adf patch`, `adf metrics`, the MCP patch tool) write the same headers. The parsed AST is unchanged, so commands that read these files behave as before. `adf sync` hashes the raw bytes, so once one of these writers rewrites a sync source, `adf sync --check` reports it as changed until `adf sync --write`. That check fails the same way after any edit. `adf populate` and the other text-preserving writers add a section header in the same form `adf fmt` emits; a non-standard text-style decoration (say `⚙ NOTES`) is no longer given a U+FE0F that `adf fmt` would then remove. New export `canonicalDecoration(key, decoration)` in `@stackbilt/adf`. This repo's `.ai/*.adf` headers were rewritten to `⚠️ CONSTRAINTS`.

- **`bootstrap --ci github` and `setup --ci github` no longer overwrite a hand-edited `.github/workflows/charter-governance.yml`, and no longer silently widen its permissions** (`#374`). Without `--force`, an existing workflow is refreshed only when it is the current npm or pnpm template; a switch between the two is named (`Updated … (package manager npm → pnpm; adds pnpm/action-setup@v4)`, `packageManagerChange` in JSON). The known templates, each with a `safe` flag and a hand-written description, are listed in `commands/github-workflow.ts`. Every older Charter template had `contents: read` and ran on `pull_request` only. It is now kept with a warning that says what refreshing it changes (`contents: read → write; adds push and workflow_dispatch triggers; adds a step that commits and pushes to the repo`) and names `--force`; when the upgrade also switches package manager, the warning, the `Replaced` line and JSON (`packageManagerChange`) say so (`package manager npm → pnpm (adds pnpm/action-setup@v4)`). Before this change `bootstrap --ci github` made that upgrade silently. Any other workflow is treated as hand-edited and kept byte for byte, with a warning that states what the current template grants and asks you to review it before `--force`. Charter does not describe what a hand edit changed. The warnings name what else `--force` does in each command, and say that deleting the workflow and re-running replaces only it. Comparison drops a leading BOM, CRLF line endings, trailing whitespace and trailing blank lines. A pinned `@stackbilt/cli@x.y.z` is normalized too, but the only template that pinned one predates the current permissions, so a pinned workflow is kept unless `--force`. JSON: bootstrap lists a kept workflow in `setup.details.kept` (platform separator, like the `.charter/` entries) and records `setup.details.workflow: { file, action, reason?, changes?, templatePermissions?, backup?, packageManagerChange? }`; setup sets `workflow.kept`, `workflow.reason`, `workflow.changes` and `workflow.templatePermissions`. `reason` is `hand-edited` or `would-widen-permissions`. Under `--force` the workflow is first copied to `.charter/.backup/github-workflows-charter-governance.yml.<timestamp>` (gitignored), then replaced, printed as `Replaced … (backup: …; changes: …)` or `(backup: …; the current Charter template grants …)` (`setup.details.replaced` in bootstrap JSON; `workflow.backup` and an entry in top-level `replaced` in setup JSON). If that backup fails, the workflow is not replaced and the command exits 1: bootstrap lists it in `setup.details.notReplaced`, fails the setup step (JSON `status: "partial"`) and prints `Bootstrap finished with failures.` instead of `Bootstrap complete.`; setup lists it in `notReplaced` (with `reason`), sets `workflow.notReplaced: true` and prints `CI policy gate: not replaced (backup failed)`. A missing workflow is still created and an identical one left alone. `setup --ci github` without `--force` now refreshes the other package manager's current template, which it used to leave alone; setup's `mutationPlan.workflow.action` is `noop` for a workflow it will keep.
Expand Down
Loading
Loading