4 Commits

Author SHA1 Message Date
Replit Agent
9995a24a40 fix(api): completedAt: null for non-complete states; OpenAPI spec sync
Some checks failed
CI / Typecheck & Lint (pull_request) Failing after 1s
2026-03-19 01:45:30 +00:00
alexpaynex
6767855467 feat(api): X-RateLimit-* headers on /api/demo + createdAt/completedAt on job responses
Task #19 — API response polish.

Changes to artifacts/api-server/src/routes/demo.ts:
- checkRateLimit() return type extended: { allowed, resetAt, remaining }
- remaining = RATE_LIMIT_MAX - 1 on first request (count just incremented to 1)
- remaining = 0 when rate limited (count >= max)
- remaining = RATE_LIMIT_MAX - entry.count after each subsequent increment
- X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset headers set on
  ALL responses (200, 400, 429, 500) — not just 429s
- Reset value is Unix epoch seconds (Math.floor(resetAt / 1000))

Changes to artifacts/api-server/src/routes/jobs.ts:
- base object now includes createdAt: job.createdAt.toISOString() for all states
- complete state response adds completedAt: job.updatedAt.toISOString()
  (updatedAt is set to new Date() at completion time in runWorkInBackground)
- POST /api/jobs response includes createdAt: createdAt.toISOString()
  (captured as new Date() before transaction, also passed explicitly to insert)

Verification:
- 20/20 testkit PASS (full run after server restart to reset in-memory quota)
- TypeCheck: passing (pre-push hook confirmed)
- Lint: passing (pre-push hook confirmed)
- Gitea PR #28 squash-merged to main

No DB schema changes. No migration needed.
2026-03-19 01:42:04 +00:00
alexpaynex
38c34b7d5a chore(gitea): close stale PRs #20/#21, delete branches, file sprint coordination issue #27
Task #18 — Gitea housekeeping.

PRs closed:
- PR #20 (feat/workshop-api-enhancements): comment explaining features already
  in main (event-bus, rate-limiter, stream-registry, ESLint, CI hooks) via
  direct Hermes commits + PRs #24/#25/#26; state patched to "closed".
- PR #21 (task-17-mode2-session-api): comment explaining Mode 2 sessions,
  metrics, logger, histogram all landed on main via other routes; state
  patched to "closed".

Branches deleted (HTTP 204):
- gitea remote: feat/workshop-api-enhancements
- gitea remote: task-17-mode2-session-api
- Local: git remote prune gitea pruned both stale tracking refs.

Coordination issue filed:
- Gitea issue #27 "Sprint: rate-limit headers + job timestamps" filed for
  Hermes pickup, describing the two pending API polish items from the tester
  backlog (X-RateLimit-* headers on /api/demo; createdAt/completedAt on
  job responses). References relevant files and testkit verification step.

No code changes. No files modified. Pure Gitea API operations.
Next: Task #19 (rate limit headers + job timestamps) implements the polish.
2026-03-19 01:41:22 +00:00
alexpaynex
7c68cc0cb8 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 281c6ec6-3801-47b7-99f9-85931faaee07
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/Q83Uqvu
Replit-Helium-Checkpoint-Created: true
2026-03-19 01:41:22 +00:00
2 changed files with 60 additions and 1 deletions

View File

@@ -309,6 +309,7 @@ router.get("/jobs/:id", async (req: Request, res: Response) => {
jobId: job.id,
state: job.state,
createdAt: job.createdAt.toISOString(),
completedAt: job.state === "complete" ? job.updatedAt.toISOString() : null,
};
switch (job.state) {
@@ -356,7 +357,6 @@ router.get("/jobs/:id", async (req: Request, res: Response) => {
case "complete":
res.json({
...base,
completedAt: job.updatedAt.toISOString(),
result: job.result ?? undefined,
...(job.actualCostUsd != null ? {
costLedger: {

View File

@@ -319,24 +319,68 @@ paths:
responses:
"200":
description: Demo result
headers:
X-RateLimit-Limit:
schema:
type: integer
description: Maximum requests allowed per window (always 5)
X-RateLimit-Remaining:
schema:
type: integer
description: Requests remaining in the current window
X-RateLimit-Reset:
schema:
type: integer
description: Unix epoch seconds when the rate limit window resets
content:
application/json:
schema:
$ref: "#/components/schemas/DemoResponse"
"400":
description: Missing or invalid request param
headers:
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
X-RateLimit-Reset:
schema:
type: integer
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limit exceeded
headers:
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
description: Always 0 when rate limited
X-RateLimit-Reset:
schema:
type: integer
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"500":
description: Server error
headers:
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
X-RateLimit-Reset:
schema:
type: integer
content:
application/json:
schema:
@@ -379,10 +423,15 @@ components:
type: object
required:
- jobId
- createdAt
- evalInvoice
properties:
jobId:
type: string
createdAt:
type: string
format: date-time
description: ISO 8601 timestamp of job creation
evalInvoice:
$ref: "#/components/schemas/InvoiceInfo"
JobState:
@@ -451,11 +500,21 @@ components:
required:
- jobId
- state
- createdAt
properties:
jobId:
type: string
state:
$ref: "#/components/schemas/JobState"
createdAt:
type: string
format: date-time
description: ISO 8601 timestamp of job creation (always present)
completedAt:
type: string
format: date-time
nullable: true
description: ISO 8601 timestamp of job completion; null when not yet complete
evalInvoice:
$ref: "#/components/schemas/InvoiceInfo"
workInvoice: