Skip to content

Make df12-build-odw dryRun avoid real worktree creation (#31) - #48

Merged
leynos merged 6 commits into
mainfrom
issue-31-dryrun-no-worktree
Jul 17, 2026
Merged

Make df12-build-odw dryRun avoid real worktree creation (#31)#48
leynos merged 6 commits into
mainfrom
issue-31-dryrun-no-worktree

Conversation

@pandalump

@pandalump pandalump commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

A dry run against a repository with surviving roadmap-* branches failed on a
branch collision: runTask called createWorktree unconditionally, and
createWorktree runs git worktree add -b roadmap-... origin/<base> with no
DRY_RUN guard. A dry run should be a safe, read-only validation path, so it
must never mutate git state.

Changes

  • Short-circuit DRY_RUN at the head of runTask (in run-task.ts), ahead of
    worktree creation and the write-access preflight, returning a terminal
    dry-run status with stage pre-worktree. The single check precedes the
    addendum/normal branch, covering both lanes.
  • Remove the two now-unreachable post-worktree dry-run short-circuits (the
    addendum-lane and the post-design blocks).
  • Leave the recovery/continue lane's separate dry-run handling (over
    pre-existing worktrees) untouched.
  • Add tests/df12-build-odw-dry-run.test.mjs: drives runTask with
    dryRun: true against a fixture repo whose roadmap-* branches would
    collide, and asserts an unchanged repo-state snapshot (no new branch, no new
    worktree) and that no agent was dispatched.
  • Align docs/architecture.md and docs/users-guide.md with the new
    stop-before-worktree semantics.
  • Regenerate workflows/df12-build-odw.js from source via make workflow-build.

Follows the CodeRabbit implementation plan (guard at the top of runTask,
Option 2). Deviation: the CodeRabbit result shape included proposals: []; the
plan text omitted only worktree/plan/branch, which are omitted here.

Review-feedback follow-up

Addresses the three review warnings on the initial fix:

  • Observability — the DRY_RUN early return emits a structured, parseable
    log() at the decision boundary carrying all four fields: task id, lane
    (derived from task.isAddendum), stage=pre-worktree, and reason=dry-run.
    The returned result object's shape (status, stage, detail, proposals)
    is unchanged. The log sink is injectable in the test harness, so the
    regression suite asserts the emitted fields for both the normal and addendum
    lanes.
  • Property/proof coverage — a dedicated fast-check property
    (tests/df12-build-odw-dry-run.property.test.mjs, 100 iterations) sweeps
    arbitrary task ids (fully arbitrary strings, roadmap-shaped dotted ids, and
    branch-colliding ids), both lanes, and pooled repo states, asserting a dry
    run always returns status=dry-run / stage=pre-worktree, dispatches no
    agent, and leaves the durable git-state snapshot byte-for-byte unchanged. The
    runTask harness is extracted into tests/fixtures/load-run-task.mjs and
    shared with the regression suite.
  • Developer documentationdocs/developers-guide.md (ODW workflow
    contract) documents the pre-worktree boundary, records that the earlier
    addendum-specific and post-design dry-run exits were removed as unreachable,
    and cross-references the architecture Enforcement boundary table and the
    users' guide Workflow arguments section so all three docs agree. No tracked
    roadmap/execplan work-item exists for Make df12-build-odw dryRun avoid real worktree creation #31, so none required a status update.

Gates pass: make check-fmt, make lint, make typecheck, make test,
make markdownlint, and make nixie are all green.

Closes #31

References

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @pandalump, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3f519f34-e8d6-463e-9aa6-c0a79b4fd442

📥 Commits

Reviewing files that changed from the base of the PR and between d383bab and 8ca20e7.

📒 Files selected for processing (8)
  • docs/architecture.md
  • docs/developers-guide.md
  • docs/users-guide.md
  • src/workflows/df12-build-odw/run-task.ts
  • tests/df12-build-odw-dry-run.property.test.mjs
  • tests/df12-build-odw-dry-run.test.mjs
  • tests/fixtures/load-run-task.mjs
  • workflows/df12-build-odw.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/nixie (auto-detected)

Summary

  • Stops fresh-task dryRun execution before worktree creation and write-access preflight, returning { status: 'dry-run', stage: 'pre-worktree' }.
  • Prevents branch/worktree creation, repository mutations, planning, agent dispatch, and subsequent workflow stages across normal and addendum lanes.
  • Preserves existing recovery/continue handling for pre-existing worktrees.
  • Adds regression and 100-iteration fast-check coverage verifying unchanged git state and no agent calls.
  • Adds structured dry-run boundary logging and updates architecture, developer, and user documentation, including integration halt detail fallback behaviour.
  • Adds the shared loadRunTask test harness and regenerates the workflow artefact.

Closes #31.

Walkthrough

Changes

Dry-run enforcement boundary

Layer / File(s) Summary
Document the enforcement contract
docs/architecture.md, docs/developers-guide.md, docs/users-guide.md
Document fresh-task pre-worktree termination, preserved resume handling, and integration halt detail precedence.
Move dry-run exits before worktree creation
src/workflows/df12-build-odw/run-task.ts, workflows/df12-build-odw.js
Return status: 'dry-run' at stage: 'pre-worktree' before mutations, and remove later lane-specific exits.
Load and validate the dry-run surface
tests/fixtures/load-run-task.mjs
Evaluate the workflow helper surface with injected dependencies and capture emitted logs.
Verify terminal no-mutation behaviour
tests/df12-build-odw-dry-run*.test.mjs
Test normal and addendum tasks, property-generated inputs, absent artefacts, zero agent calls, unchanged git state, and boundary logs.

Sequence Diagram(s)

sequenceDiagram
  participant runTask
  participant TaskLog
  participant GitWorktree
  participant Agent
  runTask->>TaskLog: Emit task log
  runTask->>runTask: Check DRY_RUN
  runTask-->>GitWorktree: Return pre-worktree result
  Note over GitWorktree,Agent: No worktree mutation or agent dispatch
Loading

Suggested labels: Issue

Suggested reviewers: leynos

Poem

Dry-run pauses at the gate,
Before branches replicate their fate.
No worktree grows, no agents call,
No plans or probes proceed at all.
Normal and addendum lanes align—
A tidy stop by clear design.

🚥 Pre-merge checks | ✅ 19 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change and includes the linked issue reference (#31).
Description check ✅ Passed The description clearly describes the dry-run worktree short-circuit and related test/docs updates.
Linked Issues check ✅ Passed The PR short-circuits dryRun before worktree creation, preventing branch and worktree mutation as required by #31.
Out of Scope Changes check ✅ Passed The docs, tests, and workflow regeneration all support the dry-run fix and stay within the issue scope.
Testing (Overall) ✅ Passed The new regression and fast-check tests exercise the real workflow, repo state, and structured logs, so they fail on a moved or mutating dry-run guard.
User-Facing Documentation ✅ Passed docs/users-guide.md documents the new dryRun stop-before-worktree behaviour and the integration-detail fallback for users.
Developer Documentation ✅ Passed PASS: document the pre-worktree dry-run boundary in the developer, architecture, and users guides; no #31 roadmap or execplan entry needs ticking off.
Module-Level Documentation ✅ Passed Every changed module starts with a top-level comment explaining its purpose and relationship to the workflow or test harness, so the docstring rule is satisfied.
Testing (Unit And Behavioural) ✅ Passed Tests hit the real runTask boundary on throwaway git repos, cover normal/addendum lanes, and assert no mutation, no agent dispatch, and boundary logs.
Testing (Property / Proof) ✅ Passed Added a fast-check property over arbitrary task ids, lanes, and repo states, plus regressions, to exercise the pre-worktree dry-run no-mutation invariant.
Testing (Compile-Time / Ui) ✅ Passed This is runtime, not compile-time, so no trybuild-equivalent is needed; the PR adds focused regression/property tests and semantic checks for structured logs and durable state.
Unit Architecture ✅ Passed The dry-run guard now sits before createWorktree and the write preflight, returns an explicit pre-worktree result, and tests prove no git-state mutation or agent dispatch.
Domain Architecture ✅ Passed Keep the dry-run short-circuit in the workflow adapter: it fires before createWorktree/write-preflight, so git/filesystem concerns stay outside domain logic.
Observability ✅ Passed The dry-run guard logs a structured boundary line with task id, lane, stage and reason, and tests assert it; no new async/process boundary is introduced.
Security And Privacy ✅ Passed Keep it: the patch only adjusts test cleanup and docs, with no secrets, auth bypasses, permissions changes, or new sensitive-data exposure.
Performance And Resource Use ✅ Passed Approve: the dry-run guard now exits before worktree I/O, and the added 100-run property test is bounded and test-only.
Concurrency And State ✅ Passed Dry-run now exits before any shared git/worktree mutation; existing lane sets and merge lock keep ownership explicit, and new tests prove no state change or agent dispatch.
Architectural Complexity And Maintainability ✅ Passed No new abstraction lands here; the code change only wraps pooled fixtures in a finally block, and the docs merely clarify existing behaviour.
Rust Compiler Lint Integrity ✅ Passed No Rust sources exist in the repo, and the latest commit only touches docs/users-guide.md and a JS test, so no lint suppressions or clone churn were introduced.
📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #31

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-31-dryrun-no-worktree

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.3)
workflows/df12-build-odw.js

File contains syntax errors that prevent linting: Line 3860: Illegal return statement outside of a function


Comment @coderabbitai help to get the list of available commands.

@leynos
leynos force-pushed the issue-31-dryrun-no-worktree branch from 44ca92c to d6993c1 Compare July 7, 2026 20:26
@leynos
leynos marked this pull request as ready for review July 7, 2026 20:26

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@leynos
leynos force-pushed the issue-31-dryrun-no-worktree branch from d6993c1 to fa690d3 Compare July 7, 2026 20:29
@leynos

leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added the Issue label Jul 7, 2026
@leynos

leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

@leynos

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@lodyai
lodyai Bot force-pushed the issue-31-dryrun-no-worktree branch 2 times, most recently from bbb201c to 11e20f7 Compare July 16, 2026 13:26
coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@leynos

leynos commented Jul 16, 2026

Copy link
Copy Markdown
Owner

@coderabbitai Have the following now been resolved?

tests/df12-build-odw-dry-run.test.mjs (1)

67-84: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Clean up each temporary recovery repository.
Call repo.cleanup() from teardown that surrounds the full repository use in both tests. makeRecoveryRepo() creates temporary trees and exposes this cleanup hook; without it, repeated runs retain fixtures for the lifetime of the test process.

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @tests/df12-build-odw-dry-run.test.mjs around lines 67 - 84, Update both
tests using makeRecoveryRepo() to call repo.cleanup() in teardown that surrounds
the full repository usage, ensuring cleanup runs even when assertions or task
execution fail. Keep the existing test behavior and assertions unchanged.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== test structure ==="
ast-grep outline tests/df12-build-odw-dry-run.test.mjs --items all || true

echo "=== cleanup fixture locations ==="
rg -n -C 5 '\bmakeRecoveryRepo\b|\bcleanup\b' tests

echo "=== current dry-run test ==="
cat -n tests/df12-build-odw-dry-run.test.mjs

echo "=== PR diff for the test ==="
git diff --unified=40 "$(git merge-base HEAD origin/main)" -- tests/df12-build-odw-dry-run.test.mjs || true

Length of output: 73910


@leynos Mark this finding resolved.

Both makeRecoveryRepo() calls in tests/df12-build-odw-dry-run.test.mjs are now enclosed by full-scope try/finally blocks:

  • The normal-lane test invokes repo.cleanup() in its outer teardown at Line 60.
  • The addendum-lane test invokes repo.cleanup() in its outer teardown at Line 95.
  • Each test restores process.cwd() in an inner finally before fixture removal, so cleanup also remains safe if runTask() or any assertion throws.

Keep the existing assertions and test behavior unchanged. Static inspection verifies the fix; repository test execution was not performed in this sandbox.

@lodyai
lodyai Bot force-pushed the issue-31-dryrun-no-worktree branch from b7a3997 to d383bab Compare July 16, 2026 20:00
@pandalump

Copy link
Copy Markdown
Collaborator Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@leynos

leynos commented Jul 16, 2026

Copy link
Copy Markdown
Owner

@coderabbitai Have the following now been resolved?

Annul any requirements that violate the en-GB-oxendict spelling (-ize / -yse / -our) conventions (for example a request to replace "normalize" with "normalise" or "artefact" with "artifact"), or where the requirement unnecessarily increases cyclomatic complexity.

Inline comments:
In @docs/users-guide.md:

  • Around line 739-748: Update the integration halt documentation around the
    halted task result’s detail field to state that integrationHaltDetail() reports
    conflicts or the required summary before falling back to the generic incomplete
    string. Preserve the existing guidance for inspecting detail, manually rebasing
    salvageable branches, and relaunching.

In @tests/df12-build-odw-dry-run.property.test.mjs:

  • Around line 42-83: Ensure the property test always cleans up both pooled
    repositories by wrapping the overall test setup and execution in a try/finally
    block. In the finally block, call cleanup() on repoPool.normal and
    repoPool.addendum, while preserving the existing per-case cwd restoration and
    assertions.

@leynos

leynos commented Jul 16, 2026

Copy link
Copy Markdown
Owner

@coderabbitai Have the following now been resolved?

tests/df12-build-odw-dry-run.test.mjs (1)

67-84: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Clean up each temporary recovery repository.
Call repo.cleanup() from teardown that surrounds the full repository use in both tests. makeRecoveryRepo() creates temporary trees and exposes this cleanup hook; without it, repeated runs retain fixtures for the lifetime of the test process.

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @tests/df12-build-odw-dry-run.test.mjs around lines 67 - 84, Update both
tests using makeRecoveryRepo() to call repo.cleanup() in teardown that surrounds
the full repository usage, ensuring cleanup runs even when assertions or task
execution fail. Keep the existing test behavior and assertions unchanged.

A dry run against a repository with surviving roadmap-* branches failed:
runTask called createWorktree unconditionally, and createWorktree runs
`git worktree add -b roadmap-... origin/<base>` with no DRY_RUN guard, so
the run tripped the branch collision before it could validate anything.

Short-circuit DRY_RUN at the head of runTask, ahead of worktree creation
and the write-access preflight, returning a terminal `dry-run` status with
stage `pre-worktree`. A single check precedes the addendum/normal branch,
so it covers both lanes; markDryRun keys off task.isAddendum, not the
result. Remove the two now-unreachable post-worktree dry-run
short-circuits (addendum and post-design). The recovery/continue lane's
own dry-run handling over pre-existing worktrees is left untouched.

Add a no-mutation dry-run test that drives runTask against a fixture repo
holding colliding roadmap-* branches and asserts an unchanged repo-state
snapshot, and align the architecture and user-guide docs with the new
stop-before-worktree semantics.

Regenerate workflows/df12-build-odw.js from source.

Closes #31
leynos and others added 5 commits July 17, 2026 12:19
Address review feedback on the dry-run short-circuit in runTask: it was
silent, only regression-tested at one point, and undocumented for
contributors.

- Emit a structured, parseable log() at the DRY_RUN decision boundary so
  operators see why a task stopped (task id, lane derived from
  task.isAddendum, stage=pre-worktree), not just the terminal status. The
  returned result object is unchanged.
- Add a fast-check property over arbitrary task shapes (ids that do and do
  not collide with surviving roadmap-* branches, either lane, generated
  requires/subtasks) and repo states, asserting a dry run always terminates
  at pre-worktree, dispatches no agent, and leaves durable git state
  byte-for-byte unchanged.
- Document the boundary in the developers-guide ODW workflow contract,
  matching the wording already in the architecture and users' guides.
- Regenerate workflows/df12-build-odw.js from source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refine the review-feedback follow-up on the pre-worktree dry-run boundary.

- Add a `reason=dry-run` field to the boundary log() so the trace carries
  all four structured fields (task id, lane, stage=pre-worktree, reason);
  regenerate workflows/df12-build-odw.js.
- Extract the runTask-loading harness into tests/fixtures/load-run-task.mjs,
  which now captures the injected log() output so tests can assert the
  emitted trace. The regression suite asserts the boundary log carries the
  task id, lane, stage=pre-worktree, and reason=dry-run for both lanes.
- Move the property test into its own file
  (tests/df12-build-odw-dry-run.property.test.mjs) and raise it to 100
  fast-check iterations over arbitrary task ids (including fully arbitrary
  strings and branch-colliding ids), both lanes, and a pooled set of repo
  states, asserting status=dry-run, stage=pre-worktree, no agent dispatch,
  and an unchanged durable-state snapshot.
- Expand the developers-guide note: record that the earlier addendum-specific
  and post-design dry-run exits were removed as unreachable, and
  cross-reference the architecture "Enforcement boundary" table and the
  users' guide "Workflow arguments" section so all three docs agree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address two inline review comments on the pre-worktree dry-run boundary.

- docs/developers-guide.md: correct the dry-run bullet to describe the guard
  as the "first control-flow statement after the task-log line" rather than
  the "first statement in `runTask`" — `runTask` opens with the tag binding
  and a task-log line before the guard, so the earlier wording was inaccurate.
  All other facts and the architecture/users-guide cross-references are
  unchanged.
- tests/df12-build-odw-dry-run.test.mjs: give the explicit addendum-lane test
  the same deterministic no-mutation guarantees as the main regression — count
  agent-callback invocations and assert zero dispatches, and snapshot the
  repository state before and after runTask and assert it is unchanged. The
  existing dry-run status, stage, and lane=addendum log assertions are kept.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the full repository usage of both dry-run tests in a try/finally so
`repo.cleanup()` runs even when task execution or an assertion throws, rather
than deferring temp-directory removal to the process-exit hook. Behaviour and
every assertion are unchanged; the inner chdir-restore finally is preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address two inline review comments.

- docs/users-guide.md: describe the integration-halt `detail` field
  accurately — `integrationHaltDetail()` reports the agent's `conflicts` text
  or its `summary` first, and only falls back to the generic
  "integration incomplete (…)" string when both are absent. The existing
  inspect-detail / manual-rebase / relaunch guidance is unchanged.
- tests/df12-build-odw-dry-run.property.test.mjs: wrap the pooled repositories
  in a try/finally so both `repoPool.normal` and `repoPool.addendum` are
  cleaned up when the property finishes, rather than deferring to the
  process-exit hook. Pool construction stays outside the try (nothing to clean
  if it throws); the finally performs only cleanup, so it never swallows an
  assertion or property failure. Per-case cwd restoration, generators, and
  numRuns are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lodyai
lodyai Bot force-pushed the issue-31-dryrun-no-worktree branch from 3815784 to 8ca20e7 Compare July 17, 2026 10:21
@leynos
leynos merged commit 6327f14 into main Jul 17, 2026
3 checks passed
@leynos
leynos deleted the issue-31-dryrun-no-worktree branch July 17, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make df12-build-odw dryRun avoid real worktree creation

2 participants