Skip to content

fix(config): report uncompilable stored index patterns - #2728

Merged
ScriptedAlchemy merged 2 commits into
masterfrom
fleet/root-cause-swallowed-errors
Sep 30, 2026
Merged

ScriptedAlchemy merged 2 commits into
masterfrom
fleet/root-cause-swallowed-errors

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Root cause

Releases through v1.0.0-beta.63 (the published build) stored index.exclude.v1 / index.include.v1 patterns without compiling them; #2514 added the compile check to the write path only. When the runtime pin later decoded such a stored pattern, compilable_index_patterns dropped it and logged one tracing::warn! per process (deduplicated through a process-global WARNED set). A broken exclude therefore silently widened what gets indexed. The only trace was a log line.

What changed

  • ResolvedSetting (the ConfigurationGet result) gains findings: Vec<ConfigurationSettingFindingV1>. A stored index path pattern that does not compile is invalid_index_path_pattern { pattern, message, legal_actions: [set, unset] }.
  • One classifier, partition_index_patterns, serves both the runtime pin (applied patterns) and setting_findings (the typed report). The project and profile get paths both call setting_findings. The WARNED set and its log line are deleted.
  • tracedecay doctor reads both index keys for the served current project and reports each finding as an issue naming the key, the pattern, the compiler message and the repair.
  • The registry doc for validate_written_value now describes the finding instead of the skip.
  • SDK regenerated (additive: new result field and its two types). The dashboard contracts are unchanged.

Why indexing continues without the pattern instead of refusing

The configuration crate's documented rule for a check that tightens across releases (ConfigurationRegistry::validate_written_value) is to refuse new writes while stored values keep loading. That split exists so read-time validation cannot strand a store. A refusal inside PinnedRuntimeConfiguration::new would fail project open. ProjectConfigurationRuntime::current() builds the same pin, so that refusal would also take down tracedecay_configuration_set / _unset, the only legal repair. The pin keeps applying the remaining patterns, and the unapplied one is reported as a typed finding through the configuration read and doctor instead of a log line.

Fail before / pass after

Test: config::tests::runtime_configuration_cutover::a_stored_uncompilable_index_pattern_is_a_typed_setting_finding in tracedecay-project. It seeds an isolated registered project store the way beta.63 left it (canonical revision with the daemon source binding plus index.exclude.v1 = ["src/[abc", "docs/**"]), opens it through the production open_runtime_configuration_for_registered_database path, and reads through the daemon configuration client.

With the production changes reverted:

assertion `left == right` failed
  left: Null
 right: Array [Object {"kind": String("invalid_index_path_pattern"), "legal_actions": Array [String("set"), String("unset")], "message": String("unclosed character class; missing ']'"), "pattern": String("src/[abc")}]
test result: FAILED. 0 passed; 1 failed

With the fix (after rebasing onto master): test result: ok. 20 passed; 0 failed (tracedecay-project --lib config::).

Runtime journey (the real upgrade path)

Isolated HOME/TRACEDECAY_DATA_DIR, one daemon at a time under systemd-run --user --scope -p MemoryMax=6G -p MemorySwapMax=1G.

  1. Shipped tracedecay 1.0.0-beta.63 daemon: tracedecay init, then tracedecay_configuration_set index.exclude.v1 ["src/[abc","docs/**"] → Outcome: effect (accepted unchecked). Daemon stopped.
  2. This branch's debug CLI daemon on the same profile:
$ tracedecay tool tracedecay_configuration_get --args '{"key":"index.exclude.v1"}'
      "effective_value": { "kind": "string_list", "value": [ "src/[abc", "docs/**" ] },
      "findings": [
        { "kind": "invalid_index_path_pattern", "legal_actions": [ "set", "unset" ],
          "message": "unclosed character class; missing ']'", "pattern": "src/[abc" }
      ],
- Status: `success`

$ tracedecay doctor
  ✘ index.exclude.v1 stores pattern "src/[abc" that does not compile (unclosed character class; missing ']'); indexing runs without it. Set index.exclude.v1 to patterns that compile or unset it (tracedecay_configuration_set / tracedecay_configuration_unset)
exit=1

$ tracedecay tool tracedecay_files --args '{"format":"json"}'
{"count":1,"files":[{"bytes":17,"path":"src/lib.rs","symbols":1}],"layout":"grouped"}   # docs/** still applied

$ tracedecay tool tracedecay_configuration_set ... ["src/[abc"]
Error: tracedecay_configuration_set refused the request (configuration.invalid_request)
$ tracedecay tool tracedecay_configuration_unset ... index.exclude.v1   → Outcome: effect
$ tracedecay tool tracedecay_configuration_get ...                      → "findings": [],
$ tracedecay doctor | grep -c index.exclude.v1                          → 0

Daemon stopped; pgrep -af 'tracedecay daemon run' shows none from this lane.

Checks

  • On the final tree (master a908b62 merged in; the run_doctor conflict with fix(feedback): publish the advisory mount's own typed state #2724 was resolved by keeping master's shape and guarding the new check on daemon_status == Some(Ok(Some(_)))):
    • cargo test -p tracedecay-project --lib config::: 20 passed (includes the new test)
    • cargo test -p tracedecay --lib doctor::: 50 passed
    • clippy on all six touched crates: 0 errors, 0 warnings; contracts:check: up to date; fmt: clean
  • Earlier on the same change:
  • cargo test -p tracedecay-configuration --lib: 37 passed; -p tracedecay-contracts --lib: 432 passed
  • cargo test -p tracedecay-global-db --lib configuration: 46 passed; -p tracedecay-daemon-service --lib configuration: 16 passed
  • cargo clippy -p tracedecay-contracts -p tracedecay-configuration -p tracedecay-global-db -p tracedecay-daemon-service -p tracedecay-project -p tracedecay --all-targets -- -D warnings: clean
  • cargo fmt --all -- --check: clean; pnpm --dir dashboard run contracts:check: contracts up to date
  • ripwire --quality-delta=HEAD: gating=0

Follow-ups outside this PR

  • The companion finding (unrepresentable Git paths vanish from coverage) needs code_index_scheduler/reconcile.rs and registry.rs, which other lanes own, plus a naming decision: code-index: unrepresentable Git paths vanish from the snapshot while status reports complete #2721.
  • Doctor's canonical configuration family (configuration_read_from_pin in doctor_kernel.rs, owned by the doctor lane) still reports configuration.resolved.in-sync for a pin with unapplied patterns. It could read setting_findings to report partial coverage.

Releases through v1.0.0-beta.63 stored index.exclude.v1 and
index.include.v1 patterns without compiling them. The runtime pin dropped
such a pattern behind a process-deduplicated tracing::warn!, so a broken
exclude silently widened what gets indexed.

A stored pattern that does not compile is now a typed setting finding.
ConfigurationGet returns it in ResolvedSetting.findings with the pattern,
the compiler message and the legal actions (set, unset), and `tracedecay
doctor` reports it as an issue naming the key and its repair. The pin
keeps applying the remaining patterns: the configuration control plane is
built on the same pin, so refusing it would also refuse the set/unset that
repairs it. New writes of such a pattern stay refused. The WARNED set is
deleted.
@changeset-bot

changeset-bot Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: afb5969

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

…allowed-errors

# Conflicts:
#	crates/tracedecay/src/doctor.rs
@ScriptedAlchemy
ScriptedAlchemy merged commit e27b73d into master Sep 30, 2026
4 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the fleet/root-cause-swallowed-errors branch September 30, 2026 03:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant