Skip to main content

Working with AI Assistants

:::info Source Sourced from the repo-root AGENTS.md. :::

Authoritative operating manual for all AI assistants (Claude Code, Cursor, Copilot, Aider, etc.) working in this repo. All rules here are non-negotiable unless a spec document explicitly supersedes them. On conflict: spec docs in https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech win. This file is a distillation; specs are source of truth.


0. Repository Orientation

  • Name: Ghasi-edTech
  • Shape: Turborepo monorepo, pnpm workspaces. Root package.json must declare "workspaces": ["services/*", "packages/*"], and pnpm-workspace.yaml must list the same globs in the same order (platform convention; keep them in sync). The Docusaurus portal lives in ghasi-e-documentation/docs-portal/, not under apps/ here.
  • Languages: TypeScript 5.6+ (primary), Python 3.11+ (ML/data only), Go 1.21+ (optional perf-critical)
  • Domain: Enterprise multi-tenant ed-tech platform (LMS runtime, authoring, marketplace, AI tutoring, offline-first)
  • Specs root: https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech — numbered specs, docs, docs\roadmap\, and docs\standards\ live under ...\docs\ in that repo (not in this monorepo).
  • Product backlog (epics & user stories — documentation repo): https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTechEPICS.md (frontend/product-area epics), USER_STORIES.md (detailed stories, US-* IDs), optional per-epic files EP-*.md, plus supporting files (FRONTEND_ROADMAP.md, jira-import.csv). Use this folder in addition to docs/07-epics-and-user-stories.md when resolving an epic/story by title, EPIC-* id, US-* id, or when the story is not listed under 07-epics-and-user-stories.md.
  • Epic ID alignment (Jira vs backlog): In backlog/EPICS.md, epics use ids like EPIC-SYN-001 (product-area prefix + sequence). Jira may use a different epic key or EP-xx style number — those numbers do not have to match docs/07-epics-and-user-stories.md or each other. When a user cites EP-xx or a Jira epic name, search backlog/EPICS.md and USER_STORIES.md by epic title or by Epic ID: / EPIC-*; do not reject a match solely because EP-xx ≠ backlog numbering.
    • Per-service specs (17 markdown files) live under https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech<name>\ — not under services/<name>/ in this code repo. The /scaffold-service command generates code only and verifies those files exist; it does not create or duplicate specs here.
    • If the folder layout is renamed (e.g. to specs/), update every reference in .cursor/rules/*.mdc, CLAUDE.md, and this file.

Version control: AI assistant files (Cursor, Claude Code, GitHub Copilot)

  • Commit (so every machine and teammate gets the same behavior): root commands/, .cursor/rules/, .cursor/workflows/, .claude/rules/, .claude/commands/, .github/rules/, .github/commands/, .github/copilot-instruction.md (and any other Copilot instruction files you add under .github/), plus AGENTS.md, CLAUDE.md, and .cursorrules when they change.
  • Commit (generated platform artifacts that must stay in lock-step with source): services/<name>/openapi.json (per-service Swagger), postman/ (collections, environments with blank + disabled secret values, test-data stubs, scripts, README), packages/sdk-client/ (TypeScript client generated from OpenAPI). The Docusaurus developer portal lives in the ghasi-e-documentation repo under docs-portal/; regenerate it there with pnpm regen after /sync-api-surface (see commands/sync-docs-portal.md). Re-running generators on a clean tree must produce zero diff where applicable.
  • Do not commit ephemeral pipeline output: .claude/artifacts/ (session-local working files from playbooks such as /dev-pipeline). It is listed in .gitignore and .cursorignore.
  • Do not commit machine-local Claude Code settings: .claude/settings.local.json (also gitignored).
  • Do not commit real secrets in postman/environments/*.postman_environment.json. Production environment values stay blank with enabled: false. Real credentials live in 1Password / KMS only.

Canonical spec files (read when relevant)

TopicFile
Enterprise architecturehttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
DDD + bounded contextshttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Microservice listhttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Event-driven architecturehttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
API designhttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Data modelshttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Security + tenancyhttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Observabilityhttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Testinghttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Frontend designhttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Frontend workflowshttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Authoring toolhttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
LMS runtime playerhttps://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech
Multi-platform frontend (implementation)https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTechmandatory before any work under apps/web-application, apps/mobile-shell, apps/desktop-shell, or UI surfaces that implement web/mobile/desktop shells. Read at minimum the platform file: docs/frontend/web/04-web-app-specification.md, docs/frontend/mobile/05-mobile-app-specification.md, or docs/frontend/desktop/06-desktop-app-specification.md when touching that surface; read docs/frontend/common/02-architecture-overview.md when architecture or cross-cutting client concerns change. This repo’s docs/FRONTEND_SPECS.md summarizes paths and scope.
Frontend/product epics & stories (backlog folder)https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech, ...\backlog\USER_STORIES.md, and ...\backlog\EP-*.md when present — search here for epic title and Epic ID: (e.g. EPIC-SYN-001 Offline Sync, Outbox & Conflict Resolution). Jira epic keys / EP-xx labels may differ; do not require numeric parity with 07-epics.

Supporting standards (compact, for day-to-day decisions)

  • https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech — required files + directory skeleton for every new service
  • https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech — one-page naming authority
  • https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech — canonical error codes
  • https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech — merge checklist

ghasi-Multi-agent workflows, user stories, and spec access (mandatory)

  • Documentation repo path: D:\GhasiTech\ghasi-e-documentation\Ghasi-edTech\ — add the ghasi-e-documentation repo as a workspace folder in the IDE when working from Cursor so paths resolve. The platform slug (e.g. ghasi-edtech in JSON) maps to the Ghasi-edTech directory name under that repo.
  • If specs cannot be loaded (file not found, repo not attached, permission error): halt and ask the user. Do not implement from summaries, prior chat, or ghasi-Multi-agent artifacts alone; do not silently deviate from the documented API/event/data contracts.
  • User stories must be traced to docs/07-epics-and-user-stories.md and/or backlog/EPICS.md + backlog/USER_STORIES.md (and any matching backlog/EP-*.md) in the documentation repo, plus a user-supplied path when given, and to services/<name>/ specs for each owning/collaborator service. Before concluding that an epic/story id does not exist, search the backlog/ folder (by id, title, or keyword). Stories that span multiple services require work per bounded context named in the story or an explicit written scope decision from the user.
  • Acceptance criteria, preconditions, postconditions, and story-level definition of done in the documentation are binding. Gaps or contradictions require clarification before coding — not silent deferral.
  • ghasi-Multi-agent workflow (v3.2): create a feature branch before implementation; if clarifications are needed, emit clarifications-needed.md and run the Clarification Agent (clarifications-resolved.md) — do not end the workflow; resume from spec-review through PR after answers. Execution is complete only when all in-scope ACs and DoD are green (roll-up in workflow-state.md: implementation_overall / execution_complete) unless each deferred AC has a written Jira key in workflow-state.md (ac_deferrals_to_jira). Cross-service stories must cover every named collaborator. At PR / handoff, update workflow-state.md with that roll-up. You MAY write a local implementation-status.md (full AC table) for your workspace — it is gitignored and must not be committed; the committed source of truth for status is workflow-state.md.

See also: .claude/commands/ghasi-Multi-agent.md, .cursor/workflows/ghasi-Multi-agent.yaml (version 3.2).

Why docs/frontend/** was sometimes skipped (root cause)

  1. Not in the original “canonical spec files” table — agents were directed to docs/08-…docs/11-… and docs/standards/ but the nested docs/frontend/ tree was not listed, so implementers defaulted to parent guidelines only.
  2. ghasi-Multi-agent load-context did not enumerate docs/frontend/** — the workflow loaded docs/07-…, docs/standards/…, and services/<name>/ but did not require the frontend subtree; orchestrators could pass spec-review without reading platform-specific 04/05/06.
  3. CLAUDE.md “do not pre-load specs” — appropriate for token use, but without an explicit exception for docs/frontend on frontend tasks, agents skipped the implementation specs.
  4. Second workspace not attached — if ghasi-e-documentation is not added as a Cursor workspace folder, absolute paths fail; agents must halt (see Hard stop in ghasi-Multi-agent docs) rather than guess.
  5. .cursor/rules/90-frontend.mdc referenced 0811 paths in the header but not docs/frontend/README.md — now aligned (see that file).

1. Tech Stack Mandates

ConcernChoiceForbidden
Backend frameworkNestJS 11 (TS 5.6+ strict)Raw Express for services, Fastify, Koa
ORMDrizzle ORM (chosen in service specs; authoring/assignment/delivery specs all use Drizzle + drizzle-kit)TypeORM, Sequelize, raw SQL strings in app code
Migrationsdrizzle-kit (generate + push); plain SQL migrations committed to src/infrastructure/migrations/Ad-hoc schema drift, hand-applied SQL
DBPostgreSQL 16 (per-service schema, RLS mandatory)Cross-service DB reads, shared tables
CacheRedis 7In-memory maps for multi-instance state
Message brokerNATS JetStreamRabbitMQ, plain Kafka, polling
SearchOpenSearch (lexical) + pgvector (semantic)Elastic cloud, managed Pinecone
ValidationZod (TS), Pydantic (Py), Ajv for JSON SchemaHand-rolled validators, any casts
ID generationULID (prefer) or UUIDv7branded typesAutoincrement, plain UUID, sequences
Frontend (web)Next.js 14+ App Router, TailwindCSS, Radix UI, Lucide iconsRecharts (a11y gaps), Redux global store, MUI
Client stateTanStack Query + ZustandRedux, MobX
FormsReact Hook Form + Zod (shared with backend DTOs)Formik, hand-rolled
AnimationFramer Motion (gated by prefers-reduced-motion)CSS-in-JS runtime libs for animation
i18nICU MessageFormatHardcoded strings, concatenation
ChartsVisxRecharts
Offline DBDexie (web), SQLite (native)localStorage for structured data
ObservabilityOpenTelemetry SDK via @ghasi/telemetry wrapperVendor SDKs directly (Datadog, NewRelic)
LLM clientsOnly ai-gateway-service imports provider SDKsopenai, anthropic, etc. anywhere else
TestingVitest (unit/integration), Playwright (E2E), Pact (contract), k6 (load), Stryker (mutation)Jest in new services, Cypress

2. Monorepo Layout (Turborepo)

.
├── AGENTS.md # this file
├── CLAUDE.md # Claude Code entry (imports AGENTS.md)
├── .cursorrules # legacy Cursor entry
├── .cursor/rules/*.mdc # Cursor MDC rule packs (auto-attached by glob)
├── turbo.json # Turborepo pipeline
├── package.json # root manifest: `workspaces` + `packageManager: pnpm@…`
├── pnpm-workspace.yaml # same globs as `package.json#workspaces` (required)
├── tsconfig.base.json # shared strict TS config
├── services/ # 19 NestJS microservices (one bounded context each) — **code only**
│ └── <service-name>/ # 17 spec markdowns live in doc repo: …\ghasi-e-documentation\Ghasi-edTech\services\<service-name>\
├── apps/ # optional stub only — developer portal is in ghasi-e-documentation/docs-portal/
│ └── README.md # points to the documentation repo
│ # (planned) web-learner, web-authoring, web-admin, mobile, desktop live here when scaffolded
├── packages/
│ ├── domain-primitives/ # @ghasi/domain-primitives — branded IDs, shared VOs (TenantId, ULID, ISODate, Money, AIProvenance, VectorClock)
│ ├── telemetry/ # @ghasi/telemetry — OTel wrapper, log schema v3, redaction
│ ├── event-envelope/ # @ghasi/event-envelope — EventEnvelope<T>, outbox/inbox types
│ ├── api-contracts/ # @ghasi/api-contracts — shared Zod schemas, error codes, request envelope
│ ├── ui/ # @ghasi/ui — design tokens, Radix-wrapped primitives
│ ├── config/ # @ghasi/config — shared eslint, prettier, vitest, tsconfig presets
│ └── sync-protocol/ # @ghasi/sync-protocol — LocalMutation, VectorClock, conflict types
├── event-schemas/ # Git-backed registry: {service}/{aggregate}/{event}/v{N}.json
├── terraform/ # IaC per region / env
├── k8s/ # Helm charts per service
├── (external specs) # https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech — see §0
└── scripts/ # migrations, seeding, backups

Package naming

  • Services: @ghasi/service-<name> published as internal workspace package
  • Shared: @ghasi/<domain> (e.g., @ghasi/domain-primitives, @ghasi/telemetry)
  • Apps: @ghasi/app-<surface> (e.g., @ghasi/app-web-learner)

Turbo pipeline requirements

  • build, test, lint, typecheck, test:integration, test:contract, test:e2e tasks defined on every package
  • Cache keys include tsconfig.base.json + turbo.json + relevant env vars
  • test:integration depends on Testcontainers — not cached by default
  • No task may invoke pnpm run / nested package scripts across service boundaries; use workspace package references only

3. Service Architecture (Clean + DDD, ironclad)

Every service under services/<name>/ has four layers. Dependencies flow one direction only: presentation → application → domain ← infrastructure.

Layer rules

src/
├── domain/ # PURE TypeScript. No imports from: nestjs, drizzle, prisma, nats, axios, redis, fs, aws-sdk, openai.
│ # Allowed: crypto stdlib, date-fns, zod (for VO invariants only), @ghasi/domain-primitives.
│ # Contents: aggregates, value objects, domain services, domain events, repository interfaces.
├── application/ # Use cases, command/query handlers, ports (interfaces), DTOs, mappers.
│ # Imports: domain, @ghasi/api-contracts, @ghasi/event-envelope. NEVER imports infrastructure.
│ # Forbidden: process.env, fetch, fs, any concrete adapter.
├── infrastructure/ # Adapters implementing application ports. Postgres repos, NATS pub/sub, HTTP clients, S3, Redis, KMS.
│ # All env parsing + DI wiring lives here. SDK imports ONLY here.
└── presentation/ # NestJS controllers, GraphQL resolvers, WS/SSE handlers. DTO-in, DTO-out — never domain entities leaking.

Enforcement: ESLint import/no-restricted-paths rule blocks violating imports. CI fails on violation.

Required per-service docs (matches https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

Location: https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech<name>\ (documentation repository). Do not treat services/<name>/ in this monorepo as the home for those 17 files unless the team explicitly mirrors them (optional); the authoritative set is in the documentation repo.

  1. SERVICE_OVERVIEW.md
  2. DOMAIN_MODEL.md
  3. APPLICATION_LOGIC.md
  4. API_CONTRACTS.md
  5. EVENT_SCHEMAS.md
  6. DATA_MODEL.md
  7. SYNC_CONTRACT.md
  8. AI_INTEGRATION.md
  9. SECURITY_MODEL.md
  10. OBSERVABILITY.md
  11. TESTING_STRATEGY.md
  12. DEPLOYMENT_TOPOLOGY.md
  13. FAILURE_MODES.md
  14. LOCAL_DEV_SETUP.md
  15. SERVICE_READINESS.md
  16. SERVICE_RISK_REGISTER.md
  17. MIGRATION_PLAN.md

Missing any of these in the documentation repo = the service is not ready. Do not scaffold code before all 17 exist there (stubs may be acceptable only where SERVICE_TEMPLATE.md allows; the scaffold command still requires the five critical docs to be materially filled — see commands/scaffold-service.md).


4. Naming Conventions (see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

ThingPatternExample
Service dir{bounded-context}-serviceidentity-service
Workspace pkg@ghasi/service-{name}@ghasi/service-identity
File: aggregatekebab-case.tscourse-draft.ts
File: value objectkebab-case.tstenant-id.ts
File: port{name}.port.tsai-client.port.ts
File: adapter{name}.adapter.tspostgres-course-draft.adapter.ts
File: use case{name}.use-case.tspublish-course.use-case.ts
File: controller{name}.controller.tscourses.controller.ts
File: spec*.spec.ts (unit), *.integration.spec.ts, *.contract.spec.ts, *.e2e.spec.ts
ClassPascalCaseCourseDraft, PublishCourseUseCase
Interface (port)PascalCase, no I prefixAIClient, not IAIClient
Branded IDtype {Name}Id = Branded<string, '{Name}Id'>type CourseDraftId = …
Event type{service}.{aggregate}.{event}.v{N}authoring.course_draft.published.v1
Event subject (NATS)same as event type
DB tablesnake_case_pluralcourse_drafts, play_sessions
DB columnsnake_casetenant_id, created_at
API path/api/v{N}/{resource-plural} (lowercase, kebab-case)/api/v1/course-drafts/drf_01H…/blocks
Env varSCREAMING_SNAKE_CASEDATABASE_URL, NATS_URL

5. API Contract Rules (see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

Every request MUST carry

  • Authorization: Bearer <jwt> on tenant-scoped endpoints
  • X-Tenant-Id: <tenantId>must match JWT tid; mismatch → 403 authz.tenant_not_a_member
  • Idempotency-Key: <ULID> on every POST/PUT/PATCH with body; stored 24h
  • traceparent (W3C) — generated at edge if missing
  • Accept-Language: <BCP-47>
  • If-Match: "<version>" on optimistic-concurrency writes; missing → 428, mismatch → 412

Response envelope (success)

{
"data": { },
"meta": { "requestId": "req_01H…", "apiVersion": "v1.42", "traceId": "…",
"page": { "size": 50, "cursor": "…", "nextCursor": "…" } }
}

Response envelope (error — RFC 9457 problem+json)

{
"error": {
"type": "https://errors.ghasi.io/validation/field_required",
"code": "validation.field_required",
"title": "Missing required field",
"status": 422,
"detail": "Field 'email' is required.",
"instance": "/api/v1/users",
"errors": [{ "field": "email", "code": "required" }],
"traceId": "…", "requestId": "…",
"retriable": false, "retryAfter": null,
"docUrl": "https://docs.ghasi.io/errors/validation/field_required"
}
}

Canonical error codes: https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech.

Pagination

  • Cursor-only. Offset pagination is forbidden. Hard cap page.size = 200.
  • Cursor is opaque base64url JSON: { v: 1, k: {sortFields}, d: "asc|desc", f: "filterFingerprint" }.

Versioning

  • Major in path: /api/v2/…, with ≥1 release of overlap + dual-read.
  • Minor additive via X-API-Version: 1.42 response header.
  • Deprecation: Deprecation: true, Sunset: <RFC 7231 date>, Link: <doc>; rel="deprecation".

6. Event-Driven Rules (see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

Envelope (mandatory on every event)

interface EventEnvelope<T = unknown> {
eventId: ULID;
eventType: string; // '{service}.{aggregate}.{event}' (no version here)
eventVersion: number; // 1, 2, 3…
schemaUri: string; // 'schemas://service/aggregate/event/vN#sha256-…'
source: { service: string; instance: string; commit: string };
occurredAt: ISODate;
ingestedAt: ISODate;
causationId?: ULID;
correlationId: ULID;
tenantId: TenantId;
actor: { type: 'user'|'system'|'api_key'|'service_account'; id: string };
payload: T;
partitionKey: string; // usually aggregateId
outbox?: { dbWriteTs: ISODate; outboxId: ULID };
retentionClass: 'operational'|'regulated'|'audit';
dataResidency: 'us'|'eu'|'me'|'ap';
}

Implement once in @ghasi/event-envelope. Reuse everywhere.

Outbox pattern (producers — mandatory)

  • Every domain state change that emits events writes to the outbox table in the SAME transaction as the aggregate mutation.
  • OutboxRelay worker polls unpublished rows, publishes to NATS, marks published_at, retries exponential with jitter.
  • No direct NATS publish from use cases. Go through the outbox.

Inbox pattern (consumers — mandatory)

  • inbox table: event_id PK + consumer + processed_at + result.
  • Check inbox before applying; if present → no-op (cached result).
  • Apply mutation + insert inbox in same transaction.

Schema registry

  • event-schemas/{service}/{aggregate}/{event}/v{N}.json — JSON Schema.
  • CI validates publish + consume against registry.
  • Breaking change → new vN, dual-publish window ≥1 release.

7. Data + Tenancy Rules (ironclad, see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech, https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

Every table

  • id (PK, ULID, TEXT or UUIDv7)
  • tenant_id UUID NOT NULLalways
  • created_at timestamptz NOT NULL
  • updated_at timestamptz NOT NULL
  • deleted_at timestamptz NULL — soft delete (never hard-delete)
  • version bigint NOT NULL DEFAULT 1 — optimistic locking

RLS on every table

ALTER TABLE <t> ENABLE ROW LEVEL SECURITY;
CREATE POLICY <t>_tenant_isolation ON <t>
USING (tenant_id = current_setting('app.tenant_id')::uuid);
  • Gateway sets SET LOCAL app.tenant_id = '<uuid>' per request (PgBouncer init or connection wrapper).
  • Repository methods must require tenantId parameter; never implicit.
  • Domain layer: every aggregate root holds a TenantId VO. Constructors reject cross-tenant entity refs.

IDs

  • ULID or UUIDv7, always. Wrap in branded types (type XxxId = Branded<string, 'XxxId'>).
  • URL prefix convention: crs_ (course), drf_ (draft), lsn_ (lesson), usr_ (user), ten_ (tenant), org_, evt_, req_, dev_, etc. — choose the prefix in the DATA_MODEL.md of the owning service.

Timestamps

  • Wire format: RFC 3339 UTC (2026-04-16T10:00:00.000Z).
  • Never epoch millis as primary representation.
  • Client converts to local for display.

Soft delete

  • Set deleted_at = now(). Never DELETE rows (except via GDPR erasure saga).
  • Read paths filter deleted_at IS NULL by default.

8. Security + AuthN/Z (see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

  • Passwords: argon2id (m=64 MiB, t=3, p=1). No other hash algo for passwords.
  • JWT: EdDSA Ed25519, KMS-backed, kid rotation, JWKS at /.well-known/jwks.json.
  • Access token TTL: 15 min max. Refresh: 30d sliding, single-use rotation, family-revoke on reuse.
  • Required JWT claims: iss, aud, sub, tid, tids, roles, scope, did, amr, iat, exp, jti, v.
  • MFA: TOTP / WebAuthn / recovery codes. SMS deprecated.
  • Secrets: KMS + Vault. Never in source, env files committed to Git, or logs.
  • Encryption at rest: AES-256 with per-tenant KMS data keys (envelope) for Confidential+.
  • Offline bundles: AES-256-GCM, device-derived key via HKDF(tenantKey, devicePubKey, bundleId).
  • PII deny-list in logs: password, otp, access_token, payment_pan, national_id, email, learner_free_text_answer, health_note. Enforced by @ghasi/telemetry redactor.

9. Observability (see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

  • Instrument via @ghasi/telemetry only. No direct vendor SDKs.
  • Logs: JSON, schema v3; required keys ts, level, msg, event, service, trace_id, span_id, request_id, tenant_id, actor_id_hash, app, env, region, attrs, log_schema_version.
  • msg static; variables go in attrs (snake_case, namespaced).
  • Trace sampling: health 0%, cached reads 1%, writes 10%, 100% for payments, scoring, DSAR, AI inference, offline sync replay; errors & slow tail-override to 100%.
  • Every span on DB / cache / HTTP / NATS / S3 / AI outbound carries: otel.status_code, error.type, ghasi.tenant_id, plus domain attributes (ghasi.course_id, ghasi.ai.model, ghasi.ai.cost_usd, …).
  • SLIs per service: Availability 99.9%, P95 ≤ 300 ms API, P99 ≤ 800 ms, saturation < 0.8, queue lag < 5000/partition.

10. Testing (see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

Pyramid

LayerTarget coverageGate
Unit (domain-pure, no I/O)≥90% line/branch on aggregates, 100% on VOsMerge blocker
Mutation (Stryker, changed files)≥75% aggregates, ≥85% VOsMerge blocker
Integration (Testcontainers: Postgres/Redis/NATS/OpenSearch/MinIO/Mailpit)≥80%Merge blocker
Contract (Pact for HTTP, Avro / JSON Schema for events)All public endpoints + eventsMerge blocker
E2E (Playwright, 10 critical journeys)100% journeys green on nightlyMerge blocker
A11y (axe-playwright)Zero new serious/criticalMerge blocker
Lighthouse budgetsWithin per-page budgetsHard block if >10% over
Load (k6 / Locust)Nightly stagingObservation
AI eval (golden set + safety adversarial)≥parity, zero safety violationsMerge blocker on AI changes

Mandatory per service

  • test/integration/tenant-isolation.spec.ts — two tenants, identical IDs, prove RLS blocks cross-tenant reads.
  • test/contract/ — Pact + event schema conformance tests.
  • One E2E journey minimum.

Test naming

  • given_<setup>_when_<action>_then_<outcome>.
  • Builders live in __builders__/ colocated with aggregate.

Non-negotiables

  • Every bug fix ships with a regression test. PR template enforces this.
  • Flaky tests quarantined within 24h; fix-or-delete within 5 business days (max 10 with QA-lead exception).
  • No PR merges without green CI (stages 1–10).
  • No production deploy without canary (5% / 30 min) + verified rollback plan.

11. Frontend Rules (see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech, https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech)

11.0 Multi-platform frontend specs (docs/frontend/**) — mandatory

Before implementing or refactoring web, mobile shell, or desktop shell code (apps/web-application, apps/mobile-shell, apps/desktop-shell, or shared UI that implements those contracts):

  1. Read {docs-root}/docs/frontend/README.md for the index and hierarchy (how these specs relate to 0811).
  2. Read the platform specification for the surface you change:
    • Web: docs/frontend/web/04-web-app-specification.md
    • Mobile (Capacitor): docs/frontend/mobile/05-mobile-app-specification.md
    • Desktop (Electron): docs/frontend/desktop/06-desktop-app-specification.md
  3. When changing cross-cutting client architecture (offline, sync, routing, state patterns), read docs/frontend/common/02-architecture-overview.md and any other common/ files the README lists as prerequisites.

If the implementation intentionally differs from the reference repo layout in those specs (for example a consolidated web-application package instead of five separate Next apps), document the mapping in the story output / PR / workflow-state.mddo not treat “not in spec” as permission to skip reading the spec.

  • Next.js 14+ App Router, React Server Components where appropriate.
  • Design tokens as CSS custom properties at :root. Use logical CSS (padding-inline, margin-inline-start) — LTR + RTL both must work.
  • Every component has RTL Storybook story + both-direction visual regression.
  • Accessibility: WCAG 2.2 AA. Contrast ≥ 4.5:1 body, ≥ 3:1 large/UI. Focus visible. Semantic HTML first, ARIA minimal.
  • All animations gated on prefers-reduced-motion. No exceptions.
  • State: TanStack Query (server) + Zustand (client UI). Redux forbidden.
  • Forms: React Hook Form + Zod schemas shared with backend via @ghasi/api-contracts.
  • i18n: ICU MessageFormat. Every user-facing string goes through translation pipeline. No hardcoded strings.
  • Performance budgets enforced in Lighthouse CI: see https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech.
  • Offline UX patterns mandatory where applicable: Sync Status Pill, Sync Center, Bundle Manager, optimistic mutations, conflict UI.

12. AI Rules (ironclad)

  • ai-gateway-service is the only egress point for LLM / embedding / TTS / moderation / image calls.
  • No direct openai, anthropic, cohere, etc. imports outside that service. ESLint no-restricted-imports blocks this.
  • Every AI-generated artifact persisted with AIProvenance VO:
    interface AIProvenance {
    model: string; version?: string;
    promptId?: string; promptVersion?: SemVer;
    traceId: string; decisionId?: string;
    local: boolean; generatedAt: ISODate;
    reviewedBy?: UserId; reviewedAt?: ISODate;
    cost?: { microUSD: number; tokens: { in: number; out: number } };
    safety: { input: SafetyVerdict; output: SafetyVerdict };
    cacheHit: boolean;
    }
  • AI-marked blocks persist as status: draft_ai. Promotion to reviewed requires a human decisionId. Domain invariant — cannot be bypassed.
  • Moderation: pre-call (input policy + PII redaction + prompt-injection shield) and post-call (output policy + structured-output validation).
  • Feature flags: default-off for AI features, per-tenant opt-in.
  • Provider training on tenant data: explicitly disabled; verified at integration layer.
  • Tenant-scoped embeddings only. Never cross-tenant k-NN.

13. Working Rules for AI Assistants

Before writing any code

  1. Confirm bounded context. Read SERVICE_OVERVIEW.md + DOMAIN_MODEL.md under https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech<name>\.
  2. Check the relevant spec. Table at top of this file.
  3. Check existing patterns. Grep the service for similar aggregates/use-cases/adapters. Mirror them.
  4. If the service doesn't exist yet, create the 17 docs in the documentation repo (docs/standards/SERVICE_TEMPLATE.md) — all 17 docs first there, then run /scaffold-service in this monorepo for code only.

When touching existing code

  • Never widen layer-crossing imports. If you need infrastructure in a use case, add a port in application/ports/ and an adapter in infrastructure/adapters/.
  • Never add columns without RLS + tenant_id.
  • Never publish events without outbox.
  • Never add endpoints without updating API_CONTRACTS.md, OpenAPI, and Pact consumer tests.
  • Never add AI calls outside ai-gateway-service.
  • Bug fixes ship with regression tests — same commit, not a follow-up.

Definition of Done

See https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech. Every PR must pass it.

Communication

  • When the user asks for a new service: respond with the SERVICE_TEMPLATE checklist, confirm the bounded context, and create the docs in https://github.com/ghasi-EdTech/ghasi-e-documentation/blob/master/D/Ghasi-edTech<name>\ first — then scaffold code in this repo, not specs.
  • When spec and code disagree: the spec wins. Flag the discrepancy and either update code or, with user's explicit approval, update the spec.
  • When a new rule emerges from discussion: offer to add it to this file or the relevant standards doc — don't keep it in chat-only memory.

14. Ironclad MUST / NEVER

MUST

  1. Domain layer stays framework-free and I/O-free.
  2. Every service owns its data; cross-service reads go through APIs or events + projections.
  3. All APIs and events are versioned.
  4. Tenant isolation enforced at every layer (edge, gateway, app, domain, DB RLS, storage, cache, search, vectors, logs, backups).
  5. Outbox for every event; inbox for every consumer.
  6. Idempotency-Key required on all writes.
  7. Branded IDs (ULID or UUIDv7). Never raw UUID, never autoincrement.
  8. Migrations backward-compatible within a major version; dual-read windows for breaking changes.
  9. Integration tests use Testcontainers, not mocks of infrastructure.
  10. OpenTelemetry-only instrumentation via @ghasi/telemetry.
  11. AIProvenance VO on every persisted AI-generated artifact.
  12. Every bug fix has a regression test.

NEVER

  1. Never import framework/ORM/IO into domain/.
  2. Never hardcode secrets; never commit env files with secrets.
  3. Never share DB schemas across services.
  4. Never use offset pagination.
  5. Never emit PII or sensitive data to logs (see deny-list in §8).
  6. Never bypass RLS; never skip setting app.tenant_id.
  7. Never call LLM providers directly outside ai-gateway-service.
  8. Never persist AI-generated content without AIProvenance.
  9. Never promote draft_ai to reviewed without human decisionId.
  10. Never ship a new event without registry entry + consumer contract test.
  11. Never ship an endpoint without OpenAPI + Pact + updated API_CONTRACTS.md.
  12. Never merge with flaky tests, serious/critical a11y violations, or failing CI.
  13. Never exceed 15 min access-token TTL or 30d refresh-token TTL.
  14. Never store plaintext passwords (argon2id only).
  15. Never use eval, dangerouslySetInnerHTML, or exec on user input.

When in doubt: read the spec. When the spec is silent: match existing patterns. When both are silent: ask the user before inventing.