feat: page titles must be valid, unique file names on Windows and macOS - new/rename/move refuse, lint reports Unportable Titles, new never overwrites (#155)
CI / verify (push) Successful in 1m37s
Release / release (push) Successful in 37s

Files changed:
- CHANGES.md
- README.md
- VERSION
- instructions/page-lifecycle.md
- instructions/wiki-lint/SKILL.md
- kb/CONTRACT.md
- kb/CONVENTIONS.md
- kb/CONVENTIONS.md.template
- tools/CONTRACT.md
- tools/chemenu/commands/_util.py
- tools/chemenu/commands/lint.py
- tools/chemenu/commands/new_page.py
- tools/chemenu/commands/page_ops.py
- tools/chemenu/lint_core.py
- tools/chemenu/tests/test_lint.py
- tools/chemenu/tests/test_new_page.py
- tools/chemenu/tests/test_page_ops.py
- tools/chemenu/tests/test_titles.py
- tools/chemenu/titles.py
This commit is contained in:
torben committed 2026-09-30 06:29:28 +02:00
1 parent 94deccb18d
commit 8be5e6e5f3
19 files changed
+654 -33

No files matched your search

+11 -4
View File
@@ -180,6 +180,7 @@ Scaffold a new wiki page of any type.
- 0 success
- 1 A page with this title already exists, the type is unknown, or a `--set` value is invalid
- 1 The title is not a valid file name (forbidden character, control character, reserved name such as `CON` or `Index`, trailing dot or space, empty), collides with another page by case or Unicode normalization, or the target file already exists
- 1 A `raw_files` path does not exist
- 1 A capture field the type-spec requires is missing, or set to `unknown`
- 1 `--resume` with a type other than `project`
@@ -191,6 +192,7 @@ Scaffold a new wiki page of any type.
**ON FAILURE**
- A page with this title already exists, the type is unknown, or a `--set` value is invalid -> Not transient - fix the argument and retry once
- The title is not a valid file name (forbidden character, control character, reserved name such as `CON` or `Index`, trailing dot or space, empty), collides with another page by case or Unicode normalization, or the target file already exists -> Not transient - choose another title and retry once. Nothing was created, and for `new project` no tracker project either
- A `raw_files` path does not exist -> Not transient - fix the path and retry once
- A capture field the type-spec requires is missing, or set to `unknown` -> Pass it explicitly (e.g. `--set fidelity=verbatim --set authority=reporting`), then retry once
- `--resume` with a type other than `project` -> Drop `--resume` and retry once
@@ -205,6 +207,8 @@ Scaffold a new wiki page of any type.
**NOTES**
- A title becomes a file name, so it must be valid and unique on Windows and macOS as well as Linux, whichever platform runs the command and whichever root the type writes to. The rule is `kb/CONTRACT.md` § Titles are identifiers; it is checked on the full title, after `title_prefix`.
- `new` never overwrites: a file already at the target - or one a case-insensitive file system would treat as the same file - is refused for every root, `instructions/` included.
- The type-spec drives everything: fields, directory (`base_dir`/`layout`), title prefix, and template. `types list`/`types describe` show what a type requires.
- A schema `default:` is materialized only for a field the schema also lists in `required:`.
- `--set` is repeatable, and comma-separated values fill array fields. An element that itself contains a comma is written `\,`, or passed as its own repeated `--set` for that field - repeating an array field appends.
@@ -463,14 +467,14 @@ Rename a page, or repoint references that name a page that never existed.
- 0 success
- 1 `--from` equals `--to`
- 1 Neither `--from` nor `--to` is a page
- 1 The `--to` title is already taken
- 1 The `--to` title is already taken - also by a page that differs only in case or Unicode normalization, or by a file in the page's directory - or is not a valid file name (see `kb/CONTRACT.md` § Titles are identifiers)
- 1 A page write failed partway; nothing was renamed on disk
**ON FAILURE**
- `--from` equals `--to` -> Fix the arguments and retry once
- Neither `--from` nor `--to` is a page -> Create the page first with `wikitool new`, or drop the reference with `wikitool xref remove`
- The `--to` title is already taken -> Choose another title and retry once
- The `--to` title is already taken - also by a page that differs only in case or Unicode normalization, or by a file in the page's directory - or is not a valid file name (see `kb/CONTRACT.md` § Titles are identifiers) -> Choose another title and retry once. Checked under `--dry-run` too
- A page write failed partway; nothing was renamed on disk -> Check `git status`, resolve the write failure (permissions/disk), then re-run the full command - safe, since each page's rewrite is idempotent
**NEVER**
@@ -483,6 +487,7 @@ Rename a page, or repoint references that name a page that never existed.
- If `--from` is *not* a page but is referenced, it instead repoints those references onto the existing `--to` page and moves nothing - the fix for a reference spelled `act_runner` when the page is `Act Runner`.
- Each page's rewrite is idempotent, so a re-run as-is is safe. If a write fails midway, nothing is renamed on disk and the error lists what was updated.
- `--dry-run` lists every page it would change; run it first to see the blast radius.
- Only `--to` is checked against the title rule. A page whose current title breaks it (`lint`'s Unportable Titles) can always be renamed away from it, and a title that differs from the page's own only by case (`Foo` to `FOO`) is allowed.
**SEE ALSO**
@@ -570,14 +575,14 @@ Move a page (or every misplaced page) to the directory its type-spec computes.
- 0 success
- 1 Neither or both of `--page`/`--reconcile` given
- 1 The named page is not found, or has no `type:` to compute a placement from
- 1 The destination already exists (a pre-existing duplicate-stem collision) - refused rather than silently skipped
- 1 The destination already holds an entry with the same name, or one that differs only in case or Unicode normalization (a pre-existing duplicate-stem collision) - refused rather than silently skipped
- 1 `--reconcile` failed partway
**ON FAILURE**
- Neither or both of `--page`/`--reconcile` given -> Fix the arguments and retry once
- The named page is not found, or has no `type:` to compute a placement from -> Fix the title, or give the page its `type:`, then retry once
- The destination already exists (a pre-existing duplicate-stem collision) - refused rather than silently skipped -> Resolve the collision, then retry
- The destination already holds an entry with the same name, or one that differs only in case or Unicode normalization (a pre-existing duplicate-stem collision) - refused rather than silently skipped -> Resolve the collision, then retry
- `--reconcile` failed partway -> Safe to retry as-is - `--reconcile` only re-moves what is still misplaced
**NEVER**
@@ -1097,6 +1102,7 @@ Run structural lint checks against kb/.
**NOTES**
- Structural and provenance checks over `kb/`: broken wikilinks, dangling frontmatter references, orphan pages, index drift, schema gaps, duplicate titles, title mismatches, uncovered raw files, broken `raw_files:` refs, raw files claimed by more than one source page, unmarked provenance, citation/frontmatter drift, and unbalanced generated-region markers.
- Unportable Titles is a hard finding, and hard at every `kb_version`: a page whose title is not a valid file name on Windows and macOS (forbidden character, reserved name, trailing dot or space), or that collides with another page by case or Unicode normalization. `wikitool rename` is the fix.
- Pages nested more than one directory below their collection are a hard finding - the generated catalog folds these into their area silently rather than merely reading it.
- Edges whose label is missing or not authorised by the source collection's `outbound:` are both hard once `kb_version` has reached the release that introduced labelled edges, and advisory below it.
- Advisory only: `see-also` edges whose reverse direction already carries a specific label - never migration-gated.
@@ -1110,6 +1116,7 @@ Run structural lint checks against kb/.
- `wiki-lint` skill - the procedure that runs this
- `wikitool move --reconcile` - fixes Misplaced and Nested Pages
- `wikitool rename` - fixes Unportable Titles
- `wikitool log status` - whether a full lint is due
#### `search`