Add cross-app Gishen master spec and pre-push refresh tooling.

Establish the shared Admin/Mob/Ecom/B2B contract in-repo and wire Cursor rules, reminder scripts, and a soft pre-push hook so pushes keep the master (and Admin sheet pointers) current.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
kirukib 2026-08-07 16:27:01 +03:00
parent e9a8e0ffc2
commit 40771434d1
7 changed files with 523 additions and 1 deletions

View File

@ -0,0 +1,20 @@
---
description: Refresh GISHEN-MASTER-SPEC (and admin-backend-spec) before git push
alwaysApply: true
---
# Update master spec before push
Before any **`git push`** (or when the user asks to push / open a PR that pushes):
1. Update [`docs/GISHEN-MASTER-SPEC.md`](docs/GISHEN-MASTER-SPEC.md):
- Append a **Changelog** row (today’s date + why).
- Refresh sections touched by this branch (auth, orgs, doctors, orders/guest, Rx dosing, catalogue/UOM/PDP, stock, loyalty).
- Re-skim sibling folders under `/Users/kirukib/Desktop/Yaltopia Project/` when present: `Gishen-Mob`, `Gishen-Ecom`, `Gishen-B2B` (and Dispatch Bot if dispatch changed). Note missing folders under **Sources not found**.
- Bump **Last reconciled** + Sources stamp checklist dates.
2. If Admin modules, mocks, or endpoints changed, also update [`docs/admin-backend-spec.md`](docs/admin-backend-spec.md) changelog + relevant sections.
3. Do **not** invent APIs — mark **TBD** when siblings lack a contract.
4. Prefer running `bash scripts/sync-master-spec-reminder.sh` and including refreshed docs in the commit set before push.
5. Never skip this for “docs-only is fine later” when `src/` or backend sheets changed.
Do not push unless the user explicitly asked to push.

34
.githooks/pre-push Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env bash
# Optional pre-push: warn if GISHEN-MASTER-SPEC may be stale.
# Soft-fail by default. Set GISHEN_MASTER_SPEC_STRICT=1 to block push.
set -euo pipefail
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
SCRIPT="$ROOT/scripts/sync-master-spec-reminder.sh"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Gishen: master spec check (pre-push)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [[ ! -x "$SCRIPT" && -f "$SCRIPT" ]]; then
chmod +x "$SCRIPT" || true
fi
if [[ -f "$SCRIPT" ]]; then
# Soft warn unless STRICT already set by user
bash "$SCRIPT" || {
status=$?
if [[ "${GISHEN_MASTER_SPEC_STRICT:-0}" == "1" ]]; then
echo "Push blocked: refresh docs/GISHEN-MASTER-SPEC.md then retry."
exit "$status"
fi
echo "Continuing push (soft warn). Re-run with GISHEN_MASTER_SPEC_STRICT=1 to enforce."
}
else
echo "WARN: missing $SCRIPT — update docs/GISHEN-MASTER-SPEC.md before push."
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
exit 0

View File

@ -37,7 +37,15 @@ Demo password for all accounts: `demo123`
## Docs ## Docs
Living backend contract: [`docs/admin-backend-spec.md`](docs/admin-backend-spec.md) — updated whenever modules are finalized. - **Cross-app master:** [`docs/GISHEN-MASTER-SPEC.md`](docs/GISHEN-MASTER-SPEC.md) — Admin / Mob / Ecom / B2B surface map, shared domain objects, auth providers, divergence notes. **Refresh before `git push`** (Cursor rule + optional `.githooks/pre-push`).
- **Admin backend contract:** [`docs/admin-backend-spec.md`](docs/admin-backend-spec.md) — updated whenever Admin modules are finalized.
```bash
# One-time: enable in-repo pre-push reminder for this clone
bash scripts/setup-githooks.sh
# Manual checklist / staleness check
bash scripts/sync-master-spec-reminder.sh
```
## Deploy ## Deploy
@ -56,5 +64,9 @@ VITE_API_BASE_URL=
## Related repos ## Related repos
Sibling apps under `Yaltopia Project/` (see master spec Sources stamp):
- Gishen-Mob — customer mobile (Expo)
- Gishen-Ecom — customer storefront - Gishen-Ecom — customer storefront
- Gishen-B2B — institutional portal (org self-register UI lives there; Admin creates/approves orgs) - Gishen-B2B — institutional portal (org self-register UI lives there; Admin creates/approves orgs)
- Gishen-Dispatch-Bot — rider Telegram bot

313
docs/GISHEN-MASTER-SPEC.md Normal file
View File

@ -0,0 +1,313 @@
# Gishen — Cross-App Master Spec
**Living collective contract** across Gishen client apps. Deeper API sketches live in each repo’s own backend sheet; this file is the **shared map** of roles, domain objects, auth providers, and known divergences.
| Field | Value |
| --- | --- |
| **Home repo (this file)** | `Gishen-Admin` |
| **Sibling root** | `/Users/kirukib/Desktop/Yaltopia Project/` |
| **Shared API base (TBD)** | `https://api.gishen.example/v1` (Ecom/Mob/Admin) · B2B draft: `https://api.gishenpharmacy.org/v1` — **unify TBD** |
| **Last reconciled** | 2026-08-07 |
---
## Changelog
| Date | Change |
| --- | --- |
| 2026-08-07 | **Seed batch:** Created master spec from Admin living contract + sibling Mob/Ecom/B2B/Dispatch sheets. Documented product surface map, shared domain objects (auth, orgs, doctors, orders/guest, Rx dosing, catalogue multi-UOM + PDP, stock, loyalty), auth provider matrix, divergence & sync notes, deeper-doc links, Sources stamp, and pre-push update rule. Wired `.cursor/rules` + optional `.githooks/pre-push` reminder. Linked from Admin `README.md` and `admin-backend-spec.md`. |
---
## Update rule (before every push)
This file is the **cross-app master**. Before `git push` from Gishen-Admin (humans **and** agents):
1. Diff Admin `src/` / `docs/admin-backend-spec.md` against the last Sources stamp below.
2. Skim sibling Mob / Ecom / B2B / Dispatch specs if those folders exist on disk.
3. Refresh relevant sections here; append a **Changelog** row (date + why).
4. If Admin endpoints/fields changed, also update [`admin-backend-spec.md`](./admin-backend-spec.md) in the same change set.
5. Update the **Sources stamp** (`Last reconciled` + checklist dates).
Agents: the Cursor rule `.cursor/rules/update-master-spec-before-push.mdc` applies on push requests. Optional git hook: see [Pre-push tooling](#pre-push-tooling).
---
## Sources stamp
Update these dates whenever you reconcile against a sibling.
| Source | Path (sibling area) | Found | Spec / README | Last skimmed |
| --- | --- | --- | --- | --- |
| **Admin** | `Gishen-Admin/` | ✅ | [`docs/admin-backend-spec.md`](./admin-backend-spec.md) | 2026-08-07 |
| **Mob** | `Gishen-Mob/` | ✅ | [`docs/BACKEND_SPEC.md`](../../Gishen-Mob/docs/BACKEND_SPEC.md) (relative from sibling root) | 2026-08-07 |
| **Ecom** | `Gishen-Ecom/` | ✅ | [`docs/backend.md`](../../Gishen-Ecom/docs/backend.md) | 2026-08-07 |
| **B2B** | `Gishen-B2B/` | ✅ | [`docs/backend/OVERVIEW.md`](../../Gishen-B2B/docs/backend/OVERVIEW.md) + coordination | 2026-08-07 |
| **Dispatch Bot** | `Gishen-Dispatch-Bot/` | ✅ (related) | `docs/BACKEND_SPEC.md` | 2026-08-07 |
| **Telegram Mini App** | `Gishen-Telegram-MiniApp/` | ⚠️ empty scaffold | — | 2026-08-07 |
### Sources not found
_None for the four primary surfaces (Admin / Mob / Ecom / B2B)._ Related folders present but thin: `Gishen-Telegram-MiniApp` (empty), Dispatch Bot documented as rider surface.
### Pre-push checklist (human / agent)
- [ ] Changelog row added if domain contract changed
- [ ] Auth / orders / Rx / catalogue / org notes still match siblings (or divergences listed)
- [ ] `Last reconciled` date bumped
- [ ] `admin-backend-spec.md` updated if Admin modules changed
- [ ] Reminder script run (optional): `bash scripts/sync-master-spec-reminder.sh`
---
## 1. Product surface map
One shared pharmacy platform; **four primary clients** + rider bot.
| App | Repo | Who | Primary jobs |
| --- | --- | --- | --- |
| **Admin** | `Gishen-Admin` | Pharmacy staff: `pharmacist`, `stock_manager`, `procurement`, `finance`, `marketing_manager`, `operations`, `super_admin` | Branch Rx queue, orders/POS, stock, procurement, org commercial activation, CRM/loyalty config, dispatch board, team/audit, catalogue master, doctor roster |
| **Mob** | `Gishen-Mob` | Retail customer (Expo) | Catalog/PDP, cart/checkout, guest pharmacist codes, Rx Care (scan/reminders), loyalty, family, subscriptions, B2B join/contexts, tracking |
| **Ecom** | `Gishen-Ecom` | Retail customer (Next.js PWA) | Storefront catalog, Rx upload/check, cart → order request, profile/favorites/loyalty, branches; v1 confirmation often phone/WhatsApp |
| **B2B** | `Gishen-B2B` | Institutional: `SUPER_USER`, `HR_ADMIN`, `FINANCE`, `MEMBER` | Org self-register; HR members/packages/migration; Finance statements/approvals; Member allowance + own Rx (**clinical withhold** for HR/Finance) |
| **Dispatch Bot** | `Gishen-Dispatch-Bot` | Riders/drivers | Telegram-enrolled trip status + POD; no separate rider login app for v1 |
| **Telegram Mini App** | `Gishen-Telegram-MiniApp` | Customers (planned) | Placeholder — Ecom backend mentions Telegram auth/bootstrap; **TBD** |
### Who creates what
| Artefact | Created by | Verified / fulfilled by |
| --- | --- | --- |
| Retail order (registered) | Customer (Mob/Ecom) | Pharmacist / Ops (Admin) |
| Guest order / pharmacist code (`GSH-XXXX`) | Guest/customer Mob/Ecom; staff/doctor Admin | Pharmacist Admin (`by-code` / POS) |
| Prescription intake (customer scan) | Mob/Ecom/B2B member | Pharmacist Admin |
| Doctor-authored Rx / clinical order | Doctor (hospital org) | Pharmacist Admin |
| Catalogue Item master | Stock/super_admin Admin | Consumed by Mob/Ecom as `/catalog/products` |
| Org commercial terms | Admin Finance | B2B unlocks on `org.activated` |
| Trip assign | Ops Admin | Dispatch Bot rider |
### External actors (not Admin staff roles)
| Actor | Auth surface | Notes |
| --- | --- | --- |
| **Doctor** | Separate doctor JWT (Admin sheet); hospital/clinic org affiliation | Create Rx & guest/registered patient orders; **not** a `StaffRole` |
| **End customer (guest)** | None / Mob `POST /auth/guest` / Ecom guest order | Phone required for assisted/guest checkout; loyalty gated |
| **B2B roles** | B2B portal session | Distinct from pharmacy staff; shared `customer_id` linkage TBD |
---
## 2. Shared domain objects
Fields below are the **platform-level** vocabulary. Path prefixes and exact DTOs still differ per sheet — see [§4 Divergence](#4-divergence--sync-notes).
### 2.1 Users & auth providers
| Concept | Shared idea | App notes |
| --- | --- | --- |
| Platform person | UUID user / customer | Staff vs customer vs doctor vs B2B user are **actors**, not one role enum everywhere |
| Staff | Admin `StaffUser` + `StaffRole` + optional `branchId` | Bearer JWT |
| Customer | Retail identity; loyalty points/tier | Mob `GET /users/me`; Ecom same idea |
| Doctor | Hospital/clinic-affiliated clinician | Admin roster + `POST /auth/doctor/login` |
| B2B session user | `roles[]`, `org_id`, `active_role` | Clinical withhold for HR/Finance |
| `authProvider` | `email` \| `google` \| `phone` \| `telegram` (+ Mob `guest`) | Report on `/me` / login response |
| Locale | `en` \| `am` | Admin `preferredLocale`; Mob/Ecom/B2B `locale` / Accept-Language |
See [§3 Auth matrix](#3-auth-provider-matrix).
### 2.2 Organisations
| Type (Admin) | Used for |
| --- | --- |
| `corporate` \| `ngo` \| `other` | B2B benefits clients — credit, packages, members |
| `hospital` \| `clinic` | Doctor affiliation; clinical Rx/orders |
**Lifecycle (B2B):** `pending_activation` → `active` → `suspended` \| `closed`. Admin writes commercial terms (`credit`, payment terms, `price_list_id`, contract dates) and emits activation events. B2B self-register UI → Admin approval queue.
### 2.3 Doctors
- Roster under hospital/clinic orgs in Admin (`/doctors`, org detail).
- Fields (Admin): `name`, `specialty`, `licenseNumber`, `phone`, `email`, `status`, `orgId`, …
- Can create prescriptions and orders for **guest** or registered patients, org-scoped.
- Cannot manage catalogue, RBAC, finance, other hospitals.
- Identity provider strategy vs B2B hospital users: **open** (Admin Q8 / Q17).
### 2.4 Orders (registered + guest)
| Field / idea | Meaning |
| --- | --- |
| `customerType` | Admin: `registered` \| `guest` on staff/doctor-created orders |
| Guest identity | `customerName` + **required** `customerPhone`; no `customerId` |
| Channels | Admin includes branch / POS / doctor / (ops); Mob/Ecom retail + guest codes |
| Pharmacist code | Mob/Ecom: `GSH-XXXX` via `POST /orders/guest-codes`; Admin fulfill `GET/POST .../by-code/:code` |
| Fulfillment | `pickup` \| `delivery` (+ branch / any-branch for guest codes) |
| Payments | Mob: Chapa / Telebirr / M-Pesa for delivery; `pay_at_branch` pickup-only. Ecom v1 often offline confirm |
**Guest limits (proposed):** valid phone; controlled SKUs still need approved Rx; no loyalty earn until claim/merge — **TBD** claim-by-OTP (Admin Q9–10).
### 2.5 Prescriptions + dosing schedule
| Layer | Contract |
| --- | --- |
| Admin line item | `frequency?`: `QD`\|`BID`\|`TID`\|`QID`\|`QXH`\|`custom`; `intervalHours?` (QXH); `times?`: `HH:mm`[] |
| Mob reminders | `POST /health/reminders` with `times[]`, grace, escalate; calendar UI; confirm/snooze |
| Sync intent | Persist pharmacist final `times` (+ interval) on approve/dispense so Mob can seed reminders without re-derive — **when** to push still open (Admin Q13) |
| Customer Rx Care | Mob/Ecom upload + `prescriptions/check` stock match statuses |
| B2B | Member submits Rx; HR/Finance never see clinical lines |
Admin helper: `src/lib/dosingSchedule.ts`.
### 2.6 Catalog / Items + multi-UOM + storefront PDP
| Concept | Admin (source of truth for ops) | Mob / Ecom (retail read model) |
| --- | --- | --- |
| Master product | `/admin/catalog/items` — Item with SKU, classification, media, EFDA, … | `/catalog/products` (+ slug) |
| Stock link | Inventory rows reference `itemId` | `/stock/product/:productId`, branches nearby |
| Multi-UOM | `uoms[]` + `conversionFactor` into stock `baseUnit`; `toStockQty` / `fromStockQty` | **Not yet** on Mob product type — sell unit is display `unit` string only |
| PDP fields | `storefrontUnit`, `useCase`, `compareAtPriceEtb`, side-effect lists, `directions[]`, `storageNotes[]`, `pharmacistTip` | Mob PDP: `product` + `getDrugFacts()` clinical overlay (`useCase`, form, dosages, mild/severe, storage, directions, advice) |
| Pricing lists | B2B `price_list_id` on org commercial | Ecom/Mob show retail (and credit quote at checkout) |
### 2.7 Stock
- Branch qty, batch/lot, expiry on **Stock**, not Item.
- ERP sync / field ownership / merge proposals: Admin Stock module.
- Mob/Ecom: availability APIs only (no warehouse write).
### 2.8 Loyalty
| Surface | Role |
| --- | --- |
| Admin Marketing | Config, ledger, activities, CRM points adjust, liability KPI; POS earn |
| Mob / Ecom | `GET /loyalty/me`, ledger, redeem; referrals (Ecom) |
| Guests | No earn by default until registered claim |
| Rx/Controlled | Catalogue auto-excludes loyalty eligibility (Admin) |
| B2B | Pointer/summary only; retail owns full wallet UI |
---
## 3. Auth provider matrix
| Provider | Admin (staff) | Mob (customer) | Ecom (customer) | B2B (portal) | Dispatch (rider) | Doctor |
| --- | --- | --- | --- | --- | --- | --- |
| **Google** | `POST /auth/staff/oauth/google` (ID token); mock UI | `POST /auth/oauth/google` | OTP-first sheet; Google **TBD** in older Ecom auth § | UI mock → demo session; `.../oauth/google/start` planned | — | TBD (Admin Q17) |
| **Email** | Password `POST /auth/staff/login` | `POST /auth/email/login` (+ optional register) | Magic-link sketched | Magic/OTP planned; demo persona login today | — | Email/invite primary (`/auth/doctor/login`) |
| **Phone OTP** | `phone/start` + `phone/verify` | `otp/request` + `otp/verify` (primary demo) | `otp/request` + `otp/verify` | SMS OTP planned | — | TBD |
| **Telegram** | Login Widget → `POST /auth/staff/telegram` | `POST /auth/oauth/telegram` | Telegram Mini App `POST /telegram/auth` sketched | Widget/Mini App callback planned | **Telegram user id enrollment** (identity) | Not primary |
| **Guest** | N/A (staff always auth); **guest customers** on orders | `POST /auth/guest` + guest-codes | Guest checkout by phone | N/A (org membership) | N/A | Creates guest **patients** |
| **Biometric** | — | Bind device key (Mob) | — | — | — | — |
Path naming **diverges** (`/auth/staff/*` vs `/auth/otp/*` vs `/v1/auth/*`) — consolidation TBD; shared `authProvider` enum values should stay aligned.
Env (Admin SPA): `VITE_GOOGLE_CLIENT_ID`, `VITE_TELEGRAM_BOT_USERNAME` (empty = mock UX). Server-only: bot token, OTP gateway, Google audience.
---
## 4. Divergence & sync notes
Accurate as of **2026-08-07** skim. Prefer fixing sheets over inventing a third truth here.
| Topic | Divergence | Sync action |
| --- | --- | --- |
| API base host | Admin/Mob/Ecom use `api.gishen.example`; B2B OVERVIEW uses `api.gishenpharmacy.org` | Pick one production host |
| Money | Admin demos major ETB; Ecom sheet says cents optional | Platform-wide decision (Admin open Q1) |
| Auth path prefixes | Staff `/auth/staff/*`, Mob `/auth/otp/*`, B2B `/v1/auth/*` | Shared IdP façade TBD |
| Guest orders | Admin: staff/doctor `customerType=guest`; Mob/Ecom: guest session + `GSH-XXXX` codes | Document both flows; Admin POS `by-code` must match Mob code shape |
| Guest Admin endpoints | Mob lists `GET /admin/orders/by-code/:code` | Ensure Admin sheet mirrors when POS wire-up lands |
| Rx dosing | Admin structured `frequency`/`times`; Mob reminders separate resource | On approve, map Admin times → `/health/reminders` seed |
| Catalogue ID | Admin `Item` / `itemId`; retail `productId` / slug | Mapping table or shared SKU key |
| Multi-UOM | Admin full `uoms[]`; Mob/Ecom display `unit` string only | Expose sales UOM on catalog DTO when POS/cart need conversion |
| PDP clinical | Admin stores PDP fields on Item; Mob still derives much from `clinical.ts` overrides | Prefer catalogue PDP fields once API live; keep Mob overrides as fallback |
| Loyalty paths | Aligned idea (`/loyalty/me`); Admin config under `/admin/loyalty/*` | OK |
| Org types | Admin hospital/clinic for doctors + B2B commercial orgs | Keep `OrganisationType` shared |
| Roles naming | Admin snake staff roles; B2B `HR_ADMIN` SCREAMING; Ecom older `pharmacy_staff` names | Map in backend claims; don't rename UI mid-flight without sheet sync |
| Rider auth | Dispatch: Telegram enrollment; Ecom older Flutter rider JWT sketch **superseded** for v1 | Prefer Dispatch sheet |
| Mini App | Ecom documents endpoints; Mini App folder empty | Implement or mark out of scope |
| Doctor in B2B | Not a B2B portal role today | Open: doctor realm vs hospital org user |
---
## 5. Links to deeper docs
### Admin (this repo)
| Doc | Purpose |
| --- | --- |
| [`docs/admin-backend-spec.md`](./admin-backend-spec.md) | Living Admin API/UI contract (primary depth) |
| [`README.md`](../README.md) | Runbook, demo accounts, deploy |
| `src/lib/dosingSchedule.ts` | Frequency → default times helpers |
| `src/config/navigation.ts` | Page registry |
### Mob
| Doc | Purpose |
| --- | --- |
| `Gishen-Mob/docs/BACKEND_SPEC.md` | Customer mobile API sheet |
| `Gishen-Mob/README.md` | Expo run + demo OTP |
| UI anchors | `app/(root)/auth/*`, `...(screens)/product/[id].tsx`, `prescription/*`, `checkout.tsx` |
### Ecom
| Doc | Purpose |
| --- | --- |
| `Gishen-Ecom/docs/backend.md` | Storefront + platform API proposal |
| `Gishen-Ecom/README.md` | Product proposal / demo |
| `Gishen-Ecom/docs/README.md` | Docs index |
### B2B
| Doc | Purpose |
| --- | --- |
| `Gishen-B2B/docs/README.md` | Standing rule + layout |
| `Gishen-B2B/docs/backend/OVERVIEW.md` | Tenancy, RBAC, clinical withhold |
| `Gishen-B2B/docs/backend/entities/` · `endpoints/` | Model + REST |
| `Gishen-B2B/docs/coordination/gishen-admin.md` | Admin ↔ B2B events |
| `Gishen-B2B/docs/coordination/gishen-ecom.md` | Checkout entitlement split |
| `Gishen-B2B/docs/coordination/open-items.md` | Cross-repo decisions |
| `Gishen-B2B/docs/features/INDEX.md` | Feature → pages map |
### Related
| Doc | Purpose |
| --- | --- |
| `Gishen-Dispatch-Bot/docs/BACKEND_SPEC.md` | Rider Telegram events / trips |
| `Gishen-Dispatch-Bot/docs/FEATURE_BRIEF.md` | Acceptance inventory |
---
## 6. Cross-cutting open items (rolled up)
Do not invent APIs here — track decisions:
1. Unify API host + money units (cents vs major ETB).
2. Shared auth façade for Google / Email / Phone / Telegram across actors.
3. Guest order claim by phone OTP + PII retention.
4. Checkout entitlement-split owner (B2B recommends Ecom + shared calc).
5. Doctor IdP (dedicated realm vs hospital B2B user).
6. Multi-UOM ownership ERP vs Platform.
7. When Rx `times` fan out to Mob reminders.
8. Telegram Mini App scope vs empty repo.
9. Admin ↔ Mob `by-code` / POS scan contract end-to-end.
Admin-local list: [`admin-backend-spec.md` § Open questions](./admin-backend-spec.md#open-questions).
B2B cross-repo: `Gishen-B2B/docs/coordination/open-items.md`.
---
## Pre-push tooling
Committed in **Gishen-Admin** only:
| Piece | Path | Behavior |
| --- | --- | --- |
| Cursor rule | `.cursor/rules/update-master-spec-before-push.mdc` | Agents must refresh this master (and Admin sheet if needed) before push |
| Reminder script | `scripts/sync-master-spec-reminder.sh` | Prints Sources checklist + whether master looks stale vs `src/` / Admin sheet |
| Git hook | `.githooks/pre-push` | **Warns loudly** if significant paths changed since last push while master was not updated; does **not** call LLMs. Soft warn by default (`GISHEN_MASTER_SPEC_STRICT=1` to exit 1) |
| Enable hooks | `scripts/setup-githooks.sh` | Sets **local** `core.hooksPath=.githooks` for this repo only |
```bash
# One-time per clone (optional but recommended)
bash scripts/setup-githooks.sh
# Manual check anytime
bash scripts/sync-master-spec-reminder.sh
```
Do **not** silently rewrite global git config. Local `core.hooksPath` for this repository is intentional and reversible (`git config --unset core.hooksPath`).

View File

@ -6,10 +6,13 @@
**Auth:** Bearer JWT for staff roles **Auth:** Bearer JWT for staff roles
**Mocks:** Admin SPA uses in-memory mocks until `VITE_USE_MOCKS=false` and `VITE_API_BASE_URL` point here. **Mocks:** Admin SPA uses in-memory mocks until `VITE_USE_MOCKS=false` and `VITE_API_BASE_URL` point here.
**Cross-app master:** [`GISHEN-MASTER-SPEC.md`](./GISHEN-MASTER-SPEC.md) — product surface map, shared domain objects, auth matrix, and divergence notes vs Mob / Ecom / B2B. Refresh the master (and this sheet) before pushes; see its Update rule.
## Changelog ## Changelog
| Date | Change | | Date | Change |
| --- | --- | | --- | --- |
| 2026-08-07 | Pointer to cross-app [`GISHEN-MASTER-SPEC.md`](./GISHEN-MASTER-SPEC.md) + pre-push refresh rule |
| 2026-08-06 | Scaffold: conventions, roles, auth, empty module sections | | 2026-08-06 | Scaffold: conventions, roles, auth, empty module sections |
| 2026-08-06 | Shell + RBAC + preferredLocale | | 2026-08-06 | Shell + RBAC + preferredLocale |
| 2026-08-06 | Pharmacist: prescriptions, branch orders | | 2026-08-06 | Pharmacist: prescriptions, branch orders |

14
scripts/setup-githooks.sh Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
# Enable in-repo hooks for this clone only (does not touch global git config).
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
git config core.hooksPath .githooks
chmod +x .githooks/pre-push scripts/sync-master-spec-reminder.sh scripts/setup-githooks.sh 2>/dev/null || true
echo "Set local core.hooksPath=.githooks for $(basename "$ROOT")"
echo "Pre-push will remind you to refresh docs/GISHEN-MASTER-SPEC.md"
echo "Unset with: git config --unset core.hooksPath"
echo "Strict block: GISHEN_MASTER_SPEC_STRICT=1 git push"

View File

@ -0,0 +1,126 @@
#!/usr/bin/env bash
# Reminder + staleness check for docs/GISHEN-MASTER-SPEC.md
# Does NOT call LLMs. Safe to run from pre-push or manually.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
MASTER="docs/GISHEN-MASTER-SPEC.md"
ADMIN_SPEC="docs/admin-backend-spec.md"
RED=$'\033[1;31m'
YEL=$'\033[1;33m'
GRN=$'\033[1;32m'
BLD=$'\033[1m'
RST=$'\033[0m'
echo "${BLD}Gishen master spec reminder${RST}"
echo " Master: ${MASTER}"
echo
if [[ ! -f "$MASTER" ]]; then
echo "${RED}ERROR:${RST} Missing ${MASTER}"
echo " Create/update it before push. See docs/GISHEN-MASTER-SPEC.md Update rule."
exit 1
fi
echo "${BLD}Pre-push checklist${RST}"
echo " [ ] Changelog row if domain contract changed"
echo " [ ] Skim Mob / Ecom / B2B (and Dispatch if relevant) when folders exist"
echo " [ ] Sources stamp + Last reconciled bumped"
echo " [ ] ${ADMIN_SPEC} updated if Admin modules changed"
echo
mtime_of() {
stat -f %m "$1" 2>/dev/null || stat -c %Y "$1"
}
master_mtime=$(mtime_of "$MASTER")
stale=0
newest_src=""
newest_ts=$master_mtime
consider() {
local path="$1"
[[ -e "$path" ]] || return 0
local ts
ts=$(mtime_of "$path")
if (( ts > newest_ts )); then
newest_ts=$ts
newest_src="$path"
fi
if (( ts > master_mtime )); then
stale=1
fi
}
consider "$ADMIN_SPEC"
consider "package.json"
consider "README.md"
# Sample recent TypeScript under src (portable; avoid head -z)
if [[ -d src ]]; then
while IFS= read -r f; do
[[ -n "$f" ]] || continue
consider "$f"
done < <(find src -type f \( -name '*.ts' -o -name '*.tsx' \) 2>/dev/null | head -n 200)
fi
# Relative to upstream if available
RANGE=""
if git rev-parse --verify @{u} >/dev/null 2>&1; then
RANGE="@{u}..HEAD"
elif git rev-parse --verify origin/main >/dev/null 2>&1; then
RANGE="origin/main..HEAD"
elif git rev-parse --verify origin/master >/dev/null 2>&1; then
RANGE="origin/master..HEAD"
fi
changed_significant=0
master_in_range=0
if [[ -n "$RANGE" ]]; then
if git diff --name-only "$RANGE" 2>/dev/null | grep -E '^(src/|docs/admin-backend-spec\.md|package\.json)' >/dev/null; then
changed_significant=1
fi
if git diff --name-only "$RANGE" 2>/dev/null | grep -E '^docs/GISHEN-MASTER-SPEC\.md$' >/dev/null; then
master_in_range=1
fi
fi
# Working tree: uncommitted significant changes without master touch
if git status --porcelain 2>/dev/null | grep -E '^(A |M |M|A|\?\?) (src/|docs/admin-backend-spec\.md)' >/dev/null; then
if ! git status --porcelain 2>/dev/null | grep -E 'GISHEN-MASTER-SPEC\.md' >/dev/null; then
changed_significant=1
fi
fi
if (( stale == 1 )); then
echo "${YEL}WARN:${RST} ${MASTER} looks older than significant Admin sources."
echo " Newer example: ${newest_src:-unknown}"
echo " Refresh the master changelog + Sources stamp before push."
fi
if (( changed_significant == 1 && master_in_range == 0 )); then
echo "${YEL}WARN:${RST} Significant Admin changes in push range, but ${MASTER} not updated in that range."
echo " Agents/humans: update master per Update rule, then recommit."
fi
if (( stale == 0 && (changed_significant == 0 || master_in_range == 1) )); then
echo "${GRN}OK:${RST} No obvious master-spec staleness signals (still verify changelog if you changed domain contracts)."
fi
echo
echo "Sibling skim paths (read-only):"
echo " ../Gishen-Mob/docs/BACKEND_SPEC.md"
echo " ../Gishen-Ecom/docs/backend.md"
echo " ../Gishen-B2B/docs/backend/OVERVIEW.md"
echo " ../Gishen-Dispatch-Bot/docs/BACKEND_SPEC.md"
echo
if [[ "${GISHEN_MASTER_SPEC_STRICT:-0}" == "1" ]] && (( stale == 1 || (changed_significant == 1 && master_in_range == 0) )); then
echo "${RED}STRICT:${RST} GISHEN_MASTER_SPEC_STRICT=1 → exiting 1"
exit 1
fi
exit 0