Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 36 additions & 15 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ Vitest + Playwright.

## Where things live

| What | Where |
|-----------------------------|------------------------------------------|
| DB schema | `prisma/schema.prisma` |
| tRPC routers | `src/server/routers/` |
| Plugin runtime + MCP | `src/server/services/` |
| API route handlers | `src/app/api/` |
| App pages (RSC + client) | `src/app/(app)/` |
| Design tokens | `src/app/globals.css`, `tailwind.config.ts` |
| Sample plugin | `plugins/issue-triage/` |
| DevLog | `DEVLOG.md` — update at session end |
| What | Where |
| ------------------------ | ------------------------------------------- |
| DB schema | `prisma/schema.prisma` |
| tRPC routers | `src/server/routers/` |
| Plugin runtime + MCP | `src/server/services/` |
| API route handlers | `src/app/api/` |
| App pages (RSC + client) | `src/app/(app)/` |
| Design tokens | `src/app/globals.css`, `tailwind.config.ts` |
| Sample plugin | `plugins/issue-triage/` |
| DevLog | `DEVLOG.md` — update at session end |

## Primitives

Expand Down Expand Up @@ -63,17 +63,18 @@ All tenant-scoped on `workspaceId`.

`Workspace.autoDispatch` + `autoDispatchMode` drive automatic agent
selection when an issue hits the queue unassigned. Modes:

- `MANUAL_ONLY` — dispatcher is a no-op; assignment is human-only.
- `ROUND_ROBIN` — pick the least-recently-dispatched eligible agent.
- `PRIORITY_MATCH` — prefer agents whose `capabilities` contain the
lowercase priority name (`urgent`, `high`, …); round-robin tie-break.
- `CAPABILITY_MATCH` — intersect the issue's label names with agent
capabilities; most matches wins; round-robin tie-break; zero-match
falls through to round-robin rather than stalling dispatch.
`maxConcurrent` caps active assignments per agent (0 = unlimited).
`requireApprovalBeforeStart` gates push until a human approves.
`autoStartOnAssign` determines whether AGENT_ASSIGNED fires a webhook on
assignment (vs just filling the agent's queue).
`maxConcurrent` caps active assignments per agent (0 = unlimited).
`requireApprovalBeforeStart` gates push until a human approves.
`autoStartOnAssign` determines whether AGENT_ASSIGNED fires a webhook on
assignment (vs just filling the agent's queue).

## Configurability

Expand All @@ -86,7 +87,7 @@ baked into handlers. Current knobs: `cycleLengthDays` (7),

## Granular ApiKey scopes

`ApiKey` has the usual coarse `PluginScope[]` ceiling *plus* three
`ApiKey` has the usual coarse `PluginScope[]` ceiling _plus_ three
narrowing arrays: `projectIds`, `labelIds`, `initiativeIds`, plus an
optional `linkedAgentId` pointing at the `Agent` row the key belongs
to. Empty arrays = unrestricted within the declared scopes. Non-empty =
Expand Down Expand Up @@ -149,3 +150,23 @@ hardcoded — those should stay small regardless of Appearance.
3. Append to `DEVLOG.md`
4. Commit; optional push.
5. Follow `RELEASE.md` for release tagging and deploy.

## Code work coordination

- GitHub `origin` is the code source of truth; Forge is the ownership and
delivery source of truth. Forge agents, Codex Desktop tasks, and contributors
follow the same workflow.
- One issue has one active work session, one isolated worktree/branch, and one
primary implementation PR. Check and claim the issue's Delivery session
before editing. Continue an existing session instead of creating competing
work.
- Branch names include the issue key (`codex/axi-123-description`). Never edit,
commit, or test changes in the deployment/main checkout.
- Heartbeat the work session during meaningful phases and after commits. Link
PRs through native GitHub `IMPLEMENTS` relations, never generic attachments.
- GitHub owns PR/review/check/merge truth. Forge records the delivery sequence:
Claimed → In progress → PR/review → Merged → Released → Deployed → Verified.
- Release/deploy/verification require explicit operator authority and are
serialized. See `docs/engineering/work-management.md` and `RELEASE.md`.
- Use timestamped Prisma migration directories to prevent parallel numbering
collisions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ The dashboard's **What's New** rail reads this file at request time
entries. Keep entries terse — one line per item under each version
date, grouped by `Added` / `Changed` / `Fixed` / `Removed`.

## [2026-07-15] — v0.20.0 · Coordinated code delivery

### Added

- **Issues now coordinate one active code work session across Forge, Codex Desktop, and contributors.** The Delivery card records the owner, repository, branch, base, isolated worktree, heartbeat, native implementation PR, and separate merge, release, deployment, and live-verification milestones.
- **Active delivery work remains visible after navigation.** A dashboard card shows current branches, owners, PR state, stale work, and the next delivery stage; workspace admins can tune the stale lease threshold.
- **Agents receive a code-work contract and MCP tools.** EXECUTE runs check and claim shared ownership before editing, heartbeat meaningful phases, and attach native GitHub implementation PRs instead of generic links.
- **Production deployment has a serialized exact-ref path.** A guarded script requires a clean dedicated clone, verifies the target belongs to `origin/main`, locks concurrent releases, stamps the build SHA, and smoke-tests the live sign-in route.

### Changed

- **GitHub remains authoritative for PR delivery facts.** Review, checks, mergeability, branch head, and merge state advance the Forge delivery session without creating a parallel PR status model.
- **Stale work is actionable instead of silently released.** Quiet sessions retain their ownership lease and create one shared action request until the owner resumes or an operator abandons the session.
- **Parallel contributors use timestamped Prisma migration names.** Repository, agent-context, release, docs-site, production compose, and Obsidian policies now share the same worktree and delivery rules.

## [2026-07-15] — v0.19.1 · Refreshable GitHub sync health

### Added
Expand Down
48 changes: 48 additions & 0 deletions DEVLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,54 @@

> Append-only session log. Read at session start. Update at session end.

## 2026-07-15 — Shared code-work ownership and guarded delivery

Implemented the common delivery contract for Forge agents, Codex Desktop tasks,
and contributors. A first-class WorkSession provides one active coordination
lease per issue with repository, branch, base, worktree path, owner, heartbeat,
native PR, and explicit merged/released/deployed/verified milestones. Duplicate
work is rejected with the current owner and branch; stale work remains blocking
and raises one shared ActionRequest rather than being silently discarded.

Native GitHub PR snapshots remain authoritative and advance attached sessions
through draft/review/ready/merged states using branch, aggregate checks, review
decision, and mergeability evidence. The issue rail adds a compact Delivery card
and richer GitHub evidence; the dashboard adds an active Delivery work card so
ownership and next steps survive navigation. Workspace settings now own the
stale lease threshold. Release/deploy/verification transitions require workspace
admin authority.

Added `workSessions.*` MCP tools to the bounded runtime profile and injected the
coordination policy into every EXECUTE run: claim before editing, never work in
main/deployment checkouts, heartbeat after commits/phase changes, use native
IMPLEMENTS links, and stop at operator release/deploy gates. Added a timestamped
Prisma migration, service coverage for lease conflicts, PR-derived delivery,
milestone ordering, and stale action-request deduplication.

Production compose now accepts `FORGE_SOURCE_PATH`; a dedicated clean deployment
clone was initialized at `/home/bailey/deploy/forge-prod`. The guarded deploy
script serializes with `flock`, resolves an exact tag/SHA, verifies main ancestry
and version/tag agreement, stamps the build, deploys both services, and smoke
tests the live sign-in route. Repository policy/docs and the canonical Obsidian
Delivery Workflows, Development Standards, Forge Release Workflow, and Hermes ↔
Forge Integration notes were updated to the same model.

Verification: lint and TypeScript passed; the focused coordination and MCP
profile suites passed **11/11**; the complete Vitest gate passed **1,297/1,299**
concurrently, with its one failed stale-dispatch case passing in isolation
**9/9** (the remaining test is skipped); the production docs + Next.js build
passed; and Playwright passed **37/39** concurrently, with the intentional
dashboard widget expectation corrected and both affected specs passing in a
serial follow-up **4/4**. The unrelated chat `/clear` timing case also passed in
that serial follow-up.

The pre-merge Codex review identified two narrowing-boundary omissions and one
heartbeat race. `workSessions.list` and `workSessions.claim` now enforce the
calling API key's project/label/initiative scope before reading or leasing an
issue. The stale sweep now atomically rechecks both status and heartbeat cutoff
when claiming a session for transition, so a concurrent heartbeat wins instead
of being overwritten. Focused regression coverage now passes **13/13**.

## 2026-07-15 — Read-only GitHub sync health refresh

Closed the post-release usability gap in workspace GitHub App settings. Added a
Expand Down
53 changes: 39 additions & 14 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
# Forge Release Workflow

> Canonical release policy for Forge. **GitHub Flow** — one long-lived branch
> (`main`), short-lived feature branches, ship continuously to the single
> (`main`), short-lived feature branches, and serialized releases to the single
> self-hosted instance.

## Branch Model

```
main ← the only long-lived branch; always deployable AND deployed; releases tagged here
feat/* ← short-lived feature branches from main → PR → squash-merge → main
fix/* ← bugfix branches from main
chore/* ← maintenance branches from main
docs/* ← documentation branches from main
main ← only long-lived branch; always deployable; releases tagged here
codex/<issue>-* ← Codex/Forge agent work
<contributor>/<issue>-* ← human contributor work
fix/<issue>-* ← bugfix branches from main
```

**Rules:**

- `main` is always deployable. Every merge to `main` can ship.
- Work lands via PR (review surface for humans + agents). Branch protection isn't
available on this plan, so this is convention — keep it anyway.
- Branches are short-lived — open the PR within a day or two of branching; delete
merged branches immediately.
- One Forge issue owns one active work session, isolated worktree/branch, and
primary implementation PR. Claim it before editing; stale sessions must be
resumed or abandoned, never silently replaced.
- The checkout used for production is deployment-only. Development happens in
task-owned worktrees and never in the production or integration checkout.
- No long-lived `dev`/`staging` branch. Reintroduce one only if/when a separate
**staging deploy target** exists (then: `feat/* → PR → dev (staging) → PR → main (prod)`).

Expand All @@ -28,62 +33,82 @@ docs/* ← documentation branches from main
[Semantic Versioning 2.0.0](https://semver.org/). Pre-1.0 (`0.x`): MINOR is the
"feature" bump, PATCH is fixes; breaking changes are allowed within `0.x`.

| Bump | When |
|------|------|
| **MAJOR** (`X.0.0`) | (post-1.0) breaking API/schema/UX changes |
| Bump | When |
| ------------------- | ----------------------------------------------------- |
| **MAJOR** (`X.0.0`) | (post-1.0) breaking API/schema/UX changes |
| **MINOR** (`x.Y.0`) | New features, MCP tools, UI surfaces, additive schema |
| **PATCH** (`x.y.Z`) | Bug fixes, perf, security, docs |
| **PATCH** (`x.y.Z`) | Bug fixes, perf, security, docs |

`package.json` `version` must match the tag you cut.

## Release Process

For the same local gate used before direct releases, run:

```bash
pnpm ci:local
```

### 1. Land the work
- Open a PR `feat/* → main`. CI (`.github/workflows/ci.yml`: lint · typecheck · unit ·

- Open a PR `<owner>/<issue>-* → main`. CI (`.github/workflows/ci.yml`: lint · typecheck · unit ·
Playwright e2e) must be green. **Squash-merge** with a clean message.
- Update `CHANGELOG.md` under a heading `## [YYYY-MM-DD] — vX.Y.Z · Short title` — the
bracket date drives the in-app "unseen" dot + ordering; the `— vX.Y.Z · Title` tail is
the release name shown in What's New. Group items under `### Added/Changed/Fixed/Removed`.
Bump `package.json` `version` to match the tag. (Draft with `pnpm changelog`, then curate.)
- Only the designated release owner changes the version, assembles final release
notes, and merges the release commit. Feature PRs must not race independent
version bumps or tags.

> CI note: GitHub-hosted Actions require billing to be enabled. If Actions are
> unavailable, gate the release on the equivalent local run
> (`pnpm lint && pnpm typecheck && pnpm test` + `pnpm test:e2e` against the
> `docker/docker-compose.yml` stack) and record that in the release notes.

### 2. Tag the release

```bash
git checkout main && git pull origin main
git tag vX.Y.Z && git push origin vX.Y.Z
```

(Or trigger `.github/workflows/release.yml` — manual version bump + tag + GitHub Release —
once Actions billing is restored.)

### 3. Deploy (Docker-Server)
Prod builds the working tree at `main`, stamped with the commit:

Prod builds an exact tag/commit from a dedicated clean checkout, stamped with
the commit. The deployment lock permits one release at a time:

```bash
cd /home/bailey/forge && git checkout main && git pull
flock /tmp/forge-production-deploy.lock
cd /home/bailey/deploy/forge-prod
git fetch origin main --tags
git checkout --detach vX.Y.Z
git status --porcelain # must be empty
cd ~/docker/forge
GIT_SHA=$(git -C /home/bailey/forge rev-parse --short HEAD) \
FORGE_SOURCE_PATH=/home/bailey/deploy/forge-prod \
GIT_SHA=$(git -C /home/bailey/deploy/forge-prod rev-parse --short HEAD) \
BUILD_TIME=$(date -u +%FT%TZ) docker compose build forge forge-worker
docker compose up -d # entrypoint runs `prisma migrate deploy`
# run any one-time data backfill explicitly (prod image has no tsx — copy a .cjs + `node`)
```

### 4. Smoke test

`forge.axiom-labs.dev` loads · sign-in works · core flows (issue create, agent dispatch,
MCP health) · `system.buildInfo` reports the deployed SHA · `docker compose logs forge`
clean.

### 5. Close the loop

Update `DEVLOG.md`; mark issues done; note state in the project log if it changed.
Record the exact release, deployed SHA, and live verification in the issue's
Delivery card. “Merged,” “released,” and “verified live” are separate facts.

## Hotfix

Same as a normal change — branch `fix/*` from `main`, PR, squash-merge, tag a PATCH,
deploy. (No back-merge needed; `main` is the only long-lived branch.)
</content>
19 changes: 14 additions & 5 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ export default defineConfig({
transformItems: (items) =>
items.map((item) => ({ ...item, url: `docs/${item.url.replace(/^\/+/, "")}` })),
},
ignoreDeadLinks: [
/^https?:\/\/localhost/,
/^\/api\//,
/^\/w\//,
],
ignoreDeadLinks: [/^https?:\/\/localhost/, /^\/api\//, /^\/w\//],

head: [
// Fonts — match the dashboard exactly: Inter (sans) + JetBrains
Expand Down Expand Up @@ -99,6 +95,11 @@ export default defineConfig({
{ text: "Agents", link: "/agents/overview.html", activeMatch: "/agents/" },
{ text: "Automation", link: "/automation/webhooks.html", activeMatch: "/automation/" },
{ text: "Reference", link: "/reference/mcp.html", activeMatch: "/reference/" },
{
text: "Engineering",
link: "/engineering/work-management.html",
activeMatch: "/engineering/",
},
],

sidebar: {
Expand Down Expand Up @@ -199,6 +200,14 @@ export default defineConfig({
],
},
],
"/engineering/": [
{
text: "Engineering Operations",
items: [
{ text: "Work Management & Delivery", link: "/engineering/work-management.html" },
],
},
],
},

outline: { level: [2, 3], label: "On this page" },
Expand Down
Loading
Loading