{"slug": "faq-an-ai-note-job-is-not-a-gitlab-environment", "title": "FAQ: An AI Note Job Is Not a GitLab Environment", "summary": "A developer published a cautionary guide arguing that wiring an AI review bot into GitLab CI/CD pipelines conflates distinct layers: a merge-request comment is an API write, not a deployment, and should not be registered as a GitLab environment. The post offers proposal-only YAML sketches showing how to fail closed when a model pin is missing, why a resource group enforces concurrency rather than a model version lock, and why an interruptible job flag is a cancellation hint rather than a rollback of an already-posted comment.", "body_md": "A model launch post is not a GitLab environment.\n\nI keep those layers apart when the news cycle speeds up.\n\nAre you about to wire a comment bot into `production`?\n\nThis week's feeds are full of agent demos.\n\nYour pipeline is still a rules file, a runner, and an environment record.\n\nI wrote this to split those layers before any trial starts.\n\nDisclosure: This article was prepared as part of MonkeyCode's product outreach.\n\nOperator notes describe free model access and a free server option.\n\nI am not copying a token total or a machine size.\n\nI am also not stating a time limit or a region.\n\nConfirm those details on the current product pages first.\n\nPosting a review comment means you deployed something real.\n\nGitLab environments track deployments, stops, and sometimes a public URL.\n\nA comment is an API write, not an environment entry.\n\nKeep the bot out of `environment` unless it actually deploys.\n\nUse a plain job with merge request rules instead.\n\nFail closed when the model pin is absent.\n\n```\n# Proposal only. I have not submitted this to a live project.\nai_note_check:\n  stage: test\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n  script:\n    - test -n \"$MODEL_REF\" || { echo \"missing MODEL_REF\"; exit 2; }\n    - echo \"would_review=1\" > review-plan.txt\n  artifacts:\n    paths:\n      - review-plan.txt\n    expire_in: 3 days\n```\n\nThat job fails closed when the pin is missing.\n\nIt does not call a model, and it does not touch production.\n\nTreat the file as a plan, not as a review result.\n\nA resource group keeps the same model for every run.\n\nPeople copy that key and skip the variable store.\n\nThe job name then pretends to be a version lock.\n\nA resource group limits concurrent jobs that share one name.\n\nIt does not select a model id or a prompt file.\n\nIt also does not select a vendor account for you.\n\nPin the model with a protected variable or a committed file.\n\nUse a resource group only to avoid overlapping comment writes.\n\nIf you need both controls, configure them on separate lines.\n\n```\n# Still a proposal. Concurrency is not a model pin.\nai_note_check:\n  resource_group: mr-notes-$CI_MERGE_REQUEST_IID\n```\n\nAsk yourself this before you copy that resource key.\n\nDo you need mutual exclusion, or do you need a version pin?\n\nA yes to both still requires two different settings.\n\nKilling an AI job is harmless because the job is interruptible.\n\nThe flag looks like a safety property in review.\n\nIt is only a cancellation hint for newer pipelines.\n\nThe flag lets a newer pipeline cancel the older job.\n\nIt does not roll back a comment that job already posted.\n\nMake the side effect idempotent, or skip the remote write.\n\nRecord the commit SHA in the note body you control.\n\nBuild the key before any client code runs.\n\n```\n# Unexecuted sketch. Do not echo secrets.\nnote_key=\"${CI_MERGE_REQUEST_IID:-0}-${CI_COMMIT_SHORT_SHA:-none}\"\nprintf 'note_key=%s\\n' \"$note_key\"\n```\n\nWould a second run post a second note with a new key?\n\nIf yes, your retry story is not done yet.\n\nFix the key before you enable a live token.\n\nThe free server option behaves like an environment stop action.\n\nThat story collapses two owners into one sentence.\n\nCleanup then belongs to nobody on the team.\n\nAn environment `on_stop` job is your cleanup hook on your runner.\n\nA free server is only what the current offer says it is.\n\nI will not describe its hardware, region, or lifetime here.\n\nName the cleanup owner before you start any trial.\n\nIf the offer does not name a stop action, you still need one.\n\nYour runner remains the place that executes that hook.\n\n```\n# Proposal. This cleanup does not call any vendor.\nstop_review_scratch:\n  stage: cleanup\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"ai-pin-check\"\n      when: manual\n  script:\n    - rm -f review-plan.txt\n    - echo \"local scratch removed\"\n```\n\nThat example only deletes a local workspace file.\n\nIt is not a receipt that a remote process stopped.\n\nKeep the manual job until you own a real stop path.\n\nA public agent repo automatically uses your clone strategy.\n\nThe badge feels like a configuration inheritance.\n\nYour runner never reads the badge that way.\n\n`GIT_STRATEGY` is a GitLab runner variable on your own job.\n\nThe upstream license does not set it for your pipeline.\n\nA news post does not set it either.\n\nSet fetch depth and strategy in the job you own.\n\nThen review the client for unexpected network calls.\n\nUnknown outbound behavior is a fail on protected branches.\n\n```\nvariables:\n  GIT_STRATEGY: fetch\n  GIT_DEPTH: \"20\"\n```\n\nFill that network judgment from the repo you cloned.\n\nDo not fill it from this FAQ or from a launch thread.\n\nWrite unknown when the README does not answer you.\n\nA token number in a post is your pipeline budget.\n\nThe figure then gets pasted into an environment description.\n\nNext week the page can change while your YAML stays.\n\nBudgets change, and this article is not a pricing source.\n\nOperator notes say free model access exists, plus a free server option.\n\nThey do not authorize me to freeze a token count here.\n\nPaste the live figure into the merge request body.\n\nInclude the date you actually read that page.\n\nReload the page on the day you enable the job.\n\nIf the page and this FAQ disagree, trust the page.\n\nA blog number is not an environment quota.\n\nAn undated repost is not a better source than the page.\n\nI accept a job URL, a commit SHA, and a dated offer note.\n\nI do not accept a chat screenshot as pipeline proof.\n\nI do not accept an undated token figure from a repost.\n\nPrimary docs beat a secondary recap, including this one.\n\nIf you cannot link the page, write unknown in the table.\n\nUnknown stays a fail for any protected branch job.\n\nUse this table on the merge request itself.\n\nI have not scored a real fleet with it.\n\nThe rows are gates, not a quality score for model prose.\n\n| Check | Pass looks like | Fail looks like | \n|---|---|---|\n| No production environment | YAML lacks `environment:` | Bot listed as a production deploy | \n| Model pin is separate | `MODEL_REF` protected or committed | Pin exists only in a chat | \n| Side effect is idempotent | Note key includes the SHA | Retries can duplicate notes | \n| Cleanup owner is named | Manual job or your `on_stop` | Free server assumed as cleanup | \n| Offer date is in the MR | Dated line from the live docs | Number copied from an old post | \n| Clone settings are local | `GIT_STRATEGY` set on the job | Public repo assumed to inherit it | \n\nThree failed rows means you stop the trial.\n\nA clean table still means a scratch branch only.\n\nProduction stays out of scope until a human owns each row.\n\nThis plan is a proposal for a scratch project.\n\nI did not execute it while writing this draft.\n\nLabel the pipeline result as unexecuted until you run it.\n\n`ai-pin-check`.` MODEL_REF` and confirm the job exits 2.`verify-me` only.\n\n```\n# Unexecuted checker. It only inspects a local YAML string.\nsample = \"\"\"\nai_note_check:\n  stage: test\n  script: [\"true\"]\n\"\"\"\nbad = \"environment:\" in sample and \"production\" in sample\nprint(\"production_environment\", bad)\n```\n\nExtend that check to your real file before you trust it.\n\nA green printout is not a GitLab lint result.\n\nRun the official linter if your instance allows that call.\n\nThese commands are reminders, not a captured session.\n\nI have not run them against your group.\n\n```\n# Unexecuted. Prefer CI variables over shell history.\ntest -n \"${MODEL_REF:-}\" && echo \"ref present\" || echo \"ref missing\"\nif test -n \"${MODEL_TOKEN:-}\"; then echo \"token set\"; else echo \"token unset\"; fi\n# Never printf the token itself.\n```\n\nA missing ref should stop the job.\n\nA set token should print status only, never the secret.\n\nIf your log shows the secret, rotate it before the next push.\n\nUse free model access for a human trial on a fake diff.\n\nUse the free server option only when the live offer matches policy.\n\nThe product does not become your environment, your runner, or your audit log.\n\nIf you need those roles, configure them in GitLab yourself.\n\nRemove every product mention and this checklist should still help.\n\nThat is the bar I want for a trial note like this.\n\nSkip the trial when the repository holds regulated data.\n\nSkip it when policy requires a signed quota or a fixed region.\n\nSkip it when you only want to chase this week's model headline.\n\nA headline will not name your cleanup owner.\n\nA headline will not register a runner for you either.\n\nWait until the table has an owner for every fail.\n\nI do not know your protected branch rules or your runner tags.\n\nI did not measure latency, cost, or comment quality.\n\nFree access can change or disappear without this page updating.\n\nThe script will not detect a malicious prompt by itself.\n\nIt only catches the identity mix-ups listed above.\n\nAdd your own data-policy review before any real diff leaves the laptop.\n\nOpen the YAML for the job you wanted to call a deployment.\n\nWhich table row fails first on that job?\n\nIf a trial still helps, read the current MonkeyCode pages.\n\nDate that reading in the merge request, then run the fail-closed job on a scratch branch.", "url": "https://wpnews.pro/news/faq-an-ai-note-job-is-not-a-gitlab-environment", "canonical_source": "https://dev.to/gitlab_3188/faq-an-ai-note-job-is-not-a-gitlab-environment-enm", "published_at": "2026-10-08 11:07:33+00:00", "updated_at": "2026-10-08 11:19:28.282232+00:00", "lang": "en", "topics": ["mlops", "developer-tools", "ai-agents"], "entities": ["GitLab", "MonkeyCode"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/faq-an-ai-note-job-is-not-a-gitlab-environment", "markdown": "https://wpnews.pro/news/faq-an-ai-note-job-is-not-a-gitlab-environment.md", "text": "https://wpnews.pro/news/faq-an-ai-note-job-is-not-a-gitlab-environment.txt", "jsonld": "https://wpnews.pro/news/faq-an-ai-note-job-is-not-a-gitlab-environment.jsonld"}}