# Board Sync (subcommand of `/ai-board`)

> Detail file for `/ai-board sync`. The user-facing entry is `.agents/skills/ai-board/SKILL.md`. This file owns the procedural contract for lifecycle state transitions on the project board.

## Purpose

Updates work item state on the project board at each lifecycle transition. Called internally by other skills (`/ai-brainstorm`, `/ai-build`, `/ai-pr`) and by provider-side runbooks when they need to move an item through the configured lifecycle. Fail-open: never blocks the calling skill's workflow.

## When to Use

- Called automatically by `/ai-brainstorm` (refinement, ready transitions)
- Called automatically by `/ai-build` (in_progress transition)
- Called automatically by `/ai-pr` (in_review transition)
- Manual override: `/ai-board sync <phase> <ref>`

## Inputs

| Parameter | Required | Description |
|-----------|----------|-------------|
| phase | Yes | Lifecycle phase: `refinement`, `ready`, `in_progress`, `in_review`, `done` |
| work-item-ref | Yes | Work item reference: `#45` (GitHub issue) or `AB#100` (ADO) |
| --comment | No | Comment to add to the work item (e.g., spec reference, PR URL). The `--comment` parameter is optional. "Context available" means the calling skill provides a spec URL, PR URL, or status summary in its invocation payload. |

## Process

1. **Read config** -- read `.ai-engineering/manifest.yml` `work_items` section:
   - `provider` -- determines which CLI to use
   - `state_mapping` -- maps lifecycle phase to provider-specific state name
   - `github_project` -- Projects v2 field IDs and option IDs (GitHub only)
   - `custom_fields` -- any custom fields to update per transition

2. **Validate** -- check that the requested phase has a mapping:
   - If `state_mapping.<phase>` is null: log info "State mapping not configured for <phase>, skipping", return success
   - If `github_project.number` is null and provider is github: check for labels fallback

3. **Update state** -- based on provider:

   **GitHub Projects v2** (primary):
   a. Read `github_project.owner` from manifest for the `--owner` flag.
   b. Get the project item ID for the issue:
      ```
      gh project item-list <number> --owner <github_project.owner> --format json | jq '.items[] | select(.content.number == <issue_number>)'
      ```
   c. Update the status field:
      ```
      gh project item-edit --project-id <project_id> --id <item_id> --field-id <status_field_id> --single-select-option-id <option_id>
      ```

   **GitHub Labels** (fallback):
   a. Remove existing `status-*` labels:
      ```
      gh issue edit <number> --remove-label "status-refinement,status-ready,status-in-progress,status-in-review,status-done"
      ```
   b. Add new status label:
      ```
      gh issue edit <number> --add-label "status-<phase>"
      ```

   **Azure DevOps**:
   a. Update work item state:
      ```
      az boards work-item update --id <number> --state "<mapped_state>" -o json
      ```

4. **Add comment** (if --comment provided or if context available):
   - **GitHub**: `gh issue comment <number> --body "<comment>"`
   - **Azure DevOps**: `az boards work-item update --id <number> --discussion "<comment>"`
   - Include context: spec reference, PR URL, or transition reason

5. **Update custom fields** (if configured for this transition):
   - Read `custom_fields` from manifest for fields that should update on this phase
   - Example: set "Start Date" on `in_progress` transition, set "Target Date" on `ready`
   - Respect hierarchy policy: feature-level records remain read-only even if the provider exposes writable fields

6. **Return result** -- report success or failure to caller:
   - Success: `{ "status": "updated", "phase": "<phase>", "ref": "<ref>", "provider_state": "<mapped>" }`
   - Skipped: `{ "status": "skipped", "reason": "no state mapping configured" }`
   - Failed: `{ "status": "failed", "error": "<message>", "remediation": "<hint>" }`

## Fail-Open Protocol

This skill NEVER blocks the calling skill's workflow:

1. If provider CLI is not authenticated: log warning with `gh auth login` or `az login` hint, return success
2. If project item not found: log warning "Issue #N not found in project #M", return success
3. If field update fails: log warning with error details, return success
4. If network error: log warning, return success

The calling skill checks the return status for logging but NEVER stops its own execution based on board-sync failure.

## Common Mistakes

- Using provider-specific state names instead of lifecycle phase names.
- Attempting Projects v2 update without first looking up the project item ID.

## Scripts

- `scripts/board-sync-github.sh <project-number> --owner <github_project.owner>` -- query GitHub Projects v2 items and summarize work item states. Read owner from `github_project.owner` in manifest. Path is relative to the skill directory (`.agents/skills/ai-board/`).

## Examples

### Example 1 — manual transition to in-review

User: "move issue #123 to in-review on the board"

```
/ai-board sync in_review #123
```

Looks up the project item, applies the configured state transition, optionally posts a comment with context.

### Example 2 — auto-invocation by /ai-pr

User: "open a PR for this branch" (calls /ai-pr internally)

```
# /ai-pr triggers internally:
/ai-board sync in_review AB#456
```

PR creation calls this skill to flip the linked Azure Boards work item; failures are logged but do not block PR creation (fail-open).

## Integration

Called by: `/ai-brainstorm`, `/ai-build`, `/ai-pr` (internal). Reads: `.ai-engineering/manifest.yml` `work_items` section. Writes: external provider board state. Pairs with: `/ai-board discover`.

$ARGUMENTS
