Skip to content

Latest commit

 

History

History
140 lines (97 loc) · 5.12 KB

File metadata and controls

140 lines (97 loc) · 5.12 KB

Metapi Harness Engineering

This document captures the repo-level engineering taste that should remain true even when future work is performed by autonomous agents. The goal is not to replace feature tests. The goal is to keep the repository readable, review-friendly, and hard to drift.

Why This Exists

metapi already has strong local discipline in several areas:

  • architecture tests that pin important boundaries
  • schema parity and runtime bootstrap verification across databases
  • shared protocol helpers that deliberately replace parallel implementations

What was still missing was one place that states the mechanical rules behind those choices and a lightweight loop that continuously checks for drift. This document is that shared contract.

Golden Principles

1. One Invariant, One Source Of Truth

If a helper, contract, or workflow already owns an invariant, new work should reuse that home instead of creating a second implementation.

Examples:

  • protocol shaping and normalization belong in src/server/transformers/**
  • endpoint fallback belongs in src/server/routes/proxy/endpointFlow.ts
  • proxy success/failure bookkeeping belongs in src/server/proxy-core/surfaces/sharedSurface.ts
  • schema heuristics belong in src/server/db/schemaMetadata.ts

2. Routes Are Adapters, Not Owners

src/server/routes/** should remain thin. Route files may register endpoints, read Fastify request state, and delegate to shared orchestration. They should not become the place where retry loops, protocol conversion, stream lifecycle, or billing logic lives.

If a helper is imported outside a single route file, that helper should move to proxy-core, services, transformers, or another neutral home.

3. Transformers Must Stay Protocol-Pure

Transformers are the protocol boundary. They may depend on canonical/shared contracts, but they should not reach back into route files, Fastify handlers, OAuth services, token routing, or runtime dispatch details.

In practice this means:

  • canonical is the request truth source
  • shared/normalized is the response truth source
  • compatibility layers may orchestrate retries or fallback bodies, but they do not redefine protocol semantics

4. Proxy-Core Follows The Golden Path

Proxy orchestration should prefer shared, tested paths instead of bespoke surface-local logic.

Current preferred path:

  • endpoint ranking and request shaping via upstreamEndpoint.ts
  • endpoint-attempt loops via executeEndpointFlow()
  • whole-body response decoding via readRuntimeResponseText()
  • OAuth refresh, sticky/lease behavior, billing, and proxy logging via sharedSurface.ts
  • Codex header/session semantics via provider profiles and header utils
  • Codex websocket transport via the dedicated websocket runtime, not generic fetch executors

5. Platform Capability Must Be Explicit

Platform behavior is easy to let drift because it spans adapters, discovery, endpoint preference, and routing. Any platform-specific behavior that matters at runtime should be stated once and reused, not re-inferred in multiple subsystems.

Thin adapters should stay honest. Feature-complete adapters should be tested as feature-complete. Do not silently “upgrade” support through generic defaults.

6. Database Changes Must Stay Contract-Driven

Schema work already has one of the strongest harnesses in the repo. Keep it that way.

  • update Drizzle schema and SQLite migration history together
  • regenerate checked-in contract/artifacts together
  • keep cross-dialect bootstrap/upgrade generation contract-driven
  • keep legacy startup compatibility narrow and spec-owned

7. Web Pages Are Orchestration Surfaces

Top-level pages should not be reused as shared component libraries.

  • no page-to-page imports for reusable UI
  • mobile behavior should reuse shared primitives first
  • extract repeated modals, drawers, and panels into domain subfolders early

First-Wave Drift Checks

The first repo-level drift loop is intentionally small and high-signal. It checks for:

  • transformer imports from routes/proxy
  • new proxy-core surface body reads that bypass readRuntimeResponseText()
  • new imports from routes/proxy inside proxy-core beyond the current debt baseline
  • new top-level page-to-page imports in the admin UI beyond the current debt baseline

These checks live in scripts/dev/repo-drift-check.ts and are wired into CI.

Tracked Debt Vs New Violations

The repository already contains some known architectural debt, especially where proxy-core still imports helpers from routes/proxy and where one admin page reuses another page's export.

The harness uses a ratchet:

  • tracked debt is reported so it stays visible
  • new violations fail CI

This keeps the repo moving forward without forcing a risky “rewrite everything first” migration.

Garbage Collection Loop

The harness loop is intentionally conservative:

  1. rules are encoded in repo docs and executable checks
  2. CI blocks new drift
  3. a scheduled workflow generates a drift report artifact
  4. targeted cleanup PRs can remove tracked debt in small slices

The goal is steady principal payments on technical debt, not occasional heroic cleanup sprints.