Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
60bcf61
chore: adopt branch-scoped session logs
KazanderDad May 25, 2026
68908b5
docs(backlog): mark todo superseded by GitHub issues
KazanderDad Jul 16, 2026
d453f70
chore(supabase): align local Colima startup defaults
KazanderDad Jul 16, 2026
859b7f1
chore(release): record sprint env vetting
KazanderDad Jul 16, 2026
8c7c938
chore(release): record vercel env blocker
KazanderDad Jul 16, 2026
abf8c97
chore(release): record remote supabase blocker
KazanderDad Jul 16, 2026
888ca50
chore(release): record restored supabase checks
KazanderDad Jul 19, 2026
41b65e7
chore(release): repair preview tcoin readiness checks
KazanderDad Jul 20, 2026
7f91978
fix: wire live pools into ops status API
KazanderDad Jul 20, 2026
878c6e8
Merge pull request #94 from GreenPill-TO/codex/tcoin-production-launc…
KazanderDad Jul 20, 2026
332446b
chore: configure tcoin worker scheduler target (#88)
KazanderDad Jul 20, 2026
304d773
fix: bound indexer queue drain worker (#89)
KazanderDad Jul 20, 2026
8cb1be6
docs: capture scheduler runbook evidence (#90)
KazanderDad Jul 20, 2026
e225080
docs: document worker scheduler env (#90)
KazanderDad Jul 20, 2026
578c5eb
Merge pull request #95 from GreenPill-TO/codex/tcoin-worker-scheduler…
KazanderDad Jul 20, 2026
da5fb27
docs: record preview buy tcoin blocker (#85)
KazanderDad Jul 20, 2026
4f92039
fix: pass verified auth token to user provisioning (#86)
KazanderDad Jul 20, 2026
c9d6530
chore: bump PR version
KazanderDad Jul 20, 2026
65d26af
docs: record preview otp edge blocker (#86)
KazanderDad Jul 20, 2026
99f74c6
Merge pull request #96 from GreenPill-TO/codex/tcoin-preview-smoke-buy
KazanderDad Jul 20, 2026
a3ec41f
[codex] Repair Preview user-settings and pay-link Edge deploy
KazanderDad Jul 20, 2026
5c9198a
ci(supabase): address post-merge review feedback
KazanderDad Jul 20, 2026
6f9d7ce
fix(edge): allow owned Vercel preview origins
KazanderDad Jul 21, 2026
90c5ffe
fix(edge): allow owned Vercel preview origins (#98)
KazanderDad Jul 21, 2026
bd049ee
fix(edge): allow TCOIN dev CORS origins
KazanderDad Jul 21, 2026
4d7220b
Merge pull request #99 from GreenPill-TO/codex/tcoin-dev-cors-origins
KazanderDad Jul 21, 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
12 changes: 12 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,18 @@ INDEXER_MAX_BLOCKS_PER_RUN=2000
# Example: 500
INDEXER_DISCOVERY_POOL_LIMIT=500

# Optional queue scope for scheduled indexer touch workers.
# Use `tcoin:42220` for the TorontoCoin launch worker so the scheduler does
# not claim unrelated queued scopes.
# Example: tcoin:42220
INDEXER_TOUCH_WORKER_SCOPE_KEY=tcoin:42220

# Maximum runtime for one scheduled indexer touch worker process in
# milliseconds. Timed-out workers mark the queue request and run-control health
# as failed before exiting non-zero.
# Example: 240000
INDEXER_TOUCH_WORKER_TIMEOUT_MS=240000

# Optional tracker bridge endpoint for tracker-pull ingestion.
# Leave empty unless that bridge is intentionally deployed.
# Example: http://localhost:8787/tracker/pull
Expand Down
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ ALTER TABLE users ADD COLUMN IF NOT EXISTS phone_verified boolean DEFAULT false;
ALTER TABLE users DROP COLUMN IF EXISTS phone_verified;
```

### Database Workflows
- `supabase-deploy-tcoin.yml` - Dry-run migrations on PRs to `dev`/`main`, and deploy migrations after merges to those branches.
### Database And Edge Function Workflows
- `supabase-deploy-tcoin.yml` - Dry-run migrations on PRs to `dev`/`main`, report the wallet-critical Edge Function deployment plan on PRs, and deploy migrations plus `user-settings`, `payment-links`, and `payment-requests` after merges to those branches.
- `db-pull-env.yml` - Manually generate drift migrations from the selected TCOIN Supabase environment.

TCOIN Supabase deploys use GitHub Environments `Preview – tcoin` and `Production – tcoin`, plus these secrets:
Expand Down
97 changes: 97 additions & 0 deletions .github/scripts/pr-version-bump.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { execFileSync } from "node:child_process";

const args = process.argv.slice(2);
const base = args.includes("--base") ? args[args.indexOf("--base") + 1] : process.env.GITHUB_BASE_REF;
const dryRun = args.includes("--dry-run");

if (!base || !["dev", "main"].includes(base)) {
console.error("Usage: node .github/scripts/pr-version-bump.mjs --base <dev|main> [--dry-run]");
process.exit(1);
}

function git(args, fallback = "") {
try {
return execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
} catch {
return fallback;
}
}

function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}

function writeJson(file, data) {
fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
}

function bump(version) {
const parts = String(version || "0.0.0").split(".").map((part) => Number.parseInt(part, 10));
const minor = Number.isFinite(parts[1]) ? parts[1] : 0;
const patch = Number.isFinite(parts[2]) ? parts[2] : 0;
if (base === "main") return `0.${minor + 1}.0`;
return `0.${minor}.${patch + 1}`;
}

function baseJson(file) {
const fromGit = git(["show", `origin/${base}:${file}`], "");
if (!fromGit) return null;
try {
return JSON.parse(fromGit);
} catch {
return null;
}
}

function packageCandidates() {
const changed = git(["diff", "--name-only", `origin/${base}...HEAD`], "")
.split("\n")
.filter(Boolean);
const candidates = new Set();
for (const file of changed) {
const appMatch = file.match(/^apps\/([^/]+)\//);
if (appMatch && fs.existsSync(path.join("apps", appMatch[1], "package.json"))) {
candidates.add(path.join("apps", appMatch[1], "package.json"));
}
if (file.startsWith("app/") && fs.existsSync(path.join("app", "package.json"))) {
candidates.add(path.join("app", "package.json"));
}
}
if (candidates.size === 0 && fs.existsSync("package.json")) candidates.add("package.json");
if (candidates.size === 0 && fs.existsSync(path.join("app", "package.json"))) candidates.add(path.join("app", "package.json"));
return [...candidates];
}

function updatePackage(file) {
const current = readJson(file);
const basePkg = baseJson(file) || current;
const nextVersion = bump(basePkg.version);
if (current.version === nextVersion) {
console.log(`${file} already at ${nextVersion}`);
return;
}
console.log(`${file}: ${current.version || "(none)"} -> ${nextVersion}`);
if (!dryRun) {
current.version = nextVersion;
writeJson(file, current);
}

const lockFile = path.join(path.dirname(file), "package-lock.json");
if (fs.existsSync(lockFile)) {
const lock = readJson(lockFile);
if (lock.name === current.name || path.dirname(file) !== ".") lock.version = nextVersion;
if (lock.packages && lock.packages[""]) lock.packages[""].version = nextVersion;
if (!dryRun) writeJson(lockFile, lock);
}
}

const candidates = packageCandidates();
if (candidates.length === 0) {
console.error("No package.json candidate found for version bump.");
process.exit(1);
}

for (const candidate of candidates) updatePackage(candidate);
52 changes: 52 additions & 0 deletions .github/workflows/pr-version-bump.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: PR Version Bump

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- dev
- main

permissions:
contents: write
pull-requests: read

jobs:
version-bump:
if: ${{ github.event.pull_request.draft == false }}
runs-on: ubuntu-latest
steps:
- name: Reject forked PR writeback
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
run: |
echo "PR version bump writes back to same-repo branches only. Please apply the expected package.json version bump manually for forked PRs."
exit 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow fork PRs to pass after manual bump

Forked PRs to dev/main hit this step and fail before checkout or version verification. The message asks contributors to apply the bump manually, but even a manually corrected fork PR can never get this check green, which blocks the PR if this workflow is required; skip writeback for forks or run a read-only verification instead of exiting failure.

Useful? React with 👍 / 👎.


- name: Check out PR branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Fetch base branch
run: git fetch origin ${{ github.base_ref }} --depth=1

- name: Apply expected version bump
run: node .github/scripts/pr-version-bump.mjs --base ${{ github.base_ref }}

- name: Commit version bump
run: |
if git diff --quiet; then
echo "Version already matches expected bump."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add package.json package-lock.json app/package.json app/package-lock.json apps/*/package.json apps/*/package-lock.json 2>/dev/null || true
if git diff --cached --quiet; then
echo "No package version files were staged."
exit 0
fi
git commit -m "chore: bump PR version"
git push
79 changes: 79 additions & 0 deletions .github/workflows/supabase-deploy-tcoin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ on:
types: [opened, synchronize, reopened]
paths:
- "supabase/migrations/**"
- "supabase/functions/**"
push:
branches: [dev, main]
paths:
- "supabase/migrations/**"
- "supabase/functions/**"
workflow_dispatch:
inputs:
target:
Expand All @@ -28,6 +30,11 @@ on:
options:
- dry-run
- deploy
deploy_edge_functions:
type: boolean
description: Deploy wallet-critical TCOIN Edge Functions after migration handling?
required: false
default: false

jobs:
preview:
Expand All @@ -46,12 +53,18 @@ jobs:
TARGET_LABEL: Preview – tcoin
SUPABASE_DB_URL: ${{ secrets.SUPABASE_SESSION_POOLER_TCOIN_PREVIEW || secrets.SUPABASE_SESSION_POOLER_DEV }}
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN_TCOIN_PREVIEW || secrets.SUPABASE_ACCESS_TOKEN }}
NEXT_PUBLIC_SUPABASE_URL: ${{ vars.NEXT_PUBLIC_SUPABASE_URL || secrets.NEXT_PUBLIC_SUPABASE_URL }}
RUN_MODE: ${{ github.event_name == 'pull_request' && 'dry-run' || github.event_name == 'push' && 'deploy' || github.event.inputs.mode }}
DEPLOY_EDGE_FUNCTIONS: ${{ github.event_name == 'pull_request' && 'false' || github.event_name == 'push' && 'true' || (github.event.inputs.mode == 'deploy' && github.event.inputs.deploy_edge_functions == 'true' && 'true' || 'false') }}
DB_URL_SECRET_NAME: SUPABASE_SESSION_POOLER_TCOIN_PREVIEW or SUPABASE_SESSION_POOLER_DEV
ACCESS_TOKEN_SECRET_NAME: SUPABASE_ACCESS_TOKEN_TCOIN_PREVIEW or SUPABASE_ACCESS_TOKEN
SUPABASE_URL_SECRET_NAME: NEXT_PUBLIC_SUPABASE_URL
TCOIN_EDGE_FUNCTIONS: user-settings payment-links payment-requests
steps:
- uses: actions/checkout@v4
- uses: supabase/setup-cli@v1
with:
version: 2.101.0

- name: Check required secrets
run: |
Expand All @@ -65,6 +78,10 @@ jobs:
echo "::error::Missing GitHub secret ${ACCESS_TOKEN_SECRET_NAME} for ${TARGET_LABEL}."
missing=1
fi
if [ "${DEPLOY_EDGE_FUNCTIONS:-false}" = "true" ] && [ -z "${NEXT_PUBLIC_SUPABASE_URL:-}" ]; then
echo "::error::Missing GitHub var or secret ${SUPABASE_URL_SECRET_NAME} for ${TARGET_LABEL} Edge Function deployment."
missing=1
fi
exit "$missing"

- name: Run Supabase migration command
Expand All @@ -86,6 +103,32 @@ jobs:
esac
supabase "${args[@]}"

- name: Deploy wallet-critical Edge Functions
if: env.DEPLOY_EDGE_FUNCTIONS == 'true'
run: |
set -euo pipefail
project_ref="$(node -e "const url = new URL(process.env.NEXT_PUBLIC_SUPABASE_URL); console.log(url.hostname.split('.')[0]);")"
if [ -z "$project_ref" ]; then
echo "::error::Unable to derive Supabase project ref from NEXT_PUBLIC_SUPABASE_URL."
exit 1
fi

echo "Deploying wallet-critical Edge Functions for ${TARGET_LABEL}: ${TCOIN_EDGE_FUNCTIONS}"
for function_name in ${TCOIN_EDGE_FUNCTIONS}; do
echo "Deploying ${function_name}."
supabase functions deploy "$function_name" \
--project-ref "$project_ref" \
--no-verify-jwt \
--use-api
done

- name: Report Edge Function deployment plan
if: env.DEPLOY_EDGE_FUNCTIONS != 'true'
run: |
set -euo pipefail
echo "PR validation does not deploy Edge Functions."
echo "On merge/push to dev or main, this workflow deploys: ${TCOIN_EDGE_FUNCTIONS}"

production:
name: Production TCOIN migrations
if: >-
Expand All @@ -102,12 +145,18 @@ jobs:
TARGET_LABEL: Production – tcoin
SUPABASE_DB_URL: ${{ secrets.SUPABASE_SESSION_POOLER_TCOIN_PRODUCTION || secrets.SUPABASE_SESSION_POOLER_PROD }}
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN_TCOIN_PRODUCTION || secrets.SUPABASE_ACCESS_TOKEN }}
NEXT_PUBLIC_SUPABASE_URL: ${{ vars.NEXT_PUBLIC_SUPABASE_URL || secrets.NEXT_PUBLIC_SUPABASE_URL }}
RUN_MODE: ${{ github.event_name == 'pull_request' && 'dry-run' || github.event_name == 'push' && 'deploy' || github.event.inputs.mode }}
DEPLOY_EDGE_FUNCTIONS: ${{ github.event_name == 'pull_request' && 'false' || github.event_name == 'push' && 'true' || (github.event.inputs.mode == 'deploy' && github.event.inputs.deploy_edge_functions == 'true' && 'true' || 'false') }}
DB_URL_SECRET_NAME: SUPABASE_SESSION_POOLER_TCOIN_PRODUCTION or SUPABASE_SESSION_POOLER_PROD
ACCESS_TOKEN_SECRET_NAME: SUPABASE_ACCESS_TOKEN_TCOIN_PRODUCTION or SUPABASE_ACCESS_TOKEN
SUPABASE_URL_SECRET_NAME: NEXT_PUBLIC_SUPABASE_URL
TCOIN_EDGE_FUNCTIONS: user-settings payment-links payment-requests
steps:
- uses: actions/checkout@v4
- uses: supabase/setup-cli@v1
with:
version: 2.101.0

- name: Check required secrets
run: |
Expand All @@ -121,6 +170,10 @@ jobs:
echo "::error::Missing GitHub secret ${ACCESS_TOKEN_SECRET_NAME} for ${TARGET_LABEL}."
missing=1
fi
if [ "${DEPLOY_EDGE_FUNCTIONS:-false}" = "true" ] && [ -z "${NEXT_PUBLIC_SUPABASE_URL:-}" ]; then
echo "::error::Missing GitHub var or secret ${SUPABASE_URL_SECRET_NAME} for ${TARGET_LABEL} Edge Function deployment."
missing=1
fi
exit "$missing"

- name: Run Supabase migration command
Expand All @@ -141,3 +194,29 @@ jobs:
;;
esac
supabase "${args[@]}"

- name: Deploy wallet-critical Edge Functions
if: env.DEPLOY_EDGE_FUNCTIONS == 'true'
run: |
set -euo pipefail
project_ref="$(node -e "const url = new URL(process.env.NEXT_PUBLIC_SUPABASE_URL); console.log(url.hostname.split('.')[0]);")"
if [ -z "$project_ref" ]; then
echo "::error::Unable to derive Supabase project ref from NEXT_PUBLIC_SUPABASE_URL."
exit 1
fi

echo "Deploying wallet-critical Edge Functions for ${TARGET_LABEL}: ${TCOIN_EDGE_FUNCTIONS}"
for function_name in ${TCOIN_EDGE_FUNCTIONS}; do
echo "Deploying ${function_name}."
supabase functions deploy "$function_name" \
--project-ref "$project_ref" \
--no-verify-jwt \
--use-api
done

- name: Report Edge Function deployment plan
if: env.DEPLOY_EDGE_FUNCTIONS != 'true'
run: |
set -euo pipefail
echo "PR validation does not deploy Edge Functions."
echo "On merge/push to dev or main, this workflow deploys: ${TCOIN_EDGE_FUNCTIONS}"
14 changes: 10 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Source files are organized under the `app/` directory. Agent workflow artefacts
```
.github/workflows/ # PR template + CI workflows for build, lint, supabase, secret scanning
agent-context/ # logs and recurring agent workflow context
├─ session-log.md # Mandatory - append-only per session, new entries at top of file, v ↑ per edit
├─ db-workflow.md # CI/CD overview for database changes
├─ app-context.md # Problem, approach, value prop
├─ style-guide.md # Style for the wallet app
Expand All @@ -48,7 +47,6 @@ services/ # Node service modules and worker entrypoints for index
styles/ # global styles
supabase/
├─ sql-schema.sql # latest sql schema (github action runs db pull on PR)
├─ migrations/ # Agent-generated incremental SQL patches (file name starts with session version referencing session-log.md)
└─ functions/ # Supabase Edge Functions and shared Deno-safe helpers
├─ _shared/ # Shared auth, app scoping, RBAC, validation and domain helpers
└─ */ # Domain edge functions (for example user-settings, bia-service, store-operations)
Expand Down Expand Up @@ -92,7 +90,6 @@ AGENTS.md # this file
* **Pull Requests**
* Keep pull request descriptions concise and reviewer-focused.
* If any related issues are known, mention them in PR (e.g. "Issues: #10, #11")
* **Session version tags**
* Follow SemVer (v2.0.0-alpha)
* Major IDs ("v1.0", "v2.0") mirror new feature branches
* Minor IDs ("v2.0", "v2.1") mirror different coding sesssions on the same feature branch
Expand All @@ -118,9 +115,18 @@ Agents **must** read and follow `workflow.md` each time.

* SQL migrations must be **idempotent & reversible** where practical (include rollback notes or a `-- DOWN` section; each DROP/ALTER should be preceded by IF EXISTS/IF NOT EXISTS where supported).
* Pre-commit hook **warns** (not blocks) if ESLint and similar (e.g. in Foundry) cannot start.
* Session log must bump version +1 for each session
* All new external calls must use SafeERC20 / Address.functionCall and be covered in tests.
* Agents must **never** directly modify the linked Supabase database. Only a human may run commands that target the linked database and change its state.
* Agents must always stop and ask for explicit human permission before proposing or running any Supabase command that could change the linked database, including `supabase db push`, `supabase migration up`, `supabase db reset --linked`, `supabase link`, or any `supabase --linked` write operation.
* This repo qualifies for the stricter multi-frontend Supabase boundary rule: app-facing wallet/sparechange/contracts flows should use typed Edge Functions, narrow RPCs, read-model helpers, or documented server boundaries rather than broad direct browser/client table access. Existing direct-access compatibility paths should be treated as cleanup candidates unless `docs/engineering/supabase-boundary-contract.md` and `scripts/check-no-direct-supabase-db.mjs` explicitly allow them.
* Deployed Next.js/Vercel runtime should not require `SUPABASE_SERVICE_ROLE_KEY`. That key belongs only in privileged Supabase Edge Functions and the external indexer/onramp worker/runtime paths that intentionally need service-role access.

## Branch-Scoped Session Logs And Todos

- Use `agent-context/session-log/` for active session logs. Do not add new entries to legacy `agent-context/session-log.md` files.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Update the mandatory checklist with the new log path

This new branch-log rule conflicts with the still-mandatory agent-context/workflow.md checklist, which continues to require updating session-log.md and recording balances in session-log.md. Since agents are told to follow both files each session and this commit moved the legacy file into archive/, future sessions can recreate the deprecated root log instead of using the branch-scoped file.

Useful? React with 👍 / 👎.

- For feature branches, update the branch log immediately before each commit. Use `YYYY-MM-DD-featurebranch.md` in single-app repos and `YYYY-MM-DD-app-featurebranch.md` in monorepos.
- Direct work on `main` may use `agent-context/session-log/main.md`; direct work on `dev` may use `agent-context/session-log/dev.md`.
- Each entry must include UTC timestamp, agent, branch, head, summary, validation, and follow-ups.
- Keep `agent-context/todo.md` focused on roadmap items and active follow-ups. Completed work belongs in the current branch log.
- See `agent-context/session-log/README.md` for naming and archival rules.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ For local Supabase work under Colima, start the trimmed local stack with:
```bash
pnpm supabase:start:local
```
That helper switches to the `colima-varrun` Docker context when available and reapplies the GoTrue mailer-host patch needed to suppress local `GOTRUE_MAILER_EXTERNAL_HOSTS` warnings for browser and gateway traffic (`localhost`, `127.0.0.1`, and the local `kong` gateway host).
That helper uses the shared `colima-agents` Docker context and reapplies the GoTrue mailer-host patch needed to suppress local `GOTRUE_MAILER_EXTERNAL_HOSTS` warnings for browser and gateway traffic (`localhost`, `127.0.0.1`, and the local `kong` gateway host).

Open `http://localhost:3000` in your browser. Next.js will serve the app configured by `NEXT_PUBLIC_CITYCOIN` and `NEXT_PUBLIC_APP_NAME`.

Expand Down Expand Up @@ -113,7 +113,7 @@ Do not put real secrets in checked-in files. Keep `SUPABASE_SERVICE_ROLE_KEY` ou

## CI And Database Delivery

Pull requests run frontend CI, secret scanning, and Supabase migration validation when relevant. The dedicated TCOIN Supabase workflow dry-runs migrations for PRs into `dev` against `Preview – tcoin` and PRs into `main` against `Production – tcoin`; pushes to those branches deploy migrations to the matching database after the GitHub Environment gate.
Pull requests run frontend CI, secret scanning, and Supabase validation when relevant. The dedicated TCOIN Supabase workflow dry-runs migrations for PRs into `dev` against `Preview – tcoin` and PRs into `main` against `Production – tcoin`; it also reports the wallet-critical Edge Function deployment plan. Pushes to those branches deploy migrations and the `user-settings`, `payment-links`, and `payment-requests` Edge Functions to the matching Supabase project after the GitHub Environment gate.

Agents must never directly mutate a linked Supabase database. Remote schema changes should flow through reviewed migrations and the guarded GitHub workflow, or through an explicit human/operator action described in the relevant runbook.

Expand Down
Loading
Loading