Knowledge graph — see, ask, and measure how spec, code, tests, and docs connect#222
Merged
Conversation
Derive three reverse maps from the spec's forward edges, memoized per Spec instance via a WeakMap (no Spec mutation, 0 bytes on disk): - dependents: featureId -> direct dependents (inverse depends_on) - moduleOwners: module path -> owning feature ids (many-to-many) - testRefCitations: test path -> citing feature ids (anchor-stripped) The backlink seam the graph layer (blast-radius queries, doc linking, exports) reuses. pruneToFeature only walked depends_on upward; this is the reverse direction. Tests authored impl-blind (4/4 green). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
buildImpactSlice + clad_get_impact (MCP) + clad impact (CLI): the backward complement of clad_get_context. Resolve a feature id/slug or a module path (fanning out through the many-to-many module owners) and walk the reverse-index dependents to return the blast radius — impacted features, scenarios at risk, the deduped regression test set, and the modules in the radius. Bounded by optional depth, deterministic. Live: `clad impact src/spec/load.ts` → 4 owners, 111 impacted features, 115 regression tests. Logic tested impl-blind (4/4); MCP integration tested (clad_get_impact). Glossary + verb-list updated for the new surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…f643e extractDocReferences scans docs/**/*.md (excluding fixture/benchmark dirs, stripping code spans) for F-id references and resolved relative .md links; clad sync materialises spec/_doc-links.yaml (Tier C, the greppable "which docs explain feature X" index). New DOC_LINK_INTEGRITY detector (#38): a dead relative .md link is an error; a scoped doc's unresolved F-id is a warn. Per-file `clad-doc-links: ignore` opt-out for teaching docs that use illustrative ids (spec-ids-multi-dev, ssot-testing). The "all documents connected, always current" half of the graph, made mechanical. Detector green on cladding-self (scoping = zero false-RED). Logic + detector tested impl-blind (6/6). Detector count 37→38 across prose claims; A/B reports regenerated for the new info finding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
src/graph/: buildGraph materialises ONE typed graph (feature/module/test/ scenario/capability/doc nodes; depends_on/touches/covers/binds/implements/ references/links edges) from spec + reverse-index + doc-links. Renderers: mermaid + DOT + JSON (stdout) and an Obsidian vault (one note per node with [[wikilinks]] + Backlinks) — see the graph in best-in-class viewers, no bespoke UI. `clad graph stats` ranks hubs by degree (what's load-bearing). `--focus <q> --depth N` exports a legible neighborhood subgraph. Declared a `graph` foundation layer in architecture.yaml (pure spec reader). Live: 706 nodes / 1247 edges; top hub src/cli/clad.ts (degree 33). Pure functions tested impl-blind (6/6). Verb count 19→20; glossary + SVG diagram counts (→38 detectors) updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ndexes - spec/capabilities.yaml: new `knowledge-graph` capability (Tier B) grouping the four graph features (F-ee47fc2b, F-7794a6bc, F-ee5f643e, F-569f4b37). - CHANGELOG [Unreleased]: knowledge-graph entry, framed as traceability/ retrieval (NOT correctness — honest per the A/B record). - clad sync refresh: spec/index.yaml, spec/_doc-links.yaml, attestation re-stamp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… F-02343cd1
The graph, seen our own way (more than the spec asked):
- model: GraphNode.tier (A/B/C/D) classified deterministically — features/
scenarios=A, capabilities=B, docs by first-line `Cladding · Tier X` banner
(+ known-filename fallback), modules/tests=code (no tier). Feature labels now
show the slug (slug ?? title ?? id); full title kept as `detail` for hover.
- render: getTierColor + getTierLegend; tier flows into json, obsidian
frontmatter, and mermaid (per-tier classDef coloring — bonus).
- viewer-shell + src/graph/viewer/{app.js,styles.css}: toHtmlShell emits ONE
self-contained, offline, double-clickable .html — a hand-rolled, zero-dep
canvas force-directed renderer (NOT a vendored 80KB lib: truest to the
zero-dep ethos, full control, ~no supply-chain surface). Tier colors, slug
labels, status opacity, degree-sized hubs, a sidebar (search, kind+tier
filters with counts, tier legend, Calm/Live, theme, labels), hover-
neighborhood, click-to-pin, zoom/pan, localStorage layout persistence.
- `clad graph export --format html --out f.html` (mandatory --out). Build copies
viewer assets to dist/viewer/ (schema.json precedent). eslint-ignored.
Live: 710 nodes / 1259 edges → 213KB self-contained .html, byte-deterministic,
zero external requests. Tests authored impl-blind (3/3). gate GREEN.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…64a5c159 The graph as a LIVE view, not a re-exported snapshot (the user's reframe: the graph is a pure derivation of the spec, so build the view once and let it auto-update as development proceeds). - src/cli/graph-serve.ts: `clad graph serve [--port]` — a stdlib node:http server (zero deps). GET / serves the viewer (with an injected SSE reload snippet), GET /graph.json recomputes buildGraph on EVERY request (always current — no stale-trap), GET /events is a text/event-stream channel. node:fs.watch on spec/ + docs/ broadcasts a debounced refresh → open browsers auto-reload. Hardened: headersSent-guarded error path + closeAllConnections so it shuts down cleanly (and survives EventSource disconnects). SSE keep-alive every 30s. - src/serve/server.ts: clad_get_graph MCP tool — agents read the live (optionally focused) graph in one call; never stale. Live: GET /graph.json reflects the current spec (713 nodes after this feature landed). Endpoints + broadcast tested impl-blind (2/2); clad_get_graph tested over MCP. glossary + TOOL_NAMES updated. gate GREEN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ELOG - spec/capabilities.yaml: knowledge-graph now also owns the HTML viewer (F-02343cd1) and live serve (F-64a5c159). - CHANGELOG [Unreleased]: the SSoT-tier-colored viewer + the live auto-updating serve, framed plainly (see/navigate the structure, not a correctness check). - clad sync refresh: index.yaml, _doc-links.yaml, attestation re-stamp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t + sidebar-aware fit The canvas had no CSS width/height, so it fell back to the intrinsic 300×150 and sat hidden behind the 264px sidebar — the graph area rendered blank. Size the canvas to 100vw/100vh and offset fit() by the sidebar width so the graph centers in the visible area. Verified headless: nodes draw (ctx.arc fires 31k× across the settle frames on the 713-node self-graph). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oom — F-8234ec3c
Make it extreme, and alive at rest (the user's ask):
- LAYOUT: a degree-weighted radial galaxy. High-degree hubs are pulled toward
the centre, low-degree leaves toward the rim; charge spreads angularly,
springs cluster the connected — the graph gathers into one circular galaxy
with a bright load-bearing core. Verified on the real 717-node graph: 0
NaN/Inf, hub (deg 35) at dist 412 vs median 987 (central).
- ALIVE AT REST: ambient is now the default — a slow global rotation + node
breathing + flowing edge particles + hub glow pulse, all O(n)/O(edges) draw
(rotation is a free transform; physics stays burst-only). "Calm" freezes it.
- LOOK: additive ("lighter") bloom pass so overlapping hubs build a glowing
core, over a deep-space radial-gradient background. Click-to-focus (persistent
neighborhood highlight + smooth recenter), smooth view lerp, upright labels.
Plus a headless render test (tests/graph/viewer-render.test.ts): stubs canvas/
document/window, runs the real app.js, and asserts it draws nodes, keeps the
ambient loop alive, and settles to finite positions with the hub central —
guarding the "blank canvas" / NaN-blowup regressions deterministically in-gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… F-04f50847, F-af45042a Re-architect the viewer to Obsidian quality, and add the killer that only a spec↔code-connected tool can do. Obsidian-grade (F-04f50847): - Continuous low-alpha simulation with an alphaTarget thermostat: dragging a node reheats so connected nodes follow elastically (real tension); HOVER pauses the sim (motion freezes under the cursor); release decays to rest. Frame-time normalized, slow/calm. Removed the global rotation + edge particles + the forced radial-by-degree layout (→ organic center+charge+link balance). - Four force sliders (중심 장력 / 반발력 / 링크 장력 / 링크 거리) live-bound + persisted. - nodeColor separates all classes: tiers A/B/C/D distinct hues + module(orange)/ test(green)/doc(pink) distinct (were all gray). KILLER — live conformance heal (F-af45042a): - src/stages/graph-health.ts: nodeHealth() runs cladding's drift detectors and maps each finding to its graph node (path→module/test/doc, F-id→feature), worst-severity per node. (Lives in stages/: graph→stages is forbidden, stages→graph is fine.) - clad graph serve: GET /health.json (live, watch-refreshed); the viewer overlays problem nodes (error=red pulse / warn=amber) over the pretty default + an in-sync% pill, and heals smoothly on SSE refresh (viewer self-wires events; the reload injection is gone). Static export embeds a stamped snapshot. - Verified live: /health.json flags the exact features whose modules drifted from attestation; they heal green once the gate re-attests. Galaxy viewer (F-8234ec3c) archived — superseded by F-04f50847 (radial layout, global rotation, particles replaced; bloom + click-focus carried forward). Headless tests rewritten for the new behavior (hover-pause, drag-reheat, color, finite) + a health-mapper test. 1534 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…muddy bloom The dense overlapping 'confetti' is now a breathing web: a collision pass hard-separates overlapping nodes (0 overlapping pairs on the 717-node graph, was a clump), stronger repel + smaller nodes spread it (span ~1350). Edges are colored-by-source and actually visible (the structure reads); the additive bloom (which washed the centre muddy/white) is removed; nodes get a thin bg-colored ring so touching nodes stay crisp; health is a clean pulsing ring not a blob. Force defaults retuned for an even, Obsidian-like distribution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…f7ead0, F-06dfdad6, F-d6b93648) Knowledge-graph session work on feature/ssot-knowledge-graph: - webgl-stellar-viewer (F-77f7ead0): real three.js + UnrealBloom 3D galaxy over the SSoT graph (semantic hue × degree luminosity) + live drift-health overlay, esbuild-bundled offline. Adds a 'skill' node kind (SKILL.md distinct from code). Archives the 2D canvas viewer (F-04f50847, superseded); swaps F-af45042a module. - working-set-assembler (F-06dfdad6): clad_get_working_set — one token-budgeted, code-bearing payload (focus + module code + forward needs + backward breaks + verify + budget). ~5-8x smaller than reading shard+files; clad_get_context frozen. - graph-context-wiring (F-d6b93648): PostToolUse auto impact-card after source edits + ai_hints/persona nudge so the working set is actually used. All three: strict gate GREEN + attested; 1603 tests pass; tsc/eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…6250595, F-2be3e3bb, F-15999130, F-16138071) Four features that make cladding's knowledge graph populated + measurable, on feature/ssot-knowledge-graph. The arc: 4 A/Bs found the graph tools NULL on agent correctness — then re-framing to the real goal (search/context efficiency, not "smarter agents") + fixing the empty-graph root cause turned it positive. - iterative-impact-slice (F-96250595): buildIterativeImpactSlice — seed→widen→stop (coverage/exhaustion/marginal-yield), self-describing depth+stoppedBy+coverage; fixes the fixed-depth-1 narrow-miss. Calibrated on real graph data (2 sims). - infer-depends-on (F-2be3e3bb): clad infer-deps reconstructs feature depends_on from the code import graph — the load-bearing graph edge cladding PRODUCED nowhere (vapt shipped 0 edges → empty graph → the real cause of the A/B NULLs). vapt 0→698. - inferable-depends-on detector (F-15999130): INFERABLE_DEPENDS_ON — single info finding (never blocks, even strict) surfacing the empty-graph gap so infer-deps isn't a latent never-run tool. Detector count 38→39. - graph-efficiency-measure (F-16138071): clad measure — deterministic per-feature search/context efficiency (no agent, no NULL risk). vapt: working-set 4.1x smaller context than naive (shard+all modules); dependency radius + regression set resolved for free. The goal axis, finally quantified positive. Honest framing throughout (docs/ab-evaluation/*.md): the graph does NOT make a capable agent more correct/cheaper (it greps regardless — NULL x4); its value is small context + explicit blast radius + known regression set + a queryable dependency graph, for humans and tools that use it. infer-deps writes nothing (reviewable suggestions only). All gated: 1624 tests GREEN, tsc/eslint clean, strict gate + attested per feature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…drop dead StopReason Read-only adversarial audit of the session found the work correct (4 features conform, tests honest, claims reproduce, no regressions). Two low-severity hygiene fixes, zero functional impact: - docs/ab-evaluation/case-efficiency-measurement.md: the table wrote "3,028 tok vs 14,442 tok = 4.1x (median)" on one line, which reads as a contradiction (14442/3028 ≈ 4.8). Both numbers are correct but are DIFFERENT statistics — 4.1x is the median of per-feature naive÷slice ratios; 3,028/14,442 are independent medians of slice and naive sizes. Split them + added a note so the report can't be misread. (The audit verdict marked 4.1x "VERIFIED" and missed this; caught by hand arithmetic.) - src/optimizer/iterative-slice.ts: removed 'token-budget' from the StopReason union — it was declared but never returned (budget overflow stops at max-depth first). Synced the test's ALLOWED_STOPS list. 1624 tests GREEN, tsc/eslint clean, strict gate re-attested (F-96250595). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…84d0) The final read-only graph-traversal test on the real doverunner-vapt project measured inference precision ~100% but recall ~70-75% — the largest remaining gap being dynamic/runtime imports (importlib.import_module, __import__, getattr-based, require(<expr>)) that static regex extraction cannot see (e.g. catalog/tool_inventory.py). Rather than silently under-report those edges, inferDependsOn now collects such module files into a sorted dynamicImportFiles list so a maintainer knows exactly which files to review by hand. This is an honest-recall surface, NOT a precision change — the static edges from the same files are still inferred unchanged (additive). clad infer-deps prints dynamic_import_files. Verified: cladding-self flags scripts/build.mjs (require). blind tests 3/3 in a separate file (existing 6 untouched); 1627 tests GREEN; tsc/eslint clean; strict gate re-attested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s (F-5b188856) The viewer's node colors had no clear basis: semanticHue let tier win over kind, so one hue tried to encode two axes and collided — feature and scenario both rendered blue, tier-C teal ≈ skill turquoise, and the sidebar showed the same blue labeled both "Spec · sealed" (tier) and "feature" (kind). Tier is derivable from kind, so encoding both was redundant. Hue now encodes KIND only. semanticHue ignores tier; tier moves to the sidebar filter + tooltip. KIND_COL becomes a grouped, simulation-verified palette (all Y≥125 bloom floor, colorblind 4-group separation, hub-whitening distinctness): SPEC = blue family (feature/scenario/capability) module = orange (anchor) · test = green (anchor) DOCS = pink family (doc/skill) — skill is SKILL.md, a document, moved out of the old code-adjacent turquoise. Sidebar: one color legend grouped into spec/code/test/docs zones; the SSoT tier section is a swatch-less filter (checkbox + label only, no empty box). Tier labels simplified to plain words (Spec / Design / Derived / Audit), and code nodes display as "code" in the viewer (the spec's `modules:` data model is unchanged — display label only). Honest residuals (accepted, user-preferred anchors): module orange sits near the 0-45° health-burn arc, disambiguated by the burn's 2.2-3× pulse vs a static node; orange↔green is the textbook red-green colorblind pair but is conceptually adjacent (code↔test) with a luminance backstop. 1630 tests GREEN; tsc/eslint clean; strict gate re-attested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…content-accuracy pass README.ko.html: - enterprise trust/trace/scale hero; new project-map (graph) section with a live galaxy GIF, colour legend, and a clad-graph-serve launch guide - refreshed numbers (39 detectors / 1630 tests / 196 features, 192 done / v0.7.0) - unified plain-declarative voice; blue-primary emphasis (green = pass/success only) - accurate 4-tier SSoT table (intent defined by humans, authored by the LLM per EARS) docs/img/ko/*.svg (8): unified flat blue-token design system (no shadow/pastel/accent bars, no version stamps); every claim verified against code — gate triggers 3/9/15 by cost, authorship human-defines/LLM-writes, segregation-of-duties "aligns with" (not "maps to") EU AI Act/SOX, Tier-B = project-context.md (not ai_hints), runner examples = pure executors docs/img/ko/graph.gif: 880px/8fps live-graph recording (6.9MB) .gitignore: ignore *.mov (local screen recordings) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brings develop's EARS `complex` pattern (#208) and the UNVERIFIED_AC drift detector + multi-framework JUnit matching (#204) under the knowledge-graph branch. Conflict resolution: - src/stages/detectors/index.ts — union both detector imports (inferableDependsOn + unverifiedAc; the allDetectors array already carried both) - detector count reconciled to 40 (37 base + develop's UNVERIFIED_AC + this branch's DOC_LINK_INTEGRITY & INFERABLE_DEPENDS_ON) across plugin.json (rebuilt by build:plugin), spec.yaml, capabilities.yaml, project-context.md, AGENTS.md, README.ko.html, and the ko SVGs - CHANGELOG.md — union both [Unreleased] sections - spec.yaml inventory re-synced via clad sync (199 features, 195 done, 6 capabilities, 169 test files) - README.ko.html counts refreshed (199/195 features, 1664 tests) - dist + plugin mirrors rebuilt Verified: npm test GREEN (1664/1664), self-consistency detector-count check passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hashes The merge moved 66 done features' module trees. A GREEN strict pre-push gate (type/lint/unit/coverage/deliverable-smoke all pass; STALE_ATTESTATION was the only outstanding finding) re-verified and re-stamped spec/attestation.yaml. Drift is now clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ko.html Brings README.html, README.md, README.ko.md, and docs/img/en/*.svg to parity with the finalized README.ko.html, and reconciles post-merge counts across every README. - README.html / README.md — English mirror of ko.html: enterprise hero, the project-map (knowledge-graph) section with the live galaxy GIF + colour legend + clad-graph-serve launch guide, every section, locked numbers - README.ko.md — Korean-markdown mirror (plain-declarative voice, docs/img/ko refs) - docs/img/en/*.svg (8) — English of the finalized ko diagrams (identical geometry/ style, terse English to fit fixed boxes) + docs/img/en/graph.gif - counts reconciled everywhere: 40 detectors · 199 features (195 done) · 1664 tests · 169 test files · v0.7.0 · 2026-07 (was 196/192, 39, 1630, 2026-06 in places) - ko.html: card "언제든 추적할 수 있다", Status 196/192 → 199/195, date → 2026-07 - content-accuracy carried to English: SoD "aligns with" (not "maps to") EU AI Act/SOX, Tier-B = project-context.md, runner examples = pure executors, gate 3/9/15 by cost, knowledge graph = traceability not correctness Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 done features own README/diagram modules touched by the lockstep commit; a GREEN strict pre-push gate (all other stages pass) re-verified and re-stamped spec/attestation.yaml. Drift is clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ped output at 64 KiB runGraphExportCommand wrote the rendered graph to stdout then called process.exit(0) on the next line; on a pipe, stdout.write is async, so exit killed the process before the ~64 KiB OS pipe buffer drained — `clad graph export --format json | jq` truncated at exactly 65536 B (this repo's payload is ~285 KiB). File / --out / serve / MCP (synchronous or HTTP) were unaffected. Exit from the write callback instead; same prophylactic fix for `graph stats`. Found by the pre-PR empirical verification sweep. - src/cli/graph.ts: process.stdout.write(x, () => process.exit(0)) at both stdout sites - tests/cli/graph-export-pipe.test.ts: spawns the CLI through a pipe, asserts the full >64 KiB JSON arrives and parses - count reconciliation from the new test file: tests 1664→1665, test_files 169→170 across the 4 READMEs + spec.yaml inventory; dist rebuilt; attestation re-stamped Verified: `clad graph export --format json | jq` → 199 feature nodes (was truncated); npm test 1665/1665 GREEN; pre-push strict gate green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersona permission separation") The multi-agent diagram title used "페르소나 권한 분리" / "Persona Separation of Duties", but the READMEs call these agents (not personas) and the concept is role/duty separation, not permissions — and the diagram's own subtitle already says segregation-of-duties. Aligned the title to that vocabulary: - ko: "페르소나 권한 분리" → "에이전트 역할 분리" - en: "Persona Separation of Duties" → "Agent Separation of Duties" - ko/en multi-agent.svg (title + a11y) + all four README alt texts; attestation re-stamped Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ity) The two markdown READMEs mirrored the HTML too literally (layout <table>s, an inline-styled Status block GitHub strips to bare text, centered <p>+<br> prose). Rewrote them to native markdown — text verbatim, format only: - layout 3-col tables → stacked sections / bullet trios (the hero hook, before/after/record, see/ask/measure) - Status styled-table → one clean 5-col markdown table - detector HTML table → markdown table; centered body prose → left-aligned + a blockquote - one extra blank line before each main (##) section heading - fixed a pre-existing KO detector-table count (spec↔test 5→6, so the rows sum to 40) and added the missing "capability 6개" to the KO Status footnote (EN parity) HTML siblings (README.html, README.ko.html) and all diagrams left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ing midnight spec.yaml's inventory.last_synced is a module of F-5b9f9f / F-32b1e0 / F-d6b93648, so when sync/check advances it to the current date, those features' attested tree- hash goes stale. The branch was attested on 06-30; CI ran on 07-01 UTC and re-stamped last_synced to 07-01, so the self-drift gate (`clad check --tier=pre-commit --strict`) reported STALE_ATTESTATION. Re-attested against today so the committed spec.yaml + attestation agree with a same-day CI run. Follow-up (pre-existing, separate): last_synced should not churn attestation — exclude it from the module hash, or stop writing it from check/build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The before → verify → record loop was narrated ~5× at different altitudes. Cut the cross-section redundancy and tightened wordy prose (~-93 lines; larger in rendered prose): - "how it works with host LLM": dropped the intro + the entire After/Record subsections (re-told downstream in Gate / Detector / "done is earned") → one pointer line; kept Before - removed the duplicate "no commands to memorize" line, the tagline-blurb flourish, the inline 9-stage list, and restated cost-split / colour-legend / Authority-column / ecosystem tails The "기업이~ / To trust AI" tagline + its 3 trust cards are preserved byte-for-byte (core hook, per maintainer). Numbers, honesty notes, diagrams, commands, and Docs links all intact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
qwerfunch
added a commit
that referenced
this pull request
Jul 1, 2026
* feat(drift): UNVERIFIED_AC multi-framework test_ref↔testcase matching (closes #203) (#204) * feat(drift): UNVERIFIED_AC — verify test_refs ran and passed via JUnit XML (F-96700032) UNTESTED_AC only checks that a done AC's test_refs EXIST on disk — an empty file, a test.skip, or a failing test all satisfied the gate, so the chain stopped at "AC → named file", not "AC → observed pass". UNVERIFIED_AC closes that gap when a JUnit XML report is available. - New detector UNVERIFIED_AC (src/stages/detectors/unverified-ac.ts) + a pure regex JUnit parser (src/stages/junit-report.ts, no XML dependency, mirroring the coverage-XML approach). Report path comes from gate.test_report in .cladding/config.yaml or a conventional default; failing/errored or only-skipped test_refs are an error, absent ones a warn (--strict promotes). - Graceful by default: no report present → emits nothing, leaving UNTESTED_AC's existence check as the baseline, so non-JUnit projects are unaffected. - Registered in detectors/index.ts (count auto-recomputed 37→38); catalog + self-consistency detector-count claims bumped to 38. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(drift): UNVERIFIED_AC multi-framework test_ref↔testcase matching (F-d980359c) UNVERIFIED_AC's matcher was effectively vitest-only — it keyed every testcase by `classname` and assumed that was a file path. pytest (`tests.test_foo`), Java/Kotlin FQCN (`com.example.FooTest`), and `file=`-attribute emitters never matched, so a passing test read as `absent` (a false positive under --strict) and a real fail/skip was mis-reported as "did not run". - parse: index each testcase under every path-shaped key it yields — the `file=` attribute, the `classname` as-is, and a dot→slash conversion of a dotted (no-separator) classname — sharing one accumulator (no double count). - lookup: extension-agnostic exact/suffix match (`FooTest` ↔ `FooTest.kt`). - detector: confident-or-degrade — a report with no path-like keys (e.g. jest describe-title classnames) is unmappable, so emit nothing instead of flooding false `absent` findings (preserves the low-false-positive contract). Measured A/B (OLD = classname-only): correct verdicts across a vitest/pytest/Kotlin/jest matrix 2/8 → 8/8; parse cost on a 10k-case report +1.6ms (vitest) … +7.8ms (pytest) per gate run — noise vs the ~50s gate. Blind-authored tests (tests/stages/junit-multiframework.test.ts) cover all 5 ACs. Existing UNVERIFIED_AC tests stay green (backward compatible). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * feat(ears): add the 6th canonical EARS pattern `complex` (while + when) (F-9d168287) (#208) src/spec/ears.ts implemented 5 of 6 EARS patterns; the 6th — `complex`, a precondition combined with a trigger ("While A, when B, the system shall X.") — was missing, and the validator keyed on the first trigger word only, so a multi-clause `While … when …` requirement either failed validation or was forced into a single-keyword bucket, silently dropping the trigger clause. - `EarsPattern` gains `complex`; checkEarsShape validates BOTH clauses (leading `while` precondition + a `when` trigger) and names the missing one, preserving the precondition→trigger relationship EARS exists to capture. - Purely additive: the existing five patterns validate exactly as before. - `complex` mirrored across every enum site in lockstep (types.ts, spec/schema.json ac.ears + always_ears, new.ts, MCP server enum, spec-conformance message) so HARNESS_INTEGRITY stays green. Blind-authored tests (tests/spec/ears-complex.test.ts, 14) cover the new pattern, the missing-clause messages, backward-compat for all 5 prior patterns, and the schema-mirror. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * Knowledge graph — see, ask, and measure how spec, code, tests, and docs connect (#222) * feat(graph): reverse-edge index (backlinks) — F-ee47fc2b Derive three reverse maps from the spec's forward edges, memoized per Spec instance via a WeakMap (no Spec mutation, 0 bytes on disk): - dependents: featureId -> direct dependents (inverse depends_on) - moduleOwners: module path -> owning feature ids (many-to-many) - testRefCitations: test path -> citing feature ids (anchor-stripped) The backlink seam the graph layer (blast-radius queries, doc linking, exports) reuses. pruneToFeature only walked depends_on upward; this is the reverse direction. Tests authored impl-blind (4/4 green). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): blast-radius impact query — F-7794a6bc buildImpactSlice + clad_get_impact (MCP) + clad impact (CLI): the backward complement of clad_get_context. Resolve a feature id/slug or a module path (fanning out through the many-to-many module owners) and walk the reverse-index dependents to return the blast radius — impacted features, scenarios at risk, the deduped regression test set, and the modules in the radius. Bounded by optional depth, deterministic. Live: `clad impact src/spec/load.ts` → 4 owners, 111 impacted features, 115 regression tests. Logic tested impl-blind (4/4); MCP integration tested (clad_get_impact). Glossary + verb-list updated for the new surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): doc graph — doc↔spec / doc↔doc links + integrity — F-ee5f643e extractDocReferences scans docs/**/*.md (excluding fixture/benchmark dirs, stripping code spans) for F-id references and resolved relative .md links; clad sync materialises spec/_doc-links.yaml (Tier C, the greppable "which docs explain feature X" index). New DOC_LINK_INTEGRITY detector (#38): a dead relative .md link is an error; a scoped doc's unresolved F-id is a warn. Per-file `clad-doc-links: ignore` opt-out for teaching docs that use illustrative ids (spec-ids-multi-dev, ssot-testing). The "all documents connected, always current" half of the graph, made mechanical. Detector green on cladding-self (scoping = zero false-RED). Logic + detector tested impl-blind (6/6). Detector count 37→38 across prose claims; A/B reports regenerated for the new info finding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): knowledge-graph export + hub stats — F-569f4b37 src/graph/: buildGraph materialises ONE typed graph (feature/module/test/ scenario/capability/doc nodes; depends_on/touches/covers/binds/implements/ references/links edges) from spec + reverse-index + doc-links. Renderers: mermaid + DOT + JSON (stdout) and an Obsidian vault (one note per node with [[wikilinks]] + Backlinks) — see the graph in best-in-class viewers, no bespoke UI. `clad graph stats` ranks hubs by degree (what's load-bearing). `--focus <q> --depth N` exports a legible neighborhood subgraph. Declared a `graph` foundation layer in architecture.yaml (pure spec reader). Live: 706 nodes / 1247 edges; top hub src/cli/clad.ts (degree 33). Pure functions tested impl-blind (6/6). Verb count 19→20; glossary + SVG diagram counts (→38 detectors) updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): link knowledge-graph capability + CHANGELOG + refresh indexes - spec/capabilities.yaml: new `knowledge-graph` capability (Tier B) grouping the four graph features (F-ee47fc2b, F-7794a6bc, F-ee5f643e, F-569f4b37). - CHANGELOG [Unreleased]: knowledge-graph entry, framed as traceability/ retrieval (NOT correctness — honest per the A/B record). - clad sync refresh: spec/index.yaml, spec/_doc-links.yaml, attestation re-stamp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): bespoke HTML viewer — SSoT 4-tier colors + slug labels — F-02343cd1 The graph, seen our own way (more than the spec asked): - model: GraphNode.tier (A/B/C/D) classified deterministically — features/ scenarios=A, capabilities=B, docs by first-line `Cladding · Tier X` banner (+ known-filename fallback), modules/tests=code (no tier). Feature labels now show the slug (slug ?? title ?? id); full title kept as `detail` for hover. - render: getTierColor + getTierLegend; tier flows into json, obsidian frontmatter, and mermaid (per-tier classDef coloring — bonus). - viewer-shell + src/graph/viewer/{app.js,styles.css}: toHtmlShell emits ONE self-contained, offline, double-clickable .html — a hand-rolled, zero-dep canvas force-directed renderer (NOT a vendored 80KB lib: truest to the zero-dep ethos, full control, ~no supply-chain surface). Tier colors, slug labels, status opacity, degree-sized hubs, a sidebar (search, kind+tier filters with counts, tier legend, Calm/Live, theme, labels), hover- neighborhood, click-to-pin, zoom/pan, localStorage layout persistence. - `clad graph export --format html --out f.html` (mandatory --out). Build copies viewer assets to dist/viewer/ (schema.json precedent). eslint-ignored. Live: 710 nodes / 1259 edges → 213KB self-contained .html, byte-deterministic, zero external requests. Tests authored impl-blind (3/3). gate GREEN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): live graph view — clad graph serve + clad_get_graph — F-64a5c159 The graph as a LIVE view, not a re-exported snapshot (the user's reframe: the graph is a pure derivation of the spec, so build the view once and let it auto-update as development proceeds). - src/cli/graph-serve.ts: `clad graph serve [--port]` — a stdlib node:http server (zero deps). GET / serves the viewer (with an injected SSE reload snippet), GET /graph.json recomputes buildGraph on EVERY request (always current — no stale-trap), GET /events is a text/event-stream channel. node:fs.watch on spec/ + docs/ broadcasts a debounced refresh → open browsers auto-reload. Hardened: headersSent-guarded error path + closeAllConnections so it shuts down cleanly (and survives EventSource disconnects). SSE keep-alive every 30s. - src/serve/server.ts: clad_get_graph MCP tool — agents read the live (optionally focused) graph in one call; never stale. Live: GET /graph.json reflects the current spec (713 nodes after this feature landed). Endpoints + broadcast tested impl-blind (2/2); clad_get_graph tested over MCP. glossary + TOOL_NAMES updated. gate GREEN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): link viewer + live-serve features to capability + CHANGELOG - spec/capabilities.yaml: knowledge-graph now also owns the HTML viewer (F-02343cd1) and live serve (F-64a5c159). - CHANGELOG [Unreleased]: the SSoT-tier-colored viewer + the live auto-updating serve, framed plainly (see/navigate the structure, not a correctness check). - clad sync refresh: index.yaml, _doc-links.yaml, attestation re-stamp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(graph): viewer canvas was unsized (blank graph) — size to viewport + sidebar-aware fit The canvas had no CSS width/height, so it fell back to the intrinsic 300×150 and sat hidden behind the 264px sidebar — the graph area rendered blank. Size the canvas to 100vw/100vh and offset fit() by the sidebar width so the graph centers in the visible area. Verified headless: nodes draw (ctx.arc fires 31k× across the settle frames on the 713-node self-graph). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): viewer overhaul — radial galaxy + always-on ambient + bloom — F-8234ec3c Make it extreme, and alive at rest (the user's ask): - LAYOUT: a degree-weighted radial galaxy. High-degree hubs are pulled toward the centre, low-degree leaves toward the rim; charge spreads angularly, springs cluster the connected — the graph gathers into one circular galaxy with a bright load-bearing core. Verified on the real 717-node graph: 0 NaN/Inf, hub (deg 35) at dist 412 vs median 987 (central). - ALIVE AT REST: ambient is now the default — a slow global rotation + node breathing + flowing edge particles + hub glow pulse, all O(n)/O(edges) draw (rotation is a free transform; physics stays burst-only). "Calm" freezes it. - LOOK: additive ("lighter") bloom pass so overlapping hubs build a glowing core, over a deep-space radial-gradient background. Click-to-focus (persistent neighborhood highlight + smooth recenter), smooth view lerp, upright labels. Plus a headless render test (tests/graph/viewer-render.test.ts): stubs canvas/ document/window, runs the real app.js, and asserts it draws nodes, keeps the ambient loop alive, and settles to finite positions with the hub central — guarding the "blank canvas" / NaN-blowup regressions deterministically in-gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): link galaxy viewer feature to knowledge-graph capability Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): Obsidian-grade viewer + live conformance heal (killer) — F-04f50847, F-af45042a Re-architect the viewer to Obsidian quality, and add the killer that only a spec↔code-connected tool can do. Obsidian-grade (F-04f50847): - Continuous low-alpha simulation with an alphaTarget thermostat: dragging a node reheats so connected nodes follow elastically (real tension); HOVER pauses the sim (motion freezes under the cursor); release decays to rest. Frame-time normalized, slow/calm. Removed the global rotation + edge particles + the forced radial-by-degree layout (→ organic center+charge+link balance). - Four force sliders (중심 장력 / 반발력 / 링크 장력 / 링크 거리) live-bound + persisted. - nodeColor separates all classes: tiers A/B/C/D distinct hues + module(orange)/ test(green)/doc(pink) distinct (were all gray). KILLER — live conformance heal (F-af45042a): - src/stages/graph-health.ts: nodeHealth() runs cladding's drift detectors and maps each finding to its graph node (path→module/test/doc, F-id→feature), worst-severity per node. (Lives in stages/: graph→stages is forbidden, stages→graph is fine.) - clad graph serve: GET /health.json (live, watch-refreshed); the viewer overlays problem nodes (error=red pulse / warn=amber) over the pretty default + an in-sync% pill, and heals smoothly on SSE refresh (viewer self-wires events; the reload injection is gone). Static export embeds a stamped snapshot. - Verified live: /health.json flags the exact features whose modules drifted from attestation; they heal green once the gate re-attests. Galaxy viewer (F-8234ec3c) archived — superseded by F-04f50847 (radial layout, global rotation, particles replaced; bloom + click-focus carried forward). Headless tests rewritten for the new behavior (hover-pause, drag-reheat, color, finite) + a health-mapper test. 1534 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): re-attest after viewer build (heal stale attestation) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(graph): viewer aesthetics — collision spacing, visible edges, no muddy bloom The dense overlapping 'confetti' is now a breathing web: a collision pass hard-separates overlapping nodes (0 overlapping pairs on the 717-node graph, was a clump), stronger repel + smaller nodes spread it (span ~1350). Edges are colored-by-source and actually visible (the structure reads); the additive bloom (which washed the centre muddy/white) is removed; nodes get a thin bg-colored ring so touching nodes stay crisp; health is a clean pulsing ring not a blob. Force defaults retuned for an even, Obsidian-like distribution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): WebGL stellar viewer + working-set context tooling (F-77f7ead0, F-06dfdad6, F-d6b93648) Knowledge-graph session work on feature/ssot-knowledge-graph: - webgl-stellar-viewer (F-77f7ead0): real three.js + UnrealBloom 3D galaxy over the SSoT graph (semantic hue × degree luminosity) + live drift-health overlay, esbuild-bundled offline. Adds a 'skill' node kind (SKILL.md distinct from code). Archives the 2D canvas viewer (F-04f50847, superseded); swaps F-af45042a module. - working-set-assembler (F-06dfdad6): clad_get_working_set — one token-budgeted, code-bearing payload (focus + module code + forward needs + backward breaks + verify + budget). ~5-8x smaller than reading shard+files; clad_get_context frozen. - graph-context-wiring (F-d6b93648): PostToolUse auto impact-card after source edits + ai_hints/persona nudge so the working set is actually used. All three: strict gate GREEN + attested; 1603 tests pass; tsc/eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): context-efficiency tooling + the depends_on gap fix (F-96250595, F-2be3e3bb, F-15999130, F-16138071) Four features that make cladding's knowledge graph populated + measurable, on feature/ssot-knowledge-graph. The arc: 4 A/Bs found the graph tools NULL on agent correctness — then re-framing to the real goal (search/context efficiency, not "smarter agents") + fixing the empty-graph root cause turned it positive. - iterative-impact-slice (F-96250595): buildIterativeImpactSlice — seed→widen→stop (coverage/exhaustion/marginal-yield), self-describing depth+stoppedBy+coverage; fixes the fixed-depth-1 narrow-miss. Calibrated on real graph data (2 sims). - infer-depends-on (F-2be3e3bb): clad infer-deps reconstructs feature depends_on from the code import graph — the load-bearing graph edge cladding PRODUCED nowhere (vapt shipped 0 edges → empty graph → the real cause of the A/B NULLs). vapt 0→698. - inferable-depends-on detector (F-15999130): INFERABLE_DEPENDS_ON — single info finding (never blocks, even strict) surfacing the empty-graph gap so infer-deps isn't a latent never-run tool. Detector count 38→39. - graph-efficiency-measure (F-16138071): clad measure — deterministic per-feature search/context efficiency (no agent, no NULL risk). vapt: working-set 4.1x smaller context than naive (shard+all modules); dependency radius + regression set resolved for free. The goal axis, finally quantified positive. Honest framing throughout (docs/ab-evaluation/*.md): the graph does NOT make a capable agent more correct/cheaper (it greps regardless — NULL x4); its value is small context + explicit blast radius + known regression set + a queryable dependency graph, for humans and tools that use it. infer-deps writes nothing (reviewable suggestions only). All gated: 1624 tests GREEN, tsc/eslint clean, strict gate + attested per feature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): post-audit hygiene — clarify efficiency-report stats + drop dead StopReason Read-only adversarial audit of the session found the work correct (4 features conform, tests honest, claims reproduce, no regressions). Two low-severity hygiene fixes, zero functional impact: - docs/ab-evaluation/case-efficiency-measurement.md: the table wrote "3,028 tok vs 14,442 tok = 4.1x (median)" on one line, which reads as a contradiction (14442/3028 ≈ 4.8). Both numbers are correct but are DIFFERENT statistics — 4.1x is the median of per-feature naive÷slice ratios; 3,028/14,442 are independent medians of slice and naive sizes. Split them + added a note so the report can't be misread. (The audit verdict marked 4.1x "VERIFIED" and missed this; caught by hand arithmetic.) - src/optimizer/iterative-slice.ts: removed 'token-budget' from the StopReason union — it was declared but never returned (budget overflow stops at max-depth first). Synced the test's ALLOWED_STOPS list. 1624 tests GREEN, tsc/eslint clean, strict gate re-attested (F-96250595). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(infer-deps): flag dynamic-import files for honest recall (F-0f2984d0) The final read-only graph-traversal test on the real doverunner-vapt project measured inference precision ~100% but recall ~70-75% — the largest remaining gap being dynamic/runtime imports (importlib.import_module, __import__, getattr-based, require(<expr>)) that static regex extraction cannot see (e.g. catalog/tool_inventory.py). Rather than silently under-report those edges, inferDependsOn now collects such module files into a sorted dynamicImportFiles list so a maintainer knows exactly which files to review by hand. This is an honest-recall surface, NOT a precision change — the static edges from the same files are still inferred unchanged (additive). clad infer-deps prints dynamic_import_files. Verified: cladding-self flags scripts/build.mjs (require). blind tests 3/3 in a separate file (existing 6 untouched); 1627 tests GREEN; tsc/eslint clean; strict gate re-attested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): color system — hue=kind only, grouped spec/code/test/docs (F-5b188856) The viewer's node colors had no clear basis: semanticHue let tier win over kind, so one hue tried to encode two axes and collided — feature and scenario both rendered blue, tier-C teal ≈ skill turquoise, and the sidebar showed the same blue labeled both "Spec · sealed" (tier) and "feature" (kind). Tier is derivable from kind, so encoding both was redundant. Hue now encodes KIND only. semanticHue ignores tier; tier moves to the sidebar filter + tooltip. KIND_COL becomes a grouped, simulation-verified palette (all Y≥125 bloom floor, colorblind 4-group separation, hub-whitening distinctness): SPEC = blue family (feature/scenario/capability) module = orange (anchor) · test = green (anchor) DOCS = pink family (doc/skill) — skill is SKILL.md, a document, moved out of the old code-adjacent turquoise. Sidebar: one color legend grouped into spec/code/test/docs zones; the SSoT tier section is a swatch-less filter (checkbox + label only, no empty box). Tier labels simplified to plain words (Spec / Design / Derived / Audit), and code nodes display as "code" in the viewer (the spec's `modules:` data model is unchanged — display label only). Honest residuals (accepted, user-preferred anchors): module orange sits near the 0-45° health-burn arc, disambiguated by the burn's 2.2-3× pulse vs a static node; orange↔green is the textbook red-green colorblind pair but is conceptually adjacent (code↔test) with a luminance backstop. 1630 tests GREEN; tsc/eslint clean; strict gate re-attested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme-ko): graph section + live GIF, flat blue-token diagrams, content-accuracy pass README.ko.html: - enterprise trust/trace/scale hero; new project-map (graph) section with a live galaxy GIF, colour legend, and a clad-graph-serve launch guide - refreshed numbers (39 detectors / 1630 tests / 196 features, 192 done / v0.7.0) - unified plain-declarative voice; blue-primary emphasis (green = pass/success only) - accurate 4-tier SSoT table (intent defined by humans, authored by the LLM per EARS) docs/img/ko/*.svg (8): unified flat blue-token design system (no shadow/pastel/accent bars, no version stamps); every claim verified against code — gate triggers 3/9/15 by cost, authorship human-defines/LLM-writes, segregation-of-duties "aligns with" (not "maps to") EU AI Act/SOX, Tier-B = project-context.md (not ai_hints), runner examples = pure executors docs/img/ko/graph.gif: 880px/8fps live-graph recording (6.9MB) .gitignore: ignore *.mov (local screen recordings) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(attest): re-attest after develop merge — refresh verified tree-hashes The merge moved 66 done features' module trees. A GREEN strict pre-push gate (type/lint/unit/coverage/deliverable-smoke all pass; STALE_ATTESTATION was the only outstanding finding) re-verified and re-stamped spec/attestation.yaml. Drift is now clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): sync all 4 READMEs + English diagrams to the finalized ko.html Brings README.html, README.md, README.ko.md, and docs/img/en/*.svg to parity with the finalized README.ko.html, and reconciles post-merge counts across every README. - README.html / README.md — English mirror of ko.html: enterprise hero, the project-map (knowledge-graph) section with the live galaxy GIF + colour legend + clad-graph-serve launch guide, every section, locked numbers - README.ko.md — Korean-markdown mirror (plain-declarative voice, docs/img/ko refs) - docs/img/en/*.svg (8) — English of the finalized ko diagrams (identical geometry/ style, terse English to fit fixed boxes) + docs/img/en/graph.gif - counts reconciled everywhere: 40 detectors · 199 features (195 done) · 1664 tests · 169 test files · v0.7.0 · 2026-07 (was 196/192, 39, 1630, 2026-06 in places) - ko.html: card "언제든 추적할 수 있다", Status 196/192 → 199/195, date → 2026-07 - content-accuracy carried to English: SoD "aligns with" (not "maps to") EU AI Act/SOX, Tier-B = project-context.md, runner examples = pure executors, gate 3/9/15 by cost, knowledge graph = traceability not correctness Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(attest): re-attest after all-READMEs lockstep 5 done features own README/diagram modules touched by the lockstep commit; a GREEN strict pre-push gate (all other stages pass) re-verified and re-stamped spec/attestation.yaml. Drift is clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(graph): flush stdout before exit — clad graph export truncated piped output at 64 KiB runGraphExportCommand wrote the rendered graph to stdout then called process.exit(0) on the next line; on a pipe, stdout.write is async, so exit killed the process before the ~64 KiB OS pipe buffer drained — `clad graph export --format json | jq` truncated at exactly 65536 B (this repo's payload is ~285 KiB). File / --out / serve / MCP (synchronous or HTTP) were unaffected. Exit from the write callback instead; same prophylactic fix for `graph stats`. Found by the pre-PR empirical verification sweep. - src/cli/graph.ts: process.stdout.write(x, () => process.exit(0)) at both stdout sites - tests/cli/graph-export-pipe.test.ts: spawns the CLI through a pipe, asserts the full >64 KiB JSON arrives and parses - count reconciliation from the new test file: tests 1664→1665, test_files 169→170 across the 4 READMEs + spec.yaml inventory; dist rebuilt; attestation re-stamped Verified: `clad graph export --format json | jq` → 199 feature nodes (was truncated); npm test 1665/1665 GREEN; pre-push strict gate green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(multi-agent): retitle diagram to "agent role separation" (was "persona permission separation") The multi-agent diagram title used "페르소나 권한 분리" / "Persona Separation of Duties", but the READMEs call these agents (not personas) and the concept is role/duty separation, not permissions — and the diagram's own subtitle already says segregation-of-duties. Aligned the title to that vocabulary: - ko: "페르소나 권한 분리" → "에이전트 역할 분리" - en: "Persona Separation of Duties" → "Agent Separation of Duties" - ko/en multi-agent.svg (title + a11y) + all four README alt texts; attestation re-stamped Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): make README.md + README.ko.md markdown-native (readability) The two markdown READMEs mirrored the HTML too literally (layout <table>s, an inline-styled Status block GitHub strips to bare text, centered <p>+<br> prose). Rewrote them to native markdown — text verbatim, format only: - layout 3-col tables → stacked sections / bullet trios (the hero hook, before/after/record, see/ask/measure) - Status styled-table → one clean 5-col markdown table - detector HTML table → markdown table; centered body prose → left-aligned + a blockquote - one extra blank line before each main (##) section heading - fixed a pre-existing KO detector-table count (spec↔test 5→6, so the rows sum to 40) and added the missing "capability 6개" to the KO Status footnote (EN parity) HTML siblings (README.html, README.ko.html) and all diagrams left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(attest): re-stamp at 2026-07-01 — CI stale from last_synced crossing midnight spec.yaml's inventory.last_synced is a module of F-5b9f9f / F-32b1e0 / F-d6b93648, so when sync/check advances it to the current date, those features' attested tree- hash goes stale. The branch was attested on 06-30; CI ran on 07-01 UTC and re-stamped last_synced to 07-01, so the self-drift gate (`clad check --tier=pre-commit --strict`) reported STALE_ATTESTATION. Re-attested against today so the committed spec.yaml + attestation agree with a same-day CI run. Follow-up (pre-existing, separate): last_synced should not churn attestation — exclude it from the module hash, or stop writing it from check/build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): trim prose for concision across all 4 READMEs The before → verify → record loop was narrated ~5× at different altitudes. Cut the cross-section redundancy and tightened wordy prose (~-93 lines; larger in rendered prose): - "how it works with host LLM": dropped the intro + the entire After/Record subsections (re-told downstream in Gate / Detector / "done is earned") → one pointer line; kept Before - removed the duplicate "no commands to memorize" line, the tagline-blurb flourish, the inline 9-stage list, and restated cost-split / colour-legend / Authority-column / ecosystem tails The "기업이~ / To trust AI" tagline + its 3 trust cards are preserved byte-for-byte (core hook, per maintainer). Numbers, honesty notes, diagrams, commands, and Docs links all intact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(release): v0.7.0 — version bump (9 sites) + lock + CHANGELOG + rebuild - version-bump 0.7.0 across the 9 HARNESS_INTEGRITY sites (package.json, marketplace.json, both plugin.json, gemini-extension.json, src/cli/clad.ts, src/serve/server.ts, tests/cli/clad.test.ts, spec.yaml); package-lock refreshed via npm install - CHANGELOG: [Unreleased] → [0.7.0] — 2026-07-01 — Knowledge Graph - dist + plugin mirrors rebuilt; attestation re-stamped - 1665/1665 tests green; pre-push strict gate green Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Sungju <yuyu04@naver.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
qwerfunch
added a commit
that referenced
this pull request
Jul 2, 2026
* feat(drift): UNVERIFIED_AC multi-framework test_ref↔testcase matching (closes #203) (#204) * feat(drift): UNVERIFIED_AC — verify test_refs ran and passed via JUnit XML (F-96700032) UNTESTED_AC only checks that a done AC's test_refs EXIST on disk — an empty file, a test.skip, or a failing test all satisfied the gate, so the chain stopped at "AC → named file", not "AC → observed pass". UNVERIFIED_AC closes that gap when a JUnit XML report is available. - New detector UNVERIFIED_AC (src/stages/detectors/unverified-ac.ts) + a pure regex JUnit parser (src/stages/junit-report.ts, no XML dependency, mirroring the coverage-XML approach). Report path comes from gate.test_report in .cladding/config.yaml or a conventional default; failing/errored or only-skipped test_refs are an error, absent ones a warn (--strict promotes). - Graceful by default: no report present → emits nothing, leaving UNTESTED_AC's existence check as the baseline, so non-JUnit projects are unaffected. - Registered in detectors/index.ts (count auto-recomputed 37→38); catalog + self-consistency detector-count claims bumped to 38. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(drift): UNVERIFIED_AC multi-framework test_ref↔testcase matching (F-d980359c) UNVERIFIED_AC's matcher was effectively vitest-only — it keyed every testcase by `classname` and assumed that was a file path. pytest (`tests.test_foo`), Java/Kotlin FQCN (`com.example.FooTest`), and `file=`-attribute emitters never matched, so a passing test read as `absent` (a false positive under --strict) and a real fail/skip was mis-reported as "did not run". - parse: index each testcase under every path-shaped key it yields — the `file=` attribute, the `classname` as-is, and a dot→slash conversion of a dotted (no-separator) classname — sharing one accumulator (no double count). - lookup: extension-agnostic exact/suffix match (`FooTest` ↔ `FooTest.kt`). - detector: confident-or-degrade — a report with no path-like keys (e.g. jest describe-title classnames) is unmappable, so emit nothing instead of flooding false `absent` findings (preserves the low-false-positive contract). Measured A/B (OLD = classname-only): correct verdicts across a vitest/pytest/Kotlin/jest matrix 2/8 → 8/8; parse cost on a 10k-case report +1.6ms (vitest) … +7.8ms (pytest) per gate run — noise vs the ~50s gate. Blind-authored tests (tests/stages/junit-multiframework.test.ts) cover all 5 ACs. Existing UNVERIFIED_AC tests stay green (backward compatible). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * feat(ears): add the 6th canonical EARS pattern `complex` (while + when) (F-9d168287) (#208) src/spec/ears.ts implemented 5 of 6 EARS patterns; the 6th — `complex`, a precondition combined with a trigger ("While A, when B, the system shall X.") — was missing, and the validator keyed on the first trigger word only, so a multi-clause `While … when …` requirement either failed validation or was forced into a single-keyword bucket, silently dropping the trigger clause. - `EarsPattern` gains `complex`; checkEarsShape validates BOTH clauses (leading `while` precondition + a `when` trigger) and names the missing one, preserving the precondition→trigger relationship EARS exists to capture. - Purely additive: the existing five patterns validate exactly as before. - `complex` mirrored across every enum site in lockstep (types.ts, spec/schema.json ac.ears + always_ears, new.ts, MCP server enum, spec-conformance message) so HARNESS_INTEGRITY stays green. Blind-authored tests (tests/spec/ears-complex.test.ts, 14) cover the new pattern, the missing-clause messages, backward-compat for all 5 prior patterns, and the schema-mirror. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * Knowledge graph — see, ask, and measure how spec, code, tests, and docs connect (#222) * feat(graph): reverse-edge index (backlinks) — F-ee47fc2b Derive three reverse maps from the spec's forward edges, memoized per Spec instance via a WeakMap (no Spec mutation, 0 bytes on disk): - dependents: featureId -> direct dependents (inverse depends_on) - moduleOwners: module path -> owning feature ids (many-to-many) - testRefCitations: test path -> citing feature ids (anchor-stripped) The backlink seam the graph layer (blast-radius queries, doc linking, exports) reuses. pruneToFeature only walked depends_on upward; this is the reverse direction. Tests authored impl-blind (4/4 green). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): blast-radius impact query — F-7794a6bc buildImpactSlice + clad_get_impact (MCP) + clad impact (CLI): the backward complement of clad_get_context. Resolve a feature id/slug or a module path (fanning out through the many-to-many module owners) and walk the reverse-index dependents to return the blast radius — impacted features, scenarios at risk, the deduped regression test set, and the modules in the radius. Bounded by optional depth, deterministic. Live: `clad impact src/spec/load.ts` → 4 owners, 111 impacted features, 115 regression tests. Logic tested impl-blind (4/4); MCP integration tested (clad_get_impact). Glossary + verb-list updated for the new surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): doc graph — doc↔spec / doc↔doc links + integrity — F-ee5f643e extractDocReferences scans docs/**/*.md (excluding fixture/benchmark dirs, stripping code spans) for F-id references and resolved relative .md links; clad sync materialises spec/_doc-links.yaml (Tier C, the greppable "which docs explain feature X" index). New DOC_LINK_INTEGRITY detector (#38): a dead relative .md link is an error; a scoped doc's unresolved F-id is a warn. Per-file `clad-doc-links: ignore` opt-out for teaching docs that use illustrative ids (spec-ids-multi-dev, ssot-testing). The "all documents connected, always current" half of the graph, made mechanical. Detector green on cladding-self (scoping = zero false-RED). Logic + detector tested impl-blind (6/6). Detector count 37→38 across prose claims; A/B reports regenerated for the new info finding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): knowledge-graph export + hub stats — F-569f4b37 src/graph/: buildGraph materialises ONE typed graph (feature/module/test/ scenario/capability/doc nodes; depends_on/touches/covers/binds/implements/ references/links edges) from spec + reverse-index + doc-links. Renderers: mermaid + DOT + JSON (stdout) and an Obsidian vault (one note per node with [[wikilinks]] + Backlinks) — see the graph in best-in-class viewers, no bespoke UI. `clad graph stats` ranks hubs by degree (what's load-bearing). `--focus <q> --depth N` exports a legible neighborhood subgraph. Declared a `graph` foundation layer in architecture.yaml (pure spec reader). Live: 706 nodes / 1247 edges; top hub src/cli/clad.ts (degree 33). Pure functions tested impl-blind (6/6). Verb count 19→20; glossary + SVG diagram counts (→38 detectors) updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): link knowledge-graph capability + CHANGELOG + refresh indexes - spec/capabilities.yaml: new `knowledge-graph` capability (Tier B) grouping the four graph features (F-ee47fc2b, F-7794a6bc, F-ee5f643e, F-569f4b37). - CHANGELOG [Unreleased]: knowledge-graph entry, framed as traceability/ retrieval (NOT correctness — honest per the A/B record). - clad sync refresh: spec/index.yaml, spec/_doc-links.yaml, attestation re-stamp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): bespoke HTML viewer — SSoT 4-tier colors + slug labels — F-02343cd1 The graph, seen our own way (more than the spec asked): - model: GraphNode.tier (A/B/C/D) classified deterministically — features/ scenarios=A, capabilities=B, docs by first-line `Cladding · Tier X` banner (+ known-filename fallback), modules/tests=code (no tier). Feature labels now show the slug (slug ?? title ?? id); full title kept as `detail` for hover. - render: getTierColor + getTierLegend; tier flows into json, obsidian frontmatter, and mermaid (per-tier classDef coloring — bonus). - viewer-shell + src/graph/viewer/{app.js,styles.css}: toHtmlShell emits ONE self-contained, offline, double-clickable .html — a hand-rolled, zero-dep canvas force-directed renderer (NOT a vendored 80KB lib: truest to the zero-dep ethos, full control, ~no supply-chain surface). Tier colors, slug labels, status opacity, degree-sized hubs, a sidebar (search, kind+tier filters with counts, tier legend, Calm/Live, theme, labels), hover- neighborhood, click-to-pin, zoom/pan, localStorage layout persistence. - `clad graph export --format html --out f.html` (mandatory --out). Build copies viewer assets to dist/viewer/ (schema.json precedent). eslint-ignored. Live: 710 nodes / 1259 edges → 213KB self-contained .html, byte-deterministic, zero external requests. Tests authored impl-blind (3/3). gate GREEN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): live graph view — clad graph serve + clad_get_graph — F-64a5c159 The graph as a LIVE view, not a re-exported snapshot (the user's reframe: the graph is a pure derivation of the spec, so build the view once and let it auto-update as development proceeds). - src/cli/graph-serve.ts: `clad graph serve [--port]` — a stdlib node:http server (zero deps). GET / serves the viewer (with an injected SSE reload snippet), GET /graph.json recomputes buildGraph on EVERY request (always current — no stale-trap), GET /events is a text/event-stream channel. node:fs.watch on spec/ + docs/ broadcasts a debounced refresh → open browsers auto-reload. Hardened: headersSent-guarded error path + closeAllConnections so it shuts down cleanly (and survives EventSource disconnects). SSE keep-alive every 30s. - src/serve/server.ts: clad_get_graph MCP tool — agents read the live (optionally focused) graph in one call; never stale. Live: GET /graph.json reflects the current spec (713 nodes after this feature landed). Endpoints + broadcast tested impl-blind (2/2); clad_get_graph tested over MCP. glossary + TOOL_NAMES updated. gate GREEN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): link viewer + live-serve features to capability + CHANGELOG - spec/capabilities.yaml: knowledge-graph now also owns the HTML viewer (F-02343cd1) and live serve (F-64a5c159). - CHANGELOG [Unreleased]: the SSoT-tier-colored viewer + the live auto-updating serve, framed plainly (see/navigate the structure, not a correctness check). - clad sync refresh: index.yaml, _doc-links.yaml, attestation re-stamp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(graph): viewer canvas was unsized (blank graph) — size to viewport + sidebar-aware fit The canvas had no CSS width/height, so it fell back to the intrinsic 300×150 and sat hidden behind the 264px sidebar — the graph area rendered blank. Size the canvas to 100vw/100vh and offset fit() by the sidebar width so the graph centers in the visible area. Verified headless: nodes draw (ctx.arc fires 31k× across the settle frames on the 713-node self-graph). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): viewer overhaul — radial galaxy + always-on ambient + bloom — F-8234ec3c Make it extreme, and alive at rest (the user's ask): - LAYOUT: a degree-weighted radial galaxy. High-degree hubs are pulled toward the centre, low-degree leaves toward the rim; charge spreads angularly, springs cluster the connected — the graph gathers into one circular galaxy with a bright load-bearing core. Verified on the real 717-node graph: 0 NaN/Inf, hub (deg 35) at dist 412 vs median 987 (central). - ALIVE AT REST: ambient is now the default — a slow global rotation + node breathing + flowing edge particles + hub glow pulse, all O(n)/O(edges) draw (rotation is a free transform; physics stays burst-only). "Calm" freezes it. - LOOK: additive ("lighter") bloom pass so overlapping hubs build a glowing core, over a deep-space radial-gradient background. Click-to-focus (persistent neighborhood highlight + smooth recenter), smooth view lerp, upright labels. Plus a headless render test (tests/graph/viewer-render.test.ts): stubs canvas/ document/window, runs the real app.js, and asserts it draws nodes, keeps the ambient loop alive, and settles to finite positions with the hub central — guarding the "blank canvas" / NaN-blowup regressions deterministically in-gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): link galaxy viewer feature to knowledge-graph capability Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): Obsidian-grade viewer + live conformance heal (killer) — F-04f50847, F-af45042a Re-architect the viewer to Obsidian quality, and add the killer that only a spec↔code-connected tool can do. Obsidian-grade (F-04f50847): - Continuous low-alpha simulation with an alphaTarget thermostat: dragging a node reheats so connected nodes follow elastically (real tension); HOVER pauses the sim (motion freezes under the cursor); release decays to rest. Frame-time normalized, slow/calm. Removed the global rotation + edge particles + the forced radial-by-degree layout (→ organic center+charge+link balance). - Four force sliders (중심 장력 / 반발력 / 링크 장력 / 링크 거리) live-bound + persisted. - nodeColor separates all classes: tiers A/B/C/D distinct hues + module(orange)/ test(green)/doc(pink) distinct (were all gray). KILLER — live conformance heal (F-af45042a): - src/stages/graph-health.ts: nodeHealth() runs cladding's drift detectors and maps each finding to its graph node (path→module/test/doc, F-id→feature), worst-severity per node. (Lives in stages/: graph→stages is forbidden, stages→graph is fine.) - clad graph serve: GET /health.json (live, watch-refreshed); the viewer overlays problem nodes (error=red pulse / warn=amber) over the pretty default + an in-sync% pill, and heals smoothly on SSE refresh (viewer self-wires events; the reload injection is gone). Static export embeds a stamped snapshot. - Verified live: /health.json flags the exact features whose modules drifted from attestation; they heal green once the gate re-attests. Galaxy viewer (F-8234ec3c) archived — superseded by F-04f50847 (radial layout, global rotation, particles replaced; bloom + click-focus carried forward). Headless tests rewritten for the new behavior (hover-pause, drag-reheat, color, finite) + a health-mapper test. 1534 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): re-attest after viewer build (heal stale attestation) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(graph): viewer aesthetics — collision spacing, visible edges, no muddy bloom The dense overlapping 'confetti' is now a breathing web: a collision pass hard-separates overlapping nodes (0 overlapping pairs on the 717-node graph, was a clump), stronger repel + smaller nodes spread it (span ~1350). Edges are colored-by-source and actually visible (the structure reads); the additive bloom (which washed the centre muddy/white) is removed; nodes get a thin bg-colored ring so touching nodes stay crisp; health is a clean pulsing ring not a blob. Force defaults retuned for an even, Obsidian-like distribution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): WebGL stellar viewer + working-set context tooling (F-77f7ead0, F-06dfdad6, F-d6b93648) Knowledge-graph session work on feature/ssot-knowledge-graph: - webgl-stellar-viewer (F-77f7ead0): real three.js + UnrealBloom 3D galaxy over the SSoT graph (semantic hue × degree luminosity) + live drift-health overlay, esbuild-bundled offline. Adds a 'skill' node kind (SKILL.md distinct from code). Archives the 2D canvas viewer (F-04f50847, superseded); swaps F-af45042a module. - working-set-assembler (F-06dfdad6): clad_get_working_set — one token-budgeted, code-bearing payload (focus + module code + forward needs + backward breaks + verify + budget). ~5-8x smaller than reading shard+files; clad_get_context frozen. - graph-context-wiring (F-d6b93648): PostToolUse auto impact-card after source edits + ai_hints/persona nudge so the working set is actually used. All three: strict gate GREEN + attested; 1603 tests pass; tsc/eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): context-efficiency tooling + the depends_on gap fix (F-96250595, F-2be3e3bb, F-15999130, F-16138071) Four features that make cladding's knowledge graph populated + measurable, on feature/ssot-knowledge-graph. The arc: 4 A/Bs found the graph tools NULL on agent correctness — then re-framing to the real goal (search/context efficiency, not "smarter agents") + fixing the empty-graph root cause turned it positive. - iterative-impact-slice (F-96250595): buildIterativeImpactSlice — seed→widen→stop (coverage/exhaustion/marginal-yield), self-describing depth+stoppedBy+coverage; fixes the fixed-depth-1 narrow-miss. Calibrated on real graph data (2 sims). - infer-depends-on (F-2be3e3bb): clad infer-deps reconstructs feature depends_on from the code import graph — the load-bearing graph edge cladding PRODUCED nowhere (vapt shipped 0 edges → empty graph → the real cause of the A/B NULLs). vapt 0→698. - inferable-depends-on detector (F-15999130): INFERABLE_DEPENDS_ON — single info finding (never blocks, even strict) surfacing the empty-graph gap so infer-deps isn't a latent never-run tool. Detector count 38→39. - graph-efficiency-measure (F-16138071): clad measure — deterministic per-feature search/context efficiency (no agent, no NULL risk). vapt: working-set 4.1x smaller context than naive (shard+all modules); dependency radius + regression set resolved for free. The goal axis, finally quantified positive. Honest framing throughout (docs/ab-evaluation/*.md): the graph does NOT make a capable agent more correct/cheaper (it greps regardless — NULL x4); its value is small context + explicit blast radius + known regression set + a queryable dependency graph, for humans and tools that use it. infer-deps writes nothing (reviewable suggestions only). All gated: 1624 tests GREEN, tsc/eslint clean, strict gate + attested per feature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(graph): post-audit hygiene — clarify efficiency-report stats + drop dead StopReason Read-only adversarial audit of the session found the work correct (4 features conform, tests honest, claims reproduce, no regressions). Two low-severity hygiene fixes, zero functional impact: - docs/ab-evaluation/case-efficiency-measurement.md: the table wrote "3,028 tok vs 14,442 tok = 4.1x (median)" on one line, which reads as a contradiction (14442/3028 ≈ 4.8). Both numbers are correct but are DIFFERENT statistics — 4.1x is the median of per-feature naive÷slice ratios; 3,028/14,442 are independent medians of slice and naive sizes. Split them + added a note so the report can't be misread. (The audit verdict marked 4.1x "VERIFIED" and missed this; caught by hand arithmetic.) - src/optimizer/iterative-slice.ts: removed 'token-budget' from the StopReason union — it was declared but never returned (budget overflow stops at max-depth first). Synced the test's ALLOWED_STOPS list. 1624 tests GREEN, tsc/eslint clean, strict gate re-attested (F-96250595). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(infer-deps): flag dynamic-import files for honest recall (F-0f2984d0) The final read-only graph-traversal test on the real doverunner-vapt project measured inference precision ~100% but recall ~70-75% — the largest remaining gap being dynamic/runtime imports (importlib.import_module, __import__, getattr-based, require(<expr>)) that static regex extraction cannot see (e.g. catalog/tool_inventory.py). Rather than silently under-report those edges, inferDependsOn now collects such module files into a sorted dynamicImportFiles list so a maintainer knows exactly which files to review by hand. This is an honest-recall surface, NOT a precision change — the static edges from the same files are still inferred unchanged (additive). clad infer-deps prints dynamic_import_files. Verified: cladding-self flags scripts/build.mjs (require). blind tests 3/3 in a separate file (existing 6 untouched); 1627 tests GREEN; tsc/eslint clean; strict gate re-attested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(graph): color system — hue=kind only, grouped spec/code/test/docs (F-5b188856) The viewer's node colors had no clear basis: semanticHue let tier win over kind, so one hue tried to encode two axes and collided — feature and scenario both rendered blue, tier-C teal ≈ skill turquoise, and the sidebar showed the same blue labeled both "Spec · sealed" (tier) and "feature" (kind). Tier is derivable from kind, so encoding both was redundant. Hue now encodes KIND only. semanticHue ignores tier; tier moves to the sidebar filter + tooltip. KIND_COL becomes a grouped, simulation-verified palette (all Y≥125 bloom floor, colorblind 4-group separation, hub-whitening distinctness): SPEC = blue family (feature/scenario/capability) module = orange (anchor) · test = green (anchor) DOCS = pink family (doc/skill) — skill is SKILL.md, a document, moved out of the old code-adjacent turquoise. Sidebar: one color legend grouped into spec/code/test/docs zones; the SSoT tier section is a swatch-less filter (checkbox + label only, no empty box). Tier labels simplified to plain words (Spec / Design / Derived / Audit), and code nodes display as "code" in the viewer (the spec's `modules:` data model is unchanged — display label only). Honest residuals (accepted, user-preferred anchors): module orange sits near the 0-45° health-burn arc, disambiguated by the burn's 2.2-3× pulse vs a static node; orange↔green is the textbook red-green colorblind pair but is conceptually adjacent (code↔test) with a luminance backstop. 1630 tests GREEN; tsc/eslint clean; strict gate re-attested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme-ko): graph section + live GIF, flat blue-token diagrams, content-accuracy pass README.ko.html: - enterprise trust/trace/scale hero; new project-map (graph) section with a live galaxy GIF, colour legend, and a clad-graph-serve launch guide - refreshed numbers (39 detectors / 1630 tests / 196 features, 192 done / v0.7.0) - unified plain-declarative voice; blue-primary emphasis (green = pass/success only) - accurate 4-tier SSoT table (intent defined by humans, authored by the LLM per EARS) docs/img/ko/*.svg (8): unified flat blue-token design system (no shadow/pastel/accent bars, no version stamps); every claim verified against code — gate triggers 3/9/15 by cost, authorship human-defines/LLM-writes, segregation-of-duties "aligns with" (not "maps to") EU AI Act/SOX, Tier-B = project-context.md (not ai_hints), runner examples = pure executors docs/img/ko/graph.gif: 880px/8fps live-graph recording (6.9MB) .gitignore: ignore *.mov (local screen recordings) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(attest): re-attest after develop merge — refresh verified tree-hashes The merge moved 66 done features' module trees. A GREEN strict pre-push gate (type/lint/unit/coverage/deliverable-smoke all pass; STALE_ATTESTATION was the only outstanding finding) re-verified and re-stamped spec/attestation.yaml. Drift is now clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): sync all 4 READMEs + English diagrams to the finalized ko.html Brings README.html, README.md, README.ko.md, and docs/img/en/*.svg to parity with the finalized README.ko.html, and reconciles post-merge counts across every README. - README.html / README.md — English mirror of ko.html: enterprise hero, the project-map (knowledge-graph) section with the live galaxy GIF + colour legend + clad-graph-serve launch guide, every section, locked numbers - README.ko.md — Korean-markdown mirror (plain-declarative voice, docs/img/ko refs) - docs/img/en/*.svg (8) — English of the finalized ko diagrams (identical geometry/ style, terse English to fit fixed boxes) + docs/img/en/graph.gif - counts reconciled everywhere: 40 detectors · 199 features (195 done) · 1664 tests · 169 test files · v0.7.0 · 2026-07 (was 196/192, 39, 1630, 2026-06 in places) - ko.html: card "언제든 추적할 수 있다", Status 196/192 → 199/195, date → 2026-07 - content-accuracy carried to English: SoD "aligns with" (not "maps to") EU AI Act/SOX, Tier-B = project-context.md, runner examples = pure executors, gate 3/9/15 by cost, knowledge graph = traceability not correctness Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(attest): re-attest after all-READMEs lockstep 5 done features own README/diagram modules touched by the lockstep commit; a GREEN strict pre-push gate (all other stages pass) re-verified and re-stamped spec/attestation.yaml. Drift is clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(graph): flush stdout before exit — clad graph export truncated piped output at 64 KiB runGraphExportCommand wrote the rendered graph to stdout then called process.exit(0) on the next line; on a pipe, stdout.write is async, so exit killed the process before the ~64 KiB OS pipe buffer drained — `clad graph export --format json | jq` truncated at exactly 65536 B (this repo's payload is ~285 KiB). File / --out / serve / MCP (synchronous or HTTP) were unaffected. Exit from the write callback instead; same prophylactic fix for `graph stats`. Found by the pre-PR empirical verification sweep. - src/cli/graph.ts: process.stdout.write(x, () => process.exit(0)) at both stdout sites - tests/cli/graph-export-pipe.test.ts: spawns the CLI through a pipe, asserts the full >64 KiB JSON arrives and parses - count reconciliation from the new test file: tests 1664→1665, test_files 169→170 across the 4 READMEs + spec.yaml inventory; dist rebuilt; attestation re-stamped Verified: `clad graph export --format json | jq` → 199 feature nodes (was truncated); npm test 1665/1665 GREEN; pre-push strict gate green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(multi-agent): retitle diagram to "agent role separation" (was "persona permission separation") The multi-agent diagram title used "페르소나 권한 분리" / "Persona Separation of Duties", but the READMEs call these agents (not personas) and the concept is role/duty separation, not permissions — and the diagram's own subtitle already says segregation-of-duties. Aligned the title to that vocabulary: - ko: "페르소나 권한 분리" → "에이전트 역할 분리" - en: "Persona Separation of Duties" → "Agent Separation of Duties" - ko/en multi-agent.svg (title + a11y) + all four README alt texts; attestation re-stamped Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): make README.md + README.ko.md markdown-native (readability) The two markdown READMEs mirrored the HTML too literally (layout <table>s, an inline-styled Status block GitHub strips to bare text, centered <p>+<br> prose). Rewrote them to native markdown — text verbatim, format only: - layout 3-col tables → stacked sections / bullet trios (the hero hook, before/after/record, see/ask/measure) - Status styled-table → one clean 5-col markdown table - detector HTML table → markdown table; centered body prose → left-aligned + a blockquote - one extra blank line before each main (##) section heading - fixed a pre-existing KO detector-table count (spec↔test 5→6, so the rows sum to 40) and added the missing "capability 6개" to the KO Status footnote (EN parity) HTML siblings (README.html, README.ko.html) and all diagrams left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(attest): re-stamp at 2026-07-01 — CI stale from last_synced crossing midnight spec.yaml's inventory.last_synced is a module of F-5b9f9f / F-32b1e0 / F-d6b93648, so when sync/check advances it to the current date, those features' attested tree- hash goes stale. The branch was attested on 06-30; CI ran on 07-01 UTC and re-stamped last_synced to 07-01, so the self-drift gate (`clad check --tier=pre-commit --strict`) reported STALE_ATTESTATION. Re-attested against today so the committed spec.yaml + attestation agree with a same-day CI run. Follow-up (pre-existing, separate): last_synced should not churn attestation — exclude it from the module hash, or stop writing it from check/build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): trim prose for concision across all 4 READMEs The before → verify → record loop was narrated ~5× at different altitudes. Cut the cross-section redundancy and tightened wordy prose (~-93 lines; larger in rendered prose): - "how it works with host LLM": dropped the intro + the entire After/Record subsections (re-told downstream in Gate / Detector / "done is earned") → one pointer line; kept Before - removed the duplicate "no commands to memorize" line, the tagline-blurb flourish, the inline 9-stage list, and restated cost-split / colour-legend / Authority-column / ecosystem tails The "기업이~ / To trust AI" tagline + its 3 trust cards are preserved byte-for-byte (core hook, per maintainer). Numbers, honesty notes, diagrams, commands, and Docs links all intact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Graph capability repairs — the 0.7.0 seams, simulation-verified (#225) * fix(hygiene): replace raw NUL bytes with \u0000 escapes — sources were binary to git src/graph/model.ts (edge-dedup key), src/optimizer/infer-depends-on.ts (edge-map key), src/spec/attestation.ts (sha256 separators) carried literal 0x00 chars inside string/template literals. Git's binary heuristic then hid those files from every diff/blame/review — the 0.7.0 graph core shipped as "Bin 0 -> 9363 bytes" with a review-invisible diff. The escape spelling is byte-identical at runtime (attestation digests unchanged; verified by simulation before the change). Adds a self-consistency test banning raw NUL bytes across the source tree so a file can never silently become review-invisible again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(graph): legacy F-NNN ids restored to the doc axis via a shared F-id lexer doc-references.ts matched only 6-8 hex ids, so every legacy sequential id (F-001…F-083 — 80 live shards) referenced in docs/ produced no doc→feature edge and no DOC_LINK_INTEGRITY validation, while graph-health.ts already carried the correct alternation. src/spec/feature-id.ts is now the single prose-scanning lexer for both sites (fresh RegExp per call — no shared /g state). Simulated on the live repo before implementing: +10 refs gained, all 10 resolve to real shards (0 new warns), hash-id extraction byte-identical, references edges 8→18. spec/_doc-links.yaml regeneration happens via clad sync at branch close. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(hook): PostToolUse impact card fires on host-style absolute paths Hosts send tool_input.file_path ABSOLUTE while moduleOwners keys are the spec's repo-relative posix paths, so buildImpactSlice never resolved and the impact card (F-d6b93648) never rendered in real usage. Measured on cladding-self before the fix: 0/361 module paths hit; after relativizing: 358/361 (99.2% — the 3 misses are trailing-slash directory keys unreachable via the hook). Outside-repo absolute paths degrade to not_found, relative inputs are untouched (idempotent). Card now also displays the relative path. Adds the end-to-end wiring test (runHookEvent with an absolute file_path against a real on-disk spec) that was missing when the bug shipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(working-set): module queries seed every co-owner; the budget now caps breaks_if_changed Two seams in one module, landed together deliberately — simulation showed the fan-out alone doubles budget-breach paths (17→33/144 on cladding-self, worst 2.9x the cap), so the clip must ship with it. Fan-out: a module-path query now passes its original form to the iterative impact slice, seeding ALL owners (reverse-slice already supported this) — before, only the alphabetically-first owner's dependents/tests were reported (src/cli/clad.ts: impacted 0 vs 83). Co-owners are seeds, so they surface in co_owners, not impacted; feature-id queries are byte-identical. Clip: breaks_if_changed now participates in the token budget, LAST in the clipping order (needs → code → breaks; the clip-before-code variant simulated strictly worse). Deeper dependents drop from the far end first, then tests outside the depth-1 floor; the direct set is never dropped (must-edit precedent). Fit checks measure WITH the pending 'breaks: omitted …' marker, closing the +3..10-token overshoot the old loops carried. In-budget payloads return byte-identical (pure no-op — existing 6 tests unchanged). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(measure): attribute cap-driven shrink honestly; plumb one reader through both sides Two validity fixes to clad measure, both simulation-verified before coding: Attribution: medianShrinkFactor was bounded by the 3000-token default budget — on cladding-self the '~4x smaller' headline was mostly the CAP's arithmetic, not graph value (uncapped, the structural slice is ~1.16x of naive: code + structured metadata). The report now splits fitsCount/truncatedCount with medianShrinkFit/medianShrinkTruncated and a medianStructuralRatio, and the CLI headline attributes the reduction to the budget ('budget enforces 3.9x on 163 capped') instead of selling it as shrink. What the working set actually sells: a guaranteed token budget + wired needs/breaks/verify context. One universe: the injected ModuleReader now reaches buildWorkingSet → codeExcerpt (same safety gates), so the slice and the naive baseline read the SAME universe — before, a virtual reader fed only the baseline, silently inflating the shrink factor ('Pure given (spec, reader)' is now true). The old test asserted the inflated number; rewritten to assert the honest split. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(serve): 503 error-as-data on unreadable spec; clean EADDRINUSE; watcher + Host hardening Body-before-writeHead on every non-SSE route: a mid-write or unparseable spec throws inside liveGraph(), and committing the 200+application/json headers first turned that into an HTTP 200 whose body was a prose YAML error — precisely the state the fs.watch auto-reload refetches into. Parse failures now answer 503 with a JSON {error} payload (the viewer can show a retry state); the headersSent guard remains for mid-stream (SSE) failures. Also covers schema-invalid specs (simulation). EADDRINUSE: server.on('error') now rejects the boot promise, so runGraphServeCommand prints one pulse line and exits 1 — before, the 'error' event was unhandled (raw 20-line stack) and the promise never settled. The listener stays attached so later runtime errors can't crash the process either. FSWatcher 'error' events degrade to manual refresh instead of crashing. A foreign Host header is refused (DNS-rebinding guard; the bind was already 127.0.0.1). Tests: the old 'watched-file change' test called broadcast() by hand — renamed to what it tests, and a REAL fs.watch→debounce→SSE chain test added (writes under spec/, capability-probe skip on platforms without recursive watch), plus 503, busy-port, and Host-guard cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(graph): kind-twin union for focus queries; clad_get_graph summary; health cache + all-twin badges; live viewer fixes Node identity: the same file materialises as up to three nodes (module:/test:/doc: — 95 paths on cladding-self), and every focus surface picked only the FIRST twin, silently dropping the others' edges. resolveNodeIds returns all twins, subgraph accepts a seed set, and the CLI --focus + clad_get_graph query union them (resolveNodeId keeps the old single-id contract). graph-health now badges ALL twins of a finding's path (first-twin-only left siblings looking healthy) and the viewer's drift pill counts distinct paths so twins can't double the headline number. clad_get_graph: the no-query form returned the whole graph pretty-printed — measured 285KB (~70k tokens) in one MCP result, contradicting the working-set budget discipline. It now answers a graphStats summary (counts by kind + top hubs + a hint at the CLI export, 2.1KB); a focused query still returns the real subgraph. The vacuous clad_get_working_set test (asserted a hand-maintained constant against itself; the handler was never invoked) is replaced with a real MCP round-trip: on-disk module code in must_edit.code, dependent in breaks, budget echo, isError miss. nodeHealth wraps its detector loop in primeSpecCache (the drift.ts run-scope pattern): one shard-tree parse instead of ~10 per /health.json request — measured 611ms → 21ms for the loop, results byte-identical (incl. mtime-sensitive STALE_TESTS on a drifted fixture). Viewer: SSE change detection compares the server's exact graph.json text (baseline = first fetch; never re-serialized client-side) — the old nodes.length proxy missed edge-only and same-count node changes and rendered stale. Wires the dead mobile burger button. CLI: a typo'd --format/--depth now fails loudly instead of silently rendering mermaid. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(infer-deps): extract export…from re-exports + literal dynamic import(); TS fixtures added The JS/TS branch missed two statically-extractable forms cladding's own source uses: 'export … from' re-exports (barrel files are dependencies) and literal dynamic import('…'). Both now produce edges. A NON-literal import(expr) flags the file in dynamicImportFiles (kept apart from the shared DYNAMIC_IMPORT regex — 'import (' would false-flag Python's parenthesized from-import). The whole JS/TS extraction branch shipped with zero fixtures (all Python) — a TS fixture set now pins import…from, side-effect import, require(), re-exports, dynamic import, and the single-segment ambiguity rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(render,serve,build): label escaping, corrupt-JSONL tolerance, three.js license notice Mermaid ids are deduped (safeId collapsed src/a.ts vs src/a_ts into one node silently); labels flatten quotes/newlines that broke the quoted-label syntax. DOT escapes backslashes before quotes. Obsidian wikilink aliases strip the |[[]] metacharacters that corrupt links. clad_get_events tolerates a corrupt/partial JSONL line (mid-write tail read) as an {unparseable} entry instead of crashing the tool call. The viewer bundle keeps three.js's MIT notice (legalComments 'eof' — 'none' stripped it from a distributed artifact). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: record the viewer decision reversal; correct 0.7.0 claims; cap-attribution note design.md gains a dated post-ship addendum (§8): the 'no bespoke web UI' decision was reversed (WebGL viewer + live serve shipped, and why the premise no longer applied), clad_get_dependents shipped as clad_get_impact, scope grew (working-set/iterative/infer-deps/measure), and _doc-links.yaml is a grep/human index, not the export source. The 0.7.0 CHANGELOG described the archived 2D prototype (force sliders, Live/Calm, 'no third-party library') — corrected in place with a note, plus an [Unreleased] section for this branch. Glossary's graph row now names html/serve. README no longer claims serve 'opens' the browser. case-efficiency-measurement.md carries the cap-attribution correction (the '4.1×' headline is budget-enforced, not structural). Deprecation notices say 0.8 (0.7.0 shipped every alias while claiming removal in 0.7). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * spec(graph): shards track the corrected 0.7.1 contracts; sync + build artifacts Updated ACs/test_refs where behavior contracts changed (clad_get_graph no-query summary, working-set fan-out + breaks clipping, measure honest attribution + one-reader universe, hook impact-card absolute-path wiring now pinned end-to-end), each with a Correction note recording why. src/spec/feature-id.ts claimed by the doc-graph feature. clad sync regenerated spec/_doc-links.yaml — the restored legacy F-NNN doc edges (multi-provider-roadmap, ssot-model) plus design.md's addendum references now materialize. npm run build refreshed dist + plugin mirrors + the viewer bundle (SSE text-compare + pill dedupe + burger + three.js MIT notice at EOF). Persona alias deprecation wording aligned to 0.8. Gate: clad check --tier=pre-push --strict GREEN (exit 0), full suite 1681/1681. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * feat(graph): fallback safety contract — a graph answer that cannot know says so (F-c6a32fff) (#226) The graph layer's miss/exception handling was solid, but three verified holes let "unknown" read as "safe" — every fix design was simulated against real repo data before implementation (one proposed fix was invalidated by the simulation and dropped: promoting clad_get_impact to the iterative slice would have silently shrunk 41% of responses). Ledger honesty: every impact slice now carries spec-wide edge counts {depends_on_edges, test_ref_edges}. On a blank ledger (measured on a 196-feature clone: a feature with 10 real dependents answered impacted:[] coverage:1.0, byte-identical to a verified leaf) the answer gains fallback hints — unknown, not safe; fall back to grep / the full suite. Zero known dependents stops with 'no-known-dependents' + coverage:null (44% of cladding-self features take this path); the working-set radius carries the denominator and guards the JS null→0 coercion the simulation caught. The impact card discloses '· deps unledgered'. Hook scoping: Stop and PostToolUse now mirror SessionStart's spec.yaml guard — a non-cladding cwd (or a monorepo subdir; hook cwd is process.cwd()) used to get falsely BLOCKED by ABSENCE_OF_GOVERNANCE with .cladding/ state written into the foreign tree (reproduced with the shipped bundle). Not under cladding → silence, zero writes; a present-but-broken spec keeps its honest one-time block. Gate footer: an engine fault fabricated {pass:true} on the one structural channel hook-less hosts see — now fails closed with {pass:false, unavailable:true} (pass stays, per the F-570a3f wire contract). The four graph MCP tools adopt the loadSpecOrError guidance (was raw ENOENT), clad_get_graph misses gain the discovery hint, and discovery hints name the baseline fallback. SessionStart renders an unparseable spec with no resolvable counts as 'counts unavailable' (conditional — a healthy spec/index.yaml still renders true counts). Feature cycle: shard F-c6a32fff authored via clad_create_feature, all 6 ACs test_ref-wired, flipped done through the strict pre-push gate. e2e-verified on the built binary: dense ledger {246,323} no hints / blank ledger {0,0} both hints / spec-less Stop silent with no .cladding. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Release v0.7.1 — Honest Graph (version bump + CHANGELOG + READMEs) npm run version-bump -- 0.7.1 (9 sites incl. marketplace catalog) + package-lock refresh. CHANGELOG [Unreleased] → [0.7.1] — 2026-07-02. READMEs (md/ko.md/html/ko.html): status v0.7.1, tests 1691/1691, features 200 (196 done). Full suite 1691/1691 GREEN; clad check --tier=pre-push --strict GREEN. Externally validated against npm 0.7.0 on a real 188-feature project: 23 scenarios, 15 measured improvements, 0 regressions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Sungju <yuyu04@naver.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
2026-07-01 · feature/ssot-knowledge-graph → developA live graph of how spec, code, tests, and docs connect — see it, ask what a
change breaks, and measure how little you actually need to read for a task.
Purely additive.
Added
clad graph serveopens a live view in the browser;clad graph exportwrites it to a file (Mermaid, DOT, JSON, or Obsidian canvas). Colour-coded by node type (spec, code, test, doc).clad impact <feature|file>: what depends on it, and which tests to re-run.clad context <feature>: the minimal working set — the short list of files and specs a task actually needs.clad measure: how much less you need to read for a task versus opening the whole repo (about 2.7x less, median across this repo's features).clad infer-depsreads your code's import statements to suggest which features depend on which (read-only — it never edits your spec).Changed
Fixed
clad graph exportcut off large piped output at 64 KiB (~64,000 bytes): the command exited before all the data reached the next command. Writing straight to a file was never affected. Now fixed, with a regression test; caught by an end-to-end run before this PR.1665/1665 tests · full gate green.
🤖 Generated with Claude Code