Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4b859e2
[feature] Shape agentic-ai-docs goal
glentner Jul 15, 2026
fbf6fe7
[feature] Plan agentic-ai-docs: design + phased roadmap
glentner Jul 15, 2026
020e4b4
[feature] Refine agentic-ai-docs GOAL/plan per review feedback
glentner Jul 15, 2026
af033cb
[harness] Fail the build-integrity gate on ERRORs, not just new warnings
glentner Jul 15, 2026
8289056
[harness] Standardize on a uv-synced .venv for all factory tooling
glentner Jul 15, 2026
c9b9171
[harness] Harden docs-feature/docs-plan for weaker and non-Claude har…
glentner Jul 15, 2026
3c7289e
[harness] Document the verbatim --8<-- include pattern and check_path…
glentner Jul 15, 2026
c3da5f4
[feature] Use the .venv Python for breadcrumbs/env checks in the agen…
glentner Jul 15, 2026
0546178
[harness] Use .venv/bin/mkdocs consistently in factory templates
glentner Jul 15, 2026
1dc7942
[harness] Add META.md findings log, meta_status.py, harness-log, and …
glentner Jul 15, 2026
ac7e946
[harness] Add silence-by-default meta-note step to lifecycle skills +…
glentner Jul 15, 2026
f45a2fd
[harness] Document the self-improvement loop in methodology and const…
glentner Jul 15, 2026
3778c31
[harness] Use .venv/bin/python for all factory script invocations (co…
glentner Jul 15, 2026
714177b
[feature] Draft agentic-ai-docs P1: scaffold section hub + nav anchor
glentner Jul 15, 2026
7498d2e
[feature] Draft agentic-ai-docs P2: acceptable use & etiquette page
glentner Jul 15, 2026
9f4c26d
[feature] Draft agentic-ai-docs P3: best practices & limitations page
glentner Jul 15, 2026
ae4f792
[feature] Draft agentic-ai-docs P4: MCP servers page
glentner Jul 15, 2026
a2aaac9
[feature] Draft agentic-ai-docs P5: running agents overview + on-cluster
glentner Jul 15, 2026
9ecb329
[feature] Draft agentic-ai-docs P6: running agents local (MCP + SSH)
glentner Jul 15, 2026
3223836
[feature] Draft agentic-ai-docs P7: shared context hub + /etc/agents.…
glentner Jul 15, 2026
462e528
[feature] Draft agentic-ai-docs P8: per-harness settings & permissions
glentner Jul 15, 2026
b1e87b5
[feature] Draft agentic-ai-docs P9: Gautschi Using AI Agents chapter
glentner Jul 15, 2026
c676c50
[feature] Draft agentic-ai-docs P10: integration cards, cross-links, …
glentner Jul 15, 2026
e47e46d
[content] agentic-ai-docs: apply human-review feedback
glentner Jul 15, 2026
481fbf8
[content] agentic-ai-docs: cluster-side settings use docs HTTP MCP, n…
glentner Jul 15, 2026
721a9d7
[feature] Review agentic-ai-docs: cycle 1 — approved
glentner Jul 15, 2026
b8c6a6c
[harness] Trim strict-baseline to empty: all 7 warnings fixed upstrea…
glentner Jul 15, 2026
99ec64f
[feature] agentic-ai-docs: record post-rebase integration + re-verifi…
glentner Jul 15, 2026
bcca8c9
[harness] Document the self-improvement loop in getting-started.html
glentner Jul 16, 2026
7e320c7
[content] Add missing authors key to pages
glentner Jul 27, 2026
4505a2b
[feature] agentic-ai-docs: generate per-cluster context from a data m…
glentner Jul 29, 2026
8880080
[ci] Regenerate agentic-AI context on data-model change
glentner Jul 29, 2026
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
18 changes: 11 additions & 7 deletions .agents/factory/bin/_fsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
``.agents/factory/methodology.md``).

Requires PyYAML, which is a declared project dependency (``requirements.txt``)
and lives in the project virtual environment — NOT the system interpreter. Run
these scripts with the project env active, e.g. from the repo root::
and lives in the uv-synced project virtualenv (``.venv``) — NOT the system
interpreter. Run these scripts via the venv Python from the repo root::

source .venv/bin/activate # or: conda activate rcac-docs
python3 .agents/factory/bin/<script>.py ...
.venv/bin/python .agents/factory/bin/<script>.py ...

Bootstrap the env first if it is missing (see AGENTS.md "Setup")::

uv venv && uv pip install -r requirements.txt --python .venv/bin/python

Adapted from the HyperShell software factory (`.agents/factory/bin`).
"""
Expand All @@ -27,9 +30,10 @@
import yaml
except ModuleNotFoundError as exc: # pragma: no cover - environment guard
raise SystemExit(
"PyYAML is required but was not found. Activate the project environment "
"first (e.g. `source .venv/bin/activate` or `conda activate rcac-docs`), "
"then run `python3 .agents/factory/bin/<script>.py` from the repo root."
"PyYAML is required but was not found. Run this via the uv-synced venv from "
"the repo root: `.venv/bin/python .agents/factory/bin/<script>.py`. Bootstrap "
"it first if missing: `uv venv && uv pip install -r requirements.txt "
"--python .venv/bin/python`."
) from exc


Expand Down
119 changes: 119 additions & 0 deletions .agents/factory/bin/meta_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env python3
"""Enumerate the findings in a spec's META.md as JSON.

META.md is the per-job harness-retrospective log (see
``.agents/factory/templates/META.md``). Each finding is a markdown section::

## F3 — verify gate passes on build ERRORs
`origin=docs-plan severity=high category=tooling status=open target=path/to/file`
- **What happened:** ...
- **Recommended fix:** ...

This reader is stdlib-only (no PyYAML, no mkdocs), so it cannot break on a missing
dependency — but invoke it via ``.venv/bin/python`` like every other factory script
(the project convention; see AGENTS.md "Setup"). `/docs-harness` and `/docs-publish`
use it to enumerate/count findings instead of hand-parsing markdown (the same "scripts
own the fragile parsing" principle as ``next_phase.py``). It never mutates the file.

Usage (from the repo root)::

.venv/bin/python .agents/factory/bin/meta_status.py spec/<slug>/META.md
.venv/bin/python .agents/factory/bin/meta_status.py spec/<slug>/META.md --status open
.venv/bin/python .agents/factory/bin/meta_status.py spec/<slug>/META.md --status open --severity high
.venv/bin/python .agents/factory/bin/meta_status.py spec/<slug>/META.md F1 F3 # only these ids

Exit codes: 0 ok (even with zero findings) · 2 usage / I/O error.
A missing META.md is NOT an error — it means "no findings" (prints an empty set).
"""
from __future__ import annotations

# Standard libs
import argparse
import json
import re
import sys
from pathlib import Path

# Public interface
__all__ = ["main", "parse_findings"]

# A finding starts at a "## F<n> — <title>" heading. Accept "—", "-", or ":" as the separator.
_HEADING_RE = re.compile(r"^##\s+(?P<id>F\d+)\s*[—:-]\s*(?P<title>.*\S)\s*$", re.MULTILINE)
# key=value tokens on the attributes line. Values are non-whitespace but exclude backticks, so a
# code-span-wrapped attributes line (`origin=… target=path`) does not glue the closing ` onto target.
_KV_RE = re.compile(r"(?P<key>\w+)=(?P<val>[^\s`]+)")
_ATTR_KEYS = ("origin", "severity", "category", "status", "target")


def parse_findings(text: str) -> list[dict]:
"""Parse META.md text into a list of finding dicts (in document order).

Example/commented-out findings inside HTML comments (``<!-- ... -->``) are
ignored, so the template's illustrative F1 never counts as real.
"""
# Strip HTML comments first so the template's example block is not parsed.
text = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)

findings: list[dict] = []
matches = list(_HEADING_RE.finditer(text))
for i, m in enumerate(matches):
start = m.end()
end = matches[i + 1].start() if i + 1 < len(matches) else len(text)
body = text[start:end]

attrs: dict[str, str] = {}
for line in body.splitlines():
if "status=" in line or "origin=" in line:
attrs = {kv.group("key"): kv.group("val") for kv in _KV_RE.finditer(line)}
break

finding = {"id": m.group("id"), "title": m.group("title").strip()}
for key in _ATTR_KEYS:
finding[key] = attrs.get(key, "")
findings.append(finding)
return findings


def main(argv: list[str]) -> int:
ap = argparse.ArgumentParser(description="Enumerate findings in a spec's META.md as JSON.")
ap.add_argument("path", help="path to spec/<slug>/META.md")
ap.add_argument("ids", nargs="*", help="only these finding ids (e.g. F1 F3)")
ap.add_argument("--status", help="filter by status (e.g. open)")
ap.add_argument("--severity", help="filter by severity (e.g. high)")
args = ap.parse_args(argv)

path = Path(args.path)
if not path.exists():
# A missing META.md means "no findings recorded" — a normal, clean outcome.
print(json.dumps({"path": str(path), "exists": False, "count": 0, "findings": []}, indent=2))
return 0
try:
text = path.read_text(encoding="utf-8")
except OSError as exc:
print(f"cannot read {path}: {exc}", file=sys.stderr)
return 2

findings = parse_findings(text)
wanted = set(args.ids)
selected = [
f for f in findings
if (not wanted or f["id"] in wanted)
and (not args.status or f["status"] == args.status)
and (not args.severity or f["severity"] == args.severity)
]
open_high = [f for f in findings if f["status"] == "open" and f["severity"] == "high"]
report = {
"path": str(path),
"exists": True,
"count": len(selected),
"total": len(findings),
"open": len([f for f in findings if f["status"] == "open"]),
"open_high": [f["id"] for f in open_high],
"findings": selected,
}
print(json.dumps(report, indent=2, ensure_ascii=False))
return 0


if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
2 changes: 1 addition & 1 deletion .agents/factory/bin/next_phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"what do I do next"; the model executes, the script computes the transition.

Usage (run from the repo root, with the project env active):
python3 .agents/factory/bin/next_phase.py spec/<slug>/TECH.md
.venv/bin/python .agents/factory/bin/next_phase.py spec/<slug>/TECH.md

Exit codes: 0 ok · 2 parse/validation error (message on stderr).
"""
Expand Down
8 changes: 4 additions & 4 deletions .agents/factory/bin/set_phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@

Usage examples (run from the repo root, with the project env active):
# mark a phase done and advance the pointer, stamping today's date
python3 .agents/factory/bin/set_phase.py spec/<slug>/TECH.md \
.venv/bin/python .agents/factory/bin/set_phase.py spec/<slug>/TECH.md \
--phase P2 --phase-status done --current P3 --touch

# set an in_progress phase, update the hill honesty signal
python3 .agents/factory/bin/set_phase.py spec/<slug>/TECH.md \
.venv/bin/python .agents/factory/bin/set_phase.py spec/<slug>/TECH.md \
--phase P3 --phase-status in_progress --hill uphill --touch

# record a blocked state from a failed review
python3 .agents/factory/bin/set_phase.py spec/<slug>/TECH.md \
.venv/bin/python .agents/factory/bin/set_phase.py spec/<slug>/TECH.md \
--top-status blocked --blocked-reason "review: R2 gap" --touch

# record a review verdict
python3 .agents/factory/bin/set_phase.py spec/<slug>/TECH.md \
.venv/bin/python .agents/factory/bin/set_phase.py spec/<slug>/TECH.md \
--verdict approved --reviewed-commit abc1234 --touch

Exit codes: 0 ok · 2 parse/validation error · 3 unknown --phase id.
Expand Down
48 changes: 43 additions & 5 deletions .agents/factory/bin/strict_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@
introduced. Pre-existing warnings are known debt tracked in the baseline file.

Usage (run from the repo root, with the project env active):
.venv/bin/mkdocs build --strict 2>&1 | python3 .agents/factory/bin/strict_check.py
.venv/bin/mkdocs build --strict 2>&1 | .venv/bin/python .agents/factory/bin/strict_check.py
# or from a captured log:
python3 .agents/factory/bin/strict_check.py build.log
.venv/bin/python .agents/factory/bin/strict_check.py build.log

Options:
--baseline PATH baseline file (default: .agents/factory/strict-baseline.txt)

A `mkdocs build --strict` can also fail *hard* — a macro/Jinja `UndefinedError`, a config
error, a plugin crash — which surfaces as an `ERROR` line and/or a Python traceback, NOT a
`WARNING`. Those are never acceptable debt, so this gate fails on any `ERROR`/traceback
regardless of the baseline. (We deliberately do NOT rely on the pipeline exit code / `pipefail`:
under `--strict` mkdocs aborts non-zero on *any* warning, including the tolerated baseline ones,
so this script — not the exit code — must be the arbiter.)

Exit codes:
0 no new warnings beyond the baseline (PASS)
1 new warning(s) attributable to this change (STOP)
0 no ERRORs and no new warnings beyond the baseline (PASS)
1 a build ERROR/traceback, or new warning(s) attributable to this change (STOP)
2 usage / I/O error (e.g. missing baseline file)
"""
from __future__ import annotations
Expand All @@ -30,10 +37,12 @@
from pathlib import Path

# Public interface
__all__ = ["main", "extract_warnings", "load_baseline"]
__all__ = ["main", "extract_warnings", "extract_errors", "load_baseline"]

# A mkdocs strict warning line looks like: "WARNING - <message>".
_WARNING_RE = re.compile(r"^WARNING\s*-\s*(?P<msg>.*\S)\s*$")
# A hard failure looks like "ERROR - <message>" (macros/Jinja error, config error, …).
_ERROR_RE = re.compile(r"^ERROR\s*-\s*(?P<msg>.*\S)\s*$")


def extract_warnings(text: str) -> set[str]:
Expand All @@ -46,6 +55,22 @@ def extract_warnings(text: str) -> set[str]:
return out


def extract_errors(text: str) -> set[str]:
"""Return hard-failure signals (ERROR lines + a crash traceback) in a build log.

These indicate the build did not succeed (e.g. an unescaped ``{{ }}`` in published
config raising a macros ``UndefinedError``). They are never baseline debt.
"""
out: set[str] = set()
for line in text.splitlines():
m = _ERROR_RE.match(line)
if m:
out.add(m.group("msg").strip())
if "Traceback (most recent call last):" in text:
out.add("Python traceback in build output (the build crashed)")
return out


def load_baseline(path: Path) -> set[str]:
"""Return the set of baseline warning messages (ignoring '#' and blanks)."""
out: set[str] = set()
Expand Down Expand Up @@ -78,9 +103,22 @@ def main(argv: list[str]) -> int:
return 2

found = extract_warnings(text)
errors = sorted(extract_errors(text))
new = sorted(found - baseline)
fixed = sorted(baseline - found)

if errors:
print(f"FAIL: {len(errors)} build ERROR(s) — the build did not succeed cleanly:")
for msg in errors:
print(f" ! {msg}")
print(
"\nERRORs are never baseline debt. Fix the build — a common cause here is an "
"unescaped '{{' / '{%' in published content hitting the macros/Jinja pass "
"(publish verbatim config via a '--8<--' include instead), a bad config, or a "
"plugin crash."
)
return 1

if new:
print(f"FAIL: {len(new)} new mkdocs --strict warning(s) introduced by this change:")
for msg in new:
Expand Down
Loading