refactor: Align vocabulary — component → program / deployment

Canonical terms (see docs/registry.md glossary):
- program: any catalog entry (tool/daemon/frontend) — the software
- service / job: how a program is deployed (systemd .service / .timer)
- deployment: umbrella for the unified service+job+program view

Changes:
- core: ServiceSpec/JobSpec component: → program: (component accepted as
  back-compat validation_alias); DeployedComponent → Deployment.
- api: ComponentSummary/Detail → DeploymentSummary/Detail; GET /components
  → /deployments; GatewayRoute.component → program; GatewayInfo
  .component_count → deployment_count; ServiceActionResponse/action keys
  component → program.
- app: matching type renames, /components → /deployments hook, route
  /component/:name → /deployment/:name, GatewayRoute.program.
- docs: component-registry.md → registry.md (+ canonical glossary);
  CLAUDE.md endpoint list refreshed (drop removed /tools, add typed
  program/service/job + config routes).

Tests: core 92, cli 24, api 52 green; app build clean; ruff clean.
This commit is contained in:
2026-06-14 11:05:22 -07:00
parent 482524bfe5
commit 3f3b88f17b
44 changed files with 279 additions and 237 deletions

View File

@@ -94,7 +94,7 @@ def run_create(args: argparse.Namespace) -> int:
if behavior == "daemon":
config.services[name] = ServiceSpec(
id=name,
component=name,
program=name,
run=RunPython(runner="python", program=name),
expose=ExposeSpec(
http=HttpExposeSpec(

View File

@@ -66,10 +66,10 @@ def run_info(args: argparse.Namespace) -> int:
stack = None
if program and program.stack:
stack = program.stack
elif service and service.component and service.component in config.programs:
stack = config.programs[service.component].stack
elif job and job.component and job.component in config.programs:
stack = config.programs[job.component].stack
elif service and service.program and service.program in config.programs:
stack = config.programs[service.program].stack
elif job and job.program and job.program in config.programs:
stack = config.programs[job.program].stack
if stack:
print(f" {BOLD}stack{RESET}: {stack}")
@@ -88,12 +88,12 @@ def run_info(args: argparse.Namespace) -> int:
spec = service or job
if spec:
desc = spec.description
if not desc and spec.component and spec.component in config.programs:
desc = config.programs[spec.component].description
if not desc and spec.program and spec.program in config.programs:
desc = config.programs[spec.program].description
if desc and not (program and program.description == desc):
print(f" {BOLD}description{RESET}: {desc}")
if spec.component:
print(f" {BOLD}program{RESET}: {spec.component}")
if spec.program:
print(f" {BOLD}program{RESET}: {spec.program}")
# Run spec
print(f" {BOLD}runner{RESET}: {spec.run.runner}")
@@ -149,8 +149,8 @@ def run_info(args: argparse.Namespace) -> int:
source_dir = None
if program and program.source_dir:
source_dir = program.source_dir
elif spec and spec.component and spec.component in config.programs:
source_dir = config.programs[spec.component].source_dir
elif spec and spec.program and spec.program in config.programs:
source_dir = config.programs[spec.program].source_dir
if source_dir:
claude_md = _find_claude_md(config.root, source_dir)
@@ -191,10 +191,10 @@ def _info_json(
stack = None
if program and program.stack:
stack = program.stack
elif service and service.component and service.component in config.programs:
stack = config.programs[service.component].stack
elif job and job.component and job.component in config.programs:
stack = config.programs[job.component].stack
elif service and service.program and service.program in config.programs:
stack = config.programs[service.program].stack
elif job and job.program and job.program in config.programs:
stack = config.programs[job.program].stack
if stack:
data["stack"] = stack

View File

@@ -54,13 +54,13 @@ def _resolve_stack(config: object, name: str) -> str | None:
# Check services for program ref
if name in config.services:
svc = config.services[name]
comp_name = svc.component
comp_name = svc.program
if comp_name and comp_name in config.programs:
return config.programs[comp_name].stack
# Check jobs for program ref
if name in config.jobs:
job = config.jobs[name]
comp_name = job.component
comp_name = job.program
if comp_name and comp_name in config.programs:
return config.programs[comp_name].stack
# Direct program