@event4u/agent-config 1.16.0 → 1.17.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 (203) hide show
  1. package/.agent-src/commands/{agents-audit.md → agents/audit.md} +4 -3
  2. package/.agent-src/commands/{agents-cleanup.md → agents/cleanup.md} +12 -6
  3. package/.agent-src/commands/{agents-prepare.md → agents/prepare.md} +4 -3
  4. package/.agent-src/commands/agents.md +46 -0
  5. package/.agent-src/commands/{chat-history-checkpoint.md → chat-history/checkpoint.md} +4 -4
  6. package/.agent-src/commands/{chat-history-clear.md → chat-history/clear.md} +4 -4
  7. package/.agent-src/commands/{chat-history-resume.md → chat-history/resume.md} +4 -4
  8. package/.agent-src/commands/chat-history/show.md +107 -0
  9. package/.agent-src/commands/chat-history.md +33 -89
  10. package/.agent-src/commands/{commit-in-chunks.md → commit/in-chunks.md} +15 -13
  11. package/.agent-src/commands/commit.md +22 -2
  12. package/.agent-src/commands/{context-create.md → context/create.md} +4 -3
  13. package/.agent-src/commands/{context-refactor.md → context/refactor.md} +4 -3
  14. package/.agent-src/commands/context.md +44 -0
  15. package/.agent-src/commands/{copilot-agents-init.md → copilot-agents/init.md} +4 -3
  16. package/.agent-src/commands/{copilot-agents-optimize.md → copilot-agents/optimize.md} +4 -3
  17. package/.agent-src/commands/copilot-agents.md +44 -0
  18. package/.agent-src/commands/council/default.md +221 -0
  19. package/.agent-src/commands/{council-design.md → council/design.md} +6 -5
  20. package/.agent-src/commands/{council-optimize.md → council/optimize.md} +7 -6
  21. package/.agent-src/commands/{council-pr.md → council/pr.md} +6 -5
  22. package/.agent-src/commands/council.md +47 -212
  23. package/.agent-src/commands/{create-pr-description.md → create-pr/description-only.md} +4 -2
  24. package/.agent-src/commands/create-pr.md +26 -5
  25. package/.agent-src/commands/{feature-dev.md → feature/dev.md} +5 -10
  26. package/.agent-src/commands/{feature-explore.md → feature/explore.md} +4 -8
  27. package/.agent-src/commands/{feature-plan.md → feature/plan.md} +4 -8
  28. package/.agent-src/commands/{feature-refactor.md → feature/refactor.md} +4 -8
  29. package/.agent-src/commands/{feature-roadmap.md → feature/roadmap.md} +6 -10
  30. package/.agent-src/commands/feature.md +6 -12
  31. package/.agent-src/commands/{fix-ci.md → fix/ci.md} +4 -8
  32. package/.agent-src/commands/{fix-portability.md → fix/portability.md} +4 -8
  33. package/.agent-src/commands/{fix-pr-bot-comments.md → fix/pr-bots.md} +4 -8
  34. package/.agent-src/commands/{fix-pr-developer-comments.md → fix/pr-developers.md} +4 -8
  35. package/.agent-src/commands/{fix-pr-comments.md → fix/pr.md} +7 -11
  36. package/.agent-src/commands/{fix-references.md → fix/refs.md} +4 -8
  37. package/.agent-src/commands/{fix-seeder.md → fix/seeder.md} +4 -8
  38. package/.agent-src/commands/fix.md +7 -13
  39. package/.agent-src/commands/{do-and-judge.md → judge/on-diff.md} +4 -3
  40. package/.agent-src/commands/judge/solo.md +90 -0
  41. package/.agent-src/commands/{do-in-steps.md → judge/steps.md} +4 -3
  42. package/.agent-src/commands/judge.md +35 -70
  43. package/.agent-src/commands/{memory-add.md → memory/add.md} +4 -3
  44. package/.agent-src/commands/{memory-full.md → memory/load.md} +4 -3
  45. package/.agent-src/commands/{memory-promote.md → memory/promote.md} +4 -3
  46. package/.agent-src/commands/{propose-memory.md → memory/propose.md} +4 -3
  47. package/.agent-src/commands/memory.md +48 -0
  48. package/.agent-src/commands/{module-create.md → module/create.md} +4 -3
  49. package/.agent-src/commands/{module-explore.md → module/explore.md} +4 -3
  50. package/.agent-src/commands/module.md +44 -0
  51. package/.agent-src/commands/{optimize-agents.md → optimize/agents.md} +4 -8
  52. package/.agent-src/commands/{optimize-augmentignore.md → optimize/augmentignore.md} +4 -9
  53. package/.agent-src/commands/{optimize-rtk-filters.md → optimize/rtk.md} +4 -8
  54. package/.agent-src/commands/{optimize-skills.md → optimize/skills.md} +4 -8
  55. package/.agent-src/commands/optimize.md +4 -10
  56. package/.agent-src/commands/{override-create.md → override/create.md} +4 -3
  57. package/.agent-src/commands/{override-manage.md → override/manage.md} +4 -3
  58. package/.agent-src/commands/override.md +44 -0
  59. package/.agent-src/commands/{roadmap-create.md → roadmap/create.md} +4 -3
  60. package/.agent-src/commands/{roadmap-execute.md → roadmap/execute.md} +4 -3
  61. package/.agent-src/commands/roadmap.md +44 -0
  62. package/.agent-src/commands/{tests-create.md → tests/create.md} +4 -3
  63. package/.agent-src/commands/{tests-execute.md → tests/execute.md} +4 -3
  64. package/.agent-src/commands/tests.md +44 -0
  65. package/.agent-src/contexts/communication/rules-auto/artifact-engagement-recording-mechanics.md +72 -0
  66. package/.agent-src/contexts/communication/rules-auto/augment-portability-mechanics.md +79 -0
  67. package/.agent-src/contexts/communication/rules-auto/augment-source-of-truth-mechanics.md +98 -0
  68. package/.agent-src/contexts/communication/rules-auto/cli-output-handling-mechanics.md +87 -0
  69. package/.agent-src/contexts/communication/rules-auto/command-suggestion-policy-mechanics.md +62 -0
  70. package/.agent-src/contexts/communication/rules-auto/docs-sync-mechanics.md +78 -0
  71. package/.agent-src/contexts/communication/rules-auto/package-ci-checks-mechanics.md +85 -0
  72. package/.agent-src/contexts/communication/rules-auto/review-routing-awareness-mechanics.md +65 -0
  73. package/.agent-src/contexts/communication/rules-auto/roadmap-progress-sync-mechanics.md +78 -0
  74. package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +62 -0
  75. package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +55 -0
  76. package/.agent-src/contexts/communication/rules-auto/ui-audit-gate-mechanics.md +53 -0
  77. package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +77 -0
  78. package/.agent-src/contexts/judges/no-consolidate-rationale.md +102 -0
  79. package/.agent-src/contexts/judges/persona-voice-rubric.md +140 -0
  80. package/.agent-src/rules/artifact-engagement-recording.md +13 -69
  81. package/.agent-src/rules/ask-when-uncertain.md +27 -42
  82. package/.agent-src/rules/augment-portability.md +15 -61
  83. package/.agent-src/rules/augment-source-of-truth.md +27 -93
  84. package/.agent-src/rules/cli-output-handling.md +10 -76
  85. package/.agent-src/rules/command-suggestion-policy.md +18 -59
  86. package/.agent-src/rules/commit-conventions.md +17 -14
  87. package/.agent-src/rules/direct-answers.md +34 -49
  88. package/.agent-src/rules/docker-commands.md +5 -5
  89. package/.agent-src/rules/docs-sync.md +15 -69
  90. package/.agent-src/rules/language-and-tone.md +48 -72
  91. package/.agent-src/rules/missing-tool-handling.md +28 -22
  92. package/.agent-src/rules/no-cheap-questions.md +45 -52
  93. package/.agent-src/rules/no-roadmap-references.md +73 -0
  94. package/.agent-src/rules/package-ci-checks.md +21 -61
  95. package/.agent-src/rules/preservation-guard.md +64 -29
  96. package/.agent-src/rules/review-routing-awareness.md +24 -43
  97. package/.agent-src/rules/roadmap-progress-sync.md +10 -71
  98. package/.agent-src/rules/security-sensitive-stop.md +8 -8
  99. package/.agent-src/rules/skill-quality.md +16 -48
  100. package/.agent-src/rules/slash-command-routing-policy.md +7 -4
  101. package/.agent-src/rules/think-before-action.md +52 -42
  102. package/.agent-src/rules/tool-safety.md +19 -16
  103. package/.agent-src/rules/ui-audit-gate.md +24 -38
  104. package/.agent-src/rules/user-interaction.md +13 -68
  105. package/.agent-src/skills/ai-council/SKILL.md +2 -0
  106. package/.agent-src/skills/api-testing/SKILL.md +1 -1
  107. package/.agent-src/skills/check-refs/SKILL.md +59 -40
  108. package/.agent-src/skills/conventional-commits-writing/SKILL.md +86 -28
  109. package/.agent-src/skills/copilot-agents-optimization/SKILL.md +5 -5
  110. package/.agent-src/skills/developer-like-execution/SKILL.md +4 -4
  111. package/.agent-src/skills/finishing-a-development-branch/SKILL.md +101 -65
  112. package/.agent-src/skills/flux/SKILL.md +30 -10
  113. package/.agent-src/skills/github-ci/SKILL.md +2 -2
  114. package/.agent-src/skills/judge-code-quality/SKILL.md +7 -8
  115. package/.agent-src/skills/judge-security-auditor/SKILL.md +4 -5
  116. package/.agent-src/skills/judge-test-coverage/SKILL.md +3 -4
  117. package/.agent-src/skills/lint-skills/SKILL.md +57 -39
  118. package/.agent-src/skills/md-language-check/SKILL.md +61 -39
  119. package/.agent-src/skills/override-management/SKILL.md +5 -5
  120. package/.agent-src/skills/quality-tools/SKILL.md +2 -2
  121. package/.agent-src/skills/react-shadcn-ui/SKILL.md +116 -43
  122. package/.agent-src/skills/readme-reviewer/SKILL.md +30 -29
  123. package/.agent-src/skills/readme-writing/SKILL.md +78 -53
  124. package/.agent-src/skills/readme-writing-package/SKILL.md +50 -47
  125. package/.agent-src/skills/receiving-code-review/SKILL.md +52 -47
  126. package/.agent-src/skills/refine-prompt/SKILL.md +0 -1
  127. package/.agent-src/skills/requesting-code-review/SKILL.md +35 -30
  128. package/.agent-src/skills/security/SKILL.md +7 -2
  129. package/.agent-src/skills/security-audit/SKILL.md +7 -3
  130. package/.agent-src/skills/systematic-debugging/SKILL.md +68 -60
  131. package/.agent-src/skills/test-driven-development/SKILL.md +59 -57
  132. package/.agent-src/skills/test-performance/SKILL.md +0 -1
  133. package/.agent-src/skills/traefik/SKILL.md +4 -4
  134. package/.agent-src/skills/verify-completion-evidence/SKILL.md +28 -26
  135. package/.claude-plugin/marketplace.json +22 -11
  136. package/AGENTS.md +2 -2
  137. package/CHANGELOG.md +90 -1
  138. package/README.md +18 -17
  139. package/docs/architecture.md +4 -6
  140. package/docs/catalog.md +67 -39
  141. package/docs/contracts/STABILITY.md +13 -7
  142. package/docs/contracts/adr-chat-history-split.md +1 -3
  143. package/docs/contracts/adr-command-suggestion.md +0 -2
  144. package/docs/contracts/adr-implement-ticket-runtime.md +1 -2
  145. package/docs/contracts/adr-product-ui-track.md +3 -6
  146. package/docs/contracts/adr-prompt-driven-execution.md +3 -4
  147. package/docs/contracts/agent-memory-contract.md +6 -11
  148. package/docs/contracts/artifact-engagement-flow.md +6 -9
  149. package/docs/contracts/command-clusters.md +56 -46
  150. package/docs/contracts/command-suggestion-flow.md +1 -3
  151. package/docs/contracts/context-paths.md +99 -0
  152. package/docs/contracts/file-ownership-matrix.json +6722 -0
  153. package/docs/contracts/file-ownership-matrix.md +134 -0
  154. package/docs/contracts/implement-ticket-flow.md +6 -9
  155. package/docs/contracts/linear-ai-rules-inclusion.md +0 -1
  156. package/docs/contracts/linear-ai-three-layers.md +0 -2
  157. package/docs/contracts/load-context-budget-model.md +178 -0
  158. package/docs/contracts/load-context-schema.md +1 -3
  159. package/docs/contracts/rule-interactions.md +0 -1
  160. package/docs/contracts/rule-priority-hierarchy.md +1 -1
  161. package/docs/contracts/ui-track-flow.md +7 -17
  162. package/docs/customization.md +2 -0
  163. package/docs/getting-started.md +5 -4
  164. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +100 -0
  165. package/package.json +1 -1
  166. package/scripts/_one_off_phase4_dispatch_latency.py +108 -0
  167. package/scripts/_one_off_phase6_trigger_jaccard.py +92 -0
  168. package/scripts/_phase2_shim_helper.py +109 -0
  169. package/scripts/agent-config +10 -0
  170. package/scripts/ai_council/_one_off_2a4_acceptance.py +208 -0
  171. package/scripts/ai_council/_one_off_context_layer_v1_estimate.py +67 -0
  172. package/scripts/ai_council/_one_off_context_layer_v1_review.py +292 -0
  173. package/scripts/ai_council/_one_off_followups_review.py +259 -0
  174. package/scripts/ai_council/_one_off_nondestructive_inline_audit.py +209 -0
  175. package/scripts/ai_council/_one_off_phase_2a_budget_rebalance.py +257 -0
  176. package/scripts/ai_council/_one_off_phase_2a_post_revert.py +197 -0
  177. package/scripts/ai_council/_one_off_rule_hardening_v1.py +251 -0
  178. package/scripts/ai_council/_one_off_structural_open_questions.py +232 -0
  179. package/scripts/ai_council/_one_off_structural_optimization.py +144 -0
  180. package/scripts/ai_council/_one_off_structural_v3_gaps.py +252 -0
  181. package/scripts/ai_council/_one_off_structural_v3_review.py +240 -0
  182. package/scripts/check_always_budget.py +363 -45
  183. package/scripts/check_cluster_patterns.py +159 -0
  184. package/scripts/check_command_count_messaging.py +14 -7
  185. package/scripts/check_context_paths.py +201 -0
  186. package/scripts/check_no_roadmap_refs.py +155 -0
  187. package/scripts/check_phase_coupling.py +148 -0
  188. package/scripts/check_portability.py +2 -0
  189. package/scripts/check_references.py +29 -2
  190. package/scripts/check_safety_floor_untouched.py +125 -0
  191. package/scripts/command_suggester/loader.py +4 -1
  192. package/scripts/compress.py +59 -13
  193. package/scripts/generate_index.py +6 -2
  194. package/scripts/generate_ownership_matrix.py +323 -0
  195. package/scripts/hooks/augment-roadmap-progress.sh +57 -0
  196. package/scripts/install.py +49 -28
  197. package/scripts/lint_no_new_atomic_commands.py +12 -11
  198. package/scripts/requirements-evals.txt +1 -0
  199. package/scripts/roadmap_progress_hook.py +159 -0
  200. package/scripts/schemas/command.schema.json +4 -3
  201. package/scripts/skill_linter.py +1 -0
  202. package/scripts/sync_agent_settings.py +25 -2
  203. package/scripts/update_counts.py +7 -0
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env python3
2
+ """Context-file path & orphan checker.
3
+
4
+ Validates that every `*.md` under `.agent-src.uncompressed/contexts/`:
5
+
6
+ 1. Lives in a locked sub-tree (or is one of six grandfathered root files).
7
+ 2. Does not collide on basename with another context file in another sub-tree.
8
+ 3. Is referenced by at least one rule, skill, command, or other context
9
+ (via `load_context:` frontmatter or a markdown body path mention).
10
+
11
+ Contract: docs/contracts/context-paths.md
12
+ Roadmap: road-to-structural-optimization.md § 0.6
13
+
14
+ Exit codes: 0 = clean, 1 = violations, 3 = internal error.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import json
20
+ import sys
21
+ from dataclasses import asdict, dataclass
22
+ from pathlib import Path
23
+
24
+ ROOT = Path(__file__).resolve().parent.parent
25
+ CONTEXTS_ROOT = ROOT / ".agent-src.uncompressed" / "contexts"
26
+
27
+ # Sub-trees that may contain context files. Update in lock-step with
28
+ # docs/contracts/context-paths.md whenever a roadmap revision adds one.
29
+ LOCKED_SUBTREES = (
30
+ "communication/rules-always",
31
+ "communication/rules-auto",
32
+ "judges",
33
+ "analysis",
34
+ "skills",
35
+ "chat-history",
36
+ "execution",
37
+ "authority",
38
+ )
39
+
40
+ # Files allowed to remain at the contexts root. Anything else at the root
41
+ # fails the path check. New context files MUST live in a sub-tree.
42
+ GRANDFATHERED_ROOT_FILES = frozenset({
43
+ "augment-infrastructure.md",
44
+ "documentation-hierarchy.md",
45
+ "model-recommendations.md",
46
+ "override-system.md",
47
+ "skills-and-commands.md",
48
+ "subagent-configuration.md",
49
+ })
50
+
51
+ # Directories whose content we scan for references to a context file.
52
+ # `agents/roadmaps` is included because in-flight roadmap docs are
53
+ # legitimate referrers during multi-phase rollouts — a context can land
54
+ # in phase N while its first rule/skill referrer lands in phase N+k.
55
+ # Without this scan dir, every newly-introduced context would orphan
56
+ # until the consuming artefact lands, blocking phase-by-phase commits.
57
+ REFERENCE_SCAN_DIRS = (
58
+ ".agent-src.uncompressed/rules",
59
+ ".agent-src.uncompressed/skills",
60
+ ".agent-src.uncompressed/commands",
61
+ ".agent-src.uncompressed/contexts",
62
+ "agents/roadmaps",
63
+ )
64
+
65
+
66
+ @dataclass
67
+ class Violation:
68
+ file: str
69
+ kind: str # "out-of-tree" | "root-not-grandfathered" | "collision" | "orphan"
70
+ detail: str
71
+
72
+
73
+ def _collect_contexts(root: Path) -> list[Path]:
74
+ if not (root / CONTEXTS_ROOT.relative_to(ROOT)).exists():
75
+ return []
76
+ return sorted((root / CONTEXTS_ROOT.relative_to(ROOT)).rglob("*.md"))
77
+
78
+
79
+ def _check_path(ctx: Path, contexts_root: Path) -> Violation | None:
80
+ rel = ctx.relative_to(contexts_root)
81
+ parts = rel.parts
82
+ if len(parts) == 1:
83
+ if parts[0] not in GRANDFATHERED_ROOT_FILES:
84
+ return Violation(
85
+ file=str(ctx),
86
+ kind="root-not-grandfathered",
87
+ detail=(f"new file at contexts/ root — must live in one of "
88
+ f"{sorted(LOCKED_SUBTREES)} or be added to "
89
+ "GRANDFATHERED_ROOT_FILES via a roadmap revision"),
90
+ )
91
+ return None
92
+ subtree = "/".join(parts[:-1])
93
+ # Allow nested matches: e.g. communication/rules-always/foo/bar.md still
94
+ # lives under "communication/rules-always". Direct prefix match suffices.
95
+ for allowed in LOCKED_SUBTREES:
96
+ if subtree == allowed or subtree.startswith(allowed + "/"):
97
+ return None
98
+ return Violation(
99
+ file=str(ctx),
100
+ kind="out-of-tree",
101
+ detail=(f"sub-tree '{subtree}' is not in LOCKED_SUBTREES — see "
102
+ "docs/contracts/context-paths.md to add a new sub-tree"),
103
+ )
104
+
105
+
106
+ def _check_collisions(contexts: list[Path], contexts_root: Path) -> list[Violation]:
107
+ by_name: dict[str, list[Path]] = {}
108
+ for ctx in contexts:
109
+ by_name.setdefault(ctx.name, []).append(ctx)
110
+ out: list[Violation] = []
111
+ for name, paths in by_name.items():
112
+ if len(paths) <= 1:
113
+ continue
114
+ rels = sorted(str(p.relative_to(contexts_root)) for p in paths)
115
+ for p in paths:
116
+ out.append(Violation(
117
+ file=str(p),
118
+ kind="collision",
119
+ detail=f"basename '{name}' shared with: {', '.join(r for r in rels if r != str(p.relative_to(contexts_root)))}",
120
+ ))
121
+ return out
122
+
123
+
124
+ def _build_reference_corpus(root: Path) -> str:
125
+ chunks: list[str] = []
126
+ for d in REFERENCE_SCAN_DIRS:
127
+ base = root / d
128
+ if not base.exists():
129
+ continue
130
+ for f in base.rglob("*.md"):
131
+ try:
132
+ chunks.append(f.read_text(encoding="utf-8"))
133
+ except OSError:
134
+ continue
135
+ return "\n".join(chunks)
136
+
137
+
138
+ def _check_orphans(contexts: list[Path], corpus: str, root: Path) -> list[Violation]:
139
+ out: list[Violation] = []
140
+ for ctx in contexts:
141
+ rel_src = str(ctx.relative_to(root)) # .agent-src.uncompressed/contexts/...
142
+ rel_short = rel_src.split("contexts/", 1)[-1] # judges/persona-voice-rubric.md
143
+ candidates = (rel_src, f"contexts/{rel_short}", rel_short)
144
+ # Exclude self-references: drop this file's own content from the
145
+ # corpus check by reading the file and subtracting its substring.
146
+ try:
147
+ own_text = ctx.read_text(encoding="utf-8")
148
+ except OSError:
149
+ own_text = ""
150
+ external_corpus = corpus.replace(own_text, "") if own_text else corpus
151
+ if not any(c in external_corpus for c in candidates):
152
+ out.append(Violation(
153
+ file=str(ctx),
154
+ kind="orphan",
155
+ detail="not referenced by any rule, skill, command, or other context",
156
+ ))
157
+ return out
158
+
159
+
160
+ def scan(root: Path) -> list[Violation]:
161
+ contexts_root = root / CONTEXTS_ROOT.relative_to(ROOT)
162
+ contexts = _collect_contexts(root)
163
+ violations: list[Violation] = []
164
+ for ctx in contexts:
165
+ v = _check_path(ctx, contexts_root)
166
+ if v:
167
+ violations.append(v)
168
+ violations.extend(_check_collisions(contexts, contexts_root))
169
+ corpus = _build_reference_corpus(root)
170
+ violations.extend(_check_orphans(contexts, corpus, root))
171
+ return violations
172
+
173
+
174
+ def format_text(violations: list[Violation]) -> str:
175
+ if not violations:
176
+ return "✅ No context-path violations."
177
+ lines = [f"❌ Found {len(violations)} context-path violation(s):\n"]
178
+ for v in violations:
179
+ lines.append(f" 🔴 [{v.kind}] {v.file}\n {v.detail}")
180
+ return "\n".join(lines)
181
+
182
+
183
+ def main() -> int:
184
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
185
+ parser.add_argument("--format", choices=["text", "json"], default="text")
186
+ parser.add_argument("--root", type=Path, default=ROOT)
187
+ args = parser.parse_args()
188
+ try:
189
+ violations = scan(args.root)
190
+ except Exception as e: # pragma: no cover
191
+ print(f"Internal error: {e}", file=sys.stderr)
192
+ return 3
193
+ if args.format == "json":
194
+ print(json.dumps([asdict(v) for v in violations], indent=2))
195
+ else:
196
+ print(format_text(violations))
197
+ return 1 if violations else 0
198
+
199
+
200
+ if __name__ == "__main__":
201
+ sys.exit(main())
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env python3
2
+ """No-roadmap-references checker.
3
+
4
+ Stable artifacts (rules, skills, commands, contexts, guidelines, AGENTS.md,
5
+ README, copilot-instructions) must NOT cite a specific roadmap file in
6
+ `agents/roadmaps/`. Roadmap files are transient — archived, skipped, or
7
+ deleted as work completes — and stable artifacts citing them rot.
8
+
9
+ Allowed: directory mentions (`agents/roadmaps/`, `agents/roadmaps/archive/`,
10
+ `agents/roadmaps/skipped/`). Forbidden: specific `*.md` files inside those
11
+ directories.
12
+
13
+ Contract: .agent-src.uncompressed/rules/no-roadmap-references.md
14
+
15
+ Exit codes: 0 = clean, 1 = violations, 3 = internal error.
16
+ """
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import json
21
+ import re
22
+ import sys
23
+ from dataclasses import asdict, dataclass
24
+ from pathlib import Path
25
+
26
+ ROOT = Path(__file__).resolve().parent.parent
27
+
28
+ # Stable artefact trees — every `*.md` below MUST be free of roadmap-file
29
+ # citations. Directory mentions stay allowed (the regex below excludes them).
30
+ STABLE_TREES = (
31
+ ".agent-src.uncompressed/rules",
32
+ ".agent-src.uncompressed/skills",
33
+ ".agent-src.uncompressed/commands",
34
+ ".agent-src.uncompressed/contexts",
35
+ ".agent-src.uncompressed/templates",
36
+ ".agent-src.uncompressed/personas",
37
+ "agents/contexts",
38
+ "docs/guidelines",
39
+ "docs/contracts",
40
+ )
41
+
42
+ # Stable single-file artefacts at well-known paths.
43
+ STABLE_FILES = (
44
+ "AGENTS.md",
45
+ "README.md",
46
+ "copilot-instructions.md",
47
+ "docs/architecture.md",
48
+ "docs/customization.md",
49
+ "docs/getting-started.md",
50
+ "docs/catalog.md",
51
+ )
52
+
53
+ # Roadmap-file pattern: any `*.md` file under `agents/roadmaps/` at any
54
+ # depth (including `archive/`, `skipped/`, and nested topical subfolders
55
+ # like `agent-memory/`). Directory-only mentions (`agents/roadmaps/`
56
+ # with trailing slash, no filename) and placeholder mentions like
57
+ # `agents/roadmaps/<file>.md` (angle-bracket placeholder) do NOT match.
58
+ ROADMAP_FILE_RE = re.compile(
59
+ r"agents/roadmaps/(?:[a-z0-9][a-z0-9_-]*/)*[a-z0-9][a-z0-9_-]*\.md",
60
+ re.IGNORECASE,
61
+ )
62
+
63
+ # Files that may legitimately quote forbidden patterns inside backticks for
64
+ # documentation purposes — the rule itself, the companion CI script docs,
65
+ # and the contract doc that names the rule.
66
+ SELF_DOCUMENTING_ALLOWLIST = frozenset({
67
+ ".agent-src.uncompressed/rules/no-roadmap-references.md",
68
+ "docs/guidelines/agent-infra/no-roadmap-references.md",
69
+ })
70
+
71
+
72
+ @dataclass
73
+ class Violation:
74
+ file: str
75
+ line: int
76
+ match: str
77
+
78
+
79
+ def _scan_file(path: Path, root: Path) -> list[Violation]:
80
+ rel = str(path.relative_to(root))
81
+ if rel in SELF_DOCUMENTING_ALLOWLIST:
82
+ return []
83
+ try:
84
+ text = path.read_text(encoding="utf-8")
85
+ except OSError:
86
+ return []
87
+ out: list[Violation] = []
88
+ in_fence = False
89
+ for n, line in enumerate(text.splitlines(), start=1):
90
+ # Skip fenced code blocks — path listings inside ``` are functional
91
+ # constants (command contracts, runtime checks), not link rot.
92
+ stripped = line.lstrip()
93
+ if stripped.startswith("```"):
94
+ in_fence = not in_fence
95
+ continue
96
+ if in_fence:
97
+ continue
98
+ for m in ROADMAP_FILE_RE.finditer(line):
99
+ out.append(Violation(file=rel, line=n, match=m.group(0)))
100
+ return out
101
+
102
+
103
+ def _collect_targets(root: Path) -> list[Path]:
104
+ targets: list[Path] = []
105
+ for d in STABLE_TREES:
106
+ base = root / d
107
+ if not base.exists():
108
+ continue
109
+ targets.extend(sorted(base.rglob("*.md")))
110
+ for f in STABLE_FILES:
111
+ p = root / f
112
+ if p.exists():
113
+ targets.append(p)
114
+ return targets
115
+
116
+
117
+ def scan(root: Path) -> list[Violation]:
118
+ out: list[Violation] = []
119
+ for path in _collect_targets(root):
120
+ out.extend(_scan_file(path, root))
121
+ return out
122
+
123
+
124
+ def format_text(violations: list[Violation]) -> str:
125
+ if not violations:
126
+ return "✅ No roadmap-file references in stable artifacts."
127
+ lines = [f"❌ Found {len(violations)} roadmap reference(s) in stable artifacts:\n"]
128
+ for v in violations:
129
+ lines.append(f" 🔴 {v.file}:{v.line} → {v.match}")
130
+ lines.append(
131
+ "\nPromote the durable conclusion to agents/contexts/ and cite that "
132
+ "instead. See .agent-src.uncompressed/rules/no-roadmap-references.md."
133
+ )
134
+ return "\n".join(lines)
135
+
136
+
137
+ def main() -> int:
138
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
139
+ parser.add_argument("--format", choices=["text", "json"], default="text")
140
+ parser.add_argument("--root", type=Path, default=ROOT)
141
+ args = parser.parse_args()
142
+ try:
143
+ violations = scan(args.root)
144
+ except Exception as e: # pragma: no cover
145
+ print(f"Internal error: {e}", file=sys.stderr)
146
+ return 3
147
+ if args.format == "json":
148
+ print(json.dumps([asdict(v) for v in violations], indent=2))
149
+ else:
150
+ print(format_text(violations))
151
+ return 1 if violations else 0
152
+
153
+
154
+ if __name__ == "__main__":
155
+ sys.exit(main())
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env python3
2
+ """Phase 6 → Phase 2B coupling guard (Phase 0.3.3).
3
+
4
+ Re-runs the audit recorded in `agents/roadmaps/phase6-2b-coupling.md`
5
+ on every CI run. Fails the build if any of the 13 Phase-2B target
6
+ rules introduces a reference to one of the three Phase-6-owned rules
7
+ (`chat-history-cadence`, `chat-history-ownership`,
8
+ `chat-history-visibility`) — by rule name, `load_context:` entry, or
9
+ body link / cite.
10
+
11
+ Excluded from the coupling probe (separate infrastructure, not
12
+ reshaped by Phase 6):
13
+
14
+ - The CLI dispatcher subcommand `./agent-config chat-history:hook`
15
+ and any other `chat-history:*` colon-suffix command surface.
16
+
17
+ Exit codes: 0 = decoupling intact, 1 = coupling detected,
18
+ 3 = internal error (target rule missing, unreadable file).
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import argparse
24
+ import re
25
+ import sys
26
+ from pathlib import Path
27
+
28
+ REPO_ROOT = Path(__file__).resolve().parents[1]
29
+ SRC_RULES = REPO_ROOT / ".agent-src.uncompressed" / "rules"
30
+ COMP_RULES = REPO_ROOT / ".agent-src" / "rules"
31
+
32
+ # Phase 2B priority list — see road-to-structural-optimization.md § Phase 2 → 2B.
33
+ TARGET_RULES: tuple[str, ...] = (
34
+ "roadmap-progress-sync",
35
+ "user-interaction",
36
+ "augment-source-of-truth",
37
+ "command-suggestion-policy",
38
+ "artifact-engagement-recording",
39
+ "review-routing-awareness",
40
+ "autonomous-execution",
41
+ "docs-sync",
42
+ "cli-output-handling",
43
+ "augment-portability",
44
+ "ui-audit-gate",
45
+ "skill-quality",
46
+ "package-ci-checks",
47
+ )
48
+
49
+ # Phase 6 owns these three rule names. Match must be a *rule reference*,
50
+ # not a *dispatcher reference* (chat-history:hook etc).
51
+ PHASE6_RULES: tuple[str, ...] = (
52
+ "chat-history-cadence",
53
+ "chat-history-ownership",
54
+ "chat-history-visibility",
55
+ )
56
+
57
+ # Rule-reference pattern: rule name not immediately followed by `:` (which
58
+ # would mark it as a CLI subcommand like `chat-history:hook`). Allows
59
+ # trailing word-boundary characters typical of Markdown / YAML contexts.
60
+ _RULE_REF_RE = re.compile(
61
+ r"\bchat-history-(?:cadence|ownership|visibility)\b(?!:)"
62
+ )
63
+
64
+
65
+ def _scan(file: Path) -> list[tuple[int, str]]:
66
+ """Return [(line_no, line)] of rule-reference matches in `file`."""
67
+ if not file.is_file():
68
+ return []
69
+ hits: list[tuple[int, str]] = []
70
+ for i, line in enumerate(file.read_text(encoding="utf-8").splitlines(), 1):
71
+ if _RULE_REF_RE.search(line):
72
+ hits.append((i, line.strip()))
73
+ return hits
74
+
75
+
76
+ def _check_surface(label: str, base: Path) -> tuple[int, list[str]]:
77
+ """Scan `base` for all 13 Phase-2B targets; return (#hits, formatted lines)."""
78
+ if not base.is_dir():
79
+ return 0, [f"❌ {label} dir missing: {base}"]
80
+ out: list[str] = []
81
+ total = 0
82
+ for rule in TARGET_RULES:
83
+ path = base / f"{rule}.md"
84
+ if not path.is_file():
85
+ return 0, [f"❌ target rule missing: {path}"]
86
+ hits = _scan(path)
87
+ if not hits:
88
+ continue
89
+ total += len(hits)
90
+ for line_no, line in hits:
91
+ out.append(f" {label}/{rule}.md:{line_no} {line}")
92
+ return total, out
93
+
94
+
95
+ def main() -> int:
96
+ parser = argparse.ArgumentParser(description=__doc__)
97
+ parser.add_argument(
98
+ "--quiet",
99
+ action="store_true",
100
+ help="suppress the per-rule breakdown when no coupling found",
101
+ )
102
+ args = parser.parse_args()
103
+
104
+ hits_src, lines_src = _check_surface("uncompressed", SRC_RULES)
105
+ if any(line.startswith("❌") for line in lines_src):
106
+ for line in lines_src:
107
+ print(line, file=sys.stderr)
108
+ return 3
109
+ hits_comp, lines_comp = _check_surface("compressed", COMP_RULES)
110
+ if any(line.startswith("❌") for line in lines_comp):
111
+ for line in lines_comp:
112
+ print(line, file=sys.stderr)
113
+ return 3
114
+
115
+ total = hits_src + hits_comp
116
+
117
+ if total == 0:
118
+ if not args.quiet:
119
+ print(
120
+ f"✅ Phase 6 → 2B decoupling intact: 0 rule-references "
121
+ f"across {len(TARGET_RULES)} Phase-2B targets "
122
+ f"(uncompressed + compressed surfaces)."
123
+ )
124
+ print(
125
+ " probe: rule names, load_context: entries, body "
126
+ "link/cite — dispatcher subcommand chat-history:hook "
127
+ "excluded by design."
128
+ )
129
+ return 0
130
+
131
+ print(
132
+ f"❌ Phase 6 → 2B coupling detected: {total} rule-reference(s) "
133
+ f"across Phase-2B targets:"
134
+ )
135
+ for line in lines_src + lines_comp:
136
+ print(line)
137
+ print(
138
+ "\n Action: see agents/roadmaps/phase6-2b-coupling.md. "
139
+ "Either drop the new reference, migrate it to the dispatcher "
140
+ "(chat-history:hook), or trigger the >0-hits branch in 0.3.2 "
141
+ "(Phase 6 ships call-signature contract before Phase 2B "
142
+ "touches the coupled rule)."
143
+ )
144
+ return 1
145
+
146
+
147
+ if __name__ == "__main__":
148
+ sys.exit(main())
@@ -315,6 +315,7 @@ _TASK_FENCE_RE = re.compile(r"^\s*task\s+([a-z][a-z0-9:_-]*)\b")
315
315
  # by the task-invocation detector (but still scanned for layer 1 + 2).
316
316
  _TASK_DETECTOR_SKIP = (
317
317
  "rules/augment-portability.md",
318
+ "contexts/communication/rules-auto/augment-portability-mechanics.md",
318
319
  )
319
320
 
320
321
 
@@ -422,6 +423,7 @@ _CLI_INVOCATION_MAP: list[tuple[re.Pattern, str]] = [
422
423
  # own help, the portability rule that defines the mapping).
423
424
  _CLI_DETECTOR_SKIP = (
424
425
  "rules/augment-portability.md",
426
+ "contexts/communication/rules-auto/augment-portability-mechanics.md",
425
427
  )
426
428
 
427
429
 
@@ -101,6 +101,14 @@ EXAMPLE_PATH_PATTERNS = [
101
101
  re.compile(r"skills/[\w-]+/SKILL\.md"), # example skill paths in commands
102
102
  re.compile(r"\{"), # template placeholders like {module}
103
103
  re.compile(r"\.compression-hashes\.json"), # JSON file, not .md
104
+ # Forward references inside in-flight planning docs (road-to-
105
+ # structural-optimization.md and its companion spike protocols).
106
+ # Each pattern below is removed once the matching phase lands.
107
+ re.compile(r"structural-optimization-3a-spike\.md"), # 3a.0.2
108
+ re.compile(r"contexts/judges/no-consolidate-rationale"), # 3a.0.2 abort
109
+ re.compile(r"contexts/judges/judge-shared-procedure"), # 3a.1
110
+ re.compile(r"contexts/analysis/project-analysis-core-procedure"), # 3b.1
111
+ re.compile(r"agents/roadmaps/phase6-non-overlap-evidence"), # 6.1 conditional
104
112
  ]
105
113
 
106
114
 
@@ -118,8 +126,20 @@ def collect_artifacts(root: Path) -> dict[str, set[str]]:
118
126
  arts["skills"].add(d.name)
119
127
  for f in (augment / "rules").glob("*.md") if (augment / "rules").exists() else []:
120
128
  arts["rules"].add(f.stem)
121
- for f in (augment / "commands").glob("*.md") if (augment / "commands").exists() else []:
122
- arts["commands"].add(f.stem)
129
+ cmd_dir = augment / "commands"
130
+ if cmd_dir.exists():
131
+ for f in cmd_dir.rglob("*.md"):
132
+ if f.name == "AGENTS.md":
133
+ continue
134
+ # Top-level: bare stem ("commit"). Nested: cluster-sub ("council-default")
135
+ # AND the cluster:sub form, since references may use either.
136
+ rel = f.relative_to(cmd_dir).with_suffix("")
137
+ parts = rel.parts
138
+ if len(parts) == 1:
139
+ arts["commands"].add(parts[0])
140
+ else:
141
+ arts["commands"].add("-".join(parts))
142
+ arts["commands"].add(":".join(parts))
123
143
  gdir = augment / "guidelines"
124
144
  if gdir.exists():
125
145
  for f in gdir.rglob("*.md"):
@@ -225,6 +245,13 @@ def check_file(filepath: Path, artifacts: dict[str, set[str]], root: Path) -> Li
225
245
  if any(p.search(raw_ref) for p in EXAMPLE_PATH_PATTERNS):
226
246
  continue
227
247
 
248
+ # Skip references into directories already excluded from scanning
249
+ # (gitignored audit trails, archived roadmaps). Files there are
250
+ # not committed, so existence checks would always fail in CI.
251
+ if any(raw_ref.startswith(skip + "/") or raw_ref == skip
252
+ for skip in SKIP_DIRS):
253
+ continue
254
+
228
255
  resolved = False
229
256
  # Try raw ref as-is from root (covers .agent-src/..., agents/..., etc.)
230
257
  if (root / raw_ref).exists():
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env python3
2
+ """Safety-floor exclusion linter (Phase 2A.0 of road-to-structural-optimization).
3
+
4
+ Per Q3=A locked decision (council Round 3, 2026-05-03), the four
5
+ safety-floor always-rules are out of scope for Phase 2A slimming:
6
+
7
+ - non-destructive-by-default
8
+ - commit-policy
9
+ - scope-control
10
+ - verify-before-complete
11
+
12
+ This linter compares HEAD against a baseline ref (default: ``main``)
13
+ and fails CI if any of those four rule files were modified by the
14
+ working branch.
15
+
16
+ Lift via the two-gate rollback documented in
17
+ ``agents/roadmaps/road-to-structural-optimization.md`` § Phase 2A
18
+ Abort/rollback.
19
+
20
+ Exit codes: 0 = clean (or skipped — see ``--skip-if-no-baseline``),
21
+ 1 = safety-floor file modified, 3 = internal error.
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import argparse
26
+ import subprocess
27
+ import sys
28
+ from pathlib import Path
29
+
30
+ REPO_ROOT = Path(__file__).resolve().parent.parent
31
+ RULES_DIR_REL = ".agent-src.uncompressed/rules"
32
+ SAFETY_FLOOR = (
33
+ "non-destructive-by-default.md",
34
+ "commit-policy.md",
35
+ "scope-control.md",
36
+ "verify-before-complete.md",
37
+ )
38
+
39
+
40
+ def _run_git(args: list[str]) -> tuple[int, str]:
41
+ proc = subprocess.run(
42
+ ["git", *args],
43
+ cwd=REPO_ROOT,
44
+ capture_output=True,
45
+ text=True,
46
+ check=False,
47
+ )
48
+ return proc.returncode, (proc.stdout or "") + (proc.stderr or "")
49
+
50
+
51
+ def _baseline_exists(ref: str) -> bool:
52
+ code, _ = _run_git(["rev-parse", "--verify", "--quiet", ref])
53
+ return code == 0
54
+
55
+
56
+ def _changed_files(baseline: str) -> list[str]:
57
+ code, output = _run_git(["diff", "--name-only", f"{baseline}...HEAD"])
58
+ if code != 0:
59
+ raise RuntimeError(f"git diff failed: {output}")
60
+ return [line.strip() for line in output.splitlines() if line.strip()]
61
+
62
+
63
+ def main() -> int:
64
+ parser = argparse.ArgumentParser(description=__doc__)
65
+ parser.add_argument(
66
+ "--baseline",
67
+ default="origin/main",
68
+ help="Baseline ref (default: origin/main)",
69
+ )
70
+ parser.add_argument(
71
+ "--skip-if-no-baseline",
72
+ action="store_true",
73
+ help="Exit 0 silently if baseline ref does not exist (local dev)",
74
+ )
75
+ args = parser.parse_args()
76
+
77
+ if not _baseline_exists(args.baseline):
78
+ if args.skip_if_no_baseline:
79
+ print(f"ℹ️ baseline {args.baseline} not found — skipped")
80
+ return 0
81
+ # Fallback: try plain `main`
82
+ if _baseline_exists("main"):
83
+ args.baseline = "main"
84
+ else:
85
+ print(
86
+ f"❌ baseline {args.baseline} (and `main`) not found. "
87
+ "Pass --skip-if-no-baseline to silence in local dev.",
88
+ file=sys.stderr,
89
+ )
90
+ return 3
91
+
92
+ try:
93
+ changed = _changed_files(args.baseline)
94
+ except RuntimeError as exc:
95
+ print(f"❌ {exc}", file=sys.stderr)
96
+ return 3
97
+
98
+ floor_paths = {f"{RULES_DIR_REL}/{name}" for name in SAFETY_FLOOR}
99
+ breaches = sorted(p for p in changed if p in floor_paths)
100
+
101
+ if breaches:
102
+ print(
103
+ "❌ Safety-floor rule(s) modified — Phase 2A is not allowed to "
104
+ "touch these (Q3=A locked decision):",
105
+ file=sys.stderr,
106
+ )
107
+ for path in breaches:
108
+ print(f" {path}", file=sys.stderr)
109
+ print(
110
+ "\n Lift via the two-gate rollback documented in "
111
+ "agents/roadmaps/road-to-structural-optimization.md "
112
+ "§ Phase 2A Abort/rollback.",
113
+ file=sys.stderr,
114
+ )
115
+ return 1
116
+
117
+ print(
118
+ f"✅ Safety-floor untouched ({len(SAFETY_FLOOR)} rules guarded "
119
+ f"vs. {args.baseline})."
120
+ )
121
+ return 0
122
+
123
+
124
+ if __name__ == "__main__":
125
+ sys.exit(main())