Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ metadata:
NOTHING. Review every row against the store and live Temporal.
2. dry_run=false — writes the rows the report planned.

A dry run works in any rollout mode. An APPLY additionally needs
`-p rollout_mode=observe` — the operator attesting that the mctl-agents
worker is already flipped. Without it bootstrap.py exits 2, because
below observe the DevLoopWorkflow's ownership activity short-circuits, so
a row written here for a devloop-workflow owner would never be
heartbeated, progressed or released by the workflow it names. It would
sit `active` until its liveness bound expired, held by an owner that does
not know it holds anything.

So the rollout is three steps and this is the middle one:
1. the WORKER flips to observe — the writer goes live
2. this workflow, dry then apply
3. the SHEPHERD flips — the comparison starts against a populated store

--service and --slug scope a run. Worth using for the first apply:
apply_report skips an entity the store already holds, so a row written
wrongly cannot be corrected by re-running.

Safety properties, all in orchestrator/lifecycle/bootstrap.py:
- READ FIRST. Any entity that reads UNKNOWN aborts the whole run
with nothing written: an id whose owner cannot be seen is not one
Expand All @@ -47,6 +65,9 @@ metadata:
-p agent_image=ghcr.io/mctlhq/mctl-agents:<version with bootstrap.py> \
-p dry_run=true

and, for the apply, additionally -p rollout_mode=observe. That is an
attestation and not a check: this pod cannot see the worker's mode.

agent_image has no default on purpose: the module does not exist in any
released image yet, so there is no value that could work. Argo refuses
the submission by name rather than failing inside the container on a
Expand Down Expand Up @@ -84,6 +105,22 @@ spec:
# The DEFAULT, and deliberately the safe one. bootstrap.py requires
# --apply to write anything, so an operator who submits this template
# with no parameters at all gets a report and no writes.
- name: rollout_mode
value: "off"
# The operator's ATTESTATION that the mctl-agents worker is already at
# LIFECYCLE_ROLLOUT_MODE=observe — not a verification of it. This
# parameter sets the variable in THIS pod, and the hazard it guards is
# the WORKER's mode, which this pod cannot see.
#
# Without it --apply is unreachable in production: bootstrap.py refuses
# to write below observe and exits 2, and nothing else in this template
# sets the variable. With it, an apply is a deliberate two-part
# statement — dry_run=false AND rollout_mode=observe — rather than
# something a default carried in unnoticed.
#
# Defaults to "off" so a submission that forgets it refuses rather than
# writes. The value is echoed into the report, so what was claimed is
# on the record beside what it licensed.
- name: agent_image
# NO DEFAULT, deliberately, and unlike every sibling template.
#
Expand Down Expand Up @@ -197,6 +234,8 @@ spec:
env:
- name: WORKFLOW_DRY_RUN
value: "{{workflow.parameters.dry_run}}"
- name: LIFECYCLE_ROLLOUT_MODE
value: "{{workflow.parameters.rollout_mode}}"
- name: STATE_DIR
value: /workdir/mctl-gitops/platform-gitops/agents-state
# The ownership store is admin-gated: GET /lifecycle/ownership/batch
Expand Down
17 changes: 17 additions & 0 deletions platform-gitops/services/admins/mctl-agents-worker/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,23 @@ env:
TEMPORAL_ADDRESS: temporal-frontend.temporal.svc.cluster.local:7233
TEMPORAL_NAMESPACE: mctl-agents
LOG_LEVEL: info
# The lifecycle rollout stage (ADR-010 §12, mctlhq/.github#57).
#
# This worker runs DevLoopWorkflow, the only WRITER. It flips FIRST, ahead of
# the shepherd CWFT and ahead of the ownership bootstrap, and the order is
# not cosmetic: below `observe` the ownership activity short-circuits before
# any HTTP call, so a row the bootstrap wrote for a devloop-workflow owner
# would never be heartbeated, progressed or released by the workflow it
# names. It would sit `active` until its liveness bound expired, held by an
# owner that does not know it holds anything.
#
# Writer live -> bootstrap -> shepherd flips and the comparison starts
# against a populated store.
#
# The gate is in ACTIVITY code, not workflow code, so this does not change
# which commands the workflow issues: a history recorded under one mode
# replays under another.
LIFECYCLE_ROLLOUT_MODE: observe
# GitHub App installation tokens expire after 60 min. rotate-github-app-tokens
# mints a fresh one every 30 min into Vault, but env vars are frozen at
# container start, so a long-lived pod like this one is authenticated for at
Expand Down
Loading