Forge CI reader for four forges
## Epic 11717 — Executive AI command center
Original requirement (verbatim):
> Implement Something like this. Share code with Operator Dashboard.
> 10. Executive AI command center — Major differentiator
> Create one dashboard where a founder or CTO can see: All active projects. All AI employees. Development progress. Project health. Bugs and risks. Development expenses. Deployment status. Productivity and quality metrics.
> The owner should be able to ask: "What's happening with my company today?" and receive an accurate answer.
Settled inputs: the BA PRD (task 11718, R1–R14) and the Architecture (task 11719, §2.2 is this child's spec; AD-3, AD-7). Read both artifacts on the epic before building.
## This child (Architecture C1): `apps/api/app/forge_ci.py`
Pinned seam (child "Deployments endpoint and CI health" builds against it — do not change the shape):
```python
@dataclass(frozen=True)
class CiRun:
status: Literal["success", "failure", "running", "cancelled", "unknown"]
workflow: Optional[str]; branch: str; at: Optional[datetime]; url: Optional[str]; reason: Optional[str]
def latest_run(session: Session, project: Project, viewer: AdminUser) -> CiRun: ...
```
- One read per forge, latest run/pipeline on `project.integration_branch` of the primary repo (`ProjectRepo.is_primary`, `models/projects.py:228-234`):
- GitHub `GET /repos/{o}/{r}/actions/runs?branch=&per_page=1`
- GitLab `GET /projects/{id}/pipelines?ref=&per_page=1`
- Bitbucket `GET /repositories/{ws}/{slug}/pipelines/?target.branch=&sort=-created_on&pagelen=1`
- Azure DevOps `GET {org}/{project}/_apis/build/builds?branchName=refs/heads/<b>&$top=1`
Status mapping per forge pinned in one table in the module. Written fresh in the API — never import `apps/jaahd/forge_cli/adapters/*` (apps never cross-import).
- Credential: the VIEWER's own forge connection — `git_access.resolve_for_user(session, viewer, repo)` (`git_access.py:315`) then `rest_login_for` (`:502`). Missing/dead → `unknown` with reason like "Connect GitHub in Profile > Git to see CI"; `GitAccessNeedsReconnect` → "Reconnect GitHub".
- Never raises to the caller: no repo, 5s timeout, 401/403/404, 429 → `unknown` with the reason in words. Provide a bounded batch helper (thread pool of 8, total cap 8s; a project still pending at the cap → `unknown` "Timed out") for the page builders.
- Cache: process-local TTL 5 min keyed `(project_id, branch, credential_id)`; failures cached 60s. Never persisted.
- Edition: ADD `[feature.exec_dashboard]` (label "Command center", `editions=["cloud"]`) to `packages/editions/features.toml` listing `apps/api/app/forge_ci.py` (if the row already exists because "Command center API core endpoints" merged first, append the path instead), and regenerate both `edition_features.py` files, so `forge_ci.py` never ships in the community tree (the CE orphans check in `tools/mirror/check_trees.py` would fail otherwise). Whichever of this child and the API-core child merges first creates the row; the other appends.
- Tests: recorded-response fixtures per forge (GitHub, GitLab, Bitbucket, Azure) covering success/failure/running/cancelled and each `unknown` cause; cache TTL and failure cache; timeout cap.
Requirement R10 — Deployment status (ask 10, OD-7), the parts this child owns (verbatim):
- **AC10.1** For each project it shows the last merges to the integration branch (merged / failed / queued, with time) from the merge lane, and the forge's latest CI/deploy workflow run on the integration branch (status, workflow name, time, link). Verify: on dev, for one project, compare with the forge's Actions page for that branch. The status and time match. — this child owns the CI half.
- **AC10.2** States: a project with no repo, no forge credential, a forge error or rate limiting shows "unknown" with the reason in words. The page never errors or blanks. A project with no merge lane shows only CI. Verify on dev with a project that has no repo. — this child owns the CI states.
- **AC10.3** No deploy history is stored (OD-7). Verify: the PR adds no table or column for deploy runs.
Credentials & reach:
- AC10.1 GitHub CI live — `me/GitHub (ruzhukov)` against api.github.com (reachable: app.jaah.ai and GitHub egress OK), repo `jaah-ai/jaah` branch `develop` — **verified**: credential_get OK. The coder compares `forge_ci.latest_run` with `GET /repos/jaah-ai/jaah/actions/runs?branch=develop&per_page=1`.
- AC10.1 GitLab / Bitbucket CI — `me/GitLab (rzhukov)`, `me/Bitbucket (Roman Zhukov)` — **deferred**: live check not required (AD-7). Recorded-response fixtures are the oracle.
- AC10.1 Azure DevOps CI — none on the roster — **deferred**: AD-7, fixture only.
- Local gates (`./dc check quick/full`, pytest, vitest, Playwright local e2e) — worktree only — **verified**.
Out of scope: Do NOT add a deploy/CI history table or persist CI runs (OD-7). Do NOT ingest forge issues (GitHub/GitLab/Bitbucket/Azure) or Jira bugs (OD-12). Do NOT import jaahd forge adapters. Do NOT build the exec_dashboard endpoints, the merge-lane reads (child "Deployments endpoint and CI health") or any UI. Do NOT change the Operator dashboard's behaviour, tabs, routes or data. Do NOT add a plan gate or enablement flag (OD-10).
Done means:
- AC10.1 (CI half) — GitHub: `forge_ci.latest_run` for `jaah-ai/jaah` `develop`, run live with the viewer credential, returns the same status, workflow name, time and html url as `GET /repos/jaah-ai/jaah/actions/runs?branch=develop&per_page=1`; cite both outputs. GitLab/Bitbucket/Azure: recorded-response fixture tests pass (deferred live — AD-7).
- AC10.2 (CI states) — unit tests show no repo, no credential, reconnect-needed, 401/403/404, 429, timeout and batch-cap each return `unknown` with a human-readable reason (a sentence a tenant Owner can act on, not an exception string); the function never raises.
- AC10.3 — the PR adds no migration, table or column; the cache is process-local only.
- Edition — `packages/editions/features.toml` lists `forge_ci.py` under the cloud-only `exec_dashboard` feature; regenerated edition files committed; community-edition tree check passes.
- `./dc check quick` green; merge lane `./dc check full` green.
Outcome
Built apps/api/app/forge_ci.py (GitHub/GitLab/Bitbucket/Azure latest CI run via viewer's connection, unknown+reason on every failure, 5m/60s process cache, 8x8s batch) + cloud-only exec_dashboard edition row. Live GitHub read matches API; 42 tests, quick gate green; PR #7468 queued. Memory: none applied.
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)