
# PHP API

Semua yang ada di Queuety dapat diakses melalui kelas statis `Queuety\Queuety`. Kelas ini adalah satu-satunya titik masuk untuk mendistribusikan job, membangun alur kerja, menjalankan mesin status, mengelola antrean, dan mengakses subsistem internal.

```php
use Queuety\Queuety;
```

## Akses inti mandiri

Runtime berbasis DB milik Queuety dapat diinisialisasi di luar WordPress.

Jalur mandiri ini ditujukan untuk inti orkestrasi:

- job dan worker (pool worker juga memerlukan konstanta `DB_HOST`, `DB_NAME`, `DB_USER`, dan `DB_PASSWORD`, lihat di bawah)
- alur kerja dan sinyal
- mesin status dan peristiwa
- artefak, log, metrik, dan jadwal

Ini bukan pengganti adaptor WordPress. `Queuety::on_action()`,
`replace_wp_cron()`, bootstrap plugin, dan jalur pendaftaran `wp queuety ...` yang sebenarnya
masih memerlukan WordPress.

### `Queuety::init( Connection $conn )`

Inisialisasi Queuety dengan koneksi langsung berbasis DB.

```php
use Queuety\Connection;
use Queuety\Queuety;

$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
);

Queuety::init( $conn );
```

Argumen `table_prefix` opsional mengubah bagian Queuety pada nama tabel runtime per koneksi:

```php
$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
    table_prefix: 'themequeue_',
);
```

Dengan demikian, tabel seperti `queuety_jobs` dipetakan ke `wp_themequeue_jobs`.

### `Queuety::ensure_schema()`

Pastikan tabel runtime Queuety tersedia untuk koneksi aktif.

```php
Queuety::init( $conn );
Queuety::ensure_schema();
```

Untuk model lengkap dan batas WordPress, lihat [Penggunaan Mandiri](https://queuety.inline0.dev/id/docs/features/standalone-use).

## Job

### `Queuety::dispatch( $handler, $payload )`

Distribusikan sebuah job. Menerima string nama handler atau instans `Contracts\Job`. Mengembalikan builder `PendingJob` untuk merangkai opsi.

```php
// Handler name + payload (original API)
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );

// Contracts\Job instance (v0.6.0+)
Queuety::dispatch( new SendEmailJob( to: 'user@example.com' ) );

// With options
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High )
    ->delay( 300 )
    ->max_attempts( 5 )
    ->rate_limit( 10, 60 )
    ->unique()
    ->after( $other_job_id );

// Get the job ID
$job_id = Queuety::dispatch( 'send_email', $payload )->id();
```

Jika diberikan instans `Contracts\Job`, serializer mengekstrak FQCN sebagai nama handler dan properti publik sebagai payload. Parameter `$payload` diabaikan saat `$handler` berupa instans `Job`.

### Metode `PendingJob`

| Metode | Deskripsi |
|---|---|
| `on_queue( string $queue )` | Menetapkan antrean tujuan |
| `with_priority( Priority $priority )` | Menetapkan tingkat prioritas |
| `delay( int $seconds )` | Jeda sebelum job tersedia |
| `max_attempts( int $max )` | Jumlah percobaan ulang maksimum |
| `rate_limit( int $max, int $window )` | Batas laju: eksekusi maksimum per jendela detik |
| `unique()` | Mencegah distribusi ganda |
| `after( int $job_id )` | Menunggu job lain selesai terlebih dahulu |
| `concurrency_group( string $group, int $limit )` | Memasukkan job ke grup penerimaan worker bersama |
| `cost_units( int $units )` | Menyertakan metadata biaya eksekusi relatif pada distribusi |
| `id()` | Memaksa distribusi dan mengembalikan ID job |

`concurrency_group()` dan `cost_units()` menjadi bagian dari model [Manajemen Sumber Daya](https://queuety.inline0.dev/id/docs/features/resource-management) yang lebih luas, sehingga distribusi yang sama dapat mengikuti batas konkurensi berbasis jumlah, anggaran antrean atau penyedia berbobot, serta heuristik penerimaan worker.

### `Queuety::dispatch_sync( $handler, $payload )`

Jalankan job secara sinkron dalam proses saat ini tanpa memasukkannya ke antrean.

```php
// With a Job instance
Queuety::dispatch_sync( new SendEmailJob( to: 'user@example.com', subject: 'Hi', body: 'Hello' ) );

// With a handler name and payload
Queuety::dispatch_sync( 'send_email', [ 'to' => 'user@example.com' ] );
```

Metode `handle()` milik job dipanggil secara langsung. Tidak ada baris database yang dibuat, worker tidak dilibatkan, dan middleware tidak diterapkan.

### `Queuety::batch( $jobs )`

Distribusikan beberapa job dalam satu INSERT multi-baris.

```php
$job_ids = Queuety::batch( [
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'a@example.com' ], 'queue' => 'emails' ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'b@example.com' ], 'priority' => 2 ],
] );
```

### `Queuety::create_batch( $jobs )`

Buat `BatchBuilder` untuk mendistribusikan sekelompok job dengan callback dan pelacakan progres. Lihat [Pengelompokan](https://queuety.inline0.dev/id/docs/features/batching) untuk detail lengkap.

```php
$batch = Queuety::create_batch( [
    new ImportUsersJob( $chunk_1 ),
    new ImportUsersJob( $chunk_2 ),
] )
    ->name( 'Import users' )
    ->then( ImportCompleteHandler::class )
    ->catch( ImportFailedHandler::class )
    ->finally( ImportCleanupHandler::class )
    ->allow_failures()
    ->on_queue( 'imports' )
    ->dispatch();
```

### Metode `BatchBuilder`

| Metode | Mengembalikan | Deskripsi |
|---|---|---|
| `name( string $name )` | `self` | Menetapkan nama untuk batch |
| `on_queue( string $queue )` | `self` | Menetapkan antrean untuk semua job dalam batch |
| `then( string $handler_class )` | `self` | Handler yang dipanggil saat batch berhasil diselesaikan |
| `catch( string $handler_class )` | `self` | Handler yang dipanggil saat job pertama dalam batch gagal |
| `finally( string $handler_class )` | `self` | Handler yang dipanggil saat batch selesai (berhasil atau gagal) |
| `allow_failures()` | `self` | Memungkinkan callback `then` dipanggil meski ada kegagalan |
| `dispatch()` | `Batch` | Membuat baris batch dan semua job, lalu mengembalikan objek nilai `Batch` |

### `Queuety::find_batch( $id )`

Cari batch berdasarkan ID. Mengembalikan objek nilai `Batch` atau `null`.

```php
$batch = Queuety::find_batch( $batch_id );

if ( null !== $batch ) {
    echo $batch->progress() . '% complete';
}
```

## Kontrol worker

### `Queuety::run_worker( $queue = 'default', $once = false )`

Jalankan satu loop worker secara langsung.

```php
Queuety::run_worker( 'default' );
Queuety::run_worker( 'critical,default', true );
```

### `Queuety::run_worker_pool( $workers, $queue = 'default' )`

Jalankan pool worker berukuran tetap secara langsung. Memerlukan ekstensi `pcntl` dan `posix`. Pool membaca konstanta WordPress `DB_HOST`, `DB_NAME`, `DB_USER`, dan `DB_PASSWORD` untuk koneksi anaknya (hanya awalan tabel yang berasal dari koneksi yang diinisialisasi), jadi definisikan konstanta tersebut saat menjalankan pool di luar WordPress ([#5](https://github.com/inline0/queuety/issues/5)).

```php
Queuety::run_worker_pool( 4, 'providers' );
```

### `Queuety::run_auto_scaling_worker_pool( $min_workers, $max_workers, $queue = 'default' )`

Jalankan pool worker adaptif secara langsung. Persyaratannya sama seperti `run_worker_pool()`.

```php
Queuety::run_auto_scaling_worker_pool( 2, 6, 'providers' );
```

### Objek nilai `Batch`

| Metode/Properti | Tipe | Deskripsi |
|---|---|---|
| `$id` | `int` | ID batch |
| `$name` | `string\|null` | Nama batch opsional |
| `$total_jobs` | `int` | Jumlah total job dalam batch |
| `$pending_jobs` | `int` | Job yang masih tertunda |
| `$failed_jobs` | `int` | Jumlah job yang gagal |
| `$failed_job_ids` | `array` | ID job yang gagal |
| `$created_at` | `DateTimeImmutable` | Waktu batch dibuat |
| `$finished_at` | `DateTimeImmutable\|null` | Waktu batch selesai |
| `$cancelled_at` | `DateTimeImmutable\|null` | Waktu batch dibatalkan |
| `progress()` | `int` | Persentase penyelesaian (0-100) |
| `finished()` | `bool` | Apakah batch sudah selesai |
| `has_failures()` | `bool` | Apakah ada job yang gagal |
| `cancelled()` | `bool` | Apakah batch dibatalkan |

### `Queuety::chain( $jobs )`

Buat `ChainBuilder` untuk eksekusi job berurutan. Setiap job bergantung pada keberhasilan job sebelumnya.

```php
$first_job_id = Queuety::chain( [
    new FetchDataJob( $url ),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->catch( ChainFailedHandler::class )
    ->dispatch();
```

### Metode `ChainBuilder`

| Metode | Mengembalikan | Deskripsi |
|---|---|---|
| `on_queue( string $queue )` | `self` | Menetapkan antrean untuk semua job dalam rangkaian |
| `catch( string $handler_class )` | `self` | Handler yang dipanggil saat job mana pun dalam rangkaian dikubur secara permanen |
| `dispatch()` | `int` | Membuat semua job dengan dependensi berurutan, lalu mengembalikan ID job pertama |

### `Queuety::retry( $job_id )`

Coba lagi job tertentu. Mengatur ulang statusnya menjadi `pending`.

```php
Queuety::retry( 42 );
```

### `Queuety::retry_buried()`

Coba lagi semua job yang dikubur. Mengembalikan jumlah job yang dicoba ulang.

```php
$count = Queuety::retry_buried();
```

### `Queuety::purge( $older_than_days )`

Hapus job yang sudah selesai. Mengembalikan jumlah job yang dihapus.

```php
$count = Queuety::purge();     // uses QUEUETY_RETENTION_DAYS
$count = Queuety::purge( 30 ); // purge jobs older than 30 days
```

## Antrean

### `Queuety::stats( $queue )`

Dapatkan jumlah job yang dikelompokkan berdasarkan status.

```php
$stats = Queuety::stats();          // all queues
$stats = Queuety::stats( 'emails' ); // specific queue
// ['pending' => 12, 'processing' => 3, 'completed' => 150, 'failed' => 2, 'buried' => 1]
```

### `Queuety::buried( $queue )`

Dapatkan semua job yang dikubur.

```php
$jobs = Queuety::buried();
$jobs = Queuety::buried( 'emails' );
```

### `Queuety::pause( $queue )`

Jeda antrean agar worker melewatinya.

```php
Queuety::pause( 'emails' );
```

### `Queuety::resume( $queue )`

Lanjutkan antrean yang dijeda.

```php
Queuety::resume( 'emails' );
```

### `Queuety::is_paused( $queue )`

Periksa apakah antrean dijeda.

```php
if ( Queuety::is_paused( 'emails' ) ) {
    // Queue is paused
}
```

## Alur kerja

### `Queuety::workflow( $name )`

Mulai membuat alur kerja. Mengembalikan `WorkflowBuilder`.

```php
$workflow_id = Queuety::workflow( 'generate_report' )
    ->then( FetchDataHandler::class )
    ->then( CallLLMHandler::class )
    ->then( FormatOutputHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

### metode `WorkflowBuilder`

| Metode | Deskripsi |
|---|---|
| `then( string $handler_class, ?string $name = null )` | Tambahkan langkah berurutan. Nama bersifat opsional untuk percabangan kondisional. |
| `parallel( array $handler_classes )` | Tambahkan grup langkah paralel. |
| `for_each( string $items_key, string $handler_class, ?string $result_key = null, ForEachMode $mode = ForEachMode::All, ?int $quorum = null, ?string $reducer_class = null, ?string $name = null )` | Tambahkan langkah for-each dinamis yang memperluas satu tugas cabang untuk setiap item dalam status alur kerja. |
| `repeat_while( string $target_step, ?string $state_key = null, mixed $expected = true, ?string $name = null, ?string $condition_class = null, ?int $max_iterations = null )` | Kembali ke langkah bernama sebelumnya selama kunci status publik atau kelas `RepeatCondition` cocok. |
| `repeat_until( string $target_step, ?string $state_key = null, mixed $expected = true, ?string $name = null, ?string $condition_class = null, ?int $max_iterations = null )` | Kembali ke langkah bernama sebelumnya sampai kunci status publik atau kelas `RepeatCondition` cocok. |
| `run_workflow( string $workflow_name, WorkflowBuilder $sub_builder )` | Tambahkan langkah untuk menjalankan alur kerja. |
| `delay( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | Tambahkan langkah jeda yang persisten. Semua durasi dijumlahkan. |
| `wait_for_signal( string $name, ?string $result_key = null, ?string $step_name = null, array $match_payload = [], ?string $correlation_key = null )` | Tambahkan langkah menunggu sinyal. `match_payload` dan `correlation_key` bersifat opsional dan memungkinkan Anda mengabaikan peristiwa yang tidak terkait. |
| `wait_for_signals( array $signal_names, WaitMode $mode = WaitMode::All, ?string $result_key = null, ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | Tunggu beberapa sinyal hingga semuanya atau salah satunya diterima, dengan pencocokan payload dan korelasi opsional. |
| `wait_for_approval( string $signal_name = 'approval', ?string $result_key = 'approval', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | Tunggu sinyal persetujuan manusia. |
| `wait_for_decision( string $approve_signal = 'approved', string $reject_signal = 'rejected', ?string $result_key = 'decision', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | Tunggu persetujuan atau penolakan, lalu simpan payload keputusan terstruktur. |
| `wait_for_input( string $signal_name = 'input', ?string $result_key = 'input', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | Tunggu sinyal masukan manusia. |
| `wait_for_workflow( int\|string $workflow, ?string $result_key = null, ?string $name = null )` | Tunggu hingga satu ID alur kerja, atau ID alur kerja yang tersimpan dalam status publik, selesai. |
| `wait_for_workflows( array\|string $workflows, WaitMode $mode = WaitMode::All, ?string $result_key = null, ?string $name = null, ?int $quorum = null )` | Tunggu hingga semua, salah satu, atau kuorum dari beberapa alur kerja selesai. |
| `wait_for_workflow_group( string $group_key, WaitMode $mode = WaitMode::All, ?int $quorum = null, ?string $result_key = null, ?string $name = null )` | Tunggu grup alur kerja yang telah dimulai sebelumnya berdasarkan kunci grup internal. |
| `start_workflows( string $items_key, WorkflowBuilder $workflow_builder, string $result_key = 'started_workflow_ids', string $payload_key = 'item', bool $inherit_state = true, ?string $name = null, ?string $group_key = null )` | Jalankan satu alur kerja tingkat atas untuk setiap item runtime, dengan opsi memberi tag kunci grup persisten pada kumpulan yang dimulai. |
| `start_agents( string $items_key, WorkflowBuilder $workflow_builder, string $result_key = 'agent_workflow_ids', string $payload_key = 'agent_task', bool $inherit_state = true, ?string $name = null, ?string $group_key = null )` | Alias berorientasi agen untuk `start_workflows()`. |
| `wait_for_agents( array\|string $workflows = 'agent_workflow_ids', WaitMode $mode = WaitMode::All, ?string $result_key = 'agent_results', ?string $name = null, ?int $quorum = null )` | Alias berorientasi agen untuk `wait_for_workflows()`. |
| `wait_for_agent_group( string $group_key = 'agents', WaitMode $mode = WaitMode::All, ?int $quorum = null, ?string $result_key = 'agent_results', ?string $name = null )` | Alias berorientasi agen untuk `wait_for_workflow_group()`. |
| `version( string $version )` | Beri tag versi tingkat aplikasi pada definisi alur kerja untuk pemeriksaan dan ekspor. |
| `idempotency_key( string $key )` | Jadikan pengiriman alur kerja persisten dengan kunci yang diberikan pemanggil. Pengiriman ulang dengan kunci yang sama mengembalikan ID alur kerja awal. |
| `max_transitions( int $max )` | Gagalkan alur kerja jika lebih dari `$max` transisi langkah selesai. |
| `max_for_each_items( int $max )` | Gagalkan alur kerja jika langkah `for_each()` mencoba memperluas lebih dari `$max` item. |
| `max_state_bytes( int $bytes )` | Gagalkan alur kerja jika status publik bertambah melebihi `$bytes`. |
| `max_cost_units( int $max )` | Gagalkan alur kerja jika langkah yang selesai menghabiskan lebih dari `$max` unit biaya. |
| `max_started_workflows( int $max )` | Gagalkan alur kerja sebelum mengirim lebih dari `$max` alur kerja turunan. |
| `compensate_with( string $handler_class )` | Lampirkan penangan kompensasi ke langkah yang terakhir ditambahkan. |
| `compensate_on_failure()` | Jalankan kompensasi langkah yang selesai secara otomatis saat alur kerja gagal. |
| `on_queue( string $queue )` | Tetapkan antrean untuk semua langkah. |
| `with_priority( Priority $priority )` | Tetapkan prioritas untuk semua langkah. |
| `max_attempts( int $max )` | Tetapkan jumlah maksimum percobaan ulang per langkah. |
| `on_cancel( string $handler_class )` | Daftarkan kelas penangan pembersihan untuk pembatalan. Lihat [Pembatalan](https://queuety.inline0.dev/id/docs/workflows/cancellation). |
| `prune_state_after( int $steps = 2 )` | Aktifkan pemangkasan status otomatis. Menghapus keluaran langkah yang lebih lama dari ambang batas. Lihat [Pemangkasan Status](https://queuety.inline0.dev/id/docs/features/state-pruning). |
| `must_complete_within( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | Tetapkan tenggat waktu untuk alur kerja. Semua durasi dijumlahkan. Lihat [Tenggat Waktu](https://queuety.inline0.dev/id/docs/workflows/deadlines). |
| `on_deadline( string $handler_class )` | Daftarkan penangan yang dipanggil saat tenggat waktu alur kerja terlampaui. Lihat [Tenggat Waktu](https://queuety.inline0.dev/id/docs/workflows/deadlines). |
| `dispatch( array $initial_payload = [] )` | Kirim alur kerja. Mengembalikan ID alur kerja. |

### `Queuety::workflow_status( $workflow_id )`

Dapatkan status alur kerja. Mengembalikan objek `WorkflowState` atau `null`.

```php
$state = Queuety::workflow_status( $workflow_id );
echo $state->name;          // 'generate_report'
echo $state->status->value; // 'running'
echo $state->current_step;  // 1
echo $state->total_steps;   // 3
print_r( $state->state );   // accumulated public state array
echo $state->wait_type;     // 'signal' or 'workflow' while blocked
echo $state->wait_mode;     // 'all', 'any', or 'quorum'
echo $state->definition_version; // 'research.v2'
echo $state->definition_hash;    // '4c7a...'
echo $state->idempotency_key;    // 'brief:42:research'
echo $state->artifact_count;     // 2
print_r( $state->budget );       // configured workflow budget + current usage
print_r( $state->wait_details ); // matched/remaining blockers, result key, correlation info
print_r( $state->artifact_keys ); // ['draft', 'citations']
```

`WorkflowState` juga menampilkan:

- `waiting_for`: nama sinyal atau ID alur kerja yang saat ini menghalangi proses
- `current_step_name`: nama yang dikonfigurasi untuk langkah saat ini, jika ada
- `wait_mode`: mode tunggu saat ini (`all`, `any`, atau `quorum`) saat terhalang
- `wait_details`: data pemeriksaan tambahan, seperti penghalang yang cocok dibandingkan dengan yang tersisa
- `definition_version`: nilai yang ditetapkan oleh `version()`, jika ada
- `definition_hash`: hash deterministik dari definisi alur kerja
- `idempotency_key`: kunci pengiriman persisten, jika ada
- `budget`: batas yang dikonfigurasi beserta penggunaan saat ini, jika pagar pengaman diaktifkan
- `artifact_count`: jumlah artefak tersimpan untuk alur kerja
- `artifact_keys`: kunci artefak tersimpan untuk pemeriksaan cepat

### `Queuety::retry_workflow( $workflow_id )`

Coba ulang alur kerja yang gagal dari langkah yang gagal.

```php
Queuety::retry_workflow( $workflow_id );
```

Melempar `RuntimeException` jika alur kerja sudah dikompensasi setelah kegagalan.

### `Queuety::pause_workflow( $workflow_id )`

Jeda alur kerja yang sedang berjalan. Pekerjaan yang sudah diantrekan untuk langkah saat ini mungkin tetap selesai, tetapi langkah berikutnya tidak akan diantrekan sampai Anda melanjutkan alur kerja.

```php
Queuety::pause_workflow( $workflow_id );
```

### `Queuety::resume_workflow( $workflow_id )`

Lanjutkan alur kerja yang dijeda.

```php
Queuety::resume_workflow( $workflow_id );
```

### `Queuety::cancel_workflow( $workflow_id )`

Batalkan alur kerja dan jalankan penangan pembersihan yang terdaftar. Tetapkan status ke `cancelled` dan kubur semua pekerjaan yang tertunda dan sedang diproses untuk alur kerja tersebut.

```php
Queuety::cancel_workflow( $workflow_id );
```

Melempar `RuntimeException` jika alur kerja sudah `completed` atau `cancelled`. Lihat [Pembatalan](https://queuety.inline0.dev/id/docs/workflows/cancellation) untuk detail.

### `Queuety::rewind_workflow( $workflow_id, $to_step )`

Kembalikan alur kerja ke langkah sebelumnya dan jalankan ulang dari sana. Status dipulihkan dari log peristiwa `state_after` untuk langkah yang diberikan, lalu langkah berikutnya diantrekan.

```php
Queuety::rewind_workflow( $workflow_id, to_step: 1 );
```

Melempar `RuntimeException` jika tidak ada status tercatat untuk langkah target. Memerlukan [Log Peristiwa Alur Kerja](https://queuety.inline0.dev/id/docs/workflows/event-log). Lihat [Perjalanan Waktu](https://queuety.inline0.dev/id/docs/workflows/time-travel) untuk detail.

### `Queuety::fork_workflow( $workflow_id )`

Cabangkan alur kerja yang sedang berjalan menjadi salinan independen pada statusnya saat ini. Mengembalikan ID alur kerja baru.

```php
$forked_id = Queuety::fork_workflow( $workflow_id );
```

Alur kerja hasil pencabangan memiliki ID sendiri dan beroperasi secara independen dari alur kerja asli. Lihat [Pencabangan](https://queuety.inline0.dev/id/docs/workflows/forking) untuk detail.

### `Queuety::export_workflow( $workflow_id )`

Ekspor seluruh riwayat eksekusi alur kerja sebagai larik yang dapat diserialisasi dengan JSON. Mencakup baris alur kerja, semua pekerjaan, peristiwa alur kerja, dan entri log.

```php
$data = Queuety::export_workflow( $workflow_id );
```

Lihat [Ekspor dan Pemutaran Ulang](https://queuety.inline0.dev/id/docs/workflows/export-replay) untuk struktur JSON dan contoh penggunaannya.

### `Queuety::replay_workflow( $data )`

Putar ulang alur kerja yang diekspor di lingkungan saat ini. Membuat alur kerja baru dari data ekspor dan mengembalikan ID alur kerja baru.

```php
$new_id = Queuety::replay_workflow( $data );
```

Lihat [Ekspor dan Pemutaran Ulang](https://queuety.inline0.dev/id/docs/workflows/export-replay) untuk detail.

### `Queuety::replace_wp_cron()`

Ganti sistem WordPress cron bawaan dengan penjadwal milik Queuety. Mencegat `wp_schedule_event()`, `wp_unschedule_event()`, dan `wp_get_scheduled_event()`, lalu mengarahkan peristiwa berulang melalui worker Queuety.

```php
Queuety::replace_wp_cron();
```

Hanya berfungsi jika WordPress dimuat. Lihat [WP-Cron Penggantian](https://queuety.inline0.dev/id/docs/features/wp-cron) untuk detail.

### `Queuety::restore_wp_cron()`

Pulihkan perilaku default WordPress cron dengan menghapus hook bridge Queuety cron.

```php
Queuety::restore_wp_cron();
```

Lihat [WP-Cron Penggantian](https://queuety.inline0.dev/id/docs/features/wp-cron) untuk batasan.

### `Queuety::signal( $workflow_id, $name, $data )`

Kirim sinyal ke alur kerja. Jika alur kerja sedang menunggu sinyal ini, alur tersebut langsung dilanjutkan. Jika tidak, sinyal disimpan dan digunakan saat alur kerja mencapai langkah `wait_for_signal` yang sesuai.

```php
Queuety::signal( $workflow_id, 'approved' );

// With data merged into workflow state
Queuety::signal( $workflow_id, 'approved', [
    'approved_by' => 'admin@example.com',
] );
```

Lihat [Sinyal Alur Kerja](https://queuety.inline0.dev/id/docs/workflows/signals) untuk detail.

### `Queuety::approve_workflow( $workflow_id, $data = [], $signal_name = 'approval' )`

Pembungkus semantik `signal()` untuk alur persetujuan.

```php
Queuety::approve_workflow( $workflow_id, [
    'reviewer' => 'editor@example.com',
] );
```

### `Queuety::reject_workflow( $workflow_id, $data = [], $signal_name = 'rejected' )`

Pembungkus semantik `signal()` untuk alur penolakan.

```php
Queuety::reject_workflow( $workflow_id, [
    'reason' => 'needs citations',
] );
```

### `Queuety::submit_workflow_input( $workflow_id, $data = [], $signal_name = 'input' )`

Pembungkus semantik `signal()` untuk masukan manusia yang terstruktur.

```php
Queuety::submit_workflow_input( $workflow_id, [
    'notes' => 'Ship after legal review',
] );
```

### `Queuety::put_artifact( $workflow_id, $artifact_key, $content, $kind = 'json', $step_index = null, $metadata = [] )`

Simpan satu artefak persisten untuk alur kerja.

```php
Queuety::put_artifact(
    $workflow_id,
    'research_brief',
    [ 'summary' => 'done' ],
    'json',
    metadata: [ 'source' => 'agent' ],
);
```

### `Queuety::put_current_artifact( $artifact_key, $content, $kind = 'json', $metadata = [] )`

Simpan satu artefak untuk langkah alur kerja yang sedang dijalankan.

```php
Queuety::put_current_artifact(
    'draft',
    [ 'status' => 'ready' ],
    'json',
);
```

### `Queuety::workflow_artifact( $workflow_id, $artifact_key )`

Ambil satu artefak tersimpan untuk alur kerja.

```php
$artifact = Queuety::workflow_artifact( $workflow_id, 'draft' );
```

### `Queuety::workflow_artifacts( $workflow_id, $include_content = false )`

Daftar artefak untuk alur kerja.

```php
$artifacts = Queuety::workflow_artifacts( $workflow_id );
```

### `Queuety::delete_workflow_artifact( $workflow_id, $artifact_key )`

Hapus satu artefak untuk alur kerja.

```php
Queuety::delete_workflow_artifact( $workflow_id, 'draft' );
```

## Mesin Status

### `Queuety::machine( $name )`

Mulai membuat mesin status. Mengembalikan `StateMachineBuilder`.

```php
use Queuety\Enums\StateMachineStatus;

$machine_id = Queuety::machine( 'agent_session' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( PlanSessionAction::class )
    ->on( 'planned', 'completed' )
    ->state( 'completed', StateMachineStatus::Completed )
    ->dispatch( [ 'thread_id' => 42 ] );
```

### metode `StateMachineBuilder`

| Metode | Deskripsi |
|---|---|
| `initial( string $state_name )` | Tetapkan status awal secara eksplisit. |
| `state( string $state_name, ?StateMachineStatus $terminal_status = null )` | Mulai mengonfigurasi satu status. |
| `action( string\|array $action_class )` | Daftarkan tindakan masuk antrean untuk status saat ini. Teruskan nama kelas, atau definisi terstruktur dengan `class`, `payload`, dan `config`; `payload` diteruskan ke `StateAction::handle()` sebagai `$payload`. |
| `on( string $event, string $target, string\|array\|null $guard_class = null, ?string $name = null )` | Daftarkan satu transisi peristiwa dari status saat ini. Guard menerima bentuk terstruktur yang sama dengan `action()`. |
| `on_queue( string $queue )` | Tetapkan antrean yang digunakan untuk tindakan status yang diantrekan. |
| `with_priority( Priority $priority )` | Tetapkan prioritas untuk tindakan status yang diantrekan. |
| `max_attempts( int $max_attempts )` | Tetapkan batas percobaan ulang untuk tindakan status yang diantrekan. |
| `version( string $version )` | Beri tag versi tingkat aplikasi pada definisi mesin. |
| `idempotency_key( string $key )` | Jadikan pengiriman persisten dengan kunci yang diberikan pemanggil. Pengiriman ulang dengan kunci yang sama mengembalikan ID mesin awal. |
| `dispatch( array $initial_state = [] )` | Kirim mesin dan kembalikan ID mesin. |

Kelas tindakan masuk status juga dapat menyediakan metode `config()` opsional dengan kunci kebijakan sumber daya yang sama seperti yang didukung langkah alur kerja: `concurrency_group`, `concurrency_limit`, dan `cost_units`.

### `Queuety::dispatch_state_machine_definition( $definition, $initial_state = [], $options = [] )`

Kirim bundel definisi mesin status yang diserialisasi. Sebagian besar kode aplikasi sebaiknya menggunakan `machine()->dispatch()`. Metode tingkat rendah ini memungkinkan adapter memulai mesin tanpa bergantung langsung pada builder.

```php
$machine_id = Queuety::dispatch_state_machine_definition(
    $definition,
    [ 'thread_id' => 42 ],
    [ 'idempotency_key' => 'thread:42' ],
);
```

### `Queuety::machine_status( $machine_id )`

Dapatkan status mesin saat ini. Mengembalikan objek `StateMachineState` atau `null`.

```php
$status = Queuety::machine_status( $machine_id );

echo $status->name;               // 'agent_session'
echo $status->status->value;      // 'waiting_event'
echo $status->current_state;      // 'awaiting_review'
print_r( $status->state );        // public machine state
print_r( $status->available_events ); // valid incoming events in the current state
```

`StateMachineState` juga menampilkan:

- `definition_version`
- `definition_hash`
- `idempotency_key`
- `error_message`
- `current_action`
- `terminal_status`

### `Queuety::machine_event( $machine_id, $event_name, $payload = [] )`

Kirim satu peristiwa eksternal ke mesin yang sedang menunggu.

```php
Queuety::machine_event(
    $machine_id,
    'approve',
    [
        'approved' => true,
        'reviewer' => 'editor@example.com',
    ]
);
```

Payload tersedia bagi guard transisi dan digabungkan ke status mesin publik saat peristiwa diterima.

### `Queuety::list_machines( $limit = 50, $status = null )`

Daftar baris mesin yang tersimpan untuk diperiksa.

```php
$machines = Queuety::list_machines();
$waiting  = Queuety::list_machines( status: 'waiting_event' );
```

Setiap baris mencakup ID mesin, nama, status, status saat ini, versi definisi, hash definisi, kunci idempotensi, dan stempel waktu siklus hidup.

### `Queuety::machine_timeline( $machine_id, $limit = 100, $offset = 0 )`

Dapatkan linimasa peristiwa persisten untuk satu mesin.

```php
$timeline = Queuety::machine_timeline( $machine_id );
$page_two = Queuety::machine_timeline( $machine_id, 100, 100 );
```

Setiap baris peristiwa berisi: `id`, `machine_id`, `job_id`, `state_name`, `handler`, `event`, `event_name`, `transition_name`, `queue`, `attempt`, `input`, `output`, `state_before`, `state_after`, `context`, `artifacts`, `chunks`, `error`, `duration_ms`, dan `created_at`.

### `Queuety::machine_trace( $machine_id )`

Dapatkan bundel pelacakan yang dinormalisasi untuk UI debugger mesin status.

```php
$trace = Queuety::machine_trace( $machine_id );
```

Pelacakan berisi `machine`, `states` yang dikelompokkan, `events` mentah, `jobs` tindakan berkorelasi, `logs`, `artifacts`, dan `chunks`.

### `Queuety::machine_state_at( $machine_id, $event_id )`

Dapatkan status publik setelah peristiwa pelacakan mesin tertentu, atau `null` jika tidak ditemukan.

```php
$state = Queuety::machine_state_at( $machine_id, $event_id );
```

Lihat [Mesin Status](https://queuety.inline0.dev/id/docs/state-machines) untuk model siklus hidup dan pola penggunaan.

## Templat alur kerja

### `Queuety::define_workflow( $name )`

Buat builder alur kerja untuk digunakan sebagai templat.

```php
$builder = Queuety::define_workflow( 'onboarding' )
    ->then( CreateAccountHandler::class )
    ->then( SendWelcomeEmailHandler::class );
```

### `Queuety::register_workflow_template( $builder )`

Daftarkan templat alur kerja.

```php
Queuety::register_workflow_template( $builder );
```

### `Queuety::run_workflow( $name, $payload, $options = [] )`

Kirim templat alur kerja terdaftar berdasarkan nama. Mengembalikan ID alur kerja.

```php
$workflow_id = Queuety::run_workflow( 'onboarding', [ 'email' => 'user@example.com' ] );

$workflow_id = Queuety::run_workflow(
    'onboarding',
    [ 'email' => 'user@example.com' ],
    [ 'idempotency_key' => 'onboarding:user@example.com' ],
);
```

Melempar `RuntimeException` jika templat tidak terdaftar.

### `Queuety::dispatch_workflow_definition( $definition, $payload, $options = [] )`

Kirim alur kerja dari bundel definisi runtime.

Sebagian besar kode aplikasi sebaiknya menggunakan `run_workflow()` atau `workflow()->dispatch()`. Metode tingkat rendah ini memungkinkan adapter yang dapat digunakan ulang mengirim definisi alur kerja terserialisasi tanpa bergantung pada metode internal pengelola alur kerja.

```php
$workflow_id = Queuety::dispatch_workflow_definition(
    $definition,
    [ 'email' => 'user@example.com' ],
    [ 'idempotency_key' => 'onboarding:user@example.com' ],
);
```

### `Queuety::on_action( $hook, $workflow, $map = null, $when = null, $idempotency_key = null, $priority = 10, $accepted_args = null )`

Daftarkan tindakan WordPress yang mengirim alur kerja.

```php
Queuety::on_action(
    'save_post',
    workflow: 'content_review',
    map: static fn ( int $post_id, object $post, bool $update ): array => [
        'post_id' => $post_id,
        'post_type' => $post->post_type,
        'update' => $update,
    ],
    when: static fn ( int $post_id, object $post ): bool => 'post' === $post->post_type,
    idempotency_key: static fn ( int $post_id ): string => "save_post:{$post_id}",
);
```

`$workflow` dapat berupa:

- nama templat alur kerja yang terdaftar
- `WorkflowBuilder` sebaris

`map`, `when`, dan `idempotency_key` dijalankan secara sinkron di dalam permintaan WordPress saat ini. Hanya langkah alur kerja yang berjalan secara asinkron setelah pengiriman.

Lihat [Pemicu Tindakan](https://queuety.inline0.dev/id/docs/features/action-triggers) untuk panduan lengkap.

### `Queuety::workflow_templates()`

Dapatkan registry templat alur kerja.

```php
$registry = Queuety::workflow_templates();
$template = $registry->get( 'onboarding' ); // WorkflowTemplate or null
```

### `Queuety::load_workflows( $directory, $recursive )`

Muat dan daftarkan semua file alur kerja dari direktori. Setiap file `.php` harus mengembalikan `WorkflowBuilder`. Mengembalikan jumlah alur kerja yang terdaftar.

```php
$count = Queuety::load_workflows( __DIR__ . '/workflows/' );
$count = Queuety::load_workflows( __DIR__ . '/workflows/', recursive: true );
```

### `Queuety::load_workflow_file( $file_path )`

Muat dan daftarkan satu file alur kerja. Mengembalikan `WorkflowTemplate` atau `null` jika file tidak mengembalikan builder.

```php
$template = Queuety::load_workflow_file( __DIR__ . '/workflows/onboard-user.php' );
```

## Penjadwalan

### `Queuety::schedule( $handler, $payload )`

Buat jadwal berulang. Mengembalikan builder `PendingSchedule`.

```php
Queuety::schedule( 'cleanup_handler' )
    ->every( '1 hour' )
    ->on_queue( 'maintenance' );

Queuety::schedule( 'nightly_report' )
    ->cron( '0 3 * * *' )
    ->on_queue( 'reports' );
```

### metode `PendingSchedule`

| Metode | Deskripsi |
|---|---|
| `every( string $interval )` | Tetapkan ekspresi interval (e.g. `1 hour`, `30 minutes`) |
| `cron( string $expression )` | Tetapkan ekspresi cron dengan 5 bidang |
| `overlap( OverlapPolicy $policy )` | Tetapkan kebijakan tumpang tindih (default: `Allow`). Lihat [Penjadwalan](https://queuety.inline0.dev/id/docs/features/scheduling). |
| `on_queue( string $queue )` | Tetapkan antrean target (default: `default`) |
| `id()` | Paksa pengiriman dan kembalikan ID jadwal |

### `Queuety::scheduler()`

Dapatkan instance `Scheduler` internal.

```php
$scheduler = Queuety::scheduler();
$schedules = $scheduler->list();
$scheduler->remove( 'handler_name' );
$count = $scheduler->tick(); // manually trigger
```

## Pendaftaran penangan

### `Queuety::register( $name, $class )`

Daftarkan kelas penangan dengan sebuah nama.

```php
Queuety::register( 'send_email', SendEmailHandler::class );
```

### `Queuety::discover_handlers( $directory, $namespace )`

Temukan dan daftarkan kelas penangan secara otomatis dari direktori. Mengembalikan jumlah penangan yang terdaftar.

```php
$count = Queuety::discover_handlers( __DIR__ . '/handlers', 'MyPlugin\\Handlers' );
```

### `Queuety::registry()`

Dapatkan registry penangan.

```php
$registry = Queuety::registry();
```

## Observabilitas

### `Queuety::logger()`

Dapatkan instance `Logger` untuk membuat kueri entri log.

```php
$logger = Queuety::logger();
$logs   = $logger->for_job( 42 );
$logs   = $logger->for_workflow( 7 );
$logs   = $logger->for_handler( 'send_email', 50 );
$logs   = $logger->for_event( LogEvent::Failed, 50 );
$logs   = $logger->since( new \DateTimeImmutable( '-1 hour' ), 100 );
$count  = $logger->purge( 30 );
```

### `Queuety::metrics()`

Dapatkan instance `Metrics`.

```php
$stats = Queuety::metrics()->handler_stats( 60 ); // last 60 minutes
```

### `Queuety::webhook_notifier()`

Dapatkan instance `WebhookNotifier`.

```php
$notifier = Queuety::webhook_notifier();
$id       = $notifier->register( 'job.buried', 'https://hooks.slack.com/...' );
$webhooks = $notifier->list();
$notifier->remove( $id );
```

### `Queuety::rate_limiter()`

Dapatkan instance `RateLimiter`.

```php
$limiter = Queuety::rate_limiter();
$limiter->register( 'call_openai', 60, 60 );
```

## Cache

### `Queuety::cache()`

Dapatkan instance backend cache yang aktif.

```php
$cache = Queuety::cache();
$cache->set( 'my_key', 'my_value', 60 );
$value = $cache->get( 'my_key' );
```

### `Queuety::set_cache( $cache )`

Ganti backend cache. Panggil ini sebelum `Queuety::init()` untuk menggunakan implementasi khusus.

```php
Queuety::set_cache( new RedisCache() );
Queuety::init( $connection );
```

### `QUEUETY_CACHE_TTL`

TTL cache default dalam detik (default: `5`). Tetapkan konstanta ini di `wp-config.php` untuk mengubah TTL default yang digunakan oleh operasi cache internal.

```php
define( 'QUEUETY_CACHE_TTL', 10 );
```

Lihat [Caching](https://queuety.inline0.dev/id/docs/features/caching) untuk detail tentang backend dan deteksi otomatis.

## Log peristiwa mesin status

### `Queuety::machine_events()`

Dapatkan instance `StateMachineEventLog`.

```php
$log = Queuety::machine_events();
```

## Log peristiwa alur kerja

### `Queuety::workflow_events()`

Dapatkan instance `WorkflowEventLog`.

```php
$log = Queuety::workflow_events();
```

### `Queuety::workflow_timeline( $workflow_id, $limit = 100, $offset = 0 )`

Dapatkan linimasa lengkap peristiwa untuk alur kerja. Mengembalikan larik baris peristiwa yang diurutkan berdasarkan ID.

```php
$events = Queuety::workflow_timeline( $workflow_id );
$next   = Queuety::workflow_timeline( $workflow_id, 100, 100 );
```

Setiap baris peristiwa berisi: `id`, `workflow_id`, `job_id`, `parent_event_id`, `step_index`, `step_name`, `step_type`, `handler`, `event`, `queue`, `attempt`, `input`, `output`, `state_before`, `state_after`, `context`, `artifacts`, `chunks`, `error`, `error_message` (disalin dari `error.message`), `duration_ms`, dan `created_at`.

### `Queuety::workflow_trace( $workflow_id )`

Dapatkan bundel jejak yang dinormalisasi untuk debugger alur kerja UI.

```php
$trace = Queuety::workflow_trace( $workflow_id );
```

Jejak berisi `workflow`, dikelompokkan `steps`, mentah `events`, `jobs`, `logs`, `artifacts`, `chunks`, `signals`, dan `wait_dependencies`.

### `Queuety::trace_input( $input )`

Tetapkan masukan tingkat aplikasi untuk langkah alur kerja yang sedang dijalankan.

### `Queuety::trace_output( $output )`

Tetapkan keluaran tingkat aplikasi untuk langkah alur kerja yang sedang dijalankan.

### `Queuety::trace_context( array $context )`

Gabungkan konteks tingkat aplikasi ke dalam langkah alur kerja yang sedang dijalankan.

### `Queuety::workflow_state_at( $workflow_id, $step_index )`

Dapatkan status publik setelah langkah alur kerja tertentu selesai atau dilanjutkan, atau `null` jika tidak ditemukan.

```php
$state = Queuety::workflow_state_at( $workflow_id, 2 );
```

Lihat [Log Peristiwa Alur Kerja](https://queuety.inline0.dev/id/docs/workflows/event-log) untuk kasus penggunaan dan perintah CLI.

## Instans internal

Metode ini mengembalikan instans internal untuk kasus penggunaan lanjutan.

| Metode | Mengembalikan | Deskripsi |
|---|---|---|
| `Queuety::queue()` | `Queue` | Operasi antrean (klaim, lepas, kubur) |
| `Queuety::worker()` | `Worker` | Kontrol proses pekerja |
| `Queuety::workflow_manager()` | `Workflow` | Orkestrasi alur kerja |
| `Queuety::machines()` | `StateMachine` | Pengelolaan siklus hidup mesin status |
| `Queuety::connection()` | `Connection` | Koneksi basis data |
| `Queuety::batch_manager()` | `BatchManager` | Pengelolaan siklus hidup batch (batalkan, pangkas) |
| `Queuety::chunk_store()` | `ChunkStore` | Persistensi potongan untuk langkah streaming |
| `Queuety::machine_events()` | `StateMachineEventLog` | Peristiwa jejak mesin status dan bundel jejak yang dinormalisasi |
| `Queuety::workflow_events()` | `WorkflowEventLog` | Peristiwa jejak alur kerja dan bundel jejak yang dinormalisasi |
| `Queuety::resource_manager()` | `ResourceManager` | Profil penerimaan pekerja, anggaran berbobot, dan pemeriksaan memori sistem |
| `Queuety::cache()` | `Cache` | Instans backend cache |

## Pengujian

### `Queuety::fake()`

Ganti antrean dengan objek tiruan dalam memori untuk pengujian. Mengembalikan instans `QueueFake` yang mencatat pekerjaan yang dikirim tanpa menyentuh basis data. `create_batch()` juga mencatat metadata batch tiruan; rantai diperiksa melalui pekerjaan individual yang dikirimnya.

```php
$fake = Queuety::fake();
SendEmailJob::dispatch( 'user@example.com', 'Hello', 'Hi!' );
$fake->assert_pushed( SendEmailJob::class );
```

### `Queuety::reset()`

Atur ulang semua status internal, termasuk tiruan antrean. Panggil di `tearDown()`.

```php
Queuety::reset();
```

### Metode `QueueFake`

| Metode | Deskripsi |
|---|---|
| `assert_pushed( string $class, ?Closure $callback )` | Pastikan kelas pekerjaan telah dimasukkan ke antrean. Callback opsional memfilter berdasarkan data pekerjaan. |
| `assert_pushed_times( string $class, int $count )` | Pastikan pekerjaan dimasukkan ke antrean tepat N kali. |
| `assert_not_pushed( string $class )` | Pastikan pekerjaan tidak pernah dimasukkan ke antrean. |
| `assert_nothing_pushed()` | Pastikan tidak ada pekerjaan yang dimasukkan ke antrean. |
| `assert_batched( ?Closure $callback )` | Pastikan batch telah dikirim. Callback opsional memfilter berdasarkan data batch. |
| `pushed( string $class )` | Dapatkan semua pengiriman tercatat untuk suatu kelas. Mengembalikan array. |
| `batches()` | Dapatkan semua batch yang tercatat. Mengembalikan array. |
| `reset()` | Hapus semua pekerjaan dan batch yang tercatat. |

Lihat [Pengujian](https://queuety.inline0.dev/id/docs/features/testing) untuk contoh lengkap.

## Antarmuka

### `Handler`

Antarmuka untuk handler pekerjaan sederhana.

```php
interface Handler {
    public function handle( array $payload ): void;
    public function config(): array;
}
```

`config()` dapat mengembalikan `queue`, `max_attempts`, `backoff`, `rate_limit`, `concurrency_group`, `concurrency_limit`, dan `cost_units`.

### `Step`

Antarmuka untuk handler langkah alur kerja.

```php
interface Step {
    public function handle( array $state ): array;
    public function config(): array;
}
```

`config()` dapat mengembalikan `max_attempts`, `backoff`, `concurrency_group`, `concurrency_limit`, dan `cost_units`.

### `Contracts\ForEachHandler`

Antarmuka untuk handler cabang for-each dinamis.

```php
namespace Queuety\Contracts;

interface ForEachHandler {
    public function handle_item( array $state, mixed $item, int $index ): array;
    public function config(): array;
}
```

`config()` dapat mengembalikan `max_attempts`, `backoff`, `rate_limit`, `concurrency_group`, `concurrency_limit`, dan `cost_units`.

### `Contracts\ForEachReducer`

Reducer opsional untuk hasil for-each.

```php
namespace Queuety\Contracts;

interface ForEachReducer {
    public function reduce( array $state, array $for_each ): array;
}
```

### `Contracts\Compensation`

Antarmuka untuk handler kompensasi langkah.

```php
namespace Queuety\Contracts;

interface Compensation {
    public function handle( array $state ): void;
}
```

### `Contracts\RepeatCondition`

Antarmuka untuk predikat loop yang digunakan oleh `repeat_until()` dan `repeat_while()` saat perbandingan kunci status terlalu terbatas.

```php
namespace Queuety\Contracts;

interface RepeatCondition {
    public function matches( array $state ): bool;
}
```

### `Contracts\StateAction`

Antarmuka untuk tindakan masuk status yang diantrekan.

```php
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}
```

`$payload` membawa `payload` terstruktur dari definisi tindakan, jika mesin ditentukan dengan definisi tersebut. Kembalikan pembaruan status publik. Sertakan `_event` dan, secara opsional, `_event_payload` jika tindakan harus segera melakukan transisi.

Tindakan status juga dapat menentukan metode `config()` opsional. Queuety membacanya dari `concurrency_group`, `concurrency_limit`, dan `cost_units` saat tindakan masuk diantrekan.

### `Contracts\StateGuard`

Antarmuka untuk guard transisi mesin status.

```php
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}
```

### `Contracts\Job`

Antarmuka untuk kelas pekerjaan mandiri yang dapat dikirim. Kelas pekerjaan menyimpan payload sebagai properti publik dan logika eksekusi di `handle()`.

```php
namespace Queuety\Contracts;

interface Job {
    public function handle(): void;
}
```

Kelas pekerjaan dapat menentukan metode `middleware()` opsional yang mengembalikan array instans `Contracts\Middleware`.

### `Contracts\Cache`

Antarmuka untuk backend cache. Lihat [Caching](https://queuety.inline0.dev/id/docs/features/caching) untuk implementasi bawaan dan backend khusus.

```php
namespace Queuety\Contracts;

interface Cache {
    public function get( string $key ): mixed;
    public function set( string $key, mixed $value, int $ttl = 0 ): void;
    public function delete( string $key ): void;
    public function has( string $key ): bool;
    public function add( string $key, mixed $value, int $ttl = 0 ): bool;
    public function flush(): void;
}
```

| Metode | Mengembalikan | Deskripsi |
|---|---|---|
| `get( $key )` | `mixed` | Ambil nilai. Mengembalikan `null` jika tidak ditemukan atau kedaluwarsa. |
| `set( $key, $value, $ttl )` | `void` | Simpan nilai. TTL 0 berarti tidak kedaluwarsa. |
| `delete( $key )` | `void` | Hapus nilai. |
| `has( $key )` | `bool` | Periksa apakah kunci tersedia dan belum kedaluwarsa. |
| `add( $key, $value, $ttl )` | `bool` | Atur atomik jika belum ada. Mengembalikan `true` jika nilai berhasil diatur. |
| `flush()` | `void` | Hapus semua item dari cache. |

### `WorkflowEventLog`

Mencatat transisi langkah alur kerja dengan konteks jejak lengkap. Akses melalui `Queuety::workflow_events()`.

| Metode | Mengembalikan | Deskripsi |
|---|---|---|
| `record_event( array $event )` | `void` | Catat peristiwa jejak mentah |
| `record_step_started( ... )` | `void` | Catat peristiwa step_started dengan masukan dan status sebelum eksekusi |
| `record_step_completed( ... )` | `void` | Catat peristiwa step_completed dengan masukan, keluaran, status sebelum, dan status sesudah |
| `record_step_failed( ... )` | `void` | Catat peristiwa step_failed dengan konteks jejak dan detail kesalahan terstruktur |
| `get_timeline( int $workflow_id, ?int $limit = 100, int $offset = 0 )` | `array` | Dapatkan peristiwa jejak alur kerja secara berurutan, dengan paginasi opsional |
| `get_trace( int $workflow_id )` | `array` | Dapatkan bundel jejak yang dinormalisasi |
| `get_state_at_step( int $workflow_id, int $step_index )` | `array\|null` | Dapatkan status publik setelah langkah tertentu selesai atau dilanjutkan |
| `prune( int $older_than_days )` | `int` | Hapus peristiwa yang lebih lama dari jumlah hari yang ditentukan. Mengembalikan jumlah baris yang dihapus. |

### `StateMachineEventLog`

Mencatat peristiwa siklus hidup mesin status, tindakan, transisi, dan guard dengan kolom jejak yang sama seperti peristiwa alur kerja.

| Metode | Mengembalikan | Deskripsi |
|---|---|---|
| `record_event( array $event )` | `void` | Catat peristiwa jejak mesin mentah |
| `get_timeline( int $machine_id, ?int $limit = 100, int $offset = 0 )` | `array` | Dapatkan peristiwa jejak mesin secara berurutan, dengan paginasi opsional |
| `get_trace( int $machine_id )` | `array` | Dapatkan bundel jejak mesin yang dinormalisasi |
| `get_state_at_event( int $machine_id, int $event_id )` | `array\|null` | Dapatkan status publik setelah peristiwa jejak mesin tertentu |
| `prune( int $older_than_days )` | `int` | Hapus peristiwa yang lebih lama dari jumlah hari yang ditentukan. Mengembalikan jumlah baris yang dihapus. |

### `Contracts\StreamingStep`

Antarmuka untuk handler langkah alur kerja yang menghasilkan aliran potongan. Setiap nilai yang dihasilkan langsung disimpan ke basis data. Lihat [Langkah Streaming](https://queuety.inline0.dev/id/docs/workflows/streaming).

```php
namespace Queuety\Contracts;

interface StreamingStep {
    public function stream( array $state, array $existing_chunks = [] ): \Generator;
    public function on_complete( array $chunks, array $state ): array;
    public function config(): array;
}
```

| Metode | Deskripsi |
|---|---|
| `stream( $state, $existing_chunks )` | Generator yang menghasilkan potongan string. Setiap hasil langsung memicu penulisan ke DB. Saat dicoba ulang, `$existing_chunks` berisi potongan yang sebelumnya telah disimpan. |
| `on_complete( $chunks, $state )` | Dipanggil setelah aliran selesai. Mengembalikan data untuk digabungkan ke status alur kerja. |
| `config()` | Konfigurasi opsional. Mendukung `max_attempts`, `backoff`, `concurrency_group`, `concurrency_limit`, dan `cost_units`. |

### `ChunkStore`

Mengelola persistensi potongan untuk langkah streaming. Akses melalui `Queuety::chunk_store()`.

| Metode | Mengembalikan | Deskripsi |
|---|---|---|
| `get_chunks( int $job_id )` | `string[]` | Ambil semua potongan untuk suatu pekerjaan, diurutkan berdasarkan indeks potongan |
| `append_chunk( int $job_id, int $chunk_index, string $content, ?int $workflow_id, ?int $step_index )` | `void` | Tambahkan satu potongan |
| `clear_chunks( int $job_id )` | `void` | Hapus semua potongan untuk suatu pekerjaan |
| `chunk_count( int $job_id )` | `int` | Hitung jumlah potongan untuk suatu pekerjaan |
| `get_accumulated( int $job_id )` | `string` | Gabungkan semua potongan menjadi satu string |

### `Heartbeat`

Helper statis untuk mengirim heartbeat aktivitas dari dalam handler langkah dan langkah streaming. Lihat [Heartbeat](https://queuety.inline0.dev/id/docs/features/heartbeats).

| Metode | Deskripsi |
|---|---|
| `Heartbeat::init( int $job_id, Connection $conn )` | Tetapkan konteks pekerjaan. Dipanggil otomatis oleh worker. |
| `Heartbeat::beat( array $progress = [] )` | Kirim heartbeat. Memperbarui `reserved_at` dan secara opsional menyimpan data progres di `heartbeat_data`. |
| `Heartbeat::clear()` | Hapus konteks pekerjaan. Dipanggil otomatis oleh worker. |
| `Heartbeat::current_job_id()` | Dapatkan ID pekerjaan saat ini (untuk pengujian). |

### `Contracts\Middleware`

Antarmuka untuk middleware pekerjaan. Middleware membungkus eksekusi pekerjaan dalam alur bergaya bawang.

```php
namespace Queuety\Contracts;

interface Middleware {
    public function handle( object $job, \Closure $next ): void;
}
```

## Trait

### `Dispatchable`

Menyediakan metode pengiriman statis pada kelas pekerjaan yang mengimplementasikan `Contracts\Job`. Argumen konstruktor diteruskan untuk membuat instans pekerjaan.

```php
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class SendEmailJob implements Job {
    use Dispatchable;
    // ...
}

// Dispatches a new SendEmailJob instance
SendEmailJob::dispatch( 'user@example.com', 'Subject', 'Body' );
```

| Metode | Mengembalikan | Deskripsi |
|---|---|---|
| `dispatch( ...$args )` | `PendingJob` | Buat instans dan kirimkan |
| `dispatch_if( bool $condition, ...$args )` | `PendingJob\|null` | Kirimkan hanya jika `$condition` bernilai true |
| `dispatch_unless( bool $condition, ...$args )` | `PendingJob\|null` | Kirimkan hanya jika `$condition` bernilai false |
| `with_chain( array $jobs )` | `ChainBuilder` | Mulai rantai dari kelas pekerjaan ini |

```php
// Conditional dispatch
SendEmailJob::dispatch_if( $user->wants_email, $user->email, 'Hello', 'Hi!' );
SendEmailJob::dispatch_unless( $maintenance_mode, $user->email, 'Hello', 'Hi!' );

// Chain from a job class
FetchDataJob::with_chain( [
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )->dispatch();
```

### Properti pekerjaan

Kelas pekerjaan dapat mendeklarasikan properti publik yang dibaca worker melalui refleksi untuk mengganti perilaku percobaan ulang dan batas waktu.

| Properti | Tipe | Deskripsi |
|---|---|---|
| `$tries` | `int` | Jumlah percobaan maksimum sebelum pekerjaan dikubur |
| `$timeout` | `int` | Waktu eksekusi maksimum dalam detik (memerlukan `pcntl`) |
| `$backoff` | `array` | Nilai jeda yang meningkat untuk setiap percobaan ulang |

```php
class ImportProductsJob implements Job {
    use Dispatchable;

    public int $tries = 5;
    public int $timeout = 120;
    public array $backoff = [ 10, 30, 60, 120 ];

    // ...
}
```

### Hook `failed()`

Jika pekerjaan dikubur setelah semua percobaan ulang habis, worker akan memanggil metode `failed()` pada instans pekerjaan jika ditentukan.

```php
public function failed( \Throwable $exception ): void {
    error_log( "Job failed: {$exception->getMessage()}" );
}
```

## Middleware bawaan

Semua kelas middleware mengimplementasikan `Contracts\Middleware` dan berada di namespace `Queuety\Middleware`.

| Kelas | Deskripsi |
|---|---|
| `RateLimited( int $max, int $window )` | Terapkan batas laju sebanyak `$max` eksekusi per `$window` detik |
| `Timeout( int $seconds )` | Hentikan pekerjaan setelah `$seconds` detik (memerlukan `pcntl`) |
| `UniqueJob( ?string $key )` | Cegah eksekusi bersamaan melalui kunci basis data |
| `WithoutOverlapping( string $key, int $release_after )` | Cegah eksekusi yang tumpang tindih dengan kedaluwarsa kunci otomatis |
| `ThrottlesExceptions( int $max_attempts, int $decay_minutes )` | Batasi pengecualian untuk mencegah lonjakan pekerjaan saat layanan eksternal mengalami gangguan |

Lihat [Middleware](https://queuety.inline0.dev/id/docs/features/middleware) untuk detail penggunaan dan contoh.

## Enum

### `Priority`

```php
enum Priority: int {
    case Low    = 0;
    case Normal = 1;
    case High   = 2;
    case Urgent = 3;
}
```

### `JobStatus`

```php
enum JobStatus: string {
    case Pending    = 'pending';
    case Processing = 'processing';
    case Completed  = 'completed';
    case Failed     = 'failed';
    case Buried     = 'buried';
}
```

### `WorkflowStatus`

```php
enum WorkflowStatus: string {
    case Running        = 'running';
    case Completed      = 'completed';
    case Failed         = 'failed';
    case Paused         = 'paused';
    case WaitingForSignal  = 'waiting_for_signal';
    case WaitingForWorkflows = 'waiting_for_workflows';
    case Cancelled      = 'cancelled';
}
```

### `StateMachineStatus`

```php
enum StateMachineStatus: string {
    case Running = 'running';
    case WaitingEvent = 'waiting_event';
    case Completed = 'completed';
    case Failed = 'failed';
    case Paused = 'paused';
    case Cancelled = 'cancelled';
}
```

### `BackoffStrategy`

```php
enum BackoffStrategy: string {
    case Fixed       = 'fixed';
    case Linear      = 'linear';
    case Exponential = 'exponential';
}
```

### `ForEachMode`

```php
enum ForEachMode: string {
    case All = 'all';
    case FirstSuccess = 'first_success';
    case Quorum = 'quorum';
}
```

### `WaitMode`

```php
enum WaitMode: string {
    case All    = 'all';
    case Any    = 'any';
    case Quorum = 'quorum';
}
```

### `LogEvent`

```php
enum LogEvent: string {
    case Started                  = 'started';
    case Completed                = 'completed';
    case Failed                   = 'failed';
    case Buried                   = 'buried';
    case Retried                  = 'retried';
    case WorkflowStarted          = 'workflow_started';
    case WorkflowCompleted        = 'workflow_completed';
    case WorkflowFailed           = 'workflow_failed';
    case WorkflowPaused           = 'workflow_paused';
    case WorkflowResumed          = 'workflow_resumed';
    case WorkflowCancelled        = 'workflow_cancelled';
    case WorkflowRewound          = 'workflow_rewound';
    case WorkflowForked           = 'workflow_forked';
    case WorkflowDeadlineExceeded = 'workflow_deadline_exceeded';
    case Debug                    = 'debug';
}
```

### `OverlapPolicy`

```php
enum OverlapPolicy: string {
    case Allow  = 'allow';
    case Skip   = 'skip';
    case Buffer = 'buffer';
}
```

Lihat [Penjadwalan](https://queuety.inline0.dev/id/docs/features/scheduling) untuk mengetahui cara kerja setiap kebijakan.

## Atribut

### `#[QueuetyHandler]`

PHP atribut 8 untuk pendaftaran otomatis. Lihat [PHP Atribut](https://queuety.inline0.dev/id/docs/features/attributes).

```php
use Queuety\Attributes\QueuetyHandler;

#[QueuetyHandler( name: 'send_email', queue: 'emails', max_attempts: 5 )]
class SendEmailHandler implements Handler {
    // ...
}
```

## Bridge CLI

### `Queuety::cli_command()`

Mengembalikan nama perintah root WP-CLI yang dipublikasikan.

Perintah root bawaan adalah `queuety`. Tentukan `QUEUETY_CLI_COMMAND` sebelum Queuety dimulai jika perintah harus dipublikasikan dengan nama lain.

### `Queuety::cli_command_map()`

Mengembalikan katalog perintah Queuety CLI yang dapat diserialisasi untuk harness yang perlu memahami permukaan perintah yang disediakan tanpa memindai kelas WP-CLI.

Setiap entri mencakup:
- ID operasi yang stabil
- jalur relatif CLI
- kelas dan metode handler WP-CLI
- target eksekusi yang mungkin
- adaptor yang menormalisasi argumen CLI mentah menjadi rencana eksekusi

### `Queuety::resolve_cli_command( $path, $args, $assoc_args )`

Mengubah pemanggilan CLI yang telah diuraikan menjadi rencana eksekusi yang harus dijalankan harness.

```php
$plan = Queuety::resolve_cli_command(
    [ 'workflow', 'export' ],
    [ '42' ],
    [ 'output' => '/tmp/workflow.json' ]
);

echo $plan['callable']; // Queuety\Queuety::export_workflow_to_file
print_r( $plan['arguments'] );
```

Rencana eksekusi sengaja dibuat sadar akan transport:
- perintah mengarah ke callable publik PHP dengan argumen yang dinormalisasi
- katalog mencantumkan semua target eksekusi yang mungkin di awal, sementara `callable` dan `transport` mengidentifikasi cabang yang dipilih untuk masukan yang telah diuraikan

Inilah penggunaan standar Queuety untuk harness agen: ID operasi yang stabil, target API publik, dan normalisasi yang dikelola adaptor untuk hal-hal khusus CLI seperti nilai bawaan antrean, dekode payload JSON, percabangan kumpulan worker, dan penanganan berkas ekspor alur kerja.
