|
34 | 34 | # A surface is live by where it is, never by what else its text happens to |
35 | 35 | # contain: it hands an address to a user, a host or another tool at run time, |
36 | 36 | # or it is the command someone copies. |
37 | | -LIVE_SURFACE_PREFIXES = ("loopx/", "scripts/", ".github/workflows/", "packages/") |
| 37 | +# `.github/` is whole, not just its workflows: GitHub renders |
| 38 | +# `ISSUE_TEMPLATE/config.yml` as the contact links on the new-issue page, and |
| 39 | +# `SECURITY.md`, `SUPPORT.md`, `GOVERNANCE.md` and `PULL_REQUEST_TEMPLATE.md` |
| 40 | +# each hand an address to the person reading them. |
| 41 | +LIVE_SURFACE_PREFIXES = ("loopx/", "scripts/", ".github/", "packages/") |
38 | 42 | # A built bundle is regenerated, not edited, so its baked-in address is fixed by |
39 | 43 | # the release that rebuilds it. This is the tracked-build-output cost #4677 names. |
40 | 44 | GENERATED_ASSET_PREFIXES = ("loopx/web/chat/assets/",) |
41 | 45 | # Where the pre-transfer address is the reviewed-correct content, by path and by |
42 | 46 | # use: this project's own disambiguation terms must keep matching the archived |
43 | | -# address, and prose may cite the pull request an event happened under. |
| 47 | +# address, and prose may cite the pull request an event happened under. An entry |
| 48 | +# is either a whole use shape or one exact record as "<use>:<number>", and a |
| 49 | +# record stays a record: reviewing one dated citation cannot vouch for the old |
| 50 | +# addresses later written next to it. |
44 | 51 | REVIEWED_ADDRESS_EXCEPTIONS: dict[str, frozenset[str]] = { |
45 | 52 | "packages/loopx-community-discussion/src/loopx_community_discussion/normalize.py": |
46 | 53 | frozenset({"repository", "issue"}), |
|
49 | 56 | "loopx/capabilities/issue_fix/README.md": frozenset({"pull"}), |
50 | 57 | "loopx/capabilities/issue_fix/README.zh-CN.md": frozenset({"pull"}), |
51 | 58 | "packages/loopx-codex-provider-routing/RUNBOOK.md": frozenset({"pull"}), |
| 59 | + # Governance records where the project started and which issue settled a |
| 60 | + # roster change, each by its exact reference: a category here would also |
| 61 | + # tolerate every old-owner commit or issue link later added to the file. The |
| 62 | + # ruleset link in the same file is called live and stays under review. |
| 63 | + ".github/GOVERNANCE.md": frozenset( |
| 64 | + { |
| 65 | + "commit:7dcdc9dc79226d157ba57d3e8ff4bae664f020c1", |
| 66 | + "issue:4069", |
| 67 | + } |
| 68 | + ), |
52 | 69 | } |
53 | 70 | DISAMBIGUATION_TERMS_SOURCE = ( |
54 | 71 | "packages/loopx-community-discussion/src/loopx_community_discussion/normalize.py" |
@@ -87,24 +104,41 @@ def _address_use(raw_path: str) -> str: |
87 | 104 | ) |
88 | 105 |
|
89 | 106 |
|
90 | | -# A use is either a live pointer this project must own or a dated citation that |
91 | | -# may keep the address the event happened under. |
92 | | -LIVE_ADDRESS_USES = frozenset( |
93 | | - {"repository", "issue_form", "discussion", "release_asset", "main_pointer", "branch"}) |
| 107 | +#: Uses that name one reviewable record, so an exception can quote its number. |
| 108 | +_IDENTITY_BEARING_USES = ("issue", "pull", "commit") |
| 109 | + |
| 110 | + |
| 111 | +def _address_reference(raw_path: str) -> tuple[str, str | None]: |
| 112 | + """Return one occurrence's use plus the single record it names, if any. |
| 113 | +
|
| 114 | + ``issue:4069`` is one dated citation; the bare ``issue`` shape is every |
| 115 | + old-owner issue link in a file. Only the occurrence decides either part. |
| 116 | + """ |
| 117 | + |
| 118 | + use = _address_use(raw_path) |
| 119 | + if use not in _IDENTITY_BEARING_USES: |
| 120 | + return use, None |
| 121 | + segments = [part for part in raw_path.strip("/").split("/") if part] |
| 122 | + return (use, f"{use}:{segments[1]}") if len(segments) > 1 else (use, None) |
94 | 123 |
|
95 | 124 |
|
96 | 125 | def stale_address_uses(name: str, text: str) -> list[str]: |
97 | | - """Return the old-address uses in a live surface that were never reviewed.""" |
| 126 | + """Return the old-address uses in ``name`` that were never reviewed. |
| 127 | +
|
| 128 | + Every classified use is an offender until a path-and-use exception reviews it, |
| 129 | + so widening which files are live cannot quietly reclassify a dated citation as |
| 130 | + safe: it has to be judged and named here, and an exception that names a record |
| 131 | + covers only that record. |
| 132 | + """ |
98 | 133 |
|
99 | 134 | tolerated = REVIEWED_ADDRESS_EXCEPTIONS.get(name, frozenset()) |
100 | | - return [ |
101 | | - use |
102 | | - for use in ( |
103 | | - _address_use(match.group(1) or "") |
104 | | - for match in OLD_ADDRESS_RE.finditer(text) |
105 | | - ) |
106 | | - if use not in tolerated |
107 | | - ] |
| 135 | + offenders: list[str] = [] |
| 136 | + for match in OLD_ADDRESS_RE.finditer(text): |
| 137 | + use, record = _address_reference(match.group(1) or "") |
| 138 | + if use in tolerated or (record is not None and record in tolerated): |
| 139 | + continue |
| 140 | + offenders.append(use) |
| 141 | + return offenders |
108 | 142 |
|
109 | 143 |
|
110 | 144 | def tracked_files() -> set[str]: |
@@ -248,6 +282,50 @@ def _validate_stale_address_classifier() -> None: |
248 | 282 | "main_pointer" |
249 | 283 | ]: |
250 | 284 | raise AssertionError("a documentation pointer must be named as a live address") |
| 285 | + advisory = "https://github.com/huangruiteng/loopx/security/advisories/new\n" |
| 286 | + if stale_address_uses(".github/SECURITY.md", advisory) != ["security"]: |
| 287 | + raise AssertionError( |
| 288 | + "the private-vulnerability-reporting entry is how a reporter reaches this " |
| 289 | + "project, so it must be named as a live address rather than a citation" |
| 290 | + ) |
| 291 | + if not _is_live_surface(".github/ISSUE_TEMPLATE/config.yml"): |
| 292 | + raise AssertionError( |
| 293 | + "GitHub renders ISSUE_TEMPLATE/config.yml as the contact links on its own " |
| 294 | + "new-issue page, so it is a live surface" |
| 295 | + ) |
| 296 | + if stale_address_uses( |
| 297 | + ".github/GOVERNANCE.md", |
| 298 | + "https://github.com/huangruiteng/loopx/commit/7dcdc9dc79226d157ba57d3e8ff4bae664f020c1\n", |
| 299 | + ): |
| 300 | + raise AssertionError( |
| 301 | + "widening .github/ must not turn a dated history citation into an " |
| 302 | + "offender: the commit a project started under keeps that address" |
| 303 | + ) |
| 304 | + if stale_address_uses( |
| 305 | + ".github/GOVERNANCE.md", |
| 306 | + "https://github.com/huangruiteng/loopx/issues/4069\n", |
| 307 | + ): |
| 308 | + raise AssertionError( |
| 309 | + "the issue that settled a roster change is cited under the address it " |
| 310 | + "happened at, so it must stay tolerated" |
| 311 | + ) |
| 312 | + if stale_address_uses( |
| 313 | + ".github/GOVERNANCE.md", |
| 314 | + "https://github.com/huangruiteng/loopx/issues/4070\n", |
| 315 | + ) != ["issue"]: |
| 316 | + raise AssertionError( |
| 317 | + "an exception for one reviewed issue must not tolerate another " |
| 318 | + "old-owner issue link in the same file" |
| 319 | + ) |
| 320 | + if stale_address_uses( |
| 321 | + ".github/GOVERNANCE.md", |
| 322 | + "https://github.com/huangruiteng/loopx/commit/" |
| 323 | + "1111111111111111111111111111111111111111\n", |
| 324 | + ) != ["commit"]: |
| 325 | + raise AssertionError( |
| 326 | + "an exception for one reviewed commit must not tolerate another " |
| 327 | + "old-owner commit link in the same file" |
| 328 | + ) |
251 | 329 | if _is_live_surface("loopx/web/chat/assets/index-abc123.js"): |
252 | 330 | raise AssertionError( |
253 | 331 | "a generated bundle is outside the guard: its address is fixed by the " |
|
0 commit comments