Skip to content

Return full list of adverse actions as top-level field#1512

Merged
isabeleliassen merged 14 commits intocsg-org:mainfrom
InspiringApps:feat/disciplinary-table-schema
May 4, 2026
Merged

Return full list of adverse actions as top-level field#1512
isabeleliassen merged 14 commits intocsg-org:mainfrom
InspiringApps:feat/disciplinary-table-schema

Conversation

@landonshumway-ia
Copy link
Copy Markdown
Collaborator

@landonshumway-ia landonshumway-ia commented Apr 27, 2026

Cosmetology would like to display a table of disciplinary information on the provider detail screen, separate from the privilege and license cards. To support this, we are adding a top-level 'adverseActions' field to the provider details API response object, which will include the full list of adverse actions associated with that provider.

We still keep the nested adverseActions fields under the licenses and privileges objects, which list the adverse actions associated with the specific license/privilege, this keeps us backwards compatible with how the license and privilege cards are accessing the data.

Testing List

  • yarn test:unit:all should run without errors or warnings
  • yarn serve should run without errors or warnings
  • yarn build should run without errors or warnings
  • For API configuration changes: CDK tests added/updated in backend/compact-connect/tests/unit/test_api.py
  • For API endpoint changes: OpenAPI spec updated to show latest endpoint configuration run compact-connect/bin/download_oas30.py
  • Code review

Closes #1478

Summary by CodeRabbit

  • New Features

    • Provider API responses now include a top-level provider-level adverseActions list alongside per-license adverse actions and runtime-generated privileges; schemas and API models updated.
  • Tests

    • Added unit tests and updated fixtures/snapshots to validate provider-level adverseActions serialization.
  • Chores

    • Bumped various dependency/tooling versions and adjusted CI pip upgrade steps.
  • Documentation

    • Clarified staff-user MFA recovery and archive guidance.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Warning

Rate limit exceeded

@landonshumway-ia has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 36 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c459c2f-dbcf-4b85-b769-5611de9d1071

📥 Commits

Reviewing files that changed from the base of the PR and between 3467943 and 408e60b.

⛔ Files ignored due to path filters (5)
  • backend/compact-connect-ui-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • backend/compact-connect/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js.map is excluded by !**/*.map
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js.map is excluded by !**/*.map
  • backend/cosmetology-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (41)
  • .github/workflows/check-common-cdk.yml
  • .github/workflows/check-compact-connect-ui-app.yml
  • .github/workflows/check-compact-connect.yml
  • .github/workflows/check-cosmetology-app.yml
  • .github/workflows/check-multi-account.yml
  • backend/common-cdk/requirements.in
  • backend/compact-connect-ui-app/lambdas/nodejs/package.json
  • backend/compact-connect-ui-app/requirements.in
  • backend/compact-connect-ui-app/requirements.txt
  • backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md
  • backend/compact-connect/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/compact-connect/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/compact-connect/lambdas/nodejs/package.json
  • backend/compact-connect/lambdas/nodejs/tests/ingest-event-reporter.test.ts
  • backend/compact-connect/lambdas/nodejs/tsconfig.json
  • backend/compact-connect/requirements.in
  • backend/cosmetology-app/docs/internal/api-specification/latest-oas30.json
  • backend/cosmetology-app/docs/internal/postman/postman-collection.json
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/package.json
  • backend/cosmetology-app/lambdas/nodejs/tsconfig.json
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/provider_record_util.py
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py
  • backend/cosmetology-app/lambdas/python/common/tests/resources/api/provider-detail-response.json
  • backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py
  • backend/cosmetology-app/requirements.in
  • backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py
  • backend/cosmetology-app/tests/app/test_provider_users/__init__.py
  • backend/cosmetology-app/tests/resources/snapshots/GET_PROVIDER_RESPONSE_SCHEMA.json
  • backend/multi-account/backups/requirements.in
  • backend/multi-account/control-tower/requirements.in
  • backend/multi-account/log-aggregation/requirements.in
📝 Walkthrough

Walkthrough

Adds a provider-level top-level adverseActions array to provider API responses by aggregating adverse-action records across the provider; updates response generation, marshmallow schemas, API model wiring, fixtures, snapshots, and unit tests. Also includes unrelated Node.js import/dep/tsconfig changes and CI/requirements bumps.

Changes

Provider API: response generation, schema, model, tests

Layer / File(s) Summary
Data Shape / Schema
backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py
Added adverseActions field (List(Nested(AdverseActionGeneralResponseSchema))) to ProviderReadPrivateResponseSchema and ProviderGeneralResponseSchema.
Core Implementation
backend/cosmetology-app/lambdas/python/common/cc_common/data_model/provider_record_util.py
generate_api_response_object now aggregates and includes a provider-level adverseActions list in the returned API object (removed an explanatory comment).
API Model / Contract
backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py
Centralized adverse-action JSON schema (_adverse_action_schema) and wired top-level adverseActions into the provider detail response; removed duplicate inline schema.
Fixtures / Snapshots
backend/cosmetology-app/lambdas/python/common/tests/resources/api/provider-detail-response.json, backend/cosmetology-app/tests/resources/snapshots/GET_PROVIDER_RESPONSE_SCHEMA.json
Fixture updated to include top-level adverseActions: []; snapshot schema updated to require top-level adverseActions.
Tests
backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py
Added TestGenerateApiResponseObject to assert provider-level aggregation of adverse actions; minor local variable renames in existing privilege tests.

Node.js lambdas: imports, wiring, dev deps, tsconfig

Layer / File(s) Summary
Dependency pins / resolutions
backend/cosmetology-app/lambdas/nodejs/package.json, backend/compact-connect/lambdas/nodejs/package.json, backend/compact-connect-ui-app/lambdas/nodejs/package.json
Bumped/pinned devDependencies and resolutions: fast-xml-parser, postcss, @types/node, eslint, typescript.
TypeScript compiler config
backend/cosmetology-app/lambdas/nodejs/tsconfig.json, backend/compact-connect/lambdas/nodejs/tsconfig.json, backend/compact-connect/lambdas/nodejs/tsconfig.json
Changed moduleResolution to bundler, removed baseUrl, and added explicit types (node, jest).
Import path cleanup / type imports
multiple lambda.ts and service files under backend/.../lambdas/nodejs/
Replaced internal ESM import paths with public @aws-lambda-powertools/commons/types; changed some lib/... model imports to relative ../lib/models; updated IJurisdiction imports to relative paths.
Runtime wiring
backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js, backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js
Removed S3 client import/wiring from Cognito emails lambda (no longer passed into CognitoEmailService).
Tests
backend/compact-connect/lambdas/nodejs/tests/ingest-event-reporter.test.ts
Initialized mockS3Client in test setup (beforeAll) for the suite.

CI / requirements bumps

Layer / File(s) Summary
CI workflows
.github/workflows/check-common-cdk.yml, .github/workflows/check-*.yml
Updated pip upgrade steps to install pip>=26.1 (replacing 26.0) across multiple workflows; updated inline CVE comments; added one extra pip-audit ignore.
Requirements / CDK pins
backend/common-cdk/requirements.in, backend/compact-connect/requirements.in, backend/compact-connect-ui-app/requirements.in, backend/cosmetology-app/requirements.in, backend/multi-account/*/requirements.in
Raised minimum versions for aws-cdk-lib, aws-cdk-aws-lambda-python-alpha, constructs (min), and cdk-nag across multiple requirement files (upper bounds preserved).
Autogenerated pins
backend/compact-connect-ui-app/requirements.txt
Updated pinned CDK package versions and pip-compile header (Python 3.14).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant API_Generator as API Generator
    participant Records as ProviderRecords
    participant AA_Store as AdverseActionStore
    participant Serializer as ResponseSerializer

    Client->>API_Generator: Request provider details
    API_Generator->>Records: Fetch provider + related records
    Records->>AA_Store: Query adverse-action records for provider
    AA_Store-->>Records: Return adverse-action records
    Records->>API_Generator: Return records + adverseActions
    API_Generator->>Serializer: Build response (licenses, privileges, provider-level adverseActions)
    Serializer-->>Client: Return serialized provider response with top-level `adverseActions`
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • jlkravitz
  • ChiefStief

Poem

"🐇 I hopped through fields of code and found,
Actions scattered, names unbound.
I gathered each into one neat row,
Provider-wide now they proudly show.
Tests cheered — the rabbit gave a bow."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Changes include dependency updates (npm, pip, TypeScript config, AWS CDK versions) and CI workflow updates for pip security patches, which are outside the primary scope of the disciplinary table feature. Remove unrelated dependency version updates and CI/CD workflow security patches from this PR; submit as separate maintenance pull requests to keep scope focused on the adverseActions feature.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Return full list of adverse actions as top-level field' accurately summarizes the main change: adding a top-level adverseActions field to the provider API response.
Description check ✅ Passed The PR description covers objectives, testing requirements, and references the linked issue. However, it lacks explicit details on requirements and how each was met.
Linked Issues check ✅ Passed The PR implements the core objective from #1478 by adding provider-level adverseActions field [#1478] and maintaining backward compatibility. Testing is documented and API spec was updated.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 52 minutes and 36 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py (1)

117-117: Minor: inconsistent Nested instantiation style.

Throughout this file, nested schemas are passed as instances (e.g., Nested(LicenseReadPrivateResponseSchema(), ...) on line 114 and Nested(PrivilegeReadPrivateResponseSchema(), ...) on line 115), but the new lines pass the class itself (Nested(AdverseActionGeneralResponseSchema, ...)). Both forms are accepted by marshmallow, but matching the surrounding style improves consistency.

♻️ Proposed style alignment
-    adverseActions = List(Nested(AdverseActionGeneralResponseSchema, required=False, allow_none=False))
+    adverseActions = List(Nested(AdverseActionGeneralResponseSchema(), required=False, allow_none=False))

(applies on both line 117 and line 166)

Also applies to: 166-166

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py`
at line 117, The nested schema instantiation for adverseActions uses the class
name instead of an instance, which is inconsistent with surrounding usages;
change Nested(AdverseActionGeneralResponseSchema, ...) to
Nested(AdverseActionGeneralResponseSchema(), ...) (and do the same for the other
occurrence noted) so it matches patterns like
Nested(LicenseReadPrivateResponseSchema(), ...) and
Nested(PrivilegeReadPrivateResponseSchema(), ...); update the Nested calls
referencing AdverseActionGeneralResponseSchema to pass an instance.
backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py (2)

573-596: Optional: deduplicate the test helpers.

_make_provider_records and _patch_config_for_privilege_generation are now defined identically (modulo the extra_records parameter) in three test classes in this file. A small shared mixin or module-level helpers would cut the duplication. Not blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`
around lines 573 - 596, Duplicate helper methods _make_provider_records and
_patch_config_for_privilege_generation are repeated across multiple test
classes; refactor by extracting them into a shared test helper (either a
module-level function or a mixin class) and have the test classes import/use
that shared helper instead of redefining the methods. Move the
TestDataGenerator-based logic from _make_provider_records and the
MagicMock/patch logic from _patch_config_for_privilege_generation into the new
shared helper (referencing TestDataGenerator and the patch target
'cc_common.data_model.provider_record_util.config') and update each test class
to call the centralized functions or inherit the mixin to remove duplication.

598-637: Consider adding assertions that nested per-license/per-privilege adverseActions are still populated.

The PR explicitly states the existing nested adverseActions under licenses and privileges are retained for backward compatibility. This test only asserts the new top-level field. Adding a couple of extra assertions guards against a future refactor inadvertently dropping the nested lists.

♻️ Suggested additions
         self.assertEqual(
             [license_adverse_action.to_dict(), privilege_adverse_action.to_dict()],
             api_response['adverseActions'],
         )
+        # Backward compatibility: nested adverseActions on the matching license and privilege
+        # should still be populated.
+        oh_license = next(lic for lic in api_response['licenses'] if lic['jurisdiction'] == 'oh')
+        self.assertEqual([license_adverse_action.to_dict()], oh_license['adverseActions'])
+        al_privilege = next(p for p in api_response['privileges'] if p['jurisdiction'] == 'al')
+        self.assertEqual([privilege_adverse_action.to_dict()], al_privilege['adverseActions'])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`
around lines 598 - 637, Update the test method
test_generate_api_response_object_returns_adverse_actions_as_a_top_level_field_for_all_adverse_actions
to also assert that nested per-license and per-privilege adverseActions remain
populated: after calling
ProviderUserRecords(records).generate_api_response_object(), assert that
api_response['licenses'][0]['adverseActions'] contains the
license_adverse_action.to_dict() and that the appropriate privilege entry in
api_response['privileges'] contains privilege_adverse_action.to_dict(); use
ProviderUserRecords, generate_api_response_object, and the api_response dict
keys to locate where to add these extra assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py`:
- Line 117: The nested schema instantiation for adverseActions uses the class
name instead of an instance, which is inconsistent with surrounding usages;
change Nested(AdverseActionGeneralResponseSchema, ...) to
Nested(AdverseActionGeneralResponseSchema(), ...) (and do the same for the other
occurrence noted) so it matches patterns like
Nested(LicenseReadPrivateResponseSchema(), ...) and
Nested(PrivilegeReadPrivateResponseSchema(), ...); update the Nested calls
referencing AdverseActionGeneralResponseSchema to pass an instance.

In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`:
- Around line 573-596: Duplicate helper methods _make_provider_records and
_patch_config_for_privilege_generation are repeated across multiple test
classes; refactor by extracting them into a shared test helper (either a
module-level function or a mixin class) and have the test classes import/use
that shared helper instead of redefining the methods. Move the
TestDataGenerator-based logic from _make_provider_records and the
MagicMock/patch logic from _patch_config_for_privilege_generation into the new
shared helper (referencing TestDataGenerator and the patch target
'cc_common.data_model.provider_record_util.config') and update each test class
to call the centralized functions or inherit the mixin to remove duplication.
- Around line 598-637: Update the test method
test_generate_api_response_object_returns_adverse_actions_as_a_top_level_field_for_all_adverse_actions
to also assert that nested per-license and per-privilege adverseActions remain
populated: after calling
ProviderUserRecords(records).generate_api_response_object(), assert that
api_response['licenses'][0]['adverseActions'] contains the
license_adverse_action.to_dict() and that the appropriate privilege entry in
api_response['privileges'] contains privilege_adverse_action.to_dict(); use
ProviderUserRecords, generate_api_response_object, and the api_response dict
keys to locate where to add these extra assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4b43b50d-6034-477d-8aa3-1cdeedc4278c

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9691d and aff4995.

📒 Files selected for processing (4)
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/provider_record_util.py
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py
  • backend/cosmetology-app/lambdas/python/common/tests/resources/api/provider-detail-response.json
  • backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py (1)

707-758: Reformat _adverse_action_schema to use consistent indentation.

The body of _adverse_action_schema (lines 710–757) preserves the deep indentation it had at its original inline location, which is now visually inconsistent with the rest of the file (4-space increments). This is syntactically valid because everything is inside parentheses, but it's likely to be flagged by a formatter and hurts readability.

♻️ Proposed fix to normalize indentation
     `@property`
     def _adverse_action_schema(self):
         return JsonSchema(
-                                type=JsonSchemaType.ARRAY,
-                                items=JsonSchema(
-                                    type=JsonSchemaType.OBJECT,
-                                    required=[
-                                        'type',
-                                        'compact',
-                                        'providerId',
-                                        'jurisdiction',
-                                        'licenseTypeAbbreviation',
-                                        'licenseType',
-                                        'actionAgainst',
-                                        'effectiveStartDate',
-                                        'creationDate',
-                                        'adverseActionId',
-                                        'dateOfUpdate',
-                                        'encumbranceType',
-                                    ],
-                                    properties={
-                                        'type': JsonSchema(type=JsonSchemaType.STRING, enum=['adverseAction']),
-                                        'compact': JsonSchema(
-                                            type=JsonSchemaType.STRING, enum=self.stack.node.get_context('compacts')
-                                        ),
-                                        'providerId': JsonSchema(
-                                            type=JsonSchemaType.STRING, pattern=cc_api.UUID4_FORMAT
-                                        ),
-                                        'jurisdiction': JsonSchema(
-                                            type=JsonSchemaType.STRING,
-                                            enum=self.stack.node.get_context('jurisdictions'),
-                                        ),
-                                        'licenseTypeAbbreviation': JsonSchema(type=JsonSchemaType.STRING),
-                                        'licenseType': JsonSchema(type=JsonSchemaType.STRING),
-                                        'actionAgainst': JsonSchema(type=JsonSchemaType.STRING),
-                                        'effectiveStartDate': JsonSchema(
-                                            type=JsonSchemaType.STRING, format='date', pattern=cc_api.YMD_FORMAT
-                                        ),
-                                        'creationDate': JsonSchema(
-                                            type=JsonSchemaType.STRING, format='date', pattern=cc_api.YMD_FORMAT
-                                        ),
-                                        'adverseActionId': JsonSchema(type=JsonSchemaType.STRING),
-                                        'effectiveLiftDate': JsonSchema(
-                                            type=JsonSchemaType.STRING, format='date', pattern=cc_api.YMD_FORMAT
-                                        ),
-                                        'dateOfUpdate': JsonSchema(type=JsonSchemaType.STRING, format='date-time'),
-                                        'encumbranceType': JsonSchema(type=JsonSchemaType.STRING),
-                                        'clinicalPrivilegeActionCategories': self._clinical_privilege_action_categories_schema,  # noqa: E501
-                                        'liftingUser': JsonSchema(type=JsonSchemaType.STRING),
-                                    },
-                                ),
-                            )
+            type=JsonSchemaType.ARRAY,
+            items=JsonSchema(
+                type=JsonSchemaType.OBJECT,
+                required=[
+                    'type',
+                    'compact',
+                    'providerId',
+                    'jurisdiction',
+                    'licenseTypeAbbreviation',
+                    'licenseType',
+                    'actionAgainst',
+                    'effectiveStartDate',
+                    'creationDate',
+                    'adverseActionId',
+                    'dateOfUpdate',
+                    'encumbranceType',
+                ],
+                properties={
+                    'type': JsonSchema(type=JsonSchemaType.STRING, enum=['adverseAction']),
+                    'compact': JsonSchema(
+                        type=JsonSchemaType.STRING, enum=self.stack.node.get_context('compacts')
+                    ),
+                    'providerId': JsonSchema(type=JsonSchemaType.STRING, pattern=cc_api.UUID4_FORMAT),
+                    'jurisdiction': JsonSchema(
+                        type=JsonSchemaType.STRING,
+                        enum=self.stack.node.get_context('jurisdictions'),
+                    ),
+                    'licenseTypeAbbreviation': JsonSchema(type=JsonSchemaType.STRING),
+                    'licenseType': JsonSchema(type=JsonSchemaType.STRING),
+                    'actionAgainst': JsonSchema(type=JsonSchemaType.STRING),
+                    'effectiveStartDate': JsonSchema(
+                        type=JsonSchemaType.STRING, format='date', pattern=cc_api.YMD_FORMAT
+                    ),
+                    'creationDate': JsonSchema(
+                        type=JsonSchemaType.STRING, format='date', pattern=cc_api.YMD_FORMAT
+                    ),
+                    'adverseActionId': JsonSchema(type=JsonSchemaType.STRING),
+                    'effectiveLiftDate': JsonSchema(
+                        type=JsonSchemaType.STRING, format='date', pattern=cc_api.YMD_FORMAT
+                    ),
+                    'dateOfUpdate': JsonSchema(type=JsonSchemaType.STRING, format='date-time'),
+                    'encumbranceType': JsonSchema(type=JsonSchemaType.STRING),
+                    'clinicalPrivilegeActionCategories': self._clinical_privilege_action_categories_schema,
+                    'liftingUser': JsonSchema(type=JsonSchemaType.STRING),
+                },
+            ),
+        )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py` around lines
707 - 758, The _adverse_action_schema property uses inconsistent/deep
indentation that breaks the file's 4-space style; fix it by reformatting the
return block for JsonSchema(...) so nested arguments (type=..., items=..., inner
JsonSchema(...), required=..., properties={...}) and all property entries (e.g.
'type', 'compact', 'providerId', 'effectiveStartDate', 'dateOfUpdate',
'encumbranceType', 'clinicalPrivilegeActionCategories' referencing
self._clinical_privilege_action_categories_schema, and formats using
cc_api.YMD_FORMAT) follow the file's 4-space indentation levels and align
parentheses consistently; keep the same tokens and structure (JsonSchema,
JsonSchemaType, enum calls to self.stack.node.get_context, patterns using
cc_api.UUID4_FORMAT/YMD_FORMAT) — only adjust whitespace and indentation to
match surrounding code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py`:
- Around line 707-758: The _adverse_action_schema property uses
inconsistent/deep indentation that breaks the file's 4-space style; fix it by
reformatting the return block for JsonSchema(...) so nested arguments (type=...,
items=..., inner JsonSchema(...), required=..., properties={...}) and all
property entries (e.g. 'type', 'compact', 'providerId', 'effectiveStartDate',
'dateOfUpdate', 'encumbranceType', 'clinicalPrivilegeActionCategories'
referencing self._clinical_privilege_action_categories_schema, and formats using
cc_api.YMD_FORMAT) follow the file's 4-space indentation levels and align
parentheses consistently; keep the same tokens and structure (JsonSchema,
JsonSchemaType, enum calls to self.stack.node.get_context, patterns using
cc_api.UUID4_FORMAT/YMD_FORMAT) — only adjust whitespace and indentation to
match surrounding code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 067f32c1-6c16-4da3-8602-b7606b13b0f0

📥 Commits

Reviewing files that changed from the base of the PR and between aff4995 and cfaf986.

📒 Files selected for processing (4)
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py
  • backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py
  • backend/cosmetology-app/tests/app/test_provider_users/__init__.py
  • backend/cosmetology-app/tests/resources/snapshots/GET_PROVIDER_RESPONSE_SCHEMA.json

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py (2)

598-642: Strengthen this test to avoid order-coupling and cover backward compatibility.

The current assertion at Line 639 is order-sensitive and only validates the top-level array. Consider making the top-level check order-insensitive and asserting nested licenses[].adverseActions / privileges[].adverseActions in the same scenario.

Suggested test update
-        self.assertEqual(
+        self.assertCountEqual(
             [license_adverse_action.to_dict(), privilege_adverse_action.to_dict()],
             api_response['adverseActions'],
         )
+
+        self.assertEqual(
+            [license_adverse_action.to_dict()],
+            api_response['licenses'][0]['adverseActions'],
+        )
+        privilege_al = next(p for p in api_response['privileges'] if p['jurisdiction'] == 'al')
+        self.assertEqual(
+            [privilege_adverse_action.to_dict()],
+            privilege_al['adverseActions'],
+        )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`
around lines 598 - 642, The test is order-sensitive and only asserts the
top-level api_response['adverseActions']; update the assertion to be
order-insensitive (e.g., compare sets or sort by a unique key such as the
adverse action id or serialize-to-dict tuple) when comparing
[license_adverse_action.to_dict(), privilege_adverse_action.to_dict()] against
api_response['adverseActions'], and also add assertions that the same adverse
actions appear in the nested structures produced by
ProviderUserRecords.generate_api_response_object — specifically verify that
license_adverse_action appears under
api_response['licenses'][...]['adverseActions'] for the matching license and
privilege_adverse_action appears under
api_response['privileges'][...]['adverseActions'] to cover backward
compatibility; use the existing license_adverse_action and
privilege_adverse_action test objects to locate matches.

573-597: Reduce helper duplication across test classes.

_make_provider_records and _patch_config_for_privilege_generation are now repeated in multiple classes in this file. Extracting them into a shared mixin/helper will reduce drift and future maintenance cost.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`
around lines 573 - 597, Extract the duplicated helpers _make_provider_records
and _patch_config_for_privilege_generation into a single test helper (e.g., a
TestHelpers mixin or module-level helper) and have the existing test classes
import/use it rather than redefining them; move the implementations (including
use of TestDataGenerator, serialize_to_database_record, and the MagicMock/patch
behavior for config.live_compact_jurisdictions and license_type_abbreviations)
into that shared helper, keep the same function names/signatures so callers need
only inherit or call the helper, update tests to inherit the mixin or call the
helper functions, and remove the duplicate definitions from the individual test
classes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`:
- Around line 598-642: The test is order-sensitive and only asserts the
top-level api_response['adverseActions']; update the assertion to be
order-insensitive (e.g., compare sets or sort by a unique key such as the
adverse action id or serialize-to-dict tuple) when comparing
[license_adverse_action.to_dict(), privilege_adverse_action.to_dict()] against
api_response['adverseActions'], and also add assertions that the same adverse
actions appear in the nested structures produced by
ProviderUserRecords.generate_api_response_object — specifically verify that
license_adverse_action appears under
api_response['licenses'][...]['adverseActions'] for the matching license and
privilege_adverse_action appears under
api_response['privileges'][...]['adverseActions'] to cover backward
compatibility; use the existing license_adverse_action and
privilege_adverse_action test objects to locate matches.
- Around line 573-597: Extract the duplicated helpers _make_provider_records and
_patch_config_for_privilege_generation into a single test helper (e.g., a
TestHelpers mixin or module-level helper) and have the existing test classes
import/use it rather than redefining them; move the implementations (including
use of TestDataGenerator, serialize_to_database_record, and the MagicMock/patch
behavior for config.live_compact_jurisdictions and license_type_abbreviations)
into that shared helper, keep the same function names/signatures so callers need
only inherit or call the helper, update tests to inherit the mixin or call the
helper functions, and remove the duplicate definitions from the individual test
classes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 65f2e012-8f62-4a88-9469-fde46e7a9705

📥 Commits

Reviewing files that changed from the base of the PR and between cfaf986 and ffce989.

⛔ Files ignored due to path filters (1)
  • backend/cosmetology-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • backend/cosmetology-app/lambdas/nodejs/package.json
  • backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py
  • backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py
✅ Files skipped from review due to trivial changes (1)
  • backend/cosmetology-app/lambdas/nodejs/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py

Comment thread backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py
Copy link
Copy Markdown
Collaborator

@ChiefStief ChiefStief left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@landonshumway-ia landonshumway-ia force-pushed the feat/disciplinary-table-schema branch from ffce989 to 56fc467 Compare April 28, 2026 15:08
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py (2)

573-597: Consider extracting shared test helpers to avoid drift.

_make_provider_records and _patch_config_for_privilege_generation are duplicated across test classes in this file. Pulling them into a shared base/mixin would reduce maintenance overhead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`
around lines 573 - 597, Duplicate test helper functions _make_provider_records
and _patch_config_for_privilege_generation should be extracted into a shared
test base or mixin class (e.g., ProviderTestMixin or ProviderTestBase) and
referenced by test classes; move the implementations of _make_provider_records
and _patch_config_for_privilege_generation into that shared class, keep the same
function names and behavior (including use of TestDataGenerator and the patch
target 'cc_common.data_model.provider_record_util.config'), then update existing
test classes to inherit from or import the mixin/base instead of redefining
these helpers and remove the redundant copies to avoid drift.

639-642: Prefer order-insensitive assertion for aggregated adverse actions.

This check is stricter than the contract and can fail on harmless ordering changes.

Suggested test tweak
-        self.assertEqual(
+        self.assertCountEqual(
             [license_adverse_action.to_dict(), privilege_adverse_action.to_dict()],
             api_response['adverseActions'],
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`
around lines 639 - 642, The test currently asserts exact list order for adverse
actions using self.assertEqual([license_adverse_action.to_dict(),
privilege_adverse_action.to_dict()], api_response['adverseActions']), which is
order-sensitive; change it to an order-insensitive assertion (e.g., replace with
self.assertCountEqual(api_response['adverseActions'],
[license_adverse_action.to_dict(), privilege_adverse_action.to_dict()]) or
otherwise compare as sets/sorted lists) so the test passes regardless of the
aggregated adverseActions ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`:
- Around line 573-597: Duplicate test helper functions _make_provider_records
and _patch_config_for_privilege_generation should be extracted into a shared
test base or mixin class (e.g., ProviderTestMixin or ProviderTestBase) and
referenced by test classes; move the implementations of _make_provider_records
and _patch_config_for_privilege_generation into that shared class, keep the same
function names and behavior (including use of TestDataGenerator and the patch
target 'cc_common.data_model.provider_record_util.config'), then update existing
test classes to inherit from or import the mixin/base instead of redefining
these helpers and remove the redundant copies to avoid drift.
- Around line 639-642: The test currently asserts exact list order for adverse
actions using self.assertEqual([license_adverse_action.to_dict(),
privilege_adverse_action.to_dict()], api_response['adverseActions']), which is
order-sensitive; change it to an order-insensitive assertion (e.g., replace with
self.assertCountEqual(api_response['adverseActions'],
[license_adverse_action.to_dict(), privilege_adverse_action.to_dict()]) or
otherwise compare as sets/sorted lists) so the test passes regardless of the
aggregated adverseActions ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bf7a7e58-b5e7-4dc7-bc1c-239600982f79

📥 Commits

Reviewing files that changed from the base of the PR and between ffce989 and 56fc467.

⛔ Files ignored due to path filters (1)
  • backend/cosmetology-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • backend/cosmetology-app/lambdas/nodejs/package.json
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/provider_record_util.py
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py
  • backend/cosmetology-app/lambdas/python/common/tests/resources/api/provider-detail-response.json
  • backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py
  • backend/cosmetology-app/stacks/api_stack/v1_api/api_model.py
  • backend/cosmetology-app/tests/app/test_provider_users/__init__.py
  • backend/cosmetology-app/tests/resources/snapshots/GET_PROVIDER_RESPONSE_SCHEMA.json
✅ Files skipped from review due to trivial changes (2)
  • backend/cosmetology-app/lambdas/python/common/tests/resources/api/provider-detail-response.json
  • backend/cosmetology-app/lambdas/nodejs/package.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/provider_record_util.py
  • backend/cosmetology-app/tests/resources/snapshots/GET_PROVIDER_RESPONSE_SCHEMA.json
  • backend/cosmetology-app/lambdas/python/common/cc_common/data_model/schema/provider/api.py

@landonshumway-ia
Copy link
Copy Markdown
Collaborator Author

@jlkravitz This is ready for your review. Thanks

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/cosmetology-app/lambdas/nodejs/tsconfig.json`:
- Around line 22-24: The "types" array in tsconfig.json is too restrictive
(currently only "node" and "jest"), which prevents React's ambient types (e.g.,
JSX.IntrinsicElements) from being loaded for TSX files under lib/**/*.tsx;
update the tsconfig.json "types" array to include "react" alongside "node" and
"jest" so `@types/react` is picked up for TSX compilation and JSX types are
available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e916a02f-4191-455a-aeb0-9399611ee4e4

📥 Commits

Reviewing files that changed from the base of the PR and between 56fc467 and 698beda.

⛔ Files ignored due to path filters (3)
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js.map is excluded by !**/*.map
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js.map is excluded by !**/*.map
  • backend/cosmetology-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (11)
  • backend/cosmetology-app/docs/internal/api-specification/latest-oas30.json
  • backend/cosmetology-app/docs/internal/postman/postman-collection.json
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/package.json
  • backend/cosmetology-app/lambdas/nodejs/tsconfig.json
💤 Files with no reviewable changes (2)
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js
✅ Files skipped from review due to trivial changes (5)
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/email-notification-service/lambda.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/cosmetology-app/lambdas/nodejs/package.json

Comment thread backend/cosmetology-app/lambdas/nodejs/tsconfig.json
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md (1)

44-53: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update the procedure description to match the actual steps.

Line 44 still describes the old procedure where changing the pk value causes DynamoDB to delete and recreate the record. However, the actual steps below (lines 48-53) no longer modify the pk—they add archivedDate and archivedReason fields and remove famGiv instead. This inconsistency could confuse operators following the procedure.

📝 Proposed fix to align the description with the steps
-In the DynamoDB console, if you change the partition key (pk) value of a existing DynamoDB item, it automatically deletes the old record with the old pk and creates a new one with the new pk, effectively archiving the user record. Perform the following steps:
+Archive the existing staff user record by marking it with archive metadata and removing it from the active user index. Perform the following steps:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md` around lines
44 - 53, The opening paragraph incorrectly states that changing the partition
key (pk) will archive the record; update the description to reflect the actual
procedure: do not modify pk, instead in the DynamoDB console open the staff user
item, add fields archivedDate and archivedReason, remove the famGiv field, save
to archive the user, then verify and note permissions. Make sure to reference
archivedDate, archivedReason, famGiv and pk in the text so readers understand
that pk is unchanged and archiving is performed by adding/removing fields.
🧹 Nitpick comments (1)
backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md (1)

50-50: 💤 Low value

Consider fixing grammar and improving clarity.

"lookup" should be "look up" (verb phrase). The parenthetical explanation is also quite long and could be reformatted for better readability.

✍️ Proposed grammar and clarity improvements
-   - **Remove field**: `famGiv`. If this field is not removed from the record, the user will continue to show up in the list of staff users in the UI, since we use this field in the index used to lookup the list of staff users (the first and last name of the staff user are still recorded under the `attributes` field, so no data is lost by removing the `famGiv` index field).
+   - **Remove field**: `famGiv`. If this field is not removed from the record, the user will continue to show up in the list of staff users in the UI, since we use this field in the index used to look up the list of staff users. (Note: The first and last name of the staff user are still recorded under the `attributes` field, so no data is lost by removing the `famGiv` index field.)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md` at line 50,
Edit the sentence describing the `famGiv` field to correct grammar and improve
clarity: change "lookup" to "look up", remove the long parenthetical and split
into two sentences — first state that `famGiv` should be removed to prevent the
user appearing in the staff list, then add a second sentence clarifying that the
first/last name are preserved under `attributes` so no data is lost. Keep
references to the `famGiv` index field and the `attributes` field verbatim for
clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/check-compact-connect.yml:
- Around line 144-146: The CI workflow line that suppresses CVE-2026-41066
("pip-audit --ignore-vuln GHSA-4xh5-x5gv-qwph --ignore-vuln CVE-2026-41066") is
masking a real lxml XXE vulnerability but authorizenet 1.1.6 pins lxml==4.* so
you cannot upgrade in-tree; update the workflow comment and suppression
rationale to record that authorizenet 1.1.6's install_requires pins lxml==4.*,
that lxml 6.1.0+ fixes CVE-2026-41066, and link the upstream tracker (issue
`#164`, PR `#167`) so future maintainers know this is an upstream blocker and the
acceptable temporary suppression is intentional until authorizenet is updated or
replaced.

In `@backend/compact-connect/lambdas/nodejs/package.json`:
- Line 23: The package.json currently pins "@types/node": "25.6.0" which is
mismatched with the Lambda Node.js 22.x runtime; update the dependency to the
project’s chosen Node-22-compatible `@types` version (replace "25.6.0" with the
same `@types/node` version used in the other lambda package.json that targets Node
22.x) so the TypeScript compiler cannot accidentally allow Node-25-only APIs.

In `@backend/cosmetology-app/lambdas/nodejs/package.json`:
- Line 23: The `@types/node` entry in package.json is set to 25.6.0 which targets
Node 25 and is incompatible with the Lambda runtime (NODEJS_24_X); update the
package.json dependency "@types/node" to a Node-24-compatible types release (use
a stable 20.x release such as a latest 20.*) and run your package manager to
reinstall devDependencies, then scan for and remove any Node-25-specific APIs
referenced in the code (search for usages in the lambda handlers and utility
functions) to ensure runtime compatibility.

---

Outside diff comments:
In `@backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md`:
- Around line 44-53: The opening paragraph incorrectly states that changing the
partition key (pk) will archive the record; update the description to reflect
the actual procedure: do not modify pk, instead in the DynamoDB console open the
staff user item, add fields archivedDate and archivedReason, remove the famGiv
field, save to archive the user, then verify and note permissions. Make sure to
reference archivedDate, archivedReason, famGiv and pk in the text so readers
understand that pk is unchanged and archiving is performed by adding/removing
fields.

---

Nitpick comments:
In `@backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md`:
- Line 50: Edit the sentence describing the `famGiv` field to correct grammar
and improve clarity: change "lookup" to "look up", remove the long parenthetical
and split into two sentences — first state that `famGiv` should be removed to
prevent the user appearing in the staff list, then add a second sentence
clarifying that the first/last name are preserved under `attributes` so no data
is lost. Keep references to the `famGiv` index field and the `attributes` field
verbatim for clarity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d9776193-2e04-4dd1-93f9-d05ca37c9377

📥 Commits

Reviewing files that changed from the base of the PR and between 56fc467 and 0d2e8e1.

⛔ Files ignored due to path filters (5)
  • backend/compact-connect-ui-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • backend/compact-connect/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js.map is excluded by !**/*.map
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js.map is excluded by !**/*.map
  • backend/cosmetology-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (34)
  • .github/workflows/check-common-cdk.yml
  • .github/workflows/check-compact-connect-ui-app.yml
  • .github/workflows/check-compact-connect.yml
  • .github/workflows/check-cosmetology-app.yml
  • .github/workflows/check-multi-account.yml
  • backend/common-cdk/requirements.in
  • backend/compact-connect-ui-app/lambdas/nodejs/package.json
  • backend/compact-connect-ui-app/requirements.in
  • backend/compact-connect-ui-app/requirements.txt
  • backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md
  • backend/compact-connect/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/compact-connect/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/compact-connect/lambdas/nodejs/package.json
  • backend/compact-connect/lambdas/nodejs/tests/ingest-event-reporter.test.ts
  • backend/compact-connect/lambdas/nodejs/tsconfig.json
  • backend/compact-connect/requirements.in
  • backend/cosmetology-app/docs/internal/api-specification/latest-oas30.json
  • backend/cosmetology-app/docs/internal/postman/postman-collection.json
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/package.json
  • backend/cosmetology-app/lambdas/nodejs/tsconfig.json
  • backend/cosmetology-app/requirements.in
  • backend/multi-account/backups/requirements.in
  • backend/multi-account/control-tower/requirements.in
  • backend/multi-account/log-aggregation/requirements.in
💤 Files with no reviewable changes (2)
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js
✅ Files skipped from review due to trivial changes (15)
  • backend/compact-connect/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/multi-account/control-tower/requirements.in
  • backend/multi-account/log-aggregation/requirements.in
  • backend/compact-connect-ui-app/requirements.txt
  • backend/multi-account/backups/requirements.in
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/tsconfig.json
  • backend/common-cdk/requirements.in
  • backend/cosmetology-app/lambdas/nodejs/lib/email/encumbrance-notification-service.ts

Comment thread .github/workflows/check-compact-connect.yml
Comment thread backend/compact-connect/lambdas/nodejs/package.json
Comment thread backend/cosmetology-app/lambdas/nodejs/package.json
Copy link
Copy Markdown
Collaborator

@jlkravitz jlkravitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few questions!

Comment thread backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md
Comment thread backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md (1)

44-44: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Step 2 intro conflicts with the actual archive procedure.

Line 44 still says archiving happens by changing pk, but Lines 47-53 now archive via in-place field edits/removing famGiv. This contradiction can cause incorrect manual actions. Please align the intro with the new workflow.

Suggested doc fix
-In the DynamoDB console, if you change the partition key (pk) value of a existing DynamoDB item, it automatically deletes the old record with the old pk and creates a new one with the new pk, effectively archiving the user record. Perform the following steps:
+In the DynamoDB console, archive the existing staff-user record in place by adding archive metadata and removing the `famGiv` index field so it no longer appears in active staff-user UI listings. Perform the following steps:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md` at line 44,
Update the Step 2 intro to match the current archive procedure: change the
statement that archiving happens by changing the partition key (`pk`) to explain
that archiving is performed via in-place field edits (specifically removing or
clearing the `famGiv` field) in DynamoDB rather than changing the `pk`; ensure
the Step 2 narrative and any preceding/preceding references consistently
describe the new workflow so the lines describing the actual edits (the steps
that modify/remove `famGiv`) and the intro no longer contradict each other.
🧹 Nitpick comments (1)
backend/compact-connect/lambdas/nodejs/tests/ingest-event-reporter.test.ts (1)

186-186: ⚡ Quick win

Redundant local mockS3Client shadows the newly-initialized outer mock — consider consolidating.

Tests 2–4 in "Frequent runs" each redeclare const mockS3Client = mockClient(S3Client), shadowing the outer let mockS3Client that beforeAll now properly initializes. This means the suite accumulates multiple mockClient(S3Client) intercept registrations per test run. Because IngestEventEmailService is fully mocked and S3 is never actually called, this is harmless today, but it adds noise and creates ordering sensitivity if that mock boundary ever changes.

The simpler approach: remove the local redeclarations in tests 2–4 so they reuse the outer mockS3Client, and add a mockS3Client.reset() call in beforeEach to ensure clean state between tests (mirroring how "Weekly runs" reinitializes mockSESClient per beforeEach).

♻️ Suggested consolidation
     beforeEach(() => {
         // Clear all instances and calls to constructor and all methods:
         jest.clearAllMocks();
+        mockS3Client.reset();
         mockSendReportEmail.mockClear();
         mockSendAllsWellEmail.mockClear();
     });
     it('should not send an email if there were no ingest events', async () => {
         const mockDynamoDBClient = mockClient(DynamoDBClient);
-        const mockS3Client = mockClient(S3Client);
         // ...
     it('should let DynamoDB errors escape', async () => {
         const mockDynamoDBClient = mockClient(DynamoDBClient);
-        const mockS3Client = mockClient(S3Client);
         // ...
     it('should skip compact and continue processing when compact configuration is not found', async () => {
         const mockDynamoDBClient = mockClient(DynamoDBClient);
-        const mockS3Client = mockClient(S3Client);
         // ...

Also applies to: 246-246, 271-271

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/compact-connect/lambdas/nodejs/tests/ingest-event-reporter.test.ts`
at line 186, Remove the redundant local redeclarations of mockS3Client inside
the "Frequent runs" tests so they reuse the outer let mockS3Client initialized
in beforeAll; instead add mockS3Client.reset() in the beforeEach that runs
before those tests (mirroring how mockSESClient is reset for "Weekly runs") to
clear handlers between tests, and apply the same removal of local redeclarations
at the other occurrences that shadow the outer mock (the other test blocks where
const mockS3Client = mockClient(S3Client) appears).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md`:
- Line 50: Change the verb "lookup" to the correct two-word verb "look up" in
the sentence describing the index removal; update the line that reads "we use
this field in the index used to lookup the list of staff users" so it reads "we
use this field in the index used to look up the list of staff users" (refer to
the "Remove field: `famGiv`" sentence and the token `famGiv` to locate the exact
phrase).

In
`@backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py`:
- Around line 598-642: Replace the order-sensitive equality check in the test
method
test_generate_api_response_object_returns_adverse_actions_as_a_top_level_field_for_all_adverse_actions
with an unordered comparison: change the final assertion that compares
api_response['adverseActions'] to use unittest's assertCountEqual instead of
assertEqual so the presence of ProviderUserRecords.generate_api_response_object
returned adverse actions (license_adverse_action.to_dict() and
privilege_adverse_action.to_dict()) is validated regardless of order.

---

Outside diff comments:
In `@backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md`:
- Line 44: Update the Step 2 intro to match the current archive procedure:
change the statement that archiving happens by changing the partition key (`pk`)
to explain that archiving is performed via in-place field edits (specifically
removing or clearing the `famGiv` field) in DynamoDB rather than changing the
`pk`; ensure the Step 2 narrative and any preceding/preceding references
consistently describe the new workflow so the lines describing the actual edits
(the steps that modify/remove `famGiv`) and the intro no longer contradict each
other.

---

Nitpick comments:
In `@backend/compact-connect/lambdas/nodejs/tests/ingest-event-reporter.test.ts`:
- Line 186: Remove the redundant local redeclarations of mockS3Client inside the
"Frequent runs" tests so they reuse the outer let mockS3Client initialized in
beforeAll; instead add mockS3Client.reset() in the beforeEach that runs before
those tests (mirroring how mockSESClient is reset for "Weekly runs") to clear
handlers between tests, and apply the same removal of local redeclarations at
the other occurrences that shadow the outer mock (the other test blocks where
const mockS3Client = mockClient(S3Client) appears).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c6ab871-2d0b-4264-adb6-af9ef48093a5

📥 Commits

Reviewing files that changed from the base of the PR and between 56fc467 and 3467943.

⛔ Files ignored due to path filters (5)
  • backend/compact-connect-ui-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • backend/compact-connect/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js.map is excluded by !**/*.map
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js.map is excluded by !**/*.map
  • backend/cosmetology-app/lambdas/nodejs/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (35)
  • .github/workflows/check-common-cdk.yml
  • .github/workflows/check-compact-connect-ui-app.yml
  • .github/workflows/check-compact-connect.yml
  • .github/workflows/check-cosmetology-app.yml
  • .github/workflows/check-multi-account.yml
  • backend/common-cdk/requirements.in
  • backend/compact-connect-ui-app/lambdas/nodejs/package.json
  • backend/compact-connect-ui-app/requirements.in
  • backend/compact-connect-ui-app/requirements.txt
  • backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md
  • backend/compact-connect/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/compact-connect/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/compact-connect/lambdas/nodejs/package.json
  • backend/compact-connect/lambdas/nodejs/tests/ingest-event-reporter.test.ts
  • backend/compact-connect/lambdas/nodejs/tsconfig.json
  • backend/compact-connect/requirements.in
  • backend/cosmetology-app/docs/internal/api-specification/latest-oas30.json
  • backend/cosmetology-app/docs/internal/postman/postman-collection.json
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/package.json
  • backend/cosmetology-app/lambdas/nodejs/tsconfig.json
  • backend/cosmetology-app/lambdas/python/common/tests/unit/test_provider_record_util.py
  • backend/cosmetology-app/requirements.in
  • backend/multi-account/backups/requirements.in
  • backend/multi-account/control-tower/requirements.in
  • backend/multi-account/log-aggregation/requirements.in
💤 Files with no reviewable changes (2)
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/handler.js
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.js
✅ Files skipped from review due to trivial changes (21)
  • backend/cosmetology-app/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/cognito-emails/lambda.ts
  • backend/multi-account/control-tower/requirements.in
  • backend/compact-connect-ui-app/lambdas/nodejs/package.json
  • backend/multi-account/log-aggregation/requirements.in
  • backend/compact-connect-ui-app/requirements.txt
  • backend/multi-account/backups/requirements.in
  • backend/compact-connect/requirements.in
  • .github/workflows/check-multi-account.yml
  • backend/cosmetology-app/lambdas/nodejs/package.json
  • .github/workflows/check-common-cdk.yml
  • backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/encumbrance-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/cosmetology-app/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • backend/cosmetology-app/lambdas/nodejs/email-notification-service/lambda.ts
  • backend/compact-connect/lambdas/nodejs/lib/email/investigation-notification-service.ts
  • backend/compact-connect/lambdas/nodejs/ingest-event-reporter/lambda.ts
  • .github/workflows/check-compact-connect-ui-app.yml
  • .github/workflows/check-compact-connect.yml
  • backend/compact-connect/lambdas/nodejs/cognito-emails/lambda.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • .github/workflows/check-cosmetology-app.yml
  • backend/compact-connect-ui-app/requirements.in
  • backend/common-cdk/requirements.in
  • backend/compact-connect/lambdas/nodejs/package.json
  • backend/cosmetology-app/lambdas/nodejs/tsconfig.json
  • backend/cosmetology-app/requirements.in

Comment thread backend/compact-connect/docs/devops/STAFF_USER_MFA_RECOVERY.md
@landonshumway-ia landonshumway-ia force-pushed the feat/disciplinary-table-schema branch from 3467943 to 408e60b Compare May 4, 2026 18:21
@landonshumway-ia landonshumway-ia requested a review from jlkravitz May 4, 2026 18:23
Copy link
Copy Markdown
Collaborator

@jlkravitz jlkravitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@isabeleliassen Good to merge!

@isabeleliassen isabeleliassen merged commit 5bf5493 into csg-org:main May 4, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BE discipline table

4 participants