Shared scratchpad for whoever (or whichever agent) picks this up next. Keep it
short — a line or two per item, delete entries once they are done. This is not
a changelog; git log already is one.
Architectural decisions that back these items live in ADR.md. Always update ADR.md in the same change when you add, change, or close a Handoff item that implies a decision (Handoff = list; ADR = context / decision / consequences).
Last updated: 2026-10-22 (terminal login guard, agent namespaces visible to attendees)
| Piece | Repo |
|---|---|
| Helm charts, Showroom content, dashboard | this repo (rhpds/lightwell-workshop), pushes straight to main |
Catalog item lb1815-lightwell-tenant |
rhpds/agnosticv, rh1-2027/ — via PR |
| OpenCode agent prompts, EDA rulebooks, AAP playbooks | rhpds/lw-sdlc-opencode — via PR |
| Acme sample apps (3) | redhat-ads-tech/{wire-transfer-svc,benefits-mgmt-app,report-generator-app} |
| Acme deployment manifests | redhat-ads-tech/acme-deployment — Helm chart for ArgoCD app-of-apps |
| AO Path 2 operator helper + docs | this repo — ao-trigger-helper/ (scripts + docs/) |
-
The Showroom terminal has no identity until the attendee logs in (22 Oct).
publish-remediation.shdied silently after--- Discovering Nexus ---on a tenant that had skipped Module 2'soc login. The terminal mounts a PVC at/home/lab-userbut no kubeconfig, soocfalls back to the Showroom pod's own service account (editin its own namespace only);oc whoami \| sed 's/user-//'then producedsystem:serviceaccount:<guid>-showroom:showroomand the script looked for a namespace of that name. The route lookup's error went to/dev/nullandset -edid the rest. The script now checks the identity up front and prints the login command; Module 3 has its ownLogging In to OpenShiftsection before the first command, worded to skip if Module 2's login is still live. Quick triage:~/.kube/with onlycache/and noconfigmeans they never logged in. → ADR-057 -
Attendees can see the agent's namespaces (22 Oct).
sdlc-<guid>(theopencodedeployment) andsdlc-sandboxes-<guid>(the ephemeral verification Jobs) had no RoleBinding for the tenant user, so they were invisible in the console project list. Added twoviewbindings tobootstrap-tenant/templates/rbac.yaml—view, notedit, becausesdlc-<guid>holds the GitLab PAT and the model API key. Applied by hand to all 11 tenants oncluster-2jr49; other live clusters need an Argo sync or the sameoc create rolebinding. The runtimepr-test-mr-*namespaces are still not visible. → ADR-058 -
Showroom content polish (20–21 Oct). Renamed modules: "Automated Dependency Updates" / "AI-Assisted Remediation". Removed "Path 1"/"Path 2", "deterministic", "mock" terminology. Downplayed Nexus (say "artifact manager", note JFrog Artifactory). Downplayed Renovate (one option, not the star). Converted Module 2 to numbered steps with
+continuation. Split Module 2 and 3 into nav sub-sections. Added "Using This Environment" guide to Lab Overview. Made all images clickable. All console references now "Switch to the Tab tab". Fixed lab title ("Lightwell Patch to Production"), site URL, and Showroom header logo. Removed WIP warnings. -
Resource sizing for 10+ tenants (20 Oct). TPA server 8Gi→512Mi, importer 8Gi→128Mi requests; workdir PVC 32Gi→2Gi. Quay removed entirely. AO workers scaled to 4 replicas. Storage quota 50Gi→20Gi. 10 tenants now fit on 3 workers (was impossible before). Capacity: ~10 tenants/3 workers, ~20/4 workers, ~30/5–6 workers (16 CPU/64Gi each).
-
Build revision footer on all 3 sample apps (20 Oct). Multi-stage Containerfile (Tomcat apps) or
USER 0RUN (Spring Boot) writesrevision.jsonat build time with commit SHA, message, and date. Pipeline passes--build-arg=COMMIT_SHAand single-quoted'--build-arg=COMMIT_MSG'(spaces survivexargs -n1in buildah task). Footer displays "Build: sha · message · X minutes ago". -
Pipeline run names include app name (21 Oct).
build-push-to-main→wire-transfer-svc-build,benefits-mgmt-app-build,report-generator-app-build. Easier to identify in OpenShift console. -
AgnosticV PRs (20 Oct):
- #28658 — Showroom tabs: Renovate, ServiceNow, ArgoCD + reorder
- #28660 — Move workshop UI language from cluster to tenant CI
- #28661 — Bump tenant health retries to 180
-
benefits-mgmt-app fixes (20 Oct). SSO disabled (fixed 15s load delay). Red "ACME INC. BENEFITS MANAGEMENT" header. Mock API servlet.
-
All seven validation rows now measured, and a green table is the merge gate (21 Oct). Lives in the app repos (
redhat-ads-tech/*,.tekton/build-pull-request.yaml), not here. Licence was Pending only onbenefits-mgmt-appbecausepublish-remediation.shpublished a licence-less artifact — commons-text inherits Apache-2.0 fromcommons-parent→org.apache:apacheand the script only copied inline<licenses>; h2 declares its own, which is why it looked app-specific. Both the script and the check now walk<parent>. A newbaseline-teststask re-runs the suite against the target branch's pom and diffs per-test outcomes, soRegression scanreports a real behavioural diff (~43s extra, measured).report-validationthen exits non-zero unless every row ispassed—warningandpendingblock too. Renovate MRs are unaffected (nolw:validationmarkers → early return), so Module 2 is untouched. Two traps worth remembering, both of which gave confident wrong answers rather than errors:-Dmaven.build.directorydoes not moveproject.build.directory(the baseline wrote intotarget/, failed on "Operation not permitted", and then "compared" the head run with itself for a fake "0 regressions"), andtally()'s recursive glob counted the baseline too, doubling 4/4 into 8/8. → ADR-056Rollout state. GitHub (
redhat-ads-tech/*) is the source of truth and has all three repos updated, so any newly ordered tenant gets it. Existing tenants were seeded at creation and still run the old pipeline unless the file is pushed into their GitLab copy — done so far only for8djv7(benefits-mgmt-app,wire-transfer-svc). Nothing is left broken by the mix: a tenant with the merge gate on but the old pipeline still merges normally, because the oldreport-validationexits 0 with rows Pending. The only difference is whetherRegression scanis measured. -
Merge is now gated on the PaC pipeline (21 Oct).
job-create.yamlsetsonly_allow_merge_if_pipeline_succeedson the three Acme app repos (sixthseed_apparg, defaulttrue;acme-deploymentpassesfalsebecause nothing runs a pipeline there). External Status Checks would need Ultimate — this GitLab is 17.4.1 CE — but PaC registers its PipelineRun as anexternalpipeline on the MR head commit, so the stock CE setting gates on it. Verified on8djv7/benefits-mgmt-app: blocked asci_must_passfrom the moment the MR opened (before any pipeline existed — the window fails safe),mergeableonce green, AO auto-merged 9s later with no405. Side benefit: ADR-050's fail-open is now enforced by GitLab rather than by the AO poller. Existing tenants need the setting applied to their app repos. Readdetailed_merge_status, notmerge_status— the latter only reports conflicts. → ADR-055 -
Module 3 screenshots + two content bugs (21 Oct). Added the three screenshots asked for in the 20 Oct sync:
m3-ao-task-output.png(Shaaf's per-task output panel),m3-hr-auto-approve-false.png(theAuto-approve?False branch, red callout), andm3-pipeline-run.png(closing the// TODO screenshot). While wiring them: the "Merge Request the Agent Wrote" section showed a wire-transfer/h2 title under instructions to open benefits-mgmt-app, and claimed the coloured dot "encodes severity" — it encodes the application's risk class, which is why a 9.8 CVE shows 🟢. Both fixed. -
Harness bug: the high-risk path was a silent no-op after the first run (21 Oct). Nexus fires only on component created;
next_suffixread the rhlw number from the pom onmain, which the high-risk MR never updates because it is never merged. So every re-run republishedrhlw-00001, no webhook fired, no AO run, no MR — and publish still reported PASS. Now takes the higher of the pom andlightwell-remediated. -
Tenants would not schedule — TPA reserved 16.5Gi each (20 Oct). A new tenant on
cluster-sgdqlparked with the showroom podPending:0/6 nodes are available: 3 Insufficient memory, 3 node(s) had untolerated taint(s). Not a config bug — capacity. Measured oncluster-8tskw(identical 3×~58.7Gi workers): 20.6Gi and 3.6 CPU per tenant, 16.5Gi and 2 CPU of it TPA, because the upstream chart defaultsmodules.serverandmodules.importertocpu 1 / memory 8Giand we never overrode it. Real usage: importer 8Mi (it is disabled by design), server 171–267Mi. Fixed by James in0c21217: serverrequests 100m/512Mi, limits 2Gi, importerrequests 50m/128Mi, limits 1Gi, andworkingDirectory32Gi → 2Gi.27ffde7also removes Quay, freeing ~15Gi more. Per tenant → roughly 5Gi / 1.5 CPU, so ten tenants fit. Two things to watch: the explicit limits supersede the LimitRange 8Gi default, so the server is now OOM-killable above 2Gi (7.5× its observed peak, measured at rest), and existing tenants keep the old reservation until their TPA Application re-syncs. → ADR-054 -
Fleet test runner + parallel-safety fixes (20 Oct).
qa-automation/test-fleet.shrunstest-lab-flow.shacross many guids at once (--jobs N,--stagger S, args after--passed through) and samples the cluster while they run, so a failure can be attributed to the lab or to capacity — peak Pending pods, new FailedScheduling, OOMKilled, Evicted, node CPU/memory, plus which checks failed in more than one tenant. Built for the 10-tenant scale test. Two latent bugs had to be fixed first, both of which would have given wrong answers rather than errors:test-lab-flow.shused fixed/tmp/m3-snow.txtand three siblings (now/tmp/lw-<guid>/), andpublish-remediation.shused fixed/tmp/lw-remediation.txtand/tmp/lw-placeholder.txt(nowmktemp), which would cross-contaminate tenants. Students run one at a time and were never affected. Dry-run against a stub flow script before any cluster use. -
argo-notifications-edanamespace fix (19 Oct). The Job was templated into{{ .Release.Namespace }}(openshift-gitops) while requesting thelightwell-tenant-jobsSA, which only exists inlightwell-tenant-{guid}. No pod was ever created, so the Sync hook never reached a terminal phase and every tenant order parked at wave 4 until the RHDP playbook timed out — three orders failed this way before it was spotted. Now uses thebootstrap-tenant.jobNamespacehelper like every other Job in the chart. The three live tenants were recovered by hand (terminate op, strip the hook finalizer, delete the stale Job, hard refresh), so a fresh order is still the only real proof — worth one before the next demo. → ADR-036 -
publish-remediation.shJAR fix (18 Oct). Natale's change to republish the real upstream JAR meant Syft readversion=2.13.5from the embeddedpom.properties, so the SBOM showed no version change. Fixed: the script now patchespom.propertiesand the embeddedpom.xmlinside the JAR to reflect the.rhlwversion before upload. Note which artifacts this applies to:jackson-databind,commons-fileuploadandflyway-coreall embedMETA-INF/maven/.../pom.properties, so the patch is what makes Syft report the remediated version.h2embeds none, so the patch is a no-op there and Syft falls back to the filename, which already carries the.rhlwversion. Both paths work; only the first needs the fix. -
publish-remediation.shTPA route fix (18 Oct). The SME's TPA route discovery usedoc get routein the TPA namespace, which the tenant user can't access. Added|| trueand a fallback toserver-tpa-{guid}.{domain}(the API-serving route). -
ArgoCD app-of-apps for sample apps (16 Oct). Per-tenant AppProject (
acme-{guid}), ArgoCD Application pointing at GitLabacme-{guid}/acme-deployment, role-based RBAC so tenant users see only their apps. Deployment repo cloned into GitLab during provisioning.seed-app-tagsJob reads commit SHAs after initial builds and updatesvalues.yaml. Promotion is a separate git commit (not pipeline-driven) — ready for AO integration. Superseded:promote-to-deployment-repomerged in the app repos' PR #1, so a merge to main now bumpsimageTaginacme-deploymentfrom the pipeline and ArgoCD deploys it. Verified end to end. Upstream deployment chart:redhat-ads-tech/acme-deployment. -
Sample apps now runnable (16 Oct).
report-generator-appswitched to H2 in-memory + landing page at/.benefits-mgmt-appruns on Tomcat with mock API servlet (benefits plan data).wire-transfer-svcrebranded to "Acme Inc. Bank". All three have unit tests (mvn verify) and PaCrun-testspipeline step. -
AO Path 2 operator helper in
ao-trigger-helper/— scripts POST Nexus-shapedCREATEDto EDA (needs-approval/auto-approve) and reset Acme repos. AO-context docs:DEMO-FLOW.md(narrative),architecture.md(wiring),DEMO-FLOW-INVESTIGATION.md(live snapshot). Index:docs/README.md. → ADR-031 -
OpenCode web UI shows agent sessions. Route
opencode-uinow targetsopencode-ui-gate(HTML bootstrap injecting/appinto the SPA's open- projects localStorage). Fresh browsers list Impact/Verifier transcripts; AAP still calls Serviceopencode:4096directly. Live ont8s2nwithout restarting the opencode pod. → ADR-029 -
Multi-app AO path (15–16 Oct). Built the multi-app AO+SNOW+classify path (15 Oct), then hardened merge/OpenCode/classify, removed dual-Verifier, fixed approval branching, and taught Impact/AO to short-circuit when the remediating GAV is already on
main(16 Oct). Live ont8s2nas AO v18; playbooks on tenant GitLablw-sdlc-opencode-local+ AAP sync. Details: → ADR-028, ADR-022–027 -
demo-updatelanded onrhpds/main; tenant Argo sync restored. Chart- AO canvas + snow-mock are on
main;lightwell-tenant-t8s2nsyncs from there. → ADR-028
- AO canvas + snow-mock are on
-
OpenSpec multi-app smoke leftovers closed. Benefits auto-approve with a real TPA hit; Renovate skip on classify comment. Verifier still defaults to GitLab “MR open” until ephemeral OpenCode skill is fixed (
verifier_use_opencode: true). → ADR-022 -
Tenant renders and syncs with no Lightwell Network credentials. Nexus Secret no longer uses
required;nexus-reconcile.pyskips repos it cannot create instead of exiting 1. Credentials can be injected later withautomation/gitops/bootstrap-tenant/scripts/inject-env-secrets.sh. → ADR-001 -
Dashboard is off by default (
sdlc.dashboard.enabled: false). Its credential-sync hook sits at sync-wave 3 and used to block waves 4–5. → ADR-002 -
EDA job templates are created per AAP organization, not Controller-wide by name, and all five exist. Previously the second tenant onwards got none. → ADR-003
-
sdlc.opencodePermission: allow— OpenCode defaults bash toask, and a headless pod parks on that prompt forever. → ADR-004 -
Sandbox namespaces are per tenant:
sdlc-sandboxes-<guid>and ephemeral prefixpr-test-mr-<guid>. Agent prompts readSDLC_SANDBOX_NAMESPACE/EPHEMERAL_NS_PREFIXfrom env (lw-sdlc-opencode#3, merged). → ADR-005 -
LiteLLM/MaaS wired through AgnosticV: model
qwen3-235b-a22b(switched from aliasqwen3-235bafter Vertex AI shut it down 2026-10-21), 14d virtual keys. → ADR-006 -
EDA rulebook defaults from
sdlc.orchestrator.enabled(true →sdlc-remediation.ymlAO bridge; false →sdlc-remediation-legacy.yml). SCM defaultrhpds/lw-sdlc-opencode. See AO item below. → ADR-007, ADR-009 -
impact-analyzerno longer hangs. It carriedbash."*": "ask", and agent-level permission beats the chart'ssdlc.opencodePermission: allow(the image's/app/opencode.jsonloads afterOPENCODE_CONFIG_CONTENT). Fixed in lw-sdlc-opencode#4, built assha-aad125a. → ADR-004
A full pipeline run was proven on tenant 49t9b: Nexus CREATED → EDA →
Query TPA → Impact Analyzer → OpenCode → GitLab MR → MR Verifier.
On tr59k-1 the AO path was also proven: Nexus → EDA Start → AO canvas
(Query TPA → Impact) → MR → Resume → MR Verifier.
Re-proven end to end on 8vjv7 (2026-10-09) on image sha-aad125a, with
com.h2database:h2:2.2.219.rhlw-00001: cache → Start Orchestrator → AO →
Query TPA → Impact Analyzer → MR !1 (one-line pom.xml bump) → Resume
Orchestrator → MR Verifier, ~90s from trigger to MR.
- Renovate operator deployed at cluster level (
bootstrap-infratemplates/renovate/— ArgoCD child Application pointing at the upstream OCI Helm chart v6.4.0,nonroot-v2SCC, OpenShift Route for the web UI, CRD install via template mode). Per-tenant SA + SCC +renovate-tokensecret deployed bybootstrap-tenant. Student creates the RenovateJob CR on demand from Showroom. Tested end-to-end on tenantkbdhw: Renovate discoveredlw-demo-help-app-kbdhw, foundwoodstox-core 6.0.3.rhlw-00001in the tenant Nexuslightwell-java-remediatedrepo, and created MRs. Uses the upstreamlightwell-experience/renovate-config:java-remediatedpreset viaextends. → ADR-012 - ServiceNow mock deployed per tenant via
bootstrap-tenanttemplates/snow/(namespacesnow-<guid>, Deployment, Service, Route). Default: ConfigMap-mounted app fromfiles/snow-mock/on UBI Python (comments API + UI). Override withsnow.image.SNOW_URL/SNOW_SVC_URLontenant-integration. → ADR-022 - ArgoCD admin password Job removed from
bootstrap-infra. Theset-admin-passwordJob raced with the OpenShift GitOps operator. Use the operator-managed password or "Log in via OpenShift" instead. - Nexus startupProbe added to the tenant StatefulSet (10s interval, 60 attempts = 10 min). Nexus 3.76 takes 3-5+ minutes to start and the liveness probe was killing it before startup completed.
- App-of-apps refactor of
bootstrap-infra. The monolithic chart (56 templates, single ArgoCD app) is now an app-of-apps that creates 8 child Applications, each pointing at a standalone subchart underautomation/gitops/cluster/. Components sync in parallel (GitLab + AAP at wave 0, Quay at wave 1, AO at wave 3). No AgnosticV changes —repo_pathstaysautomation/gitops/bootstrap-infra. → ADR-014 - Per-tenant GitLab groups with three Acme sample apps. Replaced the shared
lightwellgroup and singlelw-demo-help-appwith per-tenantacme-{guid}groups containingwire-transfer-svc(high risk, AI target),benefits-mgmt-app, andreport-generator-app.gitlab.appSeedsis a configurable list.REMEDIATION_APP_GITLAB_PATHpoints towire-transfer-svc. → ADR-015 - Renovate-bot GitLab user created per tenant with a scoped PAT. MRs from
Renovate show as
Renovate Botinstead ofroot. PAT stored asrenovate-tokensecret insdlc-{guid}. → ADR-012 - Keycloak login label set to "Lightwell Patch to Production Workshop" via
a sync hook Job that patches the realm
displayName. - BuildConfig
triggers: []drift fixed — OpenShift strips empty trigger arrays, causing perpetual OutOfSync. - Renovate operator web UI wired to Keycloak SSO. OIDC client
renovate-uicreated by a sync hook Job inbootstrap-infra. Students log in with their tenant SSO credentials and only see their own RenovateJobs viaspec.access.adminUsers(matched on Keycloak email, e.g.user-{guid}@demo.redhat.com). → ADR-030 - Renovate namespace moved from
sdlc-{guid}(not accessible to tenant users) to{username}-renovatewitheditRoleBinding + custom Role for RenovateJob/RenovateProject CRDs. Students create RenovateJobs from the Showroom terminal. - All hook Jobs switched to
BeforeHookCreationdelete policy (12 Jobs across tenant and cluster charts). Prevents "spec.template immutable" errors when scripts change between syncs. - SDLC Inventory auto-created in tenant AAP org. AAP 2.5+ doesn't create a default inventory; the EDA bootstrap script now creates one with a localhost host.
- Build namespace RBAC — tenant user granted
admininbuild-{guid}so they can view pipeline runs. - Repo name alignment —
lightwell-java-remediated→lightwell-remediatedacross showroom and RenovateJob config (matches ADR-021 naming). - GitHub Pages + spell check workflows added. Antora site deploys to
Pages on push to main; cspell checks
content/**/*.adocon push and PRs. - Showroom Module 2 rewritten — Renovate web UI flow (OIDC login,
Trigger button, Logs),
publish-remediation.shattachment script,maven-publicregistry URL, per-user access control, enterprise callouts (auto-merge, 100+ ecosystems, Vault/ExternalSecrets), SLSA Level 3 compliance, tradeoffs section. - PaC build infrastructure per tenant.
build-{guid}namespace with 3 PaC Repository CRs (one per app fromgitlab.appSeeds), GitLab push webhooks pointing at the PaC controller, and secrets (GitLab PAT, Nexus push credentials, TPA OIDC). Repository CRs pass tenant-specific params (image-registry,image-name,tpa-url,issuer-url) so.tekton/PipelineRun YAML is identical across tenants. Toggle:build.enabled..tekton/+Containerfileadded to all 3 source repos. Pipeline verified end-to-end: git-clone → maven → buildah → SBOM → TPA upload. → ADR-020 - Maven builds via Nexus group with Lightwell hosted repos. Removed
redundant
maven-central-{guid}andmaven-releases-{guid}(Nexus is already per-tenant). Createdlightwell-remediatedandlightwell-validatedhosted repos (aligned with official Lightwell Network naming). Updatedmaven-publicgroup to include them (search order: lightwell repos first, then defaults). Pipeline Maven task usesMAVEN_MIRROR_URLpointing at the group URL. Lightwell proxy repos in the ConfigMap stay untouched (EDA/OpenCode need them). Hosted repos need seeding with-rhlw-*artifacts separately. → ADR-021 - OCI container registry in per-tenant Nexus. Added Docker Bearer Token
Realm, OCI hosted repository (
oci) on port 5000, OCI Route (nexus-oci-{guid}.{domain}), and short-URL Route for the UI (nexus-{guid}.{domain}). Storage increased from 20Gi to 50Gi.nexus-reconcile.pycreates the OCI repo and enables the token realm idempotently. Anonymous read, admin push. Nexus bumped from 3.76.1 to 3.96.4-ubi for native OCI 1.1 referrers (Tekton Chains stores attestations without tag-based fallback). Community Edition EULA is accepted automatically by the reconcile script (required since 3.77). Verified end-to-end on fresh tenantt3st1: OCI repo created, image push + catalog working, no manual intervention. → ADR-019 - Per-tenant TPA via upstream Helm chart. Removed the shared
rhtpa-operatorandTrustedProfileAnalyzerCR fromcluster/rhads. Each tenant gets atpa-{guid}namespace with PostgreSQL, S3 (ObjectBucketClaim), TPA server, importer, and a short-URL Route. RHDA backend is optional (tpa.rhda.enabled). Keycloak OIDC clients (tpa-frontend,tpa-cli) created once at cluster level in thessorealm; client secret copied per tenant. Default scopes:create/read/update:document;delete:documentrequirestpa-adminrole. Templates live inbootstrap-tenant/templates/tpa/(not a subchart — Helm subchart value scoping preventsguidfrom reaching the subchart without AgnosticV changes). Old seed templates (seed-tpa-keycloak.py, demo-sbom ConfigMap) removed. Dataset uploaded per tenant via a Job usingtpa-cliclient credentials. → ADR-018 - OpenShift Pipelines + Tekton Chains keyless signing added to the
rhadschild app. Pipelines operator installs viasubscription-pipelines.yaml(channelpipelines-1.24, Automatic). A PostSync Job patches TektonConfig for multi-PVC support and configures Chains to use TAS Fulcio/Rekor with the cluster Kubernetes OIDC issuer. TAS namespace is nowtrusted-artifact-signer(waslightwell-tas). Keycloak OIDC clienttrusted-artifact-signeris created by therhtas-keycloak-setupSync Job. If signing is broken: check thetektonconfig-patchJob logs inopenshift-pipelines— the Job now logs a WARNING if the Chains InstallerSet audience patch did not apply. → ADR-017
00af. AO → AAP job log link 403 for tenant user → ADR-053.
create-aap-tenant only granted gateway Organization Member. That
cannot list JTs or read /jobs/{id}/stdout (verified user-cng2l:
jobs count 0, job 785 → 403). Gateway Organization Audit is a no-op
for Controller jobs. Fix: also attach Controller org Auditor via
/api/controller/v2/roles/{auditor}/users/. Chart updated; cng2l
patched live (Auditor + Member). Reused tenants need the same attach
or a re-run of create-aap-tenant.
→ ADR-053
00ae. SNOW titles → ADR-052 (lab on cng2l).
Nexus → Start forwards [unknown] despite TPA CVEcve_id: unknown as an AAP extra_var, which
shadows every later set_fact: cve_id=CVE-… from query-tpa. Parent PATCH
and child create read the extra_var and wrote [unknown] even when
package_info.cve_id was correct. Fix (local lw-sdlc-opencode, injected
into AAP project 7 on cng2l): pick first ^CVE-[0-9] from
package_info / resolved_cve_id / cve_id; normalize unknown → CVE-
at Start + parent create. Verified e2e: commons-text
1.9.rhlw-102844 → INC titles [CVE-2022-42889], benefits !10 merged,
parent Resolved; h2 2.2.224.rhlw-102845 → [CVE-2023-48795], wire !18
left open. Still push lw-sdlc-opencode (not on SCM yet).
→ ADR-052
00ad. auto_approve merge waits 2 minutes → ADR-041 (lab on cng2l).
Start Merge JT pauses merge_delay_seconds=120 before GitLab merge so
the open MR is visible. Node renamed “Wait 2m + merge…”. Inject
merge-gitlab-mr.yml + republish Start; push lw-sdlc-opencode.
00ac. Deploy fan-out → wire finalize fail → ADR-049 (lab on cng2l).
Bridge POSTed Deploy for every AO path on shared Argo Healthy; wire had
no child → finalize failed → completed_with_errors. Fix: bridge POSTs
only the app in the promote commit title; finalize soft-skips when no
parent. Bridge promote-scoped + finalize soft-skip live on cng2l; push
lw-sdlc-opencode for SCM durability.
→ ADR-049
00ab. Deploy finalize empty parent (lab cng2l). Deploy did fire
(bridge 20116e7b → lifecycle OK with parent_incident=INC0000001);
finalize failed because Deploy EDA has no trigger.parent_incident.
Fix: finalize ← ${lc_deployed.artifacts.parent_incident}; lifecycle
never exports "" (AAP drops it); finalize also looks up by
cmdb_ci/gitlab_path. Deploy v5; reinjected; catch-up finalize
#679 successful → parent Resolved. Still push lw-sdlc-opencode.
Superseded for fan-out noise by 00ac / ADR-049.
- Module 2 (Renovate) — one real bug, now fixed; the other was my
test. Diagnosed with
LOG_LEVEL=debugon the Renovate job (at default level it logs nothing about which versions it retrieved or discarded).
Real bug, fixed: the repo's renovate.json extends
github>lightwell-experience/renovate-config:java-remediated, whose
custom manager hardcodes
"registryUrlTemplate": "https://packages.redhat.com/lightwell/java/remediated".
The tenant gets 401 Dependency lookup unauthorized from it. Module 2's
packageRules.registryUrls override does not displace a manager's own
registryUrlTemplate — confirmed with both custom.regex and regex.
Fix: module 2's RenovateJob now redefines customManagers inline with the
tenant Nexus, instead of trying to patch the preset's manager.
Not a bug — my test harness: the second symptom (no update proposed
even with the right registry) was caused by the test generating
clock-derived suffixes. The versioning regex maps the rhlw digits to
major, so rhlw-121618 is a major increment of ~121k and Renovate
silently drops it:
Skipping …@2.13.5.rhlw-999999 because major increment 878381 exceeds maxMajorIncrement 500.
With an increment of 1 it works immediately — verified, Renovate opened
[Lightwell] Update dependency com.fasterxml.jackson.core:jackson-databind.
test-lab-flow.sh now derives the suffix from the pom's current rhlw
number + 1, matching the lab's rhlw-00001 / rhlw-00002 sequence.
Worth knowing generally: a suffix that jumps by more than 500 is discarded without an error. Anyone hand-triggering a demo should keep the sequence tight.
Still outstanding: publish-remediation.sh overwrites
maven-metadata.xml rather than appending, so Nexus lists only the newest
rebuild.
- CVE lookup fixed and verified; CVSS still blank.
lw-sdlc-opencodePR #8 is merged and confirmed on8gmp5(AAP project synced toa8386b6, Prepare job 328). All four new tasks ran and the MR facts now resolve from the Lightwell advisory:
cve_id: CVE-2022-42889
cve_title: Apache Commons Text prior to 1.10.0 allows RCE ...
cve_description: <full upstream text>
cvss_score: "" <- still empty
Remaining gap: cvss_score stays blank because the seeded CVE records
carry no base_score, so the MR renders "CVSS Score: not scored" even
with the CVE correctly identified. Either the dataset needs records with
scores, or the MR body should fall back to the advisory's own
severity[].score (publish-remediation.sh writes a CVSS v3 vector into
every RHLW advisory, so the data is already there).
Requires advisories published with the coordinate in the summary — i.e.
publish-remediation.sh at 3bda34f or later.
00000a. Blast radius 504s on a reused tenant. Hit three times on 8gmp5:
"benefits-mgmt-app: packages: HTTP Error 504" from
/api/v3/sbom/{id}/packages, after which classify fails closed with
"No blast radius ... Skipping remediation". The same call returns 200 in
0.46s when TPA is idle, and a fresh tenant (jq89x) produced MRs fine.
Correlates with accumulated SBOMs — 8gmp5 had 7 for one app and
Postgres at ~500m CPU. Fails safe, but it silently drops an app from the
blast radius, so a demo re-run on a warm tenant can look like the
remediation simply ignored an application. Worth a retry/backoff in
tpa_blast_radius.py rather than one attempt.
- TPA CVE dataset silently does not ingest on a fresh tenant.
Both tenants on
cluster-klrqlcame up withGHSA-OSV: 285andCVElist-v5: 0, while8gmp5on the older cluster has 222. Thetpa-dataset-upload-cveJob reportsHTTP response code: 201and "Upload and ingestion successful" — nothing lands. Consequence: no CVE records at all, so/api/v3/vulnerabilityis empty and every remediation MR rendersCVE: not referenced, which is what made Module 3 look like it had no advisory data.
Workaround (verified): re-POST the zip by hand and it ingests in ~7s —
curl -k -X POST "$TPA/api/v3/dataset?labels.importer=CVElist-v5" -H "Content-Type: application/zip" -H "Authorization: Bearer $TOK" --data-binary @lb1815-cve.zip → 201 with the full file list, and the count
goes 285 → 507.
Unproven hypothesis: the CVE and OSV upload Jobs both sit at sync-wave 3 and POST concurrently; one appears to be accepted and dropped. Serialising them (wave 3 and 4) is the obvious thing to try. Not yet tested.
Worth fixing properly — a fresh tenant is what every attendee gets, and this one fails silently with a success message.
- Gotcha, not a task: script ConfigMap edits never reach an existing
tenant. The tenant Argo Application sets
ignoreDifferences: [{jsonPointers: ["/data"], kind: ConfigMap}]— a rule from the RHDP side, in neither this repo nor agnosticv. File-backed script ConfigMaps are frozen at creation-time content while Argo reportsSynced. Affectsbridge.py,snow-mockcode/static,nexus-reconcile.py, eda/ao bootstrap sources,opencode-ui-gate.
Not a lab problem: every attendee gets a newly ordered tenant, and creation is not a diff — a fresh order always has current content. This only bites a lab developer reusing one tenant across chart changes.
Workaround when it does bite (a plain sync will not do it, hard refresh or not):
oc create configmap <name> -n <ns> --from-file=<key>=<path> \
--dry-run=client -o yaml | oc apply -f -
oc rollout restart deploy/<name> -n <ns>
How it surfaced: Shaaf's ADR-049 promote-scoping looked broken on 8djv7
when his code was fine — the tenant was running a 401-line bridge.py
against 493 in the repo, so wire-transfer-svc (never merged) was marked
deployed three times. Patched by hand on both 8djv7 and 8gmp5.
Decided not to engineer around it.
→ ADR-051
- FIXED (pending PR merge + AO republish):
auto_approvemerged with the validation table stillPending. On8djv7, AO merged the MR ~33 s after it opened whilebuild-pull-request— the pipeline that fills the table — did not start until 43 s after the merge. All seven rows stayedPendingand SNOW still recorded "auto-merged after the verifier passed".verifier_okonly checks that the Verifier JT returned successful, and those JTs report success as soon as they launch the async agent, so the gate could not fail. The h2 →wire-transfer-svcrun cleared the table logic itself (6/7 ✅ Passed, 🔴 HIGH, correctly left open) — it was only ever a race on the auto path.
Fix: merge-gitlab-mr.yml polls the build-pull-request commit
status on the MR head and asserts green before merging. Inside the merge
playbook rather than as a separate AO node (Shaaf's call, and the better
one): check and merge stay atomic, and the Start canvas keeps a single
node. The fixed merge_delay_seconds=120 pause is dropped — the real
wait already leaves the MR open ~110 s, which is the window it existed
for, and a sleep cannot tell a slow pipeline from a failed one.
This reverses the earlier "not adopted" position (previous ADR-050), decided by Natale.
To land: PR #7 must merge, then AO republish. Until both, tenants auto-merge unvalidated exactly as before. Then re-run both scenarios and confirm the auto-merged MR comes out with a filled table. → ADR-050
000a. Idea (Natale): pre-pull Tekton images with a DaemonSet. Cold cluster image pulls delay first PaC runs; a prepuller would make Module 3 timings more predictable.
0a. Remediation MR body — verified end to end on a freshly seeded tenant
(54dp7, 18 Oct). Seed -> Nexus event -> AO -> MR
[LW] 🔴 Bump h2 ... to fix CVE-2023-48795 -> pipeline fills the
validation table -> merge -> promote -> ArgoCD deploys. Agent side is
merged (sha-7447545).
→ ADR-032
0b. Upstream PRs: six merged (18 Oct), three open. The app repos own
their pipelines — the chart does not inject them (removed in 24f3114,
because James iterates on the same PaC pipelines and a seed-time copy
would silently overwrite him). #1 pipelines and #2 integration
tests are merged in all three repos. #3 artifact checks is open
in each (wire-transfer-svc,
benefits-mgmt-app,
report-generator-app):
those commits were pushed after #1 had already been merged, so they
never reached main.
0c. Validation table: six of eight rows are real. Unit and integration tests from surefire/failsafe; binary integrity verified against the checksum Nexus records (an integrity check — the tenant mirror ships no detached GPG signature, so it is labelled as such); licence compared between the base and remediated POM, normalised so h2's "MPL 2.0 or EPL 1.0" vs "MPL 2.0" + "EPL 1.0" is not a false alarm; SBOM generated by the CycloneDX Maven plugin and asserted to list the bumped coordinate; dependency compatibility asserts the pom declares what the build resolved. Regression scan is still Pending and needs a decision — "behavioural diff" means running the suite against the target-branch baseline too, which doubles build time. Nothing is ever reported green without having been measured.
The auto-approve path cannot happen→ fixed (real TPA blast radius).query-tpa.ymlmatches GAV → SBOMname→acme-{guid}/{app}(tpa_blast_radius.py). Live oncng2l: flyway → benefits; h2 → wire. Parent/child SNOW + canvas in workshop +lw-sdlc-opencode(ece73c4). Still push localfetch-app-policy.yml(auto_approveallowlist) and republish AO + sync AAP for CI/CD gates (1a). → OpenSpecao-operator-async-remediation; ADR-033
1a. Merged+Build poll PaC (ADR-039) — live on cng2l (lab inject).
AO Merged v3: SNOW merged → Poll PaC → SNOW build_*; on
build_failed, lifecycle JT fails after commenting (fail_on_bad_outcome)
so the AO run is Failed. Build WF retired; bridge quiet. Controller
project scm_update_on_launch=false + playbook inject (push
lw-sdlc-opencode for durability). TPA SBOM upload 500 remains.
→ ADR-039
1b. Prepare Remediation (ADR-040) — live on cng2l (lab inject).
Start v4: eda_trigger → JT SDLC Prepare Remediation (parent
SNOW → TPA → classify → comment → children) → apps_loop. Playbook
prepare-remediation.yml + JT #24 injected; push lw-sdlc-opencode
when you want SCM durability.
→ ADR-040
1c. snow-mock list filters cmdb_ci + guid. Lifecycle lookup uses
GET /api/v1/incidents?cmdb_ci=…&guid=…&open_only=1 instead of scanning
200 opens client-side. Create playbooks stamp guid on parent/child.
Roll snow-mock + reinject snow-lifecycle-event.yml on live tenants.
1e. benefits missing GitLab MR→EDA webhook (cng2l). Chart ensure_mr_webhook
should cover every remediation_owner=ao seed; live tenant only had it on
wire-transfer. AO auto-merge of benefits MR !1 did not start Merged WF.
Hook added by hand on benefits; confirm create-gitlab job covers all AO
seeds on next provision / re-run.
1d. Prepare set_stats concat (cng2l Start v5 fail). AAP concatenates
string/list set_stats across plays in one job →
parent_incident=INC…INC…, duplicated ao_apps without
child_incident on the first item → Impact/loop fail. Fix:
sdlc_suppress_set_stats in imported plays + one final export in
prepare-remediation.yml. Reinject before next smoke.
- Automation Orchestrator is installed by
bootstrap-infra(AO CR + CNPG) and wired per tenant viasdlc.orchestrator(enabled, webhook paths,existingSecretfromao-bootstrap). Keep SCM onrhpds/lw-sdlc-opencode. TheAPP_INTEGRATION_URL_ALLOWED_HOSTSgap is closed — verified on8vjv7, the AAP route host is present on the backend, background-worker and worker, and the AAP integration created cleanly. → ADR-008 - CI on
rhpds/lw-sdlc-opencodeis dead (org/Actions policy). Lab path is now shared BuildConfig → ImageStream (OpenShift integrated registry) viabootstrap-infraopencodeImage(ADR-013). Tenants leavesdlc.opencodeImageempty. External quay.io pins remain an override only. → ADR-013 - First infra sync after enablement must finish Job
opencode-image-publish(ImageStream build) before OpenCode pods can pull. BumpopencodeImage.gitRef/tagand tenantsdlc.opencodeImageTagtogether. nexus.lightwellNetworkis still empty in AgnosticV, so a fresh tenant comes up green with no Lightwell proxy repos and therefore no Nexus webhooks — the whole chain has no entry point until credentials are injected (scripts/inject-env-secrets.sh, thennexus-reconcilere-runs and creates both repos and both webhooks). Planned fix is a fake Lightwell Maven mirror. Real upstream (packages.redhat.com/lightwell/java/remediated/) is browsable with the credentials and carriesjackson/andwoodstox/trees, e.g.jackson-databindhas 12.rhlw-*rebuilds — useful for seeding. Naming: the chart createsredhat-packages-{validated,remediated}-{guid}. The namelightwell-java-remediatedused in ADR-012 appears nowhere inautomation/orcontent/— settle which is canonical before writing the seeding Job or the RenovatehostRules. Contents: seed only coordinates an Acme app declares directly in itspom.xml— SBOM matching is broader than the pom, and the agent dies ondependency … not found in pom.xmlotherwise. Today onlycom.h2database:h2qualifies (upstream has noorg/apache/struts/), sobenefits-mgmt-appandreport-generator-appcannot yet be targets. → ADR-016- Renovate module content (Module 2) tested end-to-end (18 Oct).
publish-remediation.shpublishes a remediated JAR (real upstream binary with patchedpom.propertiesso Syft reports the.rhlwversion) and OSV advisories to TPA. Renovate detects the bump, student merges, Tekton builds, TPA shows reduced vuln count. Screenshots added. Remaining: AgnosticV needs Showroom UI tabs for TPA and Nexus (currently patched manually on test clusters viaSHOWROOM_UI_CONFIG). - Older tenants on
zlvnrare missing EDA job templates (provisioned before the org-scoping fix). Re-run the bootstrap job or re-order them. publishing-house/spec/modules/*.mdandspec/design.mdstill say Artifactory / Deep Agent / RHACM. The Showroom content undercontent/was updated to Nexus / Lightwell OpenCode Agents / TSSC + DevSecOps; the outlines were not.
podman-compose.yamlhas a$PID/$!interpolation bug and an obsoleteversion:key.- Running the local Antora preview rewrites
site.yml(the dev-mode container injects/antora/lib/dev-mode.js). Revert it before committing.
-
Never use
.Release.Namespaceinbootstrap-tenant. The chart is deployed by an Application whose destination isopenshift-gitops, so.Release.Namespacerenders asopenshift-gitops— not the tenant namespace you probably meant. Jobs belong in{{ include "bootstrap-tenant.jobNamespace" . }}, which is where thelightwell-tenant-jobsSA lives. A Job pointed elsewhere never gets a pod (error looking up service account ...: not found), and as a Sync hook it then blocks the whole tenant sync forever.helm templaterenders it happily; only a real order catches it. -
Argo needs both a hard refresh and a sync when chart files change. An explicit sync alone picked up a changed ConfigMap key on
cd2fp, but on54dp7a change tofiles/snow-mock/app.py(pulled in by.Files.Get) was invisible: the app reportedSyncedat the right revision, did not list the ConfigMap as OutOfSync, and the live object had not been touched for three days.argocd.argoproj.io/refresh=hardfirst, then sync. Pods also need a rollout restart afterwards — a ConfigMap change does not restart them. -
The EDA activation's extra_vars never reach AO-launched job templates. AO passes its own payload, so anything set only on the activation is invisible. Per-app data has to travel in
.lightwell/policy.json, which classify carries intoaffected_repos. -
Demo Path 2 entry =
publish-remediation.shonly. Do not usetrigger-nexus-webhook.shfor demos/e2e — testing/debug when Nexus→EDA webhooks are missing. Real path: publish → Nexus CREATED webhook → EDA → AO. -
trigger-nexus-webhook.shsynthesises a version that is not in Nexus. Fine for exercising AO, but any build of the resulting MR dies onCould not find artifact. To test a build or a merge, trigger with a versionpublish-remediation.shactually published. -
After a hibernation everything takes minutes to serve. GitLab, Keycloak, TPA and the AAP gateway all return HTTP 000 until they are ready. macOS also caches the negative DNS from while the cluster was down — use
--resolve, or fire requests from inside the cluster. -
TPA OIDC setup Job must not be a Sync hook. ArgoCD processes hooks separately from regular resources. If the OIDC Job is a hook, the RBAC (Roles/RoleBindings) in the same sync never gets applied before the hook runs, causing a permissions loop. The Job is a regular resource at sync-wave 0; RBAC is at sync-wave -1.
-
TPA server needs 8Gi memory. The per-tenant LimitRange defaults to 8Gi per container. Lower limits cause OOMKill.
-
Migrating from shared to per-tenant TPA leaves orphaned resources (old seed ConfigMaps, keycloak RBAC) that ArgoCD reports as
PruneSkipped. Delete them manually or provision fresh tenants. -
Watch for fail-open. Four separate bugs here had the same shape: the component reported success and left the tenant non-functional. Prefer a loud failure at render or bootstrap time over a green sync that does nothing. → ADR-011
-
Hook Jobs now use
hook-delete-policy: BeforeHookCreation— the old Job is deleted before the new one is created on each sync. Absence between syncs means the previous run completed (check ArgoCD sync result for success/failure). -
AgnosticV
common_passwordcan lack digits.lookup('password', ..., chars=['ascii_letters', 'digits'])is a uniform sample — ~2% chance of all letters, no digits. AO rejects passwords without 3+ character classes. Workaround: the AO bootstrap script should append1!to the password, or AgnosticV should guarantee complexity. -
Do not trigger within a minute of a tenant sync. The sync re-runs
eda-bootstrap, which can disable/enable the activation and restart the webhook listener; a Nexus POST landing in that gap is lost silently. Seen on8vjv7: listener pod created 23:46:59, trigger at 23:47:00, no chain. Check theactivation-job-*pod age inaapbefore firing. -
A cached artifact cannot re-trigger. The webhook fires on component created, so re-pulling the same GAV does nothing — use a different version.
-
MR Verifier runs
verify-mr-<iid>as a Job insidesdlc-sandboxes-<guid>, not in an ephemeralpr-test-mr-*namespace. Looking for the latter and finding nothing is not a failure. -
Both OpenCode job templates start the agent asynchronously and report
successfulimmediately. A green AAP job says nothing about whether the agent worked — read theopencodepod log or the session transcript. -
Debugging a live tenant needs cluster-admin (
cluster_admin_agnosticd_sa_tokenfrom the order), not the tenant login. The tenant user is Forbidden onsdlc-<guid>andopenshift-gitops.