fix(enterprise): restore LEARNER lazy-create on invitation-accept (who-is-acting rule)#820
Conversation
…o-is-acting rule) fa769af's strict identity gate on invitation-accept broke sponsored onboarding: lib/auth.ts deliberately defers ConsulteeProfile creation to the first consumer action and names invite-accept-as-LEARNER as a sanctioned trigger, so a fresh employee accepting an org invite hit NOT_A_CONSULTEE with no self-service remedy. Rule settled with #819: identity creation requires the user's OWN action — accept keeps the EXPERT strict gate (consultant identity has real prerequisites) but lazy-creates the lightweight consumer profile; admin direct-add stays strict for BOTH roles; SSO JIT unchanged. Source-pin tests stop the gates drifting between surfaces. Closes #819. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request removes the strict NOT_A_CONSULTEE check from the invitation acceptance route, allowing ConsulteeProfile to be lazy-created for learners. It also introduces static source-code assertion tests to prevent gate constraints from drifting. Feedback on these changes highlights that reading and asserting on source files statically is a fragile testing pattern, and recommends using __dirname instead of process.cwd() to ensure robust path resolution across different environments.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const read = (p: string) => | ||
| readFileSync(join(process.cwd(), p), "utf8"); |
There was a problem hiding this comment.
Asserting the presence of specific strings or comments in source files via readFileSync is a fragile testing pattern. It is highly sensitive to non-functional changes (such as code formatting, comment edits, or variable renaming) and does not validate actual runtime behavior.
Additionally, relying on process.cwd() can fail if tests are executed from a different working directory (e.g., in a monorepo or specific CI/CD environments). Using __dirname with relative paths is more robust for locating files relative to the test file itself.
Recommendation:
Consider replacing these static source-code checks with actual integration tests or mocked unit tests that verify the runtime behavior of the route handlers. If you must keep these source-level "pins", use __dirname to resolve the paths reliably.
| const read = (p: string) => | |
| readFileSync(join(process.cwd(), p), "utf8"); | |
| const read = (p: string) => | |
| readFileSync(join(__dirname, "../../", p), "utf8"); |
…irname for path resolution Modified the read function in the invitation-accept test to ensure it correctly resolves file paths relative to the test directory, improving test reliability across different working directories.
…rner-accept-lazy-create
… site (#819) The accept route explains why ITS LEARNER gate is absent; the members route's gates never said why admin direct-add stays strict for both roles. A future reader of this file alone now sees the full rule and where the drift pins live. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Assessment (pre-merge): logic verified sound and merged-state checked against the post-#822/#823/#824
Merging. |
Context
fa769af added strict identity gates to both member-creation surfaces: EXPERT requires an existing
ConsultantProfile, LEARNER an existingConsulteeProfile. The EXPERT half implements #729's acceptance criteria and stays. The LEARNER half on invitation-accept broke the primary B2B onboarding path:lib/auth.tsdeliberately removed signup-timeConsulteeProfilecreation and names invite-accept as LEARNER as a sanctioned lazy-create trigger — with the gate, a sponsored employee who signed up via the invite link hitNOT_A_CONSULTEEon accept, with no self-service remedy short of personally booking a session first.The rule (settled in #819)
Identity creation requires the user's own action; an admin acting on someone else requires pre-existing identity.
POST /members(admin direct-add)invitation-accept(user's consenting click)The asymmetry is principled: a consultant identity carries domain, rate, verification, and payout prerequisites that no invite click can substitute for, while the consumer profile is a lightweight record any user can self-mint by booking — gating it protects nothing and blocks onboarding.
Coverage
New source-pin tests assert the gates per surface (accept:
NOT_A_CONSULTANTpresent,NOT_A_CONSULTEEabsent; members POST: both present) and that thelib/auth.tslazy-create sanction still stands — so the surfaces can't silently drift apart again. 23 tests across the three role suites pass; eslint clean;tsc --noEmitexit 0.Closes #819.
🤖 Generated with Claude Code