Skip to content

grund 0.18.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 10:57
· 858 commits to main since this release

Compatibility notices

  • §DF-a-buffer-is-the-file, §FS-scan.text.buffer-only, §FS-scan.identity.buffers, §REQ-backwards-compatibility.5, §REQ-no-wrong-citation.2: the editor judges an unsaved file as the saved file, before the save. A file that exists only as an editor buffer exists, so a stub to it is no longer stub link target missing and its ID no longer a duplicate declaration. A buffer is the file under every spelling, so an unsaved edit to a stub's target behind a symlink moves the stub at once. A buffer-only file in a directory the walk skips is not read, as the saved file would not be. Who this breaks: only editor users, and only before a save. The verdict moves both ways: a stub whose symlinked target loses its declaration in a buffer is broken-stub at once, and a citation that resolved only through an unsaved file in an ignored directory dangles. Every finding that replaces the old verdict is one the saved file already gets, naming its location and the fix. The command line, CI and the bindings see no change. Closes issue #595.
  • §DF-a-title-citation-is-a-citation, §FS-citations.recognized.title-lines, §REQ-backwards-compatibility.5, §REQ-no-missed-citation.2: a citation in a declaration's title is now a citation. A title such as # FS-a: Alpha, built on a cited FS-b now cites it: refs FS-b lists it, and FS-b is no longer reported unused. Only a citation of the ID the heading declares is still dropped. Who this breaks: a repository whose titles cite other IDs. The verdict moves both ways: a title citation that dangles, names a missing section or an unknown project alias, or is a number-only shorthand is now an error (dangling, missing-section, unknown-project, shorthand-citation), a direction rule now judges it (forbidden-citation, discouraged-citation), a glob in a title is a glob-citation warning, and every finding names its location and the fix, as for a citation anywhere else. fmt still never rewrites a title, so a shorthand there is written out by hand, to the full ID its message names.
  • §DF-bare-block-comment-line-declares, §FS-declarations.line.block-comment,
    §REQ-backwards-compatibility.5, §REQ-no-wrong-citation.2: a declaration in a block
    comment is found with or without the * gutter, and behind a /** or /*!
    opener.
    A bare FS-b: Beta line inside /** … */, /** FS-b: Beta on the opener
    line and the one-line /** FS-b: Beta */ now declare FS-b, so the false
    unknown reference on every citation of them goes, and show, list, refs, cover
    and the editor find them. A bare line declares only in a block whose */ is found.
    /** ## 1. Sub on an opener line is now a section heading, as /* ## 1. Sub is.
    The verdict moves both ways. Who this breaks: a repository with a line inside a block comment that starts, without
    *, with an ID-shaped token of a configured kind. A declaration found there is judged
    like any other: a duplicate of an ID declared elsewhere, a home, index, chapter or
    direction finding, or an unused-declaration warning, and every finding names its
    location and the fix. A bare block line that a configured kind's grammar rejects, followed by a colon, is
    a declaration-near-miss error, as its starred twin is. Measured before release over
    grund, rhei, ephor and fissile and their workspace, none of them sees a new finding.
  • §DF-closing-hashes-are-not-the-heading, §FS-declarations.heading.rendered-text, §REQ-backwards-compatibility.5, §REQ-no-wrong-citation.3: a heading's closing hashes are not part of its anchor. ## 1. Steps ## renders as "1. Steps", whose anchor is 1-steps, but grund fmt --cross-refs wrote #1-steps-, a fragment no heading carries, and show's JSON anchor said the same. Both now give 1-steps. Who this breaks: a repository holding a cross-reference link to a heading with closing hashes. Its verdict moves once: fmt --check reports each such link, and every finding names its file and location and the fix, grund fmt --cross-refs --write. Headings without closing hashes keep their anchors, and the --toc title keeps its bytes until 0.19.0.
  • §FS-config-v2.schema.sources.declarations, §FS-config-v2.schema.sources: a v2 project declares source IDs only in doc comments by default. A // FS-x: note inside a function no longer declares FS-x in a grund_config_version = 2 project, so it is no longer reported as a duplicate. Its marked citations still resolve. Write declarations = "any-comment" under [schema.sources] to keep v1's recognition. Version-1 configurations are unchanged.
  • §FS-scan.scope.extensions.default, §FS-inline-citation-style.1.1.2, §REQ-backwards-compatibility.5, §REQ-no-missed-citation.3: with no [scan] extensions, grund now reads every language it classifies. The default gains h cc cxx hpp hh hxx m mm kts jsx mjs cjs dart r R sh bash zsh after the 26 it already named, and from now on it is md plus every extension grund's language table names. Declarations in those files appear in list, refs and show, and their citations are checked. Who this breaks: a project with no extensions key that keeps a stale or misspelled § citation in a comment in one of those files under a scanned root. Its check moves from success to unknown reference at that file and line, and newly read declarations can surface duplicate declaration errors. The fix: correct the citation at the reported line. To keep the old scan: write the old 26-entry list as your extensions. A project with an explicit extensions, which every grund init scaffold has, sees no change. The verdict changes by the correction route of §REQ-backwards-compatibility.5, because the old default broke §REQ-no-missed-citation.3, and every finding that replaces it names its location and the fix. Why the default widens rather than warns is §DF-default-extensions-follow-language-table. Closes issue #705.
  • §FS-rules.6, §FS-rules.8, §DF-direction-absorbs-findings-not-rules, §REQ-backwards-compatibility.5, §REQ-no-missed-citation.1: a [citations] direction no longer hides a duplicate rule's finding it does not report itself. A rule such as Each FS must cite at least one REQ. beside [citations.FS] must = ["REQ"] now reports missing-citation (or, at should, suggested-citation) at a declaration whose only REQ citation does not resolve — a dangling ID, a missing section, or a dangling citation into a workspace member — which the direction counts and the rule does not. Who this breaks: the verdict moves from exit 0 to exit 1 for grund check --rule … --only-rule over such a declaration, which printed success and now prints docs/fs/FS-a.md:1: error: FS-a must cite REQ (--rule), and for a run that ignores unknown-reference or missing-section; an unscoped default run gains that line beside the unknown reference it already fails on. Every finding that replaces the old verdict names the declaration's location and the kind it must cite, and the fix is to cite a declaration that exists. A unit the direction itself reports is unchanged: its finding wins byte-for-byte. Closes issue #727.
  • §DF-doc-comment-fenced-heading, §FS-languages.fence.comment-block,
    §REQ-backwards-compatibility.5, §REQ-no-wrong-citation.1: a heading inside a
    fenced example in a doc-comment or docstring is no longer read as a section.
    --toc
    stops listing it, show no longer cuts the section above it at the fence, refs no
    longer names it as an enclosing section, and fmt no longer links to it. Who this
    breaks:
    a repository that cites the coordinate such a fenced line spells, which now gets
    missing section; the heading-level, duplicate-section and section-outside-declaration
    errors such a line drew go away, so the verdict moves both ways. Every finding the change
    adds names its location, the citing line, and the fix: cite a section that exists. Measured before release over grund, rhei, ephor, fissile
    and their workspace, none of them sees a new finding.
  • §FS-declarations.checks.parse-moves.2, §FS-declarations.section.relative.where, §REQ-backwards-compatibility.2: a relative section heading in a source doc-comment warns, and becomes a section in 0.19.0. In a doc-comment declaration body, a line such as /// ### .1: Username under /// ## format: Formatting is prose in this release, as before, and check now warns parse-moves at it: from grund 0.19.0 it is the heading of <ID>.format.1, as it is in Markdown. Write it ### format.1: Username to make it that section in both releases, or make it a bold label or a code span to keep it prose in both. Who this affects: only a repository with such a line in a doc-comment declaration body. Its check still exits 0, but prints the warning where it printed success, and under --format json a finding object appears where stdout was empty; the editor shows the same warning. --ignore parse-moves hides this shape together with the other three. Closes issue #665.
  • §DF-fmt-and-editor-answers-from-0-19, §FS-citations.wrap.recognized, §FS-citations.checks.unclosed-wrapper, §FS-lsp.1.3.4, §REQ-backwards-compatibility.2: from 0.19.0, fmt reads a link around a citation as complete, and the editor's title span matches the --toc title. A wrap is [§<ID>](decisions/functional/…) closed by the ) that balances its (. fmt --cross-refs re-derives its whole URL, so [§<ID>](decisions/functional/old(1).md) no longer keeps .md), and a [§<ID>](decisions/functional/ that never closes is wrapped fresh. show and the kind-index check read wraps the same way. The editor's span for a declaration or section title ends where the --toc title ends, so a C-family */ is no longer part of it. Who this affects: wherever a verdict moves, 0.18.0 warns with unclosed-wrapper, naming the ) that keeps today's reading; everything else changes output only, with nothing to migrate. The change is issue #655.
  • §DF-fmt-never-writes-into-a-link-destination, §FS-citations.zones.link-destination, §FS-citations.checks.unclosed-wrapper, §REQ-backwards-compatibility.5, §REQ-no-data-loss.2: grund fmt --cross-refs no longer writes into a link it does not own. It turned [the target](decisions/functional/§<ID>) into a link nested inside that link's URL. Where a [§<ID>](decisions/functional/ never closed, it deleted the text up to a later ) on the line. And after a ](decisions/functional/ that never closes, it linked a later citation on the same line, as in Unclosed [the target](notes and §<ID> after., inside a destination that runs to the end of the line. All three are now left as written. Who this affects: a repository that holds any of these lines. The verdict moves from fail to pass: fmt --check stops reporting the first and the third, and the second, whose fmt --check passes too, now draws the check warning unclosed-wrapper at its line, which leaves the exit status where it was. Every finding that replaces the old verdict names its location and the action to take: the ) that ends the link. Nested links an older grund already wrote are left alone too; search for ]([§ to find them and unwrap them by hand.
  • §DF-four-editor-checks-read-saved-text, §FS-scan.text.buffer, §REQ-backwards-compatibility.2: from 0.19.0 the editor judges four more checks on unsaved text. Until then, as now, the editor reads the saved file for the index check (unlinked-index-entry), the inline-code hint on a dangling citation, the AGENTS.md managed-block check and the end-to-end manifest reader. From 0.19.0 each reads the open buffer, as every other check already does, so an unsaved edit shows the finding a save would. Who this affects: only editor users, and only before a save. The command line, CI and the bindings read no buffers, and no saved repository's verdict changes, so there is nothing to migrate. Tracked in issue #624.
  • §DF-fs-home-moves-from-0-19, §FS-check.checks.fs-home-moves, §FS-init.2.4.10, §REQ-backwards-compatibility.2: the software-development template declares REQ in docs/requirements/, and from 0.19.0 the built-in FS home is docs/functional-spec/. A fresh grund init --scaffold declares REQ as folder = "docs/requirements", writes docs/requirements/README.md, lists REQ in the managed block's project map, and carries a commented [citations.REQ] block; FS stays in requirements.md in this release. Who this affects: only a project with no grund.toml whose FS declarations live in requirements.md. Its grund check now prints the fs-home-moves warning, at exit 0, naming 0.19.0 as the release where the default moves; run grund init to write a config that keeps FS in requirements.md, or move the declarations into docs/functional-spec/. Every config grund init has written names its FS home and is unaffected. The move is issue #723.
  • A marked citation holding a glob operator, such as <§>FS-login.* or <§>FS-log*in.requirements, is no longer cut to the prefix before the operator: no citation of that prefix is recorded, grund fmt no longer links it, and check reports the pattern as the glob-citation warning, which becomes an error in grund 0.19.0. A declaration cited only through such a pattern may now be reported unused, and a unit grounded or satisfying a citation rule only through one may now fail that rule. Cite the exact point, or escape the illustration with the configured marker wrapped in angle brackets. This verdict change is the correction recorded in §DF-glob-citation.2, under §REQ-backwards-compatibility.5, for the prior violation of §REQ-no-wrong-citation.1. Every finding names its location and an action the maintainer can take.
  • §FS-init.1.6, §FS-init.1.7, §FS-config.3.4.4.5, §REQ-backwards-compatibility.2: grund init --scaffold replaces grund init --docs, and --template <software-development|none> picks what init lays. --docs still works and lays the same files, after a stderr warning that it is removed in 0.19.0. --template=none writes a grund.toml with kinds = [], the new spelling of a config that declares no kinds, which earlier releases refused as an unknown key. Who this affects: a script or CI step that runs grund init --docs sees one new stderr line and nothing else until 0.19.0; spell it --scaffold. A Rust caller that builds InitOpts or InitNext with an exhaustive struct literal adds template or declares_kinds. Closes issue #653. (PR #663)
  • §DF-line-prefix-forms-declare, §FS-languages.prefixes.forms,
    §FS-inline-citation-style.1.1.2, §REQ-backwards-compatibility.5, §REQ-no-wrong-citation.2: Lua ---, Haskell
    -- | and -- ^, and Lisp ;; and ;;; comments now host declarations, and a Lisp
    doc block is no longer measured as a note.
    Their declarations appear in list,
    refs and show, and the false unknown reference on every citation of them goes.
    A ;; block directly above a def…, cl-def… or ns form, or at the top of the file,
    is documentation; one inside a body is still a note. Haskell's {-| … -} hosts no
    declaration. The [scan] extensions default gains cl, el, ss, rkt, cljs and
    cljc. The verdict moves both ways. Who this breaks: a repository that declared such an ID a second time
    elsewhere to work around this (now a duplicate at both sites), one with --- FS-Bad: or
    ;; FS-Bad: lines (now a declaration-near-miss), and a project with no extensions
    key that keeps a stale § citation in a file of one of the six new extensions (now an
    unknown reference); every finding names its location and the fix. Under grounding_level 2 or higher a Lisp doc block that cites
    nothing is a warning in this release and an error from grund 0.19.0. Closes
    #703.
  • §DF-multi-line-string-literals, §FS-languages.multi-line-string, §REQ-backwards-compatibility.5, §REQ-no-missed-citation.1, §REQ-no-wrong-citation.2: a string literal that spans lines is one literal, for the forms the spec lists.
    Rust raw and ordinary strings, Java, Kotlin, Scala, Swift and Dart triple-quoted strings, C# raw and verbatim strings, C++ raw strings, Go raw strings, JavaScript and TypeScript template literals, Python triple-quoted strings that are neither docstrings nor assigned data, shell heredocs and PHP heredocs and nowdocs.
    Fewer findings: a qualified citation on an interior line of one of these, and under strict = false a bare ID there, is no longer reported as an unknown reference, and grund fmt --check no longer asks to rewrite a trigger, a shorthand or a bare ID there.
    The verdict changes in both directions, and every finding that remains or appears keeps its location and its action.
    A new finding: a dangling citation after the closer on a literal's closing line, such as "#); // <§>api/FS-gone, was hidden and is now reported at its line. Correct or remove the citation.
    A use that disappears: under strict = false, a bare ID inside such a literal that was a declaration's only use no longer counts, so unused may name that declaration.
    Forms that are not carried are listed in §FS-languages.multi-line-string.unrecognized and read as before, and so does the rest of a file after a line §FS-languages.multi-line-string.bound cannot read to its end, such as one with a /* that never closes.
  • §FS-rules.2, §FS-config.3.9.3, §FS-rules.7.9, §REQ-backwards-compatibility.2: a rule target pinned at its own nested member's alias path now matches that member's declarations. In member group/api, Each API must cite at least one group/api/AR. judges exactly as Each API must cite at least one AR. does, so a rule that failed only because the pin matched nothing now passes. A finding the corrected match alone produces — a must not cite prohibition, an at most N or exactly N count it pushes out of bounds, outbound or inbound, or a per-target must cite each group/api/AR finding for a declaration the pin now brings in, a miss included — is a warning in this release, carrying the code it will carry as an error and ending ; a target pinned at the rule's own nested alias path now matches, and this warning becomes an error in grund 0.19.0; the exit code does not move until then. Who this breaks, from 0.19.0: a workspace with a member nested inside another whose prohibition, upper-bound or per-target (cite each) rule pins that member's own alias path. The escapes are to remove the citation, add the missing one, loosen the rule's count, or pass --ignore on the finding's code. (#627)
  • §FS-config.3.3.1, §FS-declarations.section.relative.form, §FS-section-lock.holds.entry: a name may follow a number in a section path (1.goals), and a cited one is locked. With [id] named_sections = true, ### 1.goals: Goals under ## 1. Overview, or ### .goals: Goals there, declares <ID>.1.goals, and <ID>.1.goals.latency may sit below it. A citation of such an address needs an entry in the section lock, like a relative heading's: run grund fmt --write after citing one and commit what it writes. Renumbering the chapter above it is then reported as a move rather than silently retargeting the citation. Who this affects: headings that were unmarked-heading errors become sections, which loosens a verdict; a run with --ignore unmarked-heading over a body holding them can now fail through duplicate-section, orphan-section or the lock codes. A marked citation of a mixed path, skipped until now, is resolved: one that resolves to nothing, and a rule or [citations] finding only such citations raise, warns in this release and becomes an error in 0.19.0; one whose heading sits in a stub's target outside the scan reports unrecorded-section-citation at once, which grund fmt --write fixes. refs, cover and unused count these citations. In a source doc-comment such a line stays prose and warns parse-moves; it becomes a section in 0.19.0. The managed agent block moves to v17, or v18 where a rule kind is enabled, so each repository carrying it reports agents-init once until grund init runs. Closes issue #662.
  • §FS-scan.projects.kinds, §FS-config.3.9.5.2, §FS-errors.2.5.1, §FS-errors.3.8, §FS-rule-refusals.sentences.order: every command now reads one kind verdict in its own scope, and a rule sentence is refused for the first stage it fails. A [citations] target pinned to a member, api/API, or to any project, */API, is judged against that project's kinds: it loads and is enforced where it was refused as an unknown target kind. list --selector API.terms at a workspace root reads named sections under the project that configures API, so it answers whatever grund api/API-a.terms answers. What warns until 0.19.0: a pinned or */ target its project does not have citable, or one naming an alias the workspace does not load, loads with warning: grund.toml: [citations.FS] must names an unknown target kind `FS` in namespace `api`; this becomes an error in grund 0.19.0 and its siblings, and a run that holds no workspace — a member's own run, or a project with no [workspace] — warns in the same way on a target pinned to another project; from 0.19.0 each is a load error, exit 2, so a config with such a target stops loading. What is appended until 0.19.0: a non-citable kind on list --selector or in a rule keeps its reason, unknown kind "e2e" or literal subject "e2e.terms" does not match the configured ID grammar, and gains — kind "e2e" declares no IDs — tests/e2e/ is not a citable home; this wording changes in grund 0.19.0; a section-component wildcard subject joined by and, FS-x.* must cite at least one FS and must not cite any FS., keeps its conjunction refusal and gains — section-component wildcards are not accepted in phase 1; this wording changes in grund 0.19.0. What does not move: exit codes on every refusal, the invalid-rule code, list --kind and grund id text, every documented refusal row, and --project narrowing. Who this breaks: a script that matches the exact text of a refusal above, or that expected exit 2 from loading api/API or from list --selector API.terms. Closes issue #599.
  • §DF-one-parse-answers-from-0-19, §AR-scanner.2.8, §FS-declarations.checks.parse-moves, §REQ-backwards-compatibility.2: from 0.19.0 every command reads one parse of each file, and some printed bodies, sizes and section owners change. grund 0.18.0 already reads each file once into one record and answers from it, keeping today's output byte for byte. From 0.19.0: show --full and list --size end a Markdown body at the next same-or-higher heading, plain or declaring, and leave out a // note next to a /// block and a deeper declaration's end; show ends a section at a plain heading of its level or above; --toc stops listing /// # 1. Sub and drops a heading's closing hashes from its title; a /** … */ line without * that reads ## 1. Sub becomes section .1; and refs, cover --lines and the chapter rules end a doc-comment section where show does. Who this affects: where one of these moves a check verdict, 0.18.0 warns at the line with parse-moves, naming the edit that keeps today's meaning in both releases; everything else changes output only, with nothing to migrate. Tracked in issue #639.
  • §FS-scan.path.warning, §FS-scan.path.show, §FS-scan.path.list, §FS-scan.path.id, §FS-scan.projects.narrowed.alias, §DF-path-narrowing-warns: a path that narrows what show, list or id resolve now says so, and so does a narrowed check <dir> refusing an alias the workspace root resolves. Every answer keeps its stdout and exit code. show, list and id given a path narrower than their project add a warning: line to stderr wherever reading the whole project would change the answer, naming the site outside the path and ending in from grund 0.19.0 a path narrows what is reported, not what resolves. In a workspace, check <dir>'s unknown project alias <alias> keeps its text and appends ; grund check from the workspace root resolves this alias, and from grund 0.19.0 grund check <dir> will too where the root resolves it; a member run is unchanged. Who this breaks: a consumer matching the whole alias line, or asserting an empty stderr from a narrowed show, list or id. Closes issue #620.
  • §FS-init.2.3.4.16, §FS-init.2.3.7.1: the managed block moves v17 → v19, and v18 → v20 where a rule kind is enabled. Standalone blocks omit Project namespaces; workspace blocks retain its existing text. Both setup-instructions surfaces teach member splitting. Who this breaks: every repository with an earlier grund block, including workspace repositories; grund check and grund init --check report it outdated. Migration: run grund init. (#574)
  • §FS-declarations.section.relative, §FS-section-lock, §FS-errors.5.5: a section heading may write only the component it adds. Inside a Markdown declaration body, ### .1: Username under ## format: Formatting declares FS-login.format.1; citations, refs, list, completion, JSON, diagnostics, --toc and the title hover all use the canonical path, and the default read, --brief and --full print the heading as written. A relative heading needs its parent exactly one level up, or fails with the new relative-section-parent error. Citing one needs an entry in the committed section lock, .grund/sections/<ID>.lock, or .grund/sections/<alias>/<ID>.lock of the citing project for a citation into another workspace member: run grund fmt --write after citing a relative section and commit what it writes, or check reports the new unrecorded-section-citation; section-address-changed reports a cited section whose text moved to another address, and invalid-section-lock a lock line it cannot read. Who this breaks: no default check run, because every dotted heading in a declaration body was already an error and nothing outside one changes. A run with --ignore unmarked-heading over a body holding dotted headings can now fail through relative-section-parent or duplicate-section. A malformed dotted heading's unmarked-heading message gains an appended ; under <parent> a relative heading is written <heading> clause, so a consumer matching the whole message sees it change and a prefix or code consumer does not. The managed agent block moves to v16, or v17 where a rule kind is enabled, so each repository carrying it reports agents-init once until grund init runs. Source doc-comments are unchanged in this release. Closes issue #643. (PR #666)
  • §FS-fmt.6.2.3: a repeated heading's anchor is numbered. Two headings with the same text in one file now anchor as GitHub renders them, #1-overview and then #1-overview-1 (_1 under mkdocs), in the links fmt --cross-refs writes and in the anchor show reports; before, every repeat linked to the first. Who this breaks: a repository whose committed links cite the second or a later of two identically titled headings in one file: grund fmt --check now fails on each, with a row naming the anchor it moves to and that it is numbered since grund 0.18. Run grund fmt --cross-refs --write and commit the result.
  • §FS-rules.3.5.3: a refused rule's chapter subject names the component of its
    path that failed.
    grund check --rule and a configured rule declaration's
    invalid-rule finding now refuse The requirements.1 chapter of each FS …
    with numbered chapter subjects can detach when headings move, as they
    already refused FS-login.requirements.1 …. They refuse
    The requirements.* chapter of each FS … for its wildcard. Neither is called
    a section-grammar mismatch any more. A path with an empty component, such as
    FS-login.requirements. or FS-login..requirements, is refused as not
    matching the configured section grammar rather than as numbered. That holds
    on the rule surfaces and on grund list --selector, which also drops its
    hint: line for such a path. Who this breaks: a script matching, for
    such a subject, the exact stderr of check --rule, the exact message of an
    invalid-rule finding, the exact failure message of the Python
    check(rule=…) or Node check(root, { rule }), or a refused selector's exact
    stderr. Everything after the reason, exit codes, stdout, the finding code
    and location, and every documented refusal row do not move. Closes
    issue #507.
  • §FS-rules.3.5.4: a refused rule's accepted form: is the sentence you typed
    with the refused part replaced, and it is accepted when you paste it back.

    grund check --rule and a configured rule declaration's invalid-rule
    finding no longer suggest a fixed sentence about FS, GOAL, REQ or AR
    whatever the repository configures. FS-*.requirements must cite at least one FS. is answered with The requirements chapter of each FS must cite at least one FS., and Each FS should cite no FS. with Each FS should not cite any FS.. Where grund cannot know what belongs in the gap, such as a kind the
    repository does not configure or a path subject, it offers no form:
    check --rule prints the reason and then known kinds: …, and the finding
    ends at the reason. Five documented refusals change: the path subject and
    Each POLICY now offer no form, and FS-login.*, FS-login.2 and
    Each chapter of each FS are answered with FS-login or Each FS, and a
    chapter path with a numbered, wildcard or empty component keeps the chapter
    names typed before it. The
    whitespace refusal for a presence NAME offers the sentence with the NAME
    trimmed, or nothing. Who this breaks: a script matching the exact stderr
    of check --rule, the exact message of an invalid-rule finding, or the
    exact failure message of the Python check(rule=…) or Node
    check(root, { rule }), for a refusal whose form changes or goes away, and a
    tool that expects every rule refusal to contain accepted form. Exit codes,
    stdout, the finding code and location, the reasons and the set of accepted
    sentences do not move. Closes issue #513.
  • §FS-init.2.3.5.11, §FS-config-v2.presentation: a version-2 repository's guidance moves into the AGENTS.md above each kind's places, and its managed block moves v19 → v21, or v20 → v22 where a rule kind is enabled. [presentation] rules is now executed, default home. Who this breaks: every version-2 repository with a row whose citation rule, grounding clause, field or chapter rule lands outside the root, and every one that declares a field. grund check reports agents-init until grund init runs; that run writes AGENTS.md files (and their CLAUDE.md/GEMINI.md symlinks) into the destination directories, and those bullets leave the root block, which points to them under ### Scoped guidance. Migration: run grund init; to keep everything in the root file, set [presentation] rules = "root". Version-1 repositories are unchanged. (#460)
  • §FS-section-lock.holds.fingerprint, §FS-section-lock.limits.baseline: a locked section's nested headings are fingerprinted relative to it, so a renumber that rewrites them is still reported as a move. This concerns only locks written by a development build of grund since the section lock arrived; no released grund wrote one. Such a lock may list section lock: refresh rows under grund fmt --check for sections that hold nested headings. On a tree that passes check and fmt --check with the old binary, the upgrade changes no check verdict. A move the old binary reported and nobody resolved may no longer be reported, which is why the preflight comes first. So before upgrading, get grund check and grund fmt --check passing with the old binary, resolving any move it reports; then upgrade without renumbering anything, run grund fmt --write once, and review and commit the lock diff. That re-records the current texts; it does not recover a target lost earlier (§FS-section-lock.limits.baseline).
  • §DF-stub-target-citations-and-chapters, §FS-declarations.stubs.target.5, §FS-rules.7.11, §REQ-backwards-compatibility.2: a stub's target outside the scan now lends the stubbed declaration its citations and chapters, so rules, [citations], refs and unused read it as if it were scanned. A rule or [citations] obligation the target's citations meet no longer fails at the stub, a chapter rule counts the target's chapters, and grund refs lists the target's citations. Who this affects: repositories with a stub to a declaration outside [scan] include. A prohibition the target's citations break, a count or chapter count they now exceed, and a [citations] must on a target that cites nothing are reported as warnings ending ; a citation or chapter a stub's target holds now counts, and this warning becomes an error in grund 0.19.0, and become errors in 0.19.0. Nothing is reported about the target file itself, and a citation there that resolves to nothing still counts for nothing.
  • §DF-stub-target-section-declared-twice, §FS-declarations.stubs.target.5, §REQ-backwards-compatibility.5, §REQ-no-wrong-citation.1: a section a stub's target declares twice is reported where it is written, whether or not the scan reaches the target. A stub that points outside [scan] include at a declaration claiming one section path twice now fails check with the duplicate-section error the same tree reports with the target scanned, such as src/x.rs:5: error: duplicate section FS-x.1 (also declared at src/x.rs:9). Who this breaks: the verdict changes from pass to fail for such a tree whether or not anything cites the section. Where something cites it, check used to resolve the citation to the first heading while show refused it as ambiguous; where nothing does, the next citation of that section would have been resolved the same way, silently. Every finding that replaces the old verdict is the existing error, naming its location in the target and every colliding heading, and the action is to renumber one of them.
  • §DF-stubs-by-home-and-at-the-target, §FS-rules.7.10, §FS-declarations.checks.stub-shape, §REQ-backwards-compatibility.2: a rule declared through a stub now applies, and from 0.19.0 a stub is read by its home and shown at its target. check no longer reports rule declaration has no title for a rule written as a stub, whether or not the scan reaches its target; where the target is outside [scan] include the rule now runs, and its findings are warnings until 0.19.0, so a run that passed with --ignore invalid-rule still passes. From 0.19.0 a heading whose title is one link is a stub only in a file of some kind's home, not wherever a directory named docs is on its path; check warns now, as stub-shape, at every heading that reading changes, and the exit code does not move. Also from 0.19.0, a stub to a target outside the scan is shown at the target by plain list and by the unused warning, and show --batch --all, shell completion, the editor and list --selector see the target's sections. Who this affects: repositories that write a rule as a stub, or a stub-shaped heading outside every kind's home or in a home with no docs on its path; each warning names the edit that keeps today's reading.
  • §FS-scan.checks.unread-scan-root, §FS-scan.scope.hidden: check now warns, exit unchanged, where a configured file home, version-2 files entry or include file has a hidden name. Each such file earns one unread-scan-root warning at its row's grund.toml line in place of success. Who this breaks: a script that greps for success, and a repository with such a row; rename the file to a name that is not hidden, drop the include entry, or set scan = false on a non-citable row that only lists a place. The file is still not read. The verdict changes by the correction route of §REQ-backwards-compatibility.5, because the old silence broke §REQ-no-missed-citation.1, and every finding that replaces it names its location and the fix. Why it is a warning and not a read is §DF-unread-hidden-root. Closes issue #689.
  • §FS-scan.checks.unread-scan-root, §FS-scan.scope.extensions.root: check now warns, exit unchanged, where a configured file home or include file has an extension [scan] extensions does not list. Each such file earns one unread-scan-root warning at its row's grund.toml line in place of success. Who this breaks: a script that greps for success, and a repository with such a row; add the extension, rename the file, or set scan = false on a non-citable row that only lists a place. Nothing new is read. The verdict changes by the correction route of §REQ-backwards-compatibility.5, because the old silence broke §REQ-no-missed-citation.1, and every finding that replaces it names its location and the fix. Why it is a warning and not a wider read is §DF-unread-scan-root. Closes issue #654.
  • §FS-init.2.4.3: grund init in a repository with no config now writes a minimal version-2 grund.toml: the rows, their places and titles, and nothing grund would assume anyway. New repositories therefore get slug-only IDs (FS-login, not FS-001-login); run grund config show to see every effective setting. Existing version-1 files keep loading unchanged, and init never rewrites one.
  • §FS-config-v2.schema.places: a version-2 row may list several folders and files, and the scan is derived from them. The row named code is the complement; with no code row, or one with no place, a v2 config reads the whole config root, so a v2 file that lists places and no code row now reads files outside those places as code, where it read only its places. A file belongs to the row that lists it, or else to the row of the deepest folder containing it, so a declaration in a place nested inside another row's place is now checked against that nested row: a declaration of the wrong kind there is a misplaced-declaration error where version 1 checks nothing. The same place on two rows, a place outside the config root or inside a workspace member, a folder place that links out of the project, a non-citable row with no place, and scan = false or citable = true on code are load errors at their line. Version-1 configs are unchanged.