Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions .github/workflows/mutation-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ jobs:
echo "## Changed-code mutation testing" >> "$GITHUB_STEP_SUMMARY"
echo "Mutation testing was enforced on each pull request before it entered the merge queue." >> "$GITHUB_STEP_SUMMARY"

- name: Checkout pull request head
- name: Checkout pull request merge result
if: github.event_name == 'pull_request'
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false

Expand All @@ -55,14 +54,26 @@ jobs:
if: github.event_name == 'pull_request'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
HEAD_SHA: ${{ github.sha }}
run: node scripts/stryker-diff.mjs ci --base "$BASE_SHA" --head "$HEAD_SHA"

- name: Upload mutation reports
id: mutation_report
if: always() && github.event_name == 'pull_request'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: changed-code-mutation-report
path: reports/mutation/
if-no-files-found: ignore
retention-days: 7

- name: Link mutation report artifact
if: always() && github.event_name == 'pull_request' && steps.mutation_report.outputs.artifact-url != ''
env:
ARTIFACT_URL: ${{ steps.mutation_report.outputs.artifact-url }}
run: |
{
echo ""
echo "### Download mutation reports"
echo "[Open the changed-code-mutation-report artifact]($ARTIFACT_URL), then open the package's mutation.html file."
} >> "$GITHUB_STEP_SUMMARY"
10 changes: 10 additions & 0 deletions docs/architecture/task-lifecycle-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ The model has three fixed task slots, enough to cover competing siblings and a n

Production completion also accepts a recovery-compatible `active` parent that still awaits the returning child, then clears the stale pointers. Normal model transitions never create that intermediate state, so it is covered by a focused reducer test rather than admitted as a generally valid reachable state.

## Provider handoff refinement model

The same command runs `scripts/check-provider-handoff.ts`, a separate bounded model for the concrete provider steps that refine the atomic `delegate(parent, child)` lifecycle operation. It imports the production handoff policy and profile decision functions from `src/core/task-persistence/providerHandoff.ts`; its single persistence step calls `delegateTaskToChild` rather than duplicating the persisted transition.

The model covers both a sole live parent and a nested parent whose removal exposes an unrelated root task. For each topology it checks saved, unsaved, and workspace-locked profile paths through these observable phases: remove parent, prepare child profile, create the paused child, persist delegation, start the child, and publish the child state. It enforces that pending preparation publishes no intermediate state, cannot mutate the exposed root task, creates the child with the requested mode and selected profile, and starts the child only after exactly one atomic delegation commit.

An injected legacy policy retains the pre-fix implicit-current-task targeting and intermediate publication behavior without modifying repository history. The checker requires shortest counterexamples for both an empty publication after removing a sole parent and mutation of an exposed root during nested delegation. These witnesses are regression ratchets for the provider handoff policy, not generally allowed lifecycle states.

This model deliberately keeps profile identities as opaque names/IDs and does not model API secrets, provider construction, VS Code transport latency, filesystem durability, scheduler fairness, or rollback cleanup. Focused provider tests remain responsible for proving that `ClineProvider` interprets the shared production policy correctly.

## Shared-store concurrency model

The same `pnpm lifecycle:model-check` command also runs a second bounded explorer over two `TaskHistoryStore` hosts. It imports the production `computeHistoryDelta` and `mergeHistoryDelta` functions, so its semantics match the store rather than assuming coherent caches or transactional pair writes:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"check-types": "turbo check-types --log-order grouped --output-logs new-only",
"test": "turbo test --log-order grouped --output-logs new-only",
"test:mutation-ci": "node --test scripts/stryker-diff.test.mjs",
"lifecycle:model-check": "tsx scripts/check-task-lifecycle.ts && tsx scripts/check-task-store-concurrency.ts",
"lifecycle:model-check": "tsx scripts/check-task-lifecycle.ts && tsx scripts/check-provider-handoff.ts && tsx scripts/check-task-store-concurrency.ts",
"test:coverage": "turbo test:coverage --log-order grouped --output-logs new-only",
"format": "turbo format --log-order grouped --output-logs new-only",
"build": "turbo build --log-order grouped --output-logs new-only",
Expand Down
Loading
Loading