feat: live deployment with automated acceptance checklist (phase 13)

- APP_INTERNAL_URL: in-network base URL for §5.2 callbacks and signed
  attachment URLs handed to the external services (compose: http://app:8787)
- work-performer image runs as the node user with ~/.claude mounted into
  /home/node — the claude CLI refuses --dangerously-skip-permissions as root
- scripts/acceptance.sh: re-run-safe live verification of the §13 checklist
  (demo import within one poll, subdivide sum=1 + editable, extend sibling,
  publish/bounty math, decline/claim/approve, changes-requested loop,
  approval award in metrics, unassign, AI job through real Claude Code with
  signed idempotent callback, breaker independence between the two services)
- README/DECISIONS: sudo HOME gotcha, internal URL, non-root performer

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
etalon
2026-06-12 21:06:23 +02:00
parent c69c028028
commit 6265ffa894
10 changed files with 230 additions and 8 deletions
+33
View File
@@ -104,3 +104,36 @@ Spec-silent choices, recorded as required by the build instructions.
/change-password.
- **Pages for later phases render an "under construction" placeholder** so
role-based navigation is complete and clickable now.
## Phases 512 (selected)
- **`internal/extsvc`** (not in the §14 list) holds the circuit breaker and
retrying JSON client shared by the two service clients — sharing plumbing,
not state; each service keeps its own breaker, URL, and token.
- **Mock services are separate codebases**: `services/atomizer` has its own
`go.mod` (stdlib only); `services/work-performer` is plain Node with zero
npm dependencies (plus the globally installed claude CLI in its image).
- **AI-approved tasks earn no `bountyAwards` row** — §4.5 awards are a
developer performance ledger; the AI submitter has no developer identity.
- **Bulk archive/publish are partial-success APIs** returning
`{done[], failed{}}` instead of failing the whole batch.
- **Extension siblings record `parentId = source task`** (spec literal),
so the UI tree shows extensions beneath their source.
## Phase 13 (deployment)
- **`APP_INTERNAL_URL` (compose: `http://app:8787`)** is handed to the
external services for callback URLs and signed attachment URLs — the §5.2
example uses the in-network hostname; `APP_BASE_URL` stays browser-facing.
- **The work-performer container runs as the `node` user** (uid 1000) with
`${HOME}/.claude` mounted into `/home/node/` instead of `/root/` (§9.2
shows /root): the claude CLI refuses `--dangerously-skip-permissions` as
root, so the literal spec mount can never execute jobs.
- **Run compose with the real user's HOME** — `sudo docker compose` resolves
`${HOME}` to `/root` and silently mounts the wrong Claude credentials. Use
`sudo --preserve-env=HOME docker compose …` (or run docker unprivileged).
- **UFW**: a rule allowing `172.16.0.0/12` (docker networks) to reach the
host was added so container→host callbacks work in contract tests.
- **`scripts/acceptance.sh`** automates the §13 checklist live (import →
subdivide → extend → publish → claim/decline/approve → review → award →
AI job via real Claude Code → breaker independence) and is re-run-safe.