feat(webui): worker registry and configuration schema (Closes #798) #810
Merged
sysadmin
merged 1 commits from 2026-07-22 06:48:29 -05:00
feat/issue-798-worker-registry-schema into master
Labels
Clear labels
allocator
anti-stomp
architecture
bug
chore
codex
concurrency
contamination
control-plane
dashboard
database
design
documentation
enhancement
gitea
glitchtip
important
incident
incident-bridge
integration
jenkins
labels
leases
mcp
mcp-health
mcp-menu
multi-project
mutating
nice-to-have
observability
portability
preflight
protected-branch
queue
read-only
reconnect
recovery
refactor
release
reliability
resumable-review
reviewer
roadmap
safety
security
self-hosted
sentry
stale-runtime
status:blocked
status:in-progress
status:pr-open
status:ready
terminal-lock
testing
tracker
type:bug
type:feature
type:feature
type:guardrail
visibility
workflow
workflow-hardening
workflow-hardening
Controller-owned work allocator
Prevent concurrent LLM session stomping
Architecture / structural design
OpenAI Codex client / workflow session surface
Concurrent session safety
Workflow or session contamination incident
MCP control-plane coordination and allocation authority
MCP operational dashboard/queue view
Internal coordination storage (SQLite/Postgres)
Design / investigation, no implementation
Docs / runbooks
New feature or improvement
Gitea MCP workflow
GlitchTip integration
Operational or process incident requiring durable audit trail
Sentry-to-Gitea incident bridging
Integration testing
Jenkins integration
Label taxonomy management
Lease adopt/release/expire lifecycle
MCP server / tooling
MCP namespace and runtime health
MCP menu surface
Work spanning multiple monitoring projects or Gitea repos
Mutating action; requires gating
Observability, metrics, traces, error reporting
Cross-platform / portability
Shared preflight gates before mutation
Protected branch / stable-branch policy concern
Work queue visibility and allocation
Read-only, no mutation
MCP client reconnect/reload recovery path
Recovery paths for stale/foreign leases
Code refactor / restructure
Release / versioning
Reliability / failure handling
Persist and resume prepared review verdicts across sessions
Reviewer workflow tooling
Roadmap / umbrella issue
Safety rails and fail-closed mutation guards
Security / trust boundary
Self-hosted infrastructure integration
Sentry error monitoring integration
Stale backend daemon / runtime-vs-master parity failures
Issue is blocked
Issue is being worked on
Issue has an open pull request
Issue is ready for work
Terminal review lock (#332) path
Tests / test coverage
Issue tracker hygiene / meta
Bug or defect
Feature or enhancement
Feature or enhancement
Safety gate or guardrail
Workflow state visibility for LLMs/operators
Cross-tool workflow
LLM workflow coordination hardening
LLM workflow coordination hardening
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Scaled-Tech-Consulting/Gitea-Tools#810
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #798
Parent epic: #797. This pull request implements child #798 only. It does not complete the epic, and #797 must stay open.
Problem
Epic #797 needs one control surface for the scheduled multi-LLM worker fleet (Claude, Grok, Codex, AGY, Kimi K). Every other child depends on knowing what a worker is: #799 renders registered workers, #801 validates them, #802 edits them, #803 and #804 read their schedule and scheduler metadata, #805 targets one by id, #806 keys run records to them, #807 enforces their namespace binding, #808 migrates existing ones into them. None of that can be built against an undefined record.
Nothing in the tree defined one.
rg -l -i 'worker_registry|WorkerRegistry'returned no files before this change.webui/project_registry.pyis a different entity (git projects and onboarding) with no provider or worker concept and no write path.What changed
webui/worker_registry.py(new, 647 lines). The data model, its validation, and its persistence.Two separate entity kinds. A provider describes an LLM runtime — vendor, executable, models, availability — and exists whether or not any worker uses it. A worker is a configured instance binding one provider, model, project, role, namespace, profile, workflow, schedule, timeout, enabled state, and scheduler record. Separating them is what lets #799 list all five providers when a provider has no worker configured, and it stops provider facts being copied into (and drifting across) every worker row.
Validation fails closed. Unknown fields are refused rather than ignored — a typo'd
timeout_secondwould otherwise read as "no timeout declared". A worker naming an undeclared provider is rejected, because a record that looks configured but is unrunnable is exactly the ambiguous ownership the epic requires to fail closed. Worker ids, provider ids, and LaunchAgent labels are each unique; two workers sharing a launchd label would silently overwrite each other's agent. Roles are constrained to the sanctioned set, since the role selects the namespace whose capability gates bind the worker. Timeouts must be positive integers bounded at 24h, so a worker cannot be configured to hold a lease indefinitely. Schedules are validated per kind (intervalneeds positive seconds,cronneeds five crontab fields,manualneeds neither) and fields belonging to a different kind are refused.Atomic persistence. Temp file in the same directory,
fsync, thenos.replace— the same idiom asissue_lock_store.save_lock_file. An invalid document is refused before any write, so a failed save cannot leave a partial file.Versioning and rollback. Each save archives the superseded document under its own revision number and writes the successor at
revision + 1.rollback_to_revisionrepublishes a retained revision as a new head rather than rewinding, so history is append-only and a rollback is itself reversible. History is capped at 20 revisions.webui/registry_safety.py(new, 52 lines). The credential-rejection guard, extracted so the two registries share one implementation. Registries are operator-editable files the browser layer reads, so none may carry a secret; the check refuses the shape of a credential key at any depth rather than guessing at values.webui/project_registry.py. Refactored to import that guard, deleting its module-private copy. Behaviour is identical and no public symbol changed — only the private_FORBIDDEN_*constants,_is_forbidden_key, and the local_reject_credential_keysbody were removed. The existingtest_registry_rejects_credential_keyscovers the swap and still passes. Stated plainly for the reviewer: this is the one file in the diff not created by this issue, and it is touched to avoid shipping a second copy of a security check.webui/data/workers.registry.json(new). The five providers of #797, withavailablereflecting an actual PATH probe (claude,grok,codex,agy,kimiall present; note the Kimi K provider id iskimi-kwhile its executable iskimi). Theworkersarray ships empty on purpose — populating it is #808, and inventing worker records here would assert fleet state that does not exist.Acceptance criteria
load_registry/ packagedwebui/data/workers.registry.json;TestPackagedRegistryProviderRecordvsWorkerRecord;TestSeparateEntities(provider with no workers, many workers per provider, unknown-provider refusal)WorkerRecord;TestRecordedFields, including a subtest asserting each field is individually requiredvalidate_payload;TestSchemaValidation,TestScheduleValidation_atomic_write/save_registry;TestAtomicPersistencesave_registry,list_revisions,rollback_to_revision;TestVersioningAndRollbacktests/test_webui_worker_registry.py, 44 casesTests and results
Run with
venv/bin/python -m pytestfrom the allocated worktree.tests/test_webui_worker_registry.py— 44 passed, 38 subtests passed.-k webui, includes the refactored project registry) — 149 passed, 144 subtests passed.Baseline comparison: a detached worktree pinned to master
5032965e3a39654f1d65b2a58f25931bc72ff17b— the exact base of this branch — reports 4262 passed, 11 failed, 6 skipped, 493 subtests passed running the identical command. The eleven failing node identifiers are byte-for-byte the same on both: six intest_commit_payloads.py, two intest_issue_702_review_findings_f1_f6.py, and one each intest_mcp_server.py::TestPreflightVerification,test_post_merge_moot_lease.py, andtest_reconciler_supersession_close.py. All are pre-existing author-worktree-binding and preflight fixtures; none touches the web UI. The pass delta 4262 to 4306 is exactly the 44 new cases, so this branch adds no new failures. The baseline worktree was removed after the run.Preserved behaviour
The web UI is untouched: no route added or changed, no template, no bind logic.
webui/deployment_boundary.pyand its loopback-only protections are not modified — this change adds no network surface at all. The registry is a file the module reads and writes; nothing here serves it over HTTP. That is #799.Risk
Low. The only existing file modified is
project_registry.py, and only to delegate a private helper to an identical extracted implementation, proven by its own unchanged test. Everything else is new and reachable only from the new test suite until #799 wires it up.Known limitations
availableon a provider is an operator declaration, not a live probe; executable discovery, version inspection, and auth-state checks belong to #800. Next-run computation from a schedule belongs to #803. No worker is registered yet, by design (#808). The history cap of 20 revisions is a constant rather than configuration.Provenance
feat/issue-798-worker-registry-schema/Users/jasonwalker/Development/Gitea-Tools/branches/issue-798-worker-registry-schema5463f589334f6a9520e2512a3c33929976d924ac5032965e3a39654f1d65b2a58f25931bc72ff17bgitea_commit_filesfrom the worktree, so the local worktree commit3afe0b2and the published head are siblings carrying identical trees; the published head above is authoritative for review.repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #810
issue: #798
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 36068-f6679735fdeb
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-810-session
phase: claimed
candidate_head:
5463f58933target_branch: master
target_branch_sha:
0b29404031last_activity: 2026-07-22T11:45:58Z
expires_at: 2026-07-22T11:55:58Z
blocker: none
Canonical PR State
STATE: approved
WHO_IS_NEXT: merger
NEXT_ACTION: Merge PR #810 at pinned head
5463f58933if merger gates passNEXT_PROMPT:
WHAT_HAPPENED: Independent review of next eligible open PR #810 after skipping #794 and #795 (live REQUEST_CHANGES at unchanged heads).
WHY: Scope matches #798; validation passed; author-safety ok; not already landed.
ISSUE: #798
HEAD_SHA:
5463f58933REVIEW_STATUS: APPROVED
MERGE_READY: true
BLOCKERS: none
VALIDATION: pytest tests/test_webui_worker_registry.py = 44 passed; pytest -k webui = 149 passed; worktree clean before/after
NATIVE_REVIEW_PROOF: transport=native_mcp; entrypoint=mcp_server; token_fingerprint=5ce4b79a999d0487
LAST_UPDATED_BY: sysadmin (prgs-reviewer)
Independent review detail
Verdict: APPROVE
Selected PR: #810 (next review-eligible after skip of blocked #794 and #795).
Author: jcwalker3 (not self-review).
Candidate/reviewed head:
5463f589334f6a9520e2512a3c33929976d924acScope
Implements #798 only (worker registry schema). Parent epic #797 correctly stays open. Five files: new worker_registry, registry_safety extraction, empty workers array with five providers, tests, and project_registry credential-guard delegation.
Diff review
Validation
Worktree:
/Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-810-session@5463f58venv/bin/pytest tests/test_webui_worker_registry.py→ 44 passed, 38 subtestsvenv/bin/pytest -k webui→ 149 passed, 144 subtests0b294040314e)Merge decision
Approved for merger at pinned head only.
CTH: Reviewer Handoff
Status: approved_awaiting_merge
Next owner: merger (prgs-merger / sysadmin)
Current blocker: none
Decision: APPROVE PR #810 at head
5463f58933Proof: validation 44+149 passed; author jcwalker3; not already landed; skips #794/#795 REQUEST_CHANGES at unchanged heads revalidated live
Next action: merger merges at exact pinned head if gates pass
Ready-to-paste prompt: see NEXT_PROMPT above
adopted_at: 2026-07-22T11:48:20Z
adopted_by_identity: sysadmin
adopted_by_profile: prgs-merger
adopted_from_session_id: 36068-f6679735fdeb
adopted_from_profile: prgs-reviewer
adopted_from_reviewer_identity: sysadmin
adopted_from_comment_id: 14146
adoption_reason: merger-handoff-approved-head
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #810
issue: #798
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 36066-eda5ddb64f97
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-810-session
phase: adopted
candidate_head:
5463f58933target_branch: master
target_branch_sha:
0b29404031last_activity: 2026-07-22T11:48:20Z
expires_at: 2026-07-22T11:58:20Z
blocker: none
Stale #332 review-decision lock cleanup (#594)
Status: APPLIED
sysadminprgs-merger2026-07-22T11:48:31.335615+00:00approveon PR feat(webui): worker registry and configuration schema (Closes #798) (#810)closed(merged=True)9eb0f29cefa85fd0ecbbb5f923ebc748bffa81f81prgs-reviewerManual deletion of session-state files is not the workflow.
This path only clears a lock when the referenced PR is merged/closed.