Goal
Create a deployment-time setup experience where the user edits one friendly YAML file, uses a helper command to validate/render it, and deploys without needing GitLab project IDs, clone URLs, or unsafe secret handling.
User experience we want
First deploy should feel obvious
- User copies one short YAML example.
- They change a Jira project key and GitLab repo path.
- They run
validate and optionally show.
- They do not look up project IDs or clone URLs.
- If the repo can use the default PAT, they do nothing extra.
- Startup clearly reports the accepted mapping.
Adding another team should feel like repeating the same step
- User adds another YAML binding using the same pattern.
- If that team needs a different PAT, they add a friendly
credential_ref.
- They run the same helper commands again.
- They do not change formats or embed secrets.
Troubleshooting should feel safe
- Helper validation errors point to a specific YAML binding.
- Auth errors mention the missing or unknown
credential_ref, not a raw token.
- Startup summaries show which repo uses the default credential vs a named credential ref.
Concrete v1 decisions
- Use Jira project keys like
PROJ.
- Use GitLab repo paths like
group/repo.
- v1 runtime behavior stays one Jira project -> one repo -> one MR.
- No backward compatibility or migration layer will be built.
- No live editing of running services.
- No admin page in this phase.
- ACA/Terraform is the primary deployment path to optimize for; Helm should follow the same model.
- The YAML file plus helper command is the user-facing workflow.
Recommended user-facing model
YAML source of truth
defaults:
target_branch: main
credential_ref: default
bindings:
- jira_project: PROJ
repo: group/service-a
- jira_project: OPS
repo: group/platform-tools
target_branch: develop
credential_ref: platform_team
Helper command responsibilities
The helper command is the main deployment-time UX layer.
Recommended v1 commands:
validate PATH: schema checks, duplicate Jira projects, required credential aliases, readable errors
show PATH: human-readable summary of bindings and credential usage
render-json PATH: emit the runtime JSON for JIRA_PROJECT_MAP
Optional later commands:
render-terraform
render-helm
Runtime input
The deployment layer uses helper output to populate the runtime JSON env var consumed by the app at startup.
Secret and credential model
- Default token remains
GITLAB_TOKEN.
- A binding may optionally specify a friendly
credential_ref.
- Raw PATs never appear inside the YAML mapping file.
- For ACA/Terraform, inject the same credential set into both controller and task-runner.
- Named repo credentials should use an alias-based env convention such as
GITLAB_TOKEN__PLATFORM_TEAM.
- The helper command should report which credential aliases are required by the YAML file.
- The runtime resolves
credential_ref to the matching injected secret at startup and in task-runner startup.
Current code reality to account for
Settings() and TaskRunnerSettings currently load from environment variables only, once at process startup.
JIRA_PROJECT_MAP is currently a raw JSON env-var string parsed in main.py.
- The app currently assumes a single global
settings.gitlab_token across repo resolution, clone, push, MR creation, webhook/MR review, /copilot comment handling, and GitLab polling.
- ACA/Terraform and Helm currently inject one global GitLab token into controller and runner.
Architecture in service of the experience
Shared mapping core
Build one shared mapping core used by:
- helper command validation/rendering
- startup parsing and validation
- repo resolution
- tests
This core should define:
- YAML and JSON binding models
- defaults handling
- resolved runtime models
- validation rules for duplicate Jira projects, missing repos, missing credential refs, and invalid repo paths
Helper-first deployment flow
- User edits YAML.
- Helper validates and renders JSON.
- Deployment consumes helper output.
- App starts and re-validates the rendered config.
Credential registry
Add a startup-loaded credential registry that:
- reads the default
GITLAB_TOKEN
- reads named
GITLAB_TOKEN__* aliases
- resolves each binding to the correct token
- never logs raw secrets
Resolved project registry
At controller startup:
- parse rendered JSON from the helper-generated YAML model
- resolve repo path -> GitLab project ID using the correct token
- derive or supply clone URL
- build a registry that can answer:
- Jira project -> resolved repo context
- GitLab project ID -> resolved repo context
This registry becomes the source of truth for Jira flows and GitLab-side flows.
Credential-aware runtime flows
Replace single-token assumptions across the app so the selected mapping credential is used consistently for:
- Jira coding orchestration
- task-runner clone and push flow
- webhook/MR review orchestration
/copilot comment handling
- GitLab polling
For GitLab polling, group projects by credential reference and use one client per credential group.
Implementation Plan (Updated)
Problem
The current JIRA_PROJECT_MAP workflow requires users to manually construct JSON with GitLab project IDs and clone URLs, and any mapping change requires a full redeployment. Issue #270 defines a friendly YAML-first workflow with helper commands, credential aliases, and a resolved project registry.
This plan finalizes the implementation breakdown from issue #270 with two additions:
- Graceful shutdown — controller drains in-flight orchestration before restart.
- Hot-reload — mapping changes applied without restart via a reload endpoint;
_processed_issues cleared on reload (Jira status transitions prevent true duplicate processing).
Approach
Split into 5 PRs (issue #270 proposed 3; we add graceful shutdown + hot-reload as a separate PR and split docs/E2E into its own PR). Each PR is ≤200 diff lines, standalone, and fully tested.
Gate after PR 1: User reviews and confirms the YAML/JSON schema before any runtime changes begin.
Agent Assignments & Parallelization
| Agent |
Role |
@product |
Confirm acceptance criteria for each PR before implementation begins |
@architect |
Review schema design (PR 1 gate), credential registry design (PR 2), hot-reload design (PR 4) |
@developer |
Implement all PRs. PRs are sequential (each depends on prior), but within each PR some files can be parallelized |
@designer |
Not needed — no UI components |
| Code review agent |
Cross-model review before every PR push (per hard rules) |
Parallelization opportunities
- Within PR 1:
mapping_models.py and mapping_cli.py can be developed in parallel once the schema is agreed. Tests for each can also be parallelized.
- Within PR 2:
credential_registry.py and project_registry.py are independent modules — develop and test in parallel, then wire into main.py.
- Within PR 3: Runtime flow changes across
coding_orchestrator.py, orchestrator.py, mr_comment_handler.py, gitlab_poller.py, and jira_poller.py are independent file edits — parallelize with a fleet of developer agents.
- Within PR 5: Docs updates (
configuration-reference.md, deployment-guide.md, testing-guide.md, README.md) are independent files. E2E mock updates (mock_jira.py, mock_gitlab.py) and run.sh test additions are also independent. Full fleet parallelization.
- Across PRs: Strictly sequential due to dependencies.
PR Breakdown
PR 1: YAML mapping models + helper CLI
Goal: Make first deploy obvious — user edits one YAML file, runs helper commands, deploys.
⚠️ GATE: After this PR is merged, user confirms the YAML source format and rendered JSON output format before PR 2 begins. This prevents wasted work if the schema needs revision.
Changes:
src/gitlab_copilot_agent/mapping_models.py (new) — Pydantic models for YAML bindings (BindingConfig, MappingFile) and rendered JSON (RenderedProjectMap). Defaults handling (target_branch, credential_ref). Validation: duplicate Jira keys, invalid repo paths, missing credential refs.
src/gitlab_copilot_agent/mapping_cli.py (new) — CLI entry point with three commands:
validate PATH — schema + semantic checks, human-readable errors pointing to specific bindings
show PATH — summary table of bindings, credential usage, defaults applied
render-json PATH — emit runtime JSON for JIRA_PROJECT_MAP env var
pyproject.toml — add [project.scripts] entry for CLI, add pyyaml dependency.
tests/test_mapping_models.py (new) — unit tests for models, validation, defaults, edge cases.
tests/test_mapping_cli.py (new) — CLI integration tests for validate/show/render-json.
Does NOT change: existing project_mapping.py, config.py, main.py, or runtime behavior. The helper CLI is a standalone deployment-time tool.
Agent workflow:
@architect reviews schema design (models + CLI interface)
@developer (fleet of 2): one implements mapping_models.py + tests, one implements mapping_cli.py + tests
- Code review agent (different model) reviews PR before push
Acceptance criteria:
validate catches duplicate Jira keys, missing repos, unknown credential_refs.
show displays a readable mapping summary.
render-json output is valid input for the current JIRA_PROJECT_MAP env var.
- Error messages identify the specific binding at fault.
🚦 Schema Confirmation Gate
After PR 1 merges, the user reviews:
- The YAML source format (
MappingFile model)
- The rendered JSON format (
RenderedProjectMap model)
- The CLI output of
show and render-json
No further PRs proceed until the user explicitly confirms the schema.
PR 2: Runtime schema migration + credential registry
Goal: Replace internal JIRA_PROJECT_MAP schema with the new rendered format. Add credential registry for per-repo tokens.
Changes:
src/gitlab_copilot_agent/credential_registry.py (new) — Startup-loaded registry that reads GITLAB_TOKEN (default) and GITLAB_TOKEN__<ALIAS> env vars. Resolves credential_ref → token. Never logs raw secrets.
src/gitlab_copilot_agent/project_registry.py (new) — Parsed from rendered JSON at startup. Resolves repo path → GitLab project ID + clone URL using correct token. Answers: Jira project → resolved context, GitLab project ID → resolved context.
src/gitlab_copilot_agent/config.py — Update Settings to accept the new rendered JSON format for JIRA_PROJECT_MAP. Add credential_registry property. Keep backward compat parsing during this PR (accept both old and new format).
src/gitlab_copilot_agent/project_mapping.py — Update or replace models to align with mapping_models.py rendered output.
src/gitlab_copilot_agent/main.py — Wire credential registry and project registry at startup. Replace raw ProjectMap.model_validate_json() with new registry construction. Log startup summary (accepted mappings, credential usage, no secrets).
tests/test_credential_registry.py (new) — Tests for default token, named aliases, missing refs, secret sanitization.
tests/test_project_registry.py (new) — Tests for repo path resolution, Jira key lookup, project ID lookup.
- Update
tests/conftest.py — Add fixtures/factories for credential registry and project registry.
Agent workflow:
@architect reviews credential registry design and project registry API
@developer (fleet of 2): one implements credential_registry.py + tests, one implements project_registry.py + tests
@developer (sequential after above): wire into config.py and main.py
- Code review agent reviews PR before push
Acceptance criteria:
- Default
GITLAB_TOKEN works without any credential_ref.
- Named
GITLAB_TOKEN__PLATFORM_TEAM resolves via credential_ref: platform_team.
- Startup fails fast with clear error if a binding references an unknown credential_ref.
- Startup summary logs accepted mappings without leaking secrets.
PR 3: Credential-aware runtime flows
Goal: Thread per-repo credentials through all runtime paths.
Changes:
src/gitlab_copilot_agent/coding_orchestrator.py — Accept resolved project context (includes token) instead of bare GitLabProjectMapping. Use per-project token for clone, push, MR creation.
src/gitlab_copilot_agent/task_executor.py / task_runner.py — Pass credential ref in task payload. Task runner resolves it from its own env.
src/gitlab_copilot_agent/orchestrator.py — Look up project in registry by GitLab project ID for webhook-triggered reviews; use correct token.
src/gitlab_copilot_agent/mr_comment_handler.py — Same: resolve token from registry by project ID.
src/gitlab_copilot_agent/gitlab_poller.py — Group polled projects by credential_ref; one GitLabClient per credential group.
src/gitlab_copilot_agent/jira_poller.py — Pass resolved project context (with token) to handler.
- Update existing tests to use credential-aware fixtures.
Agent workflow:
@developer (fleet of 5 — one per file group):
coding_orchestrator.py + its test
task_executor.py + task_runner.py + their tests
orchestrator.py + its test
mr_comment_handler.py + its test
gitlab_poller.py + jira_poller.py + their tests
@developer (sequential after fleet): integration pass to verify wiring across modules
- Code review agent reviews PR before push
Acceptance criteria:
- Jira coding flow uses the per-binding token end-to-end (clone → push → MR).
- Webhook review flow resolves the correct token for the MR's project.
/copilot comment handling uses the correct token.
- GitLab polling groups projects by credential and uses appropriate tokens.
- Task runner receives and uses the correct credential_ref.
PR 4: Graceful shutdown + hot-reload
Goal: Config changes without downtime.
Graceful shutdown changes:
src/gitlab_copilot_agent/main.py — Lifespan shutdown: signal pollers to stop accepting new work, await in-flight tasks (with timeout), then exit. Use asyncio.Event for coordinated drain.
src/gitlab_copilot_agent/jira_poller.py — Add drain() method: finish current _poll_once(), don't start another. Respect cancellation.
src/gitlab_copilot_agent/gitlab_poller.py — Same drain pattern.
Hot-reload changes:
src/gitlab_copilot_agent/main.py — Store project_registry and jira_poller in app.state. Add POST /config/reload endpoint.
src/gitlab_copilot_agent/jira_poller.py — Add asyncio.Lock around _poll_once(). Add reload_map(new_map) method that acquires lock, swaps map, clears _processed_issues, logs warning.
src/gitlab_copilot_agent/project_registry.py — Make registry swappable (atomic reference replacement).
tests/test_graceful_shutdown.py (new) — Tests: drain completes in-flight work, drain respects timeout, shutdown order.
tests/test_hot_reload.py (new) — Tests: reload swaps map atomically, clears processed_issues, logs warning, returns diff, rejects invalid config.
Design decisions:
- On reload,
_processed_issues is cleared. This is safe because Jira status transitions ("AI Ready" → "In Progress") prevent the JQL from returning already-dispatched issues. A structured log warning is emitted on every reload.
- Reload re-reads env vars (or a mounted config file path) and re-validates through the same pipeline as startup.
- Reload endpoint returns a summary of what changed (added/removed bindings).
Agent workflow:
@architect reviews hot-reload design (locking strategy, reload endpoint contract)
@developer (fleet of 2): one implements graceful shutdown (drain pattern in pollers + main.py), one implements hot-reload (reload endpoint + lock + registry swap)
@developer (sequential): integration test for full reload flow
- Code review agent reviews PR before push
Acceptance criteria:
- Controller restart completes in-flight Jira orchestration before exiting.
POST /config/reload swaps the project map without restart.
- Reload endpoint validates new config before applying (rejects invalid YAML/JSON).
- Reload logs a summary of changes and a warning about cleared dedup state.
PR 5: Documentation, deployment wiring, and E2E tests
Goal: Complete documentation for the YAML-first workflow. Update deployment configs. Add E2E coverage for new scenarios.
Documentation changes:
docs/wiki/configuration-reference.md — Rewrite "Jira Integration" and "Jira Project Map Format" sections for YAML-first workflow. Document credential_ref, named GITLAB_TOKEN__* convention, helper CLI usage (validate/show/render-json). Add hot-reload endpoint reference.
docs/wiki/deployment-guide.md — Add new sections: "First Deploy with YAML Mapping", "Adding a New Team/Project" (step-by-step walkthrough showing YAML edit → validate → render-json → deploy or reload), "Hot-Reload Mapping Changes" (when to use reload vs. restart), "Graceful Shutdown Behavior".
docs/wiki/testing-guide.md — Add section on testing mapping changes: how to write tests for new bindings, how to use make_settings() with credential registry fixtures, E2E test for multi-project mapping.
docs/wiki/security-model.md — Update credential handling section for named tokens and credential registry. Document that raw secrets never appear in YAML or logs.
docs/wiki/data-models.md — Add BindingConfig, MappingFile, RenderedProjectMap, CredentialRegistry, ProjectRegistry model documentation.
README.md — Update quick start and configuration sections to reference YAML workflow and helper CLI.
demo.env — Update example config with YAML mapping example (commented).
Deployment wiring:
helm/gitlab-copilot-agent/values.yaml — Add gitlab.namedTokens map for GITLAB_TOKEN__* secrets. Add support for mapping file ConfigMap.
helm/gitlab-copilot-agent/templates/secret.yaml — Template loop for named token secrets.
helm/gitlab-copilot-agent/templates/configmap.yaml — Support for rendered JSON from helper CLI output.
infra/variables-apps.tf — Add variables for named GitLab tokens.
infra/keyvault.tf — Update bootstrap secret seeding for named tokens.
infra/container-apps.tf — Wire named token env vars into controller and task-runner.
E2E test updates:
tests/e2e/mock_jira.py — Add support for multiple Jira projects (currently handles one). Add /projects endpoint returning project metadata.
tests/e2e/mock_gitlab.py — Add support for multiple repos (currently one bare repo). Accept different tokens per repo for credential verification.
tests/e2e/run.sh — Add two new E2E test flows:
- TEST 5: Multi-project Jira polling — Configure two Jira project → GitLab repo bindings. Verify both repos get MRs created with correct branches.
- TEST 6: Hot-reload mapping — Start with one binding, call
POST /config/reload to add a second binding, verify the second project starts getting processed without restart.
tests/e2e/.env.e2e — Update with multi-project mapping JSON and named token env vars.
Agent workflow (highly parallelizable):
@developer (fleet of 6 — all independent):
configuration-reference.md update
deployment-guide.md update
testing-guide.md + security-model.md + data-models.md updates
README.md + demo.env updates
- Helm chart updates (
values.yaml, secret.yaml, configmap.yaml)
- Terraform updates (
variables-apps.tf, keyvault.tf, container-apps.tf)
@developer (fleet of 3 — all independent):
mock_jira.py multi-project support
mock_gitlab.py multi-repo + per-token support
run.sh new test flows (TEST 5 + TEST 6)
@developer (sequential after all above): integration pass — run full make e2e-test
- Code review agent reviews PR before push
Acceptance criteria:
- Documentation covers first-deploy, add-a-team, hot-reload, and troubleshooting workflows.
- A user reading only the deployment guide can configure a multi-project mapping from scratch.
- Helm and Terraform examples are updated for named credential secrets.
- E2E tests verify multi-project Jira flow and hot-reload scenario.
make e2e-test passes with all 6 test flows.
Dependencies
PR 1 (models + CLI)
└→ 🚦 Schema confirmation gate (user reviews and approves schema)
└→ PR 2 (runtime schema + credential registry)
└→ PR 3 (credential-aware flows)
└→ PR 4 (graceful shutdown + hot-reload)
└→ PR 5 (docs + deployment wiring + E2E tests)
Each PR is independently mergeable and leaves the app functional.
Out of Scope (per issue #270)
- One Jira issue fan-out to multiple repos
- Admin UI/page
- Persistent mapping storage outside deployment config
- Backward compatibility migration tooling
- Raw secrets embedded in mapping YAML
- Direct runtime YAML file loading (runtime always uses rendered JSON)
Risks / Notes
- PR 2 backward compat: During transition, accept both old and new
JIRA_PROJECT_MAP formats. Remove old format support in a follow-up after deployment migration.
- PR 3 task payload size: Adding
credential_ref to task payloads is a schema change. Task runner must handle both old (no ref → default token) and new payloads during rollout.
- PR 4 reload security: The
/config/reload endpoint should not be exposed publicly. It reads from env/mounted config — no user-supplied payload. Consider adding auth or restricting to localhost.
- Hot-reload and credential registry: Reload re-reads
GITLAB_TOKEN__* env vars. In containerized deployments, env vars are immutable after start — so adding a new credential alias requires a restart. Mapping changes (add/remove bindings using existing credentials) work via reload. Document this limitation.
- E2E test stability: Multi-project E2E tests increase mock complexity. Keep mock services deterministic — use fixed project IDs and predictable response ordering.
Goal
Create a deployment-time setup experience where the user edits one friendly YAML file, uses a helper command to validate/render it, and deploys without needing GitLab project IDs, clone URLs, or unsafe secret handling.
User experience we want
First deploy should feel obvious
validateand optionallyshow.Adding another team should feel like repeating the same step
credential_ref.Troubleshooting should feel safe
credential_ref, not a raw token.Concrete v1 decisions
PROJ.group/repo.Recommended user-facing model
YAML source of truth
Helper command responsibilities
The helper command is the main deployment-time UX layer.
Recommended v1 commands:
validate PATH: schema checks, duplicate Jira projects, required credential aliases, readable errorsshow PATH: human-readable summary of bindings and credential usagerender-json PATH: emit the runtime JSON forJIRA_PROJECT_MAPOptional later commands:
render-terraformrender-helmRuntime input
The deployment layer uses helper output to populate the runtime JSON env var consumed by the app at startup.
Secret and credential model
GITLAB_TOKEN.credential_ref.GITLAB_TOKEN__PLATFORM_TEAM.credential_refto the matching injected secret at startup and in task-runner startup.Current code reality to account for
Settings()andTaskRunnerSettingscurrently load from environment variables only, once at process startup.JIRA_PROJECT_MAPis currently a raw JSON env-var string parsed inmain.py.settings.gitlab_tokenacross repo resolution, clone, push, MR creation, webhook/MR review,/copilotcomment handling, and GitLab polling.Architecture in service of the experience
Shared mapping core
Build one shared mapping core used by:
This core should define:
Helper-first deployment flow
Credential registry
Add a startup-loaded credential registry that:
GITLAB_TOKENGITLAB_TOKEN__*aliasesResolved project registry
At controller startup:
This registry becomes the source of truth for Jira flows and GitLab-side flows.
Credential-aware runtime flows
Replace single-token assumptions across the app so the selected mapping credential is used consistently for:
/copilotcomment handlingFor GitLab polling, group projects by credential reference and use one client per credential group.
Implementation Plan (Updated)
Problem
The current
JIRA_PROJECT_MAPworkflow requires users to manually construct JSON with GitLab project IDs and clone URLs, and any mapping change requires a full redeployment. Issue #270 defines a friendly YAML-first workflow with helper commands, credential aliases, and a resolved project registry.This plan finalizes the implementation breakdown from issue #270 with two additions:
_processed_issuescleared on reload (Jira status transitions prevent true duplicate processing).Approach
Split into 5 PRs (issue #270 proposed 3; we add graceful shutdown + hot-reload as a separate PR and split docs/E2E into its own PR). Each PR is ≤200 diff lines, standalone, and fully tested.
Gate after PR 1: User reviews and confirms the YAML/JSON schema before any runtime changes begin.
Agent Assignments & Parallelization
@product@architect@developer@designerParallelization opportunities
mapping_models.pyandmapping_cli.pycan be developed in parallel once the schema is agreed. Tests for each can also be parallelized.credential_registry.pyandproject_registry.pyare independent modules — develop and test in parallel, then wire intomain.py.coding_orchestrator.py,orchestrator.py,mr_comment_handler.py,gitlab_poller.py, andjira_poller.pyare independent file edits — parallelize with a fleet of developer agents.configuration-reference.md,deployment-guide.md,testing-guide.md,README.md) are independent files. E2E mock updates (mock_jira.py,mock_gitlab.py) andrun.shtest additions are also independent. Full fleet parallelization.PR Breakdown
PR 1: YAML mapping models + helper CLI
Goal: Make first deploy obvious — user edits one YAML file, runs helper commands, deploys.
Changes:
src/gitlab_copilot_agent/mapping_models.py(new) — Pydantic models for YAML bindings (BindingConfig,MappingFile) and rendered JSON (RenderedProjectMap). Defaults handling (target_branch,credential_ref). Validation: duplicate Jira keys, invalid repo paths, missing credential refs.src/gitlab_copilot_agent/mapping_cli.py(new) — CLI entry point with three commands:validate PATH— schema + semantic checks, human-readable errors pointing to specific bindingsshow PATH— summary table of bindings, credential usage, defaults appliedrender-json PATH— emit runtime JSON forJIRA_PROJECT_MAPenv varpyproject.toml— add[project.scripts]entry for CLI, addpyyamldependency.tests/test_mapping_models.py(new) — unit tests for models, validation, defaults, edge cases.tests/test_mapping_cli.py(new) — CLI integration tests for validate/show/render-json.Does NOT change: existing
project_mapping.py,config.py,main.py, or runtime behavior. The helper CLI is a standalone deployment-time tool.Agent workflow:
@architectreviews schema design (models + CLI interface)@developer(fleet of 2): one implementsmapping_models.py+ tests, one implementsmapping_cli.py+ testsAcceptance criteria:
validatecatches duplicate Jira keys, missing repos, unknown credential_refs.showdisplays a readable mapping summary.render-jsonoutput is valid input for the currentJIRA_PROJECT_MAPenv var.🚦 Schema Confirmation Gate
After PR 1 merges, the user reviews:
MappingFilemodel)RenderedProjectMapmodel)showandrender-jsonNo further PRs proceed until the user explicitly confirms the schema.
PR 2: Runtime schema migration + credential registry
Goal: Replace internal
JIRA_PROJECT_MAPschema with the new rendered format. Add credential registry for per-repo tokens.Changes:
src/gitlab_copilot_agent/credential_registry.py(new) — Startup-loaded registry that readsGITLAB_TOKEN(default) andGITLAB_TOKEN__<ALIAS>env vars. Resolvescredential_ref→ token. Never logs raw secrets.src/gitlab_copilot_agent/project_registry.py(new) — Parsed from rendered JSON at startup. Resolves repo path → GitLab project ID + clone URL using correct token. Answers: Jira project → resolved context, GitLab project ID → resolved context.src/gitlab_copilot_agent/config.py— UpdateSettingsto accept the new rendered JSON format forJIRA_PROJECT_MAP. Addcredential_registryproperty. Keep backward compat parsing during this PR (accept both old and new format).src/gitlab_copilot_agent/project_mapping.py— Update or replace models to align withmapping_models.pyrendered output.src/gitlab_copilot_agent/main.py— Wire credential registry and project registry at startup. Replace rawProjectMap.model_validate_json()with new registry construction. Log startup summary (accepted mappings, credential usage, no secrets).tests/test_credential_registry.py(new) — Tests for default token, named aliases, missing refs, secret sanitization.tests/test_project_registry.py(new) — Tests for repo path resolution, Jira key lookup, project ID lookup.tests/conftest.py— Add fixtures/factories for credential registry and project registry.Agent workflow:
@architectreviews credential registry design and project registry API@developer(fleet of 2): one implementscredential_registry.py+ tests, one implementsproject_registry.py+ tests@developer(sequential after above): wire intoconfig.pyandmain.pyAcceptance criteria:
GITLAB_TOKENworks without anycredential_ref.GITLAB_TOKEN__PLATFORM_TEAMresolves viacredential_ref: platform_team.PR 3: Credential-aware runtime flows
Goal: Thread per-repo credentials through all runtime paths.
Changes:
src/gitlab_copilot_agent/coding_orchestrator.py— Accept resolved project context (includes token) instead of bareGitLabProjectMapping. Use per-project token for clone, push, MR creation.src/gitlab_copilot_agent/task_executor.py/task_runner.py— Pass credential ref in task payload. Task runner resolves it from its own env.src/gitlab_copilot_agent/orchestrator.py— Look up project in registry by GitLab project ID for webhook-triggered reviews; use correct token.src/gitlab_copilot_agent/mr_comment_handler.py— Same: resolve token from registry by project ID.src/gitlab_copilot_agent/gitlab_poller.py— Group polled projects by credential_ref; one GitLabClient per credential group.src/gitlab_copilot_agent/jira_poller.py— Pass resolved project context (with token) to handler.Agent workflow:
@developer(fleet of 5 — one per file group):coding_orchestrator.py+ its testtask_executor.py+task_runner.py+ their testsorchestrator.py+ its testmr_comment_handler.py+ its testgitlab_poller.py+jira_poller.py+ their tests@developer(sequential after fleet): integration pass to verify wiring across modulesAcceptance criteria:
/copilotcomment handling uses the correct token.PR 4: Graceful shutdown + hot-reload
Goal: Config changes without downtime.
Graceful shutdown changes:
src/gitlab_copilot_agent/main.py— Lifespan shutdown: signal pollers to stop accepting new work, await in-flight tasks (with timeout), then exit. Useasyncio.Eventfor coordinated drain.src/gitlab_copilot_agent/jira_poller.py— Adddrain()method: finish current_poll_once(), don't start another. Respect cancellation.src/gitlab_copilot_agent/gitlab_poller.py— Same drain pattern.Hot-reload changes:
src/gitlab_copilot_agent/main.py— Storeproject_registryandjira_pollerinapp.state. AddPOST /config/reloadendpoint.src/gitlab_copilot_agent/jira_poller.py— Addasyncio.Lockaround_poll_once(). Addreload_map(new_map)method that acquires lock, swaps map, clears_processed_issues, logs warning.src/gitlab_copilot_agent/project_registry.py— Make registry swappable (atomic reference replacement).tests/test_graceful_shutdown.py(new) — Tests: drain completes in-flight work, drain respects timeout, shutdown order.tests/test_hot_reload.py(new) — Tests: reload swaps map atomically, clears processed_issues, logs warning, returns diff, rejects invalid config.Design decisions:
_processed_issuesis cleared. This is safe because Jira status transitions ("AI Ready"→"In Progress") prevent the JQL from returning already-dispatched issues. A structured log warning is emitted on every reload.Agent workflow:
@architectreviews hot-reload design (locking strategy, reload endpoint contract)@developer(fleet of 2): one implements graceful shutdown (drain pattern in pollers + main.py), one implements hot-reload (reload endpoint + lock + registry swap)@developer(sequential): integration test for full reload flowAcceptance criteria:
POST /config/reloadswaps the project map without restart.PR 5: Documentation, deployment wiring, and E2E tests
Goal: Complete documentation for the YAML-first workflow. Update deployment configs. Add E2E coverage for new scenarios.
Documentation changes:
docs/wiki/configuration-reference.md— Rewrite "Jira Integration" and "Jira Project Map Format" sections for YAML-first workflow. Documentcredential_ref, namedGITLAB_TOKEN__*convention, helper CLI usage (validate/show/render-json). Add hot-reload endpoint reference.docs/wiki/deployment-guide.md— Add new sections: "First Deploy with YAML Mapping", "Adding a New Team/Project" (step-by-step walkthrough showing YAML edit → validate → render-json → deploy or reload), "Hot-Reload Mapping Changes" (when to use reload vs. restart), "Graceful Shutdown Behavior".docs/wiki/testing-guide.md— Add section on testing mapping changes: how to write tests for new bindings, how to usemake_settings()with credential registry fixtures, E2E test for multi-project mapping.docs/wiki/security-model.md— Update credential handling section for named tokens and credential registry. Document that raw secrets never appear in YAML or logs.docs/wiki/data-models.md— AddBindingConfig,MappingFile,RenderedProjectMap,CredentialRegistry,ProjectRegistrymodel documentation.README.md— Update quick start and configuration sections to reference YAML workflow and helper CLI.demo.env— Update example config with YAML mapping example (commented).Deployment wiring:
helm/gitlab-copilot-agent/values.yaml— Addgitlab.namedTokensmap forGITLAB_TOKEN__*secrets. Add support for mapping file ConfigMap.helm/gitlab-copilot-agent/templates/secret.yaml— Template loop for named token secrets.helm/gitlab-copilot-agent/templates/configmap.yaml— Support for rendered JSON from helper CLI output.infra/variables-apps.tf— Add variables for named GitLab tokens.infra/keyvault.tf— Update bootstrap secret seeding for named tokens.infra/container-apps.tf— Wire named token env vars into controller and task-runner.E2E test updates:
tests/e2e/mock_jira.py— Add support for multiple Jira projects (currently handles one). Add/projectsendpoint returning project metadata.tests/e2e/mock_gitlab.py— Add support for multiple repos (currently one bare repo). Accept different tokens per repo for credential verification.tests/e2e/run.sh— Add two new E2E test flows:POST /config/reloadto add a second binding, verify the second project starts getting processed without restart.tests/e2e/.env.e2e— Update with multi-project mapping JSON and named token env vars.Agent workflow (highly parallelizable):
@developer(fleet of 6 — all independent):configuration-reference.mdupdatedeployment-guide.mdupdatetesting-guide.md+security-model.md+data-models.mdupdatesREADME.md+demo.envupdatesvalues.yaml,secret.yaml,configmap.yaml)variables-apps.tf,keyvault.tf,container-apps.tf)@developer(fleet of 3 — all independent):mock_jira.pymulti-project supportmock_gitlab.pymulti-repo + per-token supportrun.shnew test flows (TEST 5 + TEST 6)@developer(sequential after all above): integration pass — run fullmake e2e-testAcceptance criteria:
make e2e-testpasses with all 6 test flows.Dependencies
Each PR is independently mergeable and leaves the app functional.
Out of Scope (per issue #270)
Risks / Notes
JIRA_PROJECT_MAPformats. Remove old format support in a follow-up after deployment migration.credential_refto task payloads is a schema change. Task runner must handle both old (no ref → default token) and new payloads during rollout./config/reloadendpoint should not be exposed publicly. It reads from env/mounted config — no user-supplied payload. Consider adding auth or restricting to localhost.GITLAB_TOKEN__*env vars. In containerized deployments, env vars are immutable after start — so adding a new credential alias requires a restart. Mapping changes (add/remove bindings using existing credentials) work via reload. Document this limitation.