Commit Graph

13 Commits

Author SHA1 Message Date
steve 0526bada90 docs: land prior ADR + prompt updates
Commit pre-existing uncommitted working-tree changes that predate the
license/public-readiness work — NOT authored in this session, just flushed so
they're not lost: ADR-0003/0005/0009/0012 edits, the new ADR-0013
(embeddings-bypass + two-slot residency, already referenced by CLAUDE.md), and
the phase-0..3 prompt revisions + prompts/README.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 20:33:39 -04:00
steve 823c0b4ca8 docs: MIT license + public-readiness framing
CI / Tidy (push) Successful in 9m42s
CI / Build & Test (push) Successful in 10m32s
CI / Publish Docker Image (push) Successful in 1m16s
Add MIT LICENSE (matches gadfly/majordomo, same author). README + CLAUDE.md:
note this is a public, vibe-coded project; clarify the `go-llm` referenced in
the docs is now majordomo, and link it + gadfly as the downstream consumers
(foreman is a drop-in native-Ollama target via majordomo's ollama.Foreman
preset). CLAUDE.md gains a Build / test / run section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 20:30:52 -04:00
steve 7cd7eaff8b feat: add FOREMAN_KEEP_ALIVE config for worker model residency
CI / Tidy (push) Successful in 9m42s
CI / Build & Test (push) Successful in 10m28s
CI / Publish Docker Image (push) Successful in 21s
Allow configuring how long the worker model stays resident on the Ollama
target after a request via FOREMAN_KEEP_ALIVE env var. Accepts Ollama
duration strings ("-1" forever, "0" unload, "15m", "1h", etc). Defaults
to "-1" (pin forever). The embedder warm-up is unaffected and always
uses keep_alive=-1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 21:29:37 -04:00
steve c16e1af752 ci: push container image to gitea registry on success
CI / Tidy (push) Successful in 9m42s
CI / Build & Test (push) Successful in 10m25s
CI / Publish Docker Image (push) Successful in 1m12s
2026-05-23 19:36:43 -04:00
steve 61513aca04 fix: resolve flaky TestWebhook_LifecycleEvents caused by async delivery ordering
CI / Tidy (push) Successful in 9m40s
CI / Build & Test (push) Successful in 10m29s
The test assumed webhook events arrive in wall-clock order (queued first,
done last), but dispatcher.Fire spawns a goroutine per event with no ordering
guarantee. On a single-core CI runner the "queued" goroutine was routinely
preempted before making its HTTP POST, letting "loading"/"working"/"done"
goroutines land first.

Fix: wait until a "done" event appears in the received set (proving all
prior transitions have been dispatched by the worker), then assert that
"queued" and "done" each appear exactly once rather than checking
positional order.

Reproduced with: GOMAXPROCS=1 go test -race -count=100 -run TestWebhook_LifecycleEvents ./internal/server/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 19:21:01 -04:00
steve e119ed325b chore: add deployment docs, model script, and finalize env config
CI / Build & Test (push) Failing after 5m53s
CI / Tidy (push) Successful in 9m37s
Phase 6 deployment infrastructure: finalize Dockerfile with OCI labels,
improve .env.example with grouped config keys, add scripts/pull-models.sh
for Mac-side model setup, and add docs/deploy.md covering the full
deployment topology, prerequisites, security model, and troubleshooting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:43:10 -04:00
steve 4759a06d1b feat: add Go client package with sync facade over async /jobs
Adds client/ -- a public Go package providing a synchronous facade over
foreman's async POST /jobs API (Level 1 integration per ADR-0011).

Two delivery modes:
- Webhook receiver (preferred): ephemeral HTTP server on random port,
  pushes results immediately, verifies HMAC when configured
- Polling fallback: polls GET /jobs/{id} at configurable interval

Also includes Tags() and Embed() helpers, bearer auth support, and
comprehensive integration tests against the real foreman HTTP handlers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:38:16 -04:00
steve daf07fd759 feat: add async /jobs surface, state webhooks, and artifact handling
Add the async job submission API, webhook state notifications, and
artifact serving endpoints on top of the Phase 3 queue infrastructure.

Key changes:
- POST /jobs: async job submission with 202 + job_id ULID; optional
  state_webhook_url for push notifications on state transitions
- GET /jobs/{id}: job status polling with result, error, and artifact
  metadata; artifacts <= 256KB inlined, larger ones by URL reference
- GET /jobs/{id}/artifacts/{name}: raw artifact data serving
- Webhook dispatcher: at-least-once delivery with exponential backoff
  (5 retries); optional HMAC-SHA256 signing (X-Foreman-Signature)
- ADR-0014: state_webhook_url only honored on POST /jobs, not sync
  /api/chat (caller already blocks for result)
- Comprehensive tests for /jobs lifecycle, webhook delivery, HMAC
  verification, artifact inline/URL threshold, and TTL pruning

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:30:18 -04:00
steve 6fd050855a feat: add durable queue, single worker, and drain-by-model scheduling
Replace the Phase 2 in-flight chat gate (buffered channel) with a real
SQLite-backed job queue and single worker loop. Every /api/chat request
now creates a job row, blocks until the worker completes it, and returns
the result transparently.

Key changes:
- internal/store: NextJob (drain-by-model ordering), IncrementAttempt,
  ResetInterruptedJobs, DeleteTerminalJobsBefore; busy_timeout pragma
- internal/worker: single-threaded worker loop with Notifier for sync
  handler completion signaling; retry on ConnectionError, terminal fail
  on HTTPError; crash recovery resets interrupted jobs on startup
- internal/webhook: dispatcher infrastructure for async webhook delivery
- internal/server: chat handler rewritten to enqueue+wait; old chatGate
  removed; embeddings remain direct concurrent proxies (ADR-0013)
- internal/config: FOREMAN_MAX_ATTEMPTS, FOREMAN_JOB_TTL

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:29:32 -04:00
steve 27f196d333 feat: add Ollama target client, model poller, and native passthrough
Phase 2 of foreman: the daemon now acts as a transparent Ollama proxy.

- internal/ollama: Client interface and HTTP implementation for chat
  (streaming + non-streaming), embed, tags, ps with auth forwarding,
  NDJSON streaming via bufio.Scanner, and connection vs HTTP error
  classification via custom error types.
- internal/ollama: ModelInventory with background poller for /api/tags
  and /api/ps, degraded mode on target unreachable with model retention,
  automatic recovery on reconnect.
- internal/server: Passthrough routes (/api/chat, /api/tags, /api/ps,
  /api/embed, /api/embeddings) with model validation, chat serialization
  gate (capacity-1 channel), concurrent embedding bypass (ADR-0013),
  NDJSON streaming with per-chunk flush, and degraded health reporting.
- cmd/foreman: Full serve wiring with Ollama client, poller goroutine,
  embedder warmup (keep_alive:-1), and signal-based shutdown.

The Mac is now usable as a go-llm target through foreman.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:07:33 -04:00
steve 9cdf4b2472 feat: scaffold project with config, store, health endpoint, CI, and Dockerfile
Phase 1 of foreman: initialize the Go module, project layout, and core
infrastructure. Includes env-based configuration (FOREMAN_* namespace),
SQLite-backed durable job queue with WAL mode via modernc.org/sqlite,
stdlib HTTP server with /healthz and optional bearer-token auth middleware,
subcommand dispatch (serve + stubs), Gitea CI workflow, multi-stage
distroless Dockerfile, and comprehensive tests for all packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 17:58:36 -04:00
steve d5702f7a75 add initial prompts 2026-05-23 16:51:19 -04:00
steve 8fde024281 initial commit 2026-05-23 16:41:20 -04:00