The access control model decision record: why the RBAC vs ABAC choice you made determines your permission granularity ceiling and your audit trail completeness

The access control model is chosen during the first sprint that introduces multiple user types or organizational boundaries — the multi-tenant sprint, the first enterprise deal, the team-level sharing feature that requires distinguishing viewers from editors. Almost always it is role-based access control. The choice is obvious because the current requirement is exactly three roles and role-based access control maps one-to-one with how the team thinks about user types: Viewer can see, Editor can edit, Admin can do everything. The model that doesn't fit this framing doesn't become visible until the application grows past the expressiveness ceiling of three clean roles — the first "user A can edit records they created but not records created by user B" requirement, the first enterprise deal with a requirement to restrict access by organizational unit rather than by role, the first compliance requirement that needs a permission that doesn't correspond to a job function. Each of these requirements is handled as a special case: a new role, a code-level exception to the role check, an override table. The exception accumulation rate is low enough that no single sprint feels like a permission model crisis. The crisis is visible only in aggregate: four years later the role table has 47 rows, the exception grants table has hundreds of entries with no clear expiry policy, and the question "who can access this record" requires tracing through four tables and a ten-minute explanation of the exception history to an engineer who has never seen the access control code before.

The access control model decision record makes the model selection rationale, the expressiveness ceiling, the audit trail requirements, and the migration path explicit before the team is managing an access control implementation that has outgrown its design basis. It is not a technical specification — the permission checks in the codebase are the specification. It is the record that allows a future engineer, a new CTO, or a SOC 2 auditor to answer questions the code cannot: why RBAC rather than ABAC, what happens when the role count exceeds the model's expressiveness, which query answers "who can access resource X" in under ten minutes, and what the plan is when the current model is no longer adequate. Without the record, each of those questions requires an archaeological investigation into four years of code evolution, and the answers that emerge are "we chose RBAC because it was the obvious choice" and "there is no enumerable answer to who can access this resource without running the application."

Two things that happen when the decision is not written down

The RBAC implementation that accumulated 47 role combinations

A 35-person B2B SaaS company built its access control model in the multi-tenant sprint. The initial requirement was unambiguous: three user types — Viewer, Editor, Admin — with a simple permission matrix that could be written on a whiteboard in two minutes. RBAC was the implementation choice: a roles table with three rows, a role_permissions junction table, a user_roles assignment table, and a permission check function that looked up the user's role and evaluated the permission matrix. The sprint took three days. No decision record was written, because the choice felt too obvious to document.

Six months in: a paying customer asked for a Manager role that could edit team members' records but not the organization's billing information. A new role was added. Twelve months in: the sales team closed an enterprise deal whose security questionnaire required organizational unit scoping — users in the Sales department should not see records owned by the Engineering department, and vice versa. The team added a department column to the users table and added department-scoped permission checks alongside the role checks. Eighteen months in: a product decision introduced "personal workspaces" where users could create records visible only to themselves — a resource ownership model that the role system had no mechanism to express. The team added a record_owner_id column and a code-level check: if the record has an owner, check ownership before checking role. Twenty-four months in: a compliance requirement introduced an Auditor role that could see all records including soft-deleted ones, but a Compliance Officer role that could see all records and approve deletion requests, and a Read-Only Security Auditor role that could see all records and all internal metadata fields. Thirty months in: the first enterprise customer required a "delegated admin" capability — an Admin-level user who could manage other users' accounts but could not see financial records or export data. No existing role combination covered this — "delegated admin" was an Admin minus two permissions plus one additional capability. The team added an exception_grants table for per-user permission overrides and added a second permission check after the role check.

Four years in: the roles table had 47 rows. The exception_grants table had 312 rows, most of them added in the preceding eighteen months, with no recorded expiry date and no owner. A new engineer asked the question that exposed the structural problem: "which users can currently see record ID 8472?" The answer required a query across five tables — roles, role_permissions, user_roles, exception_grants, and the department scoping table — plus application of the ownership check logic that lived only in the permission check function, not in any enumerable data structure. Writing the query took the engineer forty-five minutes. Running it took eight seconds. The query would need to be re-written for each resource type because the permission check logic was different for each type.

The SOC 2 Type II audit, which began eighteen months before the four-year mark, required a demonstration that "access to sensitive data is limited to authorized users on a least-privilege basis." The engineering team spent three weeks preparing the audit response — not because the access control was wrong, but because no documentation existed of which roles granted which permissions, which exception grants were currently active, why specific exception grants had been created, or what the policy was for granting and revoking exception overrides. The actual behavior had to be reverse-engineered from the role-permission matrix, cross-referenced against the exception grants table, and presented to the auditor in a format that showed least-privilege compliance. The auditors requested the "who can access record category X" documentation for six resource types. Each required a custom query and a written explanation of the three overlapping permission systems. The original decision to use RBAC — the model selection reasoning, the expressiveness ceiling, the plan for when the three-role model was no longer adequate — was nowhere in any document.

The ABAC implementation that outgrew its policy engine

A 20-person developer-tools company chose attribute-based access control at founding. The lead engineer who owned the permission model sprint had read about ABAC and Open Policy Agent the week before the sprint and believed it would be more flexible as the product grew. The rationale was informal: "RBAC will eventually constrain us; let's build it right the first time." The implementation used OPA (Open Policy Agent) with Rego policies deployed as a sidecar to the main application. The first policy set was clean and small — twelve rules covering the three core resource types, all of them straightforward enough that any engineer could read and understand them. Access decisions were auditable: the team could call OPA's evaluation API with a query and receive a structured explanation of which rules had fired and why.

At six months the policy set had grown to 34 rules as new resource types were added. At twelve months it had 120 rules. The lead engineer who understood Rego deeply had left the company at month nine. The engineer who had joined to own the permission model estimated four to six hours of policy graph analysis for any PR that involved permission changes — not because the new changes were complex in isolation, but because the interaction surface between 120 Rego rules was large enough that unintended permission grants were a realistic failure mode for any modification. A PR that added a new resource type required not just the policies for that resource type, but a check that the new resource type's attribute names didn't collide with any attribute names already used in existing policies in ways that could cause unexpected rule matches.

The problem that froze the permission model for six months arrived from the compliance team: a customer required a demonstration that a specific user could not access a specific category of sensitive record. In an RBAC system, this would be a role lookup — the user's role does not have the permission, done. In the OPA system, it required tracing the evaluation of 120 Rego rules against the user's attribute set and the resource's attribute set, identifying which rules could potentially grant access, confirming that none of them fired for this user-resource combination, and producing a written explanation of why access was denied that a non-engineer could read. The trace produced by OPA's explain: full mode was 400 lines of Rego evaluation logs. Summarizing it into a one-paragraph answer for the compliance team required thirty minutes and deep Rego knowledge. Two months later a similar request arrived from a different customer. The engineer who had done the first trace had moved to a different team. The engineer assigned the second request estimated it at eight to twelve hours. The permission model was frozen while the team evaluated whether to rewrite the entire access control layer using a simpler model.

The rewrite evaluation took six months because the team could not agree on what the new model should be without understanding why ABAC had been chosen in the first place. The founder-engineer who had made the original choice remembered the general reasoning ("RBAC would constrain us eventually") but not the specific alternatives evaluated, the specific constraint scenarios that had been anticipated, or the specific criteria under which ABAC would be declared the wrong choice. That reasoning — whatever it was — had been developed in an AI chat conversation the week before the sprint and had never been committed to any document. Without it, the rewrite discussion kept revisiting first principles: should the new model be RBAC, ABAC, or ReBAC? What were the expected scale and complexity requirements in two years? What was the migration cost of each option from the current OPA implementation? The discussion could not be grounded in the original design constraints because those constraints had not been written down.

Both outcomes share the same structural cause. The B2B SaaS company's RBAC implementation was not wrong at three roles; it was right, and would have remained serviceable for years with documented exception handling policies and a written expressiveness ceiling trigger. The developer-tools company's ABAC implementation was not wrong at twelve rules; it was right, and would have remained maintainable with documented complexity management policies and a documented trigger for reducing the rule set scope. In both cases, what was missing was not the access control implementation but the decision record: the written reasoning about model selection, the explicitly documented ceiling condition, and the pre-planned response when that condition was met.

Three structural properties that are set at access control model selection time

1. The expressiveness ceiling and the exception accumulation rate

Every access control model has an expressiveness ceiling: the class of permission requirements that the model cannot satisfy without an exception, an extension, or a second parallel model. For RBAC, the ceiling is the first requirement that cannot be expressed as a simple role-to-permission mapping — "user can do X but only for resources they created" (resource ownership), "user can do X only during business hours" (environmental context), "user can do X only for resources in their organizational unit" (hierarchical scoping). For ABAC, the ceiling is the first requirement that cannot be expressed as a policy over attribute sets without the policy becoming too complex to reason about — "user can delegate their permissions to another user for a fixed time period" (delegation), "user's permissions depend on the permission chain through a resource hierarchy with arbitrary depth" (deep hierarchies). For ReBAC, the ceiling is the first requirement that cannot be expressed as a graph traversal — typically fine-grained temporal constraints that require evaluating time-dependent policies rather than static relationships.

The ceiling is not visible at selection time because the initial requirements always fit the chosen model — teams do not choose models that don't fit their initial requirements. The ceiling becomes visible when the first out-of-model requirement arrives and is handled as a special case. The exception accumulation rate is the metric that reveals when the model has been exceeded: the number of exceptions, role additions, policy additions, or override grants per sprint that exist because the permission model cannot express the requirement cleanly. A rate of zero means the model is within its expressiveness range. A rate of two or three per sprint — added roles, new exception tables, new override flags — means the model has been exceeded and the team is accumulating a second, implicit access control model on top of the first.

Document the expressiveness ceiling explicitly at selection time: which permission patterns the chosen model can express cleanly, and which patterns would require extensions or exceptions. This does not require predicting every future permission requirement; it requires naming the specific patterns that are known to be outside the model's range. For RBAC: "resource-level ownership scoping requires an exception mechanism or a role-per-resource approach"; "organizational hierarchy scoping requires a department attribute check outside the role model." For ABAC: "delegation chains with time limits require temporal attribute evaluation that may not compose cleanly with existing policies." The ceiling documentation is the trigger condition for model review: when the exception accumulation rate exceeds X per sprint, or when the first requirement of category Y arrives, the team reviews whether the current model is still the right choice rather than discovering the issue five years after the ceiling was exceeded.

2. The audit trail completeness and the "who can access this resource" query

Effective access control requires two properties: predictability — the same principal attempting the same action on the same resource always gets the same authorization outcome — and auditability — the set of principals who can access a given resource can be enumerated without executing the application. Both properties degrade as the access control model accumulates exceptions.

RBAC with role proliferation loses predictability when a user has multiple assigned roles with partially overlapping permission sets, or when exception grants override role permissions in ways that require knowing both the role assignments and the exception grants to predict the outcome. A user with three roles and two exception grants — one granting an additional permission, one revoking a permission that one of the roles would otherwise grant — has a permission surface that is not readable from the role assignments alone. ABAC with complex policy engines loses auditability when the policy set is large enough that determining whether a specific policy grants access to a specific user-resource combination requires running the policy engine rather than reading a data structure. The characteristic audit failure in both models is the same: "to determine who can access resource X, we need to run the application's authorization code against every user account, which is not feasible to do manually for a compliance report."

The "who can access this resource" query should be documented explicitly at selection time — not as pseudocode but as the actual query against the actual data stores the team has. For RBAC, this is a set of SQL joins: join user_roles to role_permissions to determine which roles grant access to the resource category, then enumerate all users with those roles, then apply any exception grants that override the role-based outcome. For ABAC, this is a policy engine batch evaluation: evaluate the policy set against every user's attribute set for the given resource's attribute set, and return the set of users for whom the evaluation returns "allow." For ReBAC, this is a graph traversal query: starting from the resource node, traverse all relationship edges that lead to the "can access" permission, and collect the user nodes reachable via those traversals.

Document the expected latency of the query at current scale and at projected scale. A query that takes ten seconds at 1,000 users and ten minutes at 100,000 users is not suitable for real-time authorization decisions but is suitable for periodic access review reports. A query that requires running the application's authorization code is not suitable for producing audit evidence because it cannot be run independently of the application. The SOC 2 access review requirement — periodic evidence that access is limited to authorized principals — requires a query that can be run on demand by an engineer responding to an audit request, not a query that requires deploying the application in an audit mode. Document whether the current access control model supports that query and, if it does not, document what would need to be built to make it possible.

3. The migration path and the model transition cost

The chosen access control model determines not just the current permission capability but the migration cost when the expressiveness ceiling is reached. The migration cost is set at selection time, before any migration is needed, and increases as the model accumulates roles, policies, and exceptions over time. Understanding the migration path at selection time is not premature planning — it is the information that allows the team to recognize when they should migrate before the migration cost exceeds the value of avoiding the rewrite.

The migration from RBAC to ABAC is a policy introduction: the existing role-to-permission matrix must be expressed as policies in the policy engine, and all permission check callsites must be updated to evaluate the policy engine rather than look up role assignments. The cost at a small scale (five roles, three resource types) is one sprint. The cost at a large scale (47 roles, twelve resource types, 300 exception grants) is six to eight sprints — the exception grants must be analyzed to determine which are permanent model extensions (requiring a policy) and which are temporary overrides (requiring an expiry mechanism), and the exception grants that cannot be expressed as policies require either a continued exception mechanism or a product decision to remove the exceptional permission. The migration from ABAC to RBAC is the reverse — policies must be analyzed and simplified into a role matrix — and is similarly expensive because the policies may express permission logic that does not have a clean role-based equivalent.

The migration from RBAC or ABAC to ReBAC is a data model migration: permissions must be expressed as relationships between entities in a graph, and the relationship data store must be populated with the existing permission state. The migration cost is relatively low for clean RBAC implementations (role assignments translate directly to group membership relationships in the graph) but high for RBAC with accumulated exceptions (exception grants must be translated to direct relationship grants, ownership checks must be translated to owner relationships, department scoping must be translated to organization hierarchy relationships). ReBAC implementations using systems like OpenFGA, SpiceDB, or Oso Cloud require learning a new schema language (OpenFGA's authorization model definition) and a new query model (check tuples), which adds a learning cost to the migration regardless of implementation complexity.

Document the migration path at selection time: what is the next model in the migration sequence if the current model's ceiling is reached, what is the estimated effort at current scale and at projected scale in two years, and what makes the migration more expensive as scale increases. The estimate made at selection time will not be accurate four years later — neither the scale nor the exception accumulation can be predicted precisely — but the existence of the estimate establishes the migration path as a known option rather than an open research question, and the documentation of what increases migration cost (exception grant count, resource type count, permission check callsite count) gives the team a way to monitor migration cost as the model evolves without waiting for a crisis to trigger the analysis.

The five ADR sections for an access control model decision

1. Access control model selection and expressiveness rationale

Document the chosen model — RBAC, ABAC, ReBAC, or a hybrid — with explicit reasoning about the alternatives evaluated and their rejection rationale. The rejection reasoning should address the expressiveness properties of each model against the current and anticipated permission requirements, not only the implementation complexity or team familiarity. A decision record that says "RBAC was chosen because it is simpler than ABAC" is less useful than one that says "RBAC was chosen because the current permission requirements map cleanly to three roles with no resource-ownership scoping, no environmental context requirements, and no delegation requirements. ABAC was evaluated and rejected because the current permission space does not require attribute-based policy evaluation and the policy engine maintenance overhead was not justified by the expressiveness benefit at current scale. The expressiveness ceiling for RBAC is the first resource-ownership scoping requirement or the first organizational hierarchy requirement; both are anticipated in the eighteen-month product roadmap but are not present in the current requirements. The trigger for re-evaluating the model is the first PR that adds a permission exception because the current role model cannot express the required permission cleanly."

Document the anticipated permission patterns that are within the model's expressiveness range, and the patterns that are outside it. This does not require a complete permission specification — that lives in the codebase. It requires naming the permission pattern categories that the model handles cleanly versus the categories that require extensions. For RBAC: "role-to-permission matrix with global resource access is within the model's range; resource-ownership scoping, time-based access, and organizational hierarchy scoping are outside the model's range and would require an exception mechanism or a model migration." For ABAC: "policy-based evaluation over user and resource attributes is within the model's range; delegation chains and deep resource hierarchies that require graph traversal are outside the model's range and would require supplementing the policy engine with relationship data."

Document the centralized authorization abstraction: a single function or service that all permission checks call, rather than inline role lookups or inline policy evaluations scattered across the codebase. The centralized abstraction is the migration enabler — changing the permission model requires changing the implementation of the authorization function, not changing every callsite. If the current implementation uses inline permission checks, document the plan and timeline for introducing the centralized abstraction, because a permission model migration without the centralized abstraction requires touching every callsite. The authentication strategy decision record governs the authentication layer; the access control decision record should document the boundary between authentication (who is this user) and authorization (what can this user do), and confirm that the authorization check happens after authentication is verified rather than being interleaved with it.

2. Role or policy inventory and the growth cap

Document the initial role set for RBAC or the initial policy set for ABAC, with explicit definitions of what each role or policy covers. For RBAC: the roles, the permissions each role grants, and the permissions each role does not grant. For ABAC: the policies, the attributes each policy evaluates, and the permission each policy grants. The initial inventory is the baseline against which growth is measured and the foundation for the audit documentation.

Document the growth cap: the maximum role count or policy count that triggers a model review before the next role or policy is added. For RBAC, a role count above a team-size-dependent threshold (twelve roles for a team of twenty is a signal that role proliferation has begun; thirty roles for a team of fifty is a signal that the model has been exceeded) is the trigger for a permission model architecture review. For ABAC, a policy count above a complexity-management threshold (forty policies for a team of twenty is the policy complexity ceiling at which new engineers struggle to understand the full policy surface) is the trigger. The growth cap is not an absolute limit — it is the condition that requires a deliberate decision to either extend the model with a documented rationale, migrate to a more expressive model, or simplify the existing model by eliminating redundant roles or policies. Without a growth cap, the model grows indefinitely until a crisis (a SOC 2 audit, a permission incident, a rewrite project) reveals that the model has been exceeded for years.

Document the exception grant policy: the conditions under which individual user permission overrides outside the role or policy model are permitted, the required justification for granting an exception, the maximum duration of an exception grant, and the review process for renewing exceptions at expiry. Exception grants without a documented policy accumulate indefinitely — the 312-row exception_grants table with no expiry dates is the outcome of an undocumented exception grant policy. The exception grant policy should be strict enough that exceptions are genuinely exceptional (not a workaround for a missing role), documented enough that the grant can be traced to a specific business requirement, and time-limited enough that the exception grants table does not become a permanent extension of the permission model that is never reviewed. The data governance decision record governs data classification; the access control decision record should confirm that the permission model covers each data classification tier (public, internal, confidential, restricted) with appropriate access controls and that the exception grant policy is more restrictive for higher-classification data.

3. Permission enforcement point architecture

Document where permission enforcement occurs in the application architecture: in the API gateway layer, in the application service layer, in the database row-level security layer, or in some combination. Each enforcement point has different properties. API gateway enforcement is early — a request that lacks permission is rejected before reaching the application — but is coarse-grained (the gateway knows the user's role or token claims, not the resource's attributes or the user's relationship to the resource). Application service layer enforcement is resource-aware — the service can evaluate the user's permission relative to the specific resource being requested — but requires that every service correctly implement the permission check. Database row-level security is the deepest enforcement layer — it prevents unauthorized access even if the application layer contains a permission check bug — but is difficult to implement for complex permission models and cannot enforce permissions that depend on application-level context.

Document the verification approach for enforcement completeness: how does the team know that every resource access path has a permission check, and that no path bypasses the enforcement point? This is not a rhetorical question — in codebases with multiple service layers, multiple API endpoints, and multiple access patterns (REST, GraphQL, batch processing, background jobs), enforcement completeness requires active verification. The common failure modes are: a new API endpoint that is added without a permission check because the developer assumed the gateway handled it (but the gateway's rules don't cover the new endpoint's resource type); a background job that accesses resources without a permission check because background jobs run as a service account rather than a user account (but the service account's permissions are not scoped to the specific resources the job should access); a batch export feature that bypasses row-level security because the export query runs outside the ORM that applies the row-level security filter. The test strategy decision record governs the test suite; the access control decision record should document the permission enforcement tests that verify: a user with role X cannot access resource Y (negative test), a user with role Z can access resource Y (positive test), and a new API endpoint that lacks a permission check is caught by a test rather than discovered in a production incident.

Document the service account permission model: the permissions assigned to service accounts used by internal services, background jobs, and infrastructure automation, and how those permissions are scoped to the minimum required for the service's function. Service accounts with broad permissions — an internal service with admin-equivalent access to all resources because it was easier to grant full access than to scope it precisely — are a common permission model gap that SOC 2 auditors specifically look for. The API gateway decision record governs the gateway layer; the access control decision record should document which permission checks are handled at the gateway layer (coarse-grained role and token validation) and which are handled at the service layer (fine-grained resource permission validation), and confirm that the two layers are designed to be complementary rather than redundant.

4. Audit trail requirements and the "who can access this resource" query

Document the access audit requirements in concrete terms: what questions must the team be able to answer from the access control data, how quickly, and in what format? The minimum set for a SOC 2 Type II audit: "enumerate all users who currently have access to resource category X" (access enumeration), "enumerate all changes to user Y's permissions in the last 90 days" (permission change history), "confirm that user Z does not have access to resource category W" (negative access verification). Each of these queries has a different data requirement. Access enumeration requires the current state of roles, policies, or relationships. Permission change history requires an append-only log of permission changes with timestamps and actor attribution. Negative access verification requires the ability to run the access control model's logic without the application code — the auditor's question is "show me that user Z cannot access this" and the answer cannot be "we ran the application and it denied access."

Write the actual queries for each of these requirements. Not pseudocode — the actual SQL, OPA query, or graph traversal query that produces the answer, against the actual database schema or policy engine API that the team uses. For RBAC: the SQL join across roles, role_permissions, user_roles, and exception_grants that returns the set of users with access to a given resource category. For ABAC: the OPA batch evaluation query that evaluates the policy set against each user's attribute set for a given resource's attribute set. For ReBAC: the ListObjects query (who can perform action X on resource type Y) or ListUsers query (which users have permission X on resource Z) that the relationship store supports natively. Include the expected latency at current scale and the latency at projected scale, because a query that takes twenty minutes at projected scale is not suitable for on-demand audit responses.

Document the permission change audit log: an append-only record of every permission change — role assignment, role revocation, policy change, exception grant, exception revocation — with the timestamp, the acting principal, and the reason for the change. The permission change log is required for SOC 2 access reviews (demonstrating that access changes follow an approved workflow), for incident investigation (determining whether a permission change preceded a suspicious access event), and for exception grant tracking (confirming that exception grants are being reviewed and revoked at expiry). If the current access control implementation does not produce a permission change log, document what would need to be added. The logging strategy decision record governs the log format and retention policy; the access control decision record should confirm that permission change events are logged in the format that satisfies the audit requirements and that the log retention period covers the audit window (typically one year for SOC 2). The observability platform decision record governs the monitoring infrastructure; if access-denied events are monitored for anomaly detection (an unusual pattern of authorization failures may indicate a compromised credential or a misconfigured service), document the monitoring configuration and the alerting threshold.

5. Migration path documentation and trigger criteria

Document the migration path: what is the next access control model if the current model's expressiveness ceiling is reached, and what is the estimated migration effort at current scale? This does not need to be a detailed migration plan — it is an orientation document that establishes the direction before the migration becomes urgent. For a team that has chosen RBAC: the next model is likely RBAC with resource ownership and organizational hierarchy extensions (RBAC+), and the subsequent model if RBAC+ is insufficient is ReBAC. For a team that has chosen ABAC with a complex policy engine: the next model may be a simplified ABAC with a reduced policy scope, or ReBAC if the permission requirements involve deep resource hierarchies. Document the centralized authorization abstraction that makes the migration possible without changing every callsite, the resource types that would need to be migrated in sequence, and the parallelism window during which the old model and the new model would run simultaneously for validation.

Document the trigger criteria: the observable conditions that require a migration decision, not just a migration discussion. Observable conditions are measurable: "role count exceeds 15" (measurable by counting rows in the roles table), "exception grant count exceeds 50" (measurable by counting rows in the exception_grants table), "the 'who can access resource X' query requires tracing through more than three tables" (measurable by examining the access enumeration query), "a new permission requirement cannot be expressed without an exception mechanism" (observable from the PR review that introduces the exception). The trigger criteria are not automatic migration triggers — reaching the threshold requires a decision, not an automatic response — but they are specific enough that the threshold can be checked at the start of each planning cycle rather than discovered after a crisis has made the migration more expensive than it would have been a year earlier.

Document the migration authorization process: who is responsible for initiating a migration review when the trigger criteria are met, who must approve the migration plan, and what evidence is required to approve migration. The migration review should be triggered by the criteria rather than by a crisis; the authorization process should be lightweight enough that it can be completed in a sprint planning session rather than requiring a project proposal and executive approval. The feature flag decision record governs the feature flag infrastructure; if the permission model migration uses a feature flag to route a subset of permission checks to the new model during the validation period, document the flag name, the flag owner, and the rollout plan. The multi-tenancy decision record governs the tenant isolation model; the access control migration must preserve the tenant isolation guarantees of the existing model — specifically, a permission granted in the new model must not allow cross-tenant access that was prevented by the old model, and the validation period must include explicit cross-tenant access tests.

The access control conversation buried in your AI chat history

Access control model decisions are made in AI chat conversations. The multi-tenant sprint typically produces several Claude or ChatGPT sessions: a conceptual discussion comparing RBAC, ABAC, and RBAC+ for the specific product requirements; a question about how to implement row-level security in Postgres or how to structure the OPA policy for a specific permission requirement; a follow-up question about whether the chosen implementation handles the first exception case cleanly. Those conversations contain the information the decision record should capture: the alternatives evaluated, the specific permission requirements that drove the model selection, the patterns that were explicitly ruled out-of-scope for the initial implementation, and the assumptions about future requirements that informed the choice. That reasoning was produced in the context of the specific product, the specific team, and the specific permission requirements. And it was never committed to any document.

The WhyChose open-source extractor recovers exactly these access control design conversations from your AI chat history — the model comparison, the policy design sessions, the exception handling discussion that shaped the current implementation. That reasoning is what the next engineer needs when they are looking at a 47-row role table and a 312-row exception_grants table and trying to determine whether the appropriate response is to add another role, to introduce a policy engine, or to migrate to ReBAC. The alternative is reverse-engineering the permission model from the codebase and the database, producing an access enumeration query from scratch for the SOC 2 auditor, and discovering that the trigger for migrating the model was exceeded three years ago when nobody was tracking the exception accumulation rate.

Further reading