Skip to content

[Feature] Split-portfolio v2: workspace + onboarding move to private repo, plus /update migration #242

@atlas-apex

Description

@atlas-apex

User Story

As a split-portfolio adopter on GitHub Free who values the public/private separation, I want EVERY adopter-specific artefact to live in the private sibling repo — not just the registry + projects/ but ALSO onboarding.yaml (company info / tech stack / team) and workspace/<name>/ (cloned managed projects) — so that my public fork holds only stock framework files + my customisations to skills/hooks/rules. And as an EXISTING split-portfolio adopter, I want /update to migrate my layout for me, not require a hand-edit dance.

Acceptance Criteria

Architecture: extended portfolio config block

  • .claude/project-config.jsonportfolio block accepts two new keys:
    • onboarding — path to onboarding.yaml in the private repo (default ../<fork>-portfolio/onboarding.yaml)
    • workspace_dir — path to the workspace dir in the private repo (default ../<fork>-portfolio/workspace)
  • _lib-portfolio-paths.sh exposes portfolio_onboarding_path and portfolio_workspace_dir helpers
  • All framework code that reads onboarding.yaml or writes to workspace/<name>/ resolves through the helpers (audit the codebase: /setup, /handover, /status, /inbox, /projects, /tasks, onboarding-check.sh, ops-root detection)

Ops-fork detection survives the move

  • _lib-ops-root.sh (and the existing inline walk in 6+ hooks) currently requires BOTH onboarding.yaml AND apexyard.projects.yaml to be present at the candidate dir. After the move, neither file is in the public fork. Walk-up needs a new public-fork anchor (proposal: marker file .apexyard-fork that the framework writes at first /setup)
  • All hooks that walk for ops-root work in both old layout (legacy) AND new layout (post-migration)

Single-fork mode unchanged

  • Single-fork adopters (no portfolio block) see ZERO behaviour change. onboarding.yaml and workspace/ stay in the fork. No migration prompted.

Migration via /update

  • /update detects split-portfolio adopters whose layout is pre-v2 (onboarding.yaml + workspace/ in the public fork)
  • Migration step OFFERS (default-yes) to move both for the operator, with explicit confirmation per file class:
    • Move onboarding.yaml → private repo, gitignore in public fork
    • Move workspace/ contents → private repo, gitignore in public fork
  • After move, write the new portfolio.{onboarding,workspace_dir} keys to .claude/project-config.json AND the .apexyard-fork marker
  • Migration is idempotent — re-running on an already-migrated adopter is a no-op
  • Migration is destructive (moves files), so it follows the framework's destructive-action conventions: explicit y/n per step, dry-run flag (/update --dry-run), backup branch on the public fork before commits

New adopters

  • Fresh /setup --split-portfolio (or whatever the entry-point ends up being) creates the v2 layout from the start, no migration needed
  • docs/multi-project.md § "Split-portfolio mode" updated to describe the v2 layout as the default

Tests

  • _lib-portfolio-paths.sh tests cover the new helpers
  • _lib-ops-root.sh test covers the new .apexyard-fork anchor
  • Migration logic in /update has at least a smoke test (build a synthetic pre-v2 layout in a sandbox, run the migration, assert the new layout)

Design Notes

Why this is a v1.4 ticket, not v1.3. v1.3 has 7 changes already queued. Adding this is real architecture work (touches portfolio paths, gitignore conventions, ops-fork detection, plus a destructive migration). Better to ship v1.3 with what's there and let this land in v1.4 with proper soak time.

The walk-up anchor question. Today's walk requires onboarding.yaml + apexyard.projects.yaml to find the public fork. After the move, both files are in the private repo. New anchor proposal: a presence-only marker file .apexyard-fork written by /setup at first run (or by /update during migration). Walk-up condition becomes: presence of .apexyard-fork (with optional fallback to legacy onboarding.yaml + apexyard.projects.yaml for un-migrated adopters during the transition window).

Symlinks in the public fork are not the answer. A naive "symlink onboarding.yaml from private repo" would mean the public fork's git status shows the symlink, plus the symlink target is OS-dependent. Better: the public fork has neither file; the framework code resolves through portfolio_onboarding_path() from the config block.

workspace/ in private repo is gitignored from the private repo too. Workspace clones have their own remotes and shouldn't be tracked anywhere — the private repo's .gitignore excludes workspace/*/ exactly the way the public fork does today.

Out of Scope

  • Per-project handbooks layered over framework handbooks (separate ticket — v3 of split portfolio if ever needed)
  • Migrating from single-fork to v2 split-portfolio — /split-portfolio skill already exists for that direction; this ticket is about moving v1 split-portfolio adopters to v2
  • Encrypted onboarding.yaml or any other crypto on top of the gitignore (out of scope; gitignore is the boundary)
  • Inside-gitignored layout (one directory holding both repos) — explicitly de-scoped per the conversation that motivated this ticket; sibling layout stays. v2 is purely about WHAT lives in the private repo, not WHERE the private repo sits relative to the public fork.

Effort Estimate

TBD — likely 1-2 days for the architecture + migration, plus tests + docs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — material gap or user-impactingenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions