Repository navigation
fix: destroy 404 wedge, adopt group-role DSL collision, dynamic normalizer + demote convergence - #37
Merged
Merged
Conversation
#32) The delete loop's catch had no 404 special-case: an already-deleted target hit the catch, returned before the state deletion (falsely claiming it was removed), and skipped all remaining targets — wedging every re-run. Treat CtApiError 404 as success-with-note: drop the state entry, save, and continue to the next target (mirrors the backup loop). Non-404 errors still stop with state saved up to that point. Extracted the loop into an exported runDeleteLoop for direct testing.
configSnippet camelCased group-role to groupRole(...), but the DSL's groupRole
is now the permission function definePermission("group_role") — pasting an
adopted role broke every subsequent plan. ConfigContext had no resource entry
for the group-role type at all.
Add a dslName field to the registry (default: camelCase of the type); the
group-role entry emits roleDefinition(...). ConfigContext now declares
roleDefinition -> define("group-role"), leaving the permission groupRole /
groupTypeRole functions untouched. Adds a round-trip property test over all
RESOURCES: every adoptable type's snippet loads cleanly through loadConfig.
…one demote (#34) a) coerceScalars parseInt-ed any /^-?\d+$/ string in the query subtree, so a zip like '01067' became 1067 and >2^53 digit strings lost precision — written back on any dynamic change, silently breaking JSONLogic string compares. Restrict coercion to canonical integer strings (/^(-?[1-9]\d*|0)$/) within Number.MAX_SAFE_INTEGER; a canonical 5 vs "5" pair still diffs equal. b) status:'none' demote never converged: actual side is the sentinel {status:'none',ruleset:{}} but desired folded the user's full ruleset — a perpetual update. Fold the desired side to the same sentinel when status is 'none' (docs still say keep the dynamic block). Also tolerate a 404 on the demote ruleset DELETE (never-dynamic / already-demoted group).
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.
Fixes three verified bugs from the 2026-07-08 codebase review.
#32 — destroy wedges permanently on already-deleted targets
The delete loop treated a 404 like any error: printed a message falsely claiming the target was removed from state, returned before the state deletion, and skipped all remaining targets. Now a 404 is success-with-note (entry dropped from state, saved, loop continues); non-404 errors still stop with an accurate "state saved up to this point" message. Loop extracted to a testable
runDeleteLoop.#31 —
ct adopt group-roleemits undeclarable configconfigSnippetemittedgroupRole(...), which the permissions feature took over. The master-data resource is now declarable asroleDefinition(...); the registry gained adslNamefield (default camelCase) so snippets always emit the real DSL function name. New round-trip property test: every registry type's adopt snippet must load cleanly throughloadConfig.#34 — dynamic normalizer corrupts leading-zero strings;
status: "none"never convergesNumber.isSafeInteger—'01067'and >2^53 digit strings pass through untouched;"5"vs5still diffs equal.status === "none", the desired side folds to the same{status:"none", ruleset:{}}sentinel as the actual side — a demoted group with its authored ruleset kept (per the documented contract) is now a no-op on re-plan. The demote ruleset DELETE tolerates 404.Verification: 231 passed / 4 skipped, typecheck + lint clean.
Closes #32. Closes #31. Closes #34.