# Plugin Layout & Architecture

**Maintainer notes for the `stackone-agent-harness` plugin.** Captures the directory
layout and placement conventions.

## Aim

A gated three-phase connector flow — **Research → Build → Validate** — that runs
unattended. Phase completion is decided by deterministic gate scripts, and all durable
run state lives in `.connector-dev/<provider>/run.json`, written only by
`scripts/state/run.js`. There are no phases 4–8, no action-testing subsystem, and no
webhook modules in this plugin.

## Plugin Directory Layout

```
plugin/
├── .claude-plugin/
│   └── plugin.json                  # Manifest: name, version, skill list
├── .mcp.json                        # Playwright MCP (auto-loaded)
├── README.md                        # Plugin overview + quickstart
├── CHANGELOG.md                     # Version history
├── connector-guide.md               # Connector authoring guide (synced into projects)
├── package.json                     # No dependencies — js-yaml is vendored in scripts/lib/vendor/
├── commands/                        # Flat, auto-discovered slash commands (5)
│   ├── connector-full.md
│   ├── connector-research.md
│   ├── connector-build.md
│   ├── connector-validate.md
│   └── connector-status.md
├── agents/                          # Subagents, grouped by phase (4)
│   ├── research/                    # resource-endpoint-fetcher, use-case-discoverer
│   └── validation/                  # provider-comparator, schema-reviewer
├── skills/                          # 10 SKILL.md files — FLAT, one directory deep. See note below.
│   ├── research-provider/           # Phase 1 orchestration
│   │   └── references/              # investigation technique (auth, endpoints, use cases…)
│   ├── build-connector/             # Phase 2 orchestration
│   │   └── references/              # action authoring + auth-flow variants
│   ├── validate-config/             # Phase 3 orchestration
│   │   └── references/              # provider-schema-comparison, validation-rules
│   ├── connector-patterns/          # corpus-mined shapes + naming conventions references
│   └── …                            # discover-actions, improve-descriptions, test-connector, etc.
├── hooks/
│   └── hooks.json                   # SessionStart/PostToolUse/Stop/PreCompact (never PreToolUse)
├── interactive/                     # Human-in-session commands — parked, not yet wired
│   ├── commands/                    # connector-onboarding, connector-test
│   └── skills/
├── scripts/                         # Deterministic helpers (Node, no jq dependency)
│   ├── README.md                    # Script catalog
│   ├── gates/                       # the five gates + runner.js + connector.js loader
│   ├── state/                       # run.js, scope.js, domain-catalog.json
│   ├── research/                    # Phase 1 helper scripts (~15)
│   └── lib/                         # safe-path.js, yaml.js (+ vendor/js-yaml.cjs)
└── docs/
    ├── README.md                    # Doc index
    ├── PATHS.md                     # Canonical path conventions
    ├── phases/                      # README + 1-research / 2-build / 3-validation
    └── maintenance/
        └── plugin-layout.md         # This file
```

## Naming conventions

- kebab-case file and folder names; snake_case JSON keys; lowercase status enums
  (`pending`, `complete`, `exhausted`).
- Commands are flat files named `connector-<verb>.md`, exposing `/connector:<verb>`.
- Gate check ids are prefixed by gate: R (research), A (auth), B (build),
  N (conventions), V (validate).

## Notes on placement

- **skills/ must stay flat.** The loader (`src/plugins/skill-loader.ts`) does one
  `readdirSync` of `skills/` and keeps entries that have a `<name>/SKILL.md`. A skill nested
  any deeper is invisible to it, and since sync copies `skills/<name>/**` recursively, it
  never reaches a sandbox either — so it is not merely unregistered, it is absent. Twenty-five
  skills once lived under `skills/build/`, `skills/research/` and `skills/validation/` and
  were unreachable on both counts for their whole life.

  So there are two options for new material, and only two:

  | Should the model be able to invoke it? | Put it |
  |---|---|
  | Yes | `skills/<name>/SKILL.md` — one level, with `name` and `description` frontmatter |
  | No — it is technique consulted mid-phase | `skills/<phase-skill>/references/<name>.md`, read by path |

  Prefer the second. A top-level skill is listed in every session whether or not it is used;
  a reference doc costs nothing until something reads it.

- **scripts/** — executable helpers invoked via `${CLAUDE_PLUGIN_ROOT}/scripts/…`.
  Plain Node throughout, zero npm dependencies — `js-yaml` is vendored under
  `scripts/lib/vendor/` and required through `scripts/lib/yaml.js`. When one resolves a path into
  `skills/`, it builds it from segments (`path.join(PLUGIN_ROOT, 'skills', …)`), which a
  text search for a slash-separated path will not find — check these by running them.
- **interactive/** — commands that require a human in the session are deliberately kept
  out of `commands/` so unattended runs don't pay their always-on context cost. The CLI
  does not sync this directory yet; see `interactive/README.md`.
- **No tests/ at plugin root** — gate behaviour is exercised by the repo-level
  `test/gates/` suite outside the plugin directory.

## Runtime Layout (`.connector-dev/`)

Per-provider state, written during a run:

```
.connector-dev/
└── <provider>/
    ├── run.json                     # THE run record — phases, scope, gap ledger, outcome
    │                                # written only by scripts/state/run.js
    ├── research/                    # Phase 1 output (see docs/phases/1-research.md)
    │   ├── api_coverage.json
    │   ├── <provider>_research.json # the artifact the research gate validates
    │   ├── auth/ · endpoints/ · use_cases/ · metadata/
    │   └── _index.md
    ├── build/
    │   └── auth-evidence.json       # provider auth-doc citations, required by A10
    └── validation/
        ├── <provider>_<resource>_doc_comparison.json   # read by validate.js (V2…)
        └── shards/                  # per-action provider-comparison evidence
```

Cross-phase state is **not** derived from filesystem presence — it is read from
`run.json`. A phase counts as complete only when its gate exited 0 and `run.js
set-phase` recorded it.
