chore: remove legacy tools/codex-skills (moved to .agents/skills), split filter drawer tests, clean up stale docs
- tools/codex-skills/ removed — skills now live in .agents/skills/ - docs/reference-agents-*.md removed — replaced by .agents/workflow.md - FilterDrawerContractTest split into Runtime + Template contract tests - bin scripts and .gitignore updated for .agents/ paths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -13,6 +13,10 @@ composer.phar
|
|||||||
config/config.php
|
config/config.php
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
# Agent workflow runs (ephemeral artifacts)
|
||||||
|
/.agents/runs/*
|
||||||
|
!/.agents/runs/.gitkeep
|
||||||
|
|
||||||
# Runtime & generated
|
# Runtime & generated
|
||||||
/web/debugger/
|
/web/debugger/
|
||||||
/data/
|
/data/
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ fi
|
|||||||
|
|
||||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
repo_root="$(cd -- "${script_dir}/.." && pwd)"
|
repo_root="$(cd -- "${script_dir}/.." && pwd)"
|
||||||
source_dir="${repo_root}/tools/codex-skills"
|
source_dir="${repo_root}/.agents/skills"
|
||||||
codex_home_dir="${CODEX_HOME:-${HOME}/.codex}"
|
codex_home_dir="${CODEX_HOME:-${HOME}/.codex}"
|
||||||
target_dir="${codex_home_dir}/skills"
|
target_dir="${codex_home_dir}/skills"
|
||||||
managed_skills=(
|
managed_skills=(
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ tmp_file="$(mktemp)"
|
|||||||
trap 'rm -f "${tmp_file}"' EXIT
|
trap 'rm -f "${tmp_file}"' EXIT
|
||||||
|
|
||||||
rg -n -o '(?:/docs|docs)/[a-z0-9_/-]+\.md' \
|
rg -n -o '(?:/docs|docs)/[a-z0-9_/-]+\.md' \
|
||||||
README.md docs tools/codex-skills agent-system \
|
README.md docs .agents \
|
||||||
-g '*.md' \
|
-g '*.md' \
|
||||||
-g '!agent-system/runs/**' \
|
-g '!.agents/runs/**' \
|
||||||
> "${tmp_file}" || true
|
> "${tmp_file}" || true
|
||||||
|
|
||||||
missing=0
|
missing=0
|
||||||
@@ -33,4 +33,4 @@ if [[ ${missing} -ne 0 ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[OK] docs links resolve (agent-system/runs/** excluded by default)"
|
echo "[OK] docs links resolve (.agents/runs/** excluded by default)"
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
# Agent Workflow
|
|
||||||
|
|
||||||
Letzte Aktualisierung: 2026-03-06
|
|
||||||
|
|
||||||
## State Machine
|
|
||||||
- `planned`
|
|
||||||
- `executing`
|
|
||||||
- `review_guard`
|
|
||||||
- `review_acceptance`
|
|
||||||
- `finalize`
|
|
||||||
- `done`
|
|
||||||
|
|
||||||
If a reviewer returns `FAIL`, state goes back to `executing` with explicit findings.
|
|
||||||
|
|
||||||
## Minimum Handover Artifacts
|
|
||||||
- Planner: `plan.json`
|
|
||||||
- Executor: `execution-report.json`
|
|
||||||
- Reviewer Guards: `review-guards.json`
|
|
||||||
- Reviewer Acceptance: `review-acceptance.json`
|
|
||||||
- Finalizer: `finalize.json`
|
|
||||||
|
|
||||||
## Guard and Gate Binding
|
|
||||||
- Planner selects required guard IDs from `agent-system/checks/guard-catalog.json`.
|
|
||||||
- Planner selects required quality gate IDs from `agent-system/checks/quality-gates.json`.
|
|
||||||
- Planner assigns stable success criteria IDs (`SC-*`) in `plan.json`.
|
|
||||||
- Executor must provide guard evidence and gate results by ID.
|
|
||||||
- Guard reviewer verifies and reports verdict against the same IDs.
|
|
||||||
- Acceptance reviewer verifies and reports verdict against the same `SC-*` IDs.
|
|
||||||
|
|
||||||
## Fail Loop Rule
|
|
||||||
- No free-text "please improve".
|
|
||||||
- Every fail must include:
|
|
||||||
- `id`
|
|
||||||
- `severity`
|
|
||||||
- `file`
|
|
||||||
- expected fix
|
|
||||||
|
|
||||||
## Practical Rollout
|
|
||||||
- Start with one small issue.
|
|
||||||
- Run workflow manually with templates.
|
|
||||||
- Stabilize contracts first.
|
|
||||||
- Add automation only after 3 to 5 successful runs.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Agent Workflow Overview
|
|
||||||
|
|
||||||
Letzte Aktualisierung: 2026-03-09
|
|
||||||
|
|
||||||
This project uses a multi-role workflow for issue and feature delivery.
|
|
||||||
|
|
||||||
Roles:
|
|
||||||
- Planner
|
|
||||||
- Executor
|
|
||||||
- Reviewer (guards and best practices)
|
|
||||||
- Reviewer (feature acceptance)
|
|
||||||
- Finalizer
|
|
||||||
|
|
||||||
Primary goals:
|
|
||||||
- predictable handovers
|
|
||||||
- strict quality gates
|
|
||||||
- fast retry loop from reviewers back to executor
|
|
||||||
- explicit guard and gate IDs (`GR-*`, `QG-*`) across all roles
|
|
||||||
|
|
||||||
Source of truth:
|
|
||||||
- guardrails and planning standards in `tools/codex-skills/`
|
|
||||||
- contracts and templates in `agent-system/`
|
|
||||||
- guard catalog: `agent-system/checks/guard-catalog.json`
|
|
||||||
- quality gates: `agent-system/checks/quality-gates.json`
|
|
||||||
|
|
||||||
Entry points:
|
|
||||||
- roles: `/docs/reference-agents-roles.md`
|
|
||||||
- workflow: `/docs/reference-agents-flow.md`
|
|
||||||
- schemas and prompts: `agent-system/`
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# Agent Roles
|
|
||||||
|
|
||||||
Letzte Aktualisierung: 2026-03-06
|
|
||||||
|
|
||||||
## Planner
|
|
||||||
- Input: issue or feature request
|
|
||||||
- Output: `plan.json` (see `agent-system/contracts/planner.schema.json`)
|
|
||||||
- Must define:
|
|
||||||
- scope in and out
|
|
||||||
- required guard IDs (`GR-*`)
|
|
||||||
- required quality gate IDs (`QG-*`)
|
|
||||||
- success criteria with stable IDs (`SC-*`)
|
|
||||||
- implementation steps
|
|
||||||
- test and acceptance checks
|
|
||||||
- risks and mitigations
|
|
||||||
|
|
||||||
## Executor
|
|
||||||
- Input: approved `plan.json`
|
|
||||||
- Output: `execution-report.json`
|
|
||||||
- Must:
|
|
||||||
- implement the plan
|
|
||||||
- run relevant checks
|
|
||||||
- document changed files and commands
|
|
||||||
|
|
||||||
## Reviewer Guards
|
|
||||||
- Input: code diff and `execution-report.json`
|
|
||||||
- Output: `review-guards.json`
|
|
||||||
- Focus:
|
|
||||||
- architecture boundaries
|
|
||||||
- security
|
|
||||||
- coding standards
|
|
||||||
- guardrail compliance
|
|
||||||
- explicit validation against required `GR-*` and `QG-*` IDs
|
|
||||||
|
|
||||||
## Reviewer Acceptance
|
|
||||||
- Input: code diff and plan success criteria
|
|
||||||
- Output: `review-acceptance.json`
|
|
||||||
- Focus:
|
|
||||||
- feature correctness
|
|
||||||
- edge cases
|
|
||||||
- acceptance criteria coverage
|
|
||||||
- explicit check mapping by `SC-*` criterion IDs from `plan.json`
|
|
||||||
|
|
||||||
## Finalizer
|
|
||||||
- Input: both review files with PASS verdict
|
|
||||||
- Output: `finalize.json`
|
|
||||||
- Must:
|
|
||||||
- verify gates are green
|
|
||||||
- prepare merge and commit metadata
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MintyPHP\Tests\Architecture;
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class FilterDrawerContractTest extends TestCase
|
|
||||||
{
|
|
||||||
use ProjectFileAssertionSupport;
|
|
||||||
|
|
||||||
private function readTemplatePageModule(string $templateFile): string
|
|
||||||
{
|
|
||||||
$template = $this->readProjectFile($templateFile);
|
|
||||||
$matched = preg_match(
|
|
||||||
"/assetVersion\\('((?:modules\\/[^\\/]+\\/)?js\\/pages\\/[^']+\\.js)'\\)/",
|
|
||||||
$template,
|
|
||||||
$captures
|
|
||||||
);
|
|
||||||
$this->assertSame(1, $matched, $templateFile . ' must reference a page module via assetVersion().');
|
|
||||||
|
|
||||||
return $this->readProjectFile('web/' . ltrim($captures[1], '/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function usesSharedListFiltersPartial(string $content): bool
|
|
||||||
{
|
|
||||||
return str_contains($content, "require templatePath('partials/app-list-filters.phtml');");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return list<string>
|
|
||||||
*/
|
|
||||||
private function drawerTemplateFiles(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'modules/addressbook/pages/address-book/index(default).phtml',
|
|
||||||
'pages/admin/users/index(default).phtml',
|
|
||||||
'pages/admin/tenants/index(default).phtml',
|
|
||||||
'pages/admin/departments/index(default).phtml',
|
|
||||||
'pages/admin/roles/index(default).phtml',
|
|
||||||
'pages/admin/permissions/index(default).phtml',
|
|
||||||
'pages/admin/mail-log/index(default).phtml',
|
|
||||||
'pages/admin/scheduled-jobs/index(default).phtml',
|
|
||||||
'pages/admin/api-audit/index(default).phtml',
|
|
||||||
'pages/admin/import-audit/index(default).phtml',
|
|
||||||
'pages/admin/user-lifecycle-audit/index(default).phtml',
|
|
||||||
'pages/admin/system-audit/index(default).phtml',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDrawerTemplatesUseStandardListWrapperForFilterExperience(): void
|
|
||||||
{
|
|
||||||
foreach ($this->drawerTemplateFiles() as $file) {
|
|
||||||
$content = $this->readProjectFile($file);
|
|
||||||
$moduleContent = $this->readTemplatePageModule($file);
|
|
||||||
$usesPartial = $this->usesSharedListFiltersPartial($content);
|
|
||||||
$this->assertStringContainsString('initStandardListPage(', $moduleContent, $file);
|
|
||||||
$this->assertStringContainsString("mode: 'drawer'", $moduleContent, $file);
|
|
||||||
$this->assertStringNotContainsString('initListFilterExperience(', $moduleContent, $file);
|
|
||||||
if (!$usesPartial) {
|
|
||||||
$this->assertStringContainsString('role="dialog"', $content, $file);
|
|
||||||
$this->assertStringContainsString('aria-modal="true"', $content, $file);
|
|
||||||
$this->assertStringContainsString('aria-labelledby="', $content, $file);
|
|
||||||
$this->assertStringContainsString('data-filter-live-region', $content, $file);
|
|
||||||
}
|
|
||||||
$this->assertStringNotContainsString('data-filter-drawer-draft-hint', $content, $file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAddressBookFilterChipsContract(): void
|
|
||||||
{
|
|
||||||
$content = $this->readProjectFile('modules/addressbook/pages/address-book/index(default).phtml');
|
|
||||||
$moduleContent = $this->readProjectFile('modules/addressbook/web/js/pages/address-book-index.js');
|
|
||||||
|
|
||||||
$this->assertStringNotContainsString('const state = collectFilterState();', $moduleContent);
|
|
||||||
$this->assertStringContainsString('data-filter-chips-clear-all-label', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-chips-remove-label', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-live-applied-message', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-live-removed-message', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-live-cleared-message', $content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUsersResetKeepsTenantParam(): void
|
|
||||||
{
|
|
||||||
$moduleContent = $this->readProjectFile('web/js/pages/admin-users-index.js');
|
|
||||||
$this->assertStringContainsString("preserveFilterParams: ['tenant']", $moduleContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testFilterDrawerAndExperienceImplementFocusTrapAndDirtyApplyUi(): void
|
|
||||||
{
|
|
||||||
$drawerJs = $this->readProjectFile('web/js/components/app-filter-drawer.js');
|
|
||||||
$this->assertStringContainsString("drawer.setAttribute('role', 'dialog')", $drawerJs);
|
|
||||||
$this->assertStringContainsString("drawer.setAttribute('aria-modal', 'true')", $drawerJs);
|
|
||||||
$this->assertStringContainsString("if (event.key !== 'Tab') {return;}", $drawerJs);
|
|
||||||
$this->assertStringContainsString('if (lastTrigger && typeof lastTrigger.focus === \'function\'', $drawerJs);
|
|
||||||
|
|
||||||
$experienceJs = $this->readProjectFile('web/js/pages/app-list-filter-experience.js');
|
|
||||||
$this->assertStringContainsString('countDraftChanges(', $experienceJs);
|
|
||||||
$this->assertStringContainsString('countActiveDraftFilters(', $experienceJs);
|
|
||||||
$this->assertStringContainsString('drawerController.setApplyEnabled(changedCount > 0);', $experienceJs);
|
|
||||||
$this->assertStringContainsString('drawerController.setApplyCount(activeCount);', $experienceJs);
|
|
||||||
$this->assertStringContainsString('announceLive(', $experienceJs);
|
|
||||||
|
|
||||||
$stateJs = $this->readProjectFile('web/js/pages/app-list-filter-state.js');
|
|
||||||
$this->assertStringContainsString('buildChipsFromMeta', $stateJs);
|
|
||||||
$this->assertStringContainsString('removeChipFromMetaState', $stateJs);
|
|
||||||
$this->assertStringContainsString('clearMetaState', $stateJs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSharedListFiltersPartialKeepsDrawerAccessibilityContract(): void
|
|
||||||
{
|
|
||||||
$content = $this->readProjectFile('templates/partials/app-list-filters.phtml');
|
|
||||||
$this->assertStringContainsString('renderGridFilterToolbar(', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-drawer-open', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-drawer-apply', $content);
|
|
||||||
$this->assertStringContainsString('data-active-filter-chips', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-chips-clear-all-label', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-chips-remove-label', $content);
|
|
||||||
$this->assertStringContainsString('role="dialog"', $content);
|
|
||||||
$this->assertStringContainsString('aria-modal="true"', $content);
|
|
||||||
$this->assertStringContainsString('aria-labelledby=', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-live-region', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-live-applied-message', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-live-removed-message', $content);
|
|
||||||
$this->assertStringContainsString('data-filter-live-cleared-message', $content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
37
tests/Architecture/FilterDrawerRuntimeContractTest.php
Normal file
37
tests/Architecture/FilterDrawerRuntimeContractTest.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class FilterDrawerRuntimeContractTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProjectFileAssertionSupport;
|
||||||
|
|
||||||
|
public function testUsersResetKeepsTenantParam(): void
|
||||||
|
{
|
||||||
|
$moduleContent = $this->readProjectFile('web/js/pages/admin-users-index.js');
|
||||||
|
$this->assertStringContainsString("preserveFilterParams: ['tenant']", $moduleContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFilterDrawerAndExperienceImplementFocusTrapAndDirtyApplyUi(): void
|
||||||
|
{
|
||||||
|
$drawerJs = $this->readProjectFile('web/js/components/app-filter-drawer.js');
|
||||||
|
$this->assertStringContainsString("drawer.setAttribute('role', 'dialog')", $drawerJs);
|
||||||
|
$this->assertStringContainsString("drawer.setAttribute('aria-modal', 'true')", $drawerJs);
|
||||||
|
$this->assertStringContainsString("if (event.key !== 'Tab') {return;}", $drawerJs);
|
||||||
|
$this->assertStringContainsString('if (lastTrigger && typeof lastTrigger.focus === \'function\'', $drawerJs);
|
||||||
|
|
||||||
|
$experienceJs = $this->readProjectFile('web/js/pages/app-list-filter-experience.js');
|
||||||
|
$this->assertStringContainsString('countDraftChanges(', $experienceJs);
|
||||||
|
$this->assertStringContainsString('countActiveDraftFilters(', $experienceJs);
|
||||||
|
$this->assertStringContainsString('drawerController.setApplyEnabled(changedCount > 0);', $experienceJs);
|
||||||
|
$this->assertStringContainsString('drawerController.setApplyCount(activeCount);', $experienceJs);
|
||||||
|
$this->assertStringContainsString('announceLive(', $experienceJs);
|
||||||
|
|
||||||
|
$stateJs = $this->readProjectFile('web/js/pages/app-list-filter-state.js');
|
||||||
|
$this->assertStringContainsString('buildChipsFromMeta', $stateJs);
|
||||||
|
$this->assertStringContainsString('removeChipFromMetaState', $stateJs);
|
||||||
|
$this->assertStringContainsString('clearMetaState', $stateJs);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
tests/Architecture/FilterDrawerTemplateContractTest.php
Normal file
59
tests/Architecture/FilterDrawerTemplateContractTest.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class FilterDrawerTemplateContractTest extends TestCase
|
||||||
|
{
|
||||||
|
use ListContractFiles;
|
||||||
|
use ProjectFileAssertionSupport;
|
||||||
|
|
||||||
|
public function testDrawerTemplatesExposeDrawerAccessibilityHooks(): void
|
||||||
|
{
|
||||||
|
foreach ($this->drawerListTemplates() as $file) {
|
||||||
|
$content = $this->readProjectFile($file);
|
||||||
|
$usesPartial = $this->usesSharedListFiltersPartial($content);
|
||||||
|
|
||||||
|
if (!$usesPartial) {
|
||||||
|
$this->assertStringContainsString('role="dialog"', $content, $file);
|
||||||
|
$this->assertStringContainsString('aria-modal="true"', $content, $file);
|
||||||
|
$this->assertStringContainsString('aria-labelledby="', $content, $file);
|
||||||
|
$this->assertStringContainsString('data-filter-live-region', $content, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertStringNotContainsString('data-filter-drawer-draft-hint', $content, $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddressBookFilterChipsContract(): void
|
||||||
|
{
|
||||||
|
$content = $this->readProjectFile('modules/addressbook/pages/address-book/index(default).phtml');
|
||||||
|
$moduleContent = $this->readProjectFile('modules/addressbook/web/js/pages/address-book-index.js');
|
||||||
|
|
||||||
|
$this->assertStringNotContainsString('const state = collectFilterState();', $moduleContent);
|
||||||
|
$this->assertStringContainsString('data-filter-chips-clear-all-label', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-chips-remove-label', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-live-applied-message', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-live-removed-message', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-live-cleared-message', $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSharedListFiltersPartialKeepsDrawerAccessibilityContract(): void
|
||||||
|
{
|
||||||
|
$content = $this->readProjectFile('templates/partials/app-list-filters.phtml');
|
||||||
|
$this->assertStringContainsString('renderGridFilterToolbar(', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-drawer-open', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-drawer-apply', $content);
|
||||||
|
$this->assertStringContainsString('data-active-filter-chips', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-chips-clear-all-label', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-chips-remove-label', $content);
|
||||||
|
$this->assertStringContainsString('role="dialog"', $content);
|
||||||
|
$this->assertStringContainsString('aria-modal="true"', $content);
|
||||||
|
$this->assertStringContainsString('aria-labelledby=', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-live-region', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-live-applied-message', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-live-removed-message', $content);
|
||||||
|
$this->assertStringContainsString('data-filter-live-cleared-message', $content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
---
|
|
||||||
name: core-guardrails
|
|
||||||
description: Verbindliche Guardrails fuer Architektur-, Refactor-, Zentralisierungs-, Standardisierungs- und Implementierungsaufgaben in diesem Starterkit. Verwenden, wenn Code geaendert, neu strukturiert oder gegen bestehende Layer-/UI-/Security-Standards geprueft werden soll.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Core Guardrails
|
|
||||||
|
|
||||||
## Ziel
|
|
||||||
|
|
||||||
Durchgaengig dieselben technischen Grenzen durchsetzen, damit Core robust, wartbar und vorhersagbar bleibt.
|
|
||||||
|
|
||||||
## Verbindliche Regeln
|
|
||||||
|
|
||||||
1. MUST Layering einhalten (`pages` -> `Service` -> `Repository`, Templates nur Rendering).
|
|
||||||
2. MUST Request/Input/Validation-Contract einhalten (`requestInput()`, `FormErrors`, API-Validation ueber `ApiResponse::validationFromFormErrors(...)`, kein `ApiResponse::readJsonBody(...)` in `pages/api/v1/**`, keine direkten Superglobals in `pages/**`).
|
|
||||||
3. MUST AuthZ/RBAC serverseitig durchsetzen und im UI nur ueber `$viewAuth` steuern.
|
|
||||||
4. MUST Security-Guards einhalten (CSRF auf POST-Endpunkten, keine unredacted PII/Secrets in Logs/Audit-Meta, SQL nur vorbereitet ueber Repository).
|
|
||||||
5. MUST UI-Standards und Data-Contracts fuer Listen/Detailseiten nutzen (Wrapper, Partials, globaler Confirm-Dialog statt `window.confirm`, inklusive globaler Grid-Standards fuer rowInteraction/pageSize).
|
|
||||||
6. MUST Frontend-Hardcuts einhalten (keine inline ESM-Module in Templates/Pages, JS-`src` mit `assetVersion(...)`, zentrale Telemetrie fuer relevante Frontend-Warnings/Fetch-Fehler).
|
|
||||||
7. MUST Quality-Gates ausfuehren und Ergebnis transparent berichten.
|
|
||||||
8. MUST bei Konflikten den regelkonformen Weg waehlen oder den Konflikt als Blocker markieren.
|
|
||||||
9. MUST NOT Extension-Architektur erfinden; dieses Thema ist aktuell Out of Scope.
|
|
||||||
|
|
||||||
## Vorgehen je Aufgabe
|
|
||||||
|
|
||||||
1. Scope bestimmen (`core`, `ui`, `api`, `mixed`).
|
|
||||||
2. Vorhandene Contracts zuerst suchen, dann wiederverwenden.
|
|
||||||
3. Aenderung minimal halten, keine Seiteneffekte ohne Grund.
|
|
||||||
4. Nach Aenderung die relevanten Gates ausfuehren.
|
|
||||||
5. Rest-Risiken explizit benennen.
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- Extension-Mechanik (Ablageorte, Hooks, Lifecycle) wird hier nicht standardisiert.
|
|
||||||
- Fuer Extensions nur den Status "nicht definiert" dokumentieren, keine impliziten Regeln erfinden.
|
|
||||||
|
|
||||||
## Referenzen
|
|
||||||
|
|
||||||
- Core-Boundaries: `references/boundaries-core.md`
|
|
||||||
- UI-Boundaries: `references/boundaries-ui.md`
|
|
||||||
- Quality-Gates: `references/quality-gates.md`
|
|
||||||
- Source-Map zu Projekt-Doku: `references/source-map.md`
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# Core Boundaries (MUST / MUST NOT)
|
|
||||||
|
|
||||||
## Schichtgrenzen
|
|
||||||
|
|
||||||
1. MUST SQL nur in `lib/Repository/**` halten.
|
|
||||||
2. MUST Business-Logik in `lib/Service/**` halten.
|
|
||||||
3. MUST `pages/**` auf Input, Auth/AuthZ, Orchestrierung, Response begrenzen.
|
|
||||||
4. MUST NOT Business- oder Permission-Logik in `templates/**` legen.
|
|
||||||
|
|
||||||
## Instanziierung
|
|
||||||
|
|
||||||
1. MUST in Actions/Helpern/Templates nur `app(Foo::class)` verwenden.
|
|
||||||
2. MUST NOT in `pages/**` direkt `new ...Service|Gateway|Repository` nutzen.
|
|
||||||
3. MUST NOT in `lib/Service/**` (ausser `*Factory.php`) direkt `new ...Factory|Service|Gateway|Repository` nutzen.
|
|
||||||
4. MUST NOT in `pages/admin/**` `Factory::class` referenzieren oder `app(...Factory::class)->create...` nutzen.
|
|
||||||
|
|
||||||
## Input / Validation
|
|
||||||
|
|
||||||
1. MUST Input in `pages/**` ueber `requestInput()` lesen.
|
|
||||||
2. MUST NOT `$_POST`, `$_GET`, `$_FILES`, `REQUEST_METHOD` in `pages/**` verwenden.
|
|
||||||
3. MUST NOT `$_SESSION`, `$_SERVER`, `$_COOKIE`, `$_ENV` in `pages/**` direkt verwenden.
|
|
||||||
4. MUST Form-Validation ueber `FormErrors` fuehren.
|
|
||||||
5. MUST API-Validation-Fehler ueber `ApiResponse::validationFromFormErrors(...)` ausgeben.
|
|
||||||
6. MUST NOT `ApiResponse::readJsonBody(...)` in `pages/api/v1/**` verwenden.
|
|
||||||
|
|
||||||
## List-Data Endpoints
|
|
||||||
|
|
||||||
1. MUST `pages/**/data().php` als GET-only Endpunkte umsetzen (`gridRequireGetRequest()`).
|
|
||||||
2. MUST Query-Filter ueber `gridParseFilters(...)` + `filter-schema.php` normalisieren.
|
|
||||||
3. MUST NOT Query-Filter inline/parallele Alias-Parser in `data().php` neu einfuehren.
|
|
||||||
|
|
||||||
## Security / API
|
|
||||||
|
|
||||||
1. MUST AuthZ serverseitig erzwingen.
|
|
||||||
2. MUST Tenant-Scope serverseitig erzwingen.
|
|
||||||
3. MUST CSRF auf POST-Endpunkten vor Body-Verarbeitung verifizieren.
|
|
||||||
4. MUST NOT PII/Secrets unredacted in Logs oder Audit-Metadata schreiben.
|
|
||||||
5. MUST SQL nur ueber Repository mit prepared statements ausfuehren.
|
|
||||||
6. MUST extern UUID-first bleiben.
|
|
||||||
7. MUST API-Fehler konsistent halten (`error`, optional `errors`).
|
|
||||||
8. MUST OpenAPI im selben Merge aktualisieren, wenn API geaendert wird.
|
|
||||||
|
|
||||||
## Pruef-Hinweis
|
|
||||||
|
|
||||||
- Harte Repo-Gates: `tests/Architecture/CoreStarterkitContractTest.php`
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# UI Boundaries (MUST / MUST NOT)
|
|
||||||
|
|
||||||
## Listen-Standard
|
|
||||||
|
|
||||||
1. MUST Listen ueber `initStandardListPage(...)` initialisieren.
|
|
||||||
2. MUST Filter-Toolbar ueber zentrale Helper/Partials rendern.
|
|
||||||
3. MUST aktive Chips/Drawer-Verhalten ueber Standard-Contracts laufen lassen.
|
|
||||||
4. MUST NOT direkt `gridFiltersFromSchema(...)` oder `initListFilterExperience(...)` in Listen-Templates verwenden.
|
|
||||||
5. MUST NOT Legacy-Filter-Toggle-Markup (`data-toolbar-toggle`, `data-filter-overflow`, `data-filter-toggle`) neu einbauen.
|
|
||||||
6. MUST rowInteraction-Standard fuer Listen mit Row-Navigation beibehalten (sichtbare Action-Spalte + Enter + Doppelklick-Fallback).
|
|
||||||
7. MUST pageSize-Standard beibehalten (10/25/50/100, Toolbar rechts, URL > localStorage > default).
|
|
||||||
|
|
||||||
## Detailseiten-Standard
|
|
||||||
|
|
||||||
1. MUST Hauptformular mit `data-standard-detail-form="1"` markieren.
|
|
||||||
2. MUST Unsaved/Shortcut/Dirty-State ueber `initStandardDetailPage(...)` nutzen.
|
|
||||||
3. MUST Detail-Aktionen ueber Action-Policy-Contract (`data-detail-action-policy`, `data-detail-action-kind`, `data-detail-confirm-message`) steuern.
|
|
||||||
4. MUST NOT inline `confirm(...)` (`onclick` / `onsubmit`) auf standardisierten Detailseiten nutzen.
|
|
||||||
|
|
||||||
## Confirm + Telemetry Standard
|
|
||||||
|
|
||||||
1. MUST Confirm-Dialoge zentral ueber den globalen Confirm-Service abwickeln.
|
|
||||||
2. MUST NOT `window.confirm(...)` in App-JS oder inline HTML-Handlern verwenden.
|
|
||||||
3. MUST relevante Frontend-Warnpfade zentral ueber Frontend-Telemetrie erfassen.
|
|
||||||
4. MUST NOT relevante UX-/Fehlerpfade nur als Console-Warnung implementieren.
|
|
||||||
|
|
||||||
## Frontend Page-Module Hard Cut
|
|
||||||
|
|
||||||
1. MUST Seiteninitialisierung ueber dedizierte Module unter `web/js/pages/*` umsetzen.
|
|
||||||
2. MUST Page-Config ueber `script[type="application/json"]` + `readPageConfig(...)` bereitstellen/lesen.
|
|
||||||
3. MUST alle JS-`src` in `pages/**` und `templates/**` ueber `assetVersion('...js...')` laden.
|
|
||||||
4. MUST NOT inline `<script type="module">...</script>` in `pages/**` oder `templates/**` verwenden.
|
|
||||||
|
|
||||||
## Component Lifecycle Runtime
|
|
||||||
|
|
||||||
1. MUST migrierte Komponenten ueber `init(root, config)` + `destroy()` standardisieren.
|
|
||||||
2. MUST NOT migrierte Komponenten per `DOMContentLoaded`/Module-Sideeffect selbst auto-initialisieren.
|
|
||||||
3. MUST Listener, Timer und Observer, die in `init(...)` entstehen, in `destroy()` wieder abbauen.
|
|
||||||
4. MUST Runtime-Konfiguration ueber Page-Config (`readPageConfig(...)`) beziehen, keine verteilten Ad-hoc-Globals.
|
|
||||||
5. MUST host-gebundene UI-Komponenten ueber explizite `data-app-component`-Hosts im Markup binden.
|
|
||||||
6. Global-Utilities ohne natuerliches Host-Element MAY via Runtime `scope: 'global'` registriert werden, MUESSEN aber denselben Lifecycle-Contract inkl. `destroy()` einhalten.
|
|
||||||
|
|
||||||
## Shared Partials
|
|
||||||
|
|
||||||
1. MUST Listen-Titlebar ueber `templates/partials/app-list-titlebar.phtml` rendern.
|
|
||||||
2. MUST List-Tabs ueber `templates/partials/app-list-tabs.phtml` rendern.
|
|
||||||
3. MUST Purge-Action ueber `templates/partials/app-list-purge-action.phtml` rendern.
|
|
||||||
4. MUST Detail-Validation-Summary ueber `templates/partials/app-details-validation-summary.phtml` rendern.
|
|
||||||
|
|
||||||
## RBAC in Templates
|
|
||||||
|
|
||||||
1. MUST UI-Capabilities nur ueber `$viewAuth` lesen.
|
|
||||||
2. MUST NOT `can('...')` in Templates verwenden.
|
|
||||||
|
|
||||||
## CSS Boundaries
|
|
||||||
|
|
||||||
1. MUST Styles moeglichst lokal in `web/css/components`, `web/css/layout` oder `web/css/pages` halten.
|
|
||||||
2. MUST NOT globale unscoped Overrides ohne klaren Scope einfuehren.
|
|
||||||
3. MUST Vendor-Overrides nur in `web/css/vendor-overrides/**` pflegen.
|
|
||||||
4. MUST bestehende CSS-Variablen bevorzugen statt neue Inline-Farbwerte zu verteilen.
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Quality Gates
|
|
||||||
|
|
||||||
## Pflicht-Gates
|
|
||||||
|
|
||||||
1. `docker compose exec php vendor/bin/phpunit`
|
|
||||||
2. `docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress`
|
|
||||||
3. `docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php`
|
|
||||||
|
|
||||||
## Struktur-Gates (schnell)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
(rg 'new\s+[^\s(]+Factory\(' lib/Service || true) | wc -l
|
|
||||||
(rg --glob '!**/*Factory.php' 'new\s+[^\s(]+(Service|Gateway|Repository)\(' lib/Service || true) | wc -l
|
|
||||||
(rg "\b(accessServicesFactory|directoryServicesFactory|userServicesFactory|authServicesFactory|tenantServicesFactory|settingServicesFactory|userRepositoryFactory|authRepositoryFactory|authGatewayFactory)\(" pages lib templates web || true) | wc -l
|
|
||||||
(rg -n 'new\s+[^\s(]+(Service|Gateway|Repository)\(' pages -g '*.php' || true) | wc -l
|
|
||||||
(rg -n '\bDB::' pages -g '*.php' || true) | wc -l
|
|
||||||
(rg -n 'app\([^\n]*Factory::class\)->create' pages/admin -g '*.php' || true) | wc -l
|
|
||||||
(rg -n 'Factory::class' pages/admin -g '*.php' || true) | wc -l
|
|
||||||
(rg -n 'ApiResponse::readJsonBody\(' pages/api/v1 -g '*.php' || true) | wc -l
|
|
||||||
```
|
|
||||||
|
|
||||||
Erwartung: jeweils `0`.
|
|
||||||
|
|
||||||
## JS-Scope
|
|
||||||
|
|
||||||
- Bei JS-Aenderungen Browser-Smoke mit DevTools-Console durchfuehren.
|
|
||||||
- Erwartung: keine neuen JS-Errors.
|
|
||||||
|
|
||||||
## Docs-/Skills-Gates (schnell)
|
|
||||||
|
|
||||||
1. `bin/docs-link-check.sh`
|
|
||||||
- prueft Doku-Links in `README.md`, `docs/**`, `tools/codex-skills/**` und `agent-system/**`
|
|
||||||
- `agent-system/runs/**` ist standardmaessig ausgeschlossen (historische Artefakte)
|
|
||||||
2. `bin/codex-skills-sync.sh --check`
|
|
||||||
- prueft Drift zwischen Repo-Skills und `~/.codex/skills`
|
|
||||||
|
|
||||||
## Berichtspflicht
|
|
||||||
|
|
||||||
- Bei jedem Gate klar angeben: `passed` / `failed` / `not run`.
|
|
||||||
- Bei `failed` den blocker explizit benennen.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Source Map (Single Source of Truth)
|
|
||||||
|
|
||||||
## Kernregeln
|
|
||||||
|
|
||||||
- Architektur- und Schichtgrenzen: `/docs/explanation-architektur.md`
|
|
||||||
- `lib/**`-Regeln und Instanziierung: `/docs/reference-lib-standards.md`
|
|
||||||
- Kurzkonventionen: `/docs/reference-konventionen.md`
|
|
||||||
- Request/Input/Validation: `/docs/howto-request-input-validation.md`
|
|
||||||
|
|
||||||
## UI-/Frontend-Contracts
|
|
||||||
|
|
||||||
- JavaScript-Standards und Wrapper: `/docs/reference-frontend-javascript.md`
|
|
||||||
- CSS-Standards und Scope-Regeln: `/docs/reference-frontend-css.md`
|
|
||||||
- DoD-Checks: `/docs/reference-entwickler-checkliste.md`
|
|
||||||
- Rollen-Workflow (Planner/Executor/Reviewer/Finalizer): `/docs/reference-agents-overview.md`
|
|
||||||
|
|
||||||
## Security / RBAC
|
|
||||||
|
|
||||||
- RBAC-Erweiterungen: `/docs/howto-rbac-permissions-playbook.md`
|
|
||||||
- Sicherheitsmodell: `/docs/explanation-sicherheitsmodell.md`
|
|
||||||
|
|
||||||
## Betrieb und Qualitaetschecks
|
|
||||||
|
|
||||||
- Entwickler-Checkliste: `/docs/reference-entwickler-checkliste.md`
|
|
||||||
- Architektur-Request-Flow: `/docs/tutorial-04-architektur-request-flow.md`
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
name: starterkit-grid-standards
|
|
||||||
description: Standardisierungs-Skill fuer GridJS-Listen im Starterkit. Verwenden, wenn Listen neu gebaut oder refactored werden sollen (rowInteraction, pageSize, filter drawer, URL-state, accessibility).
|
|
||||||
---
|
|
||||||
|
|
||||||
# Starterkit Grid Standards
|
|
||||||
|
|
||||||
## Ziel
|
|
||||||
|
|
||||||
GridJS-Listen projektweit einheitlich, zuganglich und wartbar halten.
|
|
||||||
|
|
||||||
## Wann verwenden
|
|
||||||
|
|
||||||
1. Neue Listen mit `initStandardListPage(...)` oder `createServerGrid(...)`.
|
|
||||||
2. Refactor von Legacy-Listen.
|
|
||||||
3. Review von UX/Accessibility in Listen.
|
|
||||||
|
|
||||||
## Verbindliche Regeln
|
|
||||||
|
|
||||||
1. MUST `initStandardListPage(...)` als Standard nutzen (nur begruendete Ausnahmen direkt mit `createServerGrid(...)`).
|
|
||||||
2. MUST `rowDblClick.getUrl(...)` fuer zeilenbezogene Navigation setzen, wenn Row-Navigation vorgesehen ist.
|
|
||||||
3. MUST globale `rowInteraction`-Defaults respektieren (Action-Spalte, Enter, Doppelklick-Fallback).
|
|
||||||
4. MUST globale `pageSize`-Standards respektieren (`10/25/50/100`, Toolbar-Placement, URL > storage > default).
|
|
||||||
5. MUST Filter-Drawer/Chips ueber den Standard-Contract nutzen, keine parallelen Custom-Patterns.
|
|
||||||
6. MUST NOT pro Seite eigene Row-Action-Hacks bauen, wenn der Factory-Contract ausreicht.
|
|
||||||
|
|
||||||
## Vorgehen
|
|
||||||
|
|
||||||
1. Ist-Liste gegen `references/list-checklist.md` pruefen.
|
|
||||||
2. Fehlende Standards zuerst zentral in der Factory loesen, danach nur minimale Seitenanpassungen.
|
|
||||||
3. UI/CSS nur als additive Erweiterung zu bestehenden Komponenten.
|
|
||||||
4. Relevante Gates und Browser-Smoke (Keyboard + Console) ausfuehren.
|
|
||||||
|
|
||||||
## Referenzen
|
|
||||||
|
|
||||||
- Checkliste: `references/list-checklist.md`
|
|
||||||
- Row Interaction: `references/row-interaction-standard.md`
|
|
||||||
- Page Size: `references/page-size-standard.md`
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
interface:
|
|
||||||
display_name: "Starterkit Grid Standards"
|
|
||||||
short_description: "Standardize GridJS lists and accessibility patterns"
|
|
||||||
default_prompt: "Use $starterkit-grid-standards to standardize a list page with rowInteraction, pageSize, and filter drawer contracts."
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# List Checklist
|
|
||||||
|
|
||||||
## Bootstrap
|
|
||||||
|
|
||||||
1. `initStandardListPage({ grid, filters, hooks })` verwendet.
|
|
||||||
2. `filterSchema` aus Server-Config uebergeben.
|
|
||||||
3. `urlSync` bewusst gesetzt.
|
|
||||||
|
|
||||||
## Interaction
|
|
||||||
|
|
||||||
1. `rowDblClick.getUrl(...)` liefert stabile Ziel-URL oder leer.
|
|
||||||
2. Keine Doppel-Action-Spalte (Factory auto-action vs explizite actions).
|
|
||||||
3. Enter/Pointer/Focus fuer oeffnbare Rows funktionieren.
|
|
||||||
|
|
||||||
## Pagination + URL
|
|
||||||
|
|
||||||
1. Page-size Optionen nur `10/25/50/100`.
|
|
||||||
2. URL Sync pruefen (`page`, `order`, `dir`, `limit`).
|
|
||||||
3. Default-Werte erzeugen saubere URL.
|
|
||||||
|
|
||||||
## Filter UX
|
|
||||||
|
|
||||||
1. Search sichtbar.
|
|
||||||
2. Drawer-Filter und Chips intakt.
|
|
||||||
3. Reset/Apply Verhalten reproduzierbar.
|
|
||||||
|
|
||||||
## Regression
|
|
||||||
|
|
||||||
1. Sortierung, Selection/Bulk, Doppelklick unveraendert.
|
|
||||||
2. Keine neuen Console Errors.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# Page Size Standard
|
|
||||||
|
|
||||||
## Globale Defaults
|
|
||||||
|
|
||||||
1. Optionen: `10`, `25`, `50`, `100`.
|
|
||||||
2. Default: `10`.
|
|
||||||
3. Prioritaet: `URL > localStorage > Default`.
|
|
||||||
4. Placement: obere `app-list-toolbar`, rechts ausgerichtet.
|
|
||||||
|
|
||||||
## URL Contract
|
|
||||||
|
|
||||||
1. Param: `limit`.
|
|
||||||
2. Bei Default-Limit optional clean URL (Param entfernen).
|
|
||||||
3. Bei Hard-Reload muss gesetztes URL-Limit reproduzierbar sein.
|
|
||||||
|
|
||||||
## Verhalten
|
|
||||||
|
|
||||||
1. Aenderung der Seitengroesse setzt Seite auf 1.
|
|
||||||
2. Sortierung/Filter bleiben erhalten.
|
|
||||||
3. LocalStorage-Key pro Tabelle (path + dataUrl + container).
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Row Interaction Standard
|
|
||||||
|
|
||||||
## Ziel
|
|
||||||
|
|
||||||
Row-Navigation discoverable und keyboard-freundlich machen.
|
|
||||||
|
|
||||||
## Standardverhalten
|
|
||||||
|
|
||||||
1. Sichtbare Action-Spalte rechts (`Open`/`Edit`) fuer Listen mit `rowDblClick`.
|
|
||||||
2. Enter auf fokussierter Row loest dieselbe Navigation aus.
|
|
||||||
3. Doppelklick bleibt als Fallback fuer Power-User.
|
|
||||||
4. Nur Rows mit Ziel-URL erhalten `data-row-open-url`.
|
|
||||||
|
|
||||||
## Contract
|
|
||||||
|
|
||||||
```js
|
|
||||||
rowInteraction: {
|
|
||||||
enabled: true,
|
|
||||||
showActionButton: true,
|
|
||||||
keepDblClick: true,
|
|
||||||
enableEnterOnRow: true,
|
|
||||||
actionColumnLabel: 'Actions',
|
|
||||||
resolveActionLabel: (url, rowData) => (url.includes('/edit/') ? 'Edit' : 'Open')
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Guardrails
|
|
||||||
|
|
||||||
1. Keine globale Tab-Stop-Flut auf allen Rows.
|
|
||||||
2. Fokusstil fuer Row und Action-Button sichtbar.
|
|
||||||
3. Explizite `actions.enabled: true` pro Seite darf Auto-Action uebersteuern.
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
name: starterkit-php-style-ci
|
|
||||||
description: Standard-Skill fuer PHP Coding-Style im Starterkit (php-cs-fixer, dry-run in CI, lokaler fix/check workflow). Verwenden bei Style-Einfuehrung, CI-Checks oder grossen Format-Diffs.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Starterkit PHP Style CI
|
|
||||||
|
|
||||||
## Ziel
|
|
||||||
|
|
||||||
Coding-Style pruefbar, reproduzierbar und regressionsarm machen.
|
|
||||||
|
|
||||||
## Wann verwenden
|
|
||||||
|
|
||||||
1. Einfuehrung oder Nachschaerfung von Style-Regeln.
|
|
||||||
2. CI-Dry-Run fuer Style-Checks.
|
|
||||||
3. Aufraeumen grosser Style-Diff-Wellen.
|
|
||||||
|
|
||||||
## Verbindliche Regeln
|
|
||||||
|
|
||||||
1. MUST `composer cs:check` fuer nicht-mutierende Pruefung nutzen.
|
|
||||||
2. MUST `composer cs:fix` nur bewusst und mit Diff-Kontrolle ausfuehren.
|
|
||||||
3. MUST Style-Check vor Merge gruen halten (lokal und optional CI).
|
|
||||||
4. MUST nach groesseren Fixes mindestens `phpunit` und `phpstan` laufen lassen.
|
|
||||||
5. MUST NOT manuell Einzelstellen formatieren, wenn der Fixer sie zentral loesen kann.
|
|
||||||
|
|
||||||
## Workflow
|
|
||||||
|
|
||||||
1. Baseline erfassen: `composer cs:check`.
|
|
||||||
2. Falls noetig fixen: `composer cs:fix`.
|
|
||||||
3. Diff fokussiert pruefen (Imports, Braces, Nebenwirkungen).
|
|
||||||
4. Qualitaetsgates laufen lassen.
|
|
||||||
5. CI-Dry-Run konfigurieren oder validieren.
|
|
||||||
|
|
||||||
## Referenzen
|
|
||||||
|
|
||||||
- Lokaler Workflow: `references/style-workflow.md`
|
|
||||||
- CI Dry Run: `references/ci-dry-run.md`
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
interface:
|
|
||||||
display_name: "Starterkit PHP Style CI"
|
|
||||||
short_description: "Enforce PHP coding style with safe local and CI checks"
|
|
||||||
default_prompt: "Use $starterkit-php-style-ci to set up and run php-cs-fixer check/fix workflow with CI dry-run and guardrails."
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# CI Dry Run
|
|
||||||
|
|
||||||
## Ziel
|
|
||||||
|
|
||||||
CI soll nur pruefen, nicht formatieren.
|
|
||||||
|
|
||||||
## Minimaler Check
|
|
||||||
|
|
||||||
```bash
|
|
||||||
composer cs:check
|
|
||||||
```
|
|
||||||
|
|
||||||
## Erwartung
|
|
||||||
|
|
||||||
1. Exit Code 0: Style konform.
|
|
||||||
2. Exit Code != 0: Diff ausgeben, Entwickler fuehrt lokal `composer cs:fix` aus.
|
|
||||||
|
|
||||||
## Hinweise
|
|
||||||
|
|
||||||
1. Kein Auto-Commit aus CI.
|
|
||||||
2. Bei initialen Altlasten zuerst einmalige Bereinigung auf dediziertem Branch.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Local Style Workflow
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
composer cs:check
|
|
||||||
composer cs:fix
|
|
||||||
```
|
|
||||||
|
|
||||||
## Praktikabler Ablauf
|
|
||||||
|
|
||||||
1. Erst `cs:check` laufen lassen.
|
|
||||||
2. Danach `cs:fix`.
|
|
||||||
3. Nur relevante Diffs uebernehmen, grosse Misch-Diffs vermeiden.
|
|
||||||
4. Anschliessend:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose exec php vendor/bin/phpunit
|
|
||||||
docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
|
|
||||||
```
|
|
||||||
|
|
||||||
## Review-Fokus
|
|
||||||
|
|
||||||
1. `ordered_imports`, `braces_position`, `statement_indentation`.
|
|
||||||
2. Kein semantischer Drift bei anon classes, arrow functions, array maps.
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
---
|
|
||||||
name: starterkit-planner
|
|
||||||
description: Planungs-Skill fuer Architektur-, Rollout-, Refactor- und Standardisierungsaufgaben im Starterkit. Verwenden, wenn ein decision-complete Plan mit klaren Interfaces, Tests, Risiken und Annahmen benoetigt wird.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Starterkit Planner
|
|
||||||
|
|
||||||
## Ziel
|
|
||||||
|
|
||||||
Planungen so strukturieren, dass Implementierung ohne offene Entscheidungen moeglich ist.
|
|
||||||
Bei Agent-Workflows muss der Plan maschinenlesbar in die Projekt-Contracts passen.
|
|
||||||
|
|
||||||
## Verbindliches Ausgabeformat
|
|
||||||
|
|
||||||
1. Ziel + Success Criteria
|
|
||||||
2. In/Out-of-Scope
|
|
||||||
3. Entscheidungsmatrix (Optionen + Gruende)
|
|
||||||
4. Decision-complete Implementierungsplan
|
|
||||||
5. Tests/Abnahme
|
|
||||||
6. Risiken/Migrationshinweise
|
|
||||||
7. Annahmen/Defaults
|
|
||||||
|
|
||||||
Wenn `agent-system/` verwendet wird:
|
|
||||||
|
|
||||||
1. Plan muss dem Schema `agent-system/contracts/planner.schema.json` entsprechen.
|
|
||||||
2. Success Criteria muessen stabile IDs tragen (`SC-001`, `SC-002`, ...).
|
|
||||||
3. Success Criteria muessen so formuliert sein, dass Reviewer Acceptance sie direkt gegen `criterion_id` pruefen kann.
|
|
||||||
4. Guard- und Quality-Gate-IDs muessen aus den Katalogen kommen (`GR-*`, `QG-*`).
|
|
||||||
|
|
||||||
## Vorgehen
|
|
||||||
|
|
||||||
1. Ist-Zustand kurz aus Repo-Fakten ableiten.
|
|
||||||
2. Entscheidungen mit Tradeoffs transparent machen.
|
|
||||||
3. Oeffentliche Contracts explizit benennen.
|
|
||||||
4. Success Criteria frueh mit stabilen IDs (`SC-*`) definieren.
|
|
||||||
5. Reihenfolge und Rollout so planen, dass Rueckbau moeglich bleibt.
|
|
||||||
6. Teststrategie nach Risiko priorisieren.
|
|
||||||
|
|
||||||
## Qualitaetskriterien
|
|
||||||
|
|
||||||
1. Keine vagen TODO-Entscheidungen im Plan.
|
|
||||||
2. Jeder betroffene Bereich hat klare Akzeptanzkriterien mit stabiler ID (`SC-*`).
|
|
||||||
3. Risiken sind priorisiert und beobachtbar.
|
|
||||||
4. Ungeklaerte Themen stehen explizit unter Annahmen.
|
|
||||||
5. Guard-/Gate-Referenzen sind vollstaendig und pruefbar (`GR-*`, `QG-*`).
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- Extension-Mechanik ist aktuell nicht standardisiert.
|
|
||||||
- Keine impliziten Extension-Regeln definieren.
|
|
||||||
|
|
||||||
## Referenzen
|
|
||||||
|
|
||||||
- Plan-Grundgeruest: `references/plan-template.md`
|
|
||||||
- Entscheidungsraster: `references/tradeoff-matrix.md`
|
|
||||||
- Agent Planner Contract: `agent-system/contracts/planner.schema.json`
|
|
||||||
- Guard Catalog: `agent-system/checks/guard-catalog.json`
|
|
||||||
- Quality Gates: `agent-system/checks/quality-gates.json`
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# Plan Template (Decision Complete)
|
|
||||||
|
|
||||||
## 1) Ziel + Success Criteria
|
|
||||||
|
|
||||||
- Was wird verbessert?
|
|
||||||
- Woran ist messbar, dass es erfolgreich ist?
|
|
||||||
- Success Criteria immer mit stabilen IDs formulieren (`SC-001`, `SC-002`, ...).
|
|
||||||
|
|
||||||
Beispiel:
|
|
||||||
|
|
||||||
- `SC-001`: Delete-Aktion zeigt Confirm-Dialog mit korrekter Gefahr-Variante.
|
|
||||||
- `SC-002`: Nach Confirm erfolgt genau ein Submit (kein Doppel-Submit).
|
|
||||||
|
|
||||||
## 2) Scope
|
|
||||||
|
|
||||||
- In Scope
|
|
||||||
- Out of Scope
|
|
||||||
- Scope-Typ explizit markieren (`core`, `ui`, `api`, `mixed`), wenn der Workflow es verlangt.
|
|
||||||
|
|
||||||
## 3) Oeffentliche Interfaces / Contracts
|
|
||||||
|
|
||||||
- Neue/angepasste APIs, Data-Contracts, CLI-Flags, Markup-Contracts
|
|
||||||
- Backward-Compatibility-Entscheidung
|
|
||||||
- Bei Agent-Workflow: verwendete `GR-*` und `QG-*` IDs explizit nennen.
|
|
||||||
|
|
||||||
## 4) Implementierungsschritte
|
|
||||||
|
|
||||||
1. Schritt A
|
|
||||||
2. Schritt B
|
|
||||||
3. Schritt C
|
|
||||||
|
|
||||||
Regel: Jeder Schritt ist direkt umsetzbar ohne Nachentscheid.
|
|
||||||
|
|
||||||
## 5) Test- und Abnahmeplan
|
|
||||||
|
|
||||||
- Syntax/Static Checks
|
|
||||||
- Contract-/Architekturtests
|
|
||||||
- Fachliche Smoke-Tests
|
|
||||||
- Abnahmekriterien (klar messbar)
|
|
||||||
- Abnahme-Checks 1:1 auf `SC-*` Kriterien-ID referenzieren.
|
|
||||||
|
|
||||||
## 6) Risiken / Migration
|
|
||||||
|
|
||||||
- Hauptrisiken
|
|
||||||
- Mitigation
|
|
||||||
- Rollout-/Rollback-Hinweis
|
|
||||||
|
|
||||||
## 7) Annahmen / Defaults
|
|
||||||
|
|
||||||
- Alle offenen Themen als explizite Annahme festhalten.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Tradeoff Matrix
|
|
||||||
|
|
||||||
## Bewertungsachsen
|
|
||||||
|
|
||||||
1. Robustheit / Fehlertoleranz
|
|
||||||
2. Wartbarkeit / Kopplung
|
|
||||||
3. Migrationsrisiko
|
|
||||||
4. Testbarkeit
|
|
||||||
5. Liefergeschwindigkeit
|
|
||||||
|
|
||||||
## Standard-Optionen
|
|
||||||
|
|
||||||
### Option A: Zentralisieren im Core
|
|
||||||
|
|
||||||
- Vorteile: Konsistenz, weniger Duplikate
|
|
||||||
- Nachteile: Hoher Impact, breiter Testbedarf
|
|
||||||
- Geeignet wenn: 3+ Stellen betroffen oder Contract-Sicherheit steigt deutlich
|
|
||||||
|
|
||||||
### Option B: Shared Partial/Helper
|
|
||||||
|
|
||||||
- Vorteile: Schneller Nutzen, moderates Risiko
|
|
||||||
- Nachteile: Teilweise Restduplikate moeglich
|
|
||||||
- Geeignet wenn: Wiederkehrendes UI-/Glue-Muster mit klaren Datencontracts
|
|
||||||
|
|
||||||
### Option C: Lokal belassen
|
|
||||||
|
|
||||||
- Vorteile: Geringstes kurzfristiges Risiko
|
|
||||||
- Nachteile: Langfristig mehr Wartung, inkonsistente UX
|
|
||||||
- Geeignet wenn: Einzelfall oder experimentelle Logik
|
|
||||||
|
|
||||||
## Entscheidungsregel
|
|
||||||
|
|
||||||
- Standardempfehlung: Option B vor Option A, wenn Nutzen hoch und Migrationsradius kleiner ist.
|
|
||||||
- Option C nur mit expliziter Begruendung (z. B. bewusstes Out-of-Scope oder Legacy-Risiko).
|
|
||||||
Reference in New Issue
Block a user