Factory Droid skill discovery is broken for gstack installs
- Dominant language
- TypeScript
- Stars
- 133k
- Forks
- 19.9k
- Avg merge
- 18h 46m
- Merged PRs (30d)
- 26
Description
## Problem
Skills installed via `cd ~/gstack && ./setup --host factory` are not reliably discoverable by Factory Droid. The generated skill files exist on disk, but Droid cannot discover or load them. As a result, gstack skills do not appear in the `/` menu.
## Reproduction
```bash
cd ~/gstack && ./setup --host factory
# Then restart Droid and type /
# Expected: gstack skills (qa, review, ship, etc.) appear
# Actual: no gstack skills visible
```
## Root Cause
The installation produces three incompatibilities with Droid's discovery model:
### 1. Wrong symlink type
Droid follows only **relative** symlinks in `~/.factory/skills/`. The current setup creates **absolute** symlinks:
```bash
ln -snf /Users/will/gstack/.factory/skills/gstack-qa ~/.factory/skills/gstack-qa
```
Absolute symlinks are ignored by Droid.
### 2. Skills not in Droid's read directory
Droid reads skill files from `~/.agents/skills/`. The current setup installs skills to `~/gstack/.factory/skills/` (a project subdirectory) and creates symlinks from `~/.factory/skills/` pointing back into the repo. Even if the symlinks were correct, Droid would need skills to exist as real directories in `~/.agents/skills/`.
### 3. Wrong `sourceType` in lockfile
The lockfile registration uses `sourceType: "local"`:
```json
{
"sourceType": "local",
"sourceUrl": "file:///Users/will/gstack/.factory/skills/gstack-qa"
}
```
Droid's lockfile reader **completely ignores** entries with `sourceType: "local"`. Every working skill (57 total in the registry) uses `sourceType: "github"`.
## How Droid Discovers Skills
Droid uses a two-tier discovery system:
- `~/.factory/skills/` — Droid scans here, follows relative symlinks
- `~/.agents/skills/` — Droid reads skill files from here
- `~/.agents/.skill-lock.json` — Droid's skill registry (only sourceType=github loaded)
A skill appears in `/` when **both**:
1. A relative symlink exists in `~/.factory/skills/` pointing to `~/.agents/skills/{skill}`
2. The lockfile entry has `sourceType: "github"`
## Proposed Fix
The `link_factory_skill_dirs()` function needs to:
1. **Copy** skills as real directories into `~/.agents/skills/` (not symlink from repo)
2. **Create relative symlinks** in `~/.factory/skills/` pointing to `../../.agents/skills/{skill}`
3. **Register** in `~/.agents/.skill-lock.json` with `sourceType: "github"`
Note: On a fresh profile (no existing `~/.agents/.skill-lock.json`), the lockfile registration would be skipped unless the existing lockfile check is made conditional or the lockfile is created.
## Verification
After a fix is applied, these commands should show the correct state:
```bash
# Relative symlinks exist
ls -l ~/.factory/skills/ | grep gstack
# Real directories exist
ls -l ~/.agents/skills/ | grep gstack
# Lockfile entries use sourceType=github
python3 -c "
import json
with open('$HOME/.agents/.skill-lock.json') as f:
lock = json.load(f)
for name, entry in lock['skills'].items():
if name.startswith('gstack'):
print(f'{name}: sourceType={entry[\"sourceType\"]}')"
```
## Skill Name
After fixing, skills are invoked by their `name:` field in `SKILL.md`, not the directory name:
| Directory | name field | Invoked as |
|-----------|-----------|------------|
| gstack-qa | qa | /qa |
| gstack-review | review | /review |
## References
- Droid skill discovery: `~/.factory/skills/` (relative symlinks only)
- Droid skill files: `~/.agents/skills/` (real directories)
- Droid registry: `~/.agents/.skill-lock.json` (sourceType=github required)
Contributor guide
Assessment
This issue has not been assessed yet.