The file /Users/jan/.claude/plans/bubbly-shimmying-sedgewick.md has been updated successfully.

# Replace file-based resampling config with policy-based approach

## Context

The codebase has two resampling mechanisms:
1. **Old (file-based)**: `config.json` defines explicit `resolutions` array and `trigger` count. The frontend uses these to pick from a fixed set of resolution steps on zoom.
2. **New (policy-based)**: Users choose a policy ("low"/"medium"/"high") mapping to target point counts (200/500/1000). Resolution is computed dynamically from `duration / targetPoints`. This is already fully implemented in the GraphQL resolvers and frontend MetricPlot zoom logic.

The problem: `config.json` still uses the old `resolutions`+`trigger` schema, and falls back to it when no user policy is set. We should replace this with a default policy+algorithm in config, so the whole system is consistently policy-based.

## Changes

### 1. `internal/config/config.go` — Update `ResampleConfig` struct
- Remove `Resolutions []int` and `Trigger int` fields
- Add `DefaultPolicy string` (`json:"default-policy"`) — default resample policy ("low", "medium", "high")
- Add `DefaultAlgo string` (`json:"default-algo"`) — default resample algorithm ("lttb", "average", "simple")
- Keep `MinimumPoints int` — still useful for the resampler library
- Keep `TargetPoints int` — used by policy-derived runtime config

### 2. `internal/config/schema.go` — Update JSON schema
- Replace `trigger`/`resolutions` required properties with `default-policy`
- Add `default-policy` (string, enum: ["low","medium","high"]) and `default-algo` (string, enum: ["lttb","average","simple"]) properties
- Remove `trigger` and `resolutions` properties
- No required fields (presence of `"resampling": {}` is enough to enable it)

### 3. `internal/routerConfig/routes.go` — Update `resamplingForUser()`
- When falling back to globalCfg (no user policy set), use `globalCfg.DefaultPolicy` to compute `TargetPoints` and `Trigger` dynamically, same as the user-policy path
- If `DefaultPolicy` is empty, use "medium" as default

### 4. `internal/graph/schema.resolvers.go` — Update fallback resolution logic
- In `JobMetrics()` and `NodeMetricsList()`: replace `slices.Max(config.Keys.EnableResampling.Resolutions)` fallback with policy-based computation using `config.Keys.EnableResampling.DefaultPolicy`
- Use `TargetPointsForPolicy()` + `ComputeResolution()` instead of picking from a resolutions array
- Also apply `DefaultAlgo` when no user algo preference is set

### 5. `internal/graph/resample.go` — Update `resolveResampleAlgo()`
- After checking user preference, fall back to `config.Keys.EnableResampling.DefaultAlgo` before returning ""

### 6. `config.json` (local dev) — Update to new format
```json
"resampling": {
  "default-policy": "medium",
  "default-algo": "lttb"
}
```

### 7. `configs/config.json` (example config) — Update to new format
Same as above, keeping `minimum-points` if desired.

### 8. Frontend: No changes needed
The frontend already handles both modes:
- Policy-based: uses `targetPoints` to compute resolution dynamically
- Array-based: uses `resolutions` array for fixed steps

Since `resamplingForUser()` already returns a `ResampleConfig` with `TargetPoints`+`Trigger` (no `Resolutions`), the frontend will use the policy path.

## Files to modify
1. `internal/config/config.go` (struct + Init)
2. `internal/config/schema.go` (JSON schema)
3. `internal/routerConfig/routes.go` (`resamplingForUser`)
4. `internal/graph/schema.resolvers.go` (resolver fallbacks)
5. `internal/graph/resample.go` (`resolveResampleAlgo` fallback)
6. `config.json` (local config)
7. `configs/config.json` (example config)

## Verification
1. `go build ./...` — must compile
2. `go test ./internal/metricdispatch/...` — resample policy tests pass
3. `go test ./internal/config/...` — if config tests exist
4. Start with `./cc-backend -server -dev` and verify `/config` page shows resampling options
5. Verify metric plots on job pages still support zoom/resampling

# Derive MinimumPoints automatically from policy

## Context

The previous commit replaced `resolutions`/`trigger` with `default-policy`/`default-algo` in `ResampleConfig`. The `MinimumPoints` field still exists as a manual config value (`"minimum-points"` in JSON), but it's redundant — since we now always have a policy (user pref or default), the target points from that policy are the natural minimum threshold for resampling.

The resampler library (`cc-lib/v2/resampler`) has a global `MinimumRequiredPoints` (default 1000) set via `SetMinimumRequiredPoints()`. Currently this is set in `config.Init()` only when `minimum-points > 0` in config.json.

**Goal**: Remove `MinimumPoints` from config and derive it automatically from the default policy's target points (low=200, medium=500, high=1000).

## Changes

### 1. `internal/config/config.go`
- Remove `MinimumPoints int` field from `ResampleConfig`
- In `Init()`: replace the `MinimumPoints > 0` check with policy-derived logic:
  - Use `metricdispatch.TargetPointsForPolicy()` on `DefaultPolicy` (or "medium" if empty) to get the target points
  - Call `resampler.SetMinimumRequiredPoints(targetPoints)`
- This requires importing `metricdispatch` — need to check for import cycles

**Import cycle concern**: `config` is imported by almost everything. `metricdispatch` imports `config`. So `config` cannot import `metricdispatch`.

**Solution**: Inline the policy→targetPoints mapping in `config.Init()` instead of calling `metricdispatch.TargetPointsForPolicy()`. A simple switch on `DefaultPolicy`:
```go
func targetPointsForPolicy(policy string) int {
    switch policy {
    case "low":    return 200
    case "medium": return 500
    case "high":   return 1000
    default:       return 500 // medium fallback
    }
}
```

Then in `Init()`:
```go
if Keys.EnableResampling != nil {
    policy := Keys.EnableResampling.DefaultPolicy
    if policy == "" {
        policy = "medium"
    }
    resampler.SetMinimumRequiredPoints(targetPointsForPolicy(policy))
}
```

### 2. `internal/config/schema.go`
- Remove `minimum-points` property from the `resampling` JSON schema object

### 3. `config.json` / `configs/config.json`
- Remove `"minimum-points"` key from the resampling block (it was already removed from `config.json` in the previous commit; check `configs/config.json` still has it)

## Files to modify
1. `internal/config/config.go` — remove field, add helper, update `Init()`
2. `internal/config/schema.go` — remove `minimum-points` property
3. `configs/config.json` — remove `minimum-points` from resampling block

## Verification
1. `go build ./...` — must compile
2. `go test ./internal/config/...` — config tests pass
3. `go test ./internal/metricdispatch/...` — resample policy tests still pass

-# Replace file-based resampling config with policy-based approach

+# Derive MinimumPoints automatically from policy

-The codebase has two resampling mechanisms:

-1. **Old (file-based)**: `config.json` defines explicit `resolutions` array and `trigger` count. The frontend uses these to pick from a fixed set of resolution steps on zoom.

-2. **New (policy-based)**: Users choose a policy ("low"/"medium"/"high") mapping to target point counts (200/500/1000). Resolution is computed dynamically from `duration / targetPoints`. This is already fully implemented in the GraphQL resolvers and frontend MetricPlot zoom logic.

+The previous commit replaced `resolutions`/`trigger` with `default-policy`/`default-algo` in `ResampleConfig`. The `MinimumPoints` field still exists as a manual config value (`"minimum-points"` in JSON), but it's redundant — since we now always have a policy (user pref or default), the target points from that policy are the natural minimum threshold for resampling.

-The problem: `config.json` still uses the old `resolutions`+`trigger` schema, and falls back to it when no user policy is set. We should replace this with a default policy+algorithm in config, so the whole system is consistently policy-based.

+The resampler library (`cc-lib/v2/resampler`) has a global `MinimumRequiredPoints` (default 1000) set via `SetMinimumRequiredPoints()`. Currently this is set in `config.Init()` only when `minimum-points > 0` in config.json.

+**Goal**: Remove `MinimumPoints` from config and derive it automatically from the default policy's target points (low=200, medium=500, high=1000).

-### 1. `internal/config/config.go` — Update `ResampleConfig` struct

-- Remove `Resolutions []int` and `Trigger int` fields

-- Add `DefaultPolicy string` (`json:"default-policy"`) — default resample policy ("low", "medium", "high")

-- Add `DefaultAlgo string` (`json:"default-algo"`) — default resample algorithm ("lttb", "average", "simple")

-- Keep `MinimumPoints int` — still useful for the resampler library

-- Keep `TargetPoints int` — used by policy-derived runtime config

+- Remove `MinimumPoints int` field from `ResampleConfig`

+- In `Init()`: replace the `MinimumPoints > 0` check with policy-derived logic:

+  - Use `metricdispatch.TargetPointsForPolicy()` on `DefaultPolicy` (or "medium" if empty) to get the target points

+  - Call `resampler.SetMinimumRequiredPoints(targetPoints)`

+- This requires importing `metricdispatch` — need to check for import cycles

-### 2. `internal/config/schema.go` — Update JSON schema

-- Replace `trigger`/`resolutions` required properties with `default-policy`

-- Add `default-policy` (string, enum: ["low","medium","high"]) and `default-algo` (string, enum: ["lttb","average","simple"]) properties

-- Remove `trigger` and `resolutions` properties

-- No required fields (presence of `"resampling": {}` is enough to enable it)

+**Import cycle concern**: `config` is imported by almost everything. `metricdispatch` imports `config`. So `config` cannot import `metricdispatch`.

-### 3. `internal/routerConfig/routes.go` — Update `resamplingForUser()`

-- When falling back to globalCfg (no user policy set), use `globalCfg.DefaultPolicy` to compute `TargetPoints` and `Trigger` dynamically, same as the user-policy path

-- If `DefaultPolicy` is empty, use "medium" as default

+**Solution**: Inline the policy→targetPoints mapping in `config.Init()` instead of calling `metricdispatch.TargetPointsForPolicy()`. A simple switch on `DefaultPolicy`:

+func targetPointsForPolicy(policy string) int {

+    default:       return 500 // medium fallback

-### 4. `internal/graph/schema.resolvers.go` — Update fallback resolution logic

-- In `JobMetrics()` and `NodeMetricsList()`: replace `slices.Max(config.Keys.EnableResampling.Resolutions)` fallback with policy-based computation using `config.Keys.EnableResampling.DefaultPolicy`

-- Use `TargetPointsForPolicy()` + `ComputeResolution()` instead of picking from a resolutions array

-- Also apply `DefaultAlgo` when no user algo preference is set

-### 5. `internal/graph/resample.go` — Update `resolveResampleAlgo()`

-- After checking user preference, fall back to `config.Keys.EnableResampling.DefaultAlgo` before returning ""

-### 6. `config.json` (local dev) — Update to new format

+    policy := Keys.EnableResampling.DefaultPolicy

+    resampler.SetMinimumRequiredPoints(targetPointsForPolicy(policy))

-### 7. `configs/config.json` (example config) — Update to new format

-Same as above, keeping `minimum-points` if desired.

+- Remove `minimum-points` property from the `resampling` JSON schema object

-The frontend already handles both modes:

-- Policy-based: uses `targetPoints` to compute resolution dynamically

-- Array-based: uses `resolutions` array for fixed steps

+### 3. `config.json` / `configs/config.json`

+- Remove `"minimum-points"` key from the resampling block (it was already removed from `config.json` in the previous commit; check `configs/config.json` still has it)

-Since `resamplingForUser()` already returns a `ResampleConfig` with `TargetPoints`+`Trigger` (no `Resolutions`), the frontend will use the policy path.

-1. `internal/config/config.go` (struct + Init)

-2. `internal/config/schema.go` (JSON schema)

-4. `internal/graph/schema.resolvers.go` (resolver fallbacks)

-5. `internal/graph/resample.go` (`resolveResampleAlgo` fallback)

-7. `configs/config.json` (example config)

+1. `internal/config/config.go` — remove field, add helper, update `Init()`

+2. `internal/config/schema.go` — remove `minimum-points` property

+3. `configs/config.json` — remove `minimum-points` from resampling block

-2. `go test ./internal/metricdispatch/...` — resample policy tests pass

-3. `go test ./internal/config/...` — if config tests exist

-4. Start with `./cc-backend -server -dev` and verify `/config` page shows resampling options

-5. Verify metric plots on job pages still support zoom/resampling

+2. `go test ./internal/config/...` — config tests pass

+3. `go test ./internal/metricdispatch/...` — resample policy tests still pass


ToolSearch

Question: select:ExitPlanMode


Question: select:ExitPlanMode


ExitPlanMode

# Derive MinimumPoints automatically from policy

## Context

The previous commit replaced `resolutions`/`trigger` with `default-policy`/`default-algo` in `ResampleConfig`. The `MinimumPoints` field still exists as a manual config value (`"minimum-points"` in JSON), but it's redundant — since we now always have a policy (user pref or default), the target points from that policy are the natural minimum threshold for resampling.

The resampler library (`cc-lib/v2/resampler`) has a global `MinimumRequiredPoints` (default 1000) set via `SetMinimumRequiredPoints()`. Currently this is set in `config.Init()` only when `minimum-points > 0` in config.json.

**Goal**: Remove `MinimumPoints` from config and derive it automatically from the default policy's target points (low=200, medium=500, high=1000).

## Changes

### 1. `internal/config/config.go`
- Remove `MinimumPoints int` field from `ResampleConfig`
- In `Init()`: replace the `MinimumPoints > 0` check with policy-derived logic:
  - Use `metricdispatch.TargetPointsForPolicy()` on `DefaultPolicy` (or "medium" if empty) to get the target points
  - Call `resampler.SetMinimumRequiredPoints(targetPoints)`
- This requires importing `metricdispatch` — need to check for import cycles

**Import cycle concern**: `config` is imported by almost everything. `metricdispatch` imports `config`. So `config` cannot import `metricdispatch`.

**Solution**: Inline the policy→targetPoints mapping in `config.Init()` instead of calling `metricdispatch.TargetPointsForPolicy()`. A simple switch on `DefaultPolicy`:
```go
func targetPointsForPolicy(policy string) int {
    switch policy {
    case "low":    return 200
    case "medium": return 500
    case "high":   return 1000
    default:       return 500 // medium fallback
    }
}
```

Then in `Init()`:
```go
if Keys.EnableResampling != nil {
    policy := Keys.EnableResampling.DefaultPolicy
    if policy == "" {
        policy = "medium"
    }
    resampler.SetMinimumRequiredPoints(targetPointsForPolicy(policy))
}
```

### 2. `internal/config/schema.go`
- Remove `minimum-points` property from the `resampling` JSON schema object

### 3. `config.json` / `configs/config.json`
- Remove `"minimum-points"` key from the resampling block (it was already removed from `config.json` in the previous commit; check `configs/config.json` still has it)

## Files to modify
1. `internal/config/config.go` — remove field, add helper, update `Init()`
2. `internal/config/schema.go` — remove `minimum-points` property
3. `configs/config.json` — remove `minimum-points` from resampling block

## Verification
1. `go build ./...` — must compile
2. `go test ./internal/config/...` — config tests pass
3. `go test ./internal/metricdispatch/...` — resample policy tests still pass


The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.


[Request interrupted by user for tool use]