Feature/cc json 2 analysis#4505
Conversation
ConvertTool now requires a readable project and fails with a non-zero exit when the input cannot be converted, instead of returning null (exit 0) and writing nothing. Kept local to ConvertTool — the repo-wide null->exit-0 analyser convention (MergeFilter et al.) is intentionally left unchanged. Add error-branch coverage: invalid file (non-zero exit, no output), already-2.0 input (idempotent) and stdin via the InputStream constructor. Addresses review findings #20 (FAIL-CONVEXIT) and #28 (TEST-CONVERT). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ject FolderMover.extractEdges/copyBlacklist and SubProjectExtractor's edge re-rooting rewrote `var` fields on the input project's Edge/BlacklistItem instances and returned them, corrupting the source as a side effect of building the output. Emit fresh Edge/BlacklistItem instances instead. Add a regression test asserting the source project's edges and blacklist are untouched after a move. Addresses review finding #26 (BUG-MUTATE). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…not edge hashes The strict-root check previously fired only as a side effect of an unresolved edge endpoint surfacing as a raw hash. Now that 2.0 edges with unresolvable endpoints are dropped (#12 FAIL-EDGEFALLBACK), a custom-rooted project with such edges slipped past the check and failed later with an opaque "no unique root node" error. Check the root node name directly so LargeMerge rejects a non-"root" base with the clear message regardless of its edges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e-merge truth mergedDependencyLens was built with mergeEdges=false and then ignored: a separate getMergedEdges re-derived the same distinctBy dedup, and mergedDependencyLens.edges was a misleading shadow. Build it with the strategy's actual mergesEdges flag and read mergedDependencyLens.edges, deleting getMergedEdges so the edge dedup lives in exactly one place (DependencyLens.merge). Behavior is unchanged. Addresses review finding #17 (DRY-EDGEDUP). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ce per candidate - MergeFilter now builds a fresh MergeResolverStrategy per merge group via a factory, so the never-reset stat counters no longer accumulate across MIMO groups (#18 CLEAN-STATS). - unambiguousSuffixMatch scores each candidate's suffix fit once (was twice) and lowercases the incoming path once instead of per comparison; behavior is unchanged (#19 PERF-SUFFIX). Add tests for the ambiguous-suffix tie, case-insensitive suffix matching, and per-run stat independence. Addresses review findings #18 (CLEAN-STATS) and #19 (PERF-SUFFIX). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… a two-concept test Use AssertJ containsKeys so a failure names the missing key instead of an opaque assertTrue(a && b), and split the case-sensitivity test into one concept per test (case-insensitive match vs case-sensitive drop), dropping the unnecessary inline tree rebuild. Addresses review findings #30 (TEST-KEYS) and #29 (TEST-SPLIT). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ents - The DependaCharta same-position dedup test now also asserts both leaves' outgoing weight aggregates onto the single merged node, and the DependencyLens edge-dedup test asserts which edge survives and that it keeps the first lens's attributes — not just the collection size (#31 TEST-DEDUP). - Add Arrange/Act/Assert comments to the four DcJsonParser tests that lacked them, matching the file's convention (#33 TEST-AAA). Addresses review findings #31 (TEST-DEDUP) and #33 (TEST-AAA). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the execution-time scope decisions (COMPAT-RELOCATE kept the legacy projection on LensSet, NodeId guard is '/'-only, LENS-UNIFORM applied with fixture regeneration, ConvertTool-local fail-loud, LargeMerge follow-up). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ual bug The previous test compared two independently-constructed strategies, which were always independent (the counters are per-instance) and so passed even against the pre-fix code. Reuse one strategy across two merges and assert its processed-node count accumulates, while a fresh strategy reports only its own run — directly motivating the per-group factory introduced for #18. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…descriptors The edge attribute descriptor still spelled "dependencies" out as a literal while the sibling node descriptors already used the DcJsonParser constants. Use DcJsonParser.DEPENDENCIES there too, completing the #21 (DRY-DCBOILER) de-duplication flagged during verification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NodeMaxAttributeMerger built the merged node without a checksum, so every merged node lost its contentHash. A merged 2.0 file could then no longer be used as a --base-file for incremental re-analysis, nor content-matched on a later merge. Carry the hash through when the merged nodes agree (or only one supplies it); drop it only on a genuine conflict between differing hashes. Addresses gap #3 from the cc.json 2.0 analysis audit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ing overlay edges The leaf/overlay merge strategy kept only the reference project's edges, so overlaying a dependency-bearing project (e.g. DependaCharta) onto a metrics base silently discarded all of its edges. Union and de-duplicate edges from every input regardless of strategy. The now-meaningless NodeMergerStrategy .mergesEdges flag (and its discard warning) is removed. Re-pathing differently-rooted overlay edges through the leaf resolver remains deferred (gap #4). Addresses gap #5 from the cc.json 2.0 analysis audit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Run two genuinely different producers (RawTextParser and CSVImporter) over the same logical file and assert both stamp the canonical NodeId for that tree position. This pins the property that makes cc.json 2.0 mergeable — the same file gets the same id regardless of which tool produced it — which previously had no end-to-end test. A producer that re-roots or skips canonicalization now fails loud. Addresses gap #7 from the cc.json 2.0 analysis audit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…limitations
Add a CHANGELOG [unreleased] entry for the default-output flip to the 2.0
{meta, files, lenses} format (with the visualization-still-reads-1.5 interop
note and the blacklist/markedPackages drop), the new convert command, and the
merge content-hash/edge fixes. Add a "Known limitations" section to
dev_docs/cc-json-2.0-format.md (no 1.5 output flag, cross-tool joins need
--leaf, blacklist not round-trippable).
Addresses gap #8 from the cc.json 2.0 analysis audit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capture the gaps from the post-implementation audit that are intentionally deferred: #4 (default merge doesn't reconcile differently-rooted trees) and the minor #9-17 (contentHash producer coverage, edge-attribute dedup, --large abort, foreign-id trust, schema strictness, inspect fixture, golden producer coverage, brace extractor, schema drift). Also records what was fixed/done (#3, #5, #7, #8) and accepted as-is (#1, #2, #6) this pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 1359 files, which is 1209 over the limit of 150. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (12)
📒 Files selected for processing (1359)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
|
SourceCodeParser had become a no-op stub that only printed a deprecation notice. Remove the module, its ccsh registration and attribute generator, and the references in the ccsh tests and README. Use unifiedparser instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pin meta.apiVersion to "2.0", require exactly one root in files, and forbid unknown properties on meta, file nodes and edges (lenses stays open for opaque-lens forward-compat). The 1.5 branch stays permissive so legacy 0.x/1.x files still validate. Add negative tests for a mislabeled version, an unknown key, and zero/multi-root files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add check_unifiedparser, check_dependacharta and check_convert to golden_test.sh (plus a DependaCharta input fixture) so the end-to-end ccsh -> 2.0 -> check harness exercises the flagship parser, the reworked importer and the new convert tool. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Delete ProjectToCcJson15Mapper, the CcJson15 DTO/GSON and the now-dead BlacklistTypeSerializer, and drop the apiVersion parameter from ProjectSerializer so no code path can produce 1.5. The 1.5 format is still read (the deserializer auto-detects 1.5/2.0). Tests that used the 1.5 writer as an oracle or fixture source now compare 2.0 serializations and read a static 1.x fixture instead. BREAKING CHANGE: ccsh no longer emits the legacy 1.5 cc.json format. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ProjectBuilder now carries opaqueLenses (domain/security/unknown), metrics clusters and meta.commitHash through build(), so the filters (merge, edgefilter, modify) no longer drop them when rebuilding a project. Merge unions opaque lenses (keep-first on a name collision) and dedups clusters; EdgeFilter also keeps each node's contentHash. The filter modules gain a gson dependency for the opaque-lens JsonElement type. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The analysis pipeline now works with cc.json 2.0 only. ProjectDeserializer rejects a 1.x file unless legacy reading is explicitly allowed, which only `ccsh convert` does; every other command (merge, modify, edgefilter, inspect, the importers) reports that the file is legacy and points the user at `ccsh convert`. The in-memory default version is now 2.0 and the merge compatibility gate accepts major 2 only. All 1.x test fixtures were migrated to 2.0 (the two edge fixtures were reconstructed so their edges reference real tree nodes, since 1.x tolerated dangling edges that 2.0 resolves by id and drops); the deserializer and merge-gate tests were reworked to the new contract, and a deliberate 1.x fixture is kept for the convert golden check. BREAKING CHANGE: only `ccsh convert` reads the legacy 1.x cc.json format; all other commands require 2.0 input. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rift guard
Add dev_docs/cc-json-2.0.schema.json — a self-contained, documented JSON Schema (draft-07) for the 2.0 { meta, files, lenses } format that external consumers can read or `$ref`. A new EveritValidatorTest serializes a representative 2.0 project (commitHash, contentHash, node + edge metrics/types/descriptors) and validates it against both the bundled `ccsh check` schema and the published one, so neither can drift from the CcJsonV2 DTO. Also refresh cc-json-2.0-format.md: ccsh emits 2.0 only and reads 1.x solely via convert, and link the schema.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
…ser rules
Slice 1, step 1 of the Visualization 2.0 migration. Structural only — no
existing runtime behavior changes.
- model/ccjson2.model.ts: cc.json 2.0 domain types ({meta, files, lenses}),
the single source of the 2.0 shape; reuses NodeType/AttributeDescriptors/
AttributeTypeValue from domain.model.ts. Orphan until step 2 imports it.
- fileStore/ and lenses/metrics/{repos,store,features}: empty target dirs
(.gitkeep), populated in steps 3-5.
- .dependency-cruiser.js: 9 scoped lens/fileStore boundary rules at warn
(7 lens-internal + metrics-lens-ngrx-guard + new-must-not-import-legacy),
alongside the existing features/ rules. Fire zero violations today.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… notes The slice-1 roadmap (rpi-plan/), shared CONVENTIONS, and the 2.0 architecture design companions (Ideas/). Step 1 marked complete. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two zero-violation fitness functions that ratify the existing status quo: external code already reaches these layers only through their facade, but no rule enforced it (unlike lenses + the state homes, which are already fenced). - render-model-external-access-only-via-facade: outside code reaches the renderModel composing layer only via renderModel.facade.ts, never its internal selector modules. Companion to render-model-is-top-derived (which fences direction only); this fences the surface. - load-external-access-only-via-facade: outside code enters the load/ layer only via load.facade.ts, except the ngrx effects manifest (load.effects.ts) which the composition root imports directly. Complements load-orchestrator-not-imported-by-lower-layers (direction) with entry-point. Both mirror lens-external-access-only-via-public-surface. depcruise: 0 new violations (1163 modules, 4543 deps). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the 3D engine's public surface and fences it: outside code now reaches renderer/threeViewer only through threeViewer.facade.ts. - Grow threeViewer.facade.ts with the 5 remaining public symbols consumers needed but reached raw: ThreeViewerService, ThreeStatsService, createTreemapNodes, StreetLayoutGenerator, treeMapSize. - Repoint the 7 bypassing files (features/codeMap/* + labelSettings' labelCreation.service) from threeViewer internals to the facade — 20 import edges, all mechanical path changes (same symbols). - Add threeviewer-external-access-only-via-facade at error (mirrors lens-external-access-only-via-public-surface); 0 violations. tsc clean, knip 0 unused exports, lint:architecture clean, full suite green with 45/45 snapshots byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fileStore was the last state home not fenced behind its facade. Grow fileStore.facade.ts with the files-slice public surface and repoint every external consumer, then enforce facade-only access. - Add 22 re-exports to fileStore.facade.ts: the read selectors (files, referenceFile, visibleFileStates, isDeltaState, areMultipleMapsVisible, isLoadingFile), the action creators (files actions, setIsLoadingFile, setCurrentFilesAreSampleFiles), and the slice reducers + defaults the rootStore composition registers — wired exactly like the other homes. - Repoint 35 files (46 import/re-export edges) from stores/fileStore/store/* to the facade: renderModel selectors, both lens stores, the load pipeline, feature stores/effects, the page view, and the rootStore composition itself. All mechanical path changes (same symbols); store/ internals stay private. - Add filestore-external-access-only-via-facade at error (mirrors lens-external-access-only-via-public-surface); 0 violations. tsc clean, knip 0 unused exports, lint:architecture clean, full suite green with 45/45 snapshots byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… hint `ccsh check` bypassed ProjectDeserializer and validated straight against the bundled schema, whose anyOf still accepts the wrapped 1.x shape — so a legacy file either passed silently (wrapped) or failed with an opaque schema stack trace (unwrapped), inconsistent with every other command. Add a legacy version-gate in EveritValidator that throws the shared LegacyFileException convert hint, extracted into LegacyFileException.CONVERT_HINT. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rent ProjectJsonDeserializer is the legacy-1.x-only reader, but a file predating the apiVersion field fell back to Project.API_VERSION (now "2.0"), so isAPIVersionCompatible wrongly reported it as current — a latent landmine for any guard that reads a legacy-parsed project's version. Default to ApiVersion.ONE_FIVE.versionString instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Node.children is a LinkedHashSet, so sibling emission order followed producer insertion / merge history / filesystem walk order, making the same logical project serialize to byte-different files with different meta.checksum across producers and platforms. Sort siblings (NFC name, then File before Folder) at the single 2.0 write boundary, which canonicalizes the files tree, the DFS-populated metrics-lens key order, and the checksum together. Regenerate the three order-sensitive golden fixtures (data identical apart from order + checksum) and make the Tokei round-trip assertions look nodes up by name instead of position. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NodeId.canonicalize rejected a segment containing the '/' separator with an internal-sounding "segments must be pre-split" precondition message, but the trigger is user data (e.g. csvimport with a non-'/' --path-separator, or convert of a 1.x name containing '/') and it surfaces as a raw stack trace. Reword the require to name the offending value and point at the likely cause. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On 2.0 read, a metrics-lens entry whose id resolves to no file node was dropped silently — asymmetric with the edge-endpoint path, which already warns. Emit the same kind of warning so the loss is visible, reusing the id-to-endpoint map the mapper already builds for edges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…yLens.merge The sole caller (ProjectMerger) always passed mergeEdges = true, so the else branch was unreachable and the KDoc contradicted the caller. Union edges unconditionally, matching MetricsLens.merge's signature, and update the two mergeEdges-based tests. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he legacy map LensSet.allAttributeDescriptors() used Map + Map, so a metric carrying a descriptor on both the metrics and dependency lens silently lost the metrics-lens one (right operand wins). On every filter rebuild via ProjectBuilder.fromLenses, that dropped the node-side metadata for a same-name node+edge metric. Merge such collisions instead: keep the metrics descriptor and union in the edge lens's analyzers. This is the lens->flat complement of the flat->lens routing fix (#9). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
check_convert only ran `ccsh check`, whose schema still accepts the legacy 1.x shape, so a convert regression that emitted an un-converted file stayed green; the fixture was also degenerate (no edges/types/descriptors/blacklist), so data loss went unnoticed too. Enrich legacy_1_5.cc.json with an edge, node+edge attributeTypes, a descriptor, blacklist and markedPackages, and assert the output is genuine 2.0 (meta/lenses envelope + edge fromId) and not the legacy shape, plus that the #12 blacklist/markedPackages drop-warning fires. Add a deterministic ConvertTool unit test pinning that edges/types/descriptors survive into the 2.0 lenses. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The branch-added UnifiedParser row linked to a non-existent parser/UnifiedParser/ README.md (#9). The whole table set shared the same pre-existing rot — parsers under import/|parser/, importers/filters/exporters/tools under bare prefixes, the SourceMonitor row even pointing at CSVImporter, and DEV_START_GUIDE off by a dir. Repoint every link to its real analysers/... path; all 17 now resolve. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keeps the large analysis.cg.json (and any other *.cg.json) out of accidental commits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ccsh check ran pure JSON-schema validation, which cannot express that a metrics-lens key or an edge fromId/toId must resolve to an existing file-tree node id. So a schema-valid 2.0 file with a dangling reference passed check (exit 0) while the reader silently dropped the orphaned entry (CcJsonV2ToProjectMapper only warns) -- review finding #5 part (b). Add a semantic pass in EveritValidator.validate() after schema.validate(): collect every files-tree node id, then throw ReferentialIntegrityException listing every metrics-lens key and every edge endpoint that resolves to no node id. The exception propagates to a non-zero exit exactly like a schema ValidationException; check still bypasses ProjectDeserializer, so the read-path warn-and-drop contract is unchanged. Verified end-to-end through the built ccsh: dangling edge/metric -> exit 1 with a clear message; real convert output, a 164-edge codemaatimport, and the full integrationTest (every analyser output re-checked) -> exit 0; tampering a real edge fromId -> exit 1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close the four dep-cruiser gaps flagged in the branch review, all latent-gap closures (0 violations today, each new fence proven to fire via throwaway probes): - mocks invisible: move mocks/ + *.mocks.ts from options.exclude to options.doNotFollow so inbound prod->mock edges are recorded, and add a no-prod-import-of-mocks fence (prod code must never import a test fixture). - cross-home raw-store: add a back-referenced state-home-no-cross-home-raw-store rule so a home reaches a sibling only through its facade; $1 exempts the home's own store/. - bootstrap outside fences: broaden the 7 facade-surface rules' from.path from ^app/codeCharta/ to ^app/ so app/app.config.ts + app/main.ts can no longer bypass a facade; root-store-is-sole-composer exempts app.config.ts (the sole legit composer). - renderModel facade-only was already enforced (161eba4); only inherited the bootstrap-anchor weakness, now fixed. lint:architecture: 0 violations (1165 modules, 4597 deps). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- edges.selector.spec: replace the 3 tautological cases (which asserted the selector against the very production expression it runs) with hand-computed expected values, and add the missing delta-mode case (Reference+Comparison => raw-keyed union, comparison overwrites reference per-attribute, no path prefix). - indexedDBWriter.spec: add a dedicated migrateCcStateRecordToV15 describe (drops the whole fileSettings root) and fix the full-chain upgrade title that still said v14 => now "+ v14 + v15". - 3dPrint.selectors.spec: rename 5 "from dynamicSettings" titles to "from mapState" (dynamicSettings was deleted; the selectors read mapState). - loadFile.service.spec: add a service-level cc.json 2.0 load test (meta/files/ lenses envelope) asserting id-keyed metric attributes land on nodes with list-valued authors stripped, edges resolved from id to path, and attributeTypes split across the metrics/dependency lenses. Full suite green: 384 suites, 2339 passed, 45 snapshots. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bundled first-run samples were still 1.2 format, so the default view never exercised the 2.0 reader. Replace sample1..4.cc.json (both the webpack-bundled app/…/assets/ and the served public/…/assets/ twins) with real `ccsh convert` 2.0 output — opaque hashed ids, real MD5 checksums, split metrics/dependency lenses — so first-run and ?file= loads go through the 2.0 path. - sampleFiles.ts: retype the bundled imports as CcJson2; display fileName stays "*.cc.json" so the mapSelector strip regex + explorer-tree/e2e names are unchanged. - Drop the hand-written assets/sample1.cc2.json (fake checksum/path ids); its role is now served by the real ccsh sample1. - Preserve the original 1.x sample1 as resources/sample1_legacy_1_2.cc.json and repoint the ccJson2ToCCFile render-parity spec at it — the spec now parses a real ccsh-produced fixture, and its opaque ids prove the reader's id→path join doesn't rely on id==path. Map compare is order-insensitive (ccsh sorts nodes folders-first/alphabetical); edge order + values are identical. - url.e2e: the "load a 2.0 file" test now uses the (now-2.0) sample1.cc.json. Full suite green (384 suites, 2339 passed, 45 snapshots), tsc 0 errors, lint:architecture 0 violations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sonar flagged duplicate imports (S3863): 11 files carried several import statements from the same module, e.g. codeMap.mouseEvent.service.ts imported from threeViewer.facade on eight separate lines. Biome never merged them because the organizeImports assist action was disabled. Enable it and apply it across visualization/app. Biome's assist both merges and sorts, so 581 files are reordered. The format script becomes `biome check --write --linter-enabled=false`, since `biome format --fix` does not apply assist actions -- without this the pre-commit hook would leave imports unorganized while format:check (which runs the assist) failed in CI. The linter stays disabled in the write path so the hook keeps applying only formatting + import organization, as before. Sorting is alphabetical by module path, which detached the role-grouped section comments in three barrels. Rewrite them so they no longer mislabel their neighbours: fileStore.facade lifts its four audience groups into the file docblock, metricsLens.facade and dependencyLens.facade re-scope the comments to the exports they actually describe. No export surface changed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`npm run format:check` (biome check, run over the whole repo by the Format Tests workflow) failed on eight tracked files, so CI was red on this branch. Three of them are fallout from 649b013: `biome check` runs the formatter before the organizeImports assist, so the imports that commit merged in contextMenuBlacklist.store.ts and primaryMetricNames.selector.ts were never re-wrapped and exceeded the 140-char line width, and version-manager.ts never had its two imports reordered at all. The format script is not idempotent across an import merge -- a second pass is what fixes them. The remaining five (the dependency-cruiser configs, the two cc.json schemas) had drifted independently; lint-staged only ever reformats staged files, so nothing forced them back into shape. Formatting only, no behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Biome is this repo's linter, but it never ran on the write path: `format` was `biome format --fix` (formatter only), then `biome check --write --linter-enabled=false` after 649b013 added the organizeImports assist. Lint was enforced in exactly one place -- `format:check` in the Format Tests workflow -- so a lint error surfaced in CI rather than at commit time. Drop the flag. `npm run format`, which lint-staged runs pre-commit, now also applies biome's safe lint fixes and fails on what it cannot fix. `biome lint` over the repo currently reports zero errors, so this blocks no existing work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ee selector _getUndecoratedAccumulatedData wrapped its whole delta path in an `if (isDeltaState(fileStates))` block whose only alternative was falling off the end of the function. Invert it into an early return so the delta handling reads at the top level. No behavior change; the four existing branch tests cover it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Analysers walked the repository's own .git object store, which accounted for roughly 47% of the nodes in a merged map of this repo. GitignoreHandler now prunes .git during the init walk and short-circuits shouldExclude before the empty-cache early return, matching the directory as a whole path component so .github, .gitignore and .gitattributes are untouched. RawTextParser and UnifiedParser add the exclude pattern unconditionally rather than relying on the handler, since --bypass-gitignore consults no handler at all and a root .gitignore suppresses the build-folder fallback. RawTextParser's private DEFAULT_EXCLUDES is dropped for the shared CodeChartaConstants.BUILD_FOLDERS it duplicated; the shared list additionally excludes /node_modules/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GSON's default number policy deserializes every JSON number as a Double, so any read-then-write round trip rewrote an integer attribute `1` as `1.0`. A cc.json that passed through a filter therefore no longer matched the file it came from, and the drift compounded across a merge chain. Both readers switch to ToNumberPolicy.LONG_OR_DOUBLE, which keeps integral values as Long. CoberturaStrategy's casts are hardened to (as Number).toDouble() because they can now see either type. The SVN golden file and the Tokei and DependaCharta expectations are updated to the values that are actually produced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UNION merging matched nodes by name alone, so a File and a Folder sharing a name across two inputs were merged onto one node. The survivor took one of the two types arbitrarily, and its children collided with the file's own id. MergeResolverStrategy now matches type-aware and treats a File/Folder pair as a clash rather than a match. NodeMaxAttributeMerger warns when it is nonetheless handed nodes carrying more than one concrete type, so a clash that slips past the matcher is visible instead of silent. Two existing RecursiveNodeMerger tests passed Folder operands where they meant to exercise the merge-into-one path; they now pass Files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Temporal coupling edges were emitted for every path ever seen in the log, including files renamed or deleted before HEAD. Serialization then materialized each dangling endpoint as an empty File node, so the map grew ghost files that exist in no working tree. ProjectConverter filters the edge list against the files actually present in the log before prefixing paths with /root/. ProjectToCcJsonV2Mapper still materializes missing endpoints -- edge-only producers such as CodeMaat rely on it -- but now warns with the count, so a producer leaking dangling edges is visible rather than silently absorbed. VersionControlledFile.resolveEdge did `attributes.toMutableMap().putAll(attributes)`, copying a map into itself and discarding the result, which dropped a second metric's contribution to an existing edge. It now merges the two maps. Remapping renamed paths at metric-evaluation time, and the resulting highly_coupled_files inflation, remain open. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Git quotes and octal-escapes any path containing a non-ASCII byte unless core.quotepath is off, so an escaped path from `git log` never matched the same path from `git ls-files` and the file silently lost all of its git metrics. Both commands now run with -c core.quotepath=off. That flag is not sufficient on its own: git still C-quotes a path holding a literal quote, backslash or control character, and logs captured elsewhere carry the octal escaping regardless. GitPathUnquoter decodes both forms, reassembling octal escapes as raw bytes before decoding UTF-8 so a multi-byte character split across several escapes rebuilds correctly. It returns any path that is not fully double-quoted unchanged, so it is safe to apply to every path. Encoding detection was the second half of the bug: guessEncoding mis-detected a UTF-8 log as WINDOWS-1252, and the log and the file list could be read with different charsets. determineLogEncoding prefers UTF-8 whenever the log is valid UTF-8, and both reads now share one charset. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|



{Meaningful title}
Please read the CONTRIBUTING.md before opening a PR.
Closes: #
Description
Descriptive pull request text, answering:
Definition of Done
A PR is only ready for merge once all the following acceptance criteria are fulfilled:
Screenshots or gifs