Tags

Tags give the ability to mark specific points in history as being important
  • v0.1.0

    Release: gitlab-simulation v0.1.0
    gitlab-simulation v0.1.0, tagged retroactively on 2026-09-28 for the 2026-07-06 release.
    
    ## [0.1.0] - 2026-07-06
    
    ### Added
    - Initial release. GitLab self-managed and Duo simulation provisioner for Google Compute Engine, packaged as a Claude Code plugin.
    - Three install bases: `caproni` (helm/k3s cloud-native), single-VM `omnibus`, and `dev` (gitlab-org/gitlab source + build-images container, for `rspec`/`rubocop`/`rake` without a GDK).
    - Duo Agent Platform / AIGW seeding via canonical upstream primitives, self-hosted model wiring, and an 8-section verifier.
    - AIGW slow-model scenario: a custom/patched, non-mock image and a timeout harness.
    - Airgap/offline archetypes: single-plane (in-VM mirror, then seal) and the two-plane low-to-high diode (connected forge feeding a sealed enclave).
    - SDLC-utilization seeding layer: GPT for structure, an API-driven activity layer per SDLC stage, and per-persona shaping (see `gce/seeding.md`). Exercised end to end against a fresh `omnibus` instance seeded via `manifold seed --users 60 --yes`: 60 users / 224 MRs / 186 issues / 176 pipelines collected and 5 of 6 domains lit up (Security read zero -- no Ultimate license, no runner -- the expected result for a default instance).
    - Per-simulation Playwright MCP browser verification (the multiplexer).
    - Scheduled `drift-check` CI job: static upstream-symbol drift detection against `gitlab-org/gitlab` source, no VM required.
    - `sims/` routing and fidelity test batteries, plus a `just sims-check` gate that a tag-triggered CI job re-runs before release.
    - Runner registration: `just duo-runner-register` (tag-scoped `gitlab--duo`, for Duo workload jobs) and `just ci-runner-register` (general-purpose, `run_untagged: true`, for untagged CI/scan jobs), each binding a base-correct executor (shell on `omnibus`, podman-docker on `caproni`).
    - Contributor decision-record formats under `docs/`: ADR, RFC, and PRD templates with an index, and a `CONTRIBUTING.md` pointer. ADR-0001 records the single-front-door decision.
    - Brand assets (hero, avatar, favicon) following the GitLab Public Sector design language.
    - Backing-service access recipes in `gce/gce.md`: the `gdk psql` / `gdk rails c` /
      ClickHouse-client substitutes per base (omnibus wrappers, caproni `kubectl`/CNPG,
      dev's `bundle exec rails console`). Caproni's DB and ClickHouse recipes, and dev's
      `psql` database name and user, remain lab-pending per ADR-0002; dev's profiling-gem
      availability is flagged lab-pending rather than asserted.
    - Documented `caproni-dev` as a roadmap base: caproni's running services plus a
      source mount, the planned full-stack tier for operations `dev`'s source-only
      checkout cannot reach. Not a live `--base` yet.
    - Companion pointer to the official GitLab AI skills (`gitlab-org/ai/skills`): this
      environment is the GDK-free substitute their contribution workflows run against.
    
    ### Changed
    - Description tightened to a 491-character, trigger-dense form with an explicit SaaS/durable negative boundary (see `sims/fire-battery.md` for the routing battery that validated it).
    - Front matter scopes execution to `just` plus `Read`, making the justfile the enforced front door.
    - `just` is the single interface: every operation is a `just` recipe, and `simulation.sh` is the engine it wraps. A new `ssh-cmd` recipe carries a command with quotes or pipes to the VM intact, so ad-hoc access no longer drops to the engine directly. See ADR-0001.
    
    ### Fixed
    - Marketplace self-named `gitlab-simulation` (previously `gitlab-public-sector`) so the sibling repository's marketplace can be added alongside without a name collision; the install command is `/plugin install gitlab-simulation@gitlab-simulation`.
    - `just seed <scenario>` forwards the documented `GPT_*`/`SEED_*` knobs into the remote seed run (a declared `SEED_ENV_KNOBS` allowlist). Previously only `ACCESS_TOKEN`/`GITLAB_URL` reached the seed script's shell, so a knob set in front of `just seed` was silently dropped.
    - Corrected the seeding admin-PAT note (the laptop-state `root_pat` and the on-VM PAT are distinct tokens; `just seed` uses the laptop one), documented the `just ssh` quoting limitation with the `ssh-cmd` alternative, and raised the `omnibus` setup-time estimate to ~15-25 min.
    - `simulation.sh` `usage()` now lists every subcommand it dispatches (it had omitted `rails-config`, the `airgap-*` set, `two-plane`, `validate`, and `enable-flows`), and a `rails-config` recipe was added so every subcommand has a front-door recipe.
    - Documented the `enable-flow` precondition (the top-level group's foundational-flow allowlist must be populated first, via `enable-flows` or `dap-bootstrap`) and fixed a stale `enable_flow.rb` docstring that pointed at a file which never existed.
    
  • v0.2.0

    Release: gitlab-simulation v0.2.0
    gitlab-simulation v0.2.0
    
    ## [0.2.0] - 2026-09-28
    
    ### Added
    - `just real-model` swaps the mock model for a real provider behind the same AI
      gateway, and `real-model off` swaps back. Two routes reach one, and both
      authenticate as the VM: Vertex through the instance service account, Bedrock
      through a federated role assumed with a GCE identity token a refresher keeps
      current, so neither leaves a long-lived secret on the VM. `just real-model
      proxy bedrock|vertex` puts a pinned LiteLLM container on the loopback
      interface between the gateway and the provider so the composed request becomes
      readable, and `just real-model param-strip` sends one request twice, once with
      `temperature` removed at the proxy, to demonstrate which one a provider that
      has stopped accepting the parameter refuses.
    - `just airgap-probe` reports the egress a VM actually has, on any base, without
      changing anything. It classifies exit codes rather than reading every failure
      as proof of isolation, and probes IP literals, a high port, and a second
      network so a dead resolver cannot decide the verdict.
    - The omnibus base has an airgap path of its own: `airgap-mirror` builds a dnf
      package repository served over loopback, and `airgap-verify` proves the
      instance stays healthy, that `dnf` reaches the mirror while the remote
      repositories are unreachable, and that `gitlab-ctl reconfigure` completes under
      the seal.
    - `just advanced-search` (omnibus) stands up a single-node OpenSearch or
      Elasticsearch on the VM, points GitLab at it, indexes the instance, and proves
      the result with `GET /groups/:id/search?scope=notes` -- a scope no other
      configuration serves. The engine major comes from the version matrix GitLab
      publishes, read at run time rather than transcribed. It refuses without a Premium
      or Ultimate license, and refuses on caproni and dev, where the settings would
      apply and nothing would index. `just advanced-search-status` reports engine
      health, the settings and index state without changing anything.
    - `SIM_OWNER` labels every created VM with whoever it belongs to, so a machine
      running several simulations shows which is which. Unset reads `unattributed`.
    - `just geo list` reports every Geo pair holding state on the machine.
    
    ### Fixed
    - The caproni base no longer sets `CLOUD_CONNECTOR_SELF_SIGN_TOKENS`, which
      caproni-demo's values carry and customer installs never do. With it, GitLab
      self-signed Cloud Connector tokens for every feature and the GitLab Duo health
      check ran its development probes, so a finding about token issuance or the
      health check did not describe a customer instance. Self-hosted features still
      self-sign, which is all the flow chain needs. The install fails if the key
      appears in a form the bootstrap cannot remove.
    - No command prints or keeps a minted credential's characters.
      `duo-runner-register` and `ci-runner-register` logged the first eight
      characters of the runner authentication token and now log only its length.
      The runner token and root PAT mints no longer write receipts, which held the
      full values under `~/.gitlab-simulation/receipts/` after teardown. The caproni
      install summary, which `up` shows, names the secret holding the root password
      instead of printing it, and the Geo failover report gives the registry
      notification secret's length instead of its value.
    - `dap-bootstrap` (through `self-hosted-model`) gives the self-hosted model to
      every feature a foundational flow on the running instance resolves to, read
      from its flow catalog, instead of `duo_agent_platform` alone. A flow such as
      `developer/v1` resolves its own feature, and with no model selected for it an
      offline-licensed instance sent the workflow token request to
      `cloud.gitlab.com` and failed with "Could not obtain Duo Workflow token". A
      feature the instance withholds or rejects is reported and skipped, and
      `trigger-preflight` now checks the flow's own feature.
    - `airgap-unseal` and `down` never delete a seal rule another simulation may
      still hold. Rules left under the pre-rename shared names
      (`sim-enclave-deny-egress`, `sim-enclave-allow-internal`) are deleted only
      when no other `sim-` instance exists and no instance carries the shared
      `sim-enclave` tag; otherwise they are left in place and reported with the
      instances that may depend on them. The shared tag is always removed from the
      instance being unsealed, so a simulation sealed before the rename unseals.
    - `airgap-seal` names its network tag and firewall rules for the instance it
      seals (`<instance>-sealed`, `<instance>-seal-allow-internal`,
      `<instance>-seal-deny-egress`), and `two-plane` names the forge's registry rule
      for its forge. Every sealed simulation in a project used to carry one shared
      tag and one shared pair of rules, so a second seal silently reused the first
      one's rules and tearing either simulation down unsealed the other.
    - `airgap-seal` waits until the seal is observably in effect rather than
      returning as soon as the firewall rules are recorded. Rules are enforced some
      seconds after `gcloud` returns, so a verify run in that window reported
      NOT_SEALED against a seal that was merely still landing.
    - The omnibus negative leg forces a metadata refetch. Plain `dnf makecache`
      refetches only what it considers expired, so shortly after the mirror step it
      exited 0 without contacting anything and reported the remote repositories as
      reachable on a sealed host: it was measuring cache freshness while claiming to
      measure reachability.
    - The omnibus package mirror serves on 18080 rather than 8080, which is Puma's.
      The collision surfaced as "the mirror is not answering", and had GitLab
      answered the mirror's health path at all the positive control would have been
      measuring the wrong process. The mirror step now refuses a busy port and names
      what holds it.
    - The airgap negative control is a comparison instead of an assertion, so it can
      now fail in both directions. `airgap-seal` records whether egress was reachable
      before it creates any firewall rule and refuses to seal when it was not, and
      `airgap-verify` refuses a verdict without that baseline and reports
      INCONCLUSIVE where it cannot tell a seal from a broken probe. Previously a host
      with egress wide open and only its resolver broken confirmed the airgap.
    - `airgap-mirror` and `airgap-verify` refuse on a base they do not fit, including
      an unknown base, where they used to half-run. On omnibus the base-agnostic
      negative control passed while every k3s step behind it no-opped or died, so the
      run confirmed the reassuring half and skipped the proving half.
    - caproni's mirror-pull check fails when the verify pod never starts. It ran a
      20-iteration wait and then fell through printing nothing, so a mirror that
      served nothing produced no output and no failure.
    - `two-plane verify` uses the same negative control as `airgap-verify` rather
      than its own inline copy, which had the same defect.
    - A license file is applied through `GitlabSubscriptions::UploadLicenseService`
      rather than saved onto the `License` model directly, so an offline cloud
      license provisions its `add_on_products` into add-on purchases at upload
      instead of waiting for the nightly cron. The service declines an online cloud
      license, which belongs on the activation-code path, and `just license` names
      that cause rather than relaying the admin page's markup.
    - The add-on step no longer creates an add-on purchase when provisioning
      produced none. It prints what the license carried against what was
      provisioned and says the two disagree, because a purchase the harness made
      itself is indistinguishable from one the product produced.
    - The docs no longer imply a caproni simulation carries ClickHouse or NATS. The
      `gitlab-dev-stack` chart can ship both; the caproni-demo values the simulator
      installs it with disable both, so no base provides either. `just drift-check`
      now asserts those switches, and `MAINTENANCE.md` registers the absence.
    - `--gitlab-version` is documented per base: an exact package version on
      omnibus, the Helm chart version on caproni, a git branch on dev. Only omnibus
      reaches a chosen GitLab application version.
    - Geo state is per pair (`SIM_GEO_PAIR`), where a single `geo.json` previously
      made the archetype the one base two simulations could not share. A second
      `geo up` overwrote the instance names the first cluster's `geo down` reads,
      leaving two VMs running with nothing tracking them. `geo up` now refuses when
      the state file or either instance name is already taken, and a command run
      against a pair with no state names the pairs that do have some.
    - `bootstrap-gitlab-dev.sh`'s `run.sh` wrapper exports `PATH`/`GOFLAGS` and marks
      the bind-mounted clone a Git safe directory before the wrapped command runs
      inside the container. The container's Git refused the host-owned mount as
      "dubious ownership," which surfaced misleadingly as "fatal: not in a git
      directory" from TestEnv's Gitaly tasks.
    - The `dev` base's clone loop falls back to a commit-pinned archive tarball
      once its 20 retries are exhausted. `gitlab.com` sheds `git upload-pack` under
      load well before it sheds the API or archive endpoints, so the fallback
      resolves the branch to a SHA and downloads the archive instead of failing.
    - The `dev` base bootstrap removes `config/redis.yml` after generating the
      `.example` configs. Left in place, its cluster-ports template overrides
      `resque.yml`'s standalone Redis, which the rest of the setup assumes.
    
    Pre-1.0: under SemVer 0.x a minor version may carry breaking changes (ADR-0002).