|
| 1 | +# Job Porting: Global and Project Scope |
| 2 | + |
| 3 | +Design document for `/deepwork_jobs.port` — a skill that ports DeepWork jobs between project-local and global (`~/.deepwork/`) scope. |
| 4 | + |
| 5 | +## Problem |
| 6 | + |
| 7 | +All DeepWork jobs are project-scoped today. Every path, hook, doc spec, and output reference assumes a project root. A global job needs to work in any project, which means it cannot assume any specific project structure exists. |
| 8 | + |
| 9 | +## Scope Mapping |
| 10 | + |
| 11 | +The global directory structure mirrors the project-local one: |
| 12 | + |
| 13 | +| Scope | Jobs | Doc Specs | Skills | Config | |
| 14 | +|---------|----------------------|---------------------------|---------------------|------------------------| |
| 15 | +| Project | `.deepwork/jobs/` | `.deepwork/doc_specs/` | `.claude/skills/` | `.deepwork/config.yml` | |
| 16 | +| Global | `~/.deepwork/jobs/` | `~/.deepwork/doc_specs/` | `~/.claude/skills/` | `~/.deepwork/config.yml` | |
| 17 | + |
| 18 | +This mirrors the existing convention where `.claude/CLAUDE.md` (project) and `~/.claude/CLAUDE.md` (global) coexist. |
| 19 | + |
| 20 | +## Portability Tensions |
| 21 | + |
| 22 | +### 1. Output Paths |
| 23 | + |
| 24 | +Project-local jobs have concrete output paths: |
| 25 | + |
| 26 | +```yaml |
| 27 | +outputs: |
| 28 | + - competitive_research/competitors_list.md |
| 29 | + - competitive_research/[competitor_name]/research.md |
| 30 | +``` |
| 31 | +
|
| 32 | +These assume the project wants a `competitive_research/` directory. A global job cannot assume that. The recommended approach is to parameterize the output root: |
| 33 | + |
| 34 | +```yaml |
| 35 | +inputs: |
| 36 | + - name: output_dir |
| 37 | + description: "Directory for job outputs" |
| 38 | +outputs: |
| 39 | + - "{output_dir}/competitors_list.md" |
| 40 | + - "{output_dir}/[competitor_name]/research.md" |
| 41 | +``` |
| 42 | + |
| 43 | +This aligns with how inputs already work in `job.yml` and avoids imposing structure on the target project. |
| 44 | + |
| 45 | +### 2. Doc Specs |
| 46 | + |
| 47 | +Doc specs define quality criteria, not project structure, so they are generally portable. The port operation must: |
| 48 | + |
| 49 | +- Copy referenced doc specs from `.deepwork/doc_specs/` to `~/.deepwork/doc_specs/`. |
| 50 | +- Rewrite `doc_spec:` references in `job.yml` from `.deepwork/doc_specs/X.md` to `~/.deepwork/doc_specs/X.md`. |
| 51 | +- Handle name collisions when a global doc spec with the same name already exists. |
| 52 | + |
| 53 | +### 3. Step Instructions |
| 54 | + |
| 55 | +Step instruction files (`steps/*.md`) may contain: |
| 56 | + |
| 57 | +- **Hardcoded project paths** (`src/components/`, `db/migrate/`) — not portable. |
| 58 | +- **References to project tools** ("run `npm test`", "use the Rails console") — not portable. |
| 59 | +- **Generic workflow guidance** ("ask the user for requirements", "validate against doc spec") — fully portable. |
| 60 | + |
| 61 | +The port operation must audit instructions for project-specific references and flag them for the user to resolve. |
| 62 | + |
| 63 | +### 4. Hook Scripts |
| 64 | + |
| 65 | +Hook scripts (`.sh` files in `hooks/`) are the most fragile: |
| 66 | + |
| 67 | +- They may reference project-specific binaries, paths, or environment. |
| 68 | +- Their paths in generated skills resolve as absolute from project root. |
| 69 | +- Global hooks must live at `~/.deepwork/jobs/{name}/hooks/`. |
| 70 | + |
| 71 | +Prompt-based hooks (`prompt:` and `prompt_file:`) are inherently more portable than `script:` hooks. The port operation should warn or block on script hooks that are not obviously portable. |
| 72 | + |
| 73 | +### 5. AGENTS.md |
| 74 | + |
| 75 | +`AGENTS.md` files contain project-specific context by definition (bespoke learnings from `/deepwork_jobs.learn`). When porting to global, the operation should either strip the file entirely or extract only generalizable content and flag the rest for review. |
| 76 | + |
| 77 | +When porting from global to project, the `AGENTS.md` starts empty and accumulates project-specific learnings via `/deepwork_jobs.learn` as normal. |
| 78 | + |
| 79 | +## Precedence and Override Model |
| 80 | + |
| 81 | +Following the pattern established by standard jobs and `CLAUDE.md`: |
| 82 | + |
| 83 | +``` |
| 84 | +Global job (baseline) -> Project-local job (override) |
| 85 | +``` |
| 86 | + |
| 87 | +- If a project has a local job with the same name as a global job, local wins. |
| 88 | +- `deepwork sync` generates skills from both scopes, with local taking precedence. |
| 89 | +- This allows users to install a global job and then customize it per-project. |
| 90 | + |
| 91 | +## The `scope` Field |
| 92 | + |
| 93 | +Rather than retrofitting portability onto existing jobs, add a `scope` field to `job.yml`: |
| 94 | + |
| 95 | +```yaml |
| 96 | +name: code_review |
| 97 | +scope: portable # or "local" (default) |
| 98 | +version: "1.0.0" |
| 99 | +``` |
| 100 | + |
| 101 | +When `scope: portable`: |
| 102 | + |
| 103 | +- `/deepwork_jobs.define` enforces parameterized output paths. |
| 104 | +- `/deepwork_jobs.review_job_spec` adds portability criteria to its validation. |
| 105 | +- `/deepwork_jobs.implement` generates instructions without project-specific references. |
| 106 | +- `/deepwork_jobs.learn` separates generalizable learnings from bespoke ones more aggressively. |
| 107 | + |
| 108 | +This makes portability a design-time decision rather than a post-hoc transformation. |
| 109 | + |
| 110 | +## Port Operations |
| 111 | + |
| 112 | +### Project to Global |
| 113 | + |
| 114 | +1. **Select job** — ask which local job to port. |
| 115 | +2. **Portability audit** — scan for: |
| 116 | + - Hardcoded project paths in step instructions (patterns like `src/`, `app/`, stack-specific file extensions). |
| 117 | + - Script hooks (warn, suggest converting to prompt hooks). |
| 118 | + - `AGENTS.md` content (strip or flag). |
| 119 | + - Output paths without parameterization. |
| 120 | +3. **Transform** — |
| 121 | + - Rewrite output paths to use `{output_dir}` parameter or ask the user for a strategy. |
| 122 | + - Add `output_dir` as an input to the first step if not already present. |
| 123 | + - Rewrite `doc_spec:` paths from `.deepwork/` to `~/.deepwork/`. |
| 124 | + - Remove or generalize `AGENTS.md`. |
| 125 | +4. **Copy** — write to `~/.deepwork/jobs/{name}/` and `~/.deepwork/doc_specs/`. |
| 126 | +5. **Sync global** — run `deepwork sync --global` to generate skills in `~/.claude/skills/`. |
| 127 | + |
| 128 | +### Global to Project |
| 129 | + |
| 130 | +1. **Select job** — list available global jobs. |
| 131 | +2. **Copy** — inject from `~/.deepwork/jobs/{name}/` to `.deepwork/jobs/{name}/`. |
| 132 | +3. **Localize** — rewrite `doc_spec:` paths to `.deepwork/doc_specs/`, copy doc specs into the project. |
| 133 | +4. **Optionally concretize** — ask if the user wants to replace `{output_dir}` with a concrete project path. |
| 134 | +5. **Sync** — run normal `deepwork sync`. |
| 135 | + |
| 136 | +## Infrastructure Changes |
| 137 | + |
| 138 | +The port skill depends on changes to the DeepWork CLI and sync pipeline: |
| 139 | + |
| 140 | +1. **`~/.deepwork/` directory convention** — the CLI must recognize this as the global job store. |
| 141 | +2. **`deepwork sync --global`** — sync must be scope-aware, generating skills to `~/.claude/skills/` from `~/.deepwork/jobs/`. |
| 142 | +3. **Precedence in sync** — when both global and local jobs exist with the same name, local wins. The merged set gets synced. |
| 143 | +4. **`deepwork install` awareness** — optionally inject global user jobs into projects, or let sync handle the merge at skill generation time. |
0 commit comments