Skip to content

[NO-TICKET] Add hooks/skills foundation - #6147

Draft
Strech wants to merge 8 commits into
masterfrom
no-ticket-skills-foundation
Draft

[NO-TICKET] Add hooks/skills foundation#6147
Strech wants to merge 8 commits into
masterfrom
no-ticket-skills-foundation

Conversation

@Strech

@Strech Strech commented Aug 4, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Adds a new project scoped skill /write-rbs with an enforcement via hooks system that will ensure that skill is loaded when Claude tries to create/modify the Ruby RBS files

Motivation:

This should add consistency and reduce review cycles as all the common mistakes are documented via LLM instructions. The output should be considerably better.

Change log entry

No. Internal change

Additional Notes:

Q: Why** hook and not a rules/ folder?
A: Because rules doesn't invoke on created from scratch files, only on read/edit and because of that LLM could decide not to load skill when creating a file

Q: How is the hook better than rules/?
A: With hook we are scanning entire session for skill being loaded, covering create/edit operations and it will reject editing if it wasn't done, LLM will be required to load the skill otherwise it will be denied of editing/creating RBS.

Q: Why is it ruby?
A: This is a near zero-dependency setup, the only dependency that might be handy is Spinel, to compile hook and boost execution time (which includes VM boot) from 100-200ms to 4ms.

Q: Is it testable?
A: Yes, ruby is tested via TestUnit and compiled binary passes smoke tests where ruby script output is compared to binary output for the same input.

How to test the change?

/skills to see the list, then look for project scope and find /write-rbs

@Strech
Strech requested a review from a team as a code owner August 4, 2026 09:26
@datadog-prod-us1-5

datadog-prod-us1-5 Bot commented Aug 4, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 90.04% (+0.01%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 32cdad8 | Docs | Datadog PR Page | Give us feedback!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 81b5f9ae34

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/settings.json
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard shell-based RBS writes

The hook only runs for Edit and Write, so RBS changes made through Bash bypass enforcement entirely. This includes the skill's own generation command in .claude/skills/write-rbs/SKILL.md:52-54, whose shell redirection creates an .rbs file, as well as sed -i, cp, or cat > sig/...; in those cases the skill need never be loaded despite the stated guarantee. Guard relevant Bash writes as well or otherwise prevent shell-based modifications from bypassing the check.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scoping the guard to Edit/Write and deliberately not Bash.

The guard exists to load the write-rbs skill before Claude hand-authors a signature. Shell tools (sed, cp, mv, tee) transform or copy existing bytes rather than author new types, so there is nothing for the skill to guide. Reliably detecting a write to a guarded path inside an arbitrary command line (redirects, heredocs, sed -i, interpolated paths) is not feasible, and matching Bash would fire the hook on every shell command. Scope is now documented in .claude/hooks/README.md.

Comment thread .claude/hooks/Makefile
Comment on lines +43 to +44
test:
@for t in $(HOOKS_DIR)*.test.rb; do echo "ruby $$t"; ruby "$$t" || exit 1; done

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wire the hook tests into an authoritative rake task

These tests are reachable only through this local Make target. A repository-wide search of the Rakefile and the checked GitHub/GitLab configurations finds no invocation of .claude/hooks, require-skill, or this Makefile, so normal CI can accept regressions in the denial logic while the new test suite remains unexecuted. Expose it through the authoritative Rake test structure and include it in an existing validation path.

AGENTS.md reference: AGENTS.md:L61-L66

Useful? React with 👍 / 👎.

Comment thread .claude/hooks/Makefile
Comment on lines +1 to +4
# Build and test the hooks in this directory
#
# make bootstrap Spinel if needed, compile every hook, run tests
# make test run every hook's tests (unit + smoke) under CRuby

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the new hook workflow in agent guidance

This introduces a new build, bootstrap, compilation, and verification workflow for repository scripts without updating AGENTS.md or the specialized personas. Agents entering through the repository guidance therefore will not discover the required native re-verification after hook changes, contrary to the repository rule requiring agent guidance to evolve with scripts.

AGENTS.md reference: AGENTS.md:L127-L127

Useful? React with 👍 / 👎.

Comment thread .claude/skills/write-rbs/SKILL.md Outdated
## Core rules

- MUST leave a blank line between every signature
- NEVER add a leading `::` to Ruby core classes — write `Hash`, not `::Hash`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep absolute qualification when core constants are shadowed

The blanket prohibition on leading :: produces incorrect constant resolution whenever a Datadog namespace defines a core-named constant. For example, sig/datadog/core/logger.rbs:3 intentionally declares class Logger < ::Logger; following this rule changes it to the recursive Logger < Logger, which RBS rejects as a recursive ancestor. Preserve absolute qualification when it is needed to distinguish the top-level core or standard-library constant.

Useful? React with 👍 / 👎.

Comment on lines +13 to +14
- NEVER split one file across both an inline `# :` signature and a `sig/` `.rbs` —
pick one per file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow the repository's hybrid inline/native signatures

The repository deliberately splits several profiling classes across inline Ruby signatures and standalone .rbs declarations for C-only methods: Steepfile:11-25 explicitly preserves this arrangement, and files such as sig/datadog/profiling/http_transport.rbs contain the native half. Applying this NEVER rule would force an agent to delete either checked inline types or declarations for methods that cannot live in the Ruby file, so document and preserve the native-extension exception.

Useful? React with 👍 / 👎.

Comment thread .claude/skills/write-rbs/SKILL.md Outdated
Comment on lines +67 to +68
```bash
bundle exec steep check sig/datadog/<path>.rbs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Point the targeted Steep check at Ruby source

Steep's positional check filter identifies source files to type-check, as also documented in docs/StaticTypingGuide.md:13-16; passing the path under sig/ does not target the corresponding Ruby implementation. During iteration this command can therefore appear clean without checking whether lib/datadog/<path>.rb conforms to the edited signature. Use the mirrored Ruby source path for the targeted check.

Useful? React with 👍 / 👎.


```rbs
# Good
class Point < Struct[[Integer, Integer]]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use the member-value union for Struct's element parameter

Struct has a single Elem parameter used directly as the return type of #[] and the yielded type of #each; it does not interpret a tuple as per-member positions. Consequently this declaration models point[0] and every yielded value as [Integer, Integer], and to_a as Array[[Integer, Integer]], even though the runtime values are individual integers. Use Struct[Integer] here, or a union of the member value types for heterogeneous structs.

Useful? React with 👍 / 👎.

@Strech
Strech marked this pull request as draft August 4, 2026 11:46
@Strech
Strech force-pushed the no-ticket-skills-foundation branch from ce6d945 to bc2bfef Compare August 4, 2026 16:05
Comment thread .claude/hooks/README.md
# Hooks

Claude Code hooks for this repo. A hook is a small script Claude Code runs
around tool calls — here, one Ruby file per hook.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
around tool calls — here, one Ruby file per hook.
around tool calls.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree, this is specifically against bulldozer extractions, rule is simple - 1 hook = 1 script

Comment thread .claude/hooks/README.md

```ruby
# runs as a hook or a compiled binary, but not when required by the test
Runner.new(ARGV).run($stdin.read) unless $PROGRAM_NAME.end_with?(".test.rb")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is general knowledge and does not need to be in the file.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like where is it general?

Comment thread .claude/hooks/README.md
```

Each test file carries two suites. The **unit** suite exercises the hook's logic
in process. The **smoke** suite runs the hook end to end as a subprocess against

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smoke test should be in a separate file.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, is there any argument for it?

Comment thread .claude/hooks/README.md Outdated

## Shims

`settings.json` never points at a Ruby file directly. It points at a shim in

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is settings.json?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a general knowledge of Claude

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment thread .claude/hooks/README.md Outdated

`settings.json` never points at a Ruby file directly. It points at a shim in
`shims/`, which runs the compiled binary when one exists and falls back to plain
Ruby otherwise. That keeps the wiring stable whether or not a native build is

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a native build and why is it present or not present?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default hooks shipped with no dependencies and they are ruby scripts, but for performance reasons you might want to compile them for a speedup. Rest is a general approach for it shim is either picking compiled or plain ruby

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified

Comment thread .claude/hooks/README.md Outdated
everyone builds their own, and plain Ruby remains the portable fallback.

Spinel only supports a subset of Ruby, so mind the gaps. `Hash#dig`, for one,
is unsupported — reach for `Hash#fetch` instead. Because the binary is

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely it is impractical to duplicate the diff between ruby and spinel into this readme file. My suggestion is to delete all ai slop related to it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an example of disparity and reasoning for the smoke test. I can remove it for sure, or rephrase it, but I think the reasons for smoke tests presence are makes sense to mention

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants