baldart 5.2.1 → 5.3.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,46 @@ All notable changes to BALDART will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.3.0] - 2026-07-03
9
+
10
+ **Haiku banned fleet-wide + FEAT-0067 post-mortem fixes.** The FEAT-0067 audit
11
+ (mayo, session `a8b6bc75`) found a haiku `general-purpose` commit agent running
12
+ its first git command with `cd <worktree> &&` and its SECOND without — so the
13
+ card-DONE bookkeeping commit executed in the MAIN checkout and landed on the
14
+ trunk mid-run (`13aa63b62`), forking `ssot-registry.md`, raising a merge
15
+ BLOCKER (a 5.3M-token repair), and blocking epic closure. Second haiku-class
16
+ failure after the v4.42.0 worktree-setup fabrication → the plumbing carve-out
17
+ is revoked.
18
+
19
+ - **Model policy — haiku eliminated, sonnet is the floor** (`REGISTRY.md`
20
+ § Rules rewritten): no named specialist AND no `general-purpose` plumbing may
21
+ use haiku. Replaced in every spawn site: `new2.js` (`commit:`,
22
+ `integrate:commit`, `rollback:`, `review:*:codex` driver),
23
+ `new-final-review.js` + `new-card-review.js` (`codex-preflight`), and the
24
+ `seo-analytics-strategist` agent (→ sonnet, v1.1.0). Where an op is truly
25
+ deterministic the fix stays a script (`setup-worktree.sh`,
26
+ `revert-files.sh`), not a cheaper model.
27
+ - **`new2.js` commit agents — cwd HARD RULE + placement verify**: every shell
28
+ command must be prefixed `cd <worktree> &&` in the same Bash invocation (the
29
+ cwd resets between calls), and the per-card commit agent now verifies the
30
+ returned sha equals `git -C <worktree> log -1 --format=%H`, reporting
31
+ `committed:false` with the stray sha instead of silently corrupting the
32
+ trunk.
33
+ - **`new2.js` Phase-1 retriever — schema + drift false-positive fix**: the
34
+ prompt now states the exact required StructuredOutput shape (`ok` REQUIRED —
35
+ the FEAT-0067 run burned 5+3 schema-validation retries), and `missingPaths`
36
+ excludes paths the card itself CREATES (new migrations/tests were flagged as
37
+ drift and disproved downstream).
38
+ - **`scripts/new-session-audit.mjs` — workflow-hosted agents counted**: the
39
+ analyzer now traverses `subagents/workflows/<wf>/agent-*.jsonl`; a new2 run
40
+ previously reported the orchestrator alone (FEAT-0067: 3.88M reported vs
41
+ 53.89M real — 93% of the run invisible to TOTAL, baselines, and telemetry).
42
+
43
+ Codex parity: portable / N/A — the three workflows are Claude-only by design
44
+ (documented gap with inline fallback); the agent `model:` field is omitted in
45
+ the Codex TOML transpile, so the model-policy change is Claude-side only; the
46
+ audit script is maintainer tooling, not payload.
47
+
8
48
  ## [5.2.1] - 2026-07-03
9
49
 
10
50
  **`/mw` local-trunk sync robustness.** The worktree merge's local-trunk sync
package/VERSION CHANGED
@@ -1 +1 @@
1
- 5.2.1
1
+ 5.3.0
@@ -7,6 +7,14 @@ per-item agent merge, the doctor probes, and the discovery hooks — like
7
7
 
8
8
  Formato: [Keep a Changelog](https://keepachangelog.com/) · per-agent SemVer.
9
9
 
10
+ ## [seo-analytics-strategist v1.1.0] — 2026-07-03 (framework v5.3.0)
11
+
12
+ - `model: haiku` → `model: sonnet` — haiku is banned fleet-wide (REGISTRY
13
+ § Rules, v5.3.0): the FEAT-0067 post-mortem showed a haiku plumbing agent
14
+ dropping the `cd <worktree>` invariant between two consecutive git commands
15
+ and committing to the main checkout's trunk. No named agent may run below
16
+ the sonnet floor. `version:` frontmatter added (was pre-v5.0 style).
17
+
10
18
  ## [ui-expert v2.1.0] — 2026-07-03 (framework v5.2.0)
11
19
 
12
20
  The `/ui-design` v2.0.0 studio refactor. No H2 moved; edits are within
@@ -210,9 +210,9 @@ Use this table when spawning agents via the `Task` tool. The `model` field in ea
210
210
  | **hybrid-ml-architect** | opus | — (always opus) | ML architecture is complex by nature |
211
211
  | **senior-researcher** | sonnet | opus at orchestrator's discretion for `PROFILE=deep` surveys | Web-native retrieval + synthesis — sonnet is sufficient and fast; depth comes from the profile loop (`DEPTH=`), not the model |
212
212
 
213
- **Rules**: Never use haiku for any **named specialist agent** in the matrix above. Opus for code writing and creative/complex work. Sonnet for analysis, review, and documentation. `qa-sentinel` stays on sonnet even though it is a mechanical gate runner — its failure interpretation and SCOPED-vs-FULL tiering benefit from sonnet.
213
+ **Rules**: **Haiku is BANNED fleet-wide** — never for the named specialist agents in the matrix above, and never for `general-purpose` plumbing either (see below). Opus for code writing and creative/complex work. **Sonnet is the floor** for everything else: analysis, review, documentation, AND mechanical plumbing. `qa-sentinel` stays on sonnet even though it is a mechanical gate runner — its failure interpretation and SCOPED-vs-FULL tiering benefit from sonnet.
214
214
 
215
- **Plumbing carve-out (haiku allowed):** the "never haiku" rule above governs the specialist agents. **Mechanical plumbing spawned as `general-purpose`** with no reasoning and an explicit ROLE BOUNDARY MAY use `model: "haiku"` via override. One sanctioned use today, in `/new`: the **file-scoped revert agent** that restores unauthorized files to their pre-commit state (`references/implement.md` Phase 2.4) a deterministic git/file op, never code authoring. (The background **worktree-setup** subagent `references/setup.md` §4b was haiku through v4.41.0 but moved to **`model: "sonnet"`** in v4.42.0: it runs the multi-step `/nw` skill **via the Skill tool**, a sustained tool-execution chain a too-weak model fabricates rather than executes the observed 2/2 `baseline: pass`-with-no-worktree failures. Its return is verified by the orchestrator's disk gate regardless, so a capable model is the right trade.)
215
+ **Plumbing carve-out ABOLISHED (v5.3.0):** through v5.2.0 mechanical `general-purpose` plumbing MAY have used `model: "haiku"` via override. That carve-out is **revoked** after two independent haiku failures on "trivially mechanical" ops: (1) the worktree-setup fabrication (2/2 `baseline: pass` with nothing on disk v4.42.0, moved to sonnet); (2) the FEAT-0067 commit corruption the haiku commit agent ran its first git command with `cd <worktree> &&` and its SECOND without, so a card-DONE bookkeeping commit executed in the MAIN checkout and landed on the trunk mid-run, forking `ssot-registry.md`, triggering a merge BLOCKER, and blocking epic closure. The lesson generalizes: multi-step shell plumbing is a sustained tool-execution chain, and a too-weak model drops invariants (cwd, ROLE BOUNDARY, staging scope) between steps. Where the op is truly deterministic, the fix is a **script, not a cheaper model** (`setup-worktree.sh`, `revert-files.sh`); where a model stays in the loop, the floor is `sonnet`.
216
216
 
217
217
  ## Notes
218
218
 
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  name: seo-analytics-strategist
3
3
  description: "Use this agent when you need to define SEO strategy, metadata requirements, analytics event plans, or technical SEO specifications for a company website. This agent produces actionable requirements documents that coder agents can implement directly.\n\nExamples:\n\n<example>\nContext: User is building a new company website and needs SEO foundations.\nuser: \"We're launching our company website next month and need to make sure it's discoverable on Google.\"\nassistant: \"I'll use the SEO & Analytics Strategist agent to create a comprehensive SEO and analytics plan for your website launch.\"\n<Task tool invocation to launch seo-analytics-strategist agent>\n</example>\n\n<example>\nContext: User wants to add analytics tracking to existing pages.\nuser: \"We need to track user interactions on our website - button clicks, form submissions, that kind of thing.\"\nassistant: \"Let me invoke the SEO & Analytics Strategist agent to design a complete GA4/GTM event tracking plan with exact specifications for implementation.\"\n<Task tool invocation to launch seo-analytics-strategist agent>\n</example>\n\n<example>\nContext: User mentions search rankings or organic traffic concerns.\nuser: \"Our competitors are ranking higher than us for industry keywords. What should we do?\"\nassistant: \"I'll use the SEO & Analytics Strategist agent to develop a keyword and intent map along with a metadata strategy to improve your search visibility.\"\n<Task tool invocation to launch seo-analytics-strategist agent>\n</example>\n\n<example>\nContext: Developer needs SEO requirements before implementing pages.\nuser: \"I'm about to code the new services pages. What SEO elements do I need to include?\"\nassistant: \"Let me launch the SEO & Analytics Strategist agent to provide you with exact SEO requirements including metadata, schema markup, and internal linking specifications that you can implement directly.\"\n<Task tool invocation to launch seo-analytics-strategist agent>\n</example>"
4
- model: haiku
4
+ version: 1.1.0
5
+ model: sonnet
5
6
  color: pink
6
7
  ---
7
8
 
@@ -321,7 +321,7 @@ if (typeof a.codexScriptPath === 'string' && /codex-companion\.mjs$/.test(a.code
321
321
  `Resolve the Codex companion script path. Run EXACTLY this one Bash command and nothing else:\n` +
322
322
  " ls -d ~/.claude/plugins/marketplaces/openai-codex/plugins/codex/scripts/codex-companion.mjs ~/.claude/plugins/cache/openai-codex/codex/*/scripts/codex-companion.mjs 2>/dev/null | sort -V | tail -1\n" +
323
323
  `Return codexScriptPath = the trimmed stdout (a single absolute path), or "" if the command printed nothing. Do NOT run Codex, do NOT read any other file, do NOT reason about availability — just report the command's stdout.`,
324
- { label: 'codex-preflight', phase: 'Discovery', model: 'haiku', schema: PREFLIGHT_SCHEMA }
324
+ { label: 'codex-preflight', phase: 'Discovery', model: 'sonnet', schema: PREFLIGHT_SCHEMA }
325
325
  )
326
326
  codexScriptPath = (pf && typeof pf.codexScriptPath === 'string') ? pf.codexScriptPath.trim() : ''
327
327
  } catch (_) { codexScriptPath = '' }
@@ -302,7 +302,7 @@ if (typeof a.codexScriptPath === 'string' && /codex-companion\.mjs$/.test(a.code
302
302
  `Resolve the Codex companion script path. Run EXACTLY this one Bash command and nothing else:\n` +
303
303
  " ls -d ~/.claude/plugins/marketplaces/openai-codex/plugins/codex/scripts/codex-companion.mjs ~/.claude/plugins/cache/openai-codex/codex/*/scripts/codex-companion.mjs 2>/dev/null | sort -V | tail -1\n" +
304
304
  `Return codexScriptPath = the trimmed stdout (a single absolute path), or "" if the command printed nothing. Do NOT run Codex, do NOT read any other file, do NOT reason about availability — just report the command's stdout.`,
305
- { label: 'codex-preflight', phase: 'Review', model: 'haiku', schema: PREFLIGHT_SCHEMA }
305
+ { label: 'codex-preflight', phase: 'Review', model: 'sonnet', schema: PREFLIGHT_SCHEMA }
306
306
  )
307
307
  codexScriptPath = (pf && typeof pf.codexScriptPath === 'string') ? pf.codexScriptPath.trim() : ''
308
308
  } catch (_) { codexScriptPath = '' }
@@ -505,7 +505,7 @@ async function rollbackCard(cardId, mayEdit) {
505
505
  try {
506
506
  const r = await agentSafe(
507
507
  `In the worktree ${sharedCtx.worktreePath}, restore the working tree to a CLEAN state for a FAILED card: \`git restore --source=HEAD --worktree --staged -- ${scope}\` then \`git clean -fd ${scope === '.' ? '' : '-- ' + scope}\` (with scope '.' this cleans the whole worktree — safe: all committed work lives in HEAD). Do NOT touch other cards' committed work. Confirm \`git status --porcelain\` is empty for the scope.`,
508
- { label: `rollback:${cardId}`, phase: 'Implement', agentType: 'general-purpose', model: 'haiku',
508
+ { label: `rollback:${cardId}`, phase: 'Implement', agentType: 'general-purpose', model: 'sonnet',
509
509
  schema: { type: 'object', required: ['clean'], additionalProperties: true, properties: { clean: { type: 'boolean' }, note: { type: 'string' } } } }
510
510
  )
511
511
  return !!(r && r.clean)
@@ -562,7 +562,8 @@ async function runCard(cardId, cardPath) {
562
562
  // agents/analysis-profiles.md § 3 caller routing table.
563
563
  `PROFILE=${node.hasMockup ? 'ui' : 'feature'} OUTPUT=terse — resolve your analysis profile per agents/analysis-profiles.md (Grep "### PROFILE: ${node.hasMockup ? 'ui' : 'feature'}", Read only that section + its cited shared blocks).\n\n${cardBrief}\n\n` +
564
564
  `Explore the codebase exactly as your system prompt + profile mandate for this card's scope (requirements + files_likely_touched: ${JSON.stringify(node.filesLikelyTouched || [])}). Write your COMPLETE untruncated findings (file paths, type signatures, patterns, high-risk paths) to ${baselinePath} — refresh it if a stale copy exists. The owner agent and the per-card reviewers will Read that file; keep your structured return MINIMAL.\n\n` +
565
- `Also report missingPaths: every path in files_likely_touched that does NOT exist in the worktree (factual ls/test check — no judgement). Return: { ok, missingPaths:[...], note }`,
565
+ `Also report missingPaths: every path in files_likely_touched that the card expects to ALREADY exist but does NOT exist in the worktree (factual ls/test check — no judgement). EXCLUDE paths the card itself CREATES (NEW files: migrations with placeholder timestamps, new tests, new components declared in scope) — those are not drift (FEAT-0067 false-positive class).\n\n` +
566
+ `Your StructuredOutput MUST be exactly this shape — the boolean property \`ok\` is REQUIRED at the top level (missing it fails schema validation and burns a retry): { "ok": true|false, "missingPaths": ["..."], "note": "..." }. ok:false ONLY if you could not explore the worktree at all.`,
566
567
  { label: `architect:${cardId}`, phase: 'Implement', agentType: 'codebase-architect',
567
568
  schema: { type: 'object', required: ['ok'], additionalProperties: true, properties: { ok: { type: 'boolean' }, missingPaths: { type: 'array', items: { type: 'string' } }, note: { type: 'string' } } } }
568
569
  )
@@ -853,11 +854,12 @@ async function runCard(cardId, cardPath) {
853
854
  `Read the Codex output ONLY through a [codex]-trace-stripping filter. **Fallback**: if it exits non-zero / prints CODEX_NOT_FOUND / stays empty, return note:'codex-unavailable' with EMPTY blocks/scopeExpansion — do NOT review yourself (role boundary); the workflow spawns the real code-reviewer.\n\n` +
854
855
  `Map Codex BLOCKER/HIGH findings to blocks:[{gate:'codex-light',domain,evidence}] (each non-empty gate AND evidence — F-014). Map legitimate findings BEYOND this card's AC to scopeExpansion:[{evidence,domain,withinOwnership,newAC}].\n\n` +
855
856
  `Return: { blocks:[...], scopeExpansion:[...], note }`,
856
- // Haiku, not the inherited opus: this agent is a pure DRIVER — it runs the Codex companion
857
+ // Sonnet, not the inherited opus: this agent is a pure DRIVER — it runs the Codex companion
857
858
  // (node + --wait), strips [codex] traces, and maps the companion's findings into the schema.
858
859
  // The review INTELLIGENCE is Codex (a different model, run externally); driving it needs no
859
- // opus reasoning, same as the other mechanical git/ops steps.
860
- { label: `review:${cardId}:codex`, phase: 'Implement', agentType: 'general-purpose', model: 'haiku', schema: reviewSchema }
860
+ // opus reasoning. Haiku is BANNED fleet-wide since v5.3.0 (FEAT-0067 post-mortem: a haiku
861
+ // plumbing agent dropped the `cd` on its second git command and committed to the MAIN repo).
862
+ { label: `review:${cardId}:codex`, phase: 'Implement', agentType: 'general-purpose', model: 'sonnet', schema: reviewSchema }
861
863
  ).catch(onErr)
862
864
  }
863
865
  return stdReview(ra)
@@ -929,10 +931,11 @@ async function runCard(cardId, cardPath) {
929
931
  try {
930
932
  commitRes = await agentSafe(
931
933
  `Commit card ${cardId} in worktree ${sharedCtx.worktreePath}. MECHANICAL — do NOT re-read reference modules. ROLE BOUNDARY: you NEVER modify file contents except the card YAML status/note fields, the parent-epic YAML status (epic-closure gate only), and the ssot-registry entries — source/doc changes are not yours.\n` +
932
- `Steps: (1) \`git status --porcelain\`; (2) stage = MAY-EDIT (${JSON.stringify(mayEdit)}) dirty NEVER \`git add -A\`, NEVER \`git stash\`; if dirty has files OUTSIDE MAY-EDIT, do NOT stage them and set reconcileNote; (3) commit message \`[${cardId}] <concise>\`; ${doneStep} (5) 'nothing to commit' = already committed (record HEAD).\n` +
934
+ `HARD RULE — cwd: EVERY shell command MUST begin with \`cd ${sharedCtx.worktreePath} && \` IN THE SAME Bash invocation. The shell cwd RESETS between Bash calls to the MAIN repo: a bare \`git commit\` executes against the main checkout's current branch and corrupts it (observed in FEAT-0067 — a card-DONE commit landed on the trunk mid-run). No exceptions, including 'quick' status/log commands.\n` +
935
+ `Steps: (1) \`git status --porcelain\`; (2) stage = MAY-EDIT (${JSON.stringify(mayEdit)}) ∩ dirty — NEVER \`git add -A\`, NEVER \`git stash\`; if dirty has files OUTSIDE MAY-EDIT, do NOT stage them and set reconcileNote; (3) commit message \`[${cardId}] <concise>\`; ${doneStep} (5) 'nothing to commit' = already committed (record HEAD); (6) VERIFY placement: \`cd ${sharedCtx.worktreePath} && git log -1 --format=%H\` MUST equal the sha you return — if it does not, your commit landed outside the worktree: report committed:false with reconcileNote naming the stray sha, do NOT retry blindly.\n` +
933
936
  `On COMMIT_LOCK: clear stale lock + retry once. Still locked → committed:false.\n\n` +
934
937
  `Return: { committed, commit, filesChanged, reconcileNote }`,
935
- { label: `commit:${cardId}`, phase: 'Implement', agentType: 'general-purpose', model: 'haiku',
938
+ { label: `commit:${cardId}`, phase: 'Implement', agentType: 'general-purpose', model: 'sonnet',
936
939
  schema: { type: 'object', required: ['committed'], additionalProperties: true, properties: { committed: { type: 'boolean' }, commit: { type: 'string' }, filesChanged: { type: 'array', items: { type: 'string' } }, reconcileNote: { type: 'string' } } } }
937
940
  )
938
941
  } catch (e) {
@@ -1116,10 +1119,10 @@ async function integrateCrossCard() {
1116
1119
  // ONE integration commit (the fixes cross card boundaries); mark fully-freed cards DONE in YAML.
1117
1120
  try {
1118
1121
  await agentSafe(
1119
- `Commit the cross-card integration fixes in worktree ${sharedCtx.worktreePath}. MECHANICAL — do NOT modify source beyond what is already changed on disk. Steps:\n` +
1122
+ `Commit the cross-card integration fixes in worktree ${sharedCtx.worktreePath}. MECHANICAL — do NOT modify source beyond what is already changed on disk. HARD RULE — cwd: EVERY shell command MUST begin with \`cd ${sharedCtx.worktreePath} && \` in the SAME Bash invocation (the shell cwd resets to the MAIN repo between calls; a bare git command corrupts the main checkout). Steps:\n` +
1120
1123
  `(1) \`git status --porcelain\`; (2) stage ONLY dirty files within the batch union ${JSON.stringify(batchUnion)} — NEVER \`git add -A\`, NEVER \`git stash\`; (3) for each fully-freed card YAML [${fullyFreed.join(', ') || '(none)'}] set status to DONE and add implementation_note "cross-card integration: deferred AC resolved with batch-union ownership (new2 v4.30.0)"${SESSION_ID ? ' and set provenance.implementation_session: "' + SESSION_ID + '" (do NOT touch planning_session)' : ''}; (4) commit message \`[integrate] cross-card residual fixes${fullyFreed.length ? ' (' + fullyFreed.join(', ') + ')' : ''}\`; (5) 'nothing to commit' = the resolve already committed (record HEAD).\n` +
1121
1124
  `Return: { committed, commit, filesChanged }`,
1122
- { label: 'integrate:commit', phase: 'Integrate', agentType: 'general-purpose', model: 'haiku',
1125
+ { label: 'integrate:commit', phase: 'Integrate', agentType: 'general-purpose', model: 'sonnet',
1123
1126
  schema: { type: 'object', required: ['committed'], additionalProperties: true, properties: { committed: { type: 'boolean' }, commit: { type: 'string' }, filesChanged: { type: 'array', items: { type: 'string' } } } } }
1124
1127
  )
1125
1128
  } catch (e) { if (e && e.transientExhausted) noteDegraded('outage') }
@@ -1259,7 +1262,7 @@ if (!committed.length) {
1259
1262
  // YAML status reconciliation + grep-based epic closure + leave-and-report hygiene gates), and
1260
1263
  // the correctness-critical checks (F-029 forcedDone guard, F-040 deferred guard) are enforced
1261
1264
  // in JS AFTER it returns — they do NOT depend on the agent's reasoning. Opus here is pure cost;
1262
- // haiku would be too weak for the conditional epic-closure / G19-23 divergence logic.
1265
+ // sonnet is also the fleet-wide plumbing floor (haiku banned since v5.3.0).
1263
1266
  { label: 'merge', phase: 'Merge', agentType: 'general-purpose', model: 'sonnet', schema: MERGE_SCHEMA }
1264
1267
  )
1265
1268
  } catch (e) { if (e && e.transientExhausted) noteDegraded('outage'); mergeResult = null }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "5.2.1",
3
+ "version": "5.3.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"