Skip to content

feat(cli): make integration probe route/creds configurable, default to /api/auth/profile#6

Open
jtomaszewski wants to merge 448 commits intodevelopfrom
jtomaszewski/probe-auth-profile
Open

feat(cli): make integration probe route/creds configurable, default to /api/auth/profile#6
jtomaszewski wants to merge 448 commits intodevelopfrom
jtomaszewski/probe-auth-profile

Conversation

@jtomaszewski
Copy link
Copy Markdown

Summary

Make the ephemeral integration runner's authenticated readiness probe work for non-commerce OM apps.

Previously hardcoded:

  • credentials `admin@acme.com` / `secret`
  • endpoint `GET /api/customers/people?pageSize=1`

The route assumes the `customers` module is enabled. Apps that don't ship it (e.g. education apps with portal-only flows) 404 on the probe and the ephemeral runner fails readiness every time.

This PR makes all three knobs env-configurable with backward-compatible behavior:

  • `OM_INTEGRATION_ADMIN_EMAIL` (default: `admin@acme.com`)
  • `OM_INTEGRATION_ADMIN_PASSWORD` (default: `secret`)
  • `OM_INTEGRATION_PROBE_PATH` (default: `/api/auth/profile` — new default)

Behavior change

Default probe path switches from `/api/customers/people?pageSize=1` to `/api/auth/profile`. The latter ships in the `auth` module required by every OM app, so it works universally. Apps depending on the old default can restore it via `OM_INTEGRATION_PROBE_PATH=/api/customers/people?pageSize=1`.

Test plan

  • Existing unit tests (`packages/cli/src/lib/testing/tests/integration.test.ts`) pass — mock updated to accept either path
  • CI green

🤖 Generated with Claude Code

pawelleszczewicz and others added 30 commits April 13, 2026 20:37
* hackon(HCK-0098): tests: add low-level coverage for debug.ts

* hackon(HCK-0098): tests: add low-level coverage for debug.ts

* hackon(HCK-0098): tests: add low-level coverage for debug.ts

* hackon(HCK-0098): tests: add low-level coverage for debug.ts
…#1356)

* hackon(HCK-0095): tests: add low-level coverage for presenter-enricher.ts

* hackon(HCK-0095): tests: add low-level coverage for presenter-enricher.ts
* hackon(HCK-0024): tests: add low-level coverage for openapi-paths.ts

* hackon(HCK-0024): tests: add low-level coverage for openapi-paths.ts

* fix: address review — restore __integration__ skip and remove AGENTS.md rule

* fix(tests): correct scaffold assertion to match __integration__ SKIP_DIRS behavior

The test asserted that __integration__/TC-UMES-001.spec.ts exists in scaffolded
output, but __integration__ is in SKIP_DIRS and is intentionally not copied.
Changed the assertion to verify __integration__ is absent, consistent with the
restored SKIP_DIRS contract.
…ssing (open-mercato#958) (open-mercato#1321)

When users select "All organizations" and attempt to create or update
records, some endpoints returned raw Zod validation errors (e.g.
"expected string, received undefined") while others returned the
user-friendly "Organization context is required" message.

Root cause: `withScopedPayload` in scoped.ts skipped the organization
requirement check for users with global org access (`allowedIds === null`),
allowing the downstream Zod schema validation to produce raw errors instead
of the friendly CrudHttpError message.

Fix: Remove the `hasGlobalOrgAccess` bypass in `withScopedPayload` so the
friendly error is always thrown when `requireOrganization` is true and no
organization is resolved. Users with global access can still operate by
providing `organizationId` in the payload or having a selected org.

This covers all CRUD routes using the command path (products, categories,
orders, persons, etc.) which use `parseScopedCommandInput` or
`withScopedPayload` in their `mapInput` handlers.

Closes open-mercato#958

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* hackon(HCK-0022): tests: add low-level coverage for check.ts

* hackon(HCK-0022): tests: add low-level coverage for check.ts

* fix(tests): widen timeout gap in interceptor test to prevent CI flake

The "propagates timeout failures" test used a 5ms timeout vs 20ms work
delay, which is too tight for busy CI runners where timer granularity
can cause the work to resolve before the timeout. Widened to 10ms
timeout vs 500ms work for reliable determinism.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… entity titles/links in DataGrid (open-mercato#1227)

* hackon(HCK-0035): bug: Custom fields of `kind: &open-mercato#39;relation&open-mercato#39;` render a...

* hackon(HCK-0035): bug: Custom fields of `kind: &open-mercato#39;relation&open-mercato#39;` render a...

* fix: address matgren's code review feedback

- Fix markdown rendering regression by guarding resolveOptionDisplay early-return
  with a check that resolvedDisplays has entries before short-circuiting
- Cap pageSize at 200 in options.ts to prevent unbounded queries
- Extract ~460 lines of utility code from CustomDataSection.tsx into
  packages/ui/src/backend/utils/customFieldRelationDisplay.ts
- Add AbortController to fetch cleanup in useEffect instead of boolean flag
- Replace empty catch blocks with console.debug logging per OM convention
- Add tests for customer_person_profile, catalog_product_variant,
  markdown regression, API error fallback, multi-value relations, and pageSize cap
…rward open-mercato#1287) (open-mercato#1467)

* UI Contract Violations

* fix(workflows): migrate hardcoded status colors to semantic DS tokens

Boy Scout Rule: migrate all hardcoded Tailwind colors in the workflow
events page to semantic design system tokens.

- Event type badges: bg-blue/green/red/purple → bg-status-info/success/error/neutral
- Instance status badges: same migration
- Link colors: text-blue-600 → text-primary

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: strzesniewski <michal.strzesniewski@300.codes>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pen-mercato#1317) (open-mercato#1466)

* fix(webhooks): add retry action to delivery log table (open-mercato#1068)

The delivery log DataTable's rowActions callback returned null for
non-failed rows, leaving the Actions column empty for every record.
Now every row gets a "View details" action, and failed/expired rows
also show "Retry" when the user has webhooks.manage permission.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(webhooks): add retry action to delivery log table (open-mercato#1068)

* fix(webhooks): revert Unicode escaping, add proper i18n translations

Revert mass Unicode escape conversion in de/es/pl locale files back to
readable UTF-8. Add proper translations for the new viewDetails key
(de: "Details anzeigen", es: "Ver detalles", pl: "Zobacz szczegóły").
Revert cosmetic em-dash encoding changes in page.tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: pawelleszczewicz <30691854+pawelleszczewicz@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… (carry-forward open-mercato#1277) (open-mercato#1465)

* fix(security): hash staff session and password-reset tokens with HMAC

Staff Session.token and PasswordReset.token were persisted as raw random
hex, so a DB dump/backup leak let an attacker hijack every active staff
session and reset any staff password without limit. Customer flow already
hashed tokens; only the staff side was exposed.

Tokens are now stored as HMAC-SHA256(secret, rawToken). Only the raw
token leaves the server (cookies / invite URL / reset URL); DB holds the
hash. Lookups hash the incoming token before querying, so a stolen hash
cannot be replayed against the refresh / reset / invite endpoints.

- Add auth/lib/tokenHash.ts (generateAuthToken, hashAuthToken,
  safeCompareAuthTokenHash) using AUTH_TOKEN_SECRET with AUTH_SECRET /
  NEXTAUTH_SECRET / JWT_SECRET fallbacks.
- AuthService.createSession stores HMAC, returns { token: raw }.
- AuthService.deleteSessionByToken / refreshFromSessionToken hash inputs.
- AuthService.requestPasswordReset stores HMAC, returns raw.
- AuthService.confirmPasswordReset hashes input before lookup.
- commands/users.ts sendInviteToUser and api/users/resend-invite store
  HMAC for invite tokens while keeping raw in the email URL.
- Extend authService unit tests to prove persisted value differs from
  returned raw token and lookups hash before query.

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

* fix(security): harden token hashing — remove dead code, enforce secret, dual-lookup, widen return type

- Remove unused safeCompareAuthTokenHash export (dead code)
- Throw in production when no token hashing secret is configured instead
  of silently falling back to a hardcoded default
- Add dual-lookup (hashed then raw) in session/reset token queries so
  existing unhashed rows survive deploy without a migration
- Widen createSession return to { session, token } so callers can still
  access session entity properties

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

---------

Co-authored-by: WH173-P0NY <adas.kardasz@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…open-mercato#1462)

* fix(runtime): preserve Redis URL semantics across queue and scheduler

* test(scheduler): add integration coverage for queue runtime routes

* fix(queue): widen TLS type and deprecate parseRedisUrl

- Change `tls` field type from `Record<string, never>` to
  `Record<string, unknown>` so callers can pass valid TLS options like
  `{ rejectUnauthorized: false }`.
- Mark `parseRedisUrl` as `@deprecated` — all consumers now pass the
  full URL via `{ url }` directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Patrick Madaj <patryk.madaj@they.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pen-mercato#1457)

Co-authored-by: Muhammad Usman <muhammadusmanramzan586@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… deleting (open-mercato#109) (open-mercato#1455)

When removing a deal from a person or company detail page, the action
now unlinks the association via updateCrud (removing the person/company
ID from the deal) instead of deleting the entire deal record. This
preserves the deal for other linked entities.

- Change handleDelete → handleRemove using updateCrud to strip the
  current entity from personIds/companyIds
- Add onDataRefresh and runGuardedMutation props to DealsSection
- Wire runGuardedMutation on all 4 detail pages (people, people-v2,
  companies, companies-v2)
- Add i18n keys for remove confirmation/success/error in en/pl/de/es
- Add DealsSection unit tests (5 cases covering unlink behavior)
- Add @open-mercato/ui moduleNameMapper to jest configs

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pen-mercato#1473)

* feat: add default value support for custom fields in dictionaries and entities open-mercato#824

- Implemented a new DictionaryDefaultSelector component for selecting default values from dictionary entries.
- Enhanced DictionaryFieldDefEditor to include default value selection for dictionary fields.
- Updated API to validate and normalize default values based on field kind, including support for dictionaries and currencies.
- Added localization strings for default value labels in multiple languages (de, en, es, pl).
- Updated CRUD form to apply default values on create flows while preventing overwriting of explicit initial values.
- Added tests to ensure default value functionality works correctly for various field types.

* fix after review

* readd fix

* ci: retrigger CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Marynat <jatyon14@wp.pl>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…open-mercato#1475)

Upgrade dependencies to resolve 10 open Dependabot security alerts:

- next: 16.1.7 → 16.2.3 (fixes 3 high: DoS with Server Components)
- @hono/node-server: 1.19.10 → 1.19.14 (fixes 1 medium: middleware bypass)
- hono 4.12.12 and serialize-javascript 7.0.5 already at patched versions;
  lockfile refresh picks up transitive resolution

Updated in root package.json, apps/mercato/package.json, and
packages/create-app/template/package.json.template.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…orward open-mercato#1273) (open-mercato#1477)

* fix(business_rules): accept date strings in rule form schema

Same bug as workflows (open-mercato#1269): CrudForm's <input type="date"> emits
YYYY-MM-DD strings, but the client form declared effectiveFrom/effectiveTo
as `Date | null` with `z.date()` schemas and wrapped loaded values in
`new Date(...)`. Submitting a rule with a date filled in produced
"Invalid input: expected date, received string" under the field.

Align the form with the staff/LeaveRequestForm pattern:
- type the fields as `string | null`
- validate as `z.string()`
- normalize incoming values via `toDateInputValue`, which short-circuits
  when the string already begins with YYYY-MM-DD
- also drop debug console.log calls in the edit page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(shared): extract toDateInputValue to shared utility

Consolidate the duplicated toDateInputValue helper — previously
copy-pasted across business_rules, workflows, staff, customers, and
sales modules — into @open-mercato/shared/lib/date/format.

All modules now import from the single source. Modules that returned
empty string instead of null (customers/DealForm, sales/dashboard,
staff/JobHistorySection) wrap the call with ?? ''.

Also fix the CreateBusinessRuleInput type to use z.input (pre-preprocess)
instead of z.infer (post-preprocess) so form string values pass type
checking against the dateOrNull preprocessor.

Add comprehensive unit tests for the shared utility (14 cases) and for
parseRuleToFormValues/buildRulePayload date handling (6 cases).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Konrad Alfaro <alfaro.konrad@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
open-mercato#1481)

* fix(attachments): remove markitdown shell-out, replace with pure-JS extractors (HUNT-PARSER-01)

Removes the `execFile('markitdown', ...)` call from `textExtraction.ts` that
allowed any tenant with `attachments.manage` to trigger an unrestrained Python
process on uploaded DOCX/XLSX/PPTX/MSG/PDF files with no timeout, no memory
cap, and full exposure to the transitive CVE surface of olefile/pdfminer/
openpyxl/poppler — the exact delegate-chain risk class banned in PR open-mercato#1250.

Replacement strategy (all pure-JS, no system binary required):
- text/*          → fs.readFile (UTF-8) — direct, no parser
- application/pdf → pdfjs-dist (already bundled, mirrors pdfProcessing.ts)
- DOCX/DOC        → mammoth (new pure-JS dependency, ^1.9.0)
- XLSX/PPTX/MSG   → null (safe fallback; no pure-JS extractor available yet)

Tests:
- 3 source-level regression guards: no child_process import, no markitdown
  reference, no execFile reference
- 13 behaviour tests covering the happy path for text/DOCX/PDF and the safe
  null fallback for XLSX/PPTX/MSG/unknown binaries
- Error resilience for corrupt DOCX and corrupt PDF

Also updates i18n strings in en/de/es/pl that referenced markitdown in the
partition OCR help text.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(attachments): correct .doc routing and update extraction docs (HUNT-PARSER-01)

mammoth supports only Open XML .docx, not legacy binary .doc. Remove
application/msword, application/vnd.ms-word and .doc extension from
isDocx() so those formats fall through to null instead of being passed
to mammoth and silently failing.

Replace the masking test that mocked mammoth for .doc with two explicit
null-assertion tests that verify mammoth is not called.

Update apps/docs/docs/api/attachments.mdx to reflect the pure-JS
extraction reality: remove the markitdown install section, correct
the fallback description, add a format/extractor table, and explicitly
list formats that return no content (.doc, .xlsx, .pptx, .msg etc.).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…(carry-forward open-mercato#1348) (open-mercato#1474)

* test(planner): implement Phase 3 integration tests for availability rule sets and CRUD operations open-mercato#622

* improvement

---------

Co-authored-by: Marynat <jatyon14@wp.pl>
…uard (open-mercato#1476)

* fix(auth/nav): reset attacker-controlled scope params on resolution failure and add auth.view feature guard

* Retrigger CI

* fix(auth): declare auth.view feature and grant to all roles, fix jest config

- Add auth.view feature to acl.ts so requireFeatures guard on admin nav
  endpoint resolves correctly for all authenticated users
- Grant auth.view to employee role in setup.ts defaultRoleFeatures to
  prevent locking non-admin users out of sidebar navigation
- Add @open-mercato/ui moduleNameMapper to jest.config.cjs for
  backendChrome.tsx dependency introduced by develop merge

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert(auth): drop auth.view feature gate on admin nav

The auth.view feature guard added in this PR risked locking out
existing employee/non-admin users on systems where defaultRoleFeatures
have already been seeded (the new feature would not be granted).

Revert nav metadata to requireAuth: true only — every authenticated
user can call /api/auth/admin/nav as before. The downstream
resolveBackendChromePayload still applies per-route RBAC checks via
rbacService.userHasAllFeatures, so item-level access control is
unchanged.

The security fix from this PR is preserved:
- nav.ts catch block still resets selectedOrganizationId/Tenant to
  the authenticated user's values when resolveFeatureCheckContext
  throws (prevents attacker-supplied query params from being
  forwarded to the chrome payload)
- All three security regression tests are kept

Removed:
- auth.view entry in acl.ts
- requireFeatures: ['auth.view'] in nav.ts metadata
- superadmin and employee defaults in setup.ts (back to admin: ['auth.*'])
- "requires auth.view feature in GET metadata" test
- unused metadata import in test file

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Lukasz Stasko <lukasz.stasko@autopay.eu>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… (carry-forward open-mercato#1265) (open-mercato#1469)

* Added sanitize for rich text component

* Fix after code review

---------

Co-authored-by: Adam Kanigowski <adam.kanigowski@300.codes>
* feat(auth): enforce tenantId requirement for roles

- Updated CreateRoleFormValues to make tenantId optional and changed its default value to undefined.
- Modified CLI and command files to remove handling of global roles (tenantId: null) and enforce tenantId presence.
- Changed Role entity to make tenantId non-nullable.
- Updated migration to remove orphaned global roles and enforce NOT NULL constraint on tenantId.
- Adjusted various queries and logic to align with the new tenantId requirements.

* add tests and adjust fixture

* move spec to implemented

* fix

* fix merge

* fix(auth): address code review findings in tenant-scoped roles PR

Fix spec path in RELEASE_NOTES, remove dead null guards in undo handlers,
narrow EnsureRolesOptions.tenantId type, and clean up null-check readability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(auth): replace raw em.findOne/em.find with encryption helpers

Replace all raw em.findOne() and em.find() calls with
findOneWithDecryption() and findWithDecryption() per project
conventions. Tenant-scoped entities (Role, User, RoleAcl, UserAcl,
UserRole) must always use encryption helpers.

- commands/roles.ts: 7 replacements (6 findOne + 1 find)
- commands/users.ts: 9 replacements (7 findOne + 2 find)
- api/roles/route.ts: 3 replacements (3 find)
- lib/setup-app.ts: 8 replacements (findOne calls)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(skills): enforce encryption helper rule as hard blocker in review/fix skills

The findOneWithDecryption/findWithDecryption rule was sometimes skipped
because it was listed as a soft checklist item or medium-severity
auto-detection. Strengthen enforcement across all three skills:

- code-review: checklist item now says "every hit is a blocker" with
  explicit grep instruction; security rule names both helpers and import
- review-pr: promote raw em.find/em.findOne from Medium to High
  auto-detection with explicit grep command
- fix-github-issue: add validation loop step 6 to grep changed files
  for raw em.find; self-review checklist now includes grep instruction

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(auth): fix invite test mock to handle duplicate email check

The findOneWithDecryption mock returned a truthy value for all calls,
but after the encryption helper migration the user-create command now
makes two findOneWithDecryption calls: (1) organization lookup and
(2) duplicate-email check. Use mockResolvedValueOnce to return the org
for the first call and null for the second.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Marynat <jatyon14@wp.pl>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…forward open-mercato#1307) (open-mercato#1464)

* hackon(HCK-0083): tests: add low-level coverage for crud.ts

* hackon(HCK-0083): tests: add low-level coverage for crud.ts

* hackon(HCK-0083): tests: add low-level coverage for crud.ts

* chore: resolve merge conflicts with develop

Merge develop's comprehensive organizationScope tests with PR's new
whitespace-trimming test for resolveOrganizationScopeForRequest.
Preserve all other PR changes (normalizeOrganizationId, crud.ts trim,
crud-factory scope mock, integration test).

---------

Co-authored-by: pawelleszczewicz <30691854+pawelleszczewicz@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…y-forward open-mercato#777) (open-mercato#1460)

Add formatDescription and description field to tag filter options so
tag descriptions display in the filter overlay. Also fix the useMemo
dependency array for filterDefs — customerOptions and
loadCustomerOptions were missing, causing stale closures.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…en-mercato#1463)

* feat(ui): redesign perspectives panel as Views with column chooser integration (T-FE-02)

- Refactor PerspectiveSidebar into composable components (ViewChip, ViewChipMenu, NewViewForm, ShareForm)
- Integrate ColumnChooserSection into Views sidebar (single panel replaces two)
- Replace footer Save/Discard with autosave + toast
- Add chip-based view switcher with inline rename/clone/share/delete
- Add split button [Views | name] in DataTable toolbar
- Replace checkboxes with toggle switches, add --brand-violet token
- Add Shown/Available column sections with DnD reorder and Hide all
- Fix rename duplicate constraint, clone shared role preservation
- Filter duplicate role/personal chips by name
- Panel fixed right, back arrow nav, OM sidebar-inspired spacing
- Remove standalone ColumnChooserPanel and Columns3 button from DataTable

* fix(test): update TC-CRM-032 selectors for Views sidebar column chooser

* fix(ui): align Views sidebar with design system rules

- Replace hardcoded oklch() colors in ViewChip active state with
  brand-violet token classes (bg-brand-violet/10, border-brand-violet/30,
  text-brand-violet)
- Replace hardcoded text-red-600 error color with text-status-error-text
- Replace hardcoded amber warning colors with Alert variant="warning"
- Replace hand-rolled perspectivesCheckboxClassName with Checkbox
  primitive from @open-mercato/ui/primitives/checkbox (removes text-[10px]
  arbitrary size violation)
- Replace catch (err: any) with catch (err: unknown) + instanceof checks
- Remove duplicate cn import in DataTable.tsx from merge

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address review findings in Views sidebar redesign

- Restore missing onClick handler on mobile toolbar button (dead button)
- Remove 4 unused i18n keys (footer.discard, form.visible, tabs.private, tabs.shared)
- Add empty-state guard in ShareForm when roles array is empty
- Remove spurious key={id} prop on ViewChip inner div
- Fix i18n fallback mismatch in ColumnChooserPanel ('Available' → 'Available columns')

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: zielivia <zielivia@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ato#1456)

* spec: streamlined PR label workflow and QA pipeline

Reduce 50+ labels to 15 with a clear state machine (review → changes-requested → qa → merge-queue). Integrates review-pr skill, human QA, and CI into a single coherent flow. Includes migration script and skill update plan.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: specs extensions

* spec(pr-workflow): add in-progress concurrency lock + wire skills

Adds a Concurrency Control section to the PR label workflow spec describing
how auto-skills (review-pr, fix-github-issue, review-prs, merge-buddy) MUST
claim a PR/issue before mutating it and MUST refuse to clobber an existing
claim unless --force is passed (and the user explicitly confirms).

- Spec: new Concurrency Control section with claim/release protocol, three
  signals (assignee, in-progress label, claim comment), --force semantics,
  stale-lock recovery, batch-skill skip rules.
- Spec: keep `in-progress` label (16-label inventory, was 15) and remove it
  from the deletion list in the migration script.
- review-pr SKILL.md: new step 0 (pre-claim check + claim) and step 11
  (release lock with finally semantics); --force argument documented.
- fix-github-issue SKILL.md: same step 0 and end-of-step-11 release on the
  issue; assignee remains so the human owns the resulting PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…orward open-mercato#1368) (open-mercato#1453)

* fix(auth): reject deleted users during session token refresh

refreshFromSessionToken queries User by sess.user.id without filtering
by deletedAt: null. A soft-deleted user whose session token was captured
before deletion can still refresh to new JWTs indefinitely.

Fix: add deletedAt: null to the User lookup in refreshFromSessionToken.

Tests: 2 new regression cases (deleted user rejected, active user OK).

* fix(auth): address review — findOneWithDecryption, deletedAt on confirmPasswordReset and findUserByEmail

Per review from @pkarw and @MStaniaszek1998:
- refreshFromSessionToken: use findOneWithDecryption instead of em.findOne
- confirmPasswordReset (line 109): add deletedAt: null filter so deleted
  users cannot reset their password with a valid token
- findUserByEmail (line 13): add deletedAt: null filter for consistency
  with findUsersByEmail and findUserByEmailAndTenant

* fix(auth): use findOneWithDecryption consistently in all User lookups

findUserByEmail and confirmPasswordReset still used raw em.findOne
instead of findOneWithDecryption. This aligns them with the encryption
helper mandate and the fix already applied to refreshFromSessionToken.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: RMN-45 <253251827+RMN-45@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…de effects (open-mercato#1452)

* fix(sales,workflows): add pessimistic locking to prevent duplicate side effects

Fixes two TOCTOU race conditions that could cause duplicate side effects:

1. Sales quote accept endpoint: parallel requests could both read status='sent',
   pass the status check, and invoke convert_to_order. Now the quote fetch + status
   check + update is wrapped in a transaction with PESSIMISTIC_WRITE lock.

2. Workflow resumeWorkflowAfterActivities: concurrent activity completions could
   both observe WAITING_FOR_ACTIVITIES status and run the resume flow twice. Now
   wrapped in a transaction with PESSIMISTIC_WRITE lock on the instance row.

Related to open-mercato#1339

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shared): stabilize flaky interceptor timeout test

Increase the timeout gap in the custom-route-interceptor timeout test
from 5ms/20ms to 10ms/200ms. The tight 4x margin caused intermittent
CI failures under load; the new 20x margin is reliable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
staskolukasz and others added 30 commits April 23, 2026 06:18
…AUTOINDEXING error

The autoIndexingDisabled error is served via the `search.api.errors.autoIndexingDisabled`
key, whose live translations live in the search module's own i18n files. Update the four
locales (en, de, es, pl) so the new OM_-prefixed env var name and legacy alias show up
in the error response, instead of the pre-existing truncated wording.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…o /api/auth/profile

The ephemeral integration runner's authenticated readiness probe
previously hardcoded `admin@acme.com` / `secret` and `/api/customers/people`.
The credentials are often fine (they match what `yarn initialize` seeds
by default), but the route assumes the `customers` module is enabled —
which isn't the case for non-commerce apps built on Open Mercato, so the
probe 404s and the runner fails readiness.

Make all three knobs env-configurable with backward-compatible behavior:

- `OM_INTEGRATION_ADMIN_EMAIL`    (default: `admin@acme.com`)
- `OM_INTEGRATION_ADMIN_PASSWORD` (default: `secret`)
- `OM_INTEGRATION_PROBE_PATH`     (default: `/api/auth/profile` — NEW)

The default path switches from `/api/customers/people?pageSize=1` to
`/api/auth/profile`, which ships in the `auth` module required by every
OM app. Apps depending on the old default can restore it via the env
var.
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.