Check a remote config wires up every flag, without running the app - #36
Merged
Conversation
A large config file has too many values to trace by hand, and two ways for one to be wrong that no single assertion catches. A flag whose remoteKey matches nothing is not an error — it falls through to its default, which looks exactly like a backend that sent nothing. And a value in the file that no flag reads is invisible until someone notices the feature never turned on. FlagMappingAudit reports both directions. It reuses the validation RemoteOverrideSource applies — extracted into FlagSchema.mapRemote so the two cannot drift — so a value it calls mapped is one a real apply would accept, and types are never the audit's concern. What it adds is coverage: absent flags (forward), and unconsumed values (reverse, from the declared remoteKey paths). Unconsumed values are reported but do not fail by default, because a real config carries metadata no flag reads. `strict` requires every value to be read; `ignoring` excuses the prefixes a file is expected to carry. The same typo often shows from both sides — an absent flag and an unconsumed value — which is what makes it findable. FeatureFlagTestSupport adds XCTAssertFlagsFullyMapped, which reports the whole audit at the call site. It links XCTest behind canImport, so it builds for every platform and belongs only in a test target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NnwCqKNm8rZ63Au6bpYdaD
A DocC article on validating a remote config — both coverage directions, why an unconsumed value usually is not a fault, and how to run it from a test or a debug build. Linked from Remote overrides and the topics, and the README lists it. The demo's bundled payloads are now audited end to end: every one wires up every flag it provides, passes strict with nothing unconsumed, and a deliberately misspelled path is caught as both an absent flag and an unconsumed value. This is the pattern to copy for a real config, run against the real demo config. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NnwCqKNm8rZ63Au6bpYdaD
The leaf walker treated an empty root object as a leaf with an empty path, so an empty payload reported one unconsumed value that was the empty string — a spurious strict-mode failure, and a blank bullet in the report. A root-level array produced paths with a leading dot, since the array branch did not guard the empty prefix the way the object branch did. Both fixed. A nested empty container still has a real path and stays reported, since a flag may have expected something beneath it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NnwCqKNm8rZ63Au6bpYdaD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A large config file has too many values to trace by hand, and two ways for one to
be wrong that no single assertion catches. A flag whose
remoteKeymatches nothingis not an error — it falls through to its default, which looks exactly like a
backend that sent nothing. And a value in the file that no flag reads is invisible
until someone notices the feature never turned on.
Both directions
remoteKeythe payload does not supply.Almost always a path typo, otherwise indistinguishable from a value the backend
chose not to send.
usually shows here too, which is what makes it findable.
The audit never checks types itself — it reuses the validation
RemoteOverrideSourceapplies, extracted into
FlagSchema.mapRemoteso the two cannot drift. A value itcalls applied is one a real apply would accept; any type or enum-case problem
arrives as a
RemoteOverrideProbleminmismatched, the whole list at once.Lenient by default
A real config carries metadata no flag reads, so unconsumed values are reported but
do not fail
isComplete.strict: truerequires every value to be read;ignoring: ["meta"]excuses the prefixes a file is expected to carry.A record flag's
remoteKeynames a subtree, so every leaf beneath it counts as read— a keyed list of a hundred endpoints is not a hundred unconsumed values.
Packaging
FlagMappingAuditlives inFeatureFlag, so any test target already reaches it, andrequireComplete()suits a debug-build check at launch.FeatureFlagTestSupportaddsXCTAssertFlagsFullyMapped, which reports the whole audit at the call site; it linksXCTest behind
canImport, builds for every platform, and belongs only in a testtarget.
Adversarial review
Two defects in the leaf-path walker, both at degenerate roots. An empty payload
{}reported one unconsumed value that was the empty string — a spurious strict-mode
failure and a blank bullet in the report. A root-level array produced paths with a
leading dot. Both fixed; a nested empty container still has a real path and stays
reported.
Verified
743 tests on a clean build. DocC clean with
--warnings-as-errors, both example appsbuild, and the iOS package scheme builds with the new XCTest-linking product. The
demo's own three bundled configs audit clean end to end, with a test proving a
misspelled path is caught from both sides.
🤖 Generated with Claude Code
https://claude.ai/code/session_01NnwCqKNm8rZ63Au6bpYdaD