Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .agents/rules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# MicroTodoSuite Agent Rules

This directory is the single place where agents working in any MicroTodoSuite
repository read the rules they follow. Each repository's `AGENTS.md` points
here.

## Precedence

When two sources disagree, the higher one wins:

1. `microservice-app-docs/constitution.md`
2. `microservice-app-docs/docs/Pull request and task tracking conventions.md`
3. The rules in this directory
4. Skills and prompts

A rule here never relaxes a higher source. A conflict discovered between them is
reported and resolved by a recorded decision, never by choosing the convenient
side.

## Contents

| Path | Covers |
| --- | --- |
| `iac/` | Infrastructure as code: the 26 adapted course rules (`PC-IAC-001` to `PC-IAC-026`) and the project rules that extend them (`MTS-IAC-101` onward) |
| `mcp.md` | The MCP servers agents use, which of them are mandatory for which repositories, and how they are configured and verified |

## How an agent uses these rules

- Before writing or reviewing infrastructure code, it reads `iac/README.md` and
every rule that index marks as applying to the change.
- In an infrastructure repository it runs `scripts/check-mcp.sh` first and
stops if any required documentation server does not answer (`mcp.md`).
- It MUST check provider arguments, versions, and service limits against current
vendor documentation through those servers, never from memory alone, and
names what it checked in the pull request (`iac/MTS-IAC-108`).
- It runs the automated check each rule names and quotes the result in the pull
request body.
- A rule it cannot satisfy is reported as an exception with a reason, in the
pull request and in the rule's exception register, never skipped silently.

## Language

Every rule is written in English, in the third person, with requirements in RFC
2119 form. Rules adapted from Spanish course material cite their source and
never reproduce the original text.
85 changes: 85 additions & 0 deletions .agents/rules/iac/MTS-IAC-101-naming-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# MTS-IAC-101 — Naming Reference

**Status**: Project rule, recorded 2026-09-11 (maintainer decisions G2 and G3).
Implements PC-IAC-003, PC-IAC-004, and PC-IAC-025.

## Governance codes

| Segment | Value | Meaning |
| --- | --- | --- |
| `client` | `lex` | Lexfield Legal — a fictitious law firm, the client for which GaCode Solutions, the team's company, builds and operates MicroTodoSuite |
| `project` | `mts` | MicroTodoSuite |

| `environment` | Meaning |
| --- | --- |
| `shd` | Account-level resources shared by every environment: state backend, ECR, GitHub OIDC trust, public DNS zone, transit egress hub |
| `eco` | The economical profile's shared cluster, which hosts the `dev`, `staging`, `prod`, and `demo` namespaces |
| `fdev` | Full profile, development |
| `fstg` | Full profile, staging |
| `fprd` | Full profile, production, in AWS and in the Azure recovery estate |

A logical environment inside `eco` — a namespace — goes in `{key}`
(`lex-mts-eco-role-jwtdev`).

The client was first set to `gcs` (GaCode Solutions) and corrected the same day
by the maintainer: GaCode Solutions is the team's own company, the provider, not
the client. The client is a law firm, named Lexfield Legal for this project. No
resource had yet been created under `gcs`.

## Resource types

**AWS**

| Resource | `type` | | Resource | `type` |
| --- | --- | --- | --- | --- |
| VPC | `vpc` | | IAM role | `role` |
| Subnet | `sub` | | IAM policy | `pol` |
| Route table | `rtb` | | KMS key alias | `kms` |
| Internet gateway | `igw` | | S3 bucket | `s3` |
| NAT gateway | `nat` | | ECR repository | `ecr` |
| Elastic IP | `eip` | | Secrets Manager secret | `sm` |
| Transit gateway | `tgw` | | EKS cluster | `eks` |
| TGW attachment | `tgwa` | | EKS node group | `ng` |
| VPC endpoint | `vpce` | | Launch template | `lt` |
| Flow log | `fl` | | CloudWatch log group | `cwl` |
| Security group | `sg` | | SQS queue | `sqs` |
| Network ACL | `nacl` | | Load balancer | `alb`, `nlb` |

**Azure** — abbreviations from the Cloud Adoption Framework

| Resource | `type` | | Resource | `type` |
| --- | --- | --- | --- | --- |
| Resource group | `rg` | | AKS cluster | `aks` |
| Virtual network | `vnet` | | Container registry | `acr` |
| Subnet | `snet` | | Key Vault | `kv` |
| Network security group | `nsg` | | Storage account | `st` |
| Public IP | `pip` | | Managed identity | `id` |
| Log Analytics workspace | `log` | | DNS zone | `dns` |

## Keys

Lowercase letters and digits, no hyphen, at most 10 characters: `main`,
`bootstrap`, `puba`, `priva`, `jwtdev`, `ecrpublish`, `authapi`, `logmsgproc`.

With three-letter client and project codes, a key of up to 10 characters keeps
every name within 28: `lex-mts-fdev-role-ecrpublish` is 28.

## Service constraints — recorded exceptions

| Resource | Rule the service imposes | Applied form |
| --- | --- | --- |
| S3 bucket | Globally unique name | `lex-mts-shd-s3-tfstate-{account_id}`; the 28-character limit applies to the part before the account ID (PC-IAC-008) |
| ECR repository | Allows `/` | The standard name, no path: `lex-mts-shd-ecr-authapi` |
| Container registry (Azure) | Letters and digits only, 5–50 | Separators removed: `lexmtsfprdacrdr` |
| Storage account (Azure) | Letters and digits only, 3–24 | Separators removed: `lexmtsfprdstbackup` |
| AKS node pool | 1–12 lowercase letters and digits | The key only: `system`, `workload` |
| Route 53 and Azure DNS zones | The name is the domain | The domain; `Name` tag follows the pattern |
| IAM OIDC provider | The name is the issuer URL | Unchanged; `Name` tag follows the pattern |
| Resources named by AWS | EKS cluster security group, service-linked roles | Unchanged; they carry the cluster's tags |
| KMS alias | The name begins with `alias/` | `alias/` followed by the standard name: `alias/lex-mts-shd-kms-tfstate` |
| CloudWatch log group whose name a service prescribes | EKS writes to `/aws/eks/<cluster>/cluster` | `/aws/<service>/<standard name>[/<suffix>]`; the `Name` tag is a standard name of type `cwl` |

## Out of scope

Kubernetes object names follow MTS-IAC-105. Human IAM users are not managed by
Terraform and are governed by the account's access policy, not this rule.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# MTS-IAC-102 — Module Repositories and Versioning

**Status**: Project rule, recorded 2026-09-11 (maintainer decision G4).
Supersedes PC-IAC-015's "one module, one repository".

## Repositories

Reusable modules live in **one repository per cloud provider**:

| Repository | Holds |
| --- | --- |
| `terraform-aws-modules` | AWS modules |
| `terraform-azure-modules` | Azure modules |
| `terraform-<provider>-modules` | Any provider added later |

A module is one top-level directory with the PC-IAC-001 layout. Modules never
import each other by relative path across the repository; a module that needs
another's output receives it as an input.

Live repositories consume modules and never contain a copy of one.

The repository names coincide with the public `terraform-aws-modules`
organization on the Terraform Registry. Sources therefore always use the full Git
URL, never a registry address, so the two cannot be confused:

```hcl
module "eks" {
source = "git::https://github.com/MicroTodoSuite/terraform-aws-modules.git//eks?ref=eks-v1.4.0"
}
```

## Independent versions

Each module is versioned on its own, so a change to one never forces consumers of
another to move:

- Release tooling: release-please in manifest mode, one component per module
directory.
- Tags: `<module>-v<MAJOR>.<MINOR>.<PATCH>`, for example `eks-v1.4.0`.
- Each module keeps its own `CHANGELOG.md`.
- Commits touching a module use the module's name as Conventional Commit scope
(`feat(eks): …`); a `BREAKING CHANGE` footer produces a major version.
- A commit touching two modules is split; one change, one module.

## Consumers

- A live root pins the exact tag. Upgrading is a reviewed pull request that
changes `?ref=` and shows the plan.
- Dependabot or Renovate MAY propose tag upgrades; they never merge unreviewed
into a root that owns production.

## CI in the module repository

On every pull request, every module is checked by the reusable workflow
`MicroTodoSuite/.github/.github/workflows/iac-checks.yml`: `terraform fmt`,
`terraform test` (which validates the module, PC-IAC-018), tflint, Trivy, and the
rule contracts, plus `terraform validate` of each module's `sample/`. Release pull requests are created
by release-please from merged Conventional Commits.
30 changes: 30 additions & 0 deletions .agents/rules/iac/MTS-IAC-103-account-and-region-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# MTS-IAC-103 — Account and Region Parameters

**Status**: Project rule, recorded 2026-09-11.

## Requirement

- The AWS account ID is **configuration, not a secret**, and MUST be declared once
per concern:

| Consumer | Declaration |
| --- | --- |
| Terraform in `microservice-app-ops` | `config/aws-account.env`, supplied to every root as `var.aws_account_id` |
| GitOps manifests in `microservice-app-gitops` | `config/aws-account.env`, propagated by `scripts/set-aws-account.sh` |
| GitHub Actions in every repository | Organization variable `AWS_ACCOUNT_ID` |

- No other file may carry an account ID as a literal. `tests/contract/aws-account-parameter.sh`
enforces it in each repository, with every remaining exception listed with
the exact account it permits and its reason.
- Roots MUST bind the provider to it with `allowed_account_ids`
(PC-IAC-005) and derive ARNs from it rather than typing them.
- The region is an **environment** parameter: each environment's `.tfvars`
declares `region`, and nothing else in the root types a region.
- Moving to another account is `scripts/set-aws-account.sh <id>` in each
repository plus one update of the organization variable. Retired accounts stay
listed and cannot be reused silently.

## Automated check

The two account contracts in each repository and the CI account contract in each
service repository.
26 changes: 26 additions & 0 deletions .agents/rules/iac/MTS-IAC-104-azure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# MTS-IAC-104 — Azure

**Status**: Project rule, recorded 2026-09-11. Applies the course rules to Azure.

## Requirement

- Roots configure `provider "azurerm"` with `alias = "principal"`, the
subscription ID from configuration, `features {}`, and authentication through
OIDC — never a client secret. Modules declare `azurerm.project`.
- The AzureRM provider has no `default_tags`: every resource MUST set
`tags = merge(var.common_tags, { Name = <name> }, var.additional_tags)`.
- Names follow MTS-IAC-101, including the separator-free forms for registries and
storage accounts.
- State uses the `azurerm` backend with a dedicated storage account, blob
versioning, and soft delete; the account has public network access disabled
where the runner can still reach it.
- Key Vault uses RBAC authorization and purge protection.
- AKS uses workload identity and the OIDC issuer; no service-principal secrets.
- Every AKS size and node count MUST fit the subscription's regional vCPU quota,
checked before planning. The current subscription allows six vCPUs per region.

## Agent procedure

An agent checks `az vm list-usage` for the target region and the Azure naming
rules for each resource type through the Azure documentation MCP server or
Microsoft Learn before writing Azure resources.
16 changes: 16 additions & 0 deletions .agents/rules/iac/MTS-IAC-105-kubernetes-desired-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# MTS-IAC-105 — Kubernetes Desired State

**Status**: Project rule, recorded 2026-09-11.

## Requirement

- Kubernetes state changes only through commits to `microservice-app-gitops`,
reconciled by ArgoCD. No `kubectl apply`, `patch`, `scale`, or `delete` against
a GitOps-managed cluster, except the two audited bootstrap mutations.
- Images are referenced by immutable digest; tags are not deployable.
- Cloud identifiers that manifests must carry — account, role ARNs, registry
hosts — come from Terraform outputs and one declared source per repository
(MTS-IAC-103), never typed by hand.
- Vendored upstream manifests are pinned by checksum and not edited in place.
- Kubernetes object names follow the GitOps repository's conventions, not
PC-IAC-003.
15 changes: 15 additions & 0 deletions .agents/rules/iac/MTS-IAC-106-github-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# MTS-IAC-106 — GitHub Actions as Infrastructure

**Status**: Project rule, recorded 2026-09-11.

## Requirement

- Every action MUST be pinned by full commit SHA, with the version in a comment.
- Workflows MUST declare least-privilege `permissions`, defaulting to
`contents: read`.
- Cloud access MUST use OIDC federation to a role scoped to the exact repository
and branch; static cloud keys are forbidden.
- Non-secret configuration — account ID, region, project keys — comes from
organization or repository variables; secrets from GitHub secrets.
- Shared logic lives in reusable workflows in `MicroTodoSuite/.github`, which
callers pin by SHA.
25 changes: 25 additions & 0 deletions .agents/rules/iac/MTS-IAC-107-changing-live-infrastructure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# MTS-IAC-107 — Changing Live Infrastructure

**Status**: Project rule, recorded 2026-09-11.

## Every apply

1. A timestamped external state backup under `~/backups-microtodosuite/`, or a
`no-prior-state` receipt for a new key.
2. A saved plan, inspected from `terraform show -json`, not from printed text.
3. Recorded approval of that exact plan.
4. `terraform apply -input=false <plan>`; never a convenience apply.
5. A post-apply check that the other environments' refreshed plans are still
`no-op`.

## Destruction

Destroying live infrastructure is allowed only through a maintainer-approved
rebuild plan that names, in order: every resource destroyed, every persistent
item preserved and how (state files, secret values, registry images, volume
snapshots, DNS records), the recreation order, the validation that proves the
rebuild works, and the check that leaves no orphan. The first such rebuild is the
naming-convention adoption decided on 2026-09-11 (ops spec 004).

A secret value is copied between secrets without being printed or written to
disk in clear text.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# MTS-IAC-108 — Vendor Practices and Mandatory Documentation

**Status**: Project rule, recorded 2026-09-11 at the maintainer's request.

## Requirement

The course rules (PC-IAC-001 to PC-IAC-026) and the project rules are the
baseline. The providers' current recommended practices complement them; the two
are not alternatives:

| Vendor | Practice sources |
| --- | --- |
| HashiCorp | Terraform style guide, module structure, testing, and version constraints |
| AWS | AWS Well-Architected Framework, AWS Prescriptive Guidance for the Terraform AWS provider, and each service's security best-practice guide, such as the EKS Best Practices Guide |
| Microsoft | Azure Well-Architected Framework, Cloud Adoption Framework naming and tagging, Azure Verified Modules specifications, and the Azure MCP Server's Terraform best practices |

- Where a vendor practice is stricter or more secure than a project rule, both
apply.
- Where a vendor practice contradicts a project rule — for example Azure Verified
Modules naming against PC-IAC-003 — the project rule applies in this
organization, and the difference is recorded in that rule's file as an
adaptation.
- Where a project rule contradicts a vendor's current security guidance, the rule
is treated as a defect: the agent raises it, and the maintainer records a
decision. Neither side is followed silently.
- For every resource type, provider argument, version, service limit, or naming
constraint that an agent writes or reviews, it MUST consult the vendor
documentation through the servers `rules/mcp.md` makes mandatory, at the time
of the change:

| Question | Server |
| --- | --- |
| Provider or module version, resource arguments, deprecations | `terraform` |
| AWS service behaviour, limits, regional availability, recommended practice | `aws-knowledge` |
| Azure service behaviour, naming rules, recommended practice | `microsoft-learn` and `azure` |

- Versions, arguments, and limits change; an agent MUST NOT rely on its training
data for them.
- The pull request lists what was consulted (`rules/mcp.md`).

## Automated check

`scripts/check-mcp.sh` before work begins. The planned conventions workflow will
fail an infrastructure pull request whose "How it is verified" section lists no
documentation consulted.
45 changes: 45 additions & 0 deletions .agents/rules/iac/PC-IAC-001-module-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# PC-IAC-001 — Module Structure

**Source**: course rule PC-IAC-001, v1.0. **Status**: Adapted.

## Requirement

A reference module MUST be a directory inside its provider's module repository
(MTS-IAC-102), and MUST contain:

| File | Purpose |
| --- | --- |
| `README.md` | Purpose, inputs, outputs, requirements, and an example call |
| `CHANGELOG.md` | Keep a Changelog format, maintained by the release tooling |
| `versions.tf` | `required_version` and `required_providers` (PC-IAC-006) |
| `providers.tf` | Consumer alias declarations only; never provider configuration (PC-IAC-005) |
| `variables.tf` | Every input (PC-IAC-002) |
| `locals.tf` | One `locals` block (PC-IAC-012) |
| `data.tf` | Only the data sources PC-IAC-011 allows in modules |
| `main.tf` | The module's resources |
| `outputs.tf` | Every output (PC-IAC-007) |
| `.gitignore` | Terraform state, plans, `.terraform/`, and `*.tfvars` except `sample/terraform.tfvars` |
| `sample/` | The executable example defined by PC-IAC-026 |
| `tests/` | `terraform test` files (PC-IAC-018) |

Each file MUST contain at least a descriptive comment, even when empty of code.
A module MUST NOT spread its resources across additional top-level `.tf` files;
a module large enough to need that is two modules (PC-IAC-023).

## Project adaptation

- The course places each module in its own repository. The maintainer decided on
one repository per provider (MTS-IAC-102); the layout above applies to each
module directory inside it.
- PC-IAC-018 names `examples/`; this rule and PC-IAC-026 name `sample/`. The
project uses `sample/` only.

## Automated check

A structure contract in the module repository's CI fails when a module directory
lacks any required file or `sample/` file.

## Agent procedure

When creating a module, an agent copies the repository's module template rather
than writing the layout from memory, then fills every file.
Loading
Loading