From 408902537419634391a478168a6a8134dfb65dc8 Mon Sep 17 00:00:00 2001 From: Yue Dai <54579099+yuedai-pbc@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:50:27 +0800 Subject: [PATCH 1/2] fix(hygiene): treat GitHub's own rendered pages as live surfaces `LIVE_SURFACE_PREFIXES` guarded `.github/workflows/` alone, so the files GitHub renders at a visitor or a reporter escaped the canonical-repository check entirely: the private-vulnerability entry in `SECURITY.md`, the four contact links in `ISSUE_TEMPLATE/config.yml` that GitHub prints on its own new-issue page, `SUPPORT.md`'s routing table, the PR template's guidance pointer, and the ruleset link `GOVERNANCE.md` itself calls live. Seventeen addresses named the pre-transfer owner, and they kept resolving only because the transfer left a redirect behind -- which is a courtesy, not an ownership fact. Point each of those at `loopx-project/loopx`. Governance's record of the initial public commit and of the issue that settled a roster entry keeps the address those events happened under, reviewed per path and use like the existing pull citations; the ruleset link does not, because the sentence around it calls it live. Only the widened prefix decides this, not a new rule: every classified use is an offender until a path-and-use exception reviews it, so the two citations had to be judged rather than inherited. The smoke now pins both directions -- the new-issue contact file is a live surface, and a dated commit citation under it stays tolerated. `LIVE_ADDRESS_USES` is removed: nothing referenced it, while its comment described a live-versus-citation split the implementation never applied, which is the easiest thing in this file to "fix" by editing a constant that does nothing. Signed-off-by: Yue Dai <54579099+yuedai-pbc@users.noreply.github.com> --- .github/GOVERNANCE.md | 4 +-- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/config.yml | 8 ++--- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/SECURITY.md | 2 +- .github/SUPPORT.md | 18 ++++++------ examples/repository-hygiene-smoke.py | 42 ++++++++++++++++++++++----- 7 files changed, 52 insertions(+), 26 deletions(-) diff --git a/.github/GOVERNANCE.md b/.github/GOVERNANCE.md index 54145f71f7..c214acba84 100644 --- a/.github/GOVERNANCE.md +++ b/.github/GOVERNANCE.md @@ -146,7 +146,7 @@ but do not replace the approval required from an eligible GitHub reviewer. ## Main Branch Merge Gates -The live [main ruleset](https://github.com/huangruiteng/loopx/rules/18121976) +The live [main ruleset](https://github.com/loopx-project/loopx/rules/18121976) is the operational authority. It requires a pull request, one approving review, code-owner approval where applicable, dismissal of stale approvals, approval of the last push by another reviewer, resolved review threads, and @@ -220,7 +220,7 @@ The versioned [Current Technical Directions](../docs/project/technical-directions.md) page is the canonical map of active strategic programs, maturity, contribution routes, and promotion gates. The pinned -[GitHub Discussion](https://github.com/huangruiteng/loopx/discussions/2851) is +[GitHub Discussion](https://github.com/loopx-project/loopx/discussions/2851) is its community-facing projection; an issue, Discussion, RFC, or integration branch does not override merged runtime and stable reference contracts. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 991975bb67..b2e9338f40 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Thanks for helping improve LoopX. Do not report an unpatched security vulnerability here; use [Private Vulnerability Reporting](https://github.com/huangruiteng/loopx/security/advisories/new). Remove credentials, private data, raw agent sessions, internal links, and local runtime state before submitting. + Thanks for helping improve LoopX. Do not report an unpatched security vulnerability here; use [Private Vulnerability Reporting](https://github.com/loopx-project/loopx/security/advisories/new). Remove credentials, private data, raw agent sessions, internal links, and local runtime state before submitting. - type: checkboxes id: preflight diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c57e8669e5..9203623c5c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,17 +1,17 @@ blank_issues_enabled: false contact_links: - name: Ask a question - url: https://github.com/huangruiteng/loopx/discussions/categories/q-a + url: https://github.com/loopx-project/loopx/discussions/categories/q-a about: Get community help with usage, configuration, or design questions. - name: Discord community url: https://discord.gg/XmGgQyCFZd about: Join informal onboarding, workflow, and show-and-tell conversations. - name: Report a security vulnerability - url: https://github.com/huangruiteng/loopx/security/advisories/new + url: https://github.com/loopx-project/loopx/security/advisories/new about: Privately report suspected unpatched vulnerabilities. Do not open a public issue. - name: Contributor task board - url: https://github.com/huangruiteng/loopx/blob/main/docs/development/contributor-tasks.md + url: https://github.com/loopx-project/loopx/blob/main/docs/development/contributor-tasks.md about: Start here for public, claimable work. - name: Current technical directions - url: https://github.com/huangruiteng/loopx/blob/main/docs/project/technical-directions.md + url: https://github.com/loopx-project/loopx/blob/main/docs/project/technical-directions.md about: Understand active programs, maturity, ownership boundaries, and promotion gates. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6f109ca6da..f95a319092 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -58,7 +58,7 @@ baseline/head comparison and a failing-before or mutation check, not just test c Documentation-only changes may use a static/manual row and explain runtime N/A. A passing row does not waive required real-path/backend gates. --> -See [validation disclosure guidance](https://github.com/huangruiteng/loopx/blob/main/CONTRIBUTING.md#validation-disclosure). +See [validation disclosure guidance](https://github.com/loopx-project/loopx/blob/main/CONTRIBUTING.md#validation-disclosure). ## Frontend / Visual Evidence diff --git a/.github/SECURITY.md b/.github/SECURITY.md index d2d9782cb3..c81e1e168e 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -10,7 +10,7 @@ version-specific regression. ## Reporting A Vulnerability Please report suspected vulnerabilities through -[GitHub Private Vulnerability Reporting](https://github.com/huangruiteng/loopx/security/advisories/new). +[GitHub Private Vulnerability Reporting](https://github.com/loopx-project/loopx/security/advisories/new). Do not open a public issue, discussion, or pull request for an unpatched vulnerability. diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index d64f84b2cc..cd1048aa23 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -9,10 +9,10 @@ channels. | Need | Channel | Use it for | | --- | --- | --- | -| Reproducible bug or installation failure | [GitHub Issues](https://github.com/huangruiteng/loopx/issues/new/choose) | Public, sanitized reproduction steps for behavior that can be investigated or fixed in the repository. | -| Feature request | [GitHub Issues](https://github.com/huangruiteng/loopx/issues/new/choose) | A concrete problem, desired outcome, alternatives, and the smallest useful product change. | -| Usage or design question | [GitHub Discussions: Q&A](https://github.com/huangruiteng/loopx/discussions/categories/q-a) | Questions, configuration help, and design discussion that do not yet identify a repository bug. | -| Security vulnerability | [Private Vulnerability Reporting](https://github.com/huangruiteng/loopx/security/advisories/new) | Suspected unpatched vulnerabilities. Do not post them in an issue, discussion, or chat. See [`SECURITY.md`](SECURITY.md). | +| Reproducible bug or installation failure | [GitHub Issues](https://github.com/loopx-project/loopx/issues/new/choose) | Public, sanitized reproduction steps for behavior that can be investigated or fixed in the repository. | +| Feature request | [GitHub Issues](https://github.com/loopx-project/loopx/issues/new/choose) | A concrete problem, desired outcome, alternatives, and the smallest useful product change. | +| Usage or design question | [GitHub Discussions: Q&A](https://github.com/loopx-project/loopx/discussions/categories/q-a) | Questions, configuration help, and design discussion that do not yet identify a repository bug. | +| Security vulnerability | [Private Vulnerability Reporting](https://github.com/loopx-project/loopx/security/advisories/new) | Suspected unpatched vulnerabilities. Do not post them in an issue, discussion, or chat. See [`SECURITY.md`](SECURITY.md). | | Informal peer help | [Discord](https://discord.gg/XmGgQyCFZd) | Onboarding, workflow comparison, show and tell, and community conversation. Chat is not an authoritative support or release record. | Public contributor work belongs on the @@ -25,14 +25,14 @@ requests should follow ## Official Publication Sources -- [GitHub Releases](https://github.com/huangruiteng/loopx/releases) is the +- [GitHub Releases](https://github.com/loopx-project/loopx/releases) is the authoritative source for published versions and release notes. -- [GitHub Discussions: Announcements](https://github.com/huangruiteng/loopx/discussions/categories/announcements) +- [GitHub Discussions: Announcements](https://github.com/loopx-project/loopx/discussions/categories/announcements) is the authoritative source for project announcements that are not tied to one release. The pinned - [Current technical directions and known limitations](https://github.com/huangruiteng/loopx/discussions/2851) + [Current technical directions and known limitations](https://github.com/loopx-project/loopx/discussions/2851) post is the community-facing projection of the versioned repository map. -- [GitHub Security Advisories](https://github.com/huangruiteng/loopx/security/advisories) +- [GitHub Security Advisories](https://github.com/loopx-project/loopx/security/advisories) is the authoritative source for coordinated vulnerability disclosures. Repository documentation describes the current product and contributor @@ -49,7 +49,7 @@ official publication source. Reposts, screenshots, personal accounts, and third-party communities may be useful, but they are not authoritative project communications. When sources conflict, prefer the official GitHub source for the relevant topic and ask for clarification in a public -[Discussion](https://github.com/huangruiteng/loopx/discussions). +[Discussion](https://github.com/loopx-project/loopx/discussions). ## Make A Useful Request diff --git a/examples/repository-hygiene-smoke.py b/examples/repository-hygiene-smoke.py index 76127ab427..2987df96a2 100644 --- a/examples/repository-hygiene-smoke.py +++ b/examples/repository-hygiene-smoke.py @@ -34,7 +34,11 @@ # A surface is live by where it is, never by what else its text happens to # contain: it hands an address to a user, a host or another tool at run time, # or it is the command someone copies. -LIVE_SURFACE_PREFIXES = ("loopx/", "scripts/", ".github/workflows/", "packages/") +# `.github/` is whole, not just its workflows: GitHub renders +# `ISSUE_TEMPLATE/config.yml` as the contact links on the new-issue page, and +# `SECURITY.md`, `SUPPORT.md`, `GOVERNANCE.md` and `PULL_REQUEST_TEMPLATE.md` +# each hand an address to the person reading them. +LIVE_SURFACE_PREFIXES = ("loopx/", "scripts/", ".github/", "packages/") # A built bundle is regenerated, not edited, so its baked-in address is fixed by # the release that rebuilds it. This is the tracked-build-output cost #4677 names. GENERATED_ASSET_PREFIXES = ("loopx/web/chat/assets/",) @@ -49,6 +53,10 @@ "loopx/capabilities/issue_fix/README.md": frozenset({"pull"}), "loopx/capabilities/issue_fix/README.zh-CN.md": frozenset({"pull"}), "packages/loopx-codex-provider-routing/RUNBOOK.md": frozenset({"pull"}), + # Governance records where the project started and which issue settled a + # roster change: both name the address the event happened under, while the + # ruleset link in the same file is called live and stays under review. + ".github/GOVERNANCE.md": frozenset({"commit", "issue"}), } DISAMBIGUATION_TERMS_SOURCE = ( "packages/loopx-community-discussion/src/loopx_community_discussion/normalize.py" @@ -87,14 +95,13 @@ def _address_use(raw_path: str) -> str: ) -# A use is either a live pointer this project must own or a dated citation that -# may keep the address the event happened under. -LIVE_ADDRESS_USES = frozenset( - {"repository", "issue_form", "discussion", "release_asset", "main_pointer", "branch"}) - - def stale_address_uses(name: str, text: str) -> list[str]: - """Return the old-address uses in a live surface that were never reviewed.""" + """Return the old-address uses in ``name`` that were never reviewed per use. + + Every classified use is an offender until a path-and-use exception reviews it, + so widening which files are live cannot quietly reclassify a dated citation as + safe: it has to be judged and named here. + """ tolerated = REVIEWED_ADDRESS_EXCEPTIONS.get(name, frozenset()) return [ @@ -248,6 +255,25 @@ def _validate_stale_address_classifier() -> None: "main_pointer" ]: raise AssertionError("a documentation pointer must be named as a live address") + advisory = "https://github.com/huangruiteng/loopx/security/advisories/new\n" + if stale_address_uses(".github/SECURITY.md", advisory) != ["security"]: + raise AssertionError( + "the private-vulnerability-reporting entry is how a reporter reaches this " + "project, so it must be named as a live address rather than a citation" + ) + if not _is_live_surface(".github/ISSUE_TEMPLATE/config.yml"): + raise AssertionError( + "GitHub renders ISSUE_TEMPLATE/config.yml as the contact links on its own " + "new-issue page, so it is a live surface" + ) + if stale_address_uses( + ".github/GOVERNANCE.md", + "https://github.com/huangruiteng/loopx/commit/7dcdc9dc79226d157ba57d3e8ff4bae664f020c1\n", + ): + raise AssertionError( + "widening .github/ must not turn a dated history citation into an " + "offender: the commit a project started under keeps that address" + ) if _is_live_surface("loopx/web/chat/assets/index-abc123.js"): raise AssertionError( "a generated bundle is outside the guard: its address is fixed by the " From e15959b4366a601cdd5d904d9b8e358d8848c065 Mon Sep 17 00:00:00 2001 From: yuedai-pbc <54579099+yuedai-pbc@users.noreply.github.com> Date: Thu, 24 Sep 2026 23:23:26 +0800 Subject: [PATCH 2/2] fix(hygiene): bind the governance exceptions to one record each The GOVERNANCE.md entry in REVIEWED_ADDRESS_EXCEPTIONS tolerated the whole commit and issue shapes, so widening the .github/ live prefix let any old-owner numbered issue or commit link added to that file pass unreviewed. Name the two reviewed historical references by their ids instead, and assert that another numbered issue and another commit id in the same file are still reported. Signed-off-by: yuedai-pbc <54579099+yuedai-pbc@users.noreply.github.com> --- examples/repository-hygiene-smoke.py | 78 +++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/examples/repository-hygiene-smoke.py b/examples/repository-hygiene-smoke.py index 2987df96a2..cf6dbd747e 100644 --- a/examples/repository-hygiene-smoke.py +++ b/examples/repository-hygiene-smoke.py @@ -44,7 +44,10 @@ GENERATED_ASSET_PREFIXES = ("loopx/web/chat/assets/",) # Where the pre-transfer address is the reviewed-correct content, by path and by # use: this project's own disambiguation terms must keep matching the archived -# address, and prose may cite the pull request an event happened under. +# address, and prose may cite the pull request an event happened under. An entry +# is either a whole use shape or one exact record as ":", and a +# record stays a record: reviewing one dated citation cannot vouch for the old +# addresses later written next to it. REVIEWED_ADDRESS_EXCEPTIONS: dict[str, frozenset[str]] = { "packages/loopx-community-discussion/src/loopx_community_discussion/normalize.py": frozenset({"repository", "issue"}), @@ -54,9 +57,15 @@ "loopx/capabilities/issue_fix/README.zh-CN.md": frozenset({"pull"}), "packages/loopx-codex-provider-routing/RUNBOOK.md": frozenset({"pull"}), # Governance records where the project started and which issue settled a - # roster change: both name the address the event happened under, while the + # roster change, each by its exact reference: a category here would also + # tolerate every old-owner commit or issue link later added to the file. The # ruleset link in the same file is called live and stays under review. - ".github/GOVERNANCE.md": frozenset({"commit", "issue"}), + ".github/GOVERNANCE.md": frozenset( + { + "commit:7dcdc9dc79226d157ba57d3e8ff4bae664f020c1", + "issue:4069", + } + ), } DISAMBIGUATION_TERMS_SOURCE = ( "packages/loopx-community-discussion/src/loopx_community_discussion/normalize.py" @@ -95,23 +104,41 @@ def _address_use(raw_path: str) -> str: ) +#: Uses that name one reviewable record, so an exception can quote its number. +_IDENTITY_BEARING_USES = ("issue", "pull", "commit") + + +def _address_reference(raw_path: str) -> tuple[str, str | None]: + """Return one occurrence's use plus the single record it names, if any. + + ``issue:4069`` is one dated citation; the bare ``issue`` shape is every + old-owner issue link in a file. Only the occurrence decides either part. + """ + + use = _address_use(raw_path) + if use not in _IDENTITY_BEARING_USES: + return use, None + segments = [part for part in raw_path.strip("/").split("/") if part] + return (use, f"{use}:{segments[1]}") if len(segments) > 1 else (use, None) + + def stale_address_uses(name: str, text: str) -> list[str]: - """Return the old-address uses in ``name`` that were never reviewed per use. + """Return the old-address uses in ``name`` that were never reviewed. Every classified use is an offender until a path-and-use exception reviews it, so widening which files are live cannot quietly reclassify a dated citation as - safe: it has to be judged and named here. + safe: it has to be judged and named here, and an exception that names a record + covers only that record. """ tolerated = REVIEWED_ADDRESS_EXCEPTIONS.get(name, frozenset()) - return [ - use - for use in ( - _address_use(match.group(1) or "") - for match in OLD_ADDRESS_RE.finditer(text) - ) - if use not in tolerated - ] + offenders: list[str] = [] + for match in OLD_ADDRESS_RE.finditer(text): + use, record = _address_reference(match.group(1) or "") + if use in tolerated or (record is not None and record in tolerated): + continue + offenders.append(use) + return offenders def tracked_files() -> set[str]: @@ -274,6 +301,31 @@ def _validate_stale_address_classifier() -> None: "widening .github/ must not turn a dated history citation into an " "offender: the commit a project started under keeps that address" ) + if stale_address_uses( + ".github/GOVERNANCE.md", + "https://github.com/huangruiteng/loopx/issues/4069\n", + ): + raise AssertionError( + "the issue that settled a roster change is cited under the address it " + "happened at, so it must stay tolerated" + ) + if stale_address_uses( + ".github/GOVERNANCE.md", + "https://github.com/huangruiteng/loopx/issues/4070\n", + ) != ["issue"]: + raise AssertionError( + "an exception for one reviewed issue must not tolerate another " + "old-owner issue link in the same file" + ) + if stale_address_uses( + ".github/GOVERNANCE.md", + "https://github.com/huangruiteng/loopx/commit/" + "1111111111111111111111111111111111111111\n", + ) != ["commit"]: + raise AssertionError( + "an exception for one reviewed commit must not tolerate another " + "old-owner commit link in the same file" + ) if _is_live_surface("loopx/web/chat/assets/index-abc123.js"): raise AssertionError( "a generated bundle is outside the guard: its address is fixed by the "