Repository navigation
DevCouncil v0.2.1
DevCouncil v0.2.1 Release Notes
DevCouncil v0.2.1 and DevMap v0.2.1 are the same product version. The npm package,
the Go host (devcouncil / dev), and the Rust workspace (devmap, dcstore,
dcverify, dcgrep) all report 0.2.1.
This is a consolidation and hardening release. It merges three parallel work
lanes — security remediation, host-hook migration, and DevMap evidence
hardening — into a single line of development, and fixes defects that only
became visible when those lanes were brought together.
Highlights
1. Repository input and verification boundaries
A safe_fs module for the extractor plus write and read gates across the query,
serve, store, and CLI export paths. Command construction in stopgate and
verify is hardened, and each surface carries its own security regression.
Documented in docs/SECURITY_BOUNDARIES.md.
dcgrep, dcstore, and dcverify now handle a closed stdout explicitly: a
broken pipe is reported as failure instead of being mistaken for success. All
three previously exited 101 with failed printing to stdout: Broken pipe.
2. CLI presentation and output reliability
A shared Go console package with unix and non-unix terminal backends, and a
presentation layer used by both the Go host and the DevMap CLI. Every command's
output now flows through one bounded display. Fixes shipped with regressions:
- Skipped verification was reported as
verified/passed=true. It now
returnsskipped/passed=falseand increments
completed_without_verification. - A failed JSON write was reported as success to a closed pipe.
install devmap --dry-run --jsonemitted shell commands before its JSON.export --out - --jsonaccepted two mutually exclusive output formats.- FTS-only
repairproduced no JSON receipt at all. - MCP HTTP could bind TCP and then stall before answering a request when
stderr was full. doctorandpathsno longer execute discovered or configured programs;
binary identity is inspected passively and an unavailable external version is
reported with an explicitprobe_error.- Cancellation (SIGINT) was worded as a ten-minute timeout.
Documented in docs/CLI_PRESENTATION_AUDIT.md.
3. Host lifecycle hooks are retired, with a real compatibility path
devcouncil hook <event> is now an inert compatibility path: it reads no stdin,
touches no project state, starts no subprocess, and exits 0 for every retired
event, including unknown events and flags. devcouncil hook status and
devcouncil hook disable report and remove what earlier versions installed.
--write-gate is retired. It previously claimed containment while ignoring
the request. The flag still parses, so an operator gets a real explanation
rather than unknown flag, and integrate.Run refuses it before performing any
write.
Hook cleanup was substantially hardened — it had been deleting things it did not
own:
- Exact DevCouncil invocations are recognized, including quoted Unix and Windows
executable paths;mydev hookandecho 'dev hook'are no longer matched. - Cursor, Claude, Codex, Gemini, Grok, and OpenCode layouts are all covered.
OpenCode keeps its MCP configuration and foreign plugins. - A file is deleted only when removal of owned entries empties it. An unowned
empty file is left alone. - JSON numbers keep their exact tokens (
9007199254740993was being rewritten
to9007199254740992), unrelated fields and empty groups are preserved, and
original permission bits survive rewriting (0600was being widened to
0644). - The whole selected scope is planned and validated before any file changes.
- Symlinked files and parents, oversized or ambiguous JSON, invalid modes, and
uninspectable hook shapes are refused rather than reported as clean. - Concurrent cleanup is serialized by an exclusive lock with before/after
identity checks, unique staging files, recoverable backups, and truthful
partial receipts. - A customized hook carrying a DevCouncil name produces a refusal requiring
review instead of a silent deletion.
disable hooks --dry-run no longer mutates settings; every cleanup alias shares
one set of validated modes, and conflicting or incomplete arguments are refused.
Cleanup also accepts --project-root and --client to scope what it touches.
Documented in docs/hook-migration-audit.md.
Security impact: no authorization, lease, verification-mode, host-permission,
credential, or billing policy changed. Retired event compatibility performs no
verification and emits no approval decision. MCP policy remains its own
contract. Unsupported hook enforcement is now reported explicitly rather than
implied by a successful installer receipt.
4. DevMap store retention
Sixty-four generations that each renamed one source file retained all 64
historical interned paths, even though ordinary retention kept only the last two
payload sets — nothing retired paths rows, and PathRanks::read sorts that
whole table for edge queries, so rename churn also increased read work.
The generation prune transaction now retires a path ID only when no node,
extraction payload, file membership, file digest, edge source, or edge target
still references it. Retained generations and edge-only endpoints keep their IDs,
and the transaction protects the prune from partial retirement on error. No
schema, retention-window, confidence, access, or dependency change. Six tests in
retention_churn.rs cover it; five failed against unmodified production code.
Documented in docs/devmap/STORE_FOOTPRINT_AUDIT.md.
5. DevMap update profiling
The scan/extract headline had been folding writer-lock waiting, store opening,
queue reconciliation, discovery, previous-hash loading, cached extraction,
affected-write-set computation, and resolver indexing into a single number, so a
slow edit could not be attributed. Those operations are now bracketed
individually: writer:wait, store:open, pending:reconcile, scan:read,
scan:previous_hashes, extract:files, affected:closure, resolver:index.
This changes measurement granularity, not the update algorithm. Work that did
not run stays absent rather than being reported as zero — an unchanged build
reports no extraction, closure, or resolver indexing, and --full reports no
affected-closure span. A failed store open is timed without claiming later
phases executed.
Documented in docs/devmap/UPDATE_PROFILE_AUDIT.md.
6. Attribution, extraction, and benchmark evidence
Receiver and type-position attribution work in the resolver, extractor cache and
embedded-script coverage, and soak tooling with a query checker. The competition
benchmark harness and its dated run are tracked in full — the raw per-run
stdout/stderr, the audit scripts that parse them, and artifact-sha256.json —
because REPORT.md links those outputs as failure evidence and the cited
correctness figures are only checkable against the run that produced them.
Documented in docs/devmap/ATTRIBUTION_AUDIT.md and
docs/devmap/BENCHMARK_HARDENING_AUDIT.md.
7. devmap paths no longer re-hashes every binary it finds
devmap paths --json is the first command the generated agent guide tells an
agent to run, and it hashed every devmap binary it discovered on every
invocation — 63.5 MiB of SHA-256 per call for a single installed release. The
answer changes only when a binary is rebuilt, so it is now memoised on
(path, size, mtime, ctime).
Measured interleaved on an idle machine: wall 0.302s -> 0.073s and CPU
0.296s -> 0.072s, both 4.1x, with the cold and warm distributions disjoint.
Cold wall and cold CPU agree to within 2%, so what was removed is the hashing
itself, not I/O. Six rounds confirm cold and warm answers are byte-identical.
The key derivation has one owner, devmap_query::stat_memo, lifted out of
freshness.rs rather than written a second time: a memo that hashes correctly
but keys carelessly returns a stale digest as a fresh one, and no caller can
tell.
8. Four wall-clock test assertions now measure the code, not the machine
Four tests were red on a healthy tree — worse than no test, because they teach
the reader to skip the result. None was a code defect; all four measured load.
test_large_repo_stressheld four bare< 2.0sbudgets. The same 1,000-file
extraction measures 141 ms alone and 2,372 ms under full-suite contention, a
17x load artefact, so the constant was never a property of the kernel. It is
now a self-calibrating linearity budget — the pipeline runs at 100 files and
again at 1,000, and each phase is held to 4x a linear extrapolation of the
small run, floored at the old 2 s. Load moves both measurements together and
cancels. Never stricter than what it replaced, only harder to fool.churn_past_the_cap_costs_its_bytes_not_the_deadlineasserted< 3sagainst
aCHURN_DEADLINEof 10 s and failed at 6.5 s with the property it names
intact. It now compares againstinventory::CHURN_DEADLINE.the_directory_cap_stops_the_walk_and_reports_both_numbersasserted
directories_visited == WALK_DIR_CAP(20,000), but the walk ends on the cap
orWALK_DEADLINE(5 s). Now<=; the exact count is still asserted by
fallback_entry_frontier_and_deadline_limits_are_honest.live_rows_adapt_after_a_real_terminal_resizeasserted "some row > 80 cells"
at 120 columns, which measured which phase label happened to be current. It
now asserts the renderer's own content-independent breakpoints: 6/6, from 2/6.
9. Verification plane wires dcverify for rigor gates (TASK-P7-1)
verify.Run now spawns the dcverify binary (via Go client dc/dcverify),
executing stub, secret, and diff-coverage rigor checks for both CLI
devcouncil verify and MCP devcouncil_verify_task.
- Honest reporting:
rigor_appliedreports exactly which gates executed,
whilerigor_skipped_reasonrecords why rigor was bypassed (e.g.
dcverify binary not found,coverage profile not supplied). Empty lists
are never laundered into passes. - Outside-repo candidate validation: Binary discovery refuses candidates
inside the analyzed repository (proc.LookPathOutside), preventing untrusted
code under review from acting as its own judge. - Hard-safety enforcement: An unexecutable scanner
(rigor_check_unavailable) is classified as a hard safety refusal rather than
an ignored finding. - MCP repair actions reconciled:
AllowedNextToolsForVerifynow names the
eight tools actually served by DevCouncil MCP, fixing references to
non-existent tool names (GAP-P7-NEXT-TOOLS-DRIFT). - Retired dead switches: Retired unused verification flags
(enforce_coverage_on_hardand unused verification switch) from
flags/catalog.go.
10. Dedicated bounded subprocess runner (dc-proc)
Subprocess execution logic was extracted from devmap-extract into a standalone
crate rust/dc-proc, shared with dc-verify:
- Process group termination ensures child process hierarchies do not survive
expired deadlines. - Concurrent stderr and stdout draining prevents pipe buffer exhaustion deadlocks
when subprocesses write extensive diagnostic output. - Byte-bounded captures enforce memory safety with explicit errors rather than
silent data truncation.
11. Complete host adapters and --write-gate retirement
- Host adapter implementations completed across all supported clients with
strict containment to authorized paths. --write-gateis retired with an explicit refusal on invocation, replacing the
previous no-op behavior with honest operator guidance.
12. Extractor and route resolution accuracy
- Python route extraction recognizes decorated classes (
bp.postetc.) as route
handlers. - Route handler receivers are extracted directly from AST names rather than
assumed. - Excluded outbound HTTP client calls from false-positive route detections.
13. Visualizer DOM construction security
- Flag-filter rows in the visualizer UI are constructed directly as DOM nodes
with a single unified escaping sink, preventing HTML injection.
14. Competition benchmark rerun evidence
- Includes raw stdout/stderr logs, parsing scripts, and
artifact-sha256.json
for the v0.2.1 competition benchmark rerun across version-control operations.
Upgrade notes
--write-gateis refused. If you relied on it, use MCP policy and
verification explicitly; the flag's previous behavior did not provide
containment.- If an earlier DevCouncil wrote lifecycle hooks into your host configuration,
devcouncil hook statusreports them anddevcouncil hook disableremoves
them. Use--dry-runfirst; it is now genuinely read-only. dcverifyis automatically spawned duringdevcouncil verifyand
devcouncil_verify_taskwhen present inPATHoutside the analyzed repository.
Ifdcverifyis not installed, the verification receipt explicitly records
rigor_skipped_reason: "dcverify binary not found".
Consolidation
This release merges three parallel lanes that had all grown from the same base
commit and edited the same files. Two defects existed only in the combination
and could not be seen from any single lane:
Resolver::resolve_allgained aResult<_, ResolutionLimitError>return in
one lane while another lane added tests written against the old signature.- Retiring
--write-gateleft three documents describing a flag that was
"ignored" when it is now refused before any write.
Both are fixed here.
Verification
Gates run against this tree:
cargo fmt --all --check— cleancargo clippy --workspace --all-targets -- -D warnings— cleancargo check --workspace --all-targets— cleancargo test --workspace— 302 binaries, 3229 tests, 0 failuresgo build ./...andgo test ./...— 27 packages, exit 0gofmt -l— cleannpm run ci:local— clean
live_rows_adapt_after_a_real_terminal_resize was previously called out here
as load-sensitive. Section 8 replaced that assertion with the renderer's own
breakpoints, which are content-independent; it no longer depends on machine
load.
After installing:
devcouncil --version # devcouncil 0.2.1
dev --version # devcouncil 0.2.1
devmap --version # devmap 0.2.1