gitea: migrate sources to titan/atlas-iac
This commit is contained in:
parent
3daa3019f9
commit
8d1302765f
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -480,7 +480,7 @@ PY
|
||||
set +x
|
||||
git config user.email "jenkins@bstein.dev"
|
||||
git config user.name "jenkins"
|
||||
git remote set-url origin https://${GIT_USER}:${GIT_TOKEN}@scm.bstein.dev/atlas/titan-iac.git
|
||||
git remote set-url origin https://${GIT_USER}:${GIT_TOKEN}@scm.bstein.dev/titan/atlas-iac.git
|
||||
git push origin HEAD:${FLUX_BRANCH}
|
||||
'''
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# titan-iac
|
||||
# atlas-iac
|
||||
|
||||
Flux-managed Kubernetes desired-state config for `bstein.dev`.
|
||||
|
||||
Canonical source URL:
|
||||
- `ssh://git@scm.bstein.dev:2242/atlas/titan-iac.git`
|
||||
- `ssh://git@scm.bstein.dev:2242/titan/atlas-iac.git`
|
||||
|
||||
## Scope
|
||||
|
||||
|
||||
@ -114,7 +114,7 @@ spec:
|
||||
string(
|
||||
name: 'EXPECTED_SOURCE_REVISION',
|
||||
defaultValue: '',
|
||||
description: 'Full reviewed commit already contained by atlas/titan-iac main.'
|
||||
description: 'Full reviewed commit already contained by titan/atlas-iac main.'
|
||||
)
|
||||
string(
|
||||
name: 'CONFIRM_PUBLISH',
|
||||
|
||||
@ -111,7 +111,7 @@ spec:
|
||||
string(
|
||||
name: 'EXPECTED_SOURCE_REVISION',
|
||||
defaultValue: '',
|
||||
description: 'Full reviewed commit that must be contained by atlas/titan-iac main.'
|
||||
description: 'Full reviewed commit that must be contained by titan/atlas-iac main.'
|
||||
)
|
||||
string(
|
||||
name: 'CONFIRM_PUBLISH',
|
||||
@ -246,7 +246,7 @@ spec:
|
||||
--digest-file="${WORKSPACE}/build/hermes-chat-router.digest" \
|
||||
--image-name-tag-with-digest-file="${WORKSPACE}/build/hermes-chat-router.image" \
|
||||
--label="org.opencontainers.image.revision=${source_revision}" \
|
||||
--label="org.opencontainers.image.source=https://scm.bstein.dev/atlas/titan-iac" \
|
||||
--label="org.opencontainers.image.source=https://scm.bstein.dev/titan/atlas-iac" \
|
||||
--label="org.opencontainers.image.title=hermes-chat-router" \
|
||||
--cleanup \
|
||||
--push-retry=3
|
||||
|
||||
@ -195,7 +195,7 @@ spec:
|
||||
--digest-file="${WORKSPACE}/build/hermes-voice.digest" \
|
||||
--image-name-tag-with-digest-file="${WORKSPACE}/build/hermes-voice.image" \
|
||||
--label="org.opencontainers.image.revision=${source_revision}" \
|
||||
--label="org.opencontainers.image.source=https://scm.bstein.dev/atlas/titan-iac" \
|
||||
--label="org.opencontainers.image.source=https://scm.bstein.dev/titan/atlas-iac" \
|
||||
--label="org.opencontainers.image.title=hermes-jetson-${component}" \
|
||||
--cleanup --push-retry=3
|
||||
/busybox/chmod 644 build/hermes-voice.digest build/hermes-voice.image
|
||||
|
||||
@ -106,7 +106,7 @@ spec:
|
||||
string(
|
||||
name: 'EXPECTED_SOURCE_REVISION',
|
||||
defaultValue: '',
|
||||
description: 'Full reviewed commit that must be contained by atlas/titan-iac main.'
|
||||
description: 'Full reviewed commit that must be contained by titan/atlas-iac main.'
|
||||
)
|
||||
string(
|
||||
name: 'CONFIRM_PUBLISH',
|
||||
@ -271,7 +271,7 @@ spec:
|
||||
--digest-file="${WORKSPACE}/build/hermes-webui-arm64.digest" \
|
||||
--image-name-tag-with-digest-file="${WORKSPACE}/build/hermes-webui-arm64.image" \
|
||||
--label="org.opencontainers.image.revision=${source_revision}" \
|
||||
--label="org.opencontainers.image.source=https://scm.bstein.dev/atlas/titan-iac" \
|
||||
--label="org.opencontainers.image.source=https://scm.bstein.dev/titan/atlas-iac" \
|
||||
--label="org.opencontainers.image.title=hermes-webui" \
|
||||
--cleanup \
|
||||
--push-retry=3
|
||||
@ -425,7 +425,7 @@ spec:
|
||||
--digest-file="${WORKSPACE}/build/hermes-webui-amd64.digest" \
|
||||
--image-name-tag-with-digest-file="${WORKSPACE}/build/hermes-webui-amd64.image" \
|
||||
--label="org.opencontainers.image.revision=${source_revision}" \
|
||||
--label="org.opencontainers.image.source=https://scm.bstein.dev/atlas/titan-iac" \
|
||||
--label="org.opencontainers.image.source=https://scm.bstein.dev/titan/atlas-iac" \
|
||||
--label="org.opencontainers.image.title=hermes-webui" \
|
||||
--cleanup \
|
||||
--push-retry=3
|
||||
|
||||
@ -479,7 +479,7 @@ PY
|
||||
set +x
|
||||
git config user.email "jenkins@bstein.dev"
|
||||
git config user.name "jenkins"
|
||||
git remote set-url origin https://${GIT_USER}:${GIT_TOKEN}@scm.bstein.dev/atlas/titan-iac.git
|
||||
git remote set-url origin https://${GIT_USER}:${GIT_TOKEN}@scm.bstein.dev/titan/atlas-iac.git
|
||||
git push origin HEAD:${FLUX_BRANCH}
|
||||
'''
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ spec:
|
||||
branch: main
|
||||
secretRef:
|
||||
name: flux-system-gitea
|
||||
url: ssh://git@scm.bstein.dev:2242/atlas/titan-iac.git
|
||||
url: ssh://git@scm.bstein.dev:2242/titan/atlas-iac.git
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
|
||||
@ -82,9 +82,9 @@ Default mode permits a narrow read grammar:
|
||||
`services/hermes/scm-common/scripts/gitea_api.py`). Its only admitted
|
||||
grammar is `read <api-path>`; bare HTTP methods (`GET`, `POST`, ...) are not
|
||||
part of the deployed client's grammar and are refused before spawn. Paths
|
||||
are pinned to `atlas/titan-iac` on an exact segment boundary (plus the bare
|
||||
are pinned to `titan/atlas-iac` on an exact segment boundary (plus the bare
|
||||
`/api/v1/user` identity read), so a look-alike repository such as
|
||||
`atlas/titan-iac-evil` is refused, and any relative segment — including a
|
||||
`titan/atlas-iac-evil` is refused, and any relative segment — including a
|
||||
percent-encoded `%2e%2e` — is rejected before the request is built. The
|
||||
broker holds the only forge token; the harness, the pod, and the operator
|
||||
environment never read `/vault/secrets/gitea-token` and no Git askpass
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# hermes-next: Claude backend handoff ledger
|
||||
|
||||
Branch `feature/hermes-next-hux` (origin, pushed after every card); draft PR #55 https://scm.bstein.dev/atlas/titan-iac/pulls/55 (opened through the SCM broker, never merged by Claude). Board
|
||||
Branch `feature/hermes-next-hux` (origin, pushed after every card); draft PR #55 https://scm.bstein.dev/titan/atlas-iac/pulls/55 (opened through the SCM broker, never merged by Claude). Board
|
||||
`hermes-next` on the operator Hermes Kanban; cards HUX-01..HUX-12 are parked
|
||||
(`blocked`) on purpose so the CLI lane runner never auto-claims them — the
|
||||
Claude coordinator and the Codex root session are the only implementers.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1055,7 +1055,7 @@ Code proposal: red build 22:34:59 -> pull request open 22:35:32 (33s).
|
||||
A one-line defect was seeded in `ariadne/utils/errors.py` (dropping the
|
||||
`errorMessage` key from the HTTP payload lookup) and pushed to master. Build
|
||||
409 went red, and Ariadne opened
|
||||
[atlas/ariadne PR #3](https://scm.bstein.dev/atlas/ariadne/pulls/3) from
|
||||
[atlas/ariadne PR #3](https://scm.bstein.dev/titan/ariadne/pulls/3) from
|
||||
branch `hermes-repair/409` with the correct one-line fix:
|
||||
|
||||
```diff
|
||||
|
||||
@ -72,8 +72,8 @@ Manual remote console examples
|
||||
Useful options
|
||||
- `--shutdown-mode host-poweroff|cluster-only`
|
||||
- `--expected-flux-branch main`
|
||||
- `--expected-flux-url ssh://git@scm.bstein.dev:2242/atlas/titan-iac.git`
|
||||
- `--force-flux-url ssh://git@scm.bstein.dev:2242/atlas/titan-iac.git`
|
||||
- `--expected-flux-url ssh://git@scm.bstein.dev:2242/titan/atlas-iac.git`
|
||||
- `--force-flux-url ssh://git@scm.bstein.dev:2242/titan/atlas-iac.git`
|
||||
- `--force-flux-branch main`
|
||||
- `--allow-flux-source-mutation` (required with `--force-flux-url`; breakglass only)
|
||||
- `--skip-local-bootstrap` (not recommended for cold-start recovery)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
CANONICAL_CONTROL_HOST="titan-db"
|
||||
DEFAULT_FLUX_BRANCH="main"
|
||||
EXPECTED_FLUX_URL="ssh://git@scm.bstein.dev:2242/atlas/titan-iac.git"
|
||||
EXPECTED_FLUX_URL="ssh://git@scm.bstein.dev:2242/titan/atlas-iac.git"
|
||||
SHUTDOWN_MODE="host-poweroff"
|
||||
STATE_SUBDIR=".local/share/ananke"
|
||||
HARBOR_BUNDLE_BASENAME="harbor-bootstrap-v2.14.1-arm64.tar.zst"
|
||||
|
||||
@ -107,7 +107,7 @@ esac
|
||||
EXECUTE=0
|
||||
SHUTDOWN_MODE="${SHUTDOWN_MODE:-host-poweroff}"
|
||||
EXPECTED_FLUX_BRANCH="${DEFAULT_FLUX_BRANCH:-main}"
|
||||
EXPECTED_FLUX_URL="${EXPECTED_FLUX_URL:-ssh://git@scm.bstein.dev:2242/atlas/titan-iac.git}"
|
||||
EXPECTED_FLUX_URL="${EXPECTED_FLUX_URL:-ssh://git@scm.bstein.dev:2242/titan/atlas-iac.git}"
|
||||
ALLOW_FLUX_SOURCE_MUTATION=0
|
||||
FORCE_FLUX_URL=""
|
||||
FORCE_FLUX_BRANCH=""
|
||||
|
||||
@ -27,7 +27,7 @@ done
|
||||
|
||||
JENKINS_URL="${JENKINS_URL:-https://ci.bstein.dev}"
|
||||
GITEA_URL="${GITEA_URL:-https://scm.bstein.dev}"
|
||||
CODE_REPO_OWNER="${CODE_REPO_OWNER:-atlas}"
|
||||
CODE_REPO_OWNER="${CODE_REPO_OWNER:-titan}"
|
||||
FIXTURE_JOB="hermes-triage-demo"
|
||||
CODE_JOB="hermes-code-demo"
|
||||
DEMO_NS="hermes-triage-demo"
|
||||
|
||||
@ -27,7 +27,7 @@ REF_RE = re.compile(rf"{REF_PREFIX}/[a-z0-9][a-z0-9-]{{7,63}}\Z")
|
||||
TOKEN_RE = re.compile(r"[a-z0-9][a-z0-9-]{7,63}\Z")
|
||||
SHA_RE = re.compile(r"[0-9a-f]{40}\Z")
|
||||
EXPECTED_HEAD_REF = "feature/hermes-full-handoff-acceptance"
|
||||
EXPECTED_REMOTE_URL = "https://scm.bstein.dev/atlas/titan-iac.git"
|
||||
EXPECTED_REMOTE_URL = "https://scm.bstein.dev/titan/atlas-iac.git"
|
||||
PROTECTED_NAMES = frozenset(
|
||||
{
|
||||
"default",
|
||||
@ -121,7 +121,7 @@ def _attest_worktree(root: Path, expected_head: str, expected_base_sha: str) ->
|
||||
except (configparser.Error, KeyError) as exc:
|
||||
raise ArmingError("local Git remote metadata is malformed") from exc
|
||||
if remote_url != EXPECTED_REMOTE_URL:
|
||||
raise ArmingError("origin is not the fixed Atlas titan-iac HTTPS repository")
|
||||
raise ArmingError("origin is not the fixed Titan atlas-iac HTTPS repository")
|
||||
|
||||
|
||||
def preflight(
|
||||
@ -132,7 +132,7 @@ def preflight(
|
||||
raise ArmingError("confirmation phrase does not match")
|
||||
if expected_repo != EXPECTED_REPO or request.repo != EXPECTED_REPO:
|
||||
raise ArmingError(
|
||||
"armed repository is not the fixed Atlas titan-iac repository"
|
||||
"armed repository is not the fixed Titan atlas-iac repository"
|
||||
)
|
||||
if (
|
||||
request.remote != EXPECTED_REMOTE
|
||||
|
||||
@ -122,7 +122,7 @@ class Targets:
|
||||
node_account: str = "hermes-agent"
|
||||
node_account_identity: str = "1200:1200:/home/hermes-agent:/bin/bash"
|
||||
node_count: int = 3
|
||||
repo: str = "atlas/titan-iac"
|
||||
repo: str = "titan/atlas-iac"
|
||||
remote: str = "origin"
|
||||
baseline_commit: str = "ab346f55509d584e457fe26cf90be3078f7a375c"
|
||||
dependency_pull_requests: tuple[int, ...] = (12, 14, 15, 16, 17, 18, 20)
|
||||
|
||||
@ -22,7 +22,7 @@ ARMED = "ephemeral-armed"
|
||||
MODES = (READ_ONLY, ARMED)
|
||||
|
||||
GITEA_CLIENT = "/opt/scm/gitea_api.py"
|
||||
EXPECTED_REPO = "atlas/titan-iac"
|
||||
EXPECTED_REPO = "titan/atlas-iac"
|
||||
EXPECTED_REMOTE = "origin"
|
||||
EXPECTED_BASE = "main"
|
||||
|
||||
@ -434,7 +434,7 @@ def _check_gitea(argv: tuple[str, ...], mode: str) -> None:
|
||||
and path != repo_root
|
||||
and not path.startswith((f"{repo_root}/", f"{repo_root}?"))
|
||||
):
|
||||
raise PolicyError("gitea_api.py is pinned to the Atlas titan-iac repository")
|
||||
raise PolicyError("gitea_api.py is pinned to the Titan atlas-iac repository")
|
||||
if operation == "read":
|
||||
if len(argv) != 3:
|
||||
raise PolicyError("gitea_api.py read takes exactly one API path")
|
||||
|
||||
@ -127,7 +127,7 @@ def validate_targets(targets: Targets) -> list[str]:
|
||||
)
|
||||
if targets.repo != EXPECTED_REPO or targets.remote != EXPECTED_REMOTE:
|
||||
problems.append(
|
||||
"repository and remote must be the fixed Atlas titan-iac origin"
|
||||
"repository and remote must be the fixed Titan atlas-iac origin"
|
||||
)
|
||||
if (
|
||||
targets.reviewed_pr_number != 19
|
||||
|
||||
@ -38,7 +38,7 @@ NS_DEMO = "hermes-triage-demo"
|
||||
NS_ARIADNE = "maintenance"
|
||||
JENKINS = os.environ.get("JENKINS_URL", "https://ci.bstein.dev")
|
||||
GITEA = os.environ.get("GITEA_URL", "https://scm.bstein.dev")
|
||||
CODE_REPO_OWNER = os.environ.get("CODE_REPO_OWNER", "atlas")
|
||||
CODE_REPO_OWNER = os.environ.get("CODE_REPO_OWNER", "titan")
|
||||
GRAFANA = os.environ.get("GRAFANA_URL", "https://metrics.bstein.dev")
|
||||
HERMES_UI = os.environ.get("HERMES_URL", "https://worker.bstein.dev")
|
||||
POLL_SECONDS = 6
|
||||
@ -409,7 +409,7 @@ def evidence_for(key: str, incident: str = "") -> None:
|
||||
f" whole Ariadne response box, not out of one branch. Every path ends"
|
||||
f" here: an executed action, an escalation, or a pull request all record"
|
||||
f" the same audit events and metrics.{RESET}")
|
||||
print(f" {YELLOW}issues filed by triage: {GITEA}/atlas/ariadne/issues{RESET}\n")
|
||||
print(f" {YELLOW}issues filed by triage: {GITEA}/titan/ariadne/issues{RESET}\n")
|
||||
elif key == "verify":
|
||||
if IS_CODE_JOB:
|
||||
print(f" {DIM}the branch build validates the proposal. Nothing merges"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1055,7 +1055,7 @@ Code proposal: red build 22:34:59 -> pull request open 22:35:32 (33s).
|
||||
A one-line defect was seeded in `ariadne/utils/errors.py` (dropping the
|
||||
`errorMessage` key from the HTTP payload lookup) and pushed to master. Build
|
||||
409 went red, and Ariadne opened
|
||||
[atlas/ariadne PR #3](https://scm.bstein.dev/atlas/ariadne/pulls/3) from
|
||||
[atlas/ariadne PR #3](https://scm.bstein.dev/titan/ariadne/pulls/3) from
|
||||
branch `hermes-repair/409` with the correct one-line fix:
|
||||
|
||||
```diff
|
||||
|
||||
@ -72,8 +72,8 @@ Manual remote console examples
|
||||
Useful options
|
||||
- `--shutdown-mode host-poweroff|cluster-only`
|
||||
- `--expected-flux-branch main`
|
||||
- `--expected-flux-url ssh://git@scm.bstein.dev:2242/atlas/titan-iac.git`
|
||||
- `--force-flux-url ssh://git@scm.bstein.dev:2242/atlas/titan-iac.git`
|
||||
- `--expected-flux-url ssh://git@scm.bstein.dev:2242/titan/atlas-iac.git`
|
||||
- `--force-flux-url ssh://git@scm.bstein.dev:2242/titan/atlas-iac.git`
|
||||
- `--force-flux-branch main`
|
||||
- `--allow-flux-source-mutation` (required with `--force-flux-url`; breakglass only)
|
||||
- `--skip-local-bootstrap` (not recommended for cold-start recovery)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: gitea-atlas-identity-bootstrap-5
|
||||
name: gitea-atlas-identity-bootstrap-6
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
@ -71,7 +71,7 @@ spec:
|
||||
- name: GITEA_SOURCE_OWNER
|
||||
value: bstein
|
||||
- name: GITEA_MANAGED_REPOSITORIES
|
||||
value: hermes-code-demo cassandra soteria pegasus metis ananke ariadne typhon titan-iac
|
||||
value: hermes-code-demo cassandra soteria pegasus metis ananke ariadne typhon atlas-iac
|
||||
- name: VAULT_ADDR
|
||||
value: http://vault.vault.svc.cluster.local:8200
|
||||
- name: VAULT_ROLE
|
||||
|
||||
@ -13,10 +13,10 @@ reconciler_user=atlas-reconciler
|
||||
reconciler_email=atlas-reconciler@bstein.dev
|
||||
hermes_user=hermes-automation
|
||||
hermes_email=hermes-automation@bstein.dev
|
||||
organization=atlas
|
||||
organization=titan
|
||||
owner_user=bstein
|
||||
source_owner="${GITEA_SOURCE_OWNER:-bstein}"
|
||||
managed_repositories="${GITEA_MANAGED_REPOSITORIES:-hermes-code-demo cassandra soteria pegasus metis ananke ariadne typhon titan-iac}"
|
||||
managed_repositories="${GITEA_MANAGED_REPOSITORIES:-hermes-code-demo cassandra soteria pegasus metis ananke ariadne typhon atlas-iac}"
|
||||
contributor_team=Contributors
|
||||
contributor_units='["repo.actions","repo.packages","repo.code","repo.issues","repo.wiki","repo.pulls","repo.releases","repo.projects"]'
|
||||
protected_reviewer=bstein
|
||||
@ -330,7 +330,7 @@ fi
|
||||
[ -n "${hermes_token}" ] || die "Hermes token generation failed"
|
||||
|
||||
response_file=$(mktemp)
|
||||
status=$(api_request POST /orgs "{\"username\":\"${organization}\",\"full_name\":\"Atlas\",\"description\":\"Atlas platform services\",\"visibility\":\"private\",\"repo_admin_change_team_access\":true}" "${response_file}")
|
||||
status=$(api_request POST /orgs "{\"username\":\"${organization}\",\"full_name\":\"Titan\",\"description\":\"Titan repositories for the Atlas platform\",\"visibility\":\"public\",\"repo_admin_change_team_access\":true}" "${response_file}")
|
||||
rm -f "${response_file}"
|
||||
case "${status}" in
|
||||
201|422) ;;
|
||||
@ -341,10 +341,10 @@ owners_id=$(find_team_id Owners)
|
||||
response_file=$(mktemp)
|
||||
status=$(api_request PUT "/teams/${owners_id}/members/${owner_user}" '' "${response_file}")
|
||||
rm -f "${response_file}"
|
||||
expect_status "${status}" 204 "add ${owner_user} to Atlas owners"
|
||||
expect_status "${status}" 204 "add ${owner_user} to Titan owners"
|
||||
|
||||
response_file=$(mktemp)
|
||||
status=$(api_request POST "/orgs/${organization}/teams" "{\"name\":\"${contributor_team}\",\"description\":\"Atlas automation contributors\",\"permission\":\"write\",\"includes_all_repositories\":true,\"can_create_org_repo\":false,\"units\":${contributor_units}}" "${response_file}")
|
||||
status=$(api_request POST "/orgs/${organization}/teams" "{\"name\":\"${contributor_team}\",\"description\":\"Titan automation contributors\",\"permission\":\"write\",\"includes_all_repositories\":true,\"can_create_org_repo\":false,\"units\":${contributor_units}}" "${response_file}")
|
||||
rm -f "${response_file}"
|
||||
case "${status}" in
|
||||
201|422) ;;
|
||||
@ -355,7 +355,7 @@ contributors_id=$(find_team_id "${contributor_team}")
|
||||
response_file=$(mktemp)
|
||||
status=$(api_request PUT "/teams/${contributors_id}/members/${hermes_user}" '' "${response_file}")
|
||||
rm -f "${response_file}"
|
||||
expect_status "${status}" 204 "add Hermes to Atlas contributors"
|
||||
expect_status "${status}" 204 "add Hermes to Titan contributors"
|
||||
|
||||
transfer_managed_repositories
|
||||
|
||||
@ -366,4 +366,4 @@ done
|
||||
vault_write gitea/atlas-reconciler "{\"data\":{\"username\":\"${reconciler_user}\",\"token\":\"${reconciler_token}\",\"base_url\":\"${public_url}\"}}"
|
||||
vault_write hermes/developer-gitea "{\"data\":{\"username\":\"${hermes_user}\",\"email\":\"${hermes_email}\",\"token\":\"${hermes_token}\",\"base_url\":\"${public_url}\"}}"
|
||||
|
||||
echo "Atlas organization, managed repositories, and Hermes contributor identity are ready"
|
||||
echo "Titan organization, managed repositories, and Hermes contributor identity are ready"
|
||||
|
||||
@ -151,7 +151,7 @@ It runs daemonless Kaniko v1.23.2 under the unbound `hermes-image-builder`
|
||||
ServiceAccount without a service-account token, host socket,
|
||||
privileged container, or writable Git credential. It accepts only an exact
|
||||
40-character revision that is both the checked-out commit and current
|
||||
`atlas/titan-iac` `main`, so a human must merge the source PR first.
|
||||
`titan/atlas-iac` `main`, so a human must merge the source PR first.
|
||||
|
||||
The build pod requires an ARM64 worker and gives `hardware=rpi5` the maximum
|
||||
scheduler preference. Healthy rpi4 workers remain valid fallback capacity when
|
||||
|
||||
@ -326,7 +326,7 @@ data:
|
||||
Atlas, including resource status, events, logs, and Flux/Helm evidence. It
|
||||
cannot read Secrets, exec or attach to pods, create service-account tokens,
|
||||
mutate workloads or RBAC, or reconcile Flux. Put every durable cluster
|
||||
change on a reviewed titan-iac branch. Never expose credentials in chat or
|
||||
change on a reviewed atlas-iac branch. Never expose credentials in chat or
|
||||
logs. Triage belongs at triage.bstein.dev.
|
||||
|
||||
## Atlas engineering access
|
||||
@ -334,7 +334,7 @@ data:
|
||||
The Atlas organization has private visibility. Repository visibility is
|
||||
preserved per project and may be public or private; do not infer a
|
||||
repository's visibility from the organization setting. Repositories are
|
||||
canonical at `https://scm.bstein.dev/atlas/<repo>.git`; worker Git traffic
|
||||
canonical at `https://scm.bstein.dev/titan/<repo>.git`; worker Git traffic
|
||||
uses `http://hermes-scm-broker.hermes-scm.svc.cluster.local:9081/git/atlas/<repo>.git`.
|
||||
Verify the broker remote
|
||||
and cleanly separate pre-existing changes, create a task branch, run the
|
||||
@ -396,12 +396,12 @@ data:
|
||||
|
||||
The terminal PATH contains the pinned operator tools. Start cluster work
|
||||
with `kubectl config current-context`, read-only status/events/logs, and the
|
||||
relevant `titan-iac` manifests. The broker namespace is deliberately
|
||||
relevant `atlas-iac` manifests. The broker namespace is deliberately
|
||||
excluded from agent RBAC. Kubernetes cannot express a deny on one namespace
|
||||
inside an all-namespace list, so enumerate namespaces and run namespaced
|
||||
diagnostics instead of relying on `kubectl ... --all-namespaces`. Put
|
||||
durable desired-state changes on a
|
||||
reviewable `titan-iac` branch and validate Kustomize and client dry-run.
|
||||
reviewable `atlas-iac` branch and validate Kustomize and client dry-run.
|
||||
Brad owns merge and Flux reconciliation. Direct `kubectl` mutation is not
|
||||
available to the agent; hand an explicit incident mutation back to Brad.
|
||||
|
||||
|
||||
@ -215,7 +215,7 @@ data:
|
||||
|
||||
Hermes may inspect workload and delivery metadata, logs, metrics, and
|
||||
events. Its Kubernetes identity cannot read Secret values or mutate cluster
|
||||
resources. Apply fixes through the titan-iac GitOps workflow after review.
|
||||
resources. Apply fixes through the atlas-iac GitOps workflow after review.
|
||||
HERMES-CAPABILITIES.md: |
|
||||
# What Hermes can do on Atlas
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
{"schema":"hux.release.v1","id":"rel_0001aaaa","workload":"hermes-chat-router","feature_flags":["hux.foundation"],"commit":"d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06","state":"built","evidence":{"review_url":"https://gitea.bstein.dev/atlas/titan-iac/pulls/1","merge_commit":"d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06","ci_build_url":"https://jenkins.bstein.dev/job/hermes-chat-router/12/","image_ref":"registry.bstein.dev/bstein/hermes-chat-router:git-d3cbeb06-build-12@sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd","image_digest":"sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"},"transitions":[{"from":"reviewed","to":"merged","at":"2026-08-23T08:00:00Z","by":{"type":"operator","id":"brad"},"evidence":[{"kind":"build","id":"merge"}]},{"from":"merged","to":"built","at":"2026-08-23T08:30:00Z","by":{"type":"system","id":"jenkins"},"evidence":[{"kind":"build","id":"12"}]}]}
|
||||
{"schema":"hux.release.v1","id":"rel_0001aaaa","workload":"hermes-chat-router","feature_flags":["hux.foundation"],"commit":"d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06","state":"built","evidence":{"review_url":"https://gitea.bstein.dev/titan/atlas-iac/pulls/1","merge_commit":"d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06","ci_build_url":"https://jenkins.bstein.dev/job/hermes-chat-router/12/","image_ref":"registry.bstein.dev/bstein/hermes-chat-router:git-d3cbeb06-build-12@sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd","image_digest":"sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"},"transitions":[{"from":"reviewed","to":"merged","at":"2026-08-23T08:00:00Z","by":{"type":"operator","id":"brad"},"evidence":[{"kind":"build","id":"merge"}]},{"from":"merged","to":"built","at":"2026-08-23T08:30:00Z","by":{"type":"system","id":"jenkins"},"evidence":[{"kind":"build","id":"12"}]}]}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"feature_flags": ["hux.foundation"],
|
||||
"commit": "1111111111111111111111111111111111111111",
|
||||
"state": "reviewed",
|
||||
"evidence": {"review_url": "https://git.bstein.dev/atlas/titan-iac/pulls/55"},
|
||||
"evidence": {"review_url": "https://git.bstein.dev/titan/atlas-iac/pulls/55"},
|
||||
"transitions": []
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"max_evidence_age_seconds": 900,
|
||||
"workloads": {
|
||||
"hermes-webui": {
|
||||
"review_url_prefix": "https://scm.bstein.dev/atlas/titan-iac/pulls/",
|
||||
"review_url_prefix": "https://scm.bstein.dev/titan/atlas-iac/pulls/",
|
||||
"jenkins_job_url": "https://jenkins.bstein.dev/job/hermes-webui-image",
|
||||
"image_repository": "registry.bstein.dev/bstein/hermes-webui",
|
||||
"flux_kustomization": "hermes",
|
||||
|
||||
@ -31,9 +31,9 @@ from gitea_api_policy import (
|
||||
)
|
||||
|
||||
CANONICAL_BASE_URL = "https://scm.bstein.dev"
|
||||
ALLOWED_OWNER = "atlas"
|
||||
ALLOWED_OWNER = "titan"
|
||||
DEFAULT_TOKEN_FILE = Path("/vault/secrets/gitea-token")
|
||||
CREATE_PATH_RE = re.compile(r"/api/v1/repos/atlas/([^/]+)/pulls\Z")
|
||||
CREATE_PATH_RE = re.compile(r"/api/v1/repos/titan/([^/]+)/pulls\Z")
|
||||
MAX_ERROR_BYTES = 8192
|
||||
MAX_RESPONSE_BYTES = 2 * 1024 * 1024
|
||||
MAX_API_TARGET_LENGTH = 768
|
||||
@ -106,7 +106,7 @@ def _authorize_read(
|
||||
# administrative rights. No query, no other /user route.
|
||||
_validate_query(target, set())
|
||||
return "identity"
|
||||
prefix = "/api/v1/repos/atlas/"
|
||||
prefix = "/api/v1/repos/titan/"
|
||||
remainder = target.path.removeprefix(prefix)
|
||||
if remainder == target.path:
|
||||
raise PolicyError("reads are limited to explicit Atlas repository metadata")
|
||||
|
||||
@ -337,7 +337,7 @@ class BrokerHandler(AbsoluteHeaderDeadlineMixin, BaseHTTPRequestHandler):
|
||||
_reject_forbidden(repo, "repository", (token,))
|
||||
expected = f"application/x-{service}-advertisement"
|
||||
streamed = _upstream_git_request(
|
||||
f"/atlas/{repo}.git/info/refs?service={service}",
|
||||
f"/titan/{repo}.git/info/refs?service={service}",
|
||||
method="GET",
|
||||
body=None,
|
||||
content_type=None,
|
||||
@ -424,7 +424,7 @@ class BrokerHandler(AbsoluteHeaderDeadlineMixin, BaseHTTPRequestHandler):
|
||||
_validate_receive_pack(body, token)
|
||||
expected = f"application/x-{service}-result"
|
||||
streamed = _upstream_git_request(
|
||||
f"/atlas/{repo}.git/{service}",
|
||||
f"/titan/{repo}.git/{service}",
|
||||
method="POST",
|
||||
body=body,
|
||||
body_length=body_length,
|
||||
|
||||
@ -16,7 +16,7 @@ DATA_ROOT = Path(os.environ.get("HERMES_HOME", "/opt/data"))
|
||||
PROJECT_ROOT = DATA_ROOT / "workspace/projects"
|
||||
BOARD_ROOT = DATA_ROOT / "kanban/boards"
|
||||
ATLAS_REPO = re.compile(
|
||||
r"https://scm\.bstein\.dev/atlas/(?P<repo>[A-Za-z0-9][A-Za-z0-9_.-]{0,99})\.git\Z"
|
||||
r"https://scm\.bstein\.dev/titan/(?P<repo>[A-Za-z0-9][A-Za-z0-9_.-]{0,99})\.git\Z"
|
||||
)
|
||||
SAFE_PREFIXES = frozenset(
|
||||
{"feature", "fix", "chore", "docs", "test", "refactor", "wt", "review", "hermes", "handoff"}
|
||||
@ -24,6 +24,7 @@ SAFE_PREFIXES = frozenset(
|
||||
IDENTIFIER = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}\Z")
|
||||
GIT = "/usr/bin/git"
|
||||
MAX_BOARD_BYTES = 64 * 1024
|
||||
REPOSITORY_ALIASES = {"titan-iac": "atlas-iac"}
|
||||
|
||||
|
||||
class ProjectPolicyError(ValueError):
|
||||
@ -117,7 +118,8 @@ def resolve_project(board: str) -> tuple[str, str, Path]:
|
||||
workdir.relative_to(project_root)
|
||||
except ValueError as error:
|
||||
raise ProjectPolicyError("board workdir is outside the Atlas project registry") from error
|
||||
remote = f"https://scm.bstein.dev/atlas/{board}.git"
|
||||
repository = REPOSITORY_ALIASES.get(board, board)
|
||||
remote = f"https://scm.bstein.dev/titan/{repository}.git"
|
||||
if not ATLAS_REPO.fullmatch(remote):
|
||||
raise ProjectPolicyError("board repository identity is invalid")
|
||||
if not workdir.exists():
|
||||
|
||||
@ -194,7 +194,7 @@ def _workspace_identity(destination: Path, repo: str, branch: str) -> str:
|
||||
if ref_path.is_symlink():
|
||||
raise ProtocolError("workspace branch ref must not be a symlink")
|
||||
origin = _run("remote", "get-url", "origin", cwd=destination)
|
||||
if origin != f"https://scm.bstein.dev/atlas/{repo}.git":
|
||||
if origin != f"https://scm.bstein.dev/titan/{repo}.git":
|
||||
raise ProtocolError("workspace origin does not match assignment")
|
||||
broker = _run("remote", "get-url", "hermes-broker", cwd=destination)
|
||||
if broker != _broker_repo(repo):
|
||||
@ -255,7 +255,7 @@ class Boundary:
|
||||
_run("checkout", "-b", branch, cwd=destination)
|
||||
_run(
|
||||
"remote", "set-url", "origin",
|
||||
f"https://scm.bstein.dev/atlas/{repo}.git", cwd=destination,
|
||||
f"https://scm.bstein.dev/titan/{repo}.git", cwd=destination,
|
||||
)
|
||||
_run("remote", "add", "hermes-broker", broker, cwd=destination)
|
||||
_run("config", "user.name", "Hermes Execution Worker", cwd=destination)
|
||||
@ -270,10 +270,10 @@ class Boundary:
|
||||
@staticmethod
|
||||
def _draft(repo: str, branch: str, base: str, head: str, title: str, body: str) -> str:
|
||||
query = urllib.parse.urlencode(
|
||||
{"state": "open", "head": f"atlas:{branch}", "limit": 10}
|
||||
{"state": "open", "head": f"titan:{branch}", "limit": 10}
|
||||
)
|
||||
existing = json.loads(
|
||||
scm_broker_client.read(f"/api/v1/repos/atlas/{repo}/pulls?{query}")
|
||||
scm_broker_client.read(f"/api/v1/repos/titan/{repo}/pulls?{query}")
|
||||
)
|
||||
if isinstance(existing, list) and existing:
|
||||
return str(existing[0].get("html_url") or "")
|
||||
|
||||
@ -16,7 +16,7 @@ JENKINS_ORIGIN = "https://ci.bstein.dev"
|
||||
JENKINS_BUILD_URL = f"{JENKINS_ORIGIN}/buildByToken/buildWithParameters"
|
||||
SCM_BROKER_ORIGIN = "http://hermes-scm-broker.hermes-scm.svc.cluster.local:9081"
|
||||
SCM_METADATA_URL = f"{SCM_BROKER_ORIGIN}/v1/metadata"
|
||||
SCM_REPOSITORY = "titan-iac"
|
||||
SCM_REPOSITORY = "atlas-iac"
|
||||
MAX_SCM_RESPONSE_BYTES = 512 * 1024
|
||||
JOBS = {
|
||||
"agent": {
|
||||
@ -68,7 +68,7 @@ def _open_without_redirect(request: urllib.request.Request, timeout: int):
|
||||
|
||||
def _verify_reviewed_revision_exists(revision: str) -> None:
|
||||
"""Fail before queuing when the exact reviewed commit is absent from SCM."""
|
||||
path = f"/api/v1/repos/atlas/{SCM_REPOSITORY}/git/commits/{revision}"
|
||||
path = f"/api/v1/repos/titan/{SCM_REPOSITORY}/git/commits/{revision}"
|
||||
payload = json.dumps({"path": path}, separators=(",", ":")).encode("utf-8")
|
||||
request = urllib.request.Request(
|
||||
SCM_METADATA_URL,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
name: manage-atlas-pull-requests
|
||||
description: Read bounded private Atlas repository and pull-request metadata or create a verified draft pull request after a tested branch is pushed through Hermes' least-authority Forgejo client. Use for PR handoff in scm.bstein.dev/atlas repositories. Never use it to update, merge, approve, close, delete, or force-push.
|
||||
description: Read bounded private Atlas repository and pull-request metadata or create a verified draft pull request after a tested branch is pushed through Hermes' least-authority Forgejo client. Use for PR handoff in scm.bstein.dev/titan repositories. Never use it to update, merge, approve, close, delete, or force-push.
|
||||
---
|
||||
|
||||
# Manage Atlas pull requests
|
||||
@ -19,7 +19,7 @@ credential-bearing URL.
|
||||
Pass one allowed Atlas metadata API path to the read operation:
|
||||
|
||||
```sh
|
||||
/opt/scm/gitea_api.py read /api/v1/repos/atlas/REPO/pulls/NUMBER
|
||||
/opt/scm/gitea_api.py read /api/v1/repos/titan/REPO/pulls/NUMBER
|
||||
```
|
||||
|
||||
The client exposes only repository metadata, PRs and PR evidence, branches,
|
||||
|
||||
@ -132,7 +132,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/ariadne.git')
|
||||
url('https://scm.bstein.dev/titan/ariadne.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/master')
|
||||
@ -161,7 +161,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/metis.git')
|
||||
url('https://scm.bstein.dev/titan/metis.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/master')
|
||||
@ -190,7 +190,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/ananke.git')
|
||||
url('https://scm.bstein.dev/titan/ananke.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -274,7 +274,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/pegasus.git')
|
||||
url('https://scm.bstein.dev/titan/pegasus.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -332,7 +332,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/soteria.git')
|
||||
url('https://scm.bstein.dev/titan/soteria.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -361,7 +361,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/titan-iac.git')
|
||||
url('https://scm.bstein.dev/titan/atlas-iac.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -390,7 +390,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/titan-iac.git')
|
||||
url('https://scm.bstein.dev/titan/atlas-iac.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -419,7 +419,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/typhon.git')
|
||||
url('https://scm.bstein.dev/titan/typhon.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -447,7 +447,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/cassandra.git')
|
||||
url('https://scm.bstein.dev/titan/cassandra.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -464,7 +464,7 @@ data:
|
||||
source {
|
||||
git {
|
||||
id('hermes-code-demo-branches')
|
||||
remote('https://scm.bstein.dev/atlas/hermes-code-demo.git')
|
||||
remote('https://scm.bstein.dev/titan/hermes-code-demo.git')
|
||||
credentialsId('gitea-pat')
|
||||
traits {
|
||||
gitBranchDiscovery()
|
||||
@ -497,7 +497,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/hermes-code-demo.git')
|
||||
url('https://scm.bstein.dev/titan/hermes-code-demo.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/master')
|
||||
@ -654,11 +654,11 @@ data:
|
||||
}
|
||||
pipelineJob('hermes-agent-image') {
|
||||
disabled(false)
|
||||
description('Bounded daemonless Kaniko release for one exact reviewed atlas/titan-iac revision already contained by main. Archives exact evidence, then publishes an immutable tag for Flux deployment.')
|
||||
description('Bounded daemonless Kaniko release for one exact reviewed titan/atlas-iac revision already contained by main. Archives exact evidence, then publishes an immutable tag for Flux deployment.')
|
||||
authenticationToken(System.getenv('HERMES_AGENT_IMAGE_BUILD_TOKEN'))
|
||||
parameters {
|
||||
booleanParam('PUBLISH_IMAGE', false, 'Publish the reviewed Hermes agent image.')
|
||||
stringParam('EXPECTED_SOURCE_REVISION', '', 'Full reviewed commit already contained by atlas/titan-iac main.')
|
||||
stringParam('EXPECTED_SOURCE_REVISION', '', 'Full reviewed commit already contained by titan/atlas-iac main.')
|
||||
stringParam('CONFIRM_PUBLISH', '', 'Exact confirmation: PUBLISH HERMES AGENT')
|
||||
}
|
||||
definition {
|
||||
@ -666,7 +666,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/titan-iac.git')
|
||||
url('https://scm.bstein.dev/titan/atlas-iac.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -678,11 +678,11 @@ data:
|
||||
}
|
||||
pipelineJob('hermes-webui-image') {
|
||||
disabled(false)
|
||||
description('Bounded daemonless Kaniko release for one exact reviewed atlas/titan-iac revision already contained by main. Archives exact evidence, then publishes an immutable tag for Flux deployment.')
|
||||
description('Bounded daemonless Kaniko release for one exact reviewed titan/atlas-iac revision already contained by main. Archives exact evidence, then publishes an immutable tag for Flux deployment.')
|
||||
authenticationToken(System.getenv('HERMES_AGENT_IMAGE_BUILD_TOKEN'))
|
||||
parameters {
|
||||
booleanParam('PUBLISH_IMAGE', false, 'Publish the reviewed Hermes WebUI image.')
|
||||
stringParam('EXPECTED_SOURCE_REVISION', '', 'Full reviewed commit that must be contained by atlas/titan-iac main.')
|
||||
stringParam('EXPECTED_SOURCE_REVISION', '', 'Full reviewed commit that must be contained by titan/atlas-iac main.')
|
||||
stringParam('CONFIRM_PUBLISH', '', 'Exact confirmation: PUBLISH HERMES WEBUI')
|
||||
}
|
||||
definition {
|
||||
@ -690,7 +690,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/titan-iac.git')
|
||||
url('https://scm.bstein.dev/titan/atlas-iac.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -706,7 +706,7 @@ data:
|
||||
authenticationToken(System.getenv('HERMES_AGENT_IMAGE_BUILD_TOKEN'))
|
||||
parameters {
|
||||
booleanParam('PUBLISH_IMAGE', false, 'Publish the reviewed Hermes chat-router image.')
|
||||
stringParam('EXPECTED_SOURCE_REVISION', '', 'Full reviewed commit that must be contained by atlas/titan-iac main.')
|
||||
stringParam('EXPECTED_SOURCE_REVISION', '', 'Full reviewed commit that must be contained by titan/atlas-iac main.')
|
||||
stringParam('CONFIRM_PUBLISH', '', 'Exact confirmation: PUBLISH HERMES CHAT ROUTER')
|
||||
}
|
||||
definition {
|
||||
@ -714,7 +714,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/titan-iac.git')
|
||||
url('https://scm.bstein.dev/titan/atlas-iac.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -731,7 +731,7 @@ data:
|
||||
parameters {
|
||||
booleanParam('PUBLISH_IMAGE', false, 'Publish the reviewed Hermes voice image.')
|
||||
stringParam('IMAGE_COMPONENT', '', 'Exact component: stt or tts.')
|
||||
stringParam('EXPECTED_SOURCE_REVISION', '', 'Full reviewed commit that must be contained by atlas/titan-iac main.')
|
||||
stringParam('EXPECTED_SOURCE_REVISION', '', 'Full reviewed commit that must be contained by titan/atlas-iac main.')
|
||||
stringParam('CONFIRM_PUBLISH', '', 'Exact component-specific confirmation.')
|
||||
}
|
||||
definition {
|
||||
@ -739,7 +739,7 @@ data:
|
||||
scm {
|
||||
git {
|
||||
remote {
|
||||
url('https://scm.bstein.dev/atlas/titan-iac.git')
|
||||
url('https://scm.bstein.dev/titan/atlas-iac.git')
|
||||
credentials('gitea-pat')
|
||||
}
|
||||
branches('*/main')
|
||||
@ -755,7 +755,7 @@ data:
|
||||
source {
|
||||
git {
|
||||
id('titan-iac-quality-gate')
|
||||
remote('https://scm.bstein.dev/atlas/titan-iac.git')
|
||||
remote('https://scm.bstein.dev/titan/atlas-iac.git')
|
||||
credentialsId('gitea-pat')
|
||||
traits {
|
||||
gitBranchDiscovery()
|
||||
|
||||
@ -525,7 +525,7 @@ spec:
|
||||
- name: ARIADNE_HERMES_ISSUES_ENABLED
|
||||
value: "true"
|
||||
- name: ARIADNE_HERMES_ISSUE_REPOS
|
||||
value: metis=atlas/metis,lesavka=bstein/lesavka,soteria=atlas/soteria,bstein-dev-home=bstein/bstein-dev-home,ariadne=atlas/ariadne,ananke=atlas/ananke,pegasus=atlas/pegasus,atlasbot=bstein/atlasbot
|
||||
value: metis=titan/metis,lesavka=bstein/lesavka,soteria=titan/soteria,bstein-dev-home=bstein/bstein-dev-home,ariadne=titan/ariadne,ananke=titan/ananke,pegasus=titan/pegasus,atlasbot=bstein/atlasbot
|
||||
- name: ARIADNE_HERMES_ISSUE_DEDUPE_SCOPE
|
||||
value: classification
|
||||
- name: ARIADNE_HERMES_ISSUE_MAX_PER_TICK
|
||||
@ -598,7 +598,7 @@ spec:
|
||||
- name: ARIADNE_HERMES_SONAR_MAX_EFFORT_MINUTES
|
||||
value: "20"
|
||||
- name: ARIADNE_HERMES_CODE_REPOS
|
||||
value: metis=atlas/metis,lesavka=bstein/lesavka,soteria=atlas/soteria,bstein-dev-home=bstein/bstein-dev-home,ariadne=atlas/ariadne,ananke=atlas/ananke,pegasus=atlas/pegasus,atlasbot=bstein/atlasbot
|
||||
value: metis=titan/metis,lesavka=bstein/lesavka,soteria=titan/soteria,bstein-dev-home=bstein/bstein-dev-home,ariadne=titan/ariadne,ananke=titan/ananke,pegasus=titan/pegasus,atlasbot=bstein/atlasbot
|
||||
- name: ARIADNE_HERMES_CODE_BASE_BRANCHES
|
||||
value: metis=master,lesavka=master,soteria=main,bstein-dev-home=master,ariadne=master,ananke=main,pegasus=main,atlasbot=main
|
||||
- name: ARIADNE_HERMES_CODE_PREFIXES
|
||||
|
||||
@ -125,8 +125,8 @@ MUTATIONS = (
|
||||
" def run(self,*_a,**_k):\n"
|
||||
" self.i+=1\n"
|
||||
" return Outcome(('x',),'x',1 if self.i==1 else 0)\n"
|
||||
"q=e.ArmRequest('atlas/titan-iac','origin','acceptance1',e.CONFIRMATION,expected_head='1'*40,expected_base_sha='2'*40)\n"
|
||||
"assert e._cleanup(R(),object(),q,'/api/v1/repos/atlas/titan-iac',[]).status == 'FAIL'\n",
|
||||
"q=e.ArmRequest('titan/atlas-iac','origin','acceptance1',e.CONFIRMATION,expected_head='1'*40,expected_base_sha='2'*40)\n"
|
||||
"assert e._cleanup(R(),object(),q,'/api/v1/repos/titan/atlas-iac',[]).status == 'FAIL'\n",
|
||||
),
|
||||
Mutation(
|
||||
"cleanup-accepts-open-pr",
|
||||
@ -134,7 +134,7 @@ MUTATIONS = (
|
||||
' elif "state does not match" not in problems:\n problems.append("state is not closed")\n',
|
||||
' elif False:\n problems.append("mutant")\n',
|
||||
"import hermes_handoff_ephemeral as e\n"
|
||||
"q=e.ArmRequest('atlas/titan-iac','origin','acceptance1',e.CONFIRMATION,expected_head='1'*40,expected_base_sha='2'*40)\n"
|
||||
"q=e.ArmRequest('titan/atlas-iac','origin','acceptance1',e.CONFIRMATION,expected_head='1'*40,expected_base_sha='2'*40)\n"
|
||||
"p={'number':1,'state':'open','draft':True,'merged':False,'base':{'ref':'main','sha':'2'*40},'head':{'ref':q.ref,'sha':'1'*40}}\n"
|
||||
"assert e._closed_pr_problems(p,q)\n",
|
||||
),
|
||||
@ -156,7 +156,7 @@ MUTATIONS = (
|
||||
' and not path.startswith((f"{repo_root}/", f"{repo_root}?"))\n',
|
||||
" and not path.startswith(repo_root)\n",
|
||||
"from hermes_handoff_policy import *\n"
|
||||
"try: check_argv((GITEA_CLIENT,'read','/api/v1/repos/atlas/titan-iac-evil/pulls'))\n"
|
||||
"try: check_argv((GITEA_CLIENT,'read','/api/v1/repos/titan/atlas-iac-evil/pulls'))\n"
|
||||
"except PolicyError: raise SystemExit(0)\n"
|
||||
"raise SystemExit(1)\n",
|
||||
),
|
||||
|
||||
@ -37,26 +37,26 @@
|
||||
{"board": "cassandra", "body": "In /opt/data/workspace/projects/cassandra-hermes-v69, inspect the semantic-retry pipeline, deterministic claim-repair flow, entry-hazard checks, and existing semantic/entry tests. Produce concise findings for the implementation owner identifying the exact functions, ordering constraints, existing test conventions, and safe extension points for: unsupported exact commander-cost phrasing; whole-deck universal paraphrases and metadata hedges; re-checking semantic compliance after deterministic repair; and polarity-aware caveat/negation handling. Do not modify production behavior, commit, push, deploy, or return a final task-completion result; return implementation findings only.", "expected_role": "implementation", "id": "t_fdc1a23e", "title": "Map semantic-retry compliance decision paths and repair hazards"}
|
||||
{"board": "soteria", "body": "Perform a fresh independent read-only release review of atlas/soteria PR #11. Canonical implementation head claimed by the implementer is 0143d47; verify the live PR head/base/state and review the entire base-to-head diff without trusting its result. Reproduce issue #9's original go:S3776/metrics complexity condition and prove the boolGauge extraction preserves every metric name, label, 1/0 health value, concurrency behavior, and failure path. Look for semantic drift, races, NaN/type behavior, false confidence from narrow tests, docs/LOC/style regressions, and whether the Sonar claim is actually supported. Run focused tests, go test -race ./..., go vet, gofmt, project check/quality commands, and inspect live PR checks read-only where available. Do not edit any file, comment, commit, push, merge, close the issue, deploy, or mutate Kanban outside returning this task result. Return completed with a clear SHIP or BLOCK verdict in summary/findings; review findings are findings, not task blockers. Preserve all inherited/untracked files and prove the review worktree remains clean.", "expected_role": "review", "id": "t_b1d2cd0c", "title": "Independent release review of Soteria PR 11"}
|
||||
{"board": "soteria", "body": "Resolve private atlas/soteria issue #9 (`[hermes] soteria: go:S3776`) from exact main head 47205db5b2e8041bac6a06a78f94fd3662134fc8. Work only in the isolated Hermes worktree derived from /opt/data/workspace/projects/soteria. Inspect the issue and current code independently; the issue's suggested boolGauge extraction is a hypothesis, not trusted implementation truth.\n\nAcceptance:\n- Add a focused RED-before/GREEN-after test proving the healthy PVC metric remains exactly 1 for true and 0 for false, with labels/metric identity unchanged.\n- Reduce RecordInventory cognitive complexity at internal/server/metrics.go:133 without behavior drift, phrase-specific workarounds, or unrelated refactors. If a helper is appropriate, keep it domain-named, small, documented only where useful, and tested.\n- Run targeted tests, the relevant full Go suite/race/static checks, formatting, Sonar/quality checks available locally, and any repository-specified validation. Record exact skips honestly.\n- Preserve unrelated files and verify the issue still applies at the exact starting head before editing.\n- Commit a focused branch, push to canonical atlas/soteria, and open a non-draft PR linked to issue #9 for human review. Do not merge, deploy, close the issue manually, or mutate cluster state.\n- Never expose or commit credentials. Use the runtime GIT_ASKPASS only.\n\nReport exact commit, branch, PR URL, changed files, RED/GREEN evidence, tests, skips, residual risks, and confirmation that main/issue were not modified directly.", "expected_role": "implementation", "id": "t_f1593f8c", "title": "Resolve Soteria issue 9 metrics complexity"}
|
||||
{"board": "titan-iac", "body": "Perform a fresh independent consequential review of private atlas/titan-iac PR #13 at exact head 4f8dcfbbf748e88726912bef1ae32201e1e640a6 against base 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. Work read-only in the supplied worktree. First verify local HEAD, canonical remote branch wt/t_cca008de, refs/pull/13/head, and PR API head/base exactly match. Review the complete PR diff, not prior summaries. Do not edit, stage, commit, push, comment, merge, deploy, mutate Kanban beyond normal completion, create pods, build or publish images, or expose credentials.\n\nReview the Dockerfile source-patch anchors against the pinned upstream /opt/hermes implementation and prove they fail closed under upstream drift. Reproduce the current create_task(initial_status=blocked) defect and verify the extracted patched tree makes explicit initial blocks sticky, explicit unblock reversible, dependency promotion intact, and circuit-breaker blocks non-promotable. Check whether the added build-time regression actually runs in the image build context and catches both false negatives and false positives. Review concurrency 4->2, CPU cap 3->2, and enableServiceLinks=false for resource/scheduling correctness and for any runtime dependency on generated service environment variables. Check tests, style, security, secret hygiene, line limits, kustomize render, client dry-run, and diff hygiene. Distinguish ambient live-environment test failures from PR regressions by reproducing against the base where needed. The absence of a safe image builder is a known deployment blocker, not permission to improvise privileged Docker.\n\nReturn prioritized findings with exact file/line evidence and reproducible cases. End with VERDICT: SHIP only if the code/config PR is reviewable without a material defect; otherwise VERDICT: BLOCK. Keep PR #13 open, unmerged, and undeployed for human review.", "expected_role": "review", "id": "t_06771c95", "title": "Independent review of Hermes platform PR 13 at 4f8dcfbb"}
|
||||
{"board": "titan-iac", "body": "Perform a fresh independent consequential review of private titan/atlas-iac PR #13 at exact head 4f8dcfbbf748e88726912bef1ae32201e1e640a6 against base 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. Work read-only in the supplied worktree. First verify local HEAD, canonical remote branch wt/t_cca008de, refs/pull/13/head, and PR API head/base exactly match. Review the complete PR diff, not prior summaries. Do not edit, stage, commit, push, comment, merge, deploy, mutate Kanban beyond normal completion, create pods, build or publish images, or expose credentials.\n\nReview the Dockerfile source-patch anchors against the pinned upstream /opt/hermes implementation and prove they fail closed under upstream drift. Reproduce the current create_task(initial_status=blocked) defect and verify the extracted patched tree makes explicit initial blocks sticky, explicit unblock reversible, dependency promotion intact, and circuit-breaker blocks non-promotable. Check whether the added build-time regression actually runs in the image build context and catches both false negatives and false positives. Review concurrency 4->2, CPU cap 3->2, and enableServiceLinks=false for resource/scheduling correctness and for any runtime dependency on generated service environment variables. Check tests, style, security, secret hygiene, line limits, kustomize render, client dry-run, and diff hygiene. Distinguish ambient live-environment test failures from PR regressions by reproducing against the base where needed. The absence of a safe image builder is a known deployment blocker, not permission to improvise privileged Docker.\n\nReturn prioritized findings with exact file/line evidence and reproducible cases. End with VERDICT: SHIP only if the code/config PR is reviewable without a material defect; otherwise VERDICT: BLOCK. Keep PR #13 open, unmerged, and undeployed for human review.", "expected_role": "review", "id": "t_06771c95", "title": "Independent review of Hermes platform PR 13 at 4f8dcfbb"}
|
||||
{"board": "titan-iac", "body": "Independent read-only xhigh security audit of the full-handoff acceptance harness produced by parent task t_6496c271.\n\nSETUP (do this first, trust nothing claimed): resolve the exact draft PR, head SHA, and base SHA dynamically rather than from any hardcoded number — list open/draft PRs for the repo, identify the one introducing the full-handoff acceptance harness, and record pr_number, head_sha, base_sha, plus the resolved changed-file list. Pin every finding to that head SHA. Read the full files, not just the diff hunks.\n\nSCOPE:\n1. Command construction: enumerate every place the harness builds a subprocess/exec/HTTP invocation. Confirm no shell=True or string interpolation of untrusted values, no injection via branch/PR/namespace/pod/image names, and that argument lists are built from validated inputs.\n2. Redaction BEFORE execution and BEFORE logging: verify the command line as logged/echoed is redacted prior to being run, not after, and that redaction lives at a single choke point rather than being reapplied per call site.\n3. Sanitization coverage: environment variables passed to children, HTTP headers, request/response bodies, captured stdout, captured stderr, and exception messages/tracebacks. Every one of these paths must be scrubbed. Look specifically for unscrubbed re-raises, repr()/f-string of a response or exception, and error paths that bypass the sanitizer.\n4. Credential containment: prove the harness cannot read or print Vault tokens/paths, cloud provider credentials, or SCM tokens. Trace every environment read and every credential-shaped file/secret access. Construct a concrete test that injects fake secrets into env/headers/responses and asserts they never appear in stdout, stderr, logs, or the JSON artifact.\n5. Ephemeral resource arming/cleanup and protected-branch rejection: verify arming is explicit and opt-in, cleanup is guaranteed on success, failure, timeout, and signal paths (no leaks), and that operations against protected branches are rejected early and unconditionally.\n\nCONSTRAINTS: keep the worktree pristine — no edits, no PR comments, no commit/push/merge/deploy/reconcile, no live mutations of any cluster or provider. Reading and running the harness's own unit tests in an isolated sandbox is allowed; anything that touches real infrastructure is not.\n\nDELIVERABLE: a findings list where each item has severity (blocking / non-blocking), exact path:line at the resolved head SHA, why it is exploitable or wrong, and an executable repro command or test snippet. Explicitly state which of the five scope areas you could NOT fully verify and why. Do not soften findings to reach a positive result.", "expected_role": "implementation", "id": "t_0908bc73", "title": "Audit harness security: command construction, redaction, credential exposure"}
|
||||
{"board": "titan-iac", "body": "Independent read-only xhigh audit proving that the default mode of the full-handoff acceptance harness (from parent task t_6496c271) performs zero mutations.\n\nSETUP: resolve the draft PR, head SHA, and base SHA dynamically (find the open/draft PR introducing the harness; do not assume a PR number). Record them and pin all findings to the resolved head SHA.\n\nSCOPE:\n1. Static command inventory: build an exhaustive inventory of every external command, API call, and SDK method the harness can reach in default mode. Walk call graphs from each entrypoint; include indirect paths through helpers, retries, and cleanup handlers. Classify each as read-only or mutating, citing path:line. A single reachable mutating call in default mode is a blocking finding.\n2. Dynamic proof via spies: run the harness in default mode with subprocess/HTTP/SDK layers replaced by spies that record invocations and hard-fail on any mutating verb (create/apply/patch/delete/rollout/scale/merge/push/comment/annotate/label, and POST/PUT/PATCH/DELETE to state-changing endpoints). Assert the recorded invocation set matches the static inventory — flag both extra calls and inventory entries never exercised.\n3. Mandatory NOT_RUN behavior: verify that any check which cannot be executed is reported as NOT_RUN and that NOT_RUN is never silently coerced into PASS or omitted from the artifact. Confirm whether the presence of any NOT_RUN forces a non-PASS overall result, and state precisely what the code actually does versus what the harness documentation claims.\n4. Escape hatches: identify every flag, env var, or config that can promote the harness out of read-only mode. Verify each requires explicit opt-in, is off by default, and is not reachable through defaulting/coercion bugs.\n\nCONSTRAINTS: worktree stays pristine — no edits, comments, commits, pushes, merges, deploys, reconciles, or live mutations. Run only in an isolated sandbox with spied/faked I/O; never point the harness at real infrastructure.\n\nDELIVERABLE: the full command inventory table, the spy transcript, and a blocking/non-blocking findings list with exact path:line and executable repro commands. State clearly whether 'default mode is read-only' is PROVEN, PARTIALLY PROVEN, or DISPROVEN, and what would be needed to close any gap.", "expected_role": "implementation", "id": "t_0d1d6c47", "title": "Prove default mode is read-only via spies and static command inventory"}
|
||||
{"board": "titan-iac", "body": "Perform a consequential independent release review of private atlas/titan-iac PR #13 at exact head 750dfa241f817179f6c0459a24651fd830e18c28 against exact base 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. The supplied workspace is a clean detached worktree at the exact head. Read-only review only: do not edit files, commit, push, comment, approve, merge, deploy, mutate Kanban other than completing this assigned card, create pods, build or publish images, or expose credentials. Verify local HEAD and all available remote PR identities before trusting the diff; fail closed if identity cannot be established. Inspect the entire six-file PR, not just the last commit. Independently reproduce the prior P1 threat models: unconditional-true created-event predicate, producer hardcoded blocked status, every non-park create mislabeled blocked, late producer reassignment after the pinned ladder, and anchor drift. Confirm the real API non-sticky oracle proves ordinary create and claim, failure limit 1 blocks, recomputation at limit 2 promotes ready, while explicit initial block/unblock, dependency promotion, and current-limit circuit breaker cases remain valid. Review malformed payload handling and textual-anchor limitations for false positives and false negatives. Verify the durable placement correction excludes titan-04 and titan-19 and changes only the main Hermes CPU request 350m to 300m. Run focused regression tests, relevant Hermes unit suites in sanitized and ambient/base-comparison modes, changed-file Ruff and static/secret/diff-scope checks, kustomize build, render, and client dry-run. No image build is required because the safe unprivileged builder is a separate capability. Return a prioritized findings-first verdict: SHIP only if there are no material defects and every explicit acceptance criterion has independent evidence; otherwise BLOCK with exact file and line evidence plus reproducible input. Clearly distinguish pre-existing base failures. Human review remains required after SHIP.", "expected_role": "review", "id": "t_12a22c7f", "title": "Independent SHIP review Hermes PR 13 at 750dfa24"}
|
||||
{"board": "titan-iac", "body": "Repair the live Atlas AI usage exporter reliability defect in private atlas/titan-iac, starting from exact main head 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. Work in the isolated linked worktree Hermes creates from /opt/data/workspace/projects/titan-iac. Create a focused branch, commit, push, and open a pull request for human review. Do not merge or deploy/reconcile an unreviewed change. Preserve unrelated files and never expose Codex/Claude credentials, OAuth contents, tokens, or provider response bodies in Git, logs, tests, PR text, or metrics.\n\nLive reproduced evidence to verify independently:\n- services/hermes/scripts/ai_usage_exporter.py query_codex() can reach the second process.wait(timeout=5) after kill and raise uncaught subprocess.TimeoutExpired.\n- Collector.refresh_provider does not catch that exception type, so the daemon polling thread terminates.\n- /healthz continues returning 200 because it does not represent poller liveness/freshness; the ai-usage-exporter container stays Ready while quota metrics become permanently stale.\n- A metrics client disconnect also emits a noisy BrokenPipe traceback.\n\nRequired behavior:\n1. Add RED regressions first for a Codex subprocess that cannot be reaped within both bounded waits. Cleanup must be strictly bounded and must never let TimeoutExpired escape in a way that kills polling; close selector/pipes and prevent child/process leakage as far as the host API permits.\n2. Isolate every provider refresh so any unexpected provider/cleanup exception records a sanitized failure and the polling loop continues to the other provider and future cycles. Never log exception messages or response bodies because they may contain sensitive material.\n3. Make health report the polling engine, not provider quota success: allow a bounded startup grace, remain healthy when an individual provider is unavailable (that is represented by fetch_success/staleness metrics), but return non-200 when the poller thread is dead or has stopped making bounded progress. Avoid restart loops caused merely by upstream quota/API failure.\n4. Handle metrics-client disconnects without server traceback noise.\n5. Preserve last-good quota values on failures and all existing low-cardinality metric contracts. Do not scrape interactive `/status`, enable API billing, add paid provider keys, or emit account identity/credentials.\n6. Ensure the ConfigMap/script change causes the agent exporter process to restart after a reviewed Flux rollout (use the existing config-revision convention or another tracked mechanism).\n\nValidation:\n- `pytest -q testing/tests/test_hermes_ai_usage_exporter.py testing/tests/test_atlas_ai_dashboard.py` plus relevant Hermes manifest/quality tests.\n- `ruff check` for changed Python when available.\n- regenerate Atlas dashboards only if the generator changes, and prove no unintended generated drift.\n- `kustomize build services/hermes`, `kustomize build services/monitoring`, client dry-runs for both, `git diff --check`.\n- Reinspect current live logs/readiness read-only to confirm the failure signature; do not mutate live workloads before review.\n\nReport RED-before/GREEN-after evidence, exact commit/branch/PR URL, changed files, validation, explicit skips, remaining risk, and why no credential can leak. Keep the PR open, non-draft unless Gitea requires otherwise, and unmerged.", "expected_role": "implementation", "id": "t_1e95ea6d", "title": "Repair AI usage exporter poller health and cleanup"}
|
||||
{"board": "titan-iac", "body": "Read the current private atlas/titan-iac main-head Hermes image patches, Dockerfile patch mechanism, and existing Kanban/Hermes scheduler tests without modifying tracked files. Trace create_task(initial_status='blocked'), event persistence, recompute_ready(), _has_sticky_block(), dependency completion handling, block_task/unblock_task, and circuit-breaker behavior. Identify the smallest patch location and exact focused tests needed to demonstrate RED-before/GREEN-after for: explicitly initially blocked tasks with no parents and with complete parents; ordinary dependency-blocked task promotion after dependencies complete; block_task/unblock_task; and sticky circuit-breaker blocks. Preserve dependency-driven promotion and official public behavior where possible. Return concrete file paths, relevant code paths, proposed assertions, and compatibility risks for the implementation worker.", "expected_role": "implementation", "id": "t_1fc75549", "title": "Analyze Hermes blocked-task state machine and regression strategy"}
|
||||
{"board": "titan-iac", "body": "Amend private atlas/titan-iac PR #13 after independent BLOCK review t_06771c95. Work only in /opt/data/workspace/projects/titan-iac/.worktrees/t_cca008de on branch wt/t_cca008de, starting from exact head 4f8dcfbbf748e88726912bef1ae32201e1e640a6 and base 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. Verify local/remote/pull-ref/API identity first. Preserve the complete existing six-file change. Do not deploy, merge, create pods, build/publish images, run privileged containers, expose credentials, or change unrelated files.\n\nFix the single P1 review finding with adversarial proof. Dockerfile.hermes-agent patches the sticky-block consumer but does not fail closed if upstream create_task stops emitting the expected created event payload {\"status\": task_status}. Add a unique fail-closed source anchor/precondition for the producer semantics on which the consumer patch relies; do not rewrite the producer unnecessarily. dockerfiles/hermes-kanban-blocked-regression.py needs a real-API non-sticky oracle: create/claim a normal task, record one spawn failure at failure_limit=1 so it is blocked, then recompute at failure_limit=2 and prove it promotes to ready. The test must fail under either an unconditional-true created-event predicate or producer drift that labels every created event blocked, while existing explicit initial block/unblock, dependency promotion, and circuit-breaker-at-current-limit cases pass. Add malformed/non-blocked created-payload controls if needed.\n\nReproduce RED against both the over-broad predicate mutant and the exact producer-drift mutant. Prove the amended Dockerfile patch rejects producer drift before build-time tests, all anchor drift remains fail-closed, and the exact patched temp tree passes. Run focused Hermes suites in a sanitized environment (baseline 107), full isolated unit surface (baseline 357), Ruff/AST/YAML/secret/diff/line checks, kustomize render, and client dry-run. Current live service-link/health variables caused four ambient failures that reproduced on base; report both evidence sets. Do not attempt an image build without the separate safe unprivileged builder capability.\n\nCommit and push the focused amendment to wt/t_cca008de so PR #13 advances. Verify remote branch and refs/pull/13/head equal the new commit and the PR remains open/unmerged/undeployed. Status completed only after commit/push/ref verification; use incomplete while anything remains.", "expected_role": "implementation", "id": "t_20f2fd16", "title": "Repair Hermes PR 13 producer-drift regression coverage"}
|
||||
{"board": "titan-iac", "body": "Perform a consequential independent release review of private titan/atlas-iac PR #13 at exact head 750dfa241f817179f6c0459a24651fd830e18c28 against exact base 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. The supplied workspace is a clean detached worktree at the exact head. Read-only review only: do not edit files, commit, push, comment, approve, merge, deploy, mutate Kanban other than completing this assigned card, create pods, build or publish images, or expose credentials. Verify local HEAD and all available remote PR identities before trusting the diff; fail closed if identity cannot be established. Inspect the entire six-file PR, not just the last commit. Independently reproduce the prior P1 threat models: unconditional-true created-event predicate, producer hardcoded blocked status, every non-park create mislabeled blocked, late producer reassignment after the pinned ladder, and anchor drift. Confirm the real API non-sticky oracle proves ordinary create and claim, failure limit 1 blocks, recomputation at limit 2 promotes ready, while explicit initial block/unblock, dependency promotion, and current-limit circuit breaker cases remain valid. Review malformed payload handling and textual-anchor limitations for false positives and false negatives. Verify the durable placement correction excludes titan-04 and titan-19 and changes only the main Hermes CPU request 350m to 300m. Run focused regression tests, relevant Hermes unit suites in sanitized and ambient/base-comparison modes, changed-file Ruff and static/secret/diff-scope checks, kustomize build, render, and client dry-run. No image build is required because the safe unprivileged builder is a separate capability. Return a prioritized findings-first verdict: SHIP only if there are no material defects and every explicit acceptance criterion has independent evidence; otherwise BLOCK with exact file and line evidence plus reproducible input. Clearly distinguish pre-existing base failures. Human review remains required after SHIP.", "expected_role": "review", "id": "t_12a22c7f", "title": "Independent SHIP review Hermes PR 13 at 750dfa24"}
|
||||
{"board": "titan-iac", "body": "Repair the live Atlas AI usage exporter reliability defect in private titan/atlas-iac, starting from exact main head 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. Work in the isolated linked worktree Hermes creates from /opt/data/workspace/projects/titan-iac. Create a focused branch, commit, push, and open a pull request for human review. Do not merge or deploy/reconcile an unreviewed change. Preserve unrelated files and never expose Codex/Claude credentials, OAuth contents, tokens, or provider response bodies in Git, logs, tests, PR text, or metrics.\n\nLive reproduced evidence to verify independently:\n- services/hermes/scripts/ai_usage_exporter.py query_codex() can reach the second process.wait(timeout=5) after kill and raise uncaught subprocess.TimeoutExpired.\n- Collector.refresh_provider does not catch that exception type, so the daemon polling thread terminates.\n- /healthz continues returning 200 because it does not represent poller liveness/freshness; the ai-usage-exporter container stays Ready while quota metrics become permanently stale.\n- A metrics client disconnect also emits a noisy BrokenPipe traceback.\n\nRequired behavior:\n1. Add RED regressions first for a Codex subprocess that cannot be reaped within both bounded waits. Cleanup must be strictly bounded and must never let TimeoutExpired escape in a way that kills polling; close selector/pipes and prevent child/process leakage as far as the host API permits.\n2. Isolate every provider refresh so any unexpected provider/cleanup exception records a sanitized failure and the polling loop continues to the other provider and future cycles. Never log exception messages or response bodies because they may contain sensitive material.\n3. Make health report the polling engine, not provider quota success: allow a bounded startup grace, remain healthy when an individual provider is unavailable (that is represented by fetch_success/staleness metrics), but return non-200 when the poller thread is dead or has stopped making bounded progress. Avoid restart loops caused merely by upstream quota/API failure.\n4. Handle metrics-client disconnects without server traceback noise.\n5. Preserve last-good quota values on failures and all existing low-cardinality metric contracts. Do not scrape interactive `/status`, enable API billing, add paid provider keys, or emit account identity/credentials.\n6. Ensure the ConfigMap/script change causes the agent exporter process to restart after a reviewed Flux rollout (use the existing config-revision convention or another tracked mechanism).\n\nValidation:\n- `pytest -q testing/tests/test_hermes_ai_usage_exporter.py testing/tests/test_atlas_ai_dashboard.py` plus relevant Hermes manifest/quality tests.\n- `ruff check` for changed Python when available.\n- regenerate Atlas dashboards only if the generator changes, and prove no unintended generated drift.\n- `kustomize build services/hermes`, `kustomize build services/monitoring`, client dry-runs for both, `git diff --check`.\n- Reinspect current live logs/readiness read-only to confirm the failure signature; do not mutate live workloads before review.\n\nReport RED-before/GREEN-after evidence, exact commit/branch/PR URL, changed files, validation, explicit skips, remaining risk, and why no credential can leak. Keep the PR open, non-draft unless Gitea requires otherwise, and unmerged.", "expected_role": "implementation", "id": "t_1e95ea6d", "title": "Repair AI usage exporter poller health and cleanup"}
|
||||
{"board": "titan-iac", "body": "Read the current private titan/atlas-iac main-head Hermes image patches, Dockerfile patch mechanism, and existing Kanban/Hermes scheduler tests without modifying tracked files. Trace create_task(initial_status='blocked'), event persistence, recompute_ready(), _has_sticky_block(), dependency completion handling, block_task/unblock_task, and circuit-breaker behavior. Identify the smallest patch location and exact focused tests needed to demonstrate RED-before/GREEN-after for: explicitly initially blocked tasks with no parents and with complete parents; ordinary dependency-blocked task promotion after dependencies complete; block_task/unblock_task; and sticky circuit-breaker blocks. Preserve dependency-driven promotion and official public behavior where possible. Return concrete file paths, relevant code paths, proposed assertions, and compatibility risks for the implementation worker.", "expected_role": "implementation", "id": "t_1fc75549", "title": "Analyze Hermes blocked-task state machine and regression strategy"}
|
||||
{"board": "titan-iac", "body": "Amend private titan/atlas-iac PR #13 after independent BLOCK review t_06771c95. Work only in /opt/data/workspace/projects/titan-iac/.worktrees/t_cca008de on branch wt/t_cca008de, starting from exact head 4f8dcfbbf748e88726912bef1ae32201e1e640a6 and base 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. Verify local/remote/pull-ref/API identity first. Preserve the complete existing six-file change. Do not deploy, merge, create pods, build/publish images, run privileged containers, expose credentials, or change unrelated files.\n\nFix the single P1 review finding with adversarial proof. Dockerfile.hermes-agent patches the sticky-block consumer but does not fail closed if upstream create_task stops emitting the expected created event payload {\"status\": task_status}. Add a unique fail-closed source anchor/precondition for the producer semantics on which the consumer patch relies; do not rewrite the producer unnecessarily. dockerfiles/hermes-kanban-blocked-regression.py needs a real-API non-sticky oracle: create/claim a normal task, record one spawn failure at failure_limit=1 so it is blocked, then recompute at failure_limit=2 and prove it promotes to ready. The test must fail under either an unconditional-true created-event predicate or producer drift that labels every created event blocked, while existing explicit initial block/unblock, dependency promotion, and circuit-breaker-at-current-limit cases pass. Add malformed/non-blocked created-payload controls if needed.\n\nReproduce RED against both the over-broad predicate mutant and the exact producer-drift mutant. Prove the amended Dockerfile patch rejects producer drift before build-time tests, all anchor drift remains fail-closed, and the exact patched temp tree passes. Run focused Hermes suites in a sanitized environment (baseline 107), full isolated unit surface (baseline 357), Ruff/AST/YAML/secret/diff/line checks, kustomize render, and client dry-run. Current live service-link/health variables caused four ambient failures that reproduced on base; report both evidence sets. Do not attempt an image build without the separate safe unprivileged builder capability.\n\nCommit and push the focused amendment to wt/t_cca008de so PR #13 advances. Verify remote branch and refs/pull/13/head equal the new commit and the PR remains open/unmerged/undeployed. Status completed only after commit/push/ref verification; use incomplete while anything remains.", "expected_role": "implementation", "id": "t_20f2fd16", "title": "Repair Hermes PR 13 producer-drift regression coverage"}
|
||||
{"board": "titan-iac", "body": "Perform a fresh independent read-only release review of the implementation produced by parent task t_64e1ae8d. Dynamically resolve its exact PR/head/base. Do not edit/comment/commit/push/merge/build/publish/deploy/reconcile/restart or mutate Kanban beyond returning the review.\\n\\nIndependently reproduce the zombie creation path and verify the repair prevents unreaped adopted descendants under normal completion, timeout/kill, cancellation, nested Git/SSH, concurrency, and restart without racing provider subprocess waiters or changing exit results. Audit PID1/init/subreaper/signal semantics, resource bounds and sanitized metrics. Verify compatibility with merged PR13 and then-current PR15, full quality/coverage/LOC, render/schema/client/server dry-runs, and secret scans. Return strict SHIP or BLOCK with exact executable evidence and residual rollout canary requirements.", "expected_role": "review", "id": "t_261e208f", "title": "Independent review of cli-lane process reaping"}
|
||||
{"board": "titan-iac", "body": "Independently review private atlas/titan-iac PR #12 at exact head 465d386da5237f011cb408f1a3d4b7d0c6d8ffae against exact base 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. This is a consequential read-only release review of the AI usage exporter reliability repair produced by parent task t_1e95ea6d.\n\nDo not edit files, mutate Kanban beyond returning this task result, commit, push, comment, merge, deploy, reconcile, or expose credentials/provider bodies. Verify local HEAD, canonical remote branch wt/t_1e95ea6d, and PR head are byte-identical before review. Inspect the entire three-file diff, not just the parent summary.\n\nReview especially: strictly bounded terminate/kill/reap behavior when both waits time out; selector/pipe cleanup and child leakage limits; per-provider and per-cycle exception isolation; last-good metric preservation; health semantics during startup, provider failure, dead poller, stalled provider, and interval sleep; avoidance of restart loops; client disconnect handling; thread safety; low-cardinality metric compatibility; sanitized logging; absence of OAuth/API-key/account data leakage; config-revision rollout behavior; test truthfulness and missing adversarial tests. Re-run the focused exporter/dashboard tests and proportionate manifest/render/dry-run validation independently. Treat parent tests and done status as evidence to verify, not truth.\n\nReturn prioritized findings with exact file/line references and reproducible inputs. State explicitly if no defect is found in an area, list exact tests/skips, and give a ship/not-ship verdict. Keep the PR open for human review.", "expected_role": "review", "id": "t_326d55c6", "title": "Independent release review of titan-iac PR 12"}
|
||||
{"board": "titan-iac", "body": "Independently review private titan/atlas-iac PR #12 at exact head 465d386da5237f011cb408f1a3d4b7d0c6d8ffae against exact base 0dd6ea0f02672ecafd8c5c3bde7f5677e960c164. This is a consequential read-only release review of the AI usage exporter reliability repair produced by parent task t_1e95ea6d.\n\nDo not edit files, mutate Kanban beyond returning this task result, commit, push, comment, merge, deploy, reconcile, or expose credentials/provider bodies. Verify local HEAD, canonical remote branch wt/t_1e95ea6d, and PR head are byte-identical before review. Inspect the entire three-file diff, not just the parent summary.\n\nReview especially: strictly bounded terminate/kill/reap behavior when both waits time out; selector/pipe cleanup and child leakage limits; per-provider and per-cycle exception isolation; last-good metric preservation; health semantics during startup, provider failure, dead poller, stalled provider, and interval sleep; avoidance of restart loops; client disconnect handling; thread safety; low-cardinality metric compatibility; sanitized logging; absence of OAuth/API-key/account data leakage; config-revision rollout behavior; test truthfulness and missing adversarial tests. Re-run the focused exporter/dashboard tests and proportionate manifest/render/dry-run validation independently. Treat parent tests and done status as evidence to verify, not truth.\n\nReturn prioritized findings with exact file/line references and reproducible inputs. State explicitly if no defect is found in an area, list exact tests/skips, and give a ship/not-ship verdict. Keep the PR open for human review.", "expected_role": "review", "id": "t_326d55c6", "title": "Independent release review of titan-iac PR 12"}
|
||||
{"board": "titan-iac", "body": "Implement and validate a durable least-privilege image build/publish path for Hermes agent changes. Use the existing Jenkins/Kaniko pattern or another reviewed rootless builder. Never run privileged Docker-in-Docker, never expose unauthenticated Docker or BuildKit TCP sockets, never schedule disposable builders on titan-14 or titan-18, and never print or commit registry credentials. Deliver through a focused human-reviewed PR with tests and proof that the image digest can be published and then updated through Flux. PR #13 is intentionally undeployed until this capability exists.", "expected_role": "implementation", "id": "t_404f607c", "title": "Provide a tracked unprivileged Hermes image build and publish workflow"}
|
||||
{"board": "titan-iac", "body": "After t_c31dd427 completes, independently review exact repaired PR18 head against current main. Re-run the complete t_dbdcd739 and t_5975c06a evidence, especially lease-failure persistence/restart recovery, exact-run fencing, no poison-row CrashLoop, mediator/worker drain and RWO recovery, retry branch submission, startup tooling/Flux health, focused ancestry not bundling other open drafts, all security boundaries, three-node scheduling, at least 95 percent line and branch coverage, below 500 LOC, full quality/render/dry-run/secret scans. Read-only. Return completed SHIP or completed BLOCK; a BLOCK verdict completes the review. No edits/comments/push/merge/deploy/reconcile.", "expected_role": "review", "id": "t_529c50f9", "title": "Independent re-review of final PR 18 pool repair"}
|
||||
{"board": "titan-iac", "body": "Perform a new independent read-only consequential review after parent t_8fbeb41f repairs existing PR #18. Dynamically resolve exact PR/head/base and current PR14/15/16/19 heads; distrust all claimed tests. No edits/comments/commit/push/merge/build/publish/deploy/reconcile/live mutation.\\n\\nRe-run every artifact under /opt/data/workspace/evidence/t_dbdcd739 and independently test all P0/P1/P2 integration boundaries: run ID typing/finalization/reconcile progress; workspace/branch/project routing across all Atlas boards; PR15 module/runtime compatibility; per-worker crypto isolation; sidecar/model network trust; PR14 broker-only SCM; OAuth refresh ownership; exact lease expiry/reassignment/error handling; worker-only three-node schedulability; config rollout/version skew; RBAC/network/mount isolation; canonical coverage/LOC/full gate/renders/schema/dry-runs/secret scans. Return strict SHIP or BLOCK as a completed review; findings do not require you to edit the reviewed code.", "expected_role": "review", "id": "t_5975c06a", "title": "Independent re-review of repaired PR 18 distributed pool"}
|
||||
{"board": "titan-iac", "body": "After parent repair t_69159fea completes, perform a fresh independent read-only xhigh review of draft PR #19 at its exact then-current head against then-current main. Do not trust previous claims or the earlier review. Keep the review worktree pristine and do not edit, comment, commit, push, merge, publish, deploy, reconcile, create credentials, or mutate live resources.\n\nRe-run every adversarial class from review t_e7f46d44/Codex session 01a00f52-a815-7933-9f7d-04ac16145027: structural command allowlist, credential paths, raw Secret/kubeconfig/Helm/Git controls, subprocess memory/time/process-group bounds, redact-before-truncate across JSON and human output, unknown/NaN/future/malformed/stale evidence, PATH/binary provenance, armed preflight/create uncertainty/cleanup, remote-main+PR+image+running-revision binding, three-node and chat/Telegram coverage, dependency/rollback accuracy, and zero/partial/malformed states. Prove default mode performs no mutation through spies and a complete command inventory.\n\nRequire every production module in the canonical managed/lint/line+branch coverage contract at >=95 percent and every source/test file below 500 LOC. Require full canonical quality gate, Ruff/format/compile, mutation/adversarial suite, render/dry-run, diff and secret scans all green. Return a strict completed SHIP or completed BLOCK review with exact executable evidence. A BLOCK verdict completes this review task; do not attempt implementation work.", "expected_role": "review", "id": "t_5ae177fc", "title": "Independent re-review of repaired PR 19 acceptance harness"}
|
||||
{"board": "titan-iac", "body": "Work in the isolated Titan IaC worktree created for this task. Read AGENTS.md and HERMES_ATLAS_MIGRATION_HANDOFF.md completely. Use Claude Code through Claude.ai subscription OAuth only; never configure/use ANTHROPIC_API_KEY or any Console/API-billed route.\n\nBuild the hardest remaining independent artifact: a fail-closed, read-only-by-default Hermes full-handoff acceptance harness and release runbook. Keep the change isolated to new files scripts/ops/hermes_handoff_acceptance.py, testing/tests/test_hermes_handoff_acceptance.py (split into meaningful modules if it would exceed 500 LOC), and docs/hermes_full_handoff_acceptance.md. Do not edit files changed by open PRs #14, #15, #16, #17, or the active distributed-worker task/PR. PR #13 is already merged/deployed; baseline origin/main must be ab346f55509d584e457fe26cf90be3078f7a375c or a descendant. Dynamically fetch and record exact current heads/state for #14-#17 and any distributed-worker PR; do not hard-code stale heads.\n\nThe harness must emit machine-readable JSON plus a concise human summary, redact credential-shaped values, never print environment values/tokens/private keys/cookies/Vault responses, enforce bounded timeouts/output, and classify every check PASS/FAIL/NOT_RUN/NOT_APPLICABLE. Any mandatory FAIL or NOT_RUN makes overall NO_GO. Split evidence into an external read-only operator vantage point and an actual in-pod Hermes self-probe; do not treat kubectl auth can-i --as alone as proof. Default mode must perform no mutation. Feature-branch push and draft-PR creation must be a separately armed ephemeral mode with exact unique refs, explicit confirmation, preflight, protected-main/master rejection, and verified cleanup; default reports these NOT_RUN.\n\nVerify after pending PRs merge/deploy: distinct chat/agent/triage scopes; Codex ChatGPT login and Claude claude.ai firstParty subscription with no provider API-key env vars; Switchyard provider/model/effort/fallback/latency/failure evidence; visible durable session/Kanban activity; denied cluster-admin/secrets/SA-token/impersonation/arbitrary exec-attach-portforward/workload mutation; allowed required get/list/watch/pod logs/Flux+Helm status; no worker Gitea credential and no merge/approve/close/admin route; broker clone/fetch/feature push/metadata/draft PR and protected-branch denial; every node dedicated locked hermes-agent account with no human key/sudo/disk/runtime/K3s/kubelet access; tokenless unprivileged builder and exact expected caps; immutable Harbor build tag; finalization/replay/decomposition regressions; no unexpected GitOps suspension/unhealthy state; three distinct distributed workers with coordinator-only state ownership, distinct nodes/PVCs, authenticated bounded activity/result path, stale/duplicate safety; activity visible in agent UI; chat.hermes Telegram source/topic continuity and no blank session render regression.\n\nDocument exact dependency/merge order for merged #13 plus pending #14-#17/distributed pool, image build/Flux rollout, rollback points, and final go/no-go checklist. Unit-test parsing, redaction, timeout, partial failure, zero-state, dual-vantage mismatch, mandatory skip, and ephemeral arming/cleanup behavior. Keep production files <500 LOC and achieve >=95% per-file coverage for new source. Run focused/full relevant tests, Ruff/format, py_compile, diff/secret scans and applicable render checks. Commit, push a feature/hermes-full-handoff-acceptance branch, create a draft PR only, report exact SHA/evidence. Do not merge, publish images, deploy, reconcile, create credentials, or mutate live cluster/Gitea during this implementation.", "expected_role": "implementation", "id": "t_6496c271", "title": "Build fail-closed Hermes full-handoff acceptance harness"}
|
||||
{"board": "titan-iac", "body": "Live reliability defect observed on the merged PR13 agent.hermes cli-lane-runner pod: after ~95 minutes it has 141 defunct git children, all parented to PID 1 (/opt/coordinator/cli_lane_runner.py). This is an authorized repair of our own runtime.\\n\\nDiagnose and repair subprocess lifecycle/reaping so long-lived agent.hermes workers cannot accumulate zombies or exhaust PID capacity while running Codex/Claude/Git/test commands. Work on current atlas/titan-iac main in an isolated worktree and draft PR. Do not manually mutate/deploy/restart production.\\n\\nRequirements:\\n- Reproduce the actual creation path and identify which spawn/session/process-group behavior leaves children for PID1.\\n- Implement deterministic reaping/cleanup at the owner boundary without stealing children from active provider subprocesses, corrupting streamed output, or masking exit status.\\n- Cover normal exit, timeout SIGTERM/SIGKILL, provider cancellation, nested Git/SSH helpers, concurrent lanes, runner restart, and orphan adoption. Avoid a broad signal handler if it races subprocess waiters; use an explicit supervisor/subreaper/init boundary as appropriate and justify it.\\n- Add bounded observability for active/reaped/orphaned child counts without command lines, environment, credentials, or sensitive output.\\n- Preserve PR13 blocked-task behavior and PR15 finalization/recovery compatibility. Resolve current exact PR15 head dynamically and prove clean semantic merge in the reachable order.\\n- Keep every production/test file under 500 lines, >=95% per-file coverage, quality gate green; render/dry-run Hermes and run relevant static/secret checks.\\n- Commit/push/open one draft PR, human review required. No image publication, merge, Flux reconciliation, deployment, live restart, or credential access.\\nReturn completed only with exact pushed SHA and evidence.", "expected_role": "implementation", "id": "t_64e1ae8d", "title": "Eliminate cli-lane zombie process accumulation"}
|
||||
{"board": "titan-iac", "body": "Live reliability defect observed on the merged PR13 agent.hermes cli-lane-runner pod: after ~95 minutes it has 141 defunct git children, all parented to PID 1 (/opt/coordinator/cli_lane_runner.py). This is an authorized repair of our own runtime.\\n\\nDiagnose and repair subprocess lifecycle/reaping so long-lived agent.hermes workers cannot accumulate zombies or exhaust PID capacity while running Codex/Claude/Git/test commands. Work on current titan/atlas-iac main in an isolated worktree and draft PR. Do not manually mutate/deploy/restart production.\\n\\nRequirements:\\n- Reproduce the actual creation path and identify which spawn/session/process-group behavior leaves children for PID1.\\n- Implement deterministic reaping/cleanup at the owner boundary without stealing children from active provider subprocesses, corrupting streamed output, or masking exit status.\\n- Cover normal exit, timeout SIGTERM/SIGKILL, provider cancellation, nested Git/SSH helpers, concurrent lanes, runner restart, and orphan adoption. Avoid a broad signal handler if it races subprocess waiters; use an explicit supervisor/subreaper/init boundary as appropriate and justify it.\\n- Add bounded observability for active/reaped/orphaned child counts without command lines, environment, credentials, or sensitive output.\\n- Preserve PR13 blocked-task behavior and PR15 finalization/recovery compatibility. Resolve current exact PR15 head dynamically and prove clean semantic merge in the reachable order.\\n- Keep every production/test file under 500 lines, >=95% per-file coverage, quality gate green; render/dry-run Hermes and run relevant static/secret checks.\\n- Commit/push/open one draft PR, human review required. No image publication, merge, Flux reconciliation, deployment, live restart, or credential access.\\nReturn completed only with exact pushed SHA and evidence.", "expected_role": "implementation", "id": "t_64e1ae8d", "title": "Eliminate cli-lane zombie process accumulation"}
|
||||
{"board": "titan-iac", "body": "Repair existing draft PR #19 on its SAME branch and implementation worktree after independent review t_e7f46d44 returned BLOCK twice. Exact reviewed head 8f005458282269ba5c07941814e4237f5d4cf3ac; resolve current main and all dependent PR heads dynamically. Do not create another PR.\n\nTreat the completed review transcript in Codex session 01a00f52-a815-7933-9f7d-04ac16145027 as authoritative evidence. Close every release blocker:\n1. Default mode must be structurally read-only and credential-safe. Reject kubectl auth reconcile, config view --raw, Secret/raw-secret reads, client-side or post-separator dry-run tricks, git fetch/config sshCommand, Helm values, and shell paths under service-account/Vault/runtime credential roots before subprocess creation.\n2. Bound stdout/stderr in memory while the child runs; enforce absolute timeout, kill/reap the whole process group, bound descendants/pipes, validate timeout/output/concurrency inputs, and make truncation fail closed for every evaluator.\n3. Redact before truncation and screen every JSON plus human summary/error/identity field. Never expose a credential prefix.\n4. Fail closed on unknown statuses, empty catalog, NaN/non-finite numbers, future timestamps, malformed Flux/routing rows, stale required-provider evidence, generic 404/no-route strings, or mocked/self-authored PATH evidence. Add binary/path attestation or equivalent provenance.\n5. Armed mode must preflight before any networked/mutating vantage, bind actual repo to fixed expected Atlas repo, reserve cleanup time, verify exact draft/base/head state, discover uncertain creates, and require branch plus PR cleanup success. Never false-PASS malformed/failed cleanup.\n6. Freshness must bind remote main SHA, reviewed PR head, image digest/imageID, build SHA, and running deployment revision. Require all three distributed workers/nodes; no vacuous node-count=1 default. Include chat/Telegram continuity and pool assignment checks.\n7. Add all 15 production modules to canonical managed_modules, lint_paths, and coverage.tracked_files. Every new/modified source must remain below 500 LOC and at least 95 percent line and branch coverage. Fix the two environment-sensitive tests by isolating provider-health and Gitea-token fixtures. Canonical quality gate, Ruff, format, compile, mutation/adversarial tests, render/dry-run, diff/secret scans must all be green.\n8. Correct dependency/merge/conflict graph using exact current heads. Remove unsafe rollback guidance restoring cluster-admin and false PVC-pruning assumptions.\n9. Add executable regressions for every reviewer repro, including large/malformed inputs, zero state, partial failures, timeouts, descendant pipes, and no-subprocess/no-mutation spies.\n\nKeep the worktree pristine except this repair. Amend/commit and force-with-lease only the existing feature/hermes-full-handoff-acceptance branch, update PR #19, report exact head and evidence. Human review required. No merge, publish, deploy, reconcile, credential access, or live mutation.", "expected_role": "implementation", "id": "t_69159fea", "title": "Repair PR 19 full-handoff acceptance blockers"}
|
||||
{"board": "titan-iac", "body": "After t_888cdeee completes, independently review exact repaired PR19 head against current main. Re-run all prior policy, subprocess, redaction, evaluator, empty-evidence, attestation, armed cleanup, freshness, three-node/chat, dependency, mutation, coverage and format repros. Require mandatory names-absent checks to return NOT_RUN on zero evidence; exact repo/path/impersonation boundaries; at least 95 percent line and branch coverage; below 500 LOC; full canonical green gate. Read-only. Return completed SHIP or completed BLOCK; a BLOCK verdict completes the review. No edits/comments/push/merge/deploy/reconcile.", "expected_role": "review", "id": "t_699f9a2d", "title": "Independent re-review of final PR 19 harness repair"}
|
||||
{"board": "titan-iac", "body": "Live failure observed on t_dbdcd739: a read-only independent release reviewer returned a completed BLOCK review with five P0 findings, but the local goal judge rejected completion and resumed Claude with an instruction to continue because the reviewed implementation was not SHIP. A review's job is to issue a verdict, not repair the implementation; this wastes subscription capacity and can loop.\\n\\nRepair Hermes goal/result semantics so task-role completion is judged against the assigned action. For review/diagnostic/audit tasks, status=completed with a truthful SHIP or BLOCK verdict and findings must finalize even when findings show the reviewed artifact is unfit to ship. Only genuine inability to perform the review belongs in blockers. Implementation tasks must still be held to their acceptance criteria.\\n\\nRequirements:\\n- Reproduce with exact task body/result shape and local judge path.\\n- Define a deterministic task-role/expected-output contract, preferably explicit metadata/schema rather than fragile keyword-only inference; preserve backward compatibility.\\n- Make reviewer/diagnostic completion fail closed on malformed/missing verdict/evidence, but never demand code edits prohibited by read-only scope.\\n- Preserve implementation goal looping and PR15 accepted-result durability/exact-run behavior. Dynamically merge-test current PR15 head.\\n- Cover SHIP, BLOCK-with-findings, incomplete review, blocked review, implementation incomplete, ambiguous tasks, reviewer provider fallback, restart/replay, and no duplicate turn/session.\\n- Emit bounded/sanitized judge reason and correct Kanban final result; no hidden auto-mutation.\\n- All files <500, >=95% per-file canonical coverage, quality/render/dry-run/secret checks.\\n- Draft PR only, human review; no merge/build/publish/deploy/reconcile/live restart.\\nReturn exact pushed SHA and evidence.", "expected_role": "implementation", "id": "t_6da029e0", "title": "Make goal judge finalize completed review verdicts"}
|
||||
{"board": "titan-iac", "body": "Fresh independent read-only xhigh release review of chat.hermes smoothness work from parent t_83252d81. Resolve exact draft PR/head/base; do not edit/comment/push/merge/deploy/reconcile/call real Telegram or mutate session data. Reproduce blank/inaccessible session rendering, reconnect/resume, active tool progress, API/Telegram source tags and legacy migration, deterministic durable topics across hour/day/week/router restart, bounded recent-turn+summary context and duplicate suppression, malformed/oversized history, four-tenant isolation, inbound/outbound media and path safety, and truthful previous-image edit semantics. Audit Switchyard/manual route preservation and chat/agent/triage separation. Run focused/full UI/session/router Go+race/Python tests, coverage/LOC, Kustomize/schema/client+Flux-manager server dry-runs, secret/diff scans. Return strict SHIP/BLOCK with exact executable evidence and residual risks. Keep review worktree pristine.", "expected_role": "review", "id": "t_7b7eaaf5", "title": "Independent review of chat.hermes smoothness"}
|
||||
{"board": "titan-iac", "body": "Starting from the current private atlas/titan-iac main head, create an isolated linked worktree and dedicated branch. Use the two analysis reports to implement the smallest robust Hermes-only change. First run or add focused tests that fail against the current behavior, retaining RED evidence; then patch the existing Dockerfile image-patching/test mechanism so explicitly created blocked tasks acquire sticky blocked semantics without weakening normal dependency promotion, explicit unblock behavior, or circuit-breaker blocks. Add focused GREEN tests for every required scenario. Update deployment configuration to set HERMES_CLI_LANE_CONCURRENCY=2, cap cli-lane-runner CPU limit at 2, and set enableServiceLinks: false at the hermes-agent pod-spec level; add a manifest regression proving service links are disabled and check resource requests remain sensible. Preserve unrelated work and never expose or commit credentials. Do not deploy, reconcile Flux, restart the agent, or mutate live Kanban data beyond this task lifecycle. Record exact changed files and RED/GREEN commands/results for handoff.", "expected_role": "implementation", "id": "t_7ddd9eb2", "title": "Implement Hermes isolation and blocked-task regressions on a branch"}
|
||||
{"board": "titan-iac", "body": "Starting from the current private titan/atlas-iac main head, create an isolated linked worktree and dedicated branch. Use the two analysis reports to implement the smallest robust Hermes-only change. First run or add focused tests that fail against the current behavior, retaining RED evidence; then patch the existing Dockerfile image-patching/test mechanism so explicitly created blocked tasks acquire sticky blocked semantics without weakening normal dependency promotion, explicit unblock behavior, or circuit-breaker blocks. Add focused GREEN tests for every required scenario. Update deployment configuration to set HERMES_CLI_LANE_CONCURRENCY=2, cap cli-lane-runner CPU limit at 2, and set enableServiceLinks: false at the hermes-agent pod-spec level; add a manifest regression proving service links are disabled and check resource requests remain sensible. Preserve unrelated work and never expose or commit credentials. Do not deploy, reconcile Flux, restart the agent, or mutate live Kanban data beyond this task lifecycle. Record exact changed files and RED/GREEN commands/results for handoff.", "expected_role": "implementation", "id": "t_7ddd9eb2", "title": "Implement Hermes isolation and blocked-task regressions on a branch"}
|
||||
{"board": "titan-iac", "body": "Make chat.hermes.bstein.dev smoother and truthful without changing agent.hermes or triage.hermes scope. Work in an isolated worktree and read AGENTS.md, HERMES_ATLAS_MIGRATION_HANDOFF.md, current chat/router/session code, and exact current open PR #14-#17 plus distributed-pool diff before editing. Avoid every file changed by those PRs; if a required fix overlaps, stop with a precise dependency instead of racing it. Use subscription Codex only; no provider API keys.\n\nFirst perform a read-only live/code audit, then implement only evidence-backed fixes in a focused draft PR: eliminate blank/inaccessible session render states and ensure activity progressively renders during long work; preserve reconnect/resume and lineage; label existing and new Telegram API sessions under Telegram rather than Unassigned; keep durable topic-based Telegram continuity for roughly week-scale conversations with bounded recent turns + durable compact summary rather than resending an unbounded transcript; make source/topic/session identity deterministic across pauses and router restarts; prevent duplicate stored context; keep text/image/tool capabilities truthful; preserve actual outbound Telegram media delivery and never expose MEDIA paths; explicitly distinguish whether an image-edit request can reuse a prior image attachment versus generate a new image; do not claim inbound image editing if transport does not provide the attachment.\n\nAdd adversarial tests for existing-session migration idempotence, source labels, topic expiry/reuse, summary/recent-turn bounds, duplicate suppression, resume after an hour/day/week boundary, router restart, malformed/oversized histories, WebSocket/poll disconnect, blank render fallback, active tool progress, and Telegram media/caption safety. Preserve four-tenant isolation and Switchyard routes/manual overrides. No secrets in Git/logs. Keep production files <500 LOC and >=95% per-file coverage for new source. Run relevant full tests, Ruff/Go tests/race where applicable, Kustomize/client/Flux-manager server dry-runs, diff and secret scans. Commit/push branch feature/hermes-chat-smoothness, open draft PR for Brad. Do not merge, publish, deploy, reconcile, call real Telegram, or mutate live session data.", "expected_role": "implementation", "id": "t_83252d81", "title": "Harden chat.hermes continuity and activity rendering"}
|
||||
{"board": "titan-iac", "body": "Repair existing Titan IaC draft PR #12 on its SAME branch/worktree; do not open a replacement PR. Start by read-only fetching current origin/main ab346f55 or descendant and exact current PR14 head because PR14 is splitting baseline oversized tests. Rebase/merge safely without dropping PR13/PR14 semantics; if PR14 is not merged, validate a synthetic PR12+PR14 composition and document required merge order. The exporter reliability change remains unique and must not be closed.\n\nMandatory repair: services/hermes/scripts/ai_usage_exporter.py is now 612 LOC and violates the repository 500-LOC source rule even though the legacy quality gate does not track that script. Extract meaningful bounded Codex process/query cleanup and/or polling/HTTP engine modules so every changed production file is <500 LOC; do not add exclusions or meaningless chunks. Preserve structured subscription-only quota collection, no API keys, sanitized class-name-only errors, bounded process reaping, provider isolation, last-good samples, poller-progress health, and disconnect handling. Add/retain >=95% per-file coverage and adversarial cleanup/thread/dead-health tests. Rebase current main, update config revision without clobbering newer rollout annotations, run exporter/dashboard tests, relevant full Hermes tests, Ruff/format/compile, actual make test/quality after composing PR14 as required, kustomize Hermes+monitoring, client and Flux-manager server dry-runs, diff/secret scans. Amend/force-with-lease only existing PR12 branch, verify PR #12 stays open/unmerged with exact head, stop for fresh independent review. No merge/build/publish/deploy/reconcile/live mutation.", "expected_role": "implementation", "id": "t_87cd9076", "title": "Rebase and split AI usage exporter PR 12"}
|
||||
{"board": "titan-iac", "body": "Repair existing draft PR #19 on its SAME feature/hermes-full-handoff-acceptance branch/worktree after independent review t_5ae177fc BLOCKED exact head c808baff40a12a90f43c2e5e39b94d139f88ea29. Do not create another PR.\n\nFix the mandatory P1 exactly: evaluate_names_absent must never PASS on rc=0 with zero observations. Return NOT_RUN/NO_GO on empty evidence and on pipeline/upstream/tool/catalog/jsonpath drift. Add all five real catalog regressions plus shell-pipeline and kubectl jsonpath empty-output repros.\n\nAlso close the review's reachable hardening/evidence defects: exact repo path boundary, reject dot segments, structurally forbid impersonation in all vantages unless a narrowly explicit audited inner self-probe requires it, correct the runbook format command so it passes, remove or implement inert concurrency/Telegram flags honestly, remove unattestable unused flux/helm entries or pin them, paginate/discover uncertain draft creation and surface/manual-clean exact residue, retain executable provenance for record-false steps, and do not add broad legacy LOC exceptions in this PR; coordinate with repaired PR14/15 canonical contract instead.\n\nPreserve all previously verified read-only/default safety, bounded subprocess/redaction, fail-closed parsing, binary attestation, armed cleanup, freshness/lineage, three-node/chat checks, at least 95 percent line and branch coverage and below 500 LOC. Re-run full canonical quality and every earlier adversarial/mutation test, Ruff format/check, compile, kustomize/dry-runs, exact current dependency graph, diff/secret scans. Amend/push only existing PR19 branch and stop for fresh independent review. No merge/publish/deploy/reconcile/live mutation/credential access.", "expected_role": "implementation", "id": "t_888cdeee", "title": "Repair PR 19 zero-evidence fail-open"}
|
||||
@ -64,14 +64,14 @@
|
||||
{"board": "titan-iac", "body": "Independent read-only xhigh review of the dependency, merge, rollout, and rollback plan for the full-handoff acceptance harness (from parent task t_6496c271), evaluated against actual current state rather than the plan's own claims.\n\nSETUP: resolve dynamically — the harness draft PR (number, head SHA, base SHA), and the real current status of each of PR12 through PR17 (open/draft/merged/closed, base branch, head SHA, merge conflicts, CI status). Also resolve the current distributed-pool state the rollout targets. Record what you observed with timestamps; do not rely on any status asserted in the PR description or prior task notes.\n\nSCOPE:\n1. Dependency correctness: verify the stated ordering among PR12-PR17 and the harness PR matches the actual code/config dependencies. Identify any dependency claimed but not real, or real but unclaimed. Flag any cycle or any PR that would break if merged in the stated order.\n2. Merge plan: check each PR's base branch and rebase state, detect textual and semantic conflicts between the harness PR and the others (especially shared config, schema, and entrypoint files), and assess whether the stated merge sequence is executable as written today.\n3. Rollout plan: evaluate staging against the current distributed-pool state — is the plan valid for the pool's actual size, versions, and heterogeneity? Check gating between stages, blast radius per stage, and whether the acceptance harness is a hard gate or merely advisory.\n4. Rollback plan: verify rollback is concrete and executable (exact revisions/images to revert to), covers partial-rollout states, and does not depend on the very component being rolled back. Identify any irreversible step, data/schema migration, or one-way door, and any state a rollback would strand.\n5. Freshness: confirm the plan's referenced revisions/images still exist and are current; stale references are findings.\n\nCONSTRAINTS: strictly read-only — no edits, comments, commits, pushes, merges, deploys, reconciles, or live mutations. Query state; do not change it.\n\nDELIVERABLE: an observed-state table for PR12-PR17 and the pool, then a blocking/non-blocking findings list with exact references (PR number, path:line, or resource identifier) and, for each blocker, the specific correction the plan needs. Explicitly state whether the plan is executable as written against today's state.", "expected_role": "implementation", "id": "t_8f526175", "title": "Review dependency, merge, rollout and rollback plan against PR12-PR17 state"}
|
||||
{"board": "titan-iac", "body": "Repair existing draft PR #18 on its SAME branch/worktree after independent review t_dbdcd739 returned BLOCK. Exact reviewed head: 20002527512235b29054f3636b02418168373009; current main dynamically resolve. Review evidence is durable under /opt/data/workspace/evidence/t_dbdcd739. Do not create a second PR.\\n\\nAll five P0 integration defects must close:\\n1. Coordinator.finalize compares integer current_run_id to string binding run_id, marks every result stale, then conflicting assignments wedge reconcile. Canonicalize at one boundary and use exact-run-safe DB APIs.\\n2. Existing task.workspace_path is normal for live tasks; do not capability-block every task. Define safe distributed workspace migration/ownership without shared mutable worktrees and preserve active/untracked ownership.\\n3. Accept Hermes-established safe branch conventions including wt/<task> and review/... in addition to feature/fix/etc, using actual Git ref validation and traversal denial.\\n4. Task has no repo_url/base_branch fields and current design sends every board to titan-iac. Resolve repo/base from the canonical project/board registry or explicit validated task metadata persisted by a reviewed schema migration; cover every Atlas project including Metis.\\n5. Exact merge with current PR15 must include/import every split cli_lane module and preserve its finalization semantics. No text-only compatibility claim.\\n\\nAlso close consequential handoff/security/reliability findings:\\n- Do not share one HMAC authority among all workers; derive/bind per-ordinal credentials or equivalent cryptographic isolation.\\n- Require node-role worker for execution Pods; avoid control-plane. Prefer healthy accelerator/rpi5 capacity but prove 3 distinct schedulable nodes and avoid titan-22/24/unhealthy nodes without starving Switchyard/Flux.\\n- The model container must not be able to call localhost sidecar APIs to bypass commit/untracked/result gates. Add a real mediation boundary (separate pod/network identity or unforgeable per-request capability), not instruction-only trust.\\n- Integrate the reviewed PR14 hermes-scm broker; never stage raw developer-gitea token into model-facing worker pods or bypass broker policy.\\n- Make subscription OAuth refresh ownership safe: model may use writable per-worker copies, but cannot write authoritative shared Vault credential state or the pool HMAC derivation input; prevent multi-writer refresh-token lost updates.\\n- Enforce lease expiry/reassignment with exact run fencing; surface worker exceptions to Kanban and release ordinals rather than spin; config changes must trigger controlled versioned rollout/negotiation.\\n- Remove dead/asymmetric egress, narrow credential-sync mounts, preserve tokenless SA/RBAC/network isolation.\\n- Add all new production modules to canonical lint/coverage contract, every source >=95% branch/line coverage and <500 LOC; split test baselines meaningfully if current main still needs it.\\n- Fix the env-sensitive Cassandra token test only if not already closed by PR14; do not hide failures.\\n- Run independent-style executable tests from review repros, focused/full quality, exact synthetic merges with current PR14/15/16/19, kustomize/kubeconform/client+Flux-manager server dry-runs, scheduling simulation, secret/diff scans.\\n\\nCommit/amend and force-with-lease the existing feature/hermes-distributed-worker-pool branch, update PR #18, report exact head and evidence. Human review required. No merge, image publish, Flux reconcile, deployment, live worker/node/account mutation, or credential access.", "expected_role": "implementation", "id": "t_8fbeb41f", "title": "Repair PR 18 distributed pool integration blockers"}
|
||||
{"board": "titan-iac", "body": "Independently execute and report the full quality gate for the full-handoff acceptance harness (from parent task t_6496c271). Do not accept any claimed numbers — regenerate every metric yourself.\n\nSETUP: resolve the draft PR, head SHA, and base SHA dynamically (locate the open/draft PR introducing the harness), check out the head SHA read-only, and record the exact changed-file list the gates apply to.\n\nSCOPE:\n1. LOC limit: measure every source and test file added or modified by the PR and verify each is under 500 LOC. Report the exact count per file, name any violations, and state your counting method (physical lines) explicitly.\n2. Per-file coverage: run the test suite with per-file coverage reporting and verify every source file in the PR reaches >= 95%. Report the actual per-file percentage table and every uncovered line range for files below threshold. Aggregate/total coverage is not acceptable evidence.\n3. Mutation testing: run mutation tests over the harness modules. Report the mutation score and enumerate surviving mutants with path:line — surviving mutants in verdict-computation, redaction, or read-only-enforcement code are blocking.\n4. Adversarial tests: run the adversarial/negative test suite. If gaps exist, describe the missing cases (do not commit new tests).\n5. Static and hygiene gates: run Ruff lint and Ruff format check, byte-compile all Python sources, produce the diff against the resolved base SHA, and run a secret scan over both the diff and the full tree. Report exact command lines and exit codes for each.\n\nCONSTRAINTS: keep the worktree pristine. You may run tests and read-only tooling in a sandbox, but make no edits, no formatting fixes, no comments, no commits, pushes, merges, deploys, reconciles, or live infrastructure calls. If a gate cannot run, report it as NOT_RUN with the reason — never infer a pass.\n\nDELIVERABLE: a table of gate -> exact command -> exit code -> result (PASS / FAIL / NOT_RUN), the full per-file LOC and coverage tables, the surviving-mutant list, and raw tool output for every failure. Flag each failure as blocking or non-blocking with a one-line justification.", "expected_role": "implementation", "id": "t_a585e217", "title": "Run quality gates: LOC limits, per-file coverage, mutation and adversarial tests"}
|
||||
{"board": "titan-iac", "body": "Implement the first production-safe distributed execution pool for agent.hermes in atlas/titan-iac. The purpose is to move all engineering work now done by local Codex/Claude into agent.hermes while spreading three concurrent workers across distinct cluster nodes.\n\nAuthoritative constraints:\n- Keep exactly one stateful coordinator owning Hermes Kanban/SQLite and the existing RWO hermes-agent-home. Never scale that Deployment above one and never mount its Kanban database or home PVC in a worker.\n- Add three execution workers that can be scheduled on distinct hostnames. Prefer the larger healthy ARM64 accelerator nodes, then healthy rpi5 fallback; use pod anti-affinity/topology spread and honest requests. Do not use titan-22 or titan-24. Preserve all current unhealthy-node exclusions.\n- Each worker needs its own durable RWO workspace/session volume (StatefulSet volumeClaimTemplates is acceptable) so provider sessions and Git state survive pod/node restart without cross-worker filesystem sharing.\n- The coordinator must remain the only component that claims/finalizes Kanban runs. Implement a bounded authenticated assignment/result/heartbeat channel that binds board, task_id, run_id, worker ordinal, attempt, and payload digest. Stale or replacement-run results must never complete/reclaim another run. Duplicate delivery/restart must be idempotent. Lost workers must be recoverable without double execution.\n- Workers clone/fetch only the assigned Atlas repo/feature branch through the reviewed SCM boundary; do not place a raw Gitea token in a model-facing container. Do not use a shared mutable Git worktree. Preserve unrelated/untracked files when a task explicitly owns them or fail closed with a clear blocker.\n- Use only Claude.ai subscription OAuth and ChatGPT/Codex subscription auth from Vault/runtime memory. No ANTHROPIC_API_KEY, OPENAI_API_KEY, Console/API-billed path, secret in Git/ConfigMap/log/command line. Credential refresh targets must be writable and restart-safe.\n- Give workers only the cluster/SCM capabilities required by their role. No cluster-admin, Secrets reads, service-account token creation, workload mutation, exec/attach/port-forward, or node-root access unless routed through a separately reviewed explicit operator boundary. automountServiceAccountToken must default false.\n- Preserve Switchyard automatic provider/model/effort routing and cross-provider fallback. Provider session continuity must remain attached to the exact task/run.\n- Stream sanitized worker activity, route, heartbeat, node/ordinal, and final evidence back into the existing Kanban log/session activity UI so the user can see continuous work at agent.hermes.bstein.dev.\n- Add bounded payloads, deadlines, concurrency, disk retention/GC, nofollow/atomic writes where applicable, and fail-closed startup/readiness checks.\n- Add executable adversarial tests for simultaneous claims, duplicate assignment/result, stale run, coordinator restart, worker restart, heartbeat loss, node replacement, oversized/malformed payloads, path traversal/symlinks, credential absence, cross-worker isolation, three-node scheduling contract, and visible activity.\n- Preserve compatibility with merged PR #13. Inspect open draft PRs #14/#15/#16/#17 and either avoid their surfaces or prove clean synthetic merge behavior; do not copy unreviewed assumptions from them.\n- Render services/hermes and every new Flux Kustomization, run client and Flux-equivalent server dry-runs, relevant full tests, Ruff/compile/shell/diff/secret scans. Keep source files below the repository quality limits.\n- Work only on a feature branch/worktree. Commit and push intentionally, then create one draft Gitea PR with exact validation evidence. Do not merge, publish an image, reconcile Flux, deploy, mutate live worker/node accounts, or claim live three-node proof before human review and rollout.\n\nReturn completed only when the draft PR exists at an exact pushed SHA and all implementable local acceptance checks are green. Findings belong in findings; blockers only for genuine obstacles preventing the task itself.", "expected_role": "implementation", "id": "t_af8d08d0", "title": "Implement a safe three-node agent.hermes execution pool"}
|
||||
{"board": "titan-iac", "body": "Implement the first production-safe distributed execution pool for agent.hermes in titan/atlas-iac. The purpose is to move all engineering work now done by local Codex/Claude into agent.hermes while spreading three concurrent workers across distinct cluster nodes.\n\nAuthoritative constraints:\n- Keep exactly one stateful coordinator owning Hermes Kanban/SQLite and the existing RWO hermes-agent-home. Never scale that Deployment above one and never mount its Kanban database or home PVC in a worker.\n- Add three execution workers that can be scheduled on distinct hostnames. Prefer the larger healthy ARM64 accelerator nodes, then healthy rpi5 fallback; use pod anti-affinity/topology spread and honest requests. Do not use titan-22 or titan-24. Preserve all current unhealthy-node exclusions.\n- Each worker needs its own durable RWO workspace/session volume (StatefulSet volumeClaimTemplates is acceptable) so provider sessions and Git state survive pod/node restart without cross-worker filesystem sharing.\n- The coordinator must remain the only component that claims/finalizes Kanban runs. Implement a bounded authenticated assignment/result/heartbeat channel that binds board, task_id, run_id, worker ordinal, attempt, and payload digest. Stale or replacement-run results must never complete/reclaim another run. Duplicate delivery/restart must be idempotent. Lost workers must be recoverable without double execution.\n- Workers clone/fetch only the assigned Atlas repo/feature branch through the reviewed SCM boundary; do not place a raw Gitea token in a model-facing container. Do not use a shared mutable Git worktree. Preserve unrelated/untracked files when a task explicitly owns them or fail closed with a clear blocker.\n- Use only Claude.ai subscription OAuth and ChatGPT/Codex subscription auth from Vault/runtime memory. No ANTHROPIC_API_KEY, OPENAI_API_KEY, Console/API-billed path, secret in Git/ConfigMap/log/command line. Credential refresh targets must be writable and restart-safe.\n- Give workers only the cluster/SCM capabilities required by their role. No cluster-admin, Secrets reads, service-account token creation, workload mutation, exec/attach/port-forward, or node-root access unless routed through a separately reviewed explicit operator boundary. automountServiceAccountToken must default false.\n- Preserve Switchyard automatic provider/model/effort routing and cross-provider fallback. Provider session continuity must remain attached to the exact task/run.\n- Stream sanitized worker activity, route, heartbeat, node/ordinal, and final evidence back into the existing Kanban log/session activity UI so the user can see continuous work at agent.hermes.bstein.dev.\n- Add bounded payloads, deadlines, concurrency, disk retention/GC, nofollow/atomic writes where applicable, and fail-closed startup/readiness checks.\n- Add executable adversarial tests for simultaneous claims, duplicate assignment/result, stale run, coordinator restart, worker restart, heartbeat loss, node replacement, oversized/malformed payloads, path traversal/symlinks, credential absence, cross-worker isolation, three-node scheduling contract, and visible activity.\n- Preserve compatibility with merged PR #13. Inspect open draft PRs #14/#15/#16/#17 and either avoid their surfaces or prove clean synthetic merge behavior; do not copy unreviewed assumptions from them.\n- Render services/hermes and every new Flux Kustomization, run client and Flux-equivalent server dry-runs, relevant full tests, Ruff/compile/shell/diff/secret scans. Keep source files below the repository quality limits.\n- Work only on a feature branch/worktree. Commit and push intentionally, then create one draft Gitea PR with exact validation evidence. Do not merge, publish an image, reconcile Flux, deploy, mutate live worker/node accounts, or claim live three-node proof before human review and rollout.\n\nReturn completed only when the draft PR exists at an exact pushed SHA and all implementable local acceptance checks are green. Findings belong in findings; blockers only for genuine obstacles preventing the task itself.", "expected_role": "implementation", "id": "t_af8d08d0", "title": "Implement a safe three-node agent.hermes execution pool"}
|
||||
{"board": "titan-iac", "body": "Fresh independent read-only review of the implementation produced by parent t_6da029e0. Dynamically resolve exact PR/head/base. Do not edit/comment/commit/push/merge/build/deploy/reconcile.\\n\\nReproduce the original reviewer-loop incident and verify completed review/audit/diagnostic verdicts finalize whether SHIP or BLOCK-with-findings, while malformed/incomplete reviews and unfinished implementation goals continue or fail correctly. Audit explicit role metadata, backward compatibility, restart/replay, provider fallback, session/turn duplication, PR15 semantic merge, bounded sanitized judge evidence, quality/coverage/LOC/render/dry-run. Return strict SHIP/BLOCK findings with exact evidence.", "expected_role": "review", "id": "t_b04a5e67", "title": "Independent review of goal-role completion semantics"}
|
||||
{"board": "titan-iac", "body": "Repair existing draft PR #22 on the SAME feature/hermes-review-goal-semantics branch/worktree after independent review t_b04a5e67 BLOCKED exact head b080b5f622ae0998213f3287762aea30dc931a73. Do not create another PR.\n\nClose every blocker: pass the resolved task role into unfinished_result_reason at both current and PR15 call sites so completed review BLOCK/SHIP cannot be short-circuited by implementation unfinished prose; parse explicit role/output directives from both real newlines and canonical literal backslash-n task bodies without allowing injection; make legacy inference correctly classify the real review/audit/diagnostic card corpus while conflicting or mutating cards fail closed; exclude all runner-appended goal rejection/controller history from card_scope; preserve and document deliberate single-shot semantics; prevent old accepted terminal journals from becoming invalid across upgrade or provide versioned compatibility; do not let changed-files/mutation evidence self-certify as review; use the canonical redactor extended for bare-hex Gitea PAT/basic-auth URL without reading real credentials; pin upstream context-heading contract. Add exact live-card/recovered t_dbdcd739/end-to-end execute_claim regressions. Preserve implementation goal judging, bounded safe reasons, PR15 compatibility, at least 95 percent line and branch coverage, below 500 LOC, full quality/render/dry-run/format/secret gates. Amend/push only existing PR22, no merge/deploy/reconcile.", "expected_role": "implementation", "id": "t_bc268843", "title": "Repair PR 22 review-role goal semantics"}
|
||||
{"board": "titan-iac", "body": "Provide an authenticated noninteractive Forgejo/Gitea API wrapper and concise worker guidance for scm.bstein.dev. It must use only runtime-injected credentials from the existing Vault/credential-broker path, never print tokens, never persist credentials in Git/workspaces/caches, restrict hosts and TLS verification, and make common read operations (PR head/base/state, comments/reviews/checks, branch metadata) straightforward. Mutating operations must be explicit and preserve the human-review default; no merge endpoint by default. Add tests proving URL/host allowlisting, redaction, missing-credential failure, and read-only behavior. Deliver through a focused PR and validate from an isolated Hermes worktree.", "expected_role": "implementation", "id": "t_bf2ff6ec", "title": "Give Hermes workers a first-class safe Gitea PR API client"}
|
||||
{"board": "titan-iac", "body": "Repair existing draft PR #18 on its SAME feature/hermes-distributed-worker-pool branch/worktree after independent review t_5975c06a BLOCKED exact head 689bcb6e48414854447988ba174143a307d308b9. Do not create another PR.\n\nP1 required: lease_failed must be safely retryable and must never poison the persistent SQLite store or CrashLoop the coordinator. Make state transition + Kanban recording recoverable/idempotent with exact-run fencing; catch bounded board errors; retry lease_failed; garbage-collect only after authoritative terminal evidence; ensure one poison row cannot abort later tasks/boards. Wrap startup maintenance as safely as steady state. Add the review's end-to-end wedge/conflicting-duplicate/restart/PVC-persistence repros and fault injection for DB/Kanban errors.\n\nClose consequential handoff risks too:\n- Prevent mediator/worker RWO deadlock during drain/pressure via a self-healing placement/lifecycle design and test eviction/reschedule.\n- Make retries submit safely without updating protected/existing refs: use a unique exact-run/attempt feature ref or a broker-authorized CAS update whose policy remains human-review safe. Preserve prior work and never silently discard results.\n- Avoid repeated slow global npm installs blocking 10m Flux health; use immutable image/persistent verified tooling or decouple health with bounded readiness.\n- Rebase/reconstruct PR18 so it does NOT contain open PR14/15/16/19 as ancestors. Keep PR18's own focused diff based on current main and validate disposable synthetic merges in the documented dependency order. Force-with-lease only the existing branch after exact remote lease check.\n\nPreserve all five fixed P0/security boundaries: canonical run IDs, workspace/project/branch routing, per-ordinal crypto, ConfigMap closure, no credentials/HMAC/SA token/RBAC/SCM in model container, broker-only SCM, safe OAuth ownership, three-node schedulability, >=95% line+branch, <500 LOC. Re-run every t_dbdcd739 and t_5975c06a artifact, full quality, kustomize/kubeconform/client/server dry-runs, scheduling/drain simulation, synthetic current PR merges, diff/secret scans. Amend/push only PR18 and stop for fresh human/independent review. No merge/build publish/deploy/reconcile/live mutation/credential access.", "expected_role": "implementation", "id": "t_c31dd427", "title": "Repair PR 18 lease recovery and release isolation"}
|
||||
{"board": "titan-iac", "body": "In the implementation worktree/branch, validate the completed narrow change directly rather than relying only on rendered manifests. Run all focused scheduler tests, the full relevant Hermes/Kanban regression suite, ruff/static checks, the repository-pinned Docker image build, kustomize build services/hermes, kubectl client dry-run, and git diff --check. Verify the built image contains and executes the patched scheduler behavior through the focused tests or equivalent direct image-level test path. If the tracked workflow supports authenticated internal-registry publication and publishing is necessary for PR deployability, publish only through that workflow, update the Flux manifest to the resulting immutable digest, and record it; otherwise leave a clear explicit blocker and do not invent credentials or use a public registry. Do not deploy, reconcile Flux, restart workloads, or alter live Kanban data. Return command-level pass/fail evidence, image/digest status, failures or residual risks, and any directly required corrections for the branch.", "expected_role": "implementation", "id": "t_c588a4b6", "title": "Run Hermes regression, build, and manifest validation"}
|
||||
{"board": "titan-iac", "body": "Harden agent.hermes after the live Cassandra/titan-iac proof run. Work from the current private atlas/titan-iac main head and create an isolated linked worktree/branch. This is a quality-sensitive platform change. Keep it narrowly scoped to Hermes reliability; preserve unrelated work. Open a Gitea pull request for human review, but do not merge, reconcile Flux, deploy, restart the agent, or mutate live Kanban data except this task's normal lifecycle. Never expose or commit credentials.\n\nAuthoritative live evidence to encode as regressions:\n1. Three simultaneous direct CLI workers on the 4-core hermes-agent node drove load to about 45, made the hermes and oauth2-proxy containers fail probes/restart, and temporarily left the pod 8/10 Ready. Two simultaneous workers remained stable at 10/10. Set HERMES_CLI_LANE_CONCURRENCY to 2 and cap the cli-lane-runner CPU limit at 2 so UI/auth have headroom. Verify requests remain sensible.\n2. Kubernetes service-link variables contaminate worker/test environments (for example a service named hermes-claude-broker creates names that collide with tests), while the deployment already uses DNS service names. Set enableServiceLinks: false at the hermes-agent pod-spec level and add a manifest regression proving it.\n3. Scheduler correctness defect: create_task(initial_status='blocked') records a created event with status blocked but no sticky blocked event. recompute_ready() considers blocked tasks and _has_sticky_block() only recognizes blocked/unblocked events, so an explicitly blocked task with no incomplete parent auto-promotes after the dispatcher cycle. Fix this in the existing Dockerfile patching/test mechanism without weakening dependency-driven promotion or circuit-breaker semantics. Add focused RED-before/GREEN-after tests covering: explicit initial blocked stays blocked with no parents and with already-complete parents; ordinary dependency-blocked tasks promote when dependencies complete; block_task/unblock_task remains correct; circuit-breaker blocks remain sticky. Use official public behavior where possible.\n\nInspect the existing hermes-agent image patches and tests before designing the smallest robust change. Run all focused tests, the full relevant Hermes/Kanban regression suite, ruff/static checks, Docker image build using the repository's pinned Dockerfile, kustomize build services/hermes, kubectl client dry-run, and git diff --check. If the image must be published to make the PR deployable, use only the existing authenticated internal registry workflow, update the digest in Flux manifests, and report the immutable digest; do not deploy it. If publishing is not supported by the tracked workflow, leave an explicit blocker rather than inventing credentials or using a public registry.\n\nReport exact changed files, RED/GREEN evidence, tests, image/digest status, PR URL/head/base state, residual risks, and confirm the PR is open/unmerged/undeployed. Treat a merely rendered manifest as insufficient: prove the scheduler behavior directly.", "expected_role": "implementation", "id": "t_cca008de", "title": "Harden Hermes worker isolation and blocked-task semantics"}
|
||||
{"board": "titan-iac", "body": "Inspect the current private atlas/titan-iac main-head manifests, pinned Dockerfile, image build/publish workflow, Flux image/digest conventions, and existing manifest tests without changing tracked files. Determine the precise hermes-agent pod-spec location for enableServiceLinks: false, the cli-lane-runner container location for CPU limit 2, and the configuration source for HERMES_CLI_LANE_CONCURRENCY=2. Identify how to add a regression that proves enableServiceLinks is false in the rendered/structured manifest and verify request settings remain appropriate. Document the supported authenticated internal-registry publication workflow, whether publishing is actually required for a deployable PR, and the exact validation commands for kustomize and kubectl client dry-run. Do not use credentials, publish images, reconcile Flux, deploy, restart workloads, or mutate live Kanban data.", "expected_role": "implementation", "id": "t_d06ecdaf", "title": "Audit Hermes workload isolation manifests and image workflow"}
|
||||
{"board": "titan-iac", "body": "Harden agent.hermes after the live Cassandra/titan-iac proof run. Work from the current private titan/atlas-iac main head and create an isolated linked worktree/branch. This is a quality-sensitive platform change. Keep it narrowly scoped to Hermes reliability; preserve unrelated work. Open a Gitea pull request for human review, but do not merge, reconcile Flux, deploy, restart the agent, or mutate live Kanban data except this task's normal lifecycle. Never expose or commit credentials.\n\nAuthoritative live evidence to encode as regressions:\n1. Three simultaneous direct CLI workers on the 4-core hermes-agent node drove load to about 45, made the hermes and oauth2-proxy containers fail probes/restart, and temporarily left the pod 8/10 Ready. Two simultaneous workers remained stable at 10/10. Set HERMES_CLI_LANE_CONCURRENCY to 2 and cap the cli-lane-runner CPU limit at 2 so UI/auth have headroom. Verify requests remain sensible.\n2. Kubernetes service-link variables contaminate worker/test environments (for example a service named hermes-claude-broker creates names that collide with tests), while the deployment already uses DNS service names. Set enableServiceLinks: false at the hermes-agent pod-spec level and add a manifest regression proving it.\n3. Scheduler correctness defect: create_task(initial_status='blocked') records a created event with status blocked but no sticky blocked event. recompute_ready() considers blocked tasks and _has_sticky_block() only recognizes blocked/unblocked events, so an explicitly blocked task with no incomplete parent auto-promotes after the dispatcher cycle. Fix this in the existing Dockerfile patching/test mechanism without weakening dependency-driven promotion or circuit-breaker semantics. Add focused RED-before/GREEN-after tests covering: explicit initial blocked stays blocked with no parents and with already-complete parents; ordinary dependency-blocked tasks promote when dependencies complete; block_task/unblock_task remains correct; circuit-breaker blocks remain sticky. Use official public behavior where possible.\n\nInspect the existing hermes-agent image patches and tests before designing the smallest robust change. Run all focused tests, the full relevant Hermes/Kanban regression suite, ruff/static checks, Docker image build using the repository's pinned Dockerfile, kustomize build services/hermes, kubectl client dry-run, and git diff --check. If the image must be published to make the PR deployable, use only the existing authenticated internal registry workflow, update the digest in Flux manifests, and report the immutable digest; do not deploy it. If publishing is not supported by the tracked workflow, leave an explicit blocker rather than inventing credentials or using a public registry.\n\nReport exact changed files, RED/GREEN evidence, tests, image/digest status, PR URL/head/base state, residual risks, and confirm the PR is open/unmerged/undeployed. Treat a merely rendered manifest as insufficient: prove the scheduler behavior directly.", "expected_role": "implementation", "id": "t_cca008de", "title": "Harden Hermes worker isolation and blocked-task semantics"}
|
||||
{"board": "titan-iac", "body": "Inspect the current private titan/atlas-iac main-head manifests, pinned Dockerfile, image build/publish workflow, Flux image/digest conventions, and existing manifest tests without changing tracked files. Determine the precise hermes-agent pod-spec location for enableServiceLinks: false, the cli-lane-runner container location for CPU limit 2, and the configuration source for HERMES_CLI_LANE_CONCURRENCY=2. Identify how to add a regression that proves enableServiceLinks is false in the rendered/structured manifest and verify request settings remain appropriate. Document the supported authenticated internal-registry publication workflow, whether publishing is actually required for a deployable PR, and the exact validation commands for kustomize and kubectl client dry-run. Do not use credentials, publish images, reconcile Flux, deploy, restart workloads, or mutate live Kanban data.", "expected_role": "implementation", "id": "t_d06ecdaf", "title": "Audit Hermes workload isolation manifests and image workflow"}
|
||||
{"board": "titan-iac", "body": "Perform a fresh independent read-only xhigh release/security review of the completed distributed agent.hermes worker-pool implementation produced by parent task t_af8d08d0. Read AGENTS.md and the parent result, but trust neither claimed tests nor done status. Dynamically resolve its exact pushed draft PR/head/base and review the complete diff against then-current origin/main including merged PR13 and pending PR14-PR16 compatibility. Do not edit files, mutate Kanban beyond returning result, comment, commit, push, merge, publish, deploy, reconcile, or touch live credentials.\n\nPrioritize: coordinator-only SQLite/Kanban/RWO ownership; exactly three workers on distinct eligible nodes with own RWO home/workspace; no shared mutable git; authenticated/bounded assignment, heartbeat, activity, result, retry and ack bound to board/task/run/worker ordinal/attempt/digest; replay/duplicate/stale/replacement-run/restart/partition safety; no lost accepted results; worker subscription OAuth refresh semantics without API keys; broker-only SCM and no raw Gitea token; scoped RBAC/no cluster-admin/secrets/token creation/impersonation/exec-attach-portforward/workload mutation; NetworkPolicy; SSH dedicated account compatibility; Switchyard classification/escalation; agent UI activity visibility; resource requests/anti-affinity/scheduling and RWO behavior on current nodes; rolling upgrade/rollback and coordinator/worker version skew. Run adversarial executable tests, synthetic merges with exact current PR14-PR16 heads where relevant, full focused quality gates, Kustomize/schema/client+Flux-manager server dry-runs, image/runtime static inspection, and read-only live capacity checks. Return strict SHIP or BLOCK with exact file/line and reproducible inputs. Report residual risks. Keep the review worktree pristine.", "expected_role": "review", "id": "t_dbdcd739", "title": "Independent xhigh review of distributed Hermes worker pool"}
|
||||
{"board": "titan-iac", "body": "Independently review the validated branch diff against the original reliability scope and the analysis findings. Confirm scheduler tests directly prove the required blocked-task semantics; confirm manifest configuration provides two CLI lanes, a 2-CPU cli-lane-runner cap, and pod-level enableServiceLinks: false; and reject unrelated changes, credentials, deployment actions, or weakened dependency/circuit-breaker semantics. Resolve only clearly in-scope review issues, rerun affected validation if anything changes, then open a Gitea pull request from the isolated branch to the appropriate current main base for human review. Do not merge, reconcile Flux, deploy, restart the agent, or mutate live Kanban data outside normal lifecycle. Report exact changed files, RED/GREEN and full validation evidence, image and immutable digest or publication blocker, PR URL/head/base, residual risks, and explicit confirmation that the PR is open, unmerged, and undeployed.", "expected_role": "implementation", "id": "t_e3ea56cc", "title": "Review final diff and open an unmerged Gitea PR"}
|
||||
{"board": "titan-iac", "body": "Perform a fresh independent read-only xhigh review of the full-handoff acceptance harness produced by parent t_6496c271. Resolve exact draft PR/head/base dynamically. Do not trust claimed coverage or go/no-go behavior. Keep worktree pristine; no edits/comments/commit/push/merge/deploy/reconcile or live mutations. Audit security and correctness of command construction/redaction before execution, environment/header/body/stdout/stderr/exception sanitization, bounded time/output/concurrency, JSON schema and deterministic overall PASS/NO_GO, mandatory NOT_RUN behavior, dual operator/in-pod evidence, revision/image freshness, partial/malformed/zero-state handling, and explicit ephemeral arming/cleanup/protected-branch rejection. Prove default mode is genuinely read-only through spies/static command inventory. Ensure it cannot read or print Vault/provider/SCM credentials and cannot falsely pass mocked/self-authored evidence. Verify every source/test file <500 LOC and >=95% per-file coverage, run mutation/adversarial tests, full relevant quality gate, Ruff/format/compile/diff/secret scans. Review dependency/merge/rollout/rollback plan against exact current PR12-PR17 and distributed-pool state. Return strict SHIP or BLOCK with executable repros/exact lines and residual risks.", "expected_role": "review", "id": "t_e7f46d44", "title": "Independent review of full-handoff acceptance harness"}
|
||||
|
||||
@ -25,7 +25,7 @@ _metadata_: `{"artifacts": ["/opt/data/workspace/projects/titan-iac/.worktrees/t
|
||||
## Recent work by @cli-claude-xhigh
|
||||
- t_5975c06a — Independent re-review of repaired PR 18 distributed pool (2026-08-17 14:19, 11m ago): Independent read-only xhigh re-review of draft PR #18 (feature/hermes-distributed-worker-pool) at exact head 689bcb6e48414854447988ba174143a307d308b9 against current remote main d8f2d818b9a552ea6c2d7f
|
||||
- t_5ae177fc — Independent re-review of repaired PR 19 acceptance harness (2026-08-17 13:52, 38m ago): Independent read-only xhigh re-review of draft PR #19 at exact head c808baff40a12a90f43c2e5e39b94d139f88ea29 against current remote main d8f2d818b9a552ea6c2d7fe86554be829bd5ffff (merge-base 30259b52,
|
||||
- t_261e208f — Independent review of cli-lane process reaping (2026-08-17 11:34, 2h ago): Independent read-only release review of atlas/titan-iac PR #20 (feature/hermes-cli-process-reaping) at head a242dcc786576ae1a18000cb4941836ff610dff2 vs base main d8f2d818b9a552ea6c2d7fe86554be829bd5ff
|
||||
- t_261e208f — Independent review of cli-lane process reaping (2026-08-17 11:34, 2h ago): Independent read-only release review of titan/atlas-iac PR #20 (feature/hermes-cli-process-reaping) at head a242dcc786576ae1a18000cb4941836ff610dff2 vs base main d8f2d818b9a552ea6c2d7fe86554be829bd5ff
|
||||
- t_6496c271 — Build fail-closed Hermes full-handoff acceptance harness (2026-08-17 10:17, 4h ago): Built a fail-closed, read-only-by-default Hermes full-handoff acceptance harness (71 checks, 15 new production modules under scripts/ops/, all <500 LOC, 100% per-file line coverage), a 12-module test
|
||||
- t_20f2fd16 — Repair Hermes PR 13 producer-drift regression coverage (2026-08-16 22:46, 15h ago): Amended PR #13 with commit 750dfa24 (pushed to wt/t_cca008de), fixing the single P1 review finding from t_06771c95 and applying the operator recovery addendum.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"status": "completed",
|
||||
"summary": "Independent read-only xhigh release/security review of PR #18 (atlas/titan-iac, head 20002527512235b29054f3636b02418168373009, base origin/main d8f2d818b9a552ea6c2d7fe86554be829bd5ffff which contains merged PR #13 ab346f55). Verdict: BLOCK. Five P0 defects are executably reproducible, four of which fire on the coordinator's first tick: (1) Coordinator.finalize() compares an int Kanban current_run_id to a str run_id so every accepted result is discarded while the worker is told accepted=True, and the resulting PK collision makes reconcile() throw every cycle; (2) the workspace_path gate force-blocks every live Kanban task including the two currently running ones; (3) the branch regex rejects the established wt/<task-id> convention; (4) Task has no repo_url so every board's tasks are pushed to titan-iac; (5) the pool ConfigMap does not ship the 14 modules PR #15 splits cli_lane_runner into, so coordinator and all three workers fail at import after a clean textual merge. P1 findings cover control-plane node placement, capacity/affinity steering onto the routing model and Flux controllers, model-container access to its own signing/SCM boundaries, worker poisoning of the shared Vault credential store, and bypass of PR #14's SCM authority boundary. Architecture that is sound (coordinator-only Kanban/SQLite, per-ordinal RWO PVCs, no shared mutable git, zero worker RBAC, worker-to-worker isolation, HMAC envelope binding) is called out explicitly. Two observed failures were confirmed pre-existing and unrelated to the PR. The review worktree was restored to review/hermes-distributed-worker-pool @ 0dd6ea0f and is clean including ignored paths.",
|
||||
"summary": "Independent read-only xhigh release/security review of PR #18 (titan/atlas-iac, head 20002527512235b29054f3636b02418168373009, base origin/main d8f2d818b9a552ea6c2d7fe86554be829bd5ffff which contains merged PR #13 ab346f55). Verdict: BLOCK. Five P0 defects are executably reproducible, four of which fire on the coordinator's first tick: (1) Coordinator.finalize() compares an int Kanban current_run_id to a str run_id so every accepted result is discarded while the worker is told accepted=True, and the resulting PK collision makes reconcile() throw every cycle; (2) the workspace_path gate force-blocks every live Kanban task including the two currently running ones; (3) the branch regex rejects the established wt/<task-id> convention; (4) Task has no repo_url so every board's tasks are pushed to titan-iac; (5) the pool ConfigMap does not ship the 14 modules PR #15 splits cli_lane_runner into, so coordinator and all three workers fail at import after a clean textual merge. P1 findings cover control-plane node placement, capacity/affinity steering onto the routing model and Flux controllers, model-container access to its own signing/SCM boundaries, worker poisoning of the shared Vault credential store, and bypass of PR #14's SCM authority boundary. Architecture that is sound (coordinator-only Kanban/SQLite, per-ordinal RWO PVCs, no shared mutable git, zero worker RBAC, worker-to-worker isolation, HMAC envelope binding) is called out explicitly. Two observed failures were confirmed pre-existing and unrelated to the PR. The review worktree was restored to review/hermes-distributed-worker-pool @ 0dd6ea0f and is clean including ignored paths.",
|
||||
"changed_files": [],
|
||||
"tests_run": [
|
||||
"Focused pool suite at PR head: pytest testing/tests/test_hermes_execution_pool.py test_hermes_execution_pool_assignment.py test_hermes_runtime_access.py test_hermes_auto_router.py -> 82 passed",
|
||||
@ -25,7 +25,7 @@
|
||||
"P0-1 BLOCKER - services/hermes/scripts/execution_pool_coordinator.py:248: finalize() evaluates `_task_value(task, \"current_run_id\") != binding[\"run_id\"]` without coercion. hermes_cli/kanban_db.py:853 and the schema at :1108 declare current_run_id as INTEGER, and live rows confirm typeof=integer (titan-iac t_dbdcd739 -> 23), while binding['run_id'] is a str produced by dispatch() at :386 and stored in a TEXT column. int 23 != str '23' is always True, so every result is marked stale: complete_task/block_task are never called, the Kanban task stays 'running' forever, and the worker receives accepted=True and writes terminal_at. Repro repro_finalize_stale.py output: ack {'accepted': True, 'duplicate': False}; complete_task []; block_task []; store state=stale had_result=True; free ordinals [0,1,2]. Follow-on: the row survives under PK (board,task_id,run_id) with a different payload digest, so reconcile() at :358 raises ProtocolError 'conflicting duplicate assignment' (execution_pool_protocol.py:307) on every 5s cycle, aborting the entire reconcile pass for all boards. Zero test coverage: the only tests touching Coordinator.finalize stub it (testing/tests/test_hermes_execution_pool_assignment.py:71 `pool.finalize = called.append`), and grep for current_run_id in both new test files returns nothing.",
|
||||
"P0-2 BLOCKER - services/hermes/scripts/execution_pool_coordinator.py:65-69: resolve_scm raises RuntimeError whenever task.workspace_path is non-empty; dispatch() (:396-406) and reconcile() (:347-356) convert that into kanban_db.block_task(kind='capability'). A read-only census of /opt/data/kanban/boards/*/kanban.db shows every task on titan-iac, cassandra and soteria carries workspace_path. On first coordinator tick this force-blocks the two currently running titan-iac tasks (t_6496c271 and t_dbdcd739, this review) and the two ready cli tasks (t_83252d81, t_87cd9076). Repro repro_workspace_block.py cases [1] and [2].",
|
||||
"P0-3 BLOCKER - services/hermes/scripts/execution_pool_coordinator.py:40,77: BRANCH = ^(?:feature|fix|chore|docs|test|refactor)/... rejects the branch names Hermes itself writes. hermes_cli/kanban_db.py:5447, :7275 and :7367 default worktree tasks to f'wt/{task.id}', and live rows carry wt/t_1e95ea6d as well as review/... names (t_dbdcd739 -> review/hermes-distributed-worker-pool). Repro repro_workspace_block.py case [3]: with workspace_path cleared the same task still blocks with 'task feature branch is outside the SCM branch policy'.",
|
||||
"P0-4 BLOCKER - services/hermes/scripts/execution_pool_coordinator.py:70,79-81: remote and base_branch come from _task_value(task, 'repo_url') / 'base_branch', but the hermes_cli.kanban_db.Task dataclass (kanban_db.py:838-916) defines neither field, so getattr always returns the DEFAULT_REPO https://scm.bstein.dev/atlas/titan-iac.git. Every task on every board is therefore cloned from and pushed to titan-iac. The metis scratch task t_8569b7b9 - the one live task that survives P0-2 - would run against a titan-iac checkout and push to feature/hermes-t_8569b7b9 there.",
|
||||
"P0-4 BLOCKER - services/hermes/scripts/execution_pool_coordinator.py:70,79-81: remote and base_branch come from _task_value(task, 'repo_url') / 'base_branch', but the hermes_cli.kanban_db.Task dataclass (kanban_db.py:838-916) defines neither field, so getattr always returns the DEFAULT_REPO https://scm.bstein.dev/titan/atlas-iac.git. Every task on every board is therefore cloned from and pushed to titan-iac. The metis scratch task t_8569b7b9 - the one live task that survives P0-2 - would run against a titan-iac checkout and push to feature/hermes-t_8569b7b9 there.",
|
||||
"P0-5 BLOCKER - services/hermes/kustomization.yaml:64-78 is runtime-incompatible with open PR #14... correction, PR #15 fix/hermes-result-decomposition-reliability head 7609cd77e87a64e332b2f0d670c1d7a4f62b3d9e, despite a clean textual merge. PR15 decomposes cli_lane_runner.py into 14 sibling modules (cli_lane_board, cli_lane_config, cli_lane_dispatch, cli_lane_evidence, cli_lane_execution, cli_lane_files, cli_lane_finalization, cli_lane_prompt, cli_lane_provider, cli_lane_quarantine, cli_lane_records, cli_lane_recovery, cli_lane_retention, cli_lane_routing), while the hermes-execution-pool ConfigMap ships only cli_lane_goal.py and cli_lane_runner.py. Proof repro_configmap_import.py: merge-main/pr14/pr16 import rc=0, merge-pr15 rc=1 ModuleNotFoundError: No module named 'cli_lane_board' for both execution_pool_coordinator and execution_pool_worker -> coordinator plus all three workers CrashLoopBackOff.",
|
||||
"P1 - services/hermes/execution-worker-statefulset.yaml:87-90: the second nodeSelectorTerm requires only arch=arm64, hardware=rpi5 and a hostname NotIn list, dropping the node-role.kubernetes.io/worker In ['true'] requirement that agent-deployment.yaml:109-111 uses. Measured against live capacity: 8 nodes satisfy the affinity, only 4 can fit the 1150m CPU / 2304Mi pod (titan-20 1270m free, titan-21 1520m, titan-0a 3730m, titan-0c 3630m), and titan-0a/titan-0c are k3s control-plane/master. podAntiAffinity forces 3 distinct hosts, so one worker necessarily lands on a control-plane node while running codex --dangerously-bypass-approvals-and-sandbox / claude --dangerously-skip-permissions (cli_lane_runner.py:594, :631).",
|
||||
"P1 - services/hermes/execution-worker-statefulset.yaml:91-99: preferredDuringScheduling weight 100 on node-role.kubernetes.io/accelerator steers two of three workers onto titan-20 (which runs ai/ollama, the Jetson routing model behind Switchyard AUTO) and titan-21 (which runs flux-system source-controller, image-automation-controller, image-reflector-controller and notification-controller). titan-20 retains only ~120m CPU slack after placement while the worker container's limits are 5 CPU / 6Gi, so the pool contends directly with the routing model and the GitOps control plane.",
|
||||
|
||||
@ -92,7 +92,7 @@ def test_pipeline_builds_one_exact_reviewed_router_revision() -> None:
|
||||
assert "--image-name-tag-with-digest-file=" in source
|
||||
for label in (
|
||||
"org.opencontainers.image.revision=${source_revision}",
|
||||
"org.opencontainers.image.source=https://scm.bstein.dev/atlas/titan-iac",
|
||||
"org.opencontainers.image.source=https://scm.bstein.dev/titan/atlas-iac",
|
||||
"org.opencontainers.image.title=hermes-chat-router",
|
||||
):
|
||||
assert label in source
|
||||
|
||||
@ -216,7 +216,7 @@ def test_the_two_single_shot_outcomes_that_change_are_the_documented_ones():
|
||||
("text", "secret"),
|
||||
[
|
||||
(
|
||||
f"cloned https://hermes:{SYNTHETIC_HEX}@scm.bstein.dev/atlas/x.git",
|
||||
f"cloned https://hermes:{SYNTHETIC_HEX}@scm.bstein.dev/titan/x.git",
|
||||
SYNTHETIC_HEX,
|
||||
),
|
||||
(f"exported GITEA_TOKEN={SYNTHETIC_HEX} into the lane", SYNTHETIC_HEX),
|
||||
|
||||
@ -247,7 +247,7 @@ def test_upstream_git_request_runs_under_one_wall_clock_budget(monkeypatch):
|
||||
return Response(b"result", content_type="application/x-git-upload-pack-result")
|
||||
|
||||
result = broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=b"request",
|
||||
content_type="application/x-git-upload-pack-request",
|
||||
@ -281,7 +281,7 @@ def test_upstream_git_request_cancels_guard_when_deadline_already_passed(monkeyp
|
||||
monkeypatch.setattr(broker.deadline_http, "StreamDeadline", ExpiredGuard)
|
||||
with pytest.raises(broker.PolicyError, match="deadline exceeded"):
|
||||
broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=b"request",
|
||||
content_type="application/x-git-upload-pack-request",
|
||||
|
||||
@ -44,7 +44,7 @@ def assignment_payload(**values):
|
||||
result = {
|
||||
"context": "Implement the bounded objective.",
|
||||
"assignee": "cli-auto",
|
||||
"repo_url": "https://scm.bstein.dev/atlas/titan-iac.git",
|
||||
"repo_url": "https://scm.bstein.dev/titan/atlas-iac.git",
|
||||
"branch": "feature/hermes-safe-pool",
|
||||
"base_branch": "main",
|
||||
"max_runtime_seconds": 3600,
|
||||
@ -281,7 +281,7 @@ def test_scm_workspace_is_ordinal_contained_and_rejects_symlink(tmp_path, monkey
|
||||
def test_scm_boundary_rejects_other_repo_branch_and_ordinal(monkeypatch):
|
||||
monkeypatch.setattr(scm, "ORDINAL", 0)
|
||||
for changes, message in (
|
||||
({"repo_url": "https://evil.example/atlas/titan-iac.git"}, "outside Atlas"),
|
||||
({"repo_url": "https://evil.example/titan/atlas-iac.git"}, "outside Atlas"),
|
||||
({"branch": "main"}, "reviewed namespace"),
|
||||
):
|
||||
signed = protocol.sign_envelope(
|
||||
|
||||
@ -30,13 +30,15 @@ def test_new_task_uses_explicit_reviewed_atlas_default(monkeypatch):
|
||||
project,
|
||||
"resolve_project",
|
||||
lambda board: (
|
||||
f"https://scm.bstein.dev/atlas/{board}.git", "main", Path("/unused")
|
||||
"https://scm.bstein.dev/titan/atlas-iac.git",
|
||||
"main",
|
||||
Path("/unused"),
|
||||
),
|
||||
)
|
||||
task = SimpleNamespace(id="t_deadbeef", workspace_path="", branch_name="")
|
||||
|
||||
assert project.resolve_assignment("titan-iac", task) == (
|
||||
"https://scm.bstein.dev/atlas/titan-iac.git",
|
||||
"https://scm.bstein.dev/titan/atlas-iac.git",
|
||||
"wt/t_deadbeef",
|
||||
"main",
|
||||
)
|
||||
@ -179,7 +181,7 @@ def test_assignment_rejects_unreviewed_task_branch(monkeypatch, branch):
|
||||
project,
|
||||
"resolve_project",
|
||||
lambda _board: (
|
||||
"https://scm.bstein.dev/atlas/titan-iac.git", "main", Path("/unused")
|
||||
"https://scm.bstein.dev/titan/atlas-iac.git", "main", Path("/unused")
|
||||
),
|
||||
)
|
||||
task = SimpleNamespace(
|
||||
@ -196,7 +198,7 @@ def test_registry_authority_ignores_unreviewed_task_repo_metadata(monkeypatch):
|
||||
project,
|
||||
"resolve_project",
|
||||
lambda _board: (
|
||||
"https://scm.bstein.dev/atlas/metis.git", "main", Path("/unused")
|
||||
"https://scm.bstein.dev/titan/metis.git", "main", Path("/unused")
|
||||
),
|
||||
)
|
||||
task = SimpleNamespace(
|
||||
@ -204,5 +206,5 @@ def test_registry_authority_ignores_unreviewed_task_repo_metadata(monkeypatch):
|
||||
repo_url="https://evil.example/atlas/other.git", base_branch="../main",
|
||||
)
|
||||
assert project.resolve_assignment("metis", task)[:2] == (
|
||||
"https://scm.bstein.dev/atlas/metis.git", "review/t_deadbeef"
|
||||
"https://scm.bstein.dev/titan/metis.git", "review/t_deadbeef"
|
||||
)
|
||||
|
||||
@ -44,7 +44,7 @@ def assignment_payload(**changes):
|
||||
value = {
|
||||
"context": "objective",
|
||||
"assignee": "cli-auto",
|
||||
"repo_url": "https://scm.bstein.dev/atlas/metis.git",
|
||||
"repo_url": "https://scm.bstein.dev/titan/metis.git",
|
||||
"branch": "wt/t_deadbeef",
|
||||
"base_branch": "main",
|
||||
"max_runtime_seconds": 3600,
|
||||
@ -144,7 +144,7 @@ def test_assignment_payload_is_bounded_registry_derived_and_typed(monkeypatch):
|
||||
coordinator,
|
||||
"resolve_assignment",
|
||||
lambda board, _task: (
|
||||
f"https://scm.bstein.dev/atlas/{board}.git", "wt/t_deadbeef", "main"
|
||||
f"https://scm.bstein.dev/titan/{board}.git", "wt/t_deadbeef", "main"
|
||||
),
|
||||
)
|
||||
kanban = SimpleNamespace(
|
||||
|
||||
@ -47,7 +47,7 @@ def binding(**changes):
|
||||
def payload(**changes):
|
||||
value = {
|
||||
"context": "safe objective",
|
||||
"repo_url": "https://scm.bstein.dev/atlas/metis.git",
|
||||
"repo_url": "https://scm.bstein.dev/titan/metis.git",
|
||||
"branch": "wt/t_deadbeef",
|
||||
"base_branch": "main",
|
||||
}
|
||||
@ -84,7 +84,7 @@ class FakeSCM:
|
||||
|
||||
def submit(self, envelope, request):
|
||||
self.submitted.append((envelope, request))
|
||||
return {"pull_request": "https://scm.bstein.dev/atlas/metis/pulls/7"}
|
||||
return {"pull_request": "https://scm.bstein.dev/titan/metis/pulls/7"}
|
||||
|
||||
|
||||
def test_result_schema_validation_rejects_every_unsafe_shape():
|
||||
@ -169,7 +169,7 @@ def test_heartbeat_and_finish_require_exact_current_binding(monkeypatch):
|
||||
assert finished["ack"]["accepted"] and boundary.current is None
|
||||
assert fake_scm.submitted
|
||||
assert finished["structured"]["artifacts"] == [
|
||||
"https://scm.bstein.dev/atlas/metis/pulls/7"
|
||||
"https://scm.bstein.dev/titan/metis/pulls/7"
|
||||
]
|
||||
|
||||
boundary.current = current
|
||||
@ -271,7 +271,7 @@ def test_client_main_validates_ordinal_and_starts_bounded_server(monkeypatch):
|
||||
def init_checkout(path, branch="wt/t_deadbeef"):
|
||||
subprocess.run(["git", "init", "-q", str(path)], check=True)
|
||||
commands = [
|
||||
("remote", "add", "origin", "https://scm.bstein.dev/atlas/metis.git"),
|
||||
("remote", "add", "origin", "https://scm.bstein.dev/titan/metis.git"),
|
||||
(
|
||||
"remote",
|
||||
"add",
|
||||
|
||||
@ -32,7 +32,8 @@ def registry(tmp_path, monkeypatch, board="metis", remote=None, base=None):
|
||||
git("init", "-q", cwd=checkout)
|
||||
git(
|
||||
"remote", "add", "origin",
|
||||
remote or f"https://scm.bstein.dev/atlas/{board}.git",
|
||||
remote
|
||||
or f"https://scm.bstein.dev/titan/{project.REPOSITORY_ALIASES.get(board, board)}.git",
|
||||
cwd=checkout,
|
||||
)
|
||||
if base:
|
||||
@ -74,11 +75,11 @@ def test_ref_validation_denies_traversal_and_unreviewed_namespaces(branch):
|
||||
def test_canonical_registry_resolves_repo_base_and_default_branch(tmp_path, monkeypatch):
|
||||
checkout, _ = registry(tmp_path, monkeypatch, base="trunk")
|
||||
assert project.resolve_project("metis") == (
|
||||
"https://scm.bstein.dev/atlas/metis.git", "trunk", checkout.resolve()
|
||||
"https://scm.bstein.dev/titan/metis.git", "trunk", checkout.resolve()
|
||||
)
|
||||
task = SimpleNamespace(id="t_deadbeef", branch_name="")
|
||||
assert project.resolve_assignment("metis", task) == (
|
||||
"https://scm.bstein.dev/atlas/metis.git", "wt/t_deadbeef", "trunk"
|
||||
"https://scm.bstein.dev/titan/metis.git", "wt/t_deadbeef", "trunk"
|
||||
)
|
||||
|
||||
|
||||
@ -86,7 +87,8 @@ def test_canonical_registry_resolves_repo_base_and_default_branch(tmp_path, monk
|
||||
def test_every_atlas_project_uses_its_own_registry_checkout(tmp_path, monkeypatch, board):
|
||||
registry(tmp_path, monkeypatch, board=board)
|
||||
repo, base, _ = project.resolve_project(board)
|
||||
assert repo == f"https://scm.bstein.dev/atlas/{board}.git"
|
||||
repository = project.REPOSITORY_ALIASES.get(board, board)
|
||||
assert repo == f"https://scm.bstein.dev/titan/{repository}.git"
|
||||
assert base == "main"
|
||||
|
||||
|
||||
@ -96,7 +98,7 @@ def test_registered_project_without_checkout_keeps_its_own_repo_identity(
|
||||
checkout, _ = registry(tmp_path, monkeypatch, board="metis")
|
||||
shutil.rmtree(checkout)
|
||||
assert project.resolve_project("metis") == (
|
||||
"https://scm.bstein.dev/atlas/metis.git",
|
||||
"https://scm.bstein.dev/titan/metis.git",
|
||||
"main",
|
||||
checkout.resolve(),
|
||||
)
|
||||
@ -123,7 +125,7 @@ def test_noncanonical_remote_head_falls_back_to_main(tmp_path, monkeypatch):
|
||||
|
||||
monkeypatch.setattr(project, "_run_git", run_git)
|
||||
assert project.resolve_project("metis") == (
|
||||
"https://scm.bstein.dev/atlas/metis.git",
|
||||
"https://scm.bstein.dev/titan/metis.git",
|
||||
"main",
|
||||
checkout.resolve(),
|
||||
)
|
||||
@ -162,9 +164,9 @@ def test_registry_rejects_oversized_archived_and_non_regular_entry(tmp_path, mon
|
||||
@pytest.mark.parametrize(
|
||||
"remote",
|
||||
[
|
||||
"https://token@scm.bstein.dev/atlas/metis.git",
|
||||
"https://token@scm.bstein.dev/titan/metis.git",
|
||||
"https://evil.example/atlas/metis.git",
|
||||
"ssh://git@scm.bstein.dev/atlas/metis.git",
|
||||
"ssh://git@scm.bstein.dev/titan/metis.git",
|
||||
],
|
||||
)
|
||||
def test_registry_rejects_credentialed_or_non_atlas_origin(tmp_path, monkeypatch, remote):
|
||||
|
||||
@ -62,14 +62,14 @@ def test_scm_identity_is_data_only_and_supports_packed_refs(tmp_path):
|
||||
subprocess.run(
|
||||
[
|
||||
"git", "-C", str(checkout), "remote", "add", "origin",
|
||||
"https://scm.bstein.dev/atlas/titan-iac.git",
|
||||
"https://scm.bstein.dev/titan/atlas-iac.git",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
"git", "-C", str(checkout), "remote", "add", "hermes-broker",
|
||||
scm._broker_repo("titan-iac"),
|
||||
scm._broker_repo("atlas-iac"),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
@ -101,7 +101,7 @@ def test_scm_identity_is_data_only_and_supports_packed_refs(tmp_path):
|
||||
)
|
||||
assert scm._workspace_identity(
|
||||
checkout,
|
||||
"titan-iac",
|
||||
"atlas-iac",
|
||||
"feature/hermes-safe-pool",
|
||||
) == expected
|
||||
(checkout / ".git/refs/heads/feature").mkdir(parents=True)
|
||||
@ -158,7 +158,7 @@ def test_scm_paths_and_identity_reject_symlink_and_git_tampering(
|
||||
subprocess.run(
|
||||
[
|
||||
"git", "-C", str(checkout), "remote", "set-url", "origin",
|
||||
"https://scm.bstein.dev/atlas/metis.git",
|
||||
"https://scm.bstein.dev/titan/metis.git",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
@ -190,7 +190,7 @@ def test_scm_paths_and_identity_reject_symlink_and_git_tampering(
|
||||
"http://hermes-scm-broker.hermes-scm.svc.cluster.local:9081/"
|
||||
"git/atlas/metis.git"
|
||||
if arguments[-1] == "hermes-broker"
|
||||
else "https://scm.bstein.dev/atlas/metis.git"
|
||||
else "https://scm.bstein.dev/titan/metis.git"
|
||||
),
|
||||
"symbolic-ref": "wt/t_deadbeef",
|
||||
"rev-parse": "invalid",
|
||||
|
||||
@ -60,18 +60,18 @@ def test_api_target_and_request_body_edge_paths(monkeypatch):
|
||||
module._split_api_path("/api/v1/" + "x" * 510)
|
||||
with pytest.raises(module.PolicyError, match="request body"):
|
||||
module.authorize_request(
|
||||
"GET", "/api/v1/repos/atlas/cassandra", {"unexpected": True}
|
||||
"GET", "/api/v1/repos/titan/cassandra", {"unexpected": True}
|
||||
)
|
||||
with pytest.raises(module.PolicyError, match="query"):
|
||||
module.authorize_request(
|
||||
"POST",
|
||||
"/api/v1/repos/atlas/cassandra/pulls?page=1",
|
||||
"/api/v1/repos/titan/cassandra/pulls?page=1",
|
||||
{},
|
||||
)
|
||||
with pytest.raises(module.PolicyError, match="only read"):
|
||||
module.authorize_request("TRACE", "/api/v1/repos/atlas/cassandra/pulls", None)
|
||||
module.authorize_request("TRACE", "/api/v1/repos/titan/cassandra/pulls", None)
|
||||
with pytest.raises(module.PolicyError, match="accepts only"):
|
||||
module.authorize_request("POST", "/api/v1/repos/atlas/cassandra/pulls", [])
|
||||
module.authorize_request("POST", "/api/v1/repos/titan/cassandra/pulls", [])
|
||||
|
||||
|
||||
def test_response_status_fallback_and_nested_fail_closed():
|
||||
@ -87,7 +87,7 @@ def test_response_status_fallback_and_nested_fail_closed():
|
||||
|
||||
assert (
|
||||
module.read(
|
||||
"/api/v1/repos/atlas/cassandra",
|
||||
"/api/v1/repos/titan/cassandra",
|
||||
token="synthetic",
|
||||
opener=lambda *_a, **_k: GetCodeResponse(),
|
||||
)
|
||||
@ -140,8 +140,8 @@ def test_main_executes_broker_read_and_create_paths(monkeypatch):
|
||||
monkeypatch.setitem(sys.modules, "scm_broker_client", client)
|
||||
monkeypatch.setattr(module, "_write_body", outputs.append)
|
||||
|
||||
assert module.main(["read", "/api/v1/repos/atlas/cassandra"]) == 0
|
||||
assert json.loads(outputs.pop()) == {"path": "/api/v1/repos/atlas/cassandra"}
|
||||
assert module.main(["read", "/api/v1/repos/titan/cassandra"]) == 0
|
||||
assert json.loads(outputs.pop()) == {"path": "/api/v1/repos/titan/cassandra"}
|
||||
assert (
|
||||
module.main(
|
||||
[
|
||||
@ -175,7 +175,7 @@ def test_main_handles_http_and_policy_failures(monkeypatch, capsys):
|
||||
client.read = http_failure
|
||||
client.create_draft = lambda *_a, **_k: b"{}"
|
||||
monkeypatch.setitem(sys.modules, "scm_broker_client", client)
|
||||
assert module.main(["read", "/api/v1/repos/atlas/cassandra"]) == 1
|
||||
assert module.main(["read", "/api/v1/repos/titan/cassandra"]) == 1
|
||||
assert "HTTP 503" in capsys.readouterr().err
|
||||
client.read = lambda _path: (_ for _ in ()).throw(module.PolicyError("rejected"))
|
||||
assert module.main(["read", "/not-allowed"]) == 1
|
||||
@ -362,7 +362,7 @@ def test_high_entropy_detector_covers_long_mixed_and_low_entropy_values():
|
||||
)
|
||||
def test_query_validator_covers_each_rejection_class(query):
|
||||
module = _policy("gitea_policy_query")
|
||||
target = urllib.parse.urlsplit("/api/v1/repos/atlas/cassandra/pulls?" + query)
|
||||
target = urllib.parse.urlsplit("/api/v1/repos/titan/cassandra/pulls?" + query)
|
||||
with pytest.raises(module.PolicyError):
|
||||
module._validate_query(target, {"page", "limit", "state"})
|
||||
|
||||
@ -375,6 +375,6 @@ def test_draft_title_rejects_prefix_without_content():
|
||||
|
||||
def test_query_validator_rejects_noncanonical_raw_form():
|
||||
module = _policy("gitea_policy_raw_query")
|
||||
target = urllib.parse.SplitResult("", "", "/api/v1/repos/atlas/cassandra", "%", "")
|
||||
target = urllib.parse.SplitResult("", "", "/api/v1/repos/titan/cassandra", "%", "")
|
||||
with pytest.raises(module.PolicyError, match="canonical ASCII"):
|
||||
module._validate_query(target, set())
|
||||
|
||||
@ -12,7 +12,7 @@ from testing.tests.test_hermes_gitea_support import HEAD_SHA, Response, _load
|
||||
def test_default_opener_rejects_redirects_under_an_absolute_deadline(monkeypatch):
|
||||
client = _load()
|
||||
source = urllib.request.Request(
|
||||
"https://scm.bstein.dev/api/v1/repos/atlas/cassandra",
|
||||
"https://scm.bstein.dev/api/v1/repos/titan/cassandra",
|
||||
headers={"Authorization": "token redirect-sentinel"},
|
||||
)
|
||||
|
||||
@ -42,17 +42,17 @@ def test_default_opener_rejects_redirects_under_an_absolute_deadline(monkeypatch
|
||||
@pytest.mark.parametrize(
|
||||
("base_url", "path"),
|
||||
[
|
||||
("https://evil.example", "/api/v1/repos/atlas/cassandra"),
|
||||
("http://scm.bstein.dev", "/api/v1/repos/atlas/cassandra"),
|
||||
("https://scm.bstein.dev:443", "/api/v1/repos/atlas/cassandra"),
|
||||
("https://scm.bstein.dev/", "/api/v1/repos/atlas/cassandra"),
|
||||
("HTTPS://scm.bstein.dev", "/api/v1/repos/atlas/cassandra"),
|
||||
("https://SCM.bstein.dev", "/api/v1/repos/atlas/cassandra"),
|
||||
("https://user@scm.bstein.dev", "/api/v1/repos/atlas/cassandra"),
|
||||
("https://scm.bstein.dev", "https://evil.example/api/v1/repos/atlas/cassandra"),
|
||||
("https://evil.example", "/api/v1/repos/titan/cassandra"),
|
||||
("http://scm.bstein.dev", "/api/v1/repos/titan/cassandra"),
|
||||
("https://scm.bstein.dev:443", "/api/v1/repos/titan/cassandra"),
|
||||
("https://scm.bstein.dev/", "/api/v1/repos/titan/cassandra"),
|
||||
("HTTPS://scm.bstein.dev", "/api/v1/repos/titan/cassandra"),
|
||||
("https://SCM.bstein.dev", "/api/v1/repos/titan/cassandra"),
|
||||
("https://user@scm.bstein.dev", "/api/v1/repos/titan/cassandra"),
|
||||
("https://scm.bstein.dev", "https://evil.example/api/v1/repos/titan/cassandra"),
|
||||
("https://scm.bstein.dev", "/api/v1/repos/evil/cassandra"),
|
||||
("https://scm.bstein.dev", "/api/v1/repos/%61tlas/cassandra"),
|
||||
("https://scm.bstein.dev", "/api/v1/repos/atlas/../admin"),
|
||||
("https://scm.bstein.dev", "/api/v1/repos/titan/../admin"),
|
||||
],
|
||||
)
|
||||
def test_host_owner_and_path_escape_attempts_are_rejected(base_url: str, path: str):
|
||||
@ -65,17 +65,17 @@ def test_host_owner_and_path_escape_attempts_are_rejected(base_url: str, path: s
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1\nHost: evil.example",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1\r\nX-Test: value",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1\tignored",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1\x00ignored",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1\x1fignored",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1\x7fignored",
|
||||
"/api/v1/repos/atlas/cassandra\\pulls\\1",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/%31",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1",
|
||||
" https://scm.bstein.dev/api/v1/repos/atlas/cassandra",
|
||||
"https://scm.bstein.dev/api/v1/repos/atlas/cassandra",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1\nHost: evil.example",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1\r\nX-Test: value",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1\tignored",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1\x00ignored",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1\x1fignored",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1\x7fignored",
|
||||
"/api/v1/repos/titan/cassandra\\pulls\\1",
|
||||
"/api/v1/repos/titan/cassandra/pulls/%31",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1",
|
||||
" https://scm.bstein.dev/api/v1/repos/titan/cassandra",
|
||||
"https://scm.bstein.dev/api/v1/repos/titan/cassandra",
|
||||
],
|
||||
)
|
||||
def test_raw_noncanonical_target_is_rejected_before_urlsplit_and_opener(
|
||||
@ -106,12 +106,12 @@ def test_raw_noncanonical_target_is_rejected_before_urlsplit_and_opener(
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1?",
|
||||
"//scm.bstein.dev/api/v1/repos/atlas/cassandra",
|
||||
"/api/v1/repos/atlas/cassandra/./pulls/1",
|
||||
"/api/v1/repos/atlas/cassandra/../admin",
|
||||
"/api/v1/repos/atlas/cassandra//pulls/1",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1?limit=01",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1?",
|
||||
"//scm.bstein.dev/api/v1/repos/titan/cassandra",
|
||||
"/api/v1/repos/titan/cassandra/./pulls/1",
|
||||
"/api/v1/repos/titan/cassandra/../admin",
|
||||
"/api/v1/repos/titan/cassandra//pulls/1",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1?limit=01",
|
||||
],
|
||||
)
|
||||
def test_noncanonical_round_trip_or_segments_never_reach_opener(path: str):
|
||||
@ -132,18 +132,18 @@ def test_noncanonical_round_trip_or_segments_never_reach_opener(path: str):
|
||||
"path",
|
||||
[
|
||||
"/api/v1/user",
|
||||
"/api/v1/repos/atlas/cassandra",
|
||||
"/api/v1/repos/atlas/cassandra/pulls?state=open&limit=20&page=1",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/7",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/7/commits?limit=20",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/7/files?page=1",
|
||||
"/api/v1/repos/atlas/cassandra/branches",
|
||||
"/api/v1/repos/atlas/cassandra/branches/main",
|
||||
"/api/v1/repos/atlas/cassandra/commits?limit=10",
|
||||
f"/api/v1/repos/atlas/cassandra/git/commits/{HEAD_SHA}",
|
||||
f"/api/v1/repos/atlas/cassandra/commits/{HEAD_SHA}/status",
|
||||
f"/api/v1/repos/atlas/cassandra/commits/{HEAD_SHA}/statuses?limit=10",
|
||||
f"/api/v1/repos/atlas/cassandra/statuses/{HEAD_SHA}?page=1",
|
||||
"/api/v1/repos/titan/cassandra",
|
||||
"/api/v1/repos/titan/cassandra/pulls?state=open&limit=20&page=1",
|
||||
"/api/v1/repos/titan/cassandra/pulls/7",
|
||||
"/api/v1/repos/titan/cassandra/pulls/7/commits?limit=20",
|
||||
"/api/v1/repos/titan/cassandra/pulls/7/files?page=1",
|
||||
"/api/v1/repos/titan/cassandra/branches",
|
||||
"/api/v1/repos/titan/cassandra/branches/main",
|
||||
"/api/v1/repos/titan/cassandra/commits?limit=10",
|
||||
f"/api/v1/repos/titan/cassandra/git/commits/{HEAD_SHA}",
|
||||
f"/api/v1/repos/titan/cassandra/commits/{HEAD_SHA}/status",
|
||||
f"/api/v1/repos/titan/cassandra/commits/{HEAD_SHA}/statuses?limit=10",
|
||||
f"/api/v1/repos/titan/cassandra/statuses/{HEAD_SHA}?page=1",
|
||||
],
|
||||
)
|
||||
def test_explicit_read_allowlist_accepts_only_engineering_metadata(path: str):
|
||||
@ -178,16 +178,16 @@ def test_the_identity_route_reads_only_the_bare_user_document():
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/api/v1/repos/atlas/cassandra/hooks",
|
||||
"/api/v1/repos/atlas/cassandra/actions/secrets",
|
||||
"/api/v1/repos/atlas/cassandra/actions/variables",
|
||||
"/api/v1/repos/atlas/cassandra/collaborators",
|
||||
"/api/v1/repos/atlas/cassandra/branch_protections",
|
||||
"/api/v1/repos/atlas/cassandra/keys",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/7/reviews",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/7/merge",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/7.diff",
|
||||
"/api/v1/repos/atlas/cassandra/releases",
|
||||
"/api/v1/repos/titan/cassandra/hooks",
|
||||
"/api/v1/repos/titan/cassandra/actions/secrets",
|
||||
"/api/v1/repos/titan/cassandra/actions/variables",
|
||||
"/api/v1/repos/titan/cassandra/collaborators",
|
||||
"/api/v1/repos/titan/cassandra/branch_protections",
|
||||
"/api/v1/repos/titan/cassandra/keys",
|
||||
"/api/v1/repos/titan/cassandra/pulls/7/reviews",
|
||||
"/api/v1/repos/titan/cassandra/pulls/7/merge",
|
||||
"/api/v1/repos/titan/cassandra/pulls/7.diff",
|
||||
"/api/v1/repos/titan/cassandra/releases",
|
||||
],
|
||||
)
|
||||
def test_privileged_or_content_routes_are_denied_even_for_get(path: str):
|
||||
@ -200,11 +200,11 @@ def test_privileged_or_content_routes_are_denied_even_for_get(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/api/v1/repos/atlas/cassandra/pulls?limit=51",
|
||||
"/api/v1/repos/atlas/cassandra/pulls?state=merged",
|
||||
"/api/v1/repos/atlas/cassandra/pulls?private=true",
|
||||
"/api/v1/repos/atlas/cassandra/pulls?limit=1&limit=2",
|
||||
"/api/v1/repos/atlas/cassandra?p=1",
|
||||
"/api/v1/repos/titan/cassandra/pulls?limit=51",
|
||||
"/api/v1/repos/titan/cassandra/pulls?state=merged",
|
||||
"/api/v1/repos/titan/cassandra/pulls?private=true",
|
||||
"/api/v1/repos/titan/cassandra/pulls?limit=1&limit=2",
|
||||
"/api/v1/repos/titan/cassandra?p=1",
|
||||
],
|
||||
)
|
||||
def test_read_query_is_bounded(path: str):
|
||||
@ -239,7 +239,7 @@ def test_read_query_requires_canonical_bounded_ascii(query: str):
|
||||
|
||||
with pytest.raises(client.PolicyError):
|
||||
client.authorize_request(
|
||||
"GET", f"/api/v1/repos/atlas/cassandra/pulls?{query}", None
|
||||
"GET", f"/api/v1/repos/titan/cassandra/pulls?{query}", None
|
||||
)
|
||||
|
||||
|
||||
@ -266,7 +266,7 @@ def test_oversized_numeric_or_captured_path_never_reaches_opener(suffix: str):
|
||||
called = True
|
||||
return Response(b"{}")
|
||||
|
||||
path = "/api/v1/repos/atlas/cassandra/" + suffix.format(number="9" * 4000)
|
||||
path = "/api/v1/repos/titan/cassandra/" + suffix.format(number="9" * 4000)
|
||||
with pytest.raises(client.PolicyError):
|
||||
client.read(path, token="runtime", opener=opener)
|
||||
assert called is False
|
||||
@ -290,7 +290,7 @@ def test_noncanonical_or_out_of_range_pr_number_never_reaches_opener(
|
||||
|
||||
with pytest.raises(client.PolicyError):
|
||||
client.read(
|
||||
f"/api/v1/repos/atlas/cassandra/pulls/{number}{tail}",
|
||||
f"/api/v1/repos/titan/cassandra/pulls/{number}{tail}",
|
||||
token="runtime",
|
||||
opener=opener,
|
||||
)
|
||||
@ -308,7 +308,7 @@ def test_maximum_bounded_pr_number_is_readable():
|
||||
|
||||
assert (
|
||||
client.read(
|
||||
"/api/v1/repos/atlas/cassandra/pulls/2147483647",
|
||||
"/api/v1/repos/titan/cassandra/pulls/2147483647",
|
||||
token="runtime",
|
||||
opener=opener,
|
||||
)
|
||||
|
||||
@ -16,15 +16,15 @@ from testing.tests.test_hermes_gitea_support import (
|
||||
@pytest.mark.parametrize(
|
||||
("method", "path", "data"),
|
||||
[
|
||||
("DELETE", "/api/v1/repos/atlas/cassandra/pulls/4", None),
|
||||
("POST", "/api/v1/repos/atlas/cassandra/pulls/4/merge", {}),
|
||||
("DELETE", "/api/v1/repos/titan/cassandra/pulls/4", None),
|
||||
("POST", "/api/v1/repos/titan/cassandra/pulls/4/merge", {}),
|
||||
(
|
||||
"POST",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/4/reviews",
|
||||
"/api/v1/repos/titan/cassandra/pulls/4/reviews",
|
||||
{"event": "APPROVED"},
|
||||
),
|
||||
("PATCH", "/api/v1/repos/atlas/cassandra/pulls/4", {"title": "WIP: x"}),
|
||||
("PUT", "/api/v1/repos/atlas/cassandra/branches/main", {}),
|
||||
("PATCH", "/api/v1/repos/titan/cassandra/pulls/4", {"title": "WIP: x"}),
|
||||
("PUT", "/api/v1/repos/titan/cassandra/branches/main", {}),
|
||||
],
|
||||
)
|
||||
def test_merge_approve_close_delete_update_and_other_mutations_are_rejected(
|
||||
@ -170,20 +170,20 @@ def test_create_forces_draft_title_and_same_repository_branch_names():
|
||||
client = _load()
|
||||
assert (
|
||||
client.authorize_request(
|
||||
"POST", "/api/v1/repos/atlas/cassandra/pulls", _draft_payload()
|
||||
"POST", "/api/v1/repos/titan/cassandra/pulls", _draft_payload()
|
||||
)
|
||||
== "create-draft"
|
||||
)
|
||||
with pytest.raises(client.PolicyError, match="draft-title prefix"):
|
||||
client.authorize_request(
|
||||
"POST",
|
||||
"/api/v1/repos/atlas/cassandra/pulls",
|
||||
"/api/v1/repos/titan/cassandra/pulls",
|
||||
_draft_payload(title="Not a draft"),
|
||||
)
|
||||
with pytest.raises(client.PolicyError):
|
||||
client.authorize_request(
|
||||
"POST",
|
||||
"/api/v1/repos/atlas/cassandra/pulls",
|
||||
"/api/v1/repos/titan/cassandra/pulls",
|
||||
_draft_payload(head="someone:branch"),
|
||||
)
|
||||
|
||||
@ -192,7 +192,7 @@ def test_runtime_token_is_only_an_authorization_header():
|
||||
client = _load()
|
||||
request = client.build_request(
|
||||
"POST",
|
||||
"/api/v1/repos/atlas/cassandra/pulls",
|
||||
"/api/v1/repos/titan/cassandra/pulls",
|
||||
base_url=client.CANONICAL_BASE_URL,
|
||||
token="do-not-leak",
|
||||
data=_draft_payload(),
|
||||
|
||||
@ -59,7 +59,7 @@ def test_dry_run_has_no_file_input_and_uses_no_token_or_network(monkeypatch, cap
|
||||
)
|
||||
output = json.loads(capsys.readouterr().out)
|
||||
assert output["operation"] == "create-draft"
|
||||
assert output["owner"] == "atlas"
|
||||
assert output["owner"] == "titan"
|
||||
assert "Evidence only" not in json.dumps(output)
|
||||
with pytest.raises(SystemExit):
|
||||
client.parse_args(
|
||||
@ -86,7 +86,7 @@ def test_http_error_path_redacts_token(monkeypatch, capsys):
|
||||
|
||||
def fail(*_args, **_kwargs):
|
||||
raise urllib.error.HTTPError(
|
||||
"https://scm.bstein.dev/api/v1/repos/atlas/cassandra",
|
||||
"https://scm.bstein.dev/api/v1/repos/titan/cassandra",
|
||||
403,
|
||||
"forbidden",
|
||||
{},
|
||||
@ -95,7 +95,7 @@ def test_http_error_path_redacts_token(monkeypatch, capsys):
|
||||
|
||||
monkeypatch.setattr(client, "read", lambda *_a, **_k: fail())
|
||||
|
||||
assert client.main(["read", "/api/v1/repos/atlas/cassandra"]) == 1
|
||||
assert client.main(["read", "/api/v1/repos/titan/cassandra"]) == 1
|
||||
captured = capsys.readouterr()
|
||||
assert "do-not-leak" not in captured.err
|
||||
assert "credential was disclosed" in captured.err
|
||||
|
||||
@ -38,7 +38,7 @@ def test_read_accepts_only_http_200(status: int):
|
||||
|
||||
with pytest.raises(client.PolicyError, match="unexpected HTTP status"):
|
||||
client.read(
|
||||
"/api/v1/repos/atlas/cassandra",
|
||||
"/api/v1/repos/titan/cassandra",
|
||||
token="runtime",
|
||||
opener=lambda *_a, **_k: Response(b"{}", status=status),
|
||||
)
|
||||
@ -150,7 +150,7 @@ def test_read_response_is_bounded():
|
||||
|
||||
with pytest.raises(client.PolicyError, match="safe size limit"):
|
||||
client.read(
|
||||
"/api/v1/repos/atlas/cassandra",
|
||||
"/api/v1/repos/titan/cassandra",
|
||||
token="runtime",
|
||||
opener=lambda *_a, **_k: Response(b"x" * (client.MAX_RESPONSE_BYTES + 1)),
|
||||
)
|
||||
@ -163,7 +163,7 @@ def test_direct_api_rejects_unexpected_success_content_type():
|
||||
|
||||
with pytest.raises(client.PolicyError, match="unexpected response type"):
|
||||
client.read(
|
||||
"/api/v1/repos/atlas/cassandra",
|
||||
"/api/v1/repos/titan/cassandra",
|
||||
token="runtime",
|
||||
opener=lambda *_a, **_k: response,
|
||||
)
|
||||
@ -191,7 +191,7 @@ def test_direct_api_rejects_credential_reflection(reflected: bytes):
|
||||
|
||||
with pytest.raises(client.PolicyError, match="credential material"):
|
||||
client.read(
|
||||
"/api/v1/repos/atlas/cassandra",
|
||||
"/api/v1/repos/titan/cassandra",
|
||||
token="runtime-sentinel",
|
||||
opener=lambda *_a, **_k: Response(
|
||||
b'{"unexpected":"' + reflected + b'"}'
|
||||
|
||||
@ -41,15 +41,15 @@ def _draft_response(**updates):
|
||||
"state": "open",
|
||||
"draft": True,
|
||||
"merged": False,
|
||||
"html_url": "https://scm.bstein.dev/atlas/cassandra/pulls/3",
|
||||
"url": "https://scm.bstein.dev/atlas/cassandra/pulls/3",
|
||||
"html_url": "https://scm.bstein.dev/titan/cassandra/pulls/3",
|
||||
"url": "https://scm.bstein.dev/titan/cassandra/pulls/3",
|
||||
"title": "WIP: Focused fix",
|
||||
"body": "Review evidence",
|
||||
"base": {"ref": "main", "repo": {"full_name": "atlas/cassandra"}},
|
||||
"base": {"ref": "main", "repo": {"full_name": "titan/cassandra"}},
|
||||
"head": {
|
||||
"ref": "hermes/fix",
|
||||
"sha": HEAD_SHA,
|
||||
"repo": {"full_name": "atlas/cassandra"},
|
||||
"repo": {"full_name": "titan/cassandra"},
|
||||
},
|
||||
}
|
||||
response.update(updates)
|
||||
|
||||
@ -335,13 +335,13 @@ def test_an_armed_run_does_not_mutate_until_read_only_acceptance_is_go(
|
||||
("git ls-remote origin refs/heads/ephemeral", completed(stdout="")),
|
||||
("git push", completed()),
|
||||
(
|
||||
"POST /api/v1/repos/atlas/titan-iac/pulls",
|
||||
"POST /api/v1/repos/titan/atlas-iac/pulls",
|
||||
completed(stdout=json.dumps({"number": 9})),
|
||||
),
|
||||
("PATCH", completed()),
|
||||
("DELETE", completed()),
|
||||
(
|
||||
"read /api/v1/repos/atlas/titan-iac/pulls/9",
|
||||
"read /api/v1/repos/titan/atlas-iac/pulls/9",
|
||||
completed(stdout='{"state": "closed"}'),
|
||||
),
|
||||
*HEALTHY_CLUSTER,
|
||||
|
||||
@ -310,7 +310,7 @@ def test_cleanup_fails_closed_on_partial_or_malformed_evidence(
|
||||
def test_cleanup_fails_when_close_or_verification_commands_are_bad_json_or_failures() -> (
|
||||
None
|
||||
):
|
||||
api = "/api/v1/repos/atlas/titan-iac"
|
||||
api = "/api/v1/repos/titan/atlas-iac"
|
||||
for scripted in (
|
||||
[completed(returncode=1), completed(), completed(), completed(returncode=1)],
|
||||
[completed(stdout="bad"), completed(), completed(), completed(stdout="bad")],
|
||||
|
||||
@ -32,8 +32,8 @@ def check(*argv: str, mode: str | None = None) -> None:
|
||||
("hermes", "sessions", "list", "--source", "telegram"),
|
||||
("hermes", "status"),
|
||||
(policy.GITEA_CLIENT, "read", "/api/v1/user"),
|
||||
(policy.GITEA_CLIENT, "read", "/api/v1/repos/atlas/titan-iac/pulls/19"),
|
||||
("/opt/scm/gitea_api.py", "read", "/api/v1/repos/atlas/titan-iac"),
|
||||
(policy.GITEA_CLIENT, "read", "/api/v1/repos/titan/atlas-iac/pulls/19"),
|
||||
("/opt/scm/gitea_api.py", "read", "/api/v1/repos/titan/atlas-iac"),
|
||||
],
|
||||
)
|
||||
def test_read_only_commands_are_permitted(argv: tuple[str, ...]) -> None:
|
||||
@ -80,9 +80,9 @@ def test_read_only_commands_are_permitted(argv: tuple[str, ...]) -> None:
|
||||
(policy.GITEA_CLIENT, "POST", "/api/v1/x"),
|
||||
(policy.GITEA_CLIENT, "GET"),
|
||||
(policy.GITEA_CLIENT, "GET", "/api/v1/user"),
|
||||
(policy.GITEA_CLIENT, "GET", "/api/v1/repos/atlas/titan-iac"),
|
||||
(policy.GITEA_CLIENT, "GET", "/api/v1/repos/titan/atlas-iac"),
|
||||
(policy.GITEA_CLIENT, "read", "/api/v1/user", "extra"),
|
||||
(policy.GITEA_CLIENT, "create-draft", "/api/v1/repos/atlas/titan-iac/pulls"),
|
||||
(policy.GITEA_CLIENT, "create-draft", "/api/v1/repos/titan/atlas-iac/pulls"),
|
||||
("/opt/coordinator/gitea_api.py", "read", "/api/v1/user"),
|
||||
("sh", "-c", "rm -rf /"),
|
||||
("sh", "echo"),
|
||||
@ -169,23 +169,23 @@ def test_exec_validates_its_inner_command() -> None:
|
||||
def test_the_repository_pin_has_an_exact_boundary_and_rejects_dot_segments() -> None:
|
||||
"""A prefix match without a boundary let a look-alike repository through."""
|
||||
for path in (
|
||||
"/api/v1/repos/atlas/titan-iac-evil/pulls",
|
||||
"/api/v1/repos/atlas/titan-iacx",
|
||||
"/api/v1/repos/atlas/titan-iac../pulls",
|
||||
"/api/v1/repos/titan/atlas-iac-evil/pulls",
|
||||
"/api/v1/repos/titan/titan-iacx",
|
||||
"/api/v1/repos/titan/atlas-iac../pulls",
|
||||
):
|
||||
with pytest.raises(policy.PolicyError):
|
||||
check(policy.GITEA_CLIENT, "read", path)
|
||||
for path in (
|
||||
"/api/v1/repos/atlas/titan-iac/../../user/tokens",
|
||||
"/api/v1/repos/atlas/titan-iac/%2e%2e/%2e%2e/user/tokens",
|
||||
"/api/v1/repos/atlas/titan-iac/%252e%252e/user/tokens",
|
||||
"/api/v1/repos/atlas/titan-iac/./pulls",
|
||||
"/api/v1/repos/atlas/titan-iac//pulls",
|
||||
"/api/v1/repos/titan/atlas-iac/../../user/tokens",
|
||||
"/api/v1/repos/titan/atlas-iac/%2e%2e/%2e%2e/user/tokens",
|
||||
"/api/v1/repos/titan/atlas-iac/%252e%252e/user/tokens",
|
||||
"/api/v1/repos/titan/atlas-iac/./pulls",
|
||||
"/api/v1/repos/titan/atlas-iac//pulls",
|
||||
):
|
||||
with pytest.raises(policy.PolicyError, match="relative segments"):
|
||||
check(policy.GITEA_CLIENT, "read", path)
|
||||
check(policy.GITEA_CLIENT, "read", "/api/v1/repos/atlas/titan-iac")
|
||||
check(policy.GITEA_CLIENT, "read", "/api/v1/repos/atlas/titan-iac/pulls?state=all")
|
||||
check(policy.GITEA_CLIENT, "read", "/api/v1/repos/titan/atlas-iac")
|
||||
check(policy.GITEA_CLIENT, "read", "/api/v1/repos/titan/atlas-iac/pulls?state=all")
|
||||
|
||||
|
||||
def test_the_client_grammar_is_read_not_http_methods_in_every_mode() -> None:
|
||||
@ -226,7 +226,7 @@ def test_armed_mode_opens_exactly_the_mutations_it_should() -> None:
|
||||
policy.arm_ephemeral_policy(ref)
|
||||
policy.register_ephemeral_pull(9)
|
||||
check("git", "push", "origin", f"HEAD:refs/heads/{ref}", mode=policy.ARMED)
|
||||
root = "/api/v1/repos/atlas/titan-iac"
|
||||
root = "/api/v1/repos/titan/atlas-iac"
|
||||
check(
|
||||
policy.GITEA_CLIENT,
|
||||
"POST",
|
||||
@ -338,7 +338,7 @@ def test_a_subcommand_must_precede_its_resource_arguments() -> None:
|
||||
("git", "-C", "/tmp", "status"),
|
||||
("git", "--work-tree=/tmp", "status"),
|
||||
(policy.GITEA_CLIENT, "read", "not-an-api-path"),
|
||||
(policy.GITEA_CLIENT, "read", "/api/v1/repos/atlas/other"),
|
||||
(policy.GITEA_CLIENT, "read", "/api/v1/repos/titan/other"),
|
||||
],
|
||||
)
|
||||
def test_provenance_projection_and_repository_bypasses_are_rejected(argv) -> None:
|
||||
@ -364,14 +364,14 @@ def test_armed_writes_are_pinned_to_exact_remote_ref_and_endpoints() -> None:
|
||||
check(
|
||||
policy.GITEA_CLIENT,
|
||||
"DELETE",
|
||||
"/api/v1/repos/atlas/titan-iac",
|
||||
"/api/v1/repos/titan/atlas-iac",
|
||||
mode=policy.ARMED,
|
||||
)
|
||||
with pytest.raises(policy.PolicyError):
|
||||
check(
|
||||
policy.GITEA_CLIENT,
|
||||
"PATCH",
|
||||
"/api/v1/repos/atlas/titan-iac/pulls/8",
|
||||
"/api/v1/repos/titan/atlas-iac/pulls/8",
|
||||
"--field",
|
||||
"state=closed",
|
||||
mode=policy.ARMED,
|
||||
@ -380,7 +380,7 @@ def test_armed_writes_are_pinned_to_exact_remote_ref_and_endpoints() -> None:
|
||||
check(
|
||||
policy.GITEA_CLIENT,
|
||||
"POST",
|
||||
"/api/v1/repos/atlas/titan-iac/pulls",
|
||||
"/api/v1/repos/titan/atlas-iac/pulls",
|
||||
"--field",
|
||||
"title=WIP: wrong branch",
|
||||
"--field",
|
||||
|
||||
@ -34,7 +34,7 @@ DIGEST = "sha256:" + "d" * 64
|
||||
OTHER_DIGEST = "sha256:" + "e" * 64
|
||||
REPO = "registry.bstein.dev/bstein/hermes-webui"
|
||||
JOB = "https://jenkins.bstein.dev/job/hermes-webui-image"
|
||||
PREFIX = "https://scm.bstein.dev/atlas/titan-iac/pulls/"
|
||||
PREFIX = "https://scm.bstein.dev/titan/atlas-iac/pulls/"
|
||||
REVIEW = PREFIX + "55"
|
||||
HEALTH = "https://chat.bstein.dev/healthz"
|
||||
TAG = f"git-{SHA_M}-build-20-release"
|
||||
@ -45,7 +45,7 @@ POLICY = {"schema": "hux.release_evidence_policy.v1", "max_evidence_age_seconds"
|
||||
FIXED = datetime(2026, 8, 24, 12, 0, 0, tzinfo=timezone.utc)
|
||||
NOW_S = "2026-08-24T12:00:00Z"
|
||||
OLD_S = "2026-08-24T10:00:00Z"
|
||||
GIT_API = "https://scm.bstein.dev/api/v1/repos/atlas/titan-iac/pulls/55"
|
||||
GIT_API = "https://scm.bstein.dev/api/v1/repos/titan/atlas-iac/pulls/55"
|
||||
JENKINS_API = JOB + "/lastSuccessfulBuild/api/json"
|
||||
HARBOR_API = f"https://registry.bstein.dev/api/v2.0/projects/bstein/repositories/hermes-webui/artifacts/{TAG}"
|
||||
KUSTOMIZATION_API = "https://kube.test/apis/kustomize.toolkit.fluxcd.io/v1/namespaces/hermes/kustomizations/hermes"
|
||||
|
||||
@ -25,7 +25,7 @@ def policy() -> dict:
|
||||
"schema": "hux.release_evidence_policy.v1",
|
||||
"max_evidence_age_seconds": 900,
|
||||
"workloads": {"hermes-webui": {
|
||||
"review_url_prefix": "https://scm.bstein.dev/atlas/titan-iac/pulls/",
|
||||
"review_url_prefix": "https://scm.bstein.dev/titan/atlas-iac/pulls/",
|
||||
"jenkins_job_url": "https://jenkins.bstein.dev/job/hermes-webui-image",
|
||||
"image_repository": "registry.bstein.dev/bstein/hermes-webui",
|
||||
"flux_kustomization": "hermes",
|
||||
|
||||
@ -47,7 +47,7 @@ def setup(tmp_path):
|
||||
policy_file.write_text(json.dumps({
|
||||
"schema": "hux.release_evidence_policy.v1", "max_evidence_age_seconds": 900,
|
||||
"workloads": {"hermes-webui": {
|
||||
"review_url_prefix": "https://git.bstein.dev/atlas/titan-iac/pulls/",
|
||||
"review_url_prefix": "https://git.bstein.dev/titan/atlas-iac/pulls/",
|
||||
"jenkins_job_url": "https://jenkins.bstein.dev/job/hermes",
|
||||
"image_repository": "registry.bstein.dev/bstein/hermes",
|
||||
"flux_kustomization": "hermes", "health_url": "https://chat.bstein.dev/healthz",
|
||||
@ -69,7 +69,7 @@ def create(setup, key="release-key-001", commit="1" * 40, **changes):
|
||||
router, _, _, base = setup
|
||||
body = {
|
||||
"workload": "hermes-webui", "commit": commit, "feature_flags": ["hux.foundation"],
|
||||
"evidence": {"review_url": "https://git.bstein.dev/atlas/titan-iac/pulls/55"}, **changes,
|
||||
"evidence": {"review_url": "https://git.bstein.dev/titan/atlas-iac/pulls/55"}, **changes,
|
||||
}
|
||||
return call(router, "POST", base, body, {"If-Match": "0", "Idempotency-Key": key})
|
||||
|
||||
@ -199,7 +199,7 @@ def test_only_router_reviews_and_only_evidence_trust_transitions(setup):
|
||||
assert call(router, "POST", path + "/transitions", transition, {**worker, **headers})[0] == 401
|
||||
assert call(router, "POST", base, {
|
||||
"workload": "hermes-webui", "commit": "8" * 40,
|
||||
"evidence": {"review_url": "https://git.bstein.dev/atlas/titan-iac/pulls/8"},
|
||||
"evidence": {"review_url": "https://git.bstein.dev/titan/atlas-iac/pulls/8"},
|
||||
}, {**EVIDENCE, "If-Match": "0", "Idempotency-Key": "evidence-cannot-review"})[0] == 403
|
||||
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ def test_gitea_api_builds_runtime_authenticated_same_origin_requests():
|
||||
|
||||
request = gitea_api.build_request(
|
||||
"POST",
|
||||
"/api/v1/repos/atlas/cassandra/pulls",
|
||||
"/api/v1/repos/titan/cassandra/pulls",
|
||||
base_url="https://scm.bstein.dev",
|
||||
token="runtime-only-value",
|
||||
data={
|
||||
@ -50,7 +50,7 @@ def test_gitea_api_builds_runtime_authenticated_same_origin_requests():
|
||||
)
|
||||
|
||||
assert isinstance(request, urllib.request.Request)
|
||||
assert request.full_url == "https://scm.bstein.dev/api/v1/repos/atlas/cassandra/pulls"
|
||||
assert request.full_url == "https://scm.bstein.dev/api/v1/repos/titan/cassandra/pulls"
|
||||
assert request.method == "POST"
|
||||
assert request.get_header("Authorization") == "token runtime-only-value"
|
||||
assert json.loads(request.data)["title"].startswith("WIP: ")
|
||||
@ -60,9 +60,9 @@ def test_gitea_api_builds_runtime_authenticated_same_origin_requests():
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"https://evil.example/api/v1/repos/atlas/cassandra",
|
||||
"https://evil.example/api/v1/repos/titan/cassandra",
|
||||
"/repos/atlas/cassandra",
|
||||
"/api/v1/repos/atlas/cassandra#fragment",
|
||||
"/api/v1/repos/titan/cassandra#fragment",
|
||||
],
|
||||
)
|
||||
def test_gitea_api_rejects_foreign_or_non_api_targets(path: str):
|
||||
@ -78,7 +78,7 @@ def test_gitea_api_rejects_unavailable_methods(method: str):
|
||||
|
||||
with pytest.raises(gitea_api.PolicyError):
|
||||
gitea_api.authorize_request(
|
||||
method, "/api/v1/repos/atlas/cassandra/pulls/1", None
|
||||
method, "/api/v1/repos/titan/cassandra/pulls/1", None
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -13,8 +13,8 @@ from testing.tests.test_hermes_scm_broker_support import (
|
||||
@pytest.mark.parametrize(
|
||||
"target",
|
||||
[
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1/merge",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1/reviews",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1/merge",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1/reviews",
|
||||
"/api/v1/admin/users",
|
||||
"/git/evil/cassandra.git/info/refs?service=git-upload-pack",
|
||||
"/git/atlas/cassandra.git/hooks",
|
||||
@ -35,13 +35,13 @@ def test_broker_metadata_boundary_reuses_explicit_read_allowlist():
|
||||
api = _load("gitea_api")
|
||||
|
||||
assert (
|
||||
api.authorize_request("GET", "/api/v1/repos/atlas/cassandra/pulls/1", None)
|
||||
api.authorize_request("GET", "/api/v1/repos/titan/cassandra/pulls/1", None)
|
||||
== "pull"
|
||||
)
|
||||
for path in (
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1/merge",
|
||||
"/api/v1/repos/atlas/cassandra/pulls/1/reviews",
|
||||
"/api/v1/repos/atlas/cassandra/hooks",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1/merge",
|
||||
"/api/v1/repos/titan/cassandra/pulls/1/reviews",
|
||||
"/api/v1/repos/titan/cassandra/hooks",
|
||||
"/api/v1/admin/users",
|
||||
):
|
||||
with pytest.raises(api.PolicyError):
|
||||
@ -82,7 +82,7 @@ def test_git_proxy_uses_fixed_origin_and_never_reflects_credential():
|
||||
)
|
||||
|
||||
result = broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=b"request",
|
||||
content_type="application/x-git-upload-pack-request",
|
||||
@ -94,14 +94,14 @@ def test_git_proxy_uses_fixed_origin_and_never_reflects_credential():
|
||||
assert result == b"git-result"
|
||||
request = seen[0][0]
|
||||
assert request.full_url == (
|
||||
"https://scm.bstein.dev/atlas/cassandra.git/git-upload-pack"
|
||||
"https://scm.bstein.dev/titan/cassandra.git/git-upload-pack"
|
||||
)
|
||||
assert b"runtime-sentinel" not in request.data
|
||||
assert "runtime-sentinel" not in request.full_url
|
||||
|
||||
with pytest.raises(broker.PolicyError, match="credential material"):
|
||||
broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=b"request",
|
||||
content_type="application/x-git-upload-pack-request",
|
||||
@ -115,7 +115,7 @@ def test_git_proxy_uses_fixed_origin_and_never_reflects_credential():
|
||||
encoded = broker._credential_forms("runtime-sentinel")[1]
|
||||
with pytest.raises(broker.PolicyError, match="credential material"):
|
||||
broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=b"request",
|
||||
content_type="application/x-git-upload-pack-request",
|
||||
@ -132,7 +132,7 @@ def test_git_proxy_requires_exact_http_200(status: int):
|
||||
|
||||
with pytest.raises(broker.PolicyError, match="unexpected HTTP status"):
|
||||
broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=b"request",
|
||||
content_type="application/x-git-upload-pack-request",
|
||||
|
||||
@ -89,7 +89,7 @@ def test_client_rejects_bad_status_type_size_and_json(response, match):
|
||||
with pytest.raises(expected, match=match):
|
||||
client.request(
|
||||
"/v1/metadata",
|
||||
{"path": "/api/v1/repos/atlas/cassandra"},
|
||||
{"path": "/api/v1/repos/titan/cassandra"},
|
||||
opener=lambda *_a, **_k: response,
|
||||
)
|
||||
|
||||
|
||||
@ -154,7 +154,7 @@ def test_control_metadata_and_draft_fields(monkeypatch):
|
||||
lambda token, **data: json.dumps({"token_used": bool(token), **data}).encode(),
|
||||
)
|
||||
|
||||
metadata_body = b'{"path":"/api/v1/repos/atlas/cassandra"}'
|
||||
metadata_body = b'{"path":"/api/v1/repos/titan/cassandra"}'
|
||||
metadata = _handler(broker, path="/v1/metadata", body=metadata_body)
|
||||
metadata._control()
|
||||
assert json.loads(metadata.wfile.getvalue())["path"].endswith("cassandra")
|
||||
|
||||
@ -212,7 +212,7 @@ def test_upstream_request_infers_byte_length_and_requires_stream_length():
|
||||
|
||||
assert (
|
||||
broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=b"request",
|
||||
content_type=None,
|
||||
@ -225,7 +225,7 @@ def test_upstream_request_infers_byte_length_and_requires_stream_length():
|
||||
assert seen[0][0].get_header("Content-length") == "7"
|
||||
with pytest.raises(broker.PolicyError, match="length is missing"):
|
||||
broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=io.BytesIO(b"request"),
|
||||
content_type=None,
|
||||
@ -248,7 +248,7 @@ def test_upstream_request_rejects_wrong_type_and_supports_getcode_status():
|
||||
|
||||
assert (
|
||||
broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/info/refs?service=git-upload-pack",
|
||||
"/titan/cassandra.git/info/refs?service=git-upload-pack",
|
||||
method="GET",
|
||||
body=None,
|
||||
content_type=None,
|
||||
@ -262,7 +262,7 @@ def test_upstream_request_rejects_wrong_type_and_supports_getcode_status():
|
||||
)
|
||||
with pytest.raises(broker.PolicyError, match="response type"):
|
||||
broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/info/refs?service=git-upload-pack",
|
||||
"/titan/cassandra.git/info/refs?service=git-upload-pack",
|
||||
method="GET",
|
||||
body=None,
|
||||
content_type=None,
|
||||
|
||||
@ -19,11 +19,11 @@ def test_agent_broker_client_sends_no_credential_or_authorization_header():
|
||||
seen.append((request, timeout))
|
||||
return Response(b"{}", content_type="application/json")
|
||||
|
||||
assert client.read("/api/v1/repos/atlas/cassandra", opener=opener) == b"{}"
|
||||
assert client.read("/api/v1/repos/titan/cassandra", opener=opener) == b"{}"
|
||||
request = seen[0][0]
|
||||
assert request.full_url == client.BROKER_ORIGIN + "/v1/metadata"
|
||||
assert request.get_header("Authorization") is None
|
||||
assert json.loads(request.data) == {"path": "/api/v1/repos/atlas/cassandra"}
|
||||
assert json.loads(request.data) == {"path": "/api/v1/repos/titan/cassandra"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("raw", ["", "01", "9" * 4000, "134217729", "12"])
|
||||
@ -83,7 +83,7 @@ def test_large_upstream_response_is_streamed_through_bounded_spool():
|
||||
return Response(body, content_type="application/x-git-upload-pack-result")
|
||||
|
||||
streamed = broker._upstream_git_request(
|
||||
"/atlas/cassandra.git/git-upload-pack",
|
||||
"/titan/cassandra.git/git-upload-pack",
|
||||
method="POST",
|
||||
body=io.BytesIO(b"request"),
|
||||
body_length=7,
|
||||
|
||||
@ -52,7 +52,7 @@ def test_real_backend_http_contracts_are_consumable_by_the_webui(tmp_path: Path)
|
||||
evidence_policy.write_text(json.dumps({
|
||||
"schema": "hux.release_evidence_policy.v1", "max_evidence_age_seconds": 3600,
|
||||
"workloads": {"hermes-webui": {
|
||||
"review_url_prefix": "https://git.bstein.dev/atlas/titan-iac/",
|
||||
"review_url_prefix": "https://git.bstein.dev/titan/atlas-iac/",
|
||||
"jenkins_job_url": "https://jenkins.bstein.dev/job/hermes-webui",
|
||||
"image_repository": "registry.bstein.dev/bstein/hermes-webui",
|
||||
"flux_kustomization": "hermes-chat", "health_url": "https://chat.bstein.dev/healthz",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user