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
40 changes: 30 additions & 10 deletions docs/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,13 +353,14 @@ files.glob = ["*.yml", "*.yaml"]

`inherit.sets` names bundled sets to inherit; it does not add settings. There
is no `true` shorthand — naming the sets is cheap, and what a repository
inherits should be written in the repository. Twenty-one are compiled into the
inherits should be written in the repository. Twenty-two are compiled into the
binary and mirrored in [`policy/base/`](../policy/base), each **named by what
it refuses** so the name predicts the rule list:

| set | refuses |
|---|---|
| `process-residue` | authoring and process residue in committed content — conflict markers, home paths, dated and status metadata, tracker and thread references, private data paths — and the residue a process leaves in the policy file itself: a rule transcribed out of a set. **Installs `pre-commit` and `manual`**, and the two report different things |
| `process-residue` | authoring and process residue in committed content — conflict markers, home paths, dated and status metadata, tracker and thread references in documentation, private data paths — and the residue a process leaves in the policy file itself: a rule transcribed out of a set. **Installs `pre-commit` and `manual`**, and the two report different things |
| `code-residue` | a tracker reference in a source, configuration, build or packaging file: the tracker rule of `process-residue` over every file that is not Markdown, reST or plain text, under its own id. Its own set because a scope is not something `[inherit]` lets a repository choose, and the release that widened the rule in place was undone by hand in every tree that measured what it reported. For a tree whose comments already cite durable contracts. **Installs `pre-commit` and `manual`**, the same ceiling as the set it was split from |
| `credentials` | credential material — private keys and service tokens, literal credential values, populated environment files, browser profile and session stores. A literal credential value is two rules split on the quote: in source the value half must be a quoted literal, so `password: modem_config.password.clone()` and `token = raw.trim_start_matches('v')` are expressions and not findings; in a config file — `.env`, INI, YAML, TOML, JSON, XML, properties — the text after the separator is the value whether quoted or not, and `-in-config` reads it unquoted |
| `unmanaged-pins` | a version pinned where no manifest holds it — a shell install line, a `releases/download/vX.Y.Z` URL, a versioned `curl` or `wget` |
| `host-identity` | the machine the author is standing on — its username, home path, hostname and default route, read at scan time and searched for in content |
Expand Down Expand Up @@ -562,18 +563,37 @@ Each is named separately because taking one is a separate decision:
has on purpose, `host-identity` shells out to read the running machine, and
`captured-fixtures` refuses the script a parser's own test corpus is made of.
The `process-residue` set rejects GitHub issue and PR URLs and numbered tracker
references in source, configuration, and documentation, including
systemd `Documentation=` fields. Keep the durable explanation in the repository;
issues can point to code, but code must not require an issue to explain it.
Captured logs, bug reports, and benchmark results belong in issues. Benchmark
programs and synthetic fixtures belong in the repository. Git history owns edit
dates and change history; tracked files describe the current contract.
references in documentation — Markdown, reST and plain text — under
`no-task-tracker-references`. The same pattern over every other tracked file —
source, configuration, scripts, packaging, including systemd `Documentation=`
fields — is `no-task-tracker-references-in-code`, the one rule of the
`code-residue` set, and the two exclude each other's files so a line is under
exactly one of them. The scopes are two sets rather than one rule because a
scope is not something `[inherit]` lets a repository choose: it takes sets
whole and disables rules by id. The release that widened the docs rule to every
file in place was undone by hand in every tree that measured what it reported,
a docs-only copy under the same id in each; a set is the opt-in a copy was
standing in for. Keep the durable explanation in the repository; issues can
point to code, but code must not require an issue to explain it. Captured
logs, bug reports, and benchmark results belong in issues. Benchmark programs
and synthetic fixtures belong in the repository. Git history owns edit dates
and change history; tracked files describe the current contract.

`no-process-history-references`, in the same set and over the same
documentation globs, refuses the narrative form alone — a sentence that says a
point was settled in a discussion, in an issue, a thread or a PR, and names no
record — and nothing the tracker rule reads. It used to carry the tracker URL
and the `issue #N` form too, so a
documentation line in either was reported twice under two ids, and a consumer
reading a doubled report disabled one of the rules. The two are disjoint now:
the record-naming forms, in any case of `issue` and `pr`, are the tracker
rule's.

The static checks recognize tracker references, authoring-date headers,
changelog filenames, and log filenames. Identifying a pasted bug report,
benchmark result, or narrative edit history requires review; these checks do
not claim to recognize arbitrary prose. The tracker rule retains the set's test
directory and Go test exclusions for synthetic examples, and excludes `testdata`.
not claim to recognize arbitrary prose. Both tracker rules retain the set's test
directory and Go test exclusions for synthetic examples, and exclude `testdata`.
Date and log rules exclude `tests/fixtures`, `test/fixtures`, and `testdata`.
Unqualified all-uppercase identifiers are not treated as repository shorthand,
and bare numbers need prose context, an opening delimiter, or a list separator
Expand Down
86 changes: 86 additions & 0 deletions policy/base/code-residue.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Base rule set: code-residue (a tracker reference in a source, configuration,
# build or packaging file -- the every-file scope of the rule that
# `process-residue` runs over documentation)
#
# The pattern `no-task-tracker-references` reads over Markdown, reST and plain
# text, read here over everything else a repository tracks: source, unit
# files, scripts, manifests, lockfiles. Pull in with:
#
# [inherit]
# sets = ["code-residue"]
#
# WHY THIS IS ITS OWN SET rather than the scope of the rule in
# `process-residue`. For one release it was: the documentation glob came off
# the tracker rule and the rule read every tracked file, in every repository
# that inherited the set, with no diff in any of them. Two consumers under one
# superproject measured what that cost. The first run under the wider scope
# reported 27 findings on a pointer bump that touched none of the reported
# lines; a sibling counted 276 across its source, packaging and scripts on a
# commit that touched none of them either. Both shadowed the rule with a
# docs-only copy under the same id, which is one release's widening undone by
# hand in every tree that noticed. A third superproject, whose source comments
# cite an issue as the place a measurement lives, disabled the rule in every
# policy it has. Each of those was a consumer choosing between a rule that
# refuses every commit and no rule at all, because a scope is not something
# `[inherit]` lets a repository choose: it inherits SETS, whole, and disables
# rules by id. Splitting the scope across two sets is what puts the choice
# back where the consumer can make it.
#
# WHO TAKES IT. A tree whose comments have already been rewritten to cite
# durable contracts -- a document in the repository, a named constant, the
# test that proves the behaviour -- rather than the tracker item that once
# argued for them. There the rule finds nothing on the day it is taken and
# refuses the next comment that reaches for an issue number instead of an
# explanation. A tree that has not done that rewriting takes `process-residue`
# alone and keeps the tracker rule on its documentation, which is the scope
# the rule shipped with and the one it is back on.
#
# THE TWO RULES ARE DISJOINT BY FILE, not by pattern. Both compile the same
# expression; this one excludes the three documentation globs the other
# selects, and both exclude the same test directories and fixture trees. So a
# line is under exactly one of them, a repository that inherits both sets
# never sees the same line reported under two ids, and a pattern edit lands in
# both or in neither.
#
# THE FIXTURE EXCLUSIONS ARE THE ONES `process-residue` STATES. A Go test
# sits beside its subject as `<name>_test.go`, a Python or JavaScript test
# under `tests/`, and a synthetic fixture under `testdata/`; each legitimately
# carries example-bad content and each is left out here for the reason argued
# at the head of that set.

# WHAT THIS SET MAY INSTALL. `stages` is the ceiling on the hooks a rule here
# may declare, and it is the ceiling `process-residue` carries, because this
# set is one of that set's rules under another scope and a rule that moves
# between the two -- which is how this set came to exist -- must find the same
# ceiling on both sides. Nothing here reaches it: the one rule is a content
# rule the scan owns, and it declares no hook. A rule added to this set that
# reached past this line would be refused at load.
[set]
stages = ["pre-commit", "manual"]

[rule.no-task-tracker-references-in-code]
message = """
Source, configuration, and build files must not depend on volatile issue or
PR records. State the durable contract and rationale in a comment, or link to
repository documentation. Issues may link to code; code must not depend on
issues. Keep logs, bug reports, and benchmark results in issues, and edit
chronology in Git history.
"""
# The expression is the one `no-task-tracker-references` compiles, arm for
# arm, and the argument for each arm is written above that rule: what an
# unqualified repository name needs, what a bare number needs before it, why a
# list separator counts, and why the words `issue` and `pr` are read in any
# case. It is duplicated rather than referenced because a rule is a table of
# its own and there is nothing in the policy language to point one rule's
# pattern at another's; the corpus holds the two to the same samples.
regexp = 'github\.com/[^[:space:])]+/(?:issues|pull)/[0-9]+|\b(?:[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+|[A-Za-z0-9_.-]*[a-z][A-Za-z0-9_.-]*)#[0-9]+\b|(?:^|\b[a-z]+[\t ]+|(?i:\b(?:issue|pr)[\t ]+)|\(|\[|[,;/][\t ]*)#[0-9]+\b'
files.include = ["."]
files.exclude = [
"*.md",
"*.rst",
"*.txt",
"**/tests/**",
"**/test/**",
"**/*_test.go",
"**/testdata/**",
]
24 changes: 21 additions & 3 deletions policy/base/process-residue.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,35 @@ chronology in Git history.
# standards designators as repositories or task-tracking prose. The separators
# are admitted because a citation list names its repository once and leaves
# every later item bare, and a comma directly before a hash number in a
# comment is such a list far more often than anything else.
regexp = 'github\.com/[^[:space:])]+/(?:issues|pull)/[0-9]+|\b(?:[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+|[A-Za-z0-9_.-]*[a-z][A-Za-z0-9_.-]*)#[0-9]+\b|(?:^|\b[a-z]+[\t ]+|\(|\[|[,;/][\t ]*)#[0-9]+\b'
# comment is such a list far more often than anything else. The words `issue`
# and `pr` are admitted in any case, because a sentence that opens with one of
# them capitalised is the commonest way a document cites a tracker, and the
# lowercase prose-word arm reads past it.
#
# THE GLOB IS THE SCOPE THIS RULE HAD BEFORE IT WAS WIDENED, AND IT IS BACK ON
# PURPOSE. For one release the rule read every tracked file, and what that cost
# the consumers who measured it is written at the head of `code-residue`, the
# set that now carries the every-file scope under its own id. That rule and
# this one exclude each other's files, so a line is under exactly one of them,
# and a repository takes the wider one by name rather than by version bump.
regexp = 'github\.com/[^[:space:])]+/(?:issues|pull)/[0-9]+|\b(?:[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+|[A-Za-z0-9_.-]*[a-z][A-Za-z0-9_.-]*)#[0-9]+\b|(?:^|\b[a-z]+[\t ]+|(?i:\b(?:issue|pr)[\t ]+)|\(|\[|[,;/][\t ]*)#[0-9]+\b'
files.include = ["."]
files.glob = ["*.md", "*.rst", "*.txt"]
files.exclude = ["**/tests/**", "**/test/**", "**/*_test.go", "**/testdata/**"]

[rule.no-process-history-references]
message = """
Do not reference process-history records (issue/PR/thread numbers) in final
docs. Extract the durable rule or final state instead.
"""
regexp = '(?i)\b(?:issue|pr)\s+#[0-9]+\b|\bas discussed in (?:an? )?(?:issue|thread|pr)\b|(?:https?://)?github\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+/(?:issues|pull)/[0-9]+\b'
# One arm, the narrative form: a sentence that points at a conversation rather
# than at a record. The forms that name a record -- a tracker URL, and the word
# `issue` or `pr` before a hash number -- are `no-task-tracker-references`
# above, over the same files, and used to be here as well, so a line in either
# form was reported twice under two ids. A consumer reading a doubled report
# disables one of the rules, and the disabled one was this set's whole coverage
# of the number-free form. What each rule refuses is now disjoint.
regexp = '(?i)\bas discussed in (?:an? )?(?:issue|thread|pr)\b'
files.include = ["."]
files.glob = ["*.md", "*.rst", "*.txt"]
files.exclude = ["**/tests/**", "**/test/**", "**/*_test.go"]
Expand Down
41 changes: 39 additions & 2 deletions policy/base/sets.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
},
"id": "no-process-history-references",
"message": "Do not reference process-history records (issue/PR/thread numbers) in final\ndocs. Extract the durable rule or final state instead.\n",
"regexp": "(?i)\\b(?:issue|pr)\\s+#[0-9]+\\b|\\bas discussed in (?:an? )?(?:issue|thread|pr)\\b|(?:https?://)?github\\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+/(?:issues|pull)/[0-9]+\\b"
"regexp": "(?i)\\bas discussed in (?:an? )?(?:issue|thread|pr)\\b"
},
{
"files": {
Expand Down Expand Up @@ -156,6 +156,11 @@
"**/testdata/**"
],
"exclude_cfg_test": false,
"glob": [
"*.md",
"*.rst",
"*.txt"
],
"include": [
"."
],
Expand All @@ -164,7 +169,7 @@
},
"id": "no-task-tracker-references",
"message": "Source, configuration, and documentation must not depend on volatile\nissue or PR records. State the durable contract and rationale here, or link to\nrepository documentation. Issues may link to code; code must not depend on\nissues. Keep logs, bug reports, and benchmark results in issues, and edit\nchronology in Git history.\n",
"regexp": "github\\.com/[^[:space:])]+/(?:issues|pull)/[0-9]+|\\b(?:[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+|[A-Za-z0-9_.-]*[a-z][A-Za-z0-9_.-]*)#[0-9]+\\b|(?:^|\\b[a-z]+[\\t ]+|\\(|\\[|[,;/][\\t ]*)#[0-9]+\\b"
"regexp": "github\\.com/[^[:space:])]+/(?:issues|pull)/[0-9]+|\\b(?:[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+|[A-Za-z0-9_.-]*[a-z][A-Za-z0-9_.-]*)#[0-9]+\\b|(?:^|\\b[a-z]+[\\t ]+|(?i:\\b(?:issue|pr)[\\t ]+)|\\(|\\[|[,;/][\\t ]*)#[0-9]+\\b"
},
{
"files": {
Expand All @@ -191,6 +196,38 @@
"manual"
]
},
{
"commands": [],
"rules": [
{
"files": {
"exclude": [
"*.md",
"*.rst",
"*.txt",
"**/tests/**",
"**/test/**",
"**/*_test.go",
"**/testdata/**"
],
"exclude_cfg_test": false,
"include": [
"."
],
"multiline": false,
"word": false
},
"id": "no-task-tracker-references-in-code",
"message": "Source, configuration, and build files must not depend on volatile issue or\nPR records. State the durable contract and rationale in a comment, or link to\nrepository documentation. Issues may link to code; code must not depend on\nissues. Keep logs, bug reports, and benchmark results in issues, and edit\nchronology in Git history.\n",
"regexp": "github\\.com/[^[:space:])]+/(?:issues|pull)/[0-9]+|\\b(?:[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+|[A-Za-z0-9_.-]*[a-z][A-Za-z0-9_.-]*)#[0-9]+\\b|(?:^|\\b[a-z]+[\\t ]+|(?i:\\b(?:issue|pr)[\\t ]+)|\\(|\\[|[,;/][\\t ]*)#[0-9]+\\b"
}
],
"set": "code-residue",
"stages": [
"pre-commit",
"manual"
]
},
{
"commands": [],
"rules": [
Expand Down
6 changes: 6 additions & 0 deletions policy/principles.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ private_owners_optional = true
# rules.
sets = [
"process-residue",
# The tracker rule over source, scripts and policy files, which
# `process-residue` keeps to documentation. Taken here because this tree's
# comments already cite the durable thing -- an ADR, a test, a named
# constant -- and the tree was clean under the every-file scope on the day
# the split shipped it as a set of its own.
"code-residue",
"host-identity",
"broken-links",
"doc-claims",
Expand Down
9 changes: 9 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ pub(crate) const BUNDLED: &[(&str, &str)] = &[
"process-residue",
include_str!("../policy/base/process-residue.toml"),
),
// The tracker rule of `process-residue` over every file that is not
// documentation. Its own name because a scope is not something `[inherit]`
// lets a repository choose -- it takes sets whole and disables rules by id
// -- and the release that widened the rule in place was undone by hand in
// every tree that measured what it reported.
(
"code-residue",
include_str!("../policy/base/code-residue.toml"),
),
(
"credentials",
include_str!("../policy/base/credentials.toml"),
Expand Down
Loading
Loading