Skip to content

[AIGTWY-4901] Record the settings key paths ug writes - #920

Open
david-siqi-liu wants to merge 1 commit into
mainfrom
david/aigtwy-4901-1-provenance
Open

david-siqi-liu wants to merge 1 commit into
mainfrom
david/aigtwy-4901-1-provenance

Conversation

@david-siqi-liu

@david-siqi-liu david-siqi-liu commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

What this adds and why

ug writes into config files it shares with other writers: Claude Code's ~/.claude/ucode-settings.json and the OS-managed managed-settings.json, and Codex's config files. Those files also hold settings an admin, Isaac, MDM, or the developer put there. When a managed config changes (tracing turned off, a model list removed), ug has to clean up what it previously wrote, and today it removes keys by name, which can delete a value someone else owns at the same key.

This PR adds the record that lets later cleanup be surgical. For each agent and destination file, it stores the key paths ug wrote and the exact value it wrote at each. A key is removed or restored only when the file still holds the value ug last wrote there; if anyone else changed it, ug leaves it alone. No writer records or consumes it yet; the following PRs make ug configure do both. ug revert clears it.

The store lives at ~/.ucode/managed-backups/provenance.json, keyed by agent and destination path so it survives workspace switches, and is written under a file lock so concurrent ug runs don't drop each other's records. Comparisons are type-strict, and an explicit null is distinct from an absent key.

What goes inside

For each agent and file, a list of {path, value} entries: the setting ug wrote and the value it wrote.

It is deliberately not a 1:1 list of every field ug manages. It holds only the keys a cleanup path actually consults. For Claude that is effectively its full written set. For Codex today it is just model, the one field ug conditionally removes; the Codex provider block and HTTP headers are reconciled a different way and are not recorded. Values other writers put in the files are never recorded, that is the whole point.

Full store after a managed ug configure for both agents (Claude static model list, tracing on)
{
  "files": {
    "claude:/root/.claude/ucode-settings.json": [
      {
        "path": [
          "apiKeyHelper"
        ],
        "value": "/usr/local/bin/ug auth-token --host https://eng-ml-inference.staging.cloud.databricks.com"
      },
      {
        "path": [
          "availableModels"
        ],
        "value": [
          "system.ai.claude-haiku-4-5"
        ]
      },
      {
        "path": [
          "enforceAvailableModels"
        ],
        "value": true
      },
      {
        "path": [
          "env",
          "ANTHROPIC_BASE_URL"
        ],
        "value": "https://eng-ml-inference.staging.cloud.databricks.com/ai-gateway/anthropic"
      },
      {
        "path": [
          "env",
          "ANTHROPIC_CUSTOM_HEADERS"
        ],
        "value": "x-databricks-use-coding-agent-mode: true\nUser-Agent: ucode/0.0.0 claude/2.1.280"
      },
      {
        "path": [
          "env",
          "ANTHROPIC_DEFAULT_HAIKU_MODEL"
        ],
        "value": "system.ai.claude-haiku-4-5"
      },
      {
        "path": [
          "env",
          "ANTHROPIC_DEFAULT_OPUS_MODEL"
        ],
        "value": "system.ai.claude-opus-4-5"
      },
      {
        "path": [
          "env",
          "ANTHROPIC_DEFAULT_SONNET_MODEL"
        ],
        "value": "system.ai.claude-sonnet-4-5"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_API_KEY_HELPER_TTL_MS"
        ],
        "value": "900000"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_ENABLE_TELEMETRY"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS"
        ],
        "value": "900000"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_PROPAGATE_TRACEPARENT"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_USE_GATEWAY"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "ENABLE_PROMPT_CACHING_1H"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "ENABLE_TOOL_SEARCH"
        ],
        "value": "true"
      },
      {
        "path": [
          "env",
          "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
        ],
        "value": "https://eng-ml-inference.staging.cloud.databricks.com/ai-gateway/otel/v1/traces"
      },
      {
        "path": [
          "env",
          "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
        ],
        "value": "http/protobuf"
      },
      {
        "path": [
          "env",
          "OTEL_TRACES_EXPORTER"
        ],
        "value": "otlp"
      },
      {
        "path": [
          "modelPicker"
        ],
        "value": {
          "replaceBuiltInOptions": true,
          "options": [
            {
              "model": "system.ai.claude-haiku-4-5",
              "label": "Claude Haiku 4.5"
            }
          ]
        }
      },
      {
        "path": [
          "otelHeadersHelper"
        ],
        "value": "/usr/local/bin/ug otel-headers --host https://eng-ml-inference.staging.cloud.databricks.com"
      },
      {
        "path": [
          "permissions",
          "deny"
        ],
        "value": [
          "WebSearch"
        ]
      }
    ],
    "claude:/etc/claude-code/managed-settings.json": [
      {
        "path": [
          "apiKeyHelper"
        ],
        "value": "/usr/local/bin/ug auth-token --host https://eng-ml-inference.staging.cloud.databricks.com"
      },
      {
        "path": [
          "availableModels"
        ],
        "value": [
          "system.ai.claude-haiku-4-5"
        ]
      },
      {
        "path": [
          "enforceAvailableModels"
        ],
        "value": true
      },
      {
        "path": [
          "env",
          "ANTHROPIC_BASE_URL"
        ],
        "value": "https://eng-ml-inference.staging.cloud.databricks.com/ai-gateway/anthropic"
      },
      {
        "path": [
          "env",
          "ANTHROPIC_CUSTOM_HEADERS"
        ],
        "value": "x-databricks-use-coding-agent-mode: true\nUser-Agent: ucode/0.0.0 claude/2.1.280"
      },
      {
        "path": [
          "env",
          "ANTHROPIC_DEFAULT_HAIKU_MODEL"
        ],
        "value": "system.ai.claude-haiku-4-5"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_API_KEY_HELPER_TTL_MS"
        ],
        "value": "900000"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_ENABLE_TELEMETRY"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS"
        ],
        "value": "900000"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_PROPAGATE_TRACEPARENT"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "CLAUDE_CODE_USE_GATEWAY"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "ENABLE_PROMPT_CACHING_1H"
        ],
        "value": "1"
      },
      {
        "path": [
          "env",
          "ENABLE_TOOL_SEARCH"
        ],
        "value": "true"
      },
      {
        "path": [
          "env",
          "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
        ],
        "value": "https://eng-ml-inference.staging.cloud.databricks.com/ai-gateway/otel/v1/traces"
      },
      {
        "path": [
          "env",
          "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
        ],
        "value": "http/protobuf"
      },
      {
        "path": [
          "env",
          "OTEL_TRACES_EXPORTER"
        ],
        "value": "otlp"
      },
      {
        "path": [
          "modelPicker"
        ],
        "value": {
          "replaceBuiltInOptions": true,
          "options": [
            {
              "model": "system.ai.claude-haiku-4-5",
              "label": "Claude Haiku 4.5"
            }
          ]
        }
      },
      {
        "path": [
          "otelHeadersHelper"
        ],
        "value": "/usr/local/bin/ug otel-headers --host https://eng-ml-inference.staging.cloud.databricks.com"
      },
      {
        "path": [
          "permissions",
          "deny"
        ],
        "value": [
          "WebSearch"
        ]
      }
    ],
    "codex:/root/.codex/ucode.config.toml": [
      {
        "path": [
          "model"
        ],
        "value": "system.ai.gpt-5-4-nano"
      }
    ],
    "codex:/etc/codex/managed_config.toml": [
      {
        "path": [
          "model"
        ],
        "value": "system.ai.gpt-5-4-nano"
      }
    ]
  }
}

Adds an AGENTS.md section ("How ug tracks what it wrote (provenance)") explaining the record's purpose and mechanism.

Part of AIGTWY-4901. Intended behavior follows the field allow list in AGENTS.md.

This pull request and its description were written by Isaac.

@david-siqi-liu
david-siqi-liu added this pull request to stack #924 October 1, 2026 02:13
@david-siqi-liu
david-siqi-liu removed this pull request from stack #924 October 1, 2026 02:14
@david-siqi-liu
david-siqi-liu force-pushed the david/aigtwy-4901-1-provenance branch from 5f7fc91 to 35ebe60 Compare October 1, 2026 02:28
@david-siqi-liu
david-siqi-liu changed the base branch from david/picker-ownership-gate to main October 1, 2026 02:28
@david-siqi-liu
david-siqi-liu added this pull request to stack #926 October 1, 2026 02:29
@david-siqi-liu david-siqi-liu changed the title Record the settings key paths ug writes, per destination file [AIGTWY-4901] Record the settings key paths ug writes Oct 1, 2026
@david-siqi-liu
david-siqi-liu force-pushed the david/aigtwy-4901-1-provenance branch 4 times, most recently from 07e74c7 to 609ed43 Compare October 2, 2026 13:31
@david-siqi-liu
david-siqi-liu force-pushed the david/aigtwy-4901-1-provenance branch 2 times, most recently from af1b80f to c222dab Compare October 6, 2026 01:34
@david-siqi-liu david-siqi-liu added the ug-review Run the automated UG review label Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

UG review

Adds a persistent per-file ownership store and reconciliation helpers, with revert now deleting the store. Configure and launch do not yet record or consume provenance.

Minor

  • Expose shared configuration helpers — src/ucode/provenance.py:186: can we expose the reused path, sentinel, and file-write helpers publicly rather than coupling provenance cleanup to five private managed_files APIs?

Automated advisory review of fff7dffe7ea9 using Lilly's UG review rubric. It does not approve or block this PR.

@david-siqi-liu

Copy link
Copy Markdown
Collaborator Author

Fixed in 0f4fa29: owned_after_write now deep-copies every recorded value, so the record is a snapshot rather than an alias of the generated overlay or the written document. I found no current caller that mutates a recorded value in place (env values are strings; list/dict values are replaced, not edited), so this closes a latent hazard rather than a live bug. Added test_owned_values_do_not_alias_the_written_document, which fails on the old code.

@david-siqi-liu
david-siqi-liu force-pushed the david/aigtwy-4901-1-provenance branch from c222dab to 0f4fa29 Compare October 6, 2026 15:30
@david-siqi-liu

Copy link
Copy Markdown
Collaborator Author

Major: fixed in d22ee06. values_at (used to seed ownership for files without a record) now deep-copies too, matching owned_after_write, so neither path can alias the written document. Added test_values_do_not_alias_the_document, which fails on the old code.

Minor (private managed_files helpers): leaving as is in this stack. Promoting _MISSING, _path_value, _set_path_value, _delete_path_value and _write_private_file to a public API means renaming them across managed_files and its existing callers, an unrelated refactor; happy to do it as a follow-up.

@david-siqi-liu
david-siqi-liu force-pushed the david/aigtwy-4901-1-provenance branch 2 times, most recently from d22ee06 to 7d82776 Compare October 6, 2026 17:14
@david-siqi-liu

Copy link
Copy Markdown
Collaborator Author

Major (absent-store cleanup): fixed in 7d82776. clear_provenance now returns before taking the lock when there is no store, so ug revert never creates, chmods, or locks ~/.ucode/managed-backups just to find nothing to delete. Added test_clear_without_a_store_leaves_the_backup_dir_untouched, which fails on the old code.

Minor (private helpers): same as above, deferring the public API for the managed_files helpers to a follow-up.

@david-siqi-liu
david-siqi-liu force-pushed the david/aigtwy-4901-1-provenance branch 2 times, most recently from fda8d8b to 2f84de4 Compare October 6, 2026 21:13
@david-siqi-liu
david-siqi-liu removed this pull request from stack #926 October 7, 2026 14:26
@david-siqi-liu
david-siqi-liu force-pushed the david/aigtwy-4901-1-provenance branch from 2f84de4 to 4c75920 Compare October 7, 2026 14:26
@david-siqi-liu
david-siqi-liu added this pull request to stack #1022 October 7, 2026 14:27
@david-siqi-liu david-siqi-liu changed the title [AIGTWY-4901] Record the settings key paths ug writes [AIGTWY-4901] Record the settings key paths ug writes, and only remove a Codex model ug wrote Oct 7, 2026
Cleanup in the Claude and Codex writers removes settings by key name, so it
also deletes values another tool or the developer wrote. This adds the
provenance record later changes use to prove authorship: for each agent and
destination file, the key paths ug wrote and the exact values it wrote there.

- provenance.py stores the record under the managed-backups directory, guarded
  by a file lock so concurrent ug runs don't drop each other's records.
- owned_after_write keeps only values that landed, or that ug wrote earlier
  and nobody changed; retire and retire_group restore a baseline value or
  delete only owned, unedited paths. Comparisons are type-strict.
- values_at reads the values a document holds at given key paths.
- ug revert clears the store after clearing state.

Co-authored-by: Isaac <no-reply@databricks.com>
@david-siqi-liu
david-siqi-liu removed this pull request from stack #1022 October 7, 2026 14:32
@david-siqi-liu
david-siqi-liu added this pull request to stack #1023 October 7, 2026 14:32
@david-siqi-liu david-siqi-liu changed the title [AIGTWY-4901] Record the settings key paths ug writes, and only remove a Codex model ug wrote [AIGTWY-4901] Record the settings key paths ug writes Oct 7, 2026
@david-siqi-liu
david-siqi-liu force-pushed the david/aigtwy-4901-1-provenance branch from 4c75920 to fff7dff Compare October 7, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ug-review Run the automated UG review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant