# Agent Workflow Instructions — api_hub (all 4 tracks)

Read this **before writing any code**. It is the single source of truth for how the 4
parallel agents (A: Identity/Billing, B: Catalog, C: Learning, D: Engagement/Tenants/Analytics)
work in separate git worktrees without stepping on each other.

**Read order before starting:**
1. `Laravel/api_hub/docs/README.md` — architecture, DB map, event + service contracts
2. This file (`agent_instructions.md`)
3. `Laravel/api_hub/docs/developer_{X}.md` — your track's task list
4. `.github/copilot-instructions.md` — repo coding conventions (if present)

---

## 1. Git workflow (push-only — the orchestrator merges)

You do **not** have PR/merge rights in this environment (no `gh`, no GitHub token — you can
`git push` over SSH, that's it). A single **orchestrator** process is the ONLY writer to
`main`: it picks up the task branches you push, verifies them, and merges the green ones.
Follow this exactly:

- You work in **your own git worktree** branched from `main`. Never work directly on `main`.
- **One branch per task**: `track-{x}/T-{X}-NN-short-name` (e.g. `track-b/T-B-04-curriculum-trees`).
  The branch name MUST contain `T-{X}-NN` — the orchestrator discovers branches by that token.
- **Rebase before pushing**: `git fetch origin && git rebase origin/main`. If main moved
  (likely — 3 other agents ship daily), rebase first; if a conflict touches a file you don't
  own, STOP and file a blocker (see §7) — do not resolve someone else's file.
- **Push per task** (a branch may contain 2–3 tightly-coupled small tasks, e.g. Phase 0
  basics — list every task ID in the push's commit messages). Push with
  `git push origin track-{x}/T-{X}-NN-short-name`.
- **Do NOT open a PR, do NOT merge into main, do NOT touch `main`.** When you `git push`,
  the orchestrator will: (1) reject the branch if it touches a file outside your track's
  ownership (§2), (2) run the FULL `php artisan test` suite, and (3) merge to `main` only if
  green. A rejected/failed branch is your bug — fix it on a fresh branch and push again.
- Commit messages: `[T-X-NN] imperative description`. Add `Co-authored-by: openhands
  <openhands@all-hands.dev>` to every commit.
- Never push to `main`, never force-push a branch another agent may have, never rewrite
  shared history.
- A task is **done** only once its branch is merged into `main` (check
  `git branch -r --merged origin/main` for `.../T-{X}-NN-...`). Only branch off main after
  your predecessor task's branch has merged.

## 2. File ownership (hard boundaries — violating = automatic rework)

You may **create/edit only** inside your track's paths. Everything else is read-only for you.

| Area | A — Identity/Billing | B — Catalog | C — Learning | D — Engagement |
|---|---|---|---|---|
| Models | `app/Models/User`, `app/Models/Identity/`, `app/Models/Billing/` | `app/Models/Catalog/` | `app/Models/Learning/` | `app/Models/Engagement/`, `app/Models/Analytics/` |
| Controllers | `app/Http/Controllers/Identity/`, `…/Billing/`, `…/Settings/` | `…/Catalog/`, `…/Assessment/` | `…/Learning/`, `…/Parent/` | `…/Engagement/`, `…/Support/`, `…/Affiliate/`, `…/Tenants/`, `…/Dashboard/` |
| Services | `app/Services/{Auth,Rbac,Audit,Token,Subscription,Billing,Seats,DataProtection,Settings,ApiKey}/` | `app/Services/Catalog/` | `app/Services/Learning/` | `app/Services/Engagement/` |
| Events (produce) | `app/Events/{Identity,Billing}/` | `app/Events/Catalog/` | `app/Events/Learning/` | `app/Events/Engagement/` |
| Listeners/Listeners & Jobs (consume) | `app/Listeners/Identity/`, `app/Jobs/Billing/` | `app/Listeners/Catalog/` | `app/Listeners/Learning/` | `app/Listeners/Engagement/`, `app/Listeners/Analytics/`, `app/Jobs/Analytics/` |
| Migrations | `database/migrations/identity_billing/` **+** `catalog/` files with prefix `20260924_01` (plans/pricing/discounts/affiliate config per T-A-13…16) | `database/migrations/catalog/` prefix `20260924_02` | `database/migrations/learning/` prefix `20260924_03` | `database/migrations/engagement/` prefix `20260924_04`, `analytics/` prefix `20260924_05` |
| Routes | `routes/api_identity.php` (**and** owns the loader that groups all 4 files in Phase 0, T-A-05) | `routes/api_catalog.php` | `routes/api_learning.php` | `routes/api_engagement.php` |
| Factories/Seeders | `database/{factories,seeders}/IdentityBilling/` | `…/Catalog/` | `…/Learning/` | `…/Engagement/` |
| Tests | `tests/Feature/IdentityBilling/` | `tests/Feature/Catalog/` | `tests/Feature/Learning/` | `tests/Feature/Engagement/` **+** `tests/Feature/E2e/` |
| Docs | `docs/developer_A.md`, `docs/status/track_a.md` | `docs/developer_B.md`, `docs/status/track_b.md` | `docs/developer_C.md`, `docs/status/track_c.md` | `docs/developer_D.md`, `docs/status/track_d.md` |

**Shared files — owner in parentheses; others must never edit:**
- `.env.example`, `config/database.php`, `composer.json` — **(A, Phase 0)**. If you need a
  new composer package or env var, file a blocker; A adds it in ≤1 task cycle.
- `app/Models/BaseDomainModel.php`, response envelope, exception handler, RBAC middleware —
  **(A, Phase 0)**. If broken: blocker, not a local patch.
- `README.md` "Contract Change Log" section — **any agent may append** (append-only; low
  conflict risk) but never rewrite other lines.
- `Documents/` — **locked for everyone. Never modify, ever.**

**Migration timestamp rule** (prevents filename collisions across worktrees): use the
fixed per-track hour prefix from the table above, next free minute+second (e.g. Track B
starts at `20260924_020001_create_boards_table.php` and increments). Never use "now".

## 3. Contract & interface freeze

- The 4 cross-track interfaces (`SubscriptionService::isEntitled`, `NotificationService`,
  `CatalogService`, `ProgressService`) and the 15 domain events in `README.md` are **frozen
  as of Phase 0**.
- **Additive** changes (new optional method/event field) are allowed: implement in your
  track, append one line to the Contract Change Log in `README.md` (task ID, what, why).
- **Breaking** changes (rename, signature change, field removal) are **forbidden** during
  the build. If you believe a contract is wrong: STOP, write a blocker in your status file
  with the concrete proposal, and continue on non-dependent tasks. Do not unilaterally
  change a contract another track compiles against.
- Consume other tracks' contracts **only through their interface** (constructor-injected),
  never by reaching into another track's model/repository/table directly.
- Cross-DB rule: your code may only open your own connection(s). Cross-domain references are
  plain indexed columns (`user_id`, `course_id`…), **never foreign keys, never joins** into
  another DB. Analytics ingestion consumes events, never reads other DBs.

## 4. Task execution loop

Repeat for each task in your file (in listed order):

1. **Check deps.** `DEPS:` tasks must be **merged to main** (verify: branch exists in git
   log / task marked done in the other track's status file). Not merged → you may not
   start; move to the next task in your file whose deps are satisfied (skip-ahead is
   expected — never idle-wait). `DEPS⚡` tasks only need the **contract/interface to exist**
   (Phase 0 stubs) — you may start immediately and test against your own test doubles.
2. **Branch** from latest main, name it per §1.
3. **Implement** the task. Migrations in your folder with your prefix; models in your
   namespace; every new endpoint: RBAC middleware (deny-by-default), audit hook (if the
   entity is auditable), standard envelope, 4xx/409/423/429 semantics per README conventions.
4. **Test every acceptance criterion.** Each AC gets ≥1 named test
   (`test_<ac-phrase>_…`). Tests live in your test dir only. You may NOT modify another
   track's tests or fixtures; to test against another track's seeded data, use their public
   seeding command (`catalog:seed` etc.) from your own test bootstrap.
5. **Run your suite**: `php artisan test --filter <YourTrackNamespace>`. Must be green.
   Before merging a Phase-gate PR, also run the full suite once (`php artisan test`) —
   if a failure is outside your namespace, file it as a blocker against that track, don't
   fix it yourself.
6. **Update status** (below), **commit, push, open PR** per §1.
7. Once merged: check the task off in `docs/developer_{X}.md` (append `- [x] T-X-NN —
   <date> — <PR#>` under the task) — you own that file, no conflict.

## 5. Definition of Done (per task)

- [ ] Every listed acceptance criterion implemented **and** covered by a test that asserts
      the criterion (not just "no error")
- [ ] Migrations run clean on the 5-connection SQLite sim; `migrate:fresh` idempotent
- [ ] No writes/reads outside your owned connection(s); no cross-DB FKs/joins
- [ ] Endpoints: authenticated + permission-gated; negative tests present (401/403/404/409
      where the AC demands them)
- [ ] Contract stubs (if any) match README signatures exactly
- [ ] Status file updated; task checked off in your developer file; PR merged

## 6. Status reporting (how the 4 agents stay visible to each other)

Maintain `docs/status/track_{x}.md` (create in your first PR). Format:

```markdown
# Track X Status
| Task | Status | Branch/PR | Notes / blockers |
| T-X-01 | ✅ done 2026-09-25 | PR #12 | |
| T-X-02 | 🔨 in progress | track-x/T-X-02-… | |
| T-X-05 | ⛔ blocked on T-A-09 | | A's user-lifecycle PR not merged |
```

Update it in the **same PR** as the work. Statuses: `🔨 in progress`, `✅ done <date>`,
`⛔ blocked on <task-id>`, `⚠️ blocker filed: <summary>`. Other agents' status files are
your only view of their progress — read them before assuming someone is stuck.

## 7. Blockers & contract-change protocol

- File a blocker in your status file: which task, what's missing, exact expectation
  (contract signature / merged task ID), proposed workaround if any.
- Then **continue with any task in your file that is not dependent on the blocker**.
  A track should never be fully idle; if everything remaining is blocked, say so
  explicitly in the status file with the list of unblocking task IDs.
- Only these things justify waiting >1 PR cycle on another track: a `DEPS:` task, a
  missing contract, a full-suite failure outside your namespace, an A-owned shared file
  change.

## 8. Environment & conventions

- PHP/Laravel per repo; Composer deps via A only (composer.json).
- 5 databases; in dev use the SQLite-sim pattern from T-A-01 (one file per DB). Never
  connect production credentials; never commit `.env`.
- Queues: `queue:work` database driver; tests use `Queue::fake()` unless the task is about
  the queue itself (retries, dead-letter).
- Time: store UTC, format per request; test time-based rules (grace periods, cooldowns,
  SLAs) with Carbon test-now.
- Secrets/keys in tests: fake tokens only; webhooks HMAC with a test secret.

## 9. Phase gates

When all tasks of a phase in your file are merged, run that phase's gate checklist
(`developer_{X}.md` → "Phase Gates"), record `GATE n passed <date>` in your status file,
and note it in the PR of the final gate task. Gating = full-suite green for your namespace
+ gate checklist assertions.

## 10. Absolute prohibitions

1. Never edit `Documents/`.
2. Never edit another track's files (per §2 matrix) — "just to fix a bug I noticed":
   file a blocker instead.
3. Never break a frozen contract; additive changes only.
4. Never push to `main` or force-push shared branches.
5. Never use "now" for migration timestamps.
6. Never add a cross-DB foreign key or join.
7. Never run another track's tests "to fix them"; never modify their fixtures.
8. Never mark a task done without merged code + passing tests + status update.
