# AWP Coding Standard — Lazy inside, strict at the seam

**Status:** Canonical.  
**Source:** Adapted from `platform-modules/mod` for an application/control-plane repository.  
**Audience:** AI coding agents first, humans second.

## 0. One principle

> **Be lazy in the implementation. Be strict at the seam.**

AWP has four recurring hard boundaries:

| Boundary | Question |
|---|---|
| Trust boundary | Can untrusted input/code corrupt, leak, escalate, or destroy state? |
| Module/interface seam | Can the implementation be replaced without callers learning its internals? |
| Lifecycle boundary | Can retry/crash/duplicate/stale ownership/partial failure corrupt authoritative state? |
| Plane boundary | Is edge/gateway, control-plane, or execution-plane responsibility leaking into another plane? |

Minimal implementation is encouraged only above the hard floor.

**Hard floor:** trust-boundary validation, security, accessibility, data-loss prevention, durable ownership, idempotency where required, audit, and the canonical plane/provider boundaries.

## 1. Read before coding

Read in this order:

1. affected authoritative spec under `docs/specs/`, if it exists;
2. `docs/plans/AWP-ARCHITECTURE-BASELINE.md`;
3. `docs/plans/AWP-DEPLOYMENT-TOPOLOGY.md`;
4. `docs/plans/AWP-FOSS-DECISIONS.md`;
5. affected implementation plan/task;
6. this coding standard;
7. provider/module contracts.

If code, plan and authoritative spec disagree:

**STOP -> amend the authoritative contract/plan -> resume.**

Do not use older research/review wording to override the current architecture baseline.

## 2. Implementation ladder

Before adding structure, stop at the first rung that holds:

1. Does this need to exist? If no, do not build it.
2. Does `@platform-modules/*` or selected FOSS already solve the generic mechanics? Reuse it.
3. Does Node/PostgreSQL/Kubernetes/Web Standards already solve it? Use the native primitive.
4. Can one small function/data structure solve it? Write that.
5. Only then add the minimum deeper Module/provider abstraction required.

Refuse:

- speculative Controller/Service/Repository stacks;
- identity wrappers/twin types with no semantics;
- a class where function + data closes the problem;
- provider SDK types escaping adapters;
- Cloudflare/edge-vendor types in domain/application code;
- Kubernetes types in domain code;
- local copies of generic mechanics that belong upstream in `platform-modules/mod`;
- a second workflow/CI/deployment authority because a library looks convenient.

## 3. Ownership law

AWP owns:

```text
intent
policy
final authorization
authoritative lifecycle state
primitive relationships
configuration semantics
decisions/approvals
provenance
business-event/audit semantics
CI/CD product meaning
operator UX
```

Adapters/providers own replaceable mechanics.

Coding/review agents implement, test, review and fix code. They do not silently own:

- workspace creation/cleanup;
- scheduling;
- Git publication/merge;
- CI progression policy;
- release/deployment progression;
- approval disposition;
- product-policy changes;
- authoritative spec changes.

## 4. Three-plane law

Canonical runtime topology:

```text
public Internet
  -> edge/gateway plane
  -> authoritative control plane
  -> K3s execution plane
```

Required dependency direction:

```text
gateway -> runtime-neutral contracts
web     -> runtime-neutral contracts
control -> contracts + application + domain + providers

control adapters -> DBOS/PostgreSQL/Kubernetes/GitHub/Fabro/etc.

gateway -X-> DBOS
gateway -X-> Kubernetes adapters
gateway -X-> control-plane repositories
domain  -X-> Hono/Cloudflare/Kubernetes/GitHub/Fabro SDK types
```

Gateway and control plane remain independently buildable/deployable even when hosted together.

The gateway may parse/verify credentials and reject invalid requests. Final Principal resolution, authorization, policy and lifecycle transitions occur in the control plane.

Do not trust forwarded identity merely because it arrived in a gateway-created header; the internal caller and Principal context require an integrity/authentication contract.

## 5. Module seam checklist

Every public Module/interface must satisfy:

- [ ] Typed intentional inputs/outputs.
- [ ] Stable error codes; never parse error strings for behavior.
- [ ] Provider/runtime client types do not escape.
- [ ] No deployment-specific defaults at the seam.
- [ ] Interface hides meaningful behavior; no ceremony-only wrapper.
- [ ] Behavioral tests cover success and failure/edge behavior.
- [ ] Trust-boundary validation occurs at the seam.
- [ ] Retry/idempotency/reconciliation semantics are explicit where external retry is possible.
- [ ] Persistence/transaction expectations are explicit.
- [ ] Authorization/capability requirements are explicit.
- [ ] Configuration source/effective value semantics are explicit where applicable.
- [ ] Business-event/audit requirements are explicit where applicable.
- [ ] Provider-native IDs map to AWP identity rather than replacing it.
- [ ] Interface is machine-legible enough for an agent without reading internals.

## 6. Platform reuse gate

Before creating generic infrastructure, inspect the actual current `platform-modules/mod` package state.

Every foundational spec records:

```text
## Platform Reuse
Relevant package(s):
Exact exported seam/subpath:
Decision:
  REUSE | ADAPT | SELECTIVE | UPSTREAM-CHANGE | DEFER | PATTERN | REJECT
What Platform owns:
What AWP owns:
Known gaps:
Integration tests:
Pinned version/revision:
```

Generic missing capability in an existing Platform Module should normally be fixed upstream. Do not upstream AWP product policy.

## 7. Runtime baseline

AWP control-plane code is:

```text
TypeScript
Node
ESM
pnpm
PostgreSQL
Drizzle
```

Exact supported versions come from the runtime ADR, package manifest/lockfile and CI matrix. Do not hard-code an obsolete runtime version in architectural code or secondary documentation.

Use strict TypeScript.

No import-time network connections or hidden process-wide side effects.

Hono is transport only. Domain/application functions do not accept Hono `Context`, Node HTTP objects, Cloudflare bindings, or provider SDK clients.

## 8. Database and lifecycle transactions

AWP owns its PostgreSQL schema and product state.

Critical state changes that must be atomic belong in one transaction, including required event/audit/outbox writes.

Canonical lifecycle mutation:

```text
load state
-> authenticate/resolve Principal
-> authorize
-> evaluate guard/policy
-> write state
-> write required event/audit/outbox
-> commit
```

Use specialized stores only when concurrency semantics earn them. Do not turn every table into generic CRUD ceremony.

Provider state is reconciled/observed input; it does not replace persisted AWP lifecycle state.

## 9. Durable execution

`DurableWorkflowProvider` defaults to DBOS + PostgreSQL.

DBOS owns durable workflow checkpoint/recovery mechanics. AWP owns domain lifecycle meaning, policy, authorization, IDs, events and audit.

Do not build a second general durable workflow engine beside DBOS.

External executions such as AgentRun, FactoryRun, CI, Git publication and Deployment still require explicit semantics for:

```text
AWP identity
provider identity mapping
attempt
owner/token where applicable
heartbeat/observation where applicable
timeout
retry
cancel
idempotency
reconciliation
terminal result/failure
```

Do not hand-roll leases for ordinary in-process DBOS workflow steps merely to satisfy a pattern.

## 10. Events, audit, realtime and telemetry

Keep distinct:

**Business events** — durable reason/state-transition facts.  
**Audit** — who did what to what.  
**Realtime** — non-authoritative projection/fan-out.  
**Telemetry** — logs, metrics, traces.

Never use logs or WebSocket connection state as lifecycle truth.

Durable events include at least:

- event ID/type/schema version;
- occurred-at time;
- actor/Principal;
- correlation and causation IDs;
- relevant primitive IDs;
- attempt where relevant;
- structured payload.

Realtime reconnect must recover from authoritative state.

OpenTelemetry-compatible instrumentation/export is the telemetry boundary; Collector/backend infrastructure is optional unless a deployment needs it.

## 11. Errors

Errors crossing Module/API/CLI/MCP boundaries need:

- stable code;
- human-readable explanation;
- safe structured details;
- relevant primitive/context IDs;
- retryability where meaningful.

Do not leak raw provider errors to product UX. Preserve diagnostic cause where safe.

## 12. Authentication, authorization and credentials

AWP Principal:

```text
Human | Agent | System
```

Provider/model credentials and publication credentials are references behind dedicated boundaries; never copy secret material into ordinary config/run records.

Distinguish:

- credentials **to AWP**;
- gateway/internal service authentication;
- credentials AWP uses **for providers**;
- short-lived execution credentials;
- trusted Git publication authority.

Agents and CI runners must not receive reusable forge publication credentials.

Security/capability ceilings narrow downstream; child scopes cannot self-broaden.

## 13. Workspaces and execution

Every coding execution runs in an isolated AWP Workspace.

Baseline provider is native Kubernetes Pod/PVC/ServiceAccount/NetworkPolicy; `Workspace` remains the domain abstraction.

Disposable compute must not destroy recoverable work.

ExecutionProfile resolution is adopt-first:

```text
.devcontainer
Dockerfile/Compose
mise/Devbox
language/tool/package manifests
then small AWP generic template if needed
```

Resolved environment/image/toolchain/service provenance is immutable for the run.

Untrusted AgentRuns use gVisor where compatible; exceptions are explicit policy, not global weakening.

## 14. CI / ARC law

GitHub Actions is the initial CI provider. ARC on K3s is day-one runner infrastructure.

ARC owns:

- runner scale-set/listener mechanics;
- runner registration;
- runner pod lifecycle/cleanup.

AWP owns:

- CIExecution/CIResult semantics;
- Project/WorkItem/ChangeSet correlation;
- required-check/policy interpretation;
- approvals/progression;
- history/operator UX;
- resolver/optimizer policy.

Runner rules:

```text
ephemeral by default
no per-machine persistent runner administration
no reusable Git publication credential
no universal privileged/host-access runner
no protected secrets for untrusted/fork PRs
separate runner groups/scale sets when trust/privilege differs
resource/network/namespace isolation
cleanup after every job
```

Use gVisor for runner workloads where compatible. Workflows requiring privileged build mechanics use an explicit isolated trusted runner profile; do not make privilege the baseline.

Direct workspace checks may be preflight/ad-hoc work but do not replace GitHub Actions CI.

## 15. Git and publication

AWP owns workspace lifecycle and Git progression.

Agents may modify their workspace and produce a ChangeSet. They do not own publication/merge.

Trusted publication path must verify:

```text
authorized attempt
allowed repository/ref
expected base/head
ChangeSet fidelity
TOCTOU conditions
provider result
final remote state
```

Publication credentials stay in the trusted control-plane boundary.

## 16. Deployment law

Baseline Kubernetes deployment is:

```text
Helm/manifests render
-> server-side dry-run/diff
-> policy/approval
-> Kubernetes Server-Side Apply
-> rollout/health observation
-> durable result
-> rollback to known-good release when required
```

Flux is not baseline. Add it only when continuous GitOps reconciliation independent of AWP is an explicit requirement.

## 17. Provider adapters

Start with one direct implementation of the provider contract.

No `AdapterController -> AdapterService -> AdapterRepository -> SDKClient` ceremony unless behavior genuinely earns it.

Translate provider-specific:

- IDs;
- errors;
- retries;
- health;
- events;
- cancellation;
- version/capability differences

at the adapter seam.

## 18. Configuration and policy

Never scatter environment/config reads through domain Modules.

Hierarchy:

```text
System -> Project -> Plan -> Task -> Run
```

Ordinary settings override according to schema. Security/capability ceilings only narrow.

Distinguish desired/applied/observed configuration where drift matters.

Human-in-the-loop/autonomy is an AWP-owned typed policy/rule model. The 0–100 control is UX over rules, not the backend policy representation. No external generic policy engine is required by default.

## 19. UI implementation law

Visual baseline:

1. Astryx component if available.
2. AWP-owned implementation for a real Astryx gap.

Existing `deck-ui` is reference/source only. A port becomes AWP-owned and AWP-named.

Small interaction libraries selected by architecture may own mechanics only: Pragmatic Drag and Drop, React Flow, Chart.js.

Accessibility and responsive behavior are hard-floor requirements.

## 20. Testing

Minimum layers where applicable:

- pure domain unit tests;
- persistence/transaction/concurrency tests;
- provider contract/reconciliation tests;
- gateway/control trust-boundary tests;
- API/CLI/MCP contract tests;
- workspace crash/WIP recovery tests;
- ARC runner lifecycle/security tests;
- Git publication fidelity/TOCTOU tests;
- UI interaction/accessibility tests;
- end-to-end critical journeys.

A passing unit-test command is not a typecheck. Run the real package typecheck separately.

A test that cannot fail when the behavior breaks is not proof.

## 21. Verification before completion

Before declaring work complete:

- [ ] authoritative spec still matches;
- [ ] architecture plane/provider boundaries preserved;
- [ ] plan/task acceptance criteria satisfied;
- [ ] typecheck clean;
- [ ] tests clean;
- [ ] lint/build clean where applicable;
- [ ] no ignored warnings/notices;
- [ ] migration/config changes verified;
- [ ] provider failure/reconciliation path tested;
- [ ] security/trust path tested;
- [ ] idempotency/retry/recovery tested where applicable;
- [ ] UI responsive/a11y checked where applicable;
- [ ] Platform reuse/upstream decision recorded;
- [ ] docs/spec updated for architectural behavior.

## 22. Agent summary

1. Reuse before building.
2. Minimal inside; strict at seams.
3. Spec first.
4. Gateway, control and execution planes do not leak into each other.
5. AWP owns lifecycle; providers own mechanics.
6. PostgreSQL is product truth; provider state is reconciled evidence/mechanics.
7. DBOS is the durable-workflow default; do not create another general workflow engine.
8. Business events, audit, realtime and telemetry are different.
9. Provider SDKs stay behind adapters.
10. ARC/GitHub Actions CI is day-one; runners are ephemeral and least-privileged.
11. Agents/runners never own reusable Git publication authority.
12. Astryx first; AWP-owned UI gaps only.
13. Generic Platform gaps go upstream when appropriate.
14. Verify the actual property, not a proxy signal.
