Enforces active voice and unique key naming for Simple History event logger classes.
Install
mkdir -p .claude/skills/logger-messages && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/5107" && unzip -o skill.zip -d .claude/skills/logger-messages && rm skill.zipInstalls to .claude/skills/logger-messages
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Enforces active voice for logger messages and the Event Details API. Use when writing a new logger class or modifying message arrays in getInfo().Key capabilities
- →Convert passive voice to active voice
- →Verify global message key uniqueness
- →Check for prohibited technical jargon
- →Standardize getInfo() array output formats
How it works
Validates message strings against linguistic constraints and runs filesystem searches to ensure uniqueness of identifier keys.
Inputs & outputs
When to use logger-messages
- →Creating a new logger class for a custom event
- →Fixing passive voice in existing getInfo() messages
- →Verifying message key uniqueness using grep
- →Standardizing logger output format
About this skill
Logger Message Guidelines
Write clear, user-friendly messages for Simple History event logs.
Core Principle: Active Voice
Write as if someone is telling you what they just did.
✅ DO ❌ DON'T
─────────────────────────────────────────────
Activated plugin Plugin was activated
Created menu Menu has been created
Updated settings Settings were updated
Published post Post has been published
In Logger Classes
public function getInfo() {
return [
'messages' => [
'plugin_activated' => __( 'Activated plugin', 'simple-history' ),
'plugin_deactivated' => __( 'Deactivated plugin', 'simple-history' ),
'post_updated' => __( 'Updated post "{post_title}"', 'simple-history' ),
],
];
}
Message Key Uniqueness
Keys must be globally unique across all loggers (used as RFC 5424 MSGID).
// ✅ Good - descriptive prefix
'plugin_activated', 'theme_switched', 'user_logged_in'
// ❌ Bad - too generic
'activated', 'updated', 'deleted'
Verify uniqueness: grep -r "'your_key'" loggers/
Common Verbs
- Create: Created, Added, Generated
- Modify: Updated, Changed, Edited
- Delete: Deleted, Removed, Trashed
- Toggle: Activated, Deactivated, Enabled, Disabled
Avoid
- ❌ "was [verb]" - passive
- ❌ "has been [verb]" - passive
- ❌ Technical jargon users won't understand
No Links Inside Message Text
The message body is a declarative sentence ("what happened?"). Action links (rendered below the message, see the action-links skill) are the canonical "what can I do?" affordance. Wrapping {post_title} (or any other interpolated token) in an <a> tag inside the template puts a CTA mid-sentence and competes with the action row.
Rule: Inline links inside message templates are permitted only when they point somewhere the action row cannot reach. If get_action_links() already covers the destination (Edit, View, Revisions, the overview page, …), the message must be plain text.
// ❌ Don't — Edit/View action links already point to the post.
'post_updated' => __( 'Updated post "<a href="...">{post_title}</a>"', 'simple-history' ),
// ✅ Do — plain title, action row handles navigation.
'post_updated' => __( 'Updated post "{post_title}"', 'simple-history' ),
Deleted items: still plain text. A dead link is worse than no link — the overview action link (All pages, All plugins) is the right hand-off.
Legitimate exception: the link goes somewhere no action link can express (e.g. an arbitrary external reference). Then an inline link is additive, not redundant.
Not an urgent migration — apply opportunistically when touching a logger for other reasons.
Context Key Naming
Prefix all context keys with the entity name to avoid collisions and keep keys self-documenting.
// ✅ Good - prefixed with entity
'plugin_name', 'plugin_current_version', 'theme_new_version'
'site_health_status', 'site_health_label', 'site_health_badge_label'
// ❌ Bad - too generic
'test', 'label', 'status', 'name', 'version'
Event Details Output
Use the Event Details API for get_log_row_details_output(). Never build raw HTML with SimpleHistoryLogitem__keyValueTable.
use Simple_History\Event_Details\Event_Details_Group;
use Simple_History\Event_Details\Event_Details_Group_Table_Formatter;
use Simple_History\Event_Details\Event_Details_Item;
public function get_log_row_details_output( $row ) {
$group = new Event_Details_Group();
$group->set_formatter( new Event_Details_Group_Table_Formatter() );
$group->add_items(
array(
// Read value directly from context key.
new Event_Details_Item( 'status', __( 'Status', 'simple-history' ) ),
// Read new/prev pair from context (looks for key_new and key_prev).
new Event_Details_Item( array( 'setting_name' ), __( 'Setting', 'simple-history' ) ),
)
);
return $group;
}
Formatters:
Event_Details_Group_Table_Formatter— key-value table (default)Event_Details_Group_Diff_Table_Formatter— before/after with diffsEvent_Details_Group_Inline_Formatter— compact inline text
Manual values (when context keys don't match conventions):
( new Event_Details_Item( null, __( 'Label', 'simple-history' ) ) )
->set_new_value( $value )
See docs/architecture/event-details.md for full API reference.
RAW Formatters (Escape Hatch)
When the structured API can't express your output (images, HTML content, color swatches):
Item_RAW_Formatter— Full custom HTML/JSON for an item (no name column)Item_Table_Row_RAW_Formatter— Table row with escaped name + raw HTML value
use Simple_History\Event_Details\Event_Details_Item_Table_Row_RAW_Formatter;
$raw_formatter = ( new Event_Details_Item_Table_Row_RAW_Formatter() )
->set_html_output( sprintf( '<a href="%1$s">%2$s</a>', esc_url( $url ), esc_html( $url ) ) )
->set_json_output( [ 'url' => $url ] );
$item = ( new Event_Details_Item( null, __( 'URL', 'simple-history' ) ) )
->set_formatter( $raw_formatter );
Use RAW formatters sparingly — only when no structured formatter fits.
Links Below Events: Use Action Links, Not Details
Navigational links (Edit, View, Preview) belong in get_action_links(), not inside get_log_row_details_output(). See the action-links skill.
Old loggers often embed <a> tags in the details table (e.g., "View/Edit" comment link, "View plugin info" thickbox). When migrating these loggers:
- Move navigational links to
get_action_links() - Keep only informational data in Event Details
The only case for a link inside details is when the value itself is a URL (e.g., a plugin's homepage URL displayed as data). Use Item_Table_Row_RAW_Formatter for that.
Migrating from Old HTML to Event Details
Many older loggers build HTML manually with SimpleHistoryLogitem__keyValueTable tables. When migrating:
| Old pattern | New approach |
|---|---|
<table class='SimpleHistoryLogitem__keyValueTable'> with <tr>/<td> | Event_Details_Group + Group_Table_Formatter |
<ins> / <del> for changed values | Event_Details_Item with set_values() (auto-generates ins/del) |
<span class='SimpleHistoryLogitem__inlineDivided'> | Event_Details_Group + Group_Inline_Formatter |
Inline <a href> links to edit/view | Move to get_action_links() |
| Images, color swatches, shortcode output | Item_RAW_Formatter or Item_Table_Row_RAW_Formatter |
Standalone <p> text blocks (not key-value) | Group_Inline_Formatter with a single item, or RAW formatter |
Value transforms (e.g., true → "Enabled", locale → display name): Transform in PHP, then pass to set_new_value() / set_prev_value().
Conditional rows: Don't set values for items you want hidden — the container auto-removes empty items.
Detailed Resources
- examples.md - Extensive examples across all WordPress contexts
- integration.md - Complete logger class implementation
When not to use it
- →When writing documentation, not event logs
- →When message text requires dynamic links
Prerequisites
Limitations
- →Cannot automatically identify all complex jargon
- →Limited to the scope of defined loggers directory
How it compares
It enforces strict grammatical and programmatic standards for log readability rather than relying on author intuition.
Compared to similar skills
logger-messages side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| logger-messages (this skill) | 1 | 2mo | No flags | Beginner |
| documentation-review | 11 | 5mo | No flags | Beginner |
| docs-review | 10 | 7mo | No flags | Beginner |
| workthrough | 10 | 8mo | Review | Beginner |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by bonny
View all by bonny →You might also like
documentation-review
stacklok
Reviews documentation for factual accuracy
docs-review
metabase
Review documentation changes for compliance with the Metabase writing style guide. Use when reviewing pull requests, files, or diffs containing documentation markdown files.
workthrough
bear2u
Automatically document all development work and code modifications in a structured workthrough format. Use this skill after completing any development task, bug fix, feature implementation, or code refactoring to create comprehensive documentation.
claude-md-improver
anthropics
Audit and improve CLAUDE.md files in repositories. Use when user asks to check, audit, update, improve, or fix CLAUDE.md files. Scans for all CLAUDE.md files, evaluates quality against templates, outputs quality report, then makes targeted updates. Also use when the user mentions "CLAUDE.md maintenance" or "project memory optimization".
anti-slop
rand
Comprehensive toolkit for detecting and eliminating "AI slop" - generic, low-quality AI-generated patterns in natural language, code, and design. Use when reviewing or improving content quality, preventing generic AI patterns, cleaning up existing content, or enforcing quality standards in writing, code, or design work.
agent-md-refactor
davila7
Refactor bloated AGENTS.md, CLAUDE.md, or similar agent instruction files to follow progressive disclosure principles. Splits monolithic files into organized, linked documentation.