baldart 5.0.1 → 5.1.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 (32) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +3 -3
  3. package/VERSION +1 -1
  4. package/framework/.claude/agents/CHANGELOG.md +45 -0
  5. package/framework/.claude/agents/REGISTRY.md +3 -3
  6. package/framework/.claude/agents/senior-researcher.md +166 -151
  7. package/framework/.claude/skills/prd/CHANGELOG.md +16 -0
  8. package/framework/.claude/skills/prd/SKILL.md +1 -1
  9. package/framework/.claude/skills/prd/references/discovery-phase.md +5 -1
  10. package/framework/.claude/skills/prd/references/research-phase.md +32 -12
  11. package/framework/.claude/skills/research/CHANGELOG.md +9 -0
  12. package/framework/.claude/skills/research/SKILL.md +94 -0
  13. package/framework/.claude/skills/research/assets/report-compare.template.md +50 -0
  14. package/framework/.claude/skills/research/assets/report-decision.template.md +42 -0
  15. package/framework/.claude/skills/research/assets/report-deep.template.md +61 -0
  16. package/framework/.claude/skills/research/assets/report-regulatory.template.md +44 -0
  17. package/framework/.claude/skills/research/references/playbook.md +112 -0
  18. package/framework/.claude/skills/research/scripts/rebuild-research-index.mjs +77 -0
  19. package/framework/agents/index.md +2 -0
  20. package/framework/agents/research-protocol.md +228 -0
  21. package/framework/agents/skills-mapping.md +17 -0
  22. package/framework/docs/PROJECT-CONFIGURATION.md +23 -0
  23. package/framework/templates/baldart.config.template.yml +6 -0
  24. package/framework/templates/research-index.template.md +13 -0
  25. package/framework/templates/research-sources.CHANGELOG.md +14 -0
  26. package/framework/templates/research-sources.template.md +31 -0
  27. package/package.json +1 -1
  28. package/src/commands/configure.js +26 -0
  29. package/src/commands/doctor.js +82 -0
  30. package/src/commands/update.js +48 -1
  31. package/src/utils/research-library.js +92 -0
  32. package/src/utils/symlinks.js +3 -0
@@ -0,0 +1,228 @@
1
+ # Research Protocol — profiles, library, reuse, and the source matrix
2
+
3
+ **Purpose**: the single SSOT for how BALDART conducts research. Consumed by the
4
+ `senior-researcher` agent (worker) and the `/research` skill (interactive
5
+ orchestrator); `/prd` Step 2.5 spawns the worker directly with the tokens
6
+ defined here. One research contract used to fit every request — a
7
+ publication-grade literature review — which conflicted with what orchestrators
8
+ actually need (fast, stack-aware, decision-ready answers) and buried finished
9
+ research where it was never reused. This module fixes both: **profiles** shape
10
+ the output to the purpose, the **library** makes every report a reusable,
11
+ indexed asset, and the **source matrix** routes each research nature to the
12
+ right sources and grows over time.
13
+
14
+ This is the research-side sibling of `analysis-profiles.md` (same mechanism:
15
+ one agent + a `PROFILE=` prompt token + one SSOT module — never a twin agent).
16
+ Prompt-level, zero new config keys beyond `paths.research_dir`, zero runtime
17
+ dependency.
18
+
19
+ ## Contract
20
+
21
+ - **Dispatch**: consumers cite a section as
22
+ `agents/research-protocol.md SECTION=<profiles|library|reuse|sources>`.
23
+ When you (an agent or skill) need the full procedure, Grep this file for the
24
+ heading `### SECTION: <name>` and Read ONLY that section — never this module
25
+ end-to-end.
26
+ - **Degrade-safe**: everything here degrades, never aborts. No
27
+ `paths.research_dir` → deliver to the prompt-supplied path (or
28
+ `${paths.docs_dir}/` as last resort) and skip the library steps. No
29
+ `SOURCES.md` → fall back to the framework default matrix. No matrix at all →
30
+ proceed with judgment and say so in the report.
31
+ - The agent body keeps 1-line BINDING versions of these rules inline; if the
32
+ inline line and this module diverge, **this module wins** — fix the agent.
33
+
34
+ ---
35
+
36
+ ### SECTION: profiles
37
+
38
+ The `PROFILE=<decision|deep|compare|regulatory>` token in the spawn prompt
39
+ selects the research contract. **Token absent → `deep`** (preserves the legacy
40
+ full-report semantics for direct invocation and for existing spawners that
41
+ pass no token).
42
+
43
+ | Profile | Typical consumer | Output contract | Depth |
44
+ |---|---|---|---|
45
+ | `decision` | `/prd` Step 2.5, orchestrators mid-pipeline | Per-topic blocks, **recommendation first**: Recommendation (for THIS stack) → Best practices → Gotchas / anti-patterns → **Contradictions & Unknowns (mandatory section)** → Sources. Matches the `/prd` "Processing Findings" shape byte-for-byte. | Time-boxed; 1–3 questions; no ceremony sections |
46
+ | `deep` | direct user invocation, `/research` | The full §0–§11 report (Retrieval Index, TOC, Executive Summary, Taxonomy, Comparative Analysis, Key Findings, Recommendation, Risks, Evidence Map, Annotated Bibliography, Appendix with Search Log) | Full survey; see the DEPTH token below |
47
+ | `compare` | technology / vendor evaluations | Comparison matrix on fixed axes — performance, cost, complexity, risk, maturity, adoption — then Recommendation + explicit "when NOT to use" per option | Medium; one matrix, no unbounded exploration |
48
+ | `regulatory` | `/prd` S2 signal, compliance questions | Obligation checklist (requirement → source → deadline/threshold → implementation note), jurisdiction-aware; normative sources only for the obligations themselves | High rigor, tightly scoped to the named jurisdiction(s) |
49
+
50
+ Rules that apply to EVERY profile:
51
+
52
+ - **AI-readable minimum**: every report — not just `deep` — is consumed by an
53
+ AI reader with limited context: short paragraphs, dense factual bullets,
54
+ modular self-contained sections, consistent terminology. The full ceremony
55
+ (Retrieval Index, Evidence Map, Search Log) belongs to `deep` only; the
56
+ lighter profiles carry traceability through their per-topic **Sources**
57
+ lists (dated + evidence-labeled).
58
+ - **Recommendation is stack-bound**: evaluate against the stack signature and
59
+ the internal-repo findings supplied in the prompt — "best for THIS project",
60
+ never "best in the abstract".
61
+ - **Recency bias**: for fast-moving topics (frameworks, APIs, tooling) prefer
62
+ sources < 18 months old; date-stamp every source in the bibliography.
63
+ - **Contradictions surface early**: when evidence is split or absent, say so
64
+ near the top (in `decision` it is a mandatory section) — a hidden
65
+ disagreement is the failure mode that sends a project in the wrong
66
+ direction.
67
+
68
+ **`DEPTH=<1..3>` (deep profile only, default 1)** — the iterative
69
+ breadth/depth loop, opt-in so legacy spawns cost what they cost today:
70
+
71
+ - `DEPTH=1` (default): single research pass — today's behavior.
72
+ - `DEPTH=2..3`: before searching, generate 3–5 expert *perspectives* on the
73
+ topic (architect, operator, security, cost-owner, end-user — pick what fits)
74
+ and derive each perspective's questions. Then per round: search → extract
75
+ *learnings* + *follow-up directions* → recurse into the most promising
76
+ directions until DEPTH is exhausted. Each round is bounded by the tool
77
+ budget (`agents/agent-operating-protocol.md SECTION=tool-budget`).
78
+ - At `DEPTH>=2`, before finalizing run a citation spot-check (CoVe-lite):
79
+ WebFetch the sources behind the decision-driving claims and verify they
80
+ actually support them; demote or drop any that don't.
81
+
82
+ ### SECTION: library
83
+
84
+ The research library lives at `${paths.research_dir}` (key in
85
+ `baldart.config.yml`; seeded by the BALDART CLI):
86
+
87
+ ```
88
+ ${paths.research_dir}/
89
+ INDEX.md <- THE map. Agents do lookup HERE first, never by walking dirs.
90
+ SOURCES.md <- live source matrix (consumer-owned; see SECTION: sources)
91
+ architecture/ integrations/ regulatory/ ux-patterns/ algorithms/ tooling/
92
+ _archive/ <- superseded reports: banner added, row removed from INDEX
93
+ ```
94
+
95
+ - **Category**: pick the closest of the six; a report that genuinely fits none
96
+ goes in the closest match with a distinguishing tag (do NOT invent new
97
+ top-level directories — the closed set keeps lookup deterministic).
98
+ - **Report frontmatter** (every report, mandatory — this is the source of
99
+ truth; INDEX.md is a regenerable view over it):
100
+
101
+ ```yaml
102
+ ---
103
+ id: RES-<YYYYMMDD>-<slug> # date + slug, NEVER max+1 (see Concurrency)
104
+ title: <one line>
105
+ category: architecture|integrations|regulatory|ux-patterns|algorithms|tooling
106
+ tags: [<free-form, lowercase, for findability — be generous>]
107
+ profile: decision|deep|compare|regulatory
108
+ nature: scientific|dev|regulatory|ux|market
109
+ stack_signature: "<framework + database + auth, from baldart.config.yml>"
110
+ date: <YYYY-MM-DD>
111
+ valid_until: <YYYY-MM-DD> # date + category TTL (see SECTION: reuse)
112
+ status: current # current | superseded
113
+ questions:
114
+ - <the research questions answered>
115
+ sources_count: <n>
116
+ related: [] # ids of related reports
117
+ ---
118
+ ```
119
+
120
+ - **INDEX.md row format** (append-only; one row per report):
121
+
122
+ ```
123
+ | <id> | <title> | <category> | <profile> | <tags csv> | <date> | <valid_until> | <path> |
124
+ ```
125
+
126
+ - **Archive, never delete**: a superseded report moves to `_archive/`, gets a
127
+ first-line banner `> [!SUPERSEDED] Replaced by <id>. Non-authoritative.`,
128
+ `status: superseded` in frontmatter, and its INDEX row is removed (out of
129
+ the index = invisible to lookup, history preserved in git).
130
+ - **Concurrency**: IDs are `RES-<YYYYMMDD>-<slug>` (add a short unique suffix
131
+ only on a real collision) — never "max existing + 1", which races under
132
+ parallel spawns. When an orchestrator (the `/research` skill, `/prd`) runs
133
+ the show, **the orchestrator pre-allocates id + full path in each spawn
134
+ prompt and serializes the INDEX appends itself** in its filing step; a
135
+ worker spawned solo appends its own row. Reconciliation is deterministic:
136
+ `rebuild-research-index.mjs` (shipped with the `/research` skill)
137
+ regenerates INDEX.md from report frontmatter.
138
+ - **Delivery-path resolution** (total, in order — never undefined, never
139
+ abort): (1) an explicit output path in the spawn prompt is AUTHORITATIVE;
140
+ (2) else `${paths.research_dir}/<category>/<id>-<slug>.md`;
141
+ (3) else (no path, no key) `${paths.docs_dir}/`.
142
+
143
+ ### SECTION: reuse
144
+
145
+ Research repeats across features and projects. Before ANY new research, run
146
+ this pre-flight (skip it only when `paths.research_dir` is missing/empty —
147
+ then use the prompt-supplied path and proceed as a plain one-shot run):
148
+
149
+ 1. **Lookup**: Read `INDEX.md` (only the index — not the reports). Match the
150
+ topic against `title`, `tags`, `category`.
151
+ 2. **On hit, judge fitness**:
152
+ - **Freshness**: compare today against `valid_until`. Category TTLs (used
153
+ to SET `valid_until` at write time): `regulatory` 6 months ·
154
+ `integrations`/`tooling` 6–12 · `ux-patterns` 12 ·
155
+ `architecture`/`algorithms` 24.
156
+ - **Stack match**: compare the report's `stack_signature` with the current
157
+ one.
158
+ 3. **Decide and DECLARE the decision in your report/return** (`REUSE:` line):
159
+ - `FULL_REUSE` — fresh + same stack + questions covered → return the
160
+ existing report; do not search again.
161
+ - `DELTA` — partially stale, stack drifted, or questions partially covered
162
+ → update ONLY what expired, publish as a new report that `related:`-links
163
+ the old one; the old report is archived per SECTION: library.
164
+ - `NEW` — no usable hit.
165
+ - **Anti-false-reuse guard**: doubtful staleness or a different stack →
166
+ `DELTA`, never `FULL_REUSE`. Reuse must never silently serve a stale
167
+ answer to a decision-making consumer.
168
+ 4. **Orchestrator polling contract**: when the spawn prompt pre-allocates an
169
+ output path, ALWAYS write a file there — the new report (`NEW`/`DELTA`), or
170
+ a stub for `FULL_REUSE`:
171
+
172
+ ```
173
+ RESEARCH_OUTPUT_PATH: <this file's own path>
174
+ REUSE: FULL_REUSE -> <path of the existing report>
175
+ ```
176
+
177
+ so a file-exists poll never times out and never matches a stale artifact.
178
+
179
+ ### SECTION: sources
180
+
181
+ The source matrix routes each research **nature** to the right sources. Schema
182
+ (rows = natures; the columns are fixed):
183
+
184
+ | Column | Meaning |
185
+ |---|---|
186
+ | `nature` | scientific · dev · regulatory · ux · market |
187
+ | `primary` | search these first; sufficient alone for [STRONG]/[MODERATE] claims |
188
+ | `secondary` | corroboration and leads; never sole support for a decision-driving claim |
189
+ | `avoid` | known-poor signal for this nature (content farms, marketing, SEO spam) |
190
+ | `notes` | nature-specific heuristics (recency windows, venue quality cues) |
191
+
192
+ **Resolution order** (no dual-SSOT — the default matrix lives in ONE file):
193
+
194
+ 1. `${paths.research_dir}/SOURCES.md` — the live, consumer-owned matrix
195
+ (framework defaults + the project's `## Local additions`).
196
+ 2. `.framework/framework/templates/research-sources.template.md` — the
197
+ framework default matrix (readable inside every consumer install).
198
+ 3. Neither readable → proceed with judgment and state in the report that no
199
+ source matrix was available.
200
+
201
+ **Growth loop** (how the matrix improves project after project): when a run
202
+ hits a matrix gap — an unmapped nature, a newly discovered source that proved
203
+ high-quality in THIS run, or a mapped source that proved poor — append to the
204
+ END of the report:
205
+
206
+ ```markdown
207
+ ## SOURCE_MATRIX_CANDIDATE
208
+
209
+ - nature: <matrix row>
210
+ change: add-source | demote-source | new-nature
211
+ source: <name + URL>
212
+ tier: primary | secondary | avoid
213
+ evidence: <what happened in this run that proves it, 1-3 lines>
214
+
215
+ ### Prompt for BALDART (copy-paste)
216
+
217
+ Aggiorna la matrice fonti in `framework/templates/research-sources.template.md`:
218
+ <change> per nature `<nature>`: `<source>` come `<tier>` — evidenza: <evidence>.
219
+ Bump `matrix_version` (minor per add, patch per demote/note) e aggiungi la voce
220
+ a `framework/templates/research-sources.CHANGELOG.md`.
221
+ ```
222
+
223
+ (the research twin of skill-improver's `## Upstream Candidates`). Also mention
224
+ the candidate in your COMPACT return so the orchestrator can act on it. The
225
+ `/research` skill then offers two applications: (a) immediately to the local
226
+ `SOURCES.md` `## Local additions`, and/or (b) the copy-paste prompt upstream to
227
+ the BALDART maintainer session. Emit a candidate ONLY with run-local evidence —
228
+ never from general knowledge.
@@ -422,6 +422,23 @@ competing methodology — it detects the task type and passes the matching
422
422
  - "/capture"
423
423
  - "Salva sintesi" / "distilla conversazione" / "questa conversazione in wiki"
424
424
 
425
+ ### research
426
+
427
+ **When to use**:
428
+
429
+ - Interactive, routed research over the research library (`paths.research_dir`)
430
+ - Scopes the question, routes profile (`decision`/`deep`/`compare`/`regulatory`) +
431
+ source policy by nature, checks the library for reusable prior research
432
+ BEFORE searching, launches `senior-researcher`, files + indexes the report
433
+ - Closes the source-matrix growth loop (`SOURCE_MATRIX_CANDIDATE`)
434
+ - NOT for `/prd`'s embedded research step (prd spawns the agent directly)
435
+
436
+ **Triggers**:
437
+
438
+ - "/research"
439
+ - "Fai una ricerca" / "ricerca best practice" / "confronta X e Y"
440
+ - "È già stato ricercato?" / "cosa dice la letteratura su"
441
+
425
442
  ### graph-align
426
443
 
427
444
  **When to use**:
@@ -105,6 +105,7 @@ Empty string `""` means the concept doesn't exist in your project. Skills gated
105
105
  | `backlog_dir` | `backlog` | new, prd, context-primer |
106
106
  | `adrs_dir` | `docs/decisions` | prd, prd-add |
107
107
  | `prd_dir` | `docs/prd` | prd, prd-add, ui-design |
108
+ | `research_dir` | `docs/research` | senior-researcher, research, prd (Step 2.5) — the research library (v5.1.0) |
108
109
  | `docs_dir` | `docs` | code-reviewer, senior-researcher, worktree-manager (rg-search fallback root) |
109
110
  | `references_dir` | `docs/references` | new, prd, context-primer, doc-writing-for-rag, simplify |
110
111
  | `wiki_dir` | `docs/wiki` | capture, context-primer |
@@ -129,6 +130,28 @@ e2e_review:
129
130
  | `pixel_diff_threshold` | float `0.0`–`1.0` | `0.02` | When the implementation screenshot vs mockup pixel-diff is below this fraction, the orchestrator skips the Vision call for that route and treats it as a pass. Primary latency / cost saver — most routes pass pixel-diff cleanly. Set higher to skip Vision more aggressively; set to `0.0` to always invoke Vision. |
130
131
  | `require_override_reason` | bool | `true` | When self-heal exhausts iterations and the user chooses to override the gate, a written reason is mandatory. The reason is recorded both in the batch tracker's `## Issues & Flags` (as `[E2E-OVERRIDE] <reason>`) and in `.baldart/e2e-review/<CARD-ID>/report.json`. Set `false` to allow silent override (not recommended). |
131
132
 
133
+ ### 4.2.2 `research_dir` — the research library (v5.1.0+)
134
+
135
+ Consumer-owned home of every research report produced by `senior-researcher`
136
+ (directly, via the `/research` skill, or by `/prd` Step 2.5). Layout, report
137
+ frontmatter, reuse pre-flight and the source matrix are defined in
138
+ `framework/agents/research-protocol.md`.
139
+
140
+ Lifecycle:
141
+
142
+ - **Creation** — `baldart configure` (right after writing the key; default
143
+ `docs/research` is proposed on FIRST encounter only) and `baldart doctor`
144
+ (`create-research-dir`, confirmable). Seeds `INDEX.md` (the library map) and
145
+ `SOURCES.md` (the live source matrix) from framework templates.
146
+ - **Update** — `baldart update` only backfills missing seeds into an EXISTING
147
+ directory; it never recreates a deleted library.
148
+ - **Opt-out** — set the key to `""`: respected everywhere, never re-proposed,
149
+ never resurrected.
150
+ - **Source matrix versioning** — `SOURCES.md` carries `matrix_version`
151
+ frontmatter; when the framework template ships a newer matrix, `doctor`
152
+ raises an ADVISORY (never overwrites — your `## Local additions` and manual
153
+ merges are yours).
154
+
132
155
  ### 4.3 `identity` — brand and audience facts
133
156
 
134
157
  | Key | Type | Notes |
@@ -46,6 +46,12 @@ paths:
46
46
  backlog_dir: "" # e.g. backlog
47
47
  adrs_dir: "" # e.g. docs/decisions
48
48
  prd_dir: "" # e.g. docs/prd
49
+ # Research library (since v5.1.0). Consumer-owned home of every research
50
+ # report (senior-researcher / /research skill / /prd Step 2.5), indexed for
51
+ # reuse. Seeded with INDEX.md + SOURCES.md by `configure`/`doctor`; `update`
52
+ # only backfills seeds into an existing dir. Empty = deliberate opt-out
53
+ # (never re-proposed, never resurrected). Protocol: agents/research-protocol.md.
54
+ research_dir: "" # e.g. docs/research
49
55
  docs_dir: "" # e.g. docs (umbrella docs root; rg-search fallback used by code-reviewer / senior-researcher / worktree-manager)
50
56
  references_dir: "" # e.g. docs/references
51
57
  wiki_dir: "" # e.g. docs/wiki (LLM-wiki overlay)
@@ -0,0 +1,13 @@
1
+ # Research Library — INDEX
2
+
3
+ > **This file is THE map of the research library.** Agents resolve reuse
4
+ > lookups against this index only — a report without a row here is invisible.
5
+ > Rows are **append-only**; superseded reports move to `_archive/` and their
6
+ > row is REMOVED (archive, never delete). The frontmatter of each report is
7
+ > the source of truth; this index is a regenerable view
8
+ > (`rebuild-research-index.mjs` in the `/research` skill reconciles it).
9
+ >
10
+ > Protocol: `agents/research-protocol.md SECTION=library`.
11
+
12
+ | id | title | category | profile | tags | date | valid_until | path |
13
+ |----|-------|----------|---------|------|------|-------------|------|
@@ -0,0 +1,14 @@
1
+ # Changelog — research source matrix
2
+
3
+ History of `research-sources.template.md` (`matrix_version` frontmatter).
4
+ Framework-internal (never installed into consumers — consumers hold their own
5
+ seeded `SOURCES.md` copy and merge updates via the doctor advisory). Bump
6
+ `matrix_version` in the same edit: MINOR for added sources/natures, PATCH for
7
+ demotions and note refinements.
8
+
9
+ ## 1.0.0 — 2026-07-03 (framework v5.1.0)
10
+
11
+ - Initial default matrix: 5 natures (scientific, dev, regulatory, ux, market)
12
+ × primary/secondary/avoid/notes, distilled from the v5.1.0 research-layer
13
+ design pass (OSS recon: GPT Researcher, dzhng/deep-research, STORM,
14
+ open_deep_research; house evidence-strength labels).
@@ -0,0 +1,31 @@
1
+ ---
2
+ matrix_version: 1.0.0
3
+ ---
4
+
5
+ # Research Source Matrix
6
+
7
+ > Routes each research **nature** to the right sources. Read by
8
+ > `senior-researcher` on every run (resolution order and growth loop:
9
+ > `agents/research-protocol.md SECTION=sources`). This copy is
10
+ > **consumer-owned**: BALDART seeds it once and never overwrites it. Framework
11
+ > updates to the default matrix arrive as a doctor ADVISORY (compare
12
+ > `matrix_version` against the framework template) — merging is a human
13
+ > decision. Project-specific sources go under `## Local additions`.
14
+
15
+ ## Default matrix (framework, v1.0.0)
16
+
17
+ | nature | primary | secondary | avoid | notes |
18
+ |---|---|---|---|---|
19
+ | **scientific** (algorithms, protocols, ML, formal methods) | Peer-reviewed venues (ACM DL, IEEE Xplore, USENIX, NeurIPS/ICML/ICLR), arXiv (flag as preprint), standards bodies (W3C, IETF, NIST) | Survey papers as entry points; Google Scholar citation chains; reputable lab/engineering blogs (paper-backed) | Popular-science rewrites; Medium summaries of papers; vendor "research" without artifacts | Prefer replicated results; extract method + dataset + metrics, not just conclusions |
20
+ | **dev** (frameworks, libraries, integration patterns, tooling) | Official documentation + release notes; the project's own GitHub repo (issues, discussions, RFCs); official engineering blogs | High-signal engineering blogs; conference talks; Stack Overflow accepted answers (check dates); HN threads for failure modes | SEO content farms ("Top 10 X in 2026"); AI-generated tutorial mills; outdated tutorials (> 18 months for fast-moving stacks) | Version-pin every claim (behavior changes across majors); GitHub issues reveal real gotchas docs hide |
21
+ | **regulatory** (compliance, privacy, accessibility, sector rules) | Primary legal texts (EUR-Lex, official gazettes); regulator guidance (EDPB, garanti, agencies); official standards (WCAG/W3C, PCI SSC, ISO abstracts) | Big-firm legal analyses (dated + jurisdiction-tagged); IAPP; specialized compliance blogs | Vendor compliance-marketing pages; forum legal opinions; anything without a jurisdiction + date | ALWAYS name the jurisdiction; obligations cite the primary text, practices may cite secondary |
22
+ | **ux** (interaction patterns, accessibility-in-practice, design systems) | Nielsen Norman Group; established design systems (Material, HIG, Carbon, Polaris) as pattern evidence; WCAG techniques; peer-reviewed HCI (CHI) | Baymard (e-commerce); reputable case studies with data; a11y practitioner blogs (Deque, TPGi) | Dribbble/Behance as evidence of usability (aesthetics ≠ usability); engagement-bait "UX laws" listicles | Pattern adoption across 2+ major design systems = [MODERATE] evidence of validity |
23
+ | **market** (vendor evaluation, pricing, adoption, ecosystem health) | Official pricing pages + changelogs + SLAs + status pages (dated snapshots); public financial/usage disclosures | Independent benchmarks (methodology visible); StackShare-class adoption signals; comparative engineering posts by USERS of the tool | Vendor-vs-competitor comparison pages; sponsored reviews; analyst quadrants without methodology access | Pricing claims MUST carry the retrieval date; ecosystem health = commit cadence + issue response, not stars |
24
+
25
+ ## Local additions
26
+
27
+ <!-- Project-specific sources grow here (via the /research skill's matrix loop
28
+ or by hand). Same columns. BALDART never touches this section. -->
29
+
30
+ | nature | primary | secondary | avoid | notes |
31
+ |---|---|---|---|---|
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "5.0.1",
3
+ "version": "5.1.0",
4
4
  "description": "Claude Agent Framework - Reusable framework for coordinating AI agents and humans in software projects",
5
5
  "bin": {
6
6
  "baldart": "./bin/baldart.js"
@@ -479,6 +479,7 @@ function detect(cwd = process.cwd()) {
479
479
  backlog_dir: exists('backlog') ? 'backlog' : '',
480
480
  adrs_dir: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0 ? 'docs/decisions' : '',
481
481
  prd_dir: exists('docs/prd') ? 'docs/prd' : '',
482
+ research_dir: exists('docs/research') ? 'docs/research' : '',
482
483
  docs_dir: exists('docs') ? 'docs' : '',
483
484
  references_dir: exists('docs/references') ? 'docs/references' : '',
484
485
  wiki_dir: exists('docs/wiki') ? 'docs/wiki' : '',
@@ -814,6 +815,7 @@ async function interactivePrompts(merged, detected) {
814
815
  ['backlog_dir', 'Backlog directory', () => merged.features.has_backlog],
815
816
  ['adrs_dir', 'ADR directory', () => merged.features.has_adrs],
816
817
  ['prd_dir', 'PRD directory', () => merged.features.has_prd_workflow],
818
+ ['research_dir', 'Research library directory (empty = disable)', () => true],
817
819
  ['docs_dir', 'Docs root directory (rg-search umbrella)', () => true],
818
820
  ['references_dir', 'References docs root', () => true],
819
821
  ['wiki_dir', 'Wiki overlay directory', () => merged.features.has_wiki_overlay],
@@ -1425,6 +1427,16 @@ async function configure(opts = {}) {
1425
1427
  // mergePreserving(target, source) keeps target where set, fills from source.
1426
1428
  let merged = mergePreserving(existing || {}, detected);
1427
1429
  merged = mergePreserving(merged, template);
1430
+
1431
+ // paths.research_dir (v5.1.0): default `docs/research` on FIRST encounter only
1432
+ // (key never declared in the consumer's config). An explicitly emptied key is
1433
+ // a deliberate opt-out and is respected everywhere — never re-proposed, never
1434
+ // resurrected (research-protocol.md § degrade-safe).
1435
+ const researchKeyDeclared = !!(existing && existing.paths && 'research_dir' in existing.paths);
1436
+ if (!researchKeyDeclared && merged.paths && !merged.paths.research_dir) {
1437
+ merged.paths.research_dir = 'docs/research';
1438
+ if (detected.paths) detected.paths.research_dir = 'docs/research';
1439
+ }
1428
1440
  merged.version = SCHEMA_VERSION;
1429
1441
 
1430
1442
  const dsSignals = detected.stack.design_system_signals || {};
@@ -1502,6 +1514,20 @@ async function configure(opts = {}) {
1502
1514
  UI.warning(`Agent re-merge after configure failed (${err.message}) — run \`npx baldart doctor\` to heal.`);
1503
1515
  }
1504
1516
 
1517
+ // v5.1.0 — research library: create + seed ${paths.research_dir} right after
1518
+ // the key is written. Creation ownership lives HERE (and in doctor repairs);
1519
+ // `update` only backfills seeds into an existing dir. Fail-safe: configure
1520
+ // must never die on a scaffolding hiccup — doctor heals later.
1521
+ try {
1522
+ const { ensureResearchLibrary } = require('../utils/research-library');
1523
+ const createdResearch = ensureResearchLibrary(cwd, merged, { createDir: true });
1524
+ if (createdResearch.length) {
1525
+ UI.success(`Research library initialized at ${merged.paths.research_dir}/`);
1526
+ }
1527
+ } catch (err) {
1528
+ UI.warning(`Research library setup failed (${err.message}) — run \`npx baldart doctor\` to heal.`);
1529
+ }
1530
+
1505
1531
  // Ensure overlays dir exists (user-owned)
1506
1532
  const overlaysAbs = path.join(cwd, OVERLAYS_DIR);
1507
1533
  if (!fs.existsSync(overlaysAbs)) {
@@ -656,6 +656,39 @@ async function detectState(cwd, opts = {}) {
656
656
  }
657
657
  } catch (_) { /* never block doctor on design-system probe */ }
658
658
 
659
+ // ---- Research library (since v5.1.0) --------------------------------
660
+ // Gated on paths.research_dir being SET (empty = deliberate opt-out —
661
+ // never re-proposed, per research-protocol.md § degrade-safe). Three
662
+ // probes: dir missing (repair), seeds missing (backfill), source-matrix
663
+ // version behind the framework template (ADVISORY only — merging the
664
+ // matrix is a human decision, doctor never overwrites SOURCES.md).
665
+ state.researchDirMissing = false;
666
+ state.researchSeedMissing = [];
667
+ state.researchSourcesStale = null;
668
+ try {
669
+ const researchDir = (config && !config.__malformed && config.paths && config.paths.research_dir) || '';
670
+ if (typeof researchDir === 'string' && researchDir.trim()) {
671
+ const dirAbs = path.join(cwd, researchDir.trim());
672
+ if (!fs.existsSync(dirAbs)) {
673
+ state.researchDirMissing = true;
674
+ } else {
675
+ for (const seed of ['INDEX.md', 'SOURCES.md']) {
676
+ if (!fs.existsSync(path.join(dirAbs, seed))) state.researchSeedMissing.push(seed);
677
+ }
678
+ const { readMatrixVersion } = require('../utils/research-library');
679
+ const live = readMatrixVersion(path.join(dirAbs, 'SOURCES.md'));
680
+ const tpl = readMatrixVersion(path.join(cwd, '.framework', 'framework', 'templates', 'research-sources.template.md'));
681
+ if (live && tpl && live !== tpl) {
682
+ const toNum = (v) => v.split('.').map(Number);
683
+ const [lM, lm, lp] = toNum(live); const [tM, tm, tp] = toNum(tpl);
684
+ if (tM > lM || (tM === lM && (tm > lm || (tm === lm && tp > lp)))) {
685
+ state.researchSourcesStale = { live, template: tpl };
686
+ }
687
+ }
688
+ }
689
+ }
690
+ } catch (_) { /* never block doctor on research probe */ }
691
+
659
692
  // ---- Auto-deploy allowlist presence (since v4.59.0) ----------------
660
693
  // Purely informational: git.auto_deploy bounds what `/new -auto-ship`
661
694
  // may execute as an OUTWARD action. An empty allowlist is the SAFE
@@ -1343,6 +1376,55 @@ function planActions(state) {
1343
1376
  });
1344
1377
  }
1345
1378
 
1379
+ // Research library (since v5.1.0). Creation/repair lives HERE (and in
1380
+ // configure) — update only backfills seeds into an existing dir. autoOk:
1381
+ // unlike the i18n registry ("no safe auto-create without project context"),
1382
+ // the research seeds are fully GENERIC (an empty index + the framework
1383
+ // default source matrix) — zero project context needed, so unattended
1384
+ // creation is safe. Precedent for consumer-space writes: tokens-build.
1385
+ if (state.researchDirMissing) {
1386
+ actions.push({
1387
+ key: 'create-research-dir',
1388
+ label: 'Create the research library (paths.research_dir is set but the directory is missing)',
1389
+ why: 'baldart.config.yml declares a research library but the directory does not exist on disk. Creating it (with the INDEX.md + SOURCES.md seeds and the category subdirs) restores the reuse pre-flight for senior-researcher / /research / /prd Step 2.5. If you deleted it on purpose, empty paths.research_dir instead — an empty key is a respected opt-out.',
1390
+ autoOk: true, // generic seeds, idempotent, no project context needed
1391
+ run: async () => {
1392
+ const cfg = loadConfig(state.cwd);
1393
+ const { ensureResearchLibrary } = require('../utils/research-library');
1394
+ const created = ensureResearchLibrary(state.cwd, cfg, { createDir: true });
1395
+ if (created.length) UI.success(`Research library initialized (${created.join(', ')}).`);
1396
+ else UI.info('Nothing to create — library already present.');
1397
+ },
1398
+ });
1399
+ }
1400
+ if (state.researchSeedMissing && state.researchSeedMissing.length > 0) {
1401
+ actions.push({
1402
+ key: 'backfill-research-seeds',
1403
+ label: `Backfill research library seed file(s): ${state.researchSeedMissing.join(', ')}`,
1404
+ why: `The research library directory exists but ${state.researchSeedMissing.join(' and ')} are missing (pre-v5.1 install or manual cleanup). The seeds are generic framework templates (empty index / default source matrix); existing files are never overwritten.`,
1405
+ autoOk: true, // copy-if-absent only
1406
+ run: async () => {
1407
+ const cfg = loadConfig(state.cwd);
1408
+ const { ensureResearchLibrary } = require('../utils/research-library');
1409
+ const created = ensureResearchLibrary(state.cwd, cfg, { createDir: false });
1410
+ if (created.length) UI.success(`Seeded: ${created.join(', ')}.`);
1411
+ else UI.info('Nothing to backfill.');
1412
+ },
1413
+ });
1414
+ }
1415
+ if (state.researchSourcesStale) {
1416
+ actions.push({
1417
+ key: 'research-sources-advisory',
1418
+ label: `Research source matrix behind framework default (${state.researchSourcesStale.live} < ${state.researchSourcesStale.template})`,
1419
+ why: `Your SOURCES.md carries matrix_version ${state.researchSourcesStale.live} while the framework template ships ${state.researchSourcesStale.template}. ADVISORY only — SOURCES.md is consumer-owned and doctor never overwrites it. Compare with .framework/framework/templates/research-sources.template.md (its CHANGELOG sibling lists what changed) and merge what you want, keeping your "## Local additions".`,
1420
+ autoOk: true, // advisory print-only; run() does NOT mutate anything
1421
+ run: () => {
1422
+ UI.info(`Source matrix: local ${state.researchSourcesStale.live} vs framework ${state.researchSourcesStale.template}.`);
1423
+ UI.info('Diff against .framework/framework/templates/research-sources.template.md and merge manually (your `## Local additions` section is yours).');
1424
+ },
1425
+ });
1426
+ }
1427
+
1346
1428
  // Auto-deploy allowlist (since v4.59.0). Advisory print-only — surfaced ONLY
1347
1429
  // when the allowlist is non-empty (the safe default of empty/unset opens
1348
1430
  // nothing, so it warrants no nag). Reminds the user that `/new -auto-ship`
@@ -183,6 +183,28 @@ function isBaldartManagedPath(p, extra = []) {
183
183
  return BALDART_MANAGED_PATTERNS.some((rx) => rx.test(p)) || extra.some((rx) => rx.test(p));
184
184
  }
185
185
 
186
+ // Research-library seed paths (v5.1.0). The library CONTENT is consumer-owned
187
+ // (like docs/prd/) and never enters the managed patterns; only the two seed
188
+ // files BALDART itself creates are auto-commit candidates — and only while
189
+ // UNTRACKED (see postUpdateAutoCommit), so a consumer-edited tracked
190
+ // SOURCES.md/INDEX.md is never swept into a reconcile commit.
191
+ function researchSeedPaths(cwd = process.cwd()) {
192
+ try {
193
+ const fs = require('fs');
194
+ const path = require('path');
195
+ const yaml = require('js-yaml');
196
+ const cfgPath = path.join(cwd, 'baldart.config.yml');
197
+ if (!fs.existsSync(cfgPath)) return [];
198
+ const cfg = yaml.load(fs.readFileSync(cfgPath, 'utf8'));
199
+ const dir = cfg && cfg.paths && cfg.paths.research_dir;
200
+ if (typeof dir !== 'string' || !dir.trim()) return [];
201
+ const norm = dir.trim().replace(/\\/g, '/').replace(/\/+$/, '');
202
+ return [`${norm}/INDEX.md`, `${norm}/SOURCES.md`];
203
+ } catch (_) {
204
+ return [];
205
+ }
206
+ }
207
+
186
208
  // Re-generate the root primitives (AGENTS.md + CLAUDE.md) from the versioned
187
209
  // skeletons + baldart.config.yml + overlays. Idempotent + byte-stable, so it is
188
210
  // safe to call on both the aligned-install and post-pull paths. Never throws —
@@ -252,10 +274,14 @@ async function postUpdateAutoCommit(git, newVersion, options) {
252
274
  const managed = [];
253
275
  const userOwned = [];
254
276
  const extraPatterns = i18nRegistryPatterns();
277
+ // Research seeds: managed ONLY while untracked (created by BALDART, never yet
278
+ // the consumer's). Tracked-and-modified seeds are user content — untouched.
279
+ const untracked = new Set([...status.not_added, ...status.created]);
280
+ const researchSeedNew = researchSeedPaths().filter((p) => untracked.has(p));
255
281
  for (const p of allDirty) {
256
282
  if (!p || seen.has(p)) continue;
257
283
  seen.add(p);
258
- (isBaldartManagedPath(p, extraPatterns) ? managed : userOwned).push(p);
284
+ (isBaldartManagedPath(p, extraPatterns) || researchSeedNew.includes(p) ? managed : userOwned).push(p);
259
285
  }
260
286
  if (managed.length === 0) {
261
287
  return;
@@ -986,6 +1012,17 @@ async function update(options = {}, unknownArgs = []) {
986
1012
  // --json is programmatic → force autonomous so a handwritten file is never mutated.
987
1013
  reconcileRootPrimitives(process.cwd(), alignedTools, options && options.json === true ? true : undefined);
988
1014
 
1015
+ // Research-library seed backfill (v5.1.0) — seeds only, into an EXISTING
1016
+ // dir. Never creates the dir here (creation ownership: configure/doctor;
1017
+ // update must not resurrect a deliberately deleted library). Silent + idempotent.
1018
+ try {
1019
+ const fsl = require('fs');
1020
+ const pathl = require('path');
1021
+ const yaml = require('js-yaml');
1022
+ const cfg = yaml.load(fsl.readFileSync(pathl.join(process.cwd(), 'baldart.config.yml'), 'utf8'));
1023
+ require('../utils/research-library').ensureResearchLibrary(process.cwd(), cfg, { createDir: false });
1024
+ } catch (_) { /* doctor heals */ }
1025
+
989
1026
  // Backfill hooks on the aligned/self-heal path too. Same gap class as the
990
1027
  // payload reconcile above: a consumer who framework-updated with an OLDER
991
1028
  // CLI (before a given hook shipped — e.g. the S2 Codex hooks) then aligns
@@ -1351,6 +1388,16 @@ async function update(options = {}, unknownArgs = []) {
1351
1388
  // a primitive/slot/overlay change lands. Idempotent + byte-stable.
1352
1389
  reconcileRootPrimitives(process.cwd(), enabledTools, options && options.json === true ? true : undefined);
1353
1390
 
1391
+ // Research-library seed backfill (v5.1.0) — seeds only, into an EXISTING dir
1392
+ // (creation ownership: configure/doctor; never resurrect a deleted library).
1393
+ try {
1394
+ const fsl = require('fs');
1395
+ const pathl = require('path');
1396
+ const yaml = require('js-yaml');
1397
+ const cfg = yaml.load(fsl.readFileSync(pathl.join(process.cwd(), 'baldart.config.yml'), 'utf8'));
1398
+ require('../utils/research-library').ensureResearchLibrary(process.cwd(), cfg, { createDir: false });
1399
+ } catch (_) { /* doctor heals */ }
1400
+
1354
1401
  // Routines wizard (since v2.1.0) — surfaces routines added in the new framework version
1355
1402
  try {
1356
1403
  const routinesCmd = require('./routines');