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
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ Organization documentation rules are checked by `python scripts/check_style.py`,
consumer.
- Do not commit private or licensed source data.

<!-- content-boundary:begin -->
- Public material carries no real resource names or accounts: write
`gs://<bucket>/...` and `<account-id>`, and use synthetic values in
examples.
- Read bucket names, accounts and hostnames from `vars.*`, the
environment, or protected configuration; do not hard-code them in
source, workflows or documents.
- A hash is not anonymisation: a commit id, digest or truncated
identifier that leads back to the real object does not belong in
public material.
- Judge a change by `git diff` and `git ls-files`; `.gitignore` only
affects untracked files and is not evidence that nothing leaked.
<!-- content-boundary:end -->

## Commits and pull requests

Conventional Commits with an English imperative subject of 72 characters or
Expand Down
14 changes: 14 additions & 0 deletions scripts/check_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@

DATE_IN_NAME = re.compile(r"(?<!\d)20\d{2}[-_]?\d{2}[-_]?\d{2}(?!\d)")

# The block `tools/sync_agents.py` writes into AGENTS.md. A rule stated only in
# the guide is read by whoever opens the guide; this one is read by every agent
# that opens the repository, which is the only point early enough to matter.
CONTENT_BOUNDARY = "<!-- content-boundary:begin -->"

# A date in the name is a defect when the file describes something that keeps
# changing, because the date goes stale while the name stays. It is the
# identity of the record when the file is a snapshot of one date: two analyses
Expand Down Expand Up @@ -355,6 +360,15 @@ def check_agent_files(root: Path, report: Report) -> None:
if content != "@AGENTS.md":
report.add("agents", "CLAUDE.md must contain the single line @AGENTS.md")

agents = root / "AGENTS.md"
if agents.exists():
text = agents.read_text(encoding="utf-8", errors="ignore")
if CONTENT_BOUNDARY not in text:
report.add(
"agents",
"AGENTS.md is missing the content-boundary block, see section 5",
)


def check_language(root: Path, report: Report) -> None:
marketing = re.compile(
Expand Down
18 changes: 16 additions & 2 deletions scripts/test_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def test_placeholder_bucket_is_not_reported(self):
self.assertEqual(self.findings(text), [])

def test_real_bucket_is_reported(self):
found = self.findings("Default root `gs://research-inputs/research/v2/input/`.\n")
found = self.findings("Default root `gs://acme-prod-inputs/research/v2/input/`.\n")
self.assertTrue(any("non-placeholder bucket" in m for m in found))

def test_sha_in_url_is_not_reported(self):
Expand Down Expand Up @@ -184,14 +184,28 @@ def test_untracked_agents_md_is_reported(self):
self.assertTrue(any("not tracked by Git" in m for m in found))

def test_tracked_agents_md_passes(self):
self.write("AGENTS.md", "# rules\n")
self.write("AGENTS.md", "# rules\n\n" + check_style.CONTENT_BOUNDARY + "\n")
self.write("CLAUDE.md", "@AGENTS.md\n")
self.git("init", "--quiet")
self.git("add", "AGENTS.md", "CLAUDE.md")
report = self.report()
check_style.check_agent_files(self.root, report)
self.assertEqual(self.messages(report, "agents"), [])

def test_content_boundary_block_is_required(self):
# A rule that lives only in the guide is read by whoever opens the
# guide. The block is the copy that reaches an agent before it writes,
# so its absence is a finding rather than a preference.
self.write("AGENTS.md", "# rules\n")
self.write("CLAUDE.md", "@AGENTS.md\n")
self.git("init", "--quiet")
self.git("add", "AGENTS.md", "CLAUDE.md")
report = self.report()
check_style.check_agent_files(self.root, report)
self.assertTrue(
any("content-boundary" in m for m in self.messages(report, "agents"))
)

def test_claude_must_be_the_single_line(self):
self.write("AGENTS.md", "# rules\n")
self.write("CLAUDE.md", "# rules\n\ncopied body\n")
Expand Down
Loading