# WP Agentic Writer - Definition of Done

## Purpose

This contract prevents the "fix A, break B" cycle by ensuring every change to chat, planning, writing, refinement, context, provider, or cost has explicit ownership and accountability.

---

## Contract Checklist

For every PR touching these domains: chat, planning, writing, refinement, context, provider, cost

### 1. Storage Layer Declaration

**Required:** State which storage layer is authoritative for the change.

| State Type | Authoritative Storage |
|------------|----------------------|
| Conversation messages | `wpaw_conversations.messages` (via Context Service) |
| Article outline/plan | `post_meta._wpaw_plan` (via Context Service) |
| Per-post configuration | `post_meta._wpaw_post_config` (via Context Service) |
| User preferences | `wp_agentic_writer_settings` |
| Cost records | `wpaw_cost_tracking` |
| Image recommendations | `wpaw_images` |
| Session state | `wpaw_conversations` |

If the change touches multiple storage layers, explain why and ensure they stay in sync.

### 1a. Context Service Usage

**Required for all generation paths:** Use `WP_Agentic_Writer_Context_Service` as the unified interface.

```php
// Get context (single source of truth)
$context_service = WP_Agentic_Writer_Context_Service::get_instance();
$context = $context_service->get_context($session_id, $post_id);

// Save messages to session table
$context_service->save_messages($session_id, $messages);

// Save plan to post meta
$context_service->save_plan($post_id, $plan);

// Save config to post meta
$context_service->save_post_config($post_id, $config);
```

**Rules:**
- Conversation messages → always use `save_messages()` or `add_message()` (writes to session table)
- Plan/Config → use `save_plan()` / `save_post_config()` (writes to post meta)
- Legacy `_wpaw_chat_history` post meta → migrate on first access via `migrate_legacy_chat_history()`

### 2. Provider Transparency

**Required:** Include provider/model metadata in the response.

```php
// Every AI response must include:
$result = [
    'content' => '...',
    'provider' => 'openrouter', // actual provider used
    'model' => 'anthropic/claude-3.5-haiku', // actual model used
    'cost' => 0.0025,
    'warnings' => [] // any issues (fallback used, etc)
];
```

### 3. Cost Record Integrity

**Required:** Every API request must update cost records intentionally.

- Successful calls → record actual cost
- Failed calls → record attempt with error status
- Skipped calls → no record needed
- Never silently fail to record costs

### 4. Workflow Test Requirement

**Required:** Test at least one complete workflow path.

Minimum paths to test:
1. **Chat → Plan → Write** (happy path)
2. **Write → Stop → Resume** (pause/resume)
3. **Plan → Clear Context → New Plan** (context reset)

For each path, verify:
- State persists correctly
- Cost records are accurate
- Errors are handled gracefully

### 5. No Double Source of Truth

**Required:** The same state must not exist in two places.

- If session table is authoritative, don't also trust post meta for the same data
- If you copy data for performance, document the sync mechanism
- If two sources diverge, one must win (document which)

---

## Storage Layer Map

```
┌─────────────────────────────────────────────────────────────┐
│                    FRONTEND (sidebar.js)                   │
│  React State ← localStorage ← Session Table ← Post Meta    │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                    BACKEND (PHP)                            │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐ │
│  │ Sessions    │  │ Post Meta    │  │ Settings/Cost       │ │
│  │ wpaw_conv   │  │ _wpaw_plan  │  │ wpaw_cost_tracking  │ │
│  │             │  │ _wpaw_*     │  │ wp_agentic_writer_* │ │
│  │ Authority:  │  │ Authority:  │  │ Authority:          │ │
│  │ Messages    │  │ Plan/Config │  │ Settings/Costs      │ │
│  └─────────────┘  └─────────────┘  └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```

**Priority Rules:**
1. `wpaw_conversations` is authoritative for conversation messages
2. `post_meta` is authoritative for article plan and per-post config
3. `wpaw_cost_tracking` is authoritative for usage costs
4. `wp_agentic_writer_settings` is authoritative for plugin settings

---

## Error Handling Contract

### For AI/Provider Errors

```php
// Always return meaningful errors, not silent failures
if ( is_wp_error( $result ) ) {
    return [
        'success' => false,
        'error_code' => $result->get_error_code(),
        'error_message' => $result->get_error_message(),
        'provider' => $actual_provider_used ?? 'unknown',
        'can_retry' => is_retryable_error( $result )
    ];
}
```

### For Database Errors

```php
// Tables must exist before operations; verify and create if needed
$image_manager->ensure_tables(); // Call this before any DB operation
if ( is_wp_error( $check ) ) {
    return $check; // Return WP_Error with clear message
}
```

### For Validation Errors

```php
// Validate early, fail clearly
if ( empty( $post_id ) ) {
    return new WP_Error( 'missing_post_id', 'Post ID is required', 400 );
}
```

---

## Provider Selection Contract

### Explicit Fallback

If a provider fails and you fall back to another:

1. Log the fallback with both provider names
2. Include `warnings: ['Provider X unavailable, fell back to Y']` in response
3. UI must show actual provider used, not selected provider

### Provider Health Check

Before expensive operations, optionally verify provider is reachable:

```php
// In provider-manager.php, expose health status
public static function get_provider_health( $provider_name ) {
    $provider = self::get_provider_instance( $provider_name, 'chat' );
    if ( ! $provider || ! $provider->is_configured() ) {
        return ['status' => 'unconfigured'];
    }
    // Optional: test reachability
    return ['status' => 'ready'];
}
```

---

## Migration Safety Contract

When adding new database tables or fields:

1. Always use `CREATE TABLE IF NOT EXISTS`
2. Provide migration for existing installations
3. Handle missing tables gracefully (create on demand)
4. Version each table independently

---

## Security Contract

### Session Access

- Every session endpoint must verify ownership
- Users can only access their own sessions
- For post-linked sessions, verify `current_user_can('edit_post', $post_id)`

### Input Validation

- Sanitize all inputs before database operations
- Use WordPress sanitization functions
- Never trust user-provided data

### Output Escaping

- All output to frontend must be escaped appropriately
- Use `wp_json_encode()` for JSON
- Use `esc_html()`, `esc_attr()` for text

---

## Testing Requirements

### Before Merging

- [ ] PHP syntax check passes
- [ ] JS syntax check passes
- [ ] All new functions have docblocks
- [ ] No hardcoded credentials or API keys
- [ ] Error paths are tested (even if manually)

### For New Features

- [ ] At least one workflow path tested end-to-end
- [ ] Error handling documented
- [ ] Cost implications considered
- [ ] Storage layer declaration written

---

## Changelog Policy

When making changes, update `CHANGELOG.md` with:

```
## [Unreleased]

### Added
- Feature description

### Changed
- Behavior change

### Fixed
- Bug fix description

### Security
- Security fix description
```

Format: Keep unreleased at top, use semantic versioning for releases.