Why:
svc.sh only knew two hardcoded environments (dev/prod) and always built
the current workspace, so there was no way to deploy a specific Dev- repo
commit or a Releases artifact, and no record of what was actually started.
Evidence:
- scripts/svc.sh commits dev|release lists deployable commits (release
entries annotated with version + devCommit; both mark HEAD and the
environments already running them).
- deploy --from local|dev@<sha>|release@<version|sha> materializes the
chosen source: dev commits build in an isolated worktree
(runtime/.worktrees/<sha>, submodule pins verified to land exactly),
release artifacts are sha256-verified against versions/<v>/manifest.json
(a forged manifest is refused); --pick offers an interactive numbered
choice and --dry-run prints the plan.
- Environment names are generalized (^[a-z][a-z0-9-]{0,15}$, derived port
8800-9099 on collision-free scan); each env is self-contained under
runtime/<env>/{bin,plugins,data} with svc.env (current state) and
history.jsonl (append-only deploy/start/stop events). status scans all
registered envs and history [env] [N] merges the event stream.
- Old artifacts that predate --dsn/--auth have those flags auto-omitted
(probed via serve usage); legacy svc.env files still render in status.
- Verified end to end in the task worktree: stub-binary lifecycle, a real
release@0.1.0 deploy (HTTP 200, source shown in status, commits marked),
failure paths (sha mismatch, immediate exit, non-TTY --pick).
Impact:
scripts/svc.sh users gain multi-version side-by-side environments and
rollback by redeploying an older source; existing dev/prod defaults and
safety constraints (own-pidfile-only stop, port-occupancy check) are
unchanged. Environments deployed by the previous script need one fresh
deploy before start/restart works again. Makefile gains svc-commits and
svc-history targets.
Refs: .task/feature-svc-deploy-source-registry.md(任务卡=PR 描述)