# Phase 35 Plan Check - Iteration 2 (Revision Verification)

**Checked:** 2026-05-24
**Phase directory:** .planning/phases/35-psyche-sync-cross-machine-context-backup-via-private-gh-repo/
**Iteration:** 2
**Plans checked:** 9 (35-01 through 35-09)
**Prior iteration:** Iteration 1 FAILED with 1 blocker (B1) + 4 warnings (W1, W2, W3, W4). Planner was asked to revise B1 + W2.

## Methodology

Targeted revision-verification. Iteration-1 PASSED 12 dimensions and all 7 critical architectural constraints except B1 (a concrete compile-time failure in Plan 35-02 Task 2). Iteration 2 scope:

1. Verify B1 fix landed in Plan 35-02: classify_and_record_outcome signature takes &Result<T, GitError> by reference; ALL instructions to add #[derive(Clone)] to GitError deleted; call sites in pull_branch / push_branch pass by reference.
2. Verify W2 fix landed in Plan 35-07: depends_on frontmatter includes 35-02 alongside 35-01.
3. Re-verify ground-truth invariant: src/common/git.rs lines 428-438 confirm GitError is still #[derive(Debug)] only, non-Clone, so the Resolution C rewrite is necessary and correct.
4. Spot-check that Plans 35-01, 35-03, 35-04, 35-05, 35-06, 35-08, 35-09 were NOT touched and still match their iteration-1 PASS state.

File modification times confirm scope of revision: 35-01 (03:00), 35-02 (03:29 REVISED), 35-03 (03:01), 35-04 (03:07), 35-05 (03:09), 35-06 (03:13), 35-07 (03:32 REVISED), 35-08 (03:15), 35-09 (03:17). Only the two plans flagged in iteration 1 were modified. No collateral edits.

## B1 -- Verification (Plan 35-02 Task 2)

### B1.1 -- Clone-derive instructions removed

Scanned 35-02-PLAN.md for any instruction to add Clone to GitError. Eight occurrences of "Clone" remain, all of which are NEGATIVE assertions (DO NOT add it, or test that it was NOT added). Specifically:

- line 48: key_link documentation explaining the by-reference design choice
- line 84: interface doc showing current shape "#[derive(Debug)] // NOTE: NOT Clone"
- line 237: test instruction stating the plan MUST NOT add Clone to GitError
- line 241: acceptance criterion: GitError enum is UNCHANGED, no #[derive(Clone)] added
- line 310: explicit prohibition: DO NOT add #[derive(Clone)] to GitError ... E0204
- lines 365 + 374: test code asserting Clone absence in src/common/git.rs
- line 420: success criterion: GitError is untouched (no Clone derive, std::io::Error is not Clone per E0204)

Zero instructions to add Clone. B1.1 PASS.

### B1.2 -- classify_and_record_outcome signature is by reference

Plan 35-02 specifies the signature at three reinforcing places:

- must_haves.truths (line 25): "classify_and_record_outcome accepts &Result<T, GitError> BY REFERENCE -- GitError is NOT Clone"
- acceptance_criteria (line 240): "Signature is exactly pub(crate) fn classify_and_record_outcome<T>(result: &Result<T, GitError>, branch: &str) (by reference). NO <T: Clone> bound. NO ownership transfer."
- action step 1 (line 251): code block opens with "pub(crate) fn classify_and_record_outcome<T>(result: &Result<T, GitError>, _branch: &str) {"

The body uses "match result { ... }" with pattern-borrows (Err(GitError::Nonzero { stderr }) if is_remote_404(stderr)), no ownership taken, no clone. B1.2 PASS.

### B1.3 -- Call sites pass by reference

- pull_branch action (line 304): "classify_and_record_outcome(&r, branch); // BY REFERENCE -- does NOT consume r" followed by "r.map(|_| ())" which retains ownership and converts the success payload.
- push_branch action (line 315): same pattern, "classify_and_record_outcome(&r, branch); // BY REFERENCE" then "r.map(|_| ())".

Both call sites use &r immutable-borrow. B1.3 PASS.

### B1.4 -- Compile-time pin tests added

Plan 35-02 Task 2 step 8 adds two new tests in tests/source_order_sync.rs:

- classify_is_by_reference_not_by_value: scans src/common/sync.rs for the literal "fn classify_and_record_outcome<T>(result: &Result<T, GitError>" and fails the build if the signature drifts.
- git_error_remains_non_clone: scans src/common/git.rs around the "pub(crate) enum GitError" line; fails if Clone appears in the preceding 80 bytes (the derive block).

These tests turn B1 into a hard regression gate at execution time. B1.4 PASS.

### B1.5 -- Ground-truth confirmation

src/common/git.rs lines 428-438 read at iteration-2 time confirm only #[derive(Debug)] on the enum. std::io::Error is the Io variant payload, adding Clone would still fail with E0204. The Resolution C approach (by-reference signature) is the correct fix and is what the plan now specifies. B1.5 PASS.

### B1 Overall

**B1 RESOLVED.** All five sub-checks pass. The plan can no longer instruct the executor to commit the E0204 compile failure.

## W2 -- Verification (Plan 35-07 depends_on)

Iteration-1 finding: Plan 35-07 frontmatter declared "depends_on: [35-01]" but its Task 1 imports "crate::common::sync::gh_present" from Plan 35-02 and Task 2 adds a helper to src/common/sync.rs (Plan 35-02 module).

Iteration-2 frontmatter (lines 6-8) now reads:

    depends_on:
      - 35-01
      - 35-02

Confirmed by grep across the phase directory: 35-07 now lists both 35-01 and 35-02. Wave assignment stays Wave 3 (max(Wave 35-01, Wave 35-02) + 1 = max(1, 2) + 1 = 3), still consistent.

Additional reinforcement: the plan objective block (line 63) now contains an explicit "Dependency note (W2 from iteration-1 check)" paragraph documenting why both 35-01 and 35-02 are listed. The new key_link at lines 47-49 also pins the contract: from queue_sync_prompt_if_due to sync::gh_present + sync.rs helpers via Plan 35-02 dependency -- gh_present + sync.rs module must exist before this plan can compile.

**W2 RESOLVED.**

## Regression Sweep -- Other 7 Plans

### Mod-time check

35-01, 35-03, 35-04, 35-05, 35-06, 35-08, 35-09 all carry mtimes between 03:00 and 03:17, earlier than the iteration-1 check ran. 35-02 (03:29) and 35-07 (03:32) are the only files touched in the revision pass. No collateral edits.

### Clone-derive leak check

Searched all other plans for the strings "GitError" and "derive(Clone". Only matches are:

- 35-04 lines 110, 137, 154, 173: references to GitError as a return type in function signatures (Result<(), GitError>, Result<Vec<String>, GitError>). NO #[derive(...)] instructions.
- 35-09 line 92: same pattern -- Result<(), GitError> in a verification grep. NO derive instructions.

No other plan instructs the executor to modify GitError derive list. The B1 fix is isolated to 35-02. PASS.

### Coverage re-check

Iteration-1 coverage table is unchanged because the revision touched signatures + frontmatter only, not requirement mappings:

| Requirement | Plans | Status |
|-------------|-------|--------|
| SYNC-BOOTSTRAP-01 | 35-01, 35-04, 35-09 | COVERED |
| SYNC-AUTO-01 | 35-07, 35-09 | COVERED |
| SYNC-MANUAL-01 | 35-04, 35-09 | COVERED |
| SYNC-HOOK-01 | 35-02, 35-03, 35-05, 35-06, 35-09 | COVERED |
| SYNC-NOOP-01 | 35-02, 35-08, 35-09 | COVERED |

All 5 requirement IDs still in at least one plan requirements field. PASS.

### Dependency graph re-check

| Plan | depends_on | Implied Wave | OK |
|------|------------|--------------|----|
| 35-01 | [] | 1 | yes |
| 35-03 | [] | 1 | yes |
| 35-02 | [35-01, 35-03] | 2 | yes |
| 35-04 | [35-01, 35-02] | 3 | yes |
| 35-05 | [35-02, 35-03] | 3 | yes |
| 35-06 | [35-02, 35-04] | 4 | yes |
| 35-07 | [35-01, 35-02] | 3 | **CHANGED, now correct** |
| 35-08 | [35-01, 35-02] | 4 | yes |
| 35-09 | [35-01..35-08] | 5 | yes |

No cycles. No forward references. No missing references. PASS.

### Critical Architectural Constraints re-check

All 7 constraints from the iteration-1 check remain honored, none was contradicted by the revision:

1. Orthogonal-hook side-effects in Plan 35-05: untouched. PASS.
2. No new run_git_checked wrapper in Plan 35-02: REVISED plan still uses "git::run_git_checked(args, None, SYNC_TIMEOUT)" directly (lines 302-303, 314). PASS.
3. commit_*_payload insertion lines in Plan 35-06: untouched. PASS.
4. Perch-path vs worktree-path in 35-02, 35-06: REVISED 35-02 still uses agent_worktree_path / project_worktree_path / seed_path (no perch_path::resolve_*). PASS.
5. psyche.md untouched: neither 35-02 nor 35-07 touches psyche.md (verified in files_modified frontmatter). PASS.
6. Settings.json location: REVISED 35-02 still consumes via owlery::read_sync_settings / write_sync_settings. REVISED 35-07 same. PASS.
7. Post-eadb1ee vocabulary in 35-04 SKILL.md: untouched. PASS.

### Context Compliance re-check

D-01..D-19 trace map unchanged. The revision did not drop, contradict, or scope-reduce any locked decision. PASS.

### Scope Reduction Detection re-check

Scanned both revised plans for scope-reduction language (v1, static for now, simplified, placeholder, future enhancement, not wired, stub, too complex).

- 35-02 line 296: "On parse failure, fall back to now_iso.to_string() (caller still records the failure; the gate just fires immediately on next attempt -- acceptable graceful degrade)." This is a degradation policy for iso_plus_duration, NOT a scope reduction of a D-XX. D-18 specifies the schedule, not the parse-failure recovery path. Accept.
- 35-07: no scope-reduction language. The "Dependency note (W2 from iteration-1 check)" is meta-documentation, not a scope cut.

No new blockers. PASS.

## Residual Warnings (carried over from iteration 1)

These were classified non-blocking in iter-1 and the planner was not asked to address them. Listed for visibility:

- **W1 -- Plan 35-04 Task 1 scope density**: still present. Cognitive load remains high but file count stays within budget. Non-blocking.
- **W3 -- RESEARCH.md Open Questions heading lacks (RESOLVED) suffix**: still present. All 5 questions have inline recommendations. Documentation polish; not a blocker for execution.
- **W4 -- Plan 35-04 Task 2 step E conditional wording**: still present. Conditional resolves to a no-op (verified at iter-1). Plan still executes correctly. Cosmetic.

If the user wants to clear these before execution, they can be addressed in a final touch-up pass.

## Structured Issues

    issues:
      - id: B1
        status: RESOLVED
        plan: 35-02
        description: "Resolution C applied: classify_and_record_outcome takes &Result<T, GitError> by reference. All #[derive(Clone)] instructions deleted. Two new compile-time pin tests added (classify_is_by_reference_not_by_value + git_error_remains_non_clone)."
      - id: W2
        status: RESOLVED
        plan: 35-07
        description: "depends_on now lists [35-01, 35-02]. Frontmatter accurate; wave assignment (3) unchanged. Reinforced with explicit dependency note in objective and a new key_link pinning the contract."
      - id: W1
        status: CARRY-OVER
        severity: warning
        plan: 35-04
        description: "Task 1 cognitive load remains high. Non-blocking; not in revision scope."
      - id: W3
        status: CARRY-OVER
        severity: warning
        plan: research
        description: "RESEARCH.md Open Questions heading still missing (RESOLVED) suffix. Substantively resolved via inline recommendations; literal suffix would tighten Dimension 11 strict gate."
      - id: W4
        status: CARRY-OVER
        severity: warning
        plan: 35-04
        description: "Conditional plugin.json step framing is verbose for a no-op. Cosmetic."

## Recommendation

Both iteration-1 issues that the planner was asked to address are now RESOLVED:

- **B1 fixed:** Plan 35-02 specifies a by-reference classifier signature, deletes every Clone-derive instruction, and adds two regression pin tests that fail the build if the signature or GitError derive list drifts.
- **W2 fixed:** Plan 35-07 frontmatter depends_on now lists both 35-01 and 35-02. Wave assignment unchanged and still correct.

No regressions detected in the other 7 plans (mtime confirms they were not touched). All 5 requirement IDs remain covered. All 7 critical architectural constraints remain honored. D-01..D-19 still fully traced.

The plan set is ready for execution. Carry-over warnings W1, W3, W4 are non-blocking polish and can be addressed in a touch-up pass or deferred.

## PLAN CHECK PASSED
