PROBLEM / REVISION / AUTHORIZED SCOPE
  Active record: doc/problems/captjohn-advisor/spec-analysis.txt
    (consolidated 2026-10-12).
  Specification: probs/problem-captjohn-advisor.lisp, revision of 2026-10-12
    (basic model + dead-end cut + remaining-moves bound).
  Scope (approved 2026-10-11/12): write the spec section by section with user
    review, add the agreed enhancements, final review and search-advisor
    handoff. Checks run by the user at the Windows REPL. Existing
    probs/problem-captjohn.lisp not read (comparison not requested).

INPUT ANALYSIS AND OBJECTIVE
  Current: problem-analysis.txt (revision 2). Objective, answer identity
    and design decisions as agreed there. The WOULDWORK MAPPING line
    "algorithm depth-first" for both phases predates its D11 correction;
    D11 governs (backtracking placement, depth-first route).
  Ordered identical tokens retained over engine symmetry pruning.

REPRESENTATION AND AUTHORING APPROACH
  Path B (hand-authored), problem-type planning, cost 1 per action.
  Objects: cells R<row>C<col> from *captjohn-grid-size* (row 1 top); token
    kinds captain, vessel, guard, crew.
  Dynamic: (located token cell) bijective - layout, both directions; grass =
    no occupant. next-to-place (placement only). john-cell, visited (route).
  Static, derived by init actions: cell-position> (row, col, row-major
    index); vertical, diagonal, king neighbor lists. Authored instance data:
    placement order (next-in-order>), same-kind order (after-same-kind>),
    fixed grass (clue 7).
  No phase fact: place needs a next token, move needs none; completed
    layouts are placement goals and are not expanded under backtracking.
  Clues: clue-broken?, one line per clue over same-row?/same-col? and the
    neighbor lists; each clue tested once its tokens are placed, grass
    clues (4, 6) once no token remains. A clue interpreter was rejected
    as excess machinery (user, 2026-10-11).
  Semantic settings: placement EVERY (exhaustive); route MIN-LENGTH.
    Execution choices: placement backtracking, tree, depth 6 (token count),
    16 threads; route depth-first, graph, user depth cutoff, 16 threads.
  REPL sequence (in the file header, verified 2026-10-11/12):
    (stage captjohn-advisor)
    (ww-set *algorithm* backtracking)
    (solve)
    uniqueness test on *unique-solution-states* (expect 1)
    (setf *captjohn-layout* (list-database (problem-state.idb (solution.goal (first *solution-paths*)))))
    (ww-set *algorithm* depth-first)
    (ww-set *tree-or-graph* graph)
    (ww-set *solution-type* min-length)
    (ww-set *depth-cutoff* 6)
    (solve-subgoal *captjohn-layout* (route-complete?))

SECTION REVIEW AND REQUIREMENT MAPPING (all sections accepted)
  Clues 1, 2, 3, 5, 8 -> clue-broken? lines, tested on every placement.
  Clues 4, 6 -> clue-broken? grass lines, when the grid is full.
  Clue 7 -> fixed-grass R1C2; place never uses it.
  One token per cell -> place takes only empty cells; bijective located.
  Each layout once -> same-kind tokens take increasing cell index.
  Placement goal -> no next token (clue checks already dropped failures).
  Uniqueness -> *unique-solution-states* length 1 (REPL test).
  Route: king step to an unvisited cell; never a guard; ship only once all
    crew visited or being left -> move action. Start cell counts as
    visited once left.
  Route goal -> route-complete? (John on the ship; crew already enforced by
    move). Shortest -> MIN-LENGTH, depth-first graph; merging identical
    (cell, visited) states is sound (same futures).
  Final review 2026-10-12 found no fidelity defects. Instance data inside
    rules, to change for other instances: column 3 (clue 3), crew1 named in
    clue 6, token lists, placement order, fixed grass, placement depth 6.

ENHANCEMENTS FOR THE LARGER FAMILY (both implemented, enabled, exercised)
  Dead-end cut -> prune-state? + reachable-cells.
    Growth: larger grids where guards and John's trail seal off pockets.
    Rule: spread from John's cell through unvisited, guard-free cells (crew
      passable); the ship is reached but not spread through. Prune if an
      unvisited crew cell or the ship is not reached. False during placement.
    Soundness: future moves enter only unvisited non-guard cells; the ship
      ends the trip; the visited set only grows.
    Integration: backtracking calls the hook only for states it will expand,
      after goal acceptance (ww-backtracker.lisp backtrack); depth-first in
      expand (ww-planner.lisp). Completed layouts are never pruned.
    Cost: one recursive sweep per expansion; reached set is a list
      (member), quadratic in reached cells.
  Remaining-moves bound -> min-steps-remaining? (single query).
    Rule: max of (a) king distance John->ship, and for each uncollected crew
      John->crew + crew->ship; (b) uncollected crew + 1 (0 on the ship).
      0 during placement.
    Admissibility: king distance never exceeds king moves; guards and the
      visited set only lengthen routes; each uncollected crew cell and the
      ship need their own entering move. Value 0 at the route goal.
    Integration: prunes only with a depth cutoff or after a first route
      (ww-searcher.lisp min-steps-remaining-bound-prunes-p).
  Interaction: independent sound tests on the same state; no shared
    assumptions.

CHECKS AND EVIDENCE (user, Windows REPL, 16 threads)
  Static grid facts (2026-10-11): nine cell-position> facts, index 1-9;
    neighbor lists correct (e.g. R2C2: 8 king; vertical R1C2 R3C2;
    diagonal the four corners).
  Placement (backtracking, EVERY, depth 6): 1 unique layout -
    row1: guard1, grass, crew1; row2: grass, grass, john;
    row3: ship, guard2, crew2. Hand-checked against clues 1-8.
  Layout handoff: saved facts are JOHN-CELL and LOCATED1/LOCATED2 index
    facts; make-start-state-from-facts accepts them as-is.
  Route (depth-first, graph, MIN-LENGTH, depth 6), 6 moves every run:
    R2C3 -> R3C3 (crew2) -> R2C2 -> R1C3 (crew1) -> R1C2 -> R2C1 -> R3C1
    (ship). Hand check: 6 is forced; the other crew order dead-ends.
    States processed: 48 (no hooks), 40 (dead-end cut), 37 (both hooks;
    min-steps pruned 11 = 29.7%; fallback evaluated 35, skipped 0).
    Final run 2026-10-12: 0.003 s, completed normally (search exhausted
    under the incumbent bound).
  Source review: parallel backtracking uniqueness count is valid with
    threads (register-parallel-solution-bt -> record-solution-bt under
    lock; finalize-parallel-search-results rebuilds
    *unique-solution-states*).

READINESS AND OPEN ISSUES
  - Final review done 2026-10-12; no correctness blockers.
  - Warning-free staging of the final revision: passed (user, 2026-10-12).
  - Specification accepted for the agreed scope (2026-10-12).
  - Bounded run with both enhancements: passed (above).
  - "Passing" = stepping onto a guard (tentative; a diagonal squeeze rule
    would add one test to move and to reachable-cells).
  - Engine work done during this phase (user-directed 2026-10-11):
    validator accepts mv-assign; backtracker runs finally/next followups;
    init-action static facts no longer leak into the backtracking idb;
    misleading CSP staging note removed; doc/spec-advisor.md updated.
  - Deferred to the search advisor: overhead and benefit of both hooks on
    larger grids; a stronger tour bound; placement order; graph memory.

HANDOFF AND NEXT STEP
  Handoff: spec-analysis-search-handoff.txt (2026-10-12).
  Proposed first search-advisor question: on a hand-placed larger layout,
    how much do the dead-end cut and the remaining-moves bound each save
    in route time, and does either cost more than it saves?
  Not authorized: search-advisor phase, benchmarks.

CORRECTIONS AND HISTORY
  2026-10-12 Record consolidated at final review; the pre-consolidation
    copy was deleted at the user's request.
  2026-10-12 Wrap-up: pre-specification snapshot
    problem-analysis-2026-10-11-before-spec.txt deleted (new advisor rule,
    doc/spec-advisor.md 3.2); problem-analysis.txt is the agreed account.
  2026-10-11 First placement run failed: (> 1 nil) in PLACE-EFF-FN for guard1.
    Cause: static relations with two arguments of the same type (fluent
    positions included) are symmetric (ww-installer.lisp symmetric-type-
    indexes; ww-support.lisp add-proposition adds all permutations), so
    (after-same-kind guard2 guard1) also asserted (after-same-kind guard1
    guard2) and guard1 looked for an unplaced predecessor; next-in-order
    and cell-position (row/col both $coordinate) had the same exposure.
    Fix: renamed to cell-position>, next-in-order>, after-same-kind>
    (directional). doc/spec-advisor.md 4.4 now notes that fluent
    arguments count toward symmetry.
  2026-10-11 Second placement run (backtracking, 16 threads, EVERY, depth 6)
    completed but recorded 5,040 unique layouts = 8*7*6*5*4*3/4, i.e. no
    clue pruning at all. Cause: the backtracker ignores finally followups
    (translate-assert-bt records followups but no backtracking code
    processes update.followups; only ww-planner.lisp process-followups and
    solution replay do), so check-clues! never ran during search; the
    printed path showed INCONSISTENT-STATE only because the display replays
    followups. Interim fix: check-clues! called directly in the assert.
    Engine since updated (ww-backtracker.lisp apply-choice-followups-bt runs
    next/finally followups after the forward update, inside the undo frame;
    inconsistency is tested on the resulting state), so the spec is back to
    (finally (check-clues!)). Spec $-variable assignments changed from setq
    to assign (user preference, 2026-10-11); doc/spec-advisor.md updated.
