Skip to content

GT-324/435: repair the code-side half of the production path, and measure what is actually left - #307

Merged
beyondnetPeru merged 6 commits into
developfrom
fix/gt-324-production-path-code-half
Jul 31, 2026
Merged

GT-324/435: repair the code-side half of the production path, and measure what is actually left#307
beyondnetPeru merged 6 commits into
developfrom
fix/gt-324-production-path-code-half

Conversation

@beyondnetPeru

@beyondnetPeru beyondnetPeru commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Advances GT-324 and GT-435. Closes neither — everything that remains is owner-gated, and this PR says so rather than crediting a deploy nobody ran.

The rows were partly stale

Row's claim Finding Evidence
workflow builds+pushes core-api and mcp-server to GHCR true, and understated — the matrix carries three services now, incl. agent-runtime ci-cd.yml:549-564
"the deploy job runs but curl fails Could not resolve host" stale — the job no longer runs at all; its condition gained && vars.VPS_DEPLOY_ENABLED == 'true' (GT-567), deliberately OFF ci-cd.yml:615
the recorded error text superseded: at retirement the real error was curl: (28) Failed to connect … port 8000, a timeout, not DNS 645374e9
the reliability row (IN-PROGRESS; owned by #310) correctly open and further from done than the row readsCircuitBreaker appears in zero .ts files under src, and the k6/chaos assets at product/infra/load/ run in no workflow. Available work, not blocked work grep -rl "CircuitBreaker" src --include='*.ts' → empty

GT-435 cannot close: its own criterion requires every child DONE-or-deferred, and three children remain open. The VPS milestone row likewise has all three criteria unmet, including a rollback that has never been exercised — it is assessed here, not claimed; no board row for it is modified by this PR.

Fixed here

  1. src/sdk/cli/Dockerfile shipped a dead entrypoint. CMD ["node","dist/main.js","mcp","serve","--transport","http"] invokes the mcp subcommand deleted in dc0b9667, so the container printed CLI help and exited 0 — a silent no-op, not a crash. Rebuilt on the repo-root pattern its three siblings use, with a real ENTRYPOINT and --help as the default.
    Correction to the audit note that prompted this: the old file was not unbuildable — it fails only when the package directory is the build context; from the repo root it builds fine. Verified both ways by the integrating session. The dead CMD is the real defect.
  2. product/infra/vps-coolify/README.md (+ .es.md) — the owner's runbook was wrong in five ways, each sufficient to guarantee a failed deploy: a pre-src/ copy instruction that would overwrite a working build with an off-path template; a build context of src/apps/core-api when every real Dockerfile needs the repo root and says so in its own header; DATABASE_URL/REDIS_URL on core-api, which ADR-0101 forbids; mcp documented on port 3001 when the image EXPOSEs 3000; agent-runtime omitted entirely.
  3. product/infra/docker-compose.yml — two services that could never start. bff and mcp built from a context of illustrative Dockerfiles with no package.json, and mounted a path that docker compose config resolves to a sibling of the repository root. Removing them also makes deployment-topology.md's claim that no bff remains on any deploy path true rather than aspirational.
  4. docker-images.yml — the Helm charts named a tag the pipeline could not produce. type=ref,event=tag publishes v1.1.0 verbatim while the mcp chart asks for 1.1.0. Added type=semver.

Verification

Re-run independently by the integrating session, not taken on report:

  • docker build -f src/sdk/cli/Dockerfile . → exit 0; the old image's default command prints help and exits 0 (the defect, reproduced); the new image runs validate --core /app/corpus and returns a real verdict — **Status:** failed, **Rules Checked:** 86, **Rules Skipped:** 247. It governs; it does not merely --help.
  • All three CI service images (core-api, mcp-server, agent-runtime) build green from the repo root exactly as docker-services does them.
  • Not reproduced in runner conditions: these ran on darwin/Docker 29.4.3, not ubuntu-latest. Context-identical, but not the runner.
  • Which tags actually exist in GHCR is not verifiable from heredocker manifest inspect returns unauthorized, and no attempt was made to authenticate.

Owner checklist

Written into product/infra/vps-coolify/README.md §Phase 4 in both languages. The short form: create the three Coolify applications first (a hook cannot exist before its app), then set COOLIFY_API_TOKEN, then the three deploy hooks as full URLs (https://<host>/api/v1/deploy?uuid=<uuid> — a bare UUID or a scheme-less host reproduces exactly the failure recorded against GT-324), then the repo variable VPS_DEPLOY_ENABLED=true (without it the job is skipped entirely), then the per-app encrypted env — and no DATABASE_URL.

Deliberately not decided here: the Helm chart tags 0.0.2 (core-api) and 0.1.0 (agent-runtime). No git tag would produce them, so that is a version-intent call for the owner; guessing silently is the failure mode this board keeps catching.

Reported, not fixed: src/apps/core-api/.dockerignore and src/packages/mcp-server/.dockerignore are inert — with the context at the repo root BuildKit loads <context>/.dockerignore, and every build logged transferring context: 2B. Builds are green either way; a repo-wide ignore rule is not worth the risk for a cosmetic win.

🤖 Generated with Claude Code


Claim note: this PR assesses the reliability and VPS-milestone rows but modifies neither, and does not claim them. The reliability row is owned by #310. Retitled and rephrased so the claim guard stops reading an assessment as a claim.

beyondnetPeru and others added 6 commits July 30, 2026 14:33
release: align main — the CLI and MCP resolve the SDK that carries the security wave, 1.2.2 (GT-634)
A board row carries a STATUS but not who is working it or where, so two sessions
can both read `PENDING` and both start, correctly. On 2026-07-30 that happened
three times in a day.

`50-validate-gap-claim` derives the claim set from OPEN PULL REQUESTS — every
`GT-*` in a PR's title, body or branch name — and fails when one id is claimed by
two of them, naming both with their branches and titles.

DERIVED, NEVER HAND-WRITTEN, and the row asked for that on purpose: a hand-written
claim goes stale in the direction that matters, because someone forgets to remove
it and the next session works around a claim nobody holds.

THREE CRITERIA OF FOUR, and the fourth is left open with its reason rather than
stretched to fit. A claim list committed to the board would be derived from live
GitHub state, so it would be stale the moment anyone opened a pull request — and
GT-630's chain exists precisely to insist derived artifacts reach a fixed point. A
perpetually-stale artifact inside that chain would be worse than none. Making the
claim discoverable from the board needs something the board can render without
committing, and that is not built. The row stays IN-PROGRESS.

What it cannot see is in its own failure text, not implied: a branch with no open
pull request claims nothing. Opening the PR early — draft is enough — is what
makes the claim visible, which turns a convention into something with a check
behind it.

Anti-vacuous: a pull-request query that cannot be answered is a hard failure, not
a quiet pass. A guard built because two sessions could not see each other must not
stay silent when it cannot look. Zero open PRs is reported in words, because it
looks identical to a broken query otherwise.

Verified: 10/10 fixtures; 42-validate-guard-denominators 62 guards classified;
43-validate-guard-negative-fixtures 39/39 observed red, up from 38. Run against
the real repository it reports 0 claims across 5 open PRs — checked rather than
assumed: all five are Dependabot bumps naming no gap, so the zero is real and not
an empty query. 08-validate-tracking (640 gaps), 01, 04, 41 within budget, 46
chain at a fixed point. Counters: 600/640, in-progress 14 -> 15, pending 22 -> 21.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(ci): one gap, one claim — the claim guard, advancing GT-639
`.git/EVOLITH_*` is a DIRECTORY only in the primary checkout; inside a `git
worktree` it is a FILE holding a `gitdir:` pointer, so every write/read to
`.git/EVOLITH_*` was ENOTDIR. In pre-commit that made `set -e` abort the
commit outright; in pre-push the `rm -f` failed and TARGET stayed unread —
together the reason `--no-verify` had become habitual from a worktree.

`git rev-parse --git-dir` resolves both layouts; both hooks now go through
it via a shared `write_push_intent`/read path, per-worktree, so the push
intent belongs to the checkout that was committed in rather than the repo
as a whole.

Also in pre-push: `assert_checkoutable` refuses up front, before repo
optimization or board sync run, when the merge target branch (main/develop)
is already checked out in another worktree — `git checkout "$to"` would
otherwise fail mid-dispatch with the push half-done.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fix(ci): pre-commit/pre-push hooks assumed .git is always a directory
…GT-435/GT-448)

Four defects on the deploy path that needed nobody's credentials. Each was
measured, not argued: three of the four are proven by a command that ran.

1. `src/sdk/cli/Dockerfile` was unbuildable and would have shipped a dead
   entrypoint. Its context was `src/sdk/cli`, so `COPY package*.json` matched
   only `package.json` — the monorepo's single lockfile is at the root — and
   `npm ci` refused. Reproduced: `RUN npm ci` exit 1. Its CMD also invoked the
   `mcp` subcommand, deleted from the CLI in dc0b966, so a build-only fix
   would have published an image that dies on start. The CI job that built it
   was retired in 645374e for exactly these reasons, deliberately leaving the
   artifact for someone to decide about; this repairs it rather than leaving a
   trap. Now builds from the repo root like its three siblings and runs as a
   governance tool against a mounted workspace.
   VERIFIED: build exit 0; `--version` -> 1.2.2; `validate --core /app/corpus`
   against a mounted repo loads 333 rules and returns a real verdict.

2. `product/infra/vps-coolify/README.md` (+ .es) is the owner's production
   runbook and it was wrong in ways that guarantee a failed deploy.
   - Phase 2 told the owner to `cp product/infra/docker/bff.Dockerfile
     apps/core-api/Dockerfile` — a pre-`src/` path, and it would overwrite the
     working monorepo build with a template that deployment-topology.md already
     records as being on no deploy path.
   - Phase 3 set the Coolify build context to `src/apps/core-api`. Every real
     Dockerfile COPYs repo-root-relative paths and says so in its own header
     (`base directory = /`); that context fails on the first COPY.
   - Phase 3 instructed setting `DATABASE_URL`/`REDIS_URL` on core-api, which
     `product/infra/README.md` explicitly forbids two directories away: the
     Core is stateless per ADR-0101 and declares zero DB dependencies.
   - mcp-server was documented on port 3001; the image EXPOSEs 3000.
   - agent-runtime was absent although GT-437 put it in the deploy matrix.
   - Phase 4 told the owner to register the Coolify hook as a GitHub *repo
     webhook*. The pipeline never reads that. It reads three repository secrets
     and one repository variable, and it is switched OFF right now behind
     `vars.VPS_DEPLOY_ENABLED` (GT-567) — a fact the runbook did not mention.
   Phase 4 now carries the exact owner checklist: which secret, which shape,
   and why a UUID or a bare path produces the `Could not resolve host` recorded
   against GT-324.

3. `product/infra/docker-compose.yml` declared `bff` and `mcp` services that
   could never start: build context `./docker` holds two illustrative
   Dockerfiles and no package.json, and both mounted `../../../rulesets`, which
   `docker compose config` resolves to a SIBLING OF THE REPOSITORY ROOT — the
   corpus has been at `src/rulesets` since the GT-450 cutover. Removed, with
   the reasoning in place, which also makes deployment-topology.md's claim that
   "no bff remains on any deploy path" true instead of aspirational.
   VERIFIED: `docker compose config` exit 0, zero remaining dead mounts.

4. `docker-images.yml` published the git tag verbatim (`v1.1.0`), while the
   Helm charts reference bare semver (`evolith-mcp` tag `1.1.0`). No release
   this workflow ever ran could satisfy `helm install` with default values —
   the chart named a tag the pipeline was structurally incapable of producing.
   Added `type=semver`, so `v1.1.0` now also publishes `1.1.0` and `1.1`.

NOT claimed, and deliberately not faked: nothing was deployed. The three CI
service images were rebuilt from the repo root exactly as `docker-services`
does them (core-api, mcp-server, agent-runtime — all exit 0), but GHCR answers
`unauthorized` from here, so which tags actually exist in the registry is not
verifiable from a dev machine. The `core-api` chart (`0.0.2`) and
`agent-runtime` chart (`0.1.0`) still name tags no git tag would produce; that
is a version-intent question for the owner, not one to guess at silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@beyondnetPeru
beyondnetPeru requested a review from a team as a code owner July 31, 2026 01:59
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

Copy link
Copy Markdown

📊 Bilingual Coverage Impact

PR Changes

  • Paired EN/ES files modified: 1
  • New EN files needing ES translation: 0

Repository Coverage

Metric Value
Total EN files 524
Total ES files 498
Paired files 0
Coverage 0%

Good: All EN changes have ES counterparts.


Generated by GitHub Actions

@beyondnetPeru beyondnetPeru changed the title GT-324/435/448: repair the code-side half of the production path, and measure what is actually left GT-324/435: repair the code-side half of the production path, and measure what is actually left Jul 31, 2026
@beyondnetPeru
beyondnetPeru merged commit fd9ecc0 into develop Jul 31, 2026
33 of 34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant