diff --git a/.claude/skills/allium/SKILL.md b/.claude/skills/allium/SKILL.md index 43a3950badee..07657ab15f45 100644 --- a/.claude/skills/allium/SKILL.md +++ b/.claude/skills/allium/SKILL.md @@ -1,27 +1,15 @@ --- name: allium -description: Give your AI agents something more useful than a prompt. Velocity through clarity. -version: 3 -auto_trigger: - - file_patterns: ["**/*.allium"] - - keywords: ["allium", "allium spec", "allium specification", ".allium file"] +description: "Writes, reads, and validates .allium behavioural specification files. Use when the user asks to create an allium spec, edit a .allium file, write a behavioural specification, or understand allium syntax." +metadata: + version: 3 + auto_trigger_file_patterns: "**/*.allium" + auto_trigger_keywords: "allium, allium spec, allium specification, .allium file" --- # Allium -Allium is a formal language for capturing software behaviour at the domain level. It sits between informal feature descriptions and implementation, providing a precise way to specify what software does without prescribing how it's built. - -The name comes from the botanical family containing onions and shallots, continuing a tradition in behaviour specification tooling established by Cucumber and Gherkin. - -Key principles: - -- Describes observable behaviour, not implementation -- Captures domain logic that matters at the behavioural level -- Generates integration and end-to-end tests (not unit tests) -- Forces ambiguities into the open before implementation -- Implementation-agnostic: the same spec could be implemented in any language - -Allium does NOT specify programming language or framework choices, database schemas or storage mechanisms, API designs or UI layouts, or internal algorithms (unless they are domain-level concerns). +Allium is a formal language for capturing software behaviour at the domain level. It describes observable behaviour, not implementation, and generates integration and end-to-end tests. ## Routing table @@ -74,38 +62,7 @@ value TimeRange { start: Timestamp, end: Timestamp, duration: end - start } ### Sum type -A base entity declares a discriminator field whose capitalised values name the variants. Variants use the `variant` keyword. - -``` -entity Node { - path: Path - kind: Branch | Leaf -- discriminator field -} - -variant Branch : Node { - children: List -} - -variant Leaf : Node { - data: List - log: List -} -``` - -Lowercase pipe values are enum literals (`status: pending | active`). Capitalised values are variant references (`kind: Branch | Leaf`). Type guards (`requires:` or `if` branches) narrow to a variant and unlock its fields. - -### Module given - -Declares the entity instances a module's rules operate on. All rules inherit these bindings. Not every module needs one: rules scoped by triggers on domain entities get their entities from the trigger. `given` is for specs where rules operate on shared instances that exist once per module scope. - -``` -given { - pipeline: HiringPipeline - calendar: InterviewCalendar -} -``` - -Imported module instances are accessed via qualified names (`scheduling/calendar`) and do not appear in the local `given` block. Distinct from surface `context`, which binds a parametric scope for a boundary contract. +Capitalised pipe values are variant references (`kind: Branch | Leaf`), lowercase are enum literals (`status: pending | active`). See [language reference](./references/language-reference.md) for full variant syntax. ### Rule @@ -125,43 +82,20 @@ rule InvitationExpires { ### Trigger types -- **External stimulus**: `when: CandidateSelectsSlot(invitation, slot)` — action from outside the system -- **State transition**: `when: interview: Interview.status transitions_to scheduled` — entity changed state (transition only, not creation) -- **State becomes**: `when: interview: Interview.status becomes scheduled` — entity has this value, whether by creation or transition -- **Temporal**: `when: invitation: Invitation.expires_at <= now` — time-based condition (always add a `requires` guard against re-firing) -- **Derived condition**: `when: interview: Interview.all_feedback_in` — derived value becomes true -- **Entity creation**: `when: batch: DigestBatch.created` — fires when a new entity is created -- **Chained**: `when: AllConfirmationsResolved(candidacy)` — subscribes to a trigger emission from another rule's ensures clause - -All entity-scoped triggers use explicit `var: Type` binding. Use `_` as a discard binding where the name is not needed: `when: _: Invitation.expires_at <= now`, `when: SomeEvent(_, slot)`. - -### Rule-level iteration - -A `for` clause applies the rule body once per element in a collection: - -``` -rule ProcessDigests { - when: schedule: DigestSchedule.next_run_at <= now - for user in Users where notification_setting.digest_enabled: - let settings = user.notification_setting - ensures: DigestBatch.created(user: user, ...) -} -``` +- **External stimulus**: `when: CandidateSelectsSlot(invitation, slot)` +- **State transition**: `when: interview: Interview.status transitions_to scheduled` (transition only, not creation) +- **State becomes**: `when: interview: Interview.status becomes scheduled` (creation or transition) +- **Temporal**: `when: invitation: Invitation.expires_at <= now` (always add `requires` guard) +- **Derived condition**: `when: interview: Interview.all_feedback_in` +- **Entity creation**: `when: batch: DigestBatch.created` +- **Chained**: `when: AllConfirmationsResolved(candidacy)` (subscribes to trigger emission) ### Ensures patterns -Ensures clauses have four outcome forms: - - **State changes**: `entity.field = value` -- **Entity creation**: `Entity.created(...)` — the single canonical creation verb -- **Trigger emission**: `TriggerName(params)` — emits an event for other rules to chain from -- **Entity removal**: `not exists entity` — asserts the entity no longer exists - -These forms compose with `for` iteration (`for x in collection: ...`), `if`/`else` conditionals and `let` bindings. - -Entity creation uses `.created()` exclusively. Domain meaning lives in entity names and rule names, not in creation verbs. - -In state change assignments, the right-hand expression references pre-rule field values. Conditions within ensures blocks (`if` guards, creation parameters, trigger emission parameters) reference the resulting state. +- **Entity creation**: `Entity.created(...)` (the only creation verb) +- **Trigger emission**: `TriggerName(params)` +- **Entity removal**: `not exists entity` ### Surface @@ -185,120 +119,18 @@ surface InterviewerDashboard { } ``` -Surfaces define contracts at boundaries. The `facing` clause names the external party, `context` scopes the entity. The remaining clauses use a single vocabulary regardless of whether the boundary is user-facing or code-to-code: `exposes` (visible data, supports `for` iteration over collections), `provides` (available operations with optional when-guards), `contracts:` (references module-level `contract` declarations with `demands`/`fulfils` direction markers), `@guarantee` (named prose assertions about the boundary), `@guidance` (non-normative advice), `related` (associated surfaces reachable from this one), `timeout` (references to temporal rules that apply within the surface's context). - -The `facing` clause accepts either an actor type (with a corresponding `actor` declaration and `identified_by` mapping) or an entity type directly. Use actor declarations when the boundary has specific identity requirements; use entity types when any instance can interact (e.g., `facing visitor: User`). For integration surfaces where the external party is code, declare an actor type with a minimal `identified_by` expression. Actors that reference `within` in their `identified_by` expression must declare the expected context type: `within: Workspace`. - -### Surface-to-implementation contract - -The `exposes` block is the field-level contract: the implementation returns exactly these fields, the consumer uses exactly these fields. Do not add fields not listed. Do not omit fields that are listed. - -### Contract - -```allium -contract Codec { - serialize: (value: Any) -> ByteArray - deserialize: (bytes: ByteArray) -> Any - - @invariant Roundtrip - -- deserialize(serialize(value)) produces a value - -- equivalent to the original for all supported types. -} -``` +Surfaces define boundary contracts: `facing` names the external party, `context` scopes the entity, `exposes` lists visible data, `provides` lists available operations, `contracts:` references module-level contract declarations. -Contracts are module-level declarations referenced by name in surface `contracts:` clauses (`demands Codec`, `fulfils EventSubmitter`). See [Contracts](./references/language-reference.md#contracts) for declaration syntax and referencing rules. +For full syntax of contracts, expressions, config, defaults, invariants, transition graphs, state-dependent fields, deferred specs, and open questions, see the [language reference](./references/language-reference.md). -### Expressions +## Authoring workflow -Navigation: `interview.candidacy.candidate.email`, `reply_to?.author` (optional), `timezone ?? "UTC"` (null coalescing). Collections: `slots.count`, `slot in invitation.slots`, `interviewers.any(i => i.can_solo)`, `for item in collection: item.status = cancelled`, `permissions + inherited` (set union), `old - new` (set difference). Comparisons: `status = pending`, `count >= 2`, `status in {confirmed, declined}`, `provider not in providers`. Boolean logic: `a and b`, `a or b`, `not a`, `a implies b`. - -### Modular specs - -``` -use "github.com/allium-specs/google-oauth/abc123def" as oauth -``` - -Qualified names reference entities across specs: `oauth/Session`. Coordinates are immutable (git SHAs or content hashes). Local specs use relative paths: `use "./candidacy.allium" as candidacy`. - -### Config - -``` -config { - invitation_expiry: Duration = 7.days - max_login_attempts: Integer = 5 - extended_expiry: Duration = invitation_expiry * 2 -- expression-form default - sync_timeout: Duration = core/config.default_timeout -- config parameter reference -} -``` - -Rules reference config values as `config.invitation_expiry`. For default entity instances, use `default`. - -### Defaults - -``` -default Role viewer = { name: "viewer", permissions: { "documents.read" } } -``` - -### Invariant - -```allium -invariant NonNegativeBalance { - for account in Accounts: - account.balance >= 0 -} -``` - -Expression-bearing invariants (`invariant Name { expression }`) assert properties over entity state. They are logical assertions, not runtime checks. Distinct from prose annotations (`@invariant Name`) in contracts, which use the `@` sigil to mark content the checker does not evaluate. See [Invariants](./references/language-reference.md#invariants). - -### Transition graph (v3) - -``` -entity Order { - status: pending | confirmed | shipped | delivered | cancelled - - transitions status { - pending -> confirmed - confirmed -> shipped - shipped -> delivered - pending -> cancelled - confirmed -> cancelled - terminal: delivered, cancelled - } -} -``` - -### State-dependent field presence (v3) - -``` -entity Order { - status: pending | confirmed | shipped | delivered | cancelled - customer: Customer - total: Money - tracking_number: String when status = shipped | delivered - shipped_at: Timestamp when status = shipped | delivered - - transitions status { - pending -> confirmed - confirmed -> shipped - shipped -> delivered - pending -> cancelled - confirmed -> cancelled - terminal: delivered, cancelled - } -} -``` - -### Deferred specs - -``` -deferred InterviewerMatching.suggest -- see: detailed/interviewer-matching.allium -``` - -### Open questions - -``` -open question "Admin ownership - should admins be assigned to specific roles?" -``` +1. Define entities with fields, relationships, and derived values +2. Write rules with triggers, guards, and ensures clauses +3. Add surfaces for boundary contracts (UI, API, integration points) +4. Add config, invariants, and open questions as needed +5. Validate with the `allium` CLI (if installed) or against the language reference +6. Fix any reported issues and re-validate ## Verification diff --git a/.claude/skills/create-core-check/SKILL.md b/.claude/skills/create-core-check/SKILL.md index 3bb095816b3f..2f87257bf451 100644 --- a/.claude/skills/create-core-check/SKILL.md +++ b/.claude/skills/create-core-check/SKILL.md @@ -1,12 +1,11 @@ --- name: create-core-check -description: Create a new Go core check that collects metrics and sends them to Datadog -allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion -argument-hint: "[check-name]" +description: "Scaffolds a new Go core check for the Datadog Agent that collects metrics, service checks, or events. Use when the user asks to create a new agent check, add a custom core check, scaffold a Go metric collector, or build a new Datadog Agent integration." +metadata: + allowed-tools: "Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion" + argument-hint: "[check-name]" --- -Create a new Go-based core check for the Datadog Agent. Core checks collect metrics, service checks, or events and send them to Datadog at regular intervals. - ## Instructions ### Step 1: Gather information from the user @@ -134,36 +133,6 @@ Follow the test patterns from the reference file read in Step 2. The standard te 4. Report the results to the user. -## Sender Methods Reference - -The sender (`c.GetSender()`) provides these methods for submitting data: - -| Method | Description | -|---|---| -| `Gauge(metric, value, hostname, tags)` | Submit a gauge metric | -| `Rate(metric, value, hostname, tags)` | Submit a rate metric | -| `Count(metric, value, hostname, tags)` | Submit a count metric | -| `MonotonicCount(metric, value, hostname, tags)` | Submit a monotonic count | -| `Histogram(metric, value, hostname, tags)` | Submit a histogram metric | -| `Distribution(metric, value, hostname, tags)` | Submit a distribution metric | -| `ServiceCheck(name, status, hostname, tags, message)` | Submit a service check | -| `Event(event)` | Submit an event | -| `Commit()` | Flush all submitted data — **must be called at end of Run()** | - -- Pass `""` for hostname to use the agent's default hostname. -- Pass `nil` for tags if no tags are needed. -- Service check statuses: `servicecheck.ServiceCheckOK`, `ServiceCheckWarning`, `ServiceCheckCritical`, `ServiceCheckUnknown` (from `pkg/metrics/servicecheck`). - -## Important Notes - -- `CheckBase` provides default implementations for most `Check` interface methods. You only need to override `Run()` and optionally `Configure()`, `Stop()`, and `Interval()`. -- `CommonConfigure` handles standard configuration: collection interval (`min_collection_interval`), custom tags, service tag, etc. -- `FinalizeCheckServiceTag()` must be called after `CommonConfigure` to apply the service tag to the sender. -- Always call `sender.Commit()` at the end of `Run()` to flush data. -- For multi-instance checks, `BuildID()` must be called **before** `CommonConfigure()`. -- The `option.None[func() check.Check]()` pattern is used for platform stubs — the loader skips checks with no factory. -- `integration.FakeConfigHash` is the constant to use in tests for the config digest parameter. - ## Usage - `/create-core-check` — Interactive: prompts for all details diff --git a/.claude/skills/create-pr/SKILL.md b/.claude/skills/create-pr/SKILL.md index 21e571bfed58..1fefe6bc80f2 100644 --- a/.claude/skills/create-pr/SKILL.md +++ b/.claude/skills/create-pr/SKILL.md @@ -1,9 +1,10 @@ --- name: create-pr -description: Create a pull request for the current branch with proper labels and description -disable-model-invocation: true -allowed-tools: Bash, Read, Glob -argument-hint: "[--real] [additional labels...]" +description: "Creates a pull request for the current branch with proper labels, conventional commit title, and Datadog PR template. Use when the user asks to open a PR, submit code for review, create a pull request, or push changes for merging." +metadata: + disable-model-invocation: true + allowed-tools: "Bash, Read, Glob" + argument-hint: "[--real] [additional labels...]" --- Create a pull request for the current branch following the Datadog Agent contributing guidelines. @@ -39,17 +40,6 @@ Create a pull request for the current branch following the Datadog Agent contrib - **Describe how you validated your changes**: How you validated the change (tests added/run, benchmarks, manual testing). Only needed when testing included work not covered by test suites. - **Additional Notes**: Any extra context, links to predecessor PRs if part of a chain, notes that make code understanding easier. **Only include this section if there is genuinely useful context to add** — omit it entirely rather than filling it with filler. -## PR Description Guidelines (from CONTRIBUTING.md) - -The PR description should incorporate everything reviewers and future maintainers need: -- A description of what is changed -- A reason why the change is made (pointing to an issue is a good reason) -- When testing had to include work not covered by test suites, a description of how you validated your change -- Any relevant benchmarks -- Additional notes that make code understanding easier -- If part of a chain of PRs, point to the predecessors -- If there are drawbacks or tradeoffs, raise them - ## Example ```bash diff --git a/.claude/skills/create-status-provider/SKILL.md b/.claude/skills/create-status-provider/SKILL.md index c1d73067a11c..31fa95ff9a75 100644 --- a/.claude/skills/create-status-provider/SKILL.md +++ b/.claude/skills/create-status-provider/SKILL.md @@ -1,8 +1,9 @@ --- name: create-status-provider -description: Add a new section to the agent status output (agent status command) -allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion -argument-hint: "[provider-name]" +description: "Adds a new status provider section to the Datadog Agent status command output in JSON, text, and HTML formats. Use when the user asks to extend the agent status output, add a status section, create a status provider, or customize what the agent status command displays." +metadata: + allowed-tools: "Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion" + argument-hint: "[provider-name]" --- Add a new status provider to the Datadog Agent. Status providers contribute sections to the `agent status` output in JSON, plain text, and HTML formats. @@ -32,7 +33,29 @@ Before writing any code, read the appropriate reference files to follow existing ### Step 3: Implement the provider -Create the provider implementation following the reference. Also create the `status_templates/.tmpl` and `status_templates/HTML.tmpl` files following the templates in the reference's `status_templates/` directory. +Create the provider implementation following the reference. Minimal provider skeleton: + +```go +//go:embed status_templates +var templatesFS embed.FS + +type myProvider struct{} + +func (p myProvider) Name() string { return "My Feature" } +func (p myProvider) Section() string { return "my_section" } +func (p myProvider) JSON(_ bool, stats map[string]interface{}) error { + stats["myFeatureStats"] = collectStats() + return nil +} +func (p myProvider) Text(_ bool, buffer io.Writer) error { + return status.RenderText(templatesFS, "myfeature.tmpl", buffer, collectStats()) +} +func (p myProvider) HTML(_ bool, buffer io.Writer) error { + return status.RenderHTML(templatesFS, "myfeatureHTML.tmpl", buffer, collectStats()) +} +``` + +Also create `status_templates/.tmpl` and `status_templates/HTML.tmpl` following the templates in the reference's `status_templates/` directory. To make a provider conditional, return `nil` from the constructor when the feature is disabled. diff --git a/.claude/skills/omnibus-to-bazel/SKILL.md b/.claude/skills/omnibus-to-bazel/SKILL.md index dce23fffaadf..8c9e4cc361e1 100644 --- a/.claude/skills/omnibus-to-bazel/SKILL.md +++ b/.claude/skills/omnibus-to-bazel/SKILL.md @@ -1,8 +1,9 @@ --- name: omnibus-to-bazel -description: Convert an omnibus/config/software/.rb dependency to a Bazel third-party dep under deps/. Use when asked to migrate, convert, or add a dep from omnibus to Bazel. -argument-hint: "" -allowed-tools: Read, Write, Edit, Glob, Grep, Bash +description: "Converts an omnibus software dependency to a Bazel third-party dep under deps/. Use when the user asks to migrate, convert, or move a dependency from omnibus to Bazel, or add a new Bazel dep from an existing omnibus .rb file." +metadata: + argument-hint: "software-name" + allowed-tools: "Read, Write, Edit, Glob, Grep, Bash" --- Convert `omnibus/config/software/$ARGUMENTS.rb` into a Bazel third-party dep.