server-setup.sh configured ufw and stopped there, which leaves SSH open
to unlimited password guessing and the kernel on defaults that are wrong
for an internet-facing host.
Adds three drop-in files, so a re-run replaces its own config and never
edits a distro file in place:
/etc/ssh/sshd_config.d/10-marketplaces-hardening.conf
password and keyboard-interactive auth off, root key-only,
no agent/X11 forwarding, MaxAuthTries 3, 30s login grace
/etc/fail2ban/jail.d/marketplaces.local
sshd, nginx-http-auth, nginx-bad-request; 5 in 10m, 1h ban
/etc/sysctl.d/99-marketplaces-hardening.conf
no redirects or source routing, rp_filter, SYN cookies,
forwarding off, restricted kernel pointers and dmesg
Both accounts on the host are key-only by construction - the deploy user
is created with no password at all - so disabling password auth cannot
lock anyone out. It only closes guessing against a credential nobody
intended to exist.
The sshd block runs `sshd -t` first and removes its own drop-in if the
test fails. A bad sshd config that takes effect on a remote box is how
people lock themselves out permanently.
DEPLOYMENT.md §3.2 documents all three plus the post-provision checks.
Not copied from the reference implementation: its hardcoded server IP.
Kept as-is because ours is already better: add-domain.sh pre-checks the
DNS A record and runs nginx -t before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The VK-only scaffolding had a shape problem worth fixing before anything
was built on it: completeCallback(code, codeVerifier) took the PKCE
verifier from the client, which forces the browser to generate and hold
it. We are a confidential client - a browser-held verifier buys nothing
and adds a place to steal it from.
Replaces the four vk-id-* files with a provider-agnostic surface:
getAuthorizeUrl(provider, returnTo?)
listIdentities()
unlink(provider)
completeCallback is gone entirely. The backend mints and stores state and
code_verifier single-use for 10 minutes, handles the provider's callback
itself, issues the session cookie and redirects. VK and Yandex differ
only in a path segment, because everything that actually differs between
them - PKCE handling, VK's device_id, Yandex's Basic-auth exchange -
lives backend-side.
vk-id-login becomes social-login-button with a provider input; adding
Yandex to the UI is an input value, not new code. Adds yandex_id to
ExternalIdentityProvider, plus optional email/phone/displayName since VK
frequently returns no email.
social-identity-gateway.spec.ts (5 tests) asserts the requests carry no
code_verifier and no client_secret, so reintroducing a browser-held
verifier fails the build rather than passing review.
PHASE-8 §2 rewritten to match: the four endpoints, backend-owned state
and verifier, UNIQUE (provider, providerUserId) with conflict routed to
controlled resolution rather than a silent rebind, per-tenant OAuth app
config under the Track S §4.2 envelope, and both providers' full endpoint
sets. Two things recorded there because they are expensive to discover
later: VK's callback returns device_id alongside code and the token
exchange fails without it, and both providers validate redirect_uri
against an exact registered list - which a multi-tenant platform cannot
satisfy without a central identity host (FH-0.1, still undecided).
256 tests pass. Build green, boundaries and cycles green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Writes the 14 harvested mechanisms from FORK-ANALYSIS-2026-08-21.md into
the backend contracts. Each section is dated 2026-08-21 and tagged FH-*
so any wording traces back to why it is worded that way.
The through-line: several contracts stated correctness as behaviour
("the webhook must be idempotent"). Behaviour written as an if-statement
gets deleted by a refactor and the failure mode is a double charge. These
sections restate it as schema and mechanism.
PHASE-3 3.1 conditional-write reservation, 409 on zero rows, cart-wide
rollback, 15 min TTL
3.2 InventoryMovement append-only journal with resultingAvailable
6 bulk import idempotent by SKU, rollback while unsold
6a digital code pools, revealed only when paid
PHASE-7 5 unique constraints for payment idempotency and webhook
replay, insert-first handling, signature over raw body,
24h poll as reconciliation not primary
TRACK-S 2.1 session model - 32 bytes stored as SHA-256 only, HttpOnly,
one cookie per contour, Argon2id params, mandatory TOTP
2.2 origin allowlist ahead of routing on every cookie mutation
4.2 AES-256-GCM envelope for stored secrets, HMAC fingerprints
8a order manager as a separate contour, scoped by membership
rows rather than by configuration
PHASE-9 5.1 revision immutability, version = max+1, pointer flipped
in-transaction, operational state does not travel
5.2 clone carry / no-carry list, inventory to zero
5.3 signed read-only preview, non-GET 404s while previewing
6 host normalization, verifiedAt required, cache invalidation
PHASE-10 3a server re-runs the editor's validation, clamp-and-fallback
PHASE-2 3.1 order publicToken, snapshot completeness, never updated
FH-2.12 rejected on the merits: our marketplace lifecycle state machine
is richer than theirs, adopting it would be a downgrade. Recorded in the
TODO so it is not raised again.
Also adds BACKEND-HANDOFF.md sections 0 and 0a - nine falsifiable
invariants as a release gate, each cross-referenced to the contract that
specifies it, plus PR and release discipline. And ADR-0006 recording what
we take, what we reject, what we keep because ours is better, and the
organizational question it deliberately does not settle.
No implementation changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bundle budget was warning-only: initial warning 700 kB, error 1.8 MB.
Measured today the initial bundle is 1.55 MB raw / 324.58 kB transfer -
up from the 1.15 MB measured on 11 August, so it had been growing with
nothing to stop it.
Lowers maximumError to 1.6 MB. That is a ratchet, not a target: just
above today's size so the bundle cannot grow, with the 700 kB warning
left in place as the goal. Lower it each time the number comes down.
Adds scripts/ci/scan-bundle.sh (npm run scan:bundle), run in CI after
the build. Seven patterns: both provider auth headers, the partner ID
shape, client_secret, private key blocks, AWS keys, Telegram bot
tokens. The legacy payment code that put credentials in the browser is
already deleted; this is what stops it coming back. Verified in both
directions - clean against the real dist, exit 1 against a planted
credential.
Measurement also corrected two assumptions recorded in the harvest
TODO: admin and editor code is already lazy-loaded, so the initial
bundle is main alone rather than a deployable-split problem; and mock
gateway fixtures do reach production chunks, which is now filed as
FH-E.6 with the cause identified.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
detectLocation() fetched http://ip-api.com over plaintext from an HTTPS
storefront. Browsers block mixed active content, so the request never
completed and auto-detect only ever took its error branch - region
detection has been dead in production, not merely insecure. The attempt
also handed every visitor's IP to a third party from the page itself.
Geo now resolves through the tenant API at {baseUrl}/geo/resolve, the
same base /regions already uses. The server reads the client IP; the
browser sends nothing and receives no third-party payload.
The endpoint is specified in BACKEND-API-REFERENCE.md and is not built
yet. Until it ships the client falls back to the manual region picker -
identical to the behaviour production already had.
Adds location.service.spec.ts: geo goes to the tenant API, no request
leaves that origin or uses http://, failure degrades to the manual
picker, and detection is not retried once attempted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verified zero remaining callers for each before deleting (grepped
src/app for every method name individually), not assumed from the
earlier commit's dead-code note.
Deleted from api.service.ts:
- createPayment() - legacy direct QR creation (POST {qrBaseUrl}/qr)
- createCartPayment() - legacy /cart payment creation, client-sent amount
- createPaymentIntent() - superseded by @marketplaces/payment's gateway
- checkCartPaymentStatus(), checkCartCardPaymentStatus(), checkPaymentStatus()
- legacy QR/card status polls, superseded by the same gateway
- resolvePaymentQrId/resolvePaymentQrUrl/resolvePaymentLink/
resolveBankPaymentUrl - QrCreateResponse field-normalization helpers,
no longer had a caller once the methods above were gone
- Types: QrCreateRequest, QrCreateResponse, CartPaymentRequest,
PaymentIntentRequest, QrDynamicStatusResponse
- Fields: qrBaseUrl, cartPaymentPartnerId - no longer read by anything
- Imports: HttpHeaders, environment - no longer used in this file
Did NOT touch createOrder() or createCheckoutSession() - both still have
live callers in cart.component.ts, confirmed before deciding what to keep.
cart.component.ts: corrected the createPaymentIntent() comment, which
referenced the deleted method/helper names, to name what actually got
deleted instead of what was merely "dead as of that commit."
docs/backend/FRONTEND-API-SURFACE-COMPLETE.md: moved the 4 now-deleted
QR/card endpoints out of the "still live" legacy table into a dated removal
note - the doc's own premise is "every endpoint this codebase currently
calls," so it was wrong to leave them listed as called once they weren't.
Legacy-undocumented count corrected 15->11, total 97->93.
Verified: production build succeeds (no dangling references), 247/247 unit
tests, arch:check clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deep analysis of marketplaces-main.zip (hub.numus.cc/numus/marketplaces).
Findings:
- Not a fork of us. Separate platform monorepo (NestJS + Postgres +
2 Angular apps + infra) sharing an older dexarmarket ancestor.
- Our repo is vendored inside it as reference/parallel-frontend/,
SHA-256 pinned, dated 11 Aug 2026, classified "reference, not production".
- Zero VK/Yandex/OAuth code anywhere in their source. Their only
customer login is Telegram, proxied to an external service.
- They lead on backend truth and ops; we lead on frontend depth,
tests, e2e, and framework currency.
Three of their audit findings are still live in our code and are
defects, not just posture: plaintext ip-api.com call from an HTTPS
origin (mixed content, region detect silently dead), unvalidated
bypassSecurityTrustResourceUrl on a bank URL rendered in an iframe,
and provider credentials plus a partner ID literal in the bundle.
Adds:
- docs/FORK-ANALYSIS-2026-08-21.md - full comparison, their audit of
us assessed line by line, and a VK ID + Yandex ID design.
- docs/superpowers/specs/2026-08-21-fork-harvest-design.md - working
brief, five lanes, four waves, scope and rejection rules.
- docs/FORK-HARVEST-TODO.md - 42 items with effort, dependencies and
acceptance criteria. Improvements only; nothing regresses our
Angular version, test count, or architecture governance.
No implementation changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Direct question caught a real gap: "everything is there? payment auth?"
The census only grepped src/app/ - auth moved into the external
@marketplaces/auth package this session, and its HTTP calls were never
captured. Seven real endpoints were silently absent from a doc that called
itself "complete":
- 3 Telegram QR/session endpoints (POST/GET/DELETE .../users/sessions) -
live today, customer and admin login share them, which is exactly why
every admin endpoint must independently verify authorization server-side
- 4 ed25519 admin challenge/response endpoints - specified in
BACKEND-HANDOFF.md §3 and the package's own auth-api.model.ts, but not
built server-side. Client shows backend-unavailable until they exist.
Added §0.3 stating plainly what actually connects auth to payment: there is
no separate payment login. Checkout, order pricing (§20), and partner
credentials (§16) each ride on whichever of the two sessions above is
active, or on the partner API's own separate signed-request auth (§6 of
that contract - unrelated to Telegram/ed25519, already built, not a gap).
The real payment gap is §1 (QR/card creation and polling, undocumented
anywhere), not auth.
Counts corrected: 51->54 specified, 90->97 total. Added item 0 to the
action list, ahead of everything else: the ed25519 endpoints are the single
most serious open issue named anywhere in docs/backend/, and every other
item on the list assumes a working admin session to authorize against.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Updates FRONTEND-API-SURFACE-COMPLETE.md to cover everything built after it
was first generated - this is the final version for this pass, not a
snapshot mid-way through.
Added, none of which existed in the doc before:
- §18: the 4 marketplace revision endpoints (create draft/validate/
publish/rollback) - built this session, missing from the census entirely.
Flags the same draft/validated/preview/published ambiguity already
documented in the model itself, so backend sees it without having to
read source.
- §19: RoutingContext as an optional field addition on GET
/api/admin/v2/orders/{id} - not a new endpoint, a response-shape ask.
- §20: order pricing-breakdown fields (unitPriceMinor, lineTotalMinor,
priceSnapshotId, discountMinor, fxQuoteId, deliveryMinor) feeding the new
total-formula panel. States plainly that the panel shows nothing rather
than a wrong number while these are absent.
- §21: 8 optional dashboard-metrics fields (GMV, conversion, moderation
queue, etc.) - explicitly labelled a genuine ask, not a confirmed
contract, since no spec exists for this endpoint at all (§15).
- §22: states directly that the frontend's new double-click guard does not
replace backend idempotency enforcement and was never meant to - closes
one UI race, does nothing for a retried request or a duplicate webhook.
Counts updated (47->51 specified, 86->90 total; +3 response-shape asks that
aren't new endpoints). Closing section states the one item genuinely
blocked pending a live backend (F60, the full acceptance-path E2E) so
nobody mistakes "frontend backlog complete" for "nothing left to build."
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Generated directly from source (every this.http.get/post/patch/put/delete
call across core/, features/admin/, api.service.ts) rather than written
from memory - a census, not a design doc.
47 already match an existing Phase/Track contract exactly. 24 are inferred
from this codebase's own REST conventions with no contract doc stating them
- each flagged in source at its call site, not just in this doc, so backend
sees the reasoning next to the code. 15 are legacy endpoints
(/category, /cart, /qr, /websession, ...) with no contract anywhere,
still live today.
Biggest concrete gap surfaced: three full admin domains (transactions,
monitoring, moderation) have real UI and real gateways calling
/api/admin/v2/{resource} by convention, with zero backend contract written
for any of them.
One real bug found and fixed while building this, not just flagged:
connector-api.gateway.ts's replay() called
POST /api/admin/v2/integrations/dead-letter/{id}/replay, omitting the
{connectorId} segment the contract's own path requires
(PHASE-4-CONNECTOR-FRAMEWORK-CONTRACT.md §7). Fixed the interface, both
gateway implementations, and the doc entry in the same pass - no callers
existed yet, so this shipped without ever being exercised by a UI.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adding a domain was a manual per-domain script run. With domains arriving
continuously that does not hold, so certificate issuance is now automatic.
HTTP already needed no work: the nginx catch-all serves any Host and the SPA
resolves its tenant from that header. Only TLS needed a name-by-name step.
Two mechanisms:
- setup-wildcard-tls.sh issues one DNS-01 wildcard for *.<apex>, after which a
new tenant subdomain is live over HTTPS with zero certificate work.
- sync-domains.sh reconciles tenant-owned domains against a desired list on a
10-minute timer: issues what is missing, skips certificates with >30 days
left, skips names already covered by the wildcard, waits out unpropagated
DNS, and caps issuance per run so a bad source cannot burn the weekly ACME
budget.
Safety properties worth stating: a failed fetch of the desired list aborts the
run rather than reading as "remove every domain"; removing a domain disables
its server block but keeps the certificate, so re-adding is instant; malformed
hostnames are rejected before reaching certbot or an nginx server_name.
The source is pluggable - a file today, the Phase 9 domain registry once it
exists, whose MarketplaceDomain statuses already match what this needs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was no CD: pushing to main deployed nothing, and deploys were a manual
copy onto the server. This adds the missing half.
- .github/workflows/deploy.yml - build, upload to a per-commit release
directory, swap the symlink atomically, reload nginx, verify over HTTP.
The swap only happens after the upload is verified to contain index.html,
so a failed deploy leaves the previous release serving.
- scripts/deploy/server-setup.sh - idempotent one-time provisioning: nginx,
certbot, ufw, and a key-only deploy user whose sole sudo right is
"systemctl reload nginx".
- scripts/deploy/add-domain.sh - per-domain server block plus TLS issuance,
run once a domain's A record resolves to the server.
- docs/DEPLOYMENT.md - setup order, required CI secrets, rollback, limits.
Also adds .gitattributes: the shell scripts were being checked out with CRLF
endings, which makes bash fail on the shebang line on Linux.
Host keys are pinned via DEPLOY_KNOWN_HOSTS rather than trusted on first use.
No credentials are committed; all four deploy secrets are supplied by CI.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A partner integration request landed for programmatic merchant-hierarchy
management (Company/Project/Store/PaymentPoint). Built the answer generically:
partner-specific behaviour is a PartnerProfile config row, and no partner name
appears in any entity, field, endpoint or status value.
New:
- docs/backend/PARTNER-PROVISIONING-API-CONTRACT.md - hierarchy, idempotency,
node-scoped public-key credentials, TEST/LIVE partition, routing context
- docs/context/adrs/ADR-0003-generic-partner-provisioning-api.md
Amended, because the schema impact must land before Phase 1 is implemented:
- Phase 1 gains RoutingContext on CheckoutSession/PaymentIntent/Payment,
frozen at checkout-session creation and immutable after
- Phase 7 gains routing on Refund/ReconciliationRecord, plus the rule that
seller settlement splits happen after routing, never as a hierarchy level
- Phase 9 gains Company/Project above Marketplace and PaymentPoint below it,
with a backfill sequence for existing marketplaces
- Track S gains partner credentials: public key only, node-scoped authority,
rotation with overlap, immediate revoke, audit coverage
Also: Track P (P1-P10) in the delivery plan, and backend ownership closed as
answered across the contract set.
Card payment was checked, not added - qr and card both already ship in
cart.component.ts with separate create paths and status pollers.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Verdaccio registry introduced earlier is unreachable from CI (listens on
127.0.0.1:4873 behind a firewall allowing only 80/443/SSH), which broke the
architecture-governance workflow - its npm ci step could no longer resolve
@marketplaces/auth.
Packages are now published to git release branches (release/auth,
release/payment in vitanovaPackages) whose root is the package itself, and
installed with git+<repo>#release/auth. No registry, token, tunnel, or CI
secret - anonymous git read is enough.
- package.json: git dependency; .npmrc removed (no scope mapping needed)
- vitanovaPackages release.yml rebuilt to force-push release branches
- ADR-0001 amended with the distribution change and why the registry lost
- BACKEND-HANDOFF: added the multi-tenancy section (hostname -> tenantKey ->
per-tenant bootstrap config), corrected the install and deploy notes, and
recorded that no CD pipeline exists
- PACKAGE-EXTRACTION / PACKAGES-USAGE rewritten for the git-branch flow
Verified: npm ci, arch:check:boundaries, ng build, 103/103 tests, all with
no credentials configured.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- docs/backend/BACKEND-HANDOFF.md: single entry point for a backend dev -
reading order, verified infrastructure state (nginx running, Postgres
inactive, no API on :8080, no TLS, no DNS automation, no CI runner),
auth surface, and the day-one setup that is still outstanding
- docs/PACKAGES-USAGE.md: install, required DI providers, full exported
API for both auth mechanisms, and how to ship a package change
- PACKAGE-EXTRACTION.md now covers build/release/infra only and points at
the usage guide; CI section reflects the two real workflows
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Stood up Verdaccio (Docker, on the dev server) as a private npm registry
since no public registry/NPM_TOKEN exists yet. Published @marketplaces/auth
and @marketplaces/payment there, removed the local packages/ staging copy
from this repo, and switched marketplaces to install @marketplaces/auth
0.1.0 as a real npm dependency through the registry.
- .npmrc scopes @marketplaces to the Verdaccio registry (no token committed;
each installer/CI supplies its own via npm login or an env-injected token)
- Verified: fresh npm install, ng build, arch:check:boundaries, and full
test suite (103/103) all pass against the registry-installed package
- Registry is reachable only via SSH tunnel today (firewall allows 80/443/
SSH only); public/CI access is a follow-up decision, documented in
docs/PACKAGE-EXTRACTION.md
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- ADR-0001: decision to extract auth/payment into shared @marketplaces/* packages
- Scaffold packages/auth, packages/payment; @marketplaces/auth now holds the real
telegram (customer+admin QR/session) and ed25519 (future admin challenge/response)
auth implementation, pushed to sources.vitanova.network/sdarbinyan/vitanovaPackages
- Rewire ~30 call sites to import from @marketplaces/auth; delete migrated originals
from core/auth, core/admin-auth, services/, models/
- Replace environment coupling with AUTH_API_URL/TELEGRAM_BOT_USERNAME injection
tokens and isDevMode(); wired as file:packages/auth pending registry publish
- Add TRACK-S §8: bootstrap per-marketplace admin login + marketplace-scoped
sub-admin invite/role endpoints
- Build, arch:check:boundaries, and full test suite (103/103) all green
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Track A: analytics event pipeline (traffic/catalog/commerce/operational/
quality events), synthetic-traffic separation enforced server-side by
environment/token, never a client-settable flag.
- Track S: 17-role/3-scope RBAC enforcement, audit log, secrets, rate
limiting, step-up auth - closes this session's most serious finding
(admin role model is decorative today, any authenticated admin has
full access regardless of assigned role).
- docs/backend/README.md: index of all 12 contract docs (Phases 1-10 +
2 tracks) in build order, plus what's deliberately excluded (namespace
migration, per-connector adapters, extra payment providers) and the
one thing still genuinely open across all of them - backend ownership.
- Cross-linked from BACKEND-API-REFERENCE.md and the delivery plan so the
index is discoverable from either entry point.
This closes out documentation for every phase/track in
PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md that doesn't require a further business
decision. Nothing left undocumented on our side pending only implementation
and backend-ownership assignment.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Phase 8: Customer/ExternalIdentity/ContactChannel, VK ID OAuth 2.1/PKCE
built first per Sprint 0.1 ("do all after vk"), then OTP, then MAX/
Telegram bot linking, then the Notification Orchestrator + Delivery
Conversation State Machine. Hard rule carried through: bots never touch
financial statuses, only delivery fields via a dedicated Delivery Service.
- Phase 9: Marketplace/MarketplaceDomain/MarketplaceFeatureSet/
MarketplaceRevision, full Hostinger DNS automation sequence (snapshot
before change, never touch MX/SPF/DKIM/DMARC/CAA), lifecycle state
machine that must expose its own blocker on every transition, publish
model with orders/payments/inventory explicitly excluded from revisions.
- Phase 10: Gorbushka-class content entities (Shop/Service/Floor/
SchemePin/RentListing/News/Lead/MallSettings), explicitly lowest
priority and gated on Commerce Core being real first.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Phase 5: Seller Portal from scratch (zero backend bytes exist today) -
SellerOrganization/SellerUser/SellerMarketplaceMembership, all endpoints
scoped server-side to the unified-orders Fulfillment model from Phase 2.
- Phase 6: server-owned Cart/CartLine/CheckoutSession, extending Phase 1's
server-authoritative-amount contract into the cart itself. Replaces
localStorage/Telegram-CloudStorage cart persistence.
- Phase 7: Refund and ReconciliationRecord entities, settlement contract.
Flags additional payment providers (wallets/BNPL) as still an open
business decision - not blocking, schema is provider-agnostic already.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Continues the Phase 1 contract doc with the same wire-contract-only style.
All three build on Sprint 0.1's answered decisions - no further business
input needed to start implementation once backend ownership is confirmed:
- Phase 2: canonical Order/OrderLine/Fulfillment/OrderEvent per the unified
multi-seller decision (one Order, per-seller Fulfillment groups), event
bus, Notification Center contract.
- Phase 3: Product/Offer split, InventoryRecord, publish-time executability
validation (the mechanism behind "no branch may distinguish an inspector
from a normal buyer").
- Phase 4: generic config-driven connector framework per the "no fixed
marketplace list" decision - onboarding a new partner is configuration
against a fixed pipeline, not a bespoke integration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User answered 8 of 9 Sprint 0.1 blocking decisions (2026-08-17); backend
ownership stays open pending a clearer re-ask. Recorded and propagated:
- Payment chain unfrozen -> BACKEND-API-REFERENCE.md §7 and the Phase 1
contract doc's status banner both updated; Phase 1/6/7 unblocked.
- No fixed external-marketplace list -> Phase 4's connector framework
respecified as config-driven/generic; Sprint 4.2 retired as "per named
marketplace," replaced with a generic onboarding runbook.
- FX rate source: ours, in-house, as the default (not just a fallback) ->
Phase 1 contract's `source` field can read "internal" as the normal case.
- VK ID before OTP -> Phase 8 sprints resequenced (VK ID now 8.2, OTP 8.3).
- Multi-seller orders: unified -> Phase 3.3, Phase 5.2, and Z16 updated to
the resolved model (one Order, per-seller Fulfillment groups).
- "Fixed 5-second payment" claim: confirmed non-issue, PAYMENT_POLL_INTERVAL_MS
is already 5000 (real polling cadence, not an artificial delay).
- API namespace: new endpoints only (/api/v2/...), no forced migration of
legacy endpoints.
- Document version: v3.1 is canonical.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- models/category.model.ts: Category -> CategoryApiModel, disambiguated
from core/categories/models/category-domain.model.ts's Category (admin
domain shape). Removes a dead unused import in item.utils.ts along the
way. Only live consumer was services/api.service.ts, updated in place.
- BACKEND-API-REFERENCE.md §5: corrected two rows documenting the
TOKEN_EXPIRED/INVALID_SIGNATURE auth-error bug as still open - the fix
(reading error.error.code before falling back to HTTP status) is
already in auth.service.ts. Doc was stale, not the code.
- Sprint 0.2 audit: AdminRole duplication and the
PRODUCT_DATA_PROVIDER/CATEGORY_REPOSITORY dead mock branches were
already resolved in a prior pass - verified, no code change needed.
- docs/backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md: new wire contract
for Money/FxQuote/PriceSnapshot/payment state machine, so backend can
start Phase 1 the moment the frozen payment chain is unblocked.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds InjectionToken + factory for Orders, Products, Users, Transactions,
Monitoring, Moderation (mirrors existing Categories/Dashboard pattern) and
repoints their facades plus the derived Analytics/Customers facades and
admin-order-watcher off the mock LocalGateway class directly. No behavior
change today - still resolves to the mock - but a real backend can now be
bound per domain with zero facade edits.
Docs: full gap analysis of Product Plan v3.1 against current repo state,
and a phased delivery plan (10 phases, 34 sprints, 5 tracks) breaking every
identified gap into scoped, sequenced work.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Removes all tracked repo documentation (root status docs, docs/,
docs/architecture/foundation/**, docs/archive/**, docs/context/BACKEND-AUDIT.md
+ adrs, src/assets/mock/README.md) and replaces it with:
- GAPS-AND-IMPROVEMENTS.md — role-based findings (user, PO, QA, backend,
accessibility, engineering) plus automated code-review passes over the
storefront and backoffice, each with file:line references. Findings only,
no fixes applied.
- BACKEND-API-REFERENCE.md — single consolidated backend contract: auth
(both mechanisms), bootstrap, pagination/sorting/filtering conventions,
error model, every live/mock-only endpoint with JSON examples, and the
admin-domain DI-token seam gaps.
Open items and unresolved decisions from the deleted docs (KNOWN-ISSUES,
PRODUCT_BACKLOG, SPRINT-PLAN-NEXT, Seller-Management audits, etc.) were
harvested into the two new files before deletion, not lost.
CLAUDE.md/AGENTS.md/GEMINI.md/.claude/ and docs/context/{INDEX,LOG,
MAINTENANCE,README}.md are untouched — confirmed gitignored, never part of
git history, outside this cleanup's scope.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Third attempt, done properly this time - first two were reverted
(one stopped cleanly on real conflicts, one botched sequencing and
deleted the old focus-trap before finishing the swap).
DialogComponent gains closeOnEscape/closeOnBackdropClick (default true,
backward-compatible with its 13 other call sites) and ariaLabel (for
dialogs with no visible title header). FOCUSABLE_SELECTOR now includes
iframe for the bank-payment panel's focus trap.
Cart wires closeOnBackdropClick=false on both dialogs (in-flight payment
shouldn't cancel on a stray click) and closeOnEscape tied to the bank
popup's open state, so Escape closes the nested bank iframe first and
falls back to the QR view - matches the original priority exactly.
Original geometry (500px QR modal/40px padding, 960x760 bank modal/
56-16-16 padding, both mobile breakpoints) preserved via :host ::ng-deep
overrides scoped per dialog instance - same pattern already used by
product-carousel-widget.component.ts.
cart.component.ts loses ~90 lines of hand-rolled ViewChild/HostListener/
focus-trap code - app-dialog owns all of it now.
Verified live in browser: dialog sizing/padding/aria-label correct at
mobile+desktop, backdrop-click confirmed inert, Escape-priority confirmed
(bank closes first, then QR), initial focus lands on close button.
83/83 tests pass, tsc/build clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@lucide/angular shipped its entire ~1500-icon set in the initial bundle
despite the app only using 85 named-imported icons - confirmed upstream
tree-shaking failure (sideEffects:false, clean named imports, single
non-splittable fesm file). Replaced icon-registry.ts/icon.component.ts
with hand-rolled inline SVG rendering of just the 85 used icons,
transcribed from lucide's own node data for pixel-identical output.
Zero call-site changes - AppIconName and app-icon's public API unchanged.
Also: karma-coverage wired (npm run test:coverage), baseline captured
in docs/SPRINT-PLAN-NEXT.md (32% statements / 18.5% branches).
Cart-modal -> app-dialog migration was attempted and reverted - real
conflicts (backdrop-close, nested-modal escape priority, iframe sizing),
documented in docs/FUTURE_FEATURES.md for a properly scoped follow-up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
11MB of the ~14MB initial bundle is the full @lucide/angular icon set
despite clean named imports for ~85 icons - confirmed by build inspection,
previously undocumented (existing note only covered the two lazy chunks).
Root cause is upstream tree-shaking, not app code. Real fixes (package
upgrade or dropping the dependency) need sign-off before touching.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sprint G: audited every BootstrapConfig field for a real runtime consumer
(docs/DEAD-CONFIG-AUDIT.md). Wired 3 previously-dead editable fields:
footer.logoUrl, company.address.street/contacts.phone, catalog.suggestionsEnabled.
Remaining dead fields needing a business/design decision tracked in
PRODUCT_BACKLOG.md/KNOWN-ISSUES.md, not silently left.
Sprint H: 6 new spec files (test count 57 -> 83), covering ProjectEditorFacade
(undo/redo, draft persistence, publish gating), AdminAnalyticsFacade
(never-fabricate-a-number contract), and regression coverage for this
session's carousel/hero/profile-toggle fixes.
Sprint I: widget settingsSchema (declared in widget-manifest.json, never
validated) now enforced via a new lightweight schema check in
ProjectValidator, surfaced through the existing issuesByField pipeline.
Same check reused in diagnostics so editor and diagnostics can't disagree.
Verification: tsc clean, ng build clean, 83/83 tests pass, barry-cache
validate clean (2 pre-existing unrelated warnings only).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sprint E: homepage section editor now filters the layout-strategy picker
to each widget's widget-manifest.json supportedLayouts instead of always
showing all 5 strategies. columns field gated to widgets that read it
(hero, product-collection carousel).
Sprint F: closes client bug report (no items-per-page control, hero
carousel not manually/automatically scrollable, no 1-2 slide big-carousel
option). Product carousel item width now driven by layout.columns
(reused, was already editable but dead). Hero widget gains prev/next
arrows, touch swipe, and 1-2 panel mode via the same field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sprint A: storefront header profile control (login/logout only, no menu),
wired to existing customer Telegram auth (AuthService).
Sprint B: backoffice/reports page, reuses AdminAnalyticsFacade (Sales,
Top Products, Marketplace Health cards + CSV export).
Sprint C: backoffice/settings page, admin UI density preference
(comfortable/compact), localStorage-persisted, applied to app-table
across all admin list pages.
Sprint D: admin bottom-nav Help -> mailto using existing supportEmail,
Documentation -> external link via new TenantConfig.documentationUrl.
AdminNavLink gains externalHref for non-routerLink nav entries.
Docs: docs/GLOBAL-SPRINT-PLAN.md tracks the full sprint breakdown.
docs/COMING-SOON-AUDIT.md removed, folded into docs/KNOWN-ISSUES.md.
docs/BACKEND.md updated with the new documentationUrl bootstrap field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Principal-architect-level review of the entire Seller Management body
of work (7 prior docs + all touched code), verified against fresh
tsc --noEmit and arch:check runs, not recalled from memory.
Real bug found and fixed (in scope per this mission's "unless
absolutely required" carve-out - a one-line correctness fix to
already-committed code, not new feature work):
AdminSellerManagementPageComponent.sellerManagementEnabled read the
bootstrap snapshot once via a plain signal() at construction, not
reactively via bootstrapRevision() the way UiRuntimeFacade/SeoService
both correctly do elsewhere in this codebase. Fixed to computed() keyed
on bootstrapRevision(). Currently invisible (flag is always false,
signal was never even read in the template) but would have gone stale
the moment bootstrap ever reloaded with the flag true. tsc clean after
the fix.
Findings documented in Seller-Management-Final-Design-Review.md (no
Critical/High severity found anywhere):
- Medium: SellerConfig (bootstrap wire shape) and Seller/SellerBranding
(domain entity) are two unreconciled type hierarchies for the same
concept - self-flagged already in BACKEND.md SS11.6, restated here as
an independently-confirmed finding rather than letting it drift.
- Medium: no reusable capability-guard abstraction exists anywhere in
the codebase, despite ADR-009/ADR-011 both prescribing "check the
flag in one place" - ADR-009's own described FeatureFlagService was
never built. Fine with one consumer, a real drift risk the moment a
second one needs the same check.
- Medium: the flag's true branch has never been exercised, even
manually - every verification claim in this whole body of work was
tested at the flag's real value (false).
- Low/nice-to-have: sellerId typed as bare string instead of the UUID
alias used everywhere else in the new sellers domain; MarketplaceRef
vs TenantConfig overlap (deliberate, documented, but worth watching);
documentation-to-code ratio (8 docs, zero backend bytes) carries a
consolidation-burden risk, especially the Unified/Split-Orders
question restated independently in 4 different docs.
- Explicitly checked for and did NOT find: circular dependencies,
scattered tenant/seller conditionals, over-engineering relative to
the typed-models-only mandate, or any auth/payment code touched.
Verdict: not an unqualified "ready for implementation" - two Medium
findings should be resolved by decision/small build before real
backend work starts, not because they block anything today but
because both compound in cost the longer they're left unresolved.
Everything actually built (typed foundation, disabled-by-default flag,
Phase 1 UI, plus the bug this review fixed) is solid and ready to
stay exactly as-is. No Critical or High-severity issue found anywhere.
Documentation only, no code. Synthesizes the 3 prior audits
(Seller-Management.md, the Backoffice readiness audit, the Storefront
audit) plus BACKEND.md SS11 into one migration plan covering all 17
requested modules: Authentication, Authorization, Bootstrap, Products,
Categories, Orders, Payments, Transactions, Reviews, Analytics, Media,
Search, CMS, Builder, Settings, Notifications, Emails, Audit Logs.
Per module: current behavior, future behavior, migration strategy,
backward compatibility, risk, effort, and an endpoint classification
(No change / Minor change / Major change / New endpoint) grounded in
facts already established in the prior audits - no new exploration,
no invented specifics.
Headline finding: Orders (the Unified-vs-Split-Orders decision) is the
single highest-risk, most consequential item in the whole plan -
payments/refunds/reporting all depend on it, and it can't be resolved
by an additive field the way every other domain's seller-scoping can.
Payments stays untouched (ADR-010, frozen) under the Unified path;
only Split Orders would ever touch the payment flow, and only then
with the same scrutiny the original frozen implementation got.
Cross-cutting sections included per mission: Database changes
(one nullable seller_id column touches existing tables, everything
else is new tables - no NOT NULL migration ever required), Permission
changes, Caching (bootstrap cache key must include resolved seller
identity), Indexes, Security (seller-to-seller isolation treated with
tenant-isolation rigor), Performance, API Versioning (ties to the
already-open BACKEND.md SS2.10 decision), Migration order (14 numbered
dependency steps), and 6 recommended implementation phases (A:
Foundation through F: Operational polish).
Every phase explicitly re-asserts the non-negotiable constraint:
modules.sellerManagement.enabled=false must show zero behavioral
difference before/after each phase ships.
Linked from docs/architecture/foundation/README.md alongside the
other Seller Management docs.