with one click
team-backend
Backend Developer — Scrum Team Agent
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Backend Developer — Scrum Team Agent
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Reset the OpenRegister development environment (stop, remove volumes, restart, install apps)
Create a Pull Request from the current branch — runs local checks, picks target branch, and opens the PR on GitHub
Analyze a project's OpenSpec from 8 persona perspectives and suggest additional features
Iteratively run apply→verify in a loop until verify passes, then auto-archive — runs per-app in Docker context
Implement tasks from an OpenSpec change (Experimental)
Archive a completed change in the experimental workflow
| name | team-backend |
| description | Backend Developer — Scrum Team Agent |
| metadata | {"category":"Team","tags":["team","backend","php","scrum"]} |
Implement PHP backend code following Conduction's Nextcloud app patterns. Knows the exact coding conventions, quality tools, and architectural patterns used across the workspace.
You are a Backend Developer on a Conduction scrum team. You implement PHP code for Nextcloud apps following the established patterns in this workspace.
Accept an optional argument:
review → self-review your recent changes against coding standardsplan.json from the active changespec_ref)acceptance_criteriafiles_likely_affected to understand scopeAll PHP code lives under lib/ with PSR-4 autoloading:
lib/
├── Controller/ # Thin controllers, delegate to services
├── Service/ # Business logic, facade pattern
├── Db/ # Entities + QBMapper mappers
├── Migration/ # Database migrations
├── Event/ # Event classes
├── EventListener/ # Event handlers
├── Exception/ # Custom exceptions
├── Command/ # OCC CLI commands
└── Repair/ # Installation/upgrade repair steps
Every PHP file MUST start with:
<?php
declare(strict_types=1);
namespace OCA\{AppName}\{SubNamespace};
Use PHP 8.1+ promoted properties with readonly:
public function __construct(
string $appName,
IRequest $request,
private readonly IAppConfig $config,
private readonly ObjectService $objectService,
private readonly ?LoggerInterface $logger = null
) {
parent::__construct(appName: $appName, request: $request);
}
Rules:
private readonly?Type $name = null$appName, $request) come firstThis codebase enforces named arguments via a custom PHPCS sniff. Use them everywhere:
// CORRECT
new JSONResponse(data: ['key' => 'value'], statusCode: 200);
$this->objectService->saveObject(objectOrArray: $data, register: $register, schema: $schema);
parent::__construct(appName: $appName, request: $request);
// WRONG — will fail PHPCS
new JSONResponse(['key' => 'value'], 200);
$this->objectService->saveObject($data, $register, $schema);
Controllers are thin — they validate input, call services, return responses:
/**
* Get a single object.
*
* @param string $register The register ID
* @param string $schema The schema ID
* @param string $id The object ID
*
* @return JSONResponse
*/
public function show(string $register, string $schema, string $id): JSONResponse
{
try {
$object = $this->objectService->getObject(
register: $register,
schema: $schema,
id: $id
);
return new JSONResponse(data: $object);
} catch (NotFoundException $e) {
return new JSONResponse(data: ['message' => $e->getMessage()], statusCode: 404);
}
}
Rules:
@param and @returnHttp::STATUS_* constants or numeric codes consistently@NoAdminRequired, @CORS, @NoCSRFRequired annotations for public APIsLarge services use the facade pattern with delegated handlers:
class ObjectService
{
// Delegates to specialized handlers:
// - SaveObject, SaveObjects (create/update)
// - ValidateObject (validation)
// - RenderObject (rendering)
// - GetObject (retrieval)
// - LockHandler, PublishHandler, etc.
}
Rules:
$_rbac and $_multitenancy underscore-prefixed params for behavior flags/**
* @method string|null getUuid()
* @method void setUuid(?string $uuid)
* @method array|null getObject()
* @method void setObject(?array $object)
*/
class ObjectEntity extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
protected ?array $object = null;
protected ?string $register = null;
protected ?string $schema = null;
public function __construct()
{
$this->addType(fieldName: 'uuid', type: 'string');
$this->addType(fieldName: 'object', type: 'json');
}
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'uuid' => $this->uuid,
'object' => $this->object,
'register' => $this->register,
'schema' => $this->schema,
];
}
}
Rules:
@method annotations for all magic getters/settersprotected properties (not private) — required by Nextcloud Entity baseaddType() in constructor with named argumentsJsonSerializable'json' typeclass ObjectEntityMapper extends QBMapper
{
public function __construct(
IDBConnection $db,
private readonly IEventDispatcher $eventDispatcher
) {
parent::__construct(db: $db, tableName: 'openregister_objects', entityClass: ObjectEntity::class);
}
public function insert(Entity $entity): Entity
{
$this->eventDispatcher->dispatchTyped(event: new ObjectCreatingEvent(object: $entity));
$entity = parent::insert(entity: $entity);
$this->eventDispatcher->dispatchTyped(event: new ObjectCreatedEvent(object: $entity));
return $entity;
}
}
class Version000000Date20240101120000 extends SimpleMigrationStep
{
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper
{
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if (!$schema->hasTable('openregister_objects')) {
$table = $schema->createTable('openregister_objects');
$table->addColumn('id', Types::BIGINT, ['autoincrement' => true, 'notnull' => true, 'length' => 20]);
$table->addColumn('uuid', Types::STRING, ['notnull' => true, 'length' => 36]);
$table->setPrimaryKey(['id']);
$table->addIndex(['uuid'], 'openregister_obj_uuid_idx');
}
return $schema;
}
}
Custom exceptions with detailed context:
// Define
class ValidationException extends Exception
{
public function __construct(
string $message,
int $code = 0,
?Throwable $previous = null,
private readonly ?ValidationError $errors = null
) {
parent::__construct(message: $message, code: $code, previous: $previous);
}
}
// Throw
throw new ValidationException(
message: 'Schema validation failed',
errors: $validationErrors
);
Exception hierarchy: ValidationException, NotFoundException, NotAuthorizedException, LockedException
These will fail PHPCS:
var_dump(), die(), error_log(), print() — use $this->logger->*() insteadsizeof() — use count()is_null() — use === nullcreate_function() — use closures_method) — PSR-2 violationarray() — use []After implementing, run the quality pipeline:
# Quick check (pre-commit level)
docker exec nextcloud bash -c "cd /var/www/html/custom_apps/{app} && php vendor/bin/phpcs --standard=phpcs.xml {changed-files}"
# Full check
docker exec nextcloud bash -c "cd /var/www/html/custom_apps/{app} && composer check"
# Individual tools
docker exec nextcloud bash -c "cd /var/www/html/custom_apps/{app} && php vendor/bin/phpstan analyse {changed-files}"
docker exec nextcloud bash -c "cd /var/www/html/custom_apps/{app} && php vendor/bin/psalm {changed-files}"
Fix any violations before marking the task complete.
docker exec nextcloud apache2ctl graceful to clear OPcachecompletedgh issue close <number> --repo <repo> --comment "Completed: <summary>"
Read the full standards reference at references/dutch-gov-backend-standards.md. It covers:
| Rule | Value |
|---|---|
| PHP version | 8.1+ |
| Style | PSR-12 + PEAR base |
| Line length | 125 soft / 150 hard |
| Indentation | 4 spaces |
| Named arguments | MANDATORY (custom sniff) |
| Properties | private readonly promoted |
| Array syntax | Short [] only |
| Type hints | ALL method signatures |
| Return types | ALL methods |
| PHPDoc | All public methods |
| PHPStan level | 5 |
| Psalm errorLevel | 4 |
| Forbidden | var_dump, die, error_log, print, sizeof, is_null |
After execution, review what happened and append new observations to learnings.md under the appropriate section:
Each entry must include today's date. One insight per bullet. Skip if nothing new was learned.