Skip to content

[Architecture]: define config rebase provenance for deletion vs unseen keys #1478

Description

@lidge-jun

Client or integration

OpenCodex Dashboard / management API

Area

Configuration persistence / provider and model management

Summary

The rebase-on-save landed for #1273 cannot distinguish two situations that look
identical from the merge's point of view:

  • a key is present on disk and absent in the live config because this writer**
    **deleted it;
  • a key is present on disk and absent in the live config because this writer**
    **never held it — another writer added it to the file after our config was
    loaded.

Absence alone does not say which. Both readings are defensible and each breaks
the other's scenario:

Scenario Deletion reading Ignorance reading
live deletes a field, disk still has it (PUT /api/grok/selection with []) correct: field is cleared wrong: field comes back
another writer adds a key after we loaded, we save something unrelated wrong: key is dropped correct: key survives

Two independent fixes for #1462 landed on opposite sides of this. d6e4545b2 on
dev restricts the rebase to keys the baseline knew about, which fixes the Grok
case; the contributor's later head d51bd2856 arms provenance on every config
load, which preserves disk-added keys but leaves the Grok case failing (9 pass /
1 fail on tests/grok-management-api.test.ts). Reimplementing the first rule
inside the second design breaks its own regression, an independently loaded config rebases a non-server save.

Both scenarios currently pass on dev, so this is not an active user-facing
defect — it is an unresolved rule that the next change in this area will hit.

Reproduction

Deletion case, against d51bd2856:

  1. const config = loadConfig(); config.grokExcludedModels = ["a"]; saveConfig(config);
  2. PUT /api/grok/selection with {"excluded": []} on a server whose live
    config was loaded before step 1.
  3. Expected grokExcludedModels to be gone; it is still ["a"].

Ignorance case, against a rebase restricted to baseline-known keys:

  1. const stale = loadConfig(); then, from a second loadConfig(), set
    disabledModels and saveConfig().
  2. stale.contextCapValue = 240_000; saveConfig(stale);
  3. Expected disabledModels to survive; it is dropped.

Version

2.12.x (dev at 62be62222)

Operating system

macOS 15 (arm64); not platform-specific

Provider and model

Not provider-specific

Logs or error output

deletion case:   Expected: undefined      Received: [ "a" ]
ignorance case:  Expected: ["disk/model"] Received: undefined

Anything else

Two directions look viable, and the choice belongs to whoever owns this
subsystem rather than to whichever patch merges last:

  1. Record deletion intent where the deletion happens. There are eight or more
    delete config.<field> sites across the management routes; each would mark
    the removal so the merge can tell intent from absence.
  2. Refresh the baseline on every cooperating write rather than arming it once
    at server startup, so a key added by another writer is no longer invisible to
    this instance.

Context and the full two-scenario analysis:
devlog/_plan/260811_bug_pair_landing/080_wp10_publication_plan.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or requestroadmapAccepted long-term work item; needs its own project cycle

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions