Changelog
Это содержимое пока не доступно на вашем языке.
All notable changes to Forgeplan are documented here. Format loosely follows
Keep a Changelog. Semver is MAJOR.MINOR.PATCH
with pre-1.0 minor bumps for breaking changes.
The canonical source is CHANGELOG.md
in the repository. This page is generated from it at build time via scripts/copy-changelog.mjs.
All notable changes to Forgeplan are documented here. Format extends
Keep a Changelog with a ### Internal
section for engineering details (tests, refactoring, tooling) that do not
fit the six user-facing categories. Semver: MAJOR.MINOR.PATCH with
pre-1.0 minor bumps for breaking changes.
This file starts at v0.17.0. For prior releases, see git tags and the
corresponding sprint evidence under .forgeplan/evidence/.
[Unreleased]
Section titled “[Unreleased]”[0.32.1] - 2026-05-21
Section titled “[0.32.1] - 2026-05-21”Hotfix release. v0.32.0 shipped with a Cargo.toml regression that prevented Windows binary publication via cargo-dist. macOS / Linux binaries also did not publish because cargo-dist exits the entire workflow when any target fails. No semantic / behaviour changes - purely a build-time dependency placement fix.
- Windows build -
tera/walkdir/globsetwere accidentally placed inside the[target.'cfg(unix)'.dependencies]block (commita3b6ed3audit-r3 closure on 2026-05-20 when addinglibcforO_NOFOLLOW). The three crates are cross-platform and used unconditionally insrc/ingest/andsrc/projection/. Moved back into the canonical[dependencies]block; onlylibc = "0.2"remains unix-gated. cargo-dist v0.32.0 Release workflow failed with 20error[E0432]/E0433]“cannot find module or crate” errors onx86_64-pc-windows-msvc; v0.32.1 Release workflow re-triggers and publishes all four target binaries plus the homebrew-tap formula bump.
Internal
Section titled “Internal”- v0.32.0 GitHub release page has zero binary assets (visible blocker). v0.32.1 supersedes it for distribution; the v0.32.0 tag remains on
mainas the canonical commit but should not be referenced for installation. Users on Homebrew get the upgrade once cargo-dist updatesForgePlan/homebrew-tapFormula/forgeplan.rb (automated, no human intervention). - Catch-up doc updates from PR #317 (was on
devonly): CLAUDE.mdCurrent statusrefreshed to v0.32 banner; README dogfood table refreshed to current counts.
[0.32.0] - 2026-05-20
Section titled “[0.32.0] - 2026-05-20”Sprint headline: Epic #287 brownfield extraction surface + 3 pre-Epic issues (#286/#288/#289) + 6 dogfood-discovered issues, closed inline across multiple adversarial audit rounds.
Three parallel sprints landed into one release:
A. Issues #286 / #288 / #289 - pre-Epic methodology hardening
Section titled “A. Issues #286 / #288 / #289 - pre-Epic methodology hardening”- Link upsert +
forgeplan_unlinkMCP tool (issue #286) -forgeplan_linkgains optionalreplace: boolparameter. When true and an edge between(source, target)already exists with a different relation, the old edge is replaced (audit-r2 ordering: add new first, then delete old, so a partial failure leaves the OLD edge intact). Used to fix mis-typedbased_on↔informsrelations that previously cascaded a CL penalty through R_eff with no MCP-safe recovery path (onlyforgeplan_deletewould work, destroying the artifact). Newforgeplan_unlinktool +forgeplan link --replaceCLI flag. TheLinkResponse.auto_activatedfield is new (see #288 below); consumers using#[serde(deny_unknown_fields)]need to relax that. - Pipeline hygiene primitives (issue #288) -
forgeplan_linkgains optionalauto_activate_source_if_complete: bool. When true and the source matches the canonical “ready to activate” predicate (kind=evidence + status=draft + body has verdict + congruence_level), it is silently promoteddraft → active. Audit-r2 dropped the defunctR_eff>0check from the gate (evidence artifacts have no incoming evidence by design, so cached R_eff is always 0 - the check was dead code). Audit-r3 F1 unified this predicate aslifecycle::ready_to_activate, shared by auto-activate, thestale_drafts.ReadyToActivatehealth classification, and thestuck_draftanomaly. ResponseLinkResponse.auto_activated: Option<String>carries the id when the transition fires. Failure is non-fatal - the link itself already committed; tracing::warn fires and the response hints at manual retry.forgeplan_healthgainsstale_drafts: Vec<StaleDraft>(24h threshold default;VerdictThresholds.stale_ready_draftsdefault 5 → Unhealthy). Hint chain enhanced - when the link source is a complete-but-still-draft EVID, the response ends withActivate: forgeplan_activate <id>so the next step is unambiguous. forgeplan_anomaliestool + v1 catalog (issue #289) - new MCP tool + CLI command surfaces pipeline anomalies with structured severity + tier classification (auto / adi / user) so plugin-layer orchestrators (/forge-cycle,/forge-cleanup) can dispatch resolutions without re-classifying. v1 catalog:stuck_draft,orphan_link,mistyped_based_on,missing_must_section,expired_evidence,weakest_link_unresolvable,phase_mismatch,circular_dependency,duplicate_artifact. Filters:kind/severity/since(the last backed by a per-anomaly observed_at journal at.forgeplan/anomalies-journal.jsonl, audit-r2 lands true diff semantics; audit-r3 adds GC of resolved-anomaly entries + deterministic file ordering). CLI:forgeplan anomalies [--kind X] [--severity Y] [--since TS] [--json]. CLI JSON output emits_next_actionmatching the MCP wire shape (audit-r3 HIGH-1 closure for PRD-071 parity).- MCP tool count: 73 tools total in v0.32.0 (authoritative count via
#[tool(...)]macro; drift detector fix in commit a3e52c4 - prior narrative cited wrong baseline). v0.31.x had 64. This release addsforgeplan_unlink(#286) andforgeplan_anomalies(#289) at section A (+2), plus 7 brownfield tools in section B (#287 Epic):forgeplan_hypothesis_status,_promote,_coverage_business,_contradictions,_orphans,_interview_packet_draft,_ingest. Net delta: +9 over v0.31.x (64 → 73).
A.1 Breaking changes (issues #286 / #288 / #289)
Section titled “A.1 Breaking changes (issues #286 / #288 / #289)”forgeplan_core::dispatch::SerialEntryis now#[non_exhaustive](CR-H3). Downstream Rust callers can no longer use struct-literal construction or exhaustive pattern-matching across crate boundaries - useSerialEntry::new(id, reason)instead. The JSON wire format is unchanged (serde keeps the same shape). AddedDisplayimpl rendering<id> (<reason>)for uniform CLI output.forgeplan_mcp::types::DispatchSerialEntrymirrors the same#[non_exhaustive]+Display+newcontract (CR-H3). AddedFrom<forgeplan_core::dispatch::SerialEntry>so server.rs can flatten the core type into the MCP DTO without manual destructuring.- Dispatch serial-queue reason phrasing changed (CR-H4):
blocked by dependency on <PARENT>→blocked by dependencies: <PARENT>[, <PARENT>...]. The new phrasing is plural even with one parent and lists every unresolved parent (sorted). Substring-matchers should switch from"blocked by dependency"to"blocked by dependencies".
A.2 Fixed (issues #286 / #288 / #289)
Section titled “A.2 Fixed (issues #286 / #288 / #289)”- CR-C4:
secrets.yaml→secrets.env- the template shipped byforgeplan initwas named.yamlbut contained shellexportsyntax, failing YAML lint andserde_yaml::from_str. Renamed to.env(dotenv/direnv convention) across all 7 code surfaces + 2 doc surfaces. Existing workspaces migrate viaforgeplan init --force. Also removes the literalsk-...placeholder from the template - that prefix was raising TruffleHog/gitleaks false-positives on every CI run. - SEC-H1: backfill on existing workspaces -
forgeplan init --forcenow writes.gitkeepplaceholders into every empty artifact subdir and creates thesecrets.envtemplate if missing. Idempotent - never clobbers a user-edited template containing real keys. Closes the migration gap for workspaces created before PRD-077 FR-001 / FR-002. - CR-H4: dispatch multi-parent blocker reason -
forgeplan dispatchandforgeplan_dispatchMCP tool previously reported a single arbitrary parent for blocked artifacts (HashMapcollapsed multi-parent edges to last-seen target) AND countedinformsas a structural blocker (the relation type was dropped in the lookup build). Both fixed by extractingbuild_blocker_reasons_from_slicetoforgeplan_core::dispatch- CLI and MCP now share one implementation. The helper usesgraph::topological::is_structural_relation(the same predicatekahn_sortuses), so the canonical structural-relation list (based_on,refines,supersedes,contradicts) drives both “what blocks the topo sort?” and “what blocks dispatch?” - they can no longer drift. Multiple parents are listed alphabetically:blocked by dependencies: PRD-001, PRD-002, RFC-003.
B. Epic #287 - brownfield extraction surface
Section titled “B. Epic #287 - brownfield extraction surface”6 new ArtifactKind variants (use_case, glossary, invariant, scenario, hypothesis, domain_model) with Factum/Intent two-tier validation, 7 new MCP tools (forgeplan_hypothesis_status / _promote / _coverage_business / _contradictions / _orphans / _interview_packet_draft / _ingest), brownfield graph rendering (forgeplan_graph --brownfield-only), and a Hypothesis lifecycle state machine (parked → inferred → strong-inferred → verified → refuted with terminal absorbing states).
5 adversarial audit rounds - r4 (13 findings closed), r5 (8 deferred items closed inline), r6 (ARCH-H1 consolidation + PROB-069), r7 (full-branch review, 13 more findings), plus E2E coverage. ADR-014 added for AnomalyKind catalog evolution policy.
Phase E (forgeplan_render_canonical + forgeplan_export_rag) split into marketplace#79 - those tools live in plugin layer (wrap brownfield-pack skills C10/C12), not core.
C. 6 dogfood-discovered issues (#290..#295)
Section titled “C. 6 dogfood-discovered issues (#290..#295)”Collected from real-world workspace usage in marketplace repo, filed upstream, fixed in this release:
- #290
forgeplan_release_notessplit-repo discovery - newfind_git_rootwith 3-tier discovery (start → walk up bounded by.forgeplan/marker or 8 hops → walk down one level with symlink rejection and deterministic ordering). Fixes “not a git repository” failure when.forgeplan/and.git/are in different directories. - #291
forgeplan_restoretarget_status override - newapply_restore_with_targetaccepts explicittarget_status(draft/active/deprecated). Lifecycle FSM validates the transition before mutation. Response surfacesrestored_status+restored_status_sourceso operators see exactly which state was applied. - #292
forgeplan_discover_*status field disambiguation - addedsession_status(explicit session state) andartifact_status(alwaysdraftfor newly-created findings) to all three discover tools. Legacystatusfield kept for backward compat (zero breakage on existing consumers)._field_warningsdeprecation channel announces upcoming removal of legacystatus. - #293
forgeplan_driftmarkdown-table parser -extract_path_from_linenow handles backtick-quoted paths, pipe-delimited table cells, and(planned)/(done)annotations. Closes silent false-negative where drift detector returnedchanged_files: []for artifacts with table-formatted## Affected Files. Control chars / ANSI escapes also rejected (closes downstream-display injection vector). - #294
forgeplan_activateerror UX - “No evidence linked” error now teaches the canonical 5-step fix order verbatim in the error message (forgeplan_new evidence → update → link informs → activate EVID → activate parent). Operators no longer reach for--forceas a shortcut. - #295
forgeplan_new(kind=evidence, parent_id=...)auto-link - optionalparent_idparameter creates theinformslink in one call. Closes the 3-step boilerplate (new → update → link) observed in 100% of measured EVIDs across 12 sprints. Auto-link usesadd_link_with_projection(file-first per ADR-003); response carriesauto_linked+auto_link_warningsfields.
D. Audit-r-dogfood - 8 findings closed inline before release
Section titled “D. Audit-r-dogfood - 8 findings closed inline before release”3 parallel adversarial agents (security/code-quality/architecture) reviewed the 6 dogfood fixes. 24 findings total (after dedup). Closed inline:
- SEC-1 HIGH -
find_git_rootrejects symlinks viasymlink_metadata, bounds walk-up by.forgeplan/marker, sorts walk-down candidates alphabetically. Closes: attacker plantssubdir/.git -> /other-user-repo/.git→ release_notes leaks their commits. - CODE-1 HIGH -
restored_status_sourcecompares actual values (after trim+lowercase) instead of justtarget_status.is_some(). Previously reportedexplicit_overrideeven on no-op overrides. - SEC-2 MED -
target_statusFSM transition validation. Forbidden transitions (e.g.deprecated → draft) rejected before any mutation. - SEC-3 MED - control chars + Unicode line separators (
U+0085/2028/2029) rejected inextract_path_from_lineresults. Closes ANSI-escape injection in MCPDriftedFile.path. - SEC-4 MED -
auto_link_warningsroute errors throughsanitize_error_chain. Wave 9 SEC-H3 regression closed - raw{e}HOME paths no longer leak. - CODE-2 MED -
_field_warningsdeprecation channel added to_startand_complete(was only in_finding). All three discover tools now consistent. - CODE-4 / ARCH-2 MED -
NewArtifactResponsegets properauto_linked+auto_link_warningsDTO fields (#[serde(skip_serializing_if)]). Replaces post-hocserde_json::Valuemutation that broke JsonSchema codegen for TS clients. - CODE-7 LOW -
target_statustrimmed before validation soSome(" active ")no longer falls through with inscrutable error.
E. New methodology documentation
Section titled “E. New methodology documentation”docs/methodology/CROSS-REPO-WORKFLOW.ru.md- protocol for coordinating issues / fixes / feature requests across the multi-repo ForgePlan org (forgeplancore ↔marketplace↔forgeplan-hud↔ …). Codifies the 5 typical failure modes when 1→5 repos org grows without conventions, label taxonomy proposal, session-start inbox sweep protocol for AI agents, 4-phase implementation roadmap.
Deferred to v0.33.0 (tracked as GitHub issues)
Section titled “Deferred to v0.33.0 (tracked as GitHub issues)”- #296 - Architectural debt cluster (ARCH-1/3/4/5/6/7): ADR for status deprecation, explicit
git_rootparam, invert restore as shim, undo::restore projection exemption, methodology doc location, shared evidence_recipe helper. - #297 - Security follow-ups (SEC-5/6): TOCTOU window in find_git_root, sanitize_for_hint on activate error path.
- #298 - Code quality edge cases (CODE-5/6/8/9/10/11): paths with
(parens)in name, tab-indented bullets, walk_artifact_changes integration test, backticks-in-path edge.
Cross-repo follow-ups
Section titled “Cross-repo follow-ups”- marketplace#86 - docs update for
discover_findingworkaround references (now obsolete after #292 closure). - marketplace#79 - Phase E split:
forgeplan_render_canonical+forgeplan_export_raginforgeplan-brownfield-packplugin.
Tests, pipeline, scope
Section titled “Tests, pipeline, scope”- 3061 tests PASS on release candidate
feat/issues-286-288-289(was 3034 pre-merge on Epic branch + 27 inherited from dev v0.32.0 release prep via dogfood PR #299 /fix/issues-290-295-dogfood-findings). - cargo clippy clean, cargo fmt clean, scripts/smoke-test.sh PASS on release binary (14 kinds including 6 brownfield).
- 5 audit rounds + 1 final audit-r-release-candidate. Total findings closed inline: r4=13, r5=8, r6=2 (ARCH-H1 + PROB-069), r7=13, r-dogfood=8, r-release=4 → 48 closures, 0 net CRIT/HIGH deferred.
- ADR-014 new methodology artefact (
EVID-128 informs ADR-014). - PROB-069 flaky stress-test budget calibration closed (
EVID-129 informs PROB-069). - PROB-068 init/scan-import data-loss closure re-published as
EVID-130(was silently shadowed by a duplicateEVID-122ID in markdown projection - audit-r-release F1-CODE fix preserves the evidence).
Security (RED-LINE #10 compliance)
Section titled “Security (RED-LINE #10 compliance)”Dependabot alerts triage for v0.32.0, per RED-LINE #10 mandate:
- #33 -
devalueHIGH severity (Svelte DoS via sparse array deserialization, GHSA-77vg-94rm-hx3p, ecosystemnpm,website/package-lock.json). Status: addressed - bumped 5.6.4 → 5.8.1 in commit82d4634(PR #302). Limited exposure:website/is a static Astro SSG; the Rust CLI/MCP binaries do not ship JS. Post-bumpnpm auditreports 0 vulnerabilities. The GitHub alert will auto-close after this release reachesmain. - #3 -
lruLOW severity (IterMutStacked Borrows violation, GHSA-rhfx-m35p-ff5j, ecosystemrust). Status: accepted-with-justification - same as v0.28.0 / v0.29.0 / v0.30.0 / v0.31.0 changelogs. Transitive viatantivy 0.24→lance 4.0→lancedb 0.27;cargo update -p lruis a no-op because tantivy pinslru = "^0.12". Direct upgrade to 0.16.3 requires bumping tantivy major, cascading into Lance/LanceDB upgrades - out of scope for v0.32.0 patch. Impact in our context is LOW: Stacked Borrows is a Miri-only analyser concern, not a runtime exploit; the vulnerable code path is internal to tantivy’s caching layer, never reached by forgeplan user input; forgeplan is local-first CLI/MCP with no remote-trigger surface. Tracked for v0.33+ when LanceDB upgrade lands.
Hardening sprint (closes #305 + #306 + #308 - PR #309)
Section titled “Hardening sprint (closes #305 + #306 + #308 - PR #309)”Post-release-prep hardening sprint discovered + fixed 1 new bug + 1 docs drift + 3 audit follow-ups:
- PROB-074 / #305 - MCP server stale lance handle after external reindex. Long-running MCP daemon held LanceDB Dataset handles pinned to a manifest snapshot from server-start time; external
forgeplan reindexrewrote the lance fragments, leaving the cached UUIDs pointing at deleted files. Discovered live during v0.32 surface validation: 5/6 MCP tools failed withlance error: Not found: ...lance/data/<uuid>.lance;forgeplan_sessionworked (file-only, no LanceDB), validating ADR-003 file-first invariant. Fix: hybridis_stale_manifest_error(lancedb::Error downcast + string-match fallback) +LanceStore::refresh()viaTable::checkout_latest()+with_retry_on_staleretry loop (N=3, 100/250ms backoff) + 250ms refresh-rate debounce + MCPsafe_mcp_error_anyhowPRD-071-anchoredWait:hint emission onMutationError::RetryExhausted. 14 new tests. - #306 - Docs drift: MCP tool count 81 → 73.
scripts/check-mcp-tool-count.shcountedasync fn forgeplan_*declarations, which include 8 inline#[cfg(test)]test functions sharing the prefix (e.g.forgeplan_get_accepts_slug_or_display_id). Authoritative count via the#[tool(...)]macro = 73. Script regex changed to^[[:space:]]*#\[tool\(. Sanity floor bumped from< 10to< 30. Updated 20+ doc locations across README, CLAUDE, docs/, website/. - PROB-075 / #308 - PROB-074 audit follow-ups F-2/F-3/F-4 closed inline. F-2 retry budget +
MutationError::RetryExhaustedtyped variant (non_exhaustive - non-breaking). F-3 rate-limit vialast_refresh_ms: AtomicI64CAS-debounced at 250ms. F-4lancedb::Errordowncast first with string match as fallback (defense-in-depth -lancedbis a direct workspace dep,lance-coreskipped per existing version-skew rationale). F-6 (manifest-version-skew test gap) deferred to v0.33. - Adversarial 3-agent audit (security-expert + code-reviewer + architect-reviewer) found 13 findings (1 CRIT + 4 HIGH + 4 MEDIUM + 4 LOW). All 5 blockers closed inline by second coder pass. Architect’s “phantom hypothesis” concern (Concern #4 - “PROB-074 may not fire in production, LanceDB auto-recovers”) refuted by two independent signals: (a) live in-session reproducer of PROB-074 while preparing to invoke ADI itself via MCP; (b) ADI cycle output recommended exactly the H1+H2 hybrid we shipped, confidence: High. Captured as EVID-132.
Internal - sprint hygiene
Section titled “Internal - sprint hygiene”- CI workflow
.github/workflows/forgeplan-health.yml- replaced brokenmv .forgeplan .forgeplan-src && init && cp -r src/{kind}/ dst/(trailing-slash flat-copy bug + Epic #287 brownfield kinds never added) with singleforgeplan reindexcall (lance/is gitignored per ADR-003, reindex rebuilds from.forgeplan/<kind>/*.md). - PRD-002 stub superseded by ADR-006 - old broken CI workflow hid the stub for 2 months. PRD-002 scope was absorbed by ADR-006 + RFC-001 + PRD-013/018/040/041/042 long ago.
- Test count: 3084 / 0 failed (+17 over v0.31 baseline of 3067).
- Clippy: 0 warnings on default + semantic-search feature configs.
- Drift detector: 0 drift after sweep.
[0.31.0] - 2026-05-13
Section titled “[0.31.0] - 2026-05-13”Sprint headline: Wave 9 polish + 5-agent adversarial audit closure. Closes PROB-051 end-to-end (R_eff=0.80 grade B), addresses 19 audit findings (1 CRITICAL, 2 HIGH, 9 MED, 7 LOW) across logic, architecture, security, and test-quality dimensions. 26 commits, ~3500 LOC net.
Key security closures landing in this release:
- SEC-C1+C2 input gate -
validate_titlelifted toforgeplan-core::artifact::validationand gated at all 4 mutation paths (CLI new, CLI update, MCP new, MCP update). Pre-fix, 3 of 4 paths bypassed control-char + BIDI override rejection - Trojan Source titles could be planted viaforgeplan updateor MCP. - SEC-H1 output gate -
sanitize_for_hintpropagated to 8 CLI command print sites (list, search, blindspots, journal, decay, embed, promote, calibrate-estimate). LOG-001 closure for the v0.30 audit was partial; this completes it. - SEC-H2 HOME sanitizer hardening -
sanitize_text_withnow masks bare HOME at end-of-string or followed by a non-path boundary character. Pre-fix, error chains of the form"chdir failed: /Users/alice"leaked the username through MCP error JSON and Claude Desktop logs. - SEC-H3 MCP error sanitisation - all 40+
McpError::internal_errorcall sites route throughsanitize_error_chain. Pre-fix only 3 typed-error variants (StoreFatal,StoreTransient,FileNotFound) went through the sanitiser. - ARCH-C1 health JSON helper extract -
pub fn forgeplan_core::health::health_report_to_jsonis now the single source of truth for the CLI--json+ MCPforgeplan_healthwire shape. Eliminates the third recurrence of “two surfaces serialising the same domain object” (PROB-064 was the first, CR-001 the second). Helper also bundles SEC-M1 closure - every title/message/reason/issue/advisory string is sanitised before serialisation.
forgeplan release-notes [--since <ref>] [--until <ref>] [--output text|markdown|json] [--draft]- auto-generate Keep-a-Changelog–shaped release notes from artifacts
that changed between two git refs. Walks
git log --diff-filter=AMover.forgeplan/{prds,problems,evidence,rfcs,adrs,specs,epics,solutions}/, resolves each touched basename to a canonical artifact id (handles pre-merge slug form per SPEC-005 and post-mergeKIND-NNN), categorises by kind + status + security-link: PRD→Added, PROB→Fixed, EVID-on-security→Security, RFC/ADR/Spec/Epic→Changed, plain EVID→Internal. Quality gate (default): only records withstatus==activeorr_eff_score > 0are emitted;--draftwaives. Companion MCP toolforgeplan_release_notesreturns the same JSON shape. Closes v0.31.0 Wave 4 MAJOR-3 (manual CHANGELOG sync pain) - the feature self-uses at the next release. Refs: EVID-123.
- auto-generate Keep-a-Changelog–shaped release notes from artifacts
that changed between two git refs. Walks
forgeplan health --strictflag - exit 1 if verdict ∈ {NeedsAttention, Unhealthy} OR any of {orphans, blind_spots, active_stubs, at_risk} > 0. Designed for CI gates that want a single boolean signal (defaultforgeplan healthalways exits 0 - advisory tool). Empty workspaces (verdict=Empty) exit 0 - “no artifacts” is not a critical signal. Advisory-only signals (phase mismatches alone) stay advisory and exit 0, consistent with PROB-063 (advisory ≠ critical). JSON mode (--json --strict) adds a parseableexit_codeinteger field so CI scripts can branch on a single value instead of recomputing the gate from counts. Backward compatible: legacy--ci/--fail-onthresholds path untouched.advisory_phase_mismatchesJSON key вforgeplan health --jsonoutput (alias for legacyphase_mismatches, matches MCPforgeplan_healthcross-surface). Non-breaking: legacy key retained для backward compat. Future deprecation possible в major version bump. Closes PROB-064 advisory/critical mismatch reporting drift between CLI and MCP surfaces. Refs: EVID-118.
- PROB-067 -
forgeplan_newID-counter race across parallel git worktrees. Two workers in sibling worktrees of the same repo invokingforgeplan_newsimultaneously could each computemax(NNN)+1and produce duplicateEVID-NNNnumbers (or silently overwrite a pre-existing artifact body, per the v0.31.0 sprint incident with EVID-118/EVID-119). Fix combo Option B + Option D:- Cross-worktree per-kind lock - id allocation now serializes
through
<git-common-dir>/forgeplan/id-<KIND>.lock(shared across all worktrees of the same repo) with graceful fallback to a workspace-local lock when no git common-dir is available (fresh clones, non-git directories). Per-kind granularity keepsnew prdandnew evidenceallocations independent. - Post-write collision detection - after the projection write
the allocator re-checks that no sibling file owns the same
{ID}-prefix; on collision the file is rolled back and the allocation retries with the next number (cap 5, panic above to surface a broken lock instead of silent corruption). Bothforgeplan new(CLI) andforgeplan_new(MCP) call the newforgeplan_core::artifact::id_alloc::allocate_and_create_artifactentry point. New regression coverage: 3 unit tests inforgeplan_core::artifact::id_alloc::tests+ 2 CLI process-level stress tests incli_prob_067_id_race.rs(5 parallelnew evidence
- 10 mixed-kind
new evidence/new note). Refs: PROB-067, EVID-TBD.
- Cross-worktree per-kind lock - id allocation now serializes
through
Security
Section titled “Security”-
Bump openssl 0.10.78 → 0.10.79 - closes two GitHub Dependabot alerts on the transitive dependency tree (reqwest → rustls → lance):
- #27 (HIGH) - rust-openssl undefined behavior в
X509Ref::ocsp_respondersдля non-UTF-8 OCSP URLs (CVE pending). - #28 (MEDIUM) - heap buffer overflow при AES key-wrap-with-padding encryption.
- #27 (HIGH) - rust-openssl undefined behavior в
-
Bump mermaid 11.14.0 → 11.15.0 в
website/- closes four GitHub Dependabot alerts on the documentation site dependency tree:- #32 (MEDIUM, GHSA-6m6c-36f7-fhxh) - Gantt charts vulnerable to infinite-loop DoS.
- #31 (MEDIUM, GHSA-ghcm-xqfw-q4vr) - improper sanitization of
classDefin state diagrams (HTML injection). - #30 (MEDIUM, GHSA-87f9-hvmw-gh4p) - improper sanitization of configuration (CSS injection).
- #29 (MEDIUM, GHSA-xcj9-5m2h-648r) - improper sanitization of
classDefsin diagrams (CSS injection).
-
Bump uuid 11.1.0 → 14.0.0 в
website/- closes Dependabot alert #26 (MEDIUM, GHSA-w5hq-g745-h8pq) - missing buffer bounds check вv3/v5/v6when buf is provided. Major bump landed via mermaid 11.15.0 broadening its peer range (^11 || ^12 || ^13 || ^14); npm resolved to latest 14.0.0.npm auditreports 0 vulnerabilities post-bump. -
lru0.12.5 alert deferred (#3, LOW, GHSA-rhfx-m35p-ff5j) -IterMutStacked Borrows violation. Blocked upstream:tantivy v0.24.2pinslru = "^0.12.0", andlance v4.0.0pinstantivy = "=0.24.1". Bump requireslance ≥4.1(not yet released с newer tantivy). Justification: LOW severity, Forgeplan не используетlru::IterMutнапрямую - code path is tantivy-internal text index iteration, not exposed через our search API. Tracked для re-triage on next lance upstream release. -
cargo-denyCI gate added - new.github/workflows/security.ymlrunscargo deny check advisories licenses bans sourceson push to dev/main, on PRs against dev/main, и weekly cron (Mon 07:23 UTC). Config:deny.tomlat repo root. Initial rolloutcontinue-on-error: true- establishes baseline, then flips to blocking after first clean run. Closes RED LINE #10 mechanism gap (Dependabot mandate had no automated enforcement before release). Schema is cargo-deny 0.19+ (action @v2.x).
Internal
Section titled “Internal”- +33 CLI integration tests для 16 previously-untested commands
in
crates/forgeplan-cli/tests/cli_uncovered_coverage.rs:embed,tree,git_sync,log_cmd,context,promote,reopen,scan_import,setup_skill,tag,recall,remember,migrate,migrate_dry_run,reconcile_ids,ci_assign_id. Thewatchcommand is intentionally deferred - long-running foreground watcher is untestable viaassert_cmdбез SIGTERM handling и timing races (module-level unit tests cover the core debouncer logic). - +59 MCP tool contract tests в
crates/forgeplan-mcp/tests/integration_full_coverage.rs- coverage expanded 14 → 61 unique handlers (100% user-facing tools reachable через JSON-RPC duplex transport). LLM-dependent group (capture / reason / decompose / generate) pinsis_error=truecontract; other reachability-only assertions tracked for future tightening (PROB-065 candidate). - +6 smoke-test.sh operations (13 → 19):
tree,progress,claim/releasecycle,dispatchplanner JSON,phase-advanceround-trip. Runtime budget ≤3s on warm cache. - Shared MCP test fixture extracted to
crates/forgeplan-mcp/tests/common/mod.rs(McpFixture + helpers) - removes ~200 LOC of verbatim duplication betweenintegration_e2e.rsиintegration_full_coverage.rs.
[0.30.0] - 2026-05-06 - defensive sprint: security trio + cache self-healing + MCP transport parity + Wave 3 paper cuts
Section titled “[0.30.0] - 2026-05-06 - defensive sprint: security trio + cache self-healing + MCP transport parity + Wave 3 paper cuts”Highlights (11 PROBs closed in single defensive sprint):
- Security: PROB-053 shell-execution gate (CWE-78/94 default-deny + escape_debug warning), PROB-052
which_in_pathTOCTOU/CWE-426 hardening (canonicalize + perm gate + symmetric override paths), PROB-054produces_atprompt-injection-via-filesystem validator (CWE-94/OWASP A03) - Trust calculus: PROB-057 R_eff cache self-healing on link/unlink/activate (closes 4-consumer stale-state leak), PROB-058 (4/6 ACs) MCP transport parity для cache invalidation + score lock + concurrent-writer regression test
- Quality: PROB-051 (4/7 items) phase-fold unification + perf scans + module rustdocs, PROB-056
partial_verdictfield surfaces phase-fold contract в type system, PROB-032 search score breakdown coherent с total - Paper cuts: PROB-027/030/033 verified-already-closed via E2E + PROB-038 NEW validator strip pipeline (HTML comments + fenced code + inline backticks), PROB-028 reindex resilience против Phase-1 abort, PROB-059 body↔links drift validate warning (strict
## Related Artifactsparser)
Cross-surface symmetry pattern emerged 7× в этом sprint - fixing security primitive on primary path while leaving symmetric override paths unguarded. Now baseline audit prompt asks “grep ALL consumers” before declaring closure.
Test count: 1977 → 1489 lib + integration suites = ~1995 tests (+ regression coverage по каждому PROB).
Quality gates: cargo fmt --check, cargo clippy --workspace --all-targets --features test-helpers -- -D warnings, cargo test --workspace --features test-helpers - all clean across all 38 suites, 0 failures.
Deferred to v0.31.0+:
- PROB-049 follow-up retry-loop (typed-error refactor через scoring path)
- PROB-051 MEDIUM/LOW deferred items (L-M1/M2/M3, P-M1/M2, P-L5, D-LOW-2/4)
- PROB-058 deferred ACs (driver-trait parity, r_eff_local perf bound)
- PROB-059 follow-ups:
forgeplan reconcileinteractive command, workspace-wide drift cleanup
Added (Validation - PROB-059 closure, body↔links drift warning)
Section titled “Added (Validation - PROB-059 closure, body↔links drift warning)”-
PROB-059 ✅ -
body-links-driftvalidate warning (SHOULD-level). Newbody-links-driftrule вvalidation::base_rules()flags artifacts whose body## Related Artifactstable mentions IDs not present в frontmatterlinks:array. Source-of-truth-divergence pattern: agent authors body table claiming relations, forgetsforgeplan link X Y --relation ..., и Lance index sees isolated node despite markdown looking linked.Strict parser by design: targets only formal
## Related Artifactstable rows. Free-text mentions (“see also PRD-005”) elsewhere в body are ignored - incidental references shouldn’t trigger drift warnings. HTML comments, fenced code blocks, и inline backtick code stripped via sharedstrip_non_prose_for_leakagehelper (DRY против PROB-038).Warning message includes missing IDs +
forgeplan linkcommand template:~ [SHOULD] body-links-drift: Body's `## Related Artifacts` tablementions PRD-005, RFC-001 but frontmatter `links:` array doesn'treference them. Run: forgeplan link <this-id> <target> --relation<informs|based_on|refines|...>Tests: +6 unit tests (
extract_related_artifacts_table_ids_*andextract_frontmatter_link_targets_*) covering happy path, free-text exclusion, HTML comments stripped, no-section empty, frontmatter parsing.Deferred к follow-up:
forgeplan reconcileinteractive command (out of scope для v1), workspace-wide--applybulk fix, template hint в frontmatter,--strictflag для CI.
Fixed (Wave 3 batch closure - PROB-027 + PROB-030 + PROB-033 + PROB-038)
Section titled “Fixed (Wave 3 batch closure - PROB-027 + PROB-030 + PROB-033 + PROB-038)”-
PROB-027 ✅ verified-already-closed -
forgeplan reindexnow rebuilds LanceDB from scratch whenlance/dir missing. Closure shipped в earlier sprint (LanceStore::init()instead ofopen()inreindex.rs:35). E2E verified:rm -rf .forgeplan/lance && forgeplan reindexrecreates table + repopulates от .md files. -
PROB-030 ✅ verified-already-closed - BM25 prefix search regression. Closure shipped:
combined_scoreusesbm25_norm.max(keyword_score)at smart.rs:153 (substring fallback already в place). E2E verified:forgeplan search "auth"returns 2 PRDs titled “Authentication …” с kw=0.80. -
PROB-033 ✅ verified-already-closed -
forgeplan new evidenceno longer blocked by session state machine on fresh workspace. E2E verified: fresh init → new prd → new evidence works without--force. -
PROB-038 ✅ NEW closure - Validator false-positive on tech names в HTML comments. Pre-PROB-038
find_tech_leakage()scanned raw text including<!-- -->comments и code fences. Template guidance comments с phrases like “DON’T leak React/Django/AWS into FR” были false-flagged.Fix: new private
strip_non_prose_for_leakage()helper performs three passes before tech-leakage scanning:- Strip HTML comments (
<!-- ... -->, single и multi-line) replacing с blank lines чтобы preserve line numbers - Strip fenced code blocks (```…```)
- Strip inline backtick code (`Tech`)
Real prose leakage в FR/NFR continues to trigger - only template guidance + code/quote contexts are immune.
E2E impact: fresh PRD via
forgeplan new prd "Auth Test"validate output went от 7 false positives (aws, django, docker, oauth2, postgresql, react, redis, rest) → 1 residual (OAuth2mention в template’s NFR-003 example row - actual prose, not in scope of this fix). 86% reduction.Tests: +5 unit tests in
validation::checks::tests(single-line / multi-line HTML / fenced code / inline backticks / regression guard). Suite: lib 1477 → 1483 PASS, 0 failures across 38 suites. - Strip HTML comments (
Fixed (Search UX - PROB-032 closure, score breakdown lies)
Section titled “Fixed (Search UX - PROB-032 closure, score breakdown lies)”-
PROB-032 ✅ -
forgeplan searchscore breakdown coherent с total. Pre-PROB-032 the display showedkw=0.0 sem=0.0 r=0.0 g=0.0while total score was non-zero (e.g. 0.57) - violating “sum ≈ total” expectation и lying к user about ranking composition.Root cause:
SmartSearchResultcarries TWO keyword channels (keyword_scoresubstring +bm25_scoreBM25).combined_score()usesmax(bm25, keyword_score)as base, but CLI display showed толькоkeyword_score. When match was via BM25 tokenization (e.g. “auth” matches “authentication” via stemming но not as substring),keyword_score= 0 hence misleadingkw=0.0display.Two-part fix в
crates/forgeplan-cli/src/commands/search.rs:- Display
max(bm25_score, keyword_score)so the visible value reflects the actually-contributing channel (matches whatcombined_score()consumes) - Bump precision
{:.1}→{:.2}so contributions of 0.02–0.09 no longer round-down к 0.0
Real E2E verified: query “api error” against fresh workspace now shows
kw=0.36 sem=0.00 r=0.00 g=0.00matching total 0.36 (was pre-fixkw=0.0 ... total 0.36).Architectural pattern same as PROB-029 verdict aggregator: hidden fold logic + display path that lies about the fold. When scoring formula uses
max()/mean()/weighted_sum()over multiple channels, display MUST show the actually-contributing channel(s). - Display
Fixed (Reindex - PROB-028 closure, reindex resilience)
Section titled “Fixed (Reindex - PROB-028 closure, reindex resilience)”-
PROB-028 ✅ -
forgeplan reindexresilience против Phase-1 abort. v0.17.1 introduced Phase 2/3 orphan trim (rows whose.mddisappeared, и orphan relations cascading from trimmed artifacts) but the trim path was unreachable on workspaces с ANY title-divergent record. Phase 1 propagated the first per-file error via?и aborted the entire reindex, leaving Phase 2/3 dead.Real-world bug observed today: project workspace had orphan PRD-001 / SPEC-001 from a scan-import smoke test earlier в session.
forgeplan reindexerrored on a single SESSION-2026-04-06 record (FileNotFoundfromsync_body_from_filebecause frontmattertitle:on disk diverged from DB-stored title), aborted, и left the orphans untrimmed. Workspace stayed unhealthy для weeks despite the trim logic existing.Two-part fix в
crates/forgeplan-cli/src/commands/reindex.rs:- Pass file’s parsed title (от frontmatter) к
sync_body_from_fileinstead of DB-storedrecord.titleso its internal path computation matches the actual file. Title divergence no longer triggersFileNotFound. - Per-file errors now log via
eprintln!("WARN ...")+errors += 1continueвместо?-abort. Phase 2/3 orphan trim ALWAYS runs after the per-file loop completes, regardless of how many individual files failed.
Tests: +2 CLI integration tests (
cli_reindex_resilience):reindex_trims_orphan_after_md_file_deleted- PROB-028 AC-5 verbatimreindex_continues_after_per_file_error_and_still_trims_orphans- recreates project-workspace bug shape
Real E2E: project workspace orphans (PRD-001, SPEC-001) trimmed cleanly после fix;
forgeplan healthnow reports clean (0 orphans).Lesson: when introducing a new pipeline phase, audit ALL paths через which the previous phase can short-circuit.
?propagation в aforloop is the most common offender - converting кmatch … { Ok ⇒ continue; Err ⇒ log + continue }is the pattern. - Pass file’s parsed title (от frontmatter) к
Refactor (Architecture - PROB-056 closure, leaky verdict abstraction)
Section titled “Refactor (Architecture - PROB-056 closure, leaky verdict abstraction)”-
PROB-056 ✅ -
HealthReport.partial_verdictfield surfaces phase-fold contract в the type system. Pre-PROB-056 the singleverdictfield silently switched semantic between callers:health_report()populated it as partial (phase_mismatches=0),health_report_with_phase()populated it as folded (PROB-051 closure). External library consumers callinghealth_reportdirectly couldn’t tell от the type signature что theirverdictwas partial.Fix: new
partial_verdict: Verdictfield onHealthReportalways carries the value computed с phase_mismatches=0.verdictcontinues carry the “best-known” value (folded когда available viahealth_report_with_phase, partial otherwise). External library consumers tracking additional context MUST consumepartial_verdictas base для their owncompute_verdict_with()recomputation.Wire format:
verdictJSON field unchanged. Newpartial_verdictappears in--jsonoutput (additive - non-breaking). CLI/MCP code paths unchanged - both already route throughhealth_report_with_phase(PROB-051 closure) soverdictis the right value to consume.Tests: +2 unit tests validating invariants:
health_report_partial_verdict_equals_verdict_when_no_phase- legacy path both fields equalhealth_report_with_phase_partial_verdict_invariant-partial_verdictALWAYS equals legacyverdictдля same workspace, even when post-foldverdictdiverges
Design choice: additive (new field) vs hard rename (
verdict→partial_verdict). Picked additive - hard rename would force all CLI/MCP/external code to migrate while still leaving the dual-semantic foot-gun on the JSON wire field name. Additive split letsverdictkeep “best-known” user-facing semantic (= what consumers actually want) иpartial_verdictbecomes explicit advanced-case access. Mirror of PROB-049 typed-errors lineage (typed alternative alongside legacy surface, incremental migration).Suite: lib 1475 → 1477 PASS, 0 failures across 38 suites.
Fixed (Security - PROB-054 closure, prompt-injection-via-filesystem)
Section titled “Fixed (Security - PROB-054 closure, prompt-injection-via-filesystem)”-
PROB-054 ✅ -
produces_atprompt-injection validator (CWE-94 / OWASP A03). Pre-PROB-054Step.produces_atpath was spliced into theclaude --printnatural-language prompt body viato_string_lossy()без character validation:Write output to `<produces_at>` using the Write tool.A path containing a backtick (
reports/backdoor.md) closed the markdown code-fence и turned everything after into prompt content the agent treated as authoritative instruction. Same class for$(variable expansion),;(command separator hint),\n/\r(line-break injection).This was a separate attack surface от argv injection (PROB-050 A-15 closure). The argv guard и the prompt-body guard are independent concerns even when validating the same field.
Fix: new
validate_produces_at_chars(&Path) -> Result<(), String>helper inclaude_print.rswith conservative allowlist regex^[A-Za-z0-9._/-]+$(alphanumeric, dot, underscore, forward slash, hyphen). Wired into both:assemble_prompt()- validates BEFORE splicing into prompt bodyadd_dir_for_produces_at()- symmetric guard so argv splice fails on the same input
Error messages use
escape_debugfor log-injection defense-in-depth (mirrors PROB-053 shell-exec warning pattern).Tests: +6 unit tests (5 char-class + 1 symmetric add_dir guard; mandate was 3). Suite: lib 1469 → 1475 PASS.
Fixed (Trust calculus + Performance - PROB-051 partial closure, Wave-1 Round 5 audit deferred)
Section titled “Fixed (Trust calculus + Performance - PROB-051 partial closure, Wave-1 Round 5 audit deferred)”-
PROB-051 partial ✅ - phase-fold unification + perf scans + module docs (Roadmap Tier 2 v0.30.0 Wave 1.3). Closes 4 of 7 deferred Round 5 audit items in single sprint:
-
L-H3 phase-fold unification: pre-PROB-051 the MCP
forgeplan_healthhandler computed phase mismatches inline (читаяread_phaseдля each active artifact) и folded the count into the verdict, while CLIforgeplan healthignored phase tracking entirely- same workspace could return DIFFERENT verdicts через CLI vs MCP.
New
forgeplan_core::health::health_report_with_phase(store, ws)folds the mismatch count viacompute_verdict_withand is consumed by both CLI и MCP, guaranteeing identical verdicts.
- same workspace could return DIFFERENT verdicts через CLI vs MCP.
New
-
P-H1 single-scan: pre-PROB-051 MCP forgeplan_health called
store.list_records(None)twice (once insidehealth_report, once again for phase mismatch loop). Post-PROB-051 the new function does a single scan и passes records через to both consumers - eliminates the duplicate query on every MCP health call. -
P-H2 parallel
read_phase: pre-PROB-051 phase reads ran sequentially per active artifact (~1ms each on disk-cached fs). Post-PROB-051 usesfutures::stream::iter().buffer_unordered(16)so a 200-active-artifact workspace doesn’t pay 200 sequential syscalls. -
D-H1
projection/mod.rsmodule docs: 50+ line//!block introducing ADR-003 file-first invariant, helper categories (Create/Update/Delete/Link/Re-render), MutationContext rationale, typed-error semantics. Closes the discoverability gap surfaced by documentation auditor. -
D-H2
health/mod.rsmodule docs: 60+ line//!block describing public surface (legacyhealth_reportvs phase-awarehealth_report_with_phase), 4-levelVerdictaggregator, performance posture, file layout. IncludesVerdict::Emptyrationale (PR-E Round 6 closure context).
Side benefit: CLI
forgeplan healthnow renders aPhase mismatches (N)advisory section in text mode andphase_mismatches[]array в--jsonoutput - operators using CLI no longer have to switch to MCP to see this advisory data.Tests: +2 lib unit tests (
health_report_with_phase_matches_legacy_for_empty_workspace,health_report_with_phase_matches_legacy_when_no_mismatches) - guard against future drift between the two folding paths. Suite: lib 1467 → 1469 PASS, 0 failures across 38 suites.Deferred to follow-up (PROB-051 backlog): L-M1 (at_risk in VerdictThresholds), L-M2 (truncation order), L-M3 (boundary tests), P-M1/M2 (perf items needing benchmark scaffold first), P-L5 (config caching), D-LOW-2/4 (doc cleanup), Round 4 M1 (typed-error sanitisation).
-
Fixed (Security - PROB-052 closure, Round 7 audit)
Section titled “Fixed (Security - PROB-052 closure, Round 7 audit)”-
PROB-052 ✅ -
which_in_pathTOCTOU + symlink-follow + perm gate hardening (CWE-367 + CWE-426). Pre-PROB-052 the PATH-search helper didis_file()(silently follows symlinks), nocanonicalize, no exec-bit / write-bit checks, no parent-directory permission check. Round 6 audit MED-1 flagged the function as exploitable on the default Homebrew posture (/usr/local/bin0o775 group=admin - any admin user can plant a hijacking binary).Fix: new
pub(super) resolve_safe_pathhelper:canonicalizeresolves symlinks to the real target (eliminates the operator-time swap window; shrinks residual TOCTOU to two adjacent syscalls).- On Unix, rejects binaries with
mode & 0o022 != 0(group-write OR world-write) AND parent dirs with the same gate. - Windows skips the perm gate (ACL out of scope, documented) but still applies canonicalize + non-file rejection.
- Empty PATH entries (POSIX
:interpreted as.) are explicitly skipped - no implicit cwd lookup (hijack vector for cloned hostile repos).
Round 7 audit (2026-05-06) closures:
- HIGH-1 (consumer-side bypass): pre-Round-7 the override
branches in
AgentDispatcher::resolve_claude_binary,PluginDispatcher::resolve_binary, иresolve_forgeplan_binaryused bareis_file(). Round 7 routed all 4 surfaces throughresolve_safe_pathso the gate applies symmetrically - operator config settingclaude_binary = /usr/local/bin/claude(group=admin Homebrew dir) is now rejected just like the PATH-resolved case. - MED-1/MED-2 log-injection:
tracing::warn!rejection messages now useescape_debugmirroring the PROB-053 shell-exec warning pattern. Neweprintln!operator-visible channel surfaces rejections безRUST_LOG=warn. - HIGH-3 docstring precision: mode-bit gate explicitly named (0o020 = group-write, 0o002 = world-write); setuid/setgid/sticky out of scope documented.
Tests (7 new unit tests in
helpers.rs):- canonicalize symlink to real target
- reject group-writable binary
- reject group-writable parent dir
- reject empty PATH entry (skip implicit cwd)
- reject group-writable override (HIGH-1 closure)
- canonicalize safe override (HIGH-1 closure)
- Windows skip permission gate (cfg(not(unix)))
All tests use
DISPATCH_ENV_LOCKfor cross-test PATH isolation (PROB-050 A-6 pattern).AC tracking: AC-1/3/5/6 closed; AC-2 partial (file write-bit clause closed; parent-dir ownership clause re-scoped - single-user threat model bounded by parent-mode gate; multi-user shared workstation deferred until trigger fires); AC-4 caching re-scoped as “no caching by design” - dispatcher recreated per
dispatch()call, per the AC’s own MUST-NOT clause (“MUST NOT cache the resolved path indefinitely”) which aOnceCell<PathBuf>would violate.Files touched:
helpers.rs(+130 src + +160 tests),agent_dispatcher.rs::resolve_claude_binary,plugin_dispatcher.rs::resolve_binary. Suite: lib 1464 → 1467 (+3 net; sprint shipped 7 - exceeds AC-5 mandate of 3).
Added (Security - PROB-053 / PRD-074 closure)
Section titled “Added (Security - PROB-053 / PRD-074 closure)”-
PROB-053 / PRD-074 ✅ -
Delegation::Commandshell-execution gate (CWE-78 / CWE-94 default-deny). The dispatcher refusesdelegate_to: commandsteps unless ONE of the two opt-ins is present:- CLI flag
--allow-shellonforgeplan playbook run(per invocation, dedicated shell-exec gate; independent от existing--yesADR-009 confirmation). - Workspace config
[playbook] allow_shell = trueв.forgeplan/config.yaml(trusted-local pre-approval; do NOT set в repos that fetch playbooks from network/marketplace).
User-visible warning: every
Delegation::Commandstep prints! shell-exec: <argv...>to stderr (eprintln, NOT tracing::warn) с full argv (escape_debug-sanitized to bound CWE-117 / CWE-150 terminal injection) перед spawning. Operator-readable regardless ofRUST_LOG. Bound at 4 KiB renders pathological argv с(truncated, original argv N args)marker.Config-only auto-approval banner: when the gate opens via config opt-in (CLI flag absent), an additional
!! shell-exec: AUTO-APPROVED via [playbook] allow_shell=truebanner fires once at run start, surfacing post-hoc that the run inherited shell-exec permission from a checked-in dotfile rather than this invocation.MCP parity:
forgeplan_playbook_runlearnsallow_shell: boolparameter (defaultfalse); tool description documents the requirement so agent integrations discover the gate by readingtools/listrather than by trial-and-error.Config parse errors are no longer silent: pre-Round-7 a malformed
.forgeplan/config.yamlwould silently causeworkspace_allow_shellto default кfalse, regressing trusted-local workflows без warning (same failure-mode class as PROB-035 / PROB-039). Now CLI emitsWarning: failed to read workspace config ...to stderr; MCP emitstracing::warn!with structurederrorfield.Reference playbooks:
marketplace/playbooks/release.yamlheader documents the--allow-shellrequirement.audit.yaml+brownfield-docs.yamluse Plugin/Skill dispatchers (not Command) и не affected.Migration for operators: existing CI scripts using
forgeplan playbook run X --yesдля shell playbooks must add--allow-shell(or set[playbook] allow_shell = trueonce в workspace config). The error path emits aFix:hint pointing к the flag.BREAKING (forgeplan-core lib only):
validate_command_delegate_security(step, allow_shell)parameter renamed fromyes_flag(semantics changed - now a dedicated shell-exec opt-in, not the blanket —yes).SecurityError::ShellRequiresYesrenamed →SecurityError::ShellRequiresAllowShell(variant tag matches the flag name;#[non_exhaustive]already required_ =>arms). Deprecatedpub const ShellRequiresYesplaceholder shim allows one-release migration window.ExecutorConfiggainedallow_shell: boolfield (separate from existingyes_flag; defaults кfalse). Library consumers constructingExecutorConfigdirectly must add the field.Configstruct gainedplaybook: Option<PlaybookConfig>field (defaults кNone- existing workspaces parse identically).
Quality gates: cargo fmt clean, clippy
--workspace --all-targets --features test-helpers -- -D warningsclean, 1985 tests pass / 0 fail (+8 от Round 7 audit closures: shell-exec warning escape, full-argv render, pathological truncation, PlaybookConfig serde round-trip).Audit Round 7 (3 parallel adversarial agents - architect, code-reviewer, security): 9+ findings closed in this PR (HIGH-A: yes-shadow + bad hint, HIGH-B: config-only banner, HIGH-D: silent config swallow, HIGH-E: MCP description, HIGH-F: terminal-injection sanitization, HIGH-C: 4-cell test matrix, MED-C: variant rename, MED-D: full argv). Deferred to follow-up PROBs: F3 ForgeplanCore::Ingest path-traversal, F4 MCP stderr trust asymmetry, MED-E
ExecutorConfigfield coupling, MED-1 ExecutorConfig invariant, plus LOW-1..LOW-4 cosmetic. - CLI flag
Fixed (Trust calculus - PROB-057 / PRD-075 closure)
Section titled “Fixed (Trust calculus - PROB-057 / PRD-075 closure)”-
PROB-057 / PRD-075 ✅ - R_eff cache self-healing on link/unlink/activate. Discovered during the PROB-053 PR review session:
forgeplan link/forgeplan activatepreviously emitted aHint::info("verify R_eff")pointing atforgeplan score <ID>but never invoked the recompute. Cachedr_eff_scorein LanceDB stayed stale until a manualscore/score-allrun, leaking stale values to four downstream consumers -forgeplan getUI (get.rs:80), search filter--has-evidence(search/filter.rs:93-94), F-G-R quality grading (scoring/fgr.rs:150) и LLM ADI prompt context (llm/reason.rs:218). User-observed reproducer: PRD-074 reportedR_eff: 0.00fromgetwhilescorereturned 1.00 Adequate against the same EVID-104 link.Fix: new shared helper
forgeplan_core::scoring::sync_score_target(store, id) -> f64encapsulatesr_eff_recursive+update_r_eff_scoreand is called synchronously after eachlink,unlink,activatemutation.score/score-allroute through the same helper to keep one canonical “recompute + persist” path. Failure during auto-recompute is non-fatal - the mutation succeeded, andforgeplan score-allremains the authoritative full-tree reconciliation surface.Scope: target artifact only. Parent / ancestor walk left to
score-all(PRD-075 §Non-Goals - bounded mutator latency). Schema unchanged: workspaces from v0.29 read identically.r_eff_recursivesignature preserved for downstream callers.Hints updated: post-mutation hints now point to
forgeplan score-all(parent reconciliation) instead of the now-redundantforgeplan score <ID>per-target rescore.Tests: 3 new unit tests cover persistence on no-evidence path, stale-cache overwrite (regression guard for PROB-057), and unknown-id error surfacing. Full workspace test suite stays green (no regressions across the 1985-test baseline).
Changed (Trust calculus - PROB-058 partial closure, Round 9 audit)
Section titled “Changed (Trust calculus - PROB-058 partial closure, Round 9 audit)”-
PROB-058 AC-2/4/5/6 closed + MCP transport parity (Round 9 adversarial audit, 2 parallel agents). Cache self-healing now extends to the MCP transport that LLM-orchestrated agents actually use:
- MCP
forgeplan_link/forgeplan_activateacquire the workspace lock and callsync_score_targetafter mutation; CLI parity restored. Hint strings updated кforgeplan_score_all(FR-009). - MCP
forgeplan_score- pre-Round-9 this tool computedr_eff_recursivefor display but never persisted the recomputed value (latent bug since MCP launch). Now routes throughsync_score_targetto write the cache. forgeplan score/score --allacquireopen_store_locked()so concurrent CLI/MCP mutators serialize correctly. Trade-off:score --allnow holds the lock for the entire batch - на dense graphs (>200 artifacts × deep deps) this can starve concurrent callers past the 30 s lock timeout. PROB-058 AC-3 (r_eff_localvariant) tracks the bound.- AC-2 regression test: real concurrent-writer test added
(
parallel_score_all_invocations_serialize_via_workspace_lock) spawning twoforgeplan score --allprocesses via OS-level fs2 advisory lock - closes the methodology gap that motivatedfeedback_meta_tooling_discipline.md. - AC-4 hint contract: 3 negative tests (link / unlink / activate)
use line-shape match (not substring contains) to prevent
concatenated drift like
score-all && score <ID>from passing. - AC-5 threat model: PRD-075 §“Threat Model - Mutation Latency Side-Channel” documents the timing oracle posture for multi-tenant/MCP-shared deployments, with explicit trigger-to-revisit conditions.
- AC-6 docstring:
sync_score_targetrewrites scope to distinguish evidence collection (bidirectional), dependency recursion (descendant-only), and transitive parent rescore (out of scope) - fixes Round 9 HIGH-3 factual mismatch with implementation.
Deferred to follow-up sprint (PROB-058 AC-1, AC-3): driver-trait parity for
sync_score_target(требуетr_eff_recursivesignature rework across entire scoring pipeline) иr_eff_localperf-bound variant (нужен benchmark scaffold). Current workspace size profile (≤300 artifacts) does not exhibit the FR-005 100ms budget violation in practice.Tests: +3 negative hint tests, +1 concurrent-writer regression test, +1 cycle-termination test, +1 malformed-id rejection test. Suite total: 1985 baseline → ~1995+ tests pass, 0 fail across all gates (fmt / clippy —deny-warnings / workspace test).
- MCP
[0.29.0] - 2026-05-05 - verdict aggregator + typed errors + claude —print refactor + CWE-426 hardening
Section titled “[0.29.0] - 2026-05-05 - verdict aggregator + typed errors + claude —print refactor + CWE-426 hardening”Bundles 10 merge-PRs (#239..#248) since v0.28.0 (2026-05-03). Five load-bearing themes:
(1) PROB-029 - typed Verdict aggregator on HealthReport -
forgeplan health теперь возвращает структурированный Empty / Healthy / NeedsAttention / Unhealthy verdict с configurable thresholds, MCP +
CLI surfaces консистентны, banner driven off verdict (eliminates the
pre-fix “Project healthy!” disagreeing with next_actions printed
above it). Round 4 + Round 5 + Round 6 audit closures: _next_action
ladder checks active_stubs + possible_duplicates + phase_mismatches
before fallthrough; Verdict::Empty is now a proper 4th variant
(was deferred at Round 5 via manual overrides; resolved by-construction
in Round 6).
(2) PROB-049 - typed errors H-class -
MutationError::StoreError split into StoreTransient (recoverable) +
StoreFatal (not recoverable). MutationContext<'_> introduced for
all 17 projection helpers, replacing separate (workspace, store)
arguments. # Errors rustdoc added to all 17 helpers. Both new public
types are #[non_exhaustive]. BREAKING for direct library consumers
of forgeplan-core; CLI/MCP surfaces unaffected. Round 6 audit honesty
note: is_recoverable() is intentionally infrastructure-only in
v0.29.0; first MCP retry-loop consumer wires in v0.30.0.
(3) PROB-050 - claude --print dispatch refactor (A-4..A-15) -
Single source of truth in playbook::dispatch::claude_print:
invoke() (full 9-step orchestration), build_argv() (argv +
both security gates inline), parse_envelope() (UTF-8-trimmed JSON
decode - silent divergence закрыт), format_timeout_msg() (uniform
second/millisecond rendering - Round 6 audit closure: sub-second
durations now render Nms instead of truncating to “0s”).
helpers::which_in_path consolidated; 3 identical local copies
removed. DISPATCH_ENV_LOCK (cfg(test)) closes cross-test PATH-
mutation race. Argv shape byte-identical pre/post; agent diagnostic
strings unified to plugin’s pre-existing format (Round 6 audit
honesty correction - pre-claim of “no behaviour change” was incorrect
about diagnostic strings while correct about argv shape).
(4) PROB-050 A-14 - CWE-426 binary-substitution mitigation fully closed (Round 6 broadens original mitigation):
- Env-var path:
$FORGEPLAN_CLAUDE_BIN/$FORGEPLAN_BINgated behind#[cfg(test)]- release binaries do not contain the symbol. - Struct-API path (Round 6 audit HIGH-1):
pub claude_binaryfieldpub with_claude_binarybuilder - equivalent compile-time injection surface - demoted to private field; builder gated behindcfg(any(test, all(feature = "test-helpers", debug_assertions))). Symmetric acrossAgentDispatcher+PluginDispatcher. Real E2E:strings target/release/forgeplan | grep -c FORGEPLAN_CLAUDE_BIN→0(symbol absent from release binary).
(5) PROB-051 - Wave-1 audit closures (cherry-picked from
integration/w1-audit-v3): EVID-103 documents Round 4 + Round 5
audit on the integration branch (8 HIGH found+closed Round 4; 7 NEW
HIGH found Round 5 - 4 closed inline, 3 architectural deferred to
PROB-051 itself).
Pre-conditions verified before cutting
Section titled “Pre-conditions verified before cutting”cargo fmt --checkclean (0 diffs)cargo clippy --workspace --all-targets --features test-helpers -- -D warningsclean (0 warnings)cargo test --workspace --features test-helpersвсе PASS (1977 tests, 0 failed, 5 ignored, 38 suites)forgeplan healthclean - verdictHealthy, 0 blind spots, 0 orphans, 0 stale- Real E2E #1 (Verdict::Empty release binary) PASS
- Real E2E #2 (CWE-426 strings binary check, 0 occurrences) PASS
- Real E2E #3 (real workspace health smoke) PASS
- Real E2E #4..#6 (format_timeout_msg, dispatch parity, validate) PASS
Security - Dependabot (Round 4 triage, 2026-05-05)
Section titled “Security - Dependabot (Round 4 triage, 2026-05-05)”- 17 of 18 alerts from v0.28.0 round 3 closed automatically with the v0.28.0 → main merge (rust patches + npm audit fix cycle).
- 1 alert remains open:
lru 0.12.5([Alert #3], CVSS 0.0, Miri-only stacked-borrows inIterMut). Transitive viatantivy → lance → lancedb; no direct exploit surface. Carry-forward to v0.30.0 - accepted-with-justification, re-evaluate on tantivy 0.25 release. Full triage:docs/operations/dependabot-triage-2026-05-05.md.
Added (CI infrastructure, methodology)
Section titled “Added (CI infrastructure, methodology)”- PROB-050 A-30 ✅ closes -
docs/operations/QUALITY-GATES.{md,ru.md}documents all CI quality gates (fmt, clippy, test, health, validate, drift detector). Cross-referenced fromCLAUDE.md §Hooks enforcementanddocs/methodology/release-workflow.md §Pre-conditions.
Changed (forgeplan-core public API - BREAKING for direct library consumers)
Section titled “Changed (forgeplan-core public API - BREAKING for direct library consumers)”-
PROB-050 A-7 ✅ closes -
playbook::dispatch::claude_printsymbol visibility tightened. Following empirical verification (rg <name> crates/) that no in-tree consumer outside the dispatch module reads these symbols, the followingpubitems were tightened:DEFAULT_BUDGET_USD,DEFAULT_ALLOWED_TOOLS→pub(crate)helpers::DEFAULT_TIMEOUT_SECS,helpers::MAX_OUTPUT_BYTES,plugin_dispatcher::DEFAULT_PLUGIN_TIMEOUT_SECS→pub(crate)ClaudePrintResponse(struct) + its methods →pub(super)assemble_prompt,add_dir_for_produces_at,effective_allowed_tools,effective_budget_usd→pub(super)
External crates that imported these symbols will fail to compile against v0.29.0. Recommended migration: invoke the dispatch module through its public surface (
AgentDispatcher/PluginDispatcher) rather than reaching into helpers. If a use case requires a tightened symbol, open a PROB issue justifying the public contract.
Changed (forgeplan-core public API - additive, but downstream library consumers should rebuild)
Section titled “Changed (forgeplan-core public API - additive, but downstream library consumers should rebuild)”-
PROB-050 A-4 + A-5 + A-6 + A-11 + A-15 ✅ close -
claude --printdispatch refactor. Single source of truth inplaybook::dispatch::claude_print:claude_print::invoke()- full 9-step orchestration (argv + env + prompt + spawn + timeout + parse + render). AgentDispatcher and PluginDispatcher reduce to (a) variant unpack, (b) name validation, (c) binary resolution, (d) call invoke.claude_print::build_argv()- argv construction with both security gates inline (validate_allowed_tools+add_dir_for_produces_at). Argv-shape parity between dispatchers now enforced by construction.claude_print::parse_envelope()- UTF-8-trimmed JSON envelope decode. Plugin dispatcher previously had no.trim()- silent divergence from agent path closed.claude_print::format_timeout_msg()- uniform second/millisecond rendering. Agent dispatcher previously leakedDurationDebug repr; plugin dispatcher used.as_secs()only. PR-E Round 6 audit closure: sub-second durations now renderNms(was:0sfor any< 1stimeout, which confused operators chasing tight-loop timeouts). Production path isStep.timeout_seconds: u32 ≥ 1, so the common-caseNsrendering is byte-stable.helpers::which_in_pathpromoted fromfntopub(super) fn; 3 identical local copies removed from the dispatchers.claude_print::DISPATCH_ENV_LOCK-#[cfg(test)] pub(super) static tokio::sync::Mutex<()>shared byagent_dispatcher::tests,plugin_dispatcher::tests, andhelpers::tests(Round 5 audit Logic LOW-1 + PR-E audit HIGH-1: cross-test PATH-mutation race fully closed).- Behaviour delta (corrected from earlier honesty-of-claim audit):
argv shape IS byte-identical pre/post; agent-side diagnostic strings
were unified to plugin’s pre-existing format - specifically
"failed to decode claude --print JSON envelope"(was “produced unparseable JSON envelope” agent-side),format_timeout_msgoutput (wasDurationDebug repr agent-side), and the newstdout_preview=failure-context block (added on agent path; plugin path always had it). Operators / scripts / log-grep regexes that matched the old agent-only strings need a one-line update.
-
PROB-049 H-1 ✅ closes -
MutationError::StoreErrorsplit into typed variantsStoreTransient(recoverable) andStoreFatal(not recoverable). The legacyStoreError(#[from] anyhow::Error)collapse-everything variant is removed. Categorisation logic (MutationError::from_store_err) inspects theanyhow::Errorchain (lancedb / std::io shapes) and routes between the two. Default fallthrough isStoreTransient- strict refinement of legacy recoverable=true. Honesty note (PR-E Round 6 audit HIGH-2):is_recoverable()is intentionally infrastructure-only in v0.29.0 - no MCP / CLI retry loop currently consumes it. The audit flagged this as a risk (variants drifting from real failure modes without a consumer). Mitigation: this CHANGELOG block is the load-bearing contract; the first MCP retry wiring (tracked as PROB-049 follow-up for v0.30.0, candidate:forgeplan_healthcold-start LanceDB lock contention) will close the loop. Until then, downstream library consumers callingis_recoverable()should treat the boolean as a hint rather than a stable contract. -
PROB-049 H-6 ✅ closes -
MutationContextintroduced for projection helpers. All 17 file-first mutation helpers inforgeplan_core::projectionnow take&MutationContext<'_>instead of separate(workspace, store)arguments. 47 call sites updated acrossforgeplan-cli+forgeplan-mcp. The struct is#[non_exhaustive]and constructed viaMutationContext::new(...)- external library consumers may not use a struct literal. -
PROB-049 H-4 ✅ closes -
# Errorsrustdoc on all 17 projection helpers. -
PROB-029 ✅ closes - typed
Verdictaggregator (Empty / Healthy / NeedsAttention / Unhealthy) onHealthReport. Purecompute_verdict[_with]functions with configurableVerdictThresholds. Both new public types are#[non_exhaustive]. CLIforgeplan healthbanner driven off the verdict (no longer disagrees withnext_actions).next_actionsrewritten to emit concrete remediation commands. MCPforgeplan_healthand CLI--jsonboth exposeverdict+verdict_summaryfields. PR-E Round 6 audit MED closure:Verdict::Emptyis now a proper 4th variant (was deferred at Round 5 via manualverdict_summaryoverrides on CLI + MCP surfaces; both overrides removed in this release becausehuman_summary()forEmptycarries the right text by construction). CI gates that auto-promoted onverdict == "healthy"no longer promote uninitialized projects. Round 5 audit closures (HIGH Logic + Documentation): MCP_next_actionladder now checks active_stubs + possible_duplicates + phase_mismatches before the “Project healthy” fallthrough (eliminates contradiction-via-different-field); MCP tool description advertises theverdictfield for agent discovery.
Security
Section titled “Security”-
PROB-050 A-14 ✅ closes - CWE-426 binary substitution mitigated (PR-E Round 6 audit HIGH closure broadens the original mitigation). Two equivalent injection surfaces are now both closed:
-
Env-var path:
AgentDispatcher::resolve_claude_binaryand the siblinghelpers::resolve_forgeplan_binarygate their respective$FORGEPLAN_CLAUDE_BIN/$FORGEPLAN_BINenv-var overrides behind#[cfg(test)]. Release binaries silently ignore both env vars; only test builds honour them for fixture wiring. Closes the v0.28.0 release-notes promise (audit S-2 escalation, seedocs/operations/phase-b-real-e2e-2026-05-03.mdF-RUNTIME-7). -
Struct-API path (PR-E Round 6 audit HIGH-1, found by adversarial security review):
AgentDispatcher::claude_binarywas apubfield;with_claude_binarywas apubbuilder, both un-gated. A release-build caller could write attacker-controlled paths directly via the struct API, defeating the env-var hardening. BothAgentDispatcherandPluginDispatchernow: (a) keepclaude_binaryas a private field (onlynew()writesNone), and (b) gatewith_claude_binary(and the deprecated aliaseswith_task_tool/with_task_tool_path) behind#[cfg(any(test, all(feature = "test-helpers", debug_assertions)))]. Pattern mirrorsLanceStoretest-helper gating incrates/forgeplan-core/src/db/store.rs:361-384-debug_assertionsensures a downstream consumer who accidentally enablestest-helpersin a--releasebuild still gets a compile error, not a silent activation. Both surfaces now symmetric (architectural audit HIGH-1: pre-fix PluginDispatcher had no env path while AgentDispatcher had both, asymmetric hardening).
Migration for operators (CLI / brew / binary distributions): the env-var path was never a documented contract; operators relying on it for production override should pin
claudevia$PATH- that is the only supported binary-resolution surface at the CLI / playbook layer. There is no per-invocation override at the YAML schema (SPEC-003) today; tracked as PROB-050 A-31 if such a surface becomes needed.Library consumers embedding
forgeplan-coredirectly: thewith_claude_binary(path)builder is now feature-gated. For test wiring, build with--features test-helpers(and run in debug profile, or unit-test cfg). For production wiring, usenew()and rely on$PATHresolution. -
Deferred to v0.30.0 (PR-E Round 6 audit findings - pre-existing surfaces, not v0.29.0 regressions)
Section titled “Deferred to v0.30.0 (PR-E Round 6 audit findings - pre-existing surfaces, not v0.29.0 regressions)”- TOCTOU + symlink-follow in
which_in_path(Sec MED-1):is_file()follows symlinks, nocanonicalize, no executable-bit check. Window between resolve andCommand::spawnallows TOCTOU swap on a writable PATH dir. Pre-existing surface (existed before PR-E refactor). Tracked as PROB-052 - consider canonicalize + parent-dir ownership/mode check + path caching on dispatcher. Delegation::CommandCWE-78 surface (Sec MED-2):Delegation::Command { command: Vec<String> }parses directly from YAML with no allowlist / signing / user-facing warning. Real shell injection vector if playbooks loaded from network/marketplace. Tracked as PROB-053 - gate behind feature flag /--allow-shellCLI flag, or require signing for marketplace.assemble_promptproduces_at injection (Sec LOW-1): workspace-relative path is splice-formatted into natural-language prompt; backticks could close markdown code-fence and inject prompt-instructions to the agent. Pre-existing surface. Tracked as PROB-054 - validateproduces_atagainst^[A-Za-z0-9._/-]+$before splicing.claude_printgod-module split (Arch MED-2): module is 1066 LOC with ~9 responsibilities (argv, env, prompt, validators, byte-truncation, JSON parsing, failure rendering, timeout formatting, test mutex). Tracked as PROB-055 - refactor intoclaude_print/{argv.rs, envelope.rs, validators.rs, invoke.rs, test_lock.rs}keepingmod.rsas façade. Cosmetic / maintainability, not security.- MED-1 leaky-abstraction in
compute_verdict_with(Arch MED-1): storedHealthReport.verdictmay disagree with MCP-computed verdict (which folds inphase_mismatches). By-design today; consider removing stored field in v0.30.0 or renaming topartial_verdict. Tracked as PROB-056.
[0.28.0] - 2026-05-03 - file-first invariant compile-enforced + claude —print dispatchers + canonical playbooks
Section titled “[0.28.0] - 2026-05-03 - file-first invariant compile-enforced + claude —print dispatchers + canonical playbooks”Bundles 14 merge-PRs (#224..#237) since v0.27.0 (2026-04-28). Three
load-bearing themes: (1) PRD-073 file-first invariant compile-enforced
(ADR-003 - LanceStore::* mutating methods are now pub(crate),
file-first projection wrappers are the only mutation surface), (2)
ADR-011 Phase B Wave 1 - Plugin and Agent dispatchers shell out to
claude --print on the real claude 2.1.126 binary, replacing the
fictional task-tool from ADR-010, (3) Track 4-A8 canonical
playbooks - release.yaml + brownfield-docs.yaml ship as runnable
templates for marketplace skill/mapping authors.
Real-E2E verification of Phase B Wave 1 (PR 1 / 2026-05-03,
NOTE-049 + EVID-097): 5 measured real claude --print invocations
(3 happy-path success + 1 budget-error envelope decode + 1 retracted
env-export attempt), byte-identical argv recording wrapper, validation
guard reject in 0.01s. ADR-011 R_eff = 0.70 grade B (3 evidence packs,
all CL3 supports).
Dependabot: 16 of 18 open alerts auto-close on this release/v0.28.0 → main merge (lockfile in dev already at patch versions per round 2 +
round 3 triage). 2 carry-forward (lru transitive via tantivy, uuid
transitive via mermaid) с обоснованием в docs/operations/dependabot-triage-2026-05-03.md.
Pre-conditions verified before cutting: cargo fmt clean, cargo clippy —workspace —all-targets —features test-helpers — -D warnings clean, cargo test —workspace —features test-helpers all PASS (1614+ tests), forgeplan health clean.
Added (CI infrastructure)
Section titled “Added (CI infrastructure)”scripts/check-mcp-tool-count.sh- drift detector: compares actual MCP tool count incrates/forgeplan-mcp/src/server.rsagainst all documentation locations (README, CLAUDE.md, website, docs). Introduced after a v0.28.0 release audit (external OpenAI agent) found 18 stale references across the repo (counts 28 / 37 / 45 / 47 vs actual 63). Script exits 1 on any mismatch so CI blocks PRs that add/remove tools without updating docs. Supports--warnmode for local development and inline# mcp-count-drift: ignoreescape hatch for intentional historical counts..github/workflows/forgeplan-health.ymlstepMCP tool count drift check: wires the drift detector as the final gate of the Architecture Health workflow (afterforgeplan health+forgeplan validate). Closes PROB-050 A-30 “preventive value theoretical” finding (was doc-only, now enforced in CI).- See
docs/operations/QUALITY-GATES.ru.mdfor full CI gate reference (fmt / clippy / test / health / validate / drift-check), including how to run each gate locally and fix common failures.
Verification (PR 1 + PR 2.5 closures, 2026-05-03 / 2026-05-04)
Section titled “Verification (PR 1 + PR 2.5 closures, 2026-05-03 / 2026-05-04)”- NOTE-049 + EVID-097: real-E2E closure of Phase B Wave 1.
Production
claude2.1.126 invoked through PluginDispatcher AND AgentDispatcher with byte-identical argv recording wrapper. Discovered 5 net-new findings (added to PROB-050 as A-21..A-26 + 1 A-22 retract via audit C-1 pipefail discipline lesson). Total spent: ~$0.98 USD across 5 measured claude invocations. - PROB-050 A-3 ✅ closes with narrowed scope (happy + budget-error envelope verified end-to-end on healthy CLI; failure-path JSON decode coverage tracked in A-11 + A-16).
- PROB-050 A-14 wording tightened: require
#[cfg(test)]gate forFORGEPLAN_CLAUDE_BIN(audit S-2 escalates env-injection vector CWE-426 from documentation-only mitigation to compile-time gate). - PROB-050 A-28 ✅ closes via YAML rewrite (
Delegation::Agent→Delegation::Pluginsplit for colon-namespaced agent slugs inaudit.yamlsteps 1-3). Real-E2E proof on 2026-05-04: all 3 parallel agents successfully spawned, claude resolved bare slugs (architect-reviewer,code-reviewer,security-expert), 502s wall-clock real work + ~$3.50 spent - closing what would have been a guaranteedDispatchError::Transportreject pre-spawn. - PROB-050 A-29 NEW (discovered during A-28 verification):
claude_print::DEFAULT_BUDGET_USD = $1.00слишком низок для adversarial-review playbooks. All 3 audit agents hiterror_max_budget_usdat $1.05-$1.25. Operational fix applied:audit.yamlsteps 1-3 now carry explicitbudget_usd: 5.00. Methodology fix tracked as A-29 option (b) for next sprint (tierDEFAULT_BUDGET_QUICK/DEFAULT_BUDGET_REVIEW).
Added (AI documentation discoverability)
Section titled “Added (AI documentation discoverability)”website/public/robots.txt: explicitAllow: /for 18 named AI crawlers (GPTBot, ClaudeBot, Google-Extended, CCBot, Applebot, PerplexityBot, и т.д.) - Forgeplan документация specifically built for agentic consumers, signal openness explicitly rather than rely on absence ofDisallow.website/public/llms.txt: curated entry-point per emerging Anthropic/Mintlify convention (https://llmstxt.org/). Provides one-shot context for LLM agents discovering Forgeplan: methodology links, CLI/MCP reference, getting-started anchor. Without this, AI agents had to guess which paths matter.
Detail - PRD-073 file-first invariant (EVID-094 R_eff=0.80 grade A)
Section titled “Detail - PRD-073 file-first invariant (EVID-094 R_eff=0.80 grade A)”Phase 3a → 3b → 3c → 4. Four adversarial audit rounds (general / live-test / Rust-focused / final team-lead) closed 7 CRITICAL + 13 HIGH findings. PROB-048 deprecated as resolved.
Added - file-first projection helpers (15 total)
Section titled “Added - file-first projection helpers (15 total)”- 9 mutation helpers:
create_artifact_with_projection,delete_artifact_with_projection,update_metadata_with_projection,update_body_with_projection,update_depth_with_projection,add_link_with_projection,delete_link_with_projection,add_tags_with_projection,remove_tags_with_projection. Each does the {sync_before, mutate, render_after} triplet so callers can no longer forget projection. - 6 sync-from-file helpers:
sync_artifact_from_file,sync_body_from_file,sync_metadata_from_file,sync_relation_from_file,delete_orphan_artifact,delete_orphan_relation. For reindex / git_sync / watch where the file is already authoritative. add_links_batch_with_projection: deduplicates pre-sync + post-render per unique participant. 100-link bundle: ~600 LanceDB calls + 400 file ops → 2×U + N where U is unique IDs.delete_artifact_after_soft_delete: brief helper for the MCP soft-delete pattern (file already in trash, only DB row to drop).MutationErrorenum +MutationResult<T>alias introduced (typed errors); helper signature migration deferred to PRD-073 Phase 3c.marketplace/playbooks/audit.yaml: reference template for the multi-agent adversarial audit pattern. Updated header to reflect ADR-011 (claude —print via PluginDispatcher / AgentDispatcher); current YAML uses colon-namespaced agent slugs (agents-pro:architect-reviewer) which are pre-spawn-rejected byvalidate_agent_nameuntil PROB-050 A-28 introduces a colon-aware slug strategy.
Changed (BREAKING for downstream library consumers)
Section titled “Changed (BREAKING for downstream library consumers)”LanceStore::*mutating methods are nowpub(crate): 11 methods (create_artifact,update_artifact,update_valid_until,update_depth,update_body,add_tags,remove_tags,delete_artifact,add_relation,delete_relation,delete_relations_for_artifact) are no longer accessible from external crates. External callers must go throughforgeplan_core::projection::*helpers. Migration: replacestore.create_artifact(&art)withprojection::create_artifact_with_projection(&ws, &store, &art).- Slugify is now ASCII-only:
is_ascii_alphanumericinstead ofis_alphanumeric. Workspaces with cyrillic/CJK slugs requireforgeplan reindexafter pulling this version; existing files remain on disk but get a fresh ASCII slug on next render. LanceStore::update_embeddingandupdate_r_eff_scorestaypub(Class A derived data, ADR-003 Amendment 1).- BREAKING (forgeplan-core lib only): 16 mutation helpers in
projection::*migrated fromanyhow::Result<T>toMutationResult<T>(PRD-073 Phase 3c, ADR-003 Amendment 2). CLI binary and MCP server surfaces unaffected. Library consumers see the same?ergonomics via anyhow’s blanketFrom<E: std::error::Error + Send + Sync + 'static>impl. Variant taxonomy:InvalidId,InvalidKind,EmptyField,FileNotFound,ProjectionMismatch,RowNotFound,StoreError. UseMutationError::is_recoverable()to drive retry / warn-and-continue policy instead of string-matching on flattened error messages. Concrete migration example for downstream library consumers:See ADR-003 Amendment 2 (// Before (anyhow::Result):let err = create_artifact_with_projection(...).await.unwrap_err();if err.to_string().contains("invalid id") { /* ... */ }// After (MutationResult):match create_artifact_with_projection(...).await {Err(MutationError::InvalidId(_)) => /* fatal input */,Err(e) if e.is_recoverable() => /* transient - retry ok */,Err(_) => /* fatal - surface to user */,Ok(path) => /* happy path */,}.forgeplan/adrs/ADR-003-*.md) for the full before/after error matrix and Phase 3d reserved-variant notes. sync_artifact_from_fileandsync_body_from_filesignatures takeworkspace: &Pathto enableFileNotFound { id, path }typed errors with the actual on-disk location. CLI callers (reindex,git_sync,watch) updated. (PRD-073 Phase 3c)update_body_with_projectionnow returnsRowNotFound(notStoreError) for the missing-id case - fixes Wave 1A audit finding whereis_recoverable() == truewould have mislabeled an unrecoverable input error as a transient I/O failure.
Changed (behavioral - visible to CLI users)
Section titled “Changed (behavioral - visible to CLI users)”- All 22 CLI mutation handlers now hold an exclusive workspace lock
(30 s timeout) for the duration of the operation. Concurrent
forgeplan updateinvocations that previously raced now serialize cleanly. Scripts using&orxargs -Pagainst the same workspace may see lock-contention errors that were previously silent races. forgeplan deletenow creates a soft-delete receipt (parity with MCP). Recoverable viaforgeplan undo-lastorforgeplan restore <id>within 30 days.- All markdown writes are atomic (tempfile + rename). Kill -9 mid-write no longer leaves zero-length projection files.
- File frontmatter
title:now preserves non-ASCII titles verbatim (PRD-073 Phase 3c R2 audit M-R2-3 / security). Previously, an artifact created with a Cyrillic / CJK / emoji title (anything that slugifies to empty) was rendered withtitle: untitledin the file frontmatter - losing the user’s original title from the on-disk representation while the DB row preserved it. The Phase 3cprojection_slughelper now applies theuntitledfallback only to the on-disk filename (e.g.prds/PRD-001-untitled.md), and the frontmatter receives the original title. Operators with non-ASCII confidential titles should be aware that the file frontmatter now contains the full title verbatim (the slug filename already exposed partial title information pre-fix; this aligns the two surfaces). claudeCLI is now a runtime prereq for playbooks that usedelegate_to: pluginordelegate_to: agent(ADR-011, Phase B). Replaces the never-shippedclaude-code-plugin/task-toolbinaries assumed by ADR-010. Plugin and agent steps invokeclaude --print --agent <name>directly viatokio::process::Command. Existing Claude Code session is reused (noANTHROPIC_API_KEYrequired for interactive runs); CI runs need the env var. Missing binary surfacesDispatchError::DelegateMissingwith install hint pointing to https://code.claude.com/docs/en/install. New per-stepStep.budget_usd(default $1.00) andStep.allowed_tools(default[Read, Glob, Grep]) fields control invocation surface; SPEC-003 1.1 → 1.2 (additive). Skill, Command, and ForgeplanCore dispatchers are unchanged.
Added (developer-facing)
Section titled “Added (developer-facing)”- New Cargo feature
forgeplan-core/test-helpers: exposes*_for_testescape hatches onLanceStorefor downstream test fixtures. Gated ondebug_assertionsso release builds with this feature accidentally enabled still get the lockdown. Production binaries MUST NOT enable this feature; release builds with both feature on AND debug_assertions off compile-error out.
- Path-traversal CVE class on import:
idfield validation in every projection helper that composes a filesystem path. - Multi-line ratchet test scanner: was missing 21 multi-line
store\n.method(invocations under the previous literal matcher. update --depth --titleorphan-file recreation: metadata mutation now runs FIRST so subsequent depth/body renders see the new title.mem-foovsmem-foo-barprefix collision: exact-path delete viaremove_projection_at.- 4-process concurrent
forgeplan updaterace: workspace lock plus lock-then-open ordering (LanceStore connections snapshot at open). add_link / delete_linkwarn-and-continue semantics restored (target sync + post-render are best-effort, source side fatal).update_body_with_projectionordering inverted to file-first.forgeplan_importno longer leaves DB-only state.forgeplan newnon-tty similar-title prompt: explicitError: ... Fix: --allow-duplicateinstead of silent cancel.
[0.27.0] - 2026-04-28 - Real subprocess dispatchers + init recommendation hints + greenfield playbook (EPIC-007 Phase 6)
Section titled “[0.27.0] - 2026-04-28 - Real subprocess dispatchers + init recommendation hints + greenfield playbook (EPIC-007 Phase 6)”Phase 6 переводит engine layer из v0.26.0 в user-facing activation.
PRD-072 / RFC-007 / ADR-010 закрывают Phase 5 deferral: 5 production
Dispatcher impls (real subprocess через tokio::process + ForgeplanCore
direct call), forgeplan init теперь эмитит recommendation hints, и
канонический greenfield-kickoff.yaml доступен в marketplace.
Added - Real subprocess dispatchers (PRD-072 / RFC-007 / ADR-010)
Section titled “Added - Real subprocess dispatchers (PRD-072 / RFC-007 / ADR-010)”forgeplan-core::playbook::dispatch::{plugin,agent,skill,command,forgeplan_core}_dispatcher- 5 production реализаций traitDispatcher. ЗаменаMockDispatcher::AlwaysOkвplaybook run --yesи MCPforgeplan_playbook_run.PluginDispatcher(FR-1) - claude-code-plugin subprocess invocation, default 600s timeout, fallback_hint surfacing на missing-install.AgentDispatcher(FR-2) - task-tool agent-invoke, default 300s timeout, symmetric к plugin path.SkillDispatcher(FR-3) - in-process v1 stub (trace-only). Real registry resolution отложена в Wave 5.CommandDispatcher(FR-4) - security-hardened:env_clear+ allow-list, no shell expansion,--yesgate trust upstream. Default 180s.ForgeplanCoreDispatcher(FR-5) - direct internal call (no subprocess) дляingest/new/validate/activate/search. Замена Phase 5 CLI shell-out - теперь делегация выполняется в том же процессе.dispatch::helpers::run_subprocess- общая обёрткаtokio::process::Commandсkill_on_drop(true),Stdio::pipedдля stdout/stderr,Stdio::nullдля stdin, concurrent drain черезtokio::join!, 10 MiB cap, timeout с child kill.- Pre-Wave 0 split:
dispatch.rs(single 466 LOC) →dispatch/directory с per-delegate modules.mod.rsсохраняет trait + Mock/Recording stubs + DispatchError + SecurityError без изменения публичного API.
Added - Init recommendation wiring (PRD-067 AC-3/4/5/7 closed)
Section titled “Added - Init recommendation wiring (PRD-067 AC-3/4/5/7 closed)”commands::init::runextension (FR-6) - после workspace creation собирает project signals (detect_signals) + installed plugins (detect_plugins(extended_registry)) +build_recommendations+format_recommendations→ emit на stderr.- 3 bundled
KnownPlaybookdescriptors -greenfield-kickoff,brownfield-docs,brownfield-code- для recommendation engine до момента когда полные marketplace YAML файлы land. - Backward compat:
FORGEPLAN_HINTS=0или non-TTY stderr → no recommendation emission (PRD-067 AC-7). - Non-fatal degradation: signal/plugin detection failure → warning на stderr + продолжение init (no abort).
Added - Canonical greenfield playbook (PRD-072 FR-7)
Section titled “Added - Canonical greenfield playbook (PRD-072 FR-7)”marketplace/playbooks/greenfield-kickoff.yaml- 7 шагов черезForgeplanCore+ 1 optionalSkillstep. Все мандатные шаги без внешних плагинов:capture-vision(note) →stack-decision(adr) →kickoff-epic(epic) → 3×prd-feature(parallel after epic) →scaffold-docs(skill,on_error: continue).forgeplan playbook validateпроходит:OK: greenfield-kickoff (7 steps)+Done.hint.- Documentation footer в YAML - purpose, expected duration, fit в methodology.
Changed - Schema 1.0 → 1.1 (additive)
Section titled “Changed - Schema 1.0 → 1.1 (additive)”Step.timeout_seconds: Option<u32>(FR-8) - backward compat: старые playbook’и без поля грузятся OK с дефолтом per-delegate type (300s general / 600s plugin / 180s command/skill).SPEC-003 schema_versionbumped 1.0 → 1.1. Loader принимает оба значения (semver-range minor bump).
Fixed - Phase 6 real-world bugs (PR #220, commit 69ea571)
Section titled “Fixed - Phase 6 real-world bugs (PR #220, commit 69ea571)”После merge’а Phase 6 в dev manual smoke testing на release binary обнаружил 4 production bugs, которые 1834 automated тестов пропустили:
- HIGH
playbook show <name>- name lookup НЕ находилmarketplace/playbooks/. Discovery roots расширены до workspace marketplace, не только.forgeplan/playbooks/+~/.claude/plugins/*/playbooks/. Теперь shipping playbooks доступны через name lookup, не только absolute path. - HIGH
plugins doctor- exit 0 при missing plugins (документировано exit 1). Fixed:if !missing.is_empty() || !outdated.is_empty() { exit(1) }. CI gate теперь работает. - HIGH
marketplace/playbooks/brownfield-code.yaml-detect-c4-needstep missinginput.id, validate fails на step 1. Removed broken step, playbook reduced 5 → 4 steps, validate clean. - CRITICAL systemic - все error paths возвращали
exit 0(eprintln!("Error:..."); return Ok(())). Fixed ~10 sites вcommands/playbook.rs+ingest.rs: explicitstd::process::exit(1). Real CI integration теперь catches all CLI failures. - BONUS dev profile fix (commit 0acf884) -
[profile.dev] debug = "line-tables-only"снижает linker memory ~50%. Закрыт recurringcollect2: ld signal 7 [Bus error]OOM на ubuntu-latest 16GB который преследовал PR #217+ Phase 5/6 PRs. Universal CI speedup.
Fixed - PROB-047 mitigation 1 (PR #221, commit 80f458c)
Section titled “Fixed - PROB-047 mitigation 1 (PR #221, commit 80f458c)”scan-import classifier (crates/forgeplan-core/src/scan/detect.rs)
ошибочно классифицировал product guides и instruction files как
PRD-артефакты через Tier 3 content heuristics (## Goals,
## Problem, ## Decision headings). PR #218 был symptom-only
cleanup - false-positives recurred при следующем scan-import.
is_doc_path(relative_path: &Path) -> bool- blacklist для: recursivedocs/,marketplace/, plus root-level meta-files (CLAUDE.md,AGENTS.md,README.md,CHANGELOG.md,CONTRIBUTING.md,TODO.md,ROADMAP.md,LICENSE.md,SECURITY.md, plus.ru.mdlocalized variants).detect_kind_with_path(filename, relative_path, content)- path-aware variant suppresses Tier 3 ONLY. Tier 1 (frontmatterkind:) и Tier 2 (filename pattern PRD-XXX/RFC-XXX) остаются authoritative - explicit signals always win.detect_kindretained as wrapper passingNonefor path- backward compat with all 15 existing tests.
- +11 unit tests:
is_doc_pathmatrix coverage + path-aware Tier 3 suppression + Tier 1/Tier 2 precedence under docs. - EVID-092 (verdict: supports, congruence_level: 3, evidence_type: test) - same-context measurement linked to PROB-047. R_eff: 0.0 → 0.71 (B).
- Mitigations 2-5 deferred to Phase 7+ sprint (frontmatter precedence
formalization, scan-import default
--dry-run+ opt-in--apply, content_hash idempotency, brownfield test fixtures).
Workspace hygiene (PR #221)
Section titled “Workspace hygiene (PR #221)”.forgeplan/journal/(PRD-065 playbook runtime per-run JSONL) → gitignore.- PROB-046 deprecated - resolved via PRD-071 hint contract (shipped v0.25.0).
- EPIC-007 advisory phase advanced to evidence (children 4/5 shipped).
- 9 untracked scan-import false-positives removed via
forgeplan reindex. forgeplan health: “Project looks healthy” - 0 blind spots, 0 orphans, 0 phase mismatches, 0 duplicate pairs.
- +5000 LOC across
forgeplan-core::playbook::dispatch(5 dispatchers- helpers) +
commands::init::runextension + canonical YAML.
- helpers) +
- +60 unit tests (Wave 1: 44 unit tests распределены по dispatchers + helpers).
- +5 integration tests в
integration_phase6_init.rs(empty repo,.obsidianvault, legacy code with >100 commits,FORGEPLAN_HINTS=0, signal failure path). - Workspace test count: 1384+ lib + 372+ integration, all PASS.
- Code quality: 0 fmt diffs, 0 check warnings, 0 clippy warnings (rust 1.91 strict).
- 3 waves × 8 unique agents через TeamCreate Mode A:
- Pre-Wave 0: dispatch.rs split + Spike-2 manual c4-architecture run + EVID-090 (CL3)
- Wave 1: 6 parallel agents (helpers + 5 dispatchers, strict file ownership)
- Wave 2: 1 agent (init wiring + integration tests)
- Wave 3: 1 agent (greenfield-kickoff.yaml + validate)
- Wave 4: 1 agent (this - docs + EVID-091 + CHANGELOG + TODO)
Deferred to follow-up sprint
Section titled “Deferred to follow-up sprint”Step.timeout_secondsper-step override (FR-8 wiring) - schema field landed, executor wiring partial; full per-step override черезdispatch::helpers::run_subprocessparameter - Wave 5.- Real
SkillDispatcherregistry - текущий impl = trace-only stub (loggable invariants + fallback_hint). Wave 5 = real skill resolution через agent-skills capability registry. - Per-step env allow-list extension - сейчас allow-list захардкожен
в helpers (
PATH,HOME,FORGEPLAN_WORKSPACE). PRD-076 (TBD) - декларативныйstep.env:override с whitelist через mapping. - MCP
forgeplan_ingestwrapper - pure CLI command в v0.27.0 (still); MCP wrapper remains deferred (CLI cover viaforgeplan serve). - 3 canonical playbooks -
brownfield-docs.yaml,audit.yaml,release.yaml- backlog (greenfield + brownfield-code published). - Parallel step execution - sequential в v1 per PRD-065 Non-Goals.
References
Section titled “References”- ADR-010
.forgeplan/adrs/ADR-010-*.md- subprocess invocation strategy - RFC-007
.forgeplan/rfcs/RFC-007-*.md- Phase 6 dispatcher architecture - PRD-072
.forgeplan/prds/PRD-072-*.md- Phase 6 PRD (FR-1..FR-10) - EVID-090 - Spike-2 tokio::process measurement (CL3 same-context)
- EVID-091 - Phase 6 closure evidence pack (this release)
- EPIC-007 - Playbook Runtime + Pack Marketplace (parent)
[0.26.0] - 2026-04-28 - Playbook runtime + Ingest engine + Plugin detection (EPIC-007 Phase 2)
Section titled “[0.26.0] - 2026-04-28 - Playbook runtime + Ingest engine + Plugin detection (EPIC-007 Phase 2)”Forgeplan становится оркестратором. Три новых core capabilities (PRD-065 / PRD-066 / PRD-067) воплощают ADR-009: сам forgeplan-core не генерирует документы - он знает когда какой playbook запускать, кому делегировать каждый шаг, и как ингестить output в forge-граф с обязательной ## Sources секцией (hallucination-proof invariant). Реализация - четырёхволновой sprint, 9 параллельных агентов, ~9000 LOC, +168 unit tests, plus integration E2E из Wave 4.
Added - Playbook runtime (PRD-065 / SPEC-003)
Section titled “Added - Playbook runtime (PRD-065 / SPEC-003)”forgeplan-core::playbook::{types,loader,executor,dispatch,journal}- декларативная YAML-схема + runtime executor.- 5 типов делегации (strict typed, no arbitrary shell):
plugin(Claude Code plugin via Task tool),agent(subagent via Task tool),skill(agent-skills capability),command(opt-in shell),forgeplan_core(internal op:ingest/new/validate/activate/search). - DAG-ordering через
requires:(step IDs), цикл-detection, unknown-ref detection в loader. fallback_hint- точная install-команда, эмитится если plugin/skill не установлен (AC-4 PRD-065).- Journal в
.forgeplan/journal/playbook-runs.jsonl- resumable partial failures. - JSON Schema опубликована в
docs/schemas/playbook.schema.yaml(FR-2).
Added - Ingest engine (PRD-066 / SPEC-004)
Section titled “Added - Ingest engine (PRD-066 / SPEC-004)”forgeplan-core::ingest::{types,sources,template,engine,idempotency}- declarative mapping engine.- Tera-style шаблоны с whitelist filters (10):
trim,lower,upper,bullet_list,comma_list,slugify,truncate,default(value=...),replace,table. Любой не-whitelisted filter → load error (security boundary, ADR-009). ## Sourcesinvariant -sources_section.include: falseотвергается deserialization, артефакт без Sources не создаётся.compat_spec_versionper mapping - semver-pinning upstream plugin output, fail-fast при upstream breaking change.- 5 source kinds:
c4-documentation,autoresearch,git-log,ddd-model,sparc-spec. - 6 target artifact kinds:
prd,adr,epic,note,spec,problem. - Idempotency через
source_hash- re-run = update existing, не дубликаты (AC-3 PRD-066). - JSON Schema опубликована в
docs/schemas/mapping.schema.yaml(FR-2).
Added - Plugin detection + self-describing hints (PRD-067)
Section titled “Added - Plugin detection + self-describing hints (PRD-067)”forgeplan-core::plugins::{detection,registry,hints}- детектит installed plugins.- Detection paths:
~/.claude/plugins/cache/,.claude/plugins/,.agentskills/,.cursor/skills/. - Project signals:
empty_repo,legacy_code_no_docs,docs_vault_present,has_package_json,has_cargo_toml,git_commit_count. - Recommendation engine - signals × installed_plugins → applicable playbooks; emitted в init hint.
- CLI:
forgeplan plugins {list|doctor|info <name>}.
Added - CLI / MCP surface
Section titled “Added - CLI / MCP surface”- 5 new CLI commands:
forgeplan playbook {list|show|run|validate},forgeplan ingest,forgeplan plugins {list|doctor|info}. - 8 new MCP tools wrapping the same surface for agent integration.
- All новые команды эмитят PRD-071 hint markers (
Next:/Or:/Wait:/Done./Fix:) - coverage 100% по drift-prevention audit script.
Added - Canonical marketplace assets
Section titled “Added - Canonical marketplace assets”marketplace/mappings/c4-to-forge.yaml- production-ready mapping для c4-architecture plugin output. Per EVID-088 (Spike-1 measurement): target=noteпо умолчанию (неprd/spec) - code-derived артефакты не имеют product-context для PRD/SPEC validation gate.marketplace/playbooks/brownfield-code.yaml- 5-step canonical playbook:detect-c4-need→run-c4-architecture(Plugin) →ingest-c4(ForgeplanCore + mapping) →run-history-miner(Skill) →summary-note(ForgeplanCore).triggered_by: { has_git: true, commit_count_min: 50, has_docs: false }.
Added - Documentation
Section titled “Added - Documentation”docs/operations/PLAYBOOK-AUTHORING.ru.md- guide для авторов playbook’ов: 5 типов делегации, DAG, fallback hints, conventions.docs/operations/INGEST-MAPPINGS.ru.md- guide для авторов mapping’ов: Tera caveat (default(value="...")), whitelist, hallucination-proof invariant, target=note default per EVID-088.docs/README.md+docs/README.ru.md- index updates.
- +9000 LOC across 3 new modules + CLI + MCP.
- +168 unit tests (W1: 39 / W2: 110 / W3: 58, including 16 dogfood E2E from Wave 3) + Wave 4 integration E2E.
- 0 fmt diffs / 0 clippy warnings on default and
--features semantic-search. - 4 waves × 9 unique agents (1 architect + 3 W1 + 5 W2 + 4 W3 + 2 W4) с gate checks per wave.
Deferred to follow-up sprint
Section titled “Deferred to follow-up sprint”- Real Plugin / Agent / Skill dispatchers - Wave 3 экзекутор делегирует через mocked Task tool subprocess в этом релизе. Production wiring (через runtime Task tool API) - следующий sprint.
- MCP
forgeplan_ingest- pure CLI command в v0.26.0; MCP wrapper отложен (CLI cover тех же scenarios черезforgeplan serve). brownfield-docs-pack/greenfield-pack- onlybrownfield-code.yamlpublished canonical в этом релизе.- Parallel step execution - sequential в v1 per PRD-065 Non-Goals; parallelizable DAG planner - v2.
References
Section titled “References”- ADR-009
.forgeplan/adrs/ADR-009-*.md- orchestrator pivot decision - EPIC-007 - Playbook runtime + Pack marketplace (parent)
- PRD-065 / SPEC-003 - Playbook runtime + schema
- PRD-066 / SPEC-004 - Ingest engine + mapping schema
- PRD-067 - Plugin detection + hints
- EVID-088 - Spike-1 c4-to-forge concept validation (CL3)
- EVID-089 - Phase 5 implementation evidence pack
[0.25.0] - 2026-04-27 - Unified hint contract across CLI + MCP (PRD-071 complete)
Section titled “[0.25.0] - 2026-04-27 - Unified hint contract across CLI + MCP (PRD-071 complete)”Forgeplan теперь говорит агентам что делать дальше. Каждый CLI и MCP вывод эмитит один контрактный маркер (Next: / Or: / Wait: / Done. / Fix:) - никаких больше «agent reads no-hint output → re-reads CLAUDE.md → guesses → loops». 5-rule контракт (PRESENCE / ACTIONABILITY / DETERMINISM / CONDITIONALITY / CONSISTENCY) реализован за 5 циклов multi-agent sprint, audit coverage 0% → 100% (70/70).
Added - 5-rule hint contract (PRD-071)
Section titled “Added - 5-rule hint contract (PRD-071)”Next: <full command>- primary action with real IDs (no<placeholder>)Or: <command>- alternate when primary blocksWait: <condition>- async/TTL retry signalDone.- terminal success (workflow complete)Fix: <command>- error remediation (paired withError:line)- JSON output:
_next_actionfield (string or null) - MCP responses:
_next_actionin success + error data
Added - Drift prevention infrastructure
Section titled “Added - Drift prevention infrastructure”crates/forgeplan-cli/tests/hint_contract.rs- 36 integration tests asserting every covered command emits contract marker AND no forbidden placeholders. New CLI/MCP command without hint fails CI.scripts/audit-hints.sh- coverage metric (CI-ready), recognizes all 5 markers.docs/methodology/agent-protocol.md- full contract spec with good/bad hint examples and agent reading protocol.CLAUDE.md- Hint protocol section (5-line agent reference).
Changed - backward-compat preserved
Section titled “Changed - backward-compat preserved”forgeplan list --jsonandforgeplan tree --jsonretain bare-array stdout (jq '.[]'and existing scripts not broken). Hint moves to stderr in JSON mode.- All existing CLI text outputs preserved - hints are additive new lines at end.
- MCP
_next_actionfield was already present (just normalizing values).
Fixed - edge cases
Section titled “Fixed - edge cases”- 9 commands (get/delete/update/score/estimate/progress/calibrate/validate/link) now emit
Fix: forgeplan liston “Artifact not found” errors. Previously onlyError:line - failed PRESENCE rule for not-found path. - Audit script now recognizes
Fix:/Or:/Wait:/Done.markers (was onlyNext:- produced false negatives).
Sprint metrics
Section titled “Sprint metrics”- 5 cycles × 3 parallel agents = 9 unique agents
- 90 files changed (+3994, -539)
- 1140 lib + 36 hint_contract + 104 cli_integration_test = 1280 tests passing
- 0 fmt diffs, 0 clippy warnings
- EVID-086 linked to PRD-071, R_eff 0.70 (overall 0.80 A grade)
[0.24.0] - 2026-04-19 - Orchestrator dispatcher for 2-5 sub-agents (PRD-057 complete)
Section titled “[0.24.0] - 2026-04-19 - Orchestrator dispatcher for 2-5 sub-agents (PRD-057 complete)”Forgeplan now dispatches work. One MCP call - forgeplan_dispatch --agents N - hands the orchestrator a parallel-safe plan: which
artifacts each sub-agent should work on, which defer to a serial queue,
and human-readable reasoning for every decision. Ends the manual
“read graph + blocked + list + mental overlap calc” loop that was the
original PRD-057 problem statement.
Four increments (Inc 2, 3, 4) + two adversarial audit rounds (R2 3-agent mid-sprint, R3 4-agent final) + 94 net new tests (1391 total). Builds on the Inc 1 workspace lock shipped in v0.23.1.
Added - Agent identity (Inc 2, FR-009 + AC-5)
Section titled “Added - Agent identity (Inc 2, FR-009 + AC-5)”AgentIdentitycaptures which MCP client last mutated an artifact viaclientInfoand stampslast_modified_by: name/versioninto frontmatter on every write.- Unknown-frontmatter preservation -
projectionnow keeps agent-owned fm fields (last_modified_by,domain,affected_files) across re-renders triggered by unrelated tools. - Unicode / control-char rejection in
AgentIdentity::new- blocks bidi override, ZWJ, RTL, newlines, path separators. - Activity log carries the captured
clientInfo- previousNoneTODO closed.
Added - Claim protocol (Inc 3, FR-004..006, FR-014, AC-2..3)
Section titled “Added - Claim protocol (Inc 3, FR-004..006, FR-014, AC-2..3)”ClaimStore- soft-coordination signal “agent X works on ID until T”. YAML files at.forgeplan/claims/<ID>.yaml(gitignored). TTL 1 min..24 h, default 30 min. Same-agent calls renew; expired claims transparently overwritten.- Three new MCP tools:
forgeplan_claim,forgeplan_release(force: trueorchestrator escape hatch),forgeplan_claims. - Atomic writes via tempfile + rename.
- 64 KB YAML cap + path-traversal guard (R2 security HIGH fix).
Added - Orchestrator dispatcher (Inc 4, FR-001..003, FR-010..011, AC-1)
Section titled “Added - Orchestrator dispatcher (Inc 4, FR-001..003, FR-010..011, AC-1)”forgeplan_dispatchreturns{buckets, serial_queue, reasoning, candidate_count, claimed_count, blocked_count, skipped_parse_errors}.- Jaccard file-overlap detection (0.3 default threshold).
Empty
affected_filesbiases to serial (R-2 safety). - Least-loaded-first greedy packing - distributes, deterministic.
- Graph-aware - blocked artifacts excluded via
kahn_sort. - Claim-aware - claimed artifacts skipped with reasoning.
- Skill matching via
agent_skillsvs artifactdomain. - Markdown-section fallback - legacy artifacts with only
## Affected Filesbody section are hydrated viaextract_affected_files(body). - Input clamps:
MAX_AGENTS=64,MAX_SKILLS_PER_AGENT=32,MAX_AFFECTED_FILES=512, 512-byte path cap (R3 CWE-770 fix).
Added - Integration surface (FR-012, FR-013)
Section titled “Added - Integration surface (FR-012, FR-013)”forgeplan_healthbody includesactive_claims,active_claim_count,skipped_claim_files.forgeplan_get_next_actionappends claim holder + expiry when a live claim exists.
Security
Section titled “Security”- Path traversal refusal in
ClaimStore(CWE-22). - Unicode homograph rejection in
domain(CWE-176). - Resource caps on
agents, skills, file lists, YAML size (CWE-400/770). - Control chars rejected in agent identity.
Performance
Section titled “Performance”- Read-only tools (
dispatch,claims,health,get) don’t acquire the workspace lock - orchestrator 1 Hz polling doesn’t serialize writers (R2 architect MED). ClaimStore::list_active_mapfor O(1) dispatcher joins.
Testing
Section titled “Testing”- +94 tests (1297 → 1391). 13 dispatch algorithm, 26 claim store (inc. hardening), 14 MCP wiring + validation, 10 dogfood E2E, 4 workflow variations, 1 AC-4 concurrent-forgeplan_new unique-ID E2E.
- Two adversarial audit rounds (R2 3-agent, R3 4-agent with production-validator for FR/AC task-completion) - 30 findings closed with regression tests.
Deferred to v0.25+
Section titled “Deferred to v0.25+”- Shared
kv_yamlabstraction acrossphase::store+claim+ future dispatch-cache. - Per-request identity for HTTP/SSE transports.
load_frontmatter_fullprimitive to dedupe 10 read→parse sites.ListFilter::parent_epicpush-down.DispatchDecisionstructured enum forreasoning(i18n).list_active_map → HashMap<String, Claim>for holder-based routing.- ADR separating claim (ephemeral) from phase (durable) state.
- Agent profiles at
.forgeplan/agents/<agent_id>.yaml(v0.27 roadmap).
References
Section titled “References”- PRD-057
.forgeplan/prds/PRD-057-*.md - EVID-077
.forgeplan/evidence/EVID-077-*.md- R_eff=1.00, CL3
[0.23.1] - 2026-04-19 - Multi-agent workspace lock foundation (PRD-057 Inc 1)
Section titled “[0.23.1] - 2026-04-19 - Multi-agent workspace lock foundation (PRD-057 Inc 1)”First safety primitive for multi-agent workflow - workspace-level file
lock that serializes LanceDB write operations across 2-5 concurrent
sub-agents sharing a .forgeplan/ directory. Patch bump, no breaking
changes, no new user-facing tools.
forgeplan-core::workspace::lockmodule withWorkspaceLockRAII guard andacquire_workspace_lockasync helper. Usesfs2flock primitive (Unix) / LockFileEx (Windows). Released automatically on drop including process crash.- 30-second timeout with exponential backoff (10ms → 1000ms) - no indefinite hang if a sibling agent is stuck.
- Symlink guards on both workspace directory and lock file (parity with PRD-055 R3 + PRD-056 hardening).
#[must_use]on guard - compiler catches accidental immediate drop vialet _ =.
Wrapped with lock (all MCP write entry points)
Section titled “Wrapped with lock (all MCP write entry points)”forgeplan_new- prevents duplicate ID collision under concurrentnext_idallocation.forgeplan_updateforgeplan_deleteforgeplan_supersedeforgeplan_deprecate
Hygiene
Section titled “Hygiene”.gitignore:.forgeplan/.lockand.forgeplan/claims/(prep for PRD-057 Inc 2-4).
Verification
Section titled “Verification”- 1297 tests pass / 0 fail (+6 new regression tests:
acquire_creates_lock_filelock_releases_on_dropconcurrent_acquirers_serialize_and_total_time(strengthened with wall-time lower bound)timeout_surfaces_when_lock_heldsymlinked_workspace_dir_is_refused(unix)symlinked_lock_file_is_refused(unix)
cargo clippy --workspace --all-targets -D warnings: clean.cargo fmt --check: 0 diffs.- Rust 1.95 toolchain pinned via
rust-toolchain.toml.
5-agent audit Round 1 (security + logic + arch + rust + task-completion) found 1 CRITICAL + 2 HIGH + 4 MEDIUM - all fixed in the same PR before merge. Net verdict: APPROVE_WITH_FIXES from all 5 agents post- hotfix.
Not included - planned for v0.24.0
Section titled “Not included - planned for v0.24.0”Claimmodule +forgeplan_claim/_release/_claimsMCP tools (PRD-057 Inc 3).- Agent identity capture (
client_info→last_modified_byfrontmatter field) (PRD-057 Inc 2). forgeplan_dispatch --agents Ntool (PRD-057 Inc 4) - the dispatcher that suggests parallel-safe buckets based on dep graph, file-overlap Jaccard, and domain-skill matching.
Refs: EPIC-005, PRD-057 Inc 1, PR #192.
[0.23.0] - 2026-04-18 - Advisory phase state machine (PRD-056, EPIC-005)
Section titled “[0.23.0] - 2026-04-18 - Advisory phase state machine (PRD-056, EPIC-005)”First shipped child of EPIC-005 “Phase state machine & workflow-aware
methodology umbrella”. Every artifact in the greenfield workflow now
has a visible current_phase that auto-advances through the methodology
cycle (shape → validate → adi → code → test → audit → evidence → done)
with full transition history on disk.
Advisory-only - no existing tool is blocked. Full enforcement lands in a later PRD under EPIC-005.
Added - phase state module (forgeplan-core::phase)
Section titled “Added - phase state module (forgeplan-core::phase)”- Per-artifact state file at
.forgeplan/state/<ID>.yaml(gitignored) withcurrent_phase,workflow_type,advanced_at, append-onlyhistory: Vec<PhaseTransition>,schema_version. Phaseenum (Unknown/Shape/Validate/Adi/Code/Test/Audit/Evidence/Done) withas_str()andsuggested_next()helpers.WorkflowTypeenum (currently Greenfield - brownfield/hotfix/research/ review-fix/refactor are follow-up child PRDs under EPIC-005).- Atomic writes: tmp+rename with pid+nanos+AtomicU64-counter filename,
create_new(true)against symlink planting, fsync(file) + fsync(dir). - Symlink guards on both state directory AND target file, read + write.
- Path traversal defense via
validate_artifact_idat every entry point. - Size caps:
MAX_HISTORY_ENTRIES=1024(FIFO drop preserving index 0),MAX_REASON_LEN=512,MAX_STATE_FILE_BYTES=1 MiB,MAX_ARTIFACT_ID_LEN=128. - Forward-compat:
schema_version > CURRENT→ refused (no silent data loss). - Corrupt YAML quarantined to
<id>.yaml.corrupt.<timestamp>rather than clobbered - preserves audit-trail forensics.
Added - auto-advancement hooks (MCP server)
Section titled “Added - auto-advancement hooks (MCP server)”forgeplan_new→phase=shapeon successful artifact creation.forgeplan_validatePASS →phase=validate.forgeplan_activate/_supersede/_deprecate→phase=done.- All hooks fire-and-forget: failures logged via
tracing::warn, never break the calling tool (advisory invariant).
Added - MCP tools
Section titled “Added - MCP tools”forgeplan_phase <id>- read current phase + workflow_type + timestamps + full append-only history. Missing state returns{current_phase: "unknown"}, never an error.forgeplan_phase_advance <id> --to <phase> [--reason]- manual override, appends to history, does NOT validate ordering (advisory layer allows out-of-order jumps).reasoncapped at 4096 bytes at MCP boundary + 512 bytes on persist.PhaseArgJSON-Schema enum so LLM clients constrain-sample exact values - no paraphrases.
Added - integration
Section titled “Added - integration”forgeplan_getresponse now appends current phase to_next_action("… Phase: \shape` → next `validate`.”`) when tracking is active.forgeplan_healthresponse includesadvisory_phase_mismatches[]- artifacts withstatus=activebutcurrent_phasestill early-cycle (shape/validate/adi). Strictly advisory - no health failure.
Added - config
Section titled “Added - config”- New optional
phase.enabled: boolblock in.forgeplan/config.yaml(defaulttrue). Flip tofalsefor exact pre-v0.23.0 semantics without recompile.
Fixed - hygiene
Section titled “Fixed - hygiene”.gitignore: added.forgeplan/logs/(forgotten in v0.21.0 - activity log was leaking into git) and.forgeplan/state/(new in this release).
Verification
Section titled “Verification”- 1291 tests pass / 0 fail (+30 new vs v0.22.1 baseline):
- 12 phase module unit tests
- 14 regression tests (10 from Round 1 + 4 from Round 2 audits)
- 4 incidental matches
cargo clippy --workspace --all-targets -D warnings: clean.cargo fmt --check: 0 diffs.- 2 audit rounds by multi-agent panel (security + logic + rust + architect): 2 CRITICAL + 7 HIGH + 3 MEDIUM findings, all fixed before ship. R_eff(PRD-056) = 1.00, Grade A.
Not included - deferred to follow-up PRDs
Section titled “Not included - deferred to follow-up PRDs”forgeplan_phase_backfillcommand (FR-009, COULD) - populate phase state for existing ~100 artifacts.- Full phase enforcement (“замки”) - tools refuse to work not in their phase. Separate PRD under EPIC-005.
- Brownfield, audit-hotfix, research, review-fix, refactor workflow phase enums - each own child PRD under EPIC-005.
- Read-side
O_NOFOLLOWTOCTOU closure (platform module needed). thiserror-typedPhaseError(advisory module, anyhow is fine here).
Refs: EPIC-005, PRD-056, EVID-076.
[0.22.1] - 2026-04-18 - Undo hardening (post-ship audit Round 3)
Section titled “[0.22.1] - 2026-04-18 - Undo hardening (post-ship audit Round 3)”Security + correctness hotfix for the undo subsystem shipped in v0.22.0. A 4-agent multi-lens audit of the PRD-055 code found 2 CRITICAL + 5 HIGH real issues. All fixed here with regression tests.
Fixed - Security
Section titled “Fixed - Security”- Path traversal via tampered
projection_path(C-1 sec). Restore no longer trustsreceipt.snapshot.projection_pathverbatim. Destination is recomputed fromworkspace + kind + id + slug(title)and verified withcanonicalize().starts_with(workspace). An attacker-crafted receipt pointing at/etc/passwdis refused. - Unsanitized strings from receipts reached the agent (H-1 sec).
report.warnings,relations_skipped, andreceipt_idinforgeplan_restore/forgeplan_undo_lastresponses now go through the samesanitize_for_hint()pipeline used elsewhere. Prompt-injection content planted in a receipt can no longer ride into agent context. - Symlinked trash directory or source projection (H-2 sec). Both
write_receiptandtrash_projectionnowsymlink_metadata-check their inputs and refuse if either is a symlink - prevents an attacker who can write the.forgeplan/tree from redirecting rename targets outside the workspace.
Fixed - Correctness
Section titled “Fixed - Correctness”mark_consumedfailure silently left receipt unconsumed (C-1 logic, FR-011). A subsequentundo_lastre-applied the same receipt (harmless for delete, misleadingOkfor supersede/deprecate).apply_restorenow propagates the error with clear manual-recovery instructions.- Receipt ID collision at 1/65 536 under concurrent deletes (H-1
logic). Replaced the 16-bit nanos-mask suffix with a 32-bit PRNG
(
rand::random::<u32>()) → effective collision probability ~1/4 294 967 296. - Title edits after creation broke projection resolution (H-2
logic).
soft_delete_capturenow scans<kind>/<ID>-*.mdon the filesystem and uses the real filename, falling back to current-title slugify only if scan fails. Delete no longer silently leaves an orphan markdown thatscan-importwould resurrect. - Supersede/deprecate restore on collision branch overwrote a
different artifact (H-4 logic). Now refuses if
existing.kind != snapshot.kindwith an explicit error suggesting manual resolution.
Hardened
Section titled “Hardened”- Parent-directory fsync after
write_receiptfile sync (ext4/xfs durability -fsync(file)alone can lose the directory entry on a hard crash). is_cross_devicenow handles WindowsERROR_NOT_SAME_DEVICE(17) in addition to UnixEXDEV(18).
Verification
Section titled “Verification”- 1261 tests pass / 0 fail (+6 new regression tests covering each
finding: traversal-projection refusal,
mark_consumedpropagation, kind-mismatch refusal, 32-bit PRNG uniqueness, symlinked-trash refusal, symlinked-source refusal). cargo clippy --workspace --all-targets -D warnings: clean.cargo fmt --check: 0 diffs.
Refs: PRD-055 post-ship audit (4-agent panel: code-reviewer, security-auditor, rust-pro, architect-reviewer).
[0.22.0] - 2026-04-18 - Reversible destructive ops (PRD-055 complete)
Section titled “[0.22.0] - 2026-04-18 - Reversible destructive ops (PRD-055 complete)”Completes the undo story started in v0.21.0. Every destructive operation -
delete, supersede, deprecate - is now recoverable via a single tool
call within a 30-day TTL window.
Added - wrapping of destructive ops (PRD-055 increment 2)
Section titled “Added - wrapping of destructive ops (PRD-055 increment 2)”All three destructive tool handlers now go through soft_delete_capture
before mutating the store:
forgeplan_delete: writes a receipt with full snapshot (body, metadata, outgoing + incoming relations), moves the markdown projection into.forgeplan/trash/, then removes the store row.forgeplan_supersede: writes a receipt capturing the original status, then applies the lifecycle transition. Projection stays in place.forgeplan_deprecate: same pattern.
Crash invariant (PRD-055 ADR #4): receipt is written BEFORE the store mutation. A crash in between leaves a harmless orphan receipt which TTL purge later collects.
Every destructive-op response now includes a receipt_id field and a
_next_action hint pointing at forgeplan_undo_last or
forgeplan_restore <id>.
Added - restore and undo-last tools (PRD-055 increment 3)
Section titled “Added - restore and undo-last tools (PRD-055 increment 3)”forgeplan_restore id=<ID>- finds the newest non-consumed receipt for that ID, applies restore. For delete: recreates the store row, moves the projection back, re-links all captured relations. For supersede/deprecate: resets status to pre-op value and drops the new link. Orphaned relation targets are tracked inrelations_skipped.forgeplan_undo_last within_hours=<N>- finds the newest non-consumed receipt across all artifacts within the window (default 24h, max 720h), applies the same restore logic. Never guesses: returns an explicit error if the window is empty.
Transactional semantics (FR-011): receipt is marked consumed LAST. Collision handling (R-3): restore refuses if an artifact with the same ID already exists in the store.
Verification
Section titled “Verification”- 1255 tests pass / 0 fail (+19 undo tests across receipt and restore modules, +4 integration tests).
cargo clippy --workspace --all-targets -D warnings: clean.cargo fmt --check: 0 diffs.
User-visible workflow
Section titled “User-visible workflow”Before: forgeplan_delete PRD-048 → artifact permanently gone.
After:
forgeplan_delete PRD-048 → receipt written, projection moved to trash, store row removed → response: receipt_id + hint "reversible via forgeplan_undo_last"
forgeplan_undo_last → PRD-048 restored with identical body, metadata, relationsRefs: PRD-055 (now functionally complete), PRD-054.
[0.21.0] - 2026-04-18 - Activity log + soft-delete receipt infrastructure
Section titled “[0.21.0] - 2026-04-18 - Activity log + soft-delete receipt infrastructure”Builds on the v0.20.0 tool-quality work. Adds two pieces of observability and recovery infrastructure that make agent-driven use of forgeplan materially safer.
Added - Activity log (PRD-054)
Section titled “Added - Activity log (PRD-054)”Every MCP tool invocation is now recorded in an append-only JSONL file at
.forgeplan/logs/tools-YYYY-MM-DD.jsonl. One file per UTC day, daily
rotation happens automatically on first write. Each entry captures
timestamp, tool name, SHA-256-prefix hash of args (args content is
NOT logged by default - prevents secrets in titles / descriptions from
leaking into the log), duration, status (ok / tool_err / rpc_err),
workspace path, and optional client info.
Two new MCP tools surface the log:
forgeplan_activity- query withsince_hours(default 24, max 720),tool(comma-separated filter),status,limit(max 5000). Returns entries, warnings about corrupted lines, and a_next_actionhint.forgeplan_activity_stats- per-tool aggregates (count, err_count, p50/p95/total ms), sorted by total time descending.
Dispatch wrapper sits on top of rmcp’s ToolRouter.call - any existing
or future tool is logged automatically without per-handler changes. Log
writes fire-and-forget via tokio::spawn so the tool response path adds
zero latency. Log-write failures are observed via tracing::warn and
never fail the parent tool call.
CLI parity is planned for a future release.
Added - Soft-delete receipt infrastructure (PRD-055, increment 1 of 3)
Section titled “Added - Soft-delete receipt infrastructure (PRD-055, increment 1 of 3)”Foundation for reversible destructive operations. New module
forgeplan-core::undo provides the receipt data model, JSON
serialization, trash directory layout, TTL-based lazy purge, and
cross-platform filesystem rename with fallback to copy+remove for
cross-device moves.
Does NOT yet wire into forgeplan_delete / forgeplan_supersede /
forgeplan_deprecate - those still do hard-delete. Wiring is
planned for v0.22.0. This release ships the underlying primitives so
integration tests and tooling can exercise the receipt format now.
Key design decisions documented inline in PRD-055:
- Move-to-trash plus receipt, not store tombstone column
- JSON format, not binary
- One receipt per operation (inspectable history)
- Write receipt BEFORE mutation (crash invariant - orphan receipts are harmless, but the reverse order would cause data loss)
- Lazy TTL purge on invocation, no background daemon
- Relations captured in receipt, not re-derived on restore
Default TTL: 30 days. Configurable per-workspace once the wiring lands in v0.22.0.
Changed - Developer experience
Section titled “Changed - Developer experience”- Pinned Rust toolchain to 1.95 via
rust-toolchain.toml- prevents the class of bug wherecargo clippypasses locally but fails on CI due to a version skew between developer and runner (hit PR #178 on first push withclippy::unnecessary_sort_by).
Verification
Section titled “Verification”- 1245 tests pass / 0 fail (+31 new across activity + undo modules, of which 18 in activity and 13 in undo).
cargo clippy --workspace --all-targets -D warnings: clean.cargo fmt --check: 0 diffs.- E2E smoke on fresh tempdir: activity log writes 3 JSONL lines across 3 tool calls, no secret content leaks into log body.
Scope trade-offs
Section titled “Scope trade-offs”forgeplan_restore and forgeplan_undo_last MCP tools are deferred to
v0.22.0 along with the wrapping of destructive ops. Shipping the
primitives now exercises the receipt format under real CI and lets the
wiring increment land as a cleaner, smaller PR.
Refs: PRD-054, PRD-055.
[0.20.0] - 2026-04-18 - MCP silent-failure hotfix + tool quality (3-round audit)
Section titled “[0.20.0] - 2026-04-18 - MCP silent-failure hotfix + tool quality (3-round audit)”Originally a v0.19.1 hotfix for two independent silent failures blocking
MCP adoption in v0.19.0 - users who ran brew install forgeplan && forgeplan mcp install --client claude && restart Claude Code got
zero tools visible. Grew via three full audit rounds into a feature
release: every tool now carries workflow guidance and is hardened
against invisible prompt-injection.
Fixed - the original hotfix
Section titled “Fixed - the original hotfix”ServerCapabilities::default()returned empty{}- per MCP spec, clients skiptools/listwhentoolscapability is absent. All 45 tools invisible afterforgeplan mcp install. Fix:ServerCapabilities::builder().enable_tools().build()..mcp.jsoncarriedtransport: "stdio"field - not MCP spec, silently ignored by Claude Code, compounded the capability miss. Fix: droptransport;smart_mergenarrowly removes legacy entries.
Added - tool discoverability
Section titled “Added - tool discoverability”- ToolAnnotations on all 45 tools -
title,readOnlyHint,destructiveHint,idempotentHint,openWorldHint. Claude Code now auto-approves safe reads and warns before destructive ops. - Schema enums × 6 -
relation,kind,status,journal.kind,phase,gradeswitched from prose strings to JSON-Schema enums. LLMs constrain-sample against these so"informs"is verbatim, not paraphrased as"inform". _next_actionon 42/42 tools - 34 as structured JSON field on success, 8 as_next_action:prose in error text (viaerr_hinted/artifact_not_found/llm_err). Every response - success or error - tells the agent what to do next.
Security - invisible prompt-injection hardening
Section titled “Security - invisible prompt-injection hardening”sanitize_for_hint()strips structural punctuation plus invisible Unicode classes: zero-width joiners, bidi overrides/isolates, BOM, soft-hyphen, variation selectors, tag characters. Applied at everyformat!splice of user-controlled values. 15 new unit tests.llm_errno longer echoes upstream error bodies - provider errors sometimes include request IDs and header fragments; now logged only.
Fixed - silent-failure class
Section titled “Fixed - silent-failure class”unwrap_or(Value::Null)replaced withhinted_result<T>()helper - serialization failure surfaces asMcpError::internal_errorinstead of aNullresponse.forgeplan_blocked.blocked_countwas reportingcycles.len()instead ofblocked.len(); fixed.forgeplan_fpf_checkdead match arms ("deny"/"block"/"warn") - core emitsEXPLORE/INVESTIGATE/EXPLOIT; match rewritten.- Race-condition panic in
forgeplan_linkwhen artifact deleted concurrently - fixed.
Refs: PROB-039, PRD-048, three audit rounds evidence.
[0.19.0] - 2026-04-16 - One-command MCP install + Clippy 1.95 + website i18n RU
Section titled “[0.19.0] - 2026-04-16 - One-command MCP install + Clippy 1.95 + website i18n RU”Feature release: forgeplan mcp install for frictionless AI agent setup,
website i18n with 144 Russian pages, Mermaid diagrams, and Rust 1.95 clippy compliance.
forgeplan mcp install --client claude|cursor|windsurf- one-command MCP server configuration. Smart-merge replacescommand/args/transportwhile preserving userenv(API keys, custom paths). Idempotent, safe to re-run. Cross-platform: macOS / Linux / Windows.forgeplan mcp serve- alias forforgeplan serve(MCP convention naming).--use-name [forgeplan|fpl]- write the short binary name instead of absolute path. For terminal-based clients where$PATHis set up.--scope user|project- install to user-global (~/.claude.json) or project-local (./.mcp.json).--dry-run- preview proposed changes without writing.--binary-path- override binary path with validation (absolute, exists, executable, no control chars / bidi overrides).- Binary detection prefers PATH-resolved symlink over
current_exe(). Fixes Homebrew upgrade breakage where versioned Cellar path goes stale. - Symlink rejection in atomic write - prevents
.mcp.json -> /etc/passwdtype attacks via pre-planted symlinks. - Website i18n - 144 Russian pages via Starlight native i18n + Gemini 2.5 Flash batch translation. Language switcher EN↔RU. (PRD-047)
- 6 Mermaid diagrams in EN+RU docs (pipeline, ADI, R_eff, tutorial, lifecycle, graph).
- MCP setup guide -
docs/guides/mcp-setup(EN + RU). Covers quick install, smart-merge, troubleshooting. - Website UI polish - wider search bar, compact theme toggle + language
switcher, Cloudflare
/ru/redirects.
- Clippy 1.95 compliance -
collapsible_match(8 occurrences inforgeplan-core) andunnecessary_sort_by(3 occurrences) converted to match guards andsort_by_key(Reverse(...)). - PROB-026 tag canonicalization + PROB-027 reindex without
lance/. - PROB-035 + PROB-036 deprecated (resolved by PRD-046 + PRD-047).
- 1194 tests (+44 from v0.18.0 baseline 1150)
- 294 website pages (+2 from v0.18.0 baseline 292)
- 0 clippy warnings on Rust 1.95 (stricter than 1.91 / 1.94)
- PRD-048 R_eff: 0.80 (Adequate), EVID-075 active
- 2 adversarial audit rounds (4 agents), 10 CRITICAL/HIGH/MEDIUM findings, all fixed
[0.18.0] - 2026-04-11 - Production BM25 + Russian morphology + quality gates
Section titled “[0.18.0] - 2026-04-11 - Production BM25 + Russian morphology + quality gates”Feature release upgrading the search engine and codifying quality rules.
- Production BM25 engine (
bm25crate v2.3.2). Replaces 140 LOC hand-written BM25 with production-quality implementation including stemming, stop-word removal, and unicode segmentation. - Russian morphology support.
LanguageMode::Detectwithwhichlangauto-selects Snowball stemmer per document/query. “аутентификация” now matches “аутентификации” via shared stem. 17 languages supported. - Template noise stripping.
strip_indexing_noise()removes YAML frontmatter, template placeholder lines{...}, markdown table rows|...|, and HTML comments from BM25 index. Fixes false positives whereforgeplan search "auth"matched unrelated PRDs viaauthor:in frontmatter. - O(N) batch search. Single-pass
search_scores()replaces O(N²) per-record.score()calls. 193-artifact corpus: 0.23s. - 8-point verification checklist in CLAUDE.md - mandatory before every commit/PR. Covers: unit tests, edge cases, E2E on fresh workspace, verbatim template test, dogfood stress test, regression guard (A/B), negative tests, cross-language verification.
Changed
Section titled “Changed”- Health debt resolved: 8 active stubs deprecated/superseded, 5 duplicate EVID pairs deprecated, 3 orphan NOTEs linked. Health dashboard reports “Project looks healthy!” with zero warnings.
- 1150 tests pass (+19 from v0.17.2 baseline 1131).
- New regression tests: Russian morphology (2), English stemming (1), plural forms (1), stop-word resilience (1), noise stripping (2), frontmatter false-positive guard (1).
[0.17.2] - 2026-04-09 - Quality hotfix: scoring & search integrity
Section titled “[0.17.2] - 2026-04-09 - Quality hotfix: scoring & search integrity”Fixes five real bugs found during a dedicated /forge E2E verification sprint on a fresh workspace (separate from the dogfood audit that produced v0.17.1). Each bug was reproduced on the v0.17.1 release binary before fixing, and the fix verified A/B on an identical workspace.
The headline fix is PROB-034 (CRITICAL) - a silent trust-calculus regression present since v0.17.0 that inflated R_eff scores across every workspace using the default evidence template.
-
PROB-034 (CRITICAL) - Multi-line HTML comments shadowed real structured fields in
extract_field.crates/forgeplan-core/src/scoring/evidence.rs::extract_fieldskipped only lines literally starting with<!--, not lines inside a multi-line comment block. The evidence template ships with a help comment:<!--verdict: supports | weakens | refutescongruence_level: 0 | 1 | 2 | 3 (CL3=same context, CL0=opposed)-->The placeholder line
congruence_level: 0 | 1 | 2 | 3 (CL3=...)does not start with<!--, so the parser matched it,parse::<u8>()failed on the non-numeric string,explicit_clbecameNone, and the realcongruence_level: Xin the Structured Fields section below was never inspected. Every evidence artifact ever created via the default template silently reset to CL3 (no penalty), artificially inflating R_eff across every workspace since v0.17.0.- Fix:
extract_fieldnow implements a proper multi-line comment state machine - tracks anin_multiline_commentboolean, skipping all lines between<!--and-->when they span multiple lines. - Affects all fields parsed via
extract_field:verdict,congruence_level,evidence_type,source_tier- all were silently defaulted. The fix is transitive. - A/B verification on
/tmp/fp-prob034-reprowith identical workspace: v0.17.1 binary →r_eff=1.0000, CL=3; v0.17.2 binary →r_eff=0.1000, CL=0(correct for explicit CL0 evidence). - Regression tests:
extract_field_ignores_multiline_html_comments,extract_field_multiline_comment_nested_fields_all_ignored.
- Fix:
-
PROB-030 - BM25 prefix queries returned 0 results.
crates/forgeplan-core/src/search/smart.rscomputedkeyword_score(substring match) for diagnostics but passed onlybm25_normtocombined_score. BM25 is token-based, soauthdid not match the tokenauthentication, and prefix queries silently returned nothing.- Fix:
let keyword_channel = bm25_norm.max(kw);- BM25 still wins on exact-token matches (richer signal), but substring fallback kicks in when BM25 returns 0 for prefix queries. - Regression tests:
smart_search_prefix_query_falls_back_to_substring,smart_search_exact_token_still_wins_over_prefix.
- Fix:
-
PROB-031 - CLI
scorecommand had its own divergent evidence parser. The CLIparse_evidence_from_recordinscore.rsduplicated core’s function but with a different default CL (CL0 vs CL3), creating a visible contradiction: display saidCL0 = 0.1while ther_eff_recursiverollup computed1.00via core’s parser. The local CLI parser also did NOT implement the PRD-035 Sprint 13.3 H2 security precedence (min(tier_cl, explicit_cl)), opening a trust-amplification attack surface on the display path.- Fix: deleted the local duplicate and
extract_fieldhelper; importedforgeplan_core::scoring::evidence::parse_evidence_from_record. Display and rollup now read identical values by construction. - Regression test:
score_uses_core_parser_with_cl3_default_when_no_structured_fields.
- Fix: deleted the local duplicate and
-
PROB-032 -
forgeplan searchbreakdown line lied about components. Display showedkw=0.0 sem=0.0 r=0.0 g=0.0while total was 0.57. Caused by PROB-030:kwwas computed but never flowed intocombined_score.- Auto-fixed as side effect of PROB-030. Breakdown now shows real component values.
-
PROB-033 -
forgeplan new evidenceprinted confusing session warning afterforgeplan route. The session state machine attempted aRouting → Evidencetransition, which is disallowed. The file WAS created, but stderr showedSession: Cannot go from 'routing' to 'evidence'- blocking legitimate backfill, audit, brownfield, and evidence-import flows in perception if not in fact.- Fix:
forgeplan new evidenceis now phase-agnostic - it never drives the session state machine. Only decision artifacts (prd/rfc/adr/epic/spec) advance to Shaping phase. Methodology guardrail still enforces atactivatetime via PRD-043 stub detection + validation gates. - Regression test:
new_evidence_works_in_routing_phase_without_session_warning.
- Fix:
- 1137 tests pass (+6 from v0.17.1 baseline 1131).
- 6 new regression tests cover PROB-030 (2), PROB-031 (1), PROB-033 (1), PROB-034 (2).
cargo fmt --checkclean,cargo clippy --workspace --all-targets -- -D warningsclean on both default andsemantic-searchfeature.
Impact
Section titled “Impact”If you are upgrading from v0.17.0 or v0.17.1 and you have evidence
artifacts in your workspace, your R_eff scores were potentially
inflated by the CL3 default (PROB-034). Re-run forgeplan score on
critical PRDs after upgrade - any evidence that explicitly set
congruence_level in Structured Fields will now be honored, and weak
CL values may cause R_eff to drop. This is correct behavior; the
previous values were silently wrong.
[0.17.1] - 2026-04-09 - Post-v0.17.0 dogfood hotfix
Section titled “[0.17.1] - 2026-04-09 - Post-v0.17.0 dogfood hotfix”Fixes two bugs found during the v0.17.0 final dogfood audit when running
forgeplan tree and forgeplan health on the dogfood workspace itself.
PRD-043 detection (Sprint 13.1) correctly flagged the issues but two
upstream bugs prevented them from being auto-resolved.
-
PROB-028 - Phantom rows in
forgeplan tree(PRD-044).reindexPhase 2 (orphan cleanup) previously skipped rows whosekindfield failed to parse viacontinue, letting corrupt/empty kind rows escape trim forever. Additionally, orphan relations whose source or target artifact had been deleted accumulated in the relations table and surfaced as?phantoms in tree rendering.- Fix 1:
Err(_) => continuechanged to treat unparseable kind as a definite orphan (no valid kind means no valid directory means no possible file). Rows with corrupt kind now get trimmed along with normal orphans. - Fix 2: new Phase 3 in
reindextrims orphan relations where source or target no longer exists in artifacts. - Output now reports removal reason:
corrupt kind fieldvsno .md file foundvsorphan relation (source|target|both missing). reindexoutput gains a new counter: “K removed, N orphan relations”
- Fix 1:
-
PROB-029 -
forgeplan healthverdict contradicted its own warnings (PRD-045). Sprint 13.1 addedactive_stubsandpossible_duplicatesdetection (PRD-043) and wired them into the warning display, but thegenerate_next_actionssummary function was never updated to read those signals. Result: workspace with 8 stubs + 5 duplicate pairs printed “Project looks healthy” at the bottom.- Fix:
generate_next_actionsnow takespossible_duplicatesandactive_stubsas parameters; compute order reshuffled so signals are available before the summary runs. - Next actions for stubs suggest
forgeplan supersede ID --by NEWorforgeplan deprecate ID --reason "abandoned"with the concrete offending ID. - Next actions for duplicates suggest
forgeplan deprecate B --reason "duplicate of A"with the concrete pair IDs. - “Project looks healthy” message only appears when genuinely no warnings of any category exist.
- Fix:
Methodology (NOTE-044 checklist addition)
Section titled “Methodology (NOTE-044 checklist addition)”- Phase 1 Implementation gains new rule: “Every new CLI flag / command
/ config option ships with ALL of these docs (no feature lands
without): clap
--helptext, CHANGELOG entry, CLAUDE.md workflow section if user-facing,docs/methodology/subsection if command-level.” Red flag: a PR adding a flag/command without touching clap help + CHANGELOG is incomplete - block merge.
- 1131 tests pass (+3 from v0.17.0 - PRD-045 verdict aggregator tests)
- 0 warnings on both default and
--features semantic-searchbuilds - Clippy strict (
-D warnings) clean on Rust 1.94 - Dogfood verification:
forgeplan treeon dogfood workspace no longer shows?phantoms;forgeplan healthreports 3 concrete next actions instead of “looks healthy”
- PROB-028 (phantom rows reindex bug)
- PROB-029 (health verdict logic bug)
- PRD-044 (reindex trim orphans - closes PROB-028)
- PRD-045 (health verdict aggregator - closes PROB-029)
- NOTE-044 (sprint checklist framework, docs completeness rule added)
- NOTE-046 (dogfood cleanup task - duplicate EVID pairs, deferred)
- NOTE-047 (dogfood cleanup task - false-active stubs, deferred)
0.17.0 - 2026-04-08 - EPIC-003: Search, Discovery, Intelligence
Section titled “0.17.0 - 2026-04-08 - EPIC-003: Search, Discovery, Intelligence”First release of EPIC-003. Adds keyword + semantic search, brownfield discovery, scoring/routing intelligence, FPF rule surface, methodology integrity gates, and reusable sprint checklist framework.
Highlights
Section titled “Highlights”- 1109 tests passing (+280 from v0.16.0), zero failures, zero warnings on
both default and
--features semantic-searchbuilds - 7 PRDs shipped across 8 sprints (13.0 → 13.7 + post-closeout hotfix)
- FPF Knowledge Base gains semantic vector search via BGE-M3 embeddings
- Methodology integrity gates catch stub artifacts, duplicates, orphans
- Sprint checklist framework (NOTE-044) to prevent regression in future releases
Smart Search v2 - PRD-039, Sprint 13.2
- BM25 ranking replaces substring scoring in
forgeplan search - Composable filter DSL (
--status,--depth,--since,--with-evidence) - 1-hop graph neighbor expansion (opt-out via
--no-expand) - Extended MCP
searchtool parameters
Brownfield Discovery - PRD-035, Sprints 13.3 + 13.4
- Tags system in frontmatter + LanceDB schema (v3→v4 migration)
forgeplan tag/untagcommands +list --tag key=valuefilter- SourceTier → Congruence Level mapping (T1→CL3, T2→CL2, T3→CL1)
forgeplan discoverCLI command (session state machine)- MCP tools:
forgeplan_discover_start,_scan,_next,_status
Scoring & Routing Intelligence - PRD-040, Sprint 13.5
- Routing Skills Memory with exponential decay (90-day half-life)
- R_eff confidence intervals heuristic (widens with sparse/stale evidence)
forgeplan scoredisplays[low - high]interval alongside point estimate
FPF Rules Surface - PRD-041, Sprint 13.6
forgeplan fpf rules- action-grouped tree (EXPLORE/INVESTIGATE/EXPLOIT) with--flatand--jsonmodesforgeplan fpf check <id>- per-artifact rule match introspection with--verbose(unmatched list) and--json(canonical shape)- MCP tools:
forgeplan_fpf_rules(withaction/name/summary/sourcefilters) andforgeplan_fpf_check
FPF KB Vector Search - PRD-042, Sprint 13.7 (supersedes PRD-018)
embeddingcolumn (FixedSizeList<Float32, 1024>) added tofpf_spectable, backward-compatible migration viaNewColumnTransform::AllNullsLanceStore::search_fpf_by_vector(query_vec, limit)using LanceDB nativevector_searchwithDistanceType::Cosineforgeplan fpf search <query> --semanticCLI flag- MCP
forgeplan_fpf_searchgainssemantic: Option<bool>param - Two-layer graceful fallback - compile-time (feature off) + runtime (Embedder init fail / encode fail / vector search fail) → warning + keyword fallback
- NaN/Inf rejection at
insert_fpf_chunksboundary - Runtime
Embedder::dim() == EMBEDDING_DIMassertion
Methodology Integrity - PRD-043, Sprint 13.1
- Duplicate guard (
forgeplan newdetects existing similar artifacts) - Stub detection (blocks
activateon unfilled templates) - Health detection (
forgeplan health --ciexits non-zero on blind spots) - MCP warning envelope for methodology violations
- State machine:
Phaseenum withvalidate_transitionenforcing Idle → Routing → Shaping → Coding → Evidence → PR for Standard+ depth
Sprint Checklist Framework - NOTE-044 (post-closeout deliverable)
- Reusable quality gate for every future sprint, 7 phases with red flags
- Encodes lessons from Sprint 13.7 retrospective
- Explicit “what not to skip” checklist for planning / implementation / audit / fixer / re-audit / manual UX / closeout / meta phases
Changed
Section titled “Changed”- FPF KB schema: backward-compatible migration adds
embeddingcolumn (nullable). Existing workspaces work unchanged; re-ingest to populate embeddings. - MCP tool registry expanded from ~37 to ~47 tools
- CI linter:
forgeplan health --ci+validate --ciland (Sprint 11.3) - FpfStorage trait extended -
insert_fpf_chunksnow accepts optional embeddings;search_fpf_by_vectoradded to trait (no default impl, forcing explicit backend choice per Sprint 13.7 hotfix re-audit) - CLI
fpf searchinput validation - empty / oversized (>8192 chars) queries rejected before store access - MCP param length bounds on
forgeplan_fpf_searchandforgeplan_fpf_rules(id ≤128, name ≤128, action ≤64, source ≤16) - ANSI strip on user-supplied query echoed in error messages
(
No FPF sections match '{}'sanitized against control chars)
Deprecated / Superseded
Section titled “Deprecated / Superseded”- PRD-018 “FPF Knowledge Base - semantic search” - superseded by PRD-042. PRD-018 was a false-active stub with R_eff=1.0 but no real implementation, flagged by Sprint 13.1 methodology integrity work. PRD-042 closes the gap with actual BGE-M3 integration + supersedes PRD-018 to terminal state.
- Sprint 13.1.5 hardening: LazyLock
for check_stub, typedStubReportreturn,forgeplan importgate for active stubs (security bypass closed), configurableIntegrityConfigMCP limits - Sprint 13.1.7 integrity config wiring:
IntegrityConfig::validate()now called by CLI command path;forgeplan healthno longer crashes on minimal configs via#[serde(default)]on top-levelConfigfields - Sprint 13.6 FPF Rules canonical JSON: CLI and MCP now emit identical
{artifact_id, kind, status, matched, unmatched, winning, summary}shape via typedRuleCheckResult, replacing hand-rolledserde_json::json! - Sprint 13.7 post-closeout hotfix (PR #156):
FpfStorage::search_fpf_by_vectoradded to trait (closes asymmetry)- MCP handler integration harness at
crates/forgeplan-mcp/tests/ - Real BGE-M3 end-to-end test (
#[ignore], feature-gated) - Real v3 workspace migration test
- Runtime dim assert +
fpf_spec_schemarustdoc tying 1024 → BGE-M3 InMemoryStore::search_fpf_by_vectorreturnsErr(not silent empty)- Wave 2 completer work re-audited (was originally skipped)
- 1109 tests passing (+280 from v0.16.0)
- Core crate: 897 tests; CLI: 99 + 40 integration; MCP: 15 unit + 7 handler
- 42 MB release binary (strip + lto + opt-level=z)
- ~56 CLI commands, ~47 MCP tools
- 7 new PRDs activated, 1 superseded (PRD-018 → PRD-042)
- Sprint retrospective: 19 debts found, 11 fixed in hotfix, 8 backlog (NOTE-045), 6 process lessons (NOTE-044)
Methodology lessons captured
Section titled “Methodology lessons captured”- Dependent sprint branch base verification - new CLAUDE.md section covering the Sprint 13.1.5 rebase hell that taught us to verify parent branches contain expected commits before spawning teammates
- Sprint Checklist Framework (NOTE-044) - reusable 7-phase gate to prevent planning gaps (was: “user had to ask ‘what did we miss’”)
- Sprint 13.7 Deferred Debts (NOTE-045) - backlog tracking for the 8 non-blocking items that rolled forward from the retrospective
Related PRs
Section titled “Related PRs”PRs #141 → #156. See git log main..release/v0.17.0 for full list.