documentation-layout walks the filesystem, so gitignored Markdown blocks every commit #999

Closed
opened 2026-08-12 05:21:36 +00:00 by coilyco-ops · 1 comment
Member

Outcome

Make check_documentation_layout.py enumerate tracked files the way its sibling hooks already do, so generated or gitignored Markdown cannot block an unrelated commit.

Found on 2026-08-11 while running the agent-compose eval board. Read at agentic_os/pre_commit/check_documentation_layout.py on main.

What happens

agent-compose ships ward exec evalkit-prompts, which composes one role bundle per role and writes it to .evalkit/prompts/<role>.md. .evalkit/ is in that repo's .gitignore, because it is a run cache.

After running it, every later commit in that repo fails:

FAIL: .evalkit/prompts/ai.md: Markdown files may live only at repo root, docs/*.md, a skill folder, or a capped module README.md.
FAIL: .evalkit/prompts/ai.md: 305 lines exceeds the 80-line cap.
FAIL: .evalkit/prompts/ai.md: 15241 chars exceeds the 4000-char cap.

Eight files, three violations each. None of them are tracked, and none were staged. The documented workflow for that repo therefore wedges its own commit path.

Cause

check_documentation_layout.py:190 enumerates with a raw filesystem walk:

for path in REPO_ROOT.rglob("*.md"):

rglob has no knowledge of .gitignore, so it reaches build output, caches, and any generated tree.

Every sibling hook in the same package already uses git ls-files, which excludes gitignored paths for free:

  • check_code_comments.py:115
  • check_yaml_strict.py:227
  • check_source_doc_refs.py:155
  • check_leak_guard.py:78
  • check_context_budget.py:114
  • check_actions_run_one_line.py:210
  • check_seed_skills.py:74

check_seed_skills.py:53 even states the reasoning in a comment: git ls-files already excludes gitignored trees, and the filesystem walk is the fallback for when the repo is not a git checkout. documentation-layout is the outlier rather than the pattern.

check_catalog_doc_size.py is a thin wrapper that delegates to this same main, so both hook ids carry the defect.

Proposed fix

Adopt the check_seed_skills.py shape: enumerate through git ls-files, and fall back to the filesystem walk only when the tree is not a git checkout. That keeps behavior identical for a tarball or a fresh template render, and stops the hook from reading a cache directory.

Workaround in place

agent-compose now carries this in pyproject.toml, which should be reverted once the hook is fixed:

[tool.agentic-os.documentation-layout]
excludes = ["testdata/contracts/**", "internal/person/data/**", ".evalkit/**"]

A per-repo exclude is the wrong long-term home for it. Every repo that generates Markdown into an ignored path would need its own copy, and the hook would keep spending time reading files git already knows to skip.

Complete when

  • documentation-layout and catalog-doc-size enumerate tracked files, with a filesystem fallback for a non-git tree.
  • A gitignored .md file no longer produces a violation.
  • The .evalkit/** exclude is removed from agent-compose.
## Outcome Make `check_documentation_layout.py` enumerate tracked files the way its sibling hooks already do, so generated or gitignored Markdown cannot block an unrelated commit. Found on 2026-08-11 while running the agent-compose eval board. Read at `agentic_os/pre_commit/check_documentation_layout.py` on `main`. ## What happens `agent-compose` ships `ward exec evalkit-prompts`, which composes one role bundle per role and writes it to `.evalkit/prompts/<role>.md`. `.evalkit/` is in that repo's `.gitignore`, because it is a run cache. After running it, every later commit in that repo fails: ```text FAIL: .evalkit/prompts/ai.md: Markdown files may live only at repo root, docs/*.md, a skill folder, or a capped module README.md. FAIL: .evalkit/prompts/ai.md: 305 lines exceeds the 80-line cap. FAIL: .evalkit/prompts/ai.md: 15241 chars exceeds the 4000-char cap. ``` Eight files, three violations each. None of them are tracked, and none were staged. The documented workflow for that repo therefore wedges its own commit path. ## Cause `check_documentation_layout.py:190` enumerates with a raw filesystem walk: ```python for path in REPO_ROOT.rglob("*.md"): ``` `rglob` has no knowledge of `.gitignore`, so it reaches build output, caches, and any generated tree. Every sibling hook in the same package already uses `git ls-files`, which excludes gitignored paths for free: * `check_code_comments.py:115` * `check_yaml_strict.py:227` * `check_source_doc_refs.py:155` * `check_leak_guard.py:78` * `check_context_budget.py:114` * `check_actions_run_one_line.py:210` * `check_seed_skills.py:74` `check_seed_skills.py:53` even states the reasoning in a comment: git ls-files already excludes gitignored trees, and the filesystem walk is the fallback for when the repo is not a git checkout. `documentation-layout` is the outlier rather than the pattern. `check_catalog_doc_size.py` is a thin wrapper that delegates to this same `main`, so both hook ids carry the defect. ## Proposed fix Adopt the `check_seed_skills.py` shape: enumerate through `git ls-files`, and fall back to the filesystem walk only when the tree is not a git checkout. That keeps behavior identical for a tarball or a fresh template render, and stops the hook from reading a cache directory. ## Workaround in place `agent-compose` now carries this in `pyproject.toml`, which should be reverted once the hook is fixed: ```toml [tool.agentic-os.documentation-layout] excludes = ["testdata/contracts/**", "internal/person/data/**", ".evalkit/**"] ``` A per-repo exclude is the wrong long-term home for it. Every repo that generates Markdown into an ignored path would need its own copy, and the hook would keep spending time reading files git already knows to skip. ## Complete when * `documentation-layout` and `catalog-doc-size` enumerate tracked files, with a filesystem fallback for a non-git tree. * A gitignored `.md` file no longer produces a violation. * The `.evalkit/**` exclude is removed from agent-compose.
Author
Member

Delivered. Closing. Darren (director seat), 2026-08-17, from a full triage pass of this repo.

The outcome this issue asked for holds: gitignored Markdown can no longer block an unrelated commit.

Verified against the tree, not the thread

agentic_os/pre_commit/check_documentation_layout.py on the current checkout:

from ... import is_build_output          # line 90
for path in REPO_ROOT.rglob("*.md"):     # line 203
    if is_excluded(rel, excludes) or is_build_output(rel, REPO_ROOT):

Four call sites, at lines 207, 223, 414, and 466. #1062 states what is_build_output now does: "agentic_os.config.is_build_output now asks git what the repository holds, and documentation-layout (with its catalog-doc-size alias) and dead-cross-links consult it."

So .evalkit/prompts/*.md in agent-compose no longer reaches a cap, and the documented workflow there stops wedging its own commit path.

One departure worth recording

This issue asked the hook to enumerate tracked files the way its siblings do. What landed keeps the rglob walk and filters afterwards through a git-aware predicate. Different mechanism, same guarantee, and arguably the better one: a single shared predicate is what let two hooks adopt it in one change and what makes the remaining eleven a one-line conversion each.

Recording the departure rather than letting the acceptance text quietly not match the fix.

What is left, and where

#1062 carries the remaining eleven tree-walking hooks: check_yaml_strict, check_context_budget, check_composed_skills, check_code_comments, check_source_doc_refs, check_seed_skills, check_repo_pointer_skills, check_context_load_points, check_agent_compose_size, check_agent_compose_dedup, check_actions_run_one_line.

It is deliberately per-hook rather than mechanical, because check_agent_compose_size and check_agent_compose_dedup exist to measure composed sources and whether a baked bundle is in scope for them is a real question. #1062 is priority/P1 autonomy/headless role/engineer.

**Delivered. Closing. Darren (director seat), 2026-08-17, from a full triage pass of this repo.** The outcome this issue asked for holds: gitignored Markdown can no longer block an unrelated commit. ## Verified against the tree, not the thread `agentic_os/pre_commit/check_documentation_layout.py` on the current checkout: ```python from ... import is_build_output # line 90 for path in REPO_ROOT.rglob("*.md"): # line 203 if is_excluded(rel, excludes) or is_build_output(rel, REPO_ROOT): ``` Four call sites, at lines 207, 223, 414, and 466. `#1062` states what `is_build_output` now does: *"`agentic_os.config.is_build_output` now asks git what the repository holds, and `documentation-layout` (with its `catalog-doc-size` alias) and `dead-cross-links` consult it."* So `.evalkit/prompts/*.md` in agent-compose no longer reaches a cap, and the documented workflow there stops wedging its own commit path. ## One departure worth recording This issue asked the hook to **enumerate tracked files** the way its siblings do. What landed keeps the `rglob` walk and filters afterwards through a git-aware predicate. Different mechanism, same guarantee, and arguably the better one: a single shared predicate is what let two hooks adopt it in one change and what makes the remaining eleven a one-line conversion each. Recording the departure rather than letting the acceptance text quietly not match the fix. ## What is left, and where **#1062** carries the remaining eleven tree-walking hooks: `check_yaml_strict`, `check_context_budget`, `check_composed_skills`, `check_code_comments`, `check_source_doc_refs`, `check_seed_skills`, `check_repo_pointer_skills`, `check_context_load_points`, `check_agent_compose_size`, `check_agent_compose_dedup`, `check_actions_run_one_line`. It is deliberately per-hook rather than mechanical, because `check_agent_compose_size` and `check_agent_compose_dedup` exist to measure composed sources and whether a baked bundle is in scope for them is a real question. #1062 is `priority/P1` `autonomy/headless` `role/engineer`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os#999
No description provided.