@event4u/agent-config 8.9.0 → 8.11.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 (267) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +189 -0
  3. package/README.md +5 -5
  4. package/dist/agent-src/commands/challenge-me/vision.md +121 -32
  5. package/dist/agent-src/commands/challenge-me/with-docs.md +21 -8
  6. package/dist/agent-src/commands/commit/in-chunks.md +1 -1
  7. package/dist/agent-src/commands/commit.md +2 -1
  8. package/dist/agent-src/commands/cost/report.md +14 -3
  9. package/dist/agent-src/commands/feature/dev.md +4 -4
  10. package/dist/agent-src/commands/humanize.md +101 -0
  11. package/dist/agent-src/commands/jira-ticket.md +2 -2
  12. package/dist/agent-src/commands/memory/load.md +15 -0
  13. package/dist/agent-src/commands/optimize/project.md +12 -0
  14. package/dist/agent-src/commands/roadmap/create.md +12 -11
  15. package/dist/agent-src/commands/tests/create.md +29 -7
  16. package/dist/agent-src/commands/work.md +33 -0
  17. package/dist/agent-src/commands/worktree/cleanup.md +83 -0
  18. package/dist/agent-src/commands/worktree/create.md +64 -0
  19. package/dist/agent-src/commands/worktree/status.md +75 -0
  20. package/dist/agent-src/commands/worktree/verify.md +70 -0
  21. package/dist/agent-src/commands/worktree.md +85 -0
  22. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +66 -0
  23. package/dist/agent-src/contexts/execution/orchestration-benchmark-gate.md +1 -0
  24. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +91 -34
  25. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +14 -0
  26. package/dist/agent-src/contexts/execution/roadmap-writing-source-derived.md +65 -0
  27. package/dist/agent-src/contexts/execution/subagent-modes-detail.md +84 -0
  28. package/dist/agent-src/rules/autonomous-execution.md +7 -37
  29. package/dist/agent-src/rules/broken-access-control.md +7 -45
  30. package/dist/agent-src/rules/code-comment-discipline.md +148 -0
  31. package/dist/agent-src/rules/context-hygiene.md +14 -0
  32. package/dist/agent-src/rules/delegation-policy.md +10 -8
  33. package/dist/agent-src/rules/design-fidelity.md +44 -0
  34. package/dist/agent-src/rules/git-history-discipline.md +4 -79
  35. package/dist/agent-src/rules/legal-safety-floor.md +11 -55
  36. package/dist/agent-src/rules/roadmap-progress-sync.md +5 -56
  37. package/dist/agent-src/rules/spreadsheet-source-quality.md +61 -0
  38. package/dist/agent-src/rules/think-before-action.md +10 -0
  39. package/dist/agent-src/rules/verify-before-complete.md +12 -0
  40. package/dist/agent-src/skills/accessibility-auditor/evals/evals.json +15 -0
  41. package/dist/agent-src/skills/agent-docs-writing/SKILL.md +13 -12
  42. package/dist/agent-src/skills/ai-council/SKILL.md +19 -9
  43. package/dist/agent-src/skills/analysis-skill-router/evals/evals.json +13 -0
  44. package/dist/agent-src/skills/api-design/SKILL.md +7 -5
  45. package/dist/agent-src/skills/api-testing/SKILL.md +16 -2
  46. package/dist/agent-src/skills/authz-review/SKILL.md +59 -2
  47. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
  48. package/dist/agent-src/skills/bug-analyzer/SKILL.md +19 -16
  49. package/dist/agent-src/skills/character-consistency/evals/evals.json +16 -0
  50. package/dist/agent-src/skills/code-refactoring/SKILL.md +9 -9
  51. package/dist/agent-src/skills/command-routing/evals/evals.json +14 -0
  52. package/dist/agent-src/skills/content-funnel-design/SKILL.md +2 -0
  53. package/dist/agent-src/skills/dashboard-design/evals/evals.json +16 -0
  54. package/dist/agent-src/skills/data-flow-mapper/SKILL.md +1 -1
  55. package/dist/agent-src/skills/dcf-modeling/evals/evals.json +20 -0
  56. package/dist/agent-src/skills/decision-record/evals/evals.json +20 -0
  57. package/dist/agent-src/skills/defense-in-depth/SKILL.md +2 -2
  58. package/dist/agent-src/skills/dependency-upgrade/SKILL.md +7 -7
  59. package/dist/agent-src/skills/design-intelligence/SKILL.md +8 -8
  60. package/dist/agent-src/skills/design-intelligence/evals/evals.json +14 -0
  61. package/dist/agent-src/skills/design-review/SKILL.md +29 -2
  62. package/dist/agent-src/skills/design-system-capture/SKILL.md +21 -0
  63. package/dist/agent-src/skills/design-system-capture/evals/evals.json +15 -0
  64. package/dist/agent-src/skills/developer-like-execution/evals/evals.json +16 -0
  65. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +29 -0
  66. package/dist/agent-src/skills/doc-coauthoring/evals/evals.json +16 -0
  67. package/dist/agent-src/skills/editorial-calendar/SKILL.md +2 -0
  68. package/dist/agent-src/skills/editorial-calendar/evals/domain-truth.json +16 -0
  69. package/dist/agent-src/skills/editorial-calendar/evals/evals.json +16 -0
  70. package/dist/agent-src/skills/estimate-ticket/evals/evals.json +16 -0
  71. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -0
  72. package/dist/agent-src/skills/existing-ui-audit/evals/triggers.json +47 -0
  73. package/dist/agent-src/skills/fe-design/SKILL.md +43 -14
  74. package/dist/agent-src/skills/fe-design/evals/triggers.json +47 -0
  75. package/dist/agent-src/skills/forecasting/evals/evals.json +20 -0
  76. package/dist/agent-src/skills/fundraising-narrative/evals/domain-truth.json +16 -0
  77. package/dist/agent-src/skills/fundraising-narrative/evals/evals.json +16 -0
  78. package/dist/agent-src/skills/git-workflow/SKILL.md +86 -0
  79. package/dist/agent-src/skills/html-deck/SKILL.md +27 -0
  80. package/dist/agent-src/skills/humanizer/SKILL.md +147 -0
  81. package/dist/agent-src/skills/humanizer/data/patterns.md +190 -0
  82. package/dist/agent-src/skills/humanizer/evals/triggers.json +17 -0
  83. package/dist/agent-src/skills/iconography/SKILL.md +24 -0
  84. package/dist/agent-src/skills/incident-commander/evals/domain-truth.json +16 -0
  85. package/dist/agent-src/skills/incident-commander/evals/evals.json +20 -0
  86. package/dist/agent-src/skills/laravel/SKILL.md +14 -1
  87. package/dist/agent-src/skills/launch-readiness/SKILL.md +20 -19
  88. package/dist/agent-src/skills/launch-readiness/evals/evals.json +21 -0
  89. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +126 -0
  90. package/dist/agent-src/skills/logging-monitoring/SKILL.md +1 -1
  91. package/dist/agent-src/skills/logging-monitoring/evals/evals.json +16 -0
  92. package/dist/agent-src/skills/mcp/SKILL.md +1 -1
  93. package/dist/agent-src/skills/memory-consolidation/SKILL.md +14 -1
  94. package/dist/agent-src/skills/messaging-architecture/evals/domain-truth.json +16 -0
  95. package/dist/agent-src/skills/messaging-architecture/evals/evals.json +16 -0
  96. package/dist/agent-src/skills/migration-architect/SKILL.md +2 -1
  97. package/dist/agent-src/skills/perf-feedback-craft/evals/evals.json +16 -0
  98. package/dist/agent-src/skills/performance/SKILL.md +1 -1
  99. package/dist/agent-src/skills/pest-testing/SKILL.md +1 -1
  100. package/dist/agent-src/skills/php-debugging/SKILL.md +17 -0
  101. package/dist/agent-src/skills/playwright-testing/SKILL.md +16 -0
  102. package/dist/agent-src/skills/prompt-engineering-patterns/evals/evals.json +16 -0
  103. package/dist/agent-src/skills/readme-writing/SKILL.md +2 -0
  104. package/dist/agent-src/skills/readme-writing-package/SKILL.md +2 -0
  105. package/dist/agent-src/skills/refine-prompt/evals/evals.json +16 -0
  106. package/dist/agent-src/skills/refine-ticket/evals/evals.json +16 -0
  107. package/dist/agent-src/skills/release-comms/SKILL.md +2 -0
  108. package/dist/agent-src/skills/release-comms/evals/evals.json +16 -0
  109. package/dist/agent-src/skills/rice-prioritization/evals/evals.json +20 -0
  110. package/dist/agent-src/skills/roadmap-writing/SKILL.md +22 -51
  111. package/dist/agent-src/skills/runway-cognition/evals/evals.json +20 -0
  112. package/dist/agent-src/skills/scenario-modeling/evals/evals.json +20 -0
  113. package/dist/agent-src/skills/security-audit/SKILL.md +6 -9
  114. package/dist/agent-src/skills/sentry-integration/SKILL.md +2 -2
  115. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +8 -0
  116. package/dist/agent-src/skills/source-discovery/SKILL.md +26 -24
  117. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +98 -0
  118. package/dist/agent-src/skills/spreadsheet-authoring/evals/triggers.json +47 -0
  119. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +55 -46
  120. package/dist/agent-src/skills/subagent-orchestration/evals/evals.json +42 -0
  121. package/dist/agent-src/skills/systematic-debugging/SKILL.md +10 -0
  122. package/dist/agent-src/skills/systematic-debugging/evals/evals.json +20 -0
  123. package/dist/agent-src/skills/test-case-discovery/SKILL.md +118 -0
  124. package/dist/agent-src/skills/test-case-discovery/evals/triggers.json +52 -0
  125. package/dist/agent-src/skills/test-driven-development/SKILL.md +32 -6
  126. package/dist/agent-src/skills/test-driven-development/evals/evals.json +16 -0
  127. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +7 -6
  128. package/dist/agent-src/skills/threat-modeling/evals/evals.json +21 -0
  129. package/dist/agent-src/skills/typography-system/evals/evals.json +14 -0
  130. package/dist/agent-src/skills/unit-economics-modeling/evals/evals.json +24 -0
  131. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +10 -0
  132. package/dist/agent-src/skills/verify-completion-evidence/evals/evals.json +16 -0
  133. package/dist/agent-src/skills/vision-articulation/evals/evals.json +16 -0
  134. package/dist/agent-src/skills/voice-and-tone-design/evals/evals.json +16 -0
  135. package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +187 -0
  136. package/dist/agent-src/skills/worktree-lifecycle/evals/evals.json +38 -0
  137. package/dist/agent-src/skills/worktree-lifecycle/evals/triggers.json +52 -0
  138. package/dist/agent-src/templates/agent-settings.md +3 -2
  139. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +15 -1
  140. package/dist/agent-src/templates/roadmaps.md +14 -0
  141. package/dist/cli/commands/recordTriggerEval.js +4 -14
  142. package/dist/cli/commands/recordTriggerEval.js.map +1 -1
  143. package/dist/discovery/deprecation-report.md +1 -1
  144. package/dist/discovery/discovery-manifest.json +374 -88
  145. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  146. package/dist/discovery/discovery-manifest.summary.md +9 -9
  147. package/dist/discovery/orphan-report.md +1 -1
  148. package/dist/discovery/packs.json +30 -18
  149. package/dist/discovery/trust-report.md +5 -5
  150. package/dist/discovery/workspaces.json +31 -15
  151. package/dist/install/install.mjs +12 -7
  152. package/dist/mcp/registry-manifest.json +4 -4
  153. package/dist/router.json +1 -1
  154. package/dist/scripts/_lib/claude_settings_hooks.js +8 -0
  155. package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
  156. package/dist/scripts/_lib/trigger_eval_floors.js +24 -0
  157. package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -0
  158. package/dist/scripts/condense.js +12 -2
  159. package/dist/scripts/condense.js.map +1 -1
  160. package/dist/server/schemas/settings.js +5 -2
  161. package/dist/server/schemas/settings.js.map +1 -1
  162. package/docs/CLAIMS.md +24 -3
  163. package/docs/architecture.md +3 -3
  164. package/docs/benchmark.md +51 -0
  165. package/docs/catalog.md +23 -9
  166. package/docs/command-flows.md +7 -1
  167. package/docs/contracts/CHANGELOG-conventions.md +6 -0
  168. package/docs/contracts/ai-council-config.md +92 -0
  169. package/docs/contracts/command-clusters.md +1 -0
  170. package/docs/contracts/design-artifact-lifecycle.md +93 -0
  171. package/docs/contracts/design-artifact-verification.md +30 -1
  172. package/docs/contracts/evidence-based-pruning.md +1 -0
  173. package/docs/contracts/hook-architecture-v1.md +25 -0
  174. package/docs/contracts/kernel-membership.md +35 -0
  175. package/docs/contracts/measurement-baseline.md +1 -0
  176. package/docs/contracts/release-pr-gating.md +26 -0
  177. package/docs/contracts/release-sizing.md +107 -0
  178. package/docs/contracts/surface-agent-contracts.md +153 -0
  179. package/docs/contracts/write-engine.md +38 -1
  180. package/docs/customization.md +12 -0
  181. package/docs/decisions/ADR-002-kernel-bucket-overrides.md +11 -0
  182. package/docs/decisions/ADR-003-flat-cluster-subs-and-colon-syntax.md +11 -0
  183. package/docs/decisions/ADR-006-skill-tools-python-pilot.md +1 -1
  184. package/docs/decisions/ADR-028-root-layout.md +1 -1
  185. package/docs/decisions/ADR-038-canonical-settings-path.md +10 -0
  186. package/docs/decisions/ADR-092-defer-command-tier-alias-removal.md +29 -5
  187. package/docs/decisions/ADR-093-ai-council-config-user-global.md +1 -1
  188. package/docs/decisions/ADR-100-global-knowledge-card-sharing.md +7 -0
  189. package/docs/decisions/ADR-103-global-knowledge-default-off-until-measured.md +2 -2
  190. package/docs/decisions/ADR-105-automatic-subagent-orchestration.md +1 -1
  191. package/docs/decisions/ADR-114-commit-policy-iron-law-override.md +11 -0
  192. package/docs/decisions/ADR-115-command-cluster-consolidation-phase-4.md +1 -1
  193. package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +11 -1
  194. package/docs/decisions/ADR-117-subagents-auto-default-on.md +2 -1
  195. package/docs/decisions/ADR-118-loop-engineering-boundaries.md +162 -0
  196. package/docs/decisions/ADR-119-global-knowledge-default-on.md +104 -0
  197. package/docs/decisions/ADR-200-python-to-typescript-migration.md +11 -1
  198. package/docs/decisions/ADR-rule-kernel-and-router.md +11 -0
  199. package/docs/decisions/INDEX.md +11 -9
  200. package/docs/design/cross-model-parity-eval.md +82 -0
  201. package/docs/design/length-neutral-judge-rerun.md +92 -0
  202. package/docs/distribution/consumer-matrix.md +96 -0
  203. package/docs/featured-skills.md +2 -2
  204. package/docs/getting-started-by-role.md +1 -1
  205. package/docs/getting-started.md +2 -2
  206. package/docs/governance-advantage.md +1 -1
  207. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +80 -5
  208. package/docs/guidelines/code-clarity.md +106 -1
  209. package/docs/guidelines/design-antipatterns.md +20 -0
  210. package/docs/guidelines/php/php-coding-patterns.md +12 -2
  211. package/docs/proof.md +21 -15
  212. package/docs/release-runbook.md +3 -0
  213. package/docs/self-review-gate.md +51 -0
  214. package/docs/wedge/production-validator/production-validator.md +27 -0
  215. package/package.json +1 -1
  216. package/src/config/agent-settings.template.yml +29 -9
  217. package/src/scripts/_cli/cmd_doctor.ts +34 -1
  218. package/src/scripts/_lib/auto_dispatch.corpus.test.ts +87 -0
  219. package/src/scripts/_lib/claude_settings_hooks.ts +8 -0
  220. package/src/scripts/_lib/knowledge_global.ts +6 -2
  221. package/src/scripts/_lib/knowledge_global_redaction.ts +42 -4
  222. package/src/scripts/_lib/lexical_index.ts +15 -2
  223. package/src/scripts/_lib/orchestration_record.ts +27 -0
  224. package/src/scripts/_lib/orchestration_savings.ts +52 -0
  225. package/src/scripts/_lib/trigger_eval_floors.ts +31 -0
  226. package/src/scripts/ai_council/chairman.ts +90 -0
  227. package/src/scripts/ai_council/clients.ts +18 -3
  228. package/src/scripts/ai_council/config.ts +157 -0
  229. package/src/scripts/ai_council/debate_gates.ts +89 -0
  230. package/src/scripts/ai_council/orchestrator.ts +65 -2
  231. package/src/scripts/ai_council/prompts.ts +121 -11
  232. package/src/scripts/ai_council/replay.ts +17 -0
  233. package/src/scripts/ai_council/stance_tally.ts +184 -0
  234. package/src/scripts/ai_tells_rules.ts +433 -0
  235. package/src/scripts/bench_humanizer_eval.ts +240 -0
  236. package/src/scripts/build_discovery_manifest.ts +6 -0
  237. package/src/scripts/build_proof.ts +21 -9
  238. package/src/scripts/check_claims.ts +40 -0
  239. package/src/scripts/check_release_adjacent_health.ts +121 -0
  240. package/src/scripts/check_rule_invariants.ts +282 -0
  241. package/src/scripts/condense.ts +16 -2
  242. package/src/scripts/consumer_matrix.ts +392 -0
  243. package/src/scripts/council_cli.ts +147 -0
  244. package/src/scripts/detect_ai_tells.ts +312 -0
  245. package/src/scripts/generate_index.ts +3 -2
  246. package/src/scripts/install.ts +12 -0
  247. package/src/scripts/knowledge_global_cli.ts +1 -1
  248. package/src/scripts/lint_changelog_rollback.ts +293 -0
  249. package/src/scripts/lint_framework_leakage.ts +88 -7
  250. package/src/scripts/lint_framework_leakage_allowlist.json +59 -572
  251. package/src/scripts/lint_knowledge_scale.ts +30 -0
  252. package/src/scripts/lint_originality_shingles.ts +210 -0
  253. package/src/scripts/orchestration_record.ts +17 -0
  254. package/src/scripts/orchestration_savings_report.ts +36 -3
  255. package/src/scripts/schemas/agent-settings.schema.json +11 -0
  256. package/src/scripts/second_brain_retrieval.ts +44 -2
  257. package/src/scripts/self_review_gate.test.ts +65 -0
  258. package/src/scripts/self_review_gate.ts +298 -0
  259. package/src/scripts/skill_eval_coverage.ts +71 -3
  260. package/src/scripts/skill_linter.ts +1 -0
  261. package/src/scripts/trigger_eval_grandfather.json +223 -224
  262. package/src/scripts/trigger_eval_rotation.ts +344 -0
  263. package/src/scripts/update_counts.ts +5 -17
  264. package/src/scripts/worktree_cleanup_check.ts +263 -0
  265. package/src/server/schemas/settings.ts +8 -3
  266. package/src/scripts/analysis_freshness.ts +0 -362
  267. package/src/scripts/export_replay_corpus.ts +0 -208
@@ -166,6 +166,36 @@ export function runChecks(root: string): Warning[] {
166
166
  });
167
167
  }
168
168
 
169
+ // 6. Contested cards — surface knowledge cards flagged `contested: true`
170
+ // (set when `check_memory_contradiction.ts` fires) so a weak / disputed
171
+ // claim stays visibly weak across sessions instead of silently hardening.
172
+ const cardsDir = path.join(root, 'agents', 'knowledge');
173
+ const contested = listFiles(cardsDir, ['.md'])
174
+ .filter((f) => {
175
+ const base = path.basename(f).toLowerCase();
176
+ return base !== 'readme.md' && base !== 'index.md';
177
+ })
178
+ .filter((f) => {
179
+ let text: string;
180
+ try {
181
+ text = fs.readFileSync(f, 'utf-8');
182
+ } catch {
183
+ return false;
184
+ }
185
+ // frontmatter-scoped: `contested: true` on its own line near the top.
186
+ return /^\s*contested:\s*true\s*$/m.test(text.slice(0, 2000));
187
+ })
188
+ .map((f) => path.basename(f));
189
+ if (contested.length > 0) {
190
+ warnings.push({
191
+ rule: 'contested-cards',
192
+ metric: `${contested.length}/0`,
193
+ message:
194
+ `${contested.length} knowledge card(s) carry \`contested: true\`: ${contested.join(', ')}. ` +
195
+ 'Resolution path: reconcile each against its `contradictions: [id]` list (human-judged) — a contested claim must not be cited as settled until resolved.',
196
+ });
197
+ }
198
+
169
199
  // 5. Hot-context budget.
170
200
  const hotContext = path.join(root, 'agents', 'runtime', 'state', 'hot-context.md');
171
201
  let hotText = '';
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * lint_originality_shingles — mechanical re-skin detector for the artifact
4
+ * corpus (road-to-opt-subagent-harvest P1.3).
5
+ *
6
+ * Compares every pair of skills / personas / subagents by entity-neutralized
7
+ * 8-word shingle overlap. Two artifacts that differ only in the named
8
+ * framework, product, or persona ("re-skins") share almost all shingles once
9
+ * entities are masked — exactly the near-duplicate class a human review
10
+ * misses at 200+ files. Slots beside `persona-governance` / `skill-quality`.
11
+ *
12
+ * Sibling of `lint_skill_originality` (ADR-096), NOT a replacement: that
13
+ * gate runs bag-of-words Jaccard over SKILLS with pack-domain severity;
14
+ * this one masks entities and compares 8-word PHRASES across skills +
15
+ * personas + subagents — the re-skin that only swaps "Laravel" for
16
+ * "Django" scores LOW on plain token-Jaccard (different entity tokens)
17
+ * and HIGH here. Run both.
18
+ *
19
+ * Modes:
20
+ * default report-only — prints the top overlapping pairs,
21
+ * always exits 0 (first-run calibration mode).
22
+ * --threshold <pct> exits 1 when any pair's overlap (Jaccard, %) is
23
+ * >= pct and the pair is not allowlisted below.
24
+ * --top <n> rows to print (default 15).
25
+ * --quiet suppress the report (threshold mode still fails).
26
+ *
27
+ * Known-legitimate pairs (shared scaffolding, sibling carve-outs) are
28
+ * allowlisted inline with reasons — same >20-entries policy as every other
29
+ * allowlist in this repo.
30
+ */
31
+ import * as fs from 'node:fs';
32
+ import * as path from 'node:path';
33
+ import { fileURLToPath, pathToFileURL } from 'node:url';
34
+
35
+ const _HERE = path.dirname(fileURLToPath(import.meta.url));
36
+ const REPO = path.resolve(_HERE, '..', '..');
37
+
38
+ const SHINGLE_LEN = 8;
39
+
40
+ /** Entity mask — named stacks/products/personas collapse to one token so a
41
+ * re-skin that only swaps the entity still matches. Lowercase input. */
42
+ const ENTITY_RE = new RegExp(
43
+ '\\b(laravel|symfony|django|rails|react|vue|angular|nextjs|next\\.js|nuxt|' +
44
+ 'nestjs|express|fastify|flask|fastapi|spring|eloquent|doctrine|prisma|' +
45
+ 'pest|phpunit|jest|vitest|pytest|phpstan|rector|ecs|prettier|eslint|' +
46
+ 'mypy|tsc|php|python|typescript|javascript|node|go|rust|ruby|java|' +
47
+ 'tailwind|blade|livewire|flux|jira|sentry|grafana|terraform|terragrunt|' +
48
+ 'traefik|docker|aws|stripe|openai|anthropic|claude|gemini|gpt)\\b',
49
+ 'g',
50
+ );
51
+
52
+ interface Doc {
53
+ id: string;
54
+ shingles: Set<string>;
55
+ }
56
+
57
+ interface AllowPair {
58
+ a: string;
59
+ b: string;
60
+ reason: string;
61
+ }
62
+
63
+ /** Sibling artifacts that legitimately share scaffolding. Keep < 20. */
64
+ const ALLOWLIST: AllowPair[] = [
65
+ // Populated from calibration runs; empty on first ship — the default
66
+ // mode is report-only, so no entry is needed until a threshold is armed.
67
+ ];
68
+
69
+ function _normalize(body: string): string[] {
70
+ // strip fenced code, frontmatter, markdown syntax; mask entities.
71
+ let text = body.replace(/^---\n[\s\S]*?\n---\n/, '');
72
+ text = text.replace(/```[\s\S]*?```/g, ' ');
73
+ text = text.toLowerCase();
74
+ text = text.replace(ENTITY_RE, '§entity§');
75
+ text = text.replace(/[^a-z0-9§]+/g, ' ');
76
+ return text.split(' ').filter((w) => w.length > 0);
77
+ }
78
+
79
+ function _shingles(words: string[]): Set<string> {
80
+ const out = new Set<string>();
81
+ for (let i = 0; i + SHINGLE_LEN <= words.length; i++) {
82
+ out.add(words.slice(i, i + SHINGLE_LEN).join(' '));
83
+ }
84
+ return out;
85
+ }
86
+
87
+ function _collect(): Doc[] {
88
+ const docs: Doc[] = [];
89
+ const push = (id: string, file: string): void => {
90
+ let body: string;
91
+ try {
92
+ body = fs.readFileSync(file, 'utf-8');
93
+ } catch {
94
+ return;
95
+ }
96
+ const sh = _shingles(_normalize(body));
97
+ if (sh.size >= 20) {
98
+ docs.push({ id, shingles: sh });
99
+ }
100
+ };
101
+ const skillsDir = path.join(REPO, 'src', 'skills');
102
+ for (const d of fs.readdirSync(skillsDir, { withFileTypes: true })) {
103
+ if (d.isDirectory()) {
104
+ push(`skill:${d.name}`, path.join(skillsDir, d.name, 'SKILL.md'));
105
+ }
106
+ }
107
+ const personasDir = path.join(REPO, 'src', 'personas');
108
+ if (fs.existsSync(personasDir)) {
109
+ const walk = (dir: string, prefix: string): void => {
110
+ for (const d of fs.readdirSync(dir, { withFileTypes: true })) {
111
+ const full = path.join(dir, d.name);
112
+ if (d.isDirectory() && !d.name.startsWith('_')) {
113
+ walk(full, prefix);
114
+ } else if (d.isFile() && d.name.endsWith('.md') && d.name !== 'README.md') {
115
+ push(`persona:${path.basename(d.name, '.md')}`, full);
116
+ }
117
+ }
118
+ };
119
+ walk(personasDir, 'persona');
120
+ }
121
+ const subagentsDir = path.join(REPO, 'src', 'subagents');
122
+ if (fs.existsSync(subagentsDir)) {
123
+ for (const f of fs.readdirSync(subagentsDir)) {
124
+ if (f.endsWith('.md') && !f.startsWith('_')) {
125
+ push(`subagent:${path.basename(f, '.md')}`, path.join(subagentsDir, f));
126
+ }
127
+ }
128
+ }
129
+ return docs;
130
+ }
131
+
132
+ function _jaccardPct(a: Set<string>, b: Set<string>): number {
133
+ const [small, large] = a.size <= b.size ? [a, b] : [b, a];
134
+ let inter = 0;
135
+ for (const s of small) {
136
+ if (large.has(s)) inter++;
137
+ }
138
+ const union = a.size + b.size - inter;
139
+ return union === 0 ? 0 : (100 * inter) / union;
140
+ }
141
+
142
+ function _allowlisted(a: string, b: string): boolean {
143
+ return ALLOWLIST.some(
144
+ (p) => (p.a === a && p.b === b) || (p.a === b && p.b === a),
145
+ );
146
+ }
147
+
148
+ export function main(argv: string[] = process.argv.slice(2)): number {
149
+ let threshold: number | null = null;
150
+ let top = 15;
151
+ let quiet = false;
152
+ for (let i = 0; i < argv.length; i++) {
153
+ const arg = argv[i] as string;
154
+ if (arg === '--threshold') threshold = Number(argv[++i]);
155
+ else if (arg === '--top') top = Number(argv[++i]);
156
+ else if (arg === '--quiet') quiet = true;
157
+ }
158
+
159
+ const docs = _collect();
160
+ const pairs: Array<{ a: string; b: string; pct: number }> = [];
161
+ for (let i = 0; i < docs.length; i++) {
162
+ for (let j = i + 1; j < docs.length; j++) {
163
+ const pct = _jaccardPct((docs[i] as Doc).shingles, (docs[j] as Doc).shingles);
164
+ if (pct > 0.5) {
165
+ pairs.push({ a: (docs[i] as Doc).id, b: (docs[j] as Doc).id, pct });
166
+ }
167
+ }
168
+ }
169
+ pairs.sort((x, y) => y.pct - x.pct);
170
+
171
+ if (!quiet) {
172
+ process.stdout.write(
173
+ `originality-shingles: ${docs.length} docs · ${SHINGLE_LEN}-word entity-masked shingles\n`,
174
+ );
175
+ for (const p of pairs.slice(0, top)) {
176
+ process.stdout.write(` ${p.pct.toFixed(1).padStart(5)}% ${p.a} ↔ ${p.b}\n`);
177
+ }
178
+ if (pairs.length === 0) {
179
+ process.stdout.write(' no pair above 0.5% overlap\n');
180
+ }
181
+ }
182
+
183
+ if (threshold !== null && Number.isFinite(threshold)) {
184
+ const offenders = pairs.filter((p) => p.pct >= (threshold as number) && !_allowlisted(p.a, p.b));
185
+ if (offenders.length > 0) {
186
+ process.stderr.write(
187
+ `❌ originality-shingles: ${offenders.length} pair(s) at or above ${threshold}% overlap:\n`,
188
+ );
189
+ for (const p of offenders.slice(0, top)) {
190
+ process.stderr.write(` ${p.pct.toFixed(1)}% ${p.a} ↔ ${p.b}\n`);
191
+ }
192
+ return 1;
193
+ }
194
+ }
195
+ return 0;
196
+ }
197
+
198
+ function _isCliEntry(): boolean {
199
+ const a = process.argv[1];
200
+ if (!a) return false;
201
+ if (pathToFileURL(path.resolve(a)).href === import.meta.url) return true;
202
+ try {
203
+ return fs.realpathSync(a) === fs.realpathSync(fileURLToPath(import.meta.url));
204
+ } catch {
205
+ return false;
206
+ }
207
+ }
208
+ if (_isCliEntry()) {
209
+ process.exit(main());
210
+ }
@@ -11,6 +11,7 @@
11
11
  * --spawn-count 1 --token-delta -72000 --provenance measured \
12
12
  * --tier-chosen lite --tier-source inferred --task-class read-only-fanout \
13
13
  * [--tiers sonnet,opus] [--wall-clock-ms 18500] [--dispatch-outcome DONE] \
14
+ * [--first-pass-success true|false] [--escalated true|false] \
14
15
  * [--dir <audit-dir>] [--dry-run]
15
16
  *
16
17
  * Read by `src/scripts/orchestration_savings_report.ts`.
@@ -65,6 +66,19 @@ function str(flags: Flags, key: string): string | undefined {
65
66
  return typeof v === 'string' ? v : undefined;
66
67
  }
67
68
 
69
+ /**
70
+ * Parse an explicit boolean flag value. `--key true|false` → boolean; a bare
71
+ * `--key` counts as `true`; any other value is passed through so the lib's
72
+ * non-boolean validation rejects it with a clear error.
73
+ */
74
+ function bool(flags: Flags, key: string): boolean | undefined {
75
+ const v = flags[key];
76
+ if (v === undefined) return undefined;
77
+ if (v === true || v === 'true') return true;
78
+ if (v === 'false') return false;
79
+ return v as unknown as boolean; // invalid string → caught by the lib's typeof validation
80
+ }
81
+
68
82
  function int(flags: Flags, key: string): number | undefined {
69
83
  const v = str(flags, key);
70
84
  if (v === undefined) return undefined;
@@ -98,12 +112,15 @@ export function main(argv: string[] = process.argv.slice(2)): number {
98
112
  tier_chosen: str(flags, 'tier-chosen') as TierChosen | undefined,
99
113
  tier_source: str(flags, 'tier-source') as TierSource | undefined,
100
114
  task_class: str(flags, 'task-class'),
115
+ dispatch_mode: str(flags, 'dispatch-mode') as never,
101
116
  task_size_estimate: int(flags, 'task-size-estimate'),
102
117
  wall_clock_ms: int(flags, 'wall-clock-ms'),
103
118
  dispatch_tokens: int(flags, 'dispatch-tokens'),
104
119
  session_tier: str(flags, 'session-tier'),
105
120
  dispatch_outcome: str(flags, 'dispatch-outcome') as DispatchOutcome | undefined,
106
121
  verify_mode: str(flags, 'verify-mode') as VerifyMode | undefined,
122
+ first_pass_success: bool(flags, 'first-pass-success'),
123
+ escalated: bool(flags, 'escalated'),
107
124
  phase: str(flags, 'phase') as LinePhase | undefined,
108
125
  outcome: str(flags, 'outcome') as LineOutcome | undefined,
109
126
  confidence_band: str(flags, 'confidence-band') as Band | undefined,
@@ -5,6 +5,11 @@
5
5
  * and prints an aggregate token-savings report. Read-only; no network, no
6
6
  * mutation. Handles a missing/empty audit dir gracefully.
7
7
  *
8
+ * QUALITY × COST PAIRING (council verdict): the savings figures never render
9
+ * without the quality columns (first_pass_success_rate / escalation_rate).
10
+ * When ≥ 20 lines in the window carry the quality fields the real rates
11
+ * render; below 20 the columns render as "n/a (n=<count>)" beside savings.
12
+ *
8
13
  * Usage:
9
14
  * ./scripts-run src/scripts/orchestration_savings_report \
10
15
  * [--dir <path>] [--format text|json] [--weights lite=1,medium=5,high=15]
@@ -77,13 +82,41 @@ export function readAuditLines(dir: string): AuditLine[] {
77
82
  return lines;
78
83
  }
79
84
 
80
- function renderText(r: SavingsReport, dir: string): string {
85
+ /** Dispatches carrying the quality fields at/above this render the real rates; below, "n/a (n=<count>)". */
86
+ export const QUALITY_GATE_MIN_LINES = 20;
87
+
88
+ function pct(v: number): string {
89
+ return `${(v * 100).toFixed(0)}%`;
90
+ }
91
+
92
+ /**
93
+ * Render the paired cost × quality block. Council verdict: savings NEVER render
94
+ * without the quality columns — at n ≥ QUALITY_GATE_MIN_LINES the real rates
95
+ * render; below, the quality columns render as "n/a (n=<count>)" alongside the
96
+ * savings figures. There is no savings-only render path.
97
+ */
98
+ function renderPairedCostQuality(r: SavingsReport): string[] {
99
+ const q = r.quality;
100
+ const gated = q.quality_lines >= QUALITY_GATE_MIN_LINES;
101
+ const fps = gated && q.first_pass_success_rate !== null
102
+ ? `${pct(q.first_pass_success_rate)} (n=${q.first_pass_lines})`
103
+ : `n/a (n=${q.first_pass_lines})`;
104
+ const esc = gated && q.escalation_rate !== null
105
+ ? `${pct(q.escalation_rate)} (n=${q.escalated_lines})`
106
+ : `n/a (n=${q.escalated_lines})`;
107
+ return [
108
+ ' cost × quality (paired — never savings alone):',
109
+ ` net token_delta: ${r.net_token_delta} (negative = net saved) | first_pass_success_rate: ${fps}`,
110
+ ` tokens saved: ${r.tokens_saved} · tokens added: ${r.tokens_added} | escalation_rate: ${esc}`,
111
+ ];
112
+ }
113
+
114
+ export function renderText(r: SavingsReport, dir: string): string {
81
115
  const out: string[] = [];
82
116
  out.push('Orchestration savings report');
83
117
  out.push(` source: ${dir}`);
84
118
  out.push(` dispatches: ${r.dispatches} (total spawns: ${r.total_spawns})`);
85
- out.push(` net token_delta: ${r.net_token_delta} (negative = net saved)`);
86
- out.push(` tokens saved: ${r.tokens_saved} tokens added: ${r.tokens_added}`);
119
+ out.push(...renderPairedCostQuality(r));
87
120
  out.push(` provenance: measured ${r.by_provenance.measured.dispatches} (Δ ${r.by_provenance.measured.net_token_delta}) · estimated ${r.by_provenance.estimated.dispatches} (Δ ${r.by_provenance.estimated.net_token_delta})`);
88
121
  out.push(` measured share: ${(r.measured_share * 100).toFixed(0)}%`);
89
122
  if (Object.keys(r.by_tier).length) {
@@ -84,6 +84,17 @@
84
84
  }
85
85
  }
86
86
  },
87
+ "code_style": {
88
+ "type": "object",
89
+ "additionalProperties": true,
90
+ "properties": {
91
+ "docblocks": {
92
+ "type": "string",
93
+ "enum": ["minimal", "full"],
94
+ "description": "Consumed by the code-comment-discipline rule. minimal (default) = no signature-mirroring docblocks; docblocks only for machine-relevant precision (generics, array shapes) or genuine why-context. full = exported public library surface may carry one-line summary docblocks; the redundancy ban still holds."
95
+ }
96
+ }
97
+ },
87
98
  "tokens": {
88
99
  "type": "object",
89
100
  "additionalProperties": true,
@@ -38,6 +38,7 @@ import {
38
38
  _setMemoryRoot,
39
39
  retrieve,
40
40
  } from './memory_lookup.js';
41
+ import { sanitize_text } from './_lib/retrieval_sanitize.js';
41
42
  import { scoreTask } from './second_brain_score.js';
42
43
 
43
44
  const _HERE = fileURLToPath(import.meta.url);
@@ -67,11 +68,19 @@ export function bindStore(): void {
67
68
  _setIntakeRoot(path.join(STORE_DIR, 'intake-none'));
68
69
  }
69
70
 
70
- export function loadCorpus(): { type: string; k: number; tasks: RetrievalTask[] } {
71
+ export function loadCorpus(): {
72
+ type: string;
73
+ k: number;
74
+ tasks: RetrievalTask[];
75
+ stale_ids?: string[];
76
+ poisoned_ids?: string[];
77
+ } {
71
78
  const doc = YAML.parse(fs.readFileSync(path.join(STORE_DIR, 'retrieval-corpus.yml'), 'utf-8')) as {
72
79
  type: string;
73
80
  k: number;
74
81
  tasks: RetrievalTask[];
82
+ stale_ids?: string[];
83
+ poisoned_ids?: string[];
75
84
  };
76
85
  return doc;
77
86
  }
@@ -162,11 +171,17 @@ export function signTestP(wins: number, losses: number): number {
162
171
 
163
172
  export async function run(opts: { mode: 'dry-run' | 'live'; host: string; seeds: number }): Promise<Record<string, unknown>> {
164
173
  bindStore();
165
- const { type, k, tasks } = loadCorpus();
174
+ const { type, k, tasks, stale_ids = [], poisoned_ids = [] } = loadCorpus();
175
+ const staleSet = new Set(stale_ids);
176
+ const poisonedSet = new Set(poisoned_ids);
166
177
  const apiKey = opts.mode === 'live' ? load_anthropic_key() : '';
167
178
  const placebo = _placeboBodies(type);
168
179
 
169
180
  let precisionHits = 0;
181
+ let topkSlots = 0;
182
+ let staleHits = 0;
183
+ let poisonedRetrieved = 0;
184
+ let poisonedNeutralized = 0;
170
185
  let tieSetSum = 0;
171
186
  const perTask: Array<Record<string, unknown>> = [];
172
187
  const armPass: Record<Arm, number> = { 'retrieval-off': 0, 'retrieval-on': 0, placebo: 0 };
@@ -177,6 +192,18 @@ export async function run(opts: { mode: 'dry-run' | 'live'; host: string; seeds:
177
192
  for (const task of tasks) {
178
193
  const { topk, rank, tieSetSize } = retrieveFor(task, type, k);
179
194
  const ids = topk.map((r) => r.id);
195
+ topkSlots += topk.length;
196
+ for (const hit of topk) {
197
+ if (staleSet.has(hit.id)) staleHits += 1;
198
+ if (poisonedSet.has(hit.id)) {
199
+ poisonedRetrieved += 1;
200
+ // Rejection = the sanitize floor strips every hidden-instruction
201
+ // vector before the body could reach a prompt.
202
+ const clean = sanitize_text(hit.body);
203
+ if (!/[​-‏‪-‮⁦-⁩]/u.test(clean)) poisonedNeutralized += 1;
204
+ }
205
+ }
206
+
180
207
  const neededInTopK = rank >= 0 && rank < k;
181
208
  if (neededInTopK) precisionHits += 1;
182
209
  tieSetSum += tieSetSize;
@@ -226,6 +253,21 @@ export async function run(opts: { mode: 'dry-run' | 'live'; host: string; seeds:
226
253
  k,
227
254
  store: 'internal/bench/second-brain/retrieval-store/ (5 needed + 19 distractors, keyword-overlapping confusers)',
228
255
  precision_at_k: precisionAtK,
256
+ // road-to-proof-under-real-conditions Phase 5 — retrieval quality pair:
257
+ // stale_hit_rate: fraction of retrieved top-k slots occupied by
258
+ // SUPERSEDED store entries (lower is better; the substrate has no
259
+ // recency/supersede weighting yet, so a non-zero rate is the honest
260
+ // baseline the FTS5/recency activation path improves on).
261
+ stale_hit_rate: topkSlots ? staleHits / topkSlots : 0,
262
+ stale_hits: staleHits,
263
+ topk_slots: topkSlots,
264
+ // poisoned_rejection_rate: of poisoned entries that WERE retrieved,
265
+ // the fraction whose hidden-instruction vectors the sanitize floor
266
+ // strips (target 1.0; null when none was retrieved this run).
267
+ poisoned_rejection_rate: poisonedRetrieved ? poisonedNeutralized / poisonedRetrieved : null,
268
+ poisoned_retrieved: poisonedRetrieved,
269
+ poisoned_neutralized: poisonedNeutralized,
270
+
229
271
  precision_hits: precisionHits,
230
272
  mean_tie_set_size: meanTieSet,
231
273
  discrimination_note:
@@ -0,0 +1,65 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import {
4
+ classifyBlocking,
5
+ gateVerdict,
6
+ isReviewablePath,
7
+ renderReview,
8
+ type Finding,
9
+ } from './self_review_gate.js';
10
+
11
+ const f = (severity: Finding['severity'], kind: Finding['kind']): Finding => ({
12
+ severity,
13
+ kind,
14
+ title: `${severity} ${kind}`,
15
+ detail: 'x',
16
+ });
17
+
18
+ describe('classifyBlocking', () => {
19
+ it('blocks security + high and claim + critical', () => {
20
+ expect(classifyBlocking(f('high', 'security'))).toBe(true);
21
+ expect(classifyBlocking(f('critical', 'claim'))).toBe(true);
22
+ });
23
+ it('does not block low/medium security, or any style/correctness', () => {
24
+ expect(classifyBlocking(f('medium', 'security'))).toBe(false);
25
+ expect(classifyBlocking(f('low', 'claim'))).toBe(false);
26
+ expect(classifyBlocking(f('critical', 'style'))).toBe(false);
27
+ expect(classifyBlocking(f('high', 'correctness'))).toBe(false);
28
+ });
29
+ });
30
+
31
+ describe('gateVerdict', () => {
32
+ const blocking = [f('high', 'security')];
33
+ it('advisory (default shipped mode) never blocks, even with a blocking finding', () => {
34
+ expect(gateVerdict(blocking, { enforce: false })).toBe(0);
35
+ });
36
+ it('enforce blocks only on a merge-blocking finding', () => {
37
+ expect(gateVerdict(blocking, { enforce: true })).toBe(2);
38
+ expect(gateVerdict([f('critical', 'style')], { enforce: true })).toBe(0);
39
+ expect(gateVerdict([], { enforce: true })).toBe(0);
40
+ });
41
+ });
42
+
43
+ describe('isReviewablePath', () => {
44
+ it('skips generated projections + lockfiles, keeps source', () => {
45
+ expect(isReviewablePath('dist/agent-src/skills/x/SKILL.md')).toBe(false);
46
+ expect(isReviewablePath('.augment/rules/x.md')).toBe(false);
47
+ expect(isReviewablePath('.windsurfrules')).toBe(false);
48
+ expect(isReviewablePath('package-lock.json')).toBe(false);
49
+ expect(isReviewablePath('src/rules/x.md')).toBe(true);
50
+ expect(isReviewablePath('src/scripts/y.ts')).toBe(true);
51
+ });
52
+ });
53
+
54
+ describe('renderReview', () => {
55
+ it('carries the HUMAN REVIEW REQUIRED banner + the floor-not-human-review caveat', () => {
56
+ const out = renderReview([], false);
57
+ expect(out).toContain('HUMAN REVIEW REQUIRED');
58
+ expect(out).toContain('not** independent human review');
59
+ });
60
+ it('advisory phrasing says WOULD block, enforce phrasing says blocking', () => {
61
+ const blocking = [f('high', 'security')];
62
+ expect(renderReview(blocking, false)).toContain('WOULD block');
63
+ expect(renderReview(blocking, true)).toContain('merge-blocking');
64
+ });
65
+ });