# Self-registration

> How other modules can advertise their jobs to cronjob

---

Other modules can *advertise* their [scheduled jobs](../cli-scripts) so they show up in the admin UI without manual registry editing. Two ways (a module may use both); both are discovered automatically on each [tick](../tick), only for **enabled** modules:

## Marker method

`getCronJobs(): array` on the module's <a href="https://github.com/bschwede/wt-cronjob/tree/main/module.php" target="_blank" rel="noopener"><code>module.php</code></a>
 class, returning the same shape. Equivalent: `getModuleEvents(): array`, `getModuleCommands(): array`.

## Manifest file (recommended)

`modules_v4/<module>/cron-jobs.php` that returns an array of job specs:

<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">&lt;?</span>php
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// modules_v4/linkenhancer/cron-jobs.php
</span></span></span><span style="display:flex;"><span><span style="color:#c6a0f6">declare</span>(strict_types<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">return</span> [
</span></span><span style="display:flex;"><span>    [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;name&#39;</span>         <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;link-index&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;title&#39;</span>        <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;Update the link index&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;triggers&#39;</span>     <span style="color:#91d7e3;font-weight:bold">=&gt;</span> [
</span></span><span style="display:flex;"><span>            [<span style="color:#a6da95">&#39;type&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;time&#39;</span>, <span style="color:#a6da95">&#39;cron&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;*/30 * * * *&#39;</span>],
</span></span><span style="display:flex;"><span>        ],
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;command_type&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;module&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;command&#39;</span>      <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;modules_v4/linkenhancer/cli/build-link-index.php&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;args&#39;</span>         <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;--limit=5000&#39;</span>,
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>];</span></span></code></pre></div>

Translatable `title` / `description` literals in a manifest should be wrapped in
`MoreI18N::translate()` (identity marker)
so `xgettext` picks them up - translation happens at render time, never at manifest
load.

### Job spec fields

| Key | Required | Meaning |
| :--- | :--- | :--- |
| `name` | yes | slug `a-z 0-9 _ -`, max 64; stored as `<module>:<name>` |
| `title` | no | human name (defaults to `name`) |
| `triggers` | yes | list of trigger entries, at least one |
| `command_type` | yes | `module` or `core` |
| `command` | yes | the command |
| `args` | no | plain option/value tokens |
| `enabled` | no | default `false` — discovered jobs are created disabled |
| `timeout_sec` | no | default `300`, range 30 - 3600 |

Once a discovered job is inserted it is **admin-owned**: later ticks never overwrite it.

## Announcing events

The same manifest can also [**announce events**](../events) your module emits (so they show up in
the event catalog and the job form's suggestions). Instead of a plain job list, the
manifest may return an associative array with optional `jobs` and `events` lists (a
plain list still means "jobs only"):

<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">&lt;?</span>php
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// modules_v4/linkenhancer/cron-jobs.php
</span></span></span><span style="display:flex;"><span><span style="color:#c6a0f6">declare</span>(strict_types<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">return</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#39;jobs&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> [
</span></span><span style="display:flex;"><span>        [
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;name&#39;</span>     <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;link-index&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;triggers&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> [
</span></span><span style="display:flex;"><span>                [<span style="color:#a6da95">&#39;type&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;time&#39;</span>, <span style="color:#a6da95">&#39;cron&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;*/30 * * * *&#39;</span>],
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">// A job may combine several triggers:
</span></span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">// [&#39;type&#39; =&gt; &#39;event&#39;, &#39;event&#39; =&gt; &#39;linkenhancer:index-dirty&#39;],
</span></span></span><span style="display:flex;"><span>            ],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;command_type&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;module&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;command&#39;</span>      <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;modules_v4/linkenhancer/cli/build-link-index.php&#39;</span>,
</span></span><span style="display:flex;"><span>        ],
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#39;events&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> [
</span></span><span style="display:flex;"><span>        [<span style="color:#a6da95">&#39;name&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;index-dirty&#39;</span>, <span style="color:#a6da95">&#39;description&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;The link index is out of date.&#39;</span>, <span style="color:#a6da95">&#39;payload&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> [<span style="color:#a6da95">&#39;tree&#39;</span>]],
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>];</span></span></code></pre></div>

- An announced event is namespaced **`<module>:<name>`** (like the offered job keys).
- An event trigger's `event` value that carries no colon is auto-namespaced
  `<module>:<event>`; a name that already has a colon (e.g.
  `cronjob:log-edit-update`) is kept as-is.
- Equivalent marker method: `getModuleEvents(): array` (same event-spec shape).

### Event spec fields

| Key | Required | Meaning |
| :--- | :--- | :--- |
| `name` | yes | slug `a-z 0-9 _ -` (stored as `<module>:<name>`) |
| `description` | no | shown in the event catalog (translation key, max 255) |
| `payload` | no | list of payload parameter names (max 8), shown in the catalog |

## Announcing commands

The same manifest can also **announce commands** a job may run, with their available
parameters. A plain manifest list is still "jobs only"; to announce commands the
manifest returns the associative form with a `commands` list:

<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// modules_v4/linkenhancer/cron-jobs.php
</span></span></span><span style="display:flex;"><span><span style="color:#c6a0f6">declare</span>(strict_types<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">return</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#39;jobs&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> [ <span style="color:#6e738d;font-style:italic">/* … */</span> ],
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#39;commands&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> [
</span></span><span style="display:flex;"><span>        [
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;command&#39;</span>      <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;modules_v4/linkenhancer/cli/build-link-index.php&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">// &#39;command_type&#39; =&gt; &#39;module&#39;,  // optional - derived from the command value
</span></span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;description&#39;</span>  <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;Rebuild the link index incrementally.&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;params&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> [
</span></span><span style="display:flex;"><span>                [<span style="color:#a6da95">&#39;name&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;--limit&#39;</span>, <span style="color:#a6da95">&#39;optional&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#c6a0f6">true</span>, <span style="color:#a6da95">&#39;default&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;5000&#39;</span>, <span style="color:#a6da95">&#39;description&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;max records per run&#39;</span>],
</span></span><span style="display:flex;"><span>                [<span style="color:#a6da95">&#39;name&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;--since&#39;</span>, <span style="color:#a6da95">&#39;optional&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#c6a0f6">true</span>, <span style="color:#a6da95">&#39;description&#39;</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#a6da95">&#39;only changes since this timestamp&#39;</span>],
</span></span><span style="display:flex;"><span>            ],
</span></span><span style="display:flex;"><span>        ],
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>];</span></span></code></pre></div>

### Command spec fields

| Key | Required | Meaning |
| :--- | :--- | :--- |
| `command` | yes | a `modules_v4/…/cli/*.php` path or an allowlisted core command, max 255 |
| `command_type` | no | `module` or `core` (derived from `command` when omitted) |
| `description` | no | shown in the command catalog (translation key, max 255) |
| `params` | no | list of parameter descriptors (max 8) |

Each parameter descriptor has `name` (the option token, e.g. `--limit`), `optional`
(bool, default `true` — set `false` for a required option), `default` (an optional
value) and `description` (optional). Equivalent marker method:
`getModuleCommands(): array` (same command-spec shape).

> **Curation rule:** a module that announces commands **curates its own command list** —
> the glob-based discovery no longer lists *its* scripts, so internal scripts
> (<a href="https://github.com/bschwede/wt-cronjob/tree/main/cli/tick.php" target="_blank" rel="noopener"><code>tick.php</code></a>
, <a href="https://github.com/bschwede/wt-cronjob/tree/main/cli/watch.php" target="_blank" rel="noopener"><code>watch.php</code></a>
, <a href="https://github.com/bschwede/wt-cronjob/tree/main/cli/wrap.php" target="_blank" rel="noopener"><code>wrap.php</code></a>
, <a href="https://github.com/bschwede/wt-cronjob/tree/main/cli/smoke-job.php" target="_blank" rel="noopener"><code>smoke-job.php</code></a>
) are not offered as job
> commands. A module that announces nothing falls back to glob discovery of its
> `cli/` scripts (backward compatible).
