profile-compaction: CollapseConfig CRD + projection overlay + user-maged lifecycle#325
profile-compaction: CollapseConfig CRD + projection overlay + user-maged lifecycle#325entlein wants to merge 7 commits into
Conversation
|
Warning Review limit reached
More reviews will be available in 47 minutes and 40 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (17)
📒 Files selected for processing (39)
📝 WalkthroughWalkthroughThis PR implements configurable dynamic path collapse for software composition profiling via a new ChangesDynamic Path Collapse Configuration and Network Matching
🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests (beta)
|
🧩 Analysis chain
🏁 Script executed: Length of output: 193 🏁 Script executed: Length of output: 208
Atomicity Assessment —
|
| Component | Justification |
|---|---|
| CRD types + v1beta1 conversion | Core — inseparable |
| Generated deepcopy / protobuf / openapi | Codegen artifacts — must travel with the type definition |
| Generated clientset / informers / listers | Codegen artifacts — must travel with the type definition |
| etcd registration | Required to make the resource storable — inseparable from CRD introduction |
applicationprofile_processor + containerprofile_processor |
First consumer in this repo — reasonable to bundle here |
pkg/registry/file/cleanup.go |
Logically cohesive (user-managed lifecycle gate) but is a separable concern |
artifacts/collapseconfiguration-default-sample.yaml |
Low-risk, conventional to ship with the CRD |
The only split worth flagging is that cleanup.go is a behavioral policy change independent of the CRD API surface. If you ever need to bisect a regression, that separation could help — but it is not a blocking concern.
Cross-PR Coupling Flags
🔴 Hard compile-time dependency: #325 → #323
applicationprofile_processor and containerprofile_processor in #325 import and call the CollapseConfig provider, whose Go types are introduced in #323. #325 cannot compile without #323's types in the target branch. This is your expected and correctly modeled Tier 0 → Tier 2 dependency.
Merge gate: #323 must be merged (or squashed per your advisory) before #325 can target main.
🟡 Same-tier ordering: #325 and #322
Both are Tier 2. If the processors in #325 reference exec-related types added by #322 (e.g., CompareExecArgs), you have an intra-tier ordering constraint. If the processors only depend on CollapseConfig (#323) and existing types, #322 and #325 are independently mergeable in any order after #323.
Recommend verifying: Do applicationprofile_processor or containerprofile_processor in #325 import anything from #322's CompareExecArgs surface? If yes, #322 must precede #325.
🟡 Cross-repo dependency: #325 → node-agent#808
The projection overlay in node-agent#808 presumably consumes the CollapseConfig CRD registered by #325. This is your expected Tier 2 → Tier 4 dependency. No action needed in #325 itself, but node-agent#808 cannot target a release that doesn't include #325.
✅ No coupling detected: #325 ↔ #324 (networkmatch)
The networkmatch package (Tier 3) operates on a distinct resource surface and shares no apparent file-level overlap with the CollapseConfig CRD or the exec processors.
Summary Table
`#323` (Tier 0) ──► `#325` (Tier 2) ──► node-agent#808 (Tier 4)
│
possible order dep
│
`#322` (Tier 2)
The tier structure is correct. The one item to double-check before merging #325 is whether its processors import any symbol from #322 — if so, serialize #322 before #325 within Tier 2.
✅ Actions performed
Full review triggered.
| *out = new(metav1.LabelSelector) | ||
| (*in).DeepCopyInto(*out) | ||
| } | ||
| if in.IPAddresses != nil { |
There was a problem hiding this comment.
Blocking: this generated deepcopy now expects NetworkNeighbor.IPAddresses, but both internal and v1beta1 NetworkNeighbor still expose IPAddress (singular). go test ./pkg/apis/softwarecomposition fails immediately here. Unless the field rename is included in this PR/rebased prerequisite, this codegen drift leaves the branch unbuildable.
| func deflateApplicationProfileContainer(container softwarecomposition.ApplicationProfileContainer, sbomSet mapset.Set[string]) softwarecomposition.ApplicationProfileContainer { | ||
| opens, err := dynamicpathdetector.AnalyzeOpens(container.Opens, dynamicpathdetector.NewPathAnalyzer(OpenDynamicThreshold), sbomSet) | ||
| func deflateApplicationProfileContainer(container softwarecomposition.ApplicationProfileContainer, sbomSet mapset.Set[string], settings dynamicpathdetector.CollapseSettings) softwarecomposition.ApplicationProfileContainer { | ||
| opens, err := dynamicpathdetector.AnalyzeOpens(container.Opens, dynamicpathdetector.NewPathAnalyzerWithConfigs(settings.OpenDynamicThreshold, settings.CollapseConfigs), sbomSet) |
There was a problem hiding this comment.
Blocking: this PR now calls dynamicpathdetector.NewPathAnalyzerWithConfigs(...), but that symbol (along with CollapseConfig, DefaultCollapseConfigs, and the threshold constants used by collapse_config_from_crd.go) does not exist on current main. As-is, the branch still depends on an unmerged prerequisite and cannot compile until those path-wildcards changes are rebased/cherry-picked in.
| ) | ||
| apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = map[string]rest.Storage{ | ||
| "applicationprofiles": ep(applicationprofile.NewREST, applicationProfileStorageImpl), | ||
| "collapseconfigurations": ep(collapseconfiguration.NewREST), |
There was a problem hiding this comment.
Blocking: registering the CRD endpoint here does not wire it into the live deflate path. applicationProfileStorageImpl and containerProfileStorageImpl are still constructed with the default collapse-settings providers, and I cannot find any non-test call to SetCollapseSettings(...) / assignment to CollapseSettings. So CollapseConfiguration/default is stored, but never consulted when profiles are compacted; applying the sample manifest is a no-op.
matthyx
left a comment
There was a problem hiding this comment.
I found three merge blockers:
- the generated deepcopy drift now references
NetworkNeighbor.IPAddresseswhile the handwritten API types still exposeIPAddress, so the branch does not build; - this branch calls dynamicpathdetector APIs that are not on current
main(NewPathAnalyzerWithConfigs,CollapseConfig,DefaultCollapseConfigs, etc.), so it still depends on an unmerged prerequisite; - the new
CollapseConfigurationresource is registered, but the application/container profile processors still use compiled-in defaults, so the CRD is never read in production and the sample manifest is a no-op.
Please fix those before merge.
cd83527 to
7cba45a
Compare
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
…nal state Signed-off-by: entlein <einentlein@gmail.com>
NetworkNeighbor.IPAddresses (added on this branch) was missing hand-edited entries in the Marshal/Size/String/Unmarshal stanzas of generated.pb.go, the proto declaration, both conversion functions, and the internal+v1beta1 deepcopy. Result: TestNetworkNeighbor_IPAddresses_ ProtobufRoundtrip failed and the field was silently dropped on every real storage write. This patch adds the missing codec for field 9 (repeated string, wire tag 0x4a), the .proto declaration, the conversion stanzas in both directions, and the deepcopy slice copy in both type-system layers. Codegen pipeline is not run as part of build on this fork (the protoc image is x86_64-only); hand-edit follows the proven recipe from commit 0d83e2b / ad60a5b. Resolves matthyx review on network_types.go:68 (2026-05-27). Signed-off-by: entlein <einentlein@gmail.com>
…naged lifecycle Signed-off-by: entlein <einentlein@gmail.com>
… final state Signed-off-by: entlein <einentlein@gmail.com>
The CRD endpoint registered at /apis/.../collapseconfigurations stored operator-supplied CollapseConfiguration manifests but never consulted them — applicationProfileStorageImpl and containerProfileStorageImpl were constructed with the compiled-in DefaultCollapseSettings provider, and no non-test caller invoked SetCollapseSettings / assigned CollapseSettings. Applying artifacts/collapseconfiguration-default- sample.yaml was a no-op. Adds NewCRDCollapseSettingsProvider(storage.Interface) — a closure that reads CollapseConfiguration/default on every deflate call, projects through dynamicpathdetector.CollapseSettingsFromCRD, and falls back to DefaultCollapseSettings when the CR is absent or storage is nil. apiserver.go refactors the processor construction so both processors are reachable after the storage backend is built, then wires the same provider into both via SetCollapseSettings (ApplicationProfileProcessor) and direct field assignment (ContainerProfileProcessor). One shared closure keeps the two compaction paths consistent on every CR edit. The no-cache design lets bobctl autotune-style write→read cycles take effect immediately without restart or invalidation. Deflate frequency is low compared to disk Get latency; if benchmarks ever surface this as hot, wrap with a watched cache. Tests pin five contract points: fall-back on absent CR, faithful projection of an applied CR, nil-storage defence, transient-error fall-back, and live-update without invalidation. Resolves matthyx review on apiserver.go:164 (2026-05-27). Signed-off-by: entlein <einentlein@gmail.com>
7cba45a to
69c895e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/registry/file/applicationprofile_processor.go (1)
85-106:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSnapshot collapse settings once per
PreSaveexecution.Line 105 resolves settings inside the per-container loop. With a CRD-backed provider, this can trigger repeated storage lookups and can apply mixed thresholds within the same profile if config changes mid-iteration. Resolve once and reuse for all containers in this save pass.
Proposed fix
// size is the sum of all fields in all containers var size int + settings := a.effectiveCollapseSettings() // Define a function to process a slice of containers processContainers := func(containers []softwarecomposition.ApplicationProfileContainer) []softwarecomposition.ApplicationProfileContainer { for i, container := range containers { @@ - containers[i] = deflateApplicationProfileContainer(container, sbomSet, a.effectiveCollapseSettings()) + containers[i] = deflateApplicationProfileContainer(container, sbomSet, settings) size += len(containers[i].Execs)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/registry/file/applicationprofile_processor.go` around lines 85 - 106, The code calls a.effectiveCollapseSettings() inside the per-container loop (in processContainers) causing repeated CRD/storage lookups and potential mixed thresholds; fix by resolving the collapse settings once at the start of the PreSave/save pass (e.g., capture into a local variable like collapseSettings before creating/entering processContainers) and then pass that single collapseSettings value into deflateApplicationProfileContainer instead of calling a.effectiveCollapseSettings() for each container so all containers use the same resolved settings for the entire save operation.
🧹 Nitpick comments (1)
pkg/registry/file/applicationprofile_processor_test.go (1)
21-25: ⚡ Quick winDerive
/usr/libthreshold from active collapse configs instead of the global constant.These tests currently assume
/usr/libusesOpenDynamicThreshold. If a prefix-specific override is added, assertions will fail for the wrong reason. Match the/etcpattern and read threshold viaFindConfigForPath("/usr/lib/...").Proposed refactor
-func openThreshold() int { - return dynamicpathdetector.OpenDynamicThreshold +func openThresholdForPath(path string) int { + analyzer := dynamicpathdetector.NewPathAnalyzerWithConfigs( + dynamicpathdetector.OpenDynamicThreshold, + dynamicpathdetector.DefaultCollapseConfigs(), + ) + return analyzer.FindConfigForPath(path).Threshold } @@ - numOpens := openThreshold() + 1 + numOpens := openThresholdForPath("/usr/lib/x86_64-linux-gnu/libX.so.1") + 1 @@ - numOpens := openThreshold() + 1 + numOpens := openThresholdForPath("/usr/lib/x86_64-linux-gnu/libX.so.1") + 1 @@ - usrLibThreshold := openThreshold() + usrLibThreshold := openThresholdForPath("/usr/lib/libX.so") @@ - numOpens := openThreshold() + 1 + numOpens := openThresholdForPath("/usr/lib/x86_64-linux-gnu/libX.so.1") + 1Also applies to: 273-275, 303-304, 338-340, 420-421
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/registry/file/applicationprofile_processor_test.go` around lines 21 - 25, Tests currently hardcode dynamicpathdetector.OpenDynamicThreshold for /usr/lib paths; update them to derive the threshold from active collapse configs like the /etc pattern by calling dynamicpathdetector.FindConfigForPath("/usr/lib/...") (or the package's exported FindConfigForPath) and reading the config's collapse threshold instead of using OpenDynamicThreshold; change the openThreshold helper (and other occurrences referencing dynamicpathdetector.OpenDynamicThreshold) to fetch the config for "/usr/lib/..." and return its threshold, and apply the same replacement for the other test spots that currently assume the global constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pkg/registry/file/collapse_config_provider.go`:
- Around line 71-73: Replace the unbounded context used in s.Get with a bounded
context so storage reads can't hang indefinitely: create a short timeout context
(e.g., via context.WithTimeout) before calling s.Get, defer the cancel, and pass
that ctx into s.Get; keep the existing error/empty-name check and return
dynamicpathdetector.DefaultCollapseSettings() on error or deadline. Ensure you
update the call site where s.Get is invoked (the s.Get(...) call that populates
crd) and leave the rest of the function (including use of crd and
dynamicpathdetector.DefaultCollapseSettings) unchanged.
---
Outside diff comments:
In `@pkg/registry/file/applicationprofile_processor.go`:
- Around line 85-106: The code calls a.effectiveCollapseSettings() inside the
per-container loop (in processContainers) causing repeated CRD/storage lookups
and potential mixed thresholds; fix by resolving the collapse settings once at
the start of the PreSave/save pass (e.g., capture into a local variable like
collapseSettings before creating/entering processContainers) and then pass that
single collapseSettings value into deflateApplicationProfileContainer instead of
calling a.effectiveCollapseSettings() for each container so all containers use
the same resolved settings for the entire save operation.
---
Nitpick comments:
In `@pkg/registry/file/applicationprofile_processor_test.go`:
- Around line 21-25: Tests currently hardcode
dynamicpathdetector.OpenDynamicThreshold for /usr/lib paths; update them to
derive the threshold from active collapse configs like the /etc pattern by
calling dynamicpathdetector.FindConfigForPath("/usr/lib/...") (or the package's
exported FindConfigForPath) and reading the config's collapse threshold instead
of using OpenDynamicThreshold; change the openThreshold helper (and other
occurrences referencing dynamicpathdetector.OpenDynamicThreshold) to fetch the
config for "/usr/lib/..." and return its threshold, and apply the same
replacement for the other test spots that currently assume the global constant.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b453484d-6b0b-4912-91f5-2638c966dfd3
⛔ Files ignored due to path filters (17)
pkg/apis/softwarecomposition/v1beta1/generated.pb.gois excluded by!**/*.pb.gopkg/apis/softwarecomposition/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.gopkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigentry.gois excluded by!**/generated/**pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfiguration.gois excluded by!**/generated/**pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigurationspec.gois excluded by!**/generated/**pkg/generated/applyconfiguration/utils.gois excluded by!**/generated/**pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/collapseconfiguration.gois excluded by!**/generated/**pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_collapseconfiguration.gois excluded by!**/generated/**pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_softwarecomposition_client.gois excluded by!**/generated/**pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/generated_expansion.gois excluded by!**/generated/**pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/softwarecomposition_client.gois excluded by!**/generated/**pkg/generated/informers/externalversions/generic.gois excluded by!**/generated/**pkg/generated/informers/externalversions/softwarecomposition/v1beta1/collapseconfiguration.gois excluded by!**/generated/**pkg/generated/informers/externalversions/softwarecomposition/v1beta1/interface.gois excluded by!**/generated/**pkg/generated/listers/softwarecomposition/v1beta1/collapseconfiguration.gois excluded by!**/generated/**pkg/generated/listers/softwarecomposition/v1beta1/expansion_generated.gois excluded by!**/generated/**pkg/generated/openapi/zz_generated.openapi.gois excluded by!**/generated/**
📒 Files selected for processing (39)
artifacts/collapseconfiguration-default-sample.yamlpkg/apis/softwarecomposition/collapse_types.gopkg/apis/softwarecomposition/network_types.gopkg/apis/softwarecomposition/register.gopkg/apis/softwarecomposition/v1beta1/collapse_types.gopkg/apis/softwarecomposition/v1beta1/generated.protopkg/apis/softwarecomposition/v1beta1/network_types.gopkg/apis/softwarecomposition/v1beta1/network_types_protobuf_test.gopkg/apis/softwarecomposition/v1beta1/register.gopkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.gopkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.gopkg/apis/softwarecomposition/v1beta1/zz_generated.model_name.gopkg/apis/softwarecomposition/zz_generated.deepcopy.gopkg/apiserver/apiserver.gopkg/registry/file/applicationprofile_processor.gopkg/registry/file/applicationprofile_processor_collapse_provider_test.gopkg/registry/file/applicationprofile_processor_test.gopkg/registry/file/cleanup.gopkg/registry/file/cleanup_test.gopkg/registry/file/collapse_config_provider.gopkg/registry/file/collapse_config_provider_test.gopkg/registry/file/containerprofile_processor.gopkg/registry/file/containerprofile_processor_collapse_provider_test.gopkg/registry/file/dynamicpathdetector/collapse_config_from_crd.gopkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.gopkg/registry/file/networkmatch/README.mdpkg/registry/file/networkmatch/bench_test.gopkg/registry/file/networkmatch/doc.gopkg/registry/file/networkmatch/match_dns.gopkg/registry/file/networkmatch/match_dns_test.gopkg/registry/file/networkmatch/match_ip.gopkg/registry/file/networkmatch/match_ip_test.gopkg/registry/file/networkmatch/validate.gopkg/registry/file/networkmatch/validate_test.gopkg/registry/softwarecomposition/collapseconfiguration/etcd.gopkg/registry/softwarecomposition/collapseconfiguration/strategy.gopkg/registry/softwarecomposition/collapseconfiguration/strategy_test.gopkg/registry/softwarecomposition/networkneighborhood/strategy.gopkg/registry/softwarecomposition/networkneighborhood/strategy_test.go
| err := s.Get(context.Background(), key, storage.GetOptions{IgnoreNotFound: true}, crd) | ||
| if err != nil || crd.Name == "" { | ||
| return dynamicpathdetector.DefaultCollapseSettings() |
There was a problem hiding this comment.
Bound storage reads with a timeout to prevent request-path stalls.
Line 71 uses context.Background() for s.Get, which can block indefinitely during storage degradation. Use a short timeout and keep the same fallback-to-default behavior on error/deadline.
Proposed fix
import (
"context"
+ "time"
@@
const DefaultCollapseConfigurationName = "default"
+const collapseConfigurationReadTimeout = 2 * time.Second
@@
- err := s.Get(context.Background(), key, storage.GetOptions{IgnoreNotFound: true}, crd)
+ ctx, cancel := context.WithTimeout(context.Background(), collapseConfigurationReadTimeout)
+ defer cancel()
+ err := s.Get(ctx, key, storage.GetOptions{IgnoreNotFound: true}, crd)
if err != nil || crd.Name == "" {
return dynamicpathdetector.DefaultCollapseSettings()
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| err := s.Get(context.Background(), key, storage.GetOptions{IgnoreNotFound: true}, crd) | |
| if err != nil || crd.Name == "" { | |
| return dynamicpathdetector.DefaultCollapseSettings() | |
| import ( | |
| "context" | |
| "time" | |
| ) | |
| const DefaultCollapseConfigurationName = "default" | |
| const collapseConfigurationReadTimeout = 2 * time.Second | |
| ctx, cancel := context.WithTimeout(context.Background(), collapseConfigurationReadTimeout) | |
| defer cancel() | |
| err := s.Get(ctx, key, storage.GetOptions{IgnoreNotFound: true}, crd) | |
| if err != nil || crd.Name == "" { | |
| return dynamicpathdetector.DefaultCollapseSettings() | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pkg/registry/file/collapse_config_provider.go` around lines 71 - 73, Replace
the unbounded context used in s.Get with a bounded context so storage reads
can't hang indefinitely: create a short timeout context (e.g., via
context.WithTimeout) before calling s.Get, defer the cancel, and pass that ctx
into s.Get; keep the existing error/empty-name check and return
dynamicpathdetector.DefaultCollapseSettings() on error or deadline. Ensure you
update the call site where s.Get is invoked (the s.Get(...) call that populates
crd) and leave the rest of the function (including use of crd and
dynamicpathdetector.DefaultCollapseSettings) unchanged.
matthyx
left a comment
There was a problem hiding this comment.
Rechecked after the latest push: the three blockers from my previous review are fixed, but I found two new blockers.
pkg/apis/softwarecomposition/networkpolicy/v2/networkpolicy.go:340and:419still generate peers/policy refs only from the deprecated singularneighbor.IPAddress/neighbor.DNSfields. The newipAddressespayload added in this PR is never consumed here, so a profile that uses onlyipAddressesproduces no correspondingIPBlockentries in the generated NetworkPolicy.pkg/registry/file/networkneighborhood_processor.go:91merges duplicate neighbors byIdentifier, but on merge it only appendsDNSNamesandPorts. The newIPAddressesslice is not appended/deduped, so when multiple observations for the same identifier contribute different IPs, every IP except the first neighbor's slice is dropped before the profile is stored.
|
One more blocker after checking the generated artifacts: the API type now exposes
Please regenerate the applyconfiguration/OpenAPI artifacts for |
CollapseConfigCRD (v1beta1 with conversion, deepcopy, protobuf, openapi, generated clientset/informers/listers, etcd registration)lets cluster operators declare path-prefix policies that drive profile-compaction without code changes
applicationprofile_processor+containerprofile_processorconsume the CollapseConfig provider to choose per-profile collapse strategies at admission timepkg/registry/file/cleanup.go: cleanup loop skips user-managed resources (annotation-based marker, not labels —labels would silently miss every user-managed profile)
artifacts/collapseconfiguration-default-sample.yamlships a sample CRSummary by CodeRabbit
New Features
CollapseConfigurationresource for cluster administrators to dynamically configure path collapse thresholds and per-prefix overrides.Bug Fixes
Tests