---
title: "Developer"
manual: "nr_repurpose"
version: "main"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-repurpose:developer@main"
source: "Developer/Index.rst"
rendered: "2026-09-30T16:39:51+00:00"
---

# Developer {#developer}

This chapter is for developers who work on the extension itself or extend it.
How a job flows through the pipeline is described in [Architecture](https://docs.typo3.org/permalink/netresearch/nr-repurpose:architecture@main), and
the reasons behind the main design choices in [Architecture Decision Records](https://docs.typo3.org/permalink/netresearch/nr-repurpose:adr@main).

## Local environment {#developer-environment}

The repository ships a DDEV setup with the system binaries and a worker
container (see [Local development with DDEV](https://docs.typo3.org/permalink/netresearch/nr-repurpose:installation-ddev@main)). Use it to run the backend module and
the worker. The tests do not run inside DDEV; they have their own runner.

## Running the tests {#developer-tests}

Every test and quality tool runs through `Build/Scripts/runTests.sh`,
which starts the TYPO3 core-testing Docker images. The script in the
repository is a small bootstrap: on a fresh clone it runs
`composer install` and then hands over to the shared runner that
`netresearch/typo3-ci-workflows` installs as `.Build/bin/runTests.sh`.

**Test suites and quality tools**

```bash
./Build/Scripts/runTests.sh -s unit                  # unit tests
./Build/Scripts/runTests.sh -s functional            # functional tests (SQLite)
./Build/Scripts/runTests.sh -s functional -d mariadb # functional tests against MariaDB
./Build/Scripts/runTests.sh -s lint                  # PHP lint
./Build/Scripts/runTests.sh -p 8.3 -s cgl -n         # code style check
./Build/Scripts/runTests.sh -s phpstan               # static analysis
./Build/Scripts/runTests.sh -s rector -n             # Rector dry run
```

`-n` makes `cgl` and `rector` report only; without it they rewrite the
files. The default PHP version is 8.5; `-p` selects another one. Run the
code-style check on PHP 8.3, because the CI code-style job uses the first PHP
version of its matrix. PHPStan runs at level 8 over `Classes/`, as
configured in `phpstan.neon`.

## Adding a generator {#developer-generator}

A generator produces the artifacts of one type. The orchestrator runs exactly
the services tagged `nr_repurpose.artifact_generator`; a class that is not
tagged is never run, even when it implements the interface.

1.  Implement `NetresearchNrRepurposeGeneratorArtifactGeneratorInterface`
    with its two methods:

    -   `supports(GenerationContext $ctx): bool` — whether the job asked for
        this artifact, usually by reading the job's `want_*` flag from
        `$ctx->jobRow`;
    -   `generate(GenerationContext $ctx): bool` — inserts the generator's own
        artifact rows, fills them, and returns whether it succeeded.

    Extend `AbstractGenerator` rather than starting from scratch. It
    provides the Fluid theme rendering, per-run temporary directories,
    `failArtifact()` and `specializedAllowed()`, the budget and
    availability check for text-to-speech and image calls.
    `AbstractTextGenerator` is the base for a structured text format, and
    `AbstractDocumentGenerator` for a document printed to PDF.
    `PodcastGenerator` is the reference for a generator that combines an
    LLM call with specialized calls and local rendering.
1.  Register the class in `Configuration/Services.yaml` with the tag, the
    same way as the existing generators:

    **Configuration/Services.yaml**

    ```yaml
    Netresearch\NrRepurpose\Generator\MyGenerator:
      public: true
      tags: ['nr_repurpose.artifact_generator']
    ```
1.  Check the capability grants before a speech or image call
    (`$ctx->grants->audio`, `$ctx->grants->vision`, see [ADR-008: Capability Permissions Checked Before Spend](https://docs.typo3.org/permalink/netresearch/nr-repurpose:adr-008@main)),
    and call `specializedAllowed()` before spending on them.
1.  Name the pipeline step on every nr-llm call with a constant from
    `NetresearchNrRepurposeServiceCallerSource`, so nr-llm's analytics
    attribute its cost (see [Cost attribution](https://docs.typo3.org/permalink/netresearch/nr-repurpose:architecture-generation-attribution@main)).

One failing generator does not stop the others: record the failure on the
artifact with `failArtifact()` and return `false`.

## Another image or speech backend {#developer-backends}

The generators never call nr-llm's image or speech services directly. They use
`ImageGeneratorInterface` and `SpeechSynthesizerInterface`, which
`Configuration/Services.yaml` aliases to `DallEImageGenerator` and
`OpenAiSpeechSynthesizer`. A different backend is a new adapter behind the
interface and a changed alias; the adapter still reaches the provider through
nr-llm, which holds the keys (see [ADR-003: Provider Credentials Delegated to nr-llm](https://docs.typo3.org/permalink/netresearch/nr-repurpose:adr-003@main)).
