# How book-to-skill Manages Its Per-Run Work Directory

> Learn how book-to-skill manages its per-run work directory using isolated folders and environment variables. Discover the flexibility for custom workflows.

- Repository: [Virgilio Junior/book-to-skill](https://github.com/virgiliojr94/book-to-skill)
- Tags: internals
- Published: 2026-09-01

---

**`book-to-skill` creates a unique, isolated work directory for every extraction using the current process ID, stores it under the system temp folder, and allows full override via the `BOOK_SKILL_WORKDIR` environment variable.**

The `virgiliojr94/book-to-skill` repository implements a **per-run work directory** strategy that keeps each book extraction self-contained. This design prevents concurrent runs from stepping on each other's files, avoids legacy cleanup hazards, and gives operators full control when needed.

---

## Location and Default Path Generation

The default work directory lives in the system temporary folder, but with a critical safeguard: it sits as a **sibling** to the legacy fixed path, not a child.

In [`book_to_skill/config.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/config.py), the `default_output_dir()` function constructs this path:

```python
def default_output_dir() -> Path:
    """Per-run work directory, unique to this process.
    ... (see full docstring) ...
    """
    return Path(tempfile.gettempdir()) / f"book_skill_work-{os.getpid()}"

```

This placement—`/tmp/book_skill_work-{PID}` rather than `/tmp/book_skill_work/{PID}`—ensures that old cleanup scripts targeting `$TMPDIR/book_skill_work` cannot accidentally delete an active extraction's data.

---

## Process Isolation via PID-Based Naming

**Uniqueness is guaranteed by embedding `os.getpid()` in the directory name.** Two extractions running simultaneously receive distinct paths, eliminating race conditions and file corruption.

The computed paths are exported as module-level constants:

```python
OUTPUT_DIR = Path(os.environ.get("BOOK_SKILL_WORKDIR") or default_output_dir())
OUTPUT_TEXT = OUTPUT_DIR / "full_text.txt"
OUTPUT_META = OUTPUT_DIR / "metadata.json"

```

These resolve at import time. Any code importing from `book_to_skill.config` sees a stable, process-scoped workspace for the full extraction lifecycle.

---

## Environment Variable Override

Operators can bypass automatic path generation entirely using **`BOOK_SKILL_WORKDIR`**.

| Scenario | Behavior |
|----------|----------|
| Variable unset | Uses `default_output_dir()` with PID-based temp path |
| Variable set to valid path | Uses that path exactly |
| Variable set to empty string | **Still falls back** to PID-based temp path (empty string is not interpreted as current directory) |

Override example:

```bash
export BOOK_SKILL_WORKDIR=/my/custom/workdir
python -m book_to_skill path/to/book.pdf

```

```python
from book_to_skill.config import OUTPUT_DIR
assert OUTPUT_DIR == Path("/my/custom/workdir")

```

---

## Practical Usage Examples

### Accessing the default work directory

```python
from book_to_skill.config import OUTPUT_DIR, OUTPUT_TEXT, OUTPUT_META

print("Work directory:", OUTPUT_DIR)          # e.g. /tmp/book_skill_work-12345

print("Full-text file:", OUTPUT_TEXT)         # /tmp/book_skill_work-12345/full_text.txt

print("Metadata file:", OUTPUT_META)          # /tmp/.../metadata.json

```

### Verifying uniqueness across forked workers

```python
import multiprocessing, os
from book_to_skill.config import default_output_dir

def worker(_):
    print(f"PID {os.getpid()} → {default_output_dir()}")

with multiprocessing.Pool(2) as pool:
    pool.map(worker, range(2))

# Output shows two distinct temp dirs, each ending with the worker's PID

```

---

## Testing and Validation

The test suite in [`tests/test_per_run_workdir.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tests/test_per_run_workdir.py) enforces four critical guarantees:

- **Uniqueness**: Default directories differ between calls in the same process (PID-based).
- **Sibling placement**: The directory is not a child of the legacy path.
- **Temp containment**: The directory resides inside the system temp folder.
- **Override integrity**: Explicit `BOOK_SKILL_WORKDIR` wins, and empty values do not collapse to CWD.

These tests ensure the per-run work directory behaves correctly across platforms and deployment scenarios.

---

## Where the Work Directory Is Consumed

| File | Role |
|------|------|
| [`book_to_skill/config.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/config.py) | Defines `default_output_dir()`, reads `BOOK_SKILL_WORKDIR`, exports `OUTPUT_DIR`, `OUTPUT_TEXT`, `OUTPUT_META` |
| [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) | References `OUTPUT_DIR` for temporary file handling and token estimation (via `"workdir": str(OUTPUT_DIR)` in utility dictionaries) |
| [`tests/test_per_run_workdir.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tests/test_per_run_workdir.py) | Validates uniqueness, placement, and override behavior |

---

## Summary

- **PID-scoped paths** (`book_skill_work-{PID}`) guarantee isolation between concurrent extractions.
- **Sibling placement** to the legacy path prevents accidental deletion by stale cleanup scripts.
- **`BOOK_SKILL_WORKDIR`** provides full operator override without misinterpreting empty strings.
- **Computed once at import**, the work directory remains stable throughout the extraction process.

---

## Frequently Asked Questions

### How does book-to-skill prevent two runs from using the same directory?

It embeds the current process ID (`os.getpid()`) in the directory name via `default_output_dir()`. Since operating systems guarantee unique PIDs for concurrent processes, no two active runs can collide.

### Can I specify a custom work directory instead of the temp folder?

Yes. Set the `BOOK_SKILL_WORKDIR` environment variable to any absolute path. The code checks this variable first and uses it directly, bypassing the PID-based default entirely.

### What happens if BOOK_SKILL_WORKDIR is set to an empty string?

The code treats an empty string as unset and falls back to the PID-based temporary path. This prevents accidental use of the current working directory, which could pollute your project files.

### Where are the extracted full text and metadata files stored?

Inside the per-run work directory as [`full_text.txt`](https://github.com/virgiliojr94/book-to-skill/blob/main/full_text.txt) and [`metadata.json`](https://github.com/virgiliojr94/book-to-skill/blob/main/metadata.json). These paths are pre-computed as `OUTPUT_TEXT` and `OUTPUT_META` in [`book_to_skill/config.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/config.py) and imported wherever the files are written or read.