AIDE Platform — Specification
2026-10-07 (synced with the build 2026-10-10) · Ben Fielder · v1 (rewrite of the TypeScript/NestJS implementation in ../AIDE-Platform-old-stack/, with improvements)
1. Overview
AIDE (AI-Driven Engineering Framework) turns an idea into released software through a governed, six-phase lifecycle: Refinement → Specification → Planning → Execution → Review → Release. AI agents produce versioned artifacts (project request, technical specification, implementation plan, code, optimisation plan). Humans approve them at gates. Every state change is deterministic, logged and reversible by rollback.
It is for engineers and project owners who want AI-driven delivery with an audit trail and human control. Success means a user can take a one-paragraph idea to a merged, tagged release in a Git repo, approving each stage, and can roll back to any earlier project version.
This rewrite keeps the old platform’s behaviour and fixes its known problems: seven services plus Redis and NATS become one ASP.NET Core app with PostgreSQL-backed background work. Inconsistent review thresholds become one decision table. Regex-parsed agent output becomes structured output. Settings that were never saved are now persisted. Projects without ownership get members and roles. See §10 for every change.
In scope (v1):
- Accounts and sign-in (email + password, GitHub), project membership and roles
- Projects with approval mode, thresholds and limits; persisted Git, LLM and notification settings
- The lifecycle state machine, approval gates (including escalation gates), artifact versioning, approval, locking, invalidation and integrity checks
- Five cognitive agents (idea refinement, spec, plan, stepwise code generation, code review/optimisation) behind one LLM provider abstraction: Anthropic, OpenAI, Ollama, plus a deterministic Fake provider
- Execution of plan steps with the Execution Guardian, retries and escalation; Git sync to a GitHub repo or a local Git repo, with one branch and one PR per project
- Review: compliance scoring, drift detection, the release decision, review cycles
- Release (merge + tag) and rollback to any earlier project version
- Real-time project updates in the browser
- Notifications: in-app, email (SMTP), Slack, Microsoft Teams, generic webhook, with signed one-click approval links
- Admin: event log, agent run log, dead letters with retry
- The
aideCLI (a .NET global tool) using personal API tokens
Out of scope (v1): GitLab as a Git provider and the GitHub Copilot LLM adapter (both existed in the old stack; see §11). Multi-tenant organisations. Billing. Hosting outside Docker Desktop Kubernetes. Running or deploying the generated software. Editing code inside AIDE.
2. Users and permissions
| Role | Can | Cannot |
|---|---|---|
| Admin (system role) | Everything on every project; view admin pages (events, agent runs, dead letters); retry dead letters; change any user’s system role | — |
| User (system role, default) | Create projects (becomes their Owner); see only projects they are a member of; manage their API tokens and notification preferences | See or open projects they’re not a member of (404) |
| Project Owner (per project) | Everything a Contributor can, plus: decide gates, approve/reject artifacts, request release, roll back, change settings, manage members, delete the project | — |
| Project Contributor (per project) | View everything in the project; start agents and execution; create draft artifact versions; trigger review | Decide gates, approve/reject artifacts, release, roll back, change settings (403) |
| Project Viewer (per project) | View everything in the project | Any mutation in the project (403) |
System actor aide-system |
Background workers act as this identity in audit records | Sign in |
Auth: ASP.NET Core Identity with cookie auth (email + password), plus GitHub OAuth as an external login linked to the same account. The CLI uses personal API tokens sent as Authorization: Bearer aide_pat_…. API tokens can’t list, create or revoke tokens, retry dead letters or change system roles (FORBIDDEN → 403): a leaked token must not mint a replacement. Cookie-authenticated mutations need an antiforgery token (ANTIFORGERY → 403); bearer requests are exempt. A project always has at least one Owner. The first registered user becomes Admin; the last Admin can’t be demoted (CONFLICT → 409). Passwords need at least 8 characters with a letter and a digit; 10 failed sign-ins lock the account for 5 minutes (still INVALID_CREDENTIALS).
3. Functional requirements
Accounts and access
- FR-1 Users register with email, name and password, and sign in and out with a secure cookie session.
- Given no account for
a@x.com, when they register with a valid password, then the account exists, they are signed in, andGET /api/auth/mereturns them. - Given an existing email, when someone registers with it, then
EMAIL_TAKEN → 409.
- Given no account for
- FR-2 Users can sign in with GitHub. The first GitHub sign-in with an unknown email creates an account; a signed-in user can link GitHub to their existing account. A GitHub email that matches an existing unlinked account is not auto-linked (account-takeover risk): the user signs in and links it from Account. When GitHub OAuth isn’t configured, the button is hidden and
/api/auth/github*returns 404.- Given a linked GitHub account, when the user completes the GitHub flow, then they are signed in as the linked user.
- FR-3 The first registered user becomes Admin; everyone else starts as User. Admins can change system roles.
- Given an empty database, when the first user registers, then their role is Admin.
- FR-4 Users create and revoke personal API tokens (name, optional expiry). The plain token is shown once; only a hash is stored.
- Given a token, when the CLI calls the API with it, then the request is authenticated as its owner; when it is revoked or expired, then
401.
- Given a token, when the CLI calls the API with it, then the request is authenticated as its owner; when it is revoked or expired, then
Projects
- FR-5 A user creates a project through a four-step wizard: Project idea (name + idea text), Configuration (approval mode, compliance threshold, max files per step, max review cycles, token budget), Git repository, Review & create. Defaults: advisory mode, threshold 0.90, 20 files, 3 cycles.
- Given valid input, when the project is created, then it is in phase
refinement, statusactive, project version 1, the creator is Owner, the idea is stored, andPROJECT_CREATEDis recorded.
- Given valid input, when the project is created, then it is in phase
- FR-6 The projects list shows only the user’s projects (all projects for Admins), with paging, search by name, filters by phase and status, and sorting by created, updated or name.
- Given two projects where the user is a member of one, when they list projects, then they see one.
- FR-7 Owners edit name, approval mode, threshold and limits, and delete a project (with typed-name confirmation). Deletion cascades to everything the project owns.
- Given a Contributor, when they
PATCHthe project, then403.
- Given a Contributor, when they
- FR-8 Owners manage members: add an existing user by email with a role, change roles, remove members. The last Owner can’t be removed or demoted.
- Given one Owner, when they try to demote themselves, then
LAST_OWNER → 409.
- Given one Owner, when they try to demote themselves, then
- FR-9 Project settings are persisted: Git (provider, repo, default branch, access token), LLM (provider, model per agent, optional per-project API key), notifications (channels, event subscriptions). Secrets are encrypted at rest and never returned by the API (only “set / not set”). Slack and Teams webhook URLs count as secrets: they are returned only as a host-masked
targetplushasSecret. Switching a project’s Git provider to Local drops the saved token.- Given a saved GitHub token, when settings are read, then the response shows
hasToken: trueand no token value.
- Given a saved GitHub token, when settings are read, then the response shows
- FR-10 Owners test the Git connection from settings. The test reports whether the repo is reachable and writable and the default branch exists.
- Given a bad token, when they test, then the result is
ok: falsewith the provider’s reason, and nothing is saved.
- Given a bad token, when they test, then the result is
Lifecycle and gates
- FR-11 The project’s phase changes only through the lifecycle state machine (§4.1). Every change writes a state transition (from, to, actor, reason, project version) and a
PHASE_CHANGEDevent in the same transaction.- Given phase
refinementwith no approved project request, when anyone requests advance tospecification, thenPHASE_PRECONDITION_FAILED → 409with the reason, and nothing changes.
- Given phase
- FR-12 Members with write access can request “advance to next phase”. When the approval mode requires a gate for that entry (§4.2), a pending gate is created instead and the phase changes only when it is approved.
- Given advisory mode in
specificationwith an approved spec, when advance is requested, then a pendingspec_approvalgate exists and the phase is stillspecification.
- Given advisory mode in
- FR-13 Owners approve or reject a pending gate with an optional comment. Approval applies the gated transition. Rejection keeps the phase and re-runs the agent that produces the gated artifact with the comment as feedback.
- Given a pending
plan_approvalgate, when it is rejected with “split step 4”, then the gate isrejected, the plan generator runs again with that feedback, and the phase is unchanged.
- Given a pending
- FR-14 When review needs a human (§4.5), an
escalationgate is created. Its decision is one of: continue (start another review cycle), roll back (to a chosen version), or accept (mark release-eligible with the comment recorded). Roll back needs atargetVersion. Reject isn’t a decision on an escalation gate (INVALID_DECISION → 400). An escalation for a blocked step (FR-33) accepts continue (blocked steps return topendingwith attempt 0 and a job is queued) or roll back; accept isINVALID_DECISION. In review, continue adds one extra cycle: it re-enters execution through the optimisation plan, or queues a new review if there is no plan.- Given an open escalation gate, when an Owner chooses continue, then the max-cycle counter allows one more cycle and execution re-enters for the optimisation plan.
- FR-15 Pending gates send reminders after 24 hours and are flagged
overdueafter 72 hours. Nothing is decided automatically.- Given a gate pending for 73 hours, when the project page loads, then the gate shows as overdue and the Owner has been notified twice.
Artifacts
- FR-16 Artifacts are versioned per type (
project_request,technical_specification,implementation_plan,optimization_plan). Each version stores Markdown content, a SHA-256 content hash, its parent version, the project version at creation, its author (user or agent run), and its status.- Given spec v2 exists, when a new spec is produced, then it is v3 with parent v2, status
draft.
- Given spec v2 exists, when a new spec is produced, then it is v3 with parent v2, status
- FR-17 Owners approve or reject a draft. Approve records approver and time and sets
locked(§4.3). Reject records the comment and re-runs the producing agent with it. Approving an approved or locked artifact is a no-op.- Given a locked artifact, when anyone tries to change it, then
ARTIFACT_LOCKED → 409. - Given a human-edited draft (FR-18), when it is rejected, then no agent re-runs; the comment is recorded.
- Given a locked artifact, when anyone tries to change it, then
- FR-18 Contributors and Owners can create a new draft version by editing the latest version’s Markdown in the browser (or by upload via the CLI). It goes through the same approval rules.
- Given locked spec v3, when a Contributor saves an edit, then spec v4
draftexists with parent v3.
- Given locked spec v3, when a Contributor saves an edit, then spec v4
- FR-19 Approving a new version of an upstream artifact invalidates downstream artifacts and execution per the cascade in §4.4, and records
ARTIFACT_INVALIDATEDfor each.- Given an approved plan and completed steps, when spec v4 is approved, then the plan is
invalidatedand all steps of that plan are reset.
- Given an approved plan and completed steps, when spec v4 is approved, then the plan is
- FR-20 Every artifact read re-checks the content hash. On mismatch the artifact is marked
corrupted, anintegrityadmin alert is raised, and the producing agent is re-run to regenerate it. The re-run happens only when the corrupted version is the current one (the open draft or the highest locked version) and was agent-authored (nocreated_by_user_id). The alert is anINTEGRITY_ALERTevent (categoryintegrity), listed on the admin events page.- Given a row whose content was altered in the DB, when it is read, then the API returns it with
integrity: "corrupted"and an alert exists.
- Given a row whose content was altered in the DB, when it is read, then the API returns it with
- FR-21 The artifacts page shows each type’s latest version, full history, and a side-by-side diff between any two versions.
- Given spec v1 and v3, when the user picks both, then the diff shows the changed lines.
Agents
- FR-22 All LLM calls go through one provider abstraction with Anthropic, OpenAI, Ollama and Fake implementations. The provider and model come from project settings, falling back to system defaults from configuration.
- Given a project set to Ollama with model
qwen2.5-coder, when any agent runs, then the Ollama provider is called with that model.
- Given a project set to Ollama with model
- FR-23 Each agent invocation is an agent run record: agent, phase, step, provider, model, prompt template version, input artifact versions, status, token counts, cost estimate, duration, error. Runs are visible on the project and in admin.
- Given a completed spec generation, when the user opens the run, then they see model, tokens, duration and which project request version it used.
- FR-24 Prompt templates are versioned files in the repo, one per agent, rendered with named placeholders (
{{PROJECT_REQUEST}},{{TECHNICAL_SPECIFICATION}}, …). A missing placeholder value fails the run before calling the provider.- Given the spec template references
{{PROJECT_REQUEST}}, when there is no approved project request, then the run fails withMISSING_INPUTand no provider call is made.
- Given the spec template references
- FR-25 Transient provider failures (timeouts, 429, 5xx) are retried up to 3 times with exponential backoff. After that, the run is
failed, the triggering job moves to dead letters, and Owners are notified.- Given a provider that returns 503 four times, when an agent runs, then 4 attempts are logged, the run is
failed, and a dead letter exists.
- Given a provider that returns 503 four times, when an agent runs, then 4 attempts are logged, the run is
- FR-26 A project’s token budget (optional) caps total tokens. A run that would start after the budget is used is refused with
BUDGET_EXCEEDED, and Owners are notified. Owners can raise the budget.- Given budget 100k and 100k used, when execution is started, then
BUDGET_EXCEEDED → 409.
- Given budget 100k and 100k used, when execution is started, then
Refinement, specification and planning
- FR-27 Starting refinement runs the Idea Refinement agent on the idea text. It returns a project request draft and an optional list of clarifying questions. The user answers some or all and runs another round; each round creates a new draft version.
- Given a draft with three questions, when the user answers two and re-runs, then a new draft exists that used those answers, and the answers are stored with the run.
- FR-28 On entering
specification, the Spec Generator runs on the approved project request and produces a technical specification with the eleven required sections (Overview, Functional Requirements, Technical Architecture, Data Models, API Contracts, Feature Specifications, Design System & UI/UX, Security & Authentication, Testing Strategy, Deployment & Infrastructure, Open Questions). Output missing any section is rejected and retried once with the list of missing sections.- Given output missing “Testing Strategy”, when it is validated, then the agent is re-run once with that feedback.
- FR-29 On entering
planning, the Plan Generator runs on the approved spec and returns a structured plan: ordered steps, each with number, title, task, declared files (create/modify), dependencies (earlier step numbers) and optional user instructions. AIDE stores the structure and renders it to Markdown for the artifact.- Given a plan where step 3 depends on step 5, when it is validated, then it is rejected with
INVALID_PLAN(dependencies must point to earlier steps) and retried once.
- Given a plan where step 3 depends on step 5, when it is validated, then it is rejected with
- FR-30 Approving a plan creates one execution step row per plan step for that plan version, all
pending.- Given an approved plan with 12 steps, when execution steps are listed, then there are 12
pendingsteps for that plan version.
- Given an approved plan with 12 steps, when execution steps are listed, then there are 12
Execution and Git
- FR-31 Starting execution runs steps one at a time, always picking the lowest-numbered
pendingstep whose dependencies arecompleted. The Code Generator receives the project request, spec, plan (with step statuses), project rules and the current content of the step’s declared files from the working copy.- Given steps 1–3 completed and step 4 depending on 2, when execution continues, then step 4 runs next.
- FR-32 The Execution Guardian validates each step’s output before it is accepted, running all checks and reporting every violation (§4.6). Any
errorrejects the step; warnings are recorded.- Given output touching 25 files with max 20, when the Guardian runs, then the step is
rejectedwith aFILE_LIMITviolation.
- Given output touching 25 files with max 20, when the Guardian runs, then the step is
- FR-33 A rejected step is retried with the violations given to the agent as feedback: attempts 1–3 that are rejected are retried, and the 4th rejection makes the step
blockedand creates an escalation gate (reasonstep_blocked). An unparseable code-generator reply is a rejected attempt (INVALID_OUTPUT).- Given a step rejected three times, when the fourth result is rejected, then the step is
blockedand an escalation gate exists.
- Given a step rejected three times, when the fourth result is rejected, then the step is
- FR-34 An accepted step is written to the project’s working copy and committed on the project branch
aide/<project-slug>-<short-id>with subject[AIDE][Phase:execution][Step:N] <title>and a body listing the files. It is pushed when the provider is GitHub. The step records branch, commit SHA and file manifest.- Given step 4 accepted, when it is synced, then a commit with that subject is on the project branch and step 4 shows its SHA.
- FR-35 Git providers: GitHub (clone over HTTPS with the project token; PR via the GitHub API) and Local (a Git repo on the AIDE data volume; the “PR” is a recorded branch comparison). Before the first step, AIDE prepares the working copy and project branch from the default branch.
- Given a Local project, when execution starts, then a repo exists under the data volume with the project branch checked out.
- FR-36 When all steps of the current plan are completed, AIDE opens one PR (GitHub) from the project branch to the default branch, with a description generated from the spec, the plan and the step list, and advances the project to
review.- Given the last step completes, when the sync finishes, then one PR exists and the phase is
review.
- Given the last step completes, when the sync finishes, then one PR exists and the phase is
- FR-37 Users can pause and resume execution. Pause lets the running step finish and starts no new step.
- Given execution paused during step 5, when step 5 finishes, then step 6 does not start until resumed.
Review
- FR-38 On entering
review(or on manual trigger), AIDE runs a review cycle: deterministic checks (§4.7) plus the Code Review agent, which scores compliance per category against the spec with evidence (file and reason), lists violations, missing features and drift areas, and produces an optimisation plan.- Given a review cycle, when it completes, then a compliance report exists with category scores, overall score, drift level, decision and evidence, plus an
optimization_plandraft.
- Given a review cycle, when it completes, then a compliance report exists with category scores, overall score, drift level, decision and evidence, plus an
- FR-39 The release decision follows §4.5.
release_eligiblecreates apre_releasegate in every approval mode.review_requiredre-enters execution with the optimisation plan’s steps once that plan is approved (FR-40), if cycles remain. If the review proposes no steps, it gets one corrective re-run and then escalates with reasonno_optimization_plan.escalation_requiredcreates an escalation gate.- Given score 0.82, threshold 0.90, drift low, cycle 1 of 3, when the decision runs, then the project re-enters
executionwith the optimisation steps.
- Given score 0.82, threshold 0.90, drift low, cycle 1 of 3, when the decision runs, then the project re-enters
- FR-40 Re-entering execution from review runs the optimisation plan’s steps (generated by the review agent and approved like any artifact) on the same branch, then returns to review. While the plan is a draft the project stays in
review; approving it appliesreview → execution(the plan must be locked with steps left). Rejecting or corrupting it revises the plan for the same cycle.- Given an approved optimisation plan with 3 steps, when they complete, then review cycle 2 starts automatically.
- FR-41 The review page shows the latest report, compliance score trend across cycles, category breakdown, drift level and areas, and evidence for each violation.
- Given three cycles, when the review page loads, then the trend chart shows three points.
Release and rollback
- FR-42 Approving the
pre_releasegate moves the project torelease(statusreleasing) and starts the release job. The job merges the project PR (or the project branch, for Local) into the default branch, creates an annotated tagv<project version>-aide, sets statusreleased, and records the release. The tag uses the version before the release completes (approving at v6 givesv6-aide; the project is then v7). On GitHub the PR is merged through the API with the method inGitHub:ReleaseMergeMethod(mergedefault,squashorrebase), pinned to the head commit AIDE checked. Release is idempotent: a tag of that version that already contains the branch head is reused as a no-op success. If the tag name exists on other commits (a re-release after rollback), the first freev<N>-aide.2,.3… is created and recorded.- Given a pending pre-release gate, when an Owner approves it, then the branch is merged, the tag exists, and status is
released.
- Given a pending pre-release gate, when an Owner approves it, then the branch is merged, the tag exists, and status is
- FR-43 A merge conflict blocks the release. The release record is
failedwith the conflicting files, the project stays inreleasewith statusreleasing, and Owners are notified. After resolving the conflict on the branch, an Owner retries withPOST /release.- Given the default branch changed the same lines, when the release job runs, then the release is
failedwithMERGE_CONFLICTand the files listed; a retry after resolution succeeds. Other release failures are recorded on the release row (GIT_ERROR,RELEASE_CANCELLED).
- Given the default branch changed the same lines, when the release job runs, then the release is
- FR-44 Owners roll back to an earlier project version with a reason. The rollback restores the phase active at that version, invalidates artifacts and resets steps created after it, cancels pending gates and queued jobs, and records a rollback event — all in one transaction (§4.8). Released projects can roll back too; the branch, tags and release history are kept.
- Given version 7 in
review, when an Owner rolls back to version 4 (inplanning), then the phase isplanning, artifacts from versions 5–7 are invalidated, and a rollback record lists them.
- Given version 7 in
- FR-45 Rollback is limited to one per project per minute and refused while a step or agent run is in progress or a release job is running (
ROLLBACK_BLOCKED, withdetails: ["reason: step_in_progress|agent_run_in_progress|release_running|release_partially_applied"]).- Given a rollback 30 seconds ago, when another is requested, then
RATE_LIMITED → 429.
- Given a rollback 30 seconds ago, when another is requested, then
Real-time, notifications and activity
- FR-46 Project pages update live. The server pushes project events (phase, gate, artifact, step, run, review, release, rollback) to everyone viewing that project, and the page refreshes the affected data.
- Given two users on the same project page, when a gate is approved, then the other user’s page shows it within 2 seconds without reloading.
- FR-47 Notifications are sent for: gate required, gate overdue, artifact ready for approval, step blocked, run failed, budget exceeded, review completed, release completed or failed, rollback. Each user sees an in-app notification centre with unread count. Owners configure project channels: email (SMTP), Slack and Teams incoming webhooks, and a generic webhook (HMAC-signed body: header
X-AIDE-Signature: sha256=<hex HMAC-SHA256 of the raw body>, plusX-AIDE-Timestamp,X-AIDE-Delivery,X-AIDE-Event). A webhook channel requires a signing secret (VALIDATIONotherwise). Recipients: Owners for actionable types (gates, drafts, blocked steps, failed runs, budget, failed release); all members for review completed, release completed and rollback. Users can mute types in-app (user_notification_preferences); project channels are set by Owners.BUDGET_EXCEEDEDis written on every refusal but notifies only on the first refusal since the budget last changed. The unread count is polled every 30 s (paused while the tab is hidden) and refreshed shortly after a live event that can notify.- Given Slack configured for “gate required”, when a gate is created, then one Slack message is sent.
- FR-48 Gate notifications include signed one-click approve/reject links. A link is single-use, expires after 72 hours, and opens a confirmation page that requires the user to be signed in as an Owner of that project. Links exist only for
spec_approval,plan_approvalandpre_releasegates, and only whenApprovals:SigningKeyis at least 32 characters; otherwise the notification links to the project page.- Given a used link, when it is opened again, then the page says it has already been used and nothing changes.
- FR-49 Delivery is retried with backoff per channel. A channel that succeeds is never re-sent when another channel fails. There are 5 attempts with backoff 1 min, 5 min, 30 min, 2 h. A delivery that can’t be attempted is
skipped(email without SMTP settings, or the gate was decided before a retry). Every send is capped at 60 s.- Given Slack succeeds and email fails, when retry runs, then only email is retried.
- FR-50 Each project has an activity feed: transitions, gate decisions, artifact changes, agent runs and step results, newest first, filterable by type.
- Given 200 events, when the feed loads, then it shows the latest 50 with “load more”.
Admin and CLI
- FR-51 Admins see a cross-project event log (filter by project, type, status), agent run log, and dead letters with their error. They can retry a dead letter, which re-enqueues the original job. The event “status” filter is the outcome
okorfailure(events are append-only). Retry needs a browser session, and returnsNOT_FOUNDif the job isn’tdead, orCONFLICT → 409if an execution job is already queued or running for the project.- Given a dead letter, when an Admin retries it, then the job runs again and the dead letter is marked
retried.
- Given a dead letter, when an Admin retries it, then the job runs again and the dead letter is marked
- FR-52 The
aideCLI (a .NET global tool) supports:config set|get|path,login,logout,projects list,project init,status <id>,artifact list|history|show|approve|reject|edit,execute <id>(--pause,--resume),review <id>,release <id>(approves the pendingpre_releasegate, else retries the release),rollback <id> <version> --reason,logs <id>. The token is set only throughaide login(prompt, stdin or--token) and is stored per API URL and sent only to that URL; plain http is allowed only for loopback hosts. Settings precedence: flag, thenAIDE_API_URL/AIDE_TOKEN/AIDE_CONFIG, then the config file, thenhttp://localhost:8080. Output is human-readable by default and JSON with--json. Exit codes: 0 success, 1 error, 2 usage, 3 auth (401/403), 4 conflict (409).- Given a valid token, when
aide status <id> --jsonruns, then it prints the project state as JSON and exits 0; on403it exits 3.
- Given a valid token, when
4. Domain rules
4.1 Phases and transitions
- Order:
refinement → specification → planning → execution → review → release. Forward transitions go to the next phase only. - The other allowed moves are:
review → execution(review cycle), any phase → an earlier phase (rollback, or the invalidation fallback in §4.4), andrelease → release(no-op). A rollback may restore the phase the project is already in, at an earlier version. - Entry conditions:
| Enter | Requires |
|---|---|
| specification | project_request approved/locked |
| planning | technical_specification approved/locked |
| execution | spec and implementation_plan approved/locked; plan has ≥ 1 step; Git working copy prepared |
| review | all steps of the current plan (or optimisation plan) completed |
| release | latest decision release_eligible (or an escalation accepted); pre_release gate approved (the approval is the trigger); no other pending gate |
-
Entering
execution(by advance orplan_approval) prepares the Git working copy first. Entering it fromreviewneeds the optimisation plan locked with at least one step left. - Status follows phase:
refinement|specification|planning → active,execution → building,review → reviewing,release → releasing.releasedis set only when the release completes.rolled_backis set by a rollback and cleared by the next forward transition. - The project version increments on every forward phase transition and on release. It never increments on
review → execution(the review cycle loop) or a no-op;execution → reviewdoes increment. - A gated advance records the requester’s reason in the gate; the transition recorded on approval reads “
( gate approved[: ])". - Every transition is applied with optimistic concurrency on the project. Concurrent attempts produce one success and one
CONFLICT → 409.
4.2 Gates by approval mode
| Entering | strict | advisory | autonomous |
|---|---|---|---|
planning (spec_approval) |
gate | gate | none |
execution (plan_approval) |
gate | none | none |
release (pre_release) |
gate | gate | gate |
| escalation (§4.5) | gate | gate | gate |
- With no gate, the transition applies as soon as its conditions are met.
- A gate decision is final. Only pending gates can be decided (
GATE_NOT_PENDING → 409). - Rejecting a
spec_approvalgate re-runs the Spec Generator, and rejecting aplan_approvalgate re-runs the Plan Generator. The old stack re-ran idea refinement for spec gates; that’s fixed here. release_eligiblealways creates apre_releasegate, in every approval mode.- Escalation gates are decided with
continue,rollback(withtargetVersion) oraccept. Anything else, includingreject, isINVALID_DECISION → 400. - Approving a new artifact version cancels pending gates that went stale (
GATE_CANCELLED): all of them on a phase fallback, those whose artifact was invalidated, and those holding an older version of the type. The Owner re-requests the advance. Gates are never re-pointed, because decisions are final.
4.3 Artifact lifecycle
- Status flow:
draft → locked(approve),draft → rejected(reject),draft|locked → invalidated(cascade or rollback), and any status →corrupted(hash mismatch). approvedexists only as the moment of approval; stored rows go straight tolocked.- Only one
draftper type at a time. A new draft supersedes the previous draft, which becomessuperseded. Creating a draft takes an optionalbaseVersion; a stale base isCONFLICT → 409withlatestVersionin the details. - Drafts are allowed from the type’s producing phase onwards; an optimisation plan only in
review; none whilereleasing(WRONG_PHASE). A version with nocreated_by_user_idis agent-authored. - Approving a draft created at a project version above the current one (stale after a rollback or fallback) is
ARTIFACT_NOT_DRAFT → 409. - The “current” artifact of a type is the highest version with status
locked. - Content hash: SHA-256 of the UTF-8 content with line endings normalised to
\n, stored as lowercase hex.
4.4 Invalidation cascade (when a new version of the left-hand type is approved)
| Approved | Invalidates |
|---|---|
| project_request | technical_specification, implementation_plan, optimization_plan, all steps |
| technical_specification | implementation_plan, optimization_plan, all steps |
| implementation_plan | optimization_plan, steps of older plan versions |
| optimization_plan | nothing |
- Invalidation never deletes rows. It covers drafts as well as locked versions, so a stale draft can’t be approved. Steps are reset to
invalidated, queued execution jobs are cancelled, and the phase falls back to the phase that produces the first invalidated artifact. The fallback is recorded as its own transition kind (earlier phase, status follows phase, project version unchanged). - Example: approving spec v4 while in
review→ phaseplanning, statusactive.
4.5 Review decision (one table; replaces the old stack’s three inconsistent rule sets)
Inputs: score s (0–1), threshold t, drift d, cycle c, max cycles m. Evaluate the rows in order:
| # | Condition | Decision |
|---|---|---|
| 1 | s < 0.75 or d = severe |
escalation_required |
| 2 | s ≥ t |
release_eligible |
| 3 | c ≥ m |
escalation_required (max cycles) |
| 4 | c ≥ 2 and s ≤ previous s |
escalation_required (no improvement) |
| 5 | otherwise | review_required |
Worked examples (t = 0.90, m = 3):
- 0.93 / low / c1 → release_eligible
- 0.82 / low / c1 → review_required
- 0.82 / low / c3 → escalation (max cycles)
- 0.84 then 0.83 / c2 → escalation (no improvement)
- 0.95 / severe → escalation
- 0.70 / none → escalation
The previous score in row 4 is the latest earlier-cycle report on the current timeline (a failed review cycle doesn’t skip the row). Reports from before the latest rollback, or above the current project version, are not part of the timeline. Escalation reasons are low_score, severe_drift, max_cycles, no_improvement, step_blocked and no_optimization_plan.
4.6 Execution Guardian checks (all run; any error rejects)
- single-step (error): output targets exactly the requested step, which is
pendingorin_progress, and contains at least one file. - file-limit (error): files touched ≤ project max files per step.
- dependency-order (error): all declared dependencies are
completed. - plan-alignment: a produced file not declared for this step is an error; a declared file that wasn’t produced is a warning. Paths are normalised (forward slashes, no leading
./) and compared exactly. - scope-creep (error): no file declared by another step; no protected path (
.git/,.aide/, CI workflows, lockfiles, env files) unless declared in this step. - secret-scan (error): no content matching known secret patterns (API keys, private keys, tokens). Secret values are never echoed in violations.
Path safety (applies to plan validation and the Guardian):
- A path that is absolute, contains
..or a drive letter, has a.gitsegment (including Windows aliases such as.git.or~1short names), control characters, line breaks or backticks, reserved device names (CON,NUL,AUX,PRN,COM,LPT),:<>"|?*, or a segment ending in a dot or space isUNSAFE_PATH(error), even when declared. - Nothing under
.aide/is writable by agents, and no plan may declare it. Agent-generated plans may not declare any other protected path either; Owner-edited plans may (§4.6 “unless declared”). - Protected paths match case-insensitively. Duplicate normalised produced paths are an error (
DUPLICATE_PATH). - Plan approval re-validates the plan against the project’s current max files per step.
4.7 Review scoring
- Categories and weights: feature coverage 0.30, API contracts 0.20, data model 0.15, architecture 0.15, security 0.10, testing 0.10.
- Category scores: the review agent scores each category 0–1 with evidence. Deterministic checks then apply fixed penalties (critical −0.20, major −0.10, minor −0.03 each, floored at 0) to their category.
- Deterministic checks are stack-agnostic (severities: secret = critical, missing spec endpoint = major, dependency not on the allowlist = major): secret scan (security), dependency allowlist extracted from the spec’s architecture section (architecture), and every spec API endpoint path appearing somewhere in the code (API contracts). Only deterministic findings carry penalties; the agent’s own violations are reported without one. The allowlist check is skipped when the spec has no Dependencies list. Lines the secret scan flags are redacted before code is sent to the review agent.
- Overall score = Σ category × weight, rounded to 3 dp.
- Drift level comes from the agent’s drift areas, weighted critical 3, major 2, minor 1: total 0 → none, 1–3 → low, 4–8 → moderate, ≥ 9 → severe.
4.8 Rollback
- The target must be an existing project version below the current version. A target whose recorded phase is
releaseis refused (INVALID_TARGET_VERSION). Checks run in the orderINVALID_TARGET_VERSION,ROLLBACK_BLOCKED,RATE_LIMITED(withRetry-After). The reason is trimmed; blank isVALIDATION. - The phase is restored from the latest state transition recorded at that version (refinement if there is none, as for v1).
- Rollback invalidates artifacts with
project_version_at_creation > target. Steps belonging to invalidated plans are reset. Pending gates are cancelled, queued jobs are cancelled, and the review state (review_cycle, extra cycles) is cleared. Blocked and rejected steps of surviving plans return topendingwith attempt 0; completed steps stay completed, because their commits stay on the branch. - Rollback locks the project’s queued and running job rows in its transaction. A running release job blocks it; a queued release job is cancelled (
RELEASE_CANCELLED). - The Git branch is not rewritten. Later commits stay in history, and the next execution adds new commits.
- The project version is set to the target; the next forward transition makes it target + 1.
4.9 Background work
- Every state change and its follow-up jobs are written in one transaction (outbox).
- Workers claim jobs with
FOR UPDATE SKIP LOCKED. A job gets 4 attempts (1 + 3 retries) with backoff 2 s / 8 s / 32 s, then becomes a dead letter. Non-transient failures fail at once. An expired lease counts as a failed attempt. A retried dead letter becomesretriedand a new queued job with the same payload is created. - Handlers are idempotent: step completion is keyed by step id, so a re-delivered completion doesn’t double-count, which was a bug in the old stack.
- At most one execution job runs per project at a time, enforced by a partial unique index on
jobs(project_id)wheretype = 'execution'and status isqueuedorrunning. - One execution job is one step attempt. The next job is enqueued in the same transaction as the step outcome, unless paused, blocked or done. A redelivered job finds its step’s commit by an
AIDE-Step-Idtrailer. - A job’s PR or release work re-checks the phase after slow Git work and records nothing if a rollback superseded it.
5. Data model
| Entity | Key fields | Constraints / indexes | Notes |
|---|---|---|---|
| user | id, email (citext), name, system_role, github_id?, avatar_url?, created_at | unique email; unique github_id | ASP.NET Identity tables underneath; all tables are plural snake_case |
| api_token | id, user_id, name, token_hash, last_used_at, expires_at?, revoked_at? | unique token_hash | plain value shown once; also created_at on api_token, project_member and approval_link |
| project | id, name, slug, idea, current_phase, status, project_version, approval_mode, compliance_threshold numeric(3,2), max_files_per_step, max_review_cycles, token_budget?, tokens_used, execution_paused, review_cycle, extra_review_cycles, last_rollback_at?, created_at, updated_at | index (status, current_phase); xmin concurrency |
|
| project_member | project_id, user_id, role | PK (project_id, user_id) | ≥ 1 owner enforced in domain |
| project_git_settings | project_id, provider (github/local), repo_url?, default_branch, token_encrypted?, branch_name?, pull_request_url?, pull_request_number? | PK project_id | token via Data Protection |
| project_llm_settings | project_id, provider, default_model, models_by_agent jsonb, api_key_encrypted? | PK project_id | |
| project_notification_settings | project_id, channels jsonb (type, target, secret_encrypted, events[]) | PK project_id | |
| artifact | id, project_id, type, version, parent_version?, content text, content_hash, status, project_version_at_creation, created_by_user_id?, agent_run_id?, approved_by?, approved_at?, rejection_comment?, created_at | unique (project_id, type, version); index (project_id, type, status) | content immutable once inserted |
| approval_gate | id, project_id, type (spec_approval/plan_approval/pre_release/escalation), artifact_id?, status (pending/approved/rejected/cancelled), decision?, reviewer_id?, comment?, overdue, reminded_at?, decided_at?, created_at | partial unique (project_id, type) where pending | escalation decision: continue/rollback/accept; target_version? for rollback decisions |
| execution_step | id, project_id, plan_artifact_id, step_number, title, task, declared_files jsonb, dependencies int[], user_instructions?, status (pending/in_progress/completed/rejected/blocked/invalidated), attempt, violations jsonb?, file_manifest jsonb?, commit_sha?, branch_name?, started_at?, completed_at? | unique (plan_artifact_id, step_number) | |
| agent_run | id, project_id, agent, phase, step_id?, provider, model, prompt_version, input_artifacts jsonb, user_feedback?, status, attempts, input_tokens, output_tokens, cost_estimate numeric(10,4), duration_ms, error?, output jsonb?, completed_at?, created_at | index (project_id, created_at) | replaces agent_logs; one row per job (run id = job id) |
| state_transition | id, project_id, from_phase, to_phase, actor, reason, project_version, created_at | index (project_id, created_at) | |
| compliance_report | id, project_id, review_cycle, project_version, score numeric(4,3), threshold numeric(3,2), category_scores jsonb (agent score, penalty, evidence {file, reason}), violations jsonb (source deterministic/agent, evidence), missing_features jsonb, drift_level, drift_areas jsonb, decision, escalation_reason?, optimization_plan_artifact_id? (FK, set null), agent_run_id, created_at | index (project_id, review_cycle) | |
| rollback_event | id, project_id, from_version, target_version, restored_phase, reason, invalidated_artifact_ids uuid[], reset_step_ids uuid[], requeued_step_ids uuid[], cancelled_gate_ids uuid[], cancelled_job_ids uuid[], triggered_by, created_at | index (project_id, created_at) | |
| release | id, project_id, project_version, attempt, tag, merge_commit_sha?, pr_url?, status (succeeded/failed), error_code?, error_message?, conflicting_files, created_at, completed_at? | unique (project_id, project_version, attempt) | a re-release after rollback reuses a version number |
| event | id, project_id?, type, payload jsonb, actor, created_at | index (project_id, created_at); index (type) | append-only audit log; source for the live feed |
| job | id, type, payload jsonb, project_id?, status (queued/running/succeeded/failed/dead/cancelled/retried), attempts, run_after, locked_by?, locked_at?, last_error?, created_at, completed_at? | index (status, run_after); partial unique (project_id) where type = execution and status in (queued, running) | outbox + work queue |
| notification | id, user_id, project_id?, type, title, body, link, notification_key?, read_at?, created_at | index (user_id, read_at); unique (user_id, notification_key) | in-app; key is <type>:<sourceId> so redelivery never duplicates |
| user_notification_preferences | user_id, muted_types, updated_at | PK user_id | in-app mute list only |
| notification_delivery | id, project_id?, notification_key, channel, status (pending/sent/failed/dead/skipped), attempts, last_error?, next_attempt_at?, created_at, sent_at? | unique (notification_key, channel) | per-channel idempotency |
| approval_link | id, gate_id, decision, nonce_hash, expires_at, used_at? | unique nonce_hash | single use |
Database: PostgreSQL (major version: 17)
Conventions: snake_case names, UUIDv7 keys generated by the app, UTC timestamptz, enums stored as strings, jsonb only for payloads and evidence lists. The working copies for Git live on a persistent volume (/data/workspaces/<project-id>), not in the database.
6. API
All routes are under /api, JSON, ProblemDetails errors with errorCode. “Member” means any project role, “Writer” means Contributor or Owner.
| Method | Route | Body / query | Response | Errors (code → status) | Auth |
|---|---|---|---|---|---|
| POST | /api/auth/register | email, name, password | 201 user | EMAIL_TAKEN → 409, VALIDATION → 400 |
public |
| POST | /api/auth/login · /logout | email, password | 204 | INVALID_CREDENTIALS → 401 |
public / user |
| GET | /api/auth/me | — | user + system role | — | user |
| GET | /api/auth/github · /github/link · /github/callback | — | redirect (404 when OAuth isn’t configured); a failure redirects to /login?error=GITHUB_AUTH_FAILED&reason=oauth_failed\|link_required\|already_linked (a failed link lands on /account?error=…) |
— | public / user (link) |
| GET | /api/auth/providers · /antiforgery | — | whether GitHub sign-in is configured / antiforgery token cookie | — | public / user |
| GET/POST/DELETE | /api/me/tokens[/:id] | name, expiresAt? | token list / 201 token (once) / 204 | NOT_FOUND → 404 |
user |
| GET/PATCH/POST | /api/me/notifications · /unread-count · /:id/read · /read-all | page | notifications / unread count | — | user |
| GET/PUT | /api/me/notification-preferences | mutedTypes | preferences | VALIDATION → 400 |
user |
| GET | /api/projects | page, limit≤100, search, phase, status, sortBy, sortOrder | paged list | — | user |
| POST | /api/projects | wizard fields | 201 project | VALIDATION → 400 |
user |
| GET/PATCH/DELETE | /api/projects/:id | partial project | project / 204 | NOT_FOUND → 404, FORBIDDEN → 403, CONFLICT → 409 |
member / owner |
| GET | /api/projects/:id/state | — | phase, status, version, artifact summary, execution counts, review state, pending gates | — | member |
| GET/POST/PATCH/DELETE | /api/projects/:id/members[/:userId] | email, role | members | USER_NOT_FOUND → 404, LAST_OWNER → 409 |
member / owner |
| GET | /api/projects/:id/settings | — | git, llm, notifications (secrets as flags) | — | owner |
| PATCH | /api/projects/:id/settings/git · /llm · /notifications | section fields | section | VALIDATION → 400 |
owner |
| POST | /api/projects/:id/settings/git/test | — | ok, reason | — | owner |
| POST | /api/projects/:id/phase/advance | reason? | state (or pending gate) | PHASE_PRECONDITION_FAILED → 409, GATE_PENDING → 409 |
writer |
| GET | /api/projects/:id/refinement | — | current round: draft, questions, answers, status (cancelled with a reason if the job was skipped) |
— | member |
| POST | /api/projects/:id/refinement/run | answers[]? | 202 run | BUDGET_EXCEEDED → 409, WRONG_PHASE → 409, REFINEMENT_IN_PROGRESS → 409 |
writer |
| GET | /api/projects/:id/artifacts | — | latest per type | — | member |
| GET | /api/projects/:id/artifacts/:type/latest · /history · /versions/:v | — | artifact(s) with integrity | NOT_FOUND → 404 |
member |
| GET | /api/projects/:id/artifacts/:type/diff | from, to | diff (Myers, 3 lines of context, collapsed skipped runs; refuses more than 100,000 lines with VALIDATION) |
NOT_FOUND → 404 |
member |
| POST | /api/projects/:id/artifacts/:type | content, baseVersion? | 201 draft | WRONG_PHASE → 409, CONFLICT → 409 (stale base), INVALID_PLAN → 400 (plan types; per-step reasons) |
writer |
| POST | /api/projects/:id/artifacts/:artifactId/approve · /reject | comment? | artifact | ARTIFACT_NOT_DRAFT → 409, ARTIFACT_LOCKED → 409, INVALID_PLAN → 400 (approve) |
owner |
| GET | /api/projects/:id/gates | status? | gates | — | member |
| POST | /api/projects/:id/gates/:gateId/approve · /reject | comment? (+ decision, targetVersion for escalation) | gate | GATE_NOT_PENDING → 409, INVALID_DECISION → 400 |
owner |
| GET | /api/projects/:id/execution/steps | planType?, planVersion? | steps of the current plan (newest locked plan of either type) when omitted | — | member |
| POST | /api/projects/:id/execution/start · /pause · /resume | — | 202 state | PHASE_PRECONDITION_FAILED → 409, BUDGET_EXCEEDED → 409 |
writer |
| GET | /api/projects/:id/runs[/:runId] | agent?, page | agent runs | — | member |
| GET/POST | /api/projects/:id/git/pull-request | — | recorded PR / 202 re-queues the PR job when no PR is recorded for the current plan | WRONG_PHASE → 409 |
member / writer |
| POST | /api/projects/:id/review | — | 202 | WRONG_PHASE → 409, REVIEW_IN_PROGRESS → 409 |
writer |
| GET | /api/projects/:id/compliance/latest · /trend · /drift | — | report / series / drift | NOT_FOUND → 404 |
member |
| POST | /api/projects/:id/release | — (retries a failed release) | 202 release | NOT_RELEASING → 409, RELEASE_IN_PROGRESS → 409 |
owner |
| GET | /api/projects/:id/releases · /releases/latest | — | release attempts | NOT_FOUND → 404 |
member |
| POST | /api/projects/:id/rollback | targetVersion, reason | rollback record | INVALID_TARGET_VERSION → 400, ROLLBACK_BLOCKED → 409, RATE_LIMITED → 429 |
owner |
| GET | /api/projects/:id/rollbacks · /versions | — | rollback history / versions up to the current one with their recorded phase | — | member |
| GET | /api/projects/:id/activity | type?, before? | events page | — | member |
| GET/POST | /api/approvals/:token | decision | confirmation view data / gate | LINK_EXPIRED → 410, LINK_USED → 409, FORBIDDEN → 403 |
user (owner of project) |
| GET | /api/admin/events · /runs · /dead-letters | filters (events: project, type, status ok|failure), page, limit |
lists with total | — | admin (a token is enough) |
| GET | /api/admin/users | page, limit | users | — | admin |
| POST | /api/admin/dead-letters/:jobId/retry | — | job; writes DEAD_LETTER_RETRIED |
NOT_FOUND → 404 (not dead), CONFLICT → 409 |
admin (browser session) |
| PATCH | /api/admin/users/:id/role | systemRole | user | CONFLICT → 409 (last Admin) |
admin (browser session) |
| GET | /api/health/live · /ready | — | 200 / 503 | — | public |
| WS | /api/hubs/projects | join(projectId) | projectEvent messages |
FORBIDDEN on join |
member |
Common errors on every route: VALIDATION → 400 (also 405 and 415), UNAUTHENTICATED → 401, FORBIDDEN → 403, ANTIFORGERY → 403 (cookie-authenticated mutations without a valid X-XSRF-TOKEN header), NOT_FOUND → 404 (also for projects the user isn’t a member of), CONFLICT → 409 (concurrency).
Wire conventions: enums are snake_case strings (admin, spec_approval); validation error keys are camelCase (git.repoUrl, channels[0].target). Agent run and job failure codes (not HTTP errors) include MISSING_INPUT, MISSING_SECTIONS, INVALID_OUTPUT, INVALID_PLAN, PROVIDER_NOT_CONFIGURED, PROVIDER_ERROR. Adding a value to a response enum (an event category, a diff line kind) is additive. The hub requires a matching Origin (or Live:AllowedOrigins); a cookie WebSocket upgrade without one is refused with FORBIDDEN.
7. Pages and UI
| Route | Page | Key content / states |
|---|---|---|
| /login, /register | Sign in / register | email + password, “Continue with GitHub”; error · loading |
| /projects | Projects list | search, phase/status filters, sort, paging, phase badge; empty · loading · error |
| /projects/new | New project wizard | 4 steps (Idea, Configuration, Git, Review & create), per-step validation, back/next |
| /projects/:id | Overview | phase timeline with current phase, status, version, pending gates with actions, next action button, activity feed, live updates; forbidden · not found |
| /projects/:id/refinement | Refinement | current draft, clarifying questions with answer fields, “Run another round”, run status |
| /projects/:id/artifacts | Artifacts | card per type: latest version, status, integrity badge |
| /projects/:id/artifacts/:type | Artifact detail | rendered Markdown, version picker, history, diff view, edit (new draft), approve/reject with comment (owner) |
| /projects/:id/execution | Execution | step list with status, attempt, violations, commit link; start/pause/resume; live progress |
| /projects/:id/review | Review | latest score vs threshold, category breakdown, trend chart, drift level/areas, evidence list, decision |
| /projects/:id/runs | Agent runs | table: agent, model, tokens, duration, status; detail drawer |
| /projects/:id/settings | Settings | tabs: General, Members, Git (with Test), LLM, Notifications, Danger zone (delete, rollback) |
| /approvals/:token | Approval link | gate summary, confirm approve/reject; expired · used · forbidden |
| /notifications | Notifications | list with unread state, mark read |
| /account | Account | profile, linked GitHub, API tokens (create shows token once, revoke) |
| /admin, /admin/events, /admin/runs, /admin/dead-letters, /admin/users | Admin | filters, paging, retry, role change; forbidden for non-admins |
UI rules:
- Dark and light theme (system default).
- Destructive actions (delete, rollback, reject) need confirmation; delete needs the typed project name.
- Owner-only actions are hidden for other roles, and the API enforces them anyway.
- Long-running actions show a pending state until the live event arrives.
8. Non-functional requirements
- Performance: API p95 < 300 ms for reads with 50 projects and 1,000 artifacts locally (excluding agent calls). Live events reach browsers within 2 s. Agent calls stream nothing to the UI in v1; the run shows as running.
- Reliability: no lost work on pod restart. Jobs marked
runningby a dead worker are re-queued after a 5-minute lease expiry. - Security:
- HttpOnly, SameSite=Lax cookies,
Securewhenever the request is HTTPS (Auth:Cookie:SecurePolicy, defaultSameAsRequest, because the local Kubernetes app is served over plainhttp://localhost:8080; setAlwaysbehind TLS). - Antiforgery for cookie-authenticated mutations: a double-submit
XSRF-TOKENcookie andX-XSRF-TOKENheader; failure isANTIFORGERY → 403. Bearer-token requests are exempt. - Rate limiting on login (5/min/IP) and register.
- Secrets encrypted at rest with ASP.NET Data Protection; keys persisted to the data volume.
- No secret ever logged. Webhook bodies HMAC-signed. Approval tokens HMAC-signed and single-use.
- HttpOnly, SameSite=Lax cookies,
- Logging and observability:
- Structured JSON logs with project id, run id and job id.
- OpenTelemetry traces and metrics to console in dev.
/api/health/readychecks the DB and that migrations are applied.
- Accessibility: WCAG 2.2 AA for core flows (keyboard navigation, focus states, labelled forms, colour contrast in both themes).
- Browsers: current Chrome, Edge, Firefox and Safari. Desktop-first, usable at 375 px width.
- Testing:
- Every domain rule in §4 has unit tests, including the worked examples.
- Every endpoint has integration tests against PostgreSQL (Testcontainers).
- E2E uses the Fake LLM provider and the Local Git provider, so no network or keys are needed.
9. Local deployment
- URL: the app runs on Docker Desktop Kubernetes at
http://localhost:8080, in namespaceaide. The web container serves the SPA and proxies/api, including WebSocket upgrade for/api/hubs/. - Workloads:
db(PostgreSQL 17 StatefulSet),api(one replica; the API and background workers run in the same process in v1),web, and thedb-migrateJob. - Volumes:
aide-data(5 Gi PVC) on the API, mounted at/data, for Git working copies, local Git repos and Data Protection keys. - Secrets (
secrets.env, git-ignored):- DB password and connection string
- Data Protection application name
- default LLM provider keys (Anthropic, OpenAI)
- GitHub OAuth client id/secret
- approval-link signing key
- SMTP settings
- Optional: Ollama reached at
http://host.docker.internal:11434. Mailpit can be added to the dev compose file for email testing. - Seed data (dev only):
- admin
admin@aide.localwith a generated password logged once at Warning (by the pod that creates the account; it is not logged again) - enabled by
Seed:Enabled(true in Development and the local Kubernetes overlay, false elsewhere); the demo project is created once per database, so deleting it doesn’t bring it back - one demo project in
refinementusing the Fake provider and Local Git
- admin
- Dev loop: API on 5080, Vite on 5173, compose DB on 5432.
10. Decisions
Each decision is an improvement over the old stack (../AIDE-Platform-old-stack/).
- D1 — One backend, no broker.
- Old: seven NestJS services plus Redis/BullMQ and NATS/Kafka.
- New: a modular monolith with a PostgreSQL job/outbox table and hosted workers (§4.9).
- Why: transactional consistency between state and follow-up work, far less to run on a laptop.
- D2 — One review decision table (§4.5).
- Old: the state machine, decision engine and failure handler used different thresholds (0.75 vs 0.85, “cycle > 2” vs max cycles). They also disagreed on whether low compliance triggers a soft gate or an automatic rollback.
- New: low compliance escalates to a human; nothing rolls back automatically.
- D3 — The escalation (“soft”) gate is a real gate type with recorded decisions. In the old stack it was only an event with no record.
- D4 —
releasedis set only after the release succeeds. The old stack set it on entering the release phase, before the merge. - D5 — Rollback restores the phase deterministically. It uses state transitions and
project_version_at_creation. The old stack mixed project and artifact version numbers and left the phase to an unimplemented manager. - D6 — Structured agent output for plans and reviews (JSON schema / tool use), rendered to Markdown for display. This replaces regex parsing of Markdown checklists.
- D7 — Prompt templates ship in the repo, versioned, and every agent run records them with model and token usage. The old stack loaded prompts from a
.github/skills/folder that isn’t in the repo. - D8 — Stack-agnostic review.
- Old: the checkers assumed the generated code was NestJS/Prisma/TypeScript (decorator scanning, kebab-case suffixes,
apps/folders). - New: the review agent scores against the project’s own spec, and the deterministic checks are language-independent (§4.7).
- Old: the checkers assumed the generated code was NestJS/Prisma/TypeScript (decorator scanning, kebab-case suffixes,
- D9 — Project membership and roles. Old: any authenticated user could see any project.
- D10 — Persisted, encrypted project settings. Old: Git token, LLM and notification settings were accepted but never saved.
- D11 — Cookie auth + GitHub login for the browser; API tokens for the CLI. This replaces JWTs held by the dashboard and fits the same-origin deployment.
- D12 — Local Git provider so the whole flow works offline on a laptop. The GitHub provider is kept.
- D13 — Fake LLM provider with deterministic outputs for demos, E2E and integration tests.
- D14 — Single-use approval links with stored nonces. Old: links were signed but reusable until expiry.
- D15 — Token budget per project and cost estimates per run. New safeguard.
- D16 — Rejecting a spec gate re-runs the spec generator, not idea refinement (an old-stack bug).
- D17 — Idempotent step completion keyed by step id (old: a counter that could double-count). One execution job per project at a time.
- D18 — Secret-scan Guardian check added. Exact path matching replaces the old suffix-based partial matching, which let wrong files pass.
- D19 — Gate timeouts become reminders and an
overdueflag. The old stack named a 72-hour timeout but never defined its effect. - D20 — Live updates over SignalR at
/api/hubs/projects. Replaces Socket.IO, same-origin. - D21 — The CLI is rewritten as a .NET global tool in the backend solution (
src/Aide.Cli).
11. Open questions
- GitLab provider: the old stack had a GitLab provider. Keep it out of v1 (proposed), or add it in a later milestone? — Ben
- Answered 2026-10-07: out of v1. Keep the Git provider abstraction so it can be added later.
- GitHub Copilot LLM adapter: the old stack had one. Drop it (proposed; Anthropic/OpenAI/Ollama cover the need), or re-add it later? — Ben
- Answered 2026-10-07: dropped. Anthropic, OpenAI, Ollama and Fake only.
- Should agent output stream token-by-token to the UI (nice for long spec/plan generation), or is “running” enough for v1 (proposed)? — Ben
- Answered 2026-10-07: no streaming in v1; runs show as running until complete.
- Default models per agent (proposed: Anthropic for spec/plan/review, a cheaper model for refinement). Confirm the model IDs at build time from provider docs. — Ben
- Answered 2026-10-07: model IDs come from each provider’s docs at build time and are configuration, not code. Anthropic for spec, plan, code generation and review; a cheaper model for refinement.
- Built 2026-10-08: configuration is
Llm:DefaultProviderandLlm:Providers:<Anthropic|OpenAI|Ollama|Fake>:{ApiKey,BaseUrl,DefaultModel,Models:<Agent>}in appsettings. Models confirmed from provider docs: Anthropicclaude-opus-5-5(spec, plan, code generation, review) andclaude-sonnet-5-5(refinement); OpenAIgpt-6-astraandgpt-6.1-sol(refinement); Ollamaqwen2.5-coder; Fakefake-1. Cost estimates useLlm:Pricing:<model>(USD per million tokens); unpriced models report 0.
12. Milestones
- M1 Foundations:
- Auth: register/login, GitHub login, API tokens, first-user admin (FR-1–FR-4).
- Projects, wizard, list, members, persisted settings, Git test (FR-5–FR-10).
- App shell with theme and navigation.
- M2 Lifecycle core:
- State machine, gates, artifacts, invalidation and integrity (FR-11–FR-21).
- Event log, outbox/job worker, live updates, activity feed (FR-46, FR-50).
- All of §4.1–4.4 and §4.9 tested.
- M3 Agents:
- Provider abstraction (Anthropic, OpenAI, Ollama, Fake), agent runs, prompt templates, retries, budget (FR-22–FR-26).
- Refinement, spec and plan generation (FR-27–FR-30).
- M4 Execution and Git:
- Step runner, Guardian, retries/blocking, pause/resume (FR-31–FR-33, FR-37).
- Local and GitHub providers, commits, PR (FR-34–FR-36).
- M5 Review: review cycle, scoring, drift, decision, optimisation loop, review page (FR-38–FR-41).
- M6 Release and rollback: merge, tag, conflicts, rollback with limits (FR-42–FR-45).
- M7 Notifications: in-app, email, Slack, Teams, webhook, approval links, per-channel retries, gate reminders (FR-47–FR-49, FR-15).
- M8 Admin and CLI:
- Admin pages and dead-letter retry (FR-51).
- The
aideCLI (FR-52). - Final E2E through the full lifecycle with Fake + Local providers.
Revision notes
- 2026-10-10: synced with the build. Rule refinements (§4.1–4.9), schema additions (§5), extra routes and error codes (§6), cookie policy (§8), seed (§9) and the model configuration (§11) now match the code. Each is logged under Decisions in
PROGRESS.md.