[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-google-cloud-contributing":3,"mdc--yephzb-key":40,"related-repo-google-cloud-contributing":3416,"related-org-google-cloud-contributing":3450},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":27,"repoUrl":28,"updatedAt":29,"license":30,"forks":31,"topics":32,"repo":35,"sourceUrl":38,"mdContent":39},"contributing","contribute to Cloud Foundation Fabric","End-to-end workflow for contributing to Cloud Foundation Fabric: triaging GitHub issues, proactive feature development, validating with tests and Policy Troubleshooter, and submitting sanitized Pull Requests. Use when addressing a Fabric GitHub issue, developing a module or FAST stage change, or preparing a branch for a pull request.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"google-cloud","Google Cloud","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fgoogle-cloud.png","GoogleCloudPlatform",[13,17,20,23,24],{"name":14,"slug":15,"type":16},"GitHub","github","tag",{"name":18,"slug":19,"type":16},"Automation","automation",{"name":21,"slug":22,"type":16},"Engineering","engineering",{"name":9,"slug":8,"type":16},{"name":25,"slug":26,"type":16},"Pull Requests","pull-requests",2062,"https:\u002F\u002Fgithub.com\u002FGoogleCloudPlatform\u002Fcloud-foundation-fabric","2026-07-31T06:23:36.935005",null,1242,[33,8,34],"gcp","terraform",{"repoUrl":28,"stars":27,"forks":31,"topics":36,"description":37},[33,8,34],"End-to-end modular samples and landing zones toolkit for Terraform on GCP.","https:\u002F\u002Fgithub.com\u002FGoogleCloudPlatform\u002Fcloud-foundation-fabric\u002Ftree\u002FHEAD\u002Fskills\u002Fcontributing","---\nname: contributing\ndescription: \"End-to-end workflow for contributing to Cloud Foundation Fabric: triaging GitHub issues, proactive feature development, validating with tests and Policy Troubleshooter, and submitting sanitized Pull Requests. Use when addressing a Fabric GitHub issue, developing a module or FAST stage change, or preparing a branch for a pull request.\"\n---\n\n# Fabric Contribution Flow Skill\n\nThis skill defines the end-to-end workflow for contributing to Cloud Foundation Fabric. It supports two entry modes:\n- **Mode A: Issue Triage & Bug Fix**: You are addressing an assigned or reported GitHub Issue. Start at **Step 1**.\n- **Mode B: Proactive Development & PR Prep**: You are actively building a feature, refactoring code, or preparing an existing branch for a Pull Request. Jump directly to the **Design Sign-off Gate** before Step 2.\n\nIf it is unclear which mode applies, ask the user before proceeding.\n\n## Human-in-the-Loop Gates\n\nGate on steps that are hard to reverse, costly, or where your judgment is likely to diverge from a maintainer's. Keep mechanical, reversible steps (linting, tests, doc and inventory regeneration) autonomous. Gates are **blocking**: if you are running non-interactively and cannot get an answer, stop — never assume approval.\n\n| Gate | When | What the human decides |\n| :--- | :--- | :--- |\n| **Triage Disposition** | End of Step 1 (Mode A) | Whether the issue is in scope and worth pursuing: proceed, reject, or escalate. |\n| **Design Sign-off** | Before Step 2 | Approves the proposed design and scope. Mandatory for new features and interface changes; skippable for trivial fixes where the design is self-evident. |\n| **E2E Opt-in** | Step 5 | Whether to run live cloud verification. Providing a sandbox project ID **is** the consent to deploy to it. |\n| **PR Approval** | Step 6 | Reviews the final sanitized PR body before submission. |\n\n---\n\n## Step-by-Step Workflow\n\n```mermaid\ngraph TD\n    M1[Mode A: Issue Triage] --> A[1. Triage Issue]\n    A --> GT{Gate: Triage Disposition}\n    GT -->|Human approves| GD{Gate: Design Sign-off}\n    GT -->|Human rejects| X[Stop \u002F Report Back]\n    M2[Mode B: Proactive Dev] --> GD\n    GD --> B[2. Develop Fix \u002F Feature]\n    B --> C[3. Tests, Lint & Inventories]\n    C --> D[4. Pre-Submission Self-Review]\n    D -->|Issues found| B\n    D -->|Clean| GE{Gate: E2E Opt-in}\n    GE -->|Project ID provided| G[5. Live E2E Verification]\n    GE -->|Skipped| GS{Gate: PR Approval}\n    G -->|Failures| B\n    G -->|Verified| GS\n    GS --> F[6. Commit & Submit Sanitized PR]\n```\n\n### Step 1: Triage the Issue (Mode A Only)\n\n1.  **Retrieve Issue Details**: Use the GitHub CLI to view the issue context.\n    ```bash\n    gh issue view \u003Cissue-number>\n    ```\n\n2.  **Explore the Codebase**: Identify the target module (`modules\u002F\u003Cmodule-name>`) or FAST stage (`fast\u002Fstages\u002F\u003Cstage-name>`) that requires modification.\n\n3.  **Evaluate Fit & Scope**: Assess whether the issue is relevant for Fabric. Ensure it aligns with Fabric's core philosophy (modules should be lean, composable, and represent a single resource type context). Confirm the change has a sufficiently large scope and represents a generic, valuable addition to the module or FAST stage rather than a highly specific, one-off customization.\n\n4.  **Read Provider Documentation**: If the issue involves Google Cloud resources, retrieve and read the documentation for the involved GCP resources or Terraform provider resource\u002Fdatasource to ensure accurate implementation of its attributes, behaviors, and constraints.\n    *   Start from the provider registry, e.g. \u003Chttps:\u002F\u002Fregistry.terraform.io\u002Fproviders\u002Fhashicorp\u002Fgoogle\u002Flatest\u002Fdocs\u002Fresources\u002Fcompute_address>.\n    *   **Check the pinned provider range**: the repo pins the `google` provider in [default-versions.tf](..\u002F..\u002Fdefault-versions.tf). If the feature you are implementing requires provider functionality released after the pinned minimum version, that is a good reason to bump the pin — do it across the board with `uv run tools\u002Fversions.py --provider-min-version \u003Cx.y.z> --write-defaults`, which rewrites `default-versions.tf` and propagates it to every module and stage `versions.tf` (linting enforces they stay in sync). Never edit version pins in individual modules only.\n    *   Only rely on documentation for functionality available in a **released** provider version: docs from the provider's `main` branch may describe unreleased attributes that no released provider accepts at plan time.\n    > [!WARNING]\n    > Do NOT rely solely on the proposed solution, examples, or partial specifications provided in the issue description. Always retrieve and review the complete documentation schema from the official provider registry.\n    >\n    > **Workaround for Registry Page JS\u002FRedirection Errors**:\n    > If the registry page fails to load properly with your URL-fetching tool (e.g. returns \"Please enable Javascript\" or truncates due to HTML formatting issues), find the source markdown file on GitHub and fetch its raw content using `curl` to a scratch file (the `scratch\u002F` directory is gitignored), using the tag of the provider release whose docs you need (e.g. `v7.40.0`):\n    > ```bash\n    > curl -s https:\u002F\u002Fraw.githubusercontent.com\u002Fhashicorp\u002Fterraform-provider-google\u002Fv7.40.0\u002Fwebsite\u002Fdocs\u002Fr\u002Fworkstations_workstation_config.html.markdown -o scratch\u002Fworkstation_config_doc.md\n    > ```\n    > You can then search and view it locally to identify all supported arguments and blocks.\n    > Use this information to make an informed decision on which arguments to support. While 100% coverage is not always necessary or desirable, make a deliberate choice to include all arguments that are useful and align with Fabric's composability and simplicity goals, rather than missing them by omission.\n\n5.  **Gate — Triage Disposition**: Present your assessment (target module\u002Fstage, fit with Fabric's philosophy, scope evaluation) and let the human decide whether to proceed. If the issue fails the fit test, report your reasoning and stop — do NOT comment on, label, or close the issue yourself; disposition of the issue is a maintainer decision.\n\n### Gate: Design Sign-off (Modes A & B)\n\nBefore writing code, present the proposed design for approval:\n*   The target module(s)\u002Fstage(s) and the shape of the variable interface (new\u002Fchanged variables, whether `context` support is added).\n*   For provider-surface changes: the explicit list of provider arguments you intend to include and exclude, with reasoning.\n*   Whether the change requires bumping the pinned provider version (a repo-wide change — see Step 1.4).\n\nThis gate is **mandatory for new features and interface changes**. For trivial fixes (typos, one-line bugfixes with an obvious solution) where the design is self-evident, state your intent and proceed without blocking.\n\n### Step 2: Develop the Fix or Feature (Modes A & B)\n\n1.  **Align with Fabric Zen**: Ensure the design focuses on composition, encapsulates logical entities (like IAM and log sinks directly inside modules), adopts common interfaces, and keeps code flat and easy to evolve.\n    > [!IMPORTANT]\n    > You MUST strictly follow the design principles and coding conventions defined in [CONTRIBUTING.md](..\u002F..\u002FCONTRIBUTING.md) and [GEMINI.md](..\u002F..\u002FGEMINI.md) when designing variables, modules, and factories.\n\n2.  **Set Up the Git Branch**:\n    *   If you are not already on a feature branch, create one named after your username and the feature\u002Ffix (e.g., `\u003Cusername>\u002F\u003Cfeature-name>`, such as `johndoe\u002Ffeature-name`):\n        ```bash\n        git checkout master && git pull\n        git checkout -b \u003Cusername>\u002F\u003Cfeature-name>\n        ```\n    *   If preparing an existing branch (Mode B), do NOT create a new one; instead make sure it is up to date with `master` (rebase or merge) before submission.\n    *   Direct branches require push access to the repo (granted to regular contributors on request). Without it, use the fork-based flow described in [CONTRIBUTING.md](..\u002F..\u002FCONTRIBUTING.md); the rest of this workflow is identical.\n\n3.  **Apply Fabric\u002FFAST Design Conventions**:\n    *   **Context Interpolation**: If context support is relevant and needed for the module (e.g., to support resolving symbolic references like `\"$project_ids:myprj\"`), add a `context` variable block and implement `ctx`\u002F`ctx_p` locals in `main.tf`. Do not add it blindly if the module does not benefit from symbolic interpolation.\n    *   **Compact Variables**: Leverage objects with `optional()` attributes to keep user interfaces clean. Prefer `optional()` **without** an explicit default so the provider applies whatever default value it defines; only set a default when module logic needs a known value.\n    *   **Stable State Keys**: Always use maps instead of lists for collection variables to avoid index shifts in Terraform state.\n    *   **Scope Isolation**: Use private locals (prefixed with `_`) for intermediate transformations, reserving module-level locals for values referenced by resources.\n    *   **Locals Separation for Complex Manipulations**: When adding complex conditional strings, string interpolations, or transformations to resource attributes, compute the map in `locals` so the resource block references a clean index like `local.my_map[each.key]`. This keeps resource blocks legible (\u003C79 chars) and avoids cluttering resource definitions.\n    *   **Validate ENUM Variables**: When adding or exposing variables that mirror ENUM values in the underlying Terraform provider, always include a `validation` block to check for allowed values at plan time. Example from `modules\u002Fgcs`:\n        ```hcl\n        variable \"storage_class\" {\n          description = \"Bucket storage class.\"\n          type        = string\n          default     = \"STANDARD\"\n          validation {\n            condition     = contains([\"STANDARD\", \"MULTI_REGIONAL\", \"REGIONAL\", \"NEARLINE\", \"COLDLINE\", \"ARCHIVE\"], var.storage_class)\n            error_message = \"Storage class must be one of STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE.\"\n          }\n        }\n        ```\n\n4.  **Reference Implementations & Knowledge Sources**:\n    *   [compute-vm](..\u002F..\u002Fmodules\u002Fcompute-vm): Ideal reference for compact variable design, optional objects with defaults, and map variables.\n    *   [project](..\u002F..\u002Fmodules\u002Fproject): Best reference for logical entity encapsulation (IAM, log sinks, Shared VPC configurations), complex locals transformation, and context-based interpolation.\n    *   [adrs\u002F](..\u002F..\u002Fadrs\u002F): Architectural Decision Records documenting the rationale behind key design patterns (e.g. the IAM ADR explains the full `iam*` interface). Before designing, build an index of all ADRs with their title and status:\n        ```bash\n        find adrs -name '*.md' ! -name 'README.md' | sort | while read -r f; do\n          st=$(awk '\u002F^## Status\u002F {getline; while ($0 == \"\") getline; print; exit}' \"$f\")\n          printf '%s | %s | %s\\n' \"$f\" \"$(head -1 \"$f\" | sed 's\u002F^# \u002F\u002F')\" \"$st\"\n        done\n        ```\n        Read in full any ADR whose title relates to the area you are changing. Weigh them by status: ignore `Rejected` ones, treat `Draft`\u002F`Proposed` as directional rather than settled, and remember that even implemented ADRs can lag behind the code — on conflict, the current code and golden-path modules are the source of truth.\n\n5.  **Maintain Code Consistency**:\n    *   Always keep variables in `variables.tf` and outputs in `outputs.tf` in strict alphabetical order. For larger modules, variables and outputs can be grouped logically into separate files (e.g. `variables-logging.tf`), each internally alphabetically sorted.\n    *   Limit line length to 79 characters (relaxed for long attribute names and descriptions).\n    *   **JSON Schema & Factory Alignment**: When a change is made to a module that implements a factory (e.g., `cloud-workstations`) or is the base for one (e.g., `net-vpc-factory` to `2-networking`), the schemas MUST be updated (including their `.schema.md` documentation files), and the factory code should be updated to mirror the changed variable surface. Ensure you regenerate the schema documentation by running:\n        ```bash\n        uv run tools\u002Fschema_docs.py\n        ```\n    *   **Duplicated Files**: Some files (e.g. schemas, factory test fixtures) are intentionally duplicated across modules and stages, and CI enforces that copies stay identical. When changing such a file, sync all its copies; the duplicate groups are listed in [tools\u002Fduplicate-diff.py](..\u002F..\u002Ftools\u002Fduplicate-diff.py) — update that list when adding new duplicated files.\n\n6.  **Update or Add Tests**:\n    *   For modules: If the change introduces a new feature or configuration option, update an existing example in the module's `README.md` (or add a new one) to demonstrate its usage. Ensure the example includes `# tftest` parameters and update the corresponding inventory YAML files under `tests\u002Fmodules\u002F\u003Cmodule_name>\u002Fexamples\u002F`.\n    *   For FAST stages: Update `tftest.yaml` scenarios and tfvars\u002Fyaml inventories under `tests\u002Ffast\u002F...`.\n    > [!NOTE]\n    > Module directories use hyphens (`modules\u002Fnet-vpn-ha`) while test directories generally use underscores (`tests\u002Fmodules\u002Fnet_vpn_ha`). Always check the actual test directory name — a mistyped path makes `pytest` silently collect zero tests.\n\n### Step 3: Run Tests, Linting & Inventory Regeneration (Modes A & B)\n\nRun Python tooling through `uv run` — the scripts in `tools\u002F` declare their dependencies inline, so no virtual environment setup is needed. For faster Terraform testing, always set `TF_PLUGIN_CACHE_DIR=\u002Ftmp\u002Ftfcache`.\n\n1.  **Run Unified Linting**: Execute `tools\u002Flint.sh` to check copyright boilerplates, Terraform format (`terraform fmt`), alphabetical sorting, and schema validations:\n    ```bash\n    uv run --with-requirements tools\u002Frequirements.txt tools\u002Flint.sh\n    ```\n\n2.  **Update Documentation**: If you changed variables or outputs, check consistency and regenerate the README documentation tables:\n    ```bash\n    uv run tools\u002Fcheck_documentation.py modules\u002F\u003Cmodule-name>\n    uv run tools\u002Ftfdoc.py --replace modules\u002F\u003Cmodule-name>\n    ```\n\n3.  **Run Impacted Tests**: Execute `pytest` on the target module\u002Fstage (using the plugin cache directory for speed):\n    ```bash\n    mkdir -p \u002Ftmp\u002Ftfcache\n    # tftest.yaml scenarios (note: underscores in the test directory name)\n    TF_PLUGIN_CACHE_DIR=\u002Ftmp\u002Ftfcache uv run --with-requirements tests\u002Frequirements.txt pytest tests\u002Fmodules\u002F\u003Cmodule_name>\n    # README example tests\n    TF_PLUGIN_CACHE_DIR=\u002Ftmp\u002Ftfcache uv run --with-requirements tests\u002Frequirements.txt pytest -k 'modules and \u003Cmodule-name>:' tests\u002Fexamples\n    ```\n\n4.  **Regenerate Test Inventories**: If module-level tests (`tftest.yaml`) or README example inventories fail due to intentional plan output changes, regenerate them using `generate_plan_summary.py`:\n    ```bash\n    # For module-level tftest.yaml inventories:\n    uv run tools\u002Fgenerate_plan_summary.py tests\u002Fmodules\u002F\u003Cmodule_name>\u002Ftftest.yaml \u003Ctest-name> --save\n\n    # For README.md example inventories:\n    uv run tools\u002Fgenerate_plan_summary.py modules\u002F\u003Cmodule-name>\u002FREADME.md \"\u003CExample Heading>\" --save\n    ```\n    > [!CAUTION]\n    > `--save` overwrites the assertion baseline, so a regenerated inventory makes the test pass **by construction**. After regenerating, diff the inventory against the previous version (`git diff tests\u002F`), verify that every changed line maps to the intended feature or fix, and report the inventory diff to the user. If a changed line is not explained by your change, treat it as a bug in your code, not a baseline to save.\n\n### Step 4: Pre-Submission Self-Review (Modes A & B)\n\nReview your own diff (`git diff HEAD`, or `git diff master...HEAD` for an existing branch) against the repository guidelines (`GEMINI.md`, `CONTRIBUTING.md`) before submission. This is a pre-flight self-check; the canonical automated review runs in CI after the PR is opened (`Automated PR Review` workflow) — do NOT imitate its output format or header.\n\nCheck the diff against this checklist:\n\n*   [ ] Naming conventions respected (variables, outputs, resources).\n*   [ ] Context support (`ctx` variables) present where the module warrants it.\n*   [ ] IAM follows the standard `iam*` interface patterns.\n*   [ ] Examples with `# tftest` parameters cover the new\u002Fchanged behavior.\n*   [ ] **CRITICAL TESTING RULE**: if resource blocks were modified (adding a new argument or modifying an existing one), the Terraform plan output changes — the corresponding test inventory YAML files (`tests\u002F...\u002F*.yaml`) MUST be updated in the diff. If they are not, this is a critical testing failure.\n*   [ ] JSON schemas and factory code consistent with the changed variable surface.\n*   [ ] Documentation tables regenerated (`tfdoc`), alphabetical order preserved.\n\nReport the outcome as a short plain-text list of issues found (or \"no issues found\") under a `Pre-submission self-review` heading — no emojis, no status tables. Fix any issue and loop back to Step 3 until the checklist passes.\n\n### Step 5: Live Verification — E2E Sandbox & Policy Troubleshooter (Modes A & B — Optional \u002F Recommended)\n\nWhen code modifications affect GCP resource structures or APIs, run a live E2E sandbox deployment test. Run it only once the diff is stable (tests and self-review pass) to avoid repeated cloud deployments.\n\n1.  **Gate — E2E Opt-in**: Ask the user for a GCP project ID (and if applicable, parent folder \u002F billing account details) for E2E sandbox testing, stating explicitly that providing the project ID authorizes you to deploy and destroy resources in it with `-auto-approve`. If no project is provided, skip this step entirely.\n\n2.  **Verify Credentials**: Confirm `gcloud` authentication and Application Default Credentials are available (`gcloud auth list`) before attempting any deployment, and surface a clear error rather than failing mid-apply.\n\n3.  **Create Sandbox Directory**: Create a temporary sandbox folder under `scratch\u002Fe2e_sandbox\u002F` (the `scratch\u002F` directory is gitignored).\n\n4.  **Generate Test Configuration**:\n    *   Generate a root Terraform module `main.tf` in the sandbox directory.\n    *   **CRITICAL**: The `source` argument of the module call MUST point to the **local path** of the modified module in the repository (e.g. `source = \"..\u002F..\u002Fmodules\u002F\u003Cmodule-name>\"`), NOT the GitHub reference, to ensure your local changes are tested.\n    *   Set up necessary providers and variables.\n\n5.  **Deploy and Verify**:\n    *   Run `terraform init` and `terraform apply -auto-approve` in the sandbox folder.\n    *   Confirm that all resources are created successfully.\n\n6.  **Verify IAM Conditions via Policy Troubleshooter**:\n    *   When modifying IAM conditional bindings or policies, use Google Cloud's official IAM Policy Troubleshooter (`gcloud policy-troubleshoot iam`) to verify runtime evaluation against live target resources.\n    *   **CRITICAL**: GCP IAM runtime evaluates `resource.name` using numeric Project Numbers. Always test conditions against `--resource-name` with the numeric Project Number:\n        ```bash\n        gcloud policy-troubleshoot iam \u002F\u002Flogging.googleapis.com\u002Fprojects\u002F\u003CPROJECT_NUMBER>\u002Flocations\u002Fglobal\u002Fbuckets\u002F\u003CBUCKET> \\\n          --permission=logging.buckets.write \\\n          --principal-email=\u003CTEST_PRINCIPAL_EMAIL> \\\n          --resource-name=projects\u002F\u003CPROJECT_NUMBER>\u002Flocations\u002Fglobal\u002Fbuckets\u002F\u003CBUCKET> \\\n          --format=\"json(access,explainedPolicies[].bindingExplanations)\"\n        ```\n\n7.  **Destroy Resources**:\n    *   Once verified, run `terraform destroy -auto-approve` to tear down all created resources and avoid ongoing cloud costs.\n    *   Delete the sandbox directory.\n    *   If verification surfaced failures, fix the code and loop back to Step 3 before re-deploying.\n\n### Step 6: Commit & Submit the PR (Modes A & B)\n\n1.  **Sanitize Before Committing**: PII sanitization applies to **everything that leaves your machine** — commits, file contents, and the PR body. Never commit files containing real GCP project IDs, numeric project numbers, personal email addresses, or custom resource names from live testing; verify `git status` shows no stray sandbox or scratch files before staging.\n\n2.  **Commit and Push**: Stage the relevant files, commit with a short imperative message describing the change, make sure the branch is up to date with `master` (rebase if needed), and push:\n    ```bash\n    git add \u003Cfiles>\n    git commit -m \"Add \u003Cfeature> to modules\u002F\u003Cmodule-name>\"\n    git push -u origin \u003Cusername>\u002F\u003Cfeature-name>\n    ```\n\n3.  **Format the PR Title**: Do NOT use Conventional Commits format (no `feat:` or `fix:` prefixes). Use a short, capitalized, imperative title (e.g., \"Add native tag bindings support to `modules\u002Fnet-firewall-policy`\").\n\n4.  **Write the PR Body**:\n    *   Explain the problem, rationale, and the fix clearly.\n    *   **Document Verification & E2E Testing Methodology**: Detail any local unit tests, live E2E sandbox deployments, or Policy Troubleshooter API verifications performed so reviewers can see the exact testing rationale and methodology.\n    *   **CRITICAL PII SANITIZATION**: Before writing the PR description, **MUST scrub all developer PII** (real GCP project IDs, numeric project numbers, personal email addresses, usernames, and custom bucket\u002Fresource names) and replace them with generic placeholders (e.g., `my-project`, `123456789012`, `user:tester@example.com`, `test-bucket`).\n    *   Do NOT touch `CHANGELOG.md`: release notes are generated from PR labels applied by maintainers.\n\n5.  **Gate — PR Approval**: Present the final sanitized PR title and body to the user and get explicit approval before creating the PR.\n\n6.  **Create the PR**:\n    *   **CRITICAL PITFALL**: Do NOT pass the body inline on the CLI if it contains backticks (e.g. `gh pr create --body \"Fixes `bug`\"`) as the shell will evaluate backticks, corrupting the description. Do NOT use shell redirection or heredocs to create the body file either (see `GEMINI.md`).\n    *   Instead, write the body to a temporary file **using your file-writing tool**, then reference it:\n        ```bash\n        gh pr create --title \"Your PR Title\" --body-file \u002Ftmp\u002Fpr-body.md\n        ```\n",{"data":41,"body":42},{"name":4,"description":6},{"type":43,"children":44},"root",[45,54,60,100,105,112,124,249,253,259,417,424,718,724,729,755,767,773,1793,1799,1827,2374,2380,2423,2428,2542,2555,2561,2566,2997,3003,3410],{"type":46,"tag":47,"props":48,"children":50},"element","h1",{"id":49},"fabric-contribution-flow-skill",[51],{"type":52,"value":53},"text","Fabric Contribution Flow Skill",{"type":46,"tag":55,"props":56,"children":57},"p",{},[58],{"type":52,"value":59},"This skill defines the end-to-end workflow for contributing to Cloud Foundation Fabric. It supports two entry modes:",{"type":46,"tag":61,"props":62,"children":63},"ul",{},[64,83],{"type":46,"tag":65,"props":66,"children":67},"li",{},[68,74,76,81],{"type":46,"tag":69,"props":70,"children":71},"strong",{},[72],{"type":52,"value":73},"Mode A: Issue Triage & Bug Fix",{"type":52,"value":75},": You are addressing an assigned or reported GitHub Issue. Start at ",{"type":46,"tag":69,"props":77,"children":78},{},[79],{"type":52,"value":80},"Step 1",{"type":52,"value":82},".",{"type":46,"tag":65,"props":84,"children":85},{},[86,91,93,98],{"type":46,"tag":69,"props":87,"children":88},{},[89],{"type":52,"value":90},"Mode B: Proactive Development & PR Prep",{"type":52,"value":92},": You are actively building a feature, refactoring code, or preparing an existing branch for a Pull Request. Jump directly to the ",{"type":46,"tag":69,"props":94,"children":95},{},[96],{"type":52,"value":97},"Design Sign-off Gate",{"type":52,"value":99}," before Step 2.",{"type":46,"tag":55,"props":101,"children":102},{},[103],{"type":52,"value":104},"If it is unclear which mode applies, ask the user before proceeding.",{"type":46,"tag":106,"props":107,"children":109},"h2",{"id":108},"human-in-the-loop-gates",[110],{"type":52,"value":111},"Human-in-the-Loop Gates",{"type":46,"tag":55,"props":113,"children":114},{},[115,117,122],{"type":52,"value":116},"Gate on steps that are hard to reverse, costly, or where your judgment is likely to diverge from a maintainer's. Keep mechanical, reversible steps (linting, tests, doc and inventory regeneration) autonomous. Gates are ",{"type":46,"tag":69,"props":118,"children":119},{},[120],{"type":52,"value":121},"blocking",{"type":52,"value":123},": if you are running non-interactively and cannot get an answer, stop — never assume approval.",{"type":46,"tag":125,"props":126,"children":127},"table",{},[128,153],{"type":46,"tag":129,"props":130,"children":131},"thead",{},[132],{"type":46,"tag":133,"props":134,"children":135},"tr",{},[136,143,148],{"type":46,"tag":137,"props":138,"children":140},"th",{"align":139},"left",[141],{"type":52,"value":142},"Gate",{"type":46,"tag":137,"props":144,"children":145},{"align":139},[146],{"type":52,"value":147},"When",{"type":46,"tag":137,"props":149,"children":150},{"align":139},[151],{"type":52,"value":152},"What the human decides",{"type":46,"tag":154,"props":155,"children":156},"tbody",{},[157,179,200,228],{"type":46,"tag":133,"props":158,"children":159},{},[160,169,174],{"type":46,"tag":161,"props":162,"children":163},"td",{"align":139},[164],{"type":46,"tag":69,"props":165,"children":166},{},[167],{"type":52,"value":168},"Triage Disposition",{"type":46,"tag":161,"props":170,"children":171},{"align":139},[172],{"type":52,"value":173},"End of Step 1 (Mode A)",{"type":46,"tag":161,"props":175,"children":176},{"align":139},[177],{"type":52,"value":178},"Whether the issue is in scope and worth pursuing: proceed, reject, or escalate.",{"type":46,"tag":133,"props":180,"children":181},{},[182,190,195],{"type":46,"tag":161,"props":183,"children":184},{"align":139},[185],{"type":46,"tag":69,"props":186,"children":187},{},[188],{"type":52,"value":189},"Design Sign-off",{"type":46,"tag":161,"props":191,"children":192},{"align":139},[193],{"type":52,"value":194},"Before Step 2",{"type":46,"tag":161,"props":196,"children":197},{"align":139},[198],{"type":52,"value":199},"Approves the proposed design and scope. Mandatory for new features and interface changes; skippable for trivial fixes where the design is self-evident.",{"type":46,"tag":133,"props":201,"children":202},{},[203,211,216],{"type":46,"tag":161,"props":204,"children":205},{"align":139},[206],{"type":46,"tag":69,"props":207,"children":208},{},[209],{"type":52,"value":210},"E2E Opt-in",{"type":46,"tag":161,"props":212,"children":213},{"align":139},[214],{"type":52,"value":215},"Step 5",{"type":46,"tag":161,"props":217,"children":218},{"align":139},[219,221,226],{"type":52,"value":220},"Whether to run live cloud verification. Providing a sandbox project ID ",{"type":46,"tag":69,"props":222,"children":223},{},[224],{"type":52,"value":225},"is",{"type":52,"value":227}," the consent to deploy to it.",{"type":46,"tag":133,"props":229,"children":230},{},[231,239,244],{"type":46,"tag":161,"props":232,"children":233},{"align":139},[234],{"type":46,"tag":69,"props":235,"children":236},{},[237],{"type":52,"value":238},"PR Approval",{"type":46,"tag":161,"props":240,"children":241},{"align":139},[242],{"type":52,"value":243},"Step 6",{"type":46,"tag":161,"props":245,"children":246},{"align":139},[247],{"type":52,"value":248},"Reviews the final sanitized PR body before submission.",{"type":46,"tag":250,"props":251,"children":252},"hr",{},[],{"type":46,"tag":106,"props":254,"children":256},{"id":255},"step-by-step-workflow",[257],{"type":52,"value":258},"Step-by-Step Workflow",{"type":46,"tag":260,"props":261,"children":266},"pre",{"className":262,"code":263,"language":264,"meta":265,"style":265},"language-mermaid shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","graph TD\n    M1[Mode A: Issue Triage] --> A[1. Triage Issue]\n    A --> GT{Gate: Triage Disposition}\n    GT -->|Human approves| GD{Gate: Design Sign-off}\n    GT -->|Human rejects| X[Stop \u002F Report Back]\n    M2[Mode B: Proactive Dev] --> GD\n    GD --> B[2. Develop Fix \u002F Feature]\n    B --> C[3. Tests, Lint & Inventories]\n    C --> D[4. Pre-Submission Self-Review]\n    D -->|Issues found| B\n    D -->|Clean| GE{Gate: E2E Opt-in}\n    GE -->|Project ID provided| G[5. Live E2E Verification]\n    GE -->|Skipped| GS{Gate: PR Approval}\n    G -->|Failures| B\n    G -->|Verified| GS\n    GS --> F[6. Commit & Submit Sanitized PR]\n","mermaid","",[267],{"type":46,"tag":268,"props":269,"children":270},"code",{"__ignoreMap":265},[271,282,291,300,309,318,327,336,345,354,363,372,381,390,399,408],{"type":46,"tag":272,"props":273,"children":276},"span",{"class":274,"line":275},"line",1,[277],{"type":46,"tag":272,"props":278,"children":279},{},[280],{"type":52,"value":281},"graph TD\n",{"type":46,"tag":272,"props":283,"children":285},{"class":274,"line":284},2,[286],{"type":46,"tag":272,"props":287,"children":288},{},[289],{"type":52,"value":290},"    M1[Mode A: Issue Triage] --> A[1. Triage Issue]\n",{"type":46,"tag":272,"props":292,"children":294},{"class":274,"line":293},3,[295],{"type":46,"tag":272,"props":296,"children":297},{},[298],{"type":52,"value":299},"    A --> GT{Gate: Triage Disposition}\n",{"type":46,"tag":272,"props":301,"children":303},{"class":274,"line":302},4,[304],{"type":46,"tag":272,"props":305,"children":306},{},[307],{"type":52,"value":308},"    GT -->|Human approves| GD{Gate: Design Sign-off}\n",{"type":46,"tag":272,"props":310,"children":312},{"class":274,"line":311},5,[313],{"type":46,"tag":272,"props":314,"children":315},{},[316],{"type":52,"value":317},"    GT -->|Human rejects| X[Stop \u002F Report Back]\n",{"type":46,"tag":272,"props":319,"children":321},{"class":274,"line":320},6,[322],{"type":46,"tag":272,"props":323,"children":324},{},[325],{"type":52,"value":326},"    M2[Mode B: Proactive Dev] --> GD\n",{"type":46,"tag":272,"props":328,"children":330},{"class":274,"line":329},7,[331],{"type":46,"tag":272,"props":332,"children":333},{},[334],{"type":52,"value":335},"    GD --> B[2. Develop Fix \u002F Feature]\n",{"type":46,"tag":272,"props":337,"children":339},{"class":274,"line":338},8,[340],{"type":46,"tag":272,"props":341,"children":342},{},[343],{"type":52,"value":344},"    B --> C[3. Tests, Lint & Inventories]\n",{"type":46,"tag":272,"props":346,"children":348},{"class":274,"line":347},9,[349],{"type":46,"tag":272,"props":350,"children":351},{},[352],{"type":52,"value":353},"    C --> D[4. Pre-Submission Self-Review]\n",{"type":46,"tag":272,"props":355,"children":357},{"class":274,"line":356},10,[358],{"type":46,"tag":272,"props":359,"children":360},{},[361],{"type":52,"value":362},"    D -->|Issues found| B\n",{"type":46,"tag":272,"props":364,"children":366},{"class":274,"line":365},11,[367],{"type":46,"tag":272,"props":368,"children":369},{},[370],{"type":52,"value":371},"    D -->|Clean| GE{Gate: E2E Opt-in}\n",{"type":46,"tag":272,"props":373,"children":375},{"class":274,"line":374},12,[376],{"type":46,"tag":272,"props":377,"children":378},{},[379],{"type":52,"value":380},"    GE -->|Project ID provided| G[5. Live E2E Verification]\n",{"type":46,"tag":272,"props":382,"children":384},{"class":274,"line":383},13,[385],{"type":46,"tag":272,"props":386,"children":387},{},[388],{"type":52,"value":389},"    GE -->|Skipped| GS{Gate: PR Approval}\n",{"type":46,"tag":272,"props":391,"children":393},{"class":274,"line":392},14,[394],{"type":46,"tag":272,"props":395,"children":396},{},[397],{"type":52,"value":398},"    G -->|Failures| B\n",{"type":46,"tag":272,"props":400,"children":402},{"class":274,"line":401},15,[403],{"type":46,"tag":272,"props":404,"children":405},{},[406],{"type":52,"value":407},"    G -->|Verified| GS\n",{"type":46,"tag":272,"props":409,"children":411},{"class":274,"line":410},16,[412],{"type":46,"tag":272,"props":413,"children":414},{},[415],{"type":52,"value":416},"    GS --> F[6. Commit & Submit Sanitized PR]\n",{"type":46,"tag":418,"props":419,"children":421},"h3",{"id":420},"step-1-triage-the-issue-mode-a-only",[422],{"type":52,"value":423},"Step 1: Triage the Issue (Mode A Only)",{"type":46,"tag":425,"props":426,"children":427},"ol",{},[428,489,515,525,708],{"type":46,"tag":65,"props":429,"children":430},{},[431,436,438],{"type":46,"tag":69,"props":432,"children":433},{},[434],{"type":52,"value":435},"Retrieve Issue Details",{"type":52,"value":437},": Use the GitHub CLI to view the issue context.",{"type":46,"tag":260,"props":439,"children":443},{"className":440,"code":441,"language":442,"meta":265,"style":265},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","gh issue view \u003Cissue-number>\n","bash",[444],{"type":46,"tag":268,"props":445,"children":446},{"__ignoreMap":265},[447],{"type":46,"tag":272,"props":448,"children":449},{"class":274,"line":275},[450,456,462,467,473,478,484],{"type":46,"tag":272,"props":451,"children":453},{"style":452},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[454],{"type":52,"value":455},"gh",{"type":46,"tag":272,"props":457,"children":459},{"style":458},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[460],{"type":52,"value":461}," issue",{"type":46,"tag":272,"props":463,"children":464},{"style":458},[465],{"type":52,"value":466}," view",{"type":46,"tag":272,"props":468,"children":470},{"style":469},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[471],{"type":52,"value":472}," \u003C",{"type":46,"tag":272,"props":474,"children":475},{"style":458},[476],{"type":52,"value":477},"issue-numbe",{"type":46,"tag":272,"props":479,"children":481},{"style":480},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[482],{"type":52,"value":483},"r",{"type":46,"tag":272,"props":485,"children":486},{"style":469},[487],{"type":52,"value":488},">\n",{"type":46,"tag":65,"props":490,"children":491},{},[492,497,499,505,507,513],{"type":46,"tag":69,"props":493,"children":494},{},[495],{"type":52,"value":496},"Explore the Codebase",{"type":52,"value":498},": Identify the target module (",{"type":46,"tag":268,"props":500,"children":502},{"className":501},[],[503],{"type":52,"value":504},"modules\u002F\u003Cmodule-name>",{"type":52,"value":506},") or FAST stage (",{"type":46,"tag":268,"props":508,"children":510},{"className":509},[],[511],{"type":52,"value":512},"fast\u002Fstages\u002F\u003Cstage-name>",{"type":52,"value":514},") that requires modification.",{"type":46,"tag":65,"props":516,"children":517},{},[518,523],{"type":46,"tag":69,"props":519,"children":520},{},[521],{"type":52,"value":522},"Evaluate Fit & Scope",{"type":52,"value":524},": Assess whether the issue is relevant for Fabric. Ensure it aligns with Fabric's core philosophy (modules should be lean, composable, and represent a single resource type context). Confirm the change has a sufficiently large scope and represents a generic, valuable addition to the module or FAST stage rather than a highly specific, one-off customization.",{"type":46,"tag":65,"props":526,"children":527},{},[528,533,535,621],{"type":46,"tag":69,"props":529,"children":530},{},[531],{"type":52,"value":532},"Read Provider Documentation",{"type":52,"value":534},": If the issue involves Google Cloud resources, retrieve and read the documentation for the involved GCP resources or Terraform provider resource\u002Fdatasource to ensure accurate implementation of its attributes, behaviors, and constraints.",{"type":46,"tag":61,"props":536,"children":537},{},[538,552,601],{"type":46,"tag":65,"props":539,"children":540},{},[541,543,551],{"type":52,"value":542},"Start from the provider registry, e.g. ",{"type":46,"tag":544,"props":545,"children":549},"a",{"href":546,"rel":547},"https:\u002F\u002Fregistry.terraform.io\u002Fproviders\u002Fhashicorp\u002Fgoogle\u002Flatest\u002Fdocs\u002Fresources\u002Fcompute_address",[548],"nofollow",[550],{"type":52,"value":546},{"type":52,"value":82},{"type":46,"tag":65,"props":553,"children":554},{},[555,560,562,568,570,576,578,584,586,591,593,599],{"type":46,"tag":69,"props":556,"children":557},{},[558],{"type":52,"value":559},"Check the pinned provider range",{"type":52,"value":561},": the repo pins the ",{"type":46,"tag":268,"props":563,"children":565},{"className":564},[],[566],{"type":52,"value":567},"google",{"type":52,"value":569}," provider in ",{"type":46,"tag":544,"props":571,"children":573},{"href":572},"..\u002F..\u002Fdefault-versions.tf",[574],{"type":52,"value":575},"default-versions.tf",{"type":52,"value":577},". If the feature you are implementing requires provider functionality released after the pinned minimum version, that is a good reason to bump the pin — do it across the board with ",{"type":46,"tag":268,"props":579,"children":581},{"className":580},[],[582],{"type":52,"value":583},"uv run tools\u002Fversions.py --provider-min-version \u003Cx.y.z> --write-defaults",{"type":52,"value":585},", which rewrites ",{"type":46,"tag":268,"props":587,"children":589},{"className":588},[],[590],{"type":52,"value":575},{"type":52,"value":592}," and propagates it to every module and stage ",{"type":46,"tag":268,"props":594,"children":596},{"className":595},[],[597],{"type":52,"value":598},"versions.tf",{"type":52,"value":600}," (linting enforces they stay in sync). Never edit version pins in individual modules only.",{"type":46,"tag":65,"props":602,"children":603},{},[604,606,611,613,619],{"type":52,"value":605},"Only rely on documentation for functionality available in a ",{"type":46,"tag":69,"props":607,"children":608},{},[609],{"type":52,"value":610},"released",{"type":52,"value":612}," provider version: docs from the provider's ",{"type":46,"tag":268,"props":614,"children":616},{"className":615},[],[617],{"type":52,"value":618},"main",{"type":52,"value":620}," branch may describe unreleased attributes that no released provider accepts at plan time.",{"type":46,"tag":622,"props":623,"children":624},"blockquote",{},[625,635,669,703],{"type":46,"tag":55,"props":626,"children":627},{},[628,633],{"type":46,"tag":272,"props":629,"children":630},{},[631],{"type":52,"value":632},"!WARNING",{"type":52,"value":634},"\nDo NOT rely solely on the proposed solution, examples, or partial specifications provided in the issue description. Always retrieve and review the complete documentation schema from the official provider registry.",{"type":46,"tag":55,"props":636,"children":637},{},[638,643,645,651,653,659,661,667],{"type":46,"tag":69,"props":639,"children":640},{},[641],{"type":52,"value":642},"Workaround for Registry Page JS\u002FRedirection Errors",{"type":52,"value":644},":\nIf the registry page fails to load properly with your URL-fetching tool (e.g. returns \"Please enable Javascript\" or truncates due to HTML formatting issues), find the source markdown file on GitHub and fetch its raw content using ",{"type":46,"tag":268,"props":646,"children":648},{"className":647},[],[649],{"type":52,"value":650},"curl",{"type":52,"value":652}," to a scratch file (the ",{"type":46,"tag":268,"props":654,"children":656},{"className":655},[],[657],{"type":52,"value":658},"scratch\u002F",{"type":52,"value":660}," directory is gitignored), using the tag of the provider release whose docs you need (e.g. ",{"type":46,"tag":268,"props":662,"children":664},{"className":663},[],[665],{"type":52,"value":666},"v7.40.0",{"type":52,"value":668},"):",{"type":46,"tag":260,"props":670,"children":672},{"className":440,"code":671,"language":442,"meta":265,"style":265},"curl -s https:\u002F\u002Fraw.githubusercontent.com\u002Fhashicorp\u002Fterraform-provider-google\u002Fv7.40.0\u002Fwebsite\u002Fdocs\u002Fr\u002Fworkstations_workstation_config.html.markdown -o scratch\u002Fworkstation_config_doc.md\n",[673],{"type":46,"tag":268,"props":674,"children":675},{"__ignoreMap":265},[676],{"type":46,"tag":272,"props":677,"children":678},{"class":274,"line":275},[679,683,688,693,698],{"type":46,"tag":272,"props":680,"children":681},{"style":452},[682],{"type":52,"value":650},{"type":46,"tag":272,"props":684,"children":685},{"style":458},[686],{"type":52,"value":687}," -s",{"type":46,"tag":272,"props":689,"children":690},{"style":458},[691],{"type":52,"value":692}," https:\u002F\u002Fraw.githubusercontent.com\u002Fhashicorp\u002Fterraform-provider-google\u002Fv7.40.0\u002Fwebsite\u002Fdocs\u002Fr\u002Fworkstations_workstation_config.html.markdown",{"type":46,"tag":272,"props":694,"children":695},{"style":458},[696],{"type":52,"value":697}," -o",{"type":46,"tag":272,"props":699,"children":700},{"style":458},[701],{"type":52,"value":702}," scratch\u002Fworkstation_config_doc.md\n",{"type":46,"tag":55,"props":704,"children":705},{},[706],{"type":52,"value":707},"You can then search and view it locally to identify all supported arguments and blocks.\nUse this information to make an informed decision on which arguments to support. While 100% coverage is not always necessary or desirable, make a deliberate choice to include all arguments that are useful and align with Fabric's composability and simplicity goals, rather than missing them by omission.",{"type":46,"tag":65,"props":709,"children":710},{},[711,716],{"type":46,"tag":69,"props":712,"children":713},{},[714],{"type":52,"value":715},"Gate — Triage Disposition",{"type":52,"value":717},": Present your assessment (target module\u002Fstage, fit with Fabric's philosophy, scope evaluation) and let the human decide whether to proceed. If the issue fails the fit test, report your reasoning and stop — do NOT comment on, label, or close the issue yourself; disposition of the issue is a maintainer decision.",{"type":46,"tag":418,"props":719,"children":721},{"id":720},"gate-design-sign-off-modes-a-b",[722],{"type":52,"value":723},"Gate: Design Sign-off (Modes A & B)",{"type":46,"tag":55,"props":725,"children":726},{},[727],{"type":52,"value":728},"Before writing code, present the proposed design for approval:",{"type":46,"tag":61,"props":730,"children":731},{},[732,745,750],{"type":46,"tag":65,"props":733,"children":734},{},[735,737,743],{"type":52,"value":736},"The target module(s)\u002Fstage(s) and the shape of the variable interface (new\u002Fchanged variables, whether ",{"type":46,"tag":268,"props":738,"children":740},{"className":739},[],[741],{"type":52,"value":742},"context",{"type":52,"value":744}," support is added).",{"type":46,"tag":65,"props":746,"children":747},{},[748],{"type":52,"value":749},"For provider-surface changes: the explicit list of provider arguments you intend to include and exclude, with reasoning.",{"type":46,"tag":65,"props":751,"children":752},{},[753],{"type":52,"value":754},"Whether the change requires bumping the pinned provider version (a repo-wide change — see Step 1.4).",{"type":46,"tag":55,"props":756,"children":757},{},[758,760,765],{"type":52,"value":759},"This gate is ",{"type":46,"tag":69,"props":761,"children":762},{},[763],{"type":52,"value":764},"mandatory for new features and interface changes",{"type":52,"value":766},". For trivial fixes (typos, one-line bugfixes with an obvious solution) where the design is self-evident, state your intent and proceed without blocking.",{"type":46,"tag":418,"props":768,"children":770},{"id":769},"step-2-develop-the-fix-or-feature-modes-a-b",[771],{"type":52,"value":772},"Step 2: Develop the Fix or Feature (Modes A & B)",{"type":46,"tag":425,"props":774,"children":775},{},[776,815,971,1224,1566,1697],{"type":46,"tag":65,"props":777,"children":778},{},[779,784,786],{"type":46,"tag":69,"props":780,"children":781},{},[782],{"type":52,"value":783},"Align with Fabric Zen",{"type":52,"value":785},": Ensure the design focuses on composition, encapsulates logical entities (like IAM and log sinks directly inside modules), adopts common interfaces, and keeps code flat and easy to evolve.",{"type":46,"tag":622,"props":787,"children":788},{},[789],{"type":46,"tag":55,"props":790,"children":791},{},[792,797,799,805,807,813],{"type":46,"tag":272,"props":793,"children":794},{},[795],{"type":52,"value":796},"!IMPORTANT",{"type":52,"value":798},"\nYou MUST strictly follow the design principles and coding conventions defined in ",{"type":46,"tag":544,"props":800,"children":802},{"href":801},"..\u002F..\u002FCONTRIBUTING.md",[803],{"type":52,"value":804},"CONTRIBUTING.md",{"type":52,"value":806}," and ",{"type":46,"tag":544,"props":808,"children":810},{"href":809},"..\u002F..\u002FGEMINI.md",[811],{"type":52,"value":812},"GEMINI.md",{"type":52,"value":814}," when designing variables, modules, and factories.",{"type":46,"tag":65,"props":816,"children":817},{},[818,823,825],{"type":46,"tag":69,"props":819,"children":820},{},[821],{"type":52,"value":822},"Set Up the Git Branch",{"type":52,"value":824},":",{"type":46,"tag":61,"props":826,"children":827},{},[828,947,960],{"type":46,"tag":65,"props":829,"children":830},{},[831,833,839,841,847,849],{"type":52,"value":832},"If you are not already on a feature branch, create one named after your username and the feature\u002Ffix (e.g., ",{"type":46,"tag":268,"props":834,"children":836},{"className":835},[],[837],{"type":52,"value":838},"\u003Cusername>\u002F\u003Cfeature-name>",{"type":52,"value":840},", such as ",{"type":46,"tag":268,"props":842,"children":844},{"className":843},[],[845],{"type":52,"value":846},"johndoe\u002Ffeature-name",{"type":52,"value":848},"):\n",{"type":46,"tag":260,"props":850,"children":852},{"className":440,"code":851,"language":442,"meta":265,"style":265},"git checkout master && git pull\ngit checkout -b \u003Cusername>\u002F\u003Cfeature-name>\n",[853],{"type":46,"tag":268,"props":854,"children":855},{"__ignoreMap":265},[856,889],{"type":46,"tag":272,"props":857,"children":858},{"class":274,"line":275},[859,864,869,874,879,884],{"type":46,"tag":272,"props":860,"children":861},{"style":452},[862],{"type":52,"value":863},"git",{"type":46,"tag":272,"props":865,"children":866},{"style":458},[867],{"type":52,"value":868}," checkout",{"type":46,"tag":272,"props":870,"children":871},{"style":458},[872],{"type":52,"value":873}," master",{"type":46,"tag":272,"props":875,"children":876},{"style":469},[877],{"type":52,"value":878}," &&",{"type":46,"tag":272,"props":880,"children":881},{"style":452},[882],{"type":52,"value":883}," git",{"type":46,"tag":272,"props":885,"children":886},{"style":458},[887],{"type":52,"value":888}," pull\n",{"type":46,"tag":272,"props":890,"children":891},{"class":274,"line":284},[892,896,900,905,909,914,919,924,929,934,939,943],{"type":46,"tag":272,"props":893,"children":894},{"style":452},[895],{"type":52,"value":863},{"type":46,"tag":272,"props":897,"children":898},{"style":458},[899],{"type":52,"value":868},{"type":46,"tag":272,"props":901,"children":902},{"style":458},[903],{"type":52,"value":904}," -b",{"type":46,"tag":272,"props":906,"children":907},{"style":469},[908],{"type":52,"value":472},{"type":46,"tag":272,"props":910,"children":911},{"style":458},[912],{"type":52,"value":913},"usernam",{"type":46,"tag":272,"props":915,"children":916},{"style":480},[917],{"type":52,"value":918},"e",{"type":46,"tag":272,"props":920,"children":921},{"style":469},[922],{"type":52,"value":923},">",{"type":46,"tag":272,"props":925,"children":926},{"style":458},[927],{"type":52,"value":928},"\u002F",{"type":46,"tag":272,"props":930,"children":931},{"style":469},[932],{"type":52,"value":933},"\u003C",{"type":46,"tag":272,"props":935,"children":936},{"style":458},[937],{"type":52,"value":938},"feature-nam",{"type":46,"tag":272,"props":940,"children":941},{"style":480},[942],{"type":52,"value":918},{"type":46,"tag":272,"props":944,"children":945},{"style":469},[946],{"type":52,"value":488},{"type":46,"tag":65,"props":948,"children":949},{},[950,952,958],{"type":52,"value":951},"If preparing an existing branch (Mode B), do NOT create a new one; instead make sure it is up to date with ",{"type":46,"tag":268,"props":953,"children":955},{"className":954},[],[956],{"type":52,"value":957},"master",{"type":52,"value":959}," (rebase or merge) before submission.",{"type":46,"tag":65,"props":961,"children":962},{},[963,965,969],{"type":52,"value":964},"Direct branches require push access to the repo (granted to regular contributors on request). Without it, use the fork-based flow described in ",{"type":46,"tag":544,"props":966,"children":967},{"href":801},[968],{"type":52,"value":804},{"type":52,"value":970},"; the rest of this workflow is identical.",{"type":46,"tag":65,"props":972,"children":973},{},[974,979,980],{"type":46,"tag":69,"props":975,"children":976},{},[977],{"type":52,"value":978},"Apply Fabric\u002FFAST Design Conventions",{"type":52,"value":824},{"type":46,"tag":61,"props":981,"children":982},{},[983,1031,1063,1073,1091,1117],{"type":46,"tag":65,"props":984,"children":985},{},[986,991,993,999,1001,1006,1008,1014,1015,1021,1023,1029],{"type":46,"tag":69,"props":987,"children":988},{},[989],{"type":52,"value":990},"Context Interpolation",{"type":52,"value":992},": If context support is relevant and needed for the module (e.g., to support resolving symbolic references like ",{"type":46,"tag":268,"props":994,"children":996},{"className":995},[],[997],{"type":52,"value":998},"\"$project_ids:myprj\"",{"type":52,"value":1000},"), add a ",{"type":46,"tag":268,"props":1002,"children":1004},{"className":1003},[],[1005],{"type":52,"value":742},{"type":52,"value":1007}," variable block and implement ",{"type":46,"tag":268,"props":1009,"children":1011},{"className":1010},[],[1012],{"type":52,"value":1013},"ctx",{"type":52,"value":928},{"type":46,"tag":268,"props":1016,"children":1018},{"className":1017},[],[1019],{"type":52,"value":1020},"ctx_p",{"type":52,"value":1022}," locals in ",{"type":46,"tag":268,"props":1024,"children":1026},{"className":1025},[],[1027],{"type":52,"value":1028},"main.tf",{"type":52,"value":1030},". Do not add it blindly if the module does not benefit from symbolic interpolation.",{"type":46,"tag":65,"props":1032,"children":1033},{},[1034,1039,1041,1047,1049,1054,1056,1061],{"type":46,"tag":69,"props":1035,"children":1036},{},[1037],{"type":52,"value":1038},"Compact Variables",{"type":52,"value":1040},": Leverage objects with ",{"type":46,"tag":268,"props":1042,"children":1044},{"className":1043},[],[1045],{"type":52,"value":1046},"optional()",{"type":52,"value":1048}," attributes to keep user interfaces clean. Prefer ",{"type":46,"tag":268,"props":1050,"children":1052},{"className":1051},[],[1053],{"type":52,"value":1046},{"type":52,"value":1055}," ",{"type":46,"tag":69,"props":1057,"children":1058},{},[1059],{"type":52,"value":1060},"without",{"type":52,"value":1062}," an explicit default so the provider applies whatever default value it defines; only set a default when module logic needs a known value.",{"type":46,"tag":65,"props":1064,"children":1065},{},[1066,1071],{"type":46,"tag":69,"props":1067,"children":1068},{},[1069],{"type":52,"value":1070},"Stable State Keys",{"type":52,"value":1072},": Always use maps instead of lists for collection variables to avoid index shifts in Terraform state.",{"type":46,"tag":65,"props":1074,"children":1075},{},[1076,1081,1083,1089],{"type":46,"tag":69,"props":1077,"children":1078},{},[1079],{"type":52,"value":1080},"Scope Isolation",{"type":52,"value":1082},": Use private locals (prefixed with ",{"type":46,"tag":268,"props":1084,"children":1086},{"className":1085},[],[1087],{"type":52,"value":1088},"_",{"type":52,"value":1090},") for intermediate transformations, reserving module-level locals for values referenced by resources.",{"type":46,"tag":65,"props":1092,"children":1093},{},[1094,1099,1101,1107,1109,1115],{"type":46,"tag":69,"props":1095,"children":1096},{},[1097],{"type":52,"value":1098},"Locals Separation for Complex Manipulations",{"type":52,"value":1100},": When adding complex conditional strings, string interpolations, or transformations to resource attributes, compute the map in ",{"type":46,"tag":268,"props":1102,"children":1104},{"className":1103},[],[1105],{"type":52,"value":1106},"locals",{"type":52,"value":1108}," so the resource block references a clean index like ",{"type":46,"tag":268,"props":1110,"children":1112},{"className":1111},[],[1113],{"type":52,"value":1114},"local.my_map[each.key]",{"type":52,"value":1116},". This keeps resource blocks legible (\u003C79 chars) and avoids cluttering resource definitions.",{"type":46,"tag":65,"props":1118,"children":1119},{},[1120,1125,1127,1133,1135,1141,1143],{"type":46,"tag":69,"props":1121,"children":1122},{},[1123],{"type":52,"value":1124},"Validate ENUM Variables",{"type":52,"value":1126},": When adding or exposing variables that mirror ENUM values in the underlying Terraform provider, always include a ",{"type":46,"tag":268,"props":1128,"children":1130},{"className":1129},[],[1131],{"type":52,"value":1132},"validation",{"type":52,"value":1134}," block to check for allowed values at plan time. Example from ",{"type":46,"tag":268,"props":1136,"children":1138},{"className":1137},[],[1139],{"type":52,"value":1140},"modules\u002Fgcs",{"type":52,"value":1142},":\n",{"type":46,"tag":260,"props":1144,"children":1148},{"className":1145,"code":1146,"language":1147,"meta":265,"style":265},"language-hcl shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","variable \"storage_class\" {\n  description = \"Bucket storage class.\"\n  type        = string\n  default     = \"STANDARD\"\n  validation {\n    condition     = contains([\"STANDARD\", \"MULTI_REGIONAL\", \"REGIONAL\", \"NEARLINE\", \"COLDLINE\", \"ARCHIVE\"], var.storage_class)\n    error_message = \"Storage class must be one of STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE.\"\n  }\n}\n","hcl",[1149],{"type":46,"tag":268,"props":1150,"children":1151},{"__ignoreMap":265},[1152,1160,1168,1176,1184,1192,1200,1208,1216],{"type":46,"tag":272,"props":1153,"children":1154},{"class":274,"line":275},[1155],{"type":46,"tag":272,"props":1156,"children":1157},{},[1158],{"type":52,"value":1159},"variable \"storage_class\" {\n",{"type":46,"tag":272,"props":1161,"children":1162},{"class":274,"line":284},[1163],{"type":46,"tag":272,"props":1164,"children":1165},{},[1166],{"type":52,"value":1167},"  description = \"Bucket storage class.\"\n",{"type":46,"tag":272,"props":1169,"children":1170},{"class":274,"line":293},[1171],{"type":46,"tag":272,"props":1172,"children":1173},{},[1174],{"type":52,"value":1175},"  type        = string\n",{"type":46,"tag":272,"props":1177,"children":1178},{"class":274,"line":302},[1179],{"type":46,"tag":272,"props":1180,"children":1181},{},[1182],{"type":52,"value":1183},"  default     = \"STANDARD\"\n",{"type":46,"tag":272,"props":1185,"children":1186},{"class":274,"line":311},[1187],{"type":46,"tag":272,"props":1188,"children":1189},{},[1190],{"type":52,"value":1191},"  validation {\n",{"type":46,"tag":272,"props":1193,"children":1194},{"class":274,"line":320},[1195],{"type":46,"tag":272,"props":1196,"children":1197},{},[1198],{"type":52,"value":1199},"    condition     = contains([\"STANDARD\", \"MULTI_REGIONAL\", \"REGIONAL\", \"NEARLINE\", \"COLDLINE\", \"ARCHIVE\"], var.storage_class)\n",{"type":46,"tag":272,"props":1201,"children":1202},{"class":274,"line":329},[1203],{"type":46,"tag":272,"props":1204,"children":1205},{},[1206],{"type":52,"value":1207},"    error_message = \"Storage class must be one of STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE.\"\n",{"type":46,"tag":272,"props":1209,"children":1210},{"class":274,"line":338},[1211],{"type":46,"tag":272,"props":1212,"children":1213},{},[1214],{"type":52,"value":1215},"  }\n",{"type":46,"tag":272,"props":1217,"children":1218},{"class":274,"line":347},[1219],{"type":46,"tag":272,"props":1220,"children":1221},{},[1222],{"type":52,"value":1223},"}\n",{"type":46,"tag":65,"props":1225,"children":1226},{},[1227,1232,1233],{"type":46,"tag":69,"props":1228,"children":1229},{},[1230],{"type":52,"value":1231},"Reference Implementations & Knowledge Sources",{"type":52,"value":824},{"type":46,"tag":61,"props":1234,"children":1235},{},[1236,1247,1258],{"type":46,"tag":65,"props":1237,"children":1238},{},[1239,1245],{"type":46,"tag":544,"props":1240,"children":1242},{"href":1241},"..\u002F..\u002Fmodules\u002Fcompute-vm",[1243],{"type":52,"value":1244},"compute-vm",{"type":52,"value":1246},": Ideal reference for compact variable design, optional objects with defaults, and map variables.",{"type":46,"tag":65,"props":1248,"children":1249},{},[1250,1256],{"type":46,"tag":544,"props":1251,"children":1253},{"href":1252},"..\u002F..\u002Fmodules\u002Fproject",[1254],{"type":52,"value":1255},"project",{"type":52,"value":1257},": Best reference for logical entity encapsulation (IAM, log sinks, Shared VPC configurations), complex locals transformation, and context-based interpolation.",{"type":46,"tag":65,"props":1259,"children":1260},{},[1261,1267,1269,1275,1277,1541,1543,1549,1551,1557,1558,1564],{"type":46,"tag":544,"props":1262,"children":1264},{"href":1263},"..\u002F..\u002Fadrs\u002F",[1265],{"type":52,"value":1266},"adrs\u002F",{"type":52,"value":1268},": Architectural Decision Records documenting the rationale behind key design patterns (e.g. the IAM ADR explains the full ",{"type":46,"tag":268,"props":1270,"children":1272},{"className":1271},[],[1273],{"type":52,"value":1274},"iam*",{"type":52,"value":1276}," interface). Before designing, build an index of all ADRs with their title and status:\n",{"type":46,"tag":260,"props":1278,"children":1280},{"className":440,"code":1279,"language":442,"meta":265,"style":265},"find adrs -name '*.md' ! -name 'README.md' | sort | while read -r f; do\n  st=$(awk '\u002F^## Status\u002F {getline; while ($0 == \"\") getline; print; exit}' \"$f\")\n  printf '%s | %s | %s\\n' \"$f\" \"$(head -1 \"$f\" | sed 's\u002F^# \u002F\u002F')\" \"$st\"\ndone\n",[1281],{"type":46,"tag":268,"props":1282,"children":1283},{"__ignoreMap":265},[1284,1385,1436,1533],{"type":46,"tag":272,"props":1285,"children":1286},{"class":274,"line":275},[1287,1292,1297,1302,1307,1312,1317,1322,1326,1330,1335,1339,1344,1349,1353,1359,1365,1370,1375,1380],{"type":46,"tag":272,"props":1288,"children":1289},{"style":452},[1290],{"type":52,"value":1291},"find",{"type":46,"tag":272,"props":1293,"children":1294},{"style":458},[1295],{"type":52,"value":1296}," adrs",{"type":46,"tag":272,"props":1298,"children":1299},{"style":458},[1300],{"type":52,"value":1301}," -name",{"type":46,"tag":272,"props":1303,"children":1304},{"style":469},[1305],{"type":52,"value":1306}," '",{"type":46,"tag":272,"props":1308,"children":1309},{"style":458},[1310],{"type":52,"value":1311},"*.md",{"type":46,"tag":272,"props":1313,"children":1314},{"style":469},[1315],{"type":52,"value":1316},"'",{"type":46,"tag":272,"props":1318,"children":1319},{"style":458},[1320],{"type":52,"value":1321}," !",{"type":46,"tag":272,"props":1323,"children":1324},{"style":458},[1325],{"type":52,"value":1301},{"type":46,"tag":272,"props":1327,"children":1328},{"style":469},[1329],{"type":52,"value":1306},{"type":46,"tag":272,"props":1331,"children":1332},{"style":458},[1333],{"type":52,"value":1334},"README.md",{"type":46,"tag":272,"props":1336,"children":1337},{"style":469},[1338],{"type":52,"value":1316},{"type":46,"tag":272,"props":1340,"children":1341},{"style":469},[1342],{"type":52,"value":1343}," |",{"type":46,"tag":272,"props":1345,"children":1346},{"style":452},[1347],{"type":52,"value":1348}," sort",{"type":46,"tag":272,"props":1350,"children":1351},{"style":469},[1352],{"type":52,"value":1343},{"type":46,"tag":272,"props":1354,"children":1356},{"style":1355},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[1357],{"type":52,"value":1358}," while",{"type":46,"tag":272,"props":1360,"children":1362},{"style":1361},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[1363],{"type":52,"value":1364}," read",{"type":46,"tag":272,"props":1366,"children":1367},{"style":458},[1368],{"type":52,"value":1369}," -r",{"type":46,"tag":272,"props":1371,"children":1372},{"style":458},[1373],{"type":52,"value":1374}," f",{"type":46,"tag":272,"props":1376,"children":1377},{"style":469},[1378],{"type":52,"value":1379},";",{"type":46,"tag":272,"props":1381,"children":1382},{"style":1355},[1383],{"type":52,"value":1384}," do\n",{"type":46,"tag":272,"props":1386,"children":1387},{"class":274,"line":284},[1388,1393,1398,1403,1407,1412,1416,1421,1426,1431],{"type":46,"tag":272,"props":1389,"children":1390},{"style":480},[1391],{"type":52,"value":1392},"  st",{"type":46,"tag":272,"props":1394,"children":1395},{"style":469},[1396],{"type":52,"value":1397},"=$(",{"type":46,"tag":272,"props":1399,"children":1400},{"style":452},[1401],{"type":52,"value":1402},"awk",{"type":46,"tag":272,"props":1404,"children":1405},{"style":469},[1406],{"type":52,"value":1306},{"type":46,"tag":272,"props":1408,"children":1409},{"style":458},[1410],{"type":52,"value":1411},"\u002F^## Status\u002F {getline; while ($0 == \"\") getline; print; exit}",{"type":46,"tag":272,"props":1413,"children":1414},{"style":469},[1415],{"type":52,"value":1316},{"type":46,"tag":272,"props":1417,"children":1418},{"style":469},[1419],{"type":52,"value":1420}," \"",{"type":46,"tag":272,"props":1422,"children":1423},{"style":480},[1424],{"type":52,"value":1425},"$f",{"type":46,"tag":272,"props":1427,"children":1428},{"style":469},[1429],{"type":52,"value":1430},"\"",{"type":46,"tag":272,"props":1432,"children":1433},{"style":469},[1434],{"type":52,"value":1435},")\n",{"type":46,"tag":272,"props":1437,"children":1438},{"class":274,"line":293},[1439,1444,1448,1453,1457,1461,1465,1469,1474,1479,1484,1488,1492,1496,1500,1505,1509,1514,1519,1523,1528],{"type":46,"tag":272,"props":1440,"children":1441},{"style":1361},[1442],{"type":52,"value":1443},"  printf",{"type":46,"tag":272,"props":1445,"children":1446},{"style":469},[1447],{"type":52,"value":1306},{"type":46,"tag":272,"props":1449,"children":1450},{"style":458},[1451],{"type":52,"value":1452},"%s | %s | %s\\n",{"type":46,"tag":272,"props":1454,"children":1455},{"style":469},[1456],{"type":52,"value":1316},{"type":46,"tag":272,"props":1458,"children":1459},{"style":469},[1460],{"type":52,"value":1420},{"type":46,"tag":272,"props":1462,"children":1463},{"style":480},[1464],{"type":52,"value":1425},{"type":46,"tag":272,"props":1466,"children":1467},{"style":469},[1468],{"type":52,"value":1430},{"type":46,"tag":272,"props":1470,"children":1471},{"style":469},[1472],{"type":52,"value":1473}," \"$(",{"type":46,"tag":272,"props":1475,"children":1476},{"style":452},[1477],{"type":52,"value":1478},"head",{"type":46,"tag":272,"props":1480,"children":1481},{"style":458},[1482],{"type":52,"value":1483}," -1 ",{"type":46,"tag":272,"props":1485,"children":1486},{"style":469},[1487],{"type":52,"value":1430},{"type":46,"tag":272,"props":1489,"children":1490},{"style":480},[1491],{"type":52,"value":1425},{"type":46,"tag":272,"props":1493,"children":1494},{"style":469},[1495],{"type":52,"value":1430},{"type":46,"tag":272,"props":1497,"children":1498},{"style":469},[1499],{"type":52,"value":1343},{"type":46,"tag":272,"props":1501,"children":1502},{"style":452},[1503],{"type":52,"value":1504}," sed",{"type":46,"tag":272,"props":1506,"children":1507},{"style":469},[1508],{"type":52,"value":1306},{"type":46,"tag":272,"props":1510,"children":1511},{"style":458},[1512],{"type":52,"value":1513},"s\u002F^# \u002F\u002F",{"type":46,"tag":272,"props":1515,"children":1516},{"style":469},[1517],{"type":52,"value":1518},"')\"",{"type":46,"tag":272,"props":1520,"children":1521},{"style":469},[1522],{"type":52,"value":1420},{"type":46,"tag":272,"props":1524,"children":1525},{"style":480},[1526],{"type":52,"value":1527},"$st",{"type":46,"tag":272,"props":1529,"children":1530},{"style":469},[1531],{"type":52,"value":1532},"\"\n",{"type":46,"tag":272,"props":1534,"children":1535},{"class":274,"line":302},[1536],{"type":46,"tag":272,"props":1537,"children":1538},{"style":1355},[1539],{"type":52,"value":1540},"done\n",{"type":52,"value":1542},"\nRead in full any ADR whose title relates to the area you are changing. Weigh them by status: ignore ",{"type":46,"tag":268,"props":1544,"children":1546},{"className":1545},[],[1547],{"type":52,"value":1548},"Rejected",{"type":52,"value":1550}," ones, treat ",{"type":46,"tag":268,"props":1552,"children":1554},{"className":1553},[],[1555],{"type":52,"value":1556},"Draft",{"type":52,"value":928},{"type":46,"tag":268,"props":1559,"children":1561},{"className":1560},[],[1562],{"type":52,"value":1563},"Proposed",{"type":52,"value":1565}," as directional rather than settled, and remember that even implemented ADRs can lag behind the code — on conflict, the current code and golden-path modules are the source of truth.",{"type":46,"tag":65,"props":1567,"children":1568},{},[1569,1574,1575],{"type":46,"tag":69,"props":1570,"children":1571},{},[1572],{"type":52,"value":1573},"Maintain Code Consistency",{"type":52,"value":824},{"type":46,"tag":61,"props":1576,"children":1577},{},[1578,1607,1612,1679],{"type":46,"tag":65,"props":1579,"children":1580},{},[1581,1583,1589,1591,1597,1599,1605],{"type":52,"value":1582},"Always keep variables in ",{"type":46,"tag":268,"props":1584,"children":1586},{"className":1585},[],[1587],{"type":52,"value":1588},"variables.tf",{"type":52,"value":1590}," and outputs in ",{"type":46,"tag":268,"props":1592,"children":1594},{"className":1593},[],[1595],{"type":52,"value":1596},"outputs.tf",{"type":52,"value":1598}," in strict alphabetical order. For larger modules, variables and outputs can be grouped logically into separate files (e.g. ",{"type":46,"tag":268,"props":1600,"children":1602},{"className":1601},[],[1603],{"type":52,"value":1604},"variables-logging.tf",{"type":52,"value":1606},"), each internally alphabetically sorted.",{"type":46,"tag":65,"props":1608,"children":1609},{},[1610],{"type":52,"value":1611},"Limit line length to 79 characters (relaxed for long attribute names and descriptions).",{"type":46,"tag":65,"props":1613,"children":1614},{},[1615,1620,1622,1628,1630,1636,1638,1644,1646,1652,1654],{"type":46,"tag":69,"props":1616,"children":1617},{},[1618],{"type":52,"value":1619},"JSON Schema & Factory Alignment",{"type":52,"value":1621},": When a change is made to a module that implements a factory (e.g., ",{"type":46,"tag":268,"props":1623,"children":1625},{"className":1624},[],[1626],{"type":52,"value":1627},"cloud-workstations",{"type":52,"value":1629},") or is the base for one (e.g., ",{"type":46,"tag":268,"props":1631,"children":1633},{"className":1632},[],[1634],{"type":52,"value":1635},"net-vpc-factory",{"type":52,"value":1637}," to ",{"type":46,"tag":268,"props":1639,"children":1641},{"className":1640},[],[1642],{"type":52,"value":1643},"2-networking",{"type":52,"value":1645},"), the schemas MUST be updated (including their ",{"type":46,"tag":268,"props":1647,"children":1649},{"className":1648},[],[1650],{"type":52,"value":1651},".schema.md",{"type":52,"value":1653}," documentation files), and the factory code should be updated to mirror the changed variable surface. Ensure you regenerate the schema documentation by running:\n",{"type":46,"tag":260,"props":1655,"children":1657},{"className":440,"code":1656,"language":442,"meta":265,"style":265},"uv run tools\u002Fschema_docs.py\n",[1658],{"type":46,"tag":268,"props":1659,"children":1660},{"__ignoreMap":265},[1661],{"type":46,"tag":272,"props":1662,"children":1663},{"class":274,"line":275},[1664,1669,1674],{"type":46,"tag":272,"props":1665,"children":1666},{"style":452},[1667],{"type":52,"value":1668},"uv",{"type":46,"tag":272,"props":1670,"children":1671},{"style":458},[1672],{"type":52,"value":1673}," run",{"type":46,"tag":272,"props":1675,"children":1676},{"style":458},[1677],{"type":52,"value":1678}," tools\u002Fschema_docs.py\n",{"type":46,"tag":65,"props":1680,"children":1681},{},[1682,1687,1689,1695],{"type":46,"tag":69,"props":1683,"children":1684},{},[1685],{"type":52,"value":1686},"Duplicated Files",{"type":52,"value":1688},": Some files (e.g. schemas, factory test fixtures) are intentionally duplicated across modules and stages, and CI enforces that copies stay identical. When changing such a file, sync all its copies; the duplicate groups are listed in ",{"type":46,"tag":544,"props":1690,"children":1692},{"href":1691},"..\u002F..\u002Ftools\u002Fduplicate-diff.py",[1693],{"type":52,"value":1694},"tools\u002Fduplicate-diff.py",{"type":52,"value":1696}," — update that list when adding new duplicated files.",{"type":46,"tag":65,"props":1698,"children":1699},{},[1700,1705,1706,1756],{"type":46,"tag":69,"props":1701,"children":1702},{},[1703],{"type":52,"value":1704},"Update or Add Tests",{"type":52,"value":824},{"type":46,"tag":61,"props":1707,"children":1708},{},[1709,1736],{"type":46,"tag":65,"props":1710,"children":1711},{},[1712,1714,1719,1721,1727,1729,1735],{"type":52,"value":1713},"For modules: If the change introduces a new feature or configuration option, update an existing example in the module's ",{"type":46,"tag":268,"props":1715,"children":1717},{"className":1716},[],[1718],{"type":52,"value":1334},{"type":52,"value":1720}," (or add a new one) to demonstrate its usage. Ensure the example includes ",{"type":46,"tag":268,"props":1722,"children":1724},{"className":1723},[],[1725],{"type":52,"value":1726},"# tftest",{"type":52,"value":1728}," parameters and update the corresponding inventory YAML files under ",{"type":46,"tag":268,"props":1730,"children":1732},{"className":1731},[],[1733],{"type":52,"value":1734},"tests\u002Fmodules\u002F\u003Cmodule_name>\u002Fexamples\u002F",{"type":52,"value":82},{"type":46,"tag":65,"props":1737,"children":1738},{},[1739,1741,1747,1749,1755],{"type":52,"value":1740},"For FAST stages: Update ",{"type":46,"tag":268,"props":1742,"children":1744},{"className":1743},[],[1745],{"type":52,"value":1746},"tftest.yaml",{"type":52,"value":1748}," scenarios and tfvars\u002Fyaml inventories under ",{"type":46,"tag":268,"props":1750,"children":1752},{"className":1751},[],[1753],{"type":52,"value":1754},"tests\u002Ffast\u002F...",{"type":52,"value":82},{"type":46,"tag":622,"props":1757,"children":1758},{},[1759],{"type":46,"tag":55,"props":1760,"children":1761},{},[1762,1767,1769,1775,1777,1783,1785,1791],{"type":46,"tag":272,"props":1763,"children":1764},{},[1765],{"type":52,"value":1766},"!NOTE",{"type":52,"value":1768},"\nModule directories use hyphens (",{"type":46,"tag":268,"props":1770,"children":1772},{"className":1771},[],[1773],{"type":52,"value":1774},"modules\u002Fnet-vpn-ha",{"type":52,"value":1776},") while test directories generally use underscores (",{"type":46,"tag":268,"props":1778,"children":1780},{"className":1779},[],[1781],{"type":52,"value":1782},"tests\u002Fmodules\u002Fnet_vpn_ha",{"type":52,"value":1784},"). Always check the actual test directory name — a mistyped path makes ",{"type":46,"tag":268,"props":1786,"children":1788},{"className":1787},[],[1789],{"type":52,"value":1790},"pytest",{"type":52,"value":1792}," silently collect zero tests.",{"type":46,"tag":418,"props":1794,"children":1796},{"id":1795},"step-3-run-tests-linting-inventory-regeneration-modes-a-b",[1797],{"type":52,"value":1798},"Step 3: Run Tests, Linting & Inventory Regeneration (Modes A & B)",{"type":46,"tag":55,"props":1800,"children":1801},{},[1802,1804,1810,1812,1818,1820,1826],{"type":52,"value":1803},"Run Python tooling through ",{"type":46,"tag":268,"props":1805,"children":1807},{"className":1806},[],[1808],{"type":52,"value":1809},"uv run",{"type":52,"value":1811}," — the scripts in ",{"type":46,"tag":268,"props":1813,"children":1815},{"className":1814},[],[1816],{"type":52,"value":1817},"tools\u002F",{"type":52,"value":1819}," declare their dependencies inline, so no virtual environment setup is needed. For faster Terraform testing, always set ",{"type":46,"tag":268,"props":1821,"children":1823},{"className":1822},[],[1824],{"type":52,"value":1825},"TF_PLUGIN_CACHE_DIR=\u002Ftmp\u002Ftfcache",{"type":52,"value":82},{"type":46,"tag":425,"props":1828,"children":1829},{},[1830,1889,1985,2164],{"type":46,"tag":65,"props":1831,"children":1832},{},[1833,1838,1840,1846,1848,1854,1856],{"type":46,"tag":69,"props":1834,"children":1835},{},[1836],{"type":52,"value":1837},"Run Unified Linting",{"type":52,"value":1839},": Execute ",{"type":46,"tag":268,"props":1841,"children":1843},{"className":1842},[],[1844],{"type":52,"value":1845},"tools\u002Flint.sh",{"type":52,"value":1847}," to check copyright boilerplates, Terraform format (",{"type":46,"tag":268,"props":1849,"children":1851},{"className":1850},[],[1852],{"type":52,"value":1853},"terraform fmt",{"type":52,"value":1855},"), alphabetical sorting, and schema validations:",{"type":46,"tag":260,"props":1857,"children":1859},{"className":440,"code":1858,"language":442,"meta":265,"style":265},"uv run --with-requirements tools\u002Frequirements.txt tools\u002Flint.sh\n",[1860],{"type":46,"tag":268,"props":1861,"children":1862},{"__ignoreMap":265},[1863],{"type":46,"tag":272,"props":1864,"children":1865},{"class":274,"line":275},[1866,1870,1874,1879,1884],{"type":46,"tag":272,"props":1867,"children":1868},{"style":452},[1869],{"type":52,"value":1668},{"type":46,"tag":272,"props":1871,"children":1872},{"style":458},[1873],{"type":52,"value":1673},{"type":46,"tag":272,"props":1875,"children":1876},{"style":458},[1877],{"type":52,"value":1878}," --with-requirements",{"type":46,"tag":272,"props":1880,"children":1881},{"style":458},[1882],{"type":52,"value":1883}," tools\u002Frequirements.txt",{"type":46,"tag":272,"props":1885,"children":1886},{"style":458},[1887],{"type":52,"value":1888}," tools\u002Flint.sh\n",{"type":46,"tag":65,"props":1890,"children":1891},{},[1892,1897,1899],{"type":46,"tag":69,"props":1893,"children":1894},{},[1895],{"type":52,"value":1896},"Update Documentation",{"type":52,"value":1898},": If you changed variables or outputs, check consistency and regenerate the README documentation tables:",{"type":46,"tag":260,"props":1900,"children":1902},{"className":440,"code":1901,"language":442,"meta":265,"style":265},"uv run tools\u002Fcheck_documentation.py modules\u002F\u003Cmodule-name>\nuv run tools\u002Ftfdoc.py --replace modules\u002F\u003Cmodule-name>\n",[1903],{"type":46,"tag":268,"props":1904,"children":1905},{"__ignoreMap":265},[1906,1944],{"type":46,"tag":272,"props":1907,"children":1908},{"class":274,"line":275},[1909,1913,1917,1922,1927,1931,1936,1940],{"type":46,"tag":272,"props":1910,"children":1911},{"style":452},[1912],{"type":52,"value":1668},{"type":46,"tag":272,"props":1914,"children":1915},{"style":458},[1916],{"type":52,"value":1673},{"type":46,"tag":272,"props":1918,"children":1919},{"style":458},[1920],{"type":52,"value":1921}," tools\u002Fcheck_documentation.py",{"type":46,"tag":272,"props":1923,"children":1924},{"style":458},[1925],{"type":52,"value":1926}," modules\u002F",{"type":46,"tag":272,"props":1928,"children":1929},{"style":469},[1930],{"type":52,"value":933},{"type":46,"tag":272,"props":1932,"children":1933},{"style":458},[1934],{"type":52,"value":1935},"module-nam",{"type":46,"tag":272,"props":1937,"children":1938},{"style":480},[1939],{"type":52,"value":918},{"type":46,"tag":272,"props":1941,"children":1942},{"style":469},[1943],{"type":52,"value":488},{"type":46,"tag":272,"props":1945,"children":1946},{"class":274,"line":284},[1947,1951,1955,1960,1965,1969,1973,1977,1981],{"type":46,"tag":272,"props":1948,"children":1949},{"style":452},[1950],{"type":52,"value":1668},{"type":46,"tag":272,"props":1952,"children":1953},{"style":458},[1954],{"type":52,"value":1673},{"type":46,"tag":272,"props":1956,"children":1957},{"style":458},[1958],{"type":52,"value":1959}," tools\u002Ftfdoc.py",{"type":46,"tag":272,"props":1961,"children":1962},{"style":458},[1963],{"type":52,"value":1964}," --replace",{"type":46,"tag":272,"props":1966,"children":1967},{"style":458},[1968],{"type":52,"value":1926},{"type":46,"tag":272,"props":1970,"children":1971},{"style":469},[1972],{"type":52,"value":933},{"type":46,"tag":272,"props":1974,"children":1975},{"style":458},[1976],{"type":52,"value":1935},{"type":46,"tag":272,"props":1978,"children":1979},{"style":480},[1980],{"type":52,"value":918},{"type":46,"tag":272,"props":1982,"children":1983},{"style":469},[1984],{"type":52,"value":488},{"type":46,"tag":65,"props":1986,"children":1987},{},[1988,1993,1994,1999,2001],{"type":46,"tag":69,"props":1989,"children":1990},{},[1991],{"type":52,"value":1992},"Run Impacted Tests",{"type":52,"value":1839},{"type":46,"tag":268,"props":1995,"children":1997},{"className":1996},[],[1998],{"type":52,"value":1790},{"type":52,"value":2000}," on the target module\u002Fstage (using the plugin cache directory for speed):",{"type":46,"tag":260,"props":2002,"children":2004},{"className":440,"code":2003,"language":442,"meta":265,"style":265},"mkdir -p \u002Ftmp\u002Ftfcache\n# tftest.yaml scenarios (note: underscores in the test directory name)\nTF_PLUGIN_CACHE_DIR=\u002Ftmp\u002Ftfcache uv run --with-requirements tests\u002Frequirements.txt pytest tests\u002Fmodules\u002F\u003Cmodule_name>\n# README example tests\nTF_PLUGIN_CACHE_DIR=\u002Ftmp\u002Ftfcache uv run --with-requirements tests\u002Frequirements.txt pytest -k 'modules and \u003Cmodule-name>:' tests\u002Fexamples\n",[2005],{"type":46,"tag":268,"props":2006,"children":2007},{"__ignoreMap":265},[2008,2026,2035,2098,2106],{"type":46,"tag":272,"props":2009,"children":2010},{"class":274,"line":275},[2011,2016,2021],{"type":46,"tag":272,"props":2012,"children":2013},{"style":452},[2014],{"type":52,"value":2015},"mkdir",{"type":46,"tag":272,"props":2017,"children":2018},{"style":458},[2019],{"type":52,"value":2020}," -p",{"type":46,"tag":272,"props":2022,"children":2023},{"style":458},[2024],{"type":52,"value":2025}," \u002Ftmp\u002Ftfcache\n",{"type":46,"tag":272,"props":2027,"children":2028},{"class":274,"line":284},[2029],{"type":46,"tag":272,"props":2030,"children":2032},{"style":2031},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[2033],{"type":52,"value":2034},"# tftest.yaml scenarios (note: underscores in the test directory name)\n",{"type":46,"tag":272,"props":2036,"children":2037},{"class":274,"line":293},[2038,2043,2048,2053,2058,2062,2066,2071,2076,2081,2085,2090,2094],{"type":46,"tag":272,"props":2039,"children":2040},{"style":480},[2041],{"type":52,"value":2042},"TF_PLUGIN_CACHE_DIR",{"type":46,"tag":272,"props":2044,"children":2045},{"style":469},[2046],{"type":52,"value":2047},"=",{"type":46,"tag":272,"props":2049,"children":2050},{"style":458},[2051],{"type":52,"value":2052},"\u002Ftmp\u002Ftfcache",{"type":46,"tag":272,"props":2054,"children":2055},{"style":452},[2056],{"type":52,"value":2057}," uv",{"type":46,"tag":272,"props":2059,"children":2060},{"style":458},[2061],{"type":52,"value":1673},{"type":46,"tag":272,"props":2063,"children":2064},{"style":458},[2065],{"type":52,"value":1878},{"type":46,"tag":272,"props":2067,"children":2068},{"style":458},[2069],{"type":52,"value":2070}," tests\u002Frequirements.txt",{"type":46,"tag":272,"props":2072,"children":2073},{"style":458},[2074],{"type":52,"value":2075}," pytest",{"type":46,"tag":272,"props":2077,"children":2078},{"style":458},[2079],{"type":52,"value":2080}," tests\u002Fmodules\u002F",{"type":46,"tag":272,"props":2082,"children":2083},{"style":469},[2084],{"type":52,"value":933},{"type":46,"tag":272,"props":2086,"children":2087},{"style":458},[2088],{"type":52,"value":2089},"module_nam",{"type":46,"tag":272,"props":2091,"children":2092},{"style":480},[2093],{"type":52,"value":918},{"type":46,"tag":272,"props":2095,"children":2096},{"style":469},[2097],{"type":52,"value":488},{"type":46,"tag":272,"props":2099,"children":2100},{"class":274,"line":302},[2101],{"type":46,"tag":272,"props":2102,"children":2103},{"style":2031},[2104],{"type":52,"value":2105},"# README example tests\n",{"type":46,"tag":272,"props":2107,"children":2108},{"class":274,"line":311},[2109,2113,2117,2121,2125,2129,2133,2137,2141,2146,2150,2155,2159],{"type":46,"tag":272,"props":2110,"children":2111},{"style":480},[2112],{"type":52,"value":2042},{"type":46,"tag":272,"props":2114,"children":2115},{"style":469},[2116],{"type":52,"value":2047},{"type":46,"tag":272,"props":2118,"children":2119},{"style":458},[2120],{"type":52,"value":2052},{"type":46,"tag":272,"props":2122,"children":2123},{"style":452},[2124],{"type":52,"value":2057},{"type":46,"tag":272,"props":2126,"children":2127},{"style":458},[2128],{"type":52,"value":1673},{"type":46,"tag":272,"props":2130,"children":2131},{"style":458},[2132],{"type":52,"value":1878},{"type":46,"tag":272,"props":2134,"children":2135},{"style":458},[2136],{"type":52,"value":2070},{"type":46,"tag":272,"props":2138,"children":2139},{"style":458},[2140],{"type":52,"value":2075},{"type":46,"tag":272,"props":2142,"children":2143},{"style":458},[2144],{"type":52,"value":2145}," -k",{"type":46,"tag":272,"props":2147,"children":2148},{"style":469},[2149],{"type":52,"value":1306},{"type":46,"tag":272,"props":2151,"children":2152},{"style":458},[2153],{"type":52,"value":2154},"modules and \u003Cmodule-name>:",{"type":46,"tag":272,"props":2156,"children":2157},{"style":469},[2158],{"type":52,"value":1316},{"type":46,"tag":272,"props":2160,"children":2161},{"style":458},[2162],{"type":52,"value":2163}," tests\u002Fexamples\n",{"type":46,"tag":65,"props":2165,"children":2166},{},[2167,2172,2174,2179,2181,2187,2188,2340],{"type":46,"tag":69,"props":2168,"children":2169},{},[2170],{"type":52,"value":2171},"Regenerate Test Inventories",{"type":52,"value":2173},": If module-level tests (",{"type":46,"tag":268,"props":2175,"children":2177},{"className":2176},[],[2178],{"type":52,"value":1746},{"type":52,"value":2180},") or README example inventories fail due to intentional plan output changes, regenerate them using ",{"type":46,"tag":268,"props":2182,"children":2184},{"className":2183},[],[2185],{"type":52,"value":2186},"generate_plan_summary.py",{"type":52,"value":824},{"type":46,"tag":260,"props":2189,"children":2191},{"className":440,"code":2190,"language":442,"meta":265,"style":265},"# For module-level tftest.yaml inventories:\nuv run tools\u002Fgenerate_plan_summary.py tests\u002Fmodules\u002F\u003Cmodule_name>\u002Ftftest.yaml \u003Ctest-name> --save\n\n# For README.md example inventories:\nuv run tools\u002Fgenerate_plan_summary.py modules\u002F\u003Cmodule-name>\u002FREADME.md \"\u003CExample Heading>\" --save\n",[2192],{"type":46,"tag":268,"props":2193,"children":2194},{"__ignoreMap":265},[2195,2203,2266,2275,2283],{"type":46,"tag":272,"props":2196,"children":2197},{"class":274,"line":275},[2198],{"type":46,"tag":272,"props":2199,"children":2200},{"style":2031},[2201],{"type":52,"value":2202},"# For module-level tftest.yaml inventories:\n",{"type":46,"tag":272,"props":2204,"children":2205},{"class":274,"line":284},[2206,2210,2214,2219,2223,2227,2231,2235,2239,2244,2248,2253,2257,2261],{"type":46,"tag":272,"props":2207,"children":2208},{"style":452},[2209],{"type":52,"value":1668},{"type":46,"tag":272,"props":2211,"children":2212},{"style":458},[2213],{"type":52,"value":1673},{"type":46,"tag":272,"props":2215,"children":2216},{"style":458},[2217],{"type":52,"value":2218}," tools\u002Fgenerate_plan_summary.py",{"type":46,"tag":272,"props":2220,"children":2221},{"style":458},[2222],{"type":52,"value":2080},{"type":46,"tag":272,"props":2224,"children":2225},{"style":469},[2226],{"type":52,"value":933},{"type":46,"tag":272,"props":2228,"children":2229},{"style":458},[2230],{"type":52,"value":2089},{"type":46,"tag":272,"props":2232,"children":2233},{"style":480},[2234],{"type":52,"value":918},{"type":46,"tag":272,"props":2236,"children":2237},{"style":469},[2238],{"type":52,"value":923},{"type":46,"tag":272,"props":2240,"children":2241},{"style":458},[2242],{"type":52,"value":2243},"\u002Ftftest.yaml",{"type":46,"tag":272,"props":2245,"children":2246},{"style":469},[2247],{"type":52,"value":472},{"type":46,"tag":272,"props":2249,"children":2250},{"style":458},[2251],{"type":52,"value":2252},"test-nam",{"type":46,"tag":272,"props":2254,"children":2255},{"style":480},[2256],{"type":52,"value":918},{"type":46,"tag":272,"props":2258,"children":2259},{"style":469},[2260],{"type":52,"value":923},{"type":46,"tag":272,"props":2262,"children":2263},{"style":458},[2264],{"type":52,"value":2265}," --save\n",{"type":46,"tag":272,"props":2267,"children":2268},{"class":274,"line":293},[2269],{"type":46,"tag":272,"props":2270,"children":2272},{"emptyLinePlaceholder":2271},true,[2273],{"type":52,"value":2274},"\n",{"type":46,"tag":272,"props":2276,"children":2277},{"class":274,"line":302},[2278],{"type":46,"tag":272,"props":2279,"children":2280},{"style":2031},[2281],{"type":52,"value":2282},"# For README.md example inventories:\n",{"type":46,"tag":272,"props":2284,"children":2285},{"class":274,"line":311},[2286,2290,2294,2298,2302,2306,2310,2314,2318,2323,2327,2332,2336],{"type":46,"tag":272,"props":2287,"children":2288},{"style":452},[2289],{"type":52,"value":1668},{"type":46,"tag":272,"props":2291,"children":2292},{"style":458},[2293],{"type":52,"value":1673},{"type":46,"tag":272,"props":2295,"children":2296},{"style":458},[2297],{"type":52,"value":2218},{"type":46,"tag":272,"props":2299,"children":2300},{"style":458},[2301],{"type":52,"value":1926},{"type":46,"tag":272,"props":2303,"children":2304},{"style":469},[2305],{"type":52,"value":933},{"type":46,"tag":272,"props":2307,"children":2308},{"style":458},[2309],{"type":52,"value":1935},{"type":46,"tag":272,"props":2311,"children":2312},{"style":480},[2313],{"type":52,"value":918},{"type":46,"tag":272,"props":2315,"children":2316},{"style":469},[2317],{"type":52,"value":923},{"type":46,"tag":272,"props":2319,"children":2320},{"style":458},[2321],{"type":52,"value":2322},"\u002FREADME.md",{"type":46,"tag":272,"props":2324,"children":2325},{"style":469},[2326],{"type":52,"value":1420},{"type":46,"tag":272,"props":2328,"children":2329},{"style":458},[2330],{"type":52,"value":2331},"\u003CExample Heading>",{"type":46,"tag":272,"props":2333,"children":2334},{"style":469},[2335],{"type":52,"value":1430},{"type":46,"tag":272,"props":2337,"children":2338},{"style":458},[2339],{"type":52,"value":2265},{"type":46,"tag":622,"props":2341,"children":2342},{},[2343],{"type":46,"tag":55,"props":2344,"children":2345},{},[2346,2351,2357,2359,2364,2366,2372],{"type":46,"tag":272,"props":2347,"children":2348},{},[2349],{"type":52,"value":2350},"!CAUTION",{"type":46,"tag":268,"props":2352,"children":2354},{"className":2353},[],[2355],{"type":52,"value":2356},"--save",{"type":52,"value":2358}," overwrites the assertion baseline, so a regenerated inventory makes the test pass ",{"type":46,"tag":69,"props":2360,"children":2361},{},[2362],{"type":52,"value":2363},"by construction",{"type":52,"value":2365},". After regenerating, diff the inventory against the previous version (",{"type":46,"tag":268,"props":2367,"children":2369},{"className":2368},[],[2370],{"type":52,"value":2371},"git diff tests\u002F",{"type":52,"value":2373},"), verify that every changed line maps to the intended feature or fix, and report the inventory diff to the user. If a changed line is not explained by your change, treat it as a bug in your code, not a baseline to save.",{"type":46,"tag":418,"props":2375,"children":2377},{"id":2376},"step-4-pre-submission-self-review-modes-a-b",[2378],{"type":52,"value":2379},"Step 4: Pre-Submission Self-Review (Modes A & B)",{"type":46,"tag":55,"props":2381,"children":2382},{},[2383,2385,2391,2393,2399,2401,2406,2408,2413,2415,2421],{"type":52,"value":2384},"Review your own diff (",{"type":46,"tag":268,"props":2386,"children":2388},{"className":2387},[],[2389],{"type":52,"value":2390},"git diff HEAD",{"type":52,"value":2392},", or ",{"type":46,"tag":268,"props":2394,"children":2396},{"className":2395},[],[2397],{"type":52,"value":2398},"git diff master...HEAD",{"type":52,"value":2400}," for an existing branch) against the repository guidelines (",{"type":46,"tag":268,"props":2402,"children":2404},{"className":2403},[],[2405],{"type":52,"value":812},{"type":52,"value":2407},", ",{"type":46,"tag":268,"props":2409,"children":2411},{"className":2410},[],[2412],{"type":52,"value":804},{"type":52,"value":2414},") before submission. This is a pre-flight self-check; the canonical automated review runs in CI after the PR is opened (",{"type":46,"tag":268,"props":2416,"children":2418},{"className":2417},[],[2419],{"type":52,"value":2420},"Automated PR Review",{"type":52,"value":2422}," workflow) — do NOT imitate its output format or header.",{"type":46,"tag":55,"props":2424,"children":2425},{},[2426],{"type":52,"value":2427},"Check the diff against this checklist:",{"type":46,"tag":61,"props":2429,"children":2432},{"className":2430},[2431],"contains-task-list",[2433,2445,2461,2477,2493,2516,2525],{"type":46,"tag":65,"props":2434,"children":2437},{"className":2435},[2436],"task-list-item",[2438,2443],{"type":46,"tag":2439,"props":2440,"children":2442},"input",{"disabled":2271,"type":2441},"checkbox",[],{"type":52,"value":2444}," Naming conventions respected (variables, outputs, resources).",{"type":46,"tag":65,"props":2446,"children":2448},{"className":2447},[2436],[2449,2452,2454,2459],{"type":46,"tag":2439,"props":2450,"children":2451},{"disabled":2271,"type":2441},[],{"type":52,"value":2453}," Context support (",{"type":46,"tag":268,"props":2455,"children":2457},{"className":2456},[],[2458],{"type":52,"value":1013},{"type":52,"value":2460}," variables) present where the module warrants it.",{"type":46,"tag":65,"props":2462,"children":2464},{"className":2463},[2436],[2465,2468,2470,2475],{"type":46,"tag":2439,"props":2466,"children":2467},{"disabled":2271,"type":2441},[],{"type":52,"value":2469}," IAM follows the standard ",{"type":46,"tag":268,"props":2471,"children":2473},{"className":2472},[],[2474],{"type":52,"value":1274},{"type":52,"value":2476}," interface patterns.",{"type":46,"tag":65,"props":2478,"children":2480},{"className":2479},[2436],[2481,2484,2486,2491],{"type":46,"tag":2439,"props":2482,"children":2483},{"disabled":2271,"type":2441},[],{"type":52,"value":2485}," Examples with ",{"type":46,"tag":268,"props":2487,"children":2489},{"className":2488},[],[2490],{"type":52,"value":1726},{"type":52,"value":2492}," parameters cover the new\u002Fchanged behavior.",{"type":46,"tag":65,"props":2494,"children":2496},{"className":2495},[2436],[2497,2500,2501,2506,2508,2514],{"type":46,"tag":2439,"props":2498,"children":2499},{"disabled":2271,"type":2441},[],{"type":52,"value":1055},{"type":46,"tag":69,"props":2502,"children":2503},{},[2504],{"type":52,"value":2505},"CRITICAL TESTING RULE",{"type":52,"value":2507},": if resource blocks were modified (adding a new argument or modifying an existing one), the Terraform plan output changes — the corresponding test inventory YAML files (",{"type":46,"tag":268,"props":2509,"children":2511},{"className":2510},[],[2512],{"type":52,"value":2513},"tests\u002F...\u002F*.yaml",{"type":52,"value":2515},") MUST be updated in the diff. If they are not, this is a critical testing failure.",{"type":46,"tag":65,"props":2517,"children":2519},{"className":2518},[2436],[2520,2523],{"type":46,"tag":2439,"props":2521,"children":2522},{"disabled":2271,"type":2441},[],{"type":52,"value":2524}," JSON schemas and factory code consistent with the changed variable surface.",{"type":46,"tag":65,"props":2526,"children":2528},{"className":2527},[2436],[2529,2532,2534,2540],{"type":46,"tag":2439,"props":2530,"children":2531},{"disabled":2271,"type":2441},[],{"type":52,"value":2533}," Documentation tables regenerated (",{"type":46,"tag":268,"props":2535,"children":2537},{"className":2536},[],[2538],{"type":52,"value":2539},"tfdoc",{"type":52,"value":2541},"), alphabetical order preserved.",{"type":46,"tag":55,"props":2543,"children":2544},{},[2545,2547,2553],{"type":52,"value":2546},"Report the outcome as a short plain-text list of issues found (or \"no issues found\") under a ",{"type":46,"tag":268,"props":2548,"children":2550},{"className":2549},[],[2551],{"type":52,"value":2552},"Pre-submission self-review",{"type":52,"value":2554}," heading — no emojis, no status tables. Fix any issue and loop back to Step 3 until the checklist passes.",{"type":46,"tag":418,"props":2556,"children":2558},{"id":2557},"step-5-live-verification-e2e-sandbox-policy-troubleshooter-modes-a-b-optional-recommended",[2559],{"type":52,"value":2560},"Step 5: Live Verification — E2E Sandbox & Policy Troubleshooter (Modes A & B — Optional \u002F Recommended)",{"type":46,"tag":55,"props":2562,"children":2563},{},[2564],{"type":52,"value":2565},"When code modifications affect GCP resource structures or APIs, run a live E2E sandbox deployment test. Run it only once the diff is stable (tests and self-review pass) to avoid repeated cloud deployments.",{"type":46,"tag":425,"props":2567,"children":2568},{},[2569,2587,2613,2638,2700,2737,2962],{"type":46,"tag":65,"props":2570,"children":2571},{},[2572,2577,2579,2585],{"type":46,"tag":69,"props":2573,"children":2574},{},[2575],{"type":52,"value":2576},"Gate — E2E Opt-in",{"type":52,"value":2578},": Ask the user for a GCP project ID (and if applicable, parent folder \u002F billing account details) for E2E sandbox testing, stating explicitly that providing the project ID authorizes you to deploy and destroy resources in it with ",{"type":46,"tag":268,"props":2580,"children":2582},{"className":2581},[],[2583],{"type":52,"value":2584},"-auto-approve",{"type":52,"value":2586},". If no project is provided, skip this step entirely.",{"type":46,"tag":65,"props":2588,"children":2589},{},[2590,2595,2597,2603,2605,2611],{"type":46,"tag":69,"props":2591,"children":2592},{},[2593],{"type":52,"value":2594},"Verify Credentials",{"type":52,"value":2596},": Confirm ",{"type":46,"tag":268,"props":2598,"children":2600},{"className":2599},[],[2601],{"type":52,"value":2602},"gcloud",{"type":52,"value":2604}," authentication and Application Default Credentials are available (",{"type":46,"tag":268,"props":2606,"children":2608},{"className":2607},[],[2609],{"type":52,"value":2610},"gcloud auth list",{"type":52,"value":2612},") before attempting any deployment, and surface a clear error rather than failing mid-apply.",{"type":46,"tag":65,"props":2614,"children":2615},{},[2616,2621,2623,2629,2631,2636],{"type":46,"tag":69,"props":2617,"children":2618},{},[2619],{"type":52,"value":2620},"Create Sandbox Directory",{"type":52,"value":2622},": Create a temporary sandbox folder under ",{"type":46,"tag":268,"props":2624,"children":2626},{"className":2625},[],[2627],{"type":52,"value":2628},"scratch\u002Fe2e_sandbox\u002F",{"type":52,"value":2630}," (the ",{"type":46,"tag":268,"props":2632,"children":2634},{"className":2633},[],[2635],{"type":52,"value":658},{"type":52,"value":2637}," directory is gitignored).",{"type":46,"tag":65,"props":2639,"children":2640},{},[2641,2646,2647],{"type":46,"tag":69,"props":2642,"children":2643},{},[2644],{"type":52,"value":2645},"Generate Test Configuration",{"type":52,"value":824},{"type":46,"tag":61,"props":2648,"children":2649},{},[2650,2662,2695],{"type":46,"tag":65,"props":2651,"children":2652},{},[2653,2655,2660],{"type":52,"value":2654},"Generate a root Terraform module ",{"type":46,"tag":268,"props":2656,"children":2658},{"className":2657},[],[2659],{"type":52,"value":1028},{"type":52,"value":2661}," in the sandbox directory.",{"type":46,"tag":65,"props":2663,"children":2664},{},[2665,2670,2672,2678,2680,2685,2687,2693],{"type":46,"tag":69,"props":2666,"children":2667},{},[2668],{"type":52,"value":2669},"CRITICAL",{"type":52,"value":2671},": The ",{"type":46,"tag":268,"props":2673,"children":2675},{"className":2674},[],[2676],{"type":52,"value":2677},"source",{"type":52,"value":2679}," argument of the module call MUST point to the ",{"type":46,"tag":69,"props":2681,"children":2682},{},[2683],{"type":52,"value":2684},"local path",{"type":52,"value":2686}," of the modified module in the repository (e.g. ",{"type":46,"tag":268,"props":2688,"children":2690},{"className":2689},[],[2691],{"type":52,"value":2692},"source = \"..\u002F..\u002Fmodules\u002F\u003Cmodule-name>\"",{"type":52,"value":2694},"), NOT the GitHub reference, to ensure your local changes are tested.",{"type":46,"tag":65,"props":2696,"children":2697},{},[2698],{"type":52,"value":2699},"Set up necessary providers and variables.",{"type":46,"tag":65,"props":2701,"children":2702},{},[2703,2708,2709],{"type":46,"tag":69,"props":2704,"children":2705},{},[2706],{"type":52,"value":2707},"Deploy and Verify",{"type":52,"value":824},{"type":46,"tag":61,"props":2710,"children":2711},{},[2712,2732],{"type":46,"tag":65,"props":2713,"children":2714},{},[2715,2717,2723,2724,2730],{"type":52,"value":2716},"Run ",{"type":46,"tag":268,"props":2718,"children":2720},{"className":2719},[],[2721],{"type":52,"value":2722},"terraform init",{"type":52,"value":806},{"type":46,"tag":268,"props":2725,"children":2727},{"className":2726},[],[2728],{"type":52,"value":2729},"terraform apply -auto-approve",{"type":52,"value":2731}," in the sandbox folder.",{"type":46,"tag":65,"props":2733,"children":2734},{},[2735],{"type":52,"value":2736},"Confirm that all resources are created successfully.",{"type":46,"tag":65,"props":2738,"children":2739},{},[2740,2745,2746],{"type":46,"tag":69,"props":2741,"children":2742},{},[2743],{"type":52,"value":2744},"Verify IAM Conditions via Policy Troubleshooter",{"type":52,"value":824},{"type":46,"tag":61,"props":2747,"children":2748},{},[2749,2762],{"type":46,"tag":65,"props":2750,"children":2751},{},[2752,2754,2760],{"type":52,"value":2753},"When modifying IAM conditional bindings or policies, use Google Cloud's official IAM Policy Troubleshooter (",{"type":46,"tag":268,"props":2755,"children":2757},{"className":2756},[],[2758],{"type":52,"value":2759},"gcloud policy-troubleshoot iam",{"type":52,"value":2761},") to verify runtime evaluation against live target resources.",{"type":46,"tag":65,"props":2763,"children":2764},{},[2765,2769,2771,2777,2779,2785,2787],{"type":46,"tag":69,"props":2766,"children":2767},{},[2768],{"type":52,"value":2669},{"type":52,"value":2770},": GCP IAM runtime evaluates ",{"type":46,"tag":268,"props":2772,"children":2774},{"className":2773},[],[2775],{"type":52,"value":2776},"resource.name",{"type":52,"value":2778}," using numeric Project Numbers. Always test conditions against ",{"type":46,"tag":268,"props":2780,"children":2782},{"className":2781},[],[2783],{"type":52,"value":2784},"--resource-name",{"type":52,"value":2786}," with the numeric Project Number:\n",{"type":46,"tag":260,"props":2788,"children":2790},{"className":440,"code":2789,"language":442,"meta":265,"style":265},"gcloud policy-troubleshoot iam \u002F\u002Flogging.googleapis.com\u002Fprojects\u002F\u003CPROJECT_NUMBER>\u002Flocations\u002Fglobal\u002Fbuckets\u002F\u003CBUCKET> \\\n  --permission=logging.buckets.write \\\n  --principal-email=\u003CTEST_PRINCIPAL_EMAIL> \\\n  --resource-name=projects\u002F\u003CPROJECT_NUMBER>\u002Flocations\u002Fglobal\u002Fbuckets\u002F\u003CBUCKET> \\\n  --format=\"json(access,explainedPolicies[].bindingExplanations)\"\n",[2791],{"type":46,"tag":268,"props":2792,"children":2793},{"__ignoreMap":265},[2794,2862,2874,2899,2941],{"type":46,"tag":272,"props":2795,"children":2796},{"class":274,"line":275},[2797,2801,2806,2811,2816,2820,2825,2830,2834,2839,2843,2848,2853,2857],{"type":46,"tag":272,"props":2798,"children":2799},{"style":452},[2800],{"type":52,"value":2602},{"type":46,"tag":272,"props":2802,"children":2803},{"style":458},[2804],{"type":52,"value":2805}," policy-troubleshoot",{"type":46,"tag":272,"props":2807,"children":2808},{"style":458},[2809],{"type":52,"value":2810}," iam",{"type":46,"tag":272,"props":2812,"children":2813},{"style":458},[2814],{"type":52,"value":2815}," \u002F\u002Flogging.googleapis.com\u002Fprojects\u002F",{"type":46,"tag":272,"props":2817,"children":2818},{"style":469},[2819],{"type":52,"value":933},{"type":46,"tag":272,"props":2821,"children":2822},{"style":458},[2823],{"type":52,"value":2824},"PROJECT_NUMBE",{"type":46,"tag":272,"props":2826,"children":2827},{"style":480},[2828],{"type":52,"value":2829},"R",{"type":46,"tag":272,"props":2831,"children":2832},{"style":469},[2833],{"type":52,"value":923},{"type":46,"tag":272,"props":2835,"children":2836},{"style":458},[2837],{"type":52,"value":2838},"\u002Flocations\u002Fglobal\u002Fbuckets\u002F",{"type":46,"tag":272,"props":2840,"children":2841},{"style":469},[2842],{"type":52,"value":933},{"type":46,"tag":272,"props":2844,"children":2845},{"style":458},[2846],{"type":52,"value":2847},"BUCKE",{"type":46,"tag":272,"props":2849,"children":2850},{"style":480},[2851],{"type":52,"value":2852},"T",{"type":46,"tag":272,"props":2854,"children":2855},{"style":469},[2856],{"type":52,"value":923},{"type":46,"tag":272,"props":2858,"children":2859},{"style":480},[2860],{"type":52,"value":2861}," \\\n",{"type":46,"tag":272,"props":2863,"children":2864},{"class":274,"line":284},[2865,2870],{"type":46,"tag":272,"props":2866,"children":2867},{"style":458},[2868],{"type":52,"value":2869},"  --permission=logging.buckets.write",{"type":46,"tag":272,"props":2871,"children":2872},{"style":480},[2873],{"type":52,"value":2861},{"type":46,"tag":272,"props":2875,"children":2876},{"class":274,"line":293},[2877,2882,2886,2891,2895],{"type":46,"tag":272,"props":2878,"children":2879},{"style":458},[2880],{"type":52,"value":2881},"  --principal-email=",{"type":46,"tag":272,"props":2883,"children":2884},{"style":469},[2885],{"type":52,"value":933},{"type":46,"tag":272,"props":2887,"children":2888},{"style":458},[2889],{"type":52,"value":2890},"TEST_PRINCIPAL_EMAIL",{"type":46,"tag":272,"props":2892,"children":2893},{"style":469},[2894],{"type":52,"value":923},{"type":46,"tag":272,"props":2896,"children":2897},{"style":480},[2898],{"type":52,"value":2861},{"type":46,"tag":272,"props":2900,"children":2901},{"class":274,"line":302},[2902,2907,2911,2916,2920,2924,2928,2933,2937],{"type":46,"tag":272,"props":2903,"children":2904},{"style":458},[2905],{"type":52,"value":2906},"  --resource-name=projects\u002F",{"type":46,"tag":272,"props":2908,"children":2909},{"style":469},[2910],{"type":52,"value":933},{"type":46,"tag":272,"props":2912,"children":2913},{"style":458},[2914],{"type":52,"value":2915},"PROJECT_NUMBER",{"type":46,"tag":272,"props":2917,"children":2918},{"style":469},[2919],{"type":52,"value":923},{"type":46,"tag":272,"props":2921,"children":2922},{"style":458},[2923],{"type":52,"value":2838},{"type":46,"tag":272,"props":2925,"children":2926},{"style":469},[2927],{"type":52,"value":933},{"type":46,"tag":272,"props":2929,"children":2930},{"style":458},[2931],{"type":52,"value":2932},"BUCKET",{"type":46,"tag":272,"props":2934,"children":2935},{"style":469},[2936],{"type":52,"value":923},{"type":46,"tag":272,"props":2938,"children":2939},{"style":480},[2940],{"type":52,"value":2861},{"type":46,"tag":272,"props":2942,"children":2943},{"class":274,"line":311},[2944,2949,2953,2958],{"type":46,"tag":272,"props":2945,"children":2946},{"style":458},[2947],{"type":52,"value":2948},"  --format=",{"type":46,"tag":272,"props":2950,"children":2951},{"style":469},[2952],{"type":52,"value":1430},{"type":46,"tag":272,"props":2954,"children":2955},{"style":458},[2956],{"type":52,"value":2957},"json(access,explainedPolicies[].bindingExplanations)",{"type":46,"tag":272,"props":2959,"children":2960},{"style":469},[2961],{"type":52,"value":1532},{"type":46,"tag":65,"props":2963,"children":2964},{},[2965,2970,2971],{"type":46,"tag":69,"props":2966,"children":2967},{},[2968],{"type":52,"value":2969},"Destroy Resources",{"type":52,"value":824},{"type":46,"tag":61,"props":2972,"children":2973},{},[2974,2987,2992],{"type":46,"tag":65,"props":2975,"children":2976},{},[2977,2979,2985],{"type":52,"value":2978},"Once verified, run ",{"type":46,"tag":268,"props":2980,"children":2982},{"className":2981},[],[2983],{"type":52,"value":2984},"terraform destroy -auto-approve",{"type":52,"value":2986}," to tear down all created resources and avoid ongoing cloud costs.",{"type":46,"tag":65,"props":2988,"children":2989},{},[2990],{"type":52,"value":2991},"Delete the sandbox directory.",{"type":46,"tag":65,"props":2993,"children":2994},{},[2995],{"type":52,"value":2996},"If verification surfaced failures, fix the code and loop back to Step 3 before re-deploying.",{"type":46,"tag":418,"props":2998,"children":3000},{"id":2999},"step-6-commit-submit-the-pr-modes-a-b",[3001],{"type":52,"value":3002},"Step 6: Commit & Submit the PR (Modes A & B)",{"type":46,"tag":425,"props":3004,"children":3005},{},[3006,3031,3173,3207,3293,3303],{"type":46,"tag":65,"props":3007,"children":3008},{},[3009,3014,3016,3021,3023,3029],{"type":46,"tag":69,"props":3010,"children":3011},{},[3012],{"type":52,"value":3013},"Sanitize Before Committing",{"type":52,"value":3015},": PII sanitization applies to ",{"type":46,"tag":69,"props":3017,"children":3018},{},[3019],{"type":52,"value":3020},"everything that leaves your machine",{"type":52,"value":3022}," — commits, file contents, and the PR body. Never commit files containing real GCP project IDs, numeric project numbers, personal email addresses, or custom resource names from live testing; verify ",{"type":46,"tag":268,"props":3024,"children":3026},{"className":3025},[],[3027],{"type":52,"value":3028},"git status",{"type":52,"value":3030}," shows no stray sandbox or scratch files before staging.",{"type":46,"tag":65,"props":3032,"children":3033},{},[3034,3039,3041,3046,3048],{"type":46,"tag":69,"props":3035,"children":3036},{},[3037],{"type":52,"value":3038},"Commit and Push",{"type":52,"value":3040},": Stage the relevant files, commit with a short imperative message describing the change, make sure the branch is up to date with ",{"type":46,"tag":268,"props":3042,"children":3044},{"className":3043},[],[3045],{"type":52,"value":957},{"type":52,"value":3047}," (rebase if needed), and push:",{"type":46,"tag":260,"props":3049,"children":3051},{"className":440,"code":3050,"language":442,"meta":265,"style":265},"git add \u003Cfiles>\ngit commit -m \"Add \u003Cfeature> to modules\u002F\u003Cmodule-name>\"\ngit push -u origin \u003Cusername>\u002F\u003Cfeature-name>\n",[3052],{"type":46,"tag":268,"props":3053,"children":3054},{"__ignoreMap":265},[3055,3085,3115],{"type":46,"tag":272,"props":3056,"children":3057},{"class":274,"line":275},[3058,3062,3067,3071,3076,3081],{"type":46,"tag":272,"props":3059,"children":3060},{"style":452},[3061],{"type":52,"value":863},{"type":46,"tag":272,"props":3063,"children":3064},{"style":458},[3065],{"type":52,"value":3066}," add",{"type":46,"tag":272,"props":3068,"children":3069},{"style":469},[3070],{"type":52,"value":472},{"type":46,"tag":272,"props":3072,"children":3073},{"style":458},[3074],{"type":52,"value":3075},"file",{"type":46,"tag":272,"props":3077,"children":3078},{"style":480},[3079],{"type":52,"value":3080},"s",{"type":46,"tag":272,"props":3082,"children":3083},{"style":469},[3084],{"type":52,"value":488},{"type":46,"tag":272,"props":3086,"children":3087},{"class":274,"line":284},[3088,3092,3097,3102,3106,3111],{"type":46,"tag":272,"props":3089,"children":3090},{"style":452},[3091],{"type":52,"value":863},{"type":46,"tag":272,"props":3093,"children":3094},{"style":458},[3095],{"type":52,"value":3096}," commit",{"type":46,"tag":272,"props":3098,"children":3099},{"style":458},[3100],{"type":52,"value":3101}," -m",{"type":46,"tag":272,"props":3103,"children":3104},{"style":469},[3105],{"type":52,"value":1420},{"type":46,"tag":272,"props":3107,"children":3108},{"style":458},[3109],{"type":52,"value":3110},"Add \u003Cfeature> to modules\u002F\u003Cmodule-name>",{"type":46,"tag":272,"props":3112,"children":3113},{"style":469},[3114],{"type":52,"value":1532},{"type":46,"tag":272,"props":3116,"children":3117},{"class":274,"line":293},[3118,3122,3127,3132,3137,3141,3145,3149,3153,3157,3161,3165,3169],{"type":46,"tag":272,"props":3119,"children":3120},{"style":452},[3121],{"type":52,"value":863},{"type":46,"tag":272,"props":3123,"children":3124},{"style":458},[3125],{"type":52,"value":3126}," push",{"type":46,"tag":272,"props":3128,"children":3129},{"style":458},[3130],{"type":52,"value":3131}," -u",{"type":46,"tag":272,"props":3133,"children":3134},{"style":458},[3135],{"type":52,"value":3136}," origin",{"type":46,"tag":272,"props":3138,"children":3139},{"style":469},[3140],{"type":52,"value":472},{"type":46,"tag":272,"props":3142,"children":3143},{"style":458},[3144],{"type":52,"value":913},{"type":46,"tag":272,"props":3146,"children":3147},{"style":480},[3148],{"type":52,"value":918},{"type":46,"tag":272,"props":3150,"children":3151},{"style":469},[3152],{"type":52,"value":923},{"type":46,"tag":272,"props":3154,"children":3155},{"style":458},[3156],{"type":52,"value":928},{"type":46,"tag":272,"props":3158,"children":3159},{"style":469},[3160],{"type":52,"value":933},{"type":46,"tag":272,"props":3162,"children":3163},{"style":458},[3164],{"type":52,"value":938},{"type":46,"tag":272,"props":3166,"children":3167},{"style":480},[3168],{"type":52,"value":918},{"type":46,"tag":272,"props":3170,"children":3171},{"style":469},[3172],{"type":52,"value":488},{"type":46,"tag":65,"props":3174,"children":3175},{},[3176,3181,3183,3189,3191,3197,3199,3205],{"type":46,"tag":69,"props":3177,"children":3178},{},[3179],{"type":52,"value":3180},"Format the PR Title",{"type":52,"value":3182},": Do NOT use Conventional Commits format (no ",{"type":46,"tag":268,"props":3184,"children":3186},{"className":3185},[],[3187],{"type":52,"value":3188},"feat:",{"type":52,"value":3190}," or ",{"type":46,"tag":268,"props":3192,"children":3194},{"className":3193},[],[3195],{"type":52,"value":3196},"fix:",{"type":52,"value":3198}," prefixes). Use a short, capitalized, imperative title (e.g., \"Add native tag bindings support to ",{"type":46,"tag":268,"props":3200,"children":3202},{"className":3201},[],[3203],{"type":52,"value":3204},"modules\u002Fnet-firewall-policy",{"type":52,"value":3206},"\").",{"type":46,"tag":65,"props":3208,"children":3209},{},[3210,3215,3216],{"type":46,"tag":69,"props":3211,"children":3212},{},[3213],{"type":52,"value":3214},"Write the PR Body",{"type":52,"value":824},{"type":46,"tag":61,"props":3217,"children":3218},{},[3219,3224,3234,3280],{"type":46,"tag":65,"props":3220,"children":3221},{},[3222],{"type":52,"value":3223},"Explain the problem, rationale, and the fix clearly.",{"type":46,"tag":65,"props":3225,"children":3226},{},[3227,3232],{"type":46,"tag":69,"props":3228,"children":3229},{},[3230],{"type":52,"value":3231},"Document Verification & E2E Testing Methodology",{"type":52,"value":3233},": Detail any local unit tests, live E2E sandbox deployments, or Policy Troubleshooter API verifications performed so reviewers can see the exact testing rationale and methodology.",{"type":46,"tag":65,"props":3235,"children":3236},{},[3237,3242,3244,3249,3251,3257,3258,3264,3265,3271,3272,3278],{"type":46,"tag":69,"props":3238,"children":3239},{},[3240],{"type":52,"value":3241},"CRITICAL PII SANITIZATION",{"type":52,"value":3243},": Before writing the PR description, ",{"type":46,"tag":69,"props":3245,"children":3246},{},[3247],{"type":52,"value":3248},"MUST scrub all developer PII",{"type":52,"value":3250}," (real GCP project IDs, numeric project numbers, personal email addresses, usernames, and custom bucket\u002Fresource names) and replace them with generic placeholders (e.g., ",{"type":46,"tag":268,"props":3252,"children":3254},{"className":3253},[],[3255],{"type":52,"value":3256},"my-project",{"type":52,"value":2407},{"type":46,"tag":268,"props":3259,"children":3261},{"className":3260},[],[3262],{"type":52,"value":3263},"123456789012",{"type":52,"value":2407},{"type":46,"tag":268,"props":3266,"children":3268},{"className":3267},[],[3269],{"type":52,"value":3270},"user:tester@example.com",{"type":52,"value":2407},{"type":46,"tag":268,"props":3273,"children":3275},{"className":3274},[],[3276],{"type":52,"value":3277},"test-bucket",{"type":52,"value":3279},").",{"type":46,"tag":65,"props":3281,"children":3282},{},[3283,3285,3291],{"type":52,"value":3284},"Do NOT touch ",{"type":46,"tag":268,"props":3286,"children":3288},{"className":3287},[],[3289],{"type":52,"value":3290},"CHANGELOG.md",{"type":52,"value":3292},": release notes are generated from PR labels applied by maintainers.",{"type":46,"tag":65,"props":3294,"children":3295},{},[3296,3301],{"type":46,"tag":69,"props":3297,"children":3298},{},[3299],{"type":52,"value":3300},"Gate — PR Approval",{"type":52,"value":3302},": Present the final sanitized PR title and body to the user and get explicit approval before creating the PR.",{"type":46,"tag":65,"props":3304,"children":3305},{},[3306,3311,3312],{"type":46,"tag":69,"props":3307,"children":3308},{},[3309],{"type":52,"value":3310},"Create the PR",{"type":52,"value":824},{"type":46,"tag":61,"props":3313,"children":3314},{},[3315,3346],{"type":46,"tag":65,"props":3316,"children":3317},{},[3318,3323,3325,3331,3333,3338,3340,3345],{"type":46,"tag":69,"props":3319,"children":3320},{},[3321],{"type":52,"value":3322},"CRITICAL PITFALL",{"type":52,"value":3324},": Do NOT pass the body inline on the CLI if it contains backticks (e.g. ",{"type":46,"tag":268,"props":3326,"children":3328},{"className":3327},[],[3329],{"type":52,"value":3330},"gh pr create --body \"Fixes ",{"type":52,"value":3332},"bug",{"type":46,"tag":268,"props":3334,"children":3336},{"className":3335},[],[3337],{"type":52,"value":1430},{"type":52,"value":3339},") as the shell will evaluate backticks, corrupting the description. Do NOT use shell redirection or heredocs to create the body file either (see ",{"type":46,"tag":268,"props":3341,"children":3343},{"className":3342},[],[3344],{"type":52,"value":812},{"type":52,"value":3279},{"type":46,"tag":65,"props":3347,"children":3348},{},[3349,3351,3356,3358],{"type":52,"value":3350},"Instead, write the body to a temporary file ",{"type":46,"tag":69,"props":3352,"children":3353},{},[3354],{"type":52,"value":3355},"using your file-writing tool",{"type":52,"value":3357},", then reference it:\n",{"type":46,"tag":260,"props":3359,"children":3361},{"className":440,"code":3360,"language":442,"meta":265,"style":265},"gh pr create --title \"Your PR Title\" --body-file \u002Ftmp\u002Fpr-body.md\n",[3362],{"type":46,"tag":268,"props":3363,"children":3364},{"__ignoreMap":265},[3365],{"type":46,"tag":272,"props":3366,"children":3367},{"class":274,"line":275},[3368,3372,3377,3382,3387,3391,3396,3400,3405],{"type":46,"tag":272,"props":3369,"children":3370},{"style":452},[3371],{"type":52,"value":455},{"type":46,"tag":272,"props":3373,"children":3374},{"style":458},[3375],{"type":52,"value":3376}," pr",{"type":46,"tag":272,"props":3378,"children":3379},{"style":458},[3380],{"type":52,"value":3381}," create",{"type":46,"tag":272,"props":3383,"children":3384},{"style":458},[3385],{"type":52,"value":3386}," --title",{"type":46,"tag":272,"props":3388,"children":3389},{"style":469},[3390],{"type":52,"value":1420},{"type":46,"tag":272,"props":3392,"children":3393},{"style":458},[3394],{"type":52,"value":3395},"Your PR Title",{"type":46,"tag":272,"props":3397,"children":3398},{"style":469},[3399],{"type":52,"value":1430},{"type":46,"tag":272,"props":3401,"children":3402},{"style":458},[3403],{"type":52,"value":3404}," --body-file",{"type":46,"tag":272,"props":3406,"children":3407},{"style":458},[3408],{"type":52,"value":3409}," \u002Ftmp\u002Fpr-body.md\n",{"type":46,"tag":3411,"props":3412,"children":3413},"style",{},[3414],{"type":52,"value":3415},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":3417,"total":293},[3418,3426,3439],{"slug":4,"name":4,"fn":5,"description":6,"org":3419,"tags":3420,"stars":27,"repoUrl":28,"updatedAt":29},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3421,3422,3423,3424,3425],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":25,"slug":26,"type":16},{"slug":3427,"name":3427,"fn":3428,"description":3429,"org":3430,"tags":3431,"stars":27,"repoUrl":28,"updatedAt":3438},"fabric-builder","generate Terraform code for Google Cloud","Generates idiomatic Cloud Foundation Fabric (CFF) Terraform code using CFF modules. Use when users ask to create GCP resources, use Fabric modules, or generate Terraform code for Google Cloud.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3432,3433,3436],{"name":9,"slug":8,"type":16},{"name":3434,"slug":3435,"type":16},"Infrastructure as Code","infrastructure-as-code",{"name":3437,"slug":34,"type":16},"Terraform","2026-07-12T07:38:23.514555",{"slug":3440,"name":3440,"fn":3441,"description":3442,"org":3443,"tags":3444,"stars":27,"repoUrl":28,"updatedAt":3449},"fast-0-org-setup-prereqs","prepare prerequisites for FAST 0-org-setup","Guides the user step-by-step through the prerequisites for the FAST 0-org-setup stage, supporting both Standard GCP and Google Cloud Dedicated (GCD) environments. Use when a user asks to prepare or run prerequisites for 0-org-setup or bootstrap the FAST landing zone.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3445,3446],{"name":9,"slug":8,"type":16},{"name":3447,"slug":3448,"type":16},"Operations","operations","2026-07-12T07:38:28.127148",{"items":3451,"total":3610},[3452,3470,3486,3494,3500,3505,3519,3536,3553,3566,3582,3592],{"slug":3453,"name":3453,"fn":3454,"description":3455,"org":3456,"tags":3457,"stars":3467,"repoUrl":3468,"updatedAt":3469},"kb-search","search and extract local knowledge base documents","Allows listing, searching and extracting information from local knowledge base documents for information about tables\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3458,3461,3464],{"name":3459,"slug":3460,"type":16},"Documentation","documentation",{"name":3462,"slug":3463,"type":16},"Knowledge Base","knowledge-base",{"name":3465,"slug":3466,"type":16},"Search","search",6749,"https:\u002F\u002Fgithub.com\u002FGoogleCloudPlatform\u002Fknowledge-catalog","2026-07-12T07:38:52.157375",{"slug":3471,"name":3472,"fn":3473,"description":3474,"org":3475,"tags":3476,"stars":3467,"repoUrl":3468,"updatedAt":3485},"knowledgecatalogdiscoveryagent","knowledge_catalog_discovery_agent","search and rank Knowledge Catalog data entries","Analyzes user queries, extracts relevant predicates, and utilizes Knowledge Catalog Search to find and rank the most relevant data entries. Engages with the user throughout the process.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3477,3480,3481,3484],{"name":3478,"slug":3479,"type":16},"Data Analysis","data-analysis",{"name":9,"slug":8,"type":16},{"name":3482,"slug":3483,"type":16},"Knowledge Management","knowledge-management",{"name":3465,"slug":3466,"type":16},"2026-07-12T07:38:22.196851",{"slug":4,"name":4,"fn":5,"description":6,"org":3487,"tags":3488,"stars":27,"repoUrl":28,"updatedAt":29},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3489,3490,3491,3492,3493],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":25,"slug":26,"type":16},{"slug":3427,"name":3427,"fn":3428,"description":3429,"org":3495,"tags":3496,"stars":27,"repoUrl":28,"updatedAt":3438},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3497,3498,3499],{"name":9,"slug":8,"type":16},{"name":3434,"slug":3435,"type":16},{"name":3437,"slug":34,"type":16},{"slug":3440,"name":3440,"fn":3441,"description":3442,"org":3501,"tags":3502,"stars":27,"repoUrl":28,"updatedAt":3449},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3503,3504],{"name":9,"slug":8,"type":16},{"name":3447,"slug":3448,"type":16},{"slug":3506,"name":3506,"fn":3507,"description":3508,"org":3509,"tags":3510,"stars":3516,"repoUrl":3517,"updatedAt":3518},"agent-aware-cli","design agent-aware command-line interfaces","Guide for designing and implementing command-line interfaces (CLIs) that are equally usable by human developers and automated coding agents. Use when the user wants to build a CLI, apply CLI best practices, or use Go with Cobra and Viper.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3511,3514,3515],{"name":3512,"slug":3513,"type":16},"CLI","cli",{"name":21,"slug":22,"type":16},{"name":9,"slug":8,"type":16},1150,"https:\u002F\u002Fgithub.com\u002FGoogleCloudPlatform\u002Fvertex-ai-creative-studio","2026-07-12T07:39:08.41406",{"slug":3520,"name":3520,"fn":3521,"description":3522,"org":3523,"tags":3524,"stars":3516,"repoUrl":3517,"updatedAt":3535},"build-mcp-genmedia","build and configure GenAI MCP servers","Builds the mcp-genmedia Go MCP servers (nanobanana, veo, lyria, gemini-multimodal, chirp3-hd, avtool) from source and wires them into settings.json. Use this skill whenever the MCP tools are missing or broken — typically at the start of a new session, after a container restart, or when \u002Ftmp has been wiped. The prebuilt binaries in \u002Fworkspace\u002F.local\u002Fbin\u002F have no exec bit and live on a noexec mount; this skill compiles fresh executables into \u002Ftmp\u002Fbin\u002F where execution is allowed.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3525,3528,3529,3532],{"name":3526,"slug":3527,"type":16},"API Development","api-development",{"name":9,"slug":8,"type":16},{"name":3530,"slug":3531,"type":16},"LLM","llm",{"name":3533,"slug":3534,"type":16},"MCP","mcp","2026-07-12T07:39:10.911302",{"slug":3537,"name":3537,"fn":3538,"description":3539,"org":3540,"tags":3541,"stars":3516,"repoUrl":3517,"updatedAt":3552},"genmedia-audio-engineer","synthesize and mix audio content","Expert in audio synthesis, music generation, and mixing. Use when creating podcasts, background scores, or multi-track audio layering using mcp-chirp3-go, mcp-lyria-go, mcp-gemini-go, mcp-nanobanana-go, and mcp-avtool-go.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3542,3545,3548,3549],{"name":3543,"slug":3544,"type":16},"Audio","audio",{"name":3546,"slug":3547,"type":16},"Creative","creative",{"name":9,"slug":8,"type":16},{"name":3550,"slug":3551,"type":16},"Vertex AI","vertex-ai","2026-07-12T07:39:16.623879",{"slug":3554,"name":3554,"fn":3555,"description":3556,"org":3557,"tags":3558,"stars":3516,"repoUrl":3517,"updatedAt":3565},"genmedia-image-artist","generate and edit AI images","Expert in AI image generation and editing. Use when the user needs high-quality textures, character-consistent visuals, or image-to-image editing using mcp-nanobanana-go.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3559,3560,3561,3564],{"name":3546,"slug":3547,"type":16},{"name":9,"slug":8,"type":16},{"name":3562,"slug":3563,"type":16},"Image Generation","image-generation",{"name":3550,"slug":3551,"type":16},"2026-07-12T07:39:15.372822",{"slug":3567,"name":3567,"fn":3568,"description":3569,"org":3570,"tags":3571,"stars":3516,"repoUrl":3517,"updatedAt":3581},"genmedia-producer","produce multi-step media content","Expert media production assistant. Use when requested to help with storyboarding, podcast creation, audio assembly, or complex multi-step media workflows using the GenMedia MCP servers (Veo, Lyria, Gemini TTS, NanoBanana).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3572,3573,3574,3575,3578],{"name":3543,"slug":3544,"type":16},{"name":3546,"slug":3547,"type":16},{"name":9,"slug":8,"type":16},{"name":3576,"slug":3577,"type":16},"Media","media",{"name":3579,"slug":3580,"type":16},"Video","video","2026-07-12T07:39:09.672849",{"slug":3583,"name":3583,"fn":3584,"description":3585,"org":3586,"tags":3587,"stars":3516,"repoUrl":3517,"updatedAt":3591},"genmedia-video-editor","edit and compose video content","Expert in video composition, editing, and format conversion. Use when the user wants to generate high-quality video, overlay images on video, concatenate clips, create GIFs, or sync audio to video using mcp-avtool-go and mcp-veo-go.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3588,3589,3590],{"name":3546,"slug":3547,"type":16},{"name":9,"slug":8,"type":16},{"name":3579,"slug":3580,"type":16},"2026-07-12T07:39:13.749081",{"slug":3593,"name":3593,"fn":3594,"description":3595,"org":3596,"tags":3597,"stars":3516,"repoUrl":3517,"updatedAt":3609},"genmedia-voice-director","generate expressive text-to-speech with Gemini","Expert in casting, directing, and generating expressive text-to-speech using Gemini TTS. Use this when the user needs virtual voice actor personas, expressive speech generation, or multiple variations of a voiceover (like \"take 3 on the bounce\").",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3598,3599,3600,3603,3606],{"name":3543,"slug":3544,"type":16},{"name":3546,"slug":3547,"type":16},{"name":3601,"slug":3602,"type":16},"Gemini","gemini",{"name":3604,"slug":3605,"type":16},"Speech","speech",{"name":3607,"slug":3608,"type":16},"Text-to-Speech","text-to-speech","2026-07-12T07:39:17.86673",80]