# Data flow — github/spec-kit

> Canonical page: https://codevetter.com/unpack/spec-kit/data-flow

# Data flow — github/spec-kit

Scanned commit 175adbcd826befdccce8c60faa07ae3d8bbc8d87 (2026-09-22). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Inputs enter through Typer commands or content manifests (extension.yml, preset.yml, workflow.yml, bundle.yml, catalog JSON). `specify init` resolves an integration from `INTEGRATION_REGISTRY`, writes `.specify/init-options.json`, renders command templates with per-agent placeholders into the agent's config directory, and copies shared scripts/templates into `.specify/` — every written file hash-recorded in an `IntegrationManifest`. Extensions and presets layer on top via `CommandRegistrar` rendering and a priority-resolved template stack; hooks land in `.specify/extensions.yml`. Workflow runs persist resumable state under `.specify/workflows/runs/<run_id>/`. Outbound data is confined to HTTPS downloads with byte caps and optional SHA-256 pins, and subprocess calls to agent CLIs.
- `specify init` persists the user's choices to `.specify/init-options.json` via `save_init_options`; `load_init_options`/`is_ai_skills_enabled`/`resolve_active_agent_for_registration` read it back and distinguish missing-file (legacy fallback) from malformed-file (fail closed) via the `MISSING_INIT_OPTIONS_FILE` sentinel. — [src/specify_cli/_init_options.py#L9-83](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/_init_options.py#L9-L83)
- Core command templates declare per-script-type helper invocations in `scripts:` frontmatter (`sh`, `ps`, `py` entries) and carry placeholders (`{SCRIPT}`, `$ARGUMENTS`, `__SPECKIT_COMMAND_<NAME>__`) that integrations resolve at install time through `process_template()` using the integration's `invoke_separator` (`.` for markdown agents, `-` for skills agents). — [templates/commands/plan.md#L11-16](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/templates/commands/plan.md#L11-L16) [src/specify_cli/integrations/base.py#L1079-1165](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/integrations/base.py#L1079-L1165) [AGENTS.md](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/AGENTS.md)
- Every file an integration installs is recorded with a SHA-256 in `.specify/integrations/<key>.manifest.json` via `IntegrationManifest.record_file`/`record_existing`; `teardown()` delegates to `manifest.uninstall()` which deletes only files whose current hash still matches (skipping user-modified files) unless `--force`. — [src/specify_cli/integrations/manifest.py#L1-48,104-120](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/integrations/manifest.py#L1-48,104-L120) [src/specify_cli/integrations/base.py#L997-1012](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/integrations/base.py#L997-L1012) [AGENTS.md](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/AGENTS.md)
- Shared infrastructure (`_install_shared_infra_impl` in `shared_infra.py`) copies `.specify/scripts/<variant>/` and `.specify/templates/` from core_pack or the repo root, resolves `__SPECKIT_COMMAND_*__` placeholders, tracks files in `speckit.manifest.json`, and writes a managed `.specify/.gitignore` covering `feature.json` and per-machine `extensions/*/local-config.yml`. — [src/specify_cli/shared_infra.py#L1-33](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/shared_infra.py#L1-L33) [src/specify_cli/__init__.py#L135-215](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/__init__.py#L135-L215)
- Workflow run state is persisted under `.specify/workflows/runs/<run_id>/` as atomically-written `state.json` + `inputs.json` plus an appended `log.jsonl`; `RunState.load` validates `run_id` against a strict pattern before building the path, and `workflow resume` reloads the persisted workflow copy and merges new inputs over persisted ones. — [src/specify_cli/workflows/engine.py#L594-930,1094-1160](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/workflows/engine.py#L594-930,1094-L1160)
- Extension installs parse `extension.yml` into an `ExtensionManifest` (validating `provides.commands` names against `^speckit\.<ns>\.<name>$`, hooks, events, templates, scripts), copy files via `ExtensionManager.install_from_directory`/`install_from_zip`, and merge `hooks` entries into `.specify/extensions.yml` through `register_hooks`. — [src/specify_cli/extensions/__init__.py#L66-77,290-430,5063-5160](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/extensions/__init__.py#L66-77,290-430,5063-L5160) [extensions/git/extension.yml#L42-130](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/extensions/git/extension.yml#L42-L130)
- Remote content (catalog JSON, extension/preset/workflow archives) is fetched with hard byte ceilings — `MAX_DOWNLOAD_BYTES` 50 MiB, `MAX_JSON_METADATA_BYTES` 1 MiB, `MAX_JSON_CATALOG_BYTES` 8 MiB, per-entry and total zip caps — via `read_response_limited`/`safe_extract_archive`; catalog-declared `sha256` pins are verified with `hmac.compare_digest` before install. — [src/specify_cli/_download_security.py#L26-49](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/_download_security.py#L26-L49) [src/specify_cli/shared_infra.py#L42-99](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/shared_infra.py#L42-L99)
- The events flow generates a self-contained `.specify/events.py` dispatcher plus native hook config per integration; the dispatcher resolves the event's command template and runs its script confined under the project root (`EVENT_SCRIPT_PATH_CONFINEMENT = True`), with `specify event run` as the fallback entry point that accepts a ≤1 MiB stdin payload. — [src/specify_cli/events.py#L33-100](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/events.py#L33-L100) [src/specify_cli/commands/event.py#L24-58](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/commands/event.py#L24-L58)
- `CommandRegistrar` (agents.py) lazily derives `AGENT_CONFIGS` from `INTEGRATION_REGISTRY` `registrar_config` dicts (propagating `invoke_separator` and `dev_no_symlink`), then renders commands into each agent's directory/format for extensions and presets — so the registry is the single metadata source for both the integration layer and the registrar. — [src/specify_cli/agents.py#L24-75](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/agents.py#L24-L75) [src/specify_cli/_agent_config.py#L9-18](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/_agent_config.py#L9-L18)
- Skills-mode installs branch on the persisted `ai_skills` flag: `resolve_active_skills_dir` maps the project to `<agent_folder>/skills` (with symlink/containment checks via `_ensure_safe_shared_directory`), special-cases `generic` (no static folder → disabled) and `kimi` (native-skills fallback when flag unset). — [src/specify_cli/__init__.py#L286-344](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/__init__.py#L286-L344)
- The workflow engine resolves the dispatch integration by layered fallback — step `integration:` → workflow default → `.specify/integration.json` project default — with `"auto"` meaning the project-recorded integration, then `CommandStep.execute` builds the agent CLI argv via `build_exec_args` and streams output to the terminal. — [src/specify_cli/workflows/engine.py#L1700-1730](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/workflows/engine.py#L1700-L1730) [src/specify_cli/workflows/steps/command/__init__.py#L31-113](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/workflows/steps/command/__init__.py#L31-L113) [src/specify_cli/integration_state.py](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/integration_state.py)
- Catalog stacks (`CatalogStackBase` + per-domain subclasses) merge an ordered list of catalog sources, each entry carrying `url`, `priority`, and `install_allowed`; catalog entries from discovery-only catalogs are treated as untrusted — e.g. `_command_safe_id` refuses to interpolate non-`[a-z0-9-]` IDs into suggested shell commands. — [src/specify_cli/catalogs.py#L14-40](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/catalogs.py#L14-L40) [src/specify_cli/extensions/_commands.py#L59-84](https://github.com/github/spec-kit/blob/175adbcd826befdccce8c60faa07ae3d8bbc8d87/src/specify_cli/extensions/_commands.py#L59-L84)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
