Skip to content

ci: add agent docs structure linter - #475

Closed
ralphbean wants to merge 1 commit into
mainfrom
ci/lint-agent-docs
Closed

ci: add agent docs structure linter#475
ralphbean wants to merge 1 commit into
mainfrom
ci/lint-agent-docs

Conversation

@ralphbean

Copy link
Copy Markdown
Member

Summary

  • Ports hack/lint-agent-docs from fullsend-ai/fullsend, adapted for this repo's harness/ and docs/ layout
  • Adds pre-commit hook so the linter runs on CI (via the existing pre-commit run --all-files step in lint.yml)
  • Fixes docs/scribe.md to pass: adds missing ## Commands / ## Control labels stubs, renames ### Environment variables### Variables

What the linter checks

  1. Every harness/*.yaml has a doc: field pointing to an existing file
  2. Agent doc # H1 ends with " Agent"
  3. Required ## sections: How it helps, Triggers, Commands, Control labels, Configuration, How the agent works, Source
  4. ### Variables subsection under ## Configuration
  5. Extra sections are reported as info, not errors

Test plan

  • ./hack/lint-agent-docs passes
  • pre-commit run lint-agent-docs --all-files passes
  • pre-commit run --all-files passes (full suite)
  • CI green

🤖 Generated with Claude Code

Port hack/lint-agent-docs from fullsend-ai/fullsend, adapted for this
repo's layout (harness/ and docs/ paths, "Configuration" instead of
"Configuration and extension").

The linter checks:
- Every harness YAML has a doc: field pointing to an existing file
- Agent doc H1 ends with " Agent"
- Required sections are present (How it helps, Triggers, Commands,
  Control labels, Configuration, How the agent works, Source)
- ### Variables subsection exists under ## Configuration

Also adds missing Commands, Control labels sections and renames
Environment variables → Variables in docs/scribe.md to pass the linter.

Integrated via pre-commit hook, which is already run by CI.

Assisted-by: Claude claude-opus-4-6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@ralphbean
ralphbean requested a review from a team as a code owner July 27, 2026 18:48
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

CI: add agent docs reference/structure linter via pre-commit

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Add a repo-local linter to verify harness YAML doc: references and agent doc structure.
• Wire the linter into CI via the existing pre-commit run --all-files workflow.
• Update docs/scribe.md to satisfy required sections and configuration subsection naming.
Diagram

graph TD
  A["CI lint job"] --> B["pre-commit run --all-files"] --> C["lint-agent-docs hook"] --> D["hack/lint-agent-docs"]
  D --> E[("harness/*.yaml")]
  D --> F[("docs/*.md")]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Implement linter in Python (or existing internal tooling)
  • ➕ More robust YAML/Markdown parsing (avoid grep/awk edge cases)
  • ➕ Easier to unit test and extend rules
  • ➕ Better error reporting with file/line locations
  • ➖ Adds runtime/dependency and maintenance overhead vs a simple bash script
  • ➖ May be overkill for a small, stable ruleset
2. Use off-the-shelf markdown tooling (markdownlint + custom rules)
  • ➕ Leverages existing, well-tested markdown parsing
  • ➕ Can standardize formatting and structure together
  • ➖ Harder to express cross-file checks (harness YAML → doc path)
  • ➖ Custom rule authoring still required; may not match repo’s exact conventions

Recommendation: Current approach (repo-local script + pre-commit integration) is appropriate given the small surface area and the need for cross-file validation (harness YAML → docs). If the ruleset grows or false positives appear due to parsing edge cases (code fences, YAML formatting variants), consider migrating the script to Python with proper parsers and minimal unit coverage.

Files changed (3) +154 / -1

Documentation (1) +9 / -1
scribe.mdAlign Scribe agent doc with required section structure +9/-1

Align Scribe agent doc with required section structure

• Adds missing '## Commands' and '## Control labels' sections with explicit "not used" text. Renames the configuration subsection heading from '### Environment variables' to '### Variables' to match the linter’s required structure.

docs/scribe.md

Other (2) +145 / -0
.pre-commit-config.yamlAdd local pre-commit hook for agent doc linting +6/-0

Add local pre-commit hook for agent doc linting

• Registers a new 'lint-agent-docs' local hook that runs './hack/lint-agent-docs' and scopes it to 'harness/' and 'docs/'. The hook is configured with 'pass_filenames: false' so it relies on its own repo-wide scanning.

.pre-commit-config.yaml

lint-agent-docsIntroduce harness/doc structure linter script +139/-0

Introduce harness/doc structure linter script

• Adds a bash linter that (1) ensures every 'harness/*.yaml' has a 'doc:' field pointing to an existing file, (2) checks agent docs for a standard H1 suffix and required '##' sections (ignoring fenced code blocks), and (3) requires a '### Variables' subsection under '## Configuration'. Emits errors for missing requirements and informational warnings for extra sections.

hack/lint-agent-docs

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 27, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:50 PM UTC · Completed 7:06 PM UTC
Commit: b79b188 · View workflow run →

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (2)

Context used
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

Grey Divider


Action required

1. Protected paths modified in PR 📜 Skill insight § Compliance
Description
This PR modifies protected governance/infrastructure paths (.pre-commit-config.yaml and hack/).
Per the compliance checklist, changes under protected paths must be explicitly flagged for required
human review and must not be auto-approved.
Code

.pre-commit-config.yaml[R49-54]

+      - id: lint-agent-docs
+        name: lint agent doc references and structure
+        entry: ./hack/lint-agent-docs
+        language: script
+        files: ^(harness/|docs/)
+        pass_filenames: false
Relevance

⭐⭐⭐ High

Protected-path review is enforced; PR303 blocks approve when protected-path findings exist; PR184
required protected-path justification.

PR-#303
PR-#184

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The compliance rule defines .pre-commit-config.yaml and hack/ as protected
governance/infrastructure paths requiring an explicit finding. The diff adds a new pre-commit hook
and introduces a new script under hack/, triggering the rule.

.pre-commit-config.yaml[49-54]
hack/lint-agent-docs[1-10]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
This PR changes protected governance/infrastructure files, which require explicit authorization/justification and mandatory human review signals.

## Issue Context
Protected paths include `.pre-commit-config.yaml` and `hack/`. The PR description contains a summary, but it does not include an explicit linked issue/ADR reference that authorizes the governance/tooling change.

## Fix Focus Areas
- .pre-commit-config.yaml[49-54]
- hack/lint-agent-docs[1-6]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. No linked issue authorization 📜 Skill insight § Compliance
Description
This is a non-trivial change (new linter script and pre-commit integration) but the PR description
does not link to an authorizing issue. The compliance checklist requires an explicit linked issue
for non-trivial work.
Code

hack/lint-agent-docs[R1-20]

+#!/bin/bash
+# Lint: every harness YAML must have a doc: field pointing to an existing file.
+# Also checks that agent docs follow a consistent structure.
+set -euo pipefail
+shopt -s nullglob
+
+REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+HARNESS_DIR="$REPO_ROOT/harness"
+
+errors=0
+
+echo "Checking harness files for doc: field..."
+echo "================================================"
+
+for yaml_file in "$HARNESS_DIR"/*.yaml; do
+    name="$(basename "$yaml_file")"
+    doc_value="$(grep -E '^doc:' "$yaml_file" | sed 's/^doc:[[:space:]]*//' || true)"
+
+    if [[ -z "$doc_value" ]]; then
+        echo "  ERROR: $name: missing 'doc:' field"
Relevance

⭐⭐ Medium

Some precedent asks for authorization links on governance/protected changes (PR29/PR184), but
acceptance is unclear/partial.

PR-#29
PR-#184

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The diff shows a substantial new script (139 lines) and a new pre-commit hook entry, meeting the
non-trivial threshold. The compliance checklist requires an explicit linked issue for such work,
which is not present in the provided PR description.

hack/lint-agent-docs[1-139]
.pre-commit-config.yaml[49-54]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces a new CI/pre-commit linter script (non-trivial change) but lacks a linked issue that authorizes the work.

## Issue Context
The compliance rule requires that non-trivial changes (20+ lines / structural changes) include an explicit linked issue in the PR description.

## Fix Focus Areas
- hack/lint-agent-docs[1-139]
- .pre-commit-config.yaml[49-54]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Missing H1 not enforced 🐞 Bug ≡ Correctness
Description
hack/lint-agent-docs only validates the H1 suffix when an H1 was found, so a doc with no top-level
"# ..." heading silently passes this check. This weakens the linter’s ability to enforce consistent
agent doc structure in CI.
Code

hack/lint-agent-docs[R53-58]

+    # Check that the top-level heading ends with " Agent"
+    h1="$(awk 'BEGIN{f=0} /^```/{f=1-f; next} f==0 && /^# [^#]/{sub(/^# /,""); print; exit}' "$doc_path")"
+    if [[ -n "$h1" ]] && [[ "$h1" != *" Agent" ]]; then
+        echo "  $doc_basename: top heading \"# $h1\" must end with \" Agent\" (e.g. \"# ${h1} Agent\")"
+        errors=$((errors + 1))
+    fi
Relevance

⭐⭐ Medium

No historical evidence for enforcing missing H1 in this repo; hack/ path not previously present.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The linter extracts the first H1 into h1, but only errors when h1 is non-empty and does not end
with " Agent"; if h1 is empty (no H1 found), no error is recorded. Existing agent docs in this
repo consistently use an H1 like “# Code Agent”, indicating the linter should also fail when the H1
is missing entirely.

hack/lint-agent-docs[53-58]
docs/code.md[1-6]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The H1 validation currently only runs when `h1` is non-empty (`[[ -n "$h1" ]]`), which means documents that omit a top-level `# ...` heading will not fail lint.

### Issue Context
This script is now run via pre-commit/CI, so missing H1s should be treated as lint errors to match the intended documentation structure requirements.

### Fix
Treat an empty `h1` as an error (increment `errors`) and print a clear message (e.g., `missing top-level "# ... Agent" heading`). Then keep the existing suffix check for non-empty H1s.

### Fix Focus Areas
- hack/lint-agent-docs[53-58]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread .pre-commit-config.yaml
Comment on lines +49 to +54
- id: lint-agent-docs
name: lint agent doc references and structure
entry: ./hack/lint-agent-docs
language: script
files: ^(harness/|docs/)
pass_filenames: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Protected paths modified in pr 📜 Skill insight § Compliance

This PR modifies protected governance/infrastructure paths (.pre-commit-config.yaml and hack/).
Per the compliance checklist, changes under protected paths must be explicitly flagged for required
human review and must not be auto-approved.
Agent Prompt
## Issue description
This PR changes protected governance/infrastructure files, which require explicit authorization/justification and mandatory human review signals.

## Issue Context
Protected paths include `.pre-commit-config.yaml` and `hack/`. The PR description contains a summary, but it does not include an explicit linked issue/ADR reference that authorizes the governance/tooling change.

## Fix Focus Areas
- .pre-commit-config.yaml[49-54]
- hack/lint-agent-docs[1-6]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread hack/lint-agent-docs
Comment on lines +1 to +20
#!/bin/bash
# Lint: every harness YAML must have a doc: field pointing to an existing file.
# Also checks that agent docs follow a consistent structure.
set -euo pipefail
shopt -s nullglob

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HARNESS_DIR="$REPO_ROOT/harness"

errors=0

echo "Checking harness files for doc: field..."
echo "================================================"

for yaml_file in "$HARNESS_DIR"/*.yaml; do
name="$(basename "$yaml_file")"
doc_value="$(grep -E '^doc:' "$yaml_file" | sed 's/^doc:[[:space:]]*//' || true)"

if [[ -z "$doc_value" ]]; then
echo " ERROR: $name: missing 'doc:' field"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. No linked issue authorization 📜 Skill insight § Compliance

This is a non-trivial change (new linter script and pre-commit integration) but the PR description
does not link to an authorizing issue. The compliance checklist requires an explicit linked issue
for non-trivial work.
Agent Prompt
## Issue description
The PR introduces a new CI/pre-commit linter script (non-trivial change) but lacks a linked issue that authorizes the work.

## Issue Context
The compliance rule requires that non-trivial changes (20+ lines / structural changes) include an explicit linked issue in the PR description.

## Fix Focus Areas
- hack/lint-agent-docs[1-139]
- .pre-commit-config.yaml[49-54]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread hack/lint-agent-docs
Comment on lines +53 to +58
# Check that the top-level heading ends with " Agent"
h1="$(awk 'BEGIN{f=0} /^```/{f=1-f; next} f==0 && /^# [^#]/{sub(/^# /,""); print; exit}' "$doc_path")"
if [[ -n "$h1" ]] && [[ "$h1" != *" Agent" ]]; then
echo " $doc_basename: top heading \"# $h1\" must end with \" Agent\" (e.g. \"# ${h1} Agent\")"
errors=$((errors + 1))
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Missing h1 not enforced 🐞 Bug ≡ Correctness

hack/lint-agent-docs only validates the H1 suffix when an H1 was found, so a doc with no top-level
"# ..." heading silently passes this check. This weakens the linter’s ability to enforce consistent
agent doc structure in CI.
Agent Prompt
### Issue description
The H1 validation currently only runs when `h1` is non-empty (`[[ -n "$h1" ]]`), which means documents that omit a top-level `# ...` heading will not fail lint.

### Issue Context
This script is now run via pre-commit/CI, so missing H1s should be treated as lint errors to match the intended documentation structure requirements.

### Fix
Treat an empty `h1` as an error (increment `errors`) and print a clear message (e.g., `missing top-level "# ... Agent" heading`). Then keep the existing suffix check for non-empty H1s.

### Fix Focus Areas
- hack/lint-agent-docs[53-58]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@ralphbean

Copy link
Copy Markdown
Member Author

Superseded by #476, which adds the Setup section and strict extra-section enforcement.

@ralphbean ralphbean closed this Jul 27, 2026
@fullsend-ai-retro

fullsend-ai-retro Bot commented Jul 27, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 7:05 PM UTC · Completed 7:16 PM UTC
Commit: b79b188 · View workflow run →

@fullsend-ai-review

Copy link
Copy Markdown

Review skipped — this PR is already closed.

The /fs-review command only reviews open pull requests.

Posted by fullsend post-review check

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #475 — ci: add agent docs structure linter

PR #475 was a human-authored PR by ralphbean that ported a docs structure linter (hack/lint-agent-docs) to the agents repo. It was open for only 14 minutes (18:48–19:02 UTC) before being closed and superseded by PR #476, which added ## Setup as a required section and strict extra-section enforcement.

Timeline

Time (UTC) Event
18:48 PR opened, review agent dispatched (run 30295479886)
18:50 Review agent posts status comment
18:54 Qodo posts code review: 1 bug (missing H1 enforcement), 2 compliance insights
19:02 Author closes PR — superseded by #476
19:06 Review agent completes analysis (verdict: request-changes, 6 findings) but skips posting because PR is already closed

Key observations

Review agent findings were never posted. The review agent completed a full analysis with a request-changes verdict and 6 findings (1 HIGH for protected paths, 5 LOW for shell script and configuration issues), but the PR was closed ~4 minutes before the post-review script ran. The script correctly detected the closed state and skipped posting. This provides additional evidence for #337 — an early PR-state check before sub-agent dispatch would have saved ~12 minutes of Opus/Sonnet compute. The pattern matches #337's originating case (PR #5412 closed 42 seconds after session start).

Qodo caught a correctness bug the review agent missed. Qodo identified that the linter's H1 validation only runs when an H1 heading is found — documents with no H1 silently pass. The review agent's correctness sub-agent (Claude Opus) found 2 LOW-severity items about the shell script but did not flag this validation logic gap. This same bug persists in successor PR #476. See proposal below.

Security dimension dispatched with minimal surface. The review agent dispatched a Security sub-agent (Claude Opus) for a CI/linting PR and it returned 0 findings. This provides evidence for #373 (skip irrelevant review dimensions to reduce token cost).

Workflow assessment

This was a lightweight workflow: a human-authored PR received automated reviews, was quickly superseded, and the review agent's findings were never posted. The review agent's behavior was operationally correct (it properly identified protected paths, requested changes, and the post-review script correctly handled the closed state). The gap is in the correctness sub-agent's detection coverage — it missed a real bug that Qodo caught.

Proposals filed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant