@cratis/pi 7.101.0 → 7.103.0
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.
- package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +17 -0
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
- package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
- package/package/corpus/hooks/README.md +11 -1
- package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
- package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
- package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
- package/package/corpus/rules/github-actions.md +17 -0
- package/package/corpus/rules/profiles.md +10 -1
- package/package/corpus/rules/pull-requests.md +2 -0
- package/package/corpus/rules/secrets.md +4 -0
- package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
- package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
- package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
- package/package/corpus/skills/binlog-generation/LICENSE +23 -0
- package/package/corpus/skills/binlog-generation/NOTICE +22 -0
- package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
- package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
- package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
- package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
- package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
- package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
- package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
- package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
- package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
- package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
- package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
- package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
- package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
- package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
- package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/LICENSE +2 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +201 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md +158 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/caching.md +86 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md +70 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/publish-pipelines.md +67 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/required-checks-and-path-filters.md +117 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/runner-sizing-and-images.md +65 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/security-and-pinning.md +73 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/sources.md +33 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +82 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/ci-timings.sh +356 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1003.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1007.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1010.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/runs.json +102 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +244 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/docker-images.md +86 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dotnet-nuget.md +87 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/go-jvm-python-elixir.md +83 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/npm-yarn.md +78 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/other-environments.md +60 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +97 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/cratis-local-packages.sh +778 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/npm-restore-check.cjs +109 -0
- package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
- package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
- package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
- package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
- package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
- package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
- package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
- package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
- package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
- package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
- package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
- package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
- package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
- package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
- package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
- package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
- package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
- package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
- package/package/corpus/skills/dump-collect/LICENSE +23 -0
- package/package/corpus/skills/dump-collect/NOTICE +40 -0
- package/package/corpus/skills/dump-collect/SKILL.md +97 -0
- package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
- package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
- package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
- package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
- package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
- package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
- package/package/corpus/skills/extension-points/LICENSE +23 -0
- package/package/corpus/skills/extension-points/NOTICE +18 -0
- package/package/corpus/skills/extension-points/SKILL.md +248 -0
- package/package/corpus/skills/filter-syntax/LICENSE +23 -0
- package/package/corpus/skills/filter-syntax/NOTICE +17 -0
- package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
- package/package/corpus/skills/including-generated-files/LICENSE +23 -0
- package/package/corpus/skills/including-generated-files/NOTICE +17 -0
- package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
- package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
- package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
- package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
- package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
- package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
- package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
- package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
- package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
- package/package/corpus/skills/msbuild/LICENSE +23 -0
- package/package/corpus/skills/msbuild/NOTICE +55 -0
- package/package/corpus/skills/msbuild/SKILL.md +117 -0
- package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
- package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
- package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
- package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
- package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
- package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
- package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
- package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
- package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
- package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
- package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
- package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
- package/package/corpus/skills/orleans/LICENSE +21 -0
- package/package/corpus/skills/orleans/NOTICE +64 -0
- package/package/corpus/skills/orleans/SKILL.md +140 -0
- package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
- package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
- package/package/corpus/skills/orleans/references/examples.md +66 -0
- package/package/corpus/skills/orleans/references/grain-api.md +784 -0
- package/package/corpus/skills/orleans/references/grains.md +75 -0
- package/package/corpus/skills/orleans/references/hosting.md +71 -0
- package/package/corpus/skills/orleans/references/implementation.md +591 -0
- package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
- package/package/corpus/skills/orleans/references/patterns.md +811 -0
- package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
- package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
- package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
- package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
- package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
- package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
- package/package/corpus/skills/platform-detection/LICENSE +23 -0
- package/package/corpus/skills/platform-detection/NOTICE +17 -0
- package/package/corpus/skills/platform-detection/SKILL.md +230 -0
- package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
- package/package/corpus/skills/profiling/LICENSE +21 -0
- package/package/corpus/skills/profiling/NOTICE +50 -0
- package/package/corpus/skills/profiling/SKILL.md +144 -0
- package/package/corpus/skills/profiling/references/commands.md +439 -0
- package/package/corpus/skills/profiling/references/patterns.md +586 -0
- package/package/corpus/skills/profiling/references/profiling.md +190 -0
- package/package/corpus/skills/run-tests/LICENSE +23 -0
- package/package/corpus/skills/run-tests/NOTICE +19 -0
- package/package/corpus/skills/run-tests/SKILL.md +305 -0
- package/package/corpus/skills/target-authoring/LICENSE +23 -0
- package/package/corpus/skills/target-authoring/NOTICE +17 -0
- package/package/corpus/skills/target-authoring/SKILL.md +165 -0
- package/package/corpus/skills/vectorization/LICENSE +23 -0
- package/package/corpus/skills/vectorization/NOTICE +17 -0
- package/package/corpus/skills/vectorization/SKILL.md +189 -0
- package/package/profile-catalog.json +87 -1
- package/package.json +3 -2
|
@@ -45,6 +45,23 @@ paths:
|
|
|
45
45
|
`cancel-in-progress: true`, keyed `${{ github.workflow }}-${{ github.ref }}`.
|
|
46
46
|
**Never** cancel-in-progress on publish, release, or deploy workflows — a cancelled
|
|
47
47
|
half-publish is worse than a queued one.
|
|
48
|
+
- **Publish and release workflows never cancel** (`cancel-in-progress: false`) and set
|
|
49
|
+
`queue: max`. A group holds one running and one pending run; a newer arrival replaces
|
|
50
|
+
the pending one. Merges into the default branch share a group (a push, a repository-wide
|
|
51
|
+
key, or `github.ref` on a merged `pull_request` `closed` event, which is the base
|
|
52
|
+
branch), so without `queue: max` a newer merge replaces the older pending release.
|
|
53
|
+
`queue: max` keeps up to 100 pending runs, processed in order, and applies only while
|
|
54
|
+
`cancel-in-progress` is false or unset. Keep a single pending slot only for a documented
|
|
55
|
+
design where each run releases everything since the last release, so a replaced run
|
|
56
|
+
loses nothing; say so in a comment beside the `concurrency` block.
|
|
57
|
+
- **A required check must not be skippable by `paths:` alone.** A workflow that never
|
|
58
|
+
starts never reports, and the pull request hangs. Gate jobs on a change-detection job
|
|
59
|
+
and require one always-run aggregator job (`if: always()`, checks every `needs.*.result`).
|
|
60
|
+
- **Pin third-party actions by full commit SHA** (version in a comment) and set minimal
|
|
61
|
+
`permissions:` on each job. Run `zizmor` locally on edited workflows: its `unpinned-uses`
|
|
62
|
+
and `excessive-permissions` audits check exactly this. `actionlint` checks syntax,
|
|
63
|
+
expressions and embedded shell, not pinning. Do not assume CI runs either tool.
|
|
64
|
+
- To make a workflow faster, use the `cratis-ci-pipeline-performance` skill.
|
|
48
65
|
|
|
49
66
|
## Matrices and operating systems
|
|
50
67
|
|
|
@@ -87,11 +87,20 @@ profile includes neither Screenplay nor Stage.
|
|
|
87
87
|
| Profile ID | Description | Automatically Includes |
|
|
88
88
|
|---|---|---|
|
|
89
89
|
| `cratis/engineering` | Engineering conventions and workflows | `cratis/engineering/core`, `cratis/engineering/csharp`, `cratis/engineering/elixir`, `cratis/engineering/go`, `cratis/engineering/kotlin`, `cratis/engineering/react`, `cratis/engineering/typescript` |
|
|
90
|
-
| `cratis/engineering/csharp` | C# engineering conventions and framework Orleans jobs (`cratis-orleans-jobs`, alongside `orleans.md`) | `cratis/engineering/core` |
|
|
90
|
+
| `cratis/engineering/csharp` | C# engineering conventions, .NET engineering skills, and framework Orleans jobs (`cratis-orleans-jobs`, alongside `orleans.md`) | `cratis/engineering/core`, `cratis/engineering/dotnet` |
|
|
91
|
+
| `cratis/engineering/dotnet` | Upstream .NET engineering workflows | `cratis/engineering/dotnet/performance`, `cratis/engineering/dotnet/diagnostics`, `cratis/engineering/dotnet/msbuild`, `cratis/engineering/dotnet/orleans`, `cratis/engineering/dotnet/testing`, `cratis/engineering/dotnet/refactoring` |
|
|
92
|
+
| `cratis/engineering/dotnet/performance` | Microbenchmarking, performance patterns, SIMD, and profiling | (no child profiles) |
|
|
93
|
+
| `cratis/engineering/dotnet/diagnostics` | Trace and crash-dump collection | (no child profiles) |
|
|
94
|
+
| `cratis/engineering/dotnet/msbuild` | MSBuild troubleshooting, build authoring, output paths, and binlogs | (no child profiles) |
|
|
95
|
+
| `cratis/engineering/dotnet/orleans` | Orleans primitives, hosting, persistence, and runtime behavior | (no child profiles) |
|
|
96
|
+
| `cratis/engineering/dotnet/testing` | Test-platform detection, runner commands, filters, and coverage | (no child profiles) |
|
|
97
|
+
| `cratis/engineering/dotnet/refactoring` | Behavior-preserving C# refactoring and AOT compatibility | (no child profiles) |
|
|
91
98
|
| `cratis/engineering/go` | Go framework parity and immutable module releases | `cratis/engineering/core`, `cratis/language/go` |
|
|
92
99
|
| `cratis/engineering/typescript` | TypeScript engineering conventions | `cratis/engineering/core` |
|
|
93
100
|
| `cratis/engineering/react` | React engineering conventions | `cratis/engineering/core` |
|
|
94
101
|
|
|
102
|
+
The `cratis/engineering/dotnet` profiles carry upstream skills under MIT, with attribution and changes in each skill's NOTICE. Cratis rules and the `cratis-*` skills win where they differ.
|
|
103
|
+
|
|
95
104
|
The `cratis/engineering` profile also carries [issue-assignment.md](../../../rules/issue-assignment.md): when an agent starts work on a numbered issue in a known repository and nobody is assigned, it assigns the current `gh` user.
|
|
96
105
|
|
|
97
106
|
### Language Profiles
|
|
@@ -9,6 +9,8 @@ applyTo: "**/*"
|
|
|
9
9
|
|
|
10
10
|
**The PR description is published verbatim as the GitHub release. In repositories released by `cratis/release-action`, it also closes every issue written as `(#n)`; elsewhere, follow the repository's own release and issue-closing process.** Write the note the person upgrading should read, not a development write-up. The same contract applies to release notes typed into a manual workflow run or written straight into a release. Where it is installed, the `policy / verify` check enforces it on release-bound PRs into the default branch carrying `major`/`minor`/`patch` (it re-runs when the label is added); its release-note step warns on `no-release` and unlabelled PRs, which must still be written this way because any of them may become releasable. `release-action` does not run for a `no-release` PR, so `(#n)` closes nothing there. Editing the description re-runs the check. HTML comments are not shown on the release page and the check ignores them for headings, keywords and placeholders, and `release-action` removes them before publishing, so a `(#n)` only inside a comment closes nothing; the check fails it, so never put an issue reference in a comment. The template's own comment can stay: its `(#123)` examples are inline code, and the comment is not published.
|
|
11
11
|
|
|
12
|
+
**A change to a hook script or its wiring (`.cratis/ai/hooks/**`) needs a release-note line that tells consumers to run `cratis ai update`; a hook fix does not reach a repository until they do.**
|
|
13
|
+
|
|
12
14
|
### Allowed shape
|
|
13
15
|
|
|
14
16
|
- Follow the repository's pull request template (`.github/pull_request_template.md`) within this contract.
|
|
@@ -21,6 +21,10 @@ Every line is tagged **[contract]** (binding) per the Three Levels of Authority
|
|
|
21
21
|
- **[contract] Project the field you need and say which one:** `-o jsonpath='{.spec.containers[*].image}'`,
|
|
22
22
|
`-o custom-columns=…`, `kubectl get … -o name`, status fields, `kubectl logs`, `docker inspect --format`.
|
|
23
23
|
Read a `.env.example`, never the `.env`.
|
|
24
|
+
- **[contract] The hook reads command text only.** It cannot see a command piped into a shell
|
|
25
|
+
(`… | sh`, `bash -c "$(…)"`), a recursive search with no named file (`grep -r PASSWORD .`), or a
|
|
26
|
+
value reached through a variable, alias or script. These stay your rule to keep; output redaction
|
|
27
|
+
is the only backstop.
|
|
24
28
|
|
|
25
29
|
## Values
|
|
26
30
|
|
|
@@ -11,7 +11,7 @@ Three layers:
|
|
|
11
11
|
| Pattern pass | `PostToolUse` on a write | `scripts/cratis-pattern-scan.sh` | zero tokens until a match | appends a one-line reminder to context, never blocks |
|
|
12
12
|
| Hard block | `PreToolUse` on a write | `scripts/cratis-guard-writes.sh` | zero | exits **2** — the write does not happen |
|
|
13
13
|
| Hard block | `PreToolUse` on `Bash` | `scripts/cratis-guard-store-mutations.sh` | zero; parses only commands that mention `cratis` | exits **2** — the store-changing `cratis chronicle` command does not run |
|
|
14
|
-
| Hard block | `PreToolUse` on `Bash` | `scripts/cratis-guard-secrets.sh` | zero; parses only commands that mention a credential-bearing tool or file | exits **2** — a command that prints credentials (full `kubectl` specs and secrets, `env`/`printenv`, `docker inspect`, secret-manager reads, `.env`/kubeconfig/key files) does not run unless it projects a field; see [`secrets.md`](../rules/secrets.md) |
|
|
14
|
+
| Hard block | `PreToolUse` on `Bash` | `scripts/cratis-guard-secrets.sh` | zero; parses only commands that mention a credential-bearing tool or file | exits **2** — a command that prints credentials (full `kubectl` specs and secrets, `env`/`printenv`, `docker inspect`, secret-manager reads, `.env`/kubeconfig/key files; also behind `/usr/bin/env`, `command`, attached options such as `-ePATTERN`, a bare `{.}` projection) does not run unless it projects a field; piped shells and recursive searches with no named file stay uncovered; see [`secrets.md`](../rules/secrets.md) |
|
|
15
15
|
| Hard block | `PreToolUse` on `Bash` | `scripts/cratis-guard-pr-body.sh` | fetches shared rules only for `gh pr create/edit` | exits **2** for inline bodies, invalid notes or conflicting release labels; warns and allows when offline with no cache |
|
|
16
16
|
| Cleanup of delivered work | `PostToolUse` on `Bash` (`gh pr merge`, `git pull/merge/worktree`) and `SessionStart` (Pi: `tool_result` and `session_start`) | `scripts/cratis-cleanup-worktrees.sh --hook` | zero when the command is unrelated | never blocks; removes delivered worktrees and branches, reports what it kept |
|
|
17
17
|
| Quality gate (Claude) | `Stop` | `scripts/cratis-quality-gate.sh` | one build/test run, only when relevant files changed | exits **2** — the turn does not end |
|
|
@@ -252,6 +252,16 @@ execution, so it cannot validate a body that the command has yet to write, even
|
|
|
252
252
|
already exists. Dependabot's generated description remains exempt (including GitHub CLI's
|
|
253
253
|
`app/dependabot` bot identity), but its release-intent label is still checked.
|
|
254
254
|
|
|
255
|
+
A release that changes a hook script or its wiring must say in its release notes that consumers run
|
|
256
|
+
`cratis ai update`: hooks are managed files, so a fix reaches a repository only once it updates.
|
|
257
|
+
|
|
258
|
+
The direct checker exits non-zero, with remediation text, in a `github.com/Cratis/` repository whose
|
|
259
|
+
workflows hold no policy caller (none of the recognized callers above): without one, nothing enforces the
|
|
260
|
+
pull-request contract there, and a pass would claim otherwise. A repository outside Cratis still falls back to
|
|
261
|
+
the reviewed pin below, and the Bash guard stays a no-op where there is no caller. A contract spec in the AI corpus's own
|
|
262
|
+
verification runs the checker over vendored copies of the callers Cratis/Workflows publishes; with
|
|
263
|
+
`CRATIS_CONTRACT_LIVE=1` it compares them with Cratis/Workflows `main`.
|
|
264
|
+
|
|
255
265
|
Run the same checker directly:
|
|
256
266
|
|
|
257
267
|
```bash
|
|
@@ -71,6 +71,13 @@ function callers(cwd = process.cwd()) {
|
|
|
71
71
|
} catch { return []; }
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function missingCaller(repository) {
|
|
75
|
+
return `No Cratis/Workflows policy caller found for ${repository}, so its pull-request contract is not enforced and this check cannot vouch for it. `
|
|
76
|
+
+ 'Add .github/workflows/verify-pull-request-policy.yml calling Cratis/Workflows/.github/workflows/verify-pull-request-policy.yml '
|
|
77
|
+
+ '(a legacy verify-release-notes, verify-semver-label or verify-release-intent caller also counts), then run this check again. '
|
|
78
|
+
+ 'Cratis/Workflows bootstrap-common-workflows installs the caller; if the repository is meant to have none, do not use this check there.';
|
|
79
|
+
}
|
|
80
|
+
|
|
74
81
|
function repositoryName(value) {
|
|
75
82
|
return value?.replace(/^(?:https?:\/\/github\.com\/|(?:ssh:\/\/)?git@github\.com[:/])/i, '')
|
|
76
83
|
.replace(/^github\.com\//i, '').replace(/\/$/, '').replace(/\.git$/i, '');
|
|
@@ -216,7 +223,11 @@ function check(argv, repositoryCallers, lookupOptions) {
|
|
|
216
223
|
// The release-intent check above still applies to bot-authored pull requests.
|
|
217
224
|
if (author === 'dependabot[bot]') return 0;
|
|
218
225
|
const base = values.base || pull?.baseRefName || defaultBranch;
|
|
219
|
-
const
|
|
226
|
+
const found = repositoryCallers || targetCallers(repository, process.cwd(), warn);
|
|
227
|
+
// A Cratis repository without a policy caller has no enforced pull-request contract; passing here would
|
|
228
|
+
// let the author believe it has one. A fetch failure is not this case: targetCallers falls back to a caller.
|
|
229
|
+
if (!found.length && /^Cratis\/[^/]+$/i.test(repository)) throw new Error(missingCaller(repository));
|
|
230
|
+
const file = rules(warn, found);
|
|
220
231
|
if (!file) return 3;
|
|
221
232
|
const env = { PATH: process.env.PATH, HOME: homedir(), LANG: 'C.UTF-8',
|
|
222
233
|
PR_BODY: body, PR_LABELS: JSON.stringify([...labels]),
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
# Exit 2 blocks the tool call and sends stderr back to the model; every other command exits 0.
|
|
11
11
|
#
|
|
12
12
|
# What it sees is the command text. A credential reached through a variable, an alias, a function, a script
|
|
13
|
-
# file or a pipe into a shell is invisible to it
|
|
14
|
-
# `{.}`) is trusted.
|
|
13
|
+
# file or a pipe into a shell is invisible to it, as is a recursive search with no named file; a projection it
|
|
14
|
+
# cannot see through (a template or `-o jsonpath` that is not a bare `{.}`) is trusted. A redirect does not
|
|
15
|
+
# exempt anything: `env > f` is blocked like `env`. See .cratis/ai/hooks/README.md.
|
|
15
16
|
#
|
|
16
17
|
# Escape hatch for a human who authorized the read — set in the environment the agent harness was started
|
|
17
18
|
# from, never inside the command (an assignment in the command is ignored):
|
|
@@ -54,13 +55,26 @@ function base(word) { word = tolower(word); sub(/.*\//, "", word); return word }
|
|
|
54
55
|
|
|
55
56
|
# ── tokenizer: splits text into simple commands, queueing nested command text ──
|
|
56
57
|
function flush_word() {
|
|
57
|
-
if (inword) W[++nw] = word
|
|
58
|
-
word = ""; inword = 0
|
|
58
|
+
if (inword) { W[++nw] = word; WP[nw] = wplain }
|
|
59
|
+
word = ""; inword = 0; wquoted = 0; wplain = 0
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
+
# Output redirections are removed before a command is analyzed, so a redirect never shifts a CLI's subcommand
|
|
62
|
+
# or operands (`kubectl get > /dev/stdout secrets`). Nothing is exempted: the command itself is still analyzed.
|
|
63
|
+
# A word is a redirection only when its whole operator lies in the word's unquoted prefix (WP: characters before
|
|
64
|
+
# the first quoted or escaped one), so `1">"b` and `">x"` stay operands; input redirections (`< file`) stay too.
|
|
65
|
+
function end_cmd( k, m, w) {
|
|
61
66
|
flush_word()
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
m = 0
|
|
68
|
+
for (k = 1; k <= nw; k++) {
|
|
69
|
+
w = W[k]
|
|
70
|
+
if (match(w, /^([0-9]*|&)>[>|]?/) && RLENGTH <= WP[k]) {
|
|
71
|
+
if (RLENGTH == length(w)) k++
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
V[++m] = w
|
|
75
|
+
}
|
|
76
|
+
if (m > 0) analyze(V, 1, m)
|
|
77
|
+
split("", W); split("", WP); split("", V); nw = 0
|
|
64
78
|
}
|
|
65
79
|
function cmdsub(s, i, n, start, depth, c, j) {
|
|
66
80
|
start = i; depth = 1
|
|
@@ -97,23 +111,23 @@ function read_heredocs(s, i, n, h, e, line, cmp, body) {
|
|
|
97
111
|
return i
|
|
98
112
|
}
|
|
99
113
|
function tokenize(s, i, n, c, c2, j, dash, delim) {
|
|
100
|
-
n = length(s); i = 1; nw = 0; word = ""; inword = 0; nheredoc = 0; hd_shell = 0
|
|
101
|
-
split("", W)
|
|
114
|
+
n = length(s); i = 1; nw = 0; word = ""; inword = 0; wquoted = 0; wplain = 0; nheredoc = 0; hd_shell = 0
|
|
115
|
+
split("", W); split("", WP)
|
|
102
116
|
while (i <= n) {
|
|
103
117
|
c = substr(s, i, 1)
|
|
104
118
|
if (c == "\\") {
|
|
105
119
|
c2 = substr(s, i + 1, 1)
|
|
106
120
|
if (c2 == "\n") { i += 2; continue }
|
|
107
|
-
word = word c2; inword = 1; i += 2; continue
|
|
121
|
+
wquoted = 1; word = word c2; inword = 1; i += 2; continue
|
|
108
122
|
}
|
|
109
123
|
if (c == "\047") {
|
|
110
|
-
inword = 1
|
|
124
|
+
wquoted = 1; inword = 1
|
|
111
125
|
j = index(substr(s, i + 1), "\047")
|
|
112
126
|
if (j == 0) { word = word substr(s, i + 1); i = n + 1; continue }
|
|
113
127
|
word = word substr(s, i + 1, j - 1); i += j + 1; continue
|
|
114
128
|
}
|
|
115
129
|
if (c == "\"") {
|
|
116
|
-
inword = 1; i++
|
|
130
|
+
wquoted = 1; inword = 1; i++
|
|
117
131
|
while (i <= n) {
|
|
118
132
|
c = substr(s, i, 1)
|
|
119
133
|
if (c == "\\") { word = word substr(s, i + 1, 1); i += 2; continue }
|
|
@@ -128,9 +142,9 @@ function tokenize(s, i, n, c, c2, j, dash, delim) {
|
|
|
128
142
|
}
|
|
129
143
|
continue
|
|
130
144
|
}
|
|
131
|
-
if (c == "$" && substr(s, i + 1, 1) == "(") { inword = 1; i = cmdsub(s, i + 2, n); word = word "x"; continue }
|
|
145
|
+
if (c == "$" && substr(s, i + 1, 1) == "(") { wquoted = 1; inword = 1; i = cmdsub(s, i + 2, n); word = word "x"; continue }
|
|
132
146
|
if (c == "`") {
|
|
133
|
-
inword = 1
|
|
147
|
+
wquoted = 1; inword = 1
|
|
134
148
|
j = index(substr(s, i + 1), "`")
|
|
135
149
|
if (j == 0) { i = n + 1; continue }
|
|
136
150
|
enqueue(substr(s, i + 1, j - 1)); i += j + 1; word = word "x"; continue
|
|
@@ -146,6 +160,9 @@ function tokenize(s, i, n, c, c2, j, dash, delim) {
|
|
|
146
160
|
if (nheredoc > 0) i = read_heredocs(s, i, n)
|
|
147
161
|
continue
|
|
148
162
|
}
|
|
163
|
+
# `>&`, `>|` and `&>` are parts of a redirection operator, not command separators.
|
|
164
|
+
if ((c == "&" || c == "|") && inword && !wquoted && word ~ />$/) { word = word c; wplain++; i++; continue }
|
|
165
|
+
if (c == "&" && substr(s, i + 1, 1) == ">") { flush_word(); word = c; inword = 1; wplain = 1; i++; continue }
|
|
149
166
|
if (c == ";" || c == "&" || c == "|" || c == "(" || c == ")") { end_cmd(); i++; continue }
|
|
150
167
|
if (substr(s, i, 2) == "<<" && substr(s, i, 3) != "<<<") {
|
|
151
168
|
flush_word(); i += 2; dash = 0
|
|
@@ -161,6 +178,7 @@ function tokenize(s, i, n, c, c2, j, dash, delim) {
|
|
|
161
178
|
nheredoc++; HD[nheredoc] = delim; HDDASH[nheredoc] = dash
|
|
162
179
|
continue
|
|
163
180
|
}
|
|
181
|
+
if (!wquoted) wplain++
|
|
164
182
|
word = word c; inword = 1; i++
|
|
165
183
|
}
|
|
166
184
|
end_cmd()
|
|
@@ -169,7 +187,7 @@ function tokenize(s, i, n, c, c2, j, dash, delim) {
|
|
|
169
187
|
# ── option parsing shared by the CLIs: positionals P/PI, output format, -f/--format value, --raw ──
|
|
170
188
|
function kparse(A, s, n, shortvals, i, w, eq, key, val, f, rest) {
|
|
171
189
|
split("", P); split("", PI)
|
|
172
|
-
np = 0; outfmt = ""; fmtval = ""; dd = 0; raw = 0
|
|
190
|
+
np = 0; outfmt = ""; fmtval = ""; tmpl = ""; dd = 0; raw = 0
|
|
173
191
|
for (i = s; i <= n; i++) {
|
|
174
192
|
w = A[i]
|
|
175
193
|
if (w == "--") { dd = i; break }
|
|
@@ -178,6 +196,7 @@ function kparse(A, s, n, shortvals, i, w, eq, key, val, f, rest) {
|
|
|
178
196
|
if (eq) { key = substr(w, 1, eq - 1); val = substr(w, eq + 1) }
|
|
179
197
|
if (key == "--output") { if (!eq) { i++; val = A[i] } outfmt = tolower(val) }
|
|
180
198
|
else if (key == "--format") { if (!eq) { i++; val = A[i] } fmtval = val }
|
|
199
|
+
else if (key == "--template") { if (!eq) { i++; val = A[i] } tmpl = val }
|
|
181
200
|
else if (key == "--raw") raw = 1
|
|
182
201
|
else if (!eq && (key in LV)) i++
|
|
183
202
|
continue
|
|
@@ -196,6 +215,11 @@ function kparse(A, s, n, shortvals, i, w, eq, key, val, f, rest) {
|
|
|
196
215
|
np++; P[np] = w; PI[np] = i
|
|
197
216
|
}
|
|
198
217
|
}
|
|
218
|
+
# `-o jsonpath={.}` and `-o go-template={{.}}` print the whole object, exactly like -o json.
|
|
219
|
+
function proj_leaks(t) {
|
|
220
|
+
t = tolower(t); gsub(/[ \t]/, "", t)
|
|
221
|
+
return t ~ /[{](\.|@|\.items|\.items\[\*\])[}]/
|
|
222
|
+
}
|
|
199
223
|
function skipopts(A, s, n, vals) {
|
|
200
224
|
while (s <= n && substr(A[s], 1, 1) == "-") {
|
|
201
225
|
if (length(A[s]) == 2 && index(vals, substr(A[s], 2, 1))) s += 2
|
|
@@ -257,6 +281,8 @@ function kube(A, s, n, verb, m, parts, k, r, rlist) {
|
|
|
257
281
|
report("kubectl describe " r " prints the full container spec, including inline credentials", "kubectl get " r " -o jsonpath=" SQ "{.items[*].spec.containers[*].image}" SQ ", -o custom-columns=NAME:.metadata.name,STATUS:.status.phase, -o name, or kubectl get events / kubectl logs; say which field you need")
|
|
258
282
|
else if (outfmt == "json" || outfmt == "yaml")
|
|
259
283
|
report("kubectl get " r " -o " outfmt " prints the full container spec, including inline credentials", "-o jsonpath=" SQ "{.items[*].spec.containers[*].image}" SQ ", -o custom-columns=NAME:.metadata.name,STATUS:.status.phase, -o name, -o wide, or the status fields you need")
|
|
284
|
+
else if ((outfmt ~ /^(jsonpath|go-template|jsonpath-as-json)=/ || tmpl != "") && proj_leaks(tmpl outfmt))
|
|
285
|
+
report("kubectl get " r " with a projection that selects the whole object prints the full container spec, including inline credentials", "-o jsonpath=" SQ "{.items[*].spec.containers[*].image}" SQ ", -o custom-columns=NAME:.metadata.name,STATUS:.status.phase, -o name, -o wide, or the status fields you need")
|
|
260
286
|
}
|
|
261
287
|
}
|
|
262
288
|
}
|
|
@@ -323,19 +349,36 @@ function gcloud(A, s, n, k) {
|
|
|
323
349
|
if (P[1] == "secrets" && P[2] == "versions" && P[3] == "access") report("gcloud secrets versions access prints the secret", "gcloud secrets versions list, or gcloud secrets describe (metadata only)")
|
|
324
350
|
if (P[1] == "auth") for (k = 2; k <= np; k++) if (P[k] ~ /^print-(access|identity)-token$/) report("gcloud auth " P[k] " prints a token", "gcloud auth list")
|
|
325
351
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
352
|
+
# Scans one attached or clustered short option (-ePATTERN, -nePATTERN, -m5, -F:) of a pattern or script tool:
|
|
353
|
+
# sc_pat is set when it supplies the pattern or script, sc_skip when its value is the next word.
|
|
354
|
+
function scan_short(w, shortvals, sed_like, i, c, last) {
|
|
355
|
+
sc_pat = 0; sc_skip = 0; last = length(w)
|
|
356
|
+
for (i = 2; i <= last; i++) {
|
|
357
|
+
c = substr(w, i, 1)
|
|
358
|
+
if (c == "e" || c == "f") { sc_pat = 1; sc_skip = (i == last); return }
|
|
359
|
+
if (index(shortvals, c)) { sc_skip = (i == last); return }
|
|
360
|
+
if (sed_like && c == "i") return
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function reader(A, s, n, b, k, w, mode, havepat, need, afterdd, kind, valued, valued2, shortvals) {
|
|
364
|
+
b = base(A[s]); mode = ""; valued = " "; valued2 = " "; shortvals = ""
|
|
365
|
+
if (b ~ /^(grep|egrep|fgrep|rg|ag)$/) { mode = "pattern"; shortvals = (b ~ /^(grep|egrep|fgrep)$/) ? "mABCdD" : (b == "rg") ? "mABCdjEMrgtT" : "ABCGgmpW"; valued = " --glob --type --type-not --max-count --include --exclude --exclude-dir --context --after-context --before-context --max-depth --threads " }
|
|
366
|
+
else if (b ~ /^(sed|awk|gawk|jq|yq)$/) { mode = "script"; shortvals = "vF"; valued = " --indent "; valued2 = " --arg --argjson --slurpfile --rawfile " }
|
|
330
367
|
# The first operand is the pattern or script unless -e/-f/--regexp/--file/--expression supplies it; only the rest are files.
|
|
331
368
|
havepat = 0
|
|
332
|
-
if (mode != "") for (k = s + 1; k <= n; k++)
|
|
369
|
+
if (mode != "") for (k = s + 1; k <= n; k++) {
|
|
370
|
+
w = A[k]
|
|
371
|
+
if (w == "--") break
|
|
372
|
+
if (w ~ /^--(regexp|file|expression|from-file)(=|$)/) havepat = 1
|
|
373
|
+
else if (w ~ /^-[a-zA-Z]/) { scan_short(w, shortvals, b == "sed"); if (sc_pat) havepat = 1 }
|
|
374
|
+
}
|
|
333
375
|
need = (mode != "" && !havepat); afterdd = 0
|
|
334
376
|
for (k = s + 1; k <= n; k++) {
|
|
335
377
|
w = A[k]
|
|
336
378
|
if (w == "--" && !afterdd) { afterdd = 1; continue }
|
|
337
379
|
if (!afterdd && substr(w, 1, 1) == "-" && length(w) > 1) {
|
|
338
|
-
if (mode != "" &&
|
|
380
|
+
if (mode != "" && w ~ /^-[a-zA-Z]/) { scan_short(w, shortvals, b == "sed"); if (sc_skip) k++ }
|
|
381
|
+
else if (mode != "" && (w ~ /^--(regexp|file|expression|from-file)$/ || index(valued, " " w " "))) k++
|
|
339
382
|
else if (index(valued2, " " w " ")) k += 2
|
|
340
383
|
continue
|
|
341
384
|
}
|
|
@@ -345,13 +388,22 @@ function reader(A, s, n, b, k, w, mode, havepat, need, afterdd, kind, valued,
|
|
|
345
388
|
}
|
|
346
389
|
}
|
|
347
390
|
|
|
391
|
+
function drop_redirects(A, s, n, k, m, w) {
|
|
392
|
+
m = s
|
|
393
|
+
for (k = s + 1; k <= n; k++) {
|
|
394
|
+
w = A[k]
|
|
395
|
+
if (w ~ /^[0-9]*[<>]/) { if (w ~ /^[0-9]*[<>]+&?-?$/) k++; continue }
|
|
396
|
+
A[++m] = w
|
|
397
|
+
}
|
|
398
|
+
return m
|
|
399
|
+
}
|
|
348
400
|
function analyze(A, s, n, w, b, k, names, p) {
|
|
349
401
|
while (s <= n) {
|
|
350
402
|
w = A[s]; b = base(w)
|
|
351
403
|
if (is_assign(w)) { s++; continue }
|
|
352
404
|
if (w ~ /^[0-9]*[<>]/) { s += (w ~ /^[0-9]*[<>]+&?-?$/) ? 2 : 1; continue }
|
|
353
405
|
if (b in KW) { s++; continue }
|
|
354
|
-
if (b == "command" || b == "builtin") { s++; if (s <= n && (A[s] == "-v" || A[s] == "-V")) return; continue }
|
|
406
|
+
if (b == "command" || b == "builtin") { s++; if (s <= n && (A[s] == "-v" || A[s] == "-V")) return; if (s <= n && A[s] == "-p") s++; continue }
|
|
355
407
|
if (b == "sudo" || b == "doas") { s = skipopts(A, s + 1, n, "ugChprtUD"); continue }
|
|
356
408
|
if (b == "xargs") { s = skipopts(A, s + 1, n, "IniPLdsEa"); continue }
|
|
357
409
|
if (b == "timeout") { s = skipopts(A, s + 1, n, "ks") + 1; continue }
|
|
@@ -363,6 +415,7 @@ function analyze(A, s, n, w, b, k, names, p) {
|
|
|
363
415
|
while (s <= n) {
|
|
364
416
|
w = A[s]
|
|
365
417
|
if (is_assign(w)) s++
|
|
418
|
+
else if (w ~ /^[0-9]*[<>]/) s += (w ~ /^[0-9]*[<>]+&?-?$/) ? 2 : 1
|
|
366
419
|
else if (w == "-u" || w == "-C" || w == "-S" || w == "--unset" || w == "--chdir" || w == "--split-string") s += 2
|
|
367
420
|
else if (substr(w, 1, 1) == "-") s++
|
|
368
421
|
else break
|
|
@@ -393,6 +446,8 @@ function analyze(A, s, n, w, b, k, names, p) {
|
|
|
393
446
|
for (k = s + 1; k <= n; k++) if (A[k] ~ /^--show-secrets/) report("pulumi --show-secrets prints decrypted secrets", "pulumi config / pulumi stack output without --show-secrets (secrets stay masked)")
|
|
394
447
|
return
|
|
395
448
|
}
|
|
449
|
+
# A redirect is not a variable name: `printenv > f` and `export > f` still dump the whole environment.
|
|
450
|
+
if (b == "printenv" || b == "export" || b == "declare" || b == "typeset") n = drop_redirects(A, s, n)
|
|
396
451
|
if (b == "printenv") {
|
|
397
452
|
names = 0
|
|
398
453
|
for (k = s + 1; k <= n; k++) {
|
|
@@ -426,7 +481,8 @@ BEGIN {
|
|
|
426
481
|
for (i in L) KW[L[i]] = 1
|
|
427
482
|
split("pods po pod deployments deployment deploy statefulsets statefulset sts daemonsets daemonset ds jobs job cronjobs cronjob cj configmaps configmap cm replicasets replicaset rs replicationcontrollers rc all", L, " ")
|
|
428
483
|
for (i in L) SENS[L[i]] = 1
|
|
429
|
-
|
|
484
|
+
CMD = ENVIRON["GS_COMMAND"]
|
|
485
|
+
enqueue(CMD)
|
|
430
486
|
while (qi < queued) { qi++; tokenize(queue[qi]) }
|
|
431
487
|
}
|
|
432
488
|
AWK
|