@event4u/agent-config 8.8.0 → 8.10.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 (215) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +139 -263
  3. package/README.md +5 -5
  4. package/dist/agent-src/commands/agent-handoff.md +39 -2
  5. package/dist/agent-src/commands/cost/report.md +13 -3
  6. package/dist/agent-src/commands/research/deep.md +8 -0
  7. package/dist/agent-src/commands/research/report.md +7 -0
  8. package/dist/agent-src/commands/roadmap/create.md +12 -0
  9. package/dist/agent-src/commands/roadmap/process-full.md +31 -5
  10. package/dist/agent-src/commands/tests/create.md +28 -6
  11. package/dist/agent-src/commands/work.md +33 -0
  12. package/dist/agent-src/commands/worktree/cleanup.md +78 -0
  13. package/dist/agent-src/commands/worktree/create.md +62 -0
  14. package/dist/agent-src/commands/worktree/status.md +63 -0
  15. package/dist/agent-src/commands/worktree/verify.md +70 -0
  16. package/dist/agent-src/commands/worktree.md +85 -0
  17. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +30 -1
  18. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +60 -31
  19. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +37 -0
  20. package/dist/agent-src/contexts/execution/subagent-topologies.md +34 -0
  21. package/dist/agent-src/contexts/execution/verification-mechanics.md +9 -9
  22. package/dist/agent-src/rules/agent-authority.md +1 -1
  23. package/dist/agent-src/rules/communication-through-line.md +1 -1
  24. package/dist/agent-src/rules/delegation-policy.md +5 -0
  25. package/dist/agent-src/rules/design-fidelity.md +40 -0
  26. package/dist/agent-src/rules/direct-answers.md +1 -1
  27. package/dist/agent-src/rules/downstream-changes.md +1 -0
  28. package/dist/agent-src/rules/external-code-graph-interop.md +65 -0
  29. package/dist/agent-src/rules/git-history-discipline.md +18 -0
  30. package/dist/agent-src/rules/lethal-trifecta-guard.md +6 -1
  31. package/dist/agent-src/rules/minimal-safe-diff.md +58 -10
  32. package/dist/agent-src/rules/non-destructive-by-default.md +3 -0
  33. package/dist/agent-src/rules/question-not-instruction.md +76 -0
  34. package/dist/agent-src/rules/senior-engineering-discipline.md +1 -1
  35. package/dist/agent-src/rules/spreadsheet-source-quality.md +61 -0
  36. package/dist/agent-src/rules/think-before-action.md +1 -0
  37. package/dist/agent-src/rules/tool-safety.md +8 -0
  38. package/dist/agent-src/rules/untrusted-input-defense.md +87 -22
  39. package/dist/agent-src/scripts/update_roadmap_progress.ts +2 -1
  40. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  41. package/dist/agent-src/skills/accessibility-auditor/evals/evals.json +15 -0
  42. package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +10 -3
  43. package/dist/agent-src/skills/analysis-skill-router/evals/evals.json +13 -0
  44. package/dist/agent-src/skills/api-testing/SKILL.md +15 -1
  45. package/dist/agent-src/skills/character-consistency/evals/evals.json +16 -0
  46. package/dist/agent-src/skills/code-review/SKILL.md +2 -0
  47. package/dist/agent-src/skills/command-routing/evals/evals.json +14 -0
  48. package/dist/agent-src/skills/command-writing/SKILL.md +53 -40
  49. package/dist/agent-src/skills/dashboard-design/evals/evals.json +16 -0
  50. package/dist/agent-src/skills/dcf-modeling/evals/domain-truth.json +29 -0
  51. package/dist/agent-src/skills/dcf-modeling/evals/evals.json +20 -0
  52. package/dist/agent-src/skills/decision-record/evals/evals.json +20 -0
  53. package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +1 -0
  54. package/dist/agent-src/skills/design-intelligence/SKILL.md +49 -12
  55. package/dist/agent-src/skills/design-intelligence/evals/evals.json +14 -0
  56. package/dist/agent-src/skills/design-review/SKILL.md +35 -15
  57. package/dist/agent-src/skills/design-system-capture/SKILL.md +84 -24
  58. package/dist/agent-src/skills/design-system-capture/evals/evals.json +15 -0
  59. package/dist/agent-src/skills/developer-like-execution/evals/evals.json +16 -0
  60. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +27 -0
  61. package/dist/agent-src/skills/doc-coauthoring/evals/evals.json +16 -0
  62. package/dist/agent-src/skills/editorial-calendar/evals/domain-truth.json +16 -0
  63. package/dist/agent-src/skills/editorial-calendar/evals/evals.json +16 -0
  64. package/dist/agent-src/skills/estimate-ticket/evals/evals.json +16 -0
  65. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -0
  66. package/dist/agent-src/skills/existing-ui-audit/evals/triggers.json +47 -0
  67. package/dist/agent-src/skills/fe-design/SKILL.md +36 -5
  68. package/dist/agent-src/skills/fe-design/evals/triggers.json +47 -0
  69. package/dist/agent-src/skills/forecasting/evals/domain-truth.json +29 -0
  70. package/dist/agent-src/skills/forecasting/evals/evals.json +20 -0
  71. package/dist/agent-src/skills/frontend-render-security/SKILL.md +2 -2
  72. package/dist/agent-src/skills/fundraising-narrative/evals/domain-truth.json +16 -0
  73. package/dist/agent-src/skills/fundraising-narrative/evals/evals.json +16 -0
  74. package/dist/agent-src/skills/html-deck/SKILL.md +27 -0
  75. package/dist/agent-src/skills/iconography/SKILL.md +24 -0
  76. package/dist/agent-src/skills/incident-commander/evals/domain-truth.json +16 -0
  77. package/dist/agent-src/skills/incident-commander/evals/evals.json +20 -0
  78. package/dist/agent-src/skills/launch-readiness/evals/evals.json +21 -0
  79. package/dist/agent-src/skills/logging-monitoring/evals/evals.json +16 -0
  80. package/dist/agent-src/skills/markitdown/SKILL.md +1 -0
  81. package/dist/agent-src/skills/mcp/SKILL.md +24 -0
  82. package/dist/agent-src/skills/mcp-builder/SKILL.md +9 -1
  83. package/dist/agent-src/skills/memory-consolidation/SKILL.md +55 -4
  84. package/dist/agent-src/skills/messaging-architecture/evals/domain-truth.json +16 -0
  85. package/dist/agent-src/skills/messaging-architecture/evals/evals.json +16 -0
  86. package/dist/agent-src/skills/perf-feedback-craft/evals/evals.json +16 -0
  87. package/dist/agent-src/skills/pest-testing/SKILL.md +1 -1
  88. package/dist/agent-src/skills/playwright-testing/SKILL.md +16 -0
  89. package/dist/agent-src/skills/prompt-engineering-patterns/evals/evals.json +16 -0
  90. package/dist/agent-src/skills/refine-prompt/evals/evals.json +16 -0
  91. package/dist/agent-src/skills/refine-ticket/evals/evals.json +16 -0
  92. package/dist/agent-src/skills/release-comms/evals/evals.json +16 -0
  93. package/dist/agent-src/skills/rice-prioritization/evals/evals.json +20 -0
  94. package/dist/agent-src/skills/roadmap-writing/SKILL.md +39 -5
  95. package/dist/agent-src/skills/runway-cognition/evals/domain-truth.json +29 -0
  96. package/dist/agent-src/skills/runway-cognition/evals/evals.json +20 -0
  97. package/dist/agent-src/skills/scenario-modeling/evals/domain-truth.json +29 -0
  98. package/dist/agent-src/skills/scenario-modeling/evals/evals.json +20 -0
  99. package/dist/agent-src/skills/secrets-management/SKILL.md +7 -0
  100. package/dist/agent-src/skills/skill-writing/SKILL.md +12 -3
  101. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +98 -0
  102. package/dist/agent-src/skills/spreadsheet-authoring/evals/triggers.json +47 -0
  103. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +27 -33
  104. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  105. package/dist/agent-src/skills/systematic-debugging/evals/evals.json +20 -0
  106. package/dist/agent-src/skills/test-case-discovery/SKILL.md +118 -0
  107. package/dist/agent-src/skills/test-case-discovery/evals/triggers.json +52 -0
  108. package/dist/agent-src/skills/test-driven-development/SKILL.md +32 -6
  109. package/dist/agent-src/skills/test-driven-development/evals/evals.json +16 -0
  110. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +34 -4
  111. package/dist/agent-src/skills/threat-modeling/evals/evals.json +21 -0
  112. package/dist/agent-src/skills/typography-system/evals/evals.json +14 -0
  113. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -0
  114. package/dist/agent-src/skills/unit-economics-modeling/evals/domain-truth.json +40 -0
  115. package/dist/agent-src/skills/unit-economics-modeling/evals/evals.json +24 -0
  116. package/dist/agent-src/skills/verify-completion-evidence/evals/evals.json +16 -0
  117. package/dist/agent-src/skills/vision-articulation/evals/evals.json +16 -0
  118. package/dist/agent-src/skills/voice-and-tone-design/evals/evals.json +16 -0
  119. package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +180 -0
  120. package/dist/agent-src/skills/worktree-lifecycle/evals/triggers.json +52 -0
  121. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  122. package/dist/agent-src/templates/roadmaps.md +36 -0
  123. package/dist/cli/registry.js +3 -0
  124. package/dist/cli/registry.js.map +1 -1
  125. package/dist/discovery/deprecation-report.md +1 -1
  126. package/dist/discovery/discovery-manifest.json +326 -67
  127. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  128. package/dist/discovery/discovery-manifest.summary.md +6 -6
  129. package/dist/discovery/orphan-report.md +1 -1
  130. package/dist/discovery/packs.json +25 -14
  131. package/dist/discovery/trust-report.md +4 -4
  132. package/dist/discovery/workspaces.json +25 -12
  133. package/dist/install/install.mjs +136 -1
  134. package/dist/mcp/registry-manifest.json +3 -3
  135. package/dist/router.json +1 -1
  136. package/dist/scripts/_lib/claude_builtin_names.js +145 -0
  137. package/dist/scripts/_lib/claude_builtin_names.js.map +1 -0
  138. package/dist/scripts/condense.js +14 -0
  139. package/dist/scripts/condense.js.map +1 -1
  140. package/docs/CLAIMS.md +36 -8
  141. package/docs/architecture.md +4 -4
  142. package/docs/archive/CHANGELOG-pre-8.9.0.md +306 -0
  143. package/docs/benchmark.md +113 -0
  144. package/docs/catalog.md +32 -8
  145. package/docs/command-flows.md +6 -1
  146. package/docs/contracts/CHANGELOG-conventions.md +6 -0
  147. package/docs/contracts/command-clusters.md +18 -0
  148. package/docs/contracts/design-artifact-lifecycle.md +93 -0
  149. package/docs/contracts/design-artifact-verification.md +153 -0
  150. package/docs/contracts/injected-block-authenticity-model.md +66 -0
  151. package/docs/contracts/mcp-tool-inventory.md +28 -28
  152. package/docs/contracts/release-pr-gating.md +26 -0
  153. package/docs/contracts/release-sizing.md +85 -0
  154. package/docs/contracts/surface-agent-contracts.md +153 -0
  155. package/docs/decisions/ADR-061-corpus-grounding-layer.md +13 -0
  156. package/docs/decisions/ADR-115-command-cluster-consolidation-phase-4.md +1 -1
  157. package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +9 -0
  158. package/docs/distribution/consumer-matrix.md +96 -0
  159. package/docs/featured-skills.md +2 -2
  160. package/docs/getting-started-by-role.md +1 -1
  161. package/docs/getting-started.md +2 -2
  162. package/docs/governance-advantage.md +1 -1
  163. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +27 -0
  164. package/docs/guidelines/agent-infra/emphasis-budget.md +45 -0
  165. package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +151 -0
  166. package/docs/guidelines/agent-infra/tool-description-as-policy.md +35 -0
  167. package/docs/guidelines/design-canon.md +1 -0
  168. package/docs/proof.md +38 -23
  169. package/docs/release-runbook.md +3 -0
  170. package/docs/second-brain-scope.md +69 -26
  171. package/docs/self-review-gate.md +51 -0
  172. package/package.json +1 -1
  173. package/src/agent-src/scripts/update_roadmap_progress.ts +2 -1
  174. package/src/scripts/_dispatch.bash +27 -0
  175. package/src/scripts/_lib/claude_builtin_names.ts +148 -0
  176. package/src/scripts/_lib/file_slicer.ts +117 -0
  177. package/src/scripts/_lib/lexical_index.ts +155 -0
  178. package/src/scripts/_lib/orchestration_record.ts +183 -0
  179. package/src/scripts/_lib/orchestration_savings.ts +114 -9
  180. package/src/scripts/_lib/retrieval_sanitize.ts +71 -0
  181. package/src/scripts/_lib/security_lint.ts +1 -0
  182. package/src/scripts/_lib/stat_index.ts +98 -0
  183. package/src/scripts/benchmark.ts +122 -0
  184. package/src/scripts/build_proof.ts +30 -17
  185. package/src/scripts/check_memory.ts +2 -0
  186. package/src/scripts/check_quality_regression.ts +51 -0
  187. package/src/scripts/check_release_adjacent_health.ts +121 -0
  188. package/src/scripts/check_roadmap_trackable.ts +3 -2
  189. package/src/scripts/check_rule_invariants.ts +275 -0
  190. package/src/scripts/condense.ts +14 -0
  191. package/src/scripts/consumer_matrix.ts +353 -0
  192. package/src/scripts/discovery_graph.ts +0 -0
  193. package/src/scripts/external_sources_denylist.json +4 -1
  194. package/src/scripts/generate_index.ts +6 -2
  195. package/src/scripts/install-hooks.sh +25 -0
  196. package/src/scripts/install.ts +35 -2
  197. package/src/scripts/learning_sidecar.ts +0 -0
  198. package/src/scripts/lint_agent_skill_names.ts +29 -0
  199. package/src/scripts/lint_changelog_rollback.ts +293 -0
  200. package/src/scripts/lint_knowledge_scale.ts +4 -3
  201. package/src/scripts/lint_roadmap_complexity.ts +124 -0
  202. package/src/scripts/lint_versioned_cache.ts +195 -0
  203. package/src/scripts/mcp_server/consumer_tool_catalog.json +2 -1
  204. package/src/scripts/mcp_server/tools.ts +24 -1
  205. package/src/scripts/measure_lexical_ranking.ts +173 -0
  206. package/src/scripts/memory_lookup.ts +0 -0
  207. package/src/scripts/orchestration_record.ts +156 -0
  208. package/src/scripts/orchestration_savings_report.ts +65 -8
  209. package/src/scripts/schemas/skill.schema.json +4 -0
  210. package/src/scripts/score_domain_truth.ts +192 -0
  211. package/src/scripts/second_brain_retrieval.ts +320 -0
  212. package/src/scripts/second_brain_run.ts +341 -0
  213. package/src/scripts/self_review_gate.test.ts +65 -0
  214. package/src/scripts/self_review_gate.ts +272 -0
  215. package/src/scripts/trigger_eval_grandfather.json +223 -224
@@ -0,0 +1,148 @@
1
+ // Claude Code built-in command / bundled-skill names — the reserved namespace.
2
+ //
3
+ // A user- or project-scope skill (or flat command) whose name equals a
4
+ // built-in Claude Code slash command SHADOWS the built-in: typing `/mcp`
5
+ // then opens the package's skill instead of Claude Code's MCP auth dialog
6
+ // (observed 2026-07-10). The suite's contract is to complement the host,
7
+ // never to overlay or degrade it — so no Claude-facing projection may claim
8
+ // one of these names.
9
+ //
10
+ // Enforced at three layers:
11
+ // 1. `condense.ts::generate_claude_commands` — project-scope `.claude/skills/`
12
+ // command entries skip reserved slugs.
13
+ // 2. `install.ts::_apply_claude_flat_command_wrappers` — user-scope
14
+ // `~/.claude/` deploys neither a skill wrapper nor a flat command file
15
+ // for a reserved slug (nested `/cluster:sub` commands are unaffected —
16
+ // built-ins carry no `:`).
17
+ // 3. `lint_agent_skill_names.ts` — a skill in `src/skills/` (or a domain
18
+ // pack) with a reserved name fails CI unless it opts out of slash
19
+ // registration with `user-invocable: false` (Claude may still load it
20
+ // automatically; the built-in keeps the `/name`).
21
+ //
22
+ // Source: https://code.claude.com/docs/en/commands (fetched 2026-07-10),
23
+ // plus a small legacy set still present in older Claude Code versions.
24
+ // Refresh trigger: a package artefact stops appearing in Claude Code, or a
25
+ // release-notes entry adds a new built-in command.
26
+
27
+ const _CURRENT = [
28
+ 'add-dir',
29
+ 'advisor',
30
+ 'agents',
31
+ 'autofix-pr',
32
+ 'background',
33
+ 'batch',
34
+ 'branch',
35
+ 'btw',
36
+ 'cd',
37
+ 'chrome',
38
+ 'claude-api',
39
+ 'clear',
40
+ 'code-review',
41
+ 'color',
42
+ 'compact',
43
+ 'config',
44
+ 'context',
45
+ 'copy',
46
+ 'cost',
47
+ 'dataviz',
48
+ 'debug',
49
+ 'deep-research',
50
+ 'design-login',
51
+ 'design-sync',
52
+ 'desktop',
53
+ 'diff',
54
+ 'doctor',
55
+ 'effort',
56
+ 'exit',
57
+ 'export',
58
+ 'fast',
59
+ 'feedback',
60
+ 'fewer-permission-prompts',
61
+ 'focus',
62
+ 'fork',
63
+ 'goal',
64
+ 'heapdump',
65
+ 'help',
66
+ 'hooks',
67
+ 'ide',
68
+ 'init',
69
+ 'insights',
70
+ 'install-github-app',
71
+ 'install-slack-app',
72
+ 'keybindings',
73
+ 'login',
74
+ 'logout',
75
+ 'loop',
76
+ 'mcp',
77
+ 'memory',
78
+ 'mobile',
79
+ 'model',
80
+ 'passes',
81
+ 'permissions',
82
+ 'plan',
83
+ 'plugin',
84
+ 'powerup',
85
+ 'pr-comments',
86
+ 'privacy-settings',
87
+ 'radio',
88
+ 'recap',
89
+ 'release-notes',
90
+ 'reload-plugins',
91
+ 'reload-skills',
92
+ 'remote-control',
93
+ 'remote-env',
94
+ 'rename',
95
+ 'resume',
96
+ 'review',
97
+ 'rewind',
98
+ 'run',
99
+ 'run-skill-generator',
100
+ 'sandbox',
101
+ 'schedule',
102
+ 'scroll-speed',
103
+ 'security-review',
104
+ 'setup-bedrock',
105
+ 'setup-vertex',
106
+ 'simplify',
107
+ 'skills',
108
+ 'stats',
109
+ 'status',
110
+ 'statusline',
111
+ 'stickers',
112
+ 'teleport',
113
+ 'thinking',
114
+ 'trust',
115
+ 'upgrade',
116
+ 'usage',
117
+ 'verify',
118
+ 'vim',
119
+ 'web',
120
+ 'whats-new',
121
+ 'workflow',
122
+ 'workflows',
123
+ 'worktree',
124
+ ] as const;
125
+
126
+ // Retired from the current docs but still registered by older Claude Code
127
+ // versions users may run — reserving them costs nothing (no package artefact
128
+ // uses these names) and avoids version-dependent shadowing.
129
+ const _LEGACY = [
130
+ 'bashes',
131
+ 'bug',
132
+ 'migrate-installer',
133
+ 'output-style',
134
+ 'terminal-setup',
135
+ 'theme',
136
+ 'todos',
137
+ ] as const;
138
+
139
+ /** Lowercase names a Claude-facing `/name` projection must never claim. */
140
+ export const CLAUDE_CODE_BUILTIN_NAMES: ReadonlySet<string> = new Set<string>([
141
+ ..._CURRENT,
142
+ ..._LEGACY,
143
+ ]);
144
+
145
+ /** True when `slug` would shadow a Claude Code built-in command or bundled skill. */
146
+ export function is_claude_builtin_name(slug: string): boolean {
147
+ return CLAUDE_CODE_BUILTIN_NAMES.has(slug.toLowerCase());
148
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Deterministic document slicer for oversized knowledge ingest
3
+ * (road-to-retrieval-substrate-hardening B8).
4
+ *
5
+ * Splits a large markdown document into bounded slices at the cleanest
6
+ * available boundary — heading > paragraph (blank line) > line — while
7
+ * guaranteeing three invariants a downstream re-assembler can rely on:
8
+ *
9
+ * 1. GAP-FREE + NON-OVERLAPPING — slices cover contiguous line ranges over
10
+ * the whole document, in order.
11
+ * 2. CONCAT == ORIGINAL — joining every slice's `text` in order reproduces
12
+ * the input byte-for-byte (slices are exact offset ranges).
13
+ * 3. PARENTED — every slice carries its source `parentPath` (when given) and
14
+ * its `[startLine, endLine)` range, so a chunk can point home.
15
+ *
16
+ * Deterministic: identical input + budget → identical slices.
17
+ */
18
+
19
+ export interface Slice {
20
+ parentPath: string | null;
21
+ index: number;
22
+ startLine: number; // 0-based, inclusive
23
+ endLine: number; // 0-based, exclusive
24
+ text: string;
25
+ }
26
+
27
+ /** Default per-slice character budget. */
28
+ export const DEFAULT_MAX_CHARS = 2000;
29
+
30
+ function _lineStartOffsets(text: string): number[] {
31
+ const starts = [0];
32
+ for (let i = 0; i < text.length; i++) {
33
+ if (text[i] === '\n') starts.push(i + 1);
34
+ }
35
+ return starts; // one entry per line; starts[k] = offset where line k begins
36
+ }
37
+
38
+ function _isHeading(line: string): boolean {
39
+ return /^#{1,6}\s/.test(line);
40
+ }
41
+ function _isBlank(line: string): boolean {
42
+ return line.trim() === '';
43
+ }
44
+
45
+ /**
46
+ * Slice `text` into ≤ `maxChars` pieces (best-effort — a single over-long line
47
+ * is emitted whole rather than split mid-line, preserving invariant 2).
48
+ * `parentPath` is stamped on every slice.
49
+ */
50
+ export function sliceDocument(
51
+ text: string,
52
+ opts: { maxChars?: number; parentPath?: string } = {},
53
+ ): Slice[] {
54
+ const maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;
55
+ const parentPath = opts.parentPath ?? null;
56
+ if (text === '') return [];
57
+
58
+ const starts = _lineStartOffsets(text);
59
+ const nLines = starts.length;
60
+ // lineAt(k) = the raw text of line k INCLUDING its trailing newline (if any).
61
+ const lineEnd = (k: number): number => (k + 1 < nLines ? starts[k + 1]! : text.length);
62
+ const lines: string[] = [];
63
+ for (let k = 0; k < nLines; k++) lines.push(text.slice(starts[k]!, lineEnd(k)));
64
+
65
+ const slices: Slice[] = [];
66
+ let sliceStartLine = 0;
67
+ let idx = 0;
68
+ const flush = (endLine: number): void => {
69
+ if (endLine <= sliceStartLine) return;
70
+ const from = starts[sliceStartLine]!;
71
+ const to = endLine < nLines ? starts[endLine]! : text.length;
72
+ slices.push({ parentPath, index: idx++, startLine: sliceStartLine, endLine, text: text.slice(from, to) });
73
+ sliceStartLine = endLine;
74
+ };
75
+
76
+ let curChars = 0;
77
+ for (let k = 0; k < nLines; k++) {
78
+ const lineLen = lines[k]!.length;
79
+ const wouldExceed = curChars + lineLen > maxChars && k > sliceStartLine;
80
+ // Cut BEFORE a heading that opens a new section (clean boundary), or
81
+ // when the budget would overflow — preferring a preceding blank line.
82
+ const headingBoundary = _isHeading(lines[k]!) && k > sliceStartLine;
83
+ if (headingBoundary || wouldExceed) {
84
+ // Prefer to end the previous slice at the last blank line in-range,
85
+ // so a slice ends on a paragraph boundary when one is available.
86
+ let cut = k;
87
+ if (wouldExceed && !headingBoundary) {
88
+ for (let b = k - 1; b > sliceStartLine; b--) {
89
+ if (_isBlank(lines[b]!)) {
90
+ cut = b + 1;
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ flush(cut);
96
+ curChars = 0;
97
+ for (let j = sliceStartLine; j <= k; j++) curChars += lines[j]!.length;
98
+ } else {
99
+ curChars += lineLen;
100
+ }
101
+ }
102
+ flush(nLines);
103
+ return slices;
104
+ }
105
+
106
+ /** True iff the slices reproduce `text` exactly, gap-free and non-overlapping. */
107
+ export function verifySlices(text: string, slices: readonly Slice[]): boolean {
108
+ if (slices.length === 0) return text === '';
109
+ let expectedLine = 0;
110
+ const parts: string[] = [];
111
+ for (const s of slices) {
112
+ if (s.startLine !== expectedLine) return false; // gap or overlap
113
+ expectedLine = s.endLine;
114
+ parts.push(s.text);
115
+ }
116
+ return parts.join('') === text;
117
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Hand-rolled, dependency-free lexical index for memory/knowledge retrieval
3
+ * (road-to-retrieval-substrate-hardening B2).
4
+ *
5
+ * The current `memory_lookup._score` gives every keyword match the SAME coarse
6
+ * bucket (0.8 glob / 0.6 substring), so several entries tie at the top and
7
+ * retrieval "recalls but does not rank" (the mean-tie-set 3.3 finding from
8
+ * road-to-second-brain-retrieval-precision). This module replaces that bucket
9
+ * with a continuous BM25 score over an IDF-weighted term index, plus a
10
+ * character-trigram candidate prefilter so it stays cheap at corpus scale.
11
+ *
12
+ * It is the BM25 core ADR-061 already sanctions — pure Node stdlib, NO engine
13
+ * fork, NO minisearch-class dependency. `_score` stays as the below-tripwire
14
+ * mini-corpus fallback; this index activates at the `lint_knowledge_scale`
15
+ * tripwire. Deterministic: identical docs + query → identical scores + order.
16
+ */
17
+
18
+ /** BM25 term-frequency saturation. */
19
+ export const BM25_K1 = 1.5;
20
+ /** BM25 length-normalisation strength. */
21
+ export const BM25_B = 0.75;
22
+ /** Minimum token length kept (drops single chars / punctuation noise). */
23
+ export const MIN_TOKEN_LEN = 2;
24
+
25
+ export interface IndexDoc {
26
+ id: string;
27
+ /** All searchable text of the entry, already concatenated. */
28
+ text: string;
29
+ }
30
+
31
+ interface DocStats {
32
+ id: string;
33
+ len: number; // token count
34
+ tf: Map<string, number>; // term → frequency in this doc
35
+ trigrams: Set<string>;
36
+ }
37
+
38
+ /** Lowercase, split on non-alphanumeric, keep tokens ≥ MIN_TOKEN_LEN. */
39
+ export function tokenize(s: string): string[] {
40
+ const out: string[] = [];
41
+ for (const raw of s.toLowerCase().split(/[^a-z0-9]+/)) {
42
+ if (raw.length >= MIN_TOKEN_LEN) out.push(raw);
43
+ }
44
+ return out;
45
+ }
46
+
47
+ /** Character trigrams of a normalised string (alphanumerics + single spaces). */
48
+ export function trigrams(s: string): Set<string> {
49
+ const norm = s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
50
+ const out = new Set<string>();
51
+ if (norm.length < 3) {
52
+ if (norm.length > 0) out.add(norm);
53
+ return out;
54
+ }
55
+ for (let i = 0; i + 3 <= norm.length; i++) {
56
+ out.add(norm.slice(i, i + 3));
57
+ }
58
+ return out;
59
+ }
60
+
61
+ /**
62
+ * An in-memory BM25 index over a fixed document set. Built once, queried many
63
+ * times. Stateless after construction — `rank()` / `score()` never mutate it,
64
+ * so a cached instance is safe to reuse until the underlying corpus changes.
65
+ */
66
+ export class LexicalIndex {
67
+ private readonly docs: Map<string, DocStats> = new Map();
68
+ private readonly df: Map<string, number> = new Map();
69
+ private readonly n: number;
70
+ private readonly avgdl: number;
71
+
72
+ constructor(documents: readonly IndexDoc[]) {
73
+ let totalLen = 0;
74
+ for (const d of documents) {
75
+ const tokens = tokenize(d.text);
76
+ const tf = new Map<string, number>();
77
+ for (const t of tokens) tf.set(t, (tf.get(t) ?? 0) + 1);
78
+ for (const t of tf.keys()) this.df.set(t, (this.df.get(t) ?? 0) + 1);
79
+ this.docs.set(d.id, { id: d.id, len: tokens.length, tf, trigrams: trigrams(d.text) });
80
+ totalLen += tokens.length;
81
+ }
82
+ this.n = documents.length;
83
+ this.avgdl = this.n > 0 ? totalLen / this.n : 0;
84
+ }
85
+
86
+ /** Robertson–Spärck-Jones IDF, floored at 0 so common terms never subtract. */
87
+ private idf(term: string): number {
88
+ const df = this.df.get(term) ?? 0;
89
+ if (df === 0) return 0;
90
+ return Math.max(0, Math.log(1 + (this.n - df + 0.5) / (df + 0.5)));
91
+ }
92
+
93
+ /** BM25 score of one document against the tokenised query terms. */
94
+ score(queryTerms: readonly string[], docId: string): number {
95
+ const doc = this.docs.get(docId);
96
+ if (doc === undefined || doc.len === 0) return 0;
97
+ let s = 0;
98
+ for (const term of queryTerms) {
99
+ const tf = doc.tf.get(term);
100
+ if (tf === undefined) continue;
101
+ const idf = this.idf(term);
102
+ if (idf === 0) continue;
103
+ const denom = tf + BM25_K1 * (1 - BM25_B + (BM25_B * doc.len) / (this.avgdl || 1));
104
+ s += (idf * (tf * (BM25_K1 + 1))) / denom;
105
+ }
106
+ return s;
107
+ }
108
+
109
+ /**
110
+ * Rank documents for a set of query keys (phrases are tokenised). A
111
+ * character-trigram prefilter selects candidates cheaply; the guard keeps
112
+ * any doc sharing a query TOKEN even when no trigram overlaps, so the
113
+ * prefilter never drops a genuine lexical match. Ties break by id for a
114
+ * stable, deterministic order.
115
+ */
116
+ rank(queryKeys: readonly string[]): Array<{ id: string; score: number }> {
117
+ const queryText = queryKeys.join(' ');
118
+ const qTerms = tokenize(queryText);
119
+ const qTermSet = new Set(qTerms);
120
+ const qTrigrams = trigrams(queryText);
121
+
122
+ const candidates: string[] = [];
123
+ for (const [id, doc] of this.docs) {
124
+ let hit = false;
125
+ for (const t of qTrigrams) {
126
+ if (doc.trigrams.has(t)) {
127
+ hit = true;
128
+ break;
129
+ }
130
+ }
131
+ if (!hit) {
132
+ // Guard: keep a doc that shares an exact query token even with
133
+ // no trigram overlap (short tokens / edge normalisation).
134
+ for (const t of qTermSet) {
135
+ if (doc.tf.has(t)) {
136
+ hit = true;
137
+ break;
138
+ }
139
+ }
140
+ }
141
+ if (hit) candidates.push(id);
142
+ }
143
+
144
+ const scored = candidates
145
+ .map((id) => ({ id, score: this.score(qTerms, id) }))
146
+ .filter((r) => r.score > 0);
147
+ scored.sort((a, b) => (b.score - a.score) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
148
+ return scored;
149
+ }
150
+
151
+ /** Document count — for the caller's tripwire / diagnostics. */
152
+ get size(): number {
153
+ return this.n;
154
+ }
155
+ }
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Orchestration telemetry record builder (pure, no-I/O).
3
+ *
4
+ * Builds ONE validated audit-log-v1 line carrying an `orchestration`
5
+ * sub-object, from the counts the orchestrating agent already has after an
6
+ * auto-dispatch. This is the agent-behavioral capture path decided 2026-06-30
7
+ * (maintainer decision: no hook/daemon — the agent writes directly), made
8
+ * reliable + one-command here instead of hand-authored JSON.
9
+ *
10
+ * Contracts:
11
+ * - `docs/contracts/audit-log-v1.md` (the envelope)
12
+ * - `src/agent-src/contexts/execution/orchestration-telemetry.md` (the object)
13
+ *
14
+ * PRIVACY BY CONSTRUCTION: every field is a count, enum, or id — there is NO
15
+ * field capable of holding free-form content, prompt text, or a body. Do not
16
+ * add one. (Mirrors `domain-safety-pii` § Surface 2 / artifact-engagement.)
17
+ */
18
+
19
+ export type DispatchOutcome = 'DONE' | 'DONE_WITH_CONCERNS' | 'NEEDS_CONTEXT' | 'BLOCKED' | 'killed';
20
+ export type VerifyMode = 'deterministic' | 'judge' | 'none';
21
+ export type Provenance = 'measured' | 'estimated';
22
+ export type TierChosen = 'lite' | 'medium' | 'high';
23
+ export type TierSource = 'static' | 'inferred' | 'inherit';
24
+ export type Band = 'low' | 'medium' | 'high';
25
+ export type LinePhase = 'refine' | 'memory' | 'analyze' | 'plan' | 'implement' | 'test' | 'verify' | 'report';
26
+ export type LineOutcome = 'success' | 'blocked' | 'skipped' | 'error';
27
+
28
+ /** Inputs the agent supplies about one auto-dispatch. */
29
+ export interface RecordInput {
30
+ // Orchestration object
31
+ spawn_count: number;
32
+ token_delta: number;
33
+ // Optionals accept explicit `undefined` (CLI parsing yields it); the
34
+ // builder defaults every one. Compatible with exactOptionalPropertyTypes.
35
+ token_delta_provenance?: Provenance | undefined;
36
+ tiers?: string[] | undefined;
37
+ tier_chosen?: TierChosen | null | undefined;
38
+ tier_source?: TierSource | null | undefined;
39
+ task_class?: string | null | undefined;
40
+ task_size_estimate?: number | undefined;
41
+ wall_clock_ms?: number | undefined;
42
+ /** Absolute measured tokens the dispatched slice consumed (feeds the modeled cost-%). */
43
+ dispatch_tokens?: number | null | undefined;
44
+ /** The orchestrator's own tier — the baseline the downshift cost-% measures against. */
45
+ session_tier?: string | null | undefined;
46
+ dispatch_outcome?: DispatchOutcome | undefined;
47
+ verify_mode?: VerifyMode | undefined;
48
+ /** QUALITY: subagent return adopted without parent rework. Optional boolean (null = not measured). */
49
+ first_pass_success?: boolean | undefined;
50
+ /** QUALITY: slice retried on a higher tier after a verification failure. Optional boolean (null = not measured). */
51
+ escalated?: boolean | undefined;
52
+ // Audit-log envelope (sensible defaults for a dispatch record)
53
+ phase?: LinePhase | undefined;
54
+ outcome?: LineOutcome | undefined;
55
+ confidence_band?: Band | undefined;
56
+ risk_class?: Band | undefined;
57
+ persona?: string | null | undefined;
58
+ work_id?: string | undefined;
59
+ /** ISO-8601 UTC timestamp; caller supplies (keeps this fn pure/deterministic). */
60
+ ts: string;
61
+ /** Stable id (ULID or content hash); caller supplies. */
62
+ id: string;
63
+ }
64
+
65
+ export interface BuiltLine {
66
+ line: Record<string, unknown> | null;
67
+ errors: string[];
68
+ }
69
+
70
+ const DISPATCH_OUTCOMES: readonly DispatchOutcome[] = ['DONE', 'DONE_WITH_CONCERNS', 'NEEDS_CONTEXT', 'BLOCKED', 'killed'];
71
+ const VERIFY_MODES: readonly VerifyMode[] = ['deterministic', 'judge', 'none'];
72
+ const PROVENANCES: readonly Provenance[] = ['measured', 'estimated'];
73
+ const TIERS_CHOSEN: readonly TierChosen[] = ['lite', 'medium', 'high'];
74
+ const TIER_SOURCES: readonly TierSource[] = ['static', 'inferred', 'inherit'];
75
+ const BANDS: readonly Band[] = ['low', 'medium', 'high'];
76
+ const PHASES: readonly LinePhase[] = ['refine', 'memory', 'analyze', 'plan', 'implement', 'test', 'verify', 'report'];
77
+
78
+ /** Map a dispatch outcome to the audit-log envelope outcome enum. */
79
+ function envelopeOutcome(d: DispatchOutcome): LineOutcome {
80
+ if (d === 'BLOCKED') return 'blocked';
81
+ if (d === 'NEEDS_CONTEXT') return 'skipped';
82
+ if (d === 'killed') return 'error';
83
+ return 'success'; // DONE / DONE_WITH_CONCERNS
84
+ }
85
+
86
+ function isInt(n: unknown): n is number {
87
+ return typeof n === 'number' && Number.isFinite(n) && Number.isInteger(n);
88
+ }
89
+
90
+ /**
91
+ * Build + validate the audit-log-v1 line. Returns `{line, errors}`; when
92
+ * `errors` is non-empty, `line` is null and the caller must NOT write.
93
+ */
94
+ export function buildOrchestrationLine(input: RecordInput): BuiltLine {
95
+ const errors: string[] = [];
96
+
97
+ if (!isInt(input.spawn_count) || input.spawn_count < 1) {
98
+ errors.push('spawn_count must be an integer ≥ 1 (0 = handled in-session, not a dispatch — do not record)');
99
+ }
100
+ if (!isInt(input.token_delta)) errors.push('token_delta must be an integer (negative = net saved vs the in-session baseline)');
101
+ if (input.task_size_estimate !== undefined && (!isInt(input.task_size_estimate) || input.task_size_estimate < 0)) {
102
+ errors.push('task_size_estimate must be a non-negative integer');
103
+ }
104
+ if (input.wall_clock_ms !== undefined && (!isInt(input.wall_clock_ms) || input.wall_clock_ms < 0)) {
105
+ errors.push('wall_clock_ms must be a non-negative integer');
106
+ }
107
+ if (input.dispatch_tokens != null && (!isInt(input.dispatch_tokens) || input.dispatch_tokens < 0)) {
108
+ errors.push('dispatch_tokens must be a non-negative integer (absolute tokens the dispatched slice consumed)');
109
+ }
110
+
111
+ const prov: Provenance = input.token_delta_provenance ?? 'estimated';
112
+ if (!PROVENANCES.includes(prov)) errors.push(`token_delta_provenance must be one of ${PROVENANCES.join(' | ')}`);
113
+
114
+ const dOutcome: DispatchOutcome = input.dispatch_outcome ?? 'DONE';
115
+ if (!DISPATCH_OUTCOMES.includes(dOutcome)) errors.push(`dispatch_outcome must be one of ${DISPATCH_OUTCOMES.join(' | ')}`);
116
+
117
+ const vMode: VerifyMode = input.verify_mode ?? 'deterministic';
118
+ if (!VERIFY_MODES.includes(vMode)) errors.push(`verify_mode must be one of ${VERIFY_MODES.join(' | ')}`);
119
+
120
+ if (input.tier_chosen != null && !TIERS_CHOSEN.includes(input.tier_chosen)) {
121
+ errors.push(`tier_chosen must be one of ${TIERS_CHOSEN.join(' | ')} (or omitted)`);
122
+ }
123
+ if (input.tier_source != null && !TIER_SOURCES.includes(input.tier_source)) {
124
+ errors.push(`tier_source must be one of ${TIER_SOURCES.join(' | ')} (or omitted)`);
125
+ }
126
+
127
+ const band = input.confidence_band ?? 'medium';
128
+ if (!BANDS.includes(band)) errors.push(`confidence_band must be one of ${BANDS.join(' | ')}`);
129
+ const risk = input.risk_class ?? 'low';
130
+ if (!BANDS.includes(risk)) errors.push(`risk_class must be one of ${BANDS.join(' | ')}`);
131
+ const phase = input.phase ?? 'implement';
132
+ if (!PHASES.includes(phase)) errors.push(`phase must be one of ${PHASES.join(' | ')}`);
133
+
134
+ if (input.first_pass_success !== undefined && typeof input.first_pass_success !== 'boolean') {
135
+ errors.push('first_pass_success must be a boolean (true = return adopted without parent rework) or omitted');
136
+ }
137
+ if (input.escalated !== undefined && typeof input.escalated !== 'boolean') {
138
+ errors.push('escalated must be a boolean (true = retried on a higher tier after verification failure) or omitted');
139
+ }
140
+
141
+ if (!input.ts) errors.push('ts (ISO-8601 UTC) is required');
142
+ if (!input.id) errors.push('id (ULID or content hash) is required');
143
+
144
+ if (errors.length) return { line: null, errors };
145
+
146
+ const orchestration: Record<string, unknown> = {
147
+ task_size_estimate: input.task_size_estimate ?? 0,
148
+ spawn_count: input.spawn_count,
149
+ tiers: input.tiers ?? [],
150
+ token_delta: input.token_delta,
151
+ token_delta_provenance: prov,
152
+ wall_clock_ms: input.wall_clock_ms ?? 0,
153
+ outcome: dOutcome,
154
+ verify_mode: vMode,
155
+ task_class: input.task_class ?? null,
156
+ tier_chosen: input.tier_chosen ?? null,
157
+ tier_source: input.tier_source ?? null,
158
+ dispatch_tokens: input.dispatch_tokens ?? null,
159
+ session_tier: input.session_tier ?? null,
160
+ first_pass_success: input.first_pass_success ?? null,
161
+ escalated: input.escalated ?? null,
162
+ };
163
+
164
+ const line: Record<string, unknown> = {
165
+ schema_version: 1,
166
+ id: input.id,
167
+ ts: input.ts,
168
+ work_id: input.work_id ?? `orchestration-${input.ts}`,
169
+ phase,
170
+ outcome: input.outcome ?? envelopeOutcome(dOutcome),
171
+ confidence_band: band,
172
+ risk_class: risk,
173
+ memory: { asks: 0, hits: 0 },
174
+ verify: { claims: 0, first_try_passes: 0 },
175
+ rules_applied: ['delegation-policy'],
176
+ persona: input.persona ?? null,
177
+ input_kind: 'orchestration',
178
+ type: 'phase',
179
+ orchestration,
180
+ };
181
+
182
+ return { line, errors: [] };
183
+ }