The auth seam read `process.env.AUTH_ENFORCE === 'true'`, so an absent env
var meant NO authentication: any fresh container, local run, DR restore or
redeploy that dropped the variable served /api/0pt/* anonymously, taking the
tenant from the caller-supplied X-Tenant-Id header. 0pt.app is deployed and
was answering anonymously (verified: an invalid Bearer token plus an
arbitrary X-Tenant-Id returned 200), so this was a live hole, not latent.
* authEnforced() now returns true unless AUTH_ENFORCE is explicitly "false"
(trimmed, case-insensitive). Unset/empty/misspelled => ENFORCED.
* assertAuthConfigSafe() throws AuthMisconfigurationError when
NODE_ENV=production and enforcement resolved off, naming AUTH_ENFORCE. It
runs at middleware module load (startup) and on the env-driven request
path, so a production instance can neither boot nor serve a single
unauthenticated request with auth switched off.
* .env.example no longer teaches AUTH_ENFORCE=false; README's Deploy section
states the fail-closed posture (no DEPLOY.md in this repo).
* 7 new tests, incl. the regression case: with AUTH_ENFORCE unset and no
`enforce` option, an unauthenticated request is rejected with 401. No test
was inverted or deleted -- none asserted the open default; the three
enforce:false cases now describe the explicit local-dev escape hatch.
Tests: 121 passed / 1 skipped before, 128 passed / 1 skipped after.
Typecheck and build clean; advisory lint unchanged (16 pre-existing problems).
Scope: auth default only. The matcher still covers /api/0pt/* alone, and the
handlers still take tenant_id from the query/body rather than the verified
principal -- both described in the PR body, neither fixed here.
bridge_provenance answers which source a field came from; it does not
answer what happened on the wire. 1p (the SIAX egress measurement point)
adds the robots decision + sha256 of the robots.txt it was read from,
body_sha256 per fetch, and blocked_soft detection — HTTP 200 whose body
is a block page, which otherwise gets scored as corroborating data.
Consistent with CLAUDE.md §1: a measurement point, not a bypass.
Documentation only; not in use, no worker routes through it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* ci: add typecheck/lint/test quality gate (was deploying unguarded)
* ci: make quality gate functional — typecheck + test, drop unwired lint
The lint step ran `pnpm run lint` = `eslint .`, but eslint is not a dependency
of this repo (absent from pnpm-lock.yaml), so the step failed on command-not-found
and, since a failed step aborts the job, prevented typecheck from ever running —
a red, useless gate on every PR. Replace it with the vitest suite (`pnpm run test`,
88 passing) so the gate matches the PR's stated 'typecheck + test' intent and is
green on a clean tree. Typecheck now uses `pnpm run typecheck` (carries the
`-p tsconfig.json` the script defines). Wiring eslint is left as a separate decision.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* docs: correct stale quality-check-service.ts comment references
Two source comments still cited src/services/quality-check-service.ts as an
existing precedent. It doesn't exist anywhere in this repo's history (git
log --all: zero commits, any branch, ever) -- confirmed during the earlier
full-doc-corpus accuracy pass. Points each comment at the real precedent
instead (document-extraction-service.ts / iceberg-write-service.ts).
Applied from task_05ec36aa, spun off during that pass.
* fix: 17 confirmed bugs from full-repo audit (edge + Python engine)
Found by an 8-finder bug hunt across app/api, src/services, src/clients,
frontend, the Python engine, the data layer, config/infra, and tests, then
independently adversarially verified per finding (32 raw findings -> 27
confirmed real bugs; 5 refuted). This commit applies the 17 confirmed as
safe, mechanical fixes -- no architecture/design decision required. The
other 10 need a decision and are reported separately, not touched here.
Most consequential, app/api/payments/[order_id]/deposit/route.ts:
the client-supplied Origin request header was trusted unsanitized both as
a server-side fetch target (SSRF) and as Stripe Checkout success/cancel
redirect URLs (open redirect tied to a real payment flow), and a failed
order lookup silently fell through to a hardcoded deposit amount instead
of 404. Fixed by reading the order directly via lib/orders-store.ts
(mirroring the sibling release/route.ts, which already did this correctly)
instead of both trusting the Origin header and self-issuing an HTTP fetch.
Also fixed:
- 5 routes (orders, orders/[id], estimates, subscriptions, ai/parse) that
500'd instead of 400ing on a literal JSON `null` body, since req.json()
resolves (not rejects) for that input and bypasses the try/catch guard.
- app/api/0pt/lakehouse/mappings/{,[mappingId]/{,writes/}}route.ts each
instantiated their own private service instances, so a mapping created
via POST was invisible to the sibling GET and writes routes even before
the separately-tracked dataset-registration gap. Extracted a shared
singleton module, src/services/lakehouse-mapping-registry.ts.
- app/api/0pt/documents/[documentId]/extraction/route.ts collapsed every
error (not-configured, policy-denied, policy-approval-required,
not-found) into a blanket 400; now status-mapped like the sibling
sources/[sourceId]/query route.
- app/api/vehicles/route.ts: plate query param wasn't URL-encoded before
being concatenated into the outbound RDW request, allowing extra query
parameters to be injected; added a strict allowlist + encodeURIComponent.
- app/api/uploads/route.ts: POST returned a fake 200 success with
put_url: null even when "configured", instead of continuing to report
501 until presign generation is actually implemented.
- src/adapters/document-intelligence-adapter.ts: the result-polling loop
never checked response.ok, so a non-2xx poll response (auth expiry,
429, 5xx) was silently retried until a generic timeout masked the real
error.
- app/order/[id]/page.tsx: finish() never cleared the production-simulation
setInterval, so it kept firing (and re-rendering) forever after delivery.
- app/admin/page.tsx: the metrics fetch had no response/shape validation
(unlike the adjacent orders fetch), rendering literal "undefined" stat
tiles on a malformed response instead of falling back to <ErrorState/>.
- services/api/workers/pipeline.py: `config.get("seeds") or DEMO_SEEDS`
used truthy-or, so an explicit empty seeds dict was silently replaced
with hardcoded demo org numbers instead of being respected as empty.
- services/api/workers/resolve.py: corroboration (confidence/agreed_sources
bump) no longer fires for two hits from the same source under fuzzy
match -- only a genuinely independent source now counts, per docs/06's
own ">=2 independent sources agree" definition -- and a merged record's
provenance array now actually includes the corroborating source's
fields instead of only a bare source-name string.
- services/api/orchestrator/main.py: create_order's int(...) on
unvalidated client volume/confidence fields raised on any
decimal-formatted input (int("92.5") -> ValueError); now coerced via
float() first with a safe default. /vehicles/{plate} and /screen also
now catch connector exceptions and degrade gracefully, matching
pipeline.py's existing pattern for the same connectors.
- lib/orders-store.ts: postgres.js (pinned v3.4.9) returns bigint/numeric
columns as strings by default; getOrder/listOrders/setStatus were
silently handing callers stringified money fields. Added a narrow
per-row coercion instead of touching the shared client's global type
map (which would affect unrelated tables' bigint/numeric columns too).
- lib/subscriptions-store.ts: the in-memory fallback returned
oldest-first while the Postgres branch explicitly orders newest-first;
now consistent, mirroring the identical fix already in orders-store.ts.
- app/api/auth/login/route.ts: DEV_LOGIN_KEY was compared with a plain
!==, a non-constant-time comparison; replaced with crypto.timingSafeEqual.
Verified before commit: tsc --noEmit clean; vitest run 88 passed, 1 todo,
0 failures (13 files, unchanged from before this commit -- no existing
test's assertions needed to change). services/api/workers/resolve.py's
source-novelty and provenance-merge logic additionally verified by direct
execution against synthetic same-source/cross-source inputs (no Python
test suite exists in this repo to add to). services/api/workers/pipeline.py
and services/api/orchestrator/main.py verified with python -m py_compile
(no httpx/fastapi installed in this environment to run them directly).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(engine): treat explicit seeds:null as missing to avoid 500 in run_job
Four-eyes follow-up to this PR's own seeds change. Switching from
`config.get('seeds') or DEMO_SEEDS` to `config['seeds'] if 'seeds' in config`
correctly stopped an explicit empty {} from being backfilled with DEMO_SEEDS,
but regressed on client-controlled `{"seeds": null}`: seeds became None and
the downstream `seeds.get(market, [])` raised AttributeError -> 500. That path
is reachable (POST /orders stores raw body.config; POST /orders/{id}/run passes
it straight to run_job). Using `config.get('seeds') is not None` keeps the
intended behaviour (explicit {} honoured, never silently seeded with demo data)
while restoring the None-tolerance the old idiom had.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* docs: full accuracy overhaul, grounded claim-by-claim in real code
Every doc in this repo (CLAUDE.md, README.md, SECURITY.md, docs/*.md) was
audited and rewritten by a multi-pass process: independent rewrite per
cluster, adversarial re-verification of that rewrite against the actual
code, then a final cross-doc consistency pass — each stage re-checking
claims against the repo directly rather than trusting the previous stage.
Two corrections matter most:
1. docs/ICEBERG_WRITE_BOUNDARY.md, docs/11-lakehouse-write-plan.md, and
docs/P0_QUERY_SOURCE_FLOW.md asserted src/services/quality-check-service.ts,
quality-report-service.ts, src/data-platform/services/catalog-service.ts,
and sql/002_p0_dataset_quality.sql/sql/003_*.sql as real, tested code.
git log --all across every branch shows zero commits for any of these
paths, ever. 0pt's data-quality/QA enforcement does not exist in code.
Corrected everywhere, with a new docs/P0_DATASET_QUALITY_FLOW.md added
as an explicit target-spec (not-yet-built) replacement.
2. github.com/sax3l/0pt (this repo) and git.siax.io/sax3l/0pt (Gitea) are
two diverged remotes sharing part of the same history (services/api/
workers/adaptive_router.py is byte-identical between them). Gitea's main
independently accumulated a WAF-bypass/proxy-rotation scraper stack in
three 2026-07-13 commits, since removed via a Gitea PR; GitHub's main
never had it. Which remote is canonical/deployed is unresolved — flagged
consistently across the corpus, not decided here.
Also newly and consistently documented: the tenant_id/project_id-read-from-
client-body gap on the P0 lakehouse/rate-policy/source-query routes (no
auth/session system exists anywhere in this repo yet); that every CL0UD/
N0D/AUD0/ST0RE platform client is real but mock-only (zero process.env
reads for any of their *_URL vars); that two P0 mutation routes
(lakehouse/mappings, sources/rate-policies) always fail over HTTP today
because their prerequisite entity can never be created via any route; a
schema bug in sql/004 (FK references to opt_datasets/opt_sources, which
don't exist); and that 0pt.app's live deployment was independently
confirmed (curled this pass) to be running this exact codebase with
DATABASE_URL/REDIS_URL unset in production.
New file: docs/12-edge-capability-api.md — the edge-native P0 TypeScript
capability contract (app/api/0pt/*, /api/screen, /api/ai/parse), previously
undocumented and distinct from docs/04-api.md's FastAPI-engine-only scope.
Verified before commit: tsc --noEmit clean; vitest run 88 passed, 1 todo,
0 failures (13 files) — docs-only change, no source touched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* docs: reflect PR #5's lakehouse-mapping-registry + status-code fixes
Follow-up to keep docs/12-edge-capability-api.md accurate once both PRs
land: fix/0pt-full-bug-hunt (#5) fixed the shared-instance bug in the
lakehouse mapping routes and added proper status-code mapping to the
document extraction route. This doc was written against the pre-fix
state in the same pass that added it, so it needs the same correction.
Does not claim the lakehouse write path fully works now -- the deeper
dataset-registration gap (no route ever calls DatasetService.registerDataset)
is unchanged and still blocks it end-to-end; only the shared-instance half
of the bug is fixed.
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
.env.example was referenced by README.md and required by docs/09-build-plan.md
(Phase 0) but never existed in the repo. Root cause: .gitignore's `.env*` rule
silently ignored it too, so `git add .env.example` was always a no-op even if
someone created it locally.
- Add .gitignore exception: `!.env.example`.
- Add .env.example covering every process.env.* read found in this pass
(grep-verified across app/, lib/, src/), organized by concern, each section
marked "wired in code today" or "documented target, not yet read by code"
per docs/08-infrastructure.md. No real values; matches repo convention.
Excluded ODOO_BASE_URL: only appears inside a comment describing a past
refactor away from module-level env reads, not an actual process.env read.
pnpm test: 88 passed, 1 todo, 0 failures (13 files, tests/**/*.test.ts).
tsc --noEmit: clean.
The 0pt data-engine build has been broken on main since 2026-07-08 (commit
c897311). document-capture-service.ts, document-extraction-service.ts,
source-query-service.ts, external-writeback-service.ts (and, more recently,
dataset-iceberg-mapping-service.ts, iceberg-write-service.ts,
source-rate-policy-service.ts) all import six platform client modules and two
registry services that were never committed: src/clients/{aud0,cl0ud,n0d,
st0re}-client.ts, src/services/{dataset,source}-service.ts, plus
src/types/{dataset,source}.ts. `next build` failed with 17 "Module not found"
errors, and every route under app/api/0pt/* returned HTTP 500 in dev with the
Turbopack compile error leaked straight into the JSON response body.
This adds the missing files, following the same fetch-based client shape
already established by src/clients/st0re-lakehouse-client.ts (thin interface,
HTTP implementation, deterministic in-memory mock used when no base URL is
configured) and the same "pure core, thin in-memory registry, AUD0-evented
mutation" shape the existing SourceRatePolicyService/DatasetIcebergMappingService
already use for DatasetService/SourceService. Each new client mock is
documented as a mock/degrade path, never a fabricated success, per this
repo's CLAUDE.md honesty doctrine.
Also wires up "typecheck" and "test" npm scripts (tsc --noEmit / vitest run)
and adds vitest as a devDependency -- previously `pnpm test` failed
immediately with ERR_PNPM_NO_SCRIPT even though 13 test files (~89 cases)
already existed under tests/p0/.
Verified: `pnpm run typecheck` clean, `pnpm run test` 88 passed / 1 todo
(pre-existing live-ST0RE-only skip), `pnpm run build` exits 0 (39/39 routes
compiled, only the pre-existing intentional optional `mssql` import warning
remains). Re-verified live on port 4097: GET /api/0pt/sources/rate-policies,
GET /api/0pt/lakehouse/mappings, POST /api/0pt/documents/capture, GET
/api/0pt/documents/[id]/extraction, and both GET/POST
/api/0pt/sources/[id]/query now return real application responses (200/400/405)
instead of HTTP 500 with a leaked compile error.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Re-runs this session's task spec against 0pt's actual repo state, which
turned out to already have a substantial, tested implementation from a
prior pass (35b6ec6): dataset<->Iceberg-table mapping, source rate policy,
and an ST0RE lakehouse client that was honestly marked "not live-verified".
This pass found api.st0re.siax.io IS reachable from this environment and
used it -- first via curl to safely discover the real contract, then
through 0pt's own updated code -- to drive a real namespace -> table ->
snapshot write against production ST0RE (task item 3's "write ONE first
test dataset" proof). That surfaced and fixed three real bugs the
mock-only pass could not have caught:
- ST0RE requires X-Tenant-Id/X-Actor-Id headers on every lakehouse route;
the previous HTTP client sent none. Fixed via a threaded
St0reRequestContext.
- IcebergTableInput.datasetId is a foreign key into ST0RE's OWN dataset
table, not a free-form string -- sending 0pt's dataset_id 400s live with
a Postgres FK violation. Fixed by no longer sending it; 0pt's
dataset<->table linkage lives entirely in DatasetIcebergMapping.
- ST0RE enforces a real unique constraint on lakehouse namespaces
(tenant_id, project_id, name) with no upsert route. Fixed with a
client-side lookup-or-create (listNamespaces + findOrRegisterNamespace).
Also closed a field-level gap against the task's literal schema spec:
0pt.dataset_iceberg_mapping.schema.v1 now carries namespace_id (a real
reference to ST0RE's LakehouseNamespace.namespaceId, not guessed) and
mapped_at, both additive alongside the existing namespace/created_at
fields (sql/005, JSON schema, types, service).
tests/p0/iceberg-write-service.realinfra.test.ts replaces the prior
it.todo with a real SEAM-pattern live test: attempts the real HTTP call,
skips honestly if ST0RE is unreachable, never fabricates success. It ran
successfully against production ST0RE this session (real namespace/table/
snapshot UUIDs logged in docs/ICEBERG_WRITE_BOUNDARY.md).
Two ST0RE-side cross-tenant read bugs were found and flagged (not fixed
here -- out of scope for this repo): GET .../namespaces ignores its own
tenant header and query filters; GET .../tables/:id doesn't check the
caller's tenant against the table's owner.
npm test: 155 passed, 0 failed, 0 todo (up from 154 passed + 1 todo).
npx tsc --noEmit: 1 pre-existing, unrelated error in
quality-report-service.ts (present before this pass, part of the wider
uncommitted P0 data-platform build already in this repo) -- not touched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Real, tested implementation of docs/11-lakehouse-write-plan.md's items 2-8
(the "future implementation session" that doc named): 0pt is now the
canonical writer of Iceberg tables through ST0RE's real, shipped
/v1/st0re/lakehouse/* table registry (sax3l/st0re commit 9ea0abb), never
persisting lakehouse table/catalog/snapshot state itself.
- src/types + src/services for 0pt.dataset_iceberg_mapping.schema.v1
(dataset -> ST0RE tableId reference) and 0pt.source_rate_policy.schema.v1
(the sourceRatePolicy ownership repair, moved out of N0D per ADR-0030).
- src/clients/st0re-lakehouse-client.ts: new client matching ST0RE's actual
camelCase route contract, with the same mock/HTTP boundary convention as
this repo's existing cl0ud/n0d/aud0/st0re clients. Honest caveat in its
module doc comment: not verified against a live ST0RE instance in this
pass (no reachable ST0RE_URL); every test exercises the deterministic mock.
- src/services/iceberg-write-service.ts: registers tables/snapshots through
ST0RE, requests N0D jobs only for heavy writes (never executes them
in-process), emits all four AUD0 event names
(dataset_iceberg_table_mapped / iceberg_write_requested /
iceberg_snapshot_created / source_rate_policy_created).
- sql/004, packages/schemas/*.json, app/api/0pt/lakehouse + sources/rate-policies
routes, tests/p0/*.test.ts (154 passed + 1 honest it.todo for the live-ST0RE
case, up from 142 baseline, 0 failures).
- Reconciled a real collision: an earlier untracked draft schema/test pair
(tests/p0/enterprise-data-os-expansion.test.ts) asserted a different,
now-superseded source_rate_policy shape; updated that one test block only.
- docs/ICEBERG_WRITE_BOUNDARY.md states the ownership boundary plainly and
is explicit throughout about what's real-and-tested vs. contract-only.
docs/11-lakehouse-write-plan.md gets a status-update section, not a rewrite.
- Data quality rules (quality-check-service.ts, quality-report-service.ts)
were not touched -- confirmed still 0pt-owned.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Points at n0d's ADR-0030 (Apache Iceberg canonical, Delta rejected for
P0/P1) and lays out 0pt's write-path plan: dataset-to-Iceberg-table
mapping, writes through ST0RE's future table registry, keeping QA and
source-rate/crawl-compliance logic in 0pt. Schema/event names specified
precisely for a future implementation session. Written adjacent to, not
touching, the substantial live P0 data-platform build in progress in this
repo (data-contract/dataset/quality-check services).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a private/proprietary license notice matching the convention used in
top0, and a vulnerability-disclosure policy with contact, scope, and
response-time expectations. No code changes.
Three new, tenant-agnostic P0 capabilities:
- 0pt.query_source: bounded, timeout/row-capped ad-hoc SQL query execution
against registered database sources, via a Fabric/OneLake adapter
(Azure AD credential-based mssql connection), with AUD0 audit events
and an app/api/0pt/sources/[sourceId]/query endpoint.
- 0pt.document_ocr (document capture + extraction): mailbox-based document
intake via Microsoft Graph, OCR/field extraction via Azure Document
Intelligence, with capture/extraction API endpoints and services that
never fabricate extracted values.
- 0pt.write_external_record (external writeback): a generic writeback-adapter
contract plus a concrete Odoo bills adapter that matches partners via
Fabric, backing an external-writeback-service for pushing records to
downstream systems of record.
Known interim gaps:
- src/adapters/local-blob-store.ts is an interim ST0RE substitute pending
the real ST0RE artifact-store integration.
- mssql is used by the Fabric/OneLake adapter but deliberately not yet
added to package.json (pending `pnpm add mssql` once the concurrent
session's package.json/dependency changes settle).
A separate, concurrently-active session has unrelated in-flight changes
in this same working tree (data-platform catalog/lineage/feature-store,
dataset quality, lakehouse/Iceberg docs, deploy scaffolding, package.json/
pnpm-lock.yaml updates, etc.) — none of that is touched or included here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Missing GET caused a 405 → r.json() threw → error state shown instead of
the calm 'storage not connected' info note. Now returns 501+[] so the page
properly shows storageOff and 'No files yet.' empty state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Values were stored as stable Swedish IDs ('Bolag', 'Engångs') but rendered raw.
Now resolved through entityLabel()/FREQUENCY_LABEL + t() at render time so
the recap panel respects the active locale on every re-render.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RouteReveal layout component applies in-view on every route change.
useReveal hook falls back to immediate rAF when element is already visible.
bestall/page.tsx: added missing useReveal ref.
katalog/[id]/page.tsx: DatasetView uses useReveal directly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- validera: role=tablist + role=tab + aria-selected + aria-controls/id
on tab buttons; role=tabpanel + id + aria-labelledby on content div
- katalog/[id]: same ARIA tab pattern; replaced incorrect aria-current=page
on tab buttons with role=tab + aria-selected
- error.tsx: i18n all hardcoded English strings via useT()
All three changes pass tsc 0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Light/dark adaptive 32x32 PNG icons + SVG icon + apple-touch-icon
registered in metadata so browsers pick them up automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- header: mark active page with aria-current="page" + highlight underline
- portal-shell sidebar: aria-current="page" on active nav link
- order-stepper: aria-current="step" on the active step
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- aria-label + aria-value* attrs on confidence range slider (screen readers)
- aria-label on volume number input
- aria-pressed on service/destination selector buttons in beskriv flow
- aria-label on describe textarea
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds metadataBase, og:title/description/url/siteName/type, twitter card,
and robots index/follow to the root layout for proper link previews when
0pt.app links are shared in Slack, email, or social.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Improves screen-reader and keyboard nav UX: filter pill buttons now
announce pressed state, catalog search input has an accessible label,
filter button group has role="group" with label.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ensures the keyboard skip-to-content link (added in layout.tsx) has a
valid anchor target on every page in the app.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dark mode toggle (●/○) in desktop + mobile header nav
- ThemeProvider wired in layout.tsx (class-based, light default)
- Header scroll-glass uses CSS color-mix for dark-mode correctness
- PortalShell main gets id="main-content" for skip-link target
- Portal order table: copy order ID to clipboard with toast confirmation
- Admin page: toast success/error on status advance
- Skip-to-content link in layout.tsx (sr-only, visible on keyboard focus)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- toast.success/error on order creation (bestall/page.tsx)
- toast.success/error on order approval (validera/page.tsx) + fix silent
success even when API failed (was always setting approved=true)
- toast.success/error on subscription creation (portal/subscriptions)
- toast.success/error on admin status advance (admin/page.tsx)
- toast.success on production page when Stripe redirects back with ?deposit=success
- layout.tsx: skip-to-main-content link (sr-only, visible on focus)
- app/page.tsx: id="main-content" anchor for skip link
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Catalog IDs: synced /api/catalog and /katalog/[id] to consistent English slugs
(nordic-companies, tech-decisionmakers, vehicles-fleet, financial-series,
buying-signals, sanctions-pep); View links from catalog page were all broken
- Added 2 missing catalog detail entries: buying-signals, sanctions-pep
- featured-products-section: link cards to /katalog/{id} instead of hardcoded demo URL
- hero-section: 'See a dataset' now links to /katalog/nordic-companies
- layout.tsx: mount <Toaster> (sonner was installed but never mounted)
- bestall/forskott: pay() tries Stripe checkout first, falls back to demo mode
- api/payments/[order_id]/deposit: Stripe Checkout Session creation (501 if unconfigured)
- app/not-found.tsx: custom 404 page matching mono design system
- philosophy-section: fix TS type error on PRINCIPLES array
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three missed files from the i18n pass:
- /katalog: FILTERS had hardcoded Swedish labels (Alla/Bolag/Beslutsfattare/Fordon/Finansiellt)
- /katalog/[id]: entire page was Swedish — CATALOG data, TABS (with variable-shadowing bug
where map((t) shadowed useT()), SectionHead, all 5 tab bodies, order rail, PROVENANCE_LEGEND
rendered as raw Loc object. Converted to stable TabKey enum + all Loc.
- /portal/subscriptions: useT imported but never called; title/labels/options all Swedish.
tsc 0 + build green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Converts every last bare-Swedish const array across landing sections
(STATS, TILES, CHAIN, PIPELINE, MODELS, DATASETS, PRINCIPLES, COLUMNS)
and shared components (data-artifact, provenance-badge) to {en,sv} Loc
objects consumed via useT(). Verified by per-file verify agent: 10/10
clean, zero remaining bare-Swedish display text. tsc 0 + build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All display labels in MARKETS/ENTITY_TYPES/FIELDS/ENRICHMENTS/FREQUENCIES/DELIVERY_FORMATS + SERVICES/FLOW_LABEL are now {en,sv} with stable IDs/values (pricing logic, EntityType/Frequency unions, config keys unchanged). Added entityLabel()/FREQUENCY_LABEL/FORMAT_LABEL. Updated all render sites (configurator, tjanster, beskriv, katalog/[id]) to resolve via useT(). Also fixed 2 pre-existing tsc nits (auth BufferSource cast, orders-store sql.json param type). tsc 0, build green. 0pt is now fully English-by-default with SV toggle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second hardened fan-out converted the 7 untouched admin/portal files; hand-finished the configurator (8 block labels + full summary column), portal dashboard, and describe-intake. English renders by default; SV toggle flips the whole app. A few tuple-array data labels intentionally left SV (no-reshape safety) + lib/data label i18n remain as follow-up. build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Locale provider (en default, persisted) + EN/SV switcher in header; converted header + landing sections + catalog/sources/order-flow/most-of-portal to useT({en,sv}). Fixed two partial conversions (forskott TRUST object/tuple map; kallor undefined ALL + raw object renders). tsc 0, build green. Remaining: admin/* + a few partials.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/admin/verktyg uses live /api/vehicles (RDW) + /api/screen (OpenSanctions); added to admin nav. Configurator now has a Tjänst selector that applies each service's price multiplier (lib/services) to the live estimate + summary; service_type carried into the order so orders-store prices by service. tsc 0, build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lib/order-client: createOrder records each order to a localStorage ledger; portal dashboard + admin queue merge it with /api/orders; order tracker falls back to it. Wired all buy paths: configurator, beskriv (custom services), and one-click catalog Beställ. Makes buy->monitor work in the browser now; becomes server-truth when Postgres is connected. tsc 0, build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lib/services (11 categories: data, scraping, mapping, normalization, consolidation->CRM/ERP, documents/OCR, enrichment, ICP, ML, BI, feeds across the 3 flows); /tjanster catalog; /bestall/beskriv NL intake; /api/ai/parse (engine LLM or heuristic NL->config); CRM/ERP destinations; Tjänster in nav. Sells every custom data service through the platform. tsc 0, build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Edge /api/vehicles (RDW NL, live now) + /api/screen (OpenSanctions, live now), both defer to engine when ENGINE_URL set. Engine: /vehicles/{plate} + /screen endpoints; workers/resolve (blocking+fuzzy entity resolution, now used by pipeline); workers/queue (in-process job queue); sources/financials (Companies House accounts + XBRL path). py_compile + tsc + next build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PRH/CVR upgraded from level-0 stubs to live open-API connectors and wired into the pipeline fetch + adaptive router; added RDW vehicle lookup (technical/fleet only, no owner PII) and OpenSanctions name screening. All compliant httpx, VERIFY-terms noted, graceful failure. py_compile clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>