# Changelog

All notable changes to this project will be documented in this file.
Format based on [Keep a Changelog](https://keepachangelog.com/).

## [3.2.0] - 2026-06-07

**From advisory prose to enforced floors — plus the D6 SDD anchor layer.** This release moves a class of arcforge invariants out of in-context "please don't" prose and into surfaces that actually hold: arcforge's first deterministic blocking hook, an engine-side authorization gate, and CI/release gates that fail the build. The headline is the **D6 mode-split SDD pipeline**: a per-spec decision ledger and a two-tier vision-anchor layer give the spec-driven workflow a durable record of *what was decided and why*, with a deterministic, attended-only `ratify → authorize` convergence so a refiner can never silently invent a binding requirement. Alongside the SDD work: a redrawn `arc-writing-skills` ↔ `arc-evaluating` boundary (creation vs. measurement), broken install instructions corrected on every platform, path-injection and network-exposure hardening, four oversized engine files decomposed under the 700-line limit, and new CI/release gates for version drift and benchmark freshness.

### Fixed

- **Install instructions were broken on every platform's first run.** Claude pointed at a nonexistent marketplace (`arcforge@arcforge` instead of the real `arcforge@arcforge-dev`), OpenCode's symlink sequence was wrong and missing the skills link, and Codex/Gemini links 404'd against `blob/master` when the default branch is `main` — all corrected across the README, website, and platform guides.
- **Residual install-doc drift, caught by the release audit.** The Codex/Gemini Quick-Install one-liners still fetched from the legacy `master` branch (resolving only via GitHub's default-branch redirect) and now target `main`; the OpenCode plugin symlink uses idempotent `ln -sf` so re-running the install after a `git pull` no longer errors; and `hooks/pre-compact/README.md` now references the canonical `scripts/lib/thresholds.js` instead of the retired `hooks/lib/` path.
- **The journaling command was advertised as `/diary` but no command file backed it.** Every `/diary` invocation resolved to nothing because only `commands/journal.md` exists. All 14 command-token references across 8 files (pre-compact hook prompts, `inject-context.js`, `arc-journaling`/`compacting`/`reflecting`/`observing`/`managing-sessions`, and skills-reference docs) now point to the working `/journal`, while artifact nouns (`diary.js`, `~/.arcforge/diaries/`, `diary-{sessionId}.md`, "diary entry") are deliberately preserved.
- **Skill-type taxonomy mislabeled a Discipline gate as Meta.** `arc-writing-skills` listed `arc-evaluating` as a Meta example, but `arc-evaluating` is a Discipline gate registered in `arc-using`'s routing table (it fires when shipping or modifying a skill). The Meta example is now `arc-auditing-spec`, which is genuinely never auto-invoked, so contributors classify new skills against an accurate exemplar.
- **Local review dashboards bound to all interfaces while logging `localhost`.** `research-dashboard.js` listened on `0.0.0.0` but logged a misleading `localhost` URL; it now binds explicit `127.0.0.1` by default and logs the actual host, closing accidental network exposure and the log/reality mismatch.
- **Raw `session_id` and project basename flowed unsanitized into filesystem paths.** This path-traversal vector in diary/session/draft file handling is closed by new `sanitizeSessionId`/`sanitizeProjectName` helpers in `utils.js`, which clean the inputs before use and fail safe to a stable hash rather than throwing (hooks must never crash the session).
- **Eval-tooling correctness bugs.** A scenario fixture (`daemon-candidate-generation.md`) emitted only assertion A1 as failed and kept running when a proposal was rejected, masking the real failure state — it now emits all assertions A1–A5 false and exits, with a second `evidence_ref` so the stub payload is valid. A behavioral grader bug (A4 falsely flagging any read-only `[Tool: Bash]` as a regression) was fixed to guard only the real regression. The observer-daemon watchdog test's flaky wall-clock assertion was removed, relying on the already-deterministic signal coordination for the same guarantee.

### Changed

- **The `arc-writing-skills` ↔ `arc-evaluating` boundary was redrawn around creation vs. measurement.** `arc-writing-skills` now covers only skill *creation* with neutral, reasoning-based, TDD-style baseline-first guidance — the absolutist Iron Law framing ("write skill before testing? delete it, start over") is dropped. All measurement, the structured graders, the `eval-schemas.md` reference, the `arc eval ab` loop, and the SHIP/ship-gate verdict now live in `arc-evaluating`, which also gains a no-behavioral-footprint exemption so trivial edits (typos, metadata tweaks) no longer trigger a mandatory eval re-run.
- **Four oversized engine files were decomposed under the 700-line limit** via byte-identical verbatim moves behind stable public surfaces (behavior-preserving): `learning.js` 1611→744, `eval.js` 1481→635, `cli.js` 1390→665, `eval-graders.js` 1134→230 — extracting eval benchmark/scenario/trial-env/transcript/grader leaves and CLI eval/learn/obsidian handlers into new modules. `readArcforgeMarker` was also extracted out of the ~1000-line `coordinator.js` into a lightweight `scripts/lib/marker.js` (re-exported from `coordinator`, so callers are unaffected) so the hot-path PreToolUse hook can read the `.arcforge-epic` marker without loading the whole coordinator module.
- **The `mechanicalAuthorizationCheck` value-blind authorization hole was closed.** It gained a fourth `ledger` parameter and now authorizes a `D-NNN:value` trace only when the cited entry is `status:accepted`, carries a non-empty `ratified_by`, and the value exactly matches (not substring, not prose) an item in its `authorized_values`. The SDD trace classifier learned a `D-NNN:` decision branch, evaluated before the catch-all `qa` rule so decision traces like `D-014:` are no longer misclassified as `qa`.
- **`arc-refining` now mode-splits on `ARCFORGE_MODE`.** The unattended path is byte-for-byte unchanged (downgrade / leave-unbound / block); a new attended path lets the refiner draft a `status:proposed` decision and instruct the human to run `arcforge ratify` instead of inventing a concrete MUST. Ratified attended-only decisions are added as the Iron Law's third authorization source.
- **`arc-brainstorming` participates in the decision ledger.** It now appends `status:proposed` entries to `specs/<spec-id>/decisions.yml` for each significant design decision (creating the file as a YAML sequence if absent) and reads `product/vision.md` and the ledger as read-only context. It also gains an `## After This Skill` handoff section pointing to `/arc-refining`, completing the SDD pipeline-continuity chain that `refining`/`planning`/`writing-tasks` already had.
- **`arc-auditing-spec` gained advisory cross-artifact checks** (patterns 7/8/9: delta–decision links, `principle_ref` → `P-n`, ledger structure) while preserving its read-only, never-auto-invoke contract; absent ledger/vision files skip these checks without error.
- **Release notes now come from `CHANGELOG.md`.** The matching `## [X.Y.Z]` section is extracted via awk instead of squash-collapsed git-log subjects, and the release job hard-fails if no CHANGELOG section exists for the tagged version — so published GitHub releases carry the curated narrative. The `arc-releasing` skill gained a benchmark-freshness pre-release gate so future releases can't ship against a stale `evals/benchmarks/latest.json`. The duplicated `sanitizeProjectName` helper was consolidated into canonical `utils.js`, mirroring the worktree layer so session/diary paths and worktree paths derive the same safe name from the same input.
- **Generated SDD schema docs were regenerated** to add vision and decision-ledger schemas and refresh existing ones via `print-schema`. Numerous engine and hook source comments/docstrings were trimmed of stale historical references (PR/slice numbers, "Extracted from …", retired-decision notes) across `eval-*.js`, `sdd-rules.js`, `print-schema.js`, `observe`, `session-tracker`, and `learning-dashboard` — no behavioral change.

### Added

- **`arc-guard` — arcforge's first BLOCKING hook** (PreToolUse, synchronous). Every rule is self-gating and a no-op when its context marker is absent, and the hook fails open on any error so a false block can never push users to disable arcforge hooks wholesale. Bash rules (gated by the `.arcforge-epic` worktree marker) deny raw `git merge` inside an epic worktree (excluding merge-base and `--abort`/`--continue`/`--quit` conflict recovery), redirecting to the `arc-finishing-epic` flow, and deny nested arcforge loop launches — the loop rule anchors on arcforge's actual `scripts/loop.js` invocation so a user's own `game-loop.js`/`event-loop.js` is never caught. Edit/Write rules (gated by a research-config file in cwd) deny edits to the locked `research-config.md` contract and to `CANNOT modify:` paths that resolve to a real file/dir — and the research-config fence reads the file and requires `arc-researching` contract content (a `# Research Config` title or a `CANNOT modify:` scope line) before firing, so an unrelated file merely named `research-config.md` is not blocked.
- **`arc-remind` — a non-blocking, user-facing reminder hook** (PostToolUse, with per-session counters). It nudges on the PR boundary (`gh pr create`/`merge` → verify + request-review, noting whether a test ran), on `git worktree add` in an arcforge project (→ prefer `arcforge expand`), on commit/push after a `SKILL.md` edit (→ re-run the eval before shipping), on the first code edit on `main`/`master` (→ prefer a branch/worktree), and on the SDD stage gap where a `specs/<id>/spec.xml` is written while its sibling `dag.yaml` is still missing (→ `arc-planning`), keyed and fired once per spec-id.
- **`arcforge ratify <spec-id> <D-id>` — the attended-only convergence command.** It interactively transitions a proposed decision to `accepted` with a human-confirmed `authorized_values` list and a `ratified_by` marker, refusing to mint unless `ARCFORGE_MODE=attended` and no loop sentinel (`.arcforge-loop.json`) is present — a deterministic engine-side gate that does not rely on the harness.
- **An append-only per-spec decision ledger** (`specs/<spec-id>/decisions.yml`) governed by new deep-frozen `DECISION_LEDGER_RULES`, with `parseDecisionLedger`/`validateDecisionLedger` enforcing monotonic D-ids, per-entry immutability of decision/why text (HEAD-relative), and supersede-only status transitions.
- **A two-tier vision anchor layer** (`product/vision.md` north star with `P-n` principles, plus per-spec `specs/<id>/vision.md`) governed by new `VISION_RULES`, with `parseVision`/`validateVision` cross-checking that `principle_ref` fields resolve to declared `P-n` principles. `checkSpecDecisionGraph`, a pure non-git audit, links spec delta `decision=` attributes to ledger D-ids and validates ledger structure, surfaced through the new `arc-auditing-spec` patterns.
- **Two SDD defense-in-depth guard hooks.** `sdd-ledger-guard` (PreToolUse, Edit/Write) denies append-only violations and blocks any agent attempt to mint `status:accepted` or `ratified_by` via Edit/Write — only `ratify`'s fs write may set those fields, closing the forge-by-Edit hole. `sdd-ratify-guard` (PreToolUse, Bash) denies `arcforge ratify` when a loop sentinel is present, as best-effort defense (explicitly bypassable by `--dangerously-skip-permissions`; the engine gate is the real floor). `parseYamlSequence` was exported from `yaml-parser.js` to parse the root-level sequences used by decision-log and decision-ledger files.
- **`scripts/check-version-sync.js` — a version-drift gate** asserting all 9 canonical version locations match `plugin.json` (including the README badge, excluding the deliberately-stale lockfile), wired into both CI and the release workflow. CI now also runs the 5th test runner (`npm run test:observer-daemon`) plus a new `npm run check:versions` step, so the observer daemon and version drift are both gated on every push.
- **`release.yml` benchmark-freshness CI gate** (`scripts/check-benchmark-freshness.js`): a hard BLOCK (exit 1) that fails a release if eval-backed surface (`skills/`, `evals/scenarios/`, `evals/fixtures/`) changed since the previous tag but `evals/benchmarks/latest.json` was not regenerated after it; passes for first releases and doc-only releases. A complementary `ci.yml` skill-eval annotation (`scripts/check-skill-eval-annotation.js`) is a non-blocking GitHub annotation flagging PRs that change a skill's `SKILL.md` without a matching eval/test/benchmark update in the same diff.
- **A Jest coverage floor** (lines ≥ 80%, scoped to `scripts/lib`) in `jest.config.js`, plus a deterministic cross-process mutex test (`tests/node/test-locking-mutex.js`) for `locking.js` that self-forks and uses signal coordination to prove critical sections never overlap.
- **New `/recall` command file** (`commands/recall.md`) delegating to `arc-recalling`, closing a gap where the skill advertised `/recall` with no backing command. `argument-hint` frontmatter was added to 5 slash commands (`sessions`, `arc-auditing-spec`, `brainstorm`, `write-tasks`, `execute-tasks`), surfacing expected argument shapes inline in the palette. A new "Instinct & Learning Routes" table in `arc-using` disambiguates the 5 overlapping diary/instinct skills (`arc-journaling` `/journal`, `arc-reflecting` `/reflect`, `arc-recalling` `/recall`, `arc-observing`, `arc-learning`) by concrete trigger.
- **The `agents/` directory and corrected npm allowlist.** `agents/` was a shipped surface absent from the npm `files` allowlist and never reached published packages — now added. The eval scratch directory `skills/arc-evaluating-workspace` was untracked and excluded via a `!skills/*-workspace/` negation in `files` (an `.npmignore` rule would be a no-op since `files` overrides ignore-files), with a comment-only `.npmignore` documenting why.

### Removed

- **The `log-lightweight` Python telemetry hook and its entire `log_lightweight/` package** were removed after verification that nothing (no CLI command, dashboard, report, or skill) consumed its output. This drops the `python3` runtime dependency, an 8-event fan-out, the now-empty `SubagentStop`/`PermissionRequest`/`SessionEnd` hook entries, its e2e test coverage, and the `.claude/log-lightweight.config.json` gitignore entry — no replacement, as the data was unused.
- **The dead `analyze` learning pipeline** (`analyzeProjectLearning`, `appendAnalyzerCandidates`, `collectWorkflowPatterns`, scoring helpers, and draft renderers) was deleted from `learning.js` — it had zero production callers. Its ~1156-line test suite in `tests/scripts/learning.test.js` was removed alongside it, with surviving draft-rendering extracted to `learning-drafts.js`.
- **Four redundant skill-evaluation agents** were removed from `arc-writing-skills`. `skill-comparator`, `skill-analyzer`, and `description-tester` were deleted as redundant with `arc-evaluating`'s graders, and the unique `skill-grader` agent (rationalization-mining) was relocated into `arc-evaluating` together with the `eval-schemas.md` reference — so callers now find structured grading and eval schemas under the skill that owns measurement.

## [3.1.0] - 2026-06-02

**Learning Curator 3.1 pivot: LLM-curation replaces statistical pipeline.** The observer daemon's statistical analyzer is retired. In its place, the daemon now orchestrates a four-layer pipeline: Layer 3 assembles batches of sanitized observations, Layer 4 calls the Haiku LLM curator with `--json-schema` structured output to generate candidate proposals, Layer 5 ingests proposals into the persistent candidate queue, and Layer 6 (the new dashboard) provides the browser-based control plane for human review. The dashboard replaces the pre-pivot `arcforge learn analyze` command as the entry point for the learning workflow. All three authorization gates — approve, materialize, activate — remain intact; no candidate changes active session behavior without explicit human action.

### Added

- **Learning dashboard control plane** (`scripts/lib/learning-dashboard.js`, port 3334). Browser-based reviewer UI with an allowlist-based wire model (raw evidence, full payloads, and project identifiers are never served). Per-session write token guards all `POST` action requests. Standard security headers on all responses. The dashboard dispatches all seven lifecycle actions (dismiss, approve, materialize, activate, promote, evolve, deactivate) via the Layer 5 Action × Status legality matrix.
- **Layer 5 lifecycle state machine** (`scripts/lib/learning-curator/lifecycle.js`). Canonical Action × Status matrix as a pure stateless module. Exports `LIFECYCLE_STATUS`, `LIFECYCLE_ACTION`, `ACTIONS`, `isLegalAction`, `applyTransition`. The promote and evolve actions create new candidate records rather than transitioning the source — callers handle them separately.
- **Layer 7 materialization** (`scripts/lib/learning-curator/materialize.js`). Writes `.draft` artifact files for approved candidates. Inactive drafts are readable but not routed on by the skill system.
- **Layer 8 activation/deactivation** (`scripts/lib/learning-curator/activate.js`). Promotes materialized drafts to active artifact files. Deactivation path added (Slice G). Activation fails closed if drafts are missing or active files already exist.
- **`docs/guide/learning-dashboard.md`** — new user-facing operational guide covering dashboard startup, candidate statuses, available actions, CLI equivalents, storage layout, privacy invariants, security model, and common mistakes.
- **`arcforge learn dashboard [--port N]`** subcommand (`scripts/cli.js`). Starts the local dashboard server; default port 3334.
- **Layer 3 typed evidence + operation records** (`scripts/lib/learning-curator/batch-assembler.js`, `operation-record-writer.js`, `recall-record-writer.js`, `reflect-record-writer.js`). Batch assembly emits a canonical typed-evidence shape with dedupe; recall / reflect / operation flows write structured operation records.
- **Layer 4 daemon failure manifest** (`scripts/lib/learning-curator/cli.js`, `observer-daemon.sh`). When the curator call fails, the daemon writes a failure manifest distinguishing `transport_error` from `timeout` instead of failing silently.

### Changed

- **`arc-learning` SKILL.md updated** for dashboard-first flow. Quick Reference adds the dashboard command as the primary review entry point. Retired `arcforge learn analyze --project` documented explicitly as deprecated (statistical analyzer retired in v3.1). Candidate lifecycle status table added. Global promotion reframed: silent auto-promotion remains unsupported; the dashboard Promote action is the supported path and requires explicit user authorization.
- **`arc-observing` SKILL.md updated** to reflect post-pivot daemon contract. "How Observations Work" section now describes the four-layer orchestration (capture → batch assembly → LLM curation → ingestion). Direct `.md` instinct file writes by the daemon are retired — all proposals flow through the LLM curator into the candidate queue. Daemon safety section added covering the `.analyzing.lock` re-entrancy guard (30-min stale TTL), 120s watchdog, and `ARCFORGE_OBSERVE_SKIP_PATHS` / `.eval-trials/` skip filter.
- **Observer daemon** (`skills/arc-observing/scripts/observer-daemon.sh`) retires direct instinct writes. Layer 3 now calls `node $CURATOR_CLI assemble-batch --project`; Layer 4 calls `claude --model haiku --max-turns 15 --print --output-format json --json-schema`; Layer 5 calls `node $CURATOR_CLI ingest-proposal` to route the structured response into the candidate queue.
- **Layer 5 validator + ingestor hardened** (`scripts/lib/learning-curator/proposal-ingestor.js`, `schema.js`). The proposal validator rejects malformed candidates with one of 7 explicit rejection codes; ingestion fails closed on schema, evidence, or safety-flag violations.
- **Safety metadata + privacy redaction** added to the candidate path. Records carry explicit safety flags (`raw_prompt_included: false`, etc.); the shared `sanitize-observation.js` pass guarantees raw prompts and project identifiers never reach proposals or the dashboard wire. Deactivation now requires a `reviewer_ack` (`confirmed_behavior_change: true`), matching activation.

### Deprecated

- **`arcforge learn analyze`** exits with code 1 and the message: "arc learn analyze is deprecated. The statistical analyzer has been retired; candidate review now lives in the dashboard. Run: arc learn dashboard". The command remains registered in the CLI so users see a helpful migration message rather than "unknown command."

### Fixed

- **Schema/audit drifts reconciled** across the Layer 0–8 contracts (retrospective audit). Three spec drifts in `schema.js` / `hooks/observe/main.js` corrected; observation records now carry `schema_version` + a `source` collector metadata field.
- **`docs/guide/skills-reference.md` learning entries corrected.** The `arc-learning` and `arc-observing` reference entries still described the retired statistical model (Jaccard clustering, confidence-score lifecycle, `>= 0.7` auto-load) as current — they now describe the dashboard-first review flow and the four-layer daemon. A stale "auto-load >= 0.7" line in `arc-observing` SKILL.md (contradicting the removal of SessionStart auto-load) was corrected to "surface prominently."

## [3.0.1] - 2026-05-06

**Vault interface refinement + skill-authoring authority consolidation.** Patch follow-up to v3.0.0 that closes two architectural cleanups the rc.2 → 3.0.0 promotion didn't have time to land. (1) `arc-maintaining-obsidian` finishes the journey from "single-vault wiki maintainer" to true **vault interface** — the skill now resolves to a chosen vault's *paired contract* (`AGENTS.md` runtime + `SCHEMA.md` domain schema), `references/` files describe mechanism only, the vault contract wins where they overlap (Schema Authority rule, locked verbatim across all four presets — `minimal`, `llm-wiki`, `news`, `project-tracker`), and `init-vault` presets are reframed as *one-shot authoring guidance, not stamping templates*. The skill is now genuinely domain-agnostic: same three modes (ingest / query / audit) serve a Karpathy LLM-Wiki, a project tracker, a news pipeline, or a journal vault, dispatched by the vault's own contract. (2) Skill-authoring rules consolidated — `arc-writing-skills` becomes the single canonical authority and `.claude/rules/skills.md` shrinks to a 21-line contributor pointer. Frontmatter rule relaxed: previously `name` + `description` were the only allowed fields, now four optional fields (`argument-hint`, `allowed-tools`, `disable-model-invocation`, `user-invocable`) are documented with use cases, so skills can earn UX (`argument-hint` for CLI surfaces) and security (`allowed-tools` for read-only skills) at the skill layer instead of relying on the harness default.

### Changed

- **`arc-maintaining-obsidian` reshaped as generic vault interface.** SKILL.md slimmed; references reorganized so each `references/` file describes a single mechanism (`domain-contract-orientation.md`, `vault-resolution.md`, `mode-{ingest,query,audit}.md`, `bootstrap-workflow.md`, `audit-checks.md`, `page-templates.md`, `visuals-decision-tree.md`, `search-strategies.md`, `output-formats.md`, `obsidian-cli-quirks.md`, `delegation.md`, `registry-maintenance.md`). Mode Entry Gate updated: every vault-level mode runs **Domain Contract Orientation first** (read AGENTS.md + SCHEMA.md, sticky for the session), then loads the mode-specific reference. The earlier model where the skill hardcoded the six LLM-Wiki types is retired — types now live exclusively in each vault's SCHEMA.md, the skill enforces whatever's declared, and Raw Source layer is opt-in per vault via `raw_source: adopted` in AGENTS.md. The four presets' AGENTS.md files lock a verbatim Schema Authority section (6 stable rules across all presets) so the contract baseline doesn't drift between domains.
- **`arc-maintaining-obsidian` registry writes fully delegated to the CLI.** `~/.arcforge/obsidian-vaults.json` is no longer touched by the skill — every mutation goes through `${ARCFORGE_ROOT}/scripts/cli.js obsidian {register,unregister,set-default,list-vaults}` for atomic write, file locking, first-becomes-default invariant, and QMD collection lifecycle. Hand-editing was the previous failure mode; the CLI delegation eliminates it.
- **`arc-maintaining-obsidian` description broadened.** Trigger language expanded to cover casual "save this" / "file this back" / "what do I know about" plus explicit non-triggers ("Do NOT trigger for Excalidraw diagram creation — use arc-diagramming-obsidian"). The description now covers wiki / news / project tracker / journal vaults uniformly rather than reading as wiki-specific.
- **`init-vault` presets are authoring guidance, not templates.** `bootstrap-workflow.md` (the 11-step flow read at `init-vault <path> --name <name>`) explicitly instructs the skill to **read** the preset's AGENTS.md / SCHEMA.md to understand canonical shape, then **author a fresh contract** for the user's vault with their actual values — no unsubstituted `<Vault Name>` placeholders. Presets ship paired AGENTS.md + SCHEMA.md starters under `presets/{minimal,llm-wiki,news,project-tracker}/`.
- **Skill-authoring rules consolidated into `arc-writing-skills`.** `.claude/rules/skills.md` previously duplicated 7 of 10 sections with `arc-writing-skills/SKILL.md` (Naming, Frontmatter, Iron Law, Cross-Referencing, Word Count Tiers, File Layout, plus partial Skill Types). Those now live only in `arc-writing-skills`, which gains a unified "Skill Types" section spanning **two orthogonal axes** — composition (Workflow / Discipline / Meta — how triggered) and content (Technique / Pattern / Reference — what taught). The contributor-side rule file shrinks to 21 lines: pointer + test-file path convention + "Iron Law applies to skill EDITS, not just creation" reminder.
- **Frontmatter rule relaxed.** `name` + `description` remain required (1024 char combined cap); four optional fields are now permitted with documented use cases — `argument-hint` (CLI args in slash palette, pure UX), `allowed-tools` (skill-layer tool restriction, encouraged for read-only skills as defense in depth), `disable-model-invocation` (user-invocable only), `user-invocable` (slash-list visibility). Avoid `model`/`context`/`agent`/`hooks` unless concrete reason — those couple the skill to runtime concerns better managed at plugin/settings level. The previous "only two fields" rule predated `arc-writing-skills` + `skill-creator` owning description quality and effectively forbade skill-layer tool constraints, which are a security best practice. `arc-maintaining-obsidian` was the de facto exception (it carried `argument-hint` for its multi-subcommand surface); the rule now matches reality.
- **Design Anti-Patterns documented in `arc-writing-skills`.** Three eval-discovered anti-patterns — "mindset" skills (AI agents don't internalize mindsets; use bounded routing), self-contradicting invocation (never write "don't invoke me" in a skill registered in the routing table), embedded-only verification (it's defense-in-depth, not the primary mechanism) — moved into `arc-writing-skills` from `.claude/rules/skills.md`. They become discoverable to anyone editing skills, not only contributors who happen to read the rules file.
- **`arc-releasing` skill expanded to 9 canonical version locations.** v3.0.0 manually bumped `website/page/{hero,sections}.{jsx,js}` without the skill mentioning them; v3.0.1 codifies that surface so future releases catch website drift via the same grep gate. The version-bump checklist now has 9 rows (was 5) and the `npm run build:website` rebuild step is documented to keep `.jsx` / `.js` siblings consistent in a single commit.


**Stable v3 cut.** Promotes `3.0.0-rc.2` to the stable v3.0.0 release. The minimal-toolkit framing, the optional-learning subsystem with three authorization gates, the handover-not-archive default for `arc-managing-sessions`, and the multi-vault registry from rc.1 + rc.2 all carry through unchanged — see those entries for the full v3 surface area. The 13 commits between rc.2 and 3.0.0 are entirely measurement-discipline work: closing gaps the rc.2 in-the-wild use surfaced in the eval and observability paths, plus a single follow-on fix for recency-bounded report scoping.

**Eval evidence reporting hardened end-to-end.** rc.2 shipped with the v3 eval surface but two evidence-pipeline gaps stayed open: the dashboard rendered aggregate metrics only (no per-trial drilldown), and `--since` recency windows were applied inconsistently — A/B comparisons and raw dashboard rows could inherit failures from outside the requested window, which made "is this regression real or stale?" hard to answer at a glance. v3.0.0 closes both: per-trial raw metrics (`evals/benchmarks/raw/<date>.json` + `latest.json`) ship alongside aggregates with documented schema and dashboard wiring, `--since` is plumbed through every benchmark surface (summaries, A/B compared blocks, raw rows), the dashboard gains duration/token observability and a regression-coverage test, and `arc-evaluating` adds five new scenarios covering the gaps the rc.2 audit identified (preflight ceiling redesign, grader selection boundary, A/B noisy-delta interpretation, metric-regression separation, workflow-vs-skill boundary). Two further scenarios — adversarial-proxy-grader and model-grader-calibration — harden the grader audit path against negated-human-review false positives. Net effect: the eval pipeline now grades its own evidence as rigorously as it grades the agent under test, and the answer to "what does latest.json mean and what window does it cover?" is a single self-consistent surface.

### Fixed

- **`--since` recency windows applied inconsistently across eval reports.** Benchmark summaries respected `--since`, but A/B compared blocks and the raw dashboard rows did not — a current-window report could inherit failures from outside the window, undermining the whole point of recency scoping. `scripts/lib/eval.js` now threads the cutoff through every report path; `scripts/cli.js` collapses three near-duplicate output flows into a single shared one. Regression coverage in `tests/scripts/eval.test.js` (+56 lines) locks the contract. `evals/benchmarks/{2026-05-05,latest}.json`, `evals/benchmarks/raw/{2026-05-05,latest}.json`, and `evals/reports/core-skill-scenario-benchmark-2026-05-05.md` regenerated under the corrected scoping.
- **Dashboard had no per-trial drilldown.** Aggregate metrics rendered; raw metrics did not. `skills/arc-evaluating/dashboard/eval-dashboard-ui.html` (+279 lines, with 44 new test lines in `__tests__/eval-dashboard.test.js`) and `eval-dashboard.js` (+36 lines) gain duration/token observability surfaces wired to the new raw export.
- **Negated-human-review false positives in the model-grader calibration scenario.** `evals/scenarios/eval-arc-evaluating-model-grader-calibration.md` tightened to reject the negation pattern; benchmark/raw fixtures and the audit + suite reports refreshed with conservative transcript-path/reporting language.

### Added

- **Per-trial raw metrics export.** New `evals/benchmarks/raw/<date>.json` + `raw/latest.json` ship alongside aggregate benchmarks with a documented schema (`evals/benchmarks/README.md` +14 lines) and dashboard wiring. `scripts/lib/eval.js` gains the raw collection surface (+117 lines); `tests/scripts/eval.test.js` adds 110 lines of regression coverage. The arc-evaluating scenario audit and suite reports document the new surface so contributors see the shape before authoring scenarios that assume it.
- **`arc-evaluating` claim-lifecycle scenario.** `evals/scenarios/eval-arc-evaluating-claim-lifecycle-arbitration.md` (new, 67 lines) covers the previously-uncovered discovered-claims and weak-assertions promotion/retirement path. Benchmark snapshots and the audit + suite reports refresh with the new A/B result.
- **Five `arc-evaluating` benchmark-coverage scenarios.** `eval-arc-evaluating-preflight-ceiling-redesign.md`, `eval-arc-evaluating-grader-selection-boundary.md`, `eval-arc-evaluating-ab-noisy-delta-interpretation.md`, `eval-arc-evaluating-metric-regression-separation.md`, `eval-arc-evaluating-workflow-vs-skill-boundary.md` (5 × ~67 lines). Scenario audit (`evals/reports/arc-evaluating-scenario-audit-2026-05-05.md`) and a new suite report (`evals/reports/arc-evaluating-scenario-suite-report.md`) document drift, cross-scenario comparison, and remaining coverage gaps. `scripts/lib/eval.js` (+79 lines) and `tests/scripts/eval.test.js` (+48 lines) cover the supporting wiring (duration/token metrics, A/B compared fields).
- **Adversarial-proxy-grader and model-grader-calibration scenarios.** `eval-arc-evaluating-adversarial-proxy-grader.md` and `eval-arc-evaluating-model-grader-calibration.md` (new, 67 lines each) harden the grader audit path. `tests/scripts/eval.test.js` +25 lines locks the release-gate behavior.
- **Lightweight observation telemetry for the optional-learning hook.** `hooks/observe/main.js` (+83 lines) gains a sanitized telemetry path; `hooks/log_lightweight/dispatcher.py` updated to match. `hooks/__tests__/observe.test.js` adds 310 lines of coverage exercising both enabled and disabled config branches. Disabled-config keeps the existing zero-overhead semantics — the telemetry only activates when learning is explicitly enabled, consistent with the v3 optional-learning gating model.
- **Closed-loop self-improvement eval scenario.** `evals/scenarios/eval-optional-learning-closed-loop-self-improvement.md` (new, 207 lines) extends the optional-learning eval coverage with the full lifecycle path. The 16 v3 eval scenarios (managing-sessions handovers, optional-learning gates, optional-workflow activation/non-activation, sessionstart bootstrap, plugin-dir isolation) gain consistent format updates (15 lines each) so the suite presents one coherent grading surface.
- **`arcforge value evidence` audit report.** `evals/reports/arcforge-value-evidence-audit-2026-05-05.md` (new, 349 lines) and `evals/reports/README.md` (+16 lines) document the audit methodology and findings — useful baseline for contributors evaluating whether a new behavior change ships value or just churn.

### Changed

- **`scripts/cli.js` report output flow simplified** (refactor, −51/+46 lines net). Three near-duplicate output paths for benchmark summaries collapse into one shared formatter. No behavior change for shipped reports; the consolidation is what made the `--since` fix above possible without three parallel patches.
- **`arc-evaluating` SKILL.md** (+6 lines). Documents the per-trial raw metrics surface and the new release-gate scenarios so the skill's own routing reflects the v3.0.0 eval surface.
- **`hooks/log_lightweight/dispatcher.py`** (5-line tweak) aligns the lightweight dispatcher with the new observation telemetry path. Out-of-band of the learning-enabled gate, so disabled-config remains a no-op.

## [3.0.0-rc.2] - 2026-05-05

**Release candidate.** Second RC for the v3 line. Delta over rc.1: `arc-maintaining-obsidian` gains multi-vault support — the skill no longer assumes a single session vault, and the LLM-Wiki "fast adopt" use case is now real for users with more than one Obsidian vault. Cross-vault discovery UX is deferred to a post-v3.0.0 follow-up so this RC stays focused on the resolution-and-registry primitive.

**Multi-vault resolution + skill-managed registry.** rc.1 hardcoded a single vault path inside the skill, which broke the moment a user tried to maintain a Research wiki, a Work tracker, and a News pipeline as three independent LLM Wikis. rc.2 introduces a 5-step Vault Resolution cascade (`--vault=<name>` override → active Obsidian via `obsidian-cli vault` → session cache → registry default → ask) plus a skill-owned registry at `~/.arcforge/obsidian-vaults.json` that the user never hand-edits. Five new subcommands cover the registry lifecycle: `init-vault <path> --name <name>` (fast-adopt — writes a starter AGENTS.md from `references/agents-md-template.md`, writes a CLAUDE.md shim, registers, creates the QMD collection, seeds `index.md`), `register <path> --name <name> [--default]` (for already-populated vaults — does NOT auto-write AGENTS.md), `list-vaults`, `unregister <name>`, `set-default <name>`. Per-vault AGENTS.md schemas are explicitly allowed to diverge — the template is a starter, not a binding contract.

### Added

- **`arc-maintaining-obsidian` Vault Resolution + Registry Maintenance** (`skills/arc-maintaining-obsidian/SKILL.md` +66 lines). Frontmatter `argument-hint` now lists the 5 registry subcommands plus `--vault=<name>` for any mode. New "Vault Resolution" section replaces the prior "Vault Path" section with the 5-step cascade, including first-run state ("if registry is empty, suggest `init-vault` or `register` before any ingest/query/audit"). New "Registry Maintenance" section documents each subcommand's atomic side effects (registry mutation paired with QMD collection create/destroy, AGENTS.md template write, audit lint seed) and codifies the rule that the registry file is never hand-edited.
- **`references/agents-md-template.md`** (new, 185 lines). Neutral starter AGENTS.md that `init-vault` writes into a fresh vault. Mirrors the 3-layer Karpathy structure but with placeholder markers (`<YYYY-MM-DD>`, `<Vault Name>`, `<auto-set by init-vault>`, `<TODO ...>`) the user fills in. The template is explicitly invited to diverge — adding new note types (e.g., a News vault adds `style-aggregate` and `glossary`), removing unused ones, changing bilingual rules, restructuring folders.
- **`tests/skills/test_skill_arc_maintaining_obsidian.py`** (+107 lines). 12 new Iron Law tests assert: `argument-hint` includes the 5 new subcommands and `--vault` flag; SKILL.md contains "Vault Resolution" and "Registry Maintenance" sections; `references/agents-md-template.md` exists with placeholder markers and the canonical structure.

### Changed

- **`skills/arc-maintaining-obsidian/SKILL.md`** — "Vault Path" → "Vault Resolution" rename and contents replaced. The change is non-breaking for single-vault users (registry default still resolves the same vault) but adds explicit prompts for users who haven't registered any vault yet, so the skill can no longer silently fall through to ad-hoc file writes outside the registry path.

### Deferred

- **Cross-vault discovery UX** (`docs/plans/2026-05-04-arc-maintaining-obsidian-cross-vault-discovery.md`, new design doc, +124 lines). When a user asks a query that spans vaults ("compare my research notes on RAG with my client work on RAG-based search"), the current resolution picks one vault. The follow-up plan covers active-vault routing for multi-vault queries, federated search across QMD collections, and the read-mode-only relaxation of the per-vault independence model. Deferred so rc.2 ships the resolution-and-registry primitive cleanly and the discovery-UX surface gets its own focused review.

## [3.0.0-rc.1] - 2026-05-02

**Release candidate.** First RC for the v3 line. v3 is the 大道極簡 release: simple on the surface, strict underneath. Three threads land together. (1) ArcForge is reframed end-to-end as a **minimal, composable skill toolkit** — three layers (core toolkit / optional workflows / harness + eval), 33 skills classified into intent-driven groups, a bounded `arc-using` router that helps choose a path without forcing one, and a SessionStart bootstrap that injects only `ARCFORGE_ROOT` plus compact "skills are tools, not laws" guidance instead of the prior workflow-OS preamble. (2) A brand-new opt-in learning subsystem (`arc-learning` rebuilt as a candidate-lifecycle owner with file-backed queues and three independent authorization gates). (3) A session-handover redesign that demotes archive-by-default in favor of cheap, short-lived handovers. The release-flow destructive-action surface is eval-gated end-to-end so tag/push/publish actions stay on the maintainer's side of the boundary even when learning is enabled. Marketed as `-rc.1` so the new minimal surface and the optional-learning loop get one round of in-the-wild use before we cut a stable v3.0.0 — pre-existing instinct files and v2.1.0 specs are unaffected.

**Minimal composable toolkit, three layers, intent-first routing.** v2's mental model was "the workflow enforces design / TDD / review because the session injects the rules at every prompt" — heavy when the task was a one-line answer or an isolated eval. v3 reframes ArcForge as a toolkit, not a workflow OS: skills are available, but they are selected when useful, and `arc-using` is a bounded router and skill index rather than an always-on policy engine. The 33 skills are now grouped by how users actually work (Workflow, Supporting, Execution, Session · Learning, Knowledge · Wiki, Review · Eval) so the agent can pick the smallest useful path. The SessionStart `inject-skills` hook drops the prior router-injection preamble in favor of a minimal bootstrap; the eval suite hardens the new contract via `eval-sessionstart-minimal-bootstrap.md`, `eval-sessionstart-tool-minimalism.md`, `eval-arc-using-harness-isolation.md`, `eval-arc-using-read-only-nonactivation.md`, `eval-optional-workflow-simple-nonactivation.md`, `eval-optional-workflow-task-fit-activation.md`, `eval-other-skill-noninterference.md`, and `eval-plugin-dir-other-skill-isolation.md` — non-activation is now graded as rigorously as activation. The README, `docs/guide/skills-reference.md`, and the website all carry the new framing; the prior workflow-engine vocabulary is retired.

**`arc-learning` rewritten as the candidate-lifecycle owner.** v2's `arc-learning` was a thin manual-clustering helper that asked the user to run `/learn-from-session` after each session — easy to forget, so in practice nothing learned. v3 makes the loop optional-but-automatic-once-enabled: `arcforge learn enable --project` opts a project in, the existing `observe` hook starts appending sanitized observations, the lightweight analyzer runs from the same hook (with manual `arcforge learn analyze` available as backfill), and repeated patterns become **pending candidates** in `.arcforge/learning/candidates/queue.jsonl`. Three separate authorization gates stand between a candidate and any active behavior change: `approve` (allows materialization), `materialize` (writes inactive `.draft` artifacts the agent can read but not route on), and `activate` (the explicit gate that flips a draft into an active artifact). Disabled by default for both project and global scopes — fresh installs do no learning.

**`arc-managing-sessions` defaults flipped: handover, not archive.** The skill's prior default was the heavy archive-snapshot path (full enrichment, durable Markdown under `~/.arcforge/sessions/...`), which produced too many long-lived archives for sessions that only needed a 5–10 line "you are here" marker. v3 reframes the skill around four explicit handover modes (Quick / Full Context / Tail / Archive Snapshot) and an Archive Recommendation heuristic that escalates only on user request, high decision density, high operational value, long-running multi-session work, or surfaced reusable patterns. Quick Handover writes no file unless asked. Existing `save`/`resume`/`list`/`alias` archive surface preserved as the heaviest mode rather than the default.

### Added

- **Three-layer skill model documented end-to-end.** Core toolkit (routing, design, specs, planning, TDD, debugging, verification, eval) / Optional workflows (SDD, multi-epic coordination, skill authoring, looping) / Harness + eval (activation + non-activation grading). README, `docs/guide/skills-reference.md`, the website, and `arc-using` itself all carry the same framing so contributors and users see one coherent model. The website skills section shows the layer split first, then uses seven docs-aligned functional categories for lookup — counts unchanged at 33 total.
- **Bounded `arc-using` router and skill index.** v2's `arc-using` body read like a global routing law; v3 narrows it to a router-and-index that helps choose a path when routing helps and explicitly steps aside for simple answers, read-only inspection, grading, and isolated evals. Discipline skills (`arc-tdd`, `arc-debugging`, `arc-verifying`, `arc-requesting-review`, `arc-receiving-review`, `arc-evaluating`) are documented as conditional gates that fire when their condition is present, not mandatory steps for every message.
- **Minimal SessionStart bootstrap.** `hooks/inject-skills/main.sh` now injects only `ARCFORGE_ROOT` plus a one-line note that ArcForge skills are available and the agent should prefer the smallest useful workflow. The prior "always-on routing preamble" is removed — global routing pressure was generating workflow ceremony for one-line tasks. `eval-sessionstart-minimal-bootstrap.md`, `eval-sessionstart-tool-minimalism.md`, and `eval-sessionstart-grader-json-isolation.md` lock the new contract.
- **Composable-skill eval coverage** — eight new scenarios under `evals/scenarios/` exercise the v3 minimal-toolkit contract: `eval-arc-using-harness-isolation`, `eval-arc-using-read-only-nonactivation`, `eval-optional-workflow-simple-nonactivation`, `eval-optional-workflow-task-fit-activation`, `eval-other-skill-noninterference`, `eval-plugin-dir-other-skill-isolation`, `eval-sessionstart-minimal-bootstrap`, and `eval-sessionstart-tool-minimalism`. Non-activation is graded as rigorously as activation: an agent that runs a workflow on a one-line task fails the eval just as surely as one that skips a workflow when the task warrants it.
- **`arc-learning` lifecycle CLI: `arcforge learn {status,enable,disable,analyze,review,approve,reject,materialize,inspect,drafts,activate}`** — full surface for the optional-learning loop. `--project` is the default scope; global is a separate explicit enable. Every command supports `--json` for tooling. Approval, materialization, and activation are three distinct steps because each represents a different blast radius: approve commits to the analysis being correct, materialize writes inactive draft files the agent can inspect, activate is the only one that changes future routing behavior. Splitting them prevents the rationalization "I already approved it, materializing/activating is a formality."
- **Pending-candidate boundary** (`fr-ol-001`). The automatic trigger from the `observe` hook only ever **appends** candidate records — it does not approve, materialize, activate, tag, push, publish, or change runtime behavior. Eval scenario `eval-optional-learning-pending-candidate-boundary.md` exercises the contract: any agent that auto-approves on behalf of the user fails the eval. The release-flow destructive actions (tag/push/publish) sit on the far side of activation; eval scenarios `eval-optional-learning-release-flow-active-skill.md` and `eval-release-flow-destructive-action-gate.md` ensure that even an *activated* learned-behavior skill cannot execute destructive release actions without the same maintainer authorization the human path requires.
- **`arc-managing-sessions` Archive Recommendation rubric.** Codifies when to escalate from handover to archive: explicit user ask, multiple non-obvious decisions, playbook/recovery procedures, multi-session epic continuity, or reusable-pattern surfacing. New eval scenarios `eval-arc-managing-sessions-{quick-handover,resume-wait,archive-recommendation}.md` enforce the rubric — agents that default to archive on lightweight session-end requests fail the eval.
- **Eval harness coverage for the v3 surface** — 10 new scenarios in `evals/scenarios/`: three optional-learning gates (pending-candidate boundary, release-flow active-skill behavior, self-improvement candidate handling), two release-flow destructive-action gates (generic + plugin-dir-activated variant), three managing-sessions handover-mode scenarios, an `arc-evaluating` scenario-audit gate, and an `arc-verifying` stale-evidence gate. The release-eval suite (`tests/skills/test_optional_learning_release_eval.py`) hardens the destructive-action contract: 232 lines of assertions covering tag/push/publish refusal under every learning scope and activation state.
- **`scripts/lib/learning.js`** (689 lines) — single source of truth for the learning subsystem. Owns config (`learn enable/disable` writes `.arcforge/config.yml` per scope), candidate-queue I/O (append-only JSONL, deduplication on the `(scope, summary, evidence-fingerprint)` triple), the lightweight pattern analyzer (counts repeated tool sequences across sessions; emits a candidate when frequency × distinctness clears the configured threshold), materialization (writes `.draft` artifacts under `.arcforge/learning/drafts/<candidate-id>/` — readable to the agent but not loaded by the skill router), and activation (atomic move of `.draft` files into the active artifact location). `observe`-hook integration is deliberately thin: the hook calls `appendObservation()` and `maybeRunAnalyzer()` and returns immediately. Failure inside any learning call is caught and logged — coding work continues.
- **`hooks/observe/main.js` learning integration** (+57 test lines). When project learning is enabled in `.arcforge/config.yml`, the hook appends a sanitized observation record per tool call (secrets scrubbed via the existing `redact()` path) and triggers the analyzer with duplicate-suppression. Disabled-config = zero overhead. New tests in `hooks/__tests__/observe.test.js` cover both branches (enabled appends + disabled is a no-op).
- **`docs/plans/learning-subsystem-mvp.md` + `docs/plans/optional-automatic-learning.md`** — design history for the subsystem. Records the responsibility split between `arc-observing` (signal capture sensor), `arc-learning` (lifecycle owner), and `arc-reflecting` (secondary evidence source), and the rationale for three authorization gates instead of one.
- **`docs/guide/composable-skill-eval-coverage.md`** — short index of which composable skills now have dedicated eval scenarios, including the v3 additions. Lets contributors see eval-coverage gaps at a glance before shipping a new skill.
- **`tests/integration/claude-code/test-arc-learning.sh`** — end-to-end integration test that runs the full enable → observe → analyze → review → approve → materialize → activate flow against a temporary project root, asserting each stage's authorization gate fires.

### Changed

- **README + `docs/guide/skills-reference.md` rewritten for the minimal-toolkit framing.** v2 docs led with "skill-based autonomous workflow engine" and described skills as enforced by the session; v3 docs lead with "minimal, composable skill toolkit" and describe skills as tools the agent picks when they add leverage. The Quick-Start table, the skill-composition table, and the layer-by-layer index all reflect the three-layer model. The website (`website/page/hero.jsx`, `website/page/sections.jsx`) carries the same copy direction — intent-first, smallest useful workflow, simple on the surface and strict underneath.
- **`hooks/inject-skills/main.sh` reduced to minimal bootstrap.** The hook no longer injects the full `arc-using` body or any mandatory routing language. It exports `ARCFORGE_ROOT` for downstream skills and emits a compact availability/reminder block; everything else is read on demand by whichever skill the agent invokes.
- **`arc-using` SKILL.md rewritten as a bounded router + index.** Frontmatter description tightened to triggering-only language; body documents the conditional trigger table (discipline-skill gates that fire only when their condition is present) and the explicit non-routing cases (simple Q&A, read-only inspection, single-skill evals). The core rule is that workflow ceremony on a task that doesn't justify it is itself a failure mode.
- **`arc-managing-sessions` default reframed to handover-not-archive.** Frontmatter description rewritten around "ending a session and handing off to a future session" rather than "saving session state." Quick Handover (5–10 line bullets, no file by default) replaces archive snapshot as the path the skill takes when the user says "wrap up" / "let's pick this up next time" / "end session." `save`/`resume`/`list`/`alias` CLI surface preserved unchanged but reframed as the Archive Snapshot mode rather than the default. The skill's old `argument-hint` and `user-invocable: true` frontmatter removed — invocation is now entirely router-driven, and the four explicit modes (Quick / Full Context / Tail / Archive Snapshot) are documented in the body, not the argument-hint string.
- **`arc-learning` SKILL.md rewritten** for the candidate-lifecycle product surface. Old skill body documented manual instinct clustering (now a non-MVP path); v3 body documents the seven-stage pipeline (observations → analyzer → queue → review → approve → materialize → activate) with a quick-reference command table. Iron Law: learning is **disabled by default**, **automatic once enabled**, and **conservative at every behavior-change boundary** — the three gates exist to make the conservatism mechanical rather than discretionary.
- **`hooks/observe/main.js`** now branches on learning-enabled config before doing any observation work — disabled-config keeps the previous "do nothing" semantics so existing users see no change. The observe hook is the only learning-side surface that runs without explicit user action; everything else lives behind `arcforge learn` subcommands.
- **`scripts/lib/eval-blind-autotrigger.js` + `scripts/lib/eval-modality.js`** — minor adjustments to surface the new modality used by the optional-learning scenarios in autotrigger reports. No behavior change for existing scenarios; new scenarios rely on the corrected modality detection.
- **`tests/integration/claude-code/test-subagent-driven-development*`** consolidated. The standalone `test-subagent-driven-development.sh` was removed in favor of the integration variant under `test-subagent-driven-development-integration.sh`; the integration variant absorbed the standalone's assertions and gained 84 lines of additional coverage. Reduces test-file duplication and makes a single canonical entry point for SDD integration runs.

### Removed

- **`arc-managing-sessions` `user-invocable: true` + `argument-hint` frontmatter.** Router-driven invocation makes the argument-hint redundant, and `user-invocable: true` was a v2 affordance that conflicts with the v3 default of handover-not-archive (users who explicitly invoked it expected the heavy archive path). Removed cleanly — there is no v2.x → v3 migration shim because v3 is the first version in the v3 line.
- **`arc-learning` v2 manual-clustering body.** Replaced wholesale by the candidate-lifecycle pipeline. The manual `/learn-from-session` workflow is no longer documented as the main path; users who want it can still run `arcforge learn analyze --project` as a backfill/debug command, but it is not the recommended way to feed the queue.
- **Legacy standalone SDD integration runner** (`tests/integration/claude-code/test-subagent-driven-development.sh`). Folded into the integration variant; old runner removed to prevent two-script drift.

## [2.1.0] - 2026-05-01

**arc-evaluating v2 redesign.** A 47-commit, 7-epic minor release that rebuilds arcforge's measurement discipline from the inside out. v1's eval surface had six interlocking weaknesses (physical scattering, prose-only preflight, comparator with verdict authority, statistical cliff at k=3, grader limited to predefined assertions, divergent dashboard philosophies) — v2 fixes all six while preserving the canonical-source rule that keeps engine code at `scripts/lib/`. Each of the seven epics merged independently but ships together because the changes interlock: the verdict-authority strip and the INSUFFICIENT_DATA gate must land together, otherwise the analyzer agent could keep emitting shipping recommendations on data the harness flagged as insufficient. Validation methodology was `arc-writing-skills`' pressure-scenario RED/GREEN/REFACTOR — the existing `pytest` validators (`tests/skills/test_skill_arc_evaluating.py`, `tests/skills/test_eval_agents_contract.py`) stayed green throughout.

**SDD pipeline hardening — `arc-auditing-spec` (new) + spec-driven-refine v2/v3.** The second half of v2.1.0 closes the gaps between the SDD pipeline that v2.0.0 shipped and what production use actually requires. Three threads land together. (1) A new read-only `/arc-auditing-spec <spec-id>` slash-command and skill that fans out to three parallel sub-agents (cross-artifact alignment, internal consistency, state-transition integrity), aggregates findings on a structured schema, and prints a Decisions table without ever mutating the spec — invocation is always user-initiated and never auto-fires from any pipeline skill, so the audit can never silently rewrite what it just measured. (2) spec-driven-refine v2's three-axis R3 enforcement: refiner now blocks on design-internal contradictions, design ↔ Q&A conflicts, AND every produced acceptance criterion whose `<trace>` doesn't anchor in a (design phrase ∪ Q&A row) source, then routes resolution back through `arc-brainstorming iterate` via a `_pending-conflict.md` ephemeral handoff artifact — every design change still passes through the user-authorization gate the file-system layout was built to enforce, and `_pending-conflict.md` is the carved-out ephemeral exception (non-versioned, non-authoritative, deleted on successful new-design write) that keeps the rest of the Iron Law absolute. (3) spec-driven-refine v3's production path-resolution discipline: every shipped SDD skill that reads plugin-shared library content now uses `${ARCFORGE_ROOT}` / `${SKILL_ROOT}` prefixes — works in the user's project cwd, where contributor cwd-relative paths silently 404'd — enforced by a CI lint (`tests/scripts/skill-path-discipline.test.js`); the four canonical SDD schemas (`spec`, `design`, `decision-log`, `pending-conflict`) all flow through the same `print-schema.js` auto-gen pipeline with byte-equal drift gating, closing the v2 gap where two of the four schemas were hand-authored and could drift silently from their rule constants.

### Fixed

- **3 stale agent-path references in shipped surface.** `docs/guide/eval-system.md`, `skills/arc-evaluating/references/cli-and-metrics.md`, and `skills/arc-evaluating/references/grading-and-execution.md` still pointed at `agents/eval-grader.md` after the v2 folder consolidation. The implementation in `scripts/lib/eval-graders.js` was already loading from the correct `skills/arc-evaluating/agents/eval-grader.md` path — only the docs had drifted. Caught by the release-time outdated-docs audit; this is exactly the partial-migration class the audit step was designed to surface.
- **Dashboard: duration delta unit + condition toggle** (commit `e8ce209`). Duration delta was rendering in milliseconds where the surrounding chart used seconds; the condition toggle wasn't propagating the active-condition state to the chart re-render. Both fixes ride along with the v2 dashboard relocation rather than waiting for a separate dashboard-only release.
- **`arc eval lint` falsely rejecting valid `[tool_called]` behavioral assertions.** The lint parser stripped both `[ ]`/`[x]` markdown checkboxes AND `[tool_called]` behavioral markers when capturing assertion text, then `ASSERTION_ID_RE` rejected the bare remainder as missing an ID prefix. 10+ shipping scenarios in `evals/scenarios/` use `[tool_called]` assertions and would all have been rejected by `arc eval lint` despite running fine under `arc eval run`. Fix: distinguish behavioral brackets (group 1 matches `tool_\w+`) from checkboxes; preserve the bracket prefix on behavioral assertions. Caught by Codex pre-merge review.
- **`arc eval preflight` and the `eval ab` gate falsely blocking renamed scenarios.** Both `runPreflight` and `checkPreflightGate` hardcoded the lookup as `evals/scenarios/${name}.md`, while the rest of the eval CLI resolves scenarios by parsed `# Eval:` header via `findScenario`. Any scenario whose filename ≠ `# Eval:` value (e.g. after a file rename) would pass `arc eval run` but fail preflight with "scenario not found." Fix: preflight now uses the same iteration-by-parsed-name lookup, with literal-filename fallback preserved for legacy callers. Caught by Codex pre-merge review.
- **Blind comparator crash on skill names with regex metacharacters.** The sanitizer constructed `new RegExp(word, 'gi')` over each forbidden token; if `--skill-file` produced a name containing `+`, `(`, `[`, `\`, or other metachars, `arc eval ab` would crash inside `runBlindAutoTrigger` after trials had completed. Fix: escape regex metachars before constructing the RegExp. Caught by Codex pre-merge review (P1).
- **Blind comparator silently mapping unknown winner labels to B.** The mapping treated any value that wasn't `'tie'` or `'A'` as B-wins, so malformed comparator output (lowercase `'b'`, `'baseline'`, whitespace-padded `'B '`, etc.) was silently converted into a concrete baseline/treatment outcome — biasing the supplementary preference signal instead of surfacing the failure. Fix: explicit `winner === 'B'` branch; return null for any unrecognized value. Caught by Codex pre-merge review.
- **Blind comparator failures inflating tie counts.** When a blind comparison call failed, the autotrigger recorded `winner_original_label: null`. The preference-rate counter caught any non-`treatment`/`baseline` label as a tie, so failures were silently folded into the tie count and inflated the denominator. Fix: introduce an explicit `errors` field; only count actual `'tie'` labels as ties. The CLI now reports `errors: N/total (comparator failures, not folded into ties)` when nonzero. Caught by Codex pre-merge review.
- **Grading path divergence from run-JSONL path for non-alphanumeric scenario names.** `getGradingPath` derived the scenario directory via an ad-hoc `.replace(/[^a-zA-Z0-9-]/g, '-')`, while `parseEvalName` used `sanitizeFilename` (which preserves `_` and other characters). For a scenario named `my_scenario`, grading.json would land under `evals/results/my-scenario/...` while the run JSONL was at `evals/results/my_scenario/...` — splitting artifacts and breaking downstream audit/reporting. No current scenario triggers this (all use kebab-case), but the fix removes the latent divergence: `getGradingPath` now uses `sanitizeFilename` exactly like `parseEvalName`. Caught by Codex pre-merge review.
- **Preflight gate accepting unknown verdict values.** The gate only rejected `verdict === 'BLOCK'` and fell through to "cleared" for anything else. A corrupted or hand-edited preflight file with `verdict: "BLOCKED"` (typo), `"MAYBE"`, or a missing field would silently bypass the gate. Defense-in-depth fix: explicit `verdict === 'PASS'` requirement; everything else returns an error message. Caught by Codex pre-merge review.
- **Preflight failing open when baseline trials error out.** `runPreflight` computed `pass_rate` from `r.passed` only — so trials that failed with `infraError` (env setup, missing plugin dir) or `gradeError` (grader crash) silently counted as "ordinary failed trials." A scenario whose baseline trials all infra-errored produced `pass_rate=0%`, which is below the 80% ceiling, which yielded a false **PASS** verdict — letting `arc eval ab` proceed even though the scenario was never actually exercised. Fix: any errored trial in the preflight batch now produces an immediate `BLOCK` with an "errored trials, no signal" reason and `pass_rate: null`. Fail-closed semantics, matching the gate's intent. Caught by Codex pre-merge review (P1).
- **Path traversal in `/api/transcript` via sibling-directory prefix.** The dashboard's traversal guard used `resolved.startsWith(resultsDir)`, which matches not only paths inside `resultsDir` but also any sibling whose directory name *starts with* the resultsDir basename — e.g. `evals/results2/secret.txt` shares the `evals/results` string prefix and would have bypassed the check. Fix: require a real path-segment boundary (`resolved === resultsDir || resolved.startsWith(resultsDir + path.sep)`). Now `../results2/secret.txt`, `../results.bak/foo`, etc. all return 403. Caught by Codex pre-merge review (P1).
- **Preflight gate not scoped to `--model`.** Both `arc eval preflight` and `arc eval ab` accept `--model`, but the preflight cache file was keyed only by scenario content hash (`${hash}.json`). A PASS produced under model A would silently unblock A/B runs on model B even though baseline pass rate is model-dependent — bypassing the discriminability check the gate exists to enforce. Fix: cache filename now includes a sanitized model suffix (`${hash}-${modelKey}.json`), the record carries a `model` field, and `checkPreflightGate` accepts `--model` so it looks up only the matching record. Pre-fix `${hash}.json` records orphan harmlessly; users re-run preflight once. Caught by Codex pre-merge review (P1).
- **`arc eval lint` failed with "file not found" on renamed scenarios.** Same bug class as the preflight resolver fix above, but the lint dispatch in `cli.js` still hardcoded `evals/scenarios/${scenarioName}.md`. Any scenario whose filename ≠ parsed `# Eval:` value (common after a rename) would lint-fail while `arc eval run` worked fine. Fix: lint dispatch now uses the same `resolveScenarioFile` helper preflight uses (looks up by parsed `# Eval:` name with literal-filename fallback). The resolver was promoted from a private preflight helper to a shared export so future scenario-name-taking commands reuse it instead of re-implementing the broken hardcoded pattern. Caught by Codex pre-merge review.
- **Dashboard `fs.watch` fallback retried the same unsupported call forever.** On platforms where `fs.watch(..., { recursive: true })` throws (older Node on Linux without inotify aggregation), the existence-poll wrapper kept re-invoking the same recursive watch every 5 seconds — no watcher was ever established and the dashboard's SSE `trial-complete` events never fired. Fix: detect the throw, log a clear `[eval-dashboard]` warning, and switch to a polling fallback that walks `evals/results/` for new/modified `*.jsonl` files (reuses the existing `pendingFiles`/`processChanges` debouncer so append-detection semantics are unchanged). Real-time updates degrade to ~5s latency on affected platforms but remain functional. Caught by Codex pre-merge review.
- **SDD: `arc-refining` ↔ spec contract drift (3 fixes per fr-rf-015).** Skill text named the handoff artifact, deletion timing, and Phase 5.5/Phase 6 split inconsistently with `specs/spec-driven-refine/`. Realigned in `957e0fd` so the spec is once again the single source of truth and the eval scenarios pinned to spec wording stop catching false positives.
- **SDD: `<trace>` multi-value citations classified as a single citation.** Phase 6's mechanical authorization check split traces only on whitespace, so `<trace>design.md:L10-15, q-3</trace>` (one design-line citation + one Q&A row) was treated as one opaque token and never reached the per-citation classifier. Fix: split on comma so each citation is classified independently, restoring axis-3 enforcement on multi-source criteria (`1ab296a`).
- **SDD: `/arc-auditing-spec` slash-command wiring + Red Flags.** The skill body was complete before the slash-command file existed, so users invoking `/arc-auditing-spec <spec-id>` got "command not found" while contributors testing in-session saw the skill load correctly. Wired the slash command and tightened Red Flags to forbid the failure modes pre-merge review surfaced (`00c819d`).

### Changed

- **`eval-comparator` renamed to `eval-analyzer` and stripped of verdict authority.** v1's `eval-comparator` emitted SHIP / RUN_MORE_TRIALS / INVESTIGATE recommendations on top of quantitative data the harness already had confidence intervals for — re-judging deterministic numbers via LLM, contradicting arcforge's own stored feedback that the harness owns verdicts. v2 splits the role cleanly: the harness keeps verdict authority (programmatic, see `references/verdict-policy.md`); the new `eval-analyzer` does post-hoc qualitative analysis only (explain the delta, identify non-discriminative assertions, assess variance). The CLI no longer parses verdict strings from this agent.
- **arc-evaluating SKILL.md body rewritten for v2.** The v1 body was 2082 words (Meta tier), and v2 adds preflight, verdict-policy, claim-extraction, and blind-comparator concepts on top. Per `arc-writing-skills`' Discipline-skill word budget, the v2 body had to fit under the Comprehensive tier (1800 words). The rewrite extracts detail into the three new reference files (see Added) and keeps SKILL.md focused on routing, the Rationalization Table for v2-specific excuses, and the Red Flags list for v2 failure modes. The frontmatter description stays triggering-only — never a workflow summary, since that shortcut is the discovery anti-pattern `arc-writing-skills` explicitly warns against.
- **arc-evaluating registered in `arc-using` Discipline Skills routing table.** Trigger condition: "About to ship, merge, or mark complete a skill, agent, or workflow". Iron Law: "No shipping claim without an eval run that does not return INSUFFICIENT_DATA". This makes the evaluation gate actively routed through the 1% rule rather than manually remembered — closing the v1 failure mode where contributors knew they should run an eval but routinely skipped it under time pressure.
- **Eval surface co-located in `skills/arc-evaluating/`.** Agent prompt templates migrated from project-root `agents/eval-grader.md` and `agents/eval-comparator.md` into `skills/arc-evaluating/agents/`. The dashboard moved from `scripts/eval-dashboard.js` + `scripts/eval-dashboard-ui.html` into `skills/arc-evaluating/dashboard/`. The harness uses `loadAgentDef()` prompt-template loading rather than `subagent_type` registration, so this was a physical move + path update — no `.claude-plugin/plugin.json` changes required. Engine code (`scripts/lib/eval*.js`) deliberately stays in canonical-source position per `.claude/rules/architecture.md`; relocating engine code was rejected as a cosmetic encapsulation win at the cost of a documented architectural invariant.
- **Dashboard rewrite.** Single UI at the new skill-folder location. Retained the SSE+file-watcher real-time monitoring channel (which was strong in v1 for in-flight trials and which skill-creator's static viewer can't do). Adopted skill-creator's two-tab layout (Outputs / Benchmark), auto-saving per-eval feedback textboxes, arrow-key trial navigation, and condition-toggle for A/B comparisons.
- **SDD: `arc-refining` Iron Law revised — `NO INVENTION WITHOUT AUTHORIZATION. PRESERVE EVERY PRIOR DELTA. NEVER WRITE AUTHORITATIVE STATE ON BLOCK.`** v2.0.0's refiner Iron Law named "SPEC IS THE WIKI" as clause 1 — architectural metaphor, not actionable invariant. v2.1.0 rewrites clause 1 to forbid inventing concrete MUSTs from training-data inference, and narrows clause 3 from "NEVER WRITE ON BLOCK" to "NEVER WRITE AUTHORITATIVE STATE ON BLOCK" so the carved-out `_pending-conflict.md` ephemeral handoff is explicit at Iron Law level rather than buried in skill body. Under deferral, the legitimate refiner moves are exactly three: preserve design's qualitative phrasing as SHOULD/MAY, leave the axis unbound, or BLOCK with conflict file.
- **SDD: `arc-refining` Phase 4/5.5/6 — three-axis contradiction check.** v1 of the refiner checked one axis (design.md internal). v2.1.0 expands to three: axis 1 (design.md internal) and axis 2 (design ↔ user Q&A) fire pre-draft at Phase 4 via LLM judgment; axis 3 (every produced criterion's `<trace>` anchors in a design phrase ∪ Q&A row) fires post-draft as Phase 5.5 LLM judgment + Phase 6 mechanical validator over `<trace>` elements. Any axis fires → BLOCK, no `spec.xml` or `details/` written, `_pending-conflict.md` is the only artifact (`fr-rf-001`, `fr-rf-014`, `fr-rf-010`, `fr-rf-015`).
- **SDD: `arc-brainstorming` Phase 0 auto-detects `_pending-conflict.md` and enters iterate branch.** When refiner blocks, the user does not retell the conflict — Phase 0's existing scan-and-route logic now checks for the handoff file and seeds Change Intent from its body. The user picks a candidate resolution (or describes their own); brainstorming writes a new dated `docs/plans/<spec-id>/<NEW-DATE>/design.md` with the resolution baked in; the pending file is deleted on successful design write (`fr-bs-008`).
- **SDD: `arc-brainstorming` Phase 2 emits a structured decision-log.** The Q&A capture step now produces a machine-parseable `decision-log.yml` with required fields per row (`q_id`, `question`, `user_answer_verbatim`, `deferral_signal`) so refiner Phase 6's mechanical authorization check can iterate over rows by `q_id` (`fr-bs-009`). Wire format is YAML; the contract pinned in the spec is "machine-parseable + addressable by `q_id`" — format is a swappable detail.
- **SDD: shipped skills now use `${ARCFORGE_ROOT}` / `${SKILL_ROOT}` prefixes for cross-skill paths.** v2.0.0's skills referenced plugin-shared library content via cwd-relative patterns (`node -e "require('./scripts/lib/...')"` and bare prose paths like "see `scripts/lib/sdd-utils.js`") that worked in contributor dev mode (cwd = arcforge repo) but broke the moment the LLM ran them in a user's project cwd. v2.1.0 sweeps every shipped SDD skill onto the path-resolution discipline documented in `arc-writing-skills`' new Path Resolution section, and a CI lint (`tests/scripts/skill-path-discipline.test.js`) blocks the regression (`fr-cc-pl-001`, `fr-cc-pl-002`).
- **SDD: SDD pipeline schema access pattern shifted to direct read primary.** Skills now read schema markdown directly from `${ARCFORGE_ROOT}/scripts/lib/sdd-schemas/<name>.md` instead of shelling out to `print-schema.js` (which is retained for contributor-side schema introspection). Direct read is faster, has no Node-process startup, and the byte-equal drift gate keeps the markdown in lockstep with the rule constants regardless of which path the LLM took (`fr-sd-011`).
- **SDD: SDD schema rule-constant shape unified across the four schemas.** `SPEC_HEADER_RULES`, `DESIGN_DOC_RULES`, `DECISION_LOG_RULES`, and `PENDING_CONFLICT_RULES` now share one constant shape contract; `print-schema.js` renderers are siblings of one consistent helper. Reduces the chance that adding a fifth schema in the future drifts the existing four (`6f3dbe5`).

### Added

- **`INSUFFICIENT_DATA` verdict — hard gate when k < 5.** A 95% confidence interval requires enough data points for the t-distribution to produce a meaningful interval; below k = 5 the interval is too wide to distinguish a real improvement from sampling noise. v1's harness emitted SHIP from k=3 trials where no CI was ever computed (default for code-graded runs was k=3, but `eval-stats.js` only built CI95 when k≥5 — statistical rigor present in form, missing in fact). v2 makes INSUFFICIENT_DATA a hard gate for IMPROVED / REGRESSED / NO_CHANGE verdicts: shipping under INSUFFICIENT_DATA is shipping without statistical evidence, and the harness will not paper over that. Code-graded single runs are exempt (pass rate is deterministic, no interval to compute).
- **`arc eval preflight <scenario>` subcommand.** Runs 2–3 baseline pilot trials, computes pass rate, and blocks `arc eval ab` on the same scenario when baseline ≥ 0.8 (ceiling effect — no discriminative signal possible). Scenario hash (SHA-256 of raw file contents) is tracked so iteration is fast: bumping the Version field changes the hash and invalidates old preflight cache. Cache lives at `evals/preflight/<scenario_hash>.json` (gitignored). Brand-new scenarios pass automatically on first run since there's no baseline history to check. Also runs automatically as the first step of `arc eval run` and `arc eval ab`.
- **`arc eval lint <scenario>` subcommand.** Validates a scenario file against a JSON Schema covering required sections, Setup reproducibility, and Grader Config shape. Structural-only — does not run trials. Use this for early structural validation before baseline history exists, before the ceiling check is meaningful.
- **`arc eval audit` subcommand.** Aggregates `discovered_claims[]` and `weak_assertions[]` across the benchmark history and surfaces promotion candidates (claims passing in 3+ trials across 2+ scenarios with no contradictions) and retirement candidates (claims appearing in `weak_assertions` patterns or contradicted by the corpus). Promotion and retirement are deliberately **human-arbitrated** — automated promotion would create a feedback loop where the skill trains the eval which then expands the skill, drifting the canonical knowledge base without judgment. The audit output is a review document, not an action.
- **Grader extensions: `discovered_claims[]` and `weak_assertions[]`.** The model grader now extracts implicit claims from outputs ("the file has 12 fields", "query was parameterized") and verifies them independently, and critiques the predefined assertions it just evaluated. Both fields land in `grading.json` per trial. Neither affects the pass rate or the verdict — they feed `arc eval audit` for downstream human arbitration. This closes a v1 blind spot: skill-creator's grader did this and arcforge's didn't.
- **Per-trial metrics: `duration_ms`, `input_tokens`, `output_tokens`.** Recorded for every trial and persisted to the trial result file. The benchmark-report output exposes these as delta columns alongside the pass-rate delta in `arc eval compare` and `arc eval report`. Lets contributors see "this skill made the agent 2x slower for the same pass rate" or "tokens dropped 30% with no behavioral regression" — signals invisible in v1.
- **`eval-blind-comparator` agent.** Receives two anonymized outputs (Output A / Output B) and the original task prompt, derives a 3–5 criterion task-based rubric, scores each output independently on a 0.0–1.0 scale, and returns a winner ("A", "B", or "tie"). Critical constraint: the agent never knows which output came from which experimental condition, and must not use the words "baseline", "treatment", "with_skill", "without_skill", or any specific skill name in its reasoning. Auto-invoked after model-graded A/B runs as a supplementary signal alongside the main comparator; benchmark report shows assertion delta and blind preference rate side-by-side. Divergence between the two signals is itself a signal — investigate before shipping.
- **Three new reference files in `skills/arc-evaluating/references/`.** `preflight.md` (ceiling threshold 0.8, scenario hash mechanics, PASS/BLOCK semantics, exemption from INSUFFICIENT_DATA), `verdict-policy.md` (full verdict enum, asymmetric delta thresholds, INSUFFICIENT_DATA semantics, verdict-authority rules), `audit-workflow.md` (promotion/retirement arbitration steps, why human-arbitrated, relationship to grader output). Plus `grading-and-execution.md` extended with the `discovered_claims[]` and `weak_assertions[]` schemas, and `common-mistakes-catalog.md` for the full 23-entry mistake catalog (top mistakes stay inline in SKILL.md). This split was driven by the Discipline-skill word budget — keeping SKILL.md under 1800 words while making the full v2 surface knowable on demand.
- **Auto-trigger blind comparator for all-model A/B runs** (`fr-gr-005`). When `arc eval ab` runs with a model-graded scenario, the blind comparator runs automatically after the main comparison. No CLI flag required.
- **Dashboard: two-tab layout, auto-saving feedback, keyboard navigation, retained SSE** (`fr-dash-001` through `fr-dash-004`). Two tabs (Outputs / Benchmark) with URL-fragment state preservation, per-eval feedback textboxes that save on edit (no Save button), arrow-key navigation between trials, and the SSE real-time monitoring channel preserved through the refactor.
- **Obsidian vault knowledge: 1 Decision note + 5 wiki Source notes.** `Decision-ArcEvaluating-V2-Redesign` captures the rationale for the single-coherent-release approach (Option B) and why the canonical-source rule kept engine code at `scripts/lib/`. New Source notes for the three reference files and the two new agents (`eval-analyzer`, `eval-blind-comparator`). `MOC-ArcForge-Eval` refreshed with the v2 surface and a topic-map Mermaid diagram showing pre-trial gates / post-trial gates / audit flow. The renamed agent's Source note is preserved with a "renamed in v2.1.0" banner pointing readers to `eval-analyzer` — historical link graph stays intact rather than silently breaking backlinks.
- **SDD: `/arc-auditing-spec <spec-id> [--save]` slash-command + `arc-auditing-spec` skill + 3 read-only sub-agents.** New diagnostic skill that fans out to `arc-auditing-spec-cross-artifact-alignment`, `arc-auditing-spec-internal-consistency`, and `arc-auditing-spec-state-transition-integrity` in parallel via the Task tool, aggregates findings on the structured schema in `references/finding-schema.md`, and prints a Phase-2 markdown report (Summary / Overview / Detail tables) plus a Phase-5 Decisions table. Iron Law: READ-ONLY ADVISORY, NEVER MUTATE — the skill body and all three sub-agents are forbidden from Edit/Write/rename/delete or any mutating git/filesystem operation. Invocation is always user-initiated; pipeline skills (arc-brainstorming, arc-refining, arc-planning) MUST NOT auto-invoke (`fr-sc-001-ac3`). `--save` writes the report under `~/.arcforge/reviews/<project-hash>/<spec-id>/<YYYY-MM-DD-HHMM>.md`; without it, no file is written anywhere.
- **SDD: `_pending-conflict.md` ephemeral handoff artifact** (refiner block → brainstorming iterate). Carries which axis fired, the conflict description (specific design line ranges and Q&A row `q_id`s involved), 1–3 candidate resolutions, and a user-action prompt directing the user to `/arc-brainstorming iterate <spec-id>`. Non-versioned, non-authoritative, deleted by brainstorming on successful new-design write. Rules constant: `PENDING_CONFLICT_RULES` in `scripts/lib/sdd-utils.js`; renderer + drift gate in the auto-gen pipeline.
- **SDD: `DECISION_LOG_RULES` + `PENDING_CONFLICT_RULES` schema constants** (`fr-sd-012`, `fr-sd-013`). The two SDD artifacts that were hand-authored markdown in v2.0.0 now have rule-constant SoT in `scripts/lib/sdd-utils.js` and ship through the same `print-schema.js` auto-gen pipeline as `spec` and `design`. The drift gate (`tests/scripts/sdd-schemas-fresh.test.js`) covers all four schemas — markdown is byte-equal to rendered output or the test fails.
- **SDD: parsers + mechanical authorization check.** `parsePendingConflict`, `parseDecisionLog`, and the Phase 6 mechanical authorization helper land in `scripts/lib/sdd-utils.js`, exercised by `tests/scripts/sdd-{contracts,rules-invariants,utils,writer}.test.js` (`fr-sd-014`).
- **SDD: `print-schema.js` extended to render `decision-log` and `pending-conflict`** (`fr-sd-015`, `fr-sd-016`). `node scripts/lib/print-schema.js decision-log` and `... pending-conflict` print the canonical schema. Closes the v2.0.0 incompleteness where two of the four schemas existed only as hand-written markdown.
- **SDD: `arc-writing-skills` Path Resolution section.** Documents the `${ARCFORGE_ROOT}` / `${SKILL_ROOT}` prefix discipline, anti-pattern examples (cwd-relative `require('./scripts/lib/...')`, bare prose paths), and how the CI lint enforces it. Skill authors consulting their own meta-skill see the discipline before they ship.
- **SDD: new eval scenarios** for the SDD pipeline contract: `evals/scenarios/sdd-brainstorming-pending-conflict-handoff.md`, `sdd-refining-deferral-invention-guard.md`, and `sdd-refining-r3-pending-conflict-producer.md`. Tighten the contract — any drift on the conflict-handoff or deferral-invention-guard surfaces produces a failing eval before users see it.

## [2.0.0] - 2026-04-19

**Breaking release.** Spec-Driven Development (SDD) pipeline v2 redesign. No backward-compatibility shim and no in-tree migration tooling — v2 is a clean break for new specs. The new surface is intentionally stricter: the three upstream skills (brainstorming, refining, planning) now carry Iron Laws enforced by a deterministic validator in `scripts/lib/sdd-utils.js`, so malformed specs and drifting designs fail closed instead of silently producing inconsistent pipeline state.

### Changed

- **Per-spec directory layout (BREAKING).** `dag.yaml` moves from the repo root to `specs/<spec-id>/dag.yaml`. Each spec now owns its own `spec.xml`, `details/`, `dag.yaml`, and `epics/`. Worktree `.arcforge-epic` markers gained a `spec_id` field so the coordinator can update the correct per-spec DAG on sync. This is the minimum viable concurrency primitive — a security teardown sprint has no reason to share a DAG with an unrelated feature sprint. No migration tooling ships with v2 — the expectation is that v2 is adopted for new specs, not retrofitted onto live v1 layouts.
- **`scripts/lib/coordinator.js` became per-spec with lazy `dagPath` resolution.** No constructor eagerly computes a DAG path; each command resolves its own spec context. Multi-spec UX: `status` always aggregates across specs; `next` / `parallel` / `expand` / `loop` require `--spec-id <id>` when multiple specs exist (auto-detect otherwise); `merge` / `cleanup` accept positional epic ids; `sync` / `block` / `reboot` derive context from `.arcforge-epic` when run inside a worktree. Single-spec projects keep zero-flag ergonomics.
- **`arc-brainstorming` Iron Law: "NO DESIGN WITHOUT EXPLORATION FIRST".** New Phase 0 Scan and Route — the skill lists existing `specs/<spec-id>/` directories, asks the user to explicitly confirm "new topic" vs. "iterating on spec-id X", and writes one kind of design doc with conditional sections driven by filesystem state. Design doc output path changes to `docs/plans/<spec-id>/<YYYY-MM-DD>/design.md`. New rationalization explicitly forbidden: *"I'll pre-author the delta to save the refiner work"* — refiner is the delta authority, period.
- **`arc-refining` Iron Law: "SPEC IS THE WIKI — PRESERVE EVERY PRIOR DELTA. NEVER WRITE ON BLOCK".** Refiner is now the DAG completion gate keeper (blocks when any epic in `specs/<spec-id>/dag.yaml` is not `completed`), the delta authority (appends new `<delta>` to `<overview>` without overwriting prior deltas — wiki-style accumulation), and uses a Two-Pass Write pattern (build in memory → validate → atomic write only if zero ERRORs). Block behavior is terminal-only with zero filesystem state: no `refiner-report.md` artifact, no `--force` escape hatch, no partial writes. Clean retry semantics — fix the design doc, re-run.
- **`arc-planning` Iron Law: "PLANNER IS A PURE FUNCTION. DAG IS DISPOSABLE".** Signature: `(spec + latest_delta) → (specs/<spec-id>/dag.yaml + specs/<spec-id>/epics/)`. Planner MUST NOT read the design doc (three-layer separation enforced), MUST NOT write any gate (the gate lives in refiner), and overwrites `dag.yaml` every sprint with no archive sibling file. Previous epic statuses do NOT carry over — every epic starts `pending`. Git history is the only retroactive trace.
- **No "modes" in the pipeline.** Removed Path A / Path B / gamma mode / γ mode / initial / iteration / replace terminology from the three upstream skills. The refiner has one behavior that fills in version-dependent fields based on filesystem state; the design doc has one structure with conditional Context + Change Intent sections (when iterating) vs. prose (when new). These are not two modes — they are one behavior with context-sensitive content.
- **9 CLI commands updated for multi-spec UX.** `status`, `next`, `parallel`, `expand`, `merge`, `cleanup`, `sync`, `block`, `loop` all now honor per-spec semantics with the flag/auto-detect rules above.
- **Downstream skills gained per-spec path awareness.** `arc-coordinating`, `arc-using-worktrees`, `arc-finishing-epic`, and `arc-writing-tasks` read/write paths from `specs/<spec-id>/` and include single-spec dispatch gates where aggregation isn't possible.
- **`docs/guide/skills-reference.md` refreshed** to reflect new paths and the SDD v2 workflow (Two-Pass Write, DAG completion gate, latest-delta scope extraction, four delta operations). Removed stale `REFINER_INPUT section` references that no longer exist in the schema.

### Added

- **`scripts/lib/sdd-utils.js`** — deterministic validation layer and single source of truth for spec/design-doc schema. Exports: `DESIGN_DOC_RULES` + `parseDesignDoc` + `validateDesignDoc` for design docs; `SPEC_HEADER_RULES` + `parseSpecHeader` + `validateSpecHeader` for `spec.xml` identity header and `<overview>` structure; `checkDagStatus(dagPath)` for the refiner's gate. Rules live as constants in the module — skills reference them via `print-schema.js`, never embed. When skill text and validator disagree, the validator wins. This separates authoring (non-deterministic LLM) from validation (deterministic code), which was the prerequisite for trust.
- **`scripts/lib/print-schema.js`** — CLI that prints the canonical schema from `sdd-utils.js`'s rule constants. Usage: `node scripts/lib/print-schema.js design` or `... spec`. Stable user-facing contract for schema introspection; skills call this at REQUIRED BACKGROUND time to load the current schema verbatim.
- **Four-operation delta schema.** Added `<renamed ref_old="X" ref_new="Y">` as a first-class delta child type alongside `<added>`, `<modified>`, `<removed>`. Semantics: **body-unchanged only** — mechanical ref rename (grep + replace). `<renamed>` requires `ref_old` and `ref_new`; optional `<reason>`. Semantic changes must use `<removed>` + `<added>`, never `<renamed>` + `<modified>` (the validator enforces this). Emerged from the SDD v2 refactor itself needing mechanical renames that didn't fit the three-operation model.
- **Reason required in `<removed>` delta entries.** `<removed>` now MUST include a `<reason>` child; `<migration>` is optional. Both are read by the implementer LLM as teardown guidance — not archive commentary for humans. Write them with that reader in mind.
- **RFC 2119 language requirement + BDD criterion convention.** Every acceptance criterion MUST use MUST / SHOULD / MAY / MUST NOT, structured as Given/When/Then. No "should probably", "tries to", "usually" — validator produces warnings that block the Quality Checklist. Industry-standard way to write testable requirements; closes the LLM's tendency to produce soft, unverifiable language.
- **`specs/spec-driven-refine/`** — arcforge's own SDD v2 spec, formalized and iterated on-branch. This is the spec that drove the v2 redesign; it serves as both the historical record of the decisions and the canonical example of the new schema.
- **SDD v2 integration tests and eval scenarios.** `tests/integration/sdd-v2-pipeline/` exercises the full design → refine → plan → implement pipeline end-to-end with fixture regeneration. `evals/scenarios/arc-{brainstorming,refining,planning}-*` tighten the contract — any skill drift produces a failing eval before users see it. `tests/skills/` migrated to per-spec fixture layout with added multi-spec coverage.
- **Obsidian vault Decision notes** documenting the v2 shifts: `arcforge-decision-per-spec-layout`, `arcforge-decision-sdd-v2-skills-contracts`, `arcforge-decision-multi-spec-coordinator`, `arcforge-decision-spec-schema-formalization`. These complement the existing `arcforge-decision-sdd-v2-pipeline-realignment` (D1-D8 design decisions) with ship-level decisions (breaking changes, new CLI surface).

### Removed

- **Three-operation delta model.** Superseded by the four-operation schema (see Added). Pre-v2 specs continue to parse — the validator treats missing `<renamed>` entries as a non-error (empty array).
- **`REFINER_INPUT` section requirement** in design docs. Replaced by the unified schema: prose for new-spec formalization, Context + Change Intent for iteration. No separate required section; `parseDesignDoc` validates against `DESIGN_DOC_RULES`.
- **Mode vocabulary in pipeline skills.** Path A / Path B / gamma / γ / initial / iteration / replace terminology removed from `arc-brainstorming`, `arc-refining`, and `arc-planning`. Filesystem state is the source of truth for which fields apply.
- **`refiner-report.md` artifact.** Never written — refiner block behavior is now terminal-only (print errors + exit non-zero + zero filesystem state). Clean retry semantics; no stale state across invocations.
- **`arcforge backfill-markers` CLI command** (removed before the 2.0.0 release shipped). A pre-release iteration shipped this as a v1→v2 migration shim; it was pulled after confirming zero legacy users and that the shim itself had consistency holes (root `dag.yaml` fallback missing, legacy bare-epic branches not renamed). Clean-break contract stands on its own — no partial migration path.

## [1.4.1] - 2026-04-15

### Fixed

- **Diary enricher silently failing for ~30 days**: the Stop-hook background enricher (`spawnDiaryEnricher`) could not write to `~/.claude/sessions/...` because Claude Code v2.1.78+ added protection on nested Write tool calls inside `~/.claude/`. The error was invisible because the subprocess's stderr was piped to `'ignore'` and `--max-turns 2` exhausted the agent's budget before it could emit a Write. Result: 91 of 109 diary drafts sat as unenriched stubs. Fix: raise `--max-turns`, stop swallowing stderr, and move diary state to `~/.arcforge/` (see Changed)
- **Observer daemon split-brain after initial state move**: `observer-daemon.sh` still pointed at `~/.claude/instincts/` and `~/.claude/observations/` while the JS side had moved to `~/.arcforge/`. Daemon wrote lock/log to one tree, the hook read from another; SIGUSR1 coordination was broken and the daemon could not see new observations. Fix: rename `CLAUDE_DIR` → `ARCFORGE_DIR` in the bash daemon and repoint to `~/.arcforge/`
- **Leaky observer paths in `hooks/observe/main.js`**: the hook was reaching into `getInstinctsRoot()` to rebuild `.last_signal` and `.observer.lock/pid` paths inline — magic strings in hook code waiting to drift. Fix: add `getObserverSignalFile()` and `getObserverPidFile()` to `session-utils.js`; hook now uses the helpers, structural assumption lives in one place

### Changed

- **Consolidated all arcforge-owned state under `~/.arcforge/`**: sessions, diaries, instincts, diaryed, observations, and evolved directories moved from `~/.claude/` in a 3-commit sequence. Introduced `getArcforgeHome()` returning `~/.arcforge/`; deleted the `CLAUDE_DIR` constant entirely. After this, `~/.claude/` no longer holds any arcforge-owned state — Claude Code owns `~/.claude/`, arcforge owns `~/.arcforge/`, with no overlap. Existing user data migrated in place via `mv` (verified: 8 instincts still load from the new location post-migration)
- Path helpers in `scripts/lib/session-utils.js` now compose on `getArcforgeHome()`: `getProcessedLogPath`, `getObservationsPath`, `getInstinctsDir`, `getInstinctsArchivedDir`, `getGlobalInstinctsDir`, `getInstinctsGlobalIndex`, `getEvolvedLogPath`, and new `getInstinctsRoot` for the daemon coordination dir. `getInstinctsArchivedDir` composes on `getInstinctsDir` (one less repeat of the `<root>/<project>/` structural assumption)
- `scripts/lib/utils.js` `getDiaryedDir` repoints to `~/.arcforge/diaryed/`; `getSessionsDir` and diary path helpers repoint to `~/.arcforge/sessions/` and `~/.arcforge/diaries/`
- `scripts/lib/global-index.js` inner loop uses `getInstinctsDir(projName)` instead of reconstructing `path.join(instinctsBase, projName, ...)` — matches the canonical helper pattern
- `hooks/pre-compact/README.md`, `hooks/session-tracker/README.md`, `hooks/session-tracker/end.js`, `scripts/cli.js`, `scripts/lib/coordinator.js`, `scripts/lib/pending-actions.js`, `scripts/lib/worktree-paths.js`: path references updated to `~/.arcforge/`
- Skill SKILL.md files updated to reference `~/.arcforge/` paths: `arc-journaling`, `arc-learning`, `arc-managing-sessions`, `arc-observing`, `arc-reflecting`, `arc-using`, `arc-using-worktrees`; plus `arc-dispatching-teammates` baseline/green test fixtures
- `arc-journaling/scripts/auto-diary.js` and `diary.js`: emit paths under `~/.arcforge/sessions/`
- `arc-managing-sessions/scripts/sessions.js`: session list/save/load paths repointed
- `.claude/rules/architecture.md`: Worktree Isolation / `.arcforge-epic` marker section aligned with helper-based path derivation
- `docs/guide/skills-reference.md`, `docs/guide/worktree-workflow.md`: path references updated
- Test fixtures under `tests/skills/pressure/`: `arc-finishing-epic-completion-format.md`, `arc-using-path-reconstruction.md`, `arc-using-worktrees-cli-failure.md` use the current path format
- Minor: `tests/scripts/coordinator-marker-exclude.test.js` — fixed a stale comment referencing the pre-migration `~/.arcforge-worktrees/` path (and a "slinged" typo → "linked")

### Added

- `hooks/session-tracker/inject-context.js` (65 lines): injects session context at session start — part of the diary enricher's unblock (pre-creating the target path so the enricher Write becomes Edit)
- Diary enricher test coverage: `hooks/__tests__/diary-enricher.test.js` (128 lines, unit) and `hooks/__tests__/diary-enricher-e2e.test.js` (139 lines, E2E). The E2E suite prevents a future silent-failure regression — unit tests would have missed the original bug because the enricher's "failure" was Claude Code's subprocess exiting normally with no Write performed

## [1.4.0] - 2026-04-12

### Added

- **arc-dispatching-teammates** skill: Lead-present multi-epic parallel execution via Claude Code agent teammates. Fills the gap between `arc-coordinating` (single-epic interactive) and `arc-looping` (multi-epic unattended) — the discriminator is **attendance, not risk**. Caps at 5 teammates per Anthropic best practice; continuous dispatch as slots free; each teammate runs its own `/arc-implementing` → `arc-finishing-epic`
- **Obsidian bilingual notes**: All wiki-layer notes now dual-language (EN/ZH) using `[!multi-lang-{code}]` callouts. Includes `publish.js` + `publish.css` for runtime language switching on Obsidian Publish (with `MutationObserver` for SPA navigation and CSS fallback), plus `.obsidian/snippets/multi-lang.css` for local app toggling
- **Paper variant** for arc-maintaining-obsidian Source template: academic paper extraction with `reading_status` (queued/skimmed/deep-read/extracted), `methodology`, `venue`, `year`, `cites`, `cited_by`, structured Claims section (evidence + basis + status), and citation-aware propagation that auto-resolves `cites:`/`cited_by:` cross-references on ingest
- **QMD hybrid search** integration in arc-maintaining-obsidian: prefers QMD (keyword + semantic + reranking) over `obsidian-cli search` for vault discovery; includes Index Sync step (`qmd update && qmd embed`, ~3s incremental) after each ingest or audit cycle to keep new notes searchable
- **Visuals decision framework** in arc-maintaining-obsidian ingest pipeline: 4-question decision tree (image embed → entity count → relational test → spatial complexity) with Embed/Mermaid/Canvas/Excalidraw tiers. Mermaid is the default output when content is relational; Canvas and Excalidraw require user approval
- **Index pipeline step** in arc-maintaining-obsidian: ingest now writes `Classify → Confirm → Create → Visuals → Index → Propagate → Log` — `index.md` gets incremental one-line additions per new note, keeping the catalog current between full audit rebuilds
- `scripts/lib/worktree-paths.js`: Canonical path helper (`getWorktreePath`, `parseWorktreePath`, `hashRepoPath`, `getWorktreeRoot`) computing `~/.arcforge-worktrees/<project>-<hash>-<epic>/` from the absolute project root. Replaces hardcoded `.worktrees/` paths throughout the engine
- `expand --epic <id> --project-setup` CLI mode: single-epic worktree expansion with auto-detected dependency install (npm/pnpm/yarn/bun via `detectPackageManager()`, pip via `pyproject.toml` or `requirements.txt`, cargo, go). `package-manager.js` adds `getDefaultInstallCommand()` routing to the project's actual package manager — no more hardcoded `npm install`
- `docs/guide/worktree-workflow.md`: Authoritative bilingual (EN/ZH) human guide covering path derivation rules, `.arcforge-epic` marker schema, cleanup semantics, sync flow, and troubleshooting. All skills and rules defer to this doc for the full story
- `.claude/rules/dev-context.md`: Contributor-facing rule separating dev-environment facts (project-level plugin disablement, `--plugin-dir .` workflow, Ships/No-ship audience table) from shipped surface. Introduces the audience-separation principle: contributor-only concerns never belong in skills, hooks, commands, agents, templates, engine, or user docs
- `tests/skills/pressure/`: New pressure test fixture format for discipline skills (`arc-using-path-reconstruction`, `arc-using-worktrees-cli-failure`, `arc-finishing-epic-completion-format`) plus `test_pressure_fixtures.py` runner and `README.md` documenting the format
- `tests/scripts/worktree-paths.test.js`: 150+ line Jest suite covering hashing, path derivation, parsing edge cases, and sanitization
- Skill test: `tests/skills/test_skill_arc_dispatching_teammates.py` (163 lines, frontmatter + structure validation)
- Design docs: `docs/plans/2026-04-09-obsidian-bilingual-notes-design.md`, `docs/plans/2026-04-10-arc-dispatching-teammates-design.md`
- Task list: `docs/tasks/bilingual-notes-tasks.md`
- arc-maintaining-obsidian evals: 2 new scenarios (`synthesis-with-relationships-should-mermaid`, `simple-source-should-skip-visuals`) to discriminate the Visuals decision framework
- `assets/arcforge-overview.png` (README diagram — referenced from the Skills Connect section)
- `normalizeStatus()` in `scripts/lib/dag-schema.js`: maps agent-written status aliases (`done`/`finished`/`complete` → `completed`) and rejects unknown values, providing defense-in-depth for status values that bypass the `TaskStatus` enum
- `_dagTransaction()` helper in `scripts/lib/coordinator.js`: serializes read-modify-write access to `dag.yaml` under file lock — fresh read under lock, mutation, conditional write — preventing concurrent teammate processes from clobbering each other's status updates
- `_ensureArcforgeExcluded()` in `scripts/lib/coordinator.js`: adds `.arcforge-epic` to the main repo's `.git/info/exclude` at expand time, preventing teammates' `git add -A` patterns from staging the worktree marker. Uses `git rev-parse --git-common-dir` because linked worktrees share exclude config with the main repo
- `arc-dispatching-teammates` reference docs: `references/acceptance-and-retry.md` (subagent-gated acceptance, retry loop mechanics, spec-defect override protocol), `references/spawn-prompt-template.md` (full authority grant template with retry feedback section), `references/tmux-timing-race.md` (GH #40168 parallel dispatch fallback), `references/wrap-up-sequence.md` (three-action teardown: report → cleanup → TeamDelete)
- Race condition test suite: `tests/scripts/coordinator-merge-race.test.js`, `coordinator-expand-race.test.js`, `coordinator-marker-exclude.test.js`, and shared `coordinator-test-helpers.js`
- Design doc: `docs/plans/2026-04-11-coordinator-dag-transaction-sweep.md` (deferred cold-path transaction sweep for `completeTask`, `blockTask`, `cleanupWorktrees`)
- Task list: `docs/tasks/sync-status-validation-tasks.md` (7-task TDD plan for status validation)

### Changed

- **arc-diagramming-obsidian — full rewrite as hybrid EA+Playwright render-validate pipeline with HARD/SOFT layer structure and subagent delegation.** Restructured SKILL.md into two explicit layers: **HARD** (physical/mechanical invariants the tools can verify — theme detection, view-PNG-every-iteration, save verification, EA reset / id / anchor rules) and **SOFT** (concept judgments — what to add, scale reflects real importance, isomorphism self-check as heuristic not gate, Painter's Toolkit as vocabulary not menu). Pipeline reframed from 3 phases to 4: `DESIGN → BUILD → VALIDATE → SAVE`, with DESIGN now explicit (Think First, pattern + brushes, Layout Trap Audit before any EA code). Introduces three subagents that let the lead keep context clean on complex diagrams — `diagram-builder` (executes EA code from a design spec), `diagram-validator` (runs the 3-iteration render-fix loop), `diagram-saver` (persists via `ea.create()` and runs `verify_saved_diagram.py`). Added four reference docs: `depth-enhancements.md` (Research Mandate, Multi-Zoom, Evidence Artifacts for comprehensive-technical depth), `layout-heuristics.md` (Part 1 grid planning + Part 2 fix strategies), `painters-toolkit.md` (shape variety, subtitles, zone labels, containers, accents, separators, footers, size suggestions), `save-format.md` (byte-exact `.excalidraw.md` canonical template for the manual-fallback save path). New Python helpers under `references/`: `plan_layout.py` (automatic coordinate computation for 20+ elements), `check_overlaps.py` (bounding-box overlaps, arrow-text crossings, text collisions), `render_excalidraw.py` (Playwright headless render producing diagram-only PNGs without Obsidian chrome), `verify_saved_diagram.py` (post-save format-marker check + optional re-render and size-compare to catch JSON corruption); `render_template.html` + `pyproject.toml` round out the toolkit. Preferred save path is now `ea.create()` via `obsidian eval` (uses the documented `ea.elementsDict` public property) with the manual byte-exact write as fallback. Codifies the **Layout Trap Audit** — four recurring arrow-path collisions (converging-arrow corridors, back-edge horizontal routes, decision-diamond yes/no labels, back-edge label X-coordinates) that are cheaper to prevent at design time via mental trace than to fix at rendered-pixel level. Reverts the short-lived ExcalidrawAutomate-only approach (15512c6) in favor of the hybrid pipeline. Effect: higher-quality diagrams with fewer layout traps, verifiable saved output, and a clean separation between mechanical rules the tooling enforces and concept judgments the author must exercise.
- **Worktree location migration**: moved from in-repo `.worktrees/<epic>/` to home-based `~/.arcforge-worktrees/<project>-<hash>-<epic>/`. The 6-char sha256 prefix of the absolute project path prevents collisions between multiple clones of the same repo. All skills, rules, tests, and agent output stop hardcoding worktree paths — the path is derived at runtime via `scripts/lib/worktree-paths.js` and surfaced through `arcforge status --json`
- **`arc-using` Worktree Rule** now enforces **four** norms (previously three): no hardcoded paths, no manual `git worktree add`, enter via `arcforge status --json`, and — new — **direct file-editing tools are restricted to the session owning `.arcforge-epic`**. A session "owns" the side whose cwd contains the marker; to modify worktree code from base, start a fresh agent session in the worktree path instead of reaching across. This sidesteps out-of-cwd permission issues most agent platforms enforce
- **Cleanup semantics**: `coordinator.cleanup` now removes directories via `fs.rmSync` then runs a single `git worktree prune` pass. Replaces the per-epic `git worktree remove --force` with fallback — cheaper (O(1) git invocations instead of N) and works around git's refusal to remove worktrees that contain the untracked `.arcforge-epic` marker
- **Subprocess I/O**: install and test subprocesses (`_runSubprocess`) now use `stdio: 'inherit'` — streams output directly to the parent terminal. Avoids `execFileSync`'s 1 MB `maxBuffer` which long-running `npm install` / `cargo build` / `pip install` could exceed and incorrectly report as ENOBUFS
- **`arc-using-worktrees`**: simplified to a thin wrapper around `node "${SKILL_ROOT}/scripts/coordinator.js" expand --epic <id> --project-setup`. All path derivation, marker writing, and dependency install delegated to `scripts/lib/coordinator.js` — the skill is now ~180 lines down from ~400
- **`arc-finishing-epic`**: completion format now reports absolute worktree paths sourced from `arcforge status --json` (or `(removed)` when cleaned up), never reconstructed from pattern knowledge. Added explicit Step 4.6 "Look Up the Worktree Path"
- **`arc-coordinating`** Merge From Worktree: base detection now uses `parseWorktreePath()` to recognize which `git worktree list` entries are arcforge-managed — no more string-matching `.worktrees`
- **`arc-maintaining-obsidian`** Mode Entry Gate: each mode (ingest/query/audit) now reads its reference file before executing. Skipping the gate causes cascading errors (improvised schemas, missed pipeline steps, wrong extraction methods)
- **`arc-maintaining-obsidian`** raw-first ingest: Raw Source ingest always saves the immutable original to `Raw/` before creating the wiki Source note. Conflating "what the source said" with "what I understood" would lose re-extraction ability
- **`arc-maintaining-obsidian`** vault-only answers extend to surrounding commentary — query mode never fills gaps with general knowledge in framing, insights, or comparisons around vault results; surfaces gaps as GROW suggestions instead
- **`arc-maintaining-obsidian`** broken wikilink resolution strategy: choose based on Raw Source backing + reference count (3+ refs → flag for user, 1-2 refs → convert to plain text). Never create stub entity notes without source backing
- **`arc-maintaining-obsidian`** LINT verify-before-fix: findings are hypotheses, not facts — read the actual file before acting on reported issues (fixes common false positive with YAML multi-line `tags:` lists)
- **`arc-maintaining-obsidian`** LINT correctly skips Excalidraw `.md` drawings (`excalidraw-plugin: parsed` frontmatter) during audit
- **`docs/guide/skills-reference.md`**: added `arc-dispatching-teammates` entry with platform marker; platform-only markers added to `arc-looping`, `arc-evaluating`, `arc-observing`, `arc-managing-sessions` flagging them as Claude Code only
- **`README.md`**: `arc-dispatching-teammates` added to Execution Layer skill list; version badge bumped
- **`.claude/rules/architecture.md`**: Worktree Isolation section rewritten to describe home-based canonical path + `worktree-paths.js`
- **`hooks/hooks.json`** loader now supports the sync fix (see Fixed)
- **`arc-dispatching-teammates`**: reworked with subagent-gated acceptance check (dispatch `arcforge:spec-reviewer` + `arcforge:verifier` per teammate completion — lead reads reports and decides, does not run checks inline), retry loop (up to 3 retries per epic with cumulative feedback in spawn prompt), wrap-up sequence (emit Final Report with per-epic subagent evidence → cleanup accepted worktrees → shut down teammates → `TeamDelete`), per-completion teammate shutdown to prevent tmux pane accumulation, spec-defect override protocol (distinguish spec defects from impl defects via independent grep evidence), and TaskUpdate visibility warning (TaskUpdate is not a notification channel — completion must go through SendMessage). Spawn prompt template extracted to `references/spawn-prompt-template.md` with explicit `## Your Authority` section that prevents mid-phase stalling
- **`arc-finishing-epic`**: added Step 4.1 Merge Conflict Handling — multi-teammate merge conflicts escalate to lead via SendMessage using a structured `Merge Conflict (Multi-Teammate)` blocked format (conflict files, hunks verbatim, proposed resolution, risk assessment); solo-epic conflicts present to user for resolution guidance. Never auto-resolve by taking ours/theirs/guessed union
- **Coordinator DAG mutations**: `_mergeEpicsInBase` now updates `.arcforge-epic` marker to `completed` after merge (prevents sync from overwriting DAG's correct `completed` back to stale `in_progress`); `expandWorktrees`, `_syncWorktree`, `_syncBase`, and `syncEpicStatusesFromBase` wrapped in `_dagTransaction` to prevent concurrent teammate race conditions; both sync paths validate `local.status` via `normalizeStatus()` before propagating to DAG

### Fixed

- **`inject-skills` hook race condition**: the hook was registered with `"async": true`, so its output (the arc-using routing layer) arrived *after* the first assistant turn for spawned teammate subagents. The race was invisible for interactive user sessions because humans type slowly, but fatal for teammate spawns where the first prompt is delivered immediately. Removed `async: true` — the hook now fires synchronously on `SessionStart` (~829ms) and teammates reliably receive routing discipline. Root cause identified during arc-dispatching-teammates PoC (3 rounds of behavioral verification — LLM self-introspection about system prompt contents proved unreliable, so verification had to use exact-string behavioral tests)
- **`--project-setup` package manager selection**: previously hardcoded `npm install`. Now routes through `detectPackageManager()` so pnpm/yarn/bun projects use their own installer instead of corrupting the lockfile with the wrong tool
- **`git worktree remove` failures on `.arcforge-epic` marker**: git refused to remove worktrees containing the untracked marker file. Replaced with direct `fs.rmSync` + one `git worktree prune`
- **ENOBUFS on long installs**: `npm install`/`cargo build`/`pip install` no longer risk ENOBUFS thanks to streamed stdio (`_runSubprocess` with `stdio: 'inherit'`)
- **Retracted: false `claude -p` subprocess bug**: a prior debugging note claimed `arc-looping`'s `claude -p` subprocesses did not fire arcforge hooks. Controlled re-test from a neutral directory (`/tmp/loop-hook-test/`) proved this was contamination from running tests inside arcforge's dev repo, where `.claude/settings.json` deliberately disables the arcforge plugin at project level. All past eval results remain valid; the root fact has been moved to `.claude/rules/dev-context.md` per the audience-separation principle (contributor concerns never belong in shipped surface)
- **`marketplace.json` version drift**: `.claude-plugin/marketplace.json` was stuck at 1.2.0 (last manually updated two versions ago) while `plugin.json` and `package.json` had moved on. Synced all three version sources to 1.4.0 as part of this release
- **DAG read-modify-write race condition**: concurrent teammate processes (merge, expand, sync) could load stale `dag.yaml` snapshots, mutate independently, and the second save would clobber the first — leaving an epic stuck at `in_progress` despite its branch being merged successfully. `_dagTransaction()` serializes all hot-path mutation paths under a single file lock with fresh-read-before-mutate semantics
- **`done` vs `completed` status bug**: agent-written `.arcforge-epic` marker values (`done`, `finished`, `complete`) bypassed the `TaskStatus` enum, breaking `_getBlockedBy()` dependency resolution — downstream epics remained blocked despite their dependencies being complete. `normalizeStatus()` maps known aliases to canonical values and throws on unknown
- **`.arcforge-epic` marker stale after merge**: `_mergeEpicsInBase` set `epic.status = TaskStatus.COMPLETED` in the DAG but never updated the worktree marker, which retained `in_progress` from expand time. Subsequent sync would overwrite the DAG's correct value with the stale marker value, un-completing the epic
- **`.arcforge-epic` leaking into git commits**: teammates' blanket `git add -A` / `git add .` patterns staged the worktree marker file, which appeared in commit history after merge. `_ensureArcforgeExcluded()` writes the marker to `.git/info/exclude` (linked worktrees share this via `commondir`)
- **Teammate pane accumulation**: completed teammates were not shut down between dispatches during continuous dispatch, causing tmux panes to accumulate and eventually hitting pane limits. Per-completion shutdown in Step 6/7 keeps active pane count ≤5

### Removed

- `.worktrees/` in-repo worktree directory (and its `.gitignore` entries) — superseded by the home-based canonical location
- `_ensureWorktreesIgnored()` helper — orphaned after the migration
- `_runTestCommand()` internal — replaced by the generic `_runSubprocess()` used by both test verify and project setup
- `skills/arc-using-worktrees/baseline-test.md` — consolidated into the new `tests/skills/pressure/` fixture format

## [1.3.1] - 2026-04-08

### Fixed

- **arc-maintaining-obsidian**: Added Mode Entry Gate — each mode (ingest/query/audit) must read its reference file before executing, preventing improvised schemas and missed pipeline steps
- **arc-maintaining-obsidian**: Raw Source ingest now enforces "raw first, wiki second" — saves immutable original to `Raw/` before creating the wiki Source note, preserving the ability to re-extract and verify
- **arc-maintaining-obsidian**: URL extraction defaults to Defuddle over WebFetch — WebFetch returns AI-interpreted HTML while Defuddle renders in a real browser and extracts clean markdown faithful to the original
- **arc-maintaining-obsidian**: Vault-only answers now extend to surrounding commentary — no general knowledge backfill in framing, insights, or comparisons around vault results
- **arc-maintaining-obsidian**: LINT now requires verify-before-fix — findings are hypotheses, not facts; must read the actual file before acting on reported issues
- **arc-maintaining-obsidian**: LINT warns about YAML multi-line list false positives — `tags:` with no inline value is not empty if followed by indented `  -` items
- **arc-maintaining-obsidian**: Added broken wikilink resolution strategy — checks Raw Source backing, reference count, and offers plain text conversion instead of creating unsourced stub entities
- **arc-maintaining-obsidian**: Excalidraw `.md` drawings (with `excalidraw-plugin: parsed` frontmatter) now correctly skipped during LINT audit
- **arc-maintaining-obsidian**: Raw Source frontmatter template added (`source_url`, `source_author`, `fetched`) for traceability of immutable originals

## [1.3.0] - 2026-04-08

### Added

- **arc-maintaining-obsidian** skill: Unified Obsidian vault skill — merged arc-writing-obsidian, arc-querying-obsidian, and arc-auditing-obsidian into one skill with three modes (ingest, query, audit). Implements Karpathy's LLM Wiki pattern with PROPAGATE (cross-page update on ingest), EVOLVE checks, and outward GROW.
- **arc-diagramming-obsidian** skill: Excalidraw diagram generation with JSON direct write, render-validate loop, and cool minimal color palette
- **arc-querying-obsidian** skill: Vault-only query with inline citations and file-back capability (later merged into arc-maintaining-obsidian)
- **Obsidian knowledge base**: 83 wiki notes (62 Source + 11 Entity + 5 Synthesis + 5 MOC) published at https://publish.obsidian.md/greghodev/ArcForge/MOC-ArcForge — covers all skills, rules, agents, templates, guides, designs, and research
- `.claude/rules/obsidian-wiki.md`: Scope definition for what project artifacts belong in the wiki
- `.claude/rules/eval.md`: Evaluation framework rules (extracted from inline guidance)
- `.md` extraction method in `page-templates.md` for ingesting plain markdown files from `Raw/`
- `docs/guide/eval-system.md`: Core eval mechanism guide (A/B testing, discriminative scenarios, grader types)
- arc-researching refinements: Strategy selection, trial management, external research integration

### Changed

- **arc-researching** skill: Trimmed to 1776 words (under 1800 budget), added strategy/trials/external research sections
- Obsidian skills: Added obsidian-cli pipe safety warning (never pipe `obsidian read` through head/tail), path safety guidance (`file=` vs `path=`), progressive disclosure in token efficiency section
- Eval harness: Added `--no-isolate`, `--plugin-dir`, `--max-turns` flags; behavioral assertions with deterministic grading; mixed grader support; action log display in dashboard
- `docs/` folder: Removed 263KB of auto-generated reference dumps and superseded design docs — wiki knowledge base is now the authoritative documentation source

### Fixed

- **obsidian-cli pipe safety**: `obsidian read` hangs on SIGPIPE when piped through `head`/`tail` — documented workaround (read full output or use Read tool)
- **eval option resolution**: Simplified eval settings consolidation, fixed double maxTurns resolution and maxBuffer bug
- **YAML flow array parsing**: Fixed parsing of inline YAML arrays in DAG state sync
- **arc-verifying invocation**: Removed self-contradicting "don't invoke me" prohibition from a routing-table-registered skill
- **loop epic scoping**: Simplified loop epic detection after code review feedback

### Removed

- **arc-writing-obsidian**, **arc-querying-obsidian**, **arc-auditing-obsidian**: Merged into arc-maintaining-obsidian (one skill, three modes)
- `docs/guide/architecture-overview.txt` (47KB), `cli-reference.txt` (96KB), `skills-workflow.txt` (56KB), `skill-loading-platforms.txt` (54KB), `workflow-overview.txt` (9.5KB): Auto-generated reference dumps replaced by Obsidian wiki knowledge base
- `docs/plans/2026-04-07-obsidian-skills-design.md`, `obsidian-skills-improvements-design.md`: Superseded by arc-maintaining-obsidian merge design
- `docs/research/gemini-cli-skills.md`: Stale stub (12 lines, 3 months old)

## [1.2.0] - 2026-03-31

### Added

- **arc-compacting** skill: Strategic manual compaction timing at workflow phase boundaries
- **arc-evaluating** skill: Measure whether skills, agents, or workflows change AI agent behavior — with progressive-loading references (`cli-and-metrics.md`, `common-mistakes-catalog.md`, `grading-and-execution.md`)
- **arc-looping** skill: Autonomous loop execution with cross-session DAG task coordination
- **arc-managing-sessions** skill: Session save/resume with alias support and cooperative auto-memory coexistence
- **arc-researching** skill: Autonomous hypothesis-driven experimentation for metric optimization ("fixed judge + free player" pattern)
- 9 scoped rule files in `.claude/rules/` — extracted from monolithic CLAUDE.md for context-aware loading: architecture, coding-standards, git-workflow, hooks, plugin, security, skills, templates-commands-agents, testing
- 9 new agent definitions in `agents/`: debugger, eval-comparator, eval-grader, implementer, loop-operator, planner, quality-reviewer, spec-reviewer, verifier
- `AGENTS.md`: Agent catalog for Codex platform discovery
- Eval infrastructure: per-assertion code grading engine (`eval-graders.js`), statistics aggregation (`eval-stats.js`), core eval engine (`eval.js`), transcript parser (`transcript.js`)
- Eval dashboard: `eval-dashboard.js` + `eval-dashboard-ui.html` — web UI with collapsible artifacts panel and audit trail
- Eval scenarios: 11 new scenarios (debug-investigate-first, debug-stop-at-three, diary-quality, eval-grader-selection, eval-scenario-splitting, eval-trap-design, hook-inject-skills, instinct-adherence, reflect-pattern-detection, tdd-compliance) + eval skill-files for instinct testing
- Eval benchmarks: JSON snapshots for 2026-03-19, 2026-03-20, 2026-03-23
- Research dashboard: `research-dashboard.js` + `research-dashboard.html` — live monitoring with SSE and inline SVG charts
- Loop execution engine: `scripts/loop.js` for autonomous cross-session execution
- Session management: `session-aliases.js` for alias-based session tracking, `session-utils.js` expanded with new helpers
- `commands/sessions.md`: Thin delegation wrapper for session management
- `docs/guide/hooks-system.md`: Comprehensive hooks I/O visibility rules and contributor guide
- `hooks/log_lightweight/`: Refactored Python logging into 6-module package (config, dispatcher, io_writer, state, tokens, tools)
- `hooks/run-hook.cmd`: Windows-compatible hook dispatcher
- `arc-writing-skills/agents/`: 4 eval subagent definitions (description-tester, skill-analyzer, skill-comparator, skill-grader) + `references/eval-schemas.md` and `testing-skills-with-subagents.md`
- Tests: `e2e-hooks.test.js` (36 behavioral tests with real Claude Code fixtures), `observe.test.js`, `pre-compact.test.js`, `quality-check.test.js`, `coordinator.test.js`, `eval-dashboard.test.js`, `eval-integration.test.js`, `eval-stats.test.js`, `eval.test.js`, `locking.test.js`, `loop.test.js`, `package-manager.test.js`, `research-dashboard.test.js`, `session-aliases.test.js`, `session-listing.test.js`, `transcript.test.js`, `utils.test.js`, `test-models.js`, `test-yaml-parser.js`, `test_eval_agents_contract.py`, `test_eval_scenario_format.py`, `test_skill_arc_evaluating.py`

### Changed

- `CLAUDE.md`: Slimmed from 69 to 28 lines — bulk content extracted to `.claude/rules/` for scoped loading
- `arc-evaluating/SKILL.md`: 8 targeted improvements from research loop — restructured for token budget with progressive-loading references; added "competence proxy" and "skill formalizes existing behavior" to Common Mistakes
- `arc-agent-driven/SKILL.md`: Enhanced with eval-aware subagent dispatching
- `arc-writing-skills/SKILL.md`: Updated to reference new eval agents and testing-with-subagents guide
- `arc-brainstorming/SKILL.md`, `arc-observing/SKILL.md`, `arc-planning/SKILL.md`, `arc-refining/SKILL.md`, `arc-using/SKILL.md`: Minor refinements (cross-references, SKILL_ROOT, eval hooks)
- Hooks output visibility: user-facing messages switched from stderr (invisible in Claude Code) to `systemMessage` JSON format across observe, pre-compact, quality-check, and compact-suggester hooks
- `hooks/compact-suggester/main.js`: Refactored to unified `{ tools, reads, writes }` JSON state; separated compact counter from diary counter
- `hooks/session-tracker/inject-context.js`: Major refactoring for session alias support and cooperative auto-memory
- `hooks/session-tracker/end.js`: Enhanced session finalization with alias tracking
- `hooks/log-lightweight.py`: Refactored from monolithic 887-line file into 6-module package under `hooks/log_lightweight/`
- `scripts/cli.js`: Added `arc eval dashboard`, `arc research dashboard`, loop commands, and session management subcommands
- `scripts/lib/coordinator.js`: Enhanced DAG coordination with new status helpers
- `scripts/lib/models.js`: Added TaskStatus export from dag-schema
- `scripts/lib/dag-schema.js`: Added TaskStatus enum export
- `scripts/lib/utils.js`: Added new utility functions for eval and session support
- `README.md`: Updated skill descriptions and documentation links
- `CONTRIBUTING.md`: Updated hook architecture section

### Fixed

- **Hook stdin crash**: `log-lightweight` dispatcher crashed on empty stdin — now handles gracefully
- **Hook field name**: SessionStart event sends `source` field, not `trigger` — fixed across all hooks that read session start reason
- **Hook output invisible**: stderr output not visible to users in Claude Code — switched to `systemMessage` JSON protocol
- **Counter collision**: compact-suggester and diary hooks shared a counter, causing incorrect compaction timing — separated into independent counters
- `hooks/compact-suggester/README.md`: Corrected storage path documentation (`arcforge-tool-count` → `arcforge-compact-state`)
- `skills/arc-managing-sessions/SKILL.md`: Fixed command name references (`/sessions` → `/arc-managing-sessions`)
- `skills/arc-journaling/SKILL.md`: Fixed cross-reference to session commands

### Removed

- `.serena/memories/`: 4 legacy memory files (code_style_and_conventions, project_overview, suggested_commands, task_completion_checklist) — replaced by `.claude/rules/`
- `hooks/lib/package-manager.js`, `hooks/lib/thresholds.js`, `hooks/lib/utils.js`: Removed hook-local re-exports — hooks now import directly from `scripts/lib/`

## [1.1.2] - 2026-02-14

### Added

- `scripts/lib/evolve.js`: Three-type evolution engine — classifies instinct clusters into skills, commands, or agents via domain+confidence rules with keyword tiebreaker
- `learn.js generate` command: Creates skill, command, or agent scaffolds from clustered instincts (`--type`, `--name`, `--dry-run`)
- `learn.js list` command: Shows previously evolved artifacts from JSONL tracking log
- `session-utils.js`: `getEvolvedLogPath()` helper for `~/.claude/evolved/evolved.jsonl`
- Resistance-based confidence: `MANUAL_CONTRADICT_DELTA`, `MANUAL_DECAY_PER_WEEK`, `RESISTANT_SOURCES` for source-aware scoring
- Tests: `evolve.test.js` (358 lines), `confidence.test.js` additions (118 lines), `learn.test.js` (135 lines)

### Changed

- `confidence.js`: `applyContradiction()` accepts optional `source` — manual/reflection instincts receive half-strength contradiction (-0.05 vs -0.10)
- `confidence.js`: `runDecayCycle()` applies source-aware decay — resistant sources decay at 50% rate
- `instinct.js`: passes `frontmatter.source` to `applyContradiction()` for resistance-based scoring
- `learn.js`: Extracted helpers, removed duplication, derived constants from centralized modules

### Fixed

- `learn.js generate`: Refuses to overwrite existing artifacts — exits with error instead of silently clobbering
- `learn.js generate`: `--name` sanitized to prevent path traversal (`/`, `\`, `..`)
- `learn.js generate`: `--type` validated against allowed values (skill, command, agent)
- `learn.js generate`: Empty slug fallback uses domain name instead of broken paths
- `learn.js`: Evolution deduplication scoped to project to prevent cross-project false positives

## [1.1.1] - 2026-02-13

### Added

- `docs/guide/skills-reference.md`: Complete skill catalog (701 lines) with decision trees, workflow comparisons, and iron laws
- `skills/arc-observing/scripts/observer-system-prompt.md`: Separated system prompt from task prompt for observer daemon
- 9 new pytest test files: all 24 skills now have dedicated test coverage (111 tests total)
- `tests/skills/test_skill_cross_references.py`: Cross-reference validation for REQUIRED SUB-SKILL and REQUIRED BACKGROUND
- SKILL_ROOT initialization added to `arc-learning`, `arc-planning`, `arc-recalling`

### Changed

- **Learning subsystem refactored** (PR #3): sync context injection, merged stop hooks, unified bubble-up logic
- Word count policy: replaced hard 500-word assertion with 4-tier soft guidance (Lean <500w, Standard <1000w, Comprehensive <1800w, Meta <2500w)
- `arc-dispatching-parallel`: restructured dual numbering, added conflict detection fallback
- `arc-agent-driven`: added max review cycle guard (3 cycles per reviewer)
- `arc-implementing`: added explicit retry limits (2 refinement cycles)
- `arc-using-worktrees`: auto-detect test command instead of hardcoded pytest
- `observer-daemon.sh`: atomic mkdir-based locks with mv-based stale reclaim, circuit breaker (3 failures), max age TTL (2h)
- `hooks/observe/main.js`: file-based signal cooldown (30s) to prevent duplicate processing
- `hooks/session-tracker/start.js`: split into sync + async for reliable context delivery
- Branding: all remaining "Agentic-Core" references renamed to "arcforge" in INSTALL files and platform READMEs

### Removed

- Unused functions from `scripts/lib/locking.js` (`_withLockAsync`, `_isLocked`, `_forceClearLock`)
- Unused `require('node:path')` in `scripts/cli.js`
- Placeholder test assertions replaced by substantive content checks

### Fixed

- `arc-finishing-epic`: removed redundant sync step; moved DAG block + sync before worktree removal in discard option
- `arc-journaling`: corrected `/learn` command references to `/reflect` (3 occurrences)
- `arc-finishing`: resolved contradictory cleanup instructions (Step 5 now applies to Options 1 and 4 only)
- `arc-executing-tasks`: fixed duplicate step numbering
- `arc-debugging`: corrected heading capitalization
- `arc-finishing` and `arc-agent-driven`: cleaned up description text (removed workflow summaries)
- Workflow docs: `.agentic-epic` references corrected to `.arcforge-epic`

## [1.1.0] - 2026-02-10

### Added

- **arc-observing** skill: Tool call observation for behavioral pattern detection
- **arc-recalling** skill: Manual instinct creation from session insights
- **observe** hook: Tool call observation on PreToolUse and PostToolUse events
- **user-message-counter** hook: User prompt counting on UserPromptSubmit
- **pre-compact** hook: Pre-compaction state marking on PreCompact
- **session-tracker/inject-context.js** hook: Context injection at session start (diary + instincts)
- **log-lightweight** hook entries for SubagentStop, SessionEnd, PermissionRequest events
- `package.json`: license, author, repository, bin, files fields for plugin distribution
- `scripts/lib/confidence.js`: Unified confidence scoring for instincts (create → confirm → decay → archive)
- `scripts/lib/fingerprint.js`: Trigger fingerprinting with Jaccard similarity for deduplication
- `scripts/lib/global-index.js`: Cross-project instinct bubble-up tracking
- `scripts/lib/instinct-writer.js`: Instinct file creation with YAML frontmatter
- `scripts/lib/pending-actions.js`: Deferred action queue for post-session tasks
- `skills/arc-journaling/scripts/auto-diary.js`: Automatic diary generation from session data
- `skills/arc-learning/scripts/learn.js`: Pattern extraction with scan, preview, and cluster commands
- `skills/arc-observing/scripts/instinct.js`: Instinct management CLI (list, confirm, contradict)
- `skills/arc-observing/scripts/observer-daemon.sh`: Background observation daemon
- `skills/arc-recalling/scripts/recall.js`: Manual instinct save (delegates to instinct-writer)
- `commands/instinct-status.md`: Command wrapper for instinct status viewing

### Changed

- **arc-learning** skill: Major restructuring — unified instincts and learned skills into single system
- `plugin.json` and `marketplace.json`: Version bumped to 1.1.0
- `hooks/observe/main.js`: Deduplicated code, imports from canonical utils and session-utils

### Removed

- **session-evaluator** hook (never implemented)
- Dead files: `hooks/session-tracker/main.js`, `hooks/run-hook.js`, `uv.lock`, stale baseline test files, empty `docs/designs/`
- Unused exports from `utils.js` (`getPluginRoot`, `getScriptsDir`, `getHooksDir`, `readStdin`, `outputHookResponse`, `logWarning`)
- Unused exports from `locking.js` (all except `withLock`)
- Unused `getObservationsArchivePath` from `session-utils.js`
- Unused `getNewlyAvailable()` method from `coordinator.js`

### Fixed

- `hooks/quality-check/main.js`: `.catch()` on sync function replaced with `try/catch`
- `scripts/lib/confidence.js`: CRLF line endings now normalized before frontmatter parsing
- `scripts/lib/dag-schema.js`: Backslash escaping in YAML `formatValue` (escape `\` before `"`)
- `scripts/lib/dag-schema.js`: `depends_on` cross-reference validation in `validate()`
- `scripts/lib/dag-schema.js`: Removed unused `isArrayItem` parameter from `objectToYaml`
- `scripts/lib/coordinator.js`: `completeTask` now promotes parent epic from PENDING to IN_PROGRESS
- `scripts/lib/locking.js`: Lock file renamed from `.agentic-lock` to `.arcforge-lock`
- Stale `session-evaluator` references removed from user-message-counter hook and README
- Template filename corrected in architecture overview (`quality-reviewer-prompt.md`)
- README: Development section now references `npm test` (all 4 runners) instead of just `pytest`
- CONTRIBUTING.md: Added Gemini CLI to platform list, fixed stale `run-hook.js` reference
- hooks/README: Updated tree (removed deleted files, added session templates), fixed deprecated utility references
- Architecture overview: Added all `scripts/lib/` modules, fixed test runner description, updated docs tree

## [1.0.0] - 2026-02-08

### Skills (22 arc-* skills)

**Workflow**: arc-brainstorming, arc-refining, arc-planning, arc-coordinating, arc-implementing
**Execution**: arc-tdd, arc-writing-tasks, arc-executing-tasks, arc-agent-driven, arc-dispatching-parallel
**Support**: arc-debugging, arc-verifying, arc-using-worktrees, arc-finishing, arc-finishing-epic, arc-requesting-review, arc-receiving-review
**Learning**: arc-journaling, arc-reflecting, arc-learning
**Meta**: arc-using, arc-writing-skills

### CLI Engine

- DAG-based task management (`status`, `next`, `complete`, `block`)
- Git worktree orchestration (`expand`, `merge`, `cleanup`)
- Bidirectional sync between worktrees and base DAG
- File-based locking for concurrent access safety

### Hooks

- inject-skills: Session context injection
- session-tracker: Event tracking with counters
- compact-suggester: Context compaction timing
- quality-check: Code quality validation
- log-lightweight: Session logging with cost estimation

### Multi-Platform Support

- Claude Code (plugin marketplace)
- Codex CLI
- OpenCode
- Google Gemini CLI
