Skip to content

♻️ refactor: give the remaining shared domain errors a kind (#862) - #531

Open
MaloPromyze wants to merge 7 commits into
mainfrom
refactor/862-kind-carrying-throws
Open

MaloPromyze wants to merge 7 commits into
mainfrom
refactor/862-kind-carrying-throws

Conversation

@MaloPromyze

Copy link
Copy Markdown
Contributor

Explanation

The last OSS pass of PackmindHub/packmind-proprietary#862 (errors thrown from domain and application layers must carry a kind). A type-aware ESLint rule built on the proprietary side found the sites that still reach DomainExceptionFilter without a kind; this PR fixes the shared ones.

  • New unauthenticated kind (401). It joins DomainErrorKind and gets a row in the filter's KIND_POLICY (warn, message returned, not retried). The frontend has no global 401 handler (useAuthErrorHandler exists but is never called), so answering 401 never logs anyone out.
  • Sign-in. InvalidEmailOrPasswordError joins AccountsError with kind unauthenticated / reason invalid_credentials, and the hand-mapped 401 in auth.controller.ts is gone. The body keeps the same message and gains reason. TooManyLoginAttemptsError keeps its hand-mapped 429, because no kind answers 429 with bannedUntil in the body yet.
  • node-utils base use cases. When an org row is missing, the error is now MembershipOrganizationNotFoundError (404) instead of a bare Error (500). An empty organizationId now gives the real UserNotInOrganizationError (403). handleValidationError is narrowed to UserAccessError, and the unreachable admin branch throws an internal error.
  • git. NO_CHANGES_DETECTED becomes NoChangesDetectedError with the exact same message, because proprietary marketplaces jobs still match on the message until the next sync. The deployments catch sites now use instanceof.
  • linter-ast / linter-execution. Their invariants (parser not available, console-log removal failures, program parse failure) are now PackmindInternalError subclasses. Messages are unchanged, so every caller's catch behaves as before.
  • Unrelated fix found while pushing: GitService.realGit.spec.ts inherited the hook's GIT_DIR when pushing from a worktree. It committed empty "initial commit"s onto the branch, wrote a test user into the shared .git/config and set core.bare=true. Each git call in the spec now gets an env without GIT_*. I checked this against a throwaway repo: before the fix it received 9 commits and the test user, after it nothing.

Relates to PackmindHub/packmind-proprietary#862

Type of Change

  • Bug fix
  • New feature
  • Improvement/Enhancement
  • Refactoring
  • Documentation
  • Breaking change

Affected Components

  • Domain packages affected: types, node-utils, accounts, git, deployments, linter-ast, linter-execution (+ apps/api auth controller, apps/cli spec)
  • Frontend / Backend / Both: Backend (frontend checked, unchanged)
  • Breaking changes (if any): none on the wire beyond status codes that were wrong (500 → 404/403) and an added reason field on the sign-in 401 body

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing completed
  • Test coverage maintained or improved

Test Details:
Specs assert errors by instance, not by message. The new specs cover the 401 policy row, the sign-in rethrow, the org-row-gone and empty-org-id cases, the NoChangesDetectedError instance plus its exact message, and the internal linter errors. Nine existing specs in accounts, skills and standards asserted the old org-not-found message; they now assert the class. The pre-push hook (nx affected -t lint test build) passed.

TODO List

  • CHANGELOG Updated — not needed: error-typing refactor; the only visible change is wrong 500s becoming the right 4xx
  • Documentation Updated — the domain-error-handling standard needs an unauthenticated row; that is a Packmind playbook update, not a hand edit

Reviewer Notes

  • The NoChangesDetectedError message must stay 'NO_CHANGES_DETECTED' until proprietary marketplaces moves to instanceof after the sync.
  • TooManyLoginAttemptsError (429 + bannedUntil) is still kindless on purpose. It is an open decision on #862.
  • apps/cli-e2e-tests/src/helpers/setupGitRepo.ts follows the same pattern as the leaking spec. The pre-push hook excludes it, so it is left as is.

🤖 Generated with Claude Code

MaloPromyze and others added 6 commits October 1, 2026 16:48
Sign-in failures are the caller's fault and safe to show, but no kind
answered 401, so the auth controller hand-mapped them to an
HttpException. A dedicated kind lets the filter answer them centrally,
distinct from forbidden where the caller is known and their rights are
the subject.

Refs #862

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… error filter

InvalidEmailOrPasswordError was kindless, so POST /auth/signin mapped it
to a 401 HttpException by hand. It now joins the accounts error family
with kind 'unauthenticated' and reason 'invalid_credentials', and the
controller rethrows it for DomainExceptionFilter to answer 401. The body
gains a reason; message stays the same, which is what the sign-in form
shows.

TooManyLoginAttemptsError keeps its hand-mapped 429: no kind answers
with bannedUntil in the body yet.

Refs #862

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inded errors

Every member and admin use case runs these checks, so a bare Error here
answered 500 for any of them:

- a membership whose organization row is gone now throws
  MembershipOrganizationNotFoundError (not_found, organization_not_found)
  with the ids in context;
- a command with no organization id now answers the real
  UserNotInOrganizationError 403, whose constructor accepts an optional
  organization id, instead of a bare Error masking it;
- the unreachable admin branch throws UserAccessInternalError.

handleValidationError now returns UserAccessError, so its throw site is
typed as kind-carrying.

Refs #862

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed error

CommitToGitUseCase signalled "nothing to commit" with a bare Error that
callers recognised by its message. It now throws NoChangesDetectedError
(kind 'conflict', reason 'no_changes_detected', repo ids in context),
and the deployments catch sites — RemovePackageFromTargetsUseCase and
PublishArtifactsDelayedJob — match it with instanceof.

The message stays exactly 'NO_CHANGES_DETECTED' because callers outside
this repo still compare against it until they are migrated.

Refs #862

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e internal kind

These packages run in the API and in the CLI, and their bare Errors
reached the log as unstructured 500s. ConsoleLogRemovalService's guards,
ParserNotAvailableError and ExecuteLinterProgramsUseCase's program
compilation now throw PackmindInternalError subclasses (a
LinterAstInternalError and a LinterExecutionInternalError base), with
the language or cause in context. Messages are unchanged and every
caller still catches them as before.

Refs #862

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitService.realGit.spec shells out to git init, config and commit in a temp
dir, inheriting the environment. Inside a git hook run from a worktree, git
exports GIT_DIR, so those calls hit the repository running the hook: the
pre-push run committed nine empty "initial commit"s onto the branch, wrote
a test user into the shared config and set core.bare (via init --bare),
which broke the main checkout.

Every git call in the spec now gets the environment without GIT_*; Jest
sandboxes process.env, so deleting the keys in the test would not reach
child_process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Adds error classification to domain exceptions across packages.

The PR appears safe to merge; no actionable new issue was established.

Summary

The PR gives remaining shared errors typed kinds, adds a 401 policy for invalid credentials, updates callers and tests, and isolates the real-Git spec from inherited Git environment variables.

Reviews (2) · Last reviewed commit: "✅ test(node-utils): stop asserting the f..."

…ticated

backend-tests-redaction forbids asserting on stubbed logger output; the
status and body assertions already cover the unauthenticated row.

Refs #862

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Oct 1, 2026

Copy link
Copy Markdown

This branch has not been deployed

No deployments
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.

1 participant