> **messages** — skill 21 of 54 in [github/gh-aw](https://skillsdocs.com/github/gh-aw).
>
> Book (all skills, one file): https://skillsdocs.com/github/gh-aw.md
> Machine manifest: https://skillsdocs.com/github/gh-aw/.well-known/agent-skills/index.json
> Install the book: `npx skills add github/gh-aw`
> Upstream: https://github.com/github/gh-aw/blob/main/.github/skills/messages/SKILL.md @ `main`
> Raw bytes, no header: https://raw.githubusercontent.com/github/gh-aw/main/.github/skills/messages/SKILL.md
> Base for relative paths: https://raw.githubusercontent.com/github/gh-aw/main/.github/skills/messages/
> Licence: MIT — https://spdx.org/licenses/MIT.html
>
> Content © its authors, served unmodified. Takedown: https://github.com/DreambaseAI/skillsdocs/issues/new?labels=takedown&title=Takedown+request

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: messages
description: Add new safe-output message types and wire validation/rendering.
---


# Adding New Message Types Guide

Use this guide to add a new safe-output message type so it works in the current gh-aw pipeline: frontmatter → schema → Go compiler → JavaScript modules → action/workflow build output.

## Overview

The messages system lets workflow authors customize safe-output messages. The current architecture does not rely on the old `pkg/workflow/js.go` embedding registry for runtime shipping.

Current flow:

1. **Frontmatter** (YAML)
2. **JSON Schema**
3. **Go Compiler**
4. **JavaScript module** under `pkg/workflow/js/` or `actions/setup/js/`
5. **Action/workflow bundle generation** via `make actions-build` or the relevant workflow build path

## Step 1: Update JSON Schema

Add the new message field to `pkg/parser/schemas/main_workflow_schema.json` in the `messages` object:

```json
{
	"messages": {
		"properties": {
		  "my-new-message": {
		    "type": "string",
		    "description": "Description of when this message is used. Available placeholders: {placeholder1}, {placeholder2}.",
		    "examples": [
		      "Example message with {placeholder1}"
		    ]
		  }
		}
	}
}
```

**Key points:**
- Use `kebab-case` for the YAML field name (for example `my-new-message`)
- Document placeholders in the description
- Provide helpful examples
- Rebuild the schema-backed binary or run the relevant compile checks after changes

## Step 2: Update Go Struct

Add the field to `SafeOutputMessagesConfig` in `pkg/workflow/compiler.go`:

```go
type SafeOutputMessagesConfig struct {
	// ... existing fields ...
	MyNewMessage string `yaml:"my-new-message,omitempty" json:"myNewMessage,omitempty"`
}
```

**Key points:**
- Use `CamelCase` for Go field names
- Use `kebab-case` for YAML tags
- Use `camelCase` for JSON tags
- Add `omitempty` to both tags

## Step 3: Update the parser if needed

If the message needs custom parsing logic, update the workflow parser in `pkg/workflow/safe_outputs.go` or the relevant config block. Most simple string fields will be wired automatically by the existing reflection-based parser.

## Step 4: Create the JavaScript message module

Create the new module in the current shared JS location, typically `pkg/workflow/js/`:

```javascript
// @ts-check
/// <reference types="@actions/github-script" />

const { getMessages, renderTemplate, toSnakeCase } = require("./messages_core.cjs");

/**
 * @typedef {Object} MyNewMessageContext
 * @property {string} placeholder1 - Description of placeholder1
 * @property {string} placeholder2 - Description of placeholder2
 */

function getMyNewMessage(ctx) {
	const messages = getMessages();
	const templateContext = toSnakeCase(ctx);
	const defaultMessage = "Default message with {placeholder1} and {placeholder2}";

	return messages?.myNewMessage
		? renderTemplate(messages.myNewMessage, templateContext)
		: renderTemplate(defaultMessage, templateContext);
}

module.exports = {
	getMyNewMessage,
};
```

**Key points:**
- File naming: `messages_<category>.cjs`
- Reuse `./messages_core.cjs` for shared helpers
- Use JSDoc for types and default behavior
- Keep the default message sensible and deterministic

## Step 5: Add tests

Create a matching test file, for example `pkg/workflow/js/messages_my_new.test.cjs`:

```javascript
import { describe, it, expect, beforeEach, vi } from "vitest";

const mockCore = { warning: vi.fn() };
global.core = mockCore;

describe("getMyNewMessage", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		delete process.env.GH_AW_SAFE_OUTPUT_MESSAGES;
	});

	it("returns the default message when no custom template is configured", async () => {
		const { getMyNewMessage } = await import("./messages_my_new.cjs");
		const result = getMyNewMessage({ placeholder1: "value1", placeholder2: "value2" });
		expect(result).toBe("Default message with value1 and value2");
	});

	it("uses the custom template when configured", async () => {
		process.env.GH_AW_SAFE_OUTPUT_MESSAGES = JSON.stringify({ myNewMessage: "Custom: {placeholder1}" });
		const { getMyNewMessage } = await import("./messages_my_new.cjs");
		const result = getMyNewMessage({ placeholder1: "test", placeholder2: "ignored" });
		expect(result).toContain("Custom: test");
	});
});
```

Run the relevant tests with `make test-js` or the targeted Vitest file.

## Step 6: Update the core JS type metadata and exports

Update the `SafeOutputMessages` typedef and the return object in `pkg/workflow/js/messages_core.cjs`, and re-export the message helper from `pkg/workflow/js/messages.cjs`.

## Step 7: Wire it into the real build path

Do not add any new `//go:embed` entries to `pkg/workflow/js.go` for a normal message module. The current system packages JavaScript through the action-generation/build path.

Instead:

- keep the JS module in `pkg/workflow/js/` or the relevant action folder,
- update the action dependency map or action source if needed,
- rebuild the action bundle with `make actions-build`.

## Step 8: Use the message in consumer scripts

```javascript
const { getMyNewMessage } = require("./messages_my_new.cjs");

const message = getMyNewMessage({
	placeholder1: actualValue1,
	placeholder2: actualValue2,
});
```

## Step 9: Update documentation

Document the new message in the repo’s relevant safe-output docs, and keep the examples aligned with the current action-based JavaScript build flow.

## Verification Checklist

Before committing a message change:

- [ ] Frontmatter and schema updated
- [ ] Go config/struct updated if needed
- [ ] JS module created under the correct source tree
- [ ] Tests added and passing
- [ ] `messages_core.cjs` and `messages.cjs` updated if relevant
- [ ] Generated action/build output refreshed when required
- [ ] No stale embedding instructions are introduced for the current action-based JS build flow

## References

- `actions/README.md` - current action-generation/build workflow
- `pkg/workflow/js/messages_core.cjs` - shared safe-output message helpers
- `pkg/workflow/js/messages.cjs` - message exports
- `pkg/parser/schemas/main_workflow_schema.json` - schema source of truth

Update the Message Module Architecture table:
```markdown
| Module | Purpose | Exported Functions |
|--------|---------|-------------------|
| `messages_my_new.cjs` | My new message description | `getMyNewMessage` |
```

## Notes

For current gh-aw work, keep message modules aligned with the action-generation flow instead of the historical Go-embed pattern. If you need an example, review the existing safe-output modules under `pkg/workflow/js/` and the generated action files under `actions/`.
