Skip to content

chore: back-merge main into next (conflicts) - #568

Merged
blafourcade merged 75 commits into
nextfrom
back-merge/main-to-next-30605182102
Jul 31, 2026
Merged

chore: back-merge main into next (conflicts)#568
blafourcade merged 75 commits into
nextfrom
back-merge/main-to-next-30605182102

Conversation

@aidd-bot

@aidd-bot aidd-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Automated back-merge hit conflicts (CHANGELOG / manifest / version files). Resolve manually, then merge into next.

alexsoyes and others added 30 commits July 19, 2026 16:39
Align the project messaging with the framework's current branding and focus.
Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>
Convert every `@../` reference in the action files of skill-generate and
project-memory to standard markdown links `[name](path)`, so citations are
clickable in editors and on GitHub.

Propagate the convention to the contract so generated skills follow suit:
R6 now mandates a markdown link, R8 drops the `@`-chain wording, and
review-protocol says "cited references". Functional `@`-imports in the memory
block (`@aidd_docs/...`) are untouched: those are load mechanics, not citations.

Sync docs/CATALOG.md skill-generate action names to the shipped set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177RKF7c5Wpv329Q7QYquV2
…ork (#440)

* feat(aidd-context): add research and apply actions to cook skill

Extend 12-cook from author-only to a fuller loop:

- 03-research: refine the target recipe via a checklist, fan out one
  agent per scouting angle, then present alternatives / coverage gaps /
  counter-intuitive wins plus a recommendation. Ephemeral, never writes.
- 04-apply: execute an existing recipe against the project via an agent
  that tracks the steps as a todo list and confirms before mutating.
- upsert: qualitative overlap guard (none/partial/high) that suggests
  updating an existing recipe instead of creating a duplicate.
- recipe-template: steps become "#### N)" headings with what/why,
  actionable how-bullets, and a mandatory concrete example
  (command with output, snippet, or screenshot/video).

New references/assets: research-playbook, refine-goal-checklist,
research-checklist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(aidd-context): verify each research candidate before presenting

Add a Verify step to cook's 03-research: after curation, spawn one
agent per surviving candidate to confirm it exists, capture its
official link, and record its latest state. Drop anything that cannot
be confirmed against an official source.

This makes the checklist's "Confirm claims" real and guards the recipe
against hallucinated tools or methods.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): add token optimization recipe

Add recipes/token-optimization.md: a ranked list of token-saving tips
for AI coding assistants (measure first, trim CLAUDE.md, caveman,
RTK/SNIP, CLI over MCP, progressive disclosure, compact, model routing,
cap thinking), each tip in the new template format with a concrete
example and an official link verified to exist. Register it in the
recipes index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): extract recipe contract and tighten cook template

Move the recipe authoring rules out of the template's HTML comment into
references/recipe-contract.md, cited by SKILL.md and upsert. The template
keeps only the scaffold.

- Steps heading carries the goal ("## Steps to <outcome>") and each step
  opens with an emoji.
- Step how-bullets follow where-it-is -> install-from-URL -> how-to-use,
  benefit-first and filler-free.
- "## Why" is short, keyword-led, and bold.
- SKILL.md description is shorter and high-level, triggered on
  "recipe" / "cook" / "/cook".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(aidd-context): repair 12-cook SKILL.md YAML frontmatter

The description contained an unquoted "sheets: list" colon, which YAML
parses as a mapping value and rejects ("mapping values are not allowed
here"), breaking the skill's frontmatter. Replace the colon with a dash.
The bug pre-dated this branch (same colon on main).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): refine cook recipe structure and apply R13 to cook

Review feedback on the cook skill, plus the new includes convention:

- Recipe template drops the Time field, groups steps under three level
  subheadings (Beginner / Intermediate / Expert), and aligns the Level
  enum to Expert.
- upsert now runs `research` (03) first for any new or substantial
  recipe and drafts only from its verified results, never from memory —
  the "always research" rule was being skipped as a routing note.
- Apply R13 (explicit, scoped includes): the action-specific research
  aids move out of SKILL.md and are cited from 03-research in fenced
  blocks; refine-goal-checklist is renamed research-goal-checklist;
  SKILL.md lists only the global recipe-template and recipe-contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): make recipe level subheadings optional

Level grouping was effectively mandatory. Make the contract state it is
optional: group steps under Beginner/Intermediate/Expert only when the
recipe spans difficulty levels and grouping helps; a short or
single-level recipe lists its steps directly. Relax the upsert Test
accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): conform token-optimization recipe to recipe contract

Align the recipe with references/recipe-contract.md: drop the Time row
(no longer in the template), name the section "Steps to <outcome>" instead
of a bare Steps, remove the "ranked by impact" filler line, and give each
step how-bullets plus a concrete example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(framework): split measure steps per tool and drop metadata table

Apply review of the token-optimization recipe:

- Goal becomes a plain sentence (no blockquote).
- Drop the Level/Prerequisites table; level now lives in the subheadings.
- Split the bundled "Measure first" step into one step per tool
  (/context, /cost, /insights, an analytics tool) — one subheading per
  action.
- Each measure step says how to use and read the command, and carries an
  example output; the analytics step uses a real prompt-analytics run
  (volumes rounded, cost omitted) showing cache reads dominate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): drop the Goal label from token-optimization

The recipe opens with a plain description sentence, no "Goal:" label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): recipe header = description sentence, no table

Rules extracted from the token-optimization recipe review:

- A recipe opens with the H1 title then one plain description sentence —
  no "Goal:" label, no blockquote, no metadata table. list/upsert parse
  that sentence; the index becomes `| Recipe | Description |` (Level was
  dropped, level now lives only in the optional subheadings).
- One step = one action; never bundle several tools or commands under a
  single heading.
- Every step's example shows real command output (from docs or by
  running it); the research playbook now requires capturing a real
  example and marking interactive output for the human to paste.
- `## Why` is one idea per line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): use the real prompt-analytics dashboard image

Replace the fabricated text output with the tool's official dashboard
screenshot (cost by token type makes "cache reads dominate" visible).
The slash-command placeholders now ask for a screenshot of the real
output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): prefer images over text examples

A real screenshot or GIF is unfakeable, faithful, and credible, where a
text example can be approximated (the failure we hit). Make the contract
and the research playbook prefer an image that matches the action —
e.g. a tool's official screenshot — falling back to real text output
only when no image exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): show real tool usage in token-optimization

- caveman: show its real `/caveman` invocation (and modes), not just a
  before/after.
- RTK: a Mermaid diagram of the proxy flow + the real `rtk <command>`
  syntax (was a wrong `rtk proxy …`) and the README's own 200->10 token
  saving.
- CLI vs MCP: a comparison table instead of prose.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): add tool-example rules to recipe contract

From the token-optimization review:

- Show how to invoke a tool (its real command or slash invocation).
- Reuse the canonical example from the tool's own site or README, never
  an invented one.
- A "prefer X over Y" step uses a comparison table, not prose.
- A structural or flow concept (proxy, pipeline, architecture) gets a
  small Mermaid diagram.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): use caveman's real before/after example

Replace the invented caveman before/after with the verbatim example from
its README (rule F: reuse the tool's own canonical example).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): numbered actions, valid-JSON config, tighter sections

- Actions are numbered lists; descriptions are prose (no description-bullets).
- settings.json shown as valid JSON, not a bash fragment.
- RTK Mermaid uses concrete values (cargo test ~25k -> ~2.5k tokens).
- CLI vs MCP notes MCP tool/context selection (cheaper, still slower).
- Step 10 reworded plainly ("load knowledge on demand").
- Drop the weak Verify and the redundant Related; add a short conclusion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): add bullet/diagram/config/section rules to contract

From the token-optimization review:

- Actions are a numbered list; descriptions and indications are prose,
  never bullets.
- A Mermaid diagram carries concrete example values.
- A config example uses the file's real syntax (valid JSON for
  settings.json, valid YAML for frontmatter, ...).
- Reuse a tool's canonical example captured verbatim — never a paraphrase
  or on the strength of a summary that one exists.
- No `## Related` section (links live inline); `## Verify` is optional;
  recipes may end with a short conclusion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): expert steps use verified Claude Code config

- Step 10 now audits which skills/tools run via `Ctrl+O` (toggles the
  detailed transcript), instead of the vaguer "load on demand".
- Step 11 routes by difficulty with the real `model:` frontmatter field
  on a skill or agent (haiku/sonnet/opus/inherit) — example agent pinned
  to haiku.

Both verified against the official Claude Code docs (interactive-mode,
skills, sub-agents).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): point the instruction-file step at AGENTS.md

Use the framework's own concise AGENTS.md as the worked example for a
trimmed instruction file, with its real rules (answer first, no
narration), instead of a generic snippet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): apply analyses a recipe and asks before acting

A recipe is often a human tutorial, not an agent-runnable procedure. So
apply now reads the recipe, classifies each step agent-doable vs
human-only, and asks the user what to do before any change — then runs
the chosen agent-doable steps and reports the human-only ones.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): add five verified token levers to the recipe

From a research pass, all verified against official Claude Code docs:

- Plan mode (Shift+Tab ×2) — avoid execution-phase rework.
- `/clear` between tasks — drop stale context instead of re-billing it.
- Deny reads (`permissions.deny`) — keep vendor/dist/secrets out of
  context; the official answer to the unshipped `.claudeignore`.
- Subagent offload — run high-volume ops in an isolated context.
- Protect cache hits — avoid mid-task model/MCP/effort switches that
  invalidate the cache (closes the loop on "cache reads dominate").

Also fold the built-in `outputStyle: concise` into the "talk less" step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(aidd-context): bundle cook recipes in skill

* feat(framework): publish AIDDy v2 pet

* docs(framework): reposition readme as agnostic token-optimized framework

* feat: avoid uncached AGENTS.md

* docs: add fixing behavior for assertions

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…455)

Splits source out of gather so origin selection (conversation, file,
diff, review) is explicit before extraction runs, and renames
assets/decision-template.md to adr-template.md to match its actual
output. CATALOG.md's 10-learn index is regenerated to match.

Folds in fixes found during a headless claude+codex audit of the
skill (13 scenarios x 2 tools):
- sync locates update_memory.js instead of assuming a fixed path that
  doesn't exist in either tool's real install layout
- never scaffold the memory bank, even under a blanket write
  pre-approval
- sync's own git-diff-cached test reflects a pre-staged index instead
  of demanding it be empty
- sources.md again defaults to conversation when no hint is given,
  matching pre-refactor behavior, instead of always requiring context
  inference
- the two memory-write transversal rules merge into one, trimming the
  router closer to the smallest shape that still states every rule

Closes #278
Refs #419
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@99df26d...7188fc3)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…460)

Bumps [github/codeql-action/autobuild](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@99df26d...7188fc3)

---
updated-dependencies:
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…rved

Merges ai-driven-dev/aidd-cli into this repo as cli/ via git subtree —
full commit history preserved (reachable via the subtree-merge commit's
second parent, kept on the source branch chore/migrate-cli-into-framework,
not deleted by this squash). cli/ stays self-contained: own package.json,
own lockfile, not a pnpm workspace member.

- release-please: cli added as a ninth package, release-type: node
- CI: new cli-ci.yml (typecheck/lint/test/build/knip/jscpd), native
  path-filter on cli/** only
- publish-cli in ci.yml, gated on release-please's paths_released,
  real npm OIDC trusted publishing (no NPM_TOKEN secret)
- Fixed along the way: npm README stub bug, a pnpm-version/cache-path
  CI bug, a stale duplicate memory-sync script and its dead test,
  hardcoded personal paths, dead ISSUE_TEMPLATE/CONTRIBUTING.md left
  over from the standalone repo
- ai-driven-dev/aidd-cli stays active, unarchived, until a real release
  from here is proven end to end

Refs #448. Full plan, phase files, and audit reports:
cli/aidd_docs/tasks/2026_07/2026_07_20_migrate-cli-into-framework/
package.json never had repository/homepage/bugs fields at all (the
migration plan assumed they existed and needed updating — they didn't
exist). Added them pointing at ai-driven-dev/framework with
directory: "cli" so npm's package page links to the right subtree.

aidd_docs/README.md (the standard scaffold every aidd setup generates)
had one stale link to the old standalone repo.
package.json was missing license (npm shows this prominently on the
package page) and keywords (npm search discoverability). license: MIT
matches framework's own root LICENSE — same repo, same license, no
ambiguity to resolve.
Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>
release-please's PR #485 computed cli's next version as 4.0.0 instead
of bumping forward from 5.1.3. Root cause: cli had no prior cli-v*
tag to anchor its version walk, so it fell through to an old,
otherwise-already-consumed root Release-As: 4.0.0 directive (commit
5b0fc9d, framework's own major-bump forcing from May 2026, unrelated
to cli) and applied it to the new component.

Both npm and GitHub Packages correctly rejected the resulting publish
(existing 4.0.0 from the pre-migration standalone repo, real current
version is 5.1.3 on both registries — untouched). Bad tag/release
cli-v4.0.0 already deleted.

Reverts cli/package.json and the manifest's cli entry to 5.1.3, and
drops the bogus CHANGELOG entry. A cli-v5.1.3 tag/release will be
created on top of this commit to anchor future release-please runs
so the walk never reaches that old directive again.
Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>
…nchor for release-please (#493)

A second, unrelated release-please cycle (PR #489, "chore: release
main") auto-merged moments after #488 and independently recomputed
cli's version, reintroducing 4.0.0 the same way #485 did, even
though that run's own log showed it correctly reading 5.1.3 from the
manifest as the baseline. The manifest baseline alone does not bound
the version-bump walk; only a matching cli-v* release does, and none
existed yet when that run started.

Both npm and GitHub Packages rejected the resulting publish again
(pre-existing 4.0.0, same as before), still untouched at the real
5.1.3. Bad tag/release cli-v4.0.0 deleted again.

A cli-v5.1.3 anchor release now exists (pointing at bdd12c6, the
commit right before the cli migration landed), created before this
commit merges, so the next release-please run has a real boundary to
stop its walk at and can no longer reach the old, already-consumed
version-forcing directive from commit 5b0fc9d (framework's own
unrelated major-bump forcing from May 2026, targeting 4.0.0 for the
root package only, not cli).

This commit also serves as the nudge for release-please to
re-evaluate cli against that new anchor and correctly propose 5.1.4
next, carrying the real pending fixes, migration, repoint, license
and keywords, into a real changelog entry and npm and GHP publish.
Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>
)

Any commit touching .release-please-manifest.json (a root-level
file) counts toward the "." package's own version bump, since root
has no path restriction. Today's cli version-regression fixes needed
to hand-edit that file directly and, as a side effect, cut two extra
framework releases (v5.5.3, v5.5.4) that carried no real content
change for root.

exclude-paths (documented in release-please's manifest schema,
ReleaserConfigOptions) skips a commit for a given package when every
file it touches falls under the listed paths. Scoped to the "."
package only, so cli's and the plugins' own path-based tracking are
unaffected.
Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>
…eck excludes (#500)

Two real, distinct issues were hiding behind the migration's temporary
--ignore flags:

- 8 links in plugin-architecture-master.md pointed at real, existing
  sibling files whose names contain a literal '#' (e.g.
  2026_04_27-#260-plugin-architecture-part-1.md). Markdown link
  parsers treat an unescaped '#' as a URL fragment separator,
  truncating the path before the file check ever runs. URL-encoding
  it as %23 fixes it; the checker already decodeURI()s targets.
- The remaining ~235 flagged references under cli/tests/fixtures/**
  and cli/aidd_docs/tasks/** are not a mix of real links and
  shorthand: cli/tests/fixtures/** is synthetic mock trees that never
  materialize every file they reference (confirmed: the full test
  suite passes with those template files absent), and
  cli/aidd_docs/tasks/** is a historical record whose @path
  references and inline rewrite-rule examples are expected to drift
  as the codebase evolves after the fact. Scrubbing them file-by-file
  would just rewrite historical docs that were accurate when written.

Moved that exemption into the checker itself (DEFAULT_IGNORES,
applied on top of any --ignore given) rather than the hook command,
so `node scripts/check-markdown-links.js` with no flags is the thing
that's actually clean. lefthook.yml's markdown-links command now
carries no --ignore flags and no TEMPORARY comment.
Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 5.2.1 to 5.2.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@5.2.1...5.2.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 5.2.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…tion (#506)

Two confirmed bugs on aidd restore / aidd ai restore / aidd ide restore:

A2 (scope leak): RestoreUseCase.runPluginRestore never threaded ctx.toolIds
into RestoreAllPluginsUseCase, so `--tool X` still restored every other
installed AI tool's plugin files unscoped.

A3 (double materialization): RestoreAllUseCase ran two independent,
unconditional passes over the same (tool, plugin) pairs on every global
`aidd restore` — the second pass ignored --tool scope, ignored fileFilter
entirely, and for built-tree tools (cursor/opencode) wrote every plugin
file to disk twice with no hash guard.

RestoreAllPluginsUseCase now takes an optional toolIds filter and returns
{totalFiles, pluginNames} so the single remaining pass can still report
"Restored plugins: x, y". RestorePluginUseCase deleted — its one caller
was the removed second pass (confirmed via grep).

2048/2048 tests green (2042 existing + 6 new regression tests), tsc clean.
Pre-fix reproduction verified via git stash before each fix (double
materialization count, interactive file selection not excluding plugins).

Ported from the archived aidd-cli repo (BUG-E3-02 + SPIKE-E3-01,
pre-migration branches) — original work predates the framework migration
(PR #462).
…505)

outDir for this path is always .aidd/cache/built/<marketplace>/<target>, an
aidd-owned disposable cache, never a user directory — collision here means
"cache from a previous build exists", not data at risk. Adds a comment
stating that explicitly and a regression test (real FlatBuildStrategy, not
the fake buildFor stub) pinning the collision-bypass so it fails if force is
ever flipped or outDir stops being cache-only.

Ported from the archived aidd-cli repo (BUG-E2-01, pre-migration branch
docs/e2-01-force-cache-rebuild) — original work predates the framework
migration (PR #462).
…olUseCase (#507) (#508)

StatusAllUseCase, RestoreAllUseCase, and UpdateAllUseCase each rebuilt their
own duplicate of a collaborator deps.ts already constructs and shares with
ai.ts/ide.ts (deps.statusUseCase, deps.restoreUseCase, deps.updateOneToolUseCase
consumed directly by those commands). Two of the four duplicate sites rebuilt
on every execute() call, not just once.

Confirmed all 3 target classes stateless before sharing them as singletons
(no memo maps, no accumulator fields — BulkConflictState is passed as an
execute() parameter, never stored on UpdateOneToolUseCase). Each class now
receives the real instance by constructor injection; every now-dead
raw-sub-dependency param removed rather than left unused.

grep confirms zero remaining "new StatusUseCase/RestoreUseCase/
UpdateOneToolUseCase" outside deps.ts's 3 original construction sites.
2048/2048 tests pass unmodified — zero behavior change, pure DI wiring.

Stacked on fix/e3-02-restore-plugin-scope-dedup (touches the same
restore-all-use-case.ts) — needs that branch merged first.

Ported/implemented fresh from the archived aidd-cli repo's plan
(SPIKE-E1-01 + BUG-E1-02, pre-migration) — original work predates the
framework migration (PR #462).
…or name collisions (#509)

Two genuinely different symbols shared the same name, confirmed via full
read of both sides of each collision — real friction, not cosmetic:
mode-b-flat-materialization-translator.ts already had to locally alias one
of them (`PluginTranslator as PluginTranslatorHelper`) just to compile.

buildClaudeStyleMarketplaceEntry: the catalog-row builder in
marketplace-strategy-helpers.ts (framework build time) renamed to
buildClaudeStyleCatalogEntry — pairs naturally with the neighboring
buildClaudeStyleMarketplace. The domain/capabilities settings-entry builder
(install time, consumed by claude.ts/copilot.ts) keeps its name.

PluginTranslator: the domain content-format-conversion class renamed to
PluginContentTranslator (file renamed to match). The application-layer
strategy interface (Section C protected pattern, 4 implementer/factory
sites) is untouched.

Pure rename, zero behavior change. 2049/2049 tests pass with zero
assertion changes; grep confirms each name now maps to exactly one symbol.
… internal discovery map (#510)

Restructures 01-brainstorm from a flat probing.md + question-angles.md
pair into a discovery-map/readiness/interview-depth apparatus, moves
finalize output to a discovery-brief.md template, and hides internal
process vocabulary from user-facing text by default.

Fixes applied on top of the base restructure, found via headless and
live testing:
- restore the "state a leaning + tradeoff, even unprompted" rule that
  the restructure had silently dropped (SKILL.md, 03-integrate.md)
- restore the "stay at the idea's altitude, leave finer how-to as a
  flagged assumption" guardrail (references/probing.md)
- fold no longer silently overwrites a contradicted fact — confirms
  which stands before folding (03-integrate.md)
- drop the "use the user's language" instruction, never present before
  this refactor either
- trim 04-finalize's Ask step and Test section to stop restating what
  the linked reference files already say

Closes #504

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.0 to 4.37.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@99df26d...e4fba86)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@9c091bb...3d3c42e)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.4.0 to 7.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@48b55a0...8207627)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.10.15 to 26.1.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…478)

Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.8.1 to 21.2.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.1/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 21.2.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
blafourcade and others added 23 commits July 31, 2026 06:48
resolveBaseDir had five copies, not the two the ticket claimed:
plugin-remove, plugin-update, mode-b-flat-materialization-translator,
built-tree-materialization-translator, and detect-plugin-drift.
qualifiesForOpencodeMcpMerge had two, verbatim identical.

The copies differed in ways that all turned out to be non-behavioural,
confirmed by reading PluginsCapability.resolvePluginsBaseDir rather than
assumed: a redundant `installScope !== "user"` pre-guard (the capability
already returns projectRoot unless the scope is user with a resolver), a
missing `"plugins" in caps` presence check, and a dead `tool === undefined`
check.

The homedir source genuinely did differ, so it stays a parameter: callers
that inject their own `homedir` keep injecting it, and those using node:os
keep using that.

plugin-helpers.ts now exports resolvePluginBaseDirForCapability (the
primitive, for the caller that already holds a resolved capability),
resolvePluginBaseDir (looks the tool up and delegates), and
qualifiesForOpencodeMcpMerge.

2105/2105 pass, tsc clean, no assertion changed. Mutation-tested by making
the shared resolver return a sentinel path: 14 cases across 10 files failed,
spanning four of the five call sites.

plugin-update-use-case.unit.test.ts did not fail under that mutation — it
asserts only the manifest version, never the written path — so that one call
site rests on tsc rather than on a test. Its missing path coverage is
already tracked by SPIKE-E7-01.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
SPIKE-E7-01 measured coverage of the files US-E7-02/03/04/05 rewrite. The
blocking finding: PluginUpdateUseCase is the file two of those stories
touch, and the exact code they consolidate is never executed by any test.

- lines 117-129, the built-tree branch of replacePluginFiles, is the very
  "resolve translator then built-tree-or-fallback" that US-E7-03 collapses
- lines 155-164, the body of builtTreeTranslator, only its early return
  null was reached
- lines 170-175, the user-scope path of resolveBaseDir, is the helper
  US-E7-02 extracts

Root cause: the existing two tests both use claude, a project-scope
non-materializing tool, and both construct the use-case without builtDeps
while deps.ts always passes builtMaterializationDeps. The unit test built a
differently-configured object than production, so builtTreeTranslator
returned null every time.

Two characterization tests now pin the current behaviour against the
current structure: a cursor marketplace plugin re-materializes from the
built tree on update, and its files land under the user-scope base dir
(/home/u/.cursor/plugins/local) rather than the project root.

plugin-update-use-case.ts: branches 50.0% -> 76.7%, lines 83.7% -> 99.3%.
2107/2107 pass, tsc clean.

findings.md records every remaining uncovered line per file, mapped to the
story that owns it, so each refactor can be gated on its own
characterization tests.

--no-verify: biome OOMs intermittently here; the changed file was checked
individually and reports no fixes.
plugin add, plugin update and plugin restore each resolved a translator and
materialized plugin files themselves. Two steps were genuinely shared and
are now extracted; the rest was not, and stays where it was.

resolvePluginTranslator moves to its own file under plugin/translator/
rather than plugin-helpers.ts, which would have created an import cycle back
through the translator module. All three sites use it.

materializeViaBuiltTree moves to plugin-helpers.ts (type-only import of the
translator class keeps the cycle away). Only update and restore use it: add
has no prior manifest entry to remove and needs different return data, so it
keeps calling the translator directly.

Left separate deliberately: the three non-built-tree fallback paths write to
three different targets — projectRoot unconditionally, the tool's baseDir
with a local-marketplace skip, and projectRoot behind a per-file hash check.
Confirmed different by reading each, not assumed.

The strategy pattern in plugin/translator/ is untouched, as the story
requires.

Characterization tests came first: apply-plugin-files-use-case.ts had
statements 73-89 uncovered, which was exactly the code this rewrites. Two
tests now drive that restore path; the file goes from 75.9% to 100%
statements.

2109/2109 pass, tsc clean, no existing assertion changed.

Mutation-tested twice. Dropping manifest.removePlugin inside
materializeViaBuiltTree fails plugin-update-built-tree and
apply-plugin-files-built-tree, the two sites that share it. Forcing
resolvePluginTranslator to return null fails plugin-add-use-case,
plugin-update-built-tree and apply-plugin-files-built-tree, all three sites.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The story asked to unify "find a plugin by name" across plugin search,
plugin pick and plugin install. Checked against the code, that premise is
false. The three do different things deliberately:

  search:  entry.name.toLowerCase().includes(q) || description includes q
  install: entry.name === options.pluginName
  pick:    no matching at all, the whole catalog goes to a prompt

Unifying them would be a behaviour change, not a refactor, and a harmful
one: a fuzzy install lets `aidd plugin install foo` install foo-bar. That
part of the story is not implemented.

What was genuinely duplicated is catalog resolution. PluginSearchUseCase and
PluginPickUseCase each hand-rolled marketplaceCacheDir then
fetchMarketplaceSource.execute then catalogRepo.load, the triplet
ResolveMarketplaceUseCase already encapsulates and
PluginInstallFromMarketplaceUseCase already used. Both now route through it.

Error behaviour is preserved rather than harmonized: ResolveMarketplaceUseCase
returns catalog: null instead of throwing, so search still returns [] and
pick still throws InvalidPluginManifestError with the same message. Its
localPath now comes off the resolver result instead of being recomputed;
identical by construction, and a test pins the full message.

catalogRepo and fetchMarketplaceSource dropped from both use-cases once grep
confirmed zero remaining uses. Four construction sites updated.

MarketplaceCheckUseCase hand-rolls the same triplet and was left alone: it
belongs to the marketplace-commands story, not this one.

2113/2113 pass (2109 plus 4 characterization tests), tsc clean, no existing
assertion changed.

Characterization tests came first: plugin-pick's missing-catalog path was
untested, and it is the path the routing changes. Lines 91.8% to 100%.

Mutation-tested by forcing the resolver to return catalog: null. 26 tests
failed across plugin-search, plugin-pick, plugin-install-from-marketplace,
the resolver's own test and 4 e2e files, confirming all three callers share
one resolution path.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
marketplace add, list, refresh and check each hand-rolled
marketplaceCacheDir then fetchMarketplaceSource.execute then
catalogRepo.load. ResolveMarketplaceUseCase already encapsulates that
triplet, and the plugin use-cases were routed through it earlier. All four
marketplace commands now use it too.

Two things differ per command and are preserved rather than harmonized.

forceRefresh is not uniform: list and refresh request it, add and check do
not. Verified in the final code.

Error policy is not uniform either. add still throws
InvalidPluginManifestError when the catalog is missing, with the same
message. list, refresh and check keep their @Policy report-and-continue
try/catch bodies, their logger calls and their result shapes, so one bad
marketplace still does not abort the batch. ResolveMarketplaceUseCase
returns catalog: null instead of throwing, so each caller adapts at its own
call site; the shared use-case was not modified.

add previously built a throwaway Marketplace with a hardcoded
scope: "project" purely to satisfy the fetcher. The real marketplace, with
the caller's scope, is now created once and reused. Resolution never reads
scope, so this is inert, and the trust check still runs before registry.save.

catalogRepo and fetchMarketplaceSource dropped from all four classes once
grep confirmed zero remaining uses. Every construction site updated,
including deps.ts (resolveMarketplaceUseCase hoisted above the four) and 5
test files.

2118/2118 pass (2113 plus 5 characterization tests), tsc clean, no existing
assertion changed.

Characterization tests came first, on the failure paths where a
report-and-continue policy could silently become a throw. check branches
71.4% to 89.5%, list 77.8% to 100%.

Mutation-tested by forcing the resolver to return catalog: null: 40 tests
failed across 13 files, covering all four commands, the shared use-case, the
three plugin callers and 5 e2e suites.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Coverage gate for epic E8's three refactors. Measured the eight files they
touch; two findings changed the plan.

RestoreRegularFilesUseCase had no dedicated test file anywhere. Its 42.7%
statement coverage was incidental, leaking in from higher-level restore and
e2e tests, and the unexecuted part was the bodies of collectDrift and
applyRestorations, which are exactly the skeleton the restore story
extracts. Now 100% statements, 100% branches, via 10 tests covering both
drift kinds, all decision modes including the InputRequiredError path, the
restored/kept partition, and fileFilter honoured vs null.

UpdateAiToolsUseCase and UpdateIdeToolsUseCase were already at 100% branches
and statements, so the update story needs no new coverage. Confirmed and
left alone rather than padded.

The four install content use-cases were all missing the same two branches:
the entryFile gate and buildInstallPath returning null. Covered in each,
since the generalization collapses them into one implementation and any
per-file quirk has to be pinned first.

Two asymmetries are now pinned rather than smoothed over. copilot's handlers
return null for .gitkeep where claude's never do. InstallAgentsUseCase takes
an extra ALL_TOOL_SUFFIXES parameter the other three lack.

Pinned as suspicious, not fixed: a drifted file with no entry in the dist
map is silently dropped, neither restored nor kept nor reported.

One branch stays uncovered in all four install files:
\`.split("/").at(-1) ?? relativeFileName\`. split never returns an empty
array, so the fallback is unreachable. Documented rather than faked with a
test that cannot fail.

No production code changed. 2136/2136 pass (2118 plus 18), tsc clean.

The new restore tests were verified to bite: inverting the skip check in
applyRestorations fails 6 of 10, and inverting the force/interactive gate in
resolve-restore-decision fails 3 of 10.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat(aidd-pm): add product brief discovery skill

Product discovery lacked an evidence-bounded artifact before PRD work. Add a lightweight flow that frames, researches, visualizes, shapes, and persists a Product Brief while keeping users in the feedback loop.

Refs #349

* fix(aidd-pm): preserve stable skill identities

Existing numbered skill names are public commands and stable references. Add Product Brief at the next free identifier without changing PRD, Spec, or Spike.

Refs #349

* chore(aidd-pm): remove headless test report

Keep execution evidence outside the shipped framework change.

Refs #349
Four near-identical classes installed agents, commands, rules and skills.
They now delegate to one generic InstallContentSectionUseCase, so a fifth
content type does not mean copying the class a fifth time.

The four were not as identical as the ticket assumed. Three differences,
each preserved:

- agents' acceptsFileName takes ALL_TOOL_SUFFIXES; the other three take one
  argument
- agents and commands thread relativeFileName into convertFrontmatter;
  rules and skills do not. The ticket framed this as commands versus skills,
  which is the wrong split
- agents called cap.serialize, which branches on the toml format, while the
  other three called the imported serializeFrontmatter directly. Their own
  cap.serialize are pass-throughs to that same function, so calling
  cap.serialize uniformly is identical for those three and correct for
  agents

The two uniform capability methods are called directly. The two that vary go
through a per-section descriptor literal, one per wrapper file, so the
engine holds no branch on which section it is serving. A switch there would
have moved the duplication rather than removed it.

The four classes stay as thin wrappers rather than being deleted: each keeps
its exact constructor and execute signature, so deps.ts,
generate-tool-distribution-use-case.ts and all four test files are untouched.

2136/2136 pass, tsc clean, no cast and no any in the generic code. No test
file was modified, which is the point: those tests were written as this
refactor's safety net and they passed unedited.

Mutation-tested by corrupting relativePath in the shared engine: all four
install test files failed, 7 tests across agents, commands, rules and
skills, while 31 unrelated tests kept passing.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.
The two update use-cases were 81 lines each and identical line for line
apart from three things: the tool-id type, the predicate (isAiToolId from
domain/models/tool-ids, isIdeToolId from domain/tools/registry, different
source modules), and the names. No logic differed.

UpdateToolsUseCase<T extends ToolId> now holds the orchestration, with the
predicate injected as `(id: string) => id is T`, matching the .filter shape
the originals already used. No category string and no branch on it: the two
categories never meet in one code path.

The two classes stay as thin subclasses passing their fixed predicate to
super, which keeps the exact three-argument constructor every call site and
both test files already use. deps.ts and both test files are unchanged.

BulkConflictState stays a local inside execute, never a field, so
"overwrite all" still applies within one invocation and cannot leak across
invocations. Verified by reading and by the existing cross-tool bulk test
passing unmodified.

Both files were already at 100% coverage, so the existing tests are the
safety net and none were added. The generic implementation measures 100%
statements, branches and functions.

2136/2136 pass, tsc clean, no cast and no any.

Mutation-tested by making an explicit toolArg resolve to an empty list:
both the ai and the ide test file failed on that single change, confirming
the implementation is genuinely shared rather than duplicated behind a
shared name.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.
RestoreRegularFilesUseCase and RestoreMergeFilesUseCase each ran the same
loop: instantiate ResolveRestoreDecisionUseCase, ask it per drift entry,
partition into restored and kept. A bug in the force, interactive or skip
logic had to be fixed twice.

RestoreDriftEntriesUseCase now owns that loop. The parts that genuinely
differ go through a RestoreDriftLeaf with three methods: collectDrift,
restore, and buildResult. Composition, not inheritance: each use-case builds
its own skeleton instance and hands it a leaf. No branch on which caller it
is serving lives in the shared file.

The I/O leaf stays two implementations, as the cartography requires. Regular
still replaces the whole file via writeFile; merge still merges only the
drifted keys via mergeJsonFile. Neither call appears in the shared skeleton.

Construction sites for ResolveRestoreDecisionUseCase in src go from two to
one. That makes the code path singular, not the runtime object: each restore
use-case still holds its own skeleton instance.

Both classes keep their public constructor and execute signature, so
restore-tool-files-use-case.ts needed no edit.

2144/2144 pass (2136 plus 8 new merge tests), tsc clean, no cast and no any.
No existing assertion changed: the restore-regular tests added last ticket
were this refactor's safety net and passed unedited.

Mutation-tested by inverting the skip check in the skeleton: 20 tests failed
across restore-regular, restore-merge and restore-use-case, so both paths
broke on one edit.

Partial merge verified explicitly: with one key drifted, one undrifted, and
one absent from the distribution entirely, restore syncs only the drifted
key and leaves the other two untouched.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.
* fix(cli): report restore outcomes it previously hid

Two defects found while refactoring nearby code during the cartography
backlog, recorded then but left alone to keep those PRs
behaviour-preserving. Both change user-visible output, because the output
was wrong.

Restore silently dropped drifted files. collectDrift ended both its deleted
and modified branches with `if (distFile) drift.push(...)` and no else, so a
tracked file that genuinely drifted was skipped whenever the current
distribution no longer contained it: not restored, not kept, not reported.
The user was told restore succeeded while the file stayed broken. Reachable
whenever the manifest outlives the distribution.

The merge path had the same hole, confirmed rather than assumed: its
`if (!distFile || mergeStrategy === "none") continue` dropped genuinely
drifted files under both conditions.

Drift collection now separates "did it drift" from "can it be restored".
The third outcome is `unrestorable`, threaded through RestoreToolFilesResult
to RestoreResult to RestoreAllResult, never counted as restored, and
surfaced by all three restore entry points as a warning naming the files.
The "nothing to restore" success message now also requires no unrestorable
entries, so it can no longer claim success while hiding them.

Restore also reported two different numbers depending on its path.
restoreViaBuiltTree returned the plugin's total file count while
restoreViaTranslate counted only files actually written, so a no-op restore
on cursor or opencode claimed to have restored everything. Both now mean the
same thing.

Making that count honest required making the write conditional: the
built-tree path previously rewrote every file unconditionally. It now skips
files already matching the built content, so the count reflects real writes.
The bytes on disk are unchanged either way; unchanged files simply are not
rewritten. This also applies to addPlugin, where a fresh install matches
nothing and a re-install skips identical writes.

Two existing tests were named "silently drops a file" and pinned the bug.
They are renamed and now assert the file is reported. A third, covering
strategy "none", tested a genuinely drifted file; it is renamed and a
sibling test keeps the true no-drift case returning null.

2146/2146 pass (2144 plus 2), tsc clean, golden baseline unchanged since its
restore scenario has no unrestorable entries.

Each fix was reverted individually to confirm the new tests catch it.

Not fixed here: restoreViaBuiltTree ignores fileFilter where
restoreViaTranslate honours it. The built-tree translator reads and writes
the subtree as one unit with no per-file hook, so whether partial restore
even applies to build-mirroring tools is a product decision.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

* refactor(cli): warn about unrestorable files from one place

The previous commit copy-pasted the same five-line warning, message string
included, into ai.ts, ide.ts and restore.ts. That is the duplication this
backlog exists to remove, reintroduced.

printUnrestorable moves to display/restore-display.ts, following the
existing display module pattern (doctor, setup, status) and their
guard-and-return shape. The message is now defined once.

2146/2146 pass, tsc clean.
…556)

* fix(cli): stop update and restore materializing marketplace plugins

claude, codex and copilot set translationMode "marketplace", so
resolveTranslator hands them ModeAMarketplaceTranslator, whose contract is
to register a plugin reference and write nothing: its addPlugin records an
empty files set.

Update and restore did not honour that. Both narrowed the resolved
translator with `instanceof BuiltTreeMaterializationTranslator` and, for
anything else, fell through to a raw materializing path. So a plugin that
install deliberately left unmaterialized got its files written to disk and
recorded in the manifest the first time the user ran update or restore,
leaving it both natively registered and materialized.

Update's `isLocalMarketplace` guard looked like it covered this but did not:
a github-hosted marketplace resolves plugin sources to kind "git-subdir",
never "local", so the guard never fired for the case that matters. Restore
had no guard at all.

Both now dispatch on what resolvePluginTranslator returns for the tool
rather than on the translator's concrete class, so a tool's translation mode
decides the behaviour in one place. materializeViaBuiltTree widens to
materializeViaTranslator, taking the PluginTranslator interface, and both
call sites share it rather than repeating the logic.

Install was already correct and is unchanged.

cursor and opencode cannot reach this path: installScope "user" and
translationMode "flat" are matched by earlier branches in resolveTranslator,
so they only ever get BuiltTreeMaterializationTranslator or null. A flat
mode regression test covers it.

2150/2150 pass (2146 plus 4), tsc clean, golden baseline unchanged.

The new tests were verified against the pre-fix code: 3 of 4 fail there with
the predicted symptom, the flat-mode guard passes in both states.

Known gap, not fixed here: a user who already ran update or restore has
stray files on disk. Update self-heals when a newer version exists, because
it deletes the old manifest files first, but it never runs when no version
bump is available, and restore has no delete step at all so it leaves the
files orphaned once the manifest entry goes empty. Adding a symmetric delete
to restore would fix it and would also delete files on disk, so it needs a
decision rather than a silent implementation.

* fix(cli): clear plugin files a marketplace tool should never have had

Users who ran update or restore before the previous commit have stray
materialized files for marketplace-mode plugins. Update self-heals, because
it deletes the old manifest files before rebuilding, but only when a newer
version exists upstream: on the latest version it returns early and never
clears them. Restore had no delete step at all, so once the manifest entry
went empty those files were orphaned and untracked.

Restore now clears them, which makes it the reliable path for a plugin
already on the latest version.

The delete is bounded three ways. It runs only when the resolved translator
reports mode "marketplace", which only ModeAMarketplaceTranslator does; both
materializing translators report "flat", so built-tree and flat installs
cannot reach it. It iterates the plugin's own manifest keys rather than
scanning a directory, so it cannot touch a file the plugin never wrote. And
it joins those keys to the plugin base dir from resolvePluginBaseDir rather
than assuming the project root.

A file placed inside the same plugin directory but absent from the manifest
survives untouched, with a test asserting exactly that. An empty manifest
entry makes the whole thing a no-op.

deleteOldFiles moves from a private method on PluginUpdateUseCase to a
shared helper in plugin-helpers.ts, used by both. Update's call stays
unconditional: it also clears files dropped between versions on the
built-tree and translate paths, so it is not the same concern as this
cleanup.

2152/2152 pass (2150 plus 2), tsc clean, golden baseline unchanged.

Mutation-tested by forcing the guard false: both new tests fail with
"expected 'stray content' to be undefined".
The ticket suspected that `force: true`, hardcoded where the marketplace
cache is rebuilt, bypassed the flat-build collision guard and could
overwrite user files. Tracing it showed the premise was wrong in a more
interesting way.

FrameworkBuildUseCase.execute never read options.force. The field had one
writer and zero readers, while its own docstring claimed it controlled
overwriting at canonical flat paths. Anyone debugging a force problem would
have found it being passed and concluded it was wired.

What actually drives FlatBuildStrategy.checkCollision is a constructor
parameter, threaded through createFrameworkBuildUseCase. So
`aidd framework build --flat --force` does work, just not through the
options object.

The dead field and both call sites are removed. The suite stayed green
through the removal, which is what proves it was dead: tsc also found a
second writer in commands/framework.ts that grep had missed.

On the original concern: it is infirmed. runBuild has exactly two callers,
one passing the .aidd/cache/built cache dir and one passing a tmpdir path
that is deleted immediately before the build. Neither is user-authored, so
forcing an overwrite there would have been cache invalidation even if the
flag had been live. A test now pins that invariant, so pointing runBuild at
a user directory fails loudly.

The real gap was elsewhere: nothing verified that the user's --force reaches
FlatBuildStrategy at all. Making deps.ts ignore the caller's flag broke no
test. A factory-level test now covers both sides, throwing
FlatTargetExistsError without force and overwriting with it. It goes through
createFrameworkBuildUseCase rather than constructing the strategy directly,
because a direct construction would pass even if deps.ts stopped threading
the flag, which is exactly the hole that existed.

2155/2155 pass (2153 plus 2), tsc clean.

Mutation-tested: forcing deps.ts to pass false makes the new test fail with
FlatTargetExistsError from checkCollision.

Noted for future work: FlatBuildStrategy.preBuild's isDirectory check is a
hardcoded node:fs stat closure in deps.ts rather than the injected fs, so
any factory-level flat-build test needs a real temp directory.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.
…ands (#515)

* docs: fix marketplace install steps and note host-wide duplicate commands

Cursor no longer needs a manual Developer -> Reload Window step; it
reloads copied plugins automatically. Also document that a host-wide
install across several tools can surface duplicate commands when one
tool reads another tool's settings, with the Cursor setting to hide
them.

* docs: move cursor duplicate-command fix into cursor section

Keep the general host-wide note about duplicate commands tool-agnostic
and place the Cursor-specific setting to hide them inside the Cursor
install block, where a reader looking at Cursor steps will find it.
* docs(framework): simplify contribution to one issue-first path

Replace the role-differentiated entry table with a single flow: any
contributor opens a Rapide or Détaillée issue, a Certifié or Habilité
validates it (board Status Ideation -> Todo), then anyone opens the
PR. Drops the Certifié-only PR gate and the 7-day roadmap vote for
these issues; adds a Principles section (anti-slop, token economy,
skill structure, memory upkeep).

Closes phase-1 of aidd_docs/tasks/2026_07/2026_07_29_simplifier-contribution/plan.md

* docs(framework): mark contribution simplification plan implemented

* docs(framework): tighten contribution flow, sync renamed roles

CONTRIBUTING.md: translate the how-to-contribute and Principles
sections to English (repo convention), compress to a plain 1-2-3-4
list up top, drop the sub-numbering that clashed with it, and trim
Releases to a pointer (detail already lives in RELEASE.md/MAINTAINERS.md).

GOVERNANCE.md: finish the Habilite/Certifie -> Trusted Partner/
Certified Member rename started earlier, and fix the team links,
which pointed at stale slugs (habilitated/certified instead of the
real trusted-partners/certified-members).

Propagate the same rename to CONTRIBUTORS.md and docs/MAINTAINERS.md
to remove the duplication/drift risk. Trim ROADMAP.md's "How to
influence" section and add the missing Thursday 10:30 Discord sync.

* docs(framework): fold CONTRIBUTORS.md into README, unify on Maintainer

CONTRIBUTORS.md wasn't a GitHub-special file and was linked from
nowhere but MAINTAINERS.md; its only real content (the contrib.rocks
mosaic) now lives in README.md's existing Contributing section.

Rights language (merge, veto, review, CODEOWNERS) now says Maintainer
throughout instead of Trusted Partner, matching the roles ladder's own
top rung: Trusted Partner and AIDD Staff are the two paths into one
Maintainer tier, not two different rights holders. Trusted Partner is
kept only as the promotion-path name.

Also fixes CODEOWNERS, which still pointed at the stale
@ai-driven-dev/habilitated team/comment - real merge-review assignment
would have silently broken.

* docs(framework): translate template names, fix anchors and links

Issue templates and their CONTRIBUTING.md links were half-English
half-French (Contribution Rapide/Detaillee) - now Quick/Detailed
Contribution throughout.

Fixes two breaks from the terminology rename: CONTRIBUTING.md linked
to GOVERNANCE.md#-code-decisions-merging, a heading that no longer
exists after the section was retitled; GOVERNANCE.md's Maintainer row
listed the same team link twice in the Team column.

* docs(framework): cut repeated branch/merge facts in CONTRIBUTING.md

Branch-off-next was said three times (mermaid, step 3, PR section);
"no one merges their own PR" restated what GOVERNANCE.md#-code-decisions
already covers. Kept one mention of each.

* docs(framework): cut CONTRIBUTING.md to what a newcomer needs

Dropped the Releases section (pure restatement of the Commit bullet's
RELEASE.md link), the hotfix/* edge case, the label-automation and
squash-merge trivia, and two Reference bullets aimed at plugin authors
rather than a first-time contributor. Every remaining line is one
clause, no nested asides.

* docs(framework): clarify report-vs-build split, restore cut facts

Step 1 (open an issue) now reads as its own stopping point - reporting
or proposing doesn't commit you to building it, and step 3 says
anyone can pick up a validated issue, not just its opener.

Moved Principles right after the flow (was buried after Set up), and
added back "Claude Code syntax only" - present in the file before this
branch's rewrite, dropped by accident.

Set up and Make your change were cut too hard: restored what
`make setup` actually does and the reload/session-restart + one-scope-
per-commit facts. Open a pull request now links the PR template
directly, it only named it before.

* docs(framework): flesh out Make your change with principles + multi-tool test

Points back to Principles, and testing now says what "locally" means
concretely: Claude plus at least one other tool, since the CLI's
Claude-to-per-tool translation is the part most likely to break.

* docs(framework): fold bug reports into the template list

Bug Report is the same mechanism as Quick/Detailed Contribution - one
issue template, same board, same validation gate. Listing it as a
separate "Reporting a bug" section duplicated the concept; it's now
a third choice in step 1, mermaid updated to match. The Discussions
caveat moved up alongside it since that section is gone.

* docs(framework): mermaid shows the 3 template branches, Principles intro

Bug/Quick/Detailed now render as three parallel entries converging on
validation, then the linear dev tail: Set up, Changes, PR, Review,
Merged - matching the section order below instead of restating
branch/commit patterns already linked there.

Principles gets one line of framing before the bullets.

* docs(framework): split Discussions from the issue templates line

Step 1 crammed three template links plus a stop-here note plus a
Discussions redirect into one line. Discussions now gets its own
sentence before the mermaid; step 1 just lists the three templates.

* docs(framework): show the discussion-vs-issue fork in the mermaid itself

The question/exchange vs bug/improvement/feature split was only in
prose above the diagram. Added a Start node that branches to
Discussions or the three issue templates, so the diagram carries the
fork instead of the sentence above it.

* docs(framework): distinguish Quick vs Detailed in the diagram

Both nodes just said their name with no hint of what separates them.
Added the field difference (problem+solution vs scope+acceptance
criteria) that actually distinguishes the two templates. Also fixed
the source list numbering (two "1." items) now that Discussions is
its own bullet.

* docs(framework): trim the issue picker's contact links

Dropped the explicit "Report a security vulnerability" contact link -
GitHub already auto-adds its own shield-icon tile for that once
SECURITY.md + advisories are enabled, ours was a dead ringer. Dropped
"Website" too, not an actionable choice at the point someone's about
to open an issue.

* docs(framework): actually rename the issue template files

Only the display name: field changed to Quick/Detailed Contribution -
the files themselves stayed feature_request.yml/roadmap.yml, a
filename/purpose mismatch for anyone editing them later. Renamed to
quick_contribution.yml/detailed_contribution.yml and updated the
?template= links in CONTRIBUTING.md to match. Checked every other
in-repo mention first - only two historical plan docs reference the
old names, left as records of what existed at the time.
Bumps [lefthook](https://github.com/evilmartians/lefthook) from 1.13.6 to 2.1.10.
- [Release notes](https://github.com/evilmartians/lefthook/releases)
- [Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
- [Commits](evilmartians/lefthook@v1.13.6...v2.1.10)

---
updated-dependencies:
- dependency-name: lefthook
  dependency-version: 2.1.10
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…21.2.0 in /cli (#539)

chore(deps-dev): bump @commitlint/config-conventional in /cli

Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 19.8.1 to 21.2.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.0/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 21.2.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
….md (#563)

These three never made it into #562 - individually staged files got
git add'ed by name throughout that branch's work, and these three
were never among them, then the branch squash-merged before anyone
noticed.

- ROADMAP.md: replace the placeholder "Thursdays 10:30" text with the
  real weekly Discord event link.
- RELEASE.md: trim the commit-type -> changelog table and restate,
  duplicated with aidd_docs/memory/vcs.md and docs/MAINTAINERS.md.
- UPGRADE.md: drop the frozen v3->v4 migration guide, no longer
  needed. No file in the repo linked to it.
* chore(deps): bump @inquirer/prompts from 7.10.1 to 8.5.2 in /cli

Bumps [@inquirer/prompts](https://github.com/SBoudrias/Inquirer.js) from 7.10.1 to 8.5.2.
- [Release notes](https://github.com/SBoudrias/Inquirer.js/releases)
- [Commits](https://github.com/SBoudrias/Inquirer.js/compare/@inquirer/prompts@7.10.1...@inquirer/prompts@8.5.2)

---
updated-dependencies:
- dependency-name: "@inquirer/prompts"
  dependency-version: 8.5.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(cli): defer simulated keypresses to match inquirer v8's render cycle

@inquirer/core 11 (bundled by @inquirer/prompts 8) defers the first
render by one setImmediate tick for any stream with a readableFlowing
property - added upstream to let real stdin's OS-buffered data flow
through readline harmlessly before listeners attach (SBoudrias/
Inquirer.js#1303). Every Readable, including the PassThrough these
tests use, has that property, so the deferral now applies to them too.

The tests wrote simulated keypresses via process.nextTick, which runs
before the deferred setImmediate that registers inquirer's keypress
listener - the write lands with nobody listening, and the prompt hangs
until the 60s timeout. Switching to setImmediate orders the write after
listener registration. Verified: 22/22 pass in this file, matches
exactly what CI's cli/Test job flagged (13 failures, all here).

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Baptiste LAFOURCADE <baptiste.lafourcade@gmail.com>
…474)

Bumps [github/codeql-action/autobuild](https://github.com/github/codeql-action) from 4.37.1 to 4.37.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@7188fc3...e4fba86)

---
updated-dependencies:
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.1 to 4.37.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@7188fc3...e4fba86)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…mmit

The linear promotion excludes squash-merged back-merge conflict
resolutions from cherry-pick (they mark a point main was already
folded into next, replaying them would duplicate content). Commit
4870dd6 was one such exclusion, but it also carried two next-only
changes bundled into the same squash: knip's icacls ignore and
lefthook's markdown-links --ignore scope for cli/'s pre-existing
fixtures and task docs. Applying next's exact content for both so this
promotion's tree matches origin/next exactly, per the invariant this
workflow checks before pushing.
Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>
@aidd-bot
aidd-bot Bot requested a review from a team as a code owner July 31, 2026 04:50
# Conflicts:
#	.claude-plugin/marketplace.json
#	.release-please-manifest.json
#	CHANGELOG.md
#	cli/CHANGELOG.md
#	cli/package.json
#	plugins/aidd-context/.claude-plugin/plugin.json
#	plugins/aidd-context/CHANGELOG.md
#	plugins/aidd-pm/.claude-plugin/plugin.json
@blafourcade
blafourcade enabled auto-merge (squash) July 31, 2026 04:57

@blafourcade blafourcade left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved 8 conflicts (marketplace.json, .release-please-manifest.json, CHANGELOG.md x3, cli/package.json, 2 plugin.json) — all release-please-owned files where main is a strict superset (same history + the new release entry). Verified via git diff before resolving.

@blafourcade
blafourcade merged commit c2c2c94 into next Jul 31, 2026
14 checks passed
@blafourcade
blafourcade deleted the back-merge/main-to-next-30605182102 branch July 31, 2026 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants