Ctrl K
ComunidadGuías…

CONTRIBUTING.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Contributing to `ai-helm`

> **TL;DR:** branch from `main` → commit conventional → open a PR →
> CI passes → @stephane-segning reviews → merge. Non-trivial choices
> get an ADR (see below).

## Setting up locally

```bash
git clone git@github.com:ADORSYS-GIS/ai-helm.git
cd ai-helm
```

You need:
- **helm** v3.15+ (until we migrate to Helm 4 — see the 2026 audit
  punch-list item).
- **kubectl** for cluster work; not required for chart development.
- **uv** + **ruff** if you touch `tools/dashboards/` or any Python.
- **zsh** if you follow the team's local-shell convention (not required;
  the repo's scripts are POSIX-portable).

## Branches

- `main` is the only long-lived branch. Protected; no direct pushes.
- Feature branches: `<topic>/<short-name>` or `<gh-issue-id>-<topic>`
  (e.g. `feat/multi-source-appset` or `293-cnpg-backup-rollback`).
- Branches are short-lived. Open a PR within a day of starting work
  even if it's draft — that's where review conversation happens.

## Commit messages

**[Conventional Commits](https://www.conventionalcommits.org/), enforced.** The
full spec — every type, what it does to release-please/version bumps, scope rules,
breaking changes, and the enforcement — lives in
[`docs/commit-conventions.md`](docs/commit-conventions.md). The short version:

```
<type>[optional scope][!]: <description>
```

| Type | When to use | release-please effect |
|---|---|---|
| `feat(scope):` | New user-facing behavior (a new chart, dashboard, endpoint) | chart **MINOR** bump |
| `fix(scope):` | Bug fix | patch (cosmetic — publish derives the deployed patch) |
| `docs(scope):` / `refactor` / `perf` / `revert` | Docs-only / restructure / perf / revert | changelog only |
| `chore` / `ci` / `build` / `test` / `style` | Maintenance, CI, deps, tests, formatting | none (hidden) |
| any `!` or `BREAKING CHANGE:` footer | Breaks consumers | **MAJOR** (pre-1.0 chart: minor) |

- **Scope = the chart directory name** for chart changes (`feat(core-gateway): …`);
  attribution is by file path, so a commit touching `charts/<x>/**` versions `<x>`.
- **Body: explain *why*** (the diff shows what). Link the ADR: `(ADR-NNNN)`. 20–60
  line bodies are common for non-trivial changes.
- **Because we squash-merge, the PR title must also be a valid Conventional Commit**
  — it’s what lands on `main` and feeds release-please.

Co-author trailer for AI-assisted commits (running model version):

```
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
```

**Enforcement** (details in the doc): a local `commit-msg` hook rejects bad
messages at `git commit` — enable once with `git config core.hooksPath .githooks` —
and the `Commit Lint` CI gate validates the PR title + every commit on each PR.
Both call the same validator, [`tools/commit-lint.sh`](tools/commit-lint.sh).

## Architecture Decision Records (ADRs)

Anything non-obvious gets an ADR. See
[`docs/adr/README.md`](docs/adr/README.md) for what counts as
"non-obvious" and how to write one.

Process:
1. Copy [`docs/adr/template.md`](docs/adr/template.md) to
   `docs/adr/NNNN-short-imperative-title.md` (next free number,
   zero-padded to 4).
2. Status starts as `Proposed`. Move to `Accepted` when the
   implementation lands.
3. ADRs are **immutable once Accepted**. To change a decision, write
   a new ADR that supersedes the old one. Add `Status: Superseded by
   ADR-NNNN` to the old one's header with a short note explaining the
   change; preserve the body as historical record.
4. Update [`docs/adr/README.md`](docs/adr/README.md) index.

## Helm chart conventions

### Naming
- Chart directory and `Chart.yaml` `name:` must match. Audit-flagged
  drift exists in older charts; align when touching.
- First-party charts use kebab-case names (`librechat-app`, not
  `librechatApp`).

### Pinning
- **No `:latest`, no `'*'`.** Pin to explicit semver (`v1.20.2`) or
  commit SHA. The 2026 currency audit lists exceptions and the path
  to fix them.
- Chart-version pins go in `Chart.yaml` `dependencies[*].version`.
- Image tag pins go in the chart's `values.yaml`
  (`image.tag: v1.2.3`).

### Templates
- Use the `common` library for standard labels (`common.labels.standard`)
  and name helpers (`common.names.namespace`).
- Render Service / ConfigMap / Secret names from `{{ .Release.Name }}`
  or `{{ include "common.names.fullname" . }}`; **never** hardcode.
- Required-value guards: use `{{- fail "..." -}}` at the top of the
  template that depends on the value. See
  `charts/ai-model/templates/aigatewayroute.yaml` for a worked example.

### Sync waves
Lower waves first. See [`docs/architecture.md`](docs/architecture.md#sync-waves)
for the conventions and [SYNC_WAVE_PATTERN.md](SYNC_WAVE_PATTERN.md)
for the canonical document.

### The orchestrator-plus-leaves pattern
For charts whose components have different lifecycles (sync waves,
rollback granularity, per-component lifecycle), prefer the pattern
introduced in [ADR-0012](docs/adr/0012-split-ai-models-applicationset.md)
and refined in [ADR-0014](docs/adr/0014-split-librechart-and-opencode-wellknown.md):

```
charts/<thing>/                  ← orchestrator; emits ApplicationSet
charts/<thing>-<componentA>/     ← leaf
charts/<thing>-<componentB>/     ← leaf
```

The orchestrator's `Chart.yaml` depends only on `common`. The leaves
carry their own values defaults; the orchestrator's `values.yaml`
carries ArgoCD wiring (project, destination, targetRevision, per-child
sync wave) and a `children: [{name, chartPath, syncWave, enabled}]`
list that drives the ApplicationSet's List generator.

## Python tooling

Per [ADR-0008](docs/adr/0008-python-dashboard-generation.md):

- **`uv`** for everything (lockfile, virtualenv, run, install). Not
  pip, not poetry.
- **`ruff`** for lint and format. Not black, not isort, not flake8.
- **Python 3.12+.**
- `pyproject.toml` (PEP 621), commit `uv.lock`.
- Project root has a `Makefile` with `install`, `build`, `check`,
  `format`, `lint` targets that wrap `uv` commands — muscle-memory
  shortcut, not authoritative.

Example: [`tools/dashboards/`](tools/dashboards/).

## CI

Five workflows under `.github/workflows/`:

| Workflow | Triggers on | What it does |
|---|---|---|
| `helm-lint.yaml` | every push + PR | `helm lint --strict` + `helm template --dry-run` per chart |
| `dashboards-drift.yml` | changes under `tools/dashboards/` or `**/files/**/*.json` | re-runs the Python generator, fails if committed JSON differs |
| `opencode.yml` | PRs + comment `/oc` or `/opencode` | runs OpenCode auto-review (or manual-review on demand) |
| `release-helm-charts.yml` | manual dispatch + branch pushes touching `charts/` | trivy config scan, then `helm/chart-releaser-action` on dispatch |
| `security.yml` | every push | reusable workflow at `ADORSYS-GIS/ai-ops` for trivy + dep scan |

A PR is mergeable when **helm-lint** + **dashboards-drift** (when
relevant) + **security** are green. **opencode** is informational —
its review surfaces issues to think about; humans decide.

Dashboard drift specifically: if you edit a `tools/dashboards/<area>/*.py`
file, you MUST run `uv run dashboards build` and commit the
regenerated JSON. CI will fail otherwise.

## Reviewing

Anyone can comment; @stephane-segning approves and merges. Reviewers
should look for:
- An ADR exists for non-obvious choices (or one is being written in
  the same PR).
- New charts follow the conventions above.
- Image pins are explicit (no `:latest`).
- Docs land alongside code (`docs/<feature>.md` for the *how*; the ADR
  is the *why*).
- Security: no plaintext credentials in `values.yaml`; secrets via
  ESO `secretKeyRef`.
- Sync-wave annotations match the convention.

## Documentation expectations

Code changes ship with their docs. Specifically:
- A chart change touches at minimum the chart's own files; ideally
  also a `docs/<feature>.md` how-to and (when architectural) an ADR.
- A new dashboard ships with its `README.md` co-located.
- An ops workflow (backup, restore, secret rotation) gets a runbook
  under `docs/`.

If you can't tell whether something deserves a doc, err toward writing
it — the cost of a stale doc is far lower than the cost of an
undocumented decision a year later.

## Getting unstuck

- **Chart won't render.** `helm dependency update && helm template
  . --debug` from the chart dir.
- **ArgoCD shows OutOfSync after merge.** Most often a sync-wave
  ordering issue; check [SYNC_WAVE_PATTERN.md](SYNC_WAVE_PATTERN.md)
  and the application's own annotations.
- **Drift on `tools/dashboards/`.** Run `uv run dashboards build`,
  commit, push.
- **uv installs fail.** Network blocked from the runner; pre-seed
  `~/.cache/uv/` from a known-good machine, or use the chart's
  Makefile's `install` target with `--offline`.
- **Something is missing from docs.** Open a PR with a stub
  `docs/<thing>.md` — getting the doc into git, even partial, is
  better than waiting for the perfect one.

## Code of conduct

Be kind, be precise, assume good faith. Disagreements about
architecture are healthy — write an ADR with the alternatives and let
the discussion happen on a PR.

## AI Governance

This repository follows the [ADORSYS-GIS AI Governance](https://adorsys-gis.github.io/ai-governance/) practices.

- **Issues** — use the structured forms (Epic / User Story / Development Ticket) under *New issue*. Each requires a source-of-truth link, verification evidence, and an accountable owner.
- **Pull requests** — fill the **AI Usage Declaration**, link a source of truth, and provide verification evidence. The `AI Governance` CI check enforces these on every PR.
- **Principle** — AI may accelerate the work, but it must not launder ignorance into polished artifacts; humans own intent, verification, and consequences.

See the [AI working agreement](https://adorsys-gis.github.io/ai-governance/12-ai-working-agreement) and the [doctrine](https://adorsys-gis.github.io/ai-governance/13-doctrine).

## Working with AI code review

Automated AI reviewers (Codex, Copilot, and the like) are **advisory — never a merge gate.** Only
**deterministic** checks (the governance CI check, linting, tests) may block a merge, because their
output is reproducible and cannot be confabulated. Keep AI review as a non-required status check.

**Every AI-review finding is a claim, not a verdict.** Before acting on one that asserts a specific
value or behavior, verify it against the actual cited lines. AI reviewers pattern-match known bug
*shapes* and will confidently assert details about code they did not actually read — especially code
in another repository (e.g., a reusable workflow referenced by SHA). The doctrine applies to the
reviewer too: **AI output is not truth.**

### When a finding is a false positive, close the loop — don't just ignore it

1. **Reply with the evidence** — the exact lines or command output that disprove it.
2. **React 👎** on the finding.
3. **Resolve the conversation.**

This three-step loop is not busywork; each step does something that silently ignoring does not:

- **👎 is the only lever that reduces recurrence.** It is the reviewer's feedback channel. Without it,
  the same confabulation fires again every time its trigger reappears — a single false "empty marker"
  finding recurred *three times* across PRs precisely because it was refuted in prose but never
  down-voted.
- **Resolving preserves signal-to-noise.** Real findings get buried under known-false ones if threads
  stay open; resolution stops both humans and the bot from re-litigating settled points.
- **The evidence reply is an audit trail.** The next person — or AI — who hits the same flag finds the
  refutation in-thread and does not have to re-verify from scratch. Silently ignoring a false positive
  looks unaddressed, erodes trust in the review, and teaches the bot nothing.