Installer Specifications ======================== Target Location --------------- .. flowspec:: Repository root detection :id: FLSP_UBFLOW_INST_001 :status: draft :satisfies: FLST_UBFLOW_INST_01 :tags: ubflow, installer The installer shall determine the repository root by traversing parent directories from the current working directory until a ``.git`` directory is found, using the ``git rev-parse --show-toplevel`` command. If no Git repository is found, the installer shall exit with code 1 and the message: ``"Not inside a Git repository. Aborting."``. .. flowspec:: Target folder prompt and validation :id: FLSP_UBFLOW_INST_002 :status: draft :satisfies: FLST_UBFLOW_INST_01 :tags: ubflow, installer The installer shall present a Copier prompt ``target_docs_path`` with the default value ``docs/``. The entered path must satisfy all of: - resolves to an absolute path inside the repository root - the directory exists (``os.path.isdir`` returns ``True``) On failure, the prompt shall be repeated with an inline error. The resolved absolute path shall be stored as ``target_abs_path`` in the Copier context and written to ``.copier-answers.yml`` as ``target_docs_path``. Epic Management --------------- .. flowspec:: Epic enumeration from user-story tags :id: FLSP_UBFLOW_INST_003 :status: draft :satisfies: FLST_UBFLOW_INST_03 :tags: ubflow, installer The installer template shall define epics via a YAML list in ``copier.yml`` under the key ``epics``. Each entry has the fields: .. code-block:: yaml epics: - id: "target_location" title: "Target Location" default: true - id: "conflict_detection" title: "Conflict Detection" default: true The ``id`` value must match the ``epic`` tag applied to flow stories in the template's RST sources. .. flowspec:: Epic selection prompt :id: FLSP_UBFLOW_INST_004 :status: draft :satisfies: FLST_UBFLOW_INST_02 :tags: ubflow, installer Copier shall render a ``multiselect`` prompt ``selected_epics`` that lists all entries from ``epics`` (see ``FLSP_UBFLOW_INST_003``). All epics default to selected. The resolved value is a list of epic IDs stored in ``.copier-answers.yml`` as ``selected_epics``. .. flowspec:: Epic-filtered template rendering :id: FLSP_UBFLOW_INST_005 :status: draft :satisfies: FLST_UBFLOW_INST_02 :tags: ubflow, installer Each RST snippet belonging to an epic shall be wrapped in a Jinja2 conditional in the template source: .. code-block:: jinja {% if "conflict_detection" in selected_epics %} .. flowstory:: Conflict detection :id: FLST_UBFLOW_INST_10 ... {% endif %} Deactivated epics produce no output in the rendered file. Determinism and Verification ----------------------------- .. flowspec:: Deterministic Copier invocation from local source :id: FLSP_UBFLOW_INST_006 :status: draft :satisfies: FLST_UBFLOW_INST_04; FLST_UBFLOW_INST_14 :tags: ubflow, installer The installer shall invoke Copier against the locally extracted package directory (see ``FLSP_UBFLOW_INST_026``). No remote URLs or ``--vcs-ref`` flags are used: .. code-block:: bash copier copy \ --trust \ --data package_name= \ --data package_version= \ \ No prompts shall appear in replay mode. Two invocations with identical package zip contents and ``.copier-answers.yml`` shall produce identical output files. .. flowspec:: Installation manifest format :id: FLSP_UBFLOW_INST_007 :status: draft :satisfies: FLST_UBFLOW_INST_04 :tags: ubflow, installer After a successful installation, the installer shall write a manifest file ``/.ubflow-manifest.json`` with the following schema: .. code-block:: json { "schema_version": 1, "package_source": "families://aspice", "package_version": "0.1.0", "installed_at": "2026-03-14T10:00:00Z", "files": { "relative/path/to/file.rst": "sha256:" } } ``package_source`` is a URI of the form ``families://`` identifying the local family. The ``files`` map contains every file written during installation, keyed by path relative to ``target_abs_path``, with a SHA-256 hex digest. .. flowspec:: Verify command implementation :id: FLSP_UBFLOW_INST_008 :status: draft :satisfies: FLST_UBFLOW_INST_04 :tags: ubflow, installer A ``ubflow-installer verify `` command shall read ``.ubflow-manifest.json``, recompute SHA-256 for each listed file, and for each mismatch print a line: .. code-block:: text FAIL relative/path/to/file.rst (expected abc123, got def456) The command exits with code 0 if all digests match, code 2 otherwise. Target Folder Layout -------------------- .. flowspec:: Output confinement and folder structure :id: FLSP_UBFLOW_INST_009 :status: draft :satisfies: FLST_UBFLOW_INST_05 :tags: ubflow, installer The Copier template ``copier.yml`` shall set: .. code-block:: yaml _subdirectory: "template" so that all rendered files are output exclusively inside the install directory. The installer shall always place the family into the fixed subdirectory ``ubflow//`` relative to ``target_abs_path``: .. code-block:: text / └── ubflow/ └── / ├── .ubflow-answers.yml ├── .ubflow-manifest.json ├── index.rst ├── user_stories.rst ├── specs.rst ├── instructions.rst ├── skills.rst └── traceability.rst No files outside ``/ubflow//`` shall be created or modified. Template and Version Selection ------------------------------- .. flowspec:: Package version from ubflow_package.json :id: FLSP_UBFLOW_INST_010 :status: draft :satisfies: FLST_UBFLOW_INST_06; FLST_UBFLOW_INST_14; FLST_UBFLOW_INST_15 :tags: ubflow, installer The installer shall read the package version from the ``ubflow_package.json`` file inside the extracted package directory (see ``FLSP_UBFLOW_INST_024`` and ``FLSP_UBFLOW_INST_026``). The value of the ``package_version`` field is passed to Copier as a ``--data`` argument so that the template can embed the version in generated files: .. code-block:: bash copier copy --trust \ --data package_version= \ No ``--vcs-ref`` flag is used. Version pinning is achieved by shipping a specific zip file with the ubCode extension. Partial Agent Selection ----------------------- .. flowspec:: Per-agent selection prompt in Copier :id: FLSP_UBFLOW_INST_011 :status: draft :satisfies: FLST_UBFLOW_INST_07 :tags: ubflow, installer ``copier.yml`` shall define a ``multiselect`` prompt ``selected_agents`` populated from a YAML list of available agents, each with an ``id`` and ``title``. All agents default to selected. The resolved value is stored in ``.copier-answers.yml``. Template agent subdirectories are rendered only when the agent ID appears in ``selected_agents``: .. code-block:: jinja {% if "sys2" in selected_agents %} {%- include "agents/sys2/index.rst.jinja" %} {% endif %} At least one agent must be selected; Copier validator raises a ``ValueError("At least one agent must be selected")`` otherwise. Prerequisite Check and Auto-Install ------------------------------------- .. flowspec:: Pre-installation dependency check script :id: FLSP_UBFLOW_INST_012 :status: draft :satisfies: FLST_UBFLOW_INST_08 :tags: ubflow, installer The installer template shall include a Copier task hook ``tasks/check_prerequisites.py`` that is executed before rendering. The script checks for the following imports and CLI commands: .. list-table:: :header-rows: 1 :widths: 30 30 40 * - Dependency - Check method - Minimum version * - Python - ``sys.version_info`` - 3.11 * - ``sphinx-needs`` - ``importlib.metadata.version("sphinx-needs")`` - 2.0.0 * - ``copier`` - ``shutil.which("copier")`` - 9.0.0 * - ``java`` - ``shutil.which("java")`` - any (for PlantUML) The script prints all failures and exits with code 1 if any check fails, preventing Copier from rendering the template. .. flowspec:: Auto-install of Python prerequisites via pip :id: FLSP_UBFLOW_INST_013 :status: draft :satisfies: FLST_UBFLOW_INST_08 :tags: ubflow, installer When ``check_prerequisites.py`` detects missing Python packages, it shall prompt: ``"Install missing packages now? [y/N]"``. On confirmation it shall invoke: .. code-block:: bash python -m pip install "sphinx-needs>=2.0.0" "copier>=9.0.0" On decline it shall print the manual install command and exit with code 1. Missing system dependencies (``java``) are reported but never auto-installed. Update and Upgrade ------------------ .. flowspec:: Update invocation from local package source :id: FLSP_UBFLOW_INST_014 :status: draft :satisfies: FLST_UBFLOW_INST_09; FLST_UBFLOW_INST_14 :tags: ubflow, installer To upgrade an existing installation, the installer shall: 1. Extract the new package zip to a fresh temporary directory (see ``FLSP_UBFLOW_INST_026``). 2. Run ``copier copy`` with ``--overwrite`` and ``--defaults``, pointing to the new temporary directory: .. code-block:: bash copier copy \ --trust --overwrite --defaults \ --data package_version= \ \ 3. Clean up the temporary directory. Copier performs a three-way merge: base = previous template version, ours = current installed files, theirs = new package. Merge conflicts are surfaced as standard diff conflict markers in the affected files. No ``--vcs-ref`` flag is used. .. flowspec:: Pre-upgrade diff summary from local source :id: FLSP_UBFLOW_INST_015 :status: draft :satisfies: FLST_UBFLOW_INST_09; FLST_UBFLOW_INST_14 :tags: ubflow, installer Before upgrading, the installer shall run Copier in pretend mode against the new extracted temporary directory: .. code-block:: bash copier copy \ --trust --pretend --overwrite --defaults \ --data package_version= \ \ and display the resulting diff. The installer shall require the developer to enter ``y`` to proceed or ``n`` to abort. Invocation without a TTY (``sys.stdin.isatty() == False``) shall skip confirmation and proceed. Conflict Detection ------------------ .. flowspec:: Sphinx-Needs ID collision detection :id: FLSP_UBFLOW_INST_016 :status: draft :satisfies: FLST_UBFLOW_INST_10 :tags: ubflow, installer The installer task hook ``tasks/check_conflicts.py`` shall scan all ``*.rst`` files under the repository root for Sphinx-Needs IDs using the regex ``r'^\s+:id:\s+(\S+)'`` (multiline). It shall cross-reference detected IDs with all IDs defined in the template's source RST files and print a conflict table: .. code-block:: text CONFLICT FLST_UBFLOW_INST_01 found in: docs/my_stories.rst:14 The hook exits with code 1 when one or more conflicts exist, blocking the Copier render step. .. flowspec:: File path collision detection and per-file confirmation :id: FLSP_UBFLOW_INST_017 :status: draft :satisfies: FLST_UBFLOW_INST_10 :tags: ubflow, installer Before writing files, ``tasks/check_conflicts.py`` shall collect all output paths that already exist on disk. For each colliding path it shall print: .. code-block:: text EXISTS docs/aspice/index.rst — overwrite? [y/N] Files answered with ``N`` are excluded from the Copier render output via a ``skip_if`` condition rendered into the template or removed post-generation. If all colliding files are skipped, the installer exits with code 1 and reports incomplete installation. Rollback and Uninstall ----------------------- .. flowspec:: Uninstall command using manifest :id: FLSP_UBFLOW_INST_018 :status: draft :satisfies: FLST_UBFLOW_INST_11 :tags: ubflow, installer A ``ubflow-installer uninstall `` command shall read ``.ubflow-manifest.json`` and delete every file listed under ``files``. After deletion, any empty directories under ``target_abs_path`` (excluding ``target_abs_path`` itself) shall also be removed. The manifest file and ``.copier-answers.yml`` are deleted last. Files modified since installation (digest mismatch) shall be skipped and reported: .. code-block:: text SKIPPED relative/path/file.rst (modified since install — remove manually) .. flowspec:: Atomic write with auto-rollback on failure :id: FLSP_UBFLOW_INST_019 :status: draft :satisfies: FLST_UBFLOW_INST_11 :tags: ubflow, installer The Copier post-generation task ``tasks/finalize.py`` shall write all rendered files to a temporary staging directory first, then move them atomically to ``target_abs_path``. If any step fails (e.g. disk full, permission error), the task shall delete the staging directory and all partially copied target files, then exit with code 1 and: .. code-block:: text Installation failed. All written files have been removed. Repository state is unchanged. Configuration Persistence -------------------------- .. flowspec:: Copier answers file location and schema :id: FLSP_UBFLOW_INST_020 :status: draft :satisfies: FLST_UBFLOW_INST_12 :tags: ubflow, installer Copier shall write the answers file to ``/.copier-answers.yml``, controlled by the ``copier.yml`` key: .. code-block:: yaml _answers_file: ".copier-answers.yml" The file shall contain at minimum: .. code-block:: yaml package_name: "aspice" package_version: "0.1.0" target_docs_path: "docs/" selected_agents: - orchestrator - sys2 selected_epics: - core - sw_change family_name: "aspice" No URL or git commit reference is stored. The installed version is recorded via ``package_version`` and in ``.ubflow-manifest.json`` (see ``FLSP_UBFLOW_INST_007``). .. flowspec:: Answers file used for prompt pre-population :id: FLSP_UBFLOW_INST_021 :status: draft :satisfies: FLST_UBFLOW_INST_12 :tags: ubflow, installer When ``.copier-answers.yml`` is present, the installer shall pass ``--answers-file .copier-answers.yml`` to Copier. Copier automatically uses stored values as defaults for each prompt. The developer may accept each default with ``Enter`` or type a new value. Post-Install Validation ------------------------ .. flowspec:: ubflow_package.json schema :id: FLSP_UBFLOW_INST_024 :status: draft :satisfies: FLST_UBFLOW_INST_15 :tags: ubflow, installer Every ubFlow package directory shall contain a file ``ubflow_package.json`` at its root. The file shall conform to the following JSON schema: .. code-block:: json { "schema_version": 1, "package_name": "aspice", "package_version": "0.1.0", "display_name": "ASPICE Agent Family", "description": "...", "directories": [ { "id": "orchestrator", "title": "Orchestrator", "description": "Main orchestrator agent", "default": true } ], "epics": [ { "id": "core", "title": "Core Workflows", "description": "Fundamental process workflows", "default": true } ] } The ``directories`` array lists selectable Copier sub-directories. Only directories whose ``id`` is in ``selected_agents`` (passed via ``--data``) are rendered by Copier. The ``epics`` array mirrors the epic structure in ``copier.yml`` and is the source of truth for epic metadata. .. flowspec:: Family source discovery (directory or zip) :id: FLSP_UBFLOW_INST_025 :status: draft :satisfies: FLST_UBFLOW_INST_14 :tags: ubflow, installer The installer shall locate the family source by searching the following locations in order, returning the first that exists: .. list-table:: :header-rows: 1 :widths: 10 45 45 * - Priority - Location - Type * - 1 - ``/families//`` - directory * - 2 - ``/families/.zip`` - zip archive * - 3 - ``/families//`` - directory * - 4 - ``/families/.zip`` - zip archive ```` is the ubCode VS Code extension directory resolved by globbing ``~/.vscode/extensions/useblocks.ubcode-*`` and selecting the entry with the highest version suffix. An optional ``--extension-dir`` CLI argument overrides auto-discovery. If no match is found in any location, the installer shall exit with code 1 and list all searched paths. .. flowspec:: Family source preparation (directory pass-through or zip extraction) :id: FLSP_UBFLOW_INST_026 :status: draft :satisfies: FLST_UBFLOW_INST_14 :tags: ubflow, installer After finding the family source (see ``FLSP_UBFLOW_INST_025``) the installer shall prepare a working directory for Copier: **Directory source**: the directory is used directly as the Copier template source. No extraction is performed and no cleanup is needed after Copier exits. **Zip source**: the zip file is extracted to a system temporary directory (``tempfile.mkdtemp``) before invoking Copier. The temporary directory is removed unconditionally — whether Copier succeeds or fails — as soon as Copier exits. No zip or extracted file shall remain on disk afterwards. The preparation sequence is: .. code-block:: python source = resolve_family(family_name, repo_root, extension_dir) work_dir, was_extracted = prepare_source(source) try: # read ubflow_package.json from work_dir # invoke copier copy work_dir → target_abs_path finally: if was_extracted: cleanup_source(work_dir) .. flowspec:: Undefined Sphinx-Needs reference scan :id: FLSP_UBFLOW_INST_022 :status: draft :satisfies: FLST_UBFLOW_INST_13 :tags: ubflow, installer The Copier post-generation task ``tasks/validate.py`` shall parse all installed RST files and extract all link option values using the regex ``r':(?:satisfies|implements|supports|applies|employs|uses|covered_by):\s+(.+)'``. Each referenced ID shall be looked up against the ubCode MCP server via ``find_needs(need_id=)`` (or scanned locally if the MCP server is unavailable). Missing IDs shall be reported as: .. code-block:: text UNDEF FLSP_UBFLOW_INST_999 referenced in: aspice/specs.rst:42 The task exits with code 1 if undefined references exist. .. flowspec:: Sphinx build as final validation gate :id: FLSP_UBFLOW_INST_023 :status: draft :satisfies: FLST_UBFLOW_INST_13 :tags: ubflow, installer After the undefined-reference scan passes, ``tasks/validate.py`` shall invoke: .. code-block:: bash sphinx-build -b html -W --keep-going \ /_build/html where ```` is the documentation project root containing ``conf.py``. The ``-W`` flag treats all warnings as errors. Exit code 0 indicates a valid installation. Any non-zero exit code causes the task to print the Sphinx output and exit with code 1, marking the installation as incomplete. ubFlow Agent Stub ----------------- .. flowspec:: Installer creates the ubFlow agent stub idempotently :id: FLSP_UBFLOW_INST_027 :status: draft :satisfies: FLST_UBFLOW_INST_17 :tags: ubflow, installer During every install or upgrade the installer SHALL write the file ``.github/agents/ubflow.agent.md`` into the target repository root. The file content SHALL be exactly: .. code-block:: markdown --- description: ubFlow — needs-driven agent design for GitHub Copilot applyTo: '**' --- Fetch your full instruction and skill set from the ubCode MCP server before doing anything else: 1. Call `get_schema_for_need_filter` with `agent="ubflow"` to verify the ubCode extension is reachable. 2. Call `query_needs` with `type="flowinst"`, `tags=["ubflow"]`, and `agent="ubflow"` to load all binding instructions. 3. Call `query_needs` with `type="flowskill"`, `tags=["ubflow"]`, and `agent="ubflow"` to load all skills. If any call returns `{"error": "unknown_agent"}` or fails, stop and ask the user to install or update the ubCode VS Code extension. The operation SHALL be **idempotent**: if the file already exists with identical content, nothing is changed. If it exists with different content it SHALL be overwritten and the change reported to the user. .. flowspec:: Installer creates the companion bootstrap instructions file idempotently :id: FLSP_UBFLOW_INST_028 :status: draft :satisfies: FLST_UBFLOW_INST_18 :tags: ubflow, installer During every install or upgrade the installer SHALL write the file ``.github/instructions/.instructions.md`` in the target repository root. The file content SHALL be exactly: .. code-block:: markdown --- applyTo: '**' --- ## ubFlow Bootstrap — family When operating as `@`: 1. Call `mcp_ubcode_get_schema_for_need_filter` with `file_path: ubflow//ubproject.toml`. 2. Call `mcp_ubcode_get_data_for_single_need` with `id: `. 3. Load every need linked via `:applies:` and `:employs:`. 4. Only then respond to the user request. Do **not** skip these steps. If any MCP call fails, halt and ask the user to check the ubCode extension. The ```` and ```` SHALL be derived at install time from ``ubflow_package.json`` in the family source. The operation SHALL be **idempotent**: if the file already exists with identical content, nothing is changed. If it exists with different content it SHALL be overwritten and the change reported to the user. The file SHALL **not** be removed during uninstall of the family.