feat: ct adopt grants — emit paste-ready permission config from live rows (#25) - #45
Merged
Merged
Conversation
Read GET /permissions/<domainType>/<domainId>, run the rows through the planner's normalizeActual, and print a paste-ready ct.groupRole / ct.groupTypeRole block. Baseline (modifiedPid===-1) and inherited rows are excluded; revoke/deny rows are preserved-and-noted, not emitted; authIds map back to module:right via the catalog (unknown -> warning comment); scoped dataIds map to managed-group state keys (unmanaged -> placeholder comment). Grants are not state-tracked, so this prints config only and never writes state. Wired as a subcommand of ct adopt.
Member
Author
Review — REQUEST_CHANGES (verified empirically by running the emitter's output through the real
|
…t-only revocations (#25) Addresses PR #45 review (REQUEST_CHANGES): - group_type_role rows with authId >= 10000 (the churchdb:+… family, 52 catalog rights) are no longer emitted as grants — desiredTuples rejects them at plan time. They become NOTE comments explaining they reach roles via inheritance and are not writable on this domain type. - A SCOPED right returned with dataId null (granted globally in CT) no longer collapses to a bare string — which the plan-time silent-global-grant guard rejects. It is emitted as a WARNING comment; ReverseEntry.scoped and CollapsedGrant.hasUnscoped are now actually consulted. An unscoped-per-catalog right carrying dataIds (stale catalog) is likewise a WARNING, not a scope. - Comment-only grants are pending REVOCATIONS: reconciliation deletes any live grant missing from the declaration. The block now prints a header counting them and saying apply will revoke them; the command's stderr guidance and docs/permissions.md's no-op claim say the same. - Renamed the unknown-authId test: it emits a comment only, never a numeric grant (numeric rights are undeclarable in the DSL). - Comment on findByTypeId(state, "group", …): only cdb_gruppe rights truly scope by group; other scopeFields' ids could collide (pre-existing tool-wide constraint, no behavior change). - New round-trip property test: for any mix of rows, every ACTIVE emitted grant passes the real desiredTuples without throwing, on both domain types.
This was referenced Jul 9, 2026
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.
Implements the grant-adoption bullet of #25 (domainId-by-reference waits on #20, in flight; catalog lifecycle separate). This removes the hand-transcription blocker for #23: an existing instance's rights structure can now be read off into config.
Surface
ct adopt grants <domainType> <domainId>— acceptsgroup_role/group_type_role(and hyphenated spellings), prints a paste-readyct.groupRole({...})/ct.groupTypeRole({...})block to stdout. Grants aren't state-tracked, so this is config-only — stated explicitly in the output.Semantics — exactly what the planner would manage
normalizeActualas reconciliation: baseline (modifiedPid === -1) and inherited rows excluded; revoke/deny rows preserved-and-noted, never emitted.module:rightreverse-catalog mapping; unknown authId emits the numeric form with a WARNING comment instead of failing.ct adopt group <id>first.assertNotPeopleenforced.Pure
emitAdoptedGrantscore (no network) + thin command wrapper; no changes topermissions/plan.ts/types.ts(parallel #20 branch owns those).Verification: 296 tests passing (13 new), typecheck + lint clean.
Addresses #25 (grant-adoption bullet).