Skip to content
Back to skills

Ffs Init

ASecurity

Umbrella FFS initialization: install repo-scoped dependencies (Phase 0), then build or refresh the environment registry (config/environments.yaml) — propose rows from repo evidence, review them in one batched session, apply atomically, validate, update in place. Never leaks a secret VALUE (names only) and never re-asks a declined proposal.

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 26, 2026
ai-agentsgoshellbashnodeawsgitapidocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 26, 2026

npx -y skills add austinmao/feature-fix-swarm --skill ffs-init --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ffs Init?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ffs Init
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/austinmao-ffs-init/badge)](https://www.skillsdirectory.com/skills/austinmao-ffs-init)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ffs-init
description: "Umbrella FFS initialization: install repo-scoped dependencies (Phase 0), then build or refresh the environment registry (config/environments.yaml) — propose rows from repo evidence, review them in one batched session, apply atomically, validate, update in place. Never leaks a secret VALUE (names only) and never re-asks a declined proposal."
version: "1.1.0"
---

# /ffs-init [--detect-only|--answers <file>|--yes|--update|--check|--force|--reset-declines|--skip-deps]

## Host dispatch contract

- Codex: invoke skills as `$skill`; use Codex collaboration roles and GPT-5.6 model tiers.
- Claude: invoke skills as `/skill`; use Claude Agent/Skill tools and Claude model aliases.
- Examples that name both hosts are routing contracts. Never send one host's command syntax to the other.
- A bare `/skill` in this shared source denotes the Claude form; Codex dispatches the same named skill as `$skill`.

Two phases: Phase 0 installs repo-scoped dependencies (below), then the
registry flow collects operator answers for the environment registry.

For the REGISTRY the skill DECIDES nothing and WRITES nothing:
`scripts/gsd/env-registry.sh` owns detection, validation, and the atomic
write. Every registry write in this skill's flow routes through
`env-registry.sh apply` — the skill itself never touches
`config/environments.yaml` or `.ffs-init.json`, in any mode, ever. Phase 0's
only writes are the ones `deps.sh install` makes (`node_modules/` from the
committed lockfile, pip packages from `requirements-dev.txt`) and, on an
explicit confirm, whatever `setup.sh` installs under its own manifest.

## Phase 0 — dependencies (runs first; `--skip-deps` skips it)

```bash
REPO_ROOT="$(git rev-parse --show-toplevel)"
bash "$REPO_ROOT/scripts/gsd/deps.sh" check
```

1. Run `deps.sh check` and relay its report. Exit 0 with nothing missing →
   phase done, continue to the registry flow.
2. Repo-scoped rows missing (`@opengsd/gsd-core`, `filelock`) → run
   `bash "$REPO_ROOT/scripts/gsd/deps.sh" install` (interactive: let its
   npm-ci confirm reach the operator; non-interactive modes pass `--yes`).
3. No FFS install manifest (neither `.feature-fix-swarm/install-manifest.json`
   nor the user-scope cache copy) → OFFER `bash setup.sh --scope user` (or
   `--scope project --project-dir <repo>`) with an explicit confirm — it
   mutates `~/.claude`/`~/.agents`, so it is never run silently.
4. Missing SYSTEM tools (jq, gh, bats, shellcheck, …) are reported with their
   exact install command from the roster and left to the operator — this
   skill never runs a system package manager and never uses sudo.

`--detect-only` and `--check` skip Phase 0 entirely (they are read-only
modes and must stay hermetic).

## Script invocation idiom

The script lives at the ROADMAP-pinned path under the repo root — never
resolve it relative to this skill file:

```bash
REPO_ROOT="$(git rev-parse --show-toplevel)"
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" <detect|check|render|apply|seed> [flags]
```

## Interactive session (default, one batched pass)

Three question blocks mirror the registry's own structure, asked in ONE
batched interactive session — the operator answers everything once, then a
single `apply` writes atomically.

**Consent question (interactive only).** Before the batched answers, run
`bash "$REPO_ROOT/scripts/gsd/retro.sh" check-consent`. When it reports an
askable state (absent or consent-major mismatch), explain that FFS may
automatically file PUBLIC issues to `austinmao/feature-fix-swarm`, containing
only FFS script names, typed event classes/gates, numeric exit/duration facts,
model tiers, FFS version, priority/fingerprint, and derived healing metrics —
never code, paths, repository identity, or free text. Ask once, recommended
yes: `Enable these public diagnostics? [Y/n]`. Only an explicit interactive
affirmative invokes `bash "$REPO_ROOT/scripts/gsd/retro.sh" consent --grant`;
a negative or no answer invokes `consent --revoke` to record the asked decision.
Current-major granted or revoked decisions suppress the question. `/ffs-init
--reset` invokes `retro.sh consent --reset` (the CLI performs revoke-first
under its one lock) and then may re-ask. In every non-interactive mode,
including `--answers`, `--yes`, detect/check/update/force, do not ask and do
not grant. This skill never writes consent JSON directly.

**Step 0 — detect.** Run `bash "$REPO_ROOT/scripts/gsd/env-registry.sh"
detect` and capture stdout. The output is a proposal YAML (and itself a
valid `--answers` file): every row carries `confidence:` and `evidence:`,
and every row is a proposal — detection proposes, never decides
(`verified: null`). Rows previously declined by the operator are suppressed
by the script; when that happens the script prints exactly one stderr
advisory — relay it to the operator verbatim:

> `ADVISORY: <n> proposal(s) suppressed by declines in .ffs-init.json — run
> 'env-registry.sh apply --reset-declines' to clear them`

**Block 1 — `environments` rows.** Present each proposed row (name, kind,
base_url, secret_names, test_tier) with its confidence and evidence; the
operator confirms or edits per row and may add rows detection missed.
Secrets are collected by NAME only — `secret_names` is a list of variable
names like `PROD_API_TOKEN`. Never ask the operator for a secret VALUE, and
refuse one if pasted: values belong in the secret manager, and `apply`'s
leak scan rejects any that reach the answers file.

**Block 2 — `test_tiers` classification.** Present the proposed
tier-to-command mapping (rows: `tier`, `command`; optional `covers`) for
confirm/edit. The operator corrects commands detection guessed at low
confidence.

**Block 3 — `surfaces` (its own gate).** Isolated from blocks 1-2 with its
own explicit confirm, because `gates.py` parses this block verbatim — a
wrong row here changes gate behavior, not just documentation. Rows map
`surface` → `staging_instance`. Explain the `none` sentinel before asking:
`staging_instance: none` is an explicit no-staging declaration and
fail-closed — it is not a default and must be chosen deliberately. A repo
with no production surfaces OMITS this block entirely (the registry is
schema-valid without it).

**Step 4 — write answers OUTSIDE the repo, then apply.** Assemble the
confirmed rows into an answers file created via `mktemp` outside the repo
tree — never write it inside the checkout:

```bash
ANSWERS="$(mktemp -t ffs-init-answers)"
# ...emit confirmed environments/test_tiers/surfaces rows into "$ANSWERS"...
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" apply --answers "$ANSWERS"
rm -f "$ANSWERS"
```

`apply` is the ONLY write path: it validates the candidate bytes, then
writes `config/environments.yaml` and `.ffs-init.json` all-or-nothing under
a lock. Finish by running `bash "$REPO_ROOT/scripts/gsd/env-registry.sh"
check` and reporting its result to the operator.

## Exit-code taxonomy (per verb, pinned in 02-01)

- `0` — success (check advisories included; stale-verified is NEVER a gate)
- `1` — usage / schema / referential / answers / refusal
  (`ENV-REGISTRY-INVALID:` / `ENV-REGISTRY-REFUSED:` / `ENV-REGISTRY-BUSY:`
  prefixes)
- `2` — leak finding (guard convention; check on the resolved registry,
  apply on candidate bytes)
(`render` is fully implemented — workflows land only as
`.github/workflows/ffs-<name>.yml`, collisions divert to
`.github/ffs-proposals/` with a `diff -u`. There is no exit 3.)

Any concurrent `apply` fails FAST, never interleaves:

> `ENV-REGISTRY-BUSY: another apply holds .ffs-init.lock — remedy: retry
> after the concurrent apply completes`

Remedy: rerun the same `apply` after the concurrent one finishes.

## Non-interactive modes

### `--detect-only`

```bash
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" detect
```

Writes NOTHING inside the repo. Emits the proposal YAML on stdout with
per-row `confidence:` + `evidence:`; detect's stdout IS a valid `--answers`
file — pipe it to a file outside the repo, edit, and feed it to `apply`.
There is no failure path that leaves partial state: detect either prints a
proposal (rc 0) or a typed `ENV-REGISTRY-INVALID:` line (rc 1) naming what
to fix.

### `--answers <file.yaml>` (fully non-interactive)

```bash
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" apply --answers /path/outside/repo/answers.yaml
```

Answers-file shape (pinned in 02-01): required top-level keys
`environments:` (rows: `name`, `kind`; optional `base_url`, `secret_names`,
`verified`, `test_tier`, `confidence`, `evidence`) and `test_tiers:` (rows:
`tier`, `command`; optional `covers`, `confidence`, `evidence`). Optional:
`surfaces:` (rows: `surface`, `staging_instance`) and `declines:` (rows:
`heuristic`, `evidence`, optional `declined_at`). `confidence`/`evidence`
are dropped at emission — the written registry keeps the fixed six-key row
shape.

Missing required key → rc 1, nothing written (EDGE-008). The message names
the key and its expected form — key + line + expected shape only, never the
bytes it got:

> `ENV-REGISTRY-INVALID: answers missing required key <key> — expected a
> top-level '<key>:' list of rows each with '<k1>:' and '<k2>:'; nothing
> written`

Remedy: add the named key in the named shape to the answers file and rerun.

### `--yes` (accept detected defaults)

```bash
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" apply --answers "$ANSWERS" --yes
```

Accepts detected defaults with `verified: null` everywhere. `--yes` never
silently regenerates: over an existing registry it refuses (rc 1) with the
remedy in the message:

> `ENV-REGISTRY-REFUSED: config/environments.yaml already exists — remedy:
> re-run with --update to merge NEW rows only, or --force to regenerate
> from scratch`

### `--update` (merge, preserve operator edits)

```bash
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" apply --answers "$ANSWERS" --update
```

Diffs against the existing registry: adds/flags NEW rows only, and
preserves operator-set `verified:` dates and manual edits. This is the
default remedy when `--yes` refuses.

### `--check` (read-only validation, CI-runnable)

```bash
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" check
```

Validates schema, secret-value leak scan (REQ-202a), referential integrity
(unique environment names; every `staging_instance` references a declared
`kind: staging` environment), stale-verified advisory (advisory only, never
a gate), and the surfaces block round-trip under gates.py. Failure classes:

- **Schema / referential break** → rc 1, `ENV-REGISTRY-INVALID:` naming the
  key, line, and expected form. Remedy: edit the named row and rerun.
- **Leak finding** → rc 2 with the fixed, value-free contract text
  (pinned in 02-01):

  > `line N, key <name>, shape <class> — remedy: replace the literal with a
  > NAME in secret_names`

  A committed file appends: `; this file is committed — rotate the
  credential, then rewrite history to remove it`. Shape classes:
  `hex-run base64-run secret-assignment credential-url aws-access-key
  provider-token-prefix jwt pem-block`. Non-identifier key prints
  `<non-identifier key at line N>`. Matched bytes never appear on either
  stream.

### `--reset-declines`

```bash
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" apply --reset-declines
```

Clears recorded declines so suppressed proposals surface again on the next
`detect`.

### `seed` (read-only, consumed by `/preflight`)

```bash
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" seed
```

Emits preflight-manifest candidate rows (JSON array, secret NAMES only) from
the resolved registry. Additive and never authoritative — `/preflight` uses
it to seed `specs/NNN/preflight.json`; it writes nothing.

### `--force` (regenerate from scratch)

```bash
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" apply --answers "$ANSWERS" --force
```

Regenerates the registry from scratch, discarding operator edits. Only pass
`--force` after an explicit operator confirm — the skill must restate what
will be lost (all `verified:` dates and manual edits) and get a yes first.

## Decline semantics

Declines live in `.ffs-init.json` (repo root, COMMITTED, first key
`"schema": "ffs.init/v1"`, written ONLY by `apply`). Each decline is keyed
(heuristic, concrete evidence value), e.g.
`("wrangler-env", "wrangler.toml:[env.staging]")` — so NEW evidence
re-proposes, while the same evidence stays suppressed. Suppression is
announced with exactly one stderr advisory line (relay it verbatim):

> `ADVISORY: <n> proposal(s) suppressed by declines in .ffs-init.json — run
> 'env-registry.sh apply --reset-declines' to clear them`

## gh reviewer-protection probe (external prerequisite, opt-in)

Reviewer protection on the prod GitHub environment is an EXTERNAL
prerequisite, validated only by the OPT-IN `--probe-gh` advisory on
`detect`/`check` — never a gate. Plain `check` makes ZERO network calls even
when `gh` is authenticated; the probe flag is an operator extra. The verify
fences below run plain `check`/`detect` and are hermetic by construction.

## Verified against the committed registry

These fences execute on every skill-lint pass (fresh temp cwd, `REPO_ROOT`
exported, read-only, no network):

```bash verify
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" check
```

```bash verify
bash "$REPO_ROOT/scripts/gsd/env-registry.sh" detect >/dev/null
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.