# Plugin Path Conventions

The single source of truth for every path this plugin reads, writes, or references. When docs, SKILLs, commands, agents, or scripts drift from these conventions, fix them here first — then update the drifted files to match.

## The two path classes

**Static, plugin-managed** — same for every provider, every user, every run. Documented in the tables below; never variable in state. If one changes, this file changes.

**Runtime, user-provided** — depends on which provider is being built, where the user placed their connector file, which profile they chose. Persisted per-provider in state files (see [Runtime state files](#runtime-state-files)); consumers read from state, never hardcode.

---

## Canonical directories

| Purpose | Canonical path | Notes |
|---|---|---|
| Plugin state root (per-provider) | `.connector-dev/<provider>/` | **Hidden-folder convention** (like `.git/`, `.claude/`). Runtime scripts also read from `connector-dev/` (no dot) as a fallback for existing pre-migration state on disk. |
| Connection state | `.connector-dev/<provider>/connection/connection.yaml` | Written by `deploy-and-connect`. Load-bearing — many scripts and agents read it. Keep this exact path and filename stable. |
| Build state (pre-deploy) | `.connector-dev/<provider>/build-state.yaml` | Written at start of `/connector:build`. Captures `profile` and `connector_path` before `connection.yaml` exists. |
| Run state (hook contract) | `.connector-dev/<provider>/run.json` | Written **only** by `scripts/state/run.js` — completion stays computed rather than asserted. Nothing enforces this at the tool layer: a hook that refuses a write would also be able to stall an unattended run, so the rule lives in the phase instructions instead. Read by `hooks/hooks.json` SessionStart/Stop/PreCompact hooks and by every gate. Keep this exact path, filename, and the current top-level keys (`provider`, `request`, `targetPhase`, `scope`, `phases`, `gaps`, `outcome`) stable. `targetPhase` is what lets the Stop hook tell a finished research-only run from a full build that stopped after research. |
| Research output | `.connector-dev/<provider>/research/…` | Written by Phase 1 skills (`connector-research`). |
| Auth evidence (Phase 2) | `.connector-dev/<provider>/build/auth-evidence.json` | Citations of the provider's auth documentation, per mechanism. Required by gate check A10. |
| Validation artifacts (Phase 3) | `.connector-dev/<provider>/validation/…` | Per-resource doc-comparison JSON (read by `scripts/gates/validate.js`) plus provider-comparison shards. |
| Monorepo connector directory (default) | `src/configs/<provider>/` | The internal StackOne `connectors` monorepo layout. Not required by the CLI — it accepts any path. |
| Pulled connector directory (default) | `./connectors/<provider>/` | Where `stackone pull` writes by default. Overridable with `-o <path>`. |

The state root name `.connector-dev/` is spelled literally everywhere. There is no runtime substitution mechanism — SKILL prose and Bash blocks both use the literal path. If the canonical ever needs to change, update this file and re-sweep the codebase (`grep -rn "connector-dev" ...`).

---

## The `connector_path` rule

`connector_path` holds a **filesystem path** — either a folder (preferred) or a `.s1.yaml` file. Both forms are accepted by `stackone validate` and `stackone push`.

- **Folder form** (preferred): a directory containing one or more `<name>.s1.yaml` files. Works across every source: monorepo (`src/configs/<provider>/`), `stackone pull` output (`./connectors/<provider>/`), and any user-chosen custom layout. The CLI resolves the filename inside the directory itself, so the path stays valid regardless of naming convention.
- **File form**: a direct path to a `.s1.yaml` file. Use when a directory holds multiple unrelated connectors and one specific file must be selected, or when the user's layout places the YAML somewhere non-standard.

**Rule for SKILL / command / agent authors:**

> Read `connector_path` from the state file (see [Runtime state files](#runtime-state-files)) and pass it to the CLI as a filesystem path.

Correct invocations:

- ✅ `stackone push "$CONNECTOR_PATH" --profile "$PROFILE"` — folder or file, whichever is in state
- ✅ `stackone validate "$CONNECTOR_PATH"` — same
- ✅ `stackone run --connector "$CONNECTOR_PATH" --account-id "$ACCOUNT_ID" --action-id "$ACTION" --profile "$PROFILE"` — `run` accepts a file path or a provider slug (`<provider>@<version>`)

---

## Filename conventions per source

Different tools produce differently-named `.s1.yaml` files. The `connector_path` rule is designed to sidestep this — you always target the directory, never the filename. For reference:

| Source | File pattern |
|---|---|
| Monorepo hand-authored | `<provider>.connector.s1.yaml` |
| Monorepo partial files | `<provider>.<resource>.s1.partial.yaml` |
| `stackone pull` output | `<provider>_v<version>.s1.yaml` (e.g. `arxiv_v0-1-0.s1.yaml`) |
| Any custom layout | Whatever the user names it — CLI does not care as long as it ends `.s1.yaml` |

If a SKILL needs to detect the specific file inside a directory (rare — usually the CLI handles this), glob `${connector_path}/*.s1.yaml` and take the newest by mtime, or fail cleanly if there are multiple with no way to choose.

---

## Runtime state files

Two files hold per-provider runtime state. Consumers read from them; never hardcode a value that lives in state.

### `.connector-dev/<provider>/build-state.yaml` (pre-deploy)

Written by `/connector:build` at Phase 2 start (before any connector YAML exists). Read by pre-deploy SKILLs (`build-connector-config`, `build-actions`, `configure-action`, `connector-version-bump`) that need `profile` and `connector_path` before `connection.yaml` exists.

```yaml
build:
  provider: <provider>
  profile: <profile_label>          # a StackOne CLI profile label
  connector_path: <folder-or-file>  # default: src/configs/<provider>/
  started_at: <iso_timestamp>
```

### `.connector-dev/<provider>/connection/connection.yaml` (post-deploy)

Written by `deploy-and-connect` after `stackone push` succeeds. Extends the pre-existing schema with `connector_path` (copied from `build-state.yaml`). Read by every post-deploy SKILL, agent, and runtime script.

```yaml
connection:
  provider: <provider>
  profile: <profile_label>
  connector_path: <folder-or-file>   # copied from build-state.yaml at deploy time
  connected_at: <iso_timestamp>

accounts:
  - account_id: <id>
    auth_type: <auth_type>
    account_name: <optional-name>
```

**Fallback rules for consumers:**

1. Prefer `connection.yaml.connector_path` when it exists (post-deploy skills).
2. Fall back to `build-state.yaml.connector_path` if `connection.yaml` doesn't exist yet (pre-deploy skills).
3. Fall back to `src/configs/<provider>/` if neither state file exists yet (very first run of `/connector:build` before build-state is written).

### Connector-path resolution snippet (copy this into any SKILL that needs to invoke `stackone validate|push|run` on the connector YAML)

```bash
# Resolve $CONNECTOR_PATH once at the top of your Bash block; use "$CONNECTOR_PATH" thereafter.
# Precedence: connection.yaml (post-deploy) → build-state.yaml (pre-deploy) → monorepo default.
PROVIDER="{{provider}}"
CONNECTOR_PATH=$(yq -r '.connection.connector_path // ""' ".connector-dev/${PROVIDER}/connection/connection.yaml" 2>/dev/null)
[ -z "$CONNECTOR_PATH" ] && CONNECTOR_PATH=$(yq -r '.build.connector_path // ""' ".connector-dev/${PROVIDER}/build-state.yaml" 2>/dev/null)
[ -z "$CONNECTOR_PATH" ] && CONNECTOR_PATH="src/configs/${PROVIDER}/"
```

Then in the same block:

```bash
stackone validate "$CONNECTOR_PATH"
stackone push "$CONNECTOR_PATH" --profile "$PROFILE" --force
stackone run --connector "$CONNECTOR_PATH" --account-id "$ACCOUNT_ID" --action-id "$ACTION" --params "$PARAMS" --profile "$PROFILE"
```

Rationale: one state read per Bash block, one variable used everywhere. Works for the monorepo default and any user-specified layout.

---

## Static file references

These paths are hardcoded and do not vary. Reference them directly:

| File | Purpose |
|---|---|
| `docs/PATHS.md` | This file |
| `hooks/hooks.json` | Plugin hooks — SessionStart/PostToolUse/Stop/PreCompact. No PreToolUse: nothing may refuse a tool call. |
| `scripts/gates/research.js` | Research gate (R1–R11) |
| `scripts/gates/auth.js` | Auth gate (A1–A12), sub-gate of Build |
| `scripts/gates/build.js` | Build gate (B1–B16) |
| `scripts/gates/conventions.js` | Conventions gate (N1–N20) |
| `scripts/gates/validate.js` | Validate gate (V1–V8) |
| `scripts/gates/phase.js` | Runs every gate a phase owns in one invocation |
| `scripts/state/run.js` | Run state CLI — the only writer of `run.json` |
| `scripts/state/narrate.js` | Customer-facing progress lines, computed from the artifacts |
| `scripts/state/scope.js` | Scope contract resolution |

---

## Related docs

- `docs/phases/` — per-phase inputs, outputs, and exit conditions
- `scripts/README.md` — how the gate and state scripts consume state files
