Skip to content

Add future_with_report() for explicit negation-resolution audit - #294

Merged
jtdub merged 2 commits into
nextfrom
issue-285-future-report
Aug 5, 2026
Merged

Add future_with_report() for explicit negation-resolution audit#294
jtdub merged 2 commits into
nextfrom
issue-285-future-report

Conversation

@jtdub

@jtdub jtdub commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements #285 (follow-on to #269). Targets next for v4.0.0.

future() resolves negations in explicit tiers (exact match → idempotency replace → shorthand prefix → keep-as-signal), but a leftover literal no ... line was the only way a caller could detect a negation that did not apply cleanly — change-validation pipelines had to grep the render for no lines.

  • HConfig.future_with_report(config, *, prune_empty_branches=False) returns tuple[HConfig, FutureReport]; future() is byte-identical (no overloads or union returns under strict typing).
  • FutureReport (exported from hier_config) is a frozen slotted dataclass:
    • unresolved_negations — negations that matched nothing in the running config (the keep-as-signal tier)
    • idempotency_replacements — negation-form lines that displaced an idempotency-tracked line but persist in the render (e.g. IOS no logging console); positive-form idempotent value updates are normal changes and are excluded
    • Both hold HConfigChild nodes living in the returned future tree, so path()/lineage() give context. A dataclass rather than a Pydantic model because HConfigChild is a plain slotted class and the repo convention forbids arbitrary_types_allowed.
  • Collection threads a private _FutureReportBuilder through the compute_future recursion via a keyword-only parameter; compute_future_with_report() keeps the builder private to tree_algorithms.
  • Report nodes are never removed by prune_empty_branches=True: pruning only deletes childless nodes whose running-config counterpart had children, which cannot match a kept negation or a persisting replacement (covered by test).

Testing

TDD: 8 new integration tests written first (failed on missing import), covering unresolved detection with lineage, clean-change empty report, idempotency-replacement identity with the rendered node, positive-form exclusion, accumulation across recursed sections, pruning interaction, render identity with future(), and report immutability.

  • poetry run ./scripts/build.py lint-and-test exits 0 (757 passed, coverage 97.08%)
  • poetry run mkdocs build --strict passes

Docs & changelog

  • docs/user/future-config.md — new "Auditing negation resolution" section, cross-linked from the unmatched-negations bullet
  • docs/dev/api-reference.mdFutureReport in the Reporting group
  • docs/user/migrating-from-v3.md — "New in v4" bullet
  • CHANGELOG.md — Unreleased → Added (Unresolved-negation report for future() #285)

Closes #285

A leftover literal `no ...` line was the only way a caller could
detect that a change contained a negation that did not apply cleanly
in future() output, forcing change-validation pipelines to grep the
render. HConfig.future_with_report() returns the same prediction
together with a frozen FutureReport of unresolved negations and
persisting idempotency-tracked negation replacements, so pipelines
can assert `not report.unresolved_negations` instead.

FutureReport is a frozen slotted dataclass rather than a Pydantic
model because it holds HConfigChild nodes (plain slotted classes)
and the project convention forbids arbitrary_types_allowed.

Closes #285
@jtdub
jtdub requested a review from aedwardstx as a code owner August 5, 2026 00:58
Both methods carried the same construct-compute-prune pipeline, so a
change to one had to be mirrored in the other. Delegating leaves one
code path; the discarded report costs one small builder allocation
per call.
@jtdub
jtdub merged commit 12dbab3 into next Aug 5, 2026
6 checks passed
@jtdub
jtdub deleted the issue-285-future-report branch August 5, 2026 01:09
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