update
This commit is contained in:
302
.paul/phases/65-paul-delegated-apply/65-01-PLAN.md
Normal file
302
.paul/phases/65-paul-delegated-apply/65-01-PLAN.md
Normal file
@@ -0,0 +1,302 @@
|
||||
---
|
||||
phase: 65-paul-delegated-apply
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- ~/.claude/paul-framework/workflows/apply-phase.md
|
||||
- ~/.claude/paul-framework/references/delegated-apply.md
|
||||
- ~/.claude/paul-framework/references/subagent-criteria.md
|
||||
- ~/.claude/paul-framework/templates/PLAN.md
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Rozszerzyć workflow `/paul:apply` o tryb delegowany (delegated-apply), w którym orkiestrator w głównym kontekście rozbija taski z PLAN.md na mikro-zadania i zleca je sub-agentom przez Agent tool. Orkiestrator ocenia wyniki i decyduje: accept / retry / escalate.
|
||||
|
||||
## Purpose
|
||||
Główny kontekst zapełnia się zbyt szybko podczas APPLY — czytanie plików, implementacja, weryfikacja zużywają 60-80% okna kontekstowego na jedną fazę. Delegacja do sub-agentów pozwoli:
|
||||
- Zachować główny kontekst na poziomie ~15-25% zużycia (orkiestracja + ocena)
|
||||
- Wykonywać więcej faz w jednej sesji bez handoff
|
||||
- Poprawić jakość — mniejsze, fokusowe zadania dla sub-agentów
|
||||
- Umożliwić równoległe wykonywanie niezależnych tasków
|
||||
|
||||
## Output
|
||||
1. Zmodyfikowany `apply-phase.md` z trybem delegowanym
|
||||
2. Nowy reference `delegated-apply.md` z pełną specyfikacją wzorca
|
||||
3. Zaktualizowany `subagent-criteria.md` o kontekst PAUL delegacji
|
||||
4. Zaktualizowany template `PLAN.md` o pole `delegation`
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files (do edycji)
|
||||
@~/.claude/paul-framework/workflows/apply-phase.md
|
||||
@~/.claude/paul-framework/references/subagent-criteria.md
|
||||
@~/.claude/paul-framework/references/delegated-apply.md (nowy)
|
||||
@~/.claude/paul-framework/templates/PLAN.md
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Tryb delegowany aktywuje się automatycznie lub ręcznie
|
||||
```gherkin
|
||||
Given plan z polem `delegation: auto` w frontmatter
|
||||
When /paul:apply wykonuje plan
|
||||
Then orkiestrator rozbija każdy task type="auto" na mikro-prompt i zleca Agent tool
|
||||
And checkpointy (human-verify, decision) pozostają w głównym kontekście
|
||||
```
|
||||
|
||||
## AC-2: Mikro-prompt zawiera pełny kontekst dla sub-agenta
|
||||
```gherkin
|
||||
Given task z PLAN.md z polami files, action, verify, done, boundaries
|
||||
When orkiestrator buduje mikro-prompt
|
||||
Then prompt zawiera: cel zadania, ścieżki plików, dokładne instrukcje, co NIE zmieniać, jak zweryfikować
|
||||
And sub-agent nie musi czytać PLAN.md ani STATE.md (zero dodatkowego kontekstu)
|
||||
```
|
||||
|
||||
## AC-3: Orkiestrator ocenia wynik i podejmuje decyzję
|
||||
```gherkin
|
||||
Given sub-agent zwrócił wynik mikro-zadania
|
||||
When orkiestrator analizuje zwrócony summary
|
||||
Then podejmuje jedną z decyzji: accept (kontynuuj) / retry (ponów z feedbackiem) / escalate (poproś użytkownika)
|
||||
And loguje decyzję do progress trackera
|
||||
```
|
||||
|
||||
## AC-4: Fallback do trybu inline
|
||||
```gherkin
|
||||
Given plan z polem `delegation: off` lub brak pola delegation
|
||||
When /paul:apply wykonuje plan
|
||||
Then workflow działa identycznie jak dotychczas (bez zmian w zachowaniu)
|
||||
```
|
||||
|
||||
## AC-5: Równoległe wykonywanie niezależnych tasków
|
||||
```gherkin
|
||||
Given plan z 2+ taskami type="auto" które nie mają zależności między sobą
|
||||
When orkiestrator jest w trybie delegowanym
|
||||
Then może zlecić niezależne taski równolegle (multiple Agent calls w jednym kroku)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Utworzenie reference delegated-apply.md</name>
|
||||
<files>~/.claude/paul-framework/references/delegated-apply.md</files>
|
||||
<action>
|
||||
Utwórz nowy plik referencyjny opisujący wzorzec delegated-apply:
|
||||
|
||||
1. **Architektura orkiestratora:**
|
||||
- Główny kontekst = lekki koordynator (czyta plan, buduje prompty, ocenia wyniki)
|
||||
- Sub-agent = wykonawca jednego mikro-zadania (czyta pliki, implementuje, weryfikuje)
|
||||
- Przepływ: parse plan → dla każdego taska → build micro-prompt → spawn Agent → evaluate result → accept/retry/escalate
|
||||
|
||||
2. **Budowanie mikro-promptu:**
|
||||
Każdy mikro-prompt dla sub-agenta MUSI zawierać:
|
||||
```
|
||||
## Cel
|
||||
[task name z PLAN.md]
|
||||
|
||||
## Pliki do modyfikacji
|
||||
[files z PLAN.md]
|
||||
|
||||
## Instrukcje
|
||||
[action z PLAN.md — pełna treść, nie skrócona]
|
||||
|
||||
## Granice — NIE ZMIENIAJ
|
||||
[boundaries z PLAN.md]
|
||||
|
||||
## Weryfikacja
|
||||
[verify z PLAN.md]
|
||||
|
||||
## Kryteria ukończenia
|
||||
[done z PLAN.md]
|
||||
|
||||
## Kontekst projektu (minimum)
|
||||
- Stack: [z PROJECT.md — 1-2 linijki]
|
||||
- Konwencje: [kluczowe konwencje z CONVENTIONS/CLAUDE.md — max 5 linijek]
|
||||
```
|
||||
|
||||
3. **Ocena wyników (evaluation protocol):**
|
||||
- Sub-agent zwraca summary ~100-300 słów
|
||||
- Orkiestrator sprawdza:
|
||||
a) Czy pliki zostały zmodyfikowane (git diff --stat)
|
||||
b) Czy done criteria z PLAN.md są spełnione
|
||||
c) Czy boundaries nie zostały naruszone (git diff na chronionych plikach)
|
||||
- Decyzja:
|
||||
- **accept**: wynik OK → przejdź do następnego taska
|
||||
- **retry** (max 3x): wynik niekompletny → spawn Agent z feedbackiem co poprawić
|
||||
- **escalate**: 3 retry failed lub problem wymaga decyzji → pytaj użytkownika
|
||||
|
||||
4. **Tryby delegacji:**
|
||||
- `delegation: auto` — orkiestrator automatycznie deleguje taski type="auto"
|
||||
- `delegation: parallel` — jak auto, ale niezależne taski lecą równolegle
|
||||
- `delegation: off` — klasyczny inline (domyślne, backward compatible)
|
||||
|
||||
5. **Ograniczenia:**
|
||||
- Checkpointy (human-verify, decision, human-action) ZAWSZE w głównym kontekście
|
||||
- Sub-agent NIE aktualizuje STATE.md ani PLAN.md — to robi orkiestrator
|
||||
- Max 1 task per sub-agent (atomowość)
|
||||
- Sub-agent type: `general-purpose` (domyślny)
|
||||
|
||||
6. **Szacunek oszczędności kontekstu:**
|
||||
| Element | Inline (teraz) | Delegated |
|
||||
|---------|----------------|-----------|
|
||||
| Czytanie plików źródłowych | ~3-5k tokens/plik | 0 (w sub-agencie) |
|
||||
| Implementacja kodu | ~10-20k tokens | 0 (w sub-agencie) |
|
||||
| Wynik weryfikacji | ~2-5k tokens | ~0.5k (summary) |
|
||||
| Orkiestracja per task | 0 | ~1-2k tokens |
|
||||
| **Total per task** | **~15-30k** | **~2-3k** |
|
||||
|
||||
</action>
|
||||
<verify>Plik istnieje w ~/.claude/paul-framework/references/delegated-apply.md i zawiera sekcje: Architektura, Mikro-prompt, Evaluation Protocol, Tryby, Ograniczenia</verify>
|
||||
<done>AC-2 satisfied: specyfikacja mikro-promptu zdefiniowana. AC-3 satisfied: evaluation protocol zdefiniowany.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Modyfikacja apply-phase.md — dodanie trybu delegowanego</name>
|
||||
<files>~/.claude/paul-framework/workflows/apply-phase.md</files>
|
||||
<action>
|
||||
Dodaj nowy krok `determine_execution_mode` PRZED `execute_tasks` oraz zmodyfikuj `execute_tasks`:
|
||||
|
||||
1. **Nowy krok `determine_execution_mode`** (po `verify_required_skills`):
|
||||
```
|
||||
1. Sprawdź frontmatter pola `delegation`:
|
||||
- `auto` lub `parallel` → tryb delegowany
|
||||
- `off` lub brak pola → tryb inline (bez zmian)
|
||||
2. Jeśli tryb delegowany:
|
||||
- Załaduj @references/delegated-apply.md
|
||||
- Przygotuj kontekst projektu dla mikro-promptów (stack, konwencje — max 10 linijek)
|
||||
- Zidentyfikuj taski niezależne (do równoległego wykonania jeśli `parallel`)
|
||||
3. Log: "Execution mode: delegated|inline"
|
||||
```
|
||||
|
||||
2. **Rozszerzenie `execute_tasks`** — dodaj alternatywną ścieżkę dla trybu delegowanego:
|
||||
|
||||
**Jeśli tryb delegowany i task type="auto":**
|
||||
```
|
||||
1. Zbuduj mikro-prompt wg szablonu z delegated-apply.md
|
||||
2. Wywołaj Agent tool:
|
||||
- subagent_type: "general-purpose"
|
||||
- prompt: [mikro-prompt]
|
||||
- description: "PAUL task: [task name]" (3-5 słów)
|
||||
3. Odbierz wynik (summary od sub-agenta)
|
||||
4. Oceń wg evaluation protocol:
|
||||
- Sprawdź git diff --stat
|
||||
- Sprawdź done criteria
|
||||
- Sprawdź boundaries
|
||||
5. Decyzja: accept / retry / escalate
|
||||
6. Przy retry: spawn ponownie z feedbackiem (max 3 razy)
|
||||
```
|
||||
|
||||
**Jeśli `delegation: parallel` i multiple niezależne taski:**
|
||||
```
|
||||
1. Zidentyfikuj klastry niezależnych tasków
|
||||
2. Wywołaj multiple Agent tool w jednym kroku
|
||||
3. Oceń wyniki każdego
|
||||
4. Kontynuuj sekwencyjnie z taskami zależnymi
|
||||
```
|
||||
|
||||
**Checkpointy — BEZ ZMIAN:**
|
||||
Taski checkpoint:* zawsze wykonywane inline w głównym kontekście.
|
||||
|
||||
3. **Rozszerzenie `finalize`** — dodaj podsumowanie trybu:
|
||||
```
|
||||
Delegated execution summary:
|
||||
- Tasks delegated: N
|
||||
- Accepted on first try: N
|
||||
- Retried: N
|
||||
- Escalated: N
|
||||
```
|
||||
|
||||
WAŻNE: Nie usuwaj i nie modyfikuj istniejącego zachowania inline. Tryb delegowany to DODATKOWA ścieżka.
|
||||
</action>
|
||||
<verify>apply-phase.md zawiera kroki: determine_execution_mode, execute_tasks z alternatywną ścieżką delegowaną, finalize z podsumowaniem delegacji</verify>
|
||||
<done>AC-1 satisfied: tryb delegowany aktywuje się na podstawie pola delegation. AC-4 satisfied: brak pola = inline bez zmian.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Aktualizacja PLAN.md template i subagent-criteria.md</name>
|
||||
<files>~/.claude/paul-framework/templates/PLAN.md, ~/.claude/paul-framework/references/subagent-criteria.md</files>
|
||||
<action>
|
||||
1. **PLAN.md template** — dodaj pole `delegation` do frontmatter:
|
||||
```yaml
|
||||
delegation: off # off | auto | parallel
|
||||
```
|
||||
- Dodaj pole po `autonomous` z komentarzem
|
||||
- Dodaj do tabeli Frontmatter Fields: `delegation | No | Execution mode: off (inline, default), auto (delegated), parallel (delegated + parallel)`
|
||||
- Dodaj krótką sekcję "## Delegation Mode" w dokumencie opisującą kiedy użyć jakiego trybu
|
||||
|
||||
2. **subagent-criteria.md** — dodaj sekcję o kontekście PAUL:
|
||||
- Nowa sekcja "## PAUL Delegated Apply Context"
|
||||
- Wyjaśnij że w kontekście /paul:apply, sub-agenty służą do:
|
||||
a) Wykonywania tasków z PLAN.md (well-defined, files+action+verify+done)
|
||||
b) Ochrony głównego kontekstu orkiestratora
|
||||
c) Umożliwienia wielu faz w jednej sesji
|
||||
- Podkreśl że kryteria z istniejącej sekcji nadal obowiązują
|
||||
- Dodaj notę: "W PAUL delegated-apply, każdy task z PLAN.md naturalnie spełnia kryteria: Task Independence (self-contained), Clear Scope (files+action+verify+done), Complexity Sweet Spot (2-3 taski per plan = 1 task per agent)"
|
||||
</action>
|
||||
<verify>PLAN.md template zawiera pole delegation w frontmatter i tabeli. subagent-criteria.md zawiera sekcję PAUL Delegated Apply Context.</verify>
|
||||
<done>AC-1 partially satisfied: template wspiera pole delegation. AC-5 satisfied: parallel mode zdefiniowany.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
Kompletny wzorzec delegated-apply dla PAUL:
|
||||
1. Reference doc z architekturą, mikro-promptem, evaluation protocol
|
||||
2. Zmodyfikowany apply-phase.md z trybem delegowanym
|
||||
3. Zaktualizowany template i subagent-criteria
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Przejrzyj ~/.claude/paul-framework/references/delegated-apply.md
|
||||
2. Przejrzyj zmiany w ~/.claude/paul-framework/workflows/apply-phase.md
|
||||
3. Sprawdź czy PLAN.md template ma pole delegation
|
||||
4. Przetestuj na następnej fazie orderPRO z `delegation: auto`
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- ~/.claude/paul-framework/workflows/plan-phase.md (planning workflow untouched)
|
||||
- ~/.claude/paul-framework/workflows/unify-phase.md (unify workflow untouched)
|
||||
- ~/.claude/paul-framework/rules/* (framework rules untouched)
|
||||
- .paul/PROJECT.md, .paul/ROADMAP.md (project state untouched)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie zmieniamy /paul:plan ani /paul:unify — tylko /paul:apply
|
||||
- Nie tworzymy nowych komend — rozszerzamy istniejący workflow
|
||||
- Nie przerabiamy istniejącego trybu inline — dodajemy alternatywną ścieżkę
|
||||
- Nie implementujemy auto-detection rozmiaru (proste pole w frontmatter wystarczy)
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] delegated-apply.md istnieje i zawiera: architekturę, mikro-prompt template, evaluation protocol, tryby, ograniczenia
|
||||
- [ ] apply-phase.md zawiera krok determine_execution_mode i alternatywną ścieżkę delegowaną
|
||||
- [ ] PLAN.md template zawiera pole delegation w frontmatter
|
||||
- [ ] subagent-criteria.md zawiera sekcję PAUL context
|
||||
- [ ] Tryb inline (delegation: off / brak pola) działa bez zmian
|
||||
- [ ] Wszystkie checkpointy nadal działają w głównym kontekście
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 3 taski auto completed + checkpoint approved
|
||||
- Framework PAUL wspiera tryb delegated-apply
|
||||
- Backward compatible — istniejące plany bez pola delegation działają jak dotychczas
|
||||
- Gotowy do przetestowania na następnej fazie orderPRO
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/65-paul-delegated-apply/65-01-SUMMARY.md`
|
||||
</output>
|
||||
124
.paul/phases/65-paul-delegated-apply/65-01-SUMMARY.md
Normal file
124
.paul/phases/65-paul-delegated-apply/65-01-SUMMARY.md
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
phase: 65-paul-delegated-apply
|
||||
plan: 01
|
||||
subsystem: infra
|
||||
tags: [paul-framework, subagents, delegation, context-management]
|
||||
|
||||
requires:
|
||||
- phase: none
|
||||
provides: standalone enhancement
|
||||
|
||||
provides:
|
||||
- Delegated-apply execution mode for /paul:apply
|
||||
- Micro-prompt template for sub-agent task delegation
|
||||
- Evaluation protocol (accept/retry/escalate)
|
||||
- delegation field in PLAN.md frontmatter
|
||||
|
||||
affects: [all future phases using /paul:apply]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [orchestrator-subagent delegation, micro-prompt composition, evaluation protocol]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ~/.claude/paul-framework/references/delegated-apply.md
|
||||
modified:
|
||||
- ~/.claude/paul-framework/workflows/apply-phase.md
|
||||
- ~/.claude/paul-framework/templates/PLAN.md
|
||||
- ~/.claude/paul-framework/references/subagent-criteria.md
|
||||
|
||||
key-decisions:
|
||||
- "delegation: auto as default for all new plans"
|
||||
- "Max 3 retries before escalation to user"
|
||||
- "Checkpoints always inline, never delegated"
|
||||
|
||||
patterns-established:
|
||||
- "Orchestrator builds micro-prompt from PLAN.md task fields, delegates via Agent tool"
|
||||
- "Evaluation: git diff + done criteria + boundary check → accept/retry/escalate"
|
||||
|
||||
duration: ~15min
|
||||
started: 2026-04-03T20:10:00Z
|
||||
completed: 2026-04-03T20:25:00Z
|
||||
---
|
||||
|
||||
# Phase 65 Plan 01: PAUL Delegated Apply Summary
|
||||
|
||||
**Rozszerzenie /paul:apply o tryb delegowany — orkiestrator w głównym kontekście zleca mikro-zadania sub-agentom, ocenia wyniki i decyduje accept/retry/escalate. Szacowana oszczędność: ~80% kontekstu per faza.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~15min |
|
||||
| Started | 2026-04-03T20:10:00Z |
|
||||
| Completed | 2026-04-03T20:25:00Z |
|
||||
| Tasks | 3 auto + 1 checkpoint completed |
|
||||
| Files modified | 4 |
|
||||
| Execution mode | Delegated (parallel) — 3 sub-agents |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Tryb delegowany aktywuje się automatycznie | Pass | Pole `delegation: auto` (domyślne), workflow sprawdza frontmatter |
|
||||
| AC-2: Mikro-prompt zawiera pełny kontekst | Pass | Template w delegated-apply.md: cel, pliki, instrukcje, granice, weryfikacja, kontekst |
|
||||
| AC-3: Orkiestrator ocenia wynik | Pass | Evaluation protocol: git diff + done criteria + boundary check → accept/retry/escalate |
|
||||
| AC-4: Fallback do trybu inline | Pass | `delegation: off` = inline bez zmian; brak pola = auto (zmiana z oryginalnego planu) |
|
||||
| AC-5: Równoległe wykonywanie tasków | Pass | `delegation: parallel` mode zdefiniowany w workflow i reference |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Utworzono kompletny reference `delegated-apply.md` z architekturą, mikro-prompt template, evaluation protocol, trybami i ograniczeniami
|
||||
- Rozszerzono `apply-phase.md` o krok `determine_execution_mode` i alternatywną ścieżkę delegowaną (inline path bez zmian)
|
||||
- Zaktualizowano template PLAN.md o pole `delegation` (domyślnie `auto`) i sekcję Delegation Mode
|
||||
- Rozszerzono `subagent-criteria.md` o kontekst PAUL delegated-apply
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `~/.claude/paul-framework/references/delegated-apply.md` | Created | Pełna specyfikacja wzorca delegated-apply |
|
||||
| `~/.claude/paul-framework/workflows/apply-phase.md` | Modified | Nowy krok determine_execution_mode + delegated path + delegation summary w finalize |
|
||||
| `~/.claude/paul-framework/templates/PLAN.md` | Modified | Pole delegation w frontmatter (default: auto) + sekcja Delegation Mode |
|
||||
| `~/.claude/paul-framework/references/subagent-criteria.md` | Modified | Sekcja PAUL Delegated Apply Context |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| `delegation: auto` jako default | Użytkownik chce delegacji jako standardowego zachowania, nie opt-in | Wszystkie nowe plany automatycznie delegują taski |
|
||||
| Max 3 retries (zmiana z 2) | Użytkownik poprosił o zwiększenie — daje więcej szans na sukces | Mniej escalacji do użytkownika |
|
||||
| Checkpointy zawsze inline | Wymagają interakcji z użytkownikiem — nie da się delegować | Bezpieczeństwo workflow |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope additions | 1 | Zmiana default z `off` na `auto` — poproszona przez użytkownika |
|
||||
|
||||
**Total impact:** Pozytywna zmiana — lepsze UX, delegacja out-of-the-box.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Framework PAUL wspiera tryb delegated-apply we wszystkich projektach
|
||||
- Każdy nowy plan domyślnie używa `delegation: auto`
|
||||
- Gotowe do przetestowania na następnej fazie orderPRO
|
||||
|
||||
**Concerns:**
|
||||
- Wymaga przetestowania w praktyce — pierwszy real-world test na następnej fazie orderPRO pokaże czy mikro-prompty są wystarczająco dobre
|
||||
- Sub-agenty mogą potrzebować dostrojenia kontekstu projektu (stack + konwencje)
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 65-paul-delegated-apply, Plan: 01*
|
||||
*Completed: 2026-04-03*
|
||||
312
.paul/phases/66-allegro-delivery-tracking/66-01-PLAN.md
Normal file
312
.paul/phases/66-allegro-delivery-tracking/66-01-PLAN.md
Normal file
@@ -0,0 +1,312 @@
|
||||
---
|
||||
phase: 66-allegro-delivery-tracking
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/Modules/Shipments/AllegroTrackingService.php
|
||||
- src/Modules/Shipments/DeliveryStatus.php
|
||||
- src/Modules/Cron/ShipmentTrackingHandler.php
|
||||
autonomous: true
|
||||
delegation: auto
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Zaimplementować śledzenie statusu przesyłek Allegro Delivery (numery A-*) przez publiczne edge API Allegro. Aktualnie `AllegroTrackingService` zwraca `null` dla przesyłek nie-InPost. Po zmianie — pobiera statusy z `https://edge.allegro.pl/ad/tracking?packageNo={nr}` i normalizuje je do istniejącego systemu `DeliveryStatus`.
|
||||
|
||||
## Purpose
|
||||
Przesyłki Allegro Delivery (One Kurier, DPD via Allegro, itp.) nie mają śledzenia w orderPRO. Użytkownicy muszą ręcznie sprawdzać status na allegro.pl. Ta zmiana automatyzuje ten proces.
|
||||
|
||||
## Output
|
||||
- `AllegroTrackingService` pobiera statusy z edge API dla przesyłek non-InPost
|
||||
- `DeliveryStatus` ma rozszerzony mapping Allegro o statusy z edge API (opisy PL)
|
||||
- Rate limiting: max 1 request na minutę per przesyłka (w cron handler)
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Shipments/AllegroTrackingService.php
|
||||
@src/Modules/Shipments/DeliveryStatus.php
|
||||
@src/Modules/Cron/ShipmentTrackingHandler.php
|
||||
|
||||
## API Research
|
||||
Edge API endpoint (publiczny, bez autoryzacji):
|
||||
- URL: `https://edge.allegro.pl/ad/tracking?packageNo={trackingNumber}`
|
||||
- Header: `Accept: application/vnd.allegro.internal.v1+json`
|
||||
- Response: `{"status": [{"eventTimestamp": "ISO8601", "description": "Opis PL"}]}`
|
||||
- Ostatni element tablicy = aktualny status
|
||||
- Opisy są po polsku, np.:
|
||||
- "Przesyłka została przygotowana przez nadawcę"
|
||||
- "Przesyłka została nadana"
|
||||
- "Przesyłka została podjęta z punktu przez kuriera"
|
||||
- "Przesyłka została odebrana przez kuriera"
|
||||
- "Kurier przekazał przesyłkę do magazynu"
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Allegro Delivery tracking zwraca status
|
||||
```gherkin
|
||||
Given przesyłka z provider=allegro_wza i tracking_number zaczynający się od "A"
|
||||
When cron tracking handler odpytuje AllegroTrackingService
|
||||
Then serwis pobiera status z edge.allegro.pl/ad/tracking
|
||||
And zwraca znormalizowany status + opis po polsku
|
||||
```
|
||||
|
||||
## AC-2: Mapowanie opisów na znormalizowane statusy
|
||||
```gherkin
|
||||
Given odpowiedź z edge API zawiera description np. "Przesyłka została nadana"
|
||||
When AllegroTrackingService przetwarza odpowiedź
|
||||
Then mapuje opis na raw status (np. "shipped") i normalizuje przez DeliveryStatus
|
||||
And status_raw zawiera oryginalny opis z API
|
||||
```
|
||||
|
||||
## AC-3: Rate limiting — max 1 request/min
|
||||
```gherkin
|
||||
Given cron handler przetwarza wiele przesyłek allegro_wza
|
||||
When odpytuje edge API
|
||||
Then między kolejnymi requestami do edge.allegro.pl czeka minimum 60 sekund
|
||||
And inne providery (InPost, Apaczka) nie są objęte tym limitem
|
||||
```
|
||||
|
||||
## AC-4: Fallback InPost nadal działa
|
||||
```gherkin
|
||||
Given przesyłka z provider=allegro_wza i carrier_id zawierający "inpost"
|
||||
When cron tracking handler odpytuje AllegroTrackingService
|
||||
Then serwis nadal używa InPost API (nie edge API)
|
||||
And zachowanie jest identyczne jak przed zmianą
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Rozszerzenie DeliveryStatus o mapowanie Allegro edge API</name>
|
||||
<files>src/Modules/Shipments/DeliveryStatus.php</files>
|
||||
<action>
|
||||
Dodaj nową mapę `ALLEGRO_EDGE_MAP` i `ALLEGRO_EDGE_DESCRIPTIONS` do DeliveryStatus.
|
||||
|
||||
Edge API zwraca opisy po polsku (nie kody). Trzeba mapować opisy na wewnętrzne klucze, a potem na znormalizowane statusy.
|
||||
|
||||
Nowa mapa `ALLEGRO_EDGE_MAP` (klucz = slug z opisu, wartość = normalized status):
|
||||
```php
|
||||
private const ALLEGRO_EDGE_MAP = [
|
||||
'przygotowana_przez_nadawce' => self::CREATED,
|
||||
'nadana' => self::CONFIRMED,
|
||||
'podjeta_z_punktu' => self::IN_TRANSIT,
|
||||
'odebrana_przez_kuriera' => self::IN_TRANSIT,
|
||||
'przekazana_do_magazynu' => self::IN_TRANSIT,
|
||||
'w_sortowni' => self::IN_TRANSIT,
|
||||
'w_doreceniu' => self::OUT_FOR_DELIVERY,
|
||||
'gotowa_do_odbioru' => self::READY_FOR_PICKUP,
|
||||
'dostarczona' => self::DELIVERED,
|
||||
'doreczona' => self::DELIVERED,
|
||||
'zwrocona' => self::RETURNED,
|
||||
'anulowana' => self::CANCELLED,
|
||||
'problem' => self::PROBLEM,
|
||||
];
|
||||
```
|
||||
|
||||
Nowa mapa `ALLEGRO_EDGE_DESCRIPTIONS` — klucz = slug, wartość = oryginalny opis PL (identyczny jak z API).
|
||||
|
||||
Dodaj nowy provider `allegro_edge` do:
|
||||
- `PROVIDER_MAPS` array
|
||||
- `PROVIDER_DESCRIPTIONS` array
|
||||
- match w `normalize()` i `description()`
|
||||
|
||||
Dodaj statyczną metodę `slugifyAllegroDescription(string $description): string` — konwertuje opis PL na slug:
|
||||
1. Usuwa prefiks "Przesyłka została " / "Kurier "
|
||||
2. Bierze główne słowo kluczowe
|
||||
3. Zamienia polskie znaki na ASCII
|
||||
4. Zamienia spacje na podkreślenia
|
||||
5. Zwraca lowercase slug
|
||||
|
||||
WAŻNE: Metoda musi obsługiwać nieznane opisy — jeśli slug nie istnieje w mapie, zwróć sam slug jako raw_status i self::UNKNOWN jako normalized.
|
||||
</action>
|
||||
<verify>Klasa DeliveryStatus kompiluje się bez błędów. Nowe stałe ALLEGRO_EDGE_MAP i ALLEGRO_EDGE_DESCRIPTIONS istnieją. Provider 'allegro_edge' jest obsługiwany w normalize() i description().</verify>
|
||||
<done>AC-2 satisfied: mapowanie opisów na znormalizowane statusy zdefiniowane.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Implementacja fetchAllegroEdgeStatus w AllegroTrackingService</name>
|
||||
<files>src/Modules/Shipments/AllegroTrackingService.php</files>
|
||||
<action>
|
||||
Zmodyfikuj `AllegroTrackingService::getDeliveryStatus()` aby obsługiwał przesyłki Allegro Delivery (non-InPost):
|
||||
|
||||
1. W metodzie `getDeliveryStatus()`, po bloku `if (str_contains(... 'inpost'))`, zamiast `return null` dodaj:
|
||||
```php
|
||||
return $this->fetchAllegroEdgeStatus($trackingNumber);
|
||||
```
|
||||
|
||||
2. Dodaj nową prywatną metodę `fetchAllegroEdgeStatus(string $trackingNumber): ?array`:
|
||||
```php
|
||||
private function fetchAllegroEdgeStatus(string $trackingNumber): ?array
|
||||
{
|
||||
try {
|
||||
$url = 'https://edge.allegro.pl/ad/tracking?packageNo=' . rawurlencode($trackingNumber);
|
||||
$response = $this->edgeApiRequest($url);
|
||||
|
||||
$statuses = $response['status'] ?? [];
|
||||
if (!is_array($statuses) || $statuses === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ostatni element = najnowszy status
|
||||
$latest = end($statuses);
|
||||
$description = trim((string) ($latest['description'] ?? ''));
|
||||
if ($description === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$slug = DeliveryStatus::slugifyAllegroDescription($description);
|
||||
|
||||
return [
|
||||
'status' => DeliveryStatus::normalize('allegro_edge', $slug),
|
||||
'status_raw' => $description,
|
||||
'description' => $description,
|
||||
];
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Dodaj nową prywatną metodę `edgeApiRequest(string $url): array` — osobna od istniejącej `apiRequest()` bo nie wymaga autoryzacji:
|
||||
```php
|
||||
private function edgeApiRequest(string $url): array
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
if ($ch === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$opts = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_CONNECTTIMEOUT => 5,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
CURLOPT_SSL_VERIFYHOST => 2,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/vnd.allegro.internal.v1+json',
|
||||
'Content-Type: application/vnd.allegro.internal.v1+json',
|
||||
],
|
||||
];
|
||||
|
||||
$caPath = $this->getCaBundlePath();
|
||||
if ($caPath !== null) {
|
||||
$opts[CURLOPT_CAINFO] = $caPath;
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, $opts);
|
||||
$body = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$ch = null;
|
||||
|
||||
if ($body === false || $httpCode < 200 || $httpCode >= 300) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$json = json_decode((string) $body, true);
|
||||
return is_array($json) ? $json : [];
|
||||
}
|
||||
```
|
||||
|
||||
NIE zmieniaj istniejącej metody `fetchInpostStatus()` ani `apiRequest()` — to osobne ścieżki.
|
||||
</action>
|
||||
<verify>AllegroTrackingService kompiluje się. Metoda getDeliveryStatus nie zwraca null dla non-InPost przesyłek (wywołuje fetchAllegroEdgeStatus). Metoda edgeApiRequest wysyła Accept: application/vnd.allegro.internal.v1+json.</verify>
|
||||
<done>AC-1 satisfied: Allegro Delivery tracking pobiera status z edge API. AC-4 satisfied: InPost path bez zmian.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Rate limiting w ShipmentTrackingHandler dla Allegro edge API</name>
|
||||
<files>src/Modules/Cron/ShipmentTrackingHandler.php</files>
|
||||
<action>
|
||||
Dodaj rate limiting do `ShipmentTrackingHandler::handle()` — max 1 request na 60 sekund do edge.allegro.pl.
|
||||
|
||||
1. Dodaj stałą:
|
||||
```php
|
||||
private const ALLEGRO_EDGE_RATE_LIMIT_SECONDS = 60;
|
||||
```
|
||||
|
||||
2. W metodzie `handle()`, przed pętlą `foreach`, dodaj zmienną śledzącą czas ostatniego requestu Allegro edge:
|
||||
```php
|
||||
$lastAllegroEdgeRequestTime = 0.0;
|
||||
```
|
||||
|
||||
3. Wewnątrz pętli `foreach`, PO uzyskaniu `$service` a PRZED wywołaniem `$service->getDeliveryStatus()`, dodaj sprawdzenie:
|
||||
```php
|
||||
if ($provider === 'allegro_wza') {
|
||||
$carrierId = strtolower(trim((string) ($package['carrier_id'] ?? '')));
|
||||
$isInpost = str_contains($carrierId, 'inpost') || str_contains($carrierId, 'paczkomat');
|
||||
|
||||
if (!$isInpost) {
|
||||
$elapsed = microtime(true) - $lastAllegroEdgeRequestTime;
|
||||
if ($elapsed < self::ALLEGRO_EDGE_RATE_LIMIT_SECONDS) {
|
||||
$sleepTime = (int) ceil(self::ALLEGRO_EDGE_RATE_LIMIT_SECONDS - $elapsed);
|
||||
sleep($sleepTime);
|
||||
}
|
||||
// Zaraz po sleep (lub bez), przed wywołaniem getDeliveryStatus:
|
||||
$lastAllegroEdgeRequestTime = microtime(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
WAŻNE:
|
||||
- Rate limit dotyczy TYLKO requestów do edge.allegro.pl (non-InPost allegro_wza)
|
||||
- InPost i Apaczka requestów NIE ograniczaj
|
||||
- sleep() w cronie jest OK — to background process
|
||||
</action>
|
||||
<verify>ShipmentTrackingHandler kompiluje się. Stała ALLEGRO_EDGE_RATE_LIMIT_SECONDS = 60 istnieje. Kod rate limitingu jest w pętli foreach przed getDeliveryStatus dla allegro_wza non-inpost.</verify>
|
||||
<done>AC-3 satisfied: max 1 request/min do edge.allegro.pl.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- src/Modules/Shipments/InpostTrackingService.php
|
||||
- src/Modules/Shipments/ApaczkaTrackingService.php
|
||||
- src/Modules/Shipments/ShipmentTrackingInterface.php
|
||||
- src/Modules/Shipments/ShipmentTrackingRegistry.php
|
||||
- src/Modules/Shipments/ShipmentPackageRepository.php
|
||||
- src/Modules/Cron/CronHandlerFactory.php
|
||||
- Istniejące mapy INPOST_MAP, APACZKA_MAP w DeliveryStatus
|
||||
- Istniejąca metoda fetchInpostStatus() w AllegroTrackingService
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie tworzymy nowych klas — rozszerzamy istniejące
|
||||
- Nie zmieniamy schematu DB — delivery_status_raw już obsługuje dowolne stringi
|
||||
- Nie dodajemy UI — tracking UI już istnieje i działa z dowolnym statusem
|
||||
- Nie tworzymy unit testów w tym planie
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] DeliveryStatus::normalize('allegro_edge', 'nadana') zwraca 'confirmed'
|
||||
- [ ] DeliveryStatus::slugifyAllegroDescription('Przesyłka została nadana') zwraca slug mapowany na status
|
||||
- [ ] AllegroTrackingService::getDeliveryStatus() dla non-InPost allegro_wza wywołuje edge API
|
||||
- [ ] AllegroTrackingService::getDeliveryStatus() dla InPost allegro_wza nadal używa InPost API
|
||||
- [ ] ShipmentTrackingHandler ma rate limit 60s między requestami edge.allegro.pl
|
||||
- [ ] Żadne chronione pliki nie zostały zmodyfikowane
|
||||
- [ ] PHP syntax check: `php -l` na każdym zmienionym pliku
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 3 taski auto completed
|
||||
- Przesyłki Allegro Delivery (A-numery) mają automatyczny tracking statusu
|
||||
- Rate limit chroni przed blokadą przez Allegro
|
||||
- Istniejący tracking InPost i Apaczka bez zmian
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/66-allegro-delivery-tracking/66-01-SUMMARY.md`
|
||||
</output>
|
||||
96
.paul/phases/66-allegro-delivery-tracking/66-01-SUMMARY.md
Normal file
96
.paul/phases/66-allegro-delivery-tracking/66-01-SUMMARY.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
phase: 66-allegro-delivery-tracking
|
||||
plan: 01
|
||||
subsystem: shipments
|
||||
tags: [allegro, tracking, edge-api, delivery-status, cron]
|
||||
|
||||
requires:
|
||||
- phase: 27-shipment-tracking-backend
|
||||
provides: ShipmentTrackingInterface, DeliveryStatus, ShipmentTrackingHandler
|
||||
|
||||
provides:
|
||||
- Allegro Delivery tracking via edge.allegro.pl API
|
||||
- Edge API integration (no auth, public endpoint)
|
||||
- Rate limiting for edge API requests (60s)
|
||||
|
||||
affects: [allegro-tracking, delivery-status, cron-handler]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [edge-api-integration, rate-limited-cron]
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- src/Modules/Shipments/AllegroTrackingService.php
|
||||
- src/Modules/Shipments/DeliveryStatus.php
|
||||
- src/Modules/Cron/ShipmentTrackingHandler.php
|
||||
|
||||
key-decisions:
|
||||
- "Edge API (edge.allegro.pl/ad/tracking) zamiast Allegro REST API (nie daje tracking statusów)"
|
||||
- "Accept: application/vnd.allegro.internal.v1+json — wymagany nagłówek"
|
||||
- "Provider allegro_edge osobny od allegro_wza w DeliveryStatus"
|
||||
- "Rate limit 60s między requestami do edge API"
|
||||
|
||||
patterns-established:
|
||||
- "slugifyAllegroDescription() konwertuje opisy PL na slugi mapowalne na statusy"
|
||||
- "Osobna metoda edgeApiRequest() bez Bearer token (publiczny endpoint)"
|
||||
|
||||
duration: ~15min
|
||||
started: 2026-04-03T20:30:00Z
|
||||
completed: 2026-04-03T20:45:00Z
|
||||
---
|
||||
|
||||
# Phase 66 Plan 01: Allegro Delivery Tracking — Core Integration
|
||||
|
||||
**Integracja śledzenia przesyłek Allegro Delivery przez publiczne edge API — fetchAllegroEdgeStatus, mapowanie opisów PL na znormalizowane statusy, rate limiting 60s w cronie.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~15min |
|
||||
| Tasks | 3 completed (delegated) |
|
||||
| Files modified | 3 |
|
||||
| Execution mode | Delegated auto (3 sub-agents) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Allegro Delivery tracking zwraca status | Pass | fetchAllegroEdgeStatus pobiera z edge API |
|
||||
| AC-2: Mapowanie opisów na statusy | Pass | slugify + ALLEGRO_EDGE_MAP |
|
||||
| AC-3: Rate limiting max 1 req/min | Pass | sleep() w ShipmentTrackingHandler |
|
||||
| AC-4: Fallback InPost nadal działa | Pass | Warunek carrier_id inpost/paczkomat bez zmian |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- AllegroTrackingService pobiera statusy z edge.allegro.pl/ad/tracking dla non-InPost przesyłek
|
||||
- DeliveryStatus ma provider 'allegro_edge' z mapą slugów i opisów PL
|
||||
- ShipmentTrackingHandler throttluje requesty do edge API (60s)
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Shipments/DeliveryStatus.php` | Modified | ALLEGRO_EDGE_MAP, ALLEGRO_EDGE_DESCRIPTIONS, slugifyAllegroDescription() |
|
||||
| `src/Modules/Shipments/AllegroTrackingService.php` | Modified | fetchAllegroEdgeStatus(), edgeApiRequest() |
|
||||
| `src/Modules/Cron/ShipmentTrackingHandler.php` | Modified | Rate limiting 60s dla allegro_wza non-inpost |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Slug mismatch w mapie**
|
||||
- **Found during:** Orkiestrator verification
|
||||
- **Issue:** "Kurier przekazał przesyłkę do magazynu" → slug `przekazal_przesylke_do_magazynu` nie był w mapie (była `przekazana_do_magazynu`)
|
||||
- **Fix:** Dodano wariant do ALLEGRO_EDGE_MAP
|
||||
- **Verification:** Test 5/5 realnych opisów mapuje poprawnie
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:** Plan 66-02 rozszerza mapę i dodaje keyword fallback
|
||||
|
||||
---
|
||||
*Phase: 66-allegro-delivery-tracking, Plan: 01*
|
||||
*Completed: 2026-04-03*
|
||||
228
.paul/phases/66-allegro-delivery-tracking/66-02-PLAN.md
Normal file
228
.paul/phases/66-allegro-delivery-tracking/66-02-PLAN.md
Normal file
@@ -0,0 +1,228 @@
|
||||
---
|
||||
phase: 66-allegro-delivery-tracking
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["66-01"]
|
||||
files_modified:
|
||||
- src/Modules/Shipments/DeliveryStatus.php
|
||||
- src/Modules/Shipments/AllegroTrackingService.php
|
||||
autonomous: true
|
||||
delegation: auto
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
1. Uzupełnić mapę ALLEGRO_EDGE_MAP o brakujące statusy z realnych przesyłek
|
||||
2. Dodać mechanizm keyword-based fallback (guessStatusFromDescription) dla nieznanych opisów
|
||||
3. Logować nowe nierozpoznane statusy do activity_log
|
||||
|
||||
## Purpose
|
||||
Edge API Allegro zwraca opisy po polsku bez ustalonego słownika — mogą pojawić się nowe warianty. Hardcoded mapa nie wystarczy. Fallback + logowanie = system sam sobie radzi z nowymi opisami i informuje admina.
|
||||
|
||||
## Output
|
||||
- Rozszerzona mapa ALLEGRO_EDGE_MAP o 5 nowych slugów
|
||||
- Metoda guessStatusFromDescription() w DeliveryStatus jako keyword fallback
|
||||
- Logowanie nieznanych statusów w AllegroTrackingService
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Prior Work
|
||||
@.paul/phases/66-allegro-delivery-tracking/66-01-PLAN.md
|
||||
|
||||
## Nowe statusy z realnego zamówienia AD0243IOG6
|
||||
| Slug | Opis | Mapowanie |
|
||||
|------|------|-----------|
|
||||
| podjeta_z_maszyny_przez_kuriera | Przesyłka została podjęta z maszyny przez kuriera | in_transit |
|
||||
| przesylka_wyjechala_w_droge_do_punktu_docelowego | Przesyłka wyjechała w drogę do punktu docelowego | in_transit |
|
||||
| wyslana_z_sortowni | Wysłana z sortowni | in_transit |
|
||||
| wydana_do_doreczenia | Przesyłka została wydana do doręczenia | out_for_delivery |
|
||||
| przesylka_oczekuje_na_odbior | Przesyłka oczekuje na odbiór | ready_for_pickup |
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Nowe slugi w mapie
|
||||
```gherkin
|
||||
Given opis "Wysłana z sortowni" z edge API
|
||||
When slugify + normalize
|
||||
Then zwraca 'in_transit' (nie 'unknown')
|
||||
```
|
||||
|
||||
## AC-2: Fallback keyword matching
|
||||
```gherkin
|
||||
Given nieznany opis np. "Paczka jest w drodze do odbiorcy"
|
||||
When slug nie istnieje w ALLEGRO_EDGE_MAP
|
||||
Then guessStatusFromDescription() dopasowuje na podstawie słów kluczowych
|
||||
And zwraca odpowiedni znormalizowany status
|
||||
```
|
||||
|
||||
## AC-3: Logowanie nieznanych statusów
|
||||
```gherkin
|
||||
Given opis z edge API którego slug NIE jest w mapie i fallback zwraca unknown
|
||||
When AllegroTrackingService przetwarza taki status
|
||||
Then loguje do error_log: "[AllegroTracking] Nowy niezmapowany status: {opis} (slug: {slug})"
|
||||
And nadal zwraca wynik z status=unknown (nie null)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Rozszerzenie mapy + guessStatusFromDescription</name>
|
||||
<files>src/Modules/Shipments/DeliveryStatus.php</files>
|
||||
<action>
|
||||
1. Dodaj brakujące slugi do ALLEGRO_EDGE_MAP:
|
||||
```php
|
||||
'podjeta_z_maszyny_przez_kuriera' => self::IN_TRANSIT,
|
||||
'przesylka_wyjechala_w_droge_do_punktu_docelowego' => self::IN_TRANSIT,
|
||||
'wyjechala_w_droge_do_punktu_docelowego' => self::IN_TRANSIT,
|
||||
'wyslana_z_sortowni' => self::IN_TRANSIT,
|
||||
'wydana_do_doreczenia' => self::OUT_FOR_DELIVERY,
|
||||
'przesylka_oczekuje_na_odbior' => self::READY_FOR_PICKUP,
|
||||
```
|
||||
|
||||
2. Dodaj odpowiednie opisy do ALLEGRO_EDGE_DESCRIPTIONS:
|
||||
```php
|
||||
'podjeta_z_maszyny_przez_kuriera' => 'Podjęta z maszyny przez kuriera',
|
||||
'przesylka_wyjechala_w_droge_do_punktu_docelowego' => 'Wyjechała w drogę do punktu docelowego',
|
||||
'wyjechala_w_droge_do_punktu_docelowego' => 'Wyjechała w drogę do punktu docelowego',
|
||||
'wyslana_z_sortowni' => 'Wysłana z sortowni',
|
||||
'wydana_do_doreczenia' => 'Wydana do doręczenia',
|
||||
'przesylka_oczekuje_na_odbior' => 'Oczekuje na odbiór',
|
||||
```
|
||||
|
||||
3. Dodaj nową statyczną metodę `guessStatusFromDescription(string $description): string` — keyword-based fallback. Umieść ją po slugifyAllegroDescription():
|
||||
|
||||
```php
|
||||
public static function guessStatusFromDescription(string $description): string
|
||||
{
|
||||
$lower = mb_strtolower($description, 'UTF-8');
|
||||
|
||||
// Terminal statuses first
|
||||
if (str_contains($lower, 'doręczon') || str_contains($lower, 'dostarczono') || str_contains($lower, 'odebrana przez odbiorc')) {
|
||||
return self::DELIVERED;
|
||||
}
|
||||
if (str_contains($lower, 'zwrócon') || str_contains($lower, 'zwrocona')) {
|
||||
return self::RETURNED;
|
||||
}
|
||||
if (str_contains($lower, 'anulowan')) {
|
||||
return self::CANCELLED;
|
||||
}
|
||||
|
||||
// Active statuses
|
||||
if (str_contains($lower, 'doręczeni') || str_contains($lower, 'doreczenia') || str_contains($lower, 'wydana do')) {
|
||||
return self::OUT_FOR_DELIVERY;
|
||||
}
|
||||
if (str_contains($lower, 'odbiór') || str_contains($lower, 'odbior') || str_contains($lower, 'oczekuje na odb')) {
|
||||
return self::READY_FOR_PICKUP;
|
||||
}
|
||||
if (str_contains($lower, 'sortowni') || str_contains($lower, 'magazyn') || str_contains($lower, 'w drodze') || str_contains($lower, 'tranzyt') || str_contains($lower, 'kurier') || str_contains($lower, 'podjęta') || str_contains($lower, 'podjeta') || str_contains($lower, 'wyjechał') || str_contains($lower, 'wyjechala')) {
|
||||
return self::IN_TRANSIT;
|
||||
}
|
||||
if (str_contains($lower, 'nadana') || str_contains($lower, 'nadano')) {
|
||||
return self::CONFIRMED;
|
||||
}
|
||||
if (str_contains($lower, 'przygotowan') || str_contains($lower, 'utworzon')) {
|
||||
return self::CREATED;
|
||||
}
|
||||
if (str_contains($lower, 'uszkodzon') || str_contains($lower, 'problem') || str_contains($lower, 'zagubiła') || str_contains($lower, 'zagubion')) {
|
||||
return self::PROBLEM;
|
||||
}
|
||||
|
||||
return self::UNKNOWN;
|
||||
}
|
||||
```
|
||||
|
||||
WAŻNE: NIE zmieniaj istniejących metod normalize(), description(), slugifyAllegroDescription(). Tylko DODAWAJ nowe wpisy do map i nową metodę.
|
||||
</action>
|
||||
<verify>php -l przechodzi. Test: DeliveryStatus::normalize('allegro_edge', 'wyslana_z_sortowni') zwraca 'in_transit'. Test: DeliveryStatus::guessStatusFromDescription('Paczka jest w drodze') zwraca 'in_transit'.</verify>
|
||||
<done>AC-1 satisfied: nowe slugi w mapie. AC-2 satisfied: fallback method istnieje.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Integracja fallback + logowanie w AllegroTrackingService</name>
|
||||
<files>src/Modules/Shipments/AllegroTrackingService.php</files>
|
||||
<action>
|
||||
Zmodyfikuj metodę `fetchAllegroEdgeStatus()` — dodaj fallback i logowanie.
|
||||
|
||||
Po linii:
|
||||
```php
|
||||
$slug = DeliveryStatus::slugifyAllegroDescription($description);
|
||||
```
|
||||
|
||||
Zamień blok return na:
|
||||
```php
|
||||
$normalized = DeliveryStatus::normalize('allegro_edge', $slug);
|
||||
|
||||
// Fallback: jeśli slug nieznany, próbuj keyword matching
|
||||
if ($normalized === DeliveryStatus::UNKNOWN) {
|
||||
$normalized = DeliveryStatus::guessStatusFromDescription($description);
|
||||
|
||||
// Loguj niezmapowany status (do uzupełnienia mapy w przyszłości)
|
||||
error_log(sprintf(
|
||||
'[AllegroTracking] Niezmapowany status: "%s" (slug: %s, guessed: %s)',
|
||||
$description,
|
||||
$slug,
|
||||
$normalized
|
||||
));
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => $normalized,
|
||||
'status_raw' => $description,
|
||||
'description' => $description,
|
||||
];
|
||||
```
|
||||
|
||||
To zastępuje istniejący blok:
|
||||
```php
|
||||
return [
|
||||
'status' => DeliveryStatus::normalize('allegro_edge', $slug),
|
||||
'status_raw' => $description,
|
||||
'description' => $description,
|
||||
];
|
||||
```
|
||||
|
||||
WAŻNE: Nie zmieniaj nic innego w tym pliku. Tylko modyfikacja wewnątrz fetchAllegroEdgeStatus().
|
||||
</action>
|
||||
<verify>php -l przechodzi. Metoda fetchAllegroEdgeStatus zawiera fallback guessStatusFromDescription i error_log.</verify>
|
||||
<done>AC-2 partially satisfied: fallback zintegrowany. AC-3 satisfied: logowanie nieznanych statusów.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Istniejące mapy INPOST_MAP, APACZKA_MAP, ALLEGRO_MAP
|
||||
- Metody: normalize(), description(), slugifyAllegroDescription(), fetchInpostStatus()
|
||||
- src/Modules/Cron/ShipmentTrackingHandler.php (rate limit z 66-01 bez zmian)
|
||||
- Wszystkie inne pliki
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie tworzymy UI do zarządzania mapowaniem (istniejący Delivery Status Mapping UI wystarczy)
|
||||
- Nie tworzymy unit testów w tym planie
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
- [ ] php -l na obu plikach
|
||||
- [ ] DeliveryStatus::normalize('allegro_edge', 'wyslana_z_sortowni') === 'in_transit'
|
||||
- [ ] DeliveryStatus::normalize('allegro_edge', 'wydana_do_doreczenia') === 'out_for_delivery'
|
||||
- [ ] DeliveryStatus::guessStatusFromDescription('Paczka jest w drodze do odbiorcy') === 'in_transit'
|
||||
- [ ] DeliveryStatus::guessStatusFromDescription('Przesyłka odebrana w punkcie') !== 'unknown'
|
||||
- [ ] fetchAllegroEdgeStatus fallback + error_log działa
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Oba taski completed
|
||||
- Wszystkie realne opisy z API (obu zamówień) mapują się na właściwe statusy
|
||||
- Nieznane przyszłe opisy są obsługiwane przez keyword fallback
|
||||
- Nieznane statusy logowane do error_log
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/66-allegro-delivery-tracking/66-02-SUMMARY.md`
|
||||
</output>
|
||||
93
.paul/phases/66-allegro-delivery-tracking/66-02-SUMMARY.md
Normal file
93
.paul/phases/66-allegro-delivery-tracking/66-02-SUMMARY.md
Normal file
@@ -0,0 +1,93 @@
|
||||
---
|
||||
phase: 66-allegro-delivery-tracking
|
||||
plan: 02
|
||||
subsystem: shipments
|
||||
tags: [allegro, tracking, fallback, keyword-matching, logging]
|
||||
|
||||
requires:
|
||||
- phase: 66-allegro-delivery-tracking
|
||||
provides: AllegroTrackingService edge API, DeliveryStatus allegro_edge provider
|
||||
|
||||
provides:
|
||||
- Keyword-based fallback for unknown Allegro edge descriptions
|
||||
- Error logging for unmapped statuses
|
||||
- Extended ALLEGRO_EDGE_MAP with 6 new real-world slugs
|
||||
|
||||
affects: [allegro-tracking, delivery-status]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [keyword-fallback, graceful-degradation]
|
||||
|
||||
key-files:
|
||||
modified:
|
||||
- src/Modules/Shipments/DeliveryStatus.php
|
||||
- src/Modules/Shipments/AllegroTrackingService.php
|
||||
|
||||
key-decisions:
|
||||
- "Keyword fallback zamiast pytania użytkownika (cron nie ma interakcji)"
|
||||
- "error_log dla niezmapowanych statusów (monitoring bez blokowania)"
|
||||
- "IN_TRANSIT sprawdzany przed READY_FOR_PICKUP w fallback (uniknięcie fałszywego matchu 'odbior' w 'w drodze do odbiorcy')"
|
||||
|
||||
patterns-established:
|
||||
- "guessStatusFromDescription() jako graceful degradation dla nieznanych opisów"
|
||||
- "Kolejność keyword matching: terminal → active → transit → pickup (specyficzność malejąca)"
|
||||
|
||||
duration: ~10min
|
||||
started: 2026-04-03T20:45:00Z
|
||||
completed: 2026-04-03T20:55:00Z
|
||||
---
|
||||
|
||||
# Phase 66 Plan 02: Allegro Tracking Fallback + Extended Map
|
||||
|
||||
**Rozszerzenie mapy o 6 nowych slugów z realnych przesyłek, keyword-based fallback (guessStatusFromDescription) dla nieznanych opisów, logowanie nowych statusów do error_log.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~10min |
|
||||
| Tasks | 2 completed (delegated) + 1 orkiestrator fix |
|
||||
| Files modified | 2 |
|
||||
| Execution mode | Delegated auto (2 sub-agents, sequential) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Nowe slugi w mapie | Pass | 6 nowych slugów z zamówienia AD0243IOG6 |
|
||||
| AC-2: Fallback keyword matching | Pass | guessStatusFromDescription() — 9 kategorii keywords |
|
||||
| AC-3: Logowanie nieznanych statusów | Pass | error_log w fetchAllegroEdgeStatus |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- 6 nowych slugów w ALLEGRO_EDGE_MAP z realnego zamówienia AD0243IOG6
|
||||
- guessStatusFromDescription() jako keyword fallback — 9 kategorii (delivered, returned, cancelled, out_for_delivery, ready_for_pickup, in_transit, confirmed, created, problem)
|
||||
- Logowanie niezmapowanych statusów do error_log z pełnym kontekstem (opis, slug, guessed status)
|
||||
- Fix kolejności keyword matching: IN_TRANSIT przed READY_FOR_PICKUP
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Shipments/DeliveryStatus.php` | Modified | +6 slugów w mapie, +guessStatusFromDescription() |
|
||||
| `src/Modules/Shipments/AllegroTrackingService.php` | Modified | Fallback + error_log w fetchAllegroEdgeStatus() |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Kolejność keyword matching**
|
||||
- **Found during:** Orkiestrator verification
|
||||
- **Issue:** "w drodze do odbiorcy" matchował READY_FOR_PICKUP (bo `odbior`) zamiast IN_TRANSIT
|
||||
- **Fix:** IN_TRANSIT sprawdzany przed READY_FOR_PICKUP, READY_FOR_PICKUP ograniczony do `oczekuje na odb`/`gotowa do odb`
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:** Phase 66 complete — Allegro Delivery tracking działa z mapą + fallback + logowaniem
|
||||
|
||||
**Blockers:** None
|
||||
|
||||
---
|
||||
*Phase: 66-allegro-delivery-tracking, Plan: 02*
|
||||
*Completed: 2026-04-03*
|
||||
310
.paul/phases/67-paul-codex-executor/67-01-PLAN.md
Normal file
310
.paul/phases/67-paul-codex-executor/67-01-PLAN.md
Normal file
@@ -0,0 +1,310 @@
|
||||
---
|
||||
phase: 67-paul-codex-executor
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["65-01"]
|
||||
files_modified:
|
||||
- ~/.claude/paul-framework/references/delegated-apply.md
|
||||
- ~/.claude/paul-framework/workflows/apply-phase.md
|
||||
- ~/.claude/paul-framework/templates/PLAN.md
|
||||
autonomous: true
|
||||
delegation: auto
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Dodać Codex CLI (`codex exec`) jako alternatywny executor w PAUL delegated-apply. Reguła: jeśli plan ma >3 tasków auto, 1/3 z nich MUSI iść przez Codex. Plus: auto-fallback na Codex przy quota error Claude.
|
||||
|
||||
## Purpose
|
||||
- Oszczędność 5h limitu Claude Code — Codex jest osobno opłacany
|
||||
- Rotacja executorów — wymuszenie użycia Codex przy większych planach
|
||||
- Fallback — gdy Claude limit się kończy, praca nie staje
|
||||
|
||||
## Output
|
||||
- Zaktualizowany `delegated-apply.md` z sekcją Codex executor
|
||||
- Zaktualizowany `apply-phase.md` z logiką wyboru executor (Claude vs Codex)
|
||||
- Zaktualizowany `PLAN.md` template z nowym trybem delegation
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Prior Work
|
||||
@.paul/phases/65-paul-delegated-apply/65-01-SUMMARY.md
|
||||
|
||||
## Research: Codex CLI
|
||||
- Komenda: `codex exec -c 'approval_mode="full-auto"' "prompt"`
|
||||
- Model: gpt-5.3-codex
|
||||
- Synchroniczny, non-interactive, zwraca diff + summary
|
||||
- Timeout: ~30s na proste zadanie, max ~5min na złożone
|
||||
- Uwagi: pilnować UTF-8 bez BOM, polskie znaki w prompcie
|
||||
- Skill warnings (niekrytyczne): ignorować
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Reguła 1/3 Codex przy >3 taskach
|
||||
```gherkin
|
||||
Given plan z 6 taskami type="auto" i delegation: auto
|
||||
When orkiestrator przydziela executory
|
||||
Then minimum 2 taski (ceil(6/3)) idą przez Codex
|
||||
And pozostałe 4 przez Claude sub-agent
|
||||
And wybór tasków dla Codex to te najprostsze (najmniej plików)
|
||||
```
|
||||
|
||||
## AC-2: Auto-fallback Claude → Codex
|
||||
```gherkin
|
||||
Given task delegowany do Claude sub-agent
|
||||
When Agent tool zwróci błąd quota/rate-limit
|
||||
Then orkiestrator automatycznie ponawia ten sam task przez codex exec
|
||||
And loguje: "Task N: Claude quota → fallback Codex"
|
||||
```
|
||||
|
||||
## AC-3: Codex micro-prompt zawiera guard UTF-8
|
||||
```gherkin
|
||||
Given task przydzielony do Codex
|
||||
When orkiestrator buduje mikro-prompt
|
||||
Then prompt zawiera instrukcje: "IMPORTANT: All files must remain UTF-8 without BOM. Preserve Polish characters (ą,ć,ę,ł,ń,ó,ś,ź,ż)."
|
||||
```
|
||||
|
||||
## AC-4: Backward compatible
|
||||
```gherkin
|
||||
Given plan z delegation: off lub delegation: auto z <=3 taskami
|
||||
When orkiestrator wykonuje plan
|
||||
Then zachowanie identyczne jak dotychczas (bez Codex)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Rozszerzenie delegated-apply.md o Codex executor</name>
|
||||
<files>~/.claude/paul-framework/references/delegated-apply.md</files>
|
||||
<action>
|
||||
Przeczytaj plik, potem dodaj nowe sekcje.
|
||||
|
||||
1. Dodaj nową sekcję "## Codex Executor" PO sekcji "## Evaluation Protocol" a PRZED "## Delegation Modes":
|
||||
|
||||
```markdown
|
||||
## Codex Executor
|
||||
|
||||
Codex CLI (`codex exec`) służy jako alternatywny executor dla tasków z PLAN.md.
|
||||
|
||||
### Wywołanie
|
||||
|
||||
```bash
|
||||
codex exec -c 'approval_mode="full-auto"' "MICRO_PROMPT"
|
||||
```
|
||||
|
||||
- Synchroniczny, non-interactive
|
||||
- Timeout Bash: 300000ms (5 min)
|
||||
- Output zawiera diff + summary — parsowany przez orkiestratora
|
||||
|
||||
### Reguła 1/3
|
||||
|
||||
Jeśli plan ma **>3 tasków type="auto"**, minimum `ceil(count/3)` tasków MUSI iść przez Codex:
|
||||
|
||||
| Tasków auto | Min Codex | Min Claude |
|
||||
|-------------|-----------|------------|
|
||||
| 1-3 | 0 | all |
|
||||
| 4-6 | 2 | reszta |
|
||||
| 7-9 | 3 | reszta |
|
||||
| 10+ | ceil(N/3) | reszta |
|
||||
|
||||
**Wybór tasków dla Codex:** Orkiestrator przydziela Codexowi taski o najmniejszej liczbie plików w `<files>` (najprostsze). Jeśli równe — pierwsze w kolejności.
|
||||
|
||||
**KRYTYCZNE: Brak współdzielenia plików.** Codex i Claude sub-agenty NIE MOGĄ edytować tych samych plików — działają na tym samym working directory. Orkiestrator MUSI sprawdzić `<files>` każdego taska i zapewnić że:
|
||||
- Żaden plik z tasków Codex nie pokrywa się z plikami tasków Claude
|
||||
- Jeśli jest konflikt plików → oba taski idą do tego samego executora
|
||||
- Przy parallel mode: taski z tymi samymi plikami NIE lecą równolegle
|
||||
|
||||
### Auto-fallback
|
||||
|
||||
Jeśli Claude Agent tool zwróci błąd (quota, rate-limit, timeout):
|
||||
1. Orkiestrator loguje: "Task N: Claude error → fallback Codex"
|
||||
2. Ponawia TEN SAM mikro-prompt przez `codex exec`
|
||||
3. Evaluation protocol identyczny jak dla Claude
|
||||
|
||||
### Micro-prompt guard
|
||||
|
||||
Każdy mikro-prompt wysyłany do Codex MUSI zawierać na końcu:
|
||||
|
||||
```
|
||||
## IMPORTANT
|
||||
- All files must remain UTF-8 without BOM encoding
|
||||
- Preserve all Polish characters: ą,ć,ę,ł,ń,ó,ś,ź,ż,Ą,Ć,Ę,Ł,Ń,Ó,Ś,Ź,Ż
|
||||
- Do NOT convert file encoding
|
||||
- Do NOT add BOM markers
|
||||
```
|
||||
|
||||
### Evaluation po Codex
|
||||
|
||||
Identyczny jak po Claude sub-agent:
|
||||
1. git diff --stat
|
||||
2. Sprawdź done criteria
|
||||
3. Sprawdź boundaries
|
||||
4. **Dodatkowe:** `file` command na zmodyfikowanych plikach — potwierdź UTF-8
|
||||
5. Decyzja: accept / retry / escalate
|
||||
```
|
||||
|
||||
2. W sekcji "## Delegation Modes", zaktualizuj tabelę:
|
||||
|
||||
Dodaj wiersz:
|
||||
```
|
||||
| codex | `delegation: codex` | Wszystkie taski auto przez Codex exec |
|
||||
```
|
||||
|
||||
W "Kiedy użyć jakiego" dodaj:
|
||||
```
|
||||
- **`codex`** — wymuszone użycie Codex dla wszystkich tasków (np. przy niskim limicie Claude)
|
||||
```
|
||||
|
||||
3. W sekcji "## Constraints", dodaj:
|
||||
```
|
||||
- Codex exec timeout: 300s (5 min) per task — jeśli task jest większy, rozbij
|
||||
- Codex nie ma dostępu do MCP servers ani Agent tool — tylko filesystem + shell
|
||||
```
|
||||
</action>
|
||||
<verify>Plik zawiera sekcję "Codex Executor" z regułą 1/3, auto-fallback, micro-prompt guard. Tabela Delegation Modes zawiera tryb codex.</verify>
|
||||
<done>AC-1 spec: reguła 1/3 zdefiniowana. AC-2 spec: auto-fallback zdefiniowany. AC-3 spec: UTF-8 guard zdefiniowany.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Logika executor selection w apply-phase.md</name>
|
||||
<files>~/.claude/paul-framework/workflows/apply-phase.md</files>
|
||||
<action>
|
||||
Przeczytaj plik, potem zmodyfikuj.
|
||||
|
||||
1. W kroku `determine_execution_mode`, ROZSZERZ punkt 2 ("If delegated mode"):
|
||||
Dodaj pod-punkt po identyfikacji task clusters:
|
||||
```
|
||||
- Determine executor assignment per task:
|
||||
a. Count auto tasks in plan
|
||||
b. If count > 3: assign ceil(count/3) tasks to Codex executor
|
||||
- Select tasks with fewest files in <files> for Codex
|
||||
- Remaining tasks go to Claude Agent tool
|
||||
- CRITICAL: Verify no file overlap between Codex and Claude tasks
|
||||
If overlap detected → move conflicting task to same executor as its peer
|
||||
c. If delegation: codex → all auto tasks to Codex
|
||||
d. If delegation: auto/parallel with <=3 tasks → all to Claude Agent tool
|
||||
e. Log: "Executor assignment: N Claude, M Codex (no file conflicts)"
|
||||
```
|
||||
|
||||
2. W kroku `execute_tasks`, w bloku "If delegated mode and task type=auto":
|
||||
Dodaj rozgałęzienie na executor:
|
||||
```
|
||||
**If task assigned to Claude Agent tool:**
|
||||
[existing Agent tool delegation — no changes]
|
||||
|
||||
**If task assigned to Codex executor:**
|
||||
1. Build micro-prompt (same template as Claude)
|
||||
2. Append UTF-8 guard block to prompt
|
||||
3. Execute via Bash:
|
||||
codex exec -c 'approval_mode="full-auto"' "MICRO_PROMPT"
|
||||
Timeout: 300000ms
|
||||
4. Parse output for diff and summary
|
||||
5. Evaluate: git diff + done criteria + boundaries + UTF-8 check
|
||||
6. Decision: accept / retry (max 3) / escalate
|
||||
|
||||
**Auto-fallback (Claude → Codex):**
|
||||
If Agent tool returns error (quota/rate-limit/connection):
|
||||
1. Log: "Task N: [name] → Claude error: [reason] → fallback Codex"
|
||||
2. Re-execute same micro-prompt via codex exec
|
||||
3. Continue evaluation as normal
|
||||
```
|
||||
|
||||
3. W kroku `finalize`, rozszerz "Delegated execution summary":
|
||||
```
|
||||
- Tasks via Claude: N
|
||||
- Tasks via Codex: N
|
||||
- Tasks via Codex (fallback): N
|
||||
```
|
||||
|
||||
4. W sekcji `error_handling`, dodaj:
|
||||
```
|
||||
**Codex exec timeout:**
|
||||
- If codex exec exceeds 300s timeout: mark as failed
|
||||
- Offer retry via Claude Agent tool (reverse fallback)
|
||||
|
||||
**Codex encoding issue:**
|
||||
- If file check shows non-UTF-8 after Codex: auto-fix encoding
|
||||
- Log warning and continue
|
||||
```
|
||||
|
||||
WAŻNE: Nie usuwaj istniejącej logiki — rozszerzaj.
|
||||
</action>
|
||||
<verify>apply-phase.md zawiera: executor assignment w determine_execution_mode, rozgałęzienie Claude/Codex w execute_tasks, auto-fallback, Codex w finalize summary, Codex error handling.</verify>
|
||||
<done>AC-1: logika 1/3 w orkiestratorze. AC-2: auto-fallback zaimplementowany. AC-4: <=3 tasków = bez Codex.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Aktualizacja PLAN.md template</name>
|
||||
<files>~/.claude/paul-framework/templates/PLAN.md</files>
|
||||
<action>
|
||||
Przeczytaj plik, potem zmodyfikuj.
|
||||
|
||||
1. W frontmatter YAML, zaktualizuj komentarz przy delegation:
|
||||
Zmień:
|
||||
```yaml
|
||||
delegation: auto # auto (default) | parallel | off — execution mode for /paul:apply
|
||||
```
|
||||
na:
|
||||
```yaml
|
||||
delegation: auto # auto (default) | parallel | codex | off — execution mode for /paul:apply
|
||||
```
|
||||
|
||||
2. W tabeli Frontmatter Fields, zaktualizuj opis delegation:
|
||||
Zmień na:
|
||||
```
|
||||
| `delegation` | No | Execution mode: `auto` (delegated, 1/3 Codex if >3 tasks, default), `parallel` (delegated + parallel), `codex` (all tasks via Codex), `off` (inline, legacy) |
|
||||
```
|
||||
|
||||
3. W sekcji "## Delegation Mode", dodaj `codex` do tabeli:
|
||||
```
|
||||
| `codex` | When Claude limit is low or you want to force Codex for all tasks |
|
||||
```
|
||||
|
||||
WAŻNE: Nie zmieniaj nic innego.
|
||||
</action>
|
||||
<verify>PLAN.md template zawiera tryb codex w frontmatter komentarzu, tabeli i sekcji Delegation Mode.</verify>
|
||||
<done>AC-4 partially: template zaktualizowany o nowy tryb.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- ~/.claude/paul-framework/workflows/plan-phase.md
|
||||
- ~/.claude/paul-framework/workflows/unify-phase.md
|
||||
- ~/.claude/paul-framework/rules/*
|
||||
- ~/.claude/paul-framework/references/subagent-criteria.md
|
||||
- .paul/PROJECT.md, .paul/ROADMAP.md
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie tworzymy wrapper script dla codex — wywołanie bezpośrednio z Bash
|
||||
- Nie modyfikujemy Codex config (~/.codex/) — używamy -c flag
|
||||
- Nie implementujemy monitoring limitu Claude — reaktywny fallback wystarczy
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
- [ ] delegated-apply.md zawiera sekcję Codex Executor z regułą 1/3
|
||||
- [ ] apply-phase.md zawiera executor assignment + rozgałęzienie Claude/Codex
|
||||
- [ ] apply-phase.md zawiera auto-fallback Claude → Codex
|
||||
- [ ] PLAN.md template zawiera tryb codex
|
||||
- [ ] Reguła 1/3 dotyczy tylko planów >3 tasków auto
|
||||
- [ ] UTF-8 guard jest w każdym Codex mikro-prompcie
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 3 taski completed
|
||||
- PAUL apply wspiera Codex jako executor
|
||||
- Reguła 1/3 wymusza rotację przy większych planach
|
||||
- Auto-fallback chroni przed przestojem przy quota error
|
||||
- Backward compatible
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/67-paul-codex-executor/67-01-SUMMARY.md`
|
||||
</output>
|
||||
97
.paul/phases/67-paul-codex-executor/67-01-SUMMARY.md
Normal file
97
.paul/phases/67-paul-codex-executor/67-01-SUMMARY.md
Normal file
@@ -0,0 +1,97 @@
|
||||
---
|
||||
phase: 67-paul-codex-executor
|
||||
plan: 01
|
||||
subsystem: infra
|
||||
tags: [paul-framework, codex, delegation, executor, fallback]
|
||||
|
||||
requires:
|
||||
- phase: 65-paul-delegated-apply
|
||||
provides: Delegated-apply workflow, micro-prompt template, evaluation protocol
|
||||
|
||||
provides:
|
||||
- Codex CLI as alternative executor in PAUL delegated-apply
|
||||
- Rule of thirds (1/3 Codex if >3 tasks)
|
||||
- Auto-fallback Claude → Codex on quota error
|
||||
- UTF-8 guard for Codex micro-prompts
|
||||
- File overlap protection between executors
|
||||
|
||||
affects: [all future phases using /paul:apply with >3 tasks]
|
||||
|
||||
tech-stack:
|
||||
added: [codex-cli]
|
||||
patterns: [multi-executor-delegation, auto-fallback, file-conflict-detection]
|
||||
|
||||
key-files:
|
||||
modified:
|
||||
- ~/.claude/paul-framework/references/delegated-apply.md
|
||||
- ~/.claude/paul-framework/workflows/apply-phase.md
|
||||
- ~/.claude/paul-framework/templates/PLAN.md
|
||||
|
||||
key-decisions:
|
||||
- "Reguła 1/3: ceil(count/3) tasków do Codex przy >3 taskach auto"
|
||||
- "Codex dostaje najprostsze taski (fewest files)"
|
||||
- "Codex i Claude NIE MOGĄ edytować tych samych plików"
|
||||
- "Auto-fallback Claude→Codex przy quota error, reverse fallback przy Codex timeout"
|
||||
- "UTF-8 guard obowiązkowy w każdym Codex micro-prompt"
|
||||
|
||||
patterns-established:
|
||||
- "codex exec -c 'approval_mode=\"full-auto\"' jako non-interactive executor"
|
||||
- "File overlap check przed przydziałem executorów"
|
||||
- "delegation: codex jako tryb wymuszający Codex dla wszystkich tasków"
|
||||
|
||||
duration: ~10min
|
||||
started: 2026-04-03T21:00:00Z
|
||||
completed: 2026-04-03T21:10:00Z
|
||||
---
|
||||
|
||||
# Phase 67 Plan 01: PAUL Codex Executor
|
||||
|
||||
**Integracja Codex CLI jako alternatywny executor w PAUL — reguła 1/3 przy >3 taskach, auto-fallback Claude→Codex, ochrona plików przed współdzieleniem executorów, UTF-8 guard.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~10min |
|
||||
| Tasks | 3 completed (delegated, parallel) |
|
||||
| Files modified | 3 |
|
||||
| Execution mode | Delegated auto (3 sub-agents parallel) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Reguła 1/3 Codex przy >3 taskach | Pass | Zdefiniowana w delegated-apply.md + apply-phase.md |
|
||||
| AC-2: Auto-fallback Claude → Codex | Pass | W execute_tasks + error_handling |
|
||||
| AC-3: UTF-8 guard w Codex prompts | Pass | Micro-prompt guard w delegated-apply.md |
|
||||
| AC-4: Backward compatible | Pass | <=3 tasków = bez Codex, delegation: off = inline |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Sekcja "Codex Executor" w delegated-apply.md z regułą 1/3, auto-fallback, UTF-8 guard
|
||||
- Executor assignment logic w apply-phase.md z file overlap protection
|
||||
- Tryb `delegation: codex` w PLAN.md template
|
||||
- Error handling: Codex timeout, encoding issues, file conflicts
|
||||
- Anti-patterns: mixing executors on same files, Polish text without guard
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `~/.claude/paul-framework/references/delegated-apply.md` | Modified | +Codex Executor section, +codex mode, +constraints |
|
||||
| `~/.claude/paul-framework/workflows/apply-phase.md` | Modified | +executor assignment, +Codex path, +fallback, +error handling |
|
||||
| `~/.claude/paul-framework/templates/PLAN.md` | Modified | +codex mode w frontmatter, tabeli, sekcji |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed as specified.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:** PAUL wspiera 4 tryby delegation: auto, parallel, codex, off. Codex zintegrowany jako executor z pełną ochroną (file overlap, UTF-8, fallback).
|
||||
|
||||
**Blockers:** None
|
||||
|
||||
---
|
||||
*Phase: 67-paul-codex-executor, Plan: 01*
|
||||
*Completed: 2026-04-03*
|
||||
222
.paul/phases/68-code-deduplication-refactor/68-01-PLAN.md
Normal file
222
.paul/phases/68-code-deduplication-refactor/68-01-PLAN.md
Normal file
@@ -0,0 +1,222 @@
|
||||
---
|
||||
phase: 68-code-deduplication-refactor
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/Core/Http/SslCertificateResolver.php (new)
|
||||
- src/Modules/Settings/AllegroApiClient.php
|
||||
- src/Modules/Settings/ApaczkaApiClient.php
|
||||
- src/Modules/Settings/ShopproApiClient.php
|
||||
- src/Modules/Settings/AllegroOAuthClient.php
|
||||
- src/Modules/Shipments/AllegroTrackingService.php
|
||||
- src/Modules/Shipments/InpostShipmentService.php
|
||||
- src/Modules/Shipments/InpostTrackingService.php
|
||||
- src/Core/Http/ToggleableRepositoryTrait.php (new)
|
||||
- src/Modules/Automation/AutomationController.php
|
||||
- src/Modules/Settings/EmailMailboxRepository.php
|
||||
- src/Modules/Settings/EmailTemplateRepository.php
|
||||
- src/Modules/Settings/ReceiptConfigRepository.php
|
||||
- src/Modules/Settings/EmailMailboxController.php
|
||||
- src/Modules/Settings/EmailTemplateController.php
|
||||
- src/Modules/Settings/ReceiptConfigController.php
|
||||
- src/Core/Http/RedirectPathResolver.php (new)
|
||||
- src/Modules/Settings/AllegroIntegrationController.php
|
||||
- src/Modules/Settings/ApaczkaIntegrationController.php
|
||||
- src/Modules/Settings/InpostIntegrationController.php
|
||||
autonomous: true
|
||||
delegation: auto
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Wyeliminowac zduplikowana logike biznesowa i infrastrukturalna z codebase — 7x getCaBundlePath, 7x toggleStatus, 3x resolveRedirectPath — przez ekstrakcje do wspolnych klas/traitow.
|
||||
|
||||
## Purpose
|
||||
Duplikacja kodu powoduje realne bugi (jak dzisiejszy brak godzin na paragonach — ta sama logika w 2 miejscach, poprawiona tylko w 1). Konsolidacja zmniejsza ryzyko rozsynchronizowania i ulatwia utrzymanie.
|
||||
|
||||
## Output
|
||||
- `Core/Http/SslCertificateResolver.php` — statyczna metoda zastepujaca 7 kopii getCaBundlePath
|
||||
- `Core/Http/ToggleableRepositoryTrait.php` — trait z metoda toggleActive() dla repozytoriow
|
||||
- `Core/Http/RedirectPathResolver.php` — statyczna metoda zastepujaca 3 kopie resolveRedirectPath
|
||||
- 13 zrefaktoryzowanych plikow zrodlowych
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Settings/AllegroApiClient.php
|
||||
@src/Modules/Settings/ApaczkaApiClient.php
|
||||
@src/Modules/Settings/ShopproApiClient.php
|
||||
@src/Modules/Settings/AllegroOAuthClient.php
|
||||
@src/Modules/Shipments/AllegroTrackingService.php
|
||||
@src/Modules/Shipments/InpostShipmentService.php
|
||||
@src/Modules/Shipments/InpostTrackingService.php
|
||||
@src/Modules/Automation/AutomationController.php
|
||||
@src/Modules/Settings/EmailMailboxRepository.php
|
||||
@src/Modules/Settings/EmailTemplateRepository.php
|
||||
@src/Modules/Settings/ReceiptConfigRepository.php
|
||||
@src/Modules/Settings/AllegroIntegrationController.php
|
||||
@src/Modules/Settings/ApaczkaIntegrationController.php
|
||||
@src/Modules/Settings/InpostIntegrationController.php
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: getCaBundlePath wyeliminowane z 7 klas
|
||||
```gherkin
|
||||
Given 7 klas posiada identyczna prywatna metode getCaBundlePath()
|
||||
When refaktor zostanie zastosowany
|
||||
Then istnieje jedna klasa SslCertificateResolver ze statyczna metoda resolve()
|
||||
And zadna z 7 klas nie posiada juz metody getCaBundlePath
|
||||
And wszystkie wywolania uzywaja SslCertificateResolver::resolve()
|
||||
And PHP lint przechodzi bez bledow
|
||||
```
|
||||
|
||||
## AC-2: toggleStatus skonsolidowane w trait
|
||||
```gherkin
|
||||
Given 4+ repozytoriow posiada podobna metode toggleActive/toggleStatus
|
||||
When refaktor zostanie zastosowany
|
||||
Then istnieje trait ToggleableRepositoryTrait z metoda toggleActive()
|
||||
And repozytoria uzywaja traita zamiast wlasnej implementacji
|
||||
And kontrolery toggleStatus wywoluja repozytorium bez zmian w zachowaniu
|
||||
And PHP lint przechodzi bez bledow
|
||||
```
|
||||
|
||||
## AC-3: resolveRedirectPath skonsolidowane
|
||||
```gherkin
|
||||
Given 3 kontrolery integracji posiadaja identyczna metode resolveRedirectPath
|
||||
When refaktor zostanie zastosowany
|
||||
Then istnieje jedna klasa/metoda RedirectPathResolver
|
||||
And 3 kontrolery uzywaja wspolnej implementacji
|
||||
And PHP lint przechodzi bez bledow
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Ekstrakcja SslCertificateResolver z 7 kopii getCaBundlePath</name>
|
||||
<files>
|
||||
src/Core/Http/SslCertificateResolver.php (new),
|
||||
src/Modules/Settings/AllegroApiClient.php,
|
||||
src/Modules/Settings/ApaczkaApiClient.php,
|
||||
src/Modules/Settings/ShopproApiClient.php,
|
||||
src/Modules/Settings/AllegroOAuthClient.php,
|
||||
src/Modules/Shipments/AllegroTrackingService.php,
|
||||
src/Modules/Shipments/InpostShipmentService.php,
|
||||
src/Modules/Shipments/InpostTrackingService.php
|
||||
</files>
|
||||
<action>
|
||||
1. Przeczytaj getCaBundlePath() z jednej z 7 klas (identyczne)
|
||||
2. Utworz src/Core/Http/SslCertificateResolver.php:
|
||||
- final class, namespace App\Core\Http
|
||||
- public static function resolve(): string — logika z getCaBundlePath
|
||||
3. W kazdej z 7 klas:
|
||||
- Dodaj use App\Core\Http\SslCertificateResolver
|
||||
- Zamien wywolania $this->getCaBundlePath() na SslCertificateResolver::resolve()
|
||||
- Usun prywatna metode getCaBundlePath()
|
||||
4. Uruchom PHP lint na wszystkich 8 plikach
|
||||
Unikaj: zmieniania jakiejkolwiek innej logiki w tych klasach
|
||||
</action>
|
||||
<verify>PHP lint: php -l na wszystkich 8 plikach + grep -r "getCaBundlePath" src/ zwraca 0 wynikow</verify>
|
||||
<done>AC-1 satisfied: getCaBundlePath nie istnieje w zadnej z 7 klas, SslCertificateResolver jest jedynym zrodlem</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Ekstrakcja ToggleableRepositoryTrait z powtorzonej logiki toggleActive</name>
|
||||
<files>
|
||||
src/Core/Http/ToggleableRepositoryTrait.php (new),
|
||||
src/Modules/Settings/EmailMailboxRepository.php,
|
||||
src/Modules/Settings/EmailTemplateRepository.php,
|
||||
src/Modules/Settings/ReceiptConfigRepository.php,
|
||||
src/Modules/Automation/AutomationController.php,
|
||||
src/Modules/Settings/EmailMailboxController.php,
|
||||
src/Modules/Settings/EmailTemplateController.php,
|
||||
src/Modules/Settings/ReceiptConfigController.php
|
||||
</files>
|
||||
<action>
|
||||
1. Przeczytaj metody toggleActive/toggleStatus z repozytoriow — zidentyfikuj wspolny wzorzec
|
||||
2. Utworz src/Core/Http/ToggleableRepositoryTrait.php:
|
||||
- trait ToggleableRepositoryTrait, namespace App\Core\Http
|
||||
- Metoda toggleActive(string $table, int $id, string $column = 'is_active'): bool
|
||||
- Wymaga aby klasa uzywajaca traita miala property $db (Medoo)
|
||||
3. W kazdym repozytorium z toggleActive:
|
||||
- Dodaj use ToggleableRepositoryTrait
|
||||
- Usun lokalna metode toggleActive/toggleStatus
|
||||
- Jesli sygnatura sie rozni, dostosuj wywolania w kontrolerach
|
||||
4. PHP lint na wszystkich zmienionych plikach
|
||||
Unikaj: zmieniania logiki kontrolerow poza dostosowaniem wywolan toggle
|
||||
</action>
|
||||
<verify>PHP lint na wszystkich zmienionych plikach + metody toggleActive/toggleStatus nie istnieja lokalnie w repozytoriach</verify>
|
||||
<done>AC-2 satisfied: trait ToggleableRepositoryTrait jest jedynym zrodlem logiki toggle</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Ekstrakcja RedirectPathResolver z 3 kontrolerow integracji</name>
|
||||
<files>
|
||||
src/Core/Http/RedirectPathResolver.php (new),
|
||||
src/Modules/Settings/AllegroIntegrationController.php,
|
||||
src/Modules/Settings/ApaczkaIntegrationController.php,
|
||||
src/Modules/Settings/InpostIntegrationController.php
|
||||
</files>
|
||||
<action>
|
||||
1. Przeczytaj resolveRedirectPath() z 3 kontrolerow — potwierdz identycznosc
|
||||
2. Utworz src/Core/Http/RedirectPathResolver.php:
|
||||
- final class, namespace App\Core\Http
|
||||
- public static function resolve(string $requestedPath, array $allowedPaths, string $default): string
|
||||
3. W kazdym z 3 kontrolerow:
|
||||
- Dodaj use App\Core\Http\RedirectPathResolver
|
||||
- Zamien wywolania $this->resolveRedirectPath(...) na RedirectPathResolver::resolve(...)
|
||||
- Usun prywatna metode resolveRedirectPath()
|
||||
4. PHP lint na 4 plikach
|
||||
Unikaj: zmieniania logiki walidacji/przekierowan poza ekstrakcja
|
||||
</action>
|
||||
<verify>PHP lint na 4 plikach + grep -r "function resolveRedirectPath" src/ zwraca 0 wynikow</verify>
|
||||
<done>AC-3 satisfied: resolveRedirectPath nie istnieje w zadnym z 3 kontrolerow</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- database/migrations/* — brak zmian schematu
|
||||
- resources/views/* — brak zmian widokow
|
||||
- routes/web.php — brak zmian routingu (chyba ze wymagane przez nowe zależności DI)
|
||||
- Logika biznesowa w zmienianych klasach — refaktor dotyczy TYLKO duplikacji
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Ten plan obejmuje tylko getCaBundlePath, toggleStatus i resolveRedirectPath
|
||||
- Duplikacje apiRequest, downloadLabel, buildReceiverAddress, resolveColumns — osobny plan (68-02)
|
||||
- Nie refaktoryzowac validateCsrf (wymaga analizy middleware — osobny plan)
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] PHP lint przechodzi na wszystkich zmienionych plikach
|
||||
- [ ] grep -r "getCaBundlePath" src/ — 0 wynikow
|
||||
- [ ] grep -r "function resolveRedirectPath" src/ — 0 wynikow
|
||||
- [ ] Ponowna analiza duplikacji w codebase — potwierdzenie eliminacji
|
||||
- [ ] Wszystkie acceptance criteria spelnione
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 7 kopii getCaBundlePath → 1 klasa SslCertificateResolver
|
||||
- 7 kopii toggleStatus → 1 trait ToggleableRepositoryTrait
|
||||
- 3 kopie resolveRedirectPath → 1 klasa RedirectPathResolver
|
||||
- ~170 linii zduplikowanego kodu wyeliminowane
|
||||
- Zero regresji (PHP lint clean)
|
||||
- Ponowna analiza duplikacji potwierdza eliminacje
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/68-code-deduplication-refactor/68-01-SUMMARY.md`
|
||||
</output>
|
||||
149
.paul/phases/68-code-deduplication-refactor/68-01-SUMMARY.md
Normal file
149
.paul/phases/68-code-deduplication-refactor/68-01-SUMMARY.md
Normal file
@@ -0,0 +1,149 @@
|
||||
---
|
||||
phase: 68-code-deduplication-refactor
|
||||
plan: 01
|
||||
subsystem: infra
|
||||
tags: [refactor, deduplication, trait, static-helper]
|
||||
|
||||
requires: []
|
||||
provides:
|
||||
- SslCertificateResolver — single source for CA bundle path
|
||||
- ToggleableRepositoryTrait — reusable toggle pattern for repositories
|
||||
- RedirectPathResolver — single source for redirect path validation
|
||||
- ReceiptService — single source for receipt issuance logic
|
||||
affects: [future integrations, new repositories with toggle, new controllers with redirects]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [static resolver classes, repository traits]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- src/Core/Http/SslCertificateResolver.php
|
||||
- src/Core/Http/ToggleableRepositoryTrait.php
|
||||
- src/Core/Http/RedirectPathResolver.php
|
||||
- src/Modules/Accounting/ReceiptService.php
|
||||
- src/Modules/Accounting/ReceiptIssueException.php
|
||||
modified:
|
||||
- src/Modules/Settings/AllegroApiClient.php
|
||||
- src/Modules/Settings/ApaczkaApiClient.php
|
||||
- src/Modules/Settings/ShopproApiClient.php
|
||||
- src/Modules/Settings/AllegroOAuthClient.php
|
||||
- src/Modules/Shipments/AllegroTrackingService.php
|
||||
- src/Modules/Shipments/InpostShipmentService.php
|
||||
- src/Modules/Shipments/InpostTrackingService.php
|
||||
- src/Modules/Settings/EmailMailboxRepository.php
|
||||
- src/Modules/Settings/EmailTemplateRepository.php
|
||||
- src/Modules/Settings/ReceiptConfigRepository.php
|
||||
- src/Modules/Automation/AutomationRepository.php
|
||||
- src/Modules/Settings/AllegroIntegrationController.php
|
||||
- src/Modules/Settings/ApaczkaIntegrationController.php
|
||||
- src/Modules/Settings/InpostIntegrationController.php
|
||||
- src/Modules/Accounting/ReceiptController.php
|
||||
- src/Modules/Automation/AutomationService.php
|
||||
- src/Modules/Cron/CronHandlerFactory.php
|
||||
- routes/web.php
|
||||
|
||||
key-decisions:
|
||||
- "Static resolver classes for stateless utilities (SSL, redirect)"
|
||||
- "Trait for toggle pattern — repos keep thin public wrapper for backward compat"
|
||||
- "ReceiptService as full service class (not static) — needs DI dependencies"
|
||||
|
||||
patterns-established:
|
||||
- "Stateless utility → final class with static methods in Core/Http/"
|
||||
- "Shared repo behavior → trait in Core/Http/ with $this->db/$this->pdo access"
|
||||
|
||||
duration: ~25min
|
||||
started: 2026-04-03T19:10:00Z
|
||||
completed: 2026-04-03T19:35:00Z
|
||||
---
|
||||
|
||||
# Phase 68 Plan 01: Code Deduplication Refactor Summary
|
||||
|
||||
**Extracted 3 shared utilities + 1 service from 17+ duplicated methods across codebase, eliminating ~250 lines of copy-pasted code**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~25min |
|
||||
| Started | 2026-04-03T19:10:00Z |
|
||||
| Completed | 2026-04-03T19:35:00Z |
|
||||
| Tasks | 3 completed (+ 1 pre-plan ReceiptService) |
|
||||
| Files modified | 18 |
|
||||
| Delegation | 3 tasks via Claude Agent (parallel) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: getCaBundlePath eliminated from 7 classes | Pass | 0 occurrences remain, SslCertificateResolver is sole source |
|
||||
| AC-2: toggleStatus consolidated in trait | Pass | 4 repos use ToggleableRepositoryTrait, controllers unchanged |
|
||||
| AC-3: resolveRedirectPath consolidated | Pass | 0 occurrences remain, RedirectPathResolver is sole source |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Extracted `SslCertificateResolver` replacing 7 identical `getCaBundlePath()` copies
|
||||
- Extracted `ToggleableRepositoryTrait` used by 4 repositories (Automation, EmailMailbox, EmailTemplate, ReceiptConfig)
|
||||
- Extracted `RedirectPathResolver` replacing 3 identical `resolveRedirectPath()` copies
|
||||
- Extracted `ReceiptService` consolidating receipt issuance from ReceiptController + AutomationService (pre-plan bugfix that prompted this phase)
|
||||
- Fixed receipt datetime bug: `date('Y-m-d')` → `date('Y-m-d H:i:s')` in automation path
|
||||
- Migration 000077: `sale_date` DATE→DATETIME + backfill old receipts from `created_at`
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Core/Http/SslCertificateResolver.php` | Created | CA bundle path resolution (was in 7 classes) |
|
||||
| `src/Core/Http/ToggleableRepositoryTrait.php` | Created | Shared toggle active/inactive pattern |
|
||||
| `src/Core/Http/RedirectPathResolver.php` | Created | Redirect path validation (was in 3 controllers) |
|
||||
| `src/Modules/Accounting/ReceiptService.php` | Created | Centralized receipt issuance logic |
|
||||
| `src/Modules/Accounting/ReceiptIssueException.php` | Created | Dedicated exception for receipt errors |
|
||||
| `database/migrations/20260403_000077_*` | Created | sale_date DATETIME + backfill |
|
||||
| 7x API/Tracking clients | Modified | getCaBundlePath → SslCertificateResolver |
|
||||
| 4x Repositories | Modified | toggleActive → ToggleableRepositoryTrait |
|
||||
| 3x Integration controllers | Modified | resolveRedirectPath → RedirectPathResolver |
|
||||
| `ReceiptController.php` | Modified | Delegates to ReceiptService |
|
||||
| `AutomationService.php` | Modified | Delegates to ReceiptService, removed 7 private methods |
|
||||
| `CronHandlerFactory.php` | Modified | Wires ReceiptService |
|
||||
| `routes/web.php` | Modified | Wires ReceiptService |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope additions | 1 | ReceiptService extracted pre-plan as bugfix response |
|
||||
| Auto-fixed | 0 | None |
|
||||
| Deferred | 0 | None |
|
||||
|
||||
**Total impact:** ReceiptService was essential — it fixed the datetime bug AND eliminated the largest duplication case.
|
||||
|
||||
## Re-analysis Results (Post-Refactor)
|
||||
|
||||
Remaining duplications found:
|
||||
|
||||
| Priority | Issue | Copies | Recommendation |
|
||||
|----------|-------|--------|----------------|
|
||||
| HIGH | validateCsrf() | 6 | CsrfValidator service |
|
||||
| MEDIUM | isActive filter closure | 3 | Utility function |
|
||||
| LOW | array_values(array_filter()) | 12+ | ArrayHelper |
|
||||
| LOW | date instantiation pattern | 8+ | Monitor only |
|
||||
| LOW | array_map('intval') | 4 | Monitor only |
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Core/Http/ established as location for shared utilities
|
||||
- Pattern proven: static resolvers + traits work cleanly with existing architecture
|
||||
- Re-analysis baseline documented for plan 68-02
|
||||
|
||||
**Concerns:**
|
||||
- validateCsrf duplication is security-sensitive — should be next priority
|
||||
- ToggleableRepositoryTrait uses `$this->pdo` — verify consistency across repos
|
||||
|
||||
**Blockers:** None
|
||||
|
||||
---
|
||||
*Phase: 68-code-deduplication-refactor, Plan: 01*
|
||||
*Completed: 2026-04-03*
|
||||
Reference in New Issue
Block a user