Skip to content

feat(dry-run): tag-selection observability#85

Merged
bryantbiggs merged 2 commits into
mainfrom
feat/tag-selection-observability
May 5, 2026
Merged

feat(dry-run): tag-selection observability#85
bryantbiggs merged 2 commits into
mainfrom
feat/tag-selection-observability

Conversation

@bryantbiggs

Copy link
Copy Markdown
Member

Summary

  • Each resolved mapping emits one INFO line per sync run: {source} -> {target}: {kept} of {N} tags => [t1, t2]. Operators see at a glance which tags survived filtering and where they're going.
  • --dry-run now renders the full filter trace per mapping: source candidates, include path (rescued tag names with sample cap), pipeline stages with attrition, kept tags (with [via include] markers on rescued tags), dropped Pareto-sorted by reason with samples, and min_tags status. When min_tags is configured but unsatisfied, the formatter prints (kept M, real sync will FAIL with BelowMinTags) so the configuration can be fixed before running. -v removes the per-reason sample cap.
  • Default log format is now text everywhere (was JSON-by-default in Kubernetes via KUBERNETES_SERVICE_HOST auto-detection). The chart's logging.format value (default text) drives --log-format, and a new templates/NOTES.txt shows a one-time upgrade hint when the default is in use. Set logging.format: json to keep structured logs.
  • Internal: filter pipeline gets apply_with_report() returning Filtered { kept, report }. The report carries candidate_count, include_kept (rescued tag names), pipeline stages, Pareto-sorted drop reasons, and min_tags. Hot-path apply() skips drop attribution to avoid per-cycle allocations on the watch loop. partition_with_drop and push_drop_reason helpers consolidate the per-stage single-pass tracking.

Test plan

  • cargo fmt --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace (1333 passed)
  • cargo deny check
  • npm run --prefix docs build
  • helm unittest charts/ocync (63 tests passed)
  • helm install --dry-run confirms NOTES.txt renders with the upgrade hint when logging.format=text and suppresses it when logging.format=json
  • New filter.rs tests cover the kept+dropped invariant, include rescue by name, min_tags round-trip (configured/satisfied/absent), and the single-pass attribution invariant
  • New synchronize.rs E2E tests exercise the full path through select_filtered_tags -> ResolvedMapping -> dry_run::write_to, catching wire-up regressions
  • New dry_run.rs tests cover min_tags status (satisfied / FAIL / absent / blank-separator), include-rescue rendering and verbose-uncap, and the full mapping render with both [via include] markers and the FAIL warning

- Add FilterConfig::apply_with_report() returning Outcome { kept, report }
  with per-stage attrition and Pareto-sorted drop attribution. apply()
  delegates and re-checks min_tags. Existing callers stay drop-in.
- ResolvedMapping carries candidates: Option<usize> and filter_report:
  Option<Report>. None on the exact-tag fast path.
- synchronize::run emits one INFO line per mapping with per-line targets:
  "{source} -> {target}: {kept} of {N} tags  =>  [t1, t2]".
- New src/cli/commands/dry_run.rs writer-based formatter renders linear
  and include-path layouts with sample cap (5 by default, removed under
  -v). System-exclude rows surface a hint pointing at include:.
- Default log format is text everywhere; --log-format json opts in.
  Removed KUBERNETES_SERVICE_HOST auto-detection.
- Chart values.yaml gains logging.format (default text); _pod.tpl wires
  --log-format. Existing chart-deployed installs switch from JSON to
  text on upgrade; set logging.format: json to keep JSON output.
Dry-run output is the user's primary tool for confirming what will sync.
Two gaps closed and one efficiency fix:

- Surface min_tags status: when configured and unsatisfied, real-sync
  errors with BelowMinTags. The formatter now prints
  "min_tags: N (kept M, real sync will FAIL with BelowMinTags)" so the
  configuration can be fixed before running.
- Show include-rescued tag names: include_kept moves from a count to a
  Vec<String> so operators can verify their include: patterns matched
  what they intended. Rescued tags also get a [via include] marker in
  the kept section.
- Hot path: gate the report's Vec<String> materialization on track so
  the watch-mode loop pays no allocation for drop attribution.

Cleanups:
- Rename filter::Outcome -> Filtered; FilterReport.candidates ->
  candidate_count.
- Extract partition_with_drop / push_drop_reason helpers for the
  glob and semver stages (single-pass kept + dropped).
- Slim formatter signatures to take primitives (kept count, min_tags)
  instead of &ResolvedMapping + &FilterReport where only counts are
  needed.

Tests:
- New filter.rs tests cover min_tags round-trip, include-rescued
  names, and the single-pass attribution invariant.
- New synchronize.rs E2E tests exercise the full path through
  select_filtered_tags + ResolvedMapping + dry_run::write_to,
  catching wire-up regressions.

Chart and docs:
- charts/ocync/templates/NOTES.txt prints the active log format and,
  when text, a one-time upgrade hint about the JSON->text default flip.
- cli-reference.md gains a Dry-run output section explaining each
  rendered section and the -v sample-uncap interaction.
- observability.md notes that -v also uncaps dry-run sample lists.
@bryantbiggs
bryantbiggs merged commit d197816 into main May 5, 2026
16 checks passed
@bryantbiggs
bryantbiggs deleted the feat/tag-selection-observability branch May 5, 2026 18:29
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