Files
cozystack/AGENTS.md
T
Myasnikov Daniil 9bd8c17971 fix(ci): shape-filter the promote postcondition and close test gaps
Review of the previous commit found the postcondition itself could abort
a release. It grepped the bare "X.Y.Z-rc.N" substring across the tree, and
three live files carry such strings without being image refs:
kubevirt-csi-driver's go.sum pins github.com/golang/protobuf v1.4.0-rc.2
-- a version cozystack actually cut, so promoting v1.4.0 would have
failed on a Go checksum line -- metallb_test.yaml names v1.5.0-rc.2 in a
comment, and the console pnpm-lock.yaml carries rolldown@1.0.0-rc.15. The
remediation the error message gave would have made things worse: declaring
go.sum as a ref source would sed a checksum file.

Match the version only in image-reference position now: after an
image/repository/tag key with no intervening '#', or immediately before an
@sha256 digest. Narrowing it to "${RC}@sha256:" alone would have been
wrong -- cilium keeps `tag: v1.5.0` and `digest:` under separate keys, so
the version is not always adjacent to the digest. Verified against four
real rc versions that previously aborted.

The suite also only tested the fail-closed direction, which is why that
defect survived, and the collector had no completeness guard at all: a
first-party ref added at an unenumerated path on a `latest@` tag was
mirrored by nothing and passed all eighteen tests. Since the mirror's
failure mode is the worst of the three -- the published nightly points at
the private build registry -- that gap mattered most where coverage was
thinnest.

Added: a fail-open test using the three real rc versions above; a
collector completeness diff that discovers refs by content and fails on
any the enumeration misses; coverage for the OCI-artifact shape, whose
deletion previously left every suite green; and an unreadable-file test,
since a read error was being treated as "no match" and silently skipped.

The component-versioned test was vacuous -- its rc version shared no
substring with the kamaji tag it claimed to protect, so it passed by
construction. It now uses an rc version sharing kamaji's 0.19.0 prefix,
which a loosely-anchored implementation would corrupt.

Round-trip fixture discovery matched a combined 'ghcr.io/cozystack/
cozystack/' prefix, which skips keycloak-operator (host in a sibling
registry: key) and kubeovn (host in global.registry.address). It now
matches 'cozystack/cozystack' and stamps split `tag:` keys too.

Also documented two pre-existing gaps rather than half-fixing them: the
nightly host rewrite is a contiguous-substring replace and cannot reach a
split host, and a ref inside a gzip is invisible to every tool including
the postcondition, which skips binaries. Both are host-dimension defects;
the version dimension this change owns is unaffected, because the version
always lives in a tag key regardless of where the host sits.

Assisted-By: Claude <noreply@anthropic.com>
Assisted-By: GPT-5 <noreply@openai.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-07-21 20:42:28 +05:00

8.5 KiB

AI Agents Overview

This file provides structured guidance for AI coding assistants and agents working with the Cozystack project.

Activation

CRITICAL: When the user asks you to do something that matches the scope of a documented process, you MUST read the corresponding documentation file and follow the instructions exactly as written.

  • Commits, PRs, git operations (e.g., "create a commit", "make a PR", "fix review comments", "rebase", "cherry-pick")

    • Read: contributing.md
    • Action: Read the entire file and follow ALL instructions step-by-step
  • Changelog generation (e.g., "generate changelog", "create changelog", "prepare changelog for version X")

    • Read: changelog.md
    • Action: Read the entire file and follow ALL steps in the checklist. Do NOT skip any mandatory steps
  • Release creation (e.g., "create release", "prepare release", "tag release", "make a release")

    • Read: releasing.md
    • Action: Read the file and follow the referenced release process in docs/release.md
  • Image references, tags and digests (e.g., "why is this image tagged rc", "add an image to a package", "change how an image is stamped", "promotion/nightly missed an image", "where do image refs live")

    • Read: image-refs.md
    • Action: Read the entire file. Image refs live in three storage shapes and carry three classes of tag; a tool that knows only one shape skips images silently rather than failing. hack/lib/image-refs.sh is the enumeration shared by the promote, retag and mirror tooling — extend it there rather than teaching an individual script a new path. Note hack/overlay-main-images.sh is a fourth consumer that still walks the tree itself and does not source the library
  • Project structure, conventions, code layout (e.g., "where should I put X", "what's the convention for Y", "how is the project organized")

    • Read: overview.md
    • Action: Read relevant sections to understand project structure and conventions
  • E2E tests and E2E CI (e.g., "write/fix an e2e test", "stabilize a flaky test", "add a bats test", "change the e2e workflow", "why did e2e fail")

    • Read: e2e-testing.md
    • Action: Read the entire file and follow the conventions exactly — no new retries on deterministic steps, event-driven backstops before every kubectl wait, no EXIT/RETURN traps, fail-fast on HelmRelease readiness
  • General questions about contributing

    • Read: contributing.md
    • Action: Read the file to understand git workflow, commit format, PR process
  • Issue and PR labeling, triage (e.g., "label this issue", "what label should I use", "triage this", "categorize")

    • Read: .github/labels.yml
    • Action: Use labels defined there. Conventions follow the Kubernetes scheme — kind/* (type), area/* (subsystem), priority/* (urgency), triage/* (review state), lifecycle/* (auto-close), do-not-merge/* (PR blockers), security/* (severity)
    • For area/*: accuracy outweighs reuse. If no existing area/* truly fits the change, propose a new one via PR (extend .github/labels.yml and the scope mapping in .github/workflows/pr-labeler.yaml) — do not shoehorn the change into a wrong area. area/uncategorized is the auto-labeler fallback; treat it as a signal to pick a fit, create a new area, or correct the PR title
    • PR titles: a Conventional Commits header (type(scope): description, types from contributing.md) auto-applies kind/* and area/* via .github/workflows/pr-labeler.yaml. Append ! (or add a BREAKING CHANGE: footer) to apply kind/breaking-change

Important rules:

  • ✅ ONLY read the file if the task matches the documented process scope - do not read files for tasks that don't match their purpose
  • ✅ ALWAYS read the file FIRST before starting the task (when applicable)
  • ✅ Follow instructions EXACTLY as written in the documentation
  • ✅ Do NOT skip mandatory steps (especially in changelog.md)
  • ✅ Do NOT assume you know the process - always check the documentation when the task matches
  • ❌ Do NOT read files for tasks that are outside their documented scope
  • 📖 Note: overview.md can be useful as a reference to understand project structure and conventions, even when not explicitly required by the task

Project Overview

Cozystack is a Kubernetes-based platform for building cloud infrastructure with managed services (databases, VMs, K8s clusters), multi-tenancy, and GitOps delivery.

Quick Reference

Code Structure

  • packages/core/ - Core platform charts (installer, platform)
  • packages/system/ - System components (CSI, CNI, operators)
  • packages/apps/ - User-facing applications in catalog
  • packages/extra/ - Tenant-specific modules
  • cmd/, internal/, pkg/ - Go code
  • api/ - Kubernetes CRDs

Conventions

  • Helm Charts: Umbrella pattern, vendored upstream charts in charts/
  • Go Code: Controller-runtime patterns, kubebuilder style
  • Git Commits: Conventional Commits (type(scope): description) with --signoff
  • Prose: One continuous line per paragraph — see Prose Formatting

What NOT to Do

  • ❌ Edit /vendor/, zz_generated.*.go, upstream charts directly
  • ❌ Modify go.mod/go.sum manually (use go get)
  • ❌ Force push to main/master
  • ❌ Commit built artifacts from _out
  • ❌ Hardwrap a prose paragraph in markdown, a PR body, or an issue comment

Prose Formatting

Markdown files and everything an agent publishes to GitHub — PR bodies, issue bodies, review comments, release notes — use one continuous line per paragraph. Renderers collapse a soft line break into a space, so a paragraph hardwrapped at ~80 columns renders exactly like the same paragraph on one line, while breaking narrow viewports, mangling nested list and table rendering, and turning a one-word edit into a diff that reflows the entire block. The renderer decides where the line ends; the author does not.

Line breaks stay wherever they carry meaning: code fences, one item per list line, tables, headings, blockquotes, front matter, and README badge stacks. The rule governs prose paragraphs and nothing else. Commit messages are the deliberate exception — git log and the tooling around it still expect a body wrapped at ~72 columns.

This is the rule LLM agents break most often, because an ~80-column wrap is correct in the places they spend most of their time (Go comments, commit bodies) and the reflex leaks from there into documentation and GitHub. Treat it as a mechanical check at the moment of writing, not as a style preference to weigh: before saving a markdown file or passing --body to gh, look at each paragraph and confirm it is a single line.

The rule applies to prose this repository owns, and to new or changed paragraphs. Much of the existing tree predates it and is wrapped throughout; that is not a defect to go and fix. Vendored upstream chart docs (packages/*/*/charts/**) must stay verbatim — make update regenerates them — and published changelogs are historical records. Neither is in scope.

.claude/hooks/md-no-hardwrap.py enforces the rule for agents that support PreToolUse hooks (registered for Claude Code in .claude/settings.json). It refuses a Write/Edit/MultiEdit that would add a hardwrapped paragraph, list item or blockquote to a markdown file, and a gh command that would publish one to GitHub. A break counts as added only when the edit wrote both lines it joins, so fixing a typo inside a paragraph that was already wrapped goes through, while rewriting the same file as fresh hardwrapped prose does not. Anything it cannot read with certainty it allows — a guard that blocks legitimate work gets switched off, and then it guards nothing — which includes a body the shell assembles at runtime, and a --body-file written by the very command being checked, since the file does not exist yet when the hook runs. hack/md-no-hardwrap.bats covers it under make unit-tests and requires python3. Agents on other harnesses are held to the same rule without the hook.

.claude/settings.json is tracked, so keep personal Claude Code overrides in .claude/settings.local.json, which is not. A personal .claude/settings.json predating this will be overwritten on pull without warning, because git had been ignoring it.