@mrciphersmith/keryx 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/cli.js +7310 -2471
  2. package/dist/core.js +116 -10
  3. package/package.json +1 -1
  4. package/src/gdskills/bundled/install-manifest.json +349 -2
  5. package/src/gdskills/bundled/rules/core/model-selection.mdc +18 -0
  6. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1 -1
  7. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +1 -1
  8. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +1 -1
  9. package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +1 -1
  10. package/src/gdskills/bundled/skills/review/review-jev-comments/SKILL.md +184 -0
  11. package/src/gdskills/bundled/skills/review/review-jev-contract/SKILL.md +193 -0
  12. package/src/gdskills/bundled/skills/review/review-jev-docs/SKILL.md +189 -0
  13. package/src/gdskills/bundled/skills/review/review-jev-risk/SKILL.md +190 -0
  14. package/src/gdskills/bundled/skills/review/review-jev-scenarios/SKILL.md +187 -0
  15. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +88 -15
  16. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +4 -4
  17. package/src/gdskills/bundled/stacks/c-cpp/agent-refs.json +4 -0
  18. package/src/gdskills/bundled/stacks/c-cpp/governance/eval.json +1777 -0
  19. package/src/gdskills/bundled/stacks/c-cpp/governance/scout.json +31 -0
  20. package/src/gdskills/bundled/stacks/c-cpp/pack.json +42 -0
  21. package/src/gdskills/bundled/stacks/c-cpp/rules/coding-style.mdc +80 -0
  22. package/src/gdskills/bundled/stacks/c-cpp/rules/patterns.mdc +87 -0
  23. package/src/gdskills/bundled/stacks/c-cpp/rules/security.mdc +90 -0
  24. package/src/gdskills/bundled/stacks/c-cpp/rules/testing.mdc +83 -0
  25. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-build-fix/SKILL.md +153 -0
  26. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-build-fix/evals.json +74 -0
  27. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-code-review/SKILL.md +132 -0
  28. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-code-review/evals.json +73 -0
  29. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-implementation/SKILL.md +151 -0
  30. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-implementation/evals.json +74 -0
  31. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-testing/SKILL.md +152 -0
  32. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-testing/evals.json +74 -0
  33. package/src/gdskills/bundled/stacks/ci-github-gitlab/agent-refs.json +4 -0
  34. package/src/gdskills/bundled/stacks/ci-github-gitlab/governance/eval.json +1295 -0
  35. package/src/gdskills/bundled/stacks/ci-github-gitlab/governance/scout.json +26 -0
  36. package/src/gdskills/bundled/stacks/ci-github-gitlab/pack.json +41 -0
  37. package/src/gdskills/bundled/stacks/ci-github-gitlab/rules/patterns.mdc +77 -0
  38. package/src/gdskills/bundled/stacks/ci-github-gitlab/rules/security.mdc +144 -0
  39. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-build-fix/SKILL.md +121 -0
  40. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-build-fix/evals.json +73 -0
  41. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-code-review/SKILL.md +139 -0
  42. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-code-review/evals.json +73 -0
  43. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-implementation/SKILL.md +147 -0
  44. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-implementation/evals.json +74 -0
  45. package/src/gdskills/bundled/stacks/docker-k8s-terraform/agent-refs.json +4 -0
  46. package/src/gdskills/bundled/stacks/docker-k8s-terraform/governance/eval.json +865 -0
  47. package/src/gdskills/bundled/stacks/docker-k8s-terraform/governance/scout.json +16 -0
  48. package/src/gdskills/bundled/stacks/docker-k8s-terraform/pack.json +46 -0
  49. package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/coding-style.mdc +74 -0
  50. package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/patterns.mdc +81 -0
  51. package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/security.mdc +146 -0
  52. package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/testing.mdc +61 -0
  53. package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-build-fix/SKILL.md +151 -0
  54. package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-build-fix/evals.json +74 -0
  55. package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-review/SKILL.md +135 -0
  56. package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-review/evals.json +76 -0
  57. package/src/gdskills/bundled/stacks/php-laravel/agent-refs.json +4 -0
  58. package/src/gdskills/bundled/stacks/php-laravel/governance/eval.json +1829 -0
  59. package/src/gdskills/bundled/stacks/php-laravel/governance/scout.json +33 -0
  60. package/src/gdskills/bundled/stacks/php-laravel/pack.json +41 -0
  61. package/src/gdskills/bundled/stacks/php-laravel/rules/coding-style.mdc +82 -0
  62. package/src/gdskills/bundled/stacks/php-laravel/rules/patterns.mdc +80 -0
  63. package/src/gdskills/bundled/stacks/php-laravel/rules/security.mdc +80 -0
  64. package/src/gdskills/bundled/stacks/php-laravel/rules/testing.mdc +82 -0
  65. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-build-fix/SKILL.md +143 -0
  66. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-build-fix/evals.json +74 -0
  67. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-code-review/SKILL.md +126 -0
  68. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-code-review/evals.json +76 -0
  69. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-implementation/SKILL.md +140 -0
  70. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-implementation/evals.json +75 -0
  71. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-testing/SKILL.md +124 -0
  72. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-testing/evals.json +74 -0
  73. package/src/gdskills/bundled/stacks/ruby-rails/agent-refs.json +4 -0
  74. package/src/gdskills/bundled/stacks/ruby-rails/governance/eval.json +1673 -0
  75. package/src/gdskills/bundled/stacks/ruby-rails/governance/scout.json +33 -0
  76. package/src/gdskills/bundled/stacks/ruby-rails/pack.json +42 -0
  77. package/src/gdskills/bundled/stacks/ruby-rails/rules/coding-style.mdc +69 -0
  78. package/src/gdskills/bundled/stacks/ruby-rails/rules/patterns.mdc +93 -0
  79. package/src/gdskills/bundled/stacks/ruby-rails/rules/security.mdc +90 -0
  80. package/src/gdskills/bundled/stacks/ruby-rails/rules/testing.mdc +89 -0
  81. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-build-fix/SKILL.md +143 -0
  82. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-build-fix/evals.json +73 -0
  83. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-code-review/SKILL.md +134 -0
  84. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-code-review/evals.json +71 -0
  85. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-implementation/SKILL.md +141 -0
  86. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-implementation/evals.json +72 -0
  87. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-testing/SKILL.md +125 -0
  88. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-testing/evals.json +72 -0
  89. package/src/gdskills/bundled/stacks/sql-db/agent-refs.json +4 -0
  90. package/src/gdskills/bundled/stacks/sql-db/governance/eval.json +1829 -0
  91. package/src/gdskills/bundled/stacks/sql-db/governance/scout.json +30 -0
  92. package/src/gdskills/bundled/stacks/sql-db/pack.json +40 -0
  93. package/src/gdskills/bundled/stacks/sql-db/rules/coding-style.mdc +69 -0
  94. package/src/gdskills/bundled/stacks/sql-db/rules/patterns.mdc +134 -0
  95. package/src/gdskills/bundled/stacks/sql-db/rules/security.mdc +74 -0
  96. package/src/gdskills/bundled/stacks/sql-db/rules/testing.mdc +83 -0
  97. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-build-fix/SKILL.md +147 -0
  98. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-build-fix/evals.json +72 -0
  99. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-code-review/SKILL.md +132 -0
  100. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-code-review/evals.json +73 -0
  101. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-implementation/SKILL.md +153 -0
  102. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-implementation/evals.json +77 -0
  103. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-testing/SKILL.md +129 -0
  104. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-testing/evals.json +73 -0
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "query": "Use when reviewing a Dockerfile, Docker Compose file, Kubernetes/Helm manifest, or Terraform change for security and config-authoring risks -- root containers, unpinned base images, secrets baked into image layers, missing Kubernetes securityContext/NetworkPolicy, and Terraform state/secrets handling. Read-only, no edits.",
4
+ "decision": "create",
5
+ "topMatch": "docker-k8s-terraform/docker-k8s-terraform-build-fix",
6
+ "recordedAt": "2026-09-25T15:30:30.700Z",
7
+ "skillName": "docker-k8s-terraform-review"
8
+ },
9
+ {
10
+ "query": "Use when a Docker build fails, a Kubernetes/Helm manifest is rejected by validation or the API server, or a Terraform validate/plan fails -- resolves the actual root cause (a bad COPY path, an ownership/permission mismatch after switching to a non-root USER, a schema-invalid manifest, a Terraform state/address mismatch) with the smallest fix, never a suppression.",
11
+ "decision": "create",
12
+ "topMatch": "ci-github-gitlab/ci-pipeline-build-fix",
13
+ "recordedAt": "2026-09-25T15:30:36.249Z",
14
+ "skillName": "docker-k8s-terraform-build-fix"
15
+ }
16
+ ]
@@ -0,0 +1,46 @@
1
+ {
2
+ "id": "docker-k8s-terraform",
3
+ "family": "tool",
4
+ "modules": ["docker-k8s-terraform-rules", "docker-k8s-terraform-skills"],
5
+ "detectionMarkers": ["docker", "docker-compose", "terraform"],
6
+ "provenance": {
7
+ "origin": "authored",
8
+ "sourceRef": "flow 338, Wave 4 batch 6"
9
+ },
10
+ "stability": "experimental",
11
+ "skills": {
12
+ "implement": [],
13
+ "test": [],
14
+ "review": ["docker-k8s-terraform-review"],
15
+ "build-fix": ["docker-k8s-terraform-build-fix"],
16
+ "migrate": []
17
+ },
18
+ "notes": {
19
+ "implement": "No implement skill authored in this batch. W1-stack-catalog.md's target-stack table gives the reason as \"config authoring lives in the deploy quality skill\" -- that does not hold up: `src/gdskills/bundled/skills/quality/deploy/SKILL.md` is scoped to running a deployment pipeline/release (\"Use when deploying a release to any environment\"), not authoring Dockerfiles/K8s manifests/Terraform configs. No existing catalog skill actually covers authoring these files. This is a genuine, honestly-deferred gap, not a covered case -- left for a future batch, and W1-stack-catalog.md's table entry needs its own correction (tracked in that doc's flow 338 review-round notes).",
20
+ "test": "No separate test skill in this batch. This is a scope deferral, not an absence of a real concept: Terraform has `terraform test` (`.tftest.hcl` run blocks) and Helm has `helm test` (chart-defined test hooks) plus the community `helm-unittest` plugin (template unit tests with no cluster required) -- both are genuine, analogous to what go-testing/python-testing cover for their stacks. `docker-k8s-terraform-build-fix`'s reproduce-and-classify step covers running an EXISTING validator (hadolint, kubeconform/kubeval, `terraform validate`/`plan`) to diagnose a failure, which is a different job from writing/extending `.tftest.hcl` or helm-unittest test suites. Left for a future batch.",
21
+ "migrate": "No migrate skill in this batch. This is a scope deferral, not an absence of real migration concepts: Kubernetes API version deprecations (e.g. a manifest still on `apps/v1beta1` needing `apps/v1`), a Terraform provider major-version upgrade with its own breaking-change/state-migration steps, and a Helm chart's own major-version upgrade path are all genuine, recurring migration shapes for this family -- none was concrete/scoped enough to write a focused skill and eval suite around in this batch. Left for a future batch, not claimed as nonexistent."
22
+ },
23
+ "agentProfile": {
24
+ "displayName": "Docker / Kubernetes / Terraform",
25
+ "auditFocus": [
26
+ "a container image actually running as root -- no USER instruction AND no evidence the base image already defaults to a non-root user (see rules/patterns.mdc), or an explicit USER root reintroduced after a non-root user was already set -- or a Kubernetes securityContext missing runAsNonRoot/allowPrivilegeEscalation: false",
27
+ "a base image pinned only by a mutable tag (`FROM node:20`) instead of an immutable digest (`FROM node:20@sha256:...`)",
28
+ "a secret (API key, password, token) baked into an image layer via ARG/ENV/COPY instead of a build-time secret mount or runtime injection",
29
+ "a Kubernetes Pod/Deployment with no resource requests/limits, no readOnlyRootFilesystem, or capabilities not dropped to the minimum the container needs",
30
+ "a namespace or workload with no default-deny NetworkPolicy, or one scoped too broadly to serve as a real boundary",
31
+ "a Terraform resource or module with a hardcoded credential/connection string in `.tf`/`.tfvars`, a `sensitive = true` value treated as if that alone encrypted it, or state configured with a local/unencrypted backend"
32
+ ],
33
+ "buildCommands": [
34
+ "hadolint <Dockerfile> (when hadolint is available)",
35
+ "docker build . (or docker compose build for a compose project)",
36
+ "kubeconform -strict -summary <manifest-or-dir> (or kubeval, whichever the project already uses)",
37
+ "terraform validate && terraform plan (only plan, never apply, from this skill)"
38
+ ],
39
+ "fixGuardrails": [
40
+ "Never remove a USER/securityContext non-root setting, drop a NetworkPolicy, or relax a capability drop just to make a build/deploy pass.",
41
+ "Never bake a secret into an image layer (ARG/ENV/COPY of a credential) to route around a missing runtime secret — use a build-time secret mount or say the runtime wiring is still needed.",
42
+ "Never commit or read a `.tfstate` file as the source of truth for a fix, and never suggest disabling remote state locking to unblock a run.",
43
+ "Never widen a Terraform `sensitive = true` removal or a K8s NetworkPolicy loosening to silence a validator finding without saying so in the report."
44
+ ]
45
+ }
46
+ }
@@ -0,0 +1,74 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/Dockerfile", "**/Dockerfile.*", "**/*.Dockerfile", "**/*.dockerfile", "**/Containerfile", "**/docker-compose*.yml", "**/docker-compose*.yaml", "**/compose.yml", "**/compose.yaml", "k8s/**/*.yaml", "k8s/**/*.yml", "manifests/**/*.yaml", "manifests/**/*.yml", "deploy/**/*.yaml", "deploy/**/*.yml", "overlays/**/*.yaml", "**/kustomization.yaml", "**/kustomization.yml", "helm/**/*.yaml", "helm/**/templates/*.yaml", "charts/**/*.yaml", "charts/**/templates/*.yaml", "charts/*/values.yaml", "**/*.tf", "**/*.tfvars"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # Docker / Kubernetes / Terraform coding style
9
+
10
+ Narrows `core-common-rules`' stack-agnostic style rules to the file shapes
11
+ this pack owns: Dockerfiles, Docker Compose files, Kubernetes/Helm YAML, and
12
+ Terraform. Applies only to those file types — not to application source code
13
+ under any of these directories.
14
+
15
+ ## Dockerfile
16
+
17
+ - Order instructions from least to most frequently changing (base image,
18
+ system packages, dependency manifests, dependency install, then source
19
+ copy) so Docker's build cache is invalidated only by the layer that
20
+ actually changed, not by every layer after a source edit.
21
+ - Use multi-stage builds (`FROM ... AS builder`, then a slim final stage
22
+ that copies only the built artifact) for any compiled or bundled
23
+ language — the build toolchain never belongs in the shipped image.
24
+ - Prefer a minimal, actively maintained base image (`-slim`/`-alpine`, or a
25
+ distroless/hardened image) over a full OS image unless the project
26
+ genuinely needs the extra tooling at runtime.
27
+ - One logical concern per `RUN` line where it aids readability, but chain
28
+ related package-manager commands (`apt-get update && apt-get install
29
+ -y ... && rm -rf /var/lib/apt/lists/*`) into a single `RUN` so the
30
+ package cache does not persist as a separate layer.
31
+ - Ship a `.dockerignore` alongside the Dockerfile that excludes `.git/`,
32
+ `node_modules/`/`vendor/` (rebuilt inside the image), and any local
33
+ env/secret file — an unfiltered build context both bloats the image and
34
+ risks copying something that should never leave the host.
35
+
36
+ ## Docker Compose
37
+
38
+ - Pin every service's `image:` the same way a Dockerfile's `FROM` is
39
+ pinned (a tag, or a digest for anything security-sensitive) — never
40
+ `image: latest` in a committed compose file.
41
+ - Keep secrets and per-environment values in `.env`/`environment:` sourced
42
+ from the shell or a secrets file that is itself gitignored, not
43
+ hardcoded into `docker-compose.yml`.
44
+ - Name every custom network and volume explicitly rather than relying on
45
+ Compose's default naming, so multi-project setups do not collide.
46
+
47
+ ## Kubernetes and Helm
48
+
49
+ - One resource kind's concerns per manifest file where the project's
50
+ layout allows it (a Deployment, its Service, its ConfigMap as separate
51
+ files or a clearly delimited multi-document YAML) rather than one
52
+ sprawling file mixing unrelated workloads.
53
+ - Set explicit `resources.requests`/`resources.limits` on every container
54
+ — an unbounded container can starve its node's other workloads, and the
55
+ scheduler cannot place it sensibly without a request.
56
+ - Use a Helm `values.yaml` (or Kustomize overlay) for anything that varies
57
+ per environment — a hardcoded namespace, hostname, or replica count
58
+ inside a template defeats the point of templating.
59
+ - Label and select consistently (`app.kubernetes.io/name`,
60
+ `app.kubernetes.io/instance`) so Services, NetworkPolicies, and
61
+ `kubectl` tooling can all select the same workload by the same labels.
62
+
63
+ ## Terraform
64
+
65
+ - Organize a module around `main.tf`/`variables.tf`/`outputs.tf` (plus
66
+ `versions.tf` pinning the provider and Terraform version) rather than
67
+ one large file mixing resource definitions with variable declarations.
68
+ - Name resources and variables for what they are (`aws_db_instance.orders`,
69
+ not `aws_db_instance.db1`), and give every variable a `description` and,
70
+ where one exists, a `type` constraint — an untyped variable defers a
71
+ typo to `apply` time instead of `plan`/`validate` time.
72
+ - Prefer a module for any resource group reused across environments
73
+ (`modules/network`, `modules/service`) over copy-pasted `.tf` blocks per
74
+ environment directory.
@@ -0,0 +1,81 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/Dockerfile", "**/Dockerfile.*", "**/*.Dockerfile", "**/*.dockerfile", "**/Containerfile", "**/docker-compose*.yml", "**/docker-compose*.yaml", "**/compose.yml", "**/compose.yaml", "k8s/**/*.yaml", "k8s/**/*.yml", "manifests/**/*.yaml", "manifests/**/*.yml", "deploy/**/*.yaml", "deploy/**/*.yml", "overlays/**/*.yaml", "**/kustomization.yaml", "**/kustomization.yml", "helm/**/*.yaml", "helm/**/templates/*.yaml", "charts/**/*.yaml", "charts/**/templates/*.yaml", "charts/*/values.yaml", "**/*.tf", "**/*.tfvars"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # Docker / Kubernetes / Terraform patterns
9
+
10
+ Structural patterns for building, orchestrating, and provisioning
11
+ containerized workloads with this pack's three file types. Complements
12
+ `rules/coding-style.mdc` (formatting/organization) with the shape decisions
13
+ that are harder to fix after the fact.
14
+
15
+ ## Multi-stage build as the default shape
16
+
17
+ A build stage that needs a compiler, a package manager's dev dependencies,
18
+ or test tooling should never be the stage that ships. Structure:
19
+
20
+ ```dockerfile
21
+ FROM node:22-slim AS build
22
+ WORKDIR /app
23
+ COPY package.json package-lock.json ./
24
+ RUN npm ci
25
+ COPY . .
26
+ RUN npm run build && npm run test
27
+
28
+ FROM node:22-slim AS runtime
29
+ WORKDIR /app
30
+ COPY --from=build /app/dist ./dist
31
+ COPY --from=build /app/node_modules ./node_modules
32
+ CMD ["node", "dist/index.js"]
33
+ ```
34
+
35
+ The `runtime` stage never sees the source tree's dev dependencies, test
36
+ files, or build toolchain — only what `COPY --from=build` explicitly
37
+ carries over.
38
+
39
+ ## Config and code stay separate
40
+
41
+ A Kubernetes `ConfigMap`/`Secret` or a Terraform variable exists so the same
42
+ image/module deploys to more than one environment without a rebuild or a
43
+ hand edit. A hardcoded hostname, replica count, or connection string inside
44
+ a Deployment template or a `.tf` resource block is a sign the value belongs
45
+ in a `values.yaml`, a `ConfigMap`, or a `variable` block instead.
46
+
47
+ ## Least-privilege as the default, not an add-on
48
+
49
+ - A container's Dockerfile ends with a non-root `USER` instruction (or the
50
+ base image's own non-root default is kept, not overridden back to root)
51
+ — see `rules/security.mdc` for the full non-root/capability rationale.
52
+ - A Kubernetes workload requests only the RBAC verbs/resources it actually
53
+ calls — a ServiceAccount bound to a namespace-wide `edit`/`cluster-admin`
54
+ role because the specific permission set was not worked out is a pattern
55
+ to flag, not a shortcut to accept.
56
+ - A Terraform IAM/role resource is scoped to the specific actions and
57
+ resource ARNs the workload needs, not a wildcard `"*"` action or
58
+ resource granted because narrowing it took more iteration.
59
+
60
+ ## Idempotent, declarative infrastructure
61
+
62
+ - A Terraform module should be safely re-appliable: no `null_resource` or
63
+ `local-exec` provisioner standing in for a resource Terraform's provider
64
+ already models directly, and no resource whose creation depends on
65
+ something outside Terraform's own state (a manually-created bucket
66
+ referenced by name instead of imported or created by the module).
67
+ - A Kubernetes manifest describes the desired end state (replica count,
68
+ image tag, resource limits) rather than an imperative sequence of
69
+ `kubectl` commands baked into a CI script — prefer `kubectl apply -f`
70
+ (or a GitOps controller) over a hand-run sequence of `create`/`patch`
71
+ commands that drift from the checked-in manifest.
72
+
73
+ ## Module and environment layout
74
+
75
+ Prefer one Terraform module per reusable resource group (`modules/vpc`,
76
+ `modules/eks-cluster`) instantiated per environment (`environments/staging`,
77
+ `environments/production`) with environment-specific variables, over a
78
+ single flat set of `.tf` files gated by `count`/`var.environment ==
79
+ "production"` conditionals scattered through the resources themselves — the
80
+ conditional form makes it easy for one environment's change to silently
81
+ affect another's plan.
@@ -0,0 +1,146 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/Dockerfile", "**/Dockerfile.*", "**/*.Dockerfile", "**/*.dockerfile", "**/Containerfile", "**/docker-compose*.yml", "**/docker-compose*.yaml", "**/compose.yml", "**/compose.yaml", "k8s/**/*.yaml", "k8s/**/*.yml", "manifests/**/*.yaml", "manifests/**/*.yml", "deploy/**/*.yaml", "deploy/**/*.yml", "overlays/**/*.yaml", "**/kustomization.yaml", "**/kustomization.yml", "helm/**/*.yaml", "helm/**/templates/*.yaml", "charts/**/*.yaml", "charts/**/templates/*.yaml", "charts/*/values.yaml", "**/*.tf", "**/*.tfvars"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # Docker / Kubernetes / Terraform security
9
+
10
+ Narrows `core-common-rules`' stack-agnostic security rules to the risks
11
+ specific to building images, running them on Kubernetes, and provisioning
12
+ infrastructure with Terraform. This skill's scope is the source files
13
+ (Dockerfile, compose, manifests, `.tf`) — a built image's own vulnerability
14
+ scan is `security-audit`'s job, not this rule's.
15
+
16
+ ## Non-root by default
17
+
18
+ - Every Dockerfile either ends with a `USER` instruction naming a non-root
19
+ user (one created with `RUN useradd`, or the base image's own built-in
20
+ unprivileged user re-selected explicitly, e.g. `USER nginx`), or keeps a
21
+ base image's own non-root default un-overridden. A missing `USER`
22
+ instruction is not by itself proof of root — some base images (e.g.
23
+ `nginxinc/nginx-unprivileged`) already default to a non-root user with
24
+ no `USER` line needed — but it is not proof of non-root either; confirm
25
+ what the base image's own default actually is (its Dockerfile/docs, or
26
+ `docker run --rm <image> whoami`) rather than assuming either way from
27
+ the absence of a `USER` line alone.
28
+ - The matching Kubernetes `securityContext` sets `runAsNonRoot: true`,
29
+ `allowPrivilegeEscalation: false`, and drops all capabilities
30
+ (`capabilities.drop: ["ALL"]`), adding back only the specific ones the
31
+ container actually needs (e.g. `NET_BIND_SERVICE` to bind a low port).
32
+ A `privileged: true` container is a last resort that needs its own
33
+ justification in the change, never a default reached for to make a
34
+ permission error disappear.
35
+ - Set `readOnlyRootFilesystem: true` where the workload allows it, mounting
36
+ a scoped `emptyDir`/`tmpfs` for the specific paths that need to be
37
+ writable at runtime (`/tmp`, a cache directory) rather than leaving the
38
+ whole filesystem writable because one path needs to be.
39
+
40
+ ## Pin base images by digest, not tag alone
41
+
42
+ A mutable tag (`FROM node:22`, `FROM node:22-slim`) can point at a
43
+ different image tomorrow than it does today — the base image is not
44
+ actually pinned until the digest is. For anything security-sensitive
45
+ (a production image, a CI build step that must be reproducible), pin with
46
+ the immutable digest:
47
+
48
+ ```dockerfile
49
+ FROM node:22-slim@sha256:<digest>
50
+ ```
51
+
52
+ A tag alone is acceptable for local/throwaway development; flag a
53
+ digest-less `FROM` in a Dockerfile that is clearly meant to ship.
54
+
55
+ ## Never bake a secret into an image layer
56
+
57
+ `ARG`/`ENV` values, and anything `COPY`'d into an image, persist in that
58
+ layer even if a later layer deletes or overwrites the file — a secret
59
+ baked in during an early `RUN`/`COPY` is still recoverable from the image's
60
+ layer history. Use BuildKit's build-time secret mount instead, which never
61
+ writes the secret into a layer:
62
+
63
+ ```dockerfile
64
+ # syntax=docker/dockerfile:1
65
+ RUN --mount=type=secret,id=npm_token \
66
+ NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci
67
+ ```
68
+
69
+ Runtime secrets (a database password, an API key a running container
70
+ needs) belong in a Kubernetes `Secret` (or the platform's secret store),
71
+ mounted as a file or environment variable at run time — never baked into
72
+ the image `ENV` at build time.
73
+
74
+ ## Kubernetes network boundaries
75
+
76
+ A namespace with no `NetworkPolicy` allows every pod to reach every other
77
+ pod by default — that is Kubernetes' actual default, not a hardened one.
78
+ Start from a default-deny policy and open only the specific paths a
79
+ workload needs:
80
+
81
+ ```yaml
82
+ apiVersion: networking.k8s.io/v1
83
+ kind: NetworkPolicy
84
+ metadata:
85
+ name: default-deny
86
+ spec:
87
+ podSelector: {}
88
+ policyTypes: ["Ingress", "Egress"]
89
+ ```
90
+
91
+ then a second, narrower `NetworkPolicy` per workload allowing only the
92
+ `podSelector`/`namespaceSelector` traffic it actually needs. Flag a
93
+ namespace shipping workload manifests with no accompanying `NetworkPolicy`
94
+ at all, and flag a per-workload `NetworkPolicy` broad enough to defeat the
95
+ point of having one -- a `spec.ingress` entry that is a single EMPTY rule
96
+ object (`ingress: [{}]`, no `from`/`ports` inside it) matches every source
97
+ on every port, i.e. allow-all, not the deny-all the pattern above depends
98
+ on (NetworkPolicy is fail-closed: `policyTypes: ["Ingress"]` with `ingress`
99
+ omitted or `ingress: []` means NO ingress rule matches, which is deny-all
100
+ -- do not confuse an EMPTY `ingress:` array with an array containing one
101
+ EMPTY rule object; they are opposite policies), or a `podSelector: {}` on a
102
+ policy meant to be workload-scoped (matches every pod in the namespace,
103
+ not just the intended workload).
104
+
105
+ ## Terraform state and secrets
106
+
107
+ - Configure a remote backend with encryption at rest and state locking
108
+ (e.g. an S3 backend with `encrypt = true` and `use_lockfile = true` for
109
+ native S3-object-based locking — the current recommended approach,
110
+ superseding a separate DynamoDB lock table for new configurations,
111
+ though an existing DynamoDB-locked backend is not itself a problem to
112
+ flag on its own; or an HCP Terraform/Terraform Cloud workspace, which
113
+ encrypts state and locks automatically) — a local `terraform.tfstate` is
114
+ both unencrypted on disk and a single point of loss/corruption with no
115
+ locking against concurrent runs.
116
+ - Renaming or moving a resource/module block in configuration changes its
117
+ address, which makes an unrelated `terraform plan` show a destroy-then-
118
+ recreate for a resource nothing actually changed about (state still has
119
+ it under the old address) — fix with a `moved` block (`moved { from =
120
+ <old_address> to = <new_address> }`, declarative, stays in the
121
+ configuration for the next person who reads it) or the equivalent
122
+ `terraform state mv <old_address> <new_address>` (imperative, a one-off
123
+ CLI operation) — never by applying the destroy/recreate plan as-is for a
124
+ resource holding real data.
125
+ - A `force_destroy = true` on a resource that can hold data (an S3 bucket,
126
+ in particular) lets `terraform destroy`/a replacement delete a
127
+ non-empty bucket without the safety error Terraform would otherwise
128
+ raise — flag it on any resource whose data matters, especially paired
129
+ with a `moved`/`state mv` scenario where the goal is to KEEP the
130
+ existing data, not clear the way for a fresh create.
131
+ - Never commit a `.tfstate`/`.tfstate.backup` file to version control —
132
+ state routinely contains resource attributes in plaintext (a generated
133
+ password, a connection string) even when the variable that produced them
134
+ was declared `sensitive`.
135
+ - Mark any variable or output that carries a credential, connection
136
+ string, or key `sensitive = true` so Terraform redacts it from CLI plan
137
+ output and logs — but treat `sensitive = true` as a **display**
138
+ protection, not an encryption one: the value is still written to state
139
+ in plain form, and `terraform output <name>` still prints a sensitive
140
+ output's value by name. Encryption-at-rest for that value comes from the
141
+ backend configuration above, not from the `sensitive` flag.
142
+ - Never hardcode a credential directly in a `.tf`/`.tfvars` file — source
143
+ it from a secrets manager data source (Vault, AWS Secrets Manager/SSM
144
+ Parameter Store) or an injected environment variable, the same rule
145
+ `core-common-rules` already states for application source code, applied
146
+ here to infrastructure code.
@@ -0,0 +1,61 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/Dockerfile", "**/Dockerfile.*", "**/*.Dockerfile", "**/*.dockerfile", "**/Containerfile", "**/docker-compose*.yml", "**/docker-compose*.yaml", "**/compose.yml", "**/compose.yaml", "k8s/**/*.yaml", "k8s/**/*.yml", "manifests/**/*.yaml", "manifests/**/*.yml", "deploy/**/*.yaml", "deploy/**/*.yml", "overlays/**/*.yaml", "**/kustomization.yaml", "**/kustomization.yml", "helm/**/*.yaml", "helm/**/templates/*.yaml", "charts/**/*.yaml", "charts/**/templates/*.yaml", "charts/*/values.yaml", "**/*.tf", "**/*.tfvars"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # Docker / Kubernetes / Terraform validation
9
+
10
+ This pack ships no separate `test` skill yet (see `pack.json`'s
11
+ `notes.test` — genuine test-writing concepts exist for this family,
12
+ `terraform test`/`.tftest.hcl` and `helm test`/helm-unittest chief among
13
+ them; the deferral is a scope decision for this batch, not a claim that no
14
+ such concept exists). What this rule covers instead is VALIDATION: a
15
+ static, tool-driven check that an existing file is well-formed and
16
+ schema-correct, exercised from `docker-k8s-terraform-build-fix`'s own
17
+ reproduce-and-classify step. This rule states what "validated" means for
18
+ each file type so that step — and any manual check — has a concrete bar.
19
+
20
+ ## Dockerfile and Compose
21
+
22
+ - `hadolint <Dockerfile>` when the project has hadolint configured (a
23
+ `.hadolint.yaml` present, or the tool available on PATH) — catches
24
+ missing pins, `apt-get` without `--no-install-recommends`, and other
25
+ Dockerfile-specific lint findings before a build is even attempted.
26
+ - `docker build .` (or `docker compose build` for a multi-service project)
27
+ must exit 0 with no `--no-cache` needed to reproduce a passing state —
28
+ a build that only succeeds from a stale cache is not actually validated.
29
+ - For a compose project, `docker compose config` validates the merged
30
+ Compose file parses and every referenced `.env` variable resolves,
31
+ before attempting `up`.
32
+
33
+ ## Kubernetes and Helm
34
+
35
+ - `kubeconform -strict -summary <manifest-or-dir>` (or `kubeval`, whichever
36
+ the project already has configured) validates every manifest against the
37
+ Kubernetes API schema for the cluster's target version — a manifest that
38
+ parses as YAML can still fail this (unknown field, wrong type) and
39
+ should not be treated as validated on YAML-parse success alone.
40
+ - `helm template <chart> | kubeconform -strict` for a Helm chart — template
41
+ rendering can itself fail (a missing `values.yaml` key with no default)
42
+ independently of the rendered manifests' own schema validity, so both
43
+ failure modes need to be distinguished when reporting a finding.
44
+ - `kubectl apply --dry-run=server -f <manifest>` (or `--dry-run=client` when
45
+ no live cluster is reachable) catches a resource the API server would
46
+ reject that schema validation alone does not (an immutable field on an
47
+ existing resource, a missing CRD).
48
+
49
+ ## Terraform
50
+
51
+ - `terraform fmt -check` and `terraform validate` are the syntax/internal-
52
+ consistency floor — both must pass before a `terraform plan` is
53
+ attempted, since `validate` catches type errors and missing required
54
+ arguments that `plan` would otherwise fail on less legibly.
55
+ - `terraform plan` (never `apply` from this pack's skills) shows the actual
56
+ proposed change; a plan showing an unexpected destroy/replace on a
57
+ resource the change was not meant to touch is a validation failure in
58
+ its own right, not just a formality to skip past.
59
+ - A policy check (`conftest`/OPA, or the project's own policy tool) when
60
+ one is configured — treat its findings the same as a schema-validator
61
+ finding, not as optional advice to skip under time pressure.
@@ -0,0 +1,151 @@
1
+ ---
2
+ name: docker-k8s-terraform-build-fix
3
+ description: "Use when a Docker build fails, a Kubernetes/Helm manifest is rejected by validation or the API server, or a Terraform validate/plan fails -- resolves the actual root cause (a bad COPY path, an ownership/permission mismatch after switching to a non-root USER, a schema-invalid manifest, a Terraform state/address mismatch) with the smallest fix, never a suppression. Not for an application-code build failure unrelated to these config files (a Go/TypeScript/Python compile or test error) -- that is the language's own build-fix skill's territory, even when this pack's Dockerfile happens to build that language's code."
4
+ triggers:
5
+ - "docker build is failing"
6
+ - "fix this Kubernetes manifest validation error"
7
+ - "terraform plan wants to destroy and recreate this resource"
8
+ - "terraform validate is failing"
9
+ - "this Dockerfile permission denied error"
10
+ - "kubectl apply is rejecting this manifest"
11
+ metadata:
12
+ origin: authored
13
+ category: build-fix
14
+ version: "1.0.0"
15
+ compatible_harnesses: "claude,codex,cursor,zed,opencode"
16
+ license: "MIT"
17
+ ---
18
+
19
+ # Docker / Kubernetes / Terraform build fix
20
+
21
+ Resolve a `docker build` failure, a Kubernetes/Helm manifest rejected by
22
+ schema validation or the API server, or a `terraform validate`/`plan`
23
+ failure — with the smallest change that fixes the actual root cause.
24
+ `rules/coding-style.mdc` and `rules/security.mdc` govern what a "correct"
25
+ fix looks like; this skill never reaches for a suppression or a shortcut
26
+ that undoes a prior security fix (reverting to root, disabling a lint rule,
27
+ forcing an unreviewed apply) instead of fixing what actually broke.
28
+
29
+ ## Workflow
30
+
31
+ ### Step 1: Reproduce and classify
32
+
33
+ ```bash
34
+ docker build . # or: docker compose build
35
+ kubeconform -strict -summary <manifest> # or: kubectl apply --dry-run=server -f <manifest>
36
+ terraform validate && terraform plan
37
+ ```
38
+
39
+ Read the exact error text and classify it:
40
+
41
+ - **Docker build error** (a `COPY`/`ADD` path that does not exist in the
42
+ build context, a missing `.dockerignore` exclusion breaking a needed
43
+ file, a package-manager install failure, a permission-denied write after
44
+ switching to a non-root `USER`).
45
+ - **Kubernetes/Helm validation error** (an unknown field, a wrong type, a
46
+ Helm template rendering failure from a missing `values.yaml` key, or a
47
+ live API-server rejection such as an immutable field on an existing
48
+ resource).
49
+ - **Terraform error** (a type mismatch or missing required argument
50
+ `validate` catches, a provider/version constraint conflict, or a `plan`
51
+ showing an unexpected destroy/replace after a resource rename or
52
+ refactor).
53
+
54
+ ### Step 2: Fix by category
55
+
56
+ **Docker build error:** for a missing `COPY`/`ADD` source, check the build
57
+ context and `.dockerignore` before assuming the file is genuinely absent —
58
+ a `.dockerignore` entry that is too broad is a common real cause. For a
59
+ permission-denied write after a `USER` switch to a non-root user, fix
60
+ ownership at copy time (`COPY --chown=<user>:<group> ...`) or with an
61
+ explicit `RUN chown`/`mkdir -p` step **before** the `USER` instruction
62
+ switches away from root — never fix it by reverting to `USER root` or by
63
+ running `chmod 777` on the directory; both undo the non-root hardening
64
+ this pack's security rule requires instead of fixing the actual ownership
65
+ mismatch.
66
+
67
+ **Kubernetes/Helm validation error:** for a schema error, fix the field
68
+ name/type the validator names — do not delete the offending field to make
69
+ the validator stop complaining if the field is actually required for the
70
+ workload to function correctly. For a live API-server rejection on an
71
+ immutable field (e.g. a Deployment's selector), the fix is usually to
72
+ recreate the resource under change management (a new name, or a deliberate
73
+ delete+recreate the team has agreed to), not to strip the field or force
74
+ an update that the API server is correctly refusing.
75
+
76
+ **Terraform error:** for a type/argument error, fix the resource/variable
77
+ declaration `validate` names. For an unexpected destroy+recreate after a
78
+ rename or a module refactor, relink the existing real resource to its new
79
+ configuration address — either a `moved` block (`moved { from =
80
+ <old_address> to = <new_address> }`, declarative, stays in the
81
+ configuration) or the equivalent `terraform state mv <old_address>
82
+ <new_address>` (imperative, a one-off CLI operation) — never accept the
83
+ destroy+recreate plan with `-auto-approve` (or any unreviewed apply) for a
84
+ stateful resource just to make the plan "go through"; that actually
85
+ destroys and rebuilds the real infrastructure the rename was never meant
86
+ to touch.
87
+
88
+ ### Step 3: Verify
89
+
90
+ ```bash
91
+ docker build . # or: docker compose build
92
+ kubeconform -strict -summary <manifest>
93
+ terraform validate && terraform plan
94
+ ```
95
+
96
+ Re-run the specific command whose failure this fix addresses, plus any
97
+ project-configured validator (`hadolint`, `helm template | kubeconform`).
98
+ All must exit 0 — and for the Terraform case, `terraform plan` must show
99
+ no destroy/replace on the resource the fix was meant to preserve — before
100
+ reporting done.
101
+
102
+ ### Step 4: Report
103
+
104
+ ```
105
+ Fixed: Dockerfile permission-denied write to /app after switching to
106
+ USER app
107
+ - Root cause: files were COPY'd while still root, so `app` had no
108
+ write access to /app
109
+ - Fix: added --chown=app:app to the COPY instruction (no chmod, no
110
+ reverting to root)
111
+ - docker build . passes
112
+ ```
113
+
114
+ State the root cause in one sentence, not just "fixed the error."
115
+
116
+ ## Rules
117
+
118
+ - Find and fix the smallest change that addresses the actual root cause —
119
+ never widen a fix beyond what the failure requires.
120
+ - NEVER fix a permission-denied error by running `chmod 777` or reverting
121
+ to `USER root` — fix ownership at the actual `COPY`/`RUN` step instead.
122
+ - NEVER accept an unexpected Terraform destroy/replace with
123
+ `-auto-approve` (or any unreviewed apply) — use a `moved` block or
124
+ `terraform state mv` (or another state-preserving fix) when the
125
+ resource itself was not meant to change.
126
+ - NEVER delete a required Kubernetes manifest field just to make a
127
+ validator stop complaining.
128
+ - NEVER delete or skip a failing validation step to reach a green build.
129
+
130
+ ## Red Flags
131
+
132
+ | Rationalization | Why it is wrong |
133
+ |---|---|
134
+ | "I'll just `chmod 777` the directory, it's only a build image" | Undoes the non-root hardening this pack's security rule requires and grants write access to everyone, not just the user that actually needs it; fix ownership at the COPY/RUN step instead |
135
+ | "The rename makes Terraform want to destroy and recreate it, but the resource is basically the same, so `-auto-approve` should be fine" | Terraform's plan is not wrong about what it will do — accepting an unreviewed destroy+recreate on a stateful resource can genuinely lose data; a `moved` block or `terraform state mv` fixes the address without touching real infrastructure |
136
+ | "This field keeps failing validation, I'll just remove it" | If the field is actually required for the workload (a probe, a resource limit), removing it trades a caught validation error for a runtime failure later; fix the field's value instead |
137
+ | "I'll switch back to `USER root` just to unblock this build, we can fix permissions properly later" | Reintroduces a root container to solve an ownership problem that a scoped `--chown`/`chown` step solves without giving up the non-root hardening |
138
+
139
+ ## Verification
140
+
141
+ Do not report the fix done until all of the following hold:
142
+
143
+ - The specific failing command (`docker build`, `kubeconform`/`kubectl
144
+ apply --dry-run`, `terraform validate`/`plan`) now exits 0.
145
+ - For a Terraform fix: `terraform plan` shows no destroy/replace on the
146
+ resource the fix was meant to preserve.
147
+ - The change is the smallest one that addresses the stated root cause —
148
+ no unrelated files touched, and no security hardening (non-root user,
149
+ digest pin, `NetworkPolicy`) reverted to reach a green build.
150
+ - The report states the root cause in one sentence, not just "build now
151
+ passes."