The risk-surface interview cannot be re-run, and its menu offers the same set twice #206

Closed
opened 2026-08-17 18:26:49 +00:00 by john · 0 comments
Owner

The one-time risk-surface interview is the only configuration question Cadence asks on its own, and it is the one a user has the least control over: it fires implicitly, once, and there is no way to reach it again.

1. No user-triggerable entry point

The interview lives in cadence-core/references/review-triggers.md:430-465 and fires only from inside a risk_surface fire whose step-1 route.mjs resolve reports surfaces_answered: false. That is deliberate — /cad-new-project and /cad-adopt both forbid configuration questions in their own prose and success criteria (D-15) — but the consequence was not intended: once answered, the set can only be changed by hand-editing review.triggers.risk_surface.surfaces in .planning/config.json, or by deleting the key so the next fire re-asks.

grep -i surface skills/cad-config/SKILL.md cadence-core/workflows/config.md returns one hit, and it is the verb. /cad-config cannot run the interview, cannot show what the scan currently evidences, and cannot re-run it after the repo's structure has changed — which is the normal case: a project that adds Stripe six months in has a billing surface its answered set does not cover, and nothing ever revisits the question.

Wanted: a /cad-config arm that runs detect-surfaces, shows the current answered set beside what the scan now evidences, and offers to re-answer. Re-running must be non-destructive of the existing answer unless the user picks a new one.

2. The menu contains two identical options

lib/surface-scan.mjs's scanTree returns recommended = [...CATEGORIES] unconditionally. The comment above that line gives the reason and the reason is correct: absence is not provable from structure, and framework built-in auth (Django contrib.auth, Rails has_secure_password) ships no separate dependency, so a narrowed recommendation would persist a scope that skips the only blocking review the project has.

review-triggers.md:451 was written against the previous contract and still describes recommended as "what it evidenced, plus the categories no structure can ever evidence (unspeakable)". It then instructs the caller to fill the remaining option slots with "the evidenced categories alone, and all eight."

Since recommended IS all eight, option 1 and that last option are the same list. Rendered against a demo repo (Express + Stripe + Prisma + Passport, with auth/, migrations/, api/, workers/, a .sql file and an openapi.yaml), the scan evidenced six categories and the menu came out as:

  1. All eight (recommended)
  2. The six evidenced only
  3. All eight

The fix is the prose, not the code. The two arms collapse into one: recommended is all eight either way, and inconclusive now changes only the REASON the option states, never the set it offers. evidenced and unspeakable stay in the return and stay in the ask, because they are what that reason is built from.

3. Presentation

The seam already binds this correctly — references/seams.md:12-25 specifies the structured-choice tool, at most four options, recommended first and labelled, never pre-selected. Worth an explicit check that the interview is actually rendered that way at the fire site rather than as prose, since the whole interview is executed by a model reading a reference file and nothing structural enforces the rendering.

Acceptance

  • A user can reach the interview deliberately, see the current answered set and the current scan evidence side by side, and re-answer.
  • The option list contains no two identical sets.
  • review-triggers.md and lib/surface-scan.mjs agree on what recommended contains, with a prose-agreement.test.mjs arm that fails when they drift. The existing check at prose-agreement.test.mjs:180 verifies the category LIST agrees across the schema enum, the route table and the detection list, and verifies nothing about recommended — which is why this drifted silently.
The one-time risk-surface interview is the only configuration question Cadence asks on its own, and it is the one a user has the least control over: it fires implicitly, once, and there is no way to reach it again. ## 1. No user-triggerable entry point The interview lives in `cadence-core/references/review-triggers.md:430-465` and fires only from inside a `risk_surface` fire whose step-1 `route.mjs resolve` reports `surfaces_answered: false`. That is deliberate — `/cad-new-project` and `/cad-adopt` both forbid configuration questions in their own prose and success criteria (D-15) — but the consequence was not intended: once answered, the set can only be changed by hand-editing `review.triggers.risk_surface.surfaces` in `.planning/config.json`, or by deleting the key so the next fire re-asks. `grep -i surface skills/cad-config/SKILL.md cadence-core/workflows/config.md` returns one hit, and it is the verb. `/cad-config` cannot run the interview, cannot show what the scan currently evidences, and cannot re-run it after the repo's structure has changed — which is the normal case: a project that adds Stripe six months in has a `billing` surface its answered set does not cover, and nothing ever revisits the question. Wanted: a `/cad-config` arm that runs `detect-surfaces`, shows the current answered set beside what the scan now evidences, and offers to re-answer. Re-running must be non-destructive of the existing answer unless the user picks a new one. ## 2. The menu contains two identical options `lib/surface-scan.mjs`'s `scanTree` returns `recommended = [...CATEGORIES]` unconditionally. The comment above that line gives the reason and the reason is correct: absence is not provable from structure, and framework built-in auth (Django `contrib.auth`, Rails `has_secure_password`) ships no separate dependency, so a narrowed recommendation would persist a scope that skips the only blocking review the project has. `review-triggers.md:451` was written against the previous contract and still describes `recommended` as "what it evidenced, plus the categories no structure can ever evidence (`unspeakable`)". It then instructs the caller to fill the remaining option slots with "the evidenced categories alone, and all eight." Since `recommended` IS all eight, option 1 and that last option are the same list. Rendered against a demo repo (Express + Stripe + Prisma + Passport, with `auth/`, `migrations/`, `api/`, `workers/`, a `.sql` file and an `openapi.yaml`), the scan evidenced six categories and the menu came out as: 1. All eight (recommended) 2. The six evidenced only 3. All eight The fix is the prose, not the code. The two arms collapse into one: `recommended` is all eight either way, and `inconclusive` now changes only the REASON the option states, never the set it offers. `evidenced` and `unspeakable` stay in the return and stay in the ask, because they are what that reason is built from. ## 3. Presentation The seam already binds this correctly — `references/seams.md:12-25` specifies the structured-choice tool, at most four options, recommended first and labelled, never pre-selected. Worth an explicit check that the interview is actually rendered that way at the fire site rather than as prose, since the whole interview is executed by a model reading a reference file and nothing structural enforces the rendering. ## Acceptance - A user can reach the interview deliberately, see the current answered set and the current scan evidence side by side, and re-answer. - The option list contains no two identical sets. - `review-triggers.md` and `lib/surface-scan.mjs` agree on what `recommended` contains, with a `prose-agreement.test.mjs` arm that fails when they drift. The existing check at `prose-agreement.test.mjs:180` verifies the category LIST agrees across the schema enum, the route table and the detection list, and verifies nothing about `recommended` — which is why this drifted silently.
john added this to the v3.5.7 milestone 2026-08-18 10:23:31 +00:00
john closed this issue 2026-08-22 11:45:37 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: crenshawdev/cadence-archived#206
No description provided.