History / Changelog
This page is a changelog-style rendering of the repository's DEVLOG.md — a
chronological record of why things ended up the way they did, including
decisions carried over from other projects and gaps deliberately left open.
CLAUDE.md (and this site's other pages, generated from it) is the normative,
current-state reference; this page is for context on how it got there. Entries
are kept in their original order — nothing has been dropped, though older/minor
entries are summarized more tersely than major ones.
1. Frontend design system ported from LSCoreNext (2026-07-22)
Eos.Web's shell (AppShell/TopBar/SideNav/AppBreadcrumb) and login page
were reskinned using a vendored CSS design system (tokens.css/app.css) and a
shared component library (Button, PageHeader, Modal, ConfirmDialog,
EmptyState, LoadingSkeleton, ErrorBoundary, StatusBadge, MenuIcon,
Toast/ToastProvider), ported from a sister project, LSCoreNext — a separate
decoupled-migration effort against the same legacy application, found in this
repo's temp/ folder. Only its frontend visual layer was ported; it's unrelated
to Eos's backend/architecture.
Deliberately scoped as a design-only port, not a full rewrite:
- The 12 existing CRUD pages keep using Ant Design (
antd) for tables/forms — the source project never built a ported grid/form equivalent. - Data fetching stays on
axios+useEffect/useState; adopting TanStack Query (used throughout the source project) was deferred as a separate task. - The source project's per-client login-page branding logo wasn't ported — no backend support for it exists in Eos yet.
MenuIconalways falls back to a generic icon — Eos's menu DTO has noIconNamecolumn yet, unlike the source project'sMAS_MENU.IconName.
2. Lesson carried over: real classes, not invented ones
:::tip Lesson learned in the source project, carried forward as a standing rule
Nearly every shared shell component the source project built early on used
invented CSS class names with zero real styling behind them, written before
anyone had actually read tokens.css/app.css. Once those files were read, a
complete, already-working design system turned out to exist for nearly
everything — the invented classes/components had to be rewritten wholesale
(one entire CSS file deleted as fully superseded). That's why this port vendored
tokens.css/app.css wholesale up front, rather than re-implementing the look
piecemeal from screenshots or guesswork.
:::
3. Sidebar collapse + hover-flyout, ported as one unit
SideNav's collapsed icon-rail + hover-triggered flyout (200ms close-delay,
cancelled on re-entering either the icon or the flyout) was ported as-is from
the source project's own redesign pass. Known limitation carried over unfixed:
the flyout has no viewport-boundary clamping — acceptable today given how few
menu items exist, worth revisiting later.
4. Navigation approach: conventional routing, not a tab workspace
The source project explored, then reversed away from, an ERP-style multi-tab workspace (closable tabs, multiple screens open at once) in favor of conventional one-screen-per-URL routing — no usage data justified the extra complexity, and plain routing gives deep-linking for free. Eos's shell inherits this same philosophy.
5. Known gaps / deferred, from this port
- TanStack Query not adopted — still plain
axios+useEffect/useState. - No per-item menu icons until Eos's backend adds an
IconName-equivalent column. - No per-client login-page branding logo.
- This design port had not been visually verified in a browser by the agent that built it at the time (no browser tooling available in that session).
6. CitiesPage pilot: antd grid/form replaced, then a real crash found and fixed
Piloted the design system on CitiesPage as the first of the 12 CRUD pages to
come off antd: table.grid for the list, .right-panel for create/edit
(replacing Drawer), the .field/.fields-2col/.fields-3col pattern
(replacing Form/Row/Col), .cb checkboxes (replacing Switch), a native
<select> (replacing Select), ConfirmDialog (replacing Popconfirm).
:::danger Real incident: hard-crashed the browser tab
Shipped without pagination at first, and it hard-crashed the browser tab ("Aw,
Snap!") on real data. Root cause: antd's Table paginates at 10 rows/page by
default, silently protecting every page built on it from ever rendering more
than a page's worth of DOM at once. The vendored table.grid pattern has no
such limit — it renders exactly what it's given, and MAS_CITY is a real, large
reference table (world cities). Fixed by adding client-side pagination (50
rows/page) as a first pass — see the Design System and Pagination pages for how
this was later upgraded to real server-side pagination. Every one of the other
11 pages needed the same treatment as it came off antd, regardless of how
small that entity's table looked.
:::
7. Cities page slow on first open against real client data — startup cache warm-up
Reported against a real deployment: the Cities page was noticeably slow the
first time it was opened, fast after that. Root cause: CityRepository's 1hr
IMemoryCache meant the very first request after API start (or cache
expiration) was an uncached hit against a genuinely large real-client
MAS_CITY table (same root cause as entry 6's crash). Fixed by warming
ICityRepository.ListAsync() once at API startup, wrapped in try/catch so a
warm-up failure never blocks the app from starting.
8. Company logo never rendered — [Authorize] + <img> tags don't mix under JWT auth
Reported as "the top bar never shows the real company logo." Root cause:
browsers only attach the Authorization: Bearer header for requests made
through the apiClient axios interceptor — never for a bare <img> tag's own
request. CompaniesController had [Authorize] on the whole class including
GetLogo, so every logo request 401'd and silently fell back to the text logo,
indistinguishable from "no logo uploaded."
Fixed with [AllowAnonymous] on CompaniesController.GetLogo specifically —
safe because CompanyService.GetLogoAsync was already deliberately ungated by
design.
:::caution Not a general fix
ClientsController.GetLogo has the identical mismatch, but ClientService.GetLogoAsync
makes a real territory-gated access decision, so stripping its [Authorize]
the same way would be a real security regression. The correct fix there is
fetching the image through apiClient as a blob and using
URL.createObjectURL(...), keeping the backend gate intact — not yet done for
ClientsPage's logo as of the original entry.
:::
9. CORS AllowedOrigins not actually substituted at deploy — array vs. scalar
A real deploy left Cors:AllowedOrigins empty despite adding an indexed
pipeline variable (Cors.AllowedOrigins.0) — FileTransform@2's JSON
substitution reliably replaces a scalar value by dotted path name, but
substituting into a JSON array by index turned out to be unreliable in
practice. Fixed by changing Cors:AllowedOrigins to a single comma-separated
string (split in Program.cs) instead of a JSON array — a pattern worth
reusing for any future config value that might otherwise be modeled as a JSON
array.
10. Server-side pagination rolled out to all 12 grids — and a near-miss on territory gating
Followed up on entry 6's client-side pagination fix with real server-side
pagination — PagedResult<T>, repository-level ListPagedAsync for ungated
entities vs. service-level GetAllPagedAsync for gated ones, the
one-route-two-shapes GetAll convention, usePagedList + DataGrid.
Rolled out via parallel agents, each told (incorrectly) that their entity was "not territory-gated, mirrors City exactly."
:::tip Worth remembering
Every agent caught the mismatch and self-corrected — Equipment, Department,
ForumType, Queue, TrCode, and Address are all actually
MAS_USER_TERRITORY-gated (only City and Country genuinely aren't). Each agent
independently read the real service code and implemented the gated
(filter-then-page) shape instead of the literal instruction, catching what
would otherwise have been a real access-control regression across six entities
at once.
:::
The self-corrections weren't fully consistent with each other structurally
(three different approaches across entities, all equally safe), and one group
left a dead, unfiltered ListPagedAsync on their repositories as a foot-gun —
removed. Address needed its client-side type-filter threaded through to the
server as a query param rather than being applied only to the current page.
Backend and frontend both built clean; all 50 existing backend tests passed.
11. Translatable labels rolled out shell-wide and to all 12 CRUD pages
Built a full i18n system from scratch: languages sourced from real
MAS_CHOICES data (ChoiceType = 'LANGUAGES'), flag icons as real ChoiceImage
blobs (not emoji). Frontend: useTranslation() (t(key, fallback, params?),
fallback required so a missing dictionary entry never surfaces as broken UI),
LanguageProvider/useLanguage(), LanguageSelector, one dictionary file per
namespace under src/i18n/en/.
Rollout order: infrastructure first, CitiesPage converted by hand as the
reference (shared components and shell also done by hand), then one parallel
agent per remaining entity. All 11 agents produced code that compiled clean on
the first consolidated build — no fix-up pass needed. Two adaptations worth
noting for future rollouts: a translated-options constant had to move from
module scope into the component body, and two pages had a pre-existing local
variable literally named t that had to be renamed to avoid shadowing the
translation function.
12. SuperAdmin accounts had no branch to show in TopBar — fixed with a DefaultBranch fallback
Found while checking the branch selector on a real environment: TopBar's
branch block only renders when the JWT carries a branchCode claim, but a
SuperAdmin's MAS_USER.BranchCode is always null/empty/"0" by definition, so
the branch selector had silently rendered nothing for every SuperAdmin since it
was built. Fixed by adding IAppSettingRepository/AppSettingRepository and
having LoginService resolve a DefaultBranch setting as the SuperAdmin's
displayed branchCode claim, falling back to the original blank value if no
such setting exists. AuthenticatedUser.SuperAdmin itself is untouched — this
only changes what a SuperAdmin's token displays as their working branch.
Not yet done (at time of writing): no client database had been confirmed to
actually have the DefaultBranch setting row seeded.
13. A generic MAS_CHOICES admin page — the first entity with no legacy manage SP
Requested directly once the language selector needed a way to add more
LANGUAGES rows. Built as a full generic MAS_CHOICES admin page rather than
scoping to just Languages, since the table backs many unrelated picklists. This
was the first entity to have no legacy MAS_Man* stored procedure — writes
go straight through EF Core instead, documented as an explicit exception in the
CRUD pattern. Also added MAS_CHOICE_TYPES as a small read-only lookup to back
the admin page's type filter. One real design wrinkle: the existing
active-only cached ListByTypeAsync couldn't be reused for the admin grid,
which needs inactive rows too — solved with a different (uncached, live) filter
shape for the paged/admin read rather than a different payload size.
14. TopBar branch link replaced with a searchable dropdown (BranchSelector)
The "HOU — Houston [Change]" link + modal popup was replaced with
BranchSelector, a self-contained searchable combobox inline in TopBar — no
modal. Reused existing vendored CSS classes throughout; no new CSS was written.
Selecting a branch still only surfaces a toast — branch-switching itself still
wasn't wired up to the backend, purely a UI replacement. BranchSwitcherModal.tsx
was deleted as dead code.
15. /health endpoints, then API versioning
Two follow-ups from a backend-architecture review:
/health—EosDbHealthCheckchecksEosDbconnectivity, registered on both hosts.Eos.Workersneeded aFrameworkReferencetoMicrosoft.AspNetCore.Appand a swap toWebApplication.CreateBuildersince it had no ASP.NET Core surface at all.- API versioning —
api/v{major}/operations/...viaAsp.Versioning.Mvc, specified in full up front. All 17 controllers surveyed before touching anything;AuthControllerconfirmed to stay unversioned as the one cross-cutting exception. A temporary 308-redirect middleware plusAssumeDefaultVersionWhenUnspecifiedwere added as migration nets, both flagged for removal once no traffic depends on them. All 17 frontendsrc/api/*.tsfiles updated to the new/v1/paths.
16. FluentValidation wired up — global filter, one reference validator, then 12 more in parallel
FluentValidation had been a referenced dependency with zero validators. Wired
up as auto-discovery (anchored on ChoiceDto, not Program, since validators
belong in Application) plus a global ValidationActionFilter producing
ValidationProblemDetails. Surveyed all 17 controllers: 13 accept a request
body, 3 are read-only, AuthController.Login flagged as out of scope. Built
ChoiceDtoValidator by hand as the reference, then dispatched 12 parallel
agents (one per remaining DTO), each told to validate only genuinely
primary/identifying fields and derive MaximumLength from the real
EosDbContext column mapping. All 12 came back with real, verified rules;
backend built clean on the first consolidated attempt.
17. Audit-trail change detection — DtoDiff for SP writes, EF ChangeTracker for Choices
Came out of a design discussion on whether audit trails needed an
Observer-pattern implementation. Landed on: eventing (RabbitMQ) already covers
"notify subscribers," what's actually needed is a diff/changeset step — and
Eos's split write path needs two different mechanisms: DtoDiff
(reflection-based before/after comparison) for the SqlCommand/legacy-SP path,
and EF Core's own ChangeTracker for the one entity (Choices) written through
plain EF Core. The diff payload landed on DomainEvent.Changes (null for
Created/Deleted), reusing the existing eventing pipeline rather than inventing
a second one. [AuditIgnore] was added to exclude write-only fields and
fields already restricted from logging/output. Deliberately did not invent
a persisted audit-log table — Changes only reaches a RabbitMQ message body so
far; where audit history gets durably stored and queried is a separate, later
decision. Backend built clean; 61 tests passed (52 existing + 9 new).
18. DBUp replaces per-client hand-run sqlcmd for database migrations (2026-08-05)
EosDatabaseMigrator (DBUp, embedded scripts, run from every composition root's
Program.cs) replaced the old workflow of hand-running each DDL/seed script per
client — a real gap it closed: two menu-permission seed scripts had never
actually run against any database, including Sandbox, silently 403ing every
non-SuperAdmin user on two controllers. Deliberately doesn't copy legacy's own
migration tool's shape (plaintext connection string on the CLI, no journal
table) — runs in-process off the app's own resolved connection string instead.
:::caution Real incident: four scripts hardcoded Sandbox-only IDs
Four hand-run scripts hardcoded a specific RoleId/UserId "confirmed" against
Sandbox's own test data — unsafe for any other client. Fixed by dropping the
role-grant step from those scripts; granting a newly-seeded menu item to a role
is now a manual, per-client step through the existing RoleMenuGrants screen.
:::
Verified end-to-end against the real ceva-eos sandbox: all 12 embedded scripts
journaled correctly on a first run, a second run was a true no-op.
:::tip Debugging lesson: check VPN before assuming a real bug The first verification attempt failed with a raw SQL connection timeout that looked identical to a real bug — turned out to be the VPN being disconnected. Check plain TCP connectivity first before assuming a sandbox-touching test/verification failure is a real code bug. :::
19. Optimistic concurrency rollout across every eligible Mas* repository (2026-08-08/09)
Every Mas* write was last-write-wins with no concurrency token anywhere.
Settled on an application-layer UpdatedDt compare-then-write check (no table in
the legacy schema has a real rowversion column, and legacy SPs execute
unconditionally) — check-then-act, not atomic, a narrow TOCTOU window accepted as
strictly better than nothing. Rolled out to 20 entities, all verified live
against ceva-eos via throwaway harnesses.
Found and fixed four real, unrelated bugs along the way by insisting on live
verification rather than accepting "build succeeded": EmailAccountRepository/
TpAuthConfigRepository.CreateAsync throwing despite the row inserting
successfully (SCOPE_IDENTITY() returning DBNull after an RPC-style SP call —
switched to @@IDENTITY); ClientRepository.DeleteAsync and 10 similarly-shaped
call sites throwing because new SqlParameter(name, 0) resolved to the
(string, SqlDbType) overload instead of (string, object); TrCodeRepository. DeleteAsync omitting a required SP parameter; and ClientRepository.DeleteAsync
returning true for a client code that was never created (the SP's ~14
referential-integrity guards never actually check the code exists) — fixed with
an existence pre-check.
:::tip Harness gotchas worth remembering for future live-verification work
Reuse a fresh DbContext per operation (a shared one causes false Conflicts via
EF's identity map); a freshly-created row's UpdatedDt is NULL until a
baseline update, so seed a real timestamp before testing stale/correct-stamp
paths; some legacy SPs' Create leaves extra audit/HR side-effect rows behind —
an unconditional by-name cleanup still leaves zero residue.
:::
20. Automation engine: Event-Condition-Action rules, built and verified in stages (2026-08-03/04)
Replaced legacy's IFTTT-style trigger/action/scenario model with a data-driven,
DI-registered plugin model — brand-new Eos-owned tables (MAS_TRIGGERS_V2, etc.)
rather than migrating legacy's live ones, since legacy's own consumer is
confirmed still running against them for at least one client. Current shape is
in CLAUDE.md's "Automation engine" section.
:::tip Most-verified feature to date, at the time
Before Stage 4, applied the new DDL for real against ceva-eos, briefly flipped
on real queue processing, ran Eos.Workers against the sandbox's real RabbitMQ,
and published real synthetic events — a transient httpbin 503 logged as
Failed, a real 200 logged as Success, both for real, not just build+unit-test
green.
:::
Later additions the same week: an audit-log query/UI (ScenarioLogsController/
ScenarioLogsPage.tsx, the first Eos.Web page for this module), and a
scenario-evaluation trace (COM_SCENARIOS_LOG_EVAL_V2, recording every
condition's result per candidate, not just the first failure) — also verified
live against the real sandbox.
21. Azure File Share attachment storage + narrow EDI file primitive (2026-08-04, 2026-08-08)
Replicated legacy's ad-hoc Azure File Share attachment calls as a real, reusable
storage primitive — first consumer WMS_ATTACHMENTS via MasterOrder, matching
legacy's 15 MB upload cap and deleting an already-uploaded file on a rejected
Create to avoid an orphaned blob. A narrow EDI file storage primitive
(IEdiFileRepository) followed a few days later — asked the user first, since
full EDI ingest wiring is still genuinely blocked; this ships only the storage
plumbing a future adapter will need.
22–23. MAS_SYSCONFIG + three more admin CRUD pages (2026-08-06)
Built a full edit UI over MAS_SYSCONFIG (reversing its former read-only
status) plus App Settings/Email Accounts/SSO Providers admin pages, all
Reference-CRUD/pagination shape. Secret masking and preserve-if-blank behavior
were added unilaterally, since this was the first time MAS_SYSCONFIG's
credential columns became reachable over HTTP at all.
:::caution Two stale doc comments corrected by grepping the real SPs
AppSettingRepository's doc comment claimed no write path existed — wrong,
MAS_ManAppSettings exists (with a real self-assignment bug in its Update
branch, routed around). ITpAuthConfigRepository's made the same wrong claim.
Trusting an existing comment instead of the real SP body would have shipped
both wrong.
:::
SSO Providers was the highest-risk page in this pass — MAS_TP_AUTH_CONFIG
already backs a live production SSO pilot, so ClientSecret never reaches a
controller in either direction; the admin DTO carries HasClientSecret instead.
24. eos-scaffold: SP-signature-driven repository scaffolding CLI (2026-08-08)
Built a CLI to generate a full Reference-CRUD slice by introspecting the real SP signature and table schema, never by hand-transcribing a parameter list.
:::caution Real schema-drift bug found while building the tool itself
A first pass against MAS_CURRENCY generated entity.UpdatedDtUTC, which
doesn't compile — EF's scaffolder had cased the real column as UpdatedDtUtc.
The same failure mode two prior real incidents (MAS_MENU, MAS_ORDER_TYPES)
already warned about, now reproduced by a naive schema-introspection generator.
Fixed by adding a schema-drift cross-check that scans the real scaffolded entity
and drops any field with no match, printing what it dropped.
:::
25. Atomic conditional-UPDATE optimistic concurrency: City pilot (2026-08-18)
Upgraded City from the app-layer compare-then-write check (entry 19) to a real
DB-enforced atomic conditional UPDATE/DELETE, using EF Core's
IsConcurrencyToken() on the ordinary UpdatedDt column plus overriding the
tracked OriginalValue to the caller's last-seen value — EF folds the check
into the same UPDATE ... WHERE ... AND UpdatedDt=@original it already
generates and throws when the affected-row count is zero, closing the TOCTOU
window entirely. Scoped to EntityFrameworkCore-strategy repositories only —
StoredProcedure/Hybrid repos write through legacy SPs that execute
unconditionally and can't be made atomic this way.
Proven twice: a throwaway EF Core InMemory POC first, then for real against
ceva-eos. The live run caught a real test-scenario bug before it could be
reported as a false negative — a freshly-created row's UpdatedDt is NULL,
which skipped the concurrency check entirely until a baseline update was added
before constructing the actual two-writer conflict. Only City was migrated this
pass; the remaining 19 entities were rolled out later (see CLAUDE.md's
"Consider optimistic concurrency" section for the full completed scope).
26–27. Draft/publish scaffold's first two real consumers: Transport Order, then Warehouse Order (2026-08-28/29)
The generic draft/publish scaffold (built 2026-08-04, commit 370a213) had sat
unconsumed until Transport Order piloted it. Four correctness gaps surfaced
during piloting — none Transport-Order-specific, all fixed in the shared
scaffold before Warehouse Order started rather than being rediscovered
per-entity: a missing BeginTransactionAsync on the draft repository interface;
a transaction-accepting CreateAsync overload needed on every draft-enabled
repository (confirmed empirically — a second nested transaction on the same
DbContext throws at runtime); a new ICurrentUserAccessor (AsyncLocal-backed)
so the draft service layer can get a display username down to a legacy SP
parameter; and changing WriteRealEntityAsync to return (Result, Message)
instead of a bare int, so an SP business-rule rejection surfaces as a
retryable Conflict instead of an unhandled 500.
Warehouse Order's own wiring needed the same transaction-accepting-overload
treatment, just via a different failure mode (SqlCommand.ExecuteNonQueryAsync
throwing because the shared parameter-binding helper never assigned
command.Transaction) — confirming the convention generalizes rather than
being a Transport-Order-specific fix. Both passes deliberately narrowed scope:
Warehouse Order ships header-only, with line-linking (WMS_WO_DL referencing an
existing inventory item) left for a follow-up. Neither pilot added
Testcontainers/HTTP integration coverage for the new draft endpoints themselves.
28. Eos.Api.External — first partner-facing business endpoint (2026-08-31)
POST api/v1/external/masterorder, writing through the existing internal
MasterOrderService path, preceded by a read-only design audit. The audit found
a real gap before any code shipped: PartnerTokenIssuer minted a JWT with no
groupId/superAdmin/clientCode claims, so every partner call would have hit
CallerContext's territory gate and come back unconditionally Forbidden — not
a bug in the order service, a mismatch between the partner and internal token
shapes. Fixed by embedding the same claim set internal tokens already carry.
Also added a deprecated inline Basic-auth scheme (PartnerBasic) alongside the
existing OAuth2 password grant, built to produce an equivalent CallerContext
either way and marked with an RFC 8594 Deprecation header on every response.
Confirmed gap, not closed by this pass: AccessPolicyRegistryCoverageTests only
reflects over the internal Api assembly, so the new Api.External controller
is invisible to it — no equivalent coverage exists for Api.External yet.
29–31. i18n L1/L2/L3 — RTL stubs, label-translation column-parity widening, data-translation resolution (2026-08-31)
Three passes off a frontend and a backend i18n audit, all landing the same day.
L1: found substantial prior work already shipped (real ar-LB/tr-TR
content, RTL dir switching, directional-glyph handling) — generated
__NEEDS_TRANSLATION__-stubbed content only for the 3 languages that had none
(de-DE/es-ES/zh-CN, 1,197 keys × 3, mechanically generated for exact
key/value parity with the English source) rather than regenerating the two
languages with real content. L2: widened LabelTranslationsDto/its backing
repository from 19 to 124 fields (every column confirmed present on both
MAS_TRANSLATIONS and MAS_CLIENT_TRANSLATIONS), deliberately leaving the
inconclusive A-prefix columns unprojected rather than guessing their meaning.
L3: found the core data-translation mechanism (ILanguageContext,
IDescriptionTranslationService, the Accept-Language pipeline) already built by
a prior, undocumented session — built a new read-only Sku repository (the one
DescType with nowhere to inject into, no SKU repository existed at all before
this), and deliberately kept the service's real legacy-accurate 2-step fallback
chain rather than the 3-step one a spec called for.
:::tip Recurring pattern across all three passes
Each pass hit at least one point where the literal instruction conflicted with
either already-shipped work or a confirmed real constraint, and stopped to
confirm with the user rather than guessing — regenerating working translations
as empty stubs, projecting unconfirmed A-prefix columns, and replacing a
legacy-accurate fallback chain with a spec'd one that didn't match production
behavior were all avoided this way.
:::
32. eos-contracts — scoped package rename + Azure Artifacts publishing pipeline (2026-08-31)
Renamed the package to @logiswift/eos-contracts and wired up the CI publishing
pipeline a same-day audit found was still missing despite the package already
existing. eos-web deliberately stays on a file: reference to the local
folder rather than the newly-published version — intentional until a real
out-of-repo consumer needs the pinned published package, not a leftover to
"finish" later.
33. Eos.Web CLAUDE.md doc-accuracy fixes (2026-08-31)
Two small onboarding fixes from a frontend-foundation audit: corrected a stale
claim that the ~12 Reference CRUD pages still used antd (they were ported off
it back in entry 6, and antd was later dropped as a dependency entirely —
confirmed via grep before editing, not assumed), and added a short "when to
use TanStack Query instead" pointer plus an "Api.External is a separate
backend" section so a new contributor doesn't copy src/api/*.ts patterns
against the wrong auth model. A reminder that even a file specifically kept as
the current-state source of truth needs its own periodic accuracy pass.