69 lines
3.2 KiB
Markdown
69 lines
3.2 KiB
Markdown
|
|
# Discovery Connectors
|
||
|
|
|
||
|
|
**Status:** draft
|
||
|
|
**Updated:** 2026-06-27
|
||
|
|
**Repo:** config-atlas
|
||
|
|
**Workplan:** ATLAS-WP-0003 (Phase 2)
|
||
|
|
**Related:** [`../specs/ArchitectureBlueprint.md`](../specs/ArchitectureBlueprint.md) §4/§6,
|
||
|
|
[`ecosystem-boundaries.md`](ecosystem-boundaries.md) §2.4,
|
||
|
|
[`configuration-surface-schema.md`](configuration-surface-schema.md)
|
||
|
|
|
||
|
|
Connectors grow the surface registry from **automated discovery** instead of only
|
||
|
|
hand authoring — reusing `repo-scoping`'s scanner → candidate → approval model
|
||
|
|
rather than building bespoke approval machinery.
|
||
|
|
|
||
|
|
## Contract
|
||
|
|
|
||
|
|
A connector **MUST**:
|
||
|
|
|
||
|
|
- be **read-only and stateless** — never write a source system, never auto-merge;
|
||
|
|
- **never read or store configuration values or secret values** — record file
|
||
|
|
*locations* and key/flag *identities* only;
|
||
|
|
- emit **candidate** surface entries (`status: candidate`) with provenance in
|
||
|
|
`evidence.discovery_method = connector:<name>`;
|
||
|
|
- produce **schema-valid** entries (validated by `connector_base.emit_candidate`);
|
||
|
|
- **never overwrite a promoted entry** — if an id already exists under
|
||
|
|
`registry/surfaces/*.md`, the candidate is skipped (the registry is truth).
|
||
|
|
|
||
|
|
Connectors **propose**; humans/agents **dispose**.
|
||
|
|
|
||
|
|
## Candidate lifecycle
|
||
|
|
|
||
|
|
```text
|
||
|
|
connector -> registry/surfaces/candidates/<id>.md (status: candidate)
|
||
|
|
-> PR review (human or trusted agent)
|
||
|
|
-> promote: move to registry/surfaces/<id>.md (status: draft|active),
|
||
|
|
add row to registry/indexes/surfaces.yaml, set a real owner
|
||
|
|
-> or reject: delete the candidate
|
||
|
|
```
|
||
|
|
|
||
|
|
Candidates live in `registry/surfaces/candidates/` and are **excluded** from the
|
||
|
|
promoted-entry validation/index gate (`tools/validate_registry.py` globs
|
||
|
|
`registry/surfaces/*.md` non-recursively), so unreviewed candidates never become
|
||
|
|
registry truth or break the index. The candidates directory is **gitignored** —
|
||
|
|
candidates are ephemeral connector output; **promotion** moves an entry into the
|
||
|
|
tracked `registry/surfaces/` and `surfaces.yaml`.
|
||
|
|
|
||
|
|
## Connectors
|
||
|
|
|
||
|
|
| Connector | Source | Emits | Command |
|
||
|
|
|-----------|--------|-------|---------|
|
||
|
|
| `git-config` | repo config files (`*.env.example`, `values*.yaml`, `config*.yaml`); real `.env` → secret-ref | app/deploy/secret-ref candidates | `make connect-gitconfig REPO=<slug>` |
|
||
|
|
| `repo-scoping` | repo-scoping observed facts (`--facts` file or `REPO_SCOPING_URL`) | app-config candidates | `make connect-reposcoping REPO=<slug>` |
|
||
|
|
| `feature-control` | feature-control key registry (`--keys` or its index) | feature-flag candidates (link only) | `make connect-featurecontrol` |
|
||
|
|
|
||
|
|
All degrade gracefully (emit nothing) when their source is unavailable.
|
||
|
|
|
||
|
|
## Health
|
||
|
|
|
||
|
|
`make registry-health` (`tools/registry_health.py`) reports **unowned** surfaces
|
||
|
|
(missing owner, or owner not resolvable to a known identity) and **stale** surfaces
|
||
|
|
(`evidence.last_seen` older than the threshold). Ownership resolution currently
|
||
|
|
uses the reuse-surface roster as a stand-in until `domain-tree` binding is wired.
|
||
|
|
|
||
|
|
## Boundaries
|
||
|
|
|
||
|
|
Connectors reuse `repo-scoping`'s discovery model and never duplicate it; the
|
||
|
|
`feature-control` connector **links** to feature-control keys and never re-derives
|
||
|
|
evaluation logic (PRD FR-12). See [`ecosystem-boundaries.md`](ecosystem-boundaries.md).
|