Skip to content

ci: promote next to main - #920

Merged
blafourcade merged 64 commits into
mainfrom
promote/next-to-main-35987816792
Sep 24, 2026
Merged

blafourcade merged 64 commits into
mainfrom
promote/next-to-main-35987816792

Conversation

@aidd-bot

@aidd-bot aidd-bot Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Automated promotion of next to main, from a snapshot of next taken at run 35987816792. Merged as a merge commit so main keeps every conventional commit for release-please, and so both branches keep a shared merge base for the back-merge. Do not squash, do not rebase.

alexsoyes and others added 30 commits September 3, 2026 15:42
)

`opencode-plugin.js` spawned `./journal.cjs` as its own sibling. Since the
build stopped materialising every hook script flat into `.opencode/plugin/`
and started namespacing them under `.opencode/hooks/<plugin>/`, that path
names nothing: every spawn exited MODULE_NOT_FOUND, the plugin's own
try/catch swallowed it, and OpenCode recorded no session at all while still
reading as covered.

Measured, not inferred: a real `opencode run` in an installed project wrote
zero journal lines; the same run after repointing the path wrote
session_start, task_declared and turn_end, and `telemetry read` returned 2
requests, 16,303 input and 39,104 cache-read tokens.

The literal now matches what the generated bridge two files away already
resolves, `../hooks/<plugin>/`.

Two guards, because neither alone would have caught it. build.unit.test.ts
reads the JOURNAL_SCRIPT literal out of the shipped module and resolves it
against the path the build contract declares — red on flipping the literal
back, red on moving OPENCODE_HOOKS_DIR. opencode-plugin.test.js staged its
fixture in the layout the build no longer writes, so it agreed with the bug;
it now mirrors the split the loader actually sees, and goes red 2 of 3 on the
old path.

The plugin README drops to what a reader needs to understand the thing:
what it is, why it exists, how it works, how to start, what each tool can
answer. Every partly-measurable tool keeps its reason, since a limit a reader
has to look up gets read as a zero.


Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
AIDD-Session-Id: 2c21d903-3a7e-47ac-83f8-d8b7ae3aa579

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ci: reuse validated mutation gates on promotion
…813)

* fix(cli): an interactive restore that ticks nothing restores nothing

`RestoreAllUseCase` forwarded an empty checkbox answer as `files: []`, which the restore
reads as "no selection made", the same as a non-interactive run, and every drifted file was
restored. An empty selection now ends the run with nothing restored; a run where nothing
drifted, so no checkbox was shown, still restores the plugin files the picker never offers.

Red first: the new test failed with `expected '{ "respectGitignore": false, …' to be
'EDITED OUTSIDE THE CLI'`.

Closes #805

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* test(cli): the picker tests say what an empty selection now means

Two tests from #804 pinned the behaviour #805 fixes: an empty selection forwarded as
`files: []`. They now assert that the run never delegates when nothing was ticked, and that
a run with no drifted entry delegates with no selection at all.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* test(cli): kill the surviving mutants of the telemetry context

The telemetry scope held at a floor of 75 with 844 survivors and 135
uncovered mutants of 4413. Tests now name the behaviour each survivor
stood for, across the domain (attribution, report, formats), the
application use cases (report, diagnose, on, off, identity, local cost)
and the adapters (sink, evidence, identity, backlog, run journal, the
four per-tool transcript readers). Each was red against its hand-applied
mutant before it went green; nothing under src/ changed.

Mutation score measured on this tree: 95.1. The floor moves to 93.

Refs #799

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* test(cli): keep the identity tests off the real Windows profile

On Windows the identity file lives under %APPDATA%, never HOME, and these
tests moved HOME alone: the runner's CI wrote and read
C:\Users\runneradmin\AppData\Roaming\aidd\identity.json, so "no identity
exists" found one a previous test had left there. Every sandbox now moves
both together. The two tests asserting a POSIX ENOTDIR skip on Windows,
which answers ENOENT for a path running through a file.

The backlog "unreadable" case read through a file used as a folder, which
is ENOTDIR on POSIX and ENOENT on Windows. A directory where the link file
belongs is EISDIR on both; still red when ENOENT stops being told apart.

Refs #799

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* test(cli): name the POSIX-errno skip once in the identity tests

The same Windows explanation sat above two skips. Stacked with the
runtime tests, the repetition pushed tests/ past its comment baseline
(2989 against 2987); one named predicate says it once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* refactor(cli): drop the MCP exclusion no command could reach

`framework remove` was the only caller of UninstallUseCase and passed an
empty mcpFilter, so UninstallMcpExclusionUseCase never removed or recorded
anything, and nothing read the manifest's excludedMcp. Deleted: the use
case, the mcpFilter option, McpExclusion and its equality, the manifest's
exclusion methods and the excludedMcp member of a tool entry, with the
tests and the round-trip fixture that exercised only that path.

A manifest already carrying excludedMcp still loads: the tool-entry parser
reads named fields and ignores the rest, so the field is dropped on the
next write. The new test asserting the rewrite omits it failed before the
deletion (the rewrite still held excludedMcp) and passes after.

Measured: typecheck, lint, knip and the type-honesty check clean;
test:arch 126 passed; unit and integration 4994 passed; e2e 297 passed.

Refs #806

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* docs(cli): stop listing mcp-exclusions among the manifest members

The framework skill's concept table named a module the previous commit
deleted.

Refs #806

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…#818)

Removing the commit-trailer delegate rewrote prepare-commit-msg through
the atomic temp-and-rename write, which does not carry the mode over: a
hook that was executable came back without its bit, and git skips such a
hook silently, so the rest of the person's own hook stopped running.

The bit is read before the write and restored after it, only when it was
set. Two tests on a real temporary repository: the removal keeps the hook
executable (red without the fix), and a hook that was not executable stays
so (red with the restore made unconditional). The second is POSIX-only.

Fixes #817


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…nal line (#820)

v5.10.0 shipped an OpenCode telemetry plugin that spawned a journal.cjs the
build no longer puts beside it (#812): every session journalled nothing,
and smoke:real's real `opencode run` still passed, because it installs the
smoke fixture, which journals nothing, and only asks whether the bridge
loads.

smoke:real now installs this repository's own aidd-telemetry into a
throwaway project for opencode, turns measurement on, runs `opencode run`
and reads the run journal. A session opens on its first call, so only a
turn that completed can prove an empty journal: a completed turn with no
session_start line is a failure, a model that never answered is a skip
that says so.

Refs #814


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ode profiles (#821)

* test(cli): kill the surviving mutants of the copilot profile

tools-copilot under run-mutation.mjs --force, 426 mutants:
- before: 82.39, 55 survived, 20 uncovered
- after: 95.31, 19 survived, 1 uncovered

Floor raised from 80 to 93.

Refs #799

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* test(cli): kill the surviving mutants of the codex profile

tools-codex under run-mutation.mjs --force, 381 mutants:
- before: 89.50, 38 survived, 2 uncovered
- after: 96.33, 14 survived, 0 uncovered

Floor raised from 87 to 94.

Refs #799

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* test(cli): kill the surviving mutants of the opencode profile

tools-opencode under run-mutation.mjs --force, 283 mutants:
- before: 87.63, 34 survived, 1 uncovered
- after: 96.82, 8 survived, 1 uncovered

Floor raised from 81 to 94.

Refs #799

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…nothing calls (#823)

The copilot commands capability is built with convertCommandFrontmatter
directly, so commandsHandler.convertFrontmatter was never reached: the
handler serves buildFilePath alone. Copilot's commands are unchanged; the
tools suite and the golden snapshots pass as they were.

Fixes #822


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
#827)

fix(telemetry): a sink record's day comes from the moment it parses to, never from its text

telemetrySinkRecordDayKey sliced the first ten characters of any parseable
timestamp ending in Z. A moment not written in canonical ISO form got the
wrong day or no day at all: "2026-09-10T24:00:00Z" read as 2026-09-10
instead of 2026-09-11, "Thu, 10 Sep 2026 23:00:00 Z" as "Thu, 10 Se", a
key outside every period, so the record left a report without being
counted as undated. The day is now always the parsed moment's UTC day.

Two tests, both red before the change. The three comment lines that
explained the shortcut go with it, and the src comment ceiling drops by
the same three.

Fixes #825


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ed (#828)

parseWorktree's comment promised that a torn or empty worktree_id reads as
"not stated", on the ground that asString rejects "". It does not: a
session_start carrying "worktree_id": "" was read as a worktree named
nothing. An empty value is now dropped like an absent one, for the repo id
too, and the comment says what the code does.

Two tests, the first ones the worktree fields ever had: an empty id and
repo id read as absent (red before the change), and stated ones are kept
as written (red with both dropped unconditionally).

Fixes #826


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…waway home (#832)

A test that relocated only AIDD_USER_CONFIG_DIR was one mutant away from the real
profile: a mutant dropping that override sent it to ~/.config/aidd of whoever ran
the mutation. The mutant was killed, and the file was already written. One run left
a test marketplace entry in a real registry, and another overwrote a real auth.json
with a test token.

A global setup, shared by the suite's and the mutation run's unit and integration
projects, points HOME, USERPROFILE and APPDATA at a temp home and unsets the
overrides, so every route to a profile ends there. It runs in the main process
because Stryker runs vitest in worker threads: a thread's own process.env never
reaches os.homedir(), so a per-file setup left that route on the real home.

One test proves what a test sees, in threads and in forks. One proves all four
projects declare the setup.

Fixes #831


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
#834)

* fix(cli): leave what a project did not install to whoever installed it

A host keys a plugin by `<plugin>@<catalog name>` (codex, copilot, claude) or
by the plugin's name alone (cursor, `~/.cursor/plugins/local/<plugin>`). A
project installing a plugin a person already had under that key took it over,
and the project's `clean` or `plugin remove` then undid the person's own
install: a codex `[plugins."…"]` section deleted with its cache, a copilot key
flipped to `false`, a cursor `plugin.json` overwritten then deleted.

- sync: a ref the host already reports enabled, and that this project never
  recorded, is neither enabled again nor recorded in `pluginRefs`. A ref this
  project recorded stays its own on every later sync, whatever the host reports.
  An unreadable host registry changes nothing: the ref is enabled as before.
- plugin remove: a ref absent from this project's recorded `pluginRefs` is left
  enabled and named, never uninstalled.
- plugin add: a user-scope plugin directory this project had no entry for is
  neither overwritten nor tracked, and the warning names it. A re-install of
  this project's own plugin still overwrites it.

Fixes #829

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* fix(cli): keep a skipped cursor install's project hooks and manifest entry

Skipping the whole install for a user-scope plugin directory someone else
owns also skipped what the install does inside the project: the plugin's
hooks merged into `.cursor/hooks.json`, its MCP entries, and the manifest
entry. A second project on the same machine then journaled nothing, and
`aidd plugin remove <plugin> --tool cursor` failed with "Plugin is not
installed" (smoke: `plugin remove → cursor`, exit 1).

The guard moves to the two user-scope translators, between writing the
files and recording them: the user-scope directory is neither written nor
tracked, while project hooks, MCP and the manifest entry land as before.
The entry owns no files, so `plugin remove` and `clean` delete nothing of
the directory.

Refs #829

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

---------

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

* test(cli): kill the surviving mutants of the kernel

Every error class now has its exact name and message pinned, and the pure kernel helpers
that had no test of their own (jsonc stripping, confined file names, the JSON narrowers,
describeError, isMergeContentEmpty, removeRedundantGitkeeps) get one. The frontmatter
parser, semver precedence, plugin-source messages and the built-cache path parsers are
asserted structure by structure, so a dropped anchor, a swapped join or a missing branch
turns a test red.

Kernel scope, every mutant replayed: 75.2 to 97.1 (killed 1449, timeout 32, survived 45,
uncovered 0 of 1526). The 45 left are documented equivalents plus seven stryker records
as survived although the hand-applied mutation turns the named test red. Floor raised
from 71 to 95.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* test(cli): kill the surviving mutants of the runtime

GitAdapter gets its first test, against a real temporary repository: hook install and
removal under this CLI and under lefthook or husky, core.hooksPath, the trailer count and
every answer of readCommitTrailerSetup. The auth adapters, the self-updater, the file
adapter, the prompter, the HTTP client, the update check, the release resolver, the
project-root and git-environment helpers and the composition root (createDeps, wireTools,
the build registry) are asserted on their exact commands, messages, headers and shapes.

The removal test found a defect rather than a mutant: prepare-commit-msg lost its
executable bit. The fix ships on its own in #818, which this branch sits on.

Runtime scope, every mutant replayed: 69.8 to 92.2 (killed 1182, timeout 16, survived 88,
uncovered 13 of 1299). Floor raised from 67 to 90.

Two POSIX-only checks skip where the platform cannot show them: a mode bit on Windows, and
a read-only directory on Windows or as root.

Refs #799

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* test(cli): skip the trailer setup's mode-bit checks on Windows

Windows CI failed two readCommitTrailerSetup tests that expect a hook or
a delegate to read as not executable: there `access(X_OK)` answers like
`F_OK`, so every existing file reads as executable, and git needs no mode
bit to run a hook. They skip on Windows, as the removal test already does.

Refs #799

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

* test(cli): name the mode-bit skip once in the git adapter tests

The same Windows explanation sat above three skips. Stacked with the
telemetry tests, the repetition pushed tests/ past its comment baseline
(2989 against 2987); one named predicate says it once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…835)

`clean` removes every project-scope marketplace recorded in a tool's
native registrations. Sync recorded each marketplace it tried, whether or
not the host accepted it: when a host refused `marketplace add` because it
already held that name under another registration, or when the build
failed, the entry was recorded anyway, and `clean` then ran the host's
`marketplace remove <name>` against a registration this project never made.

A marketplace is now recorded when this run registered it, or took it
back from a dead registration, or when an earlier run of this project had
already recorded it. A refused or failed one is left to whoever holds it.
Plugin refs are enabled exactly as before.

Fixes #833


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…es (#836)

`tools/domain/mcp-exclusion.ts` holds only `transformFor`, the transform of an
MCP server's launch command for the target platform. The exclusion it was
named for was deleted with #816, so the name pointed at a feature that no
longer exists. The module becomes `mcp-launch-command.ts`, with its test file,
its one import and its entry in the context-boundary public-module list.
No behaviour change.

Fixes #824


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
GitHub closes a pull request whose base branch is deleted, unless the merge
itself deletes it. Deleting the base of #819 through the API after #818
merged closed #819 instead of retargeting it. `vcs.md` now says to retarget
first, and how to replay a dependent branch after its base was squashed.

Fixes #843


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
blafourcade and others added 26 commits September 11, 2026 09:32
…ng a load failure as a kill (#857)

Stryker's vitest runner scores a mutant that stops a module from loading as
survived, on its own and in every release up to 10.0.0 (stryker-js #6150);
the reporter from #851 is what turns it into a kill. Nothing in the suite
proved the last step, Stryker itself scoring that mutant Killed.

tests/fixtures/stryker-canary/settings.ts holds exactly one mutant, a static
StringLiteral whose replacement makes JSON.parse throw on import. The canary
scope mutates it with a floor of 100. It runs whenever the harness changes,
so on every Stryker or vitest upgrade, and on any change to the reporter.

Evidence:
- inventory: one mutant, Killed, "Unexpected end of JSON input";
- reporter neutralized: Survived with testsCompleted 0, and the gate fails
  (exit 1, "mutation score 0.0 is below the 100 declared in mutation-scopes.json");
- restored: score 100.0, floor 100.

The architecture check that every scope matches a file now also looks under
tests/fixtures, the one place a scope may mutate something that is not product.

Fixes #856


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
In a linked worktree git exports GIT_DIR into every hook. A pre-commit or
pre-push job that runs pnpm inside cli/ (`cd cli && pnpm lint`, `pnpm --dir
cli test`, ...) lets pnpm 12 install missing dependencies first, which ran
cli's `"prepare": "lefthook install"`. With GIT_DIR set and no GIT_WORK_TREE,
git took cli/ for the work tree root: lefthook found no config there, wrote
its example into cli/lefthook.yml and reinstalled the hooks every worktree
shares. Reproduced in throwaway repos with lefthook 2.1.12 and pnpm 12.3.4.

The root package's `prepare` already installs the hooks, so cli drops its own
along with its lefthook devDependency, which nothing else used (knip flagged
it once the script was gone). An architecture test asserts that no script of
this package calls lefthook; it failed on the old package.json.

Fixes #860


Claude-Session: https://claude.ai/code/session_011x4ms5qcGuZgYhCxfdHMUb
AIDD-Session-Id: 4acc9a1c-19bc-4468-b8b6-e86644bcba60

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Analyze only relevant scopes in parallel when their context can stay isolated. Render grounded findings, categorical savings, and an editable execution prompt without inventing unavailable metrics.
Bumps the github-actions group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

Updates `github/codeql-action/autobuild` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

Updates `github/codeql-action/analyze` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@cdf488f...b96794f)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
# Conflicts:
#	plugins/aidd-refine/.claude-plugin/plugin.json
feat(aidd-refine): add conversation improvement workflow
…cannot (#871)

Two claims in the plugin README were false, and both read as a capability.

`00-init` never runs `check`: its actions are check the CLI, enable, then prove
a session is journalled by reading a run file back. The table said "then check".

OpenCode gives no step: its profile declares `stepStart: null`, and its plugin
forwards task paths only, so no skill invocation ever reaches the journal. The
coverage table gave it a step, and a limit a reader has to look up reads as a
zero, so it is named with the others.


Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
AIDD-Session-Id: 2c21d903-3a7e-47ac-83f8-d8b7ae3aa579

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): preserve shared plugin state across project clean

Project cleanup could delete machine-owned plugin files or native refs that another project still uses. Record canonical ownership and refuse uncertain machine cleanup.

Refs #829

AIDD-Session-Id: 01a080fc-b4fe-78d3-9317-8d5ea071ca46

* test(telemetry): isolate claude session fixture from codex env

Codex task environments expose CODEX_THREAD_ID ahead of the simulated Claude anchor. Clear it only in the two Claude-anchored checks so the test reads the intended session.

AIDD-Session-Id: 01a080fc-b4fe-78d3-9317-8d5ea071ca46

* fix(cli): refuse unproven shared plugin mutations

Project install and clean must not take over pre-existing user-scope plugins, native catalogue sources, or foreign host refs. Preserve project claims until local cleanup succeeds, and require current source/ref proof before host mutation.

This is a partial draft candidate: the framework mutation floor remains pending.

Refs #829

AIDD-Session-Id: 01a080fc-b4fe-78d3-9317-8d5ea071ca46

* test(cli): harden shared-scope mutation witnesses

Prove ownership boundaries and refusal preservation through public behavior rather than weakening the mutation gate.

Framework mutation: 93.5837% against the unchanged 93% floor. Functional suite: 6700 passing tests.

Refs #829

AIDD-Session-Id: 01a080fc-b4fe-78d3-9317-8d5ea071ca46

* test(cli): align portable fixtures and native host proofs

Resolve symlink and realpath fault keys consistently on Windows. Check the correct Copilot removal contract when its binary is absent and preserve native refusal witnesses when present.

Reject malformed Codex source listings without inferring absence and retain actionable diagnostics. Mutation floors and production behavior remain unchanged.

Refs #829

AIDD-Session-Id: 01a080fc-b4fe-78d3-9317-8d5ea071ca46

* test(cli): preserve seeded identities in realpath fixtures

Resolve only symlink lookup keys, retaining virtual project and target identities. Reproduce the drive-qualified lookup and identity-preservation contracts without changing production code.

Codex mutation consolidation passes at 95.4248% with zero timeouts; 274 focused tests pass. Refs #829

AIDD-Session-Id: 01a080fc-b4fe-78d3-9317-8d5ea071ca46

* test(cli): a path assertion is written the way the platform builds it

The three Windows failures were in the assertions, not in the code they
cover. Two cache-warning expectations spelled a path POSIX while the source
builds it with `join`, so they only ever matched on a platform whose
separator is a slash — one `it.each` line, played twice. The neighbour-list
expectation compared `listAll()`, whose keys the in-memory adapter
normalizes to a single spelling by design, against a key built with `join`.

Both sides now use the same builder, so they agree on either platform by
construction rather than by luck. No production behaviour changed and no
refusal assertion was relaxed.

Refs #829

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* test(cli): the in-memory key is spelled the way the source resolves it

On Windows `resolve` prefixes the current drive, so the source wrote the
rebuilt settings to `D:/project/.claude/settings.json` while the test seeded
and read `/project/.claude/settings.json` — two different files in the
in-memory double. The rebuild was happening all along; every assertion was
looking somewhere else.

Ten failing cases in `marketplace-sync-recovery-contract` came from that one
key. Verified on the CI Windows job, the only Windows available: a throwaway
branch carried a deliberately failing case that dumped the separator, the
roots, the resolved path and every key the double held. Local simulation
cannot reach it — aliasing `node:path` to win32 breaks the test helpers
before the code under test.

Refs #829

AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* test(cli): a registered catalogue is spelled the way a host stored it

The last Windows failure was the fixture, not the code. The source compares
the directory it would register — read back through `realpath`, which
resolves — against the one the host reports, taken raw. The two agree
everywhere the root already carries a drive, which every real Windows
install does; they diverge only under a drive-less fixture root like
`/user-cache`, where `resolve` adds the current drive to one side alone.

So the host now reports what a host would hold: the resolved directory. The
rollback refusal, the legitimate update and the pre-migration migration all
keep their outcomes, because those turn on which directory is named, not on
how it is spelled.

Deliberately not touched: the comparison in `marketplace-sync-settings-use-case`.
Canonicalizing both sides there would be defensible, but it is plugin
provenance — the code #829 is about — and no evidence yet says a real
install can reach the mismatch.

Refs #829

AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* Revert "test(cli): a registered catalogue is spelled the way a host stored it"

This reverts commit 6b3d45e.

AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* fix(cli): a registered catalogue is compared by the directory, not its spelling

`realpath` answers in one spelling and a host stores another. The sync write
path compared the two with `===`, so on Windows a catalogue the host already
follows was never recognised and got registered again — the fourth scenario
of the rollback refusal, red since the suite first ran there.

The CI Windows job was the only way to see it. A throwaway branch dumped all
four scenarios at once: `realpath` returned `/user-cache/cache/built/1.0.0/…`
where the host held `\user-cache\cache\built\1.0.0\…`. Same directory, two
spellings, never equal. On posix they coincide, which is why no developer
machine ever showed it.

`samePath` folds what a case-insensitive filesystem folds, and only there: a
backslash separates on win32 and is an ordinary character in a name
everywhere else. `pathContainsOrEquals` already normalised separators for
containment; this is the equality half, and the two comparison sites in the
sync write path now use it.

An earlier attempt fixed this in the fixture instead and broke three
scenarios, because three places there carry a spelling and only two were
changed. The defect is in the comparison, not in the decor.

Refs #829

AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* test(cli): an e2e workspace hands out the path the CLI will print

The CLI resolves the roots it prints and compares; `tmpdir()` does not.
Windows returns the 8.3 short form (`C:\Users\RUNNER~1\…`) where the CLI
prints `C:\Users\runneradmin\…`, so an assertion naming the workspace
compared two spellings of one directory.

These two e2e files had never run on Windows: the integration failure fixed
in the previous commit stopped the job before them.

macOS carries the same shape through a symlink, /var to /private/var, so the
whole e2e project ran there against this change: 53 files, 302 tests, green.

Refs #829

AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* test(cli): the manifest is searched as a tree, not as its serialization

`JSON.stringify(manifest)).toContain(path)` compares an escaped rendering:
Windows backslashes come back doubled and match a raw path never. The
manifest held the right value all along — the assertion was reading the
wrong shape of it.

Two lines below, the same file already asserts structurally against the
references file. This makes the manifest assertion do the same.

Refs #829

AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…d.cwd (#892)

handleFileWritten resolved the runs directory from payload.cwd, one host's spelling read
as a rule, where handleTaskFilesObserved already reads the host's own readCwd. A host
naming its workspace any other way had every write it stated dropped, and the turn-end
walk then recorded the same file as "observed".

No declared host changes behaviour: the four naming no written path return before that
line, and Claude Code declares readCwd: (payload) => payload.cwd. The regression test
therefore carries a test-only host entry.

Refs #859

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: nightcityblade <nightcityblade@gmail.com>
The >=3.1.2 floor is open-ended, so the lockfile resolved fast-uri@4.0.0
on the production Ajv path - outside the ^3.0.1 range ajv@8 declares.
Constrain it to >=3.1.6 <4: the smallest range that keeps Ajv 8
compatibility and excludes every reported vulnerable v3 release.

Closes #463


Claude-Session: https://claude.ai/code/session_01BZF7CTRvfq3FWDqaCWhkCJ

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(aidd-vcs): recover from scoped commit hook failures

* refactor(aidd-vcs): simplify scoped hook recovery
* fix(cli): check names an anchor that belongs to another project

`hook fired` graded FAIL whenever the anchored session left no run file
here, including for a session that never ran in this project: the anchor is
inherited by any process nested inside a session, whatever directory it runs
in, so a `check` run from a shell another project's session spawned was told
the chain was broken when it was not.

The stored records already decide it. `anchorProjectElsewhere` compares what
the sink holds for the anchor against the projects this project's own run
files name, and the claim reads `unknown`, naming that project, instead of
`fail`. `fail` keeps its meaning: this project's own session ran and the hook
wrote nothing.

Two values are compared only when the same field named them — `project_id`
is a directory name, `project_remote` a URL — so an anchor no stored record
places, or places by a field this project never used, decides nothing and
falls through to the verdict it already had.

Fixes #872

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AIDD-Session-Id: 58b68db5-0bfc-485b-aefa-1fcb609ac8f7

* fix(cli): the foreign-anchor detail claims only what was read

A run file in another project is not something this command read; the stored
figures naming that project are. The wording says that, and the details suite
pins it word for word like every other claim's.

Refs #872

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AIDD-Session-Id: 58b68db5-0bfc-485b-aefa-1fcb609ac8f7

* test(telemetry): kill the mutants the foreign-anchor guard left alive

`run-mutation --changed` scored the new lines 77: the sink was asked for the
anchor in every case the tests could see, so the two guards that keep it from
being asked — an anchor this project journalled itself, and a project no run
file here names — were indistinguishable from nothing. The double now records
which vendors it was asked about, and the cases assert it was asked none.

The domain cases were comparing a record the field filter already dropped,
never `projectOfRecord` itself; they now name a project by both fields so the
record reaches it. Score 97.7, the two survivors being a type narrowing the
field filter makes unobservable.

Refs #872

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AIDD-Session-Id: 58b68db5-0bfc-485b-aefa-1fcb609ac8f7

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ule (#885)

* feat(framework): refuse an AI edit that breaks a named architecture rule

docs/ARCHITECTURE.md stated cross-plugin orthogonality and nothing verified
it, so a hardcoded sibling address landed and no one noticed.

Two rules now decide a prospective edit: a dispatch surface never names a
sibling plugin, and a skill's `## Actions` section names exactly the actions
it provides. They live in a pure module, so a test hands them the same
inputs the hook does, without either touching disk through them.

A PreToolUse hook is the enforcement, not lefthook and not CI: the decision
is to prevent the write, and PostToolUse can only report one already made.
The hook fails open on every shape it does not recognise, because it gates
every write in this repository.

The rule's own exceptions are encoded rather than assumed. An agent's
permission list, an orchestration reference and everything a plugin keeps
under assets/ stay silent, so the guard reports nothing on the tree as it
stands — 474 script tests, and the 374 governed files replayed through the
hook as writes.

Closes #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* fix(framework): the guard reads architecture, not notation

An independent review broke the first cut in seven places. The worst: the
address pattern required a leading slash or at-sign, so the bare form
`aidd-pm:04-spec` — the one a contributor types by hand — walked straight
past it. Eight such addresses were already in the tree, six of them in
reference directories the classifier never looked into.

The rule now matches an address by its own shape at any depth, and the
router rule matches a stem as a whole token: `assert` no longer counts as
named because `assert-architecture` happens to contain it. A citation is
read from the column that actually carries action names, so a table with a
keyword column is no longer refused. Writing an action file now re-checks
its own skill's router, which is the direction a contributor creates one.

The hook spliced an Edit with String.replace, which expands `$&` and `$'`
as replacement patterns where the tool writes them literally. Both branches
splice literally now.

Two exemptions were removed and one added. Router coherence no longer skips
the orchestrator — addressing and coherence are different rules. Two branches
that no test could kill got the fixtures they were missing. `00-onboard` is
exempt because its menus hand a person the command to type; that exemption
is temporary and #883 closes it.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* fix(framework): rule two enforces the direction that can be decided

A second review found the guard refused adding an action to a skill in
either order. Creating the file first was refused for not being named;
naming it first was refused for having no file behind it. The second order
is the one `aidd-context:04-skill-generate` documents, so the framework's
own generator was refused by the framework's own guard, with no way out in
the refusal text.

A citation with no file behind it cannot be told apart from a citation
written seconds before the file it names, so that direction is gone, and
with it the column heuristics and table parsing that existed only to serve
it. What remains is decidable at any moment: an action file the section
never cites.

The section names an action by citing it — a table cell, a fenced
`actions/<name>.md` path, a backticked file name — never by a word in
running prose. Containment over prose let `plan` pass because the same
section reads "the plan is the culmination"; measured over the tree it
missed 20 of 78 row deletions. Citation matching misses none that has a row.

Rule two fires on a `SKILL.md` write alone. An action file created and never
cited is caught at the next write to its router, not at its own creation —
recorded in the spec's non-goals rather than left to be discovered.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* fix(framework): a citation comes from the column a table calls Action

A third review found the refusal told a contributor to keep the section
"naming exactly the action files, no more, no fewer" — the second half of
which the guard stopped enforcing a commit ago — and never said what
counts as naming one. Someone who had written the file's name in prose had
already done what the text asked. The refusal now names the three shapes a
citation takes.

Citations were collected from every cell of every table in the section, so
a glossary satisfied rule two while routing nothing, and an action whose row
was deleted stayed covered by its name sitting in a "next step" column. Each
table now declares its own action column and only that column cites.

Resolving that column once per section rather than once per table was a
defect the mutation caught: a glossary standing before the router made the
router's own column unreadable. A table is a table.

phase-1.md still specified the direction commit 29489f7 deleted. The
sweep's reason, the fixtures it names, and its acceptance criteria now say
what the engine does. One non-goal blamed the deadlock on the wrong half of
the mechanism, and claimed an orphan action file is caught at the next
router write — there is no such guarantee, and it now says so.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* fix(framework): a table resumed after a blank line is still that table

A fourth review caught a regression the third commit's own hot path
introduced. A blank line inside a router table started a new block, that
block had no header row, so no column declared itself and every row below
the blank line cited nothing. Inserting one blank line — changing no
content — refused three actions the section visibly lists. A run of rows
with no `| --- |` under it is the same table resumed, and it keeps the
column its header declared.

Two header shapes were wrong in opposite directions. `Next action` was read
as an action column, so a deleted row could hide behind a routing hint; the
match is exact now. `Actions` was not read at all, so a router echoing its
own section heading refused every action it provides; the match takes the
plural. And a separator written `| -- |` — which `00-onboard` does, and
GitHub renders — was not recognised as a separator at all. The sweep over
the real tree is what caught that one, which is the whole reason it exists.

plan.md still claimed both directions of rule two were measured green, and
nothing recorded that one plugin source was repaired. `04-plan.md:16`
addressed a sibling skill in prose; the spec's non-goal keeps existing
violations out of scope, so the exception is now named where a reviewer
reads the contract, not left to be found in a diff.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* fix(framework): a fence holds an example, and a shared stem cites nobody

A fifth review asked for the one thing no test could supply: a recorded
refusal through a real tool call. Everything until now was verified upstream
of the wire — the tests spawn the script and hand it a payload, which proves
the script. The matcher, the `$CLAUDE_PROJECT_DIR` expansion and `node` on
`PATH` are only exercised by an actual Write, and the hook fails open at
every one of those steps, so a broken wire looks exactly like a clean tree.
`wiring-proof.md` records both halves: the refusal, verbatim, and the
permission list carrying the same address through untouched.

Two holes the review named are closed rather than disclosed. `01-plan.md`
and `04-plan.md` both reduce to `plan`, so one citation covered both and
deleting either row went unnoticed; a shared stem now cites nobody and the
numbered name still does. And a fenced block is an example: a documented
`## Actions` is no longer mistaken for the section, a `##` inside one no
longer ends it early, and an example table inside one no longer cites. The
exception is a fenced `actions/<name>.md` path, which `10-todo` uses for its
only action — the sweep over the real tree caught that the moment fences
went blank.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* fix(framework): only the path shape reads through a fence

A sixth review found the raw view carried two exceptions where the contract
named one. Reading a backticked `<name>.md` through a fence let an example
cite, so a deleted router row hid behind a fenced table — the exact defect
blanking fences was written for. `10-todo` needs the path shape and nothing
needs the other, so the exception is now one shape wide.

A fence nobody closed used to blank the rest of the file, so a `## Actions`
that is visibly there produced "has action files but no ## Actions section".
An unclosed fence is not a fence.

Two shapes stay generous and now say so in the spec instead of being found:
a separator-less run below the router is read as that router resumed, and
rule one reads addresses through fences. Both were measured against a width
check that did not discriminate them — the reviewer's own glossary has the
router's column count — so the check is gone and the limit is written down.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* test(framework): rule two decided by one property over every router shape

Six review rounds found the same class of defect one shape at a time: a
separator written with two dashes, a plural header, a blank line splitting
the table, a fenced example citing like a router, prose carrying a stem.
Each got its own test after the fact, which is how the next shape gets
missed rather than found.

One property replaces the hunt: a section citing every action it provides
is silent, and dropping any one citation yields exactly that one violation.
It runs over 432 generated shapes — three headers, three separators, four
citation forms, six kinds of surrounding noise, split and unsplit — and
over every router in the tree, removing each of the 152 citations in turn.

The shapes are enumerated, not random. A guard that fails on a seed nobody
can reproduce is worse than no guard, and the repository root carries six
dev dependencies, none of them a generator library — `fast-check` lives in
`cli/`, which this suite does not run in.

Five of the six engine mutations these rounds fixed are killed by the
property alone. The sixth, a stem shared by two action files, cannot live
in the matrix because a collision changes the expected verdict; it keeps
the unit test written for it.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* refactor(framework): the guard reads without its commentary

Six rounds of review left the rules correct and unreadable: 36% of the
engine was comment, and four functions had accumulated a branch per round
without anyone designing them together.

Names carry what the prose used to. `exemptAgentLines` is
`permissionListLines`, `findAddresses` is `addressesIn`, and the three
citation shapes are three functions instead of one paragraph and three
inline loops. `withoutFences` no longer repeats its fence detection twice,
`classifyFile` destructures the path instead of indexing it, and the
violation object is built in one place rather than three.

What stays is why, never what: the exemption that expires with #883, the
literal splice that `String.replace` would corrupt, the project root a
readdir must resolve against. The reasoning behind each rule already lives
in the plan's decisions table, and the header now points there instead of
restating it.

Comments fall from 36% to 9% in the engine and 30% to 11% in the hook, and
one function remains above the size threshold instead of six. Behaviour is
unchanged: the same 506 tests pass, and nine mutations — separator, plural
header, fences, backticked file name, resumed table, shared stem, action
column, and both exemptions — each still turn red.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* docs(framework): the decisions table holds what the code stopped saying

The refactor cut its comments on the premise that the why lives in the
plan's decisions table, then pointed there for three decisions the table
never held: a table resumed after a blank line keeping its column, a
separator row counting from two dashes, and an unterminated fence not being
a fence. A pointer that misses is worse than the comment it replaced, and it
undermines the refactor's own argument.

`citationsIn` also defended a parameter its single caller always passes.
The default is gone.

Both found by an independent review that also ran the old engine against the
new one over 4550 content cases, 60000 fuzzed paths and 26 hook payloads —
zero differences. That is stronger evidence of an unchanged behaviour than
the nine mutations the refactor commit cited.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* feat(framework): the same two rules refuse an edit at commit time, for every tool

The write-time hook only ever sees Claude Code, so a contributor on Cursor,
Codex, Copilot or opencode met no guard at all. The rules now run as a
pre-commit job, which every edit reaching a commit passes through, and
validate.yml replays it over the whole tree on each pull request.

Nothing is duplicated to get there: architecture-scan.js holds the one
filesystem layer the pure engine refuses to own, and the hook and the
command are both thin callers of it. The hook stays as the fast path,
refusing in the same turn rather than at commit time.

--root exists so the refusal itself is testable against a tree that is not
this repository. A gate whose refusal nothing exercises is a gate nobody
can trust.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…l passes (#895)

* chore(framework): the architecture rules are enforced where every tool passes

The write-time refusal was a Claude Code PreToolUse hook. It bought one
turn of latency for a mechanism no other tool can host: across every
plugin shipped here the portable hook surface is SessionStart, and a
write-time refusal has no equivalent on Codex, Cursor or Copilot.

A guard this repository can only enforce for one of the tools it supports
contradicts the premise of the repository. The pre-commit job already
covers every edit that reaches a commit, whichever tool or person made it,
and validate.yml replays it over the whole tree on each pull request, so
nothing that was enforced stops being enforced.

The hook also failed open by design, and nothing lints scripts/ or
.claude/hooks/, so it could have stopped working without anything saying so.

architecture-scan.js keeps only the three functions the command uses.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* docs(framework): the enforcement gets a line, not three paragraphs

231 words to say a pre-commit job runs. The file breakdown is readable in
the code, the reason the write-time hook went lives in its own commit and
pull request, and "never refuses a citation with no file behind it" is a
test's job to state, not a sentence's.

What stays is the rule, and the one exemption a reader applying that rule
would otherwise trip over.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* docs(framework): the mechanism moves to where mechanisms are read

ARCHITECTURE.md states the rule and the one exception to it. What runs the
rule belongs in the pre-commit gates table, which is where someone asks
what a commit checks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

* fix(framework): three reviewers found what the author did not

A critical pass over the two documents, one reader at a time, found five
defects in this branch's own edits. All five are corrected here.

- The `--no-verify` sentence was replaced by a second falsehood. Checked
  against all nine workflows this time: no workflow fires on a push to a
  feature branch, `ci.yml` and `cli-ci.yml` fire on any pull request
  whatever its base, and only `validate.yml` filters `main` and `next`,
  minus three `cli` jobs it excludes.
- Reading "Claude Code" as "an AI coding tool" made line 9 false. The
  manifest it points at is `.claude-plugin/marketplace.json`; codex emits
  `.agents/plugins/`, cursor `.cursor-plugin/`, copilot `.plugin/`. All
  three substitutions are reverted. That framing needs a rewrite, not a
  word swap.
- The exemption bullet named one of the two holes in rule one.
  `isExemptFromOrthogonality` also exempts every path under
  `plugins/aidd-orchestrator/`.
- The doc test read tables by position, so a `Runs` cell could name a
  deleted script and stay green. Tables are now found by their header
  cells and read by column name. A fifth test opens the script the table
  names.
- Its failure message named one exemption bullet after this branch split
  it into two.

Nine attacks replayed against the rewritten parser: a missing script, an
un-backticked name and a wrong event go red; a blank line inside a table,
an escaped pipe and an inserted column stay green. Four semantic
mutations each redden exactly the test that names them.

503 tests pass. Referenced paths 0 dead, doc duplication 0, architecture
rules 350 governed files clean.

Refs #250

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPREgkoNtK4PYh2YyZbztq
AIDD-Session-Id: 2261efcd-a873-4e60-9893-2217f702bde2

---------

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

Bumps the npm-dev-dependencies group with 2 updates: [js-yaml](https://github.com/nodeca/js-yaml) and [lefthook](https://github.com/evilmartians/lefthook).


Updates `js-yaml` from 5.4.1 to 5.4.2
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@5.4.1...5.4.2)

Updates `lefthook` from 2.1.12 to 2.1.14
- [Release notes](https://github.com/evilmartians/lefthook/releases)
- [Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
- [Commits](evilmartians/lefthook@v2.1.12...v2.1.14)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 5.4.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-dev-dependencies
- dependency-name: lefthook
  dependency-version: 2.1.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the github-actions group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.38.0 to 4.38.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@b96794f...1c5b675)

Updates `github/codeql-action/autobuild` from 4.38.0 to 4.38.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@b96794f...1c5b675)

Updates `github/codeql-action/analyze` from 4.38.0 to 4.38.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@b96794f...1c5b675)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [react](https://github.com/react/react/tree/HEAD/packages/react) and [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). These dependencies needed to be updated together.

Updates `react` from 19.2.7 to 19.3.0
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.3.0/packages/react)

Updates `@types/react` from 19.2.17 to 19.3.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: react
  dependency-version: 19.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: "@types/react"
  dependency-version: 19.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@inquirer/prompts](https://github.com/SBoudrias/Inquirer.js) from 8.7.0 to 8.7.2.
- [Release notes](https://github.com/SBoudrias/Inquirer.js/releases)
- [Commits](https://github.com/SBoudrias/Inquirer.js/compare/@inquirer/prompts@8.7.0...@inquirer/prompts@8.7.2)

---
updated-dependencies:
- dependency-name: "@inquirer/prompts"
  dependency-version: 8.7.2
  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>
AIDD-Session-Id: 01a0c87c-6995-74d2-9e11-dd8597116055
Fixes #618

AIDD-Session-Id: 01a0ca9a-53ab-7ec2-bdc8-d6a3b3ae164c
* feat(cli): add Kilo Code support

AIDD-Session-Id: 01a0c897-e6c0-78d0-8f7c-3e908106d4a9

* test(cli): make Kilo runtime smoke portable

AIDD-Session-Id: 01a0c897-e6c0-78d0-8f7c-3e908106d4a9

---------

Co-authored-by: Frantz Priou <waewoo@gmail.com>
Co-authored-by: Baptiste LAFOURCADE <baptiste.lafourcade@gmail.com>
* ci(release): accept only merge commits on main

A squashed promote collapses the week's commits into one hidden `ci:`
subject, so release-please reports no user-facing commits and the
entries vanish from the notes; it also collapses authorship onto whoever
merged. A rebase breaks the merge base the back-merge relies on.

The ruleset file now allows only merge commits on main. A bypass in
pull_request mode does not lift that restriction (proven in a sandbox),
so the Release PR moves from --squash --admin to --merge --admin, which
this repository already released from once (#135). The live ruleset is
left unchanged: it must be applied only after one release has merged
with --merge.

Refs #911

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 3adb1fb2-3894-4e52-afab-424803c7fcad

* feat(release): credit each release line's commit author

Generated GitHub releases never named the people behind a change.
release-please's own include-commit-authors option is a no-op in every
17.x release so far: parseConventionalCommits drops the author before
the changelog notes see it (googleapis/release-please#2761, fix pending
in #2892).

After release-please creates the cycle's releases, a CI step appends
each line's commit author as (@login), or the name when no account
resolves, resolved from the line's commit SHA. Lines already credited
are left alone, so a re-run changes nothing, and a crediting failure
never blocks the build and publish jobs. Delete the step once a fixed
release-please is pinned.

Refs #911

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 3adb1fb2-3894-4e52-afab-424803c7fcad

* docs(release): state the merge-only rule as declared, not live

The live main ruleset still allows squash and rebase until a maintainer
applies .github/rulesets/main.json, which must wait for one release to
merge with --merge. The docs now describe the rule the file declares
instead of claiming it is enforced today.

Refs #911

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 3adb1fb2-3894-4e52-afab-424803c7fcad

* fix(release): make release crediting fail loudly and never block publishing

A missing RELEASE_OUTPUTS made the script credit nothing and exit 0, and
a failing checkout before the credit step would fail the release-please
job and skip every build and publish job. The script now exits non-zero
on missing outputs, both crediting steps carry continue-on-error, and
tests pin the wiring. A second pass no longer re-appends an author name
containing parentheses, and a release is written back only when its
body changed.

Refs #911

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 3adb1fb2-3894-4e52-afab-424803c7fcad

* fix(release): merge into main with an empty commit body

The repository fills a merge commit's body with the PR title, and
release-please splits a message on each conventional line, so a PR
merged into main as a merge commit is parsed twice: the change appears
twice in the notes, the copy credited to whoever merged. No repository
setting yields an empty body with the "Merge pull request" subject.

The Release PR and promote merges now pass --body "", and hotfixes are
documented to merge with an empty description. Proven in a sandbox: a
default-body merge duplicated the line, an empty-body merge did not.

Refs #911

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 3adb1fb2-3894-4e52-afab-424803c7fcad

* fix(release): drop merge-commit duplicates when crediting releases

A hotfix merged with the default body still reaches release-please
twice. When crediting, a line whose commit is a merge commit is removed
if a non-merge line in the same release carries the same text; a lone
merge-commit line is credited to its pull request's author instead of
whoever merged it. CHANGELOG.md keeps the duplicate.

Refs #911

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 3adb1fb2-3894-4e52-afab-424803c7fcad

* refactor(release): shorten the release crediting comments

Refs #911

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 3adb1fb2-3894-4e52-afab-424803c7fcad

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(aidd-qa): scaffold acceptance QA plugin from browser QA

Moves plugins/aidd-dev/skills/11-browser-qa to
plugins/aidd-qa/skills/01-acceptance-qa (git history preserved) and
rewrites it to derive scenarios only from acceptance criteria, never
the diff or the source code. Renames its Playwright reference to
interface-browser-playwright-cli.md, adds Criterion/Expected/Actual
columns to the QA report template, and pins the architecture-rules
sweep count to 49 skills-with-actions.

Refs #908

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* feat(aidd-dev): retire browser-qa to a redirect

plugins/aidd-dev/skills/11-browser-qa now holds a single redirect
action that names the aidd-qa plugin and its install command, then
stops without loading a scope or recording evidence. Drops Browser QA
from the plugin description and README now that it moved.

Refs #908

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* chore(marketplace): register aidd-qa plugin

Adds the aidd-qa entry to marketplace.json (recommended: false, off
the curated install path like aidd-ui and aidd-telemetry), versions it
in release-please-config.json and the manifest, adds it to the
build-plugin CI matrix, and adds aidd-qa/qa as commitlint scopes.
Updates docs/ARCHITECTURE.md, docs/CATALOG.md, docs/MAINTAINERS.md,
README.md and the project memory bank (architecture, deployment,
project-brief, testing) to name the 9th plugin and its new owner of
browser acceptance QA.

Refs #908

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* docs(aidd-qa): add the plan and its validation record

Adds the phased plan behind the aidd-qa plugin (issue #908) and the
gate, host-proof, and architecture-conformance evidence collected
while implementing it.

Refs #908

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* docs(aidd-qa): correct the validation record

The previous version overstated what was observed: it claimed each
commit's hook run was gate evidence for that commit's own tree, when
every gate actually ran once against the final working tree. Records
the two intermediate trees are not independently clean, the blocked
push (pre-existing, machine-specific cli-test failure, isolated and
unrelated to this branch's changes), and deviations not yet noted.

Refs #908

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-dev): scope 06-test to developer-side validation

The skills description did not exclude acceptance QA, so it could be
picked for reviewer evidence instead of the dedicated aidd-qa plugin.
Frame it as developer-side test validation during implementation and
add that exclusion; CATALOG.md is regenerated from the new description.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-qa): restrict criteria sourcing and complete the report contract

01-load-scope admitted a plan as a criteria source outright; criteria now
come only from the issue, spec, or user story (or ones the user gives),
and a plans browser Test Scope edge case is admitted only when it maps
to one of those. The stale fewer-than-3 edge-case count check is
dropped, and the first process step now opens with a verb.

qa-report-template gains an Out of interface section so a criterion with
no browser-observable outcome is listed, never silently dropped, and the
header verdict cannot read pass without stating that list. Verdict
values are now explicit (pass | fail | blocked per scenario, plus
skipped at the header when nothing is browser-observable), and the
Duration column is restored. 03-run-scenarios is wired to fill both.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* docs: correct plugin count and the pushed validation record

architecture.mds fits-together diagram still read plugins/ dot 8 after
the aidd-qa plugin landed; the stack table already said 9. Align the
diagram with it.

The aidd-qa plugin tasks validation.md described the push as blocked on
a local node/codex PATH collision. It has since been resolved (node
binary copied into an isolated directory, prepended to PATH; a symlink
does not work because process.execPath resolves it) and cd048e6 was
pushed with the full pre-push gate passing, no --no-verify. Replace the
stale blocked account with what actually happened.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-qa): add run-verdict rule and stop load-scope early

Give run-scenarios an ordered rule for the header verdict (any fail
beats any blocked, beats no scenario ran producing skipped, else
pass), restoring the "mark the run failed" behavior review #908 found
missing after e8d3538. Move the zero-browser-observable-criteria
short circuit into load-scope itself: it now stops right after its
Filter step and reports skipped, so prerequisites and prepare-run
never run for a scope with no browser-observable criterion. Document
the exception in SKILL.md's transversal rules, since the router's
linear flow otherwise implies prerequisites always runs first. Also
merges a duplicate load-scope Test bullet review #908 flagged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-dev): describe 06-test as developer-side in its README

The 06-test SKILL.md description was narrowed to developer-side
validation with no acceptance evidence and no sibling-plugin address
in ba2a59a, but this hand-written README row still read like generic
"write and iterate on tests" coverage. Review #908 flagged the drift.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* docs: correct 06-test's catalog description and the validation record

docs/CATALOG.md still described 06-test as generic test coverage after
ba2a59a narrowed its scope; align it with the SKILL.md description
and the now-fixed aidd-dev README row.

The aidd-qa plugin's validation record had gone stale across three
commits landed after it was last corrected (ba2a59a, e8d3538,
cff70e6): its commit table stopped five commits short of HEAD and its
push section named only the first push's SHA. Rewrite both against the
current branch history, re-run the scripts suite and architecture
check on the final tree with one consistent number instead of the
earlier 503-pass/501-pass-plus-2-skip split, and describe the push
mechanism (an isolated copied-node PATH, never --no-verify) without
freezing it to a SHA the next push would make stale again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-qa): route the zero-criterion skip before prerequisites

The prior transversal rule said prerequisites never runs when
load-scope finds no browser-observable criterion, but the router
still lists 00-prerequisites ahead of 01-load-scope, so an executor
following "read only the next action's file" would check and possibly
install ffmpeg/Playwright before load-scope ever got to decide. Move
the check itself ahead of 00: the router now says to test the
criteria before invoking prerequisites at all, and to run load-scope
alone (reporting skipped) when none is browser-observable.

Also corrects 8924333's commit body, which said this short circuit
happens "after its Filter step" -- load-scope actually stops one step
later, after Locate resolves the evidence folder the skip report
needs. That commit is already pushed and is not being amended;
recorded here instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-dev): drop the addressing note from the 06-test README row

db49dea's fix copied the dispatch's own writing constraint ("no
sibling-plugin address") into the README row as if it described what
06-test does. Replace it with what a reader actually needs: 06-test is
not independent acceptance QA or reviewer evidence, mirroring the
SKILL.md description's own "Do NOT use for" clause.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* docs: correct 06-test's catalog description and the validation record

docs/CATALOG.md's 06-test row still ended with the addressing note
"no sibling-plugin address" that a6e0cda already dropped from the
README row for the same reason: it is this task's own writing
constraint, not a description of what the skill does. Replace it with
the same "not independent acceptance QA or reviewer evidence" wording.

Rewrite the validation record's "Commits" and "Push" sections, which
had gone stale across three commits (ba2a59a, e8d3538, cff70e6) and
now this repair's own nine, and describe the repair itself honestly:
what review #908 found, the routing-rule and docs-wording bugs the
first pass at fixing it introduced and a second pass corrected, and
the final gate re-run's numbers with exit codes captured directly
rather than through a wrapper that can mask them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-qa): report a fully rejected scenario set as blocked, not skipped

A run with browser-observable criteria whose every scenario got rejected
(no executable teardown, etc.) fell through to header verdict skipped,
which the template and load-scope reserve for zero browser-observable
criteria. A rejected criterion then appeared in neither Scenarios nor Out
of interface, so a candidate that was never validated read as nothing to
validate.

Rejections are now carried forward with their reason from load-scope's
Validate and prepare-run's Reset into run-scenarios' report, which adds a
Rejected section and blocks the run whenever any exists. load-scope's own
Skip step (zero criteria survive the Filter) now writes and returns
<evidence-folder>/qa.md instead of leaving the destination to the agent.
SKILL.md's router row and mermaid are corrected to say "at most one" happy
path and to show the skip exit from load-scope to the report.

Review #908.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* docs: correct the validation record's defect count and commit table, add install proof

Review #908's second follow-up pass on this record found "fourth defect"
where the load-scope-skips-prerequisites fix is really the second in its
own list, row 14's subject carrying a spurious "(final)" that never
appeared in 3c81dfb's real message, and "this repair's own three commits
(9-11, then 12-14)" undercounting a six-commit repair. All three are fixed
here; "defect four" describing the 06-test docs fix is left alone, since
that one really is the fourth defect in the list.

Also adds rows 15-16 for this round's own commits (row 16, this commit,
referred to generically since it cannot state its own SHA), a new "Second
repair re-run" gate table, and a Host proof entry for criterion 10: an
isolated-HOME sandbox install of aidd-qa for both --tool claude and
--tool codex, run to close the gap the review found between the criterion
and its recorded proof.

Review #908.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-qa): guard resets and tighten scope order after a real run

A first real run on a PWA wiped the developer's own database through the
documented test reset. Resets now require proof of test-only storage, or
one question.

- run load-scope before prerequisites; drop the special skip rule
- split partly observable criteria, quote the rest out of interface
- run-code returns per-step expected, actual, ok; a throw is tooling only
- run browser commands from a temp dir outside the application repository

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-qa): condense load-scope and align teardown with session close

- load-scope: 7 steps, 4 tests; edges only when a criterion names them,
  an unmapped plan edge is never a candidate nor a rejection
- prepare-run: prefer a test-only entry over the development one
- reference: tear down and verify baseline before closing the session;
  the no-substitution rule scopes to runner commands

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-qa): let project memory decide the entry, locate the reset guard

- prepare-run: Reuse before Preflight; drop the generic test-only
  preference, project memory decides and the reset guard asks
- load-scope: teardown is checked in prepare-run only; Show names the
  evidence folder

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* fix(aidd-qa): check storage before starting the entry

The reset guard ran only at teardown, after the entry had already started
on possibly shared storage (and run its migrations). It now runs in Reuse,
before anything starts. The router row matches load-scope's edge rule.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* refactor(aidd-qa): condense prepare-run and run-scenarios

Same behavior in fewer words: the verdict order is stated once, the
prepare-run return step folds into its output, and run-scenarios' input
names everything load-scope hands over. A blocked row reports evidence
`none`; only a failed take is kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* feat(aidd-dev): remove the retired browser-qa redirect

Browser QA now lives in the aidd-qa plugin, so aidd-dev owns no QA
surface. The catalog lists aidd-qa's renumbered actions, and the plan
records the removal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* chore(aidd-qa): start the plugin at 1.0.0

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

* chore(aidd-qa): release the plugin as 1.0.0

Pin the first release with the package's `release-as`; the manifest
stays at 0.1.0 until release-please writes 1.0.0. A `Release-As` footer
would re-version every other path the squash commit touches.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WuxKN5bm96LFhU97rsgBUW
AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@aidd-bot
aidd-bot Bot requested a review from a team as a code owner September 24, 2026 10:32
@aidd-bot
aidd-bot Bot enabled auto-merge September 24, 2026 10:32
@blafourcade
blafourcade merged commit 8e1d9a1 into main Sep 24, 2026
41 checks passed
@blafourcade
blafourcade deleted the promote/next-to-main-35987816792 branch September 24, 2026 16:22
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.

7 participants