1# Dashboard Source Management23## Purpose45The dashboard plugin SHALL let maintainers register, inspect, initialize, update, and remove OpenSpec-capable repositories from the Hermes dashboard. Source management must work even before a repository has been initialized with an `openspec/` directory.6## Requirements7### Requirement: Source listing API8The dashboard API SHALL list registered sources with live validity, repository, and OpenSpec scan information.910#### Scenario: Valid initialized source11- **GIVEN** a registered source path resolves to a repository with an `openspec/` directory12- **WHEN** the dashboard requests the source list13- **THEN** the API returns the source as valid with repo root, OpenSpec path, scanned changes, scanned ideas, scanned specs, and status counts1415#### Scenario: Existing but uninitialized source16- **GIVEN** a registered source path resolves to an existing repository without an `openspec/` directory17- **WHEN** the dashboard requests the source list18- **THEN** the API returns the source as invalid with the repo root and a `No openspec/ directory found` error1920#### Scenario: Missing path21- **GIVEN** a registered source path no longer exists22- **WHEN** the dashboard requests the source list23- **THEN** the API returns the source as invalid with an explanatory error instead of dropping the source2425### Requirement: Source path resolution26The dashboard API SHALL normalize user-provided paths and derive the repository root when possible.2728#### Scenario: Path inside a git repository29- **GIVEN** a user submits a path inside a git repository30- **WHEN** the API registers or updates a source31- **THEN** it resolves the repository root for validation and payload metadata3233#### Scenario: Non-git directory34- **GIVEN** a user submits an existing directory that is not inside a git repository35- **WHEN** the API registers or updates a source36- **THEN** it accepts that directory as the source root3738#### Scenario: Invalid path input39- **WHEN** a user submits an empty path, null-byte path, unparseable path, or non-existent path40- **THEN** the API rejects the request with a 400-level error and does not modify the registry4142### Requirement: Source CRUD operations43The dashboard API SHALL expose create, update, and delete operations for registry sources.4445#### Scenario: Register source46- **WHEN** the dashboard submits a valid source path and optional display name47- **THEN** the API stores the source and returns the source payload with live validity metadata4849#### Scenario: Update source50- **GIVEN** a registered source exists51- **WHEN** the dashboard submits a new valid path or name for that source52- **THEN** the API updates the registry and returns the refreshed source payload5354#### Scenario: Duplicate path conflict55- **GIVEN** another source already uses a submitted path56- **WHEN** the dashboard creates or updates a source with that path57- **THEN** the API returns a conflict response and preserves the existing registry state5859#### Scenario: Remove source60- **GIVEN** a registered source exists61- **WHEN** the dashboard deletes the source62- **THEN** the API removes it from the registry and subsequent source lists omit it6364### Requirement: Source initialization65The dashboard API SHALL initialize a registered source that exists but lacks an `openspec/` directory and SHALL normalize the resulting OpenSpec layout to include all directory roots used by the plugin.6667#### Scenario: OpenSpec CLI available68- **GIVEN** the OpenSpec CLI binary is available69- **WHEN** the dashboard initializes an uninitialized source70- **THEN** the API runs `openspec init <repo-root> --tools none`, ensures `openspec/changes`, `openspec/changes/archive`, `openspec/specs`, and `openspec/ideas` all exist, and returns the refreshed source payload7172#### Scenario: OpenSpec CLI unavailable73- **GIVEN** the OpenSpec CLI binary is unavailable74- **WHEN** the dashboard initializes an uninitialized source75- **THEN** the API creates `openspec/changes`, `openspec/changes/archive`, `openspec/specs`, and `openspec/ideas` and returns the refreshed source payload7677#### Scenario: Already initialized source78- **GIVEN** the source already has an `openspec/` directory79- **WHEN** the dashboard initializes the source80- **THEN** the API returns success with an `Already initialized` message and does not modify existing artifacts8182#### Scenario: CLI-listable initialized source83- **GIVEN** a source has been initialized through the dashboard API84- **WHEN** an agent or maintainer runs OpenSpec list commands in that source85- **THEN** the required OpenSpec directories exist so list operations are not blocked by a missing `changes` root8687### Requirement: Dashboard source controls88The dashboard UI SHALL present compact controls for selecting and managing registered sources.8990#### Scenario: Source selection91- **GIVEN** one or more registered sources exist92- **WHEN** the dashboard renders the OpenSpec tab93- **THEN** the user can select the active source from a top-level control and see invalid sources labelled as invalid9495#### Scenario: Source actions96- **WHEN** the user opens source management controls97- **THEN** the user can refresh sources, add a source, edit the active source, remove the active source, and initialize an uninitialized active source when applicable9899#### Scenario: Empty source list100- **GIVEN** no sources are registered101- **WHEN** the dashboard renders the OpenSpec tab102- **THEN** it shows an empty-state path to add a source instead of rendering a broken board103