From e6760c07a15205a3283c9598a2f9d33cf1279720 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Mon, 27 Apr 2026 15:28:27 -0400 Subject: [PATCH 1/4] agents: adding block kit types instructions --- AGENTS.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 3f59d5c0a..1b171143a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -281,6 +281,78 @@ Defined in `.github/workflows/ci-build.yml`, runs on push to `main`, all PRs, an 3. Add tests mirroring the module structure 4. Validate: `./scripts/run_validation.sh` +### Adding a New Block Kit Type + +Block Kit models live in `slack_sdk/models/blocks/` across three files: + +| File | Contents | +| --- | --- | +| `blocks.py` | Layout blocks (`SectionBlock`, `ActionsBlock`, `HeaderBlock`, etc.) | +| `block_elements.py` | Interactive elements (`ButtonElement`, `StaticSelectElement`, `DatePickerElement`, etc.) | +| `basic_components.py` | Composition objects (`TextObject`, `Option`, `ConfirmObject`, etc.) | + +All types are exported from `slack_sdk/models/blocks/__init__.py`. + +#### Base class hierarchy + +``` +JsonObject +├── Block → layout blocks +├── BlockElement → non-interactive elements +│ └── InteractiveElement → elements with action_id +│ └── InputInteractiveElement → elements usable inside InputBlock +├── TextObject → PlainTextObject, MarkdownTextObject +├── Option / OptionGroup +└── ConfirmObject, etc. +``` + +Choose the base class that matches the type you're adding. + +#### Steps + +1. **Define the class** in the appropriate file. Follow this pattern: + + ```python + class MyNewBlock(Block): + type = "my_new_block" + + @property + def attributes(self) -> Set[str]: + return super().attributes.union({"text", "optional_field"}) + + def __init__(self, *, text: Union[str, dict, TextObject], optional_field: Optional[str] = None, block_id: Optional[str] = None, **others: dict): + super().__init__(type=self.type, block_id=block_id) + show_unknown_key_warning(self, others) + self.text = TextObject.parse(text, default_type=PlainTextObject.type) + self.optional_field = optional_field + + @JsonValidator("text must be provided") + def _validate_text(self): + return self.text is not None + ``` + + Key conventions: + - Set `type` class attribute to the Slack API type string + - Override `attributes` to return the set of JSON field names for serialization + - Call `super().__init__()` with `type=self.type` + - Call `show_unknown_key_warning(self, others)` to log unexpected kwargs + - Use `TextObject.parse()`, `ConfirmObject.parse()`, and `BlockElement.parse()` for nested composition objects + - Add `@JsonValidator` decorators for constraints (required fields, max lengths, enum values, date formats) + - Use `@EnumValidator` for fields restricted to a set of values + +2. **Register for deserialization:** + - **Elements:** Automatic — `BlockElement.parse()` discovers subclasses at runtime via `__subclasses__()`. No manual step needed. + - **Blocks:** Manual — add an `elif` clause in `Block.parse()` (in `blocks.py`) mapping the type string to the new class. + +3. **Export the class** — add it to the imports and `__all__` list in `slack_sdk/models/blocks/__init__.py`. + +4. **Add tests** in `tests/slack_sdk/models/test_blocks.py`. Cover: + - Round-trip: `input_dict == MyNewBlock(**input_dict).to_dict()` + - Validation: required fields raise `SlackObjectFormationError` when missing + - Constraints: max lengths, enum values, format patterns + +5. **Validate:** `./scripts/run_validation.sh` + ### Fixing a Bug 1. Write a test that reproduces the bug From 142f74875892f8fb14a40036a7d1f61faf83483a Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Mon, 27 Apr 2026 15:50:42 -0400 Subject: [PATCH 2/4] separate block kit steps into slack_sdk/models --- AGENTS.md | 70 +----------------------------------- slack_sdk/models/AGENTS.md | 73 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 69 deletions(-) create mode 100644 slack_sdk/models/AGENTS.md diff --git a/AGENTS.md b/AGENTS.md index 1b171143a..48f29daef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -283,75 +283,7 @@ Defined in `.github/workflows/ci-build.yml`, runs on push to `main`, all PRs, an ### Adding a New Block Kit Type -Block Kit models live in `slack_sdk/models/blocks/` across three files: - -| File | Contents | -| --- | --- | -| `blocks.py` | Layout blocks (`SectionBlock`, `ActionsBlock`, `HeaderBlock`, etc.) | -| `block_elements.py` | Interactive elements (`ButtonElement`, `StaticSelectElement`, `DatePickerElement`, etc.) | -| `basic_components.py` | Composition objects (`TextObject`, `Option`, `ConfirmObject`, etc.) | - -All types are exported from `slack_sdk/models/blocks/__init__.py`. - -#### Base class hierarchy - -``` -JsonObject -├── Block → layout blocks -├── BlockElement → non-interactive elements -│ └── InteractiveElement → elements with action_id -│ └── InputInteractiveElement → elements usable inside InputBlock -├── TextObject → PlainTextObject, MarkdownTextObject -├── Option / OptionGroup -└── ConfirmObject, etc. -``` - -Choose the base class that matches the type you're adding. - -#### Steps - -1. **Define the class** in the appropriate file. Follow this pattern: - - ```python - class MyNewBlock(Block): - type = "my_new_block" - - @property - def attributes(self) -> Set[str]: - return super().attributes.union({"text", "optional_field"}) - - def __init__(self, *, text: Union[str, dict, TextObject], optional_field: Optional[str] = None, block_id: Optional[str] = None, **others: dict): - super().__init__(type=self.type, block_id=block_id) - show_unknown_key_warning(self, others) - self.text = TextObject.parse(text, default_type=PlainTextObject.type) - self.optional_field = optional_field - - @JsonValidator("text must be provided") - def _validate_text(self): - return self.text is not None - ``` - - Key conventions: - - Set `type` class attribute to the Slack API type string - - Override `attributes` to return the set of JSON field names for serialization - - Call `super().__init__()` with `type=self.type` - - Call `show_unknown_key_warning(self, others)` to log unexpected kwargs - - Use `TextObject.parse()`, `ConfirmObject.parse()`, and `BlockElement.parse()` for nested composition objects - - Add `@JsonValidator` decorators for constraints (required fields, max lengths, enum values, date formats) - - Use `@EnumValidator` for fields restricted to a set of values - -2. **Register for deserialization:** - - **Elements:** Automatic — `BlockElement.parse()` discovers subclasses at runtime via `__subclasses__()`. No manual step needed. - - **Blocks:** Manual — add an `elif` clause in `Block.parse()` (in `blocks.py`) mapping the type string to the new class. - -3. **Export the class** — add it to the imports and `__all__` list in `slack_sdk/models/blocks/__init__.py`. - -4. **Add tests** in `tests/slack_sdk/models/test_blocks.py`. Cover: - - Round-trip: `input_dict == MyNewBlock(**input_dict).to_dict()` - - Validation: required fields raise `SlackObjectFormationError` when missing - - Constraints: max lengths, enum values, format patterns - -5. **Validate:** `./scripts/run_validation.sh` +See [`slack_sdk/models/AGENTS.md`](slack_sdk/models/AGENTS.md) for detailed steps on defining, registering, exporting, and testing new Block Kit blocks, elements, and composition objects. ### Fixing a Bug diff --git a/slack_sdk/models/AGENTS.md b/slack_sdk/models/AGENTS.md new file mode 100644 index 000000000..1f858ce13 --- /dev/null +++ b/slack_sdk/models/AGENTS.md @@ -0,0 +1,73 @@ +# AGENTS.md — Block Kit Models + +## Adding a New Block Kit Type + +Block Kit models live in `slack_sdk/models/blocks/` across three files: + +| File | Contents | +| --- | --- | +| `blocks.py` | Layout blocks (`SectionBlock`, `ActionsBlock`, `HeaderBlock`, etc.) | +| `block_elements.py` | Interactive elements (`ButtonElement`, `StaticSelectElement`, `DatePickerElement`, etc.) | +| `basic_components.py` | Composition objects (`TextObject`, `Option`, `ConfirmObject`, etc.) | + +All types are exported from `slack_sdk/models/blocks/__init__.py`. + +### Base class hierarchy + +``` +JsonObject +├── Block → layout blocks +├── BlockElement → non-interactive elements +│ └── InteractiveElement → elements with action_id +│ └── InputInteractiveElement → elements usable inside InputBlock +├── TextObject → PlainTextObject, MarkdownTextObject +├── Option / OptionGroup +└── ConfirmObject, etc. +``` + +Choose the base class that matches the type you're adding. + +### Steps + +1. **Define the class** in the appropriate file. Follow this pattern: + + ```python + class MyNewBlock(Block): + type = "my_new_block" + + @property + def attributes(self) -> Set[str]: + return super().attributes.union({"text", "optional_field"}) + + def __init__(self, *, text: Union[str, dict, TextObject], optional_field: Optional[str] = None, block_id: Optional[str] = None, **others: dict): + super().__init__(type=self.type, block_id=block_id) + show_unknown_key_warning(self, others) + self.text = TextObject.parse(text, default_type=PlainTextObject.type) + self.optional_field = optional_field + + @JsonValidator("text must be provided") + def _validate_text(self): + return self.text is not None + ``` + + Key conventions: + - Set `type` class attribute to the Slack API type string + - Override `attributes` to return the set of JSON field names for serialization + - Call `super().__init__()` with `type=self.type` + - Call `show_unknown_key_warning(self, others)` to log unexpected kwargs + - Use `TextObject.parse()`, `ConfirmObject.parse()`, and `BlockElement.parse()` for nested composition objects + - Add `@JsonValidator` decorators for constraints (required fields, max lengths, enum values, date formats) + - Use `@EnumValidator` for fields restricted to a set of values + +2. **Register for deserialization:** + - **Elements:** Automatic — `BlockElement.parse()` discovers subclasses at runtime via `__subclasses__()`. No manual step needed. + - **Blocks:** Manual — add an `elif` clause in `Block.parse()` (in `blocks.py`) mapping the type string to the new class. + +3. **Export the class** — add it to the imports and `__all__` list in `slack_sdk/models/blocks/__init__.py`. + +4. **Add tests** in `tests/slack_sdk/models/test_blocks.py`. Cover: + - Round-trip: `input_dict == MyNewBlock(**input_dict).to_dict()` + - Validation: required fields raise `SlackObjectFormationError` when missing + - Constraints: max lengths, enum values, format patterns + +5. **Validate:** `./scripts/run_validation.sh` From 6cdcd5d5c3d4cf3aa9bfacddcb6bce1283c7321b Mon Sep 17 00:00:00 2001 From: Ale Mercado <104795114+srtaalej@users.noreply.github.com> Date: Tue, 28 Apr 2026 14:48:55 -0400 Subject: [PATCH 3/4] Update slack_sdk/models/AGENTS.md Co-authored-by: Eden Zimbelman --- slack_sdk/models/AGENTS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/slack_sdk/models/AGENTS.md b/slack_sdk/models/AGENTS.md index 1f858ce13..25d0f1de5 100644 --- a/slack_sdk/models/AGENTS.md +++ b/slack_sdk/models/AGENTS.md @@ -56,7 +56,6 @@ Choose the base class that matches the type you're adding. - Call `super().__init__()` with `type=self.type` - Call `show_unknown_key_warning(self, others)` to log unexpected kwargs - Use `TextObject.parse()`, `ConfirmObject.parse()`, and `BlockElement.parse()` for nested composition objects - - Add `@JsonValidator` decorators for constraints (required fields, max lengths, enum values, date formats) - Use `@EnumValidator` for fields restricted to a set of values 2. **Register for deserialization:** From 4f6f69e1ff45235c8c20161d992c60800db3b6c8 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Tue, 28 Apr 2026 14:57:39 -0400 Subject: [PATCH 4/4] update agents.md --- slack_sdk/models/AGENTS.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/slack_sdk/models/AGENTS.md b/slack_sdk/models/AGENTS.md index 25d0f1de5..6d3cec16b 100644 --- a/slack_sdk/models/AGENTS.md +++ b/slack_sdk/models/AGENTS.md @@ -1,5 +1,7 @@ # AGENTS.md — Block Kit Models +The `slack_sdk/models/` package provides Python classes for building [Block Kit](https://api.slack.com/block-kit) UI layouts. Each class serializes to/from the JSON payloads that the Slack API expects, with helper methods for parsing nested composition objects. + ## Adding a New Block Kit Type Block Kit models live in `slack_sdk/models/blocks/` across three files: @@ -44,10 +46,6 @@ Choose the base class that matches the type you're adding. show_unknown_key_warning(self, others) self.text = TextObject.parse(text, default_type=PlainTextObject.type) self.optional_field = optional_field - - @JsonValidator("text must be provided") - def _validate_text(self): - return self.text is not None ``` Key conventions: @@ -56,7 +54,6 @@ Choose the base class that matches the type you're adding. - Call `super().__init__()` with `type=self.type` - Call `show_unknown_key_warning(self, others)` to log unexpected kwargs - Use `TextObject.parse()`, `ConfirmObject.parse()`, and `BlockElement.parse()` for nested composition objects - - Use `@EnumValidator` for fields restricted to a set of values 2. **Register for deserialization:** - **Elements:** Automatic — `BlockElement.parse()` discovers subclasses at runtime via `__subclasses__()`. No manual step needed. @@ -66,7 +63,5 @@ Choose the base class that matches the type you're adding. 4. **Add tests** in `tests/slack_sdk/models/test_blocks.py`. Cover: - Round-trip: `input_dict == MyNewBlock(**input_dict).to_dict()` - - Validation: required fields raise `SlackObjectFormationError` when missing - - Constraints: max lengths, enum values, format patterns 5. **Validate:** `./scripts/run_validation.sh`