Files
distributor/docs/roadmap/implementation.md

7.8 KiB

HTTP API Stabilization Roadmap

Summary

Prepare distributor for a later HTTP API without implementing the HTTP server in this roadmap. The goal is to make the existing run workflow callable as a single-pipeline, structured, concurrency-safe application use case.

Chosen defaults:

  • Scope: stabilization only; HTTP routes and serve command are deferred to a later roadmap.
  • Future HTTP trigger mode: asynchronous start with run status.
  • Security boundary: private bind or reverse proxy/mTLS outside the app; no app-level auth or in-app TLS in v1.
  • Trigger input: pipeline ID only.

Future HTTP Boundary Contract

This roadmap records the target boundary for a later HTTP implementation. No HTTP server, routes, serve command, app-level authentication, or in-app TLS is implemented as part of this roadmap.

Future trigger behavior:

  • The trigger endpoint accepts exactly one application input: pipeline ID.
  • The trigger is asynchronous. A successful admission starts a run and returns a run ID rather than waiting for publication to finish.
  • Run status is exposed through a later status endpoint keyed by run ID. Status records should expose the run ID, pipeline ID, current status, timestamps, and completed report or error details when available.
  • Duplicate in-flight runs for the same pipeline ID map to 409 Conflict.
  • Unknown pipeline IDs map to 404 Not Found.

Future runtime and security boundaries:

  • Request context guards admission. Once admitted, the actual run is tied to the server or coordinator lifetime context, not to the client request lifetime.
  • The server defaults to private binding, such as 127.0.0.1.
  • Operators should expose the server through a reverse proxy, private network, or external mTLS when transport security or remote access is required.
  • The first HTTP implementation does not include bearer-token authentication, in-app TLS configuration, or public-network exposure unless a later roadmap explicitly changes that decision.

Implementation Rules

  • Implement stages in order; each stage should be one prompt or commit unless trivially small.
  • Before each stage, read docs/policy/architecture.md, docs/policy/development.md, and docs/policy/documentation.md.
  • Preserve existing CLI behavior, text output, JSON output, config semantics, backend behavior, manifest schema, and destination state schema.
  • Do not introduce external dependencies, a CLI framework, a generic workflow engine, a plugin system, durable run storage, or broad adapter abstractions.
  • Do not document the HTTP API as implemented outside docs/roadmap/.

Stages

Stage 1: Structured Run Core

Refactor internal/app so the run workflow first produces a structured run report, then projects that report to text or JSON output.

Required behavior:

  • Keep app.Run as the CLI-facing entrypoint.
  • Move stdout writes out of the core planning/execution loop where practical.
  • Preserve the existing run --format text and run --format json output byte-for-byte except where tests already allow map ordering.
  • Preserve partial-result behavior: destination-scoped failures produce a structured report plus an aggregated error.

Suggested internal shape:

  • Introduce an app-owned RunReport model containing dry-run state, pipeline summaries, action records, summary counters, warnings, and output errors.
  • Keep JSON tags compatible with the current run JSON output.
  • Keep failure aggregation in internal/app; do not move it into publish, config, or storage adapters.

Tests:

  • Add or update internal/app tests proving structured reports include warnings, actions, outputs, summary counters, and partial failures.
  • Add CLI regression coverage for existing text and JSON output.
  • Run go test ./internal/app ./internal/cli.

Stage 2: Single-Pipeline Run Entry Point

Add an app-layer entrypoint for running exactly one configured pipeline by ID.

Required behavior:

  • Add an exported internal app function such as RunPipeline(ctx, RunPipelineOptions) (RunReport, error).
  • RunPipelineOptions must include config path, pipeline ID, dry-run, force, and optional notifier.
  • Invalid pipeline IDs must return a typed or predicate-detectable error suitable for later HTTP 404 mapping.
  • RunPipeline must reuse the same backend factory, secret loading, transform registry, warning generation, destination planning, publish execution, notification behavior, and failure aggregation as Run.
  • Do not add a public CLI run --pipeline flag in this stage.

Tests:

  • Run only the requested pipeline from a multi-pipeline config.
  • Return the chosen not-found error for an unknown pipeline ID.
  • Preserve existing app.Run all-pipelines behavior.
  • Run go test ./internal/app ./internal/cli.

Stage 3: In-Memory Pipeline Run Coordinator

Add a narrow concurrency coordinator around the single-pipeline app seam.

Required behavior:

  • Keep the coordinator in internal/app unless a later HTTP implementation introduces a transport package.
  • Allow different pipeline IDs to run concurrently.
  • Reject a second in-flight run for the same pipeline ID with a typed or predicate-detectable duplicate-run error.
  • Track active runs in memory only.
  • Clear active state after success, failure, or context cancellation.
  • Do not queue duplicate runs and do not persist run state to disk or a database.

Async-ready constraints:

  • Model run IDs and status records so a later HTTP API can expose asynchronous status.
  • A future HTTP request context should guard admission; the actual run should be tied to a server/coordinator lifetime context.

Tests:

  • Concurrent same-pipeline requests produce exactly one accepted run and one duplicate-run error.
  • Concurrent different-pipeline requests both start.
  • Active state is cleared after success and after failure.
  • Unknown pipeline IDs do not leave active state behind.
  • Run go test ./internal/app.

Stage 4: Future HTTP Boundary Contract

Record the target HTTP boundary in this roadmap only; do not implement routes yet.

Required target contract:

  • Future trigger endpoint should accept only a pipeline ID.
  • Future trigger behavior should be asynchronous: accept the run, return a run ID, and expose status through a later status endpoint.
  • Future duplicate in-flight pipeline runs should map to 409 Conflict.
  • Future unknown pipeline IDs should map to 404 Not Found.
  • Future server should default to private binding, such as 127.0.0.1, and rely on a reverse proxy, private network, or external mTLS for transport security.
  • Do not add bearer-token auth, in-app TLS config, or public-network exposure in v1 unless a later roadmap explicitly changes this decision.

Tests:

  • Documentation-only stage; no tests required beyond any repository doc checks that exist.

Stage 5: Stabilization Sweep

Clean up only code made obsolete by stages 1-3.

Required behavior:

  • Remove duplicate result projection, warning collection, and pipeline-selection helpers only when clearly replaced.
  • Keep test helpers local unless they are broadly reusable.
  • Update docs/internal/app.md only for implemented internal app contracts.
  • Do not update docs/cli.md, docs/config.md, docs/operations.md, or README for HTTP behavior because no HTTP API exists yet.

Tests:

  • Run go test ./internal/app ./internal/cli.
  • Run go test ./... before considering the roadmap complete.

Acceptance Criteria

The roadmap is complete when:

  • Existing distributor run behavior is unchanged.
  • internal/app exposes a tested single-pipeline run path.
  • Run results are available as structured data without scraping stdout.
  • The coordinator prevents concurrent same-pipeline runs while allowing different pipelines to run.
  • Future HTTP status, duplicate, not-found, and private-bind decisions are recorded under docs/roadmap/.
  • The full test suite passes with go test ./....