First: thank you. Iris is a portfolio demo project — contributions improve the "real-world how-to-operate-this-thing" value for everyone who reads it later.
GitLab is the canonical source. Contributions happen there.
- Service: gitlab.com/iris-7/iris-service
- UI: gitlab.com/iris-7/iris-ui
The GitHub repos (github.com/iris-7/iris-*) are read-only
mirrors. Issues and PRs opened there will not be reviewed — the
repo description points here. See
docs/ops/ci-philosophy.md for why.
| Type | How to start |
|---|---|
| Bug report | Open a GitLab issue with the "bug" template. |
| Security vulnerability | Do not open a public issue. See SECURITY.md. |
| Documentation fix / clarification | Open an MR directly. Docs-only MRs are merged fastest. |
| New ADR / architectural suggestion | Open an issue first to discuss the problem + alternatives before writing the ADR. |
| New runbook | Open an MR adding a file to docs/ops/runbooks/ following the 5-heading template in the README there. |
| New feature | Open an issue + link an ADR draft. Features that don't advance the demo scenarios are usually declined (see ADR-0021). |
| Dependency bump | Renovate does this automatically. Manual bumps of critical libs (Spring Boot major, Java LTS) are accepted with a short ADR. |
git clone https://gitlab.com/iris-7/iris-service.git
cd iris-service
bin/dev/iris-doctor # one-command health check — must be all greenIf any check fails, follow the advice in the output. bin/iris- doctor --no-cost skips the GCP checks if you're offline.
Work on dev. The project uses
trunk-based development:
single working branch, squash-merged to main via MR.
git checkout dev
# make your changes
git add ...
git commit -m "feat(scope): short subject under 72 chars"Conventional Commits are mandatory. Enforced by
lefthook commit-msg hook + commitlint config. Accepted types:
feat | fix | docs | style | refactor | perf | test |
build | ci | chore | revert
Scope is optional but useful (feat(auth): ..., fix(ci): ...).
bin/ship/ship.sh "feat(scope): short subject"This one command:
- Pushes
dev(force-with-lease — safe in solo-dev workflow) - Opens or updates the MR dev → main with squash-before-merge
- Arms auto-merge when the pipeline succeeds
Or run each step manually with git push + glab mr create + the
auto-merge call — ship.sh --dry-run prints the exact commands.
Green CI on the MR triggers the auto-merge. The pipeline stages
(see docs/ops/ci-variables.md):
lint → test → integration → k8s → package → compat →
native → sonar → reports → infra → deploy
Most are fast (unit + integration = 3 min). Docker image build,
kind-in-CI validation and mutation tests take longer. allow_failure: true on optional stages means one red doesn't block merge.
Handled automatically by bin/ship/ship.sh --wait. Or manually:
git fetch --all
git reset --hard origin/main
git push origin dev --force-with-leaseAfter a squash-merge, dev has the pre-squash commits with content
already on main. Reset-to-main is the clean re-baseline.
- Default stack: SB4 + Java 25.
- Compat matrix: keep the other profiles (SB4+Java21, SB4+Java17, SB3+Java21, SB3+Java17) passing. If your change breaks one, note why in the commit message.
- No
@Autowiredfield injection. Constructor injection only. - No deprecated Spring Boot flags or APIs.
- Flyway migration versions are immutable — never modify a
V_N__*.sqlthat's already inmain. Bump the version.
- Angular 21 zoneless — no
fakeAsync, notick(), nodetectChanges()in tests. - Use
@if,@for,@switch— not*ngIf,*ngFor. - Prefer
signal()andcomputed()overngModel. - Charts in raw SVG (no charting library).
Comments explain why, not what. Write comments that a future maintainer with no conversation history can understand.
Good: // Rate limit matches Cloudflare's DDoS threshold per IP.
Bad: // Set rate limit.
- Unit tests run on every MR.
- Integration tests (Testcontainers) run on every MR.
- E2E tests (Playwright, planned — ROADMAP Tier-1 #2) run in kind-in-CI.
- Mutation tests (PIT) run on main + nightly.
- Every new
@Serviceor@RestControllergets at least one unit test. Every new API endpoint gets at least one integration test.
- Any new ADR-worthy decision gets an ADR. See the criteria in
docs/adr/README.md. Code-style or bug-fix decisions do NOT get an ADR. - Any new
$VARin.gitlab-ci.ymlgets a row indocs/ops/ci-variables.mdin the same commit. - Any new runbook follows the 5-heading template.
- READMEs in EN + FR — a lefthook hook blocks a commit that
touches one without the other. Skip with
LEFTHOOK=0 git commitif the edit is intentionally one-sided.
See the existing ADRs — all follow Michael Nygard's format:
# ADR-NNNN — Short verb-phrase title
- **Status**: Accepted / Superseded / Deprecated
- **Date**: YYYY-MM-DD
- **Related**: (optional) cross-refs
## Context
(What's the situation? What constraints are in play?)
## Decision
(What have we decided to do?)
## Consequences
### Positive / Negative / Neutral
## Alternatives considered
| Option | Why rejected |
## Revisit this when
(What would flip the decision?)New ADRs get the next number (0032, 0033, …). Numbering is
monotonic even when an ADR supersedes another.
- Squash-merge into
main. Keeps history linear, one MR = one commit on main. mainis protected — no direct push.- Source branch preserved (
--remove-source-branch=false) — we keepdevalive always. - Auto-merge allowed when MWPS is armed + CI green.
This is a solo-maintained project. Reviews happen asynchronously. If your MR has been idle for a week without feedback, a polite ping in a comment is welcome.
Heuristics the maintainer applies:
- Does the change advance a demo scenario, or solve a concrete problem?
- Is the ADR discipline preserved (new decisions have ADRs; non- decisions don't)?
- Does the CI stay green?
- Are docs updated?
- Does the commit message explain why in the body?
- Changes that add dependencies without a clear scenario.
- Code-style churn for its own sake.
- Features that require a paid SaaS to demo (ADR-0022 budget).
- "Modernisations" of patterns that work — if a demo scenario doesn't break, the pattern stays.
The project is built in close collaboration with Anthropic's
Claude Opus 4.7 (1 M-token
context), driven from the
Claude Code CLI
— see the "AI-assisted integration" section in the README for the
split of contributions. Contributors are welcome to use the same
tooling; the Co-Authored-By: trailer on each commit names the exact
model. Human review remains the authority on scope and arbitrage.
Thanks again for contributing. If you have any question the above
doesn't answer, open an issue with the label question.