first commit

This commit is contained in:
npc0-hue
2026-07-11 14:56:10 +08:00
commit 7e05d0a4e7
660 changed files with 78119 additions and 0 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-07-03
@@ -0,0 +1,78 @@
## 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?
@@ -0,0 +1,28 @@
## Why
Run endpoints can register and heartbeat, but they still cannot receive bounded platform jobs or report lifecycle state. This change adds the job channel needed for server lifecycle and plugin-triggered work while keeping it independent from control, log ingest, artifact transfer, and game client bridge traffic.
## What Changes
- Add typed run job protocol payloads for claim, ack, progress, result, cancel, and reconcile workflows.
- Add platform API routes that let registered run endpoints claim queued jobs, acknowledge acceptance, report progress, submit terminal results, fetch cancel requests, and reconcile active work after restart.
- Extend platform service behavior for job leasing, idempotent claims/acks/results, lifecycle validation, and cancellation metadata using the existing in-memory repository.
- Extend the run-side platform client with typed job-channel methods.
- Add focused platform service/API tests and run client tests, including job lifecycle and idempotency/reconciliation coverage.
## Capabilities
### New Capabilities
- `run-job-channel`: Platform/run job-channel lifecycle, lease, acknowledgement, progress, result, cancel, reconcile, and idempotency workflow.
### Modified Capabilities
- None.
## Impact
- Affects `platform/` and `run/` only.
- Adds Go protocol/DTO/domain/service/API code and tests for the job channel.
- Updates run/platform protocol and route documentation.
- Does not implement durable log ingest, artifact chunk transfer, game client bridge behavior, frontend pages, billing, cloud host sales, or direct plugin-to-run/plugin-to-platform bypasses.
@@ -0,0 +1,104 @@
## ADDED Requirements
### Requirement: Run job payloads are typed and bounded
The system SHALL define typed run job payloads for claim, claim response, ack, progress, result, cancel request, cancel polling, and reconcile workflows without carrying logs, artifact chunks, host paths, raw credentials, direct sockets, or large inline result bodies.
#### Scenario: Job payloads are used
- **WHEN** run or platform code sends job lifecycle data
- **THEN** it MUST use named protocol/DTO types from dedicated protocol or DTO packages
#### Scenario: Job payload stays bounded
- **WHEN** run submits job progress or result
- **THEN** the request MUST include job identity, run endpoint identity, session token, lifecycle state, progress metadata, message, error code, and result reference only
### Requirement: Platform lets run claim queued jobs
The platform SHALL expose a job claim endpoint that validates run session continuity, selects a queued job assigned to the run endpoint, leases it, and returns bounded job metadata.
#### Scenario: Run claims queued job
- **WHEN** a registered run endpoint requests a job claim and a queued job exists for that endpoint
- **THEN** platform MUST mark the job accepted, return the job metadata, lease token, attempt number, and polling hints
#### Scenario: No queued job exists
- **WHEN** a registered run endpoint requests a job claim and no queued job exists for that endpoint
- **THEN** platform MUST return an accepted empty claim response without changing unrelated jobs
#### Scenario: Claim uses invalid session token
- **WHEN** run submits a claim with a missing or stale session token
- **THEN** platform MUST return a JSON validation error and MUST NOT change job state
### Requirement: Platform accepts job acknowledgements and progress
The platform SHALL expose job ack and progress endpoints that require the active session token and active job lease for the run endpoint.
#### Scenario: Job ack succeeds
- **WHEN** run acknowledges an active lease for an accepted job
- **THEN** platform MUST keep or move the job to a running lifecycle state and return an accepted ack response
#### Scenario: Job progress succeeds
- **WHEN** run reports bounded progress for an accepted or running job
- **THEN** platform MUST update percent, message, heartbeat time, and return an accepted progress response
#### Scenario: Invalid progress is submitted
- **WHEN** run reports progress outside 0 through 100 or with a stale lease token
- **THEN** platform MUST return a JSON validation error and MUST NOT update the job
### Requirement: Platform accepts idempotent terminal job results
The platform SHALL expose a result endpoint that accepts terminal succeeded, failed, or cancelled results for an active lease and treats repeated equivalent terminal result submissions as idempotent.
#### Scenario: Job result succeeds
- **WHEN** run submits a valid terminal result for an active lease
- **THEN** platform MUST update the job terminal state, progress, result reference, and return an accepted result response
#### Scenario: Duplicate terminal result is submitted
- **WHEN** run repeats the same terminal result for a job already in that terminal state
- **THEN** platform MUST return the same accepted terminal result response without mutating unrelated metadata
#### Scenario: Conflicting terminal result is submitted
- **WHEN** run submits a different terminal result for a job already terminal
- **THEN** platform MUST return a JSON validation error and MUST NOT overwrite the existing result
### Requirement: Platform supports job cancellation polling
The platform SHALL expose a service/API path to request cancellation for a job and a run-facing path to poll cancellation for the active lease.
#### Scenario: Platform requests cancellation
- **WHEN** platform requests cancellation for an accepted or running job
- **THEN** platform MUST record the cancel request and keep the job available for run cancellation polling
#### Scenario: Run polls cancellation
- **WHEN** run polls cancellation for an active leased job with a cancel request
- **THEN** platform MUST return a cancel response naming that job and cancellation reason
### Requirement: Platform supports run reconciliation
The platform SHALL expose a reconcile endpoint that lets a registered run endpoint report active job IDs after restart and receive platform-known active jobs for that endpoint.
#### Scenario: Run reconciles active jobs
- **WHEN** run submits active job IDs for its endpoint after restart
- **THEN** platform MUST return active jobs known to the platform for that endpoint and mark unknown reported jobs for run-side cleanup
#### Scenario: Reconcile uses invalid session token
- **WHEN** run submits reconcile with a missing or stale session token
- **THEN** platform MUST return a JSON validation error and MUST NOT change job state
### Requirement: Run client performs job-channel calls
The run-side platform client SHALL provide typed claim, ack, progress, result, cancel polling, and reconcile methods that call the platform job endpoints and decode typed responses.
#### Scenario: Run sends job channel calls through client
- **WHEN** run code calls job-channel client methods
- **THEN** the client MUST send JSON `POST` requests to the matching `/api/v1/run/jobs/*` endpoints and decode typed responses
#### Scenario: Platform returns job error
- **WHEN** a platform job endpoint returns a non-success status
- **THEN** the run client MUST return an error and MUST NOT treat the job call as accepted
### Requirement: Job channel is documented separately from other channels
The run/platform route and protocol documentation SHALL identify implemented job-channel routes and explicitly keep control, log ingest, artifact transfer, and game client bridge transport separate.
#### Scenario: Contributor inspects job docs
- **WHEN** a contributor opens run or platform protocol docs
- **THEN** the docs MUST show job claim, ack, progress, result, cancel polling, and reconcile routes as implemented while heavier log/artifact channels remain deferred
### Requirement: Job channel is verified
The change SHALL include platform service/API tests, run client tests, job lifecycle tests, and idempotency/reconciliation tests.
#### Scenario: Verification commands run
- **WHEN** the change is complete
- **THEN** `go test ./...` from `platform/`, `go test ./...` from `run/`, `scripts/check-structure.sh`, and `openspec validate implement-run-job-channel --strict` MUST pass
@@ -0,0 +1,35 @@
## 1. Job Contracts
- [x] 1.1 Add typed run job protocol payloads in `run/protocol` for claim, ack, progress, result, cancel polling, and reconcile.
- [x] 1.2 Add matching platform DTO/domain contracts and conversion helpers for job-channel requests and responses.
- [x] 1.3 Add validation rules for bounded job payloads, lifecycle states, progress, session, lease, and terminal result metadata.
## 2. Platform Job Channel
- [x] 2.1 Extend platform service behavior for job claim leasing, session checks, ack, progress, terminal results, cancellation, reconcile, and idempotency.
- [x] 2.2 Implement platform job-channel HTTP routes using named DTOs and service methods.
- [x] 2.3 Add platform service/API tests for lifecycle success, no-job claim, invalid session/lease, invalid progress, cancellation, duplicate results, conflicting results, and reconcile.
## 3. Run Job Client
- [x] 3.1 Extend `run/api.PlatformClient` with typed job claim, ack, progress, result, cancel polling, and reconcile methods.
- [x] 3.2 Add run client tests for request paths, JSON payloads, response decoding, and platform error handling.
- [x] 3.3 Add an integration-style client test that claims a job, acknowledges it, reports progress, submits a result, and reconciles against a test platform job endpoint.
## 4. Documentation
- [x] 4.1 Update run and platform protocol/route documentation to mark job-channel endpoints implemented and keep log/artifact/game-client channels separate.
## 5. Verification
- [x] 5.1 Run `go test ./...` from `platform/` and record evidence.
- [x] 5.2 Run `go test ./...` from `run/` and record evidence.
- [x] 5.3 Run `scripts/check-structure.sh` and record evidence.
- [x] 5.4 Run `openspec validate implement-run-job-channel --strict` and record evidence.
## Evidence
- 2026-07-03: `go test ./...` from `platform/` passed.
- 2026-07-03: `go test ./...` from `run/` passed.
- 2026-07-03: `scripts/check-structure.sh` passed with `structure check passed`.
- 2026-07-03: `openspec validate implement-run-job-channel --strict` passed with `Change 'implement-run-job-channel' is valid`.