79 lines
5.6 KiB
Markdown
79 lines
5.6 KiB
Markdown
## Context
|
|
|
|
The platform already stores job metadata and run endpoints, and run endpoints can register through the control channel. Existing jobs can be created with `queued` state and idempotency keys, but there is no platform/run API for a run endpoint to claim a job, acknowledge acceptance, report progress, submit a terminal result, observe cancel requests, or reconcile work after restart.
|
|
|
|
This change implements the first job channel using HTTP JSON and in-memory platform state. It depends on registered run endpoint metadata and keeps job traffic separate from control, logs, artifacts, and the optional game client bridge.
|
|
|
|
## Goals / Non-Goals
|
|
|
|
**Goals:**
|
|
|
|
- Define typed run job protocol payloads in `run/protocol` and matching platform DTO/domain contracts.
|
|
- Add platform job-channel routes for claim, ack, progress, result, cancel request, cancel polling, and reconcile.
|
|
- Add service-level leasing and lifecycle transitions for queued, accepted, running, succeeded, failed, and cancelled jobs.
|
|
- Preserve idempotency for duplicate claims, acks, and terminal results using existing job IDs and idempotency keys.
|
|
- Add run-side client methods for job-channel calls.
|
|
- Add tests for job lifecycle, invalid transitions, wrong endpoint/session behavior, idempotency, and reconciliation.
|
|
|
|
**Non-Goals:**
|
|
|
|
- No real process execution, plugin action execution, scheduler loop, or background run worker.
|
|
- No durable DB persistence, lease expiry sweeper, distributed locks, or multi-run fairness algorithm.
|
|
- No log ingest, artifact chunk transfer, game client bridge, frontend behavior, billing, cloud host sales, or direct plugin-to-run access.
|
|
- No raw host paths, raw credentials, direct sockets, or large result bodies in job payloads.
|
|
|
|
## Decisions
|
|
|
|
### Decision 1: HTTP JSON job endpoints
|
|
|
|
The initial job channel uses small JSON `POST` endpoints under `/api/v1/run/jobs/*`. This matches the existing API style and keeps the lifecycle testable without introducing streaming transport.
|
|
|
|
Alternative considered: long-lived WebSocket or gRPC stream for job events. Rejected for this change because the architecture separates control, jobs, logs, and artifacts, and the first job lifecycle can be proven with bounded request/response calls.
|
|
|
|
### Decision 2: Existing job resource is the source of truth
|
|
|
|
Job-channel operations update the existing `domain.Job` stored by the repository. Claim moves a queued job to `accepted`, ack confirms acceptance or moves to `running`, progress updates bounded progress, and result writes terminal state and a bounded result reference.
|
|
|
|
Alternative considered: adding a separate run job lease table now. Rejected because current storage is in-memory and the existing job aggregate already contains run endpoint, state, progress, result reference, and idempotency key.
|
|
|
|
### Decision 3: Leases are service metadata, not persisted models
|
|
|
|
The service keeps lightweight in-memory job lease metadata keyed by job ID. Lease metadata records the run endpoint ID, session token, attempt number, lease time, last update time, cancel request flag, and terminal result fingerprint.
|
|
|
|
Alternative considered: adding durable lease models before persistence exists. Rejected because it would create model churn without improving the current in-memory system.
|
|
|
|
### Decision 4: Session token gates job calls
|
|
|
|
All run job-channel calls require the active session token for the run endpoint. This reuses the control registration session and prevents stale or wrong run endpoints from mutating job state.
|
|
|
|
Alternative considered: accepting only run endpoint ID. Rejected because hello/heartbeat already established platform-issued session continuity.
|
|
|
|
### Decision 5: Results remain bounded references
|
|
|
|
Terminal job result payloads carry status, message, error code, and `resultRef`. Large logs, files, backups, and config blobs must move through later log/artifact channels, not job result bodies.
|
|
|
|
Alternative considered: allowing inline result bodies. Rejected because job result traffic must not block control, logs, or artifact transfer and must not expose raw host paths.
|
|
|
|
## Risks / Trade-offs
|
|
|
|
- [Risk] In-memory leases disappear on platform restart. Mitigation: keep lease handling behind `service.Core` and add reconcile behavior so a run can re-report work after reconnect.
|
|
- [Risk] No lease expiry means a stuck accepted job may remain accepted. Mitigation: expose reconciliation and cancellation now; add expiry/sweeper in a later persistence/runtime change.
|
|
- [Risk] HTTP polling has latency. Mitigation: this change prioritizes correctness and testability; later transport changes can add long-polling or streaming without changing lifecycle semantics.
|
|
- [Risk] Result references cannot prove artifact availability yet. Mitigation: keep references opaque until the artifact channel change implements checksum and transfer guarantees.
|
|
|
|
## Migration Plan
|
|
|
|
1. Add job protocol, DTO, domain, validation, and service contracts.
|
|
2. Add platform API handlers and tests for job lifecycle and idempotency.
|
|
3. Add run client methods and tests for request/response behavior.
|
|
4. Update protocol and route docs.
|
|
5. Verify with platform tests, run tests, structure check, and strict OpenSpec validation.
|
|
|
|
Rollback before dependent changes is removal of the job route/client additions and this OpenSpec change. After log/artifact/server workflow changes depend on job lifecycle state, rollback must use a new OpenSpec change.
|
|
|
|
## Open Questions
|
|
|
|
- What production lease duration and retry policy should run endpoints use?
|
|
- Should queued job claim ordering later support priority, FIFO only, or per-server concurrency limits?
|
|
- Should terminal result fingerprints be signed, checksummed, or backed by artifact metadata once artifact transfer exists?
|