> ## Documentation Index
>
> Fetch the complete documentation index at: https://superpowers.frontis.nl/llms.txt
> Use this file to discover all available pages before exploring further.

---

title: Install and update the harness
summary: Agent-executable instructions to install the Frontis harness (conventions, skills, agent instructions) into a repository, or update an existing installation. The only supported install path.
order: 5

---

# Install and update the Frontis harness

Follow this page when asked to install Frontis Superpowers (the Frontis harness) in a repository, or to update an existing installation. Install and update are the same procedure; Step 3 decides which one applies.

Execute the steps yourself as the coding agent. Only involve the user where a step explicitly says so.

## Requirements

- `git` available on PATH.
- Read access to the harness repository:

```text
https://dev.azure.com/frontisbv/AI%20Vakdiscipline/_git/Frontis.Agent.Superpowers
```

Frontis employees have this access through their normal Azure DevOps credentials; no extra setup is needed.

If cloning fails with an authentication or authorization error: **stop**. The harness is Frontis-internal. Report that the user needs Azure DevOps access to the `AI Vakdiscipline` project. Do not look for mirrors or alternative sources.

- Run everything from the root of the target repository.

## Step 1 — Resolve the latest release

```bash
git ls-remote --tags https://dev.azure.com/frontisbv/AI%20Vakdiscipline/_git/Frontis.Agent.Superpowers "v*"
```

Take the highest version by semver ordering (numeric per segment: `v1.10.0` > `v1.9.0`). Ignore `^{}` peeled entries. Call it `<TAG>`.

## Step 2 — Clone the release to a temp directory

```bash
git clone --depth 1 --branch <TAG> https://dev.azure.com/frontisbv/AI%20Vakdiscipline/_git/Frontis.Agent.Superpowers <TEMP>/frontis-harness
```

Verify the clone: `<TEMP>/frontis-harness/harness-manifest.json` `version` must equal `<TAG>` without the leading `v`. On mismatch, report it and stop.

The payload is `<TEMP>/frontis-harness/harness/`. The ownership rules are in `<TEMP>/frontis-harness/harness-manifest.json`:

- `projectOwned` — files that belong to the project. Created when absent; **never overwritten on update**.
- Every other file in `harness/` is **harness-owned**: replaced on update unless locally modified (see Update, Step U3).
- `placeholders` — the placeholder tokens and where their values come from.

## Step 3 — Install or update?

- No `.frontis/superpowers.lock.json` in the target repository → **INSTALL** (also the case for repositories bootstrapped with the old dotnet-template; see Migration below).
- Lock file present → **UPDATE**.

## Install

**I1.** Compare the file list of `harness/` against the target repository. If any harness file already exists in the target, list those files to the user and ask before overwriting any of them (see Migration below for the common case).

**I2.** Copy the full contents of `harness/` (including dotfolders like `.github/`, `.frontis/`) into the repository root, with create-if-missing semantics for `projectOwned` files (per the clone's `harness-manifest.json`): never copy a `projectOwned` file over one that already exists in the target repository — leave the existing one as is. All other (harness-owned) files copy/overwrite normally.

**I3.** Collect project values. Read anything already present in a preserved (pre-existing) `.frontis/project.json`; ask the user for whatever is missing:

```text
company, product, component, hosting, environments, team, grippNumber
```

Never invent values.

**I4.** Substitute placeholders. In all copied files — notably `AGENTS.md` and `.frontis/project.json`, but check every copied file — replace exactly these tokens, no other `__X__`-style strings:

| Token              | Value                                                 |
| ------------------ | ----------------------------------------------------- |
| `__COMPANY__`      | `company`                                             |
| `__PRODUCT__`      | `product`                                             |
| `__COMPONENT__`    | `component`                                           |
| `__HOSTING__`      | `hosting`                                             |
| `__ENVIRONMENTS__` | `environments` joined with `", "` (e.g. `test, prod`) |
| `__GRIPP_NUMBER__` | `grippNumber` as a JSON number (no quotes)            |
| `__TEAM__`         | `team`                                                |

Placeholder substitution does not populate `.frontis/project.json`'s `environments` array — the payload's `["test", "prod"]` is only a placeholder. Set that array explicitly to the collected `environments` values, one JSON string per environment (example: `["test", "acc", "prod"]`).

**I5.** Apply the harness ignore rules — see [Ignore rules](#ignore-rules). Do this before suggesting a commit.

**I6.** Write `.frontis/superpowers.lock.json`:

```json
{
  "schemaVersion": "1.0.0",
  "version": "<TAG without the leading v>",
  "installedAt": "<today, YYYY-MM-DD>",
  "files": {
    "<repo-relative path of each harness-owned file>": "<lowercase-hex sha256 of the file as installed>"
  }
}
```

`files` covers every installed harness-owned file, i.e. every file under `harness/` except the `projectOwned` ones listed in `harness-manifest.json`. Hash the files as they exist in the target repository, i.e. after placeholder substitution. Normalize line endings before hashing (replace CRLF with LF) so checkouts with different `core.autocrlf` settings produce the same hash. Cross-platform hash commands: `pwsh`: `(Get-FileHash -Algorithm SHA256 <file>).Hash.ToLowerInvariant()`; bash: `shasum -a 256 <file>`.

**I7.** Delete the temp clone. Summarize created/overwritten/skipped files and suggest committing the result. Do not commit or push without the user asking.

## Update

**U1.** Read `version` from the lock file. If it equals `<TAG>` without the `v`: report that the harness is up to date and stop.

**U2.** Show what changed between the installed and latest release:

```bash
git -C <TEMP>/frontis-harness fetch --depth 1 origin tag v<installed version>
git -C <TEMP>/frontis-harness diff v<installed version> <TAG> --stat -- harness
```

Summarize this for the user before changing anything.

**U3.** For every harness-owned file in the new release, compute the intended new content: the release file with placeholder tokens substituted from the target's `.frontis/project.json` (same table as Install Step I4). Compare sha256 hashes over LF-normalized content (replace CRLF with LF before hashing) so `core.autocrlf` differences don't register as local modifications. Then:

- Current file's sha256 **equals** its lock-file hash (file untouched since install) → replace it with the intended new content.
- Current file's sha256 **differs** from its lock-file hash (locally modified) → show the user a diff between the current file and the intended new content, and ask: keep local, take new, or merge. Never silently discard a local modification.
- File is in the release but not in the lock file (new in this release) → add it.
- File is in the lock file but not in the release (removed from the harness) and its current sha256 **equals** its lock-file hash → list it and delete it.
- File is in the lock file but not in the release (removed from the harness) and its current sha256 **differs** from its lock-file hash (locally modified) → show the user a diff between the current file and its lock-file version, and ask before deleting. Never silently discard a local modification by deleting it.

`projectOwned` files are **never overwritten**. But a `projectOwned` file that is present in the release and absent in the target repository **is created**, with the release's content — this is how a newly introduced project-owned file (for example `.frontis/preferences.yaml`) reaches a repository that was installed before it existed. Apply placeholder substitution to it exactly as on install. A `projectOwned` file that already exists in the target is left exactly as it is, whatever its content.

**U4.** Apply the harness ignore rules — see [Ignore rules](#ignore-rules). Do this before suggesting a commit.

**U5.** Rewrite the lock file: new `version`, today's date, fresh hashes of every harness-owned file as now present.

**U6.** Delete the temp clone. Summarize replaced/added/deleted/kept files and suggest committing. Do not commit or push without the user asking.

## Ignore rules

The harness payload vendors Umbraco upgrade phases into a literal `phases/packages/` directory:

```text
.agents/skills/perplex-autoupgrade/references/upstream/phases/packages/
```

The standard .NET `.gitignore` rule `**/[Pp]ackages/*` — present in every Frontis Umbraco repository — matches everything below those directories. A plain `git add .` then **silently skips** the NuGet-package and urlrewrite phase files, and a fresh clone of the target repository is missing them. The `perplex-autoupgrade` wrapper skips a phase directory that is not present, so `migrate` would commit and push a green but incomplete Umbraco upgrade.

So: before suggesting a commit, ensure the target repository's root `.gitignore` contains this negation. If it is missing, append it (including the comment) **after** the existing `**/[Pp]ackages/*` rule — a negation only takes effect when it comes later:

```gitignore
# Vendored perplex-autoupgrade upgrade phases live in a literal `phases/packages/` directory and must
# not be swallowed by the NuGet `**/[Pp]ackages/*` rule above; without this, `git add .` silently
# drops whole upgrade phases and the vendored skill becomes incomplete on a fresh clone.
!**/perplex-autoupgrade/references/upstream/phases/packages/**
```

If the repository already has an equivalent negation, leave it alone. If the repository has no `.gitignore` at all, nothing is ignored and nothing needs to be added.

Verify — run this from the target repository root:

```bash
git ls-files --cached --others -- .agents/skills/perplex-autoupgrade | git check-ignore --no-index --stdin
```

`--cached --others` lists every one of those files as it exists on disk: both the ones git already tracks and the untracked ones. The untracked half is the half that matters — in the broken situation `git add .` silently skipped the phase files, so they are still untracked and a tracked-only listing (`git ls-files` without `--others`) would feed `check-ignore` nothing and wrongly report success.

Read the result as follows:

- **Correct:** the command **must print nothing and exit 1**. `git check-ignore` exits 1 when _no_ input path matches an ignore rule, so an empty output with exit code 1 is the pass condition.
- **Broken:** it exits 0 and prints one or more paths. `git check-ignore` exits 0 as soon as _any_ path matches an ignore rule, and every printed path is a vendored file that is still being ignored.

If it prints any path, the ignore rule is still winning: stop, add the negation, or move it **below** the `**/[Pp]ackages/*` rule, and re-run until the command prints nothing, before committing.

Do not add `-v` to diagnose this. With `-v` `git check-ignore` also prints paths matched by a _negation_ rule, so a correctly fixed repository starts printing lines too and "prints nothing" no longer distinguishes fixed from broken. Use the plain form above as the pass/fail check; only use `-v` separately, by hand, when you need to see which rule matched.

## Migration from the old dotnet-template install

Repositories bootstrapped with the old `dotnet new frontis-agent-superpowers` template have harness files but no lock file. Treat them as an INSTALL where Step I1 will find many pre-existing files. Preserve the values in the existing `.frontis/project.json` and `.frontis/project-context.yaml`, diff-and-ask for the rest, and finish with a lock file so the next run is a normal update.

## Safe behavior

- Never overwrite a file without having listed it first.
- Never overwrite a `projectOwned` file during an update; create one only when it is absent from the target repository.
- Never invent project values, names, or conventions.
- Never commit or push without the user asking.
- Never leave a vendored harness file behind an ignore rule — verify the ignore rules before suggesting a commit.
- Report exactly what was created, replaced, kept, and skipped.
