Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0c0788e
feat(28): /healthz probes + SegmentSubscriber lifespan wiring on the …
helloiamvu Jul 3, 2026
dc6070e
feat(28): 6 service Dockerfiles + earnings ingest entrypoints
helloiamvu Jul 3, 2026
787bb0b
feat(28-21): settlement-station backfill roster + CLI --roster/shard/…
helloiamvu Jul 3, 2026
dd13aed
feat(28): manual-only per-service deploy workflows + deploy-time IAM
helloiamvu Jul 3, 2026
741225b
fix(28): review round 1 — STT HTTP service, pilot args, injection-saf…
helloiamvu Jul 3, 2026
fde6e55
fix(28): review round 2 — reconcile ingest entrypoints to the infra e…
helloiamvu Jul 3, 2026
9a34fa5
fix(28): review round 3 — roster HKO hole, STT handoff-audio delete, …
helloiamvu Jul 3, 2026
61898c7
fix(28): review round 4 — STT delete-after-ledger + cross-project sub…
helloiamvu Jul 3, 2026
784deb8
fix(28): review round 5 — Batch secrets project + STT publisher grant
helloiamvu Jul 3, 2026
fad955f
fix(28): review round 6 — capture->STT triggering, lease extension, s…
helloiamvu Jul 3, 2026
808f7cb
fix(28): review round 7 — transcript R2 durability + capture->STT tri…
helloiamvu Jul 3, 2026
1cbcc66
fix(28): review round 7-2 — provision handoff bucket, idempotent ledg…
helloiamvu Jul 3, 2026
df919df
fix(28): review round 7-3 — GOES-footprint roster filter, yesterday-y…
helloiamvu Jul 3, 2026
a0c56f5
fix(28): review round 7-4 — serving reads earnings transcripts/facts …
helloiamvu Jul 3, 2026
a80ff5b
fix(28): review round 7-5 — fail loud on subscription capture with no…
helloiamvu Jul 3, 2026
1645d0d
fix(28): review round 7-6 — STT service derives live-streaming config…
helloiamvu Jul 3, 2026
d968421
docs(28): mark STT->rolefact trigger as deferred operator-gated orche…
helloiamvu Jul 3, 2026
2082164
Merge remote-tracking branch 'origin/main' into phase28/deploy-runtim…
helloiamvu Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/deploy-earnings-capture.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Deploy earnings-capture (28-10)

# Phase 28 (28-10) — WIF build+deploy for the earnings CAPTURE Cloud Run Job
# (Chromium/ffmpeg webcast capture) in mr-earnings-ingest / eu-west3. This is the
# AUDIO side of the firewall (D-27.9): audio dies on the Job's ephemeral disk and
# NEVER gets an R2 key. Image build/push + Job image-swap only; the job's args,
# the static-egress VPC pin (IVS), secrets + SA are Terraform-owned (infra/).
#
# OPERATOR-GATED: the live capture pipeline (IVS edge, static egress IP) is
# validated by an operator with a scheduled live call (28-10 Task 3). This
# workflow ships the deploy path; it does not run a live capture.
#
# Setup Variables (from `tofu -chdir=infra output`): WIF_PROVIDER, DEPLOY_SA_INGEST,
# AR_HOST, INGEST_PROJECT_ID.

on:
workflow_dispatch:
inputs:
image_tag:
description: "Image tag to build + deploy."
required: true
default: "latest"
type: string

permissions:
id-token: write
contents: read

env:
AR_HOST: ${{ vars.AR_HOST }}
AR_PROJECT: mostlyright-backend
AR_REPO: mostlyright
IMAGE_NAME: earnings-capture
JOB: earnings-capture
REGION: europe-west3

jobs:
deploy:
name: Build + push capture image, roll the capture Job
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Authenticate to GCP (WIF, keyless)
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ vars.WIF_PROVIDER }}
service_account: ${{ vars.DEPLOY_SA_INGEST }}

- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v2

- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker "${AR_HOST}" --quiet

- name: Build capture image
env:
IMAGE_TAG: ${{ inputs.image_tag }}
run: |
IMAGE="${AR_HOST}/${AR_PROJECT}/${AR_REPO}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"
docker build -f deploy/earnings/capture.Dockerfile -t "${IMAGE}" .

- name: Push image
run: docker push "${IMAGE}"

- name: Deploy earnings-capture Job (image swap only; config is Terraform-owned)
run: |
gcloud run jobs deploy "${JOB}" \
--project "${{ vars.INGEST_PROJECT_ID }}" \
--region "${REGION}" \
--image "${IMAGE}" \
--quiet
70 changes: 70 additions & 0 deletions .github/workflows/deploy-earnings-rolefact.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Deploy earnings-rolefact (28-13)

# Phase 28 (28-13) — WIF build+deploy for the role/fact-builder Cloud Run Job
# (CPU) in mr-earnings-ingest / eu-west3. POST-audio side of the firewall: it
# reads transcript text, builds derived facts, and writes transcript + fact
# parquet to R2 with the WRITE token — it holds NO audio and needs no audio
# toolchain. Image build/push + Job image-swap only; args + R2-write secrets + SA
# are Terraform-owned (infra/).
#
# Setup Variables (from `tofu -chdir=infra output`): WIF_PROVIDER, DEPLOY_SA_INGEST,
# AR_HOST, INGEST_PROJECT_ID.

on:
workflow_dispatch:
inputs:
image_tag:
description: "Image tag to build + deploy."
required: true
default: "latest"
type: string

permissions:
id-token: write
contents: read

env:
AR_HOST: ${{ vars.AR_HOST }}
AR_PROJECT: mostlyright-backend
AR_REPO: mostlyright
IMAGE_NAME: earnings-rolefact
JOB: earnings-rolefact
REGION: europe-west3

jobs:
deploy:
name: Build + push rolefact image, roll the rolefact Job
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Authenticate to GCP (WIF, keyless)
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ vars.WIF_PROVIDER }}
service_account: ${{ vars.DEPLOY_SA_INGEST }}

- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v2

- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker "${AR_HOST}" --quiet

- name: Build rolefact image
env:
IMAGE_TAG: ${{ inputs.image_tag }}
run: |
IMAGE="${AR_HOST}/${AR_PROJECT}/${AR_REPO}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"
docker build -f deploy/earnings/rolefact.Dockerfile -t "${IMAGE}" .

- name: Push image
run: docker push "${IMAGE}"

- name: Deploy earnings-rolefact Job (image swap only; config is Terraform-owned)
run: |
gcloud run jobs deploy "${JOB}" \
--project "${{ vars.INGEST_PROJECT_ID }}" \
--region "${REGION}" \
--image "${IMAGE}" \
--quiet
106 changes: 106 additions & 0 deletions .github/workflows/deploy-earnings-serving.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Deploy earnings-serving (28-12)

# Phase 28 (28-12) — WIF-authenticated build+deploy for the earnings serving
# Cloud Run service (/transcripts /facts /capabilities /stream) in
# mr-serving/eu-west3. KEYLESS auth via Workload Identity Federation — no SA key
# files. The service resource + its R2-read-only secret wiring + EARNINGS_API_KEY
# + EARNINGS_STREAMING_SUBSCRIPTION + the H2 min=max=1 scaling all live in infra/
# (cloud_run.tf earnings_serving); this workflow only builds+pushes the AUDIO-FREE
# image (deploy/earnings/serving.Dockerfile) and rolls a new revision.
#
# H2 (load-bearing): the SSE fan-out over ONE shared earnings-streaming
# subscription is correct ONLY at exactly one always-warm instance. The smoke
# step asserts min-instances=1 is preserved (a broken scaling config would
# silently split-brain the stream). Config is Terraform-owned; --image only swaps
# the container on the current config.
#
# Setup (repo/environment Variables, from `tofu -chdir=infra output`):
# WIF_PROVIDER = <wif_provider_name>
# DEPLOY_SA_SERVING = deploy@mr-serving...
# AR_HOST = europe-west3-docker.pkg.dev
# SERVING_PROJECT_ID = mr-serving

on:
workflow_dispatch:
inputs:
image_tag:
description: "Image tag to build + deploy (e.g. a git SHA or 'latest')."
required: true
default: "latest"
type: string

permissions:
id-token: write
contents: read

env:
AR_HOST: ${{ vars.AR_HOST }}
AR_PROJECT: mostlyright-backend
AR_REPO: mostlyright
IMAGE_NAME: earnings-serving
SERVICE: earnings-serving
REGION: europe-west3

jobs:
deploy:
name: Build + push audio-free image, roll earnings-serving revision
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Authenticate to GCP (WIF, keyless)
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ vars.WIF_PROVIDER }}
service_account: ${{ vars.DEPLOY_SA_SERVING }}

- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v2

- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker "${AR_HOST}" --quiet

- name: Build audio-free earnings-serving image
env:
IMAGE_TAG: ${{ inputs.image_tag }}
run: |
IMAGE="${AR_HOST}/${AR_PROJECT}/${AR_REPO}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"
# Build from the repo root so the Dockerfile can COPY packages/ + services/.
docker build \
-f deploy/earnings/serving.Dockerfile \
-t "${IMAGE}" \
.

- name: Assert the built image carries NO audio toolchain (firewall a)
run: |
# Defense in depth: the serving image must physically omit
# faster-whisper / av / ffmpeg / chromium (D-27.9). Fail the deploy if
# any slipped in via a transitive dep.
if docker run --rm --entrypoint sh "${IMAGE}" -c \
"pip list 2>/dev/null | grep -Eiq 'faster-whisper|^av |ffmpeg' && exit 1 || exit 0"; then
echo "audio-free image OK"
else
echo "::error::earnings-serving image contains an audio dependency — firewall a (D-27.9) breach"
exit 1
fi

- name: Push image
run: docker push "${IMAGE}"

- name: Deploy revision (image swap only; config is Terraform-owned)
run: |
gcloud run deploy "${SERVICE}" \
--project "${{ vars.SERVING_PROJECT_ID }}" \
--region "${REGION}" \
--image "${IMAGE}" \
--quiet

- name: Verify H2 min-instances=1 preserved (single always-warm SSE instance)
run: |
MIN=$(gcloud run services describe "${SERVICE}" \
--project "${{ vars.SERVING_PROJECT_ID }}" \
--region "${REGION}" \
--format="value(spec.template.metadata.annotations['autoscaling.knative.dev/minScale'])")
echo "min-instances = ${MIN:-0}"
test "${MIN:-0}" = "1" || { echo "::error::expected H2 min-instances=1 (single always-warm SSE instance)"; exit 1; }
86 changes: 86 additions & 0 deletions .github/workflows/deploy-earnings-stt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Deploy earnings-stt (28-11)

# Phase 28 (28-11) — WIF build+deploy for the STT Cloud Run service (NVIDIA L4
# GPU, scale-to-zero) in mr-earnings-ingest / us-central1 (L4 GPU is NOT in
# eu-west3 — 28-OPERATOR-INPUTS). faster-whisper / CTranslate2, NO torch (D-27.5).
# Image build/push + service image-swap only; the GPU config, bounded concurrency
# (≤ L4 quota, H8), secrets + SA are Terraform-owned (infra/cloud_run.tf stt).
#
# OPERATOR-GATED: the live GPU smoke (a real transcription on L4) is 28-11 Task 4
# (operator, autonomous:false) — this workflow ships the deploy path only.
#
# NOTE: the STT image is CUDA-based and large; the GitHub-hosted runner builds it
# but does not need a GPU (the build installs the CUDA runtime + faster-whisper;
# it never runs inference here).
#
# Setup Variables (from `tofu -chdir=infra output`): WIF_PROVIDER, DEPLOY_SA_INGEST,
# AR_HOST, INGEST_PROJECT_ID.

on:
workflow_dispatch:
inputs:
image_tag:
description: "Image tag to build + deploy."
required: true
default: "latest"
type: string

permissions:
id-token: write
contents: read

env:
AR_HOST: ${{ vars.AR_HOST }}
AR_PROJECT: mostlyright-backend
AR_REPO: mostlyright
IMAGE_NAME: earnings-stt
SERVICE: earnings-stt
REGION: us-central1

jobs:
deploy:
name: Build + push STT (CUDA) image, roll the STT service
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Authenticate to GCP (WIF, keyless)
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ vars.WIF_PROVIDER }}
service_account: ${{ vars.DEPLOY_SA_INGEST }}

- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v2

- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker "${AR_HOST}" --quiet

- name: Build STT (CUDA + faster-whisper, no torch) image
env:
IMAGE_TAG: ${{ inputs.image_tag }}
run: |
IMAGE="${AR_HOST}/${AR_PROJECT}/${AR_REPO}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"
docker build -f deploy/earnings/stt.Dockerfile -t "${IMAGE}" .

- name: Assert the STT image has NO torch (D-27.5 — CTranslate2 only)
run: |
if docker run --rm --entrypoint sh "${IMAGE}" -c \
"pip list 2>/dev/null | grep -Eiq '^torch ' && exit 1 || exit 0"; then
echo "no-torch OK"
else
echo "::error::earnings-stt image pulled torch — D-27.5 forbids it (faster-whisper/CTranslate2 only)"
exit 1
fi

- name: Push image
run: docker push "${IMAGE}"

- name: Deploy earnings-stt service (image swap only; GPU config is Terraform-owned)
run: |
gcloud run deploy "${SERVICE}" \
--project "${{ vars.INGEST_PROJECT_ID }}" \
--region "${REGION}" \
--image "${IMAGE}" \
--quiet
Loading
Loading