# Blu2Green (B2G)

Sustainability platform monorepo: **12 Yii2 (PHP 8.2) backends + 5 Angular frontends + Kong**,
run locally with Docker Compose and on AWS ECS Fargate (`main` → `b2g-prod`, `prelive` → `b2g-demo`).
`ci/service-map.json` is the authoritative list of the 18 deployable services (paths, ECR/ECS names,
health ports). **Each service folder has its own `CLAUDE.md`** with module detail; read it before
working there. `README.md` has the local-dev picture (port map, daily commands). Parts of it describe
an older committed-`vendor/` layout; where it disagrees with this file, this file wins.

## Commands
- PHP lint: `php -l <file>` (must print "No syntax errors"). Host PHP may be missing or the wrong
  version. If it isn't 8.2, use `docker run --rm -v "$PWD:/app" -w /app php:8.2-cli php -l <file>`.
- Static guards (CI runs both before Docker; must exit 0):
  `python ci/ns-case-check.py` · `python ci/curl-timeout-check.py`
- Frontend build: `cd <app> && npx ng build --configuration=docker` (apps: `b2g`, `gMarket`,
  `b2g_website`, `b2g_filems_fe`, `nibe_datamanager/nibedatamanager_fe`). Must end without errors.
- Frontend lint: `npm run lint`, only in `gMarket` and `nibedatamanager_fe` (zero errors).
- Frontend tests: `npx ng test --watch=false`. Mostly boilerplate; never skip or delete a failing test.
- Agent setup check: `php evals/check.php` (hooks behave as intended).
- Local stack: `./setup.sh` / `setup.bat` (first run), `docker compose up -d`,
  `docker compose logs -f b2g-usermgmt`, `docker compose restart b2g-micro`.

There is no PHPUnit/Codeception in CI; the `tests/` folders are Yii boilerplate. The real gate is the
two static guards plus the CI smoke test (container boots, `/healthz`, body scanned for fatals).

## Working rules (docs/ai-sdlc/HANDOFF.md §2: mandatory)
- Start non-trivial work in **plan mode**; save the approved plan to `intent/<yyyy-mm-dd-slug>/plan.md`
  (template in `intent/_templates/`). If the implementation diverges, update `plan.md` too.
- **Claude never creates commits:** `git commit`, `merge`, `rebase`, `cherry-pick`, `revert`, `am`
  and `pull` (without `--ff-only`) are **blocked by a hook**. The developer reads the full diff and
  commits, and their own commit message is how they show they understand the change. So **do not
  draft commit messages.** When done, list the changed files and ask the developer to commit. If they
  share a draft message, check it against `git diff --staged` and point out anything missing or
  overstated. Pushing to `prelive` or `main` **is a deploy** and is also blocked.
- Before reporting done: run the relevant commands above and **paste the output**. Use the
  `verifier` subagent for multi-service changes.
- If a test or guard fails, fix the code, not the test, and never regenerate a baseline.
- Policy lives in skills: `secure-api-review` (backend endpoints, curl), `frontend-endpoint-rules`
  (Angular API wiring/builds), `b2g-theme-factory` (UI), `db-data-dictionary` (table semantics).
- PR descriptions follow `docs/ai-sdlc/PULL_REQUEST_TEMPLATE.md` (explain-back + prod debugging).
  Review policy: `REVIEW.md`.
- Same mistake twice → add it to "Common mistakes" below (or the module's `CLAUDE.md`) in the same PR.

## CI/CD (CodeCommit → CodeBuild → ECS). Read `CI-CD-PROCESS.md` before touching `ci/`
- `ci/pipeline.py` is the engine (`changed`, `baseline`, `plan`, `build`, `smoke`, `deploy`, `wait`,
  `build-base`); the buildspecs are thin loops over it. Adding a service = one entry in
  `ci/service-map.json`.
- `main` → `b2g-ci`/`b2g-cd` → cluster `b2g-prod`, family prefix `b2g-`. `prelive` →
  `b2g-ci-demo`/`b2g-cd-demo` → `b2g-demo`, prefix `b2g-demo-`. Same ECR repos.
- `main` is gated (IAM `DenyDirectPushToMain`; only a PR merge deploys). **`prelive` is ungated:
  a direct push deploys.** See `ci/branch-gating/README.md`.
- CD is self-healing: it deploys based on the sha ECS is *actually running*, so a failed deploy
  retries next build, and out-of-band task-definition config (env, secrets, CPU/memory) is preserved.
- PRs live in the CodeCommit console. Merge **locally with `--no-ff`**; the console hits the
  2,500-file limit, and squash merges cause add/add conflicts later (`docs/codecommit-workflow.md`).

## Architecture: things that span services
- **Backend images**: every PHP service builds on `b2g/php-nginx:8.2` (php-fpm + nginx + supervisor,
  `docker/php-nginx/`). nginx serves `web/` on :80 and `api/` on :81 (filemst also :82–84).
  **`vendor/` is never committed** (`**/vendor/` is gitignored); each Dockerfile runs
  `composer install --no-scripts` in a deps stage. Locally, run `composer install` (PHP 8.2) in a
  backend if its `vendor/` is missing.
- **Env selection**: backends honour `APP_ENV` (`local|dev|demo|uat|prod`) first and fall back to the
  hostname (`demo.blu2green.earth` / `blu2green.earth`) when it's unset. Each `config/web.php` does
  this. `b2g_filemst` is different (see its `CLAUDE.md`). Check that the matching
  `params-<env>.php` / `db-<env>.php` exist before changing `APP_ENV`.
- **East-west calls** use env-var base URLs (`MICRO_BASE_URL`, `USERMGMT_BASE_URL`, …) merged into
  `config/params-local.php`, set in `docker-compose.yml` and the ECS task definitions. ECS Service
  Connect gives an inbound request **15s**, so every outbound curl needs a timeout.
- **Logging → CloudWatch**: awslogs ships container stderr to `/ecs/<tier>/<service>`. Mirror Yii
  logs to stderr with the `StderrLogTarget` component (`php://stderr`). It lands via the
  `fix/yii-stderr-log-target` branch, so until that merges some services still have the broken
  pattern. **Never** use a `FileTarget` on `/proc/self/fd/2`: under php-fpm it's a pipe, and
  `fopen()` throws on every request. ELK in `docker/elk/` is local-only.
- **Kong is DB-less**: routes live in `docker/kong/kong.yml` (ECS) and `kong1.yml` (Compose). After
  edits, run `docker compose restart kong`. Admin API edits are discarded.
- **Frontends**: built on the host (not in Docker locally). The `docker` configuration swaps in
  `environment.docker.ts` with `KONG = ''` (relative URLs). Never put an absolute gateway host in
  frontend code or `assets/env.js` (`docs/frontend-endpoint-rules.md`).
- **Databases**: 5 shared MySQL databases (`bgi_b2g_reg_*`, `_master_*`, `_cengex_*`, `_digitall_*`,
  `_gmarket_*`), plus service-specific ones (nibe `bgi_bridge`). A `_fk` often points into
  **another service's** database (usually `bgi_b2g_master_*`).

## Common mistakes (Claude has made or been warned about these)
- Editing `vendor/`, lockfiles, `dist/` or `ci/curl-timeout-baseline.txt`. Blocked by
  `.claude/hooks/protect_paths.php`; change the source instead.
- Setting `opcache.revalidate_freq` to `0` in `docker/php-nginx/php.ini`. It must stay `2`, or
  bind-mounted requests take 5–15s.
- Conflating `b2g_subscriptnmgmt` (referenced by frontends, no Kong route) with `b2g_subpayment`
  (actually deployed).
- Changing `package.json` without `package-lock.json` (or the reverse). Resolve both from the same
  side; installs use `--legacy-peer-deps`.
- Line endings: this clone may have `core.autocrlf=false`. Check that new or edited files are pure
  LF before handing over (`git ls-files --eol <file>`).
- PHP namespace case differing from the path. It passes on Windows and 500s on Linux.

# Compact instructions
When compacting, keep the current `intent/.../plan.md` path and its open steps, the latest
build/guard/test output, the list of files changed so far, and any user decisions.
