chore(phase-7): cleanup sweep — JVM endgame, Go-only CI, slog finish, docs - #63
Closed
grubmeshi wants to merge 10 commits into
Closed
Conversation
Ports PLAN_DETAIL_06B_gitlab.md, reusing the 06A manual template unchanged (handler
shape, event-driven report.Reporter seam, config compat, persona wiring, Dockerfile
pattern). Additive and always-green: the Kotlin gitlab-block-runner module and its
Gradle/CI legs are untouched (removal + the acceptance gate + the meshfed-release
touch-points are deferred to the flip PR, see CROSS_REPO_TODO.md — they need a live
GitLab/meshStack this environment can't provide).
Umbrella-assigned shared artifacts shipped here (first consumer, per 06A §4.4/§17):
- meshapi.Decryptor + CertDecryptor/NoopDecryptor: the shared decrypt seam every
phase-6 port injects, with the Kotlin decrypt("") == "" empty-string guard (T8/G-P11)
the underlying crypto lacked.
- meshapi.DecryptInputs: decrypts ONLY sensitive STRING/CODE/FILE input values
(Kotlin's decryptBlockRunInputs asymmetry, umbrella §7.6) — the structural fix so an
outbound payload can never carry a decrypted implementation secret. Table-driven
tests incl. a real-keypair cross-check and the secret-hygiene leak assertion.
- gitlab.ExternalCallError: the MeshHttpException equivalent (06A §4.4 shape), with the
four-way GitLab error classification (404 / identity-verification / generic /
undeserializable body) collapsing into the one wire-visible FAILED update Kotlin sends.
Gitlab persona (internal/gitlab, cmd/gitlab, bbrunner gitlab subcommand):
- One external POST (multipart trigger payload: token/ref/MESHSTACK_BEHAVIOR/
MESHSTACK_RUN + env/non-env inputs + the four callback URLs, missing-link ⇒ omit),
redirects disabled at the client (G-P10), base-URL sanitization ported from
UrlSanitizerService.
- Always-async handover (D9): register one PENDING gl-trigger step, then exactly one
final update — IN_PROGRESS+SUCCEEDED on a successful trigger, FAILED+FAILED on any
trigger-path failure — never a terminal SUCCEEDED from the runner itself.
- Polling (dispatch.Loop + InProcess, mgmt on 8103, PORT alias) and single-run (file
source, NoOp decryptor — the k8s controller pre-decrypted both inputs and the trigger
token, §2.6 caveat pinned as-is — R12 exit rule).
- New shared top-level base containers/runner-config.yml (deep-merged under the
per-impl file, base < per-impl < env): carries the well-known dev private key shared
by gitlab/azdevops/github, re-wrapped into standard multi-line PEM (Go's stdlib
encoding/pem rejects the Kotlin classpath's unbroken single-line form; DER bytes are
byte-identical, verified in internal/config/basekey_test.go).
Coverage gate: internal/gitlab joins thresholds.txt at 90 (measures 92.1%, no
exclusions) via the scenario suite (meshapitest + a fake GitLab httptest server) plus
keep-as-unit tables for the pure mappings (valueString, sanitizeBaseUrl, error
classification). Manually verified end-to-end via `docker build` + `docker run` against
a hand-rolled mock meshfed API: claim → register → trigger attempt → FAILED report all
observed with the exact pinned message shapes.
Kotlin pin tests (G-P1–G-P13) were not added — gradle is not runnable in this
environment and the module isn't being modified/removed in this commit; recorded as a
flagged follow-up in CROSS_REPO_TODO.md rather than silently skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
06A specified these for 06B (gitlab) to ship as first consumer, but phase-6b-gitlab had not landed them at authoring time (git diff phase-6a-manual..phase-6b-gitlab was empty). Ships them here to the 06A-specified contract instead, per PLAN_DETAIL_06C_azdevops.md's STOP-C4 resolution -- flagged in CROSS_REPO_TODO.md for reconciliation if 06B lands its own copy in parallel. Mirrors internal/tf.Decryptor's existing shape (Decryptor interface, certDecryptor, NoopDecryptor) and the Kotlin MeshCertDecryptionService input-only decrypt asymmetry (STRING/CODE/FILE decrypted, other sensitive types left as ciphertext, impl secrets untouched).
Kotlin's hand-rolled PrivateKeyLoader/MeshCertDecryptionService parser strips the BEGIN/END markers and every newline by hand, so every Kotlin runner image's classpath runner-config.yml ships its baked dev privateKey with no newline after "-----BEGIN PRIVATE KEY-----". Go's stdlib encoding/pem.Decode rejects that shape outright. azure-devops is the first Go port to actually decrypt (manual never does), so this was the first real exercise of config.ResolvePrivateKey against a real Kotlin-shipped key -- without this fix, both this port's own baked dev key and any customer's existing Kotlin runner-config.yml would silently fail to decrypt on the new Go image (a config-compat break, D7/D10). normalizePEM is a pure additive fallback: only tried when a bare pem.Decode already failed, so an already-valid multi-line PEM is unaffected. Verified end-to-end against the actual containers/azure-devops-block-runner baked key via both a unit test and a docker-run smoke.
…s.md) The heaviest of the four Kotlin ports (AzureDevOpsPipelinePoller/ StatusUpdater/StatusMapper had zero direct Kotlin tests): trigger a pipeline run, then either hand over IN_PROGRESS (async) or poll to a terminal report with per-stage steps (sync), on the 06A template (dispatch.RunHandler, the unified report.Reporter, config compat helpers, StandaloneClaimClassifier). - statusmapper.go: pure S-P1-S-P6 mapping-table twins of AzureDevOpsStatusMapper.kt, incl. the pinned else-holes (completed+succeededWithIssues/absent -> IN_PROGRESS). - client.go: the ADO wire client (trigger/get-run/get-timeline), the ExternalCallError type (per-package, per umbrella §4 row 14 -- not a shared meshapi type), renderValue (UseNumber-fidelity stringification, flagged JSON-not-toString delta for composite values, §16.6), redirect policy, explicit timeouts (§16.10), no base-URL sanitization (§16.7). - updates.go: RunStatus builders for every U-P1-U-P8 update shape (trigger-success, state-only, stage-batch with the one-way COMPLETED dedup, final, failed, terminal abort/failed). - handler.go: the failure ladder (register -> parse impl -> decrypt PAT -> decrypt inputs -> trigger), byte-identical U-P8 message pair selection. - poll.go: the sync poll loop, ctx-aware (plan-05 H7 amendment: reports a TERMINAL status on shutdown cancellation, ABORTED falling back to FAILED, never SUCCEEDED) with escalation semantics verified line-by-line against the Kotlin nested try/catch (a PATCH failure during a stage/fallback update is silently absorbed like a GET failure; only the timeout and final-update reports get one escalation attempt before propagating). - config.go/loadconfig.go/reporterfactory.go/singlerun.go: persona config, the R12 single-run exit tail with the sanctioned K-P2 delta, single-run mode wiring. Test suite (D16 scenario style, D6 gate ≥90%, actual 93.2%, -race clean): reproduces all 28 planned pins (6 S + 8 U + 5 P + 6 A + 2 K + 1 F) via a fake Azure DevOps httptest transport, the shared meshapitest mock, and an injected Clock (sleep-free, no goroutine races -- After() fires instantly and Now() only advances via an explicit per-test bump, so even the 30-min timeout pin runs in milliseconds). No new Kotlin JUnit tests were added (gradle is not runnable in this environment, matching the 6a precedent) -- the Go scenario suite is the surviving pin; see CROSS_REPO_TODO.md.
- cmd/azdevops/main.go + polling.go: the fit azure-devops-block-runner
binary (D1/D8) -- single-run vs polling mode selection, mgmt listener on
8101 (PORT alias), cert-based decryptor wiring.
- cmd/bbrunner: register the azdevops persona subcommand
(`bbrunner azdevops`) alongside tf/manual, for local-dev / mux
replacement (§4.1).
- containers/azure-devops-block-runner/{Dockerfile,runner-config.yml}: the
06A per-app-Dockerfile pattern, direct entrypoint, the baked local-dev
private key (umbrella §10.5) -- verified with a real `docker build` +
`docker run` smoke (config loads, private key resolves and parses,
dispatch loop starts, /healthz answers OK).
- tools/coverage/thresholds.txt: gate internal/azdevops at 90% (actual
93.2%, no exclusions -- whole package hermetically testable).
- .golangci.yml: azdevops/azdevops-test depguard groups mirroring the
manual persona's (no k8s, no go-git, no tofu).
Adds PLAN_DETAIL_06C_azdevops.md (from the plan branch) to this stacked branch, and records the phase-6c deferrals in CROSS_REPO_TODO.md: the Kotlin module removal / CI flip / meshfed-release edit all wait on the §11 acceptance gate (live Azure DevOps org + side-by-side transcript equivalence), which this environment cannot execute -- same rationale as phase-6a. Also flags the two STOP-C4/crypto template-gap fixes this slice made for the next port to be aware of.
Port PLAN_DETAIL_06D_github.md: the GITHUB_WORKFLOW persona in Go.
- App-JWT auth chain, stdlib RS256 (crypto/rsa SignPKCS1v15 + x509.ParsePKCS1PrivateKey),
no JWT dependency (§4.2/§16.13); whitespace-tolerant PKCS#1 parsing matching Kotlin (G-P2).
- githubClient: 5 REST calls, frozen headers, actions=write permission gate, 422
unsupported-input heuristic (pure classify fn), validated run/job status enums.
- Dual input modes: Mode A base64-JSON run object (impl stripped to {type}, G-P10 leak
hygiene, json.Number fidelity D8), Mode B url+token+conditional-endpoint table.
- In-handler sync poller: heuristic find-window (5 newest, -30s, 12x10s), 30m poll budget,
job-step batches (trigger step in first batch, completed-job re-report quirk G-P4),
terminal update from conclusion; ctx-cancel reports terminal ABORTED (never SUCCEEDED).
- Config compat (blockrunner: block, private-key order, SPRING_PROFILES_ACTIVE alias),
cert/NoOp decryptor, event-driven reporter seam, R12 single-run exit (G-P11 tightening).
- Persona wiring: cmd/github (+ bbrunner github subcommand), mgmt on 8102, per-app
Dockerfile + runner-config.yml (baked dev key, umbrella §10.5).
- Gate: thresholds += internal/github 90 (91.3%, no exclusions), depguard github group.
Template fit-check (§4.6) passed unchanged. Because 06B is not landed in this base
(phase-6a-manual), the ExternalCallError/DecryptInputs shapes it promised are implemented
package-locally for now and should be reconciled to the shared meshapi twins at
consolidation. JVM teardown (steps 10-11) and CI flips are deferred to consolidation to
keep this slice disjoint from the parallel 6b/6c siblings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…em ledger) Ports the phase-7 detail plan from the planning branch so phase-7-cleanup carries it; this branch is rebuilt on phase-6d-github (not phase-6a-manual) since 06B/06C/06D already landed in sibling worktrees and are prerequisites per the plan's own header.
Runs every A1-A14 verification step against phase-6d-github (fast-forwarded in, since 06B/06C/06D had already landed in sibling worktrees). task test (-race), task lint and task coverage are all green. A2 (JVM/Gradle gone) fails structurally, not as residue: PLAN_DETAIL_06D's own step-11 "JVM endgame" was never executed on phase-6d-github, and every downstream phase-7 ledger row/migration step assumes it already happened. Resolved by folding that catch-up into step 0 of this plan's migration sequence rather than rewriting scope. A5/A9/A13 also fail today but are direct consequences of A2 or already-anticipated ledger/cross-repo items (L6, L21/L22, CROSS_REPO_TODO.md) needing no plan change. A1/A3/A4/A6/A7/ A8/A10/A11/A12/A14 verify cleanly (A3/A10 with expected path/package drift from earlier phases' own sanctioned decisions). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… deprecation audit, docs
Final phase of the single-Go-binary refactor (PLAN_DETAIL_07_cleanup.md). Consolidates
the parallel slices and closes the deferred-item ledger.
JVM endgame (step 0): delete the Gradle/Kotlin tree (block-runner-core/, the four Kotlin
module dirs, gradle wrapper, build.gradle/settings.gradle/gradle.properties),
containers/jvm.Dockerfile + entrypoint-jvm.sh, the flake.nix jdk21/ktlint toolchain, and the
gradle/kotlin .gitignore + .dockerignore residue.
CI reshape (D14, L1/L2/L3): ci.yml -> lint + test + images jobs; build-images.yml Go-only;
new opt-in e2e.yml (real tofu/terraform download) gated behind the `e2e` build tag +
`task test:e2e`, keeping default CI hermetic.
slog (§8/L11): controller persona bootstrap (cmd/bbrunner) and meshapi/auth.go migrated off
stdlib log; internal/* is now log/slog-only, enforced by a new AST guard test
(internal/build/loggingstack_test.go) that stands in for the infeasible depguard "log" deny.
Deprecation audit (§7/L9), config flip (L12: run-controller sample job templates
SPRING_PROFILES_ACTIVE -> EXECUTION_MODE, old form still honored), file hygiene
(.editorconfig JVM sections, k8s manifests -> containers/run-controller/k8s/,
.agents/skills/backend-go rewrite), verification sweeps, plan-file disposition to
docs/plans/, README + docs/ARCHITECTURE.md + docs/DEPRECATIONS.md.
Paid off the temporary .golangci.yml legacy-exclusion block (L6): fixed the underlying
errcheck/forcetypeassert/makezero findings in internal/{meshapi,crypto,report} behavior-
preservingly, then removed the block.
Sanctioned behavior delta (L14, §11 change #1): the controller decrypt-failure silent hang
is fixed — a decrypt failure now reports a terminal FAILED with actionable key-mismatch
guidance via the already-accepted reportRunFailure wire shape (P5), still incrementing
run_controller_decryption_errors_total; the SilentDispatchFailure seam is removed.
L15 (single-run/handler unification) deliberately not landed (guarded should-have; glue kept
to hold the frozen single-run contract byte-identical).
task test (-race), task lint, task coverage (11/11 packages >= 90%) all green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 7 — Cleanup & Docs (final phase of the single-Go-binary refactor)
Consolidation of the parallel phase-7 slices into one always-green, reviewable PR. Closes the deferred-item ledger of
docs/plans/PLAN_DETAIL_07_cleanup.md.Base note: opened against
phase-6a-manualper the orchestrator instruction; the working branch was stacked onphase-6d-github, so this diff also carries the 06B/06C/06D content that had already landed on the shared worktree.What landed
block-runner-core/, the four Kotlin module dirs, the Gradle wrapper/*.gradle,containers/jvm.Dockerfile+entrypoint-jvm.sh, theflake.nixjdk21/ktlinttoolchain, and the gradle/kotlin.gitignore/.dockerignoreresidue.ci.yml→lint+test+images;build-images.ymlGo-only; new opt-ine2e.yml(real tofu/terraform download) behind thee2ebuild tag +task test:e2e, so default CI is hermetic for the first time.cmd/bbrunner) +internal/meshapi/auth.gomigrated off stdliblog;internal/*is nowlog/slog-only, enforced by a new AST guard test (internal/build/loggingstack_test.go)..editorconfigJVM sections removed; k8s manifests →containers/run-controller/k8s/(with a commented-out healthz probe example);.agents/skills/backend-gorewritten;SPRING_PROFILES_ACTIVE→EXECUTION_MODEin the controller sample (old form still honored).PLAN_*.mdand the supporting refactor records →docs/plans/;README.md,docs/ARCHITECTURE.md,docs/DEPRECATIONS.md.internal/{meshapi,crypto,report}(behavior-preserving) and removed the temporary.golangci.ymlexclusion block.Sanctioned deltas (§11)
FAILEDwith actionable key-mismatch guidance via the already-acceptedreportRunFailurewire shape (P5), still incrementingrun_controller_decryption_errors_total. The formerSilentDispatchFailuresilent-timeout quirk and its seam are removed. This is a customer-facing error-path wire-behavior change — STOP-D asks for explicit review of the exact new shape; please confirm meshStack's coordinator is happy with an active FAILED on decrypt failure (it was already silent-until-timeout before). Happy paths are byte-identical.persona=/run=attributes replace the[RUN CONTROLLER]/[TF RUNNER]prefixes (never a wire contract; local-dev-stack readiness markers updated lock-step, tracked inCROSS_REPO_TODO.md).lint/test/images) — branch-protection required-check names change; coordinate at merge.Deliberately not landed
cmd/tf/main.gowas kept to hold the frozen single-run wire/exit contract byte-identical; recorded as a follow-up indocs/ARCHITECTURE.md.Reviewer heads-up (pre-existing finding)
The repo's depguard
filesglobs (internal/X/**/*.go) match nothing (they need a leading**/), so the D11 layering rules are currently not enforced — pre-existing across all phases, out of this phase's scope, flagged for a follow-up. The single-logging-stack invariant is instead enforced by the new AST guard test.Gates
task test(-race),task lint(0 issues),task coverage(11/11 gated packages ≥ 90%) all green.🤖 Generated with Claude Code