@event4u/agent-config 1.23.0 → 1.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.agent-src/commands/analyze-reference-repo.md +3 -0
  2. package/.agent-src/commands/review-routing.md +7 -10
  3. package/.agent-src/commands/roadmap/process-full.md +41 -1
  4. package/.agent-src/contexts/authority/kernel-rule-edits.md +48 -0
  5. package/.agent-src/contexts/authority/scope-mechanics.md +15 -0
  6. package/.agent-src/contexts/contracts/consumer-agents-md-guide.md +127 -0
  7. package/.agent-src/contexts/contracts/emergency-triage-block.md +53 -0
  8. package/.agent-src/contexts/execution/roadmap-process-loop.md +29 -6
  9. package/.agent-src/rules/analysis-skill-routing.md +1 -1
  10. package/.agent-src/rules/artifact-drafting-protocol.md +1 -1
  11. package/.agent-src/rules/artifact-engagement-recording.md +1 -1
  12. package/.agent-src/rules/augment-source-of-truth.md +1 -1
  13. package/.agent-src/rules/autonomous-execution.md +1 -1
  14. package/.agent-src/rules/caveman-speak.md +1 -1
  15. package/.agent-src/rules/cli-output-handling.md +1 -1
  16. package/.agent-src/rules/command-suggestion-policy.md +1 -1
  17. package/.agent-src/rules/docs-sync.md +1 -1
  18. package/.agent-src/rules/guidelines.md +1 -1
  19. package/.agent-src/rules/improve-before-implement.md +1 -1
  20. package/.agent-src/rules/invite-challenge.md +1 -1
  21. package/.agent-src/rules/minimal-safe-diff.md +1 -1
  22. package/.agent-src/rules/model-recommendation.md +1 -1
  23. package/.agent-src/rules/no-attribution-footers.md +1 -1
  24. package/.agent-src/rules/no-roadmap-references.md +56 -20
  25. package/.agent-src/rules/onboarding-gate.md +1 -1
  26. package/.agent-src/rules/package-ci-checks.md +1 -1
  27. package/.agent-src/rules/reviewer-awareness.md +9 -2
  28. package/.agent-src/rules/roadmap-progress-sync.md +37 -3
  29. package/.agent-src/rules/scope-control.md +6 -0
  30. package/.agent-src/rules/security-sensitive-stop.md +1 -1
  31. package/.agent-src/rules/size-enforcement.md +1 -1
  32. package/.agent-src/rules/token-optimizer-maintenance.md +1 -1
  33. package/.agent-src/rules/ui-audit-gate.md +1 -1
  34. package/.agent-src/skills/adr-create/SKILL.md +2 -1
  35. package/.agent-src/skills/agents-md-thin-root/SKILL.md +125 -0
  36. package/.agent-src/skills/ai-council/SKILL.md +9 -7
  37. package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +9 -0
  38. package/.agent-src/skills/markitdown/SKILL.md +239 -0
  39. package/.agent-src/skills/review-routing/SKILL.md +3 -4
  40. package/.agent-src/skills/universal-project-analysis/SKILL.md +8 -0
  41. package/.agent-src/templates/AGENTS.md +18 -148
  42. package/.agent-src/templates/copilot-instructions.md +41 -17
  43. package/.agent-src/templates/github-workflows/pr-risk-review.yml +1 -1
  44. package/.agent-src/templates/scripts/pr_review_routing.py +1 -1
  45. package/.claude-plugin/marketplace.json +7 -5
  46. package/AGENTS.md +18 -205
  47. package/CHANGELOG.md +70 -0
  48. package/README.md +2 -2
  49. package/docs/architecture.md +13 -7
  50. package/docs/catalog.md +45 -29
  51. package/docs/contracts/agents-md-tech-stack.md +74 -0
  52. package/docs/contracts/linear-ai-rules-inclusion.md +1 -1
  53. package/docs/contracts/package-self-orientation.md +135 -0
  54. package/docs/contracts/rule-classification.md +4 -4
  55. package/docs/decisions/ADR-004-rule-governance-pruning.md +240 -0
  56. package/docs/getting-started.md +1 -1
  57. package/docs/guidelines/agent-infra/review-routing-data-format.md +1 -2
  58. package/package.json +1 -1
  59. package/scripts/_p4_migrate.py +5 -5
  60. package/scripts/audit_auto_rules.py +159 -0
  61. package/scripts/audit_likelihood.py +148 -0
  62. package/scripts/audit_overlap.py +145 -0
  63. package/scripts/build_rule_trigger_matrix.py +3 -5
  64. package/scripts/check_augment_description_cap.py +79 -0
  65. package/scripts/check_council_references.py +3 -3
  66. package/scripts/check_kernel_rule_bundle.py +151 -0
  67. package/scripts/check_references.py +21 -1
  68. package/scripts/compile_router.py +3 -0
  69. package/scripts/install.sh +0 -1
  70. package/scripts/lint_agents_md.py +168 -0
  71. package/scripts/measure_augment_budget.py +208 -0
  72. package/scripts/measure_markitdown_lift.py +127 -0
  73. package/scripts/schemas/rule.schema.json +2 -1
  74. package/scripts/skill_linter.py +10 -4
  75. package/scripts/spotcheck_thin_root.py +134 -0
  76. package/scripts/update_counts.py +6 -10
  77. package/.agent-src/rules/no-council-references.md +0 -76
  78. package/.agent-src/rules/review-routing-awareness.md +0 -19
  79. package/.agent-src/templates/copilot-review-instructions.md +0 -76
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env python3
2
+ """Measure the Augment workspace-guidelines budget (Phase 1.1 of
3
+ road-to-augment-limit-fit).
4
+
5
+ Mirrors Augment's accounting model for the workspace prompt:
6
+
7
+ 1. `AGENTS.md` body (full file, including frontmatter) injected verbatim.
8
+ 2. `always`-type rules under `.augment/rules/` — full body injected.
9
+ 3. `auto`-type rules — only a registry stub is injected per rule:
10
+
11
+ If the user prompt matches the description "<desc>", read the
12
+ file located in <path>
13
+
14
+ The body of an `auto` rule is NOT counted; only the stub line is.
15
+
16
+ The 49,512-char ceiling is the empirical limit observed against the
17
+ Augment Code workspace prompt (2026-05-08 baseline). This script emits
18
+ a per-component breakdown plus the total against that ceiling.
19
+
20
+ Output:
21
+ - Default: stdout summary (totals + per-component breakdown).
22
+ - `--json`: deterministic JSON.
23
+ - `--trend-append`: append a snapshot record to
24
+ `agents/.augment-budget-history.jsonl`.
25
+
26
+ Exit codes: 0 = under fail threshold, 1 = at/above fail threshold,
27
+ 3 = internal error.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import argparse
33
+ import datetime as _dt
34
+ import json
35
+ import re
36
+ import sys
37
+ from pathlib import Path
38
+
39
+ REPO_ROOT = Path(__file__).resolve().parent.parent
40
+ AGENTS_MD = REPO_ROOT / "AGENTS.md"
41
+ RULES_DIR = REPO_ROOT / ".augment" / "rules"
42
+ TREND_FILE = REPO_ROOT / "agents" / ".augment-budget-history.jsonl"
43
+
44
+ # Augment workspace-guidelines ceiling — empirical 2026-05-08.
45
+ TOTAL_CAP = 49_512
46
+ WARN_THRESHOLD = 0.85
47
+ FAIL_THRESHOLD = 0.95
48
+
49
+ # Stub template Augment injects for `type: auto` rules. Measured by
50
+ # subtracting variable-length fields (description, path) from a real
51
+ # rendered stub in the host system prompt.
52
+ STUB_TEMPLATE = (
53
+ 'If the user prompt matches the description "{desc}", '
54
+ "read the file located in {path}"
55
+ )
56
+
57
+
58
+ def parse_frontmatter(text: str) -> tuple[dict[str, str], str]:
59
+ if not text.startswith("---\n"):
60
+ return {}, text
61
+ end = text.find("\n---", 4)
62
+ if end < 0:
63
+ return {}, text
64
+ fm_block = text[4:end]
65
+ body = text[end + 4 :].lstrip("\n")
66
+ fm: dict[str, str] = {}
67
+ for line in fm_block.splitlines():
68
+ m = re.match(r"^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$", line)
69
+ if m:
70
+ fm[m.group(1)] = m.group(2).strip().strip('"').strip("'")
71
+ return fm, body
72
+
73
+
74
+ def measure() -> dict:
75
+ components: dict[str, dict] = {}
76
+
77
+ # 1. AGENTS.md
78
+ agents_text = AGENTS_MD.read_text() if AGENTS_MD.exists() else ""
79
+ components["agents_md"] = {
80
+ "path": str(AGENTS_MD.relative_to(REPO_ROOT)),
81
+ "chars": len(agents_text),
82
+ }
83
+
84
+ # 2 + 3. Rules under .augment/rules/.
85
+ always_total = 0
86
+ always_rules: list[dict] = []
87
+ auto_total = 0
88
+ auto_rules: list[dict] = []
89
+
90
+ for rule_path in sorted(RULES_DIR.glob("*.md")):
91
+ text = rule_path.read_text()
92
+ fm, _body = parse_frontmatter(text)
93
+ rtype = fm.get("type", "")
94
+ rel = str(rule_path.relative_to(REPO_ROOT))
95
+ if rtype == "always":
96
+ chars = len(text)
97
+ always_total += chars
98
+ always_rules.append({"path": rel, "chars": chars})
99
+ elif rtype == "auto":
100
+ desc = fm.get("description", "")
101
+ stub = STUB_TEMPLATE.format(desc=desc, path=rel)
102
+ chars = len(stub)
103
+ auto_total += chars
104
+ auto_rules.append(
105
+ {"path": rel, "desc_chars": len(desc), "stub_chars": chars}
106
+ )
107
+
108
+ components["always_rules"] = {
109
+ "count": len(always_rules),
110
+ "chars": always_total,
111
+ "rules": sorted(always_rules, key=lambda r: -r["chars"]),
112
+ }
113
+ components["auto_rules"] = {
114
+ "count": len(auto_rules),
115
+ "chars": auto_total,
116
+ "rules": sorted(auto_rules, key=lambda r: -r["stub_chars"]),
117
+ }
118
+
119
+ total = (
120
+ components["agents_md"]["chars"]
121
+ + always_total
122
+ + auto_total
123
+ )
124
+ return {
125
+ "ts": _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
126
+ "total": total,
127
+ "cap": TOTAL_CAP,
128
+ "utilisation": round(total / TOTAL_CAP, 4),
129
+ "components": components,
130
+ }
131
+
132
+
133
+ def render_text(data: dict) -> str:
134
+ total = data["total"]
135
+ cap = data["cap"]
136
+ util = data["utilisation"]
137
+ a = data["components"]["agents_md"]["chars"]
138
+ ar = data["components"]["always_rules"]
139
+ aur = data["components"]["auto_rules"]
140
+ lines = [
141
+ f"Augment workspace-guidelines budget — cap {cap:,} chars",
142
+ "",
143
+ f" AGENTS.md {a:>6,} chars ({a/cap*100:5.1f}%)",
144
+ f" always-rules ({ar['count']:>2}) {ar['chars']:>6,} chars ({ar['chars']/cap*100:5.1f}%)",
145
+ f" auto-rule stubs ({aur['count']:>2}) {aur['chars']:>6,} chars ({aur['chars']/cap*100:5.1f}%)",
146
+ " " + "-" * 50,
147
+ f" TOTAL {total:>6,} chars ({util*100:5.1f}%)",
148
+ "",
149
+ ]
150
+ if util >= 1.0:
151
+ lines.append(f"❌ OVER CAP by {total - cap:,} chars")
152
+ elif util >= FAIL_THRESHOLD:
153
+ lines.append(f"❌ FAIL — utilisation {util*100:.1f}% ≥ {FAIL_THRESHOLD*100:.0f}%")
154
+ elif util >= WARN_THRESHOLD:
155
+ lines.append(f"⚠️ WARN — utilisation {util*100:.1f}% ≥ {WARN_THRESHOLD*100:.0f}%")
156
+ else:
157
+ lines.append(f"✅ OK — utilisation {util*100:.1f}%")
158
+ return "\n".join(lines)
159
+
160
+
161
+ def main() -> int:
162
+ parser = argparse.ArgumentParser(description=__doc__)
163
+ parser.add_argument("--json", action="store_true", help="Emit JSON")
164
+ parser.add_argument(
165
+ "--trend-append",
166
+ action="store_true",
167
+ help="Append a snapshot record to agents/.augment-budget-history.jsonl",
168
+ )
169
+ parser.add_argument(
170
+ "--check",
171
+ action="store_true",
172
+ help="Exit non-zero when utilisation ≥ FAIL_THRESHOLD or over cap",
173
+ )
174
+ args = parser.parse_args()
175
+
176
+ data = measure()
177
+
178
+ if args.trend_append:
179
+ TREND_FILE.parent.mkdir(parents=True, exist_ok=True)
180
+ rec = {
181
+ "ts": data["ts"],
182
+ "total": data["total"],
183
+ "cap": data["cap"],
184
+ "utilisation": data["utilisation"],
185
+ "agents_md": data["components"]["agents_md"]["chars"],
186
+ "always_rules": data["components"]["always_rules"]["chars"],
187
+ "auto_rules": data["components"]["auto_rules"]["chars"],
188
+ }
189
+ with TREND_FILE.open("a") as fh:
190
+ fh.write(json.dumps(rec, sort_keys=True) + "\n")
191
+
192
+ if args.json:
193
+ print(json.dumps(data, indent=2, sort_keys=True))
194
+ else:
195
+ print(render_text(data))
196
+
197
+ if args.check:
198
+ if data["utilisation"] >= 1.0 or data["utilisation"] >= FAIL_THRESHOLD:
199
+ return 1
200
+ return 0
201
+
202
+
203
+ if __name__ == "__main__":
204
+ try:
205
+ sys.exit(main())
206
+ except Exception as exc: # pragma: no cover - defensive top-level guard
207
+ print(f"❌ measure_augment_budget: internal error: {exc}", file=sys.stderr)
208
+ sys.exit(3)
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ """Measure markitdown's token-saving lift on the bundled corpus.
3
+
4
+ Runs against `tests/fixtures/markitdown-corpus/`. By default (no flags) the
5
+ script computes the baseline-only — raw byte size and a tokens-per-4-bytes
6
+ estimate — without calling `markitdown-mcp`. With `--convert`, the script
7
+ tries to invoke `markitdown` (CLI binary) via subprocess and computes the
8
+ converted-Markdown token estimate plus the ratio per file.
9
+
10
+ Stdlib-only. Never installs anything. Never invokes a network host. Never
11
+ calls `markitdown-mcp` over HTTP — only through the `markitdown` CLI on
12
+ the user's PATH (peer-side install per the skill's Step 1 recipes).
13
+
14
+ Exit codes:
15
+ 0 — baseline produced (always, when fixtures exist)
16
+ 2 — corpus not found
17
+ 3 — `--convert` was requested but `markitdown` is not on PATH
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import argparse
23
+ import shutil
24
+ import subprocess
25
+ import sys
26
+ from pathlib import Path
27
+
28
+ REPO_ROOT = Path(__file__).resolve().parent.parent
29
+ CORPUS = REPO_ROOT / "tests" / "fixtures" / "markitdown-corpus"
30
+ TOKEN_PER_BYTES = 4 # rough OpenAI/Anthropic tokenizer-of-thumb
31
+
32
+
33
+ def _baseline_tokens(p: Path) -> int:
34
+ return max(1, p.stat().st_size // TOKEN_PER_BYTES)
35
+
36
+
37
+ def _converted_tokens(p: Path, *, binary: str) -> int | None:
38
+ try:
39
+ out = subprocess.run(
40
+ [binary, str(p)],
41
+ capture_output=True,
42
+ check=False,
43
+ text=True,
44
+ timeout=30,
45
+ )
46
+ except (OSError, subprocess.TimeoutExpired):
47
+ return None
48
+ if out.returncode != 0:
49
+ return None
50
+ chars = len(out.stdout)
51
+ if chars == 0:
52
+ return None
53
+ return max(1, chars // TOKEN_PER_BYTES)
54
+
55
+
56
+ def _format_ratio(baseline: int, converted: int | None) -> str:
57
+ if converted is None or converted == 0:
58
+ return "—"
59
+ ratio = baseline / converted
60
+ return f"{ratio:.1f}×"
61
+
62
+
63
+ def main() -> int:
64
+ parser = argparse.ArgumentParser(description="Measure markitdown lift on the bundled corpus.")
65
+ parser.add_argument(
66
+ "--convert",
67
+ action="store_true",
68
+ help="Invoke `markitdown <fixture>` per file and compute the converted-token ratio.",
69
+ )
70
+ parser.add_argument(
71
+ "--binary",
72
+ default="markitdown",
73
+ help="Name or path of the markitdown CLI binary (default: markitdown).",
74
+ )
75
+ args = parser.parse_args()
76
+
77
+ if not CORPUS.is_dir():
78
+ print(f"ERROR: corpus not found at {CORPUS}", file=sys.stderr)
79
+ print(
80
+ "Generate it: python3 tests/fixtures/markitdown-corpus/_generate.py",
81
+ file=sys.stderr,
82
+ )
83
+ return 2
84
+
85
+ fixtures = sorted(p for p in CORPUS.iterdir() if p.is_file() and p.suffix in {".pdf", ".pptx", ".docx", ".xlsx"})
86
+ if not fixtures:
87
+ print(f"ERROR: no fixtures in {CORPUS}", file=sys.stderr)
88
+ return 2
89
+
90
+ binary_path: str | None = None
91
+ if args.convert:
92
+ binary_path = shutil.which(args.binary)
93
+ if binary_path is None:
94
+ print(
95
+ f"ERROR: --convert requested but `{args.binary}` not on PATH.\n"
96
+ "Install peer-side per the skill's Step 1 recipes "
97
+ "(Docker / pipx / uv) and re-run.",
98
+ file=sys.stderr,
99
+ )
100
+ return 3
101
+
102
+ print(f"Corpus: {CORPUS.relative_to(REPO_ROOT)} ({len(fixtures)} files)")
103
+ print(f"Mode: {'convert (peer markitdown CLI)' if binary_path else 'baseline-only'}")
104
+ if binary_path:
105
+ print(f"Binary: {binary_path}")
106
+ print()
107
+ header = f"{'fixture':<32} {'bytes':>7} {'baseline tok':>13} {'converted tok':>14} {'ratio':>7}"
108
+ print(header)
109
+ print("-" * len(header))
110
+ for p in fixtures:
111
+ size = p.stat().st_size
112
+ base = _baseline_tokens(p)
113
+ converted = _converted_tokens(p, binary=binary_path) if binary_path else None
114
+ ratio = _format_ratio(base, converted)
115
+ conv_str = f"{converted}" if converted is not None else "—"
116
+ print(f"{p.name:<32} {size:>7} {base:>13} {conv_str:>14} {ratio:>7}")
117
+ print()
118
+ if not binary_path:
119
+ print(
120
+ "Re-run with --convert (after installing markitdown-mcp peer-side per the skill's "
121
+ "Step 1 recipes) for the actual ratio."
122
+ )
123
+ return 0
124
+
125
+
126
+ if __name__ == "__main__":
127
+ sys.exit(main())
@@ -9,7 +9,8 @@
9
9
  "properties": {
10
10
  "type": {
11
11
  "type": "string",
12
- "enum": ["always", "auto"]
12
+ "enum": ["always", "auto", "manual"],
13
+ "description": "`always` = injected verbatim every turn (kernel). `auto` = description stub injected, body loaded on trigger match. `manual` = no auto-injection (zero workspace-budget cost); file remains as a reference document linkable from skills/contexts. Introduced by ADR-004 to demote thin pointer-rules without breaking cross-references."
13
14
  },
14
15
  "source": {
15
16
  "type": "string",
@@ -115,7 +115,7 @@ ORDERED_STEP_PATTERN = re.compile(r"^(?:\s*|\#{1,4}\s*)(\d+)\.\s+", re.MULTILINE
115
115
  SECTION_PATTERN = re.compile(r"^##\s+(.+?)\s*$", re.MULTILINE)
116
116
  FRONTMATTER_PATTERN = re.compile(r"^---\n(.*?)\n---\n", re.DOTALL)
117
117
  DESCRIPTION_PATTERN = re.compile(r'^description:\s*"?(.*?)"?\s*$', re.MULTILINE)
118
- TYPE_PATTERN = re.compile(r'^type:\s*"?(always|auto)"?\s*$', re.MULTILINE)
118
+ TYPE_PATTERN = re.compile(r'^type:\s*"?(always|auto|manual)"?\s*$', re.MULTILINE)
119
119
  SOURCE_PATTERN = re.compile(r'^source:\s*"?(package|project)"?\s*$', re.MULTILINE)
120
120
  STATUS_PATTERN = re.compile(r'^status:\s*"?(active|deprecated|superseded)"?\s*$', re.MULTILINE)
121
121
  REPLACED_BY_PATTERN = re.compile(r'^replaced_by:\s*"?([\w-]+)"?\s*$', re.MULTILINE)
@@ -133,7 +133,7 @@ SENIOR_OUTPUT_PATTERN = re.compile(r"^##\s+Output\s*$", re.MULTILINE)
133
133
  H1_PATTERN = re.compile(r"^# .+", re.MULTILINE)
134
134
  DOUBLE_BLANK_PATTERN = re.compile(r"\n{3,}")
135
135
 
136
- VALID_RULE_TYPES = {"always", "auto"}
136
+ VALID_RULE_TYPES = {"always", "auto", "manual"}
137
137
  VALID_RULE_SOURCES = {"package", "project"}
138
138
  VALID_STATUSES = {"active", "deprecated", "superseded"}
139
139
 
@@ -683,6 +683,12 @@ def lint_router_frontmatter(rule_id: str, frontmatter: str,
683
683
  triggers = _parse_yaml_list(frontmatter, "triggers")
684
684
  routes_to = _parse_yaml_list(frontmatter, "routes_to")
685
685
 
686
+ # Manual rules are reference-only — not auto-injected, not router-routed
687
+ # (ADR-004). Skip router validation so legacy triggers/routes_to fields
688
+ # remain documented in the rule body without forcing maintenance.
689
+ if rule_type == "manual":
690
+ return issues
691
+
686
692
  is_kernel = rule_id in KERNEL_RULE_IDS or rule_type == "always"
687
693
 
688
694
  if is_kernel:
@@ -961,9 +967,9 @@ def lint_rule(path: Path, text: str) -> LintResult:
961
967
  # type field
962
968
  rule_type = extract_frontmatter_field(frontmatter, TYPE_PATTERN)
963
969
  if rule_type is None:
964
- issues.append(Issue("error", "missing_type", "Frontmatter missing 'type' field (must be 'always' or 'auto')"))
970
+ issues.append(Issue("error", "missing_type", "Frontmatter missing 'type' field (must be 'always', 'auto', or 'manual')"))
965
971
  elif rule_type not in VALID_RULE_TYPES:
966
- issues.append(Issue("error", "invalid_type", f"Invalid type '{rule_type}'; must be 'always' or 'auto'"))
972
+ issues.append(Issue("error", "invalid_type", f"Invalid type '{rule_type}'; must be 'always', 'auto', or 'manual'"))
967
973
 
968
974
  # source field
969
975
  rule_source = extract_frontmatter_field(frontmatter, SOURCE_PATTERN)
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env python3
2
+ """Phase 6.6 platform spot-check via AI council.
3
+
4
+ Sends the refactored package-root AGENTS.md and the consumer template
5
+ to Sonnet 4.5 + gpt-4o, asks each member to answer five questions
6
+ that simulate a fresh agent landing on the file. Records qualitative
7
+ verdicts in agents/reports/thin-root-platform-spotcheck.md.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ ROOT = Path(__file__).resolve().parent.parent
16
+ sys.path.insert(0, str(ROOT))
17
+
18
+ from scripts.ai_council.clients import ( # noqa: E402
19
+ AnthropicClient,
20
+ OpenAIClient,
21
+ load_anthropic_key,
22
+ load_openai_key,
23
+ )
24
+ from scripts.ai_council.orchestrator import ( # noqa: E402
25
+ CostBudget,
26
+ CouncilQuestion,
27
+ consult,
28
+ )
29
+ from scripts.ai_council.pricing import load_prices # noqa: E402
30
+
31
+ QUESTIONS = """
32
+ You are evaluating whether the AGENTS.md file below is a sufficient
33
+ entry point for an AI coding agent landing on this repository for
34
+ the first time. You see only the AGENTS.md content; you do NOT have
35
+ file-system access. Answer the following five questions in JSON
36
+ shape `{"q1": {...}, ..., "q5": {...}}` where each value is
37
+ `{"answer": <string>, "confidence": "high"|"medium"|"low",
38
+ "pointer_used": <one of the linked paths from AGENTS.md, or null>}`.
39
+
40
+ Q1. Where do I edit content in this repo / project? (a path)
41
+ Q2. What command do I run to verify everything is green before opening a PR?
42
+ Q3. Where would I find the always-active behavioural rules?
43
+ Q4. If only this file is reachable, what five things must I assume to be true to act safely? (cite the emergency-triage block)
44
+ Q5. What outboard target document would I open to learn the package-self-orientation / the consumer-fill-out guide? (a path)
45
+
46
+ After the JSON, add a short prose verdict (≤ 5 sentences) on:
47
+ - Whether the pointer-following worked (could you cite a path for Q1, Q3, Q5?)
48
+ - Whether the emergency-triage block answered Q4 unambiguously.
49
+ - One concrete improvement you'd make to the AGENTS.md.
50
+
51
+ Do not invent file paths. If a question cannot be answered from the
52
+ file alone, set `"pointer_used": null` and lower confidence.
53
+ """.strip()
54
+
55
+
56
+ def main() -> int:
57
+ package_root = (ROOT / "AGENTS.md").read_text(encoding="utf-8")
58
+ consumer_template = (
59
+ ROOT / ".agent-src.uncompressed" / "templates" / "AGENTS.md"
60
+ ).read_text(encoding="utf-8")
61
+
62
+ artefact = (
63
+ "## Artefact A — package-root AGENTS.md\n\n"
64
+ f"```markdown\n{package_root}\n```\n\n"
65
+ "## Artefact B — consumer-template AGENTS.md\n\n"
66
+ f"```markdown\n{consumer_template}\n```\n\n"
67
+ f"{QUESTIONS}\n"
68
+ )
69
+
70
+ members = [
71
+ AnthropicClient(model="claude-sonnet-4-5", api_key=load_anthropic_key()),
72
+ OpenAIClient(model="gpt-4o", api_key=load_openai_key()),
73
+ ]
74
+
75
+ question = CouncilQuestion(
76
+ mode="files",
77
+ user_prompt=artefact,
78
+ max_tokens=1500,
79
+ )
80
+ budget = CostBudget(max_total_usd=2.00, max_calls=4)
81
+ table = load_prices()
82
+
83
+ print("Running spot-check council …", file=sys.stderr)
84
+ responses = consult(members, question, budget, table=table, rounds=1)
85
+
86
+ out_dir = ROOT / "agents" / "reports"
87
+ out_dir.mkdir(parents=True, exist_ok=True)
88
+ md_path = out_dir / "thin-root-platform-spotcheck.md"
89
+ json_path = out_dir / "thin-root-platform-spotcheck.json"
90
+
91
+ md_lines = [
92
+ "# Thin-Root platform spot-check (Phase 6.6)",
93
+ "",
94
+ "> AI-council proxy for the manual platform spot-check. Two",
95
+ "> external reviewers (Sonnet 4.5, gpt-4o) simulate a fresh",
96
+ "> agent landing on the refactored AGENTS.md and answer five",
97
+ "> orientation questions from the file alone.",
98
+ "",
99
+ "## Verdicts",
100
+ "",
101
+ ]
102
+
103
+ raw = []
104
+ for r in responses:
105
+ body = r.text or f"<error: {r.error}>"
106
+ raw.append({
107
+ "provider": r.provider,
108
+ "model": r.model,
109
+ "tokens_in": r.input_tokens,
110
+ "tokens_out": r.output_tokens,
111
+ "latency_ms": r.latency_ms,
112
+ "error": r.error,
113
+ "text": body,
114
+ })
115
+ md_lines.append(f"### {r.provider} ({r.model})")
116
+ md_lines.append("")
117
+ md_lines.append(f"- tokens in: {r.input_tokens} · out: {r.output_tokens} · latency: {r.latency_ms}ms")
118
+ if r.error:
119
+ md_lines.append(f"- error: `{r.error}`")
120
+ md_lines.append("")
121
+ md_lines.append("```")
122
+ md_lines.append(body[:8000])
123
+ md_lines.append("```")
124
+ md_lines.append("")
125
+
126
+ md_path.write_text("\n".join(md_lines), encoding="utf-8")
127
+ json_path.write_text(json.dumps(raw, indent=2), encoding="utf-8")
128
+ print(f"✅ Wrote {md_path}", file=sys.stderr)
129
+ print(f"✅ Wrote {json_path}", file=sys.stderr)
130
+ return 0
131
+
132
+
133
+ if __name__ == "__main__":
134
+ raise SystemExit(main())
@@ -67,16 +67,12 @@ TARGETS: list[tuple[str, list[tuple[str, str]]]] = [
67
67
  # the raw file count this script computes.
68
68
  ],
69
69
  ),
70
- (
71
- "AGENTS.md",
72
- [
73
- (r"(skills/\s+\()(\d+)( skills\))", "skills"),
74
- (r"(rules/\s+\()(\d+)( rules\))", "rules"),
75
- (r"(commands/\s+\()(\d+)( commands\))", "commands"),
76
- (r"(guidelines/\s+\()(\d+)( guidelines\))", "guidelines"),
77
- (r"(personas/\s+\()(\d+)( personas\))", "personas"),
78
- ],
79
- ),
70
+ # Note: AGENTS.md previously held the per-directory count annotations
71
+ # (`skills/ (N skills)`, `rules/ (N rules)`, ...). The Thin-Root
72
+ # refactor (Phase 6, road-to-augment-limit-fit, 2026-05-08) made
73
+ # AGENTS.md a navigation-only surface — counts now live in README.md
74
+ # and docs/architecture.md. The corresponding pytest sentinel lives
75
+ # in tests/test_readme_hero_counts.py::test_agents_md_is_thin_root_navigation_surface.
80
76
  (
81
77
  "docs/getting-started.md",
82
78
  [
@@ -1,76 +0,0 @@
1
- ---
2
- type: "auto"
3
- tier: "mechanical-already"
4
- description: "Linking a specific file in agents/council-{questions,responses,sessions}/ from any artifact — council files are gitignored, local-only, auto-pruned; inline the convergence instead"
5
- alwaysApply: false
6
- source: package
7
- triggers:
8
- - path_prefix: "agents/council-questions/"
9
- - path_prefix: "agents/council-responses/"
10
- - path_prefix: "agents/council-sessions/"
11
- - intent: "link to council artefact"
12
- routes_to:
13
- - "skill:ai-council"
14
- validator_ignore:
15
- - type: "substring"
16
- pattern: ".agent-src.uncompressed/"
17
- reason: "Rule references the authoring tree when contrasting transient council files."
18
- ---
19
-
20
- # No Council References from Any Artifact
21
-
22
- Council artefacts under `agents/council-{questions,responses,sessions}/`
23
- are **gitignored, local-only, and auto-pruned** after
24
- `ai_council.session_retention_days` (default 7). They are
25
- disposable scratch — never part of the repo, never visible to a
26
- reviewer who clones, never durable across the retention window.
27
-
28
- A link to a specific council file rots three ways: gitignored
29
- (not in cloned repo), pruned after retention window (gone even
30
- locally), and the installed `.augment/` projection cannot follow a
31
- path that does not exist in the consumer.
32
-
33
- ## The Iron Law
34
-
35
- ```
36
- NEVER LINK TO A SPECIFIC FILE INSIDE
37
- agents/council-{questions,responses,sessions}/
38
- FROM ANY ARTIFACT — ROADMAPS INCLUDED.
39
- INLINE THE CONVERGENCE WITH DATE + MEMBERS, NEVER THE PATH.
40
- ```
41
-
42
- Applies to **every** artifact. Council artefacts are more transient
43
- than roadmaps — the local copy disappears too.
44
-
45
- ## Forbidden vs allowed
46
-
47
- **Forbidden** in any `*.md` / `*.yml` / `*.json` / `*.py`:
48
- `agents/council-questions/<file>.md`,
49
- `agents/council-responses/<file>.json`,
50
- `agents/council-sessions/<file>.json` or `<timestamp>/...`.
51
-
52
- **Allowed**: directory mentions (talking about the output convention,
53
- not a specific file); the `ai-council` skill and `/council:*` commands
54
- documenting the output path schema; inline convergence summary —
55
- e.g. *"Council (claude-sonnet-4-5 + gpt-4o, 2026-05-06) converged
56
- on …"* with date + members, no filepath.
57
-
58
- ## What to do instead
59
-
60
- Identify the durable conclusion (decision, contract, lesson),
61
- inline a convergence-summary block (members, date, cost if relevant
62
- — see `ai-council` § Output format), and optionally promote the
63
- lesson to `agents/contexts/`. The context is durable; the council
64
- file was the catalyst.
65
-
66
- Failure mode: *"I'll just link to the session JSON, it's evidence."*
67
- The session is gone in 7 days. **Inline first, link never.**
68
-
69
- ## See also
70
-
71
- - [`no-roadmap-references`](no-roadmap-references.md) — sibling rule
72
- for the roadmap layer
73
- - [`augment-source-of-truth`](augment-source-of-truth.md) — edit
74
- `.agent-src.uncompressed/`
75
- - [`ai-council`](../skills/ai-council/SKILL.md) — output path
76
- convention and convergence-summary format
@@ -1,19 +0,0 @@
1
- ---
2
- type: "auto"
3
- tier: "2a"
4
- description: "When routing reviewers or flagging risk hotspots — consult ownership-map and historical-bug-patterns before suggesting reviewers or claiming a change is safe"
5
- source: package
6
- triggers:
7
- - keyword: "reviewer"
8
- - phrase: "risk hotspot"
9
- - phrase: "ownership map"
10
- routes_to:
11
- - "skill:review-routing"
12
- ---
13
-
14
- # Review Routing Awareness
15
-
16
- **Iron Law.** Consult ownership-map and historical-bug-patterns before suggesting reviewers or claiming a change is safe.
17
-
18
- Body migrated to `skill:review-routing` (per P4 of `road-to-kernel-and-router.md`).
19
- Trigger-set above activates this routing under the `balanced` and `full` profiles.