part-model-maintainer
Skill1.1k starsChanged 35 days agoMITView on GitHub
Maintain Part domain models — fillable attributes, mutators, typed nested data, save/fetch behavior, permission checks, PHPDoc, and repository bindings. Use when adding or modifying any Discord Part class.
Trending
More talk lately.about the whole repository
Who's talking about discord-php/DiscordPHP
- @throwable.bsky.social“📦 team-reflex/discord-php v10.45.21 An unofficial API to interact with the voice and text service Discord. …”Bluesky · Feb 27, 2026 · 1 likes
- crimsonstrife/streamer.live“Bump team-reflex/discord-php from 10.18.24 to 10.18.45”GitHub · Aug 19, 2025 · 1 reactions
- crimsonstrife/streamer.live“Bump team-reflex/discord-php from 10.18.0 to 10.18.5”GitHub · Jul 8, 2025 · 1 reactions
- crimsonstrife/streamer.live“build(deps): bump team-reflex/discord-php from 10.17.0 to 10.17.3”GitHub · Jun 16, 2025 · 1 reactions
- crimsonstrife/streamer.live“build(deps): bump team-reflex/discord-php from 10.13.1 to 10.14.0”GitHub · Jun 9, 2025 · 1 reactions
The biggest 5 of 52 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 52
Talked about on GitHub and Bluesky (52 times).
On GitHub · 50
- Aug 19, 2025Bump team-reflex/discord-php from 10.18.24 to 10.18.45 — crimsonstrife/streamer.live1 reaction · 2 comments
- Jul 8, 2025Bump team-reflex/discord-php from 10.18.0 to 10.18.5 — crimsonstrife/streamer.live1 reaction · 3 comments
- Jun 16, 2025build(deps): bump team-reflex/discord-php from 10.17.0 to 10.17.3 — crimsonstrife/streamer.live1 reaction · 3 comments
- Jun 9, 2025build(deps): bump team-reflex/discord-php from 10.13.1 to 10.14.0 — crimsonstrife/streamer.live1 reaction · 3 comments
- Jun 2, 2025build(deps): bump team-reflex/discord-php from 10.10.12 to 10.11.6 — crimsonstrife/streamer.live1 reaction · 3 comments
45 more on GitHub
- May 26, 2025build(deps): bump team-reflex/discord-php from 10.10.0 to 10.10.6 — crimsonstrife/streamer.live1 reaction · 2 comments
- May 19, 2025build(deps): bump team-reflex/discord-php from 10.7.3 to 10.8.5 — crimsonstrife/streamer.live1 reaction · 2 comments
- May 5, 2025build(deps): bump team-reflex/discord-php from 10.7.1 to 10.7.2 — crimsonstrife/streamer.live1 reaction · 2 comments
- Apr 28, 2025build(deps): bump team-reflex/discord-php from 10.4.7 to 10.7.1 — crimsonstrife/streamer.live1 reaction · 3 comments
- Mar 25, 2025build(deps): bump team-reflex/discord-php from 10.4.3 to 10.4.4 — crimsonstrife/streamer.live1 reaction · 3 comments
- Jul 18, 2024✨ Add support for polls — laracord/framework1 reaction
- May 11, 2024update d.py/d.php polls — advaith1/libs1 reaction · 1 comment
- Nov 2, 2026build(deps): update team-reflex/discord-php requirement from 10.66.* to 10.71.* — smrealms/smr0 reactions
- Oct 26, 2026build(deps): update team-reflex/discord-php requirement from 10.66.* to 10.69.* — smrealms/smr0 reactions · 1 comment
- Oct 20, 2026build(deps): update team-reflex/discord-php requirement from 10.66.* to 10.68.* — smrealms/smr0 reactions · 1 comment
- Oct 19, 2026Bump team-reflex/discord-php from 10.59.0 to 10.67.0 — omnicolor/commlink0 reactions · 1 comment
- Oct 14, 2026build(deps): update team-reflex/discord-php requirement from 10.66.* to 10.67.* — smrealms/smr0 reactions · 1 comment
- Oct 13, 2026Bump team-reflex/discord-php from 10.59.0 to 10.66.3 — omnicolor/commlink0 reactions · 1 comment
- Oct 6, 2026Bump team-reflex/discord-php from 10.59.0 to 10.66.1 — omnicolor/commlink0 reactions · 1 comment
- Oct 5, 2026Bump team-reflex/discord-php from 10.59.0 to 10.65.0 — omnicolor/commlink0 reactions · 1 comment
- Oct 1, 2026build(deps): update team-reflex/discord-php requirement from 10.49.* to 10.66.* — smrealms/smr0 reactions
- Oct 1, 2026Bump team-reflex/discord-php from 10.57.0 to 10.59.0 — omnicolor/commlink0 reactions
- Sep 30, 2026build(deps): update team-reflex/discord-php requirement from 10.49.* to 10.62.* — smrealms/smr0 reactions · 1 comment
- Sep 29, 2026Bump team-reflex/discord-php from 10.57.0 to 10.58.0 — omnicolor/commlink0 reactions · 1 comment
- Sep 28, 2026build(deps): update team-reflex/discord-php requirement from 10.49.* to 10.59.* — smrealms/smr0 reactions · 1 comment
- Sep 25, 2026build(deps): update team-reflex/discord-php requirement from 10.49.* to 10.58.* — smrealms/smr0 reactions · 1 comment
- Sep 25, 2026Bump team-reflex/discord-php from 10.56.18 to 10.57.0 — omnicolor/commlink0 reactions
- Sep 23, 2026Bump team-reflex/discord-php from 10.56.17 to 10.56.18 — omnicolor/commlink0 reactions
- Sep 21, 2026build(deps): update team-reflex/discord-php requirement from 10.49.* to 10.57.* — smrealms/smr0 reactions · 1 comment
- Sep 14, 2026Bump team-reflex/discord-php from 10.56.15 to 10.56.17 — omnicolor/commlink0 reactions
- Sep 7, 2026Bump team-reflex/discord-php from 10.56.10 to 10.56.15 — omnicolor/commlink0 reactions
- Sep 7, 2026build: Bump team-reflex/discord-php from 10.48.14 to 10.56.15 — VATSIM-UK/core0 reactions
- Sep 4, 2026Bump team-reflex/discord-php from 10.56.10 to 10.56.13 — omnicolor/commlink0 reactions · 1 comment
- Sep 2, 2026Bump team-reflex/discord-php from 10.56.8 to 10.56.10 — omnicolor/commlink0 reactions
- Sep 1, 2026Bump team-reflex/discord-php from 10.56.8 to 10.56.9 — omnicolor/commlink0 reactions · 1 comment
- Aug 26, 2026Bump team-reflex/discord-php from 10.55.1 to 10.56.5 — omnicolor/commlink0 reactions
- Aug 25, 2026Bump team-reflex/discord-php from 10.55.1 to 10.56.3 — omnicolor/commlink0 reactions · 1 comment
- Aug 18, 2026build(deps): update team-reflex/discord-php requirement from 10.49.* to 10.56.* — smrealms/smr0 reactions · 1 comment
- Apr 20, 2026Bump team-reflex/discord-php from 10.47.12 to 10.48.1 — crimsonstrife/streamer.live0 reactions · 1 comment
- Feb 10, 2026Bump team-reflex/discord-php from 10.45.15 to 10.45.16 — crimsonstrife/streamer.live0 reactions · 3 comments
- Nov 4, 2025Add Discord Announcement Bot System — k33bz/lastwar-server15860 reactions · 4 comments
- Oct 20, 2025Tag "@property" has error when both ? and null types are included — phpDocumentor/phpDocumentor0 reactions · 1 comment
- Nov 15, 2024Update dependency team-reflex/discord-php to v10 — System3-2/discord-bot0 reactions
- Feb 11, 2024Thread gets terminated when using a slash command with options. — DiscordBot-PMMP/DiscordBot0 reactions · 3 comments
- Jan 14, 2024Update dependency team-reflex/discord-php to v7.3.5 — Karhal/discord-gpt0 reactions
- Jan 14, 2024Update dependency team-reflex/discord-php to v7.3.5 — System3-2/discord-bot0 reactions
- Jan 31, 2023Invalid Activity URL — DiscordBot-PMMP/DiscordBot0 reactions · 3 comments
- Dec 20, 2022Another message type not supported when delete the message — DiscordBot-PMMP/DiscordBot0 reactions · 6 comments
- Aug 19, 2022[Egg Request]: DiscordPHP — pelican-eggs/eggs0 reactions · 1 comment
- Apr 17, 2022Intents help — DiscordBot-PMMP/DiscordBot0 reactions · 4 comments
On Bluesky · 2
- Feb 27, 2026📦 team-reflex/discord-php v10.45.21 An unofficial API to interact with the voice and text service Discord. 🔗… — @throwable.bsky.social1 likes and repost
- Aug 10, 2026📦 team-reflex/discord-php v10.55.0 An unofficial API to interact with the voice and text service Discord. 🔗… — @throwable.bsky.social0 likes and reposts · 1 reply
In this repository14 other files it gives its agents.
- AGENTS.mdAGENTS.md
- async-test-and-doc-syncSkill
- builder-payload-smithSkill
- gateway-cache-sync-keeperSkill
- helpers-and-infra-keeperSkill
- interaction-flow-keeperSkill
- legacy-command-client-keeperSkill
- repository-cache-keeperSkill
- runtime-bootstrap-keeperSkill
- type-map-keeperSkill
- voice-subsystem-keeperSkill
- discord-php-bot-securitySkill
- discord-php-extensionSkill
- discord-php-interactionsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
37 rules. About 0 are common, 21 are distinctive, and 16 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈21
Distinctive · estimate
This author's own choices. Worth a look.
16
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 11
Use this skill when work touches src/Discord/Parts//*.
- Very specific: line 17
Keep Part classes as the canonical in-memory representation of Discord resources:
- Very specific: line 40
Do not start from a random leaf class alone.
- Distinctive: line 44
Every real part sits on same base contract:
- Very specific: line 50
only mass-assigns keys in $fillable
- Distinctive: line 56
If change breaks one of those assumptions, stop and re-check layer ownership.
- Very specific: line 69
When adding new API field, first ask: should this be part of canonical stored state?
- Distinctive: line 83
If child data should behave like a repository, wire it here.
- Distinctive: line 124
create vs update differences
- Very specific: line 127
Prefer makeOptionalAttributes() to avoid serializing absent optional fields.
- Very specific: line 139
When repository ownership depends on parent IDs, also inspect getRepositoryAttributes().
- Distinctive: line 153
Only override when part can be refreshed independently from Discord and that contract is meaningful.
- Very specific: line 159
Use for timestamp-ish fields.
- Very specific: line 163
Use when field is a nested Discord object and you want lazy typed hydration with caching in $attributes.
- Distinctive: line 174
Use for homogeneous nested collections of one concrete part type.
- Distinctive: line 178
Use when payload element type decides subclass at runtime.
- Very specific: line 185
Prefer this over raw factory calls for nested child parts because it preserves created linkage.
- Distinctive: line 196
Keep both if the API provides IDs and the library benefits from relation convenience.
- Distinctive: line 204
If behavior applies horizontally across siblings, prefer trait:
- Distinctive: line 209
Do not introduce a new intermediate abstract class unless there is truly no cleaner trait shape.
- Distinctive: line 213
Update class docblock whenever you add:
- Distinctive: line 220
If code and docblock drift, IDE help and generated reference docs drift too.
- Distinctive: line 224
When making a part persistable or changing save behavior:
- Distinctive: line 243
When new Discord payload includes nested object:
- Very specific: line 245
Ask if repo already has a Part type for it
- Very specific: line 246
If yes, add field to $fillable
- Distinctive: line 247
Add getter mutator using helper
- Distinctive: line 249
If nested collection, choose homogeneous vs typed collection helper
- Very specific: line 250
If nested item needs parent IDs, pass extraData
- Distinctive: line 252
Do not leave meaningful nested objects as raw arrays if equivalent typed part exists.
- Distinctive: line 256
When child repository routes depend on parent context:
- Very specific: line 259
make sure parent part has required IDs in $attributes
- Distinctive: line 271
When a payload is an existing part's object plus a few fields, extend that part rather than copying its fields or adding the extras to it.
- Very specific: line 277
don't return parent::method() when the parent's declared type is broader.
- Very specific: line 278
don't hydrate through a TYPES map that also holds types outside it (Channel::TYPES maps thread types to Thread subclasses).
- Distinctive: line 282
keep the parent's exact signature, including by-reference parameters.
- Distinctive: line 289
Stop if you see:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Skill: part-model-maintainer
Use this skill when work touches src/Discord/Parts/**/*.
This is not syntax guard. This is domain-model guard. Load it when changing what a Discord resource is, how it is hydrated, how it exposes related objects, or how it saves itself.
Goal
Keep Part classes as the canonical in-memory representation of Discord resources:
- raw API-shaped data lives in attributes
- typed access lives through mutators and helpers
- persistence delegates to repositories
- permission and high-level domain rules stay close to the part
- public magic surface stays documented
Read in this order
src/Discord/Parts/Part.phpsrc/Discord/Parts/PartTrait.php- Representative concrete parts for the family you are touching:
src/Discord/Parts/Guild/Guild.phpsrc/Discord/Parts/Channel/Channel.phpsrc/Discord/Parts/Channel/Message/Message.phpsrc/Discord/Parts/User/User.phpsrc/Discord/Parts/Guild/Member/Member.phpsrc/Discord/Parts/Channel/Thread/Thread.phpsrc/Discord/Parts/Interactions/Interaction.php
- The owning repository for the part
- Gateway events that hydrate or update the part
Do not start from a random leaf class alone. PartTrait defines most real behavior.
Core contract
Every real part sits on same base contract:
- extends
Discord\Parts\Part - inherits
PartTrait - constructed with
(Discord $discord, array $attributes = [], bool $created = false) - stores raw Discord payload fields in
$attributes - only mass-assigns keys in
$fillable - exposes related repositories from
$repositories - resolves computed or typed values through
get{Studly}Attribute() - normalizes writes through
set{Studly}Attribute() - persists through
getRepository()+save()
If change breaks one of those assumptions, stop and re-check layer ownership.
Meaning of common properties
$fillable
Whitelist for fill() and dynamic writes. If field is missing here:
- gateway hydration ignores it
- REST fetch hydration ignores it
- direct
$part->field = ...writes do not persist to$attributes - magic property docblocks become misleading
When adding new API field, first ask: should this be part of canonical stored state? If yes, add it to $fillable.
$attributes
Raw storage. Usually snake_case and Discord-shaped. Avoid putting derived state here unless the API itself uses that field.
$repositories
Map of magic property name to repository class. This is how parts expose child collections like:
$guild->channels$channel->messages$message->reactions
If child data should behave like a repository, wire it here. Do not fake repo behavior with arrays.
$created
Tracks whether part already exists on Discord side.
falsemeans local draft or partial unsaved objecttruemeans known remote object
Repository save() uses this to choose POST vs PATCH. Nested createOf() links child created state back to parent. Preserve that when materializing nested parts.
Core methods and what they mean
fill(array $attributes)
Hydrates only fillable keys. It is not "dump every payload field in object". If new data is not appearing after fetch/event handling, check $fillable first.
getAttribute() / __get()
Resolution order matters:
- repository from
$repositories - getter mutator
- raw attribute
null
This is why magic properties can expose both repositories and computed values. Preserve this behavior when adding convenience fields.
__isset() / offsetExists()
Both return getAttribute($key) !== null, so isset(), empty() and ?? agree with __get(). Repositories and mutators count, and a computed relation such as guild is set only while it resolves. Collection::get($attr, $value) relies on this to match parts by any attribute. Inside a part, isset($this->guild_id) reads through the mutator too; use isset($this->attributes['x']) only when you mean the raw payload key. Do not call isset($this->x) from getXAttribute() itself: PHP's recursion guard makes that inner check false.
setAttribute() / __set()
Setter mutator first, then raw write only if key is fillable. This protects parts from accidental shape drift. Do not bypass it by writing stray protected properties for true resource state.
getCreatableAttributes() / getUpdatableAttributes()
These define what the repository sends to Discord. They are not debug dumps. They should express API intent:
- required vs optional
- create vs update differences
- omit optional values that were never set when API cares about missing vs null
Prefer makeOptionalAttributes() to avoid serializing absent optional fields.
getRepository()
Returns the owning repository for this part instance. This is frequently context-sensitive:
Guildreturns top-level$discord->guildsChannelchooses guild channels vs private channelsMessagechooses channel messages vs webhook messagesMemberdepends onguild_idThreaddepends on parent channel
When repository ownership depends on parent IDs, also inspect getRepositoryAttributes().
save(?string $reason = null)
Part-level save() is where high-level semantic guards live:
- permission checks
- ownership routing
- special-case endpoints for current user/current member/group DM/webhook cases
Rule: if logic needs knowledge of what operation means, it likely belongs here. If logic only knows how to execute REST+cache, it belongs in repository.
fetch()
Only override when part can be refreshed independently from Discord and that contract is meaningful. If part is not fetchable, default runtime exception is fine.
Helper methods to prefer
attributeCarbonHelper($key)
Use for timestamp-ish fields. Keeps repeated Carbon::parse logic out of concrete parts.
attributePartHelper($key, $class, $extraData = [])
Use when field is a nested Discord object and you want lazy typed hydration with caching in $attributes.
Good for:
- nested
User - nested
Guild - nested
Message - nested message metadata objects
attributeCollectionHelper($key, $class, ?string $discrim = 'id', ?array $extraData = [])
Use for homogeneous nested collections of one concrete part type.
attributeTypedCollectionHelper($class, $key)
Use when payload element type decides subclass at runtime. This is important for:
- message components
- other subtype families with
TYPESmaps
createOf(string $class, array|object $data)
Prefer this over raw factory calls for nested child parts because it preserves created linkage.
Part design patterns already used in repo
Raw ID plus resolved relation
Common pair:
- raw:
guild_id,channel_id,owner_id - resolved:
guild,channel,owner
Keep both if the API provides IDs and the library benefits from relation convenience.
Constants as public vocabulary
Large resource parts publish Discord enum values and flags as class constants. Keep aliases when the project already supports deprecated constant names for compatibility.
Traits for shared semantics
If behavior applies horizontally across siblings, prefer trait:
ChannelTraitGuildTrait
Do not introduce a new intermediate abstract class unless there is truly no cleaner trait shape.
PHPDoc as magic surface contract
Update class docblock whenever you add:
- fillable raw field
- computed read-only property
- repository property
- new typed collection
If code and docblock drift, IDE help and generated reference docs drift too.
Save and routing playbook
When making a part persistable or changing save behavior:
- Define or update
getCreatableAttributes() - Define or update
getUpdatableAttributes() - Define or update
getRepository() - Define or update
save() - Define or update
getRepositoryAttributes() - Inspect owning repository endpoint vars
- Inspect gateway events that hydrate/update the part
Examples worth copying
Channel::save()handles guild permission checks and group-DM special case before repository delegationMessage::save()handles send/manage permissions and webhook message routingMember::save()handles current-member special route instead of generic repository saveThread::save()blocks unsupported creation path and redirects callers toChannel::startThread()
Nested data playbook
When new Discord payload includes nested object:
- Ask if repo already has a
Parttype for it - If yes, add field to
$fillable - Add getter mutator using helper
- Add docblock type
- If nested collection, choose homogeneous vs typed collection helper
- If nested item needs parent IDs, pass
extraData
Do not leave meaningful nested objects as raw arrays if equivalent typed part exists.
Repository-binding playbook
When child repository routes depend on parent context:
- expose repository in
$repositories - make sure parent part has required IDs in
$attributes - override
getRepositoryAttributes()when raw$attributesis not enough or not in right shape - inspect repository constructor for
$varsassumptions
Examples:
- channels need
guild_idandchannel_id - messages need
channel_id, maybeguild_id, maybe webhook context - members need
guild_id
Extending a concrete part
When a payload is an existing part's object plus a few fields, extend that part rather than copying its fields or adding the extras to it. Example: the GAME_DIRECT_MESSAGE_* payload is Discord's message object with the DM channel attached, so GameDirectMessage extends Message.
- Fields: declare only the extra keys, and merge them into the inherited
$fillablein the constructor beforeparent::__construct(), so the subclass keeps up as the parent gains fields. - Narrow return types whenever possible. When an override can only ever return a subset of what the parent declares, declare the narrowest type it truly returns; PHP allows covariant return types.
Message::getChannelAttribute()returnsPartbecause a guild message's channel may be aThread, which extendsPart, notChannel. A game DM's channel is only aDMorGroupDM, soGameDirectMessage::getChannelAttribute()returnsChannel. - When it is not possible: the return type has already shipped on a class users may extend. A user subclass overriding the method with the old type would then fatal, so narrow it only in a major release. New classes and new overrides have no such constraint.
- Make every path honour the narrower type:
- don't
return parent::method()when the parent's declared type is broader; - don't hydrate through a
TYPESmap that also holds types outside it (Channel::TYPESmaps thread types toThreadsubclasses). Choose from the subset, falling back to one of its members.
- don't
- Inherited methods that cannot work in the subclass's context (for example, REST calls through a channel the bot is not in):
- override them to return
reject(new \BadMethodCallException(...)), naming the method and what to use instead, rather than sending a request Discord will refuse or throwing synchronously; - return
falsefrom boolean guards such asisDeletable(); - keep the parent's exact signature, including by-reference parameters.
- override them to return
- Tests:
- assert the narrowed type on every path, including a payload that would have produced a type outside it;
- assert each rejected method rejects without sending a request.
Smells
Stop if you see:
- an override declaring the parent's broad return type when it can only ever return a narrower one
- a subclass that copies its parent's
$fillableinstead of merging its extra keys - an inherited method left to fail at Discord, or to throw, when the subclass knows it can never work
- raw arrays where typed nested parts already exist
- new field added to docblock but not to
$fillable save()building raw endpoints even though repository exists- permission check added only in repository while part clearly knows semantic action
- part state stored in new ad hoc property instead of
$attributes - child repository added without
getRepositoryAttributes()support - subtype family added without updating a
TYPESmap or typed collection helper path
Checklist before commit
$fillablematches intended canonical fields- getter/setter mutators added where typing or normalization needed
$repositoriesupdated if new child collection exposedgetCreatableAttributes()/getUpdatableAttributes()express API semanticsgetRepository()andgetRepositoryAttributes()route correctlysave()enforces semantic permission or special-case behavior when needed- class docblock reflects public magic surface
- a subclass's overrides declare the narrowest return type every path honours
- related repository and gateway event code still coherent
- tests/docs updated if public behavior changed
Bottom line
Part classes in this repo are not dumb DTOs and not service objects. They are typed, lazily-resolved domain resources with controlled hydration and repository-backed persistence. Keep them centered on that job.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

