|
29 | 29 | "docs/contract-matrix.md", |
30 | 30 | "system/AGENTS.md", |
31 | 31 | ) |
| 32 | +RESEARCH_DIR = ".serena/research" |
32 | 33 | FORBIDDEN_ACTIVE_CLAIMS = { |
33 | 34 | "[features].plugin_hooks = true": "Codex 0.134 treats plugin_hooks as a removed feature flag", |
34 | 35 | "features.plugin_hooks = true": "Codex 0.134 treats plugin_hooks as a removed feature flag", |
| 36 | + "requires `[features].plugin_hooks = true`": "plugin_hooks is removed in current Codex", |
| 37 | + "forces `plugin_hooks = true`": "plugin_hooks is removed in current Codex", |
35 | 38 | "active `hooks`, `plugin_hooks`, and `multi_agent`": "plugin hooks are verified through hooks/list, not an active feature flag", |
36 | 39 | "active hooks, plugin_hooks, and multi_agent": "plugin hooks are verified through hooks/list, not an active feature flag", |
37 | 40 | ":danger-no-sandbox": "current Codex built-ins use :danger-full-access for the danger profile", |
@@ -90,6 +93,26 @@ def validate_file_content(root: Path, relative: str, errors: list[str], warnings |
90 | 93 | errors.append(f"{relative}: must be first-class Claude Code memory, not only @AGENTS.md") |
91 | 94 |
|
92 | 95 |
|
| 96 | +def validate_research_content(root: Path, errors: list[str], warnings: list[str]) -> None: |
| 97 | + research_root = root / RESEARCH_DIR |
| 98 | + if not research_root.is_dir(): |
| 99 | + return |
| 100 | + for path in sorted(research_root.glob("*.md")): |
| 101 | + relative = path.relative_to(root).as_posix() |
| 102 | + text = read_text(path) |
| 103 | + header = "\n".join(text.splitlines()[:16]) |
| 104 | + superseded = "SUPERSEDED ON" in header |
| 105 | + for needle, reason in FORBIDDEN_ACTIVE_CLAIMS.items(): |
| 106 | + if needle not in text: |
| 107 | + continue |
| 108 | + if superseded: |
| 109 | + warnings.append(f"{relative}: contains superseded historical claim {needle!r}: {reason}") |
| 110 | + else: |
| 111 | + errors.append( |
| 112 | + f"{relative}: stale research claim {needle!r} requires a SUPERSEDED banner or rewrite: {reason}" |
| 113 | + ) |
| 114 | + |
| 115 | + |
93 | 116 | def parse_args() -> argparse.Namespace: |
94 | 117 | parser = argparse.ArgumentParser(description="Validate rldyour AGENTS.md and .claude/CLAUDE.md policy.") |
95 | 118 | parser.add_argument("--root", default=".", help="Repository root to inspect.") |
@@ -123,6 +146,7 @@ def main() -> int: |
123 | 146 |
|
124 | 147 | for relative in ACTIVE_DOCS: |
125 | 148 | validate_file_content(root, relative, errors, warnings) |
| 149 | + validate_research_content(root, errors, warnings) |
126 | 150 |
|
127 | 151 | payload = { |
128 | 152 | "state": state, |
|
0 commit comments