Skip to content

Repository files navigation

Flagfold

CI Python 3.11+ License: Apache-2.0

Prove what your flagd targeting rules can actually decide.

Flagfold is a local, deterministic CLI for flagd JSON. It finds unreachable targeting branches, distinguishes impossible conditions from branches shadowed by earlier rules, identifies variants that can never be served, and generates concrete evaluation contexts that witness every reachable decision. diff shows the smallest contexts where a candidate configuration changes a baseline decision.

It does not connect to a provider, upload configuration, execute embedded code, or silently sample. Unsupported semantics fail with an exact JSON path.

See it in 30 seconds

git clone https://github.com/KanadeK/flagfold.git
cd flagfold
uv sync --all-groups

# Clean configuration: exits 0
uv run flagfold analyze examples/baseline.flagd.json

# Known findings: prints evidence and exits 1
uv run flagfold analyze examples/shadowed.flagd.json

The second command reports:

Flagfold analysis (schema 1)
checkout: 5 contexts
  rule-1: reachable -> pro when {"plan":"enterprise"}
  rule-2: shadowed -> beta
  rule-3: impossible -> beta
  fallback: reachable -> control when {}
  dead variants: beta, unused
Summary: 4 semantic findings

Compare two configurations:

uv run flagfold diff examples/baseline.flagd.json examples/candidate.flagd.json
checkout: 9 contexts
  treatment -> control when {"plan":"pro"}

That witness says the old config served treatment to a pro user with no country attribute, while the candidate falls back to control.

Install

Download the wheel from the latest GitHub Release, then:

py -3.11 -m venv .venv
.\.venv\Scripts\python -m pip install .\flagfold-0.1.0-py3-none-any.whl
.\.venv\Scripts\flagfold --version

Flagfold has no runtime dependencies. Python 3.11 or newer is required.

Commands

flagfold analyze INPUT [--format text|json] [--max-contexts N]
flagfold diff BASELINE CANDIDATE [--format text|json] [--max-contexts N]
flagfold --version

--format json emits stable schemaVersion: 1 output for CI. Full field definitions are in docs/formats.md.

Exit Meaning Typical CI action
0 Analysis is clean, or diff has no changed flags Pass
1 Semantic findings or changed decisions exist Review evidence
2 File, JSON, or flagd structure is invalid Fix the path named in the error
3 Targeting uses unsupported semantics Rewrite to the supported subset or do not claim proof
4 Exact enumeration exceeds --max-contexts Raise the reviewed budget or simplify the rule

What v0.1 proves

Flagfold supports a deliberately finite JSON Logic subset:

  • ordered if branches with direct variant-name or null results;
  • and, or, and !;
  • strict equality === and inequality !== with at most one var operand;
  • in with a literal list on the right;
  • var with one non-empty top-level attribute name (no dotted nested path);
  • JSON scalar literals.

For every referenced attribute it builds equivalence-preserving values from rule literals, plus an absent state and one distinct other value. It then evaluates the full Cartesian product. Within this subset, the result is exhaustive—not a random sample.

Loose equality (==/!=), var-to-var comparison, dotted nested var paths, numeric ordering, regular expressions, string containment, nested result expressions, object/array attributes, fractional rollouts, custom flagd operators, and provider-specific formats are not supported in v0.1. Flagfold rejects them instead of guessing.

OpenFeature provides vendor-neutral evaluation APIs and an official CLI for flag manifests. Flagfold stays narrower: it analyzes the semantics of flagd's concrete targeting JSON and complements structural manifest tooling rather than replacing it.

CI example

- name: Prove flag decisions
  run: flagfold analyze config/flags.flagd.json --format json

Exit 1 is intentional when the report contains a shadowed/impossible branch or dead variant. If you only want an artifact, capture the exit code explicitly and review the JSON instead of appending a blanket || true.

Development and acceptance

uv sync --all-groups
uv run pytest --cov=flagfold --cov-branch --cov-fail-under=90
uv run ruff format --check .
uv run ruff check .
uv run mypy src tests scripts
uv build
uv run python scripts/check.py

The last command is the release-equivalent gate: it runs static checks and tests, builds both archives, inspects their contents, installs the wheel into an isolated environment, and executes real clean/finding/diff/failure paths.

Design and safety

The default 5 MiB input cap and 100,000-context budget bound local resource use. Increasing the context budget can consume substantial CPU and memory; Flagfold prints the exact required count before enumeration. Findings prove behavior of the supplied supported rule—not business correctness, production exposure, or safety of a rollout.

License

Apache-2.0. See LICENSE.

About

Offline proofs and witnesses for flagd targeting decisions

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages