From 52cf728cebde5ff03d7346c027bee5c6a8151198 Mon Sep 17 00:00:00 2001 From: WilcoLouwerse Date: Mon, 4 May 2026 17:49:49 +0200 Subject: [PATCH 1/3] docs(retrofit): use retrofit-DATE-DESCRIPTOR naming + .opsx-ignore details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Naming convention** — retrofit ghost changes are renamed from `retrofit-{descriptor}-{date}` to `retrofit-{date}-{descriptor}` so they sort chronologically together with non-retrofit OpenSpec changes (which already use date-prefixed naming, e.g. `2026-03-25-contacts-actions/`). The skills (`/opsx-annotate`, `/opsx-reverse-spec`, `/opsx-coverage-scan`) have been updated to match in ConductionNL/hydra@5a5244b. **.opsx-ignore details** — clarify that `/opsx-coverage-scan` filters buckets 1/2a/2b/4 (Bucket 3 is REQ-level, unaffected) and that the annotate and reverse-spec skills honor the ignore list transitively via the report. References the OpenRegister .opsx-ignore as a worked example. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/claude/retrofit.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/claude/retrofit.md b/docs/claude/retrofit.md index ead4d747..aa1a62fb 100644 --- a/docs/claude/retrofit.md +++ b/docs/claude/retrofit.md @@ -8,8 +8,10 @@ This playbook walks through retrofitting an app that predates the spec ↔ code Legacy code was never written against a change, so there's no `tasks.md#task-N` for `@spec` to point at. Retrofit bridges this with **ghost changes**: -- **`/opsx-annotate`** creates one ghost change per run: `retrofit-annotate-{app}-{YYYY-MM-DD}` with an empty spec delta and one task per Bucket 1 REQ. -- **`/opsx-reverse-spec`** creates one ghost change per cluster: `retrofit-{capability-or-cluster}-{YYYY-MM-DD}` with a spec delta (new REQs) + one task per new REQ. +- **`/opsx-annotate`** creates one ghost change per run: `retrofit-{YYYY-MM-DD}-annotate-{app}` with an empty spec delta and one task per Bucket 1 REQ. +- **`/opsx-reverse-spec`** creates one ghost change per cluster: `retrofit-{YYYY-MM-DD}-{capability-or-cluster}` with a spec delta (new REQs) + one task per new REQ. + +Naming convention: `retrofit-{YYYY-MM-DD}-{descriptor}` (date right after `retrofit-`). This matches the date-prefix convention used by non-retrofit OpenSpec changes (e.g. `2026-03-25-contacts-actions/`) so retrofit and non-retrofit changes sort chronologically together when the archive is listed alphabetically. Both changes are archived at the end of the run; annotations reference the ghost change's tasks.md by path. Because `@spec` is a textual reference rather than a live lookup, the path remains valid after archive. Retrofit cohort membership is flagged in spec.md frontmatter (`retrofit: true` or `retrofit_extensions: [REQ-NNN, ...]`) and synced to Specter via `sync_spec_content.py`. @@ -37,7 +39,7 @@ Run them in this exact order. Don't skip the audit step. - App working tree is clean. `/opsx-annotate` and `/opsx-reverse-spec` refuse dirty trees. - You are on a branch that contains the specs. Some apps keep specs on `beta` rather than `development` (e.g. decidesk) — check before scanning. The skills create their own `retrofit/…` feature branch off that one. - Specter DB migration applied: `python3 concurrentie-analyse/scripts/migrate_app_specs_retrofit.py`. Adds `retrofit` + `retrofit_extensions` + `spec_hash` columns to `app_specs`. Idempotent. -- Optional: `{app}/.opsx-ignore` — one glob per line (`#` for comments) for paths the scan should skip. Useful for vendor code, deliberately-unspec'd internal tools. +- Optional: `{app}/.opsx-ignore` — one glob per line (`#` for comments) for paths the scan should skip. Useful for vendor code, deliberately-unspec'd internal tools, demo/example files, generated code. Honored by `/opsx-coverage-scan` (which filters buckets 1/2a/2b/4 — Bucket 3 is REQ-level and unaffected). `/opsx-annotate` and `/opsx-reverse-spec` honor it transitively via the report — if entries change, re-scan before annotating. See `openregister/.opsx-ignore` for a worked example. ## Step-by-step @@ -66,7 +68,7 @@ Plus two meta-buckets the report lists but that need no retrofit action: `annota /opsx-annotate procest ``` -Creates the ghost change `retrofit-annotate-procest-{date}`, generates one task per REQ, adds `@spec openspec/changes/retrofit-annotate-procest-{date}/tasks.md#task-N` tags to every Bucket 1 file + method, archives the ghost change, updates `.git-blame-ignore-revs`, opens an annotation-only PR. +Creates the ghost change `retrofit-{date}-annotate-procest`, generates one task per REQ, adds `@spec openspec/changes/retrofit-{date}-annotate-procest/tasks.md#task-N` tags to every Bucket 1 file + method, archives the ghost change, updates `.git-blame-ignore-revs`, opens an annotation-only PR. Idempotent: re-running with no code changes produces no new annotations. If a dated ghost change already exists, the skill asks whether to reuse it or create a fresh one. Re-running after code changes creates a new dated ghost change. From 2e075f3d48cd2ef453bc1bf72d6c210d0c2ec2a6 Mon Sep 17 00:00:00 2001 From: WilcoLouwerse Date: Tue, 5 May 2026 12:44:03 +0200 Subject: [PATCH 2/3] docs(retrofit): describe /opsx-verify --app mode for retrofit DoD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to ConductionNL/hydra#222 / its fix. App Mode is the canonical retrofit "definition of done" check now — replaces the old line that prescribed /opsx-verify {app} (the skill never had an --app mode and that phrasing led to confusion: the per-change verify mode depends on `openspec status`, which doesn't see archived retrofits). The new "When the retrofit is done" section enumerates each App Mode check (retrofit ghost changes archived; dangling @spec; symlinks; naming; cohort frontmatter; frontmatter format) plus the workflow items that App Mode does not cover (PR merges, Bucket 3/4 triage, post-verify Specter re-sync). Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/claude/retrofit.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/claude/retrofit.md b/docs/claude/retrofit.md index aa1a62fb..02f64815 100644 --- a/docs/claude/retrofit.md +++ b/docs/claude/retrofit.md @@ -134,11 +134,24 @@ After Bucket 2 is drained, Bucket 1 is empty on the next scan, and all code unit ## When the retrofit is done -- `/opsx-verify {app}` succeeds without falling back to keyword search -- `app_specs.retrofit` / `app_specs.retrofit_extensions` populated for the app's retrofit cohort +Run `/opsx-verify --app {app}` and confirm a green report. App Mode is the canonical retrofit DoD audit — it walks every retrofit ghost change under `{app}/openspec/changes/archive/retrofit-*`, scans for dangling `@spec` paths, audits cohort frontmatter, validates naming convention, and prints a single pass/fail report. Do **not** use plain `/opsx-verify {change-name}` for this — that mode verifies a single (active) change against `openspec status`, which doesn't see archived retrofits. + +A retrofit is done when App Mode shows ✅ on every row of: + +- Retrofit ghost changes — all archived +- Tasks completion — every retrofit's tasks all `[x]` +- Dangling `@spec` paths — 0 +- Symlinks under `{app}/openspec/changes/` — 0 +- Naming convention — every retrofit folder matches `retrofit-{YYYY-MM-DD}-{descriptor}` +- Cohort frontmatter — every retrofitted capability carries `retrofit:` or `retrofit_extensions:` on its master spec, in block-YAML form with bare REQ-IDs +- Frontmatter format — block YAML, no inline lists, no full-text values + +Plus the workflow items that App Mode doesn't check: + - Annotation-only PR + per-cluster reverse-spec PRs all merged -- Bucket 3 issues triaged +- Bucket 3 issues triaged (deferred / fixed / re-classified) - Bucket 4 follow-up issue opened +- After all the above: re-run `python3 concurrentie-analyse/scripts/sync_spec_content.py {app}` so Specter's `app_specs.retrofit` / `app_specs.retrofit_extensions` columns are populated for every retrofitted capability ## Roll-out order From e94388e61e16ef6f7170d59f2b93adef5f22387c Mon Sep 17 00:00:00 2001 From: WilcoLouwerse Date: Tue, 5 May 2026 12:54:28 +0200 Subject: [PATCH 3/3] docs(retrofit): add Documentation-only retrofits section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three retrofit sub-patterns add zero new REQs but still produce a ghost change as a documentation artifact: - Cross-capability annotation patch (e.g. b2b-crossrefs) - Private-helper inheritance (e.g. schema-hooks) - Scanner misclassification cleanup (e.g. tenant-isolation-audit) These don't require cohort frontmatter on the affected capabilities — the cohort flag is for REQ provenance, not annotation provenance. Pairs with the /opsx-verify --app classifier fix in ConductionNL/hydra@. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/claude/retrofit.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/claude/retrofit.md b/docs/claude/retrofit.md index 02f64815..6f66885c 100644 --- a/docs/claude/retrofit.md +++ b/docs/claude/retrofit.md @@ -109,6 +109,18 @@ ADR conformance issues are noise during retrofit but worth tracking. Open a foll - Prereq for the retrofit columns: `python3 concurrentie-analyse/scripts/migrate_app_specs_retrofit.py` (idempotent). - Retrofit specs are filterable via `/tender-status --retrofit-only` and `/readiness-report --retrofit-only`. They're necessarily lossy (capture observed behavior, not original intent) and warrant periodic re-review. +## Documentation-only retrofits + +Not every retrofit ghost change adds new REQs. Three sub-patterns produce a ghost change without any spec delta — they are deliberate, supported, and **do not require cohort frontmatter** on the affected capabilities (the cohort flag is for tracking REQ provenance, not annotation provenance): + +| Pattern | When | Example | +|---|---|---| +| **Cross-capability annotation patch** | Bucket 2 cluster's methods turn out to map to *existing* REQs in *other* capabilities. The ghost change is one task per cross-cap reference; annotations point at those existing tasks. | `retrofit-{date}-b2b-crossrefs` — 33 tasks pointing across 15 sibling capabilities. | +| **Private-helper inheritance** | Scanner couldn't follow the call chain into private helpers. Ghost change documents which existing parent task each helper belongs to. No new REQs. | `retrofit-{date}-schema-hooks` — 7 private helpers inherit parent annotate tasks 65/67/68/69. | +| **Scanner misclassification cleanup** | Scanner placed methods under the wrong capability. Ghost change re-routes them to their actual home capabilities (whose REQs already cover the behavior). | `retrofit-{date}-tenant-isolation-audit` — 3 methods actually belong to `tenant-lifecycle` and `tenant-quotas`. | + +The proposal must explicitly say "no new REQs" / "no new REQs needed" / "no new REQs drafted" / "behaviors are fully covered" so `/opsx-verify --app` can detect the pattern. These ghost changes have no `specs/` folder. + ## How Bucket 2 handles un-spec'd methods Bucket 2 is the whole reason retrofit is a three-step flow rather than one. `/opsx-annotate` deliberately does **not** touch methods without a REQ match — it would have nothing to point at. `/opsx-reverse-spec` handles those, one cluster at a time: