Skip to content

fix(#102): make nelson-data.py init own mission-directory creation#103

Merged
harrymunro merged 1 commit into
mainfrom
claude/naughty-payne
Apr 16, 2026
Merged

fix(#102): make nelson-data.py init own mission-directory creation#103
harrymunro merged 1 commit into
mainfrom
claude/naughty-payne

Conversation

@harrymunro

Copy link
Copy Markdown
Collaborator

Closes #102.

Problem

SKILL.md told Claude to manually create .nelson/missions/{stamp}_{SESSION_ID}/, the two subdirectories, and the .active-{SESSION_ID} sidecar before running nelson-data.py init. But cmd_init/_do_init created its own timestamped directory (no SESSION_ID) and ignored Claude's.

Claude saw the mismatch and rationally abandoned its own dir ("The script is creating a new timestamped directory rather than using mine. I'll work with the one it created."). Result: the .active-{SESSION_ID} sidecar pointed at an empty orphan directory, SESSION_ID-based recovery broke, and every fresh mission left a stray dir in .nelson/missions/.

Approach

Collapse the two actors into one: give the script full ownership of the mission-directory contract. SKILL.md no longer does any manual mkdir work.

nelson-data.py init now:

  • generates an 8-char hex SESSION_ID (or accepts --session-id for deterministic tests / known-id resumes)
  • creates .nelson/missions/{stamp}_{session_id}/ with damage-reports/ and turnover-briefs/
  • writes sailing-orders.json, mission-log.json, fleet-status.json
  • writes .nelson/.active-{session_id} as the session marker (already consumed by hooks/_find_mission_dir and lifecycle/_find_active_mission)
  • prints the mission-dir path as before — stdout contract unchanged, and session_id is the segment after the last underscore

cmd_headless picks this up for free since it already delegates to _do_init.

Why option 2 over adding --mission-dir to init

  • Single source of truth. Directory layout is declared in one place (code), not two (code + prose). The drift that caused SKILL and nelson-data.py do not seem in sync. #102 becomes structurally impossible.
  • Atomic init. Dir + subdirs + three JSON files + sidecar are created in one transaction. One re-runnable command instead of a three-step handshake across a session boundary.
  • Interactive matches headless. _do_init is already the well-tested path used by cmd_headless; interactive now takes the same route.
  • Less Claude-side brittleness. No more uuidgen | cut -d- -f1, timestamp formatting, or mkdir -p dance in prose. Deterministic Python instead.
  • Smaller future blast radius. Changes to the dir format (e.g. renaming damage-reports/) touch one function.

Changes

Code:

  • nelson_data_utils.py_generate_session_id() (8-hex via secrets.token_hex), _is_valid_session_id() (enforces 8 lowercase hex chars; prevents path injection via the .active-<id> filename)
  • nelson_data_lifecycle.py::_do_init — accepts session_id=None, new dir format, writes sidecar
  • nelson-data.py — adds --session-id optional arg to the init subparser

Tests (all new in test_nelson_data.py::TestInit):

  • test_auto_generates_session_id_in_dirname
  • test_accepts_explicit_session_id
  • test_writes_active_sidecar
  • test_rejects_invalid_session_id

Docs:

  • SKILL.md §"Establish Mission Directory" — removed manual uuidgen/mkdir/sidecar-write instructions
  • references/damage-control/session-hygiene.md — new-session procedure now verifies init ran instead of describing separate creation
  • references/structured-data.mdinit description updated to reflect full ownership

Test plan

  • 222 skills/nelson/scripts/ tests pass (71 existing + new init tests)
  • 52 hooks/ tests pass (mission-dir discovery still works against the new sidecar)
  • scripts/check-references.sh green (no broken doc cross-refs)
  • End-to-end smoke test: nelson-data.py init in a fresh dir produces .nelson/missions/{stamp}_{session_id}/ + .nelson/.active-{session_id} with matching path
  • Reviewer: confirm session-resumption.md prose still reads correctly — its existing "read .nelson/.active-{SESSION_ID}" fallback continues to work unchanged since the script now writes that file

SKILL.md previously told Claude to manually create
.nelson/missions/{stamp}_{SESSION_ID}/, the two subdirectories, and the
.active-{SESSION_ID} sidecar before running nelson-data.py init. But
cmd_init/_do_init created its *own* timestamped directory (no
SESSION_ID) and ignored Claude's. Claude saw the mismatch and abandoned
its own directory, breaking the SESSION_ID linkage and leaving an empty
orphan dir behind.

Collapse the two actors into one. The script now:

- generates an 8-char hex SESSION_ID (or accepts --session-id for
  deterministic tests / known-id resumes)
- creates .nelson/missions/{stamp}_{session_id}/ with damage-reports/
  and turnover-briefs/
- writes sailing-orders.json, mission-log.json, fleet-status.json
- writes .nelson/.active-{session_id} as the session marker (already
  consumed by hooks/_find_mission_dir and lifecycle/_find_active_mission)
- prints the mission dir path as before (relative path, unchanged
  stdout contract — session_id is the segment after the last underscore)

cmd_headless inherits this for free since it delegates to _do_init.

SKILL.md, session-hygiene.md, and structured-data.md updated to defer
to the script. Four new TestInit tests cover auto-generation, explicit
--session-id, sidecar contents, and rejection of invalid session ids
(8 lowercase hex enforced to prevent path injection via the marker
filename). All 274 tests pass (222 scripts + 52 hooks).
@harrymunro harrymunro merged commit 8f1512d into main Apr 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SKILL and nelson-data.py do not seem in sync.

1 participant