Skip to content

feat(settings): a Decisions preview switch that writes config, not localStorage - #11495

Merged
bolichen97 merged 1 commit into
mainfrom
feat/decisions-preview-card
Sep 18, 2026
Merged

bolichen97 merged 1 commit into
mainfrom
feat/decisions-preview-card

Conversation

@iamwhatever

@iamwhatever iamwhatever commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Problem / Motivation

There is a new shadow-mode feature coming — Kiro Crew asking a cheap decision model called Jev a yes-or-no or pick-one question at three small moments (which skills to load, whether a skill it just learned repeats one you have, whether a scheduled job's result says anything new). It needs an off switch a user can see.

Every existing switch in Settings > Developer > Feature Previews is a browser localStorage key. This one cannot be. The gate that decides whether to ask anything runs in the gateway, and the gateway cannot read your browser's storage.

Why it matters

The switch turns on something that sends your message text off this machine. A feature like that needs a visible switch, in the place a user already looks for unreleased things, that says in plain words what leaves the machine and where it goes — not a hand edit of config.json.

What changed (motivation → approach → change)

The card writes the gateway's config instead of this device's storage.

Four existing cards Decisions (Jev)
Where the switch writes 🟦 localStorage (previewFlags.ts) 🟩 config.json decisions.preview
Who can read it 🟦 this browser 🟩 the gateway, which is what gates the feature
Route 🟦 none 🟩 PATCH /api/config/kirocrew
Gateway without the field 🟦 n/a 🟩 switch disabled, reason beside it

🟩 added · 🟦 unchanged

The switch reads the shared ['kirocrewConfig'] cache and writes through the same route the Privacy panel's telemetry switch uses, so it cannot disagree with the file about its own state. That is the whole reason it is config and not a preview flag. It stays closed to input until the write is reflected in a fresh read, so a flip never looks like it failed while the old value is still on screen.

Nothing reads decisions.preview yet. The gate, the config section and the schema entry all arrive in a separate backend PR, and the code says so rather than naming a module that does not exist. So every gateway answers the config read without a decisions section and would refuse the write as a non-editable field: the switch renders disabled with the reason beside it. A read that simply failed says something different, because an old gateway needs an update and a failed read needs a retry.

flowchart LR
  R[read config] --> Q{decisions<br/>section?}
  Q -->|no| D[disabled<br/>update the gateway]
  Q -->|read failed| E[disabled<br/>reload to retry]
  Q -->|yes| S[switch live]
  S -->|flip| P[PATCH decisions.preview]
  P --> R
  classDef added fill:#DCFCE7,stroke:#16A34A
  class R,Q,D,E,S,P added
Loading

🟩 added · 🟦 unchanged

The copy names the destination in human terms rather than a config path, expands the Jev codename in its first sentence, and states persistence the way a user meets it: saved on the machine that runs Kiro Crew, so it holds on every device you open it from. The egress sentence carries body weight rather than muted fine print, because that is the fact a reader is consenting to. Each check's mode is shown read-only: which mode a check runs in is an experiment setting, and three more switches would imply this release can act on what Jev answers, which it cannot.

The toggle carries no configKey, unlike other config-backed toggles, and that omission is a guard switched off rather than merely absent: test_settingref_schema_fixture.py asserts every generated key exists in the backend SCHEMA_REGISTRY, so naming a path the schema does not have yet would fail on main. Issue #11510 tracks re-engaging it once the backend lands, and the code names that issue.

Two claims elsewhere stopped being true and are corrected: the section's once-per-section caveat called every switch here per-device, and the feature map's developer row called every preview a client flag.

Seven new strings go through i18n into every catalog under website/src/i18n/localescatalogParity fails on any locale that misses a key or spells a placeholder a different number of times — and en-XA is regenerated. Five locale style gates ruled on the new values: French takes U+202F before :, Bengali addresses the reader as তুমি, Hindi ends sentences on purna viram, Korean writes a 조사 after an interpolation in both forms, and German's Sie <verb> rule sent one sentence's subject to lower case.

Adding a Settings primitive means regenerating both settings registries: settingsRegistry.gen.ts for Settings search, and src/kiro_crew/docs/settings-registry.generated.json, the copy a deployed gateway hands the agent when someone asks where a setting is.

Tests

decisionsPreview.test.ts pins the config reader: an absent decisions section reads as unsupported and never as off, an unresolved or failed read reads the same way, a section present with no flag reads as off, and only an exact true turns the preview on — a hand-edited "true" or 1 is not consent to send message text off the machine. It also pins which checks get a row and that a check with no mode gets none.

decisionsCard.test.tsx pins the card through the real section: disabled while the read is pending, disabled and naming the gateway's age when the section is absent, disabled and naming the read failure when the read fails, the exact config path written on a flip in both directions, a refused write reported with the switch back on the stored value, the egress sentence present in every state, and no preview-flag key written. One case holds the post-write read open to pin the window in between: while the switch still shows the stored value it must refuse input, or the flip reads as having failed and a second click writes it again.

previewSurfaces.test.tsx, FeaturePreviewIntro.test.tsx and DeveloperPanel.test.tsx all mount the section — the last one through the panel, without naming it — so each gets a query client and a stubbed config read. The anchor test's switch count rises to five, which is the guard that catches a card added outside the ring.

capture-decisions-preview-card.mjs is the screenshot harness, and it asserts rather than only capturing: each frame's own last element must sit inside the viewport, each check row must have rendered, and the refused-write frame must show the switch back on the stored value. A capture script that only writes PNGs fails toward a false pass.

Manual verification

The seven frames below are the harness output against the real built SPA, gateway-free, with the config and the write answered from fixtures — one fixture carrying the decisions section, one without, one failing the read, one refusing the write.

Screenshots / video

The card with the section present, flag off, all three checks and their modes in frame:

Decisions card, light, flag off, three checks with their modes

Decisions card, dark, flag off, three checks with their modes

The state every gateway is in today — no decisions section, so the switch refuses and says why:

Decisions card disabled, saying the gateway is older than this switch

Flag on, the two failure states, and the Settings search hit

Decisions card with the flag on

The config read failed — a different note from the one above, because the fix is a retry rather than an update:

Decisions card disabled after a failed config read

The write was refused, and the switch is back on the stored value rather than showing the click:

Decisions card reporting a refused save with the switch off

Settings search reaching the Decisions toggle

Related Issues

no linked issue closed here: the switch is one slice of the DecisionOracle work, and the backend that reads decisions.preview is a separate PR (feat/decisions-oracle, in flight). Until it lands the switch renders disabled, which is what the third screenshot shows. #11510 tracks re-engaging the schema guard afterwards and is deliberately left open.

Checklist

  • At most two commits (one is the norm), with a Conventional Commits title (feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated (if applicable)
  • No secrets, credentials, or internal references in the diff

@iamwhatever
iamwhatever requested a review from a team September 17, 2026 09:14
@iamwhatever
iamwhatever requested a review from a team as a code owner September 17, 2026 09:14
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Sep 17, 2026
@iamwhatever

Copy link
Copy Markdown
Collaborator Author

Intent: Give the upcoming DecisionOracle shadow feature a visible off switch in the place users already look for unreleased things, and make that switch write the gateway's own config rather than this device's storage — because the gate that reads the flag runs in the gateway and cannot see browser storage. The card must ship inert and say why on every gateway that has no decisions section yet.

Not a goal: Any backend. No decisions config section, no gate, no provider call, no logging — those are a separate PR (feat/decisions-oracle). Not editing the per-point arms from the UI: they are displayed read-only, because this release cannot act on what the model answers. Not adding a configKey/SettingRef deep link, which cannot exist until the schema entry does. Not touching the four existing localStorage previews.

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — 🟡 CONCERNS

Design-level review of bfcf16abf904377d86e36e655eb045dcb515218c — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design-Verdict: CONCERNS

The config contract (decisions.preview, points.<point>.arm, three point names) is authored frontend-first with its cross-layer guard deliberately off, so a mismatch fails silently.

Watch

The only mechanism that would catch the backend landing with a different path or shape — configKey + test_settingref_schema_fixture.py — is the thing this PR switches off ("that omission is a guard switched off"), and the failure mode is indistinguishable from the designed "old gateway" state: the switch stays disabled forever, nothing goes red, and the decisions_backend_required note tells users to "Update it first" — an instruction that is false in any release cut before feat/decisions-oracle ships.
Clears when: the backend PR lands carrying the exact shape authored here and #11510 re-engages configKey so the fixture guard holds the halves together.

Suggestions

  • Sequence the release so a dashboard carrying this card never ships against a gateway line that cannot satisfy "Update it first" — merge is fine, but hold the release train until feat/decisions-oracle is in it, or soften that string to name a future version.

[DESIGN-REVIEWED] bfcf16a

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of bfcf16abf904377d86e36e655eb045dcb515218c and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] bfcf16a

False positive or not applicable? A repository writer can comment:
/ai-review override gpt bfcf16abf904377d86e36e655eb045dcb515218c: <one-sentence reason>

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of bfcf16abf904377d86e36e655eb045dcb515218c — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

First-Principles-Verdict: CONCERNS

The entire card gates a backend that is not on main — zero readers of decisions.preview, so every user's switch renders disabled until feat/decisions-oracle merges.

Not justified as shipped

  • Items 1–3 — zero consumers: the description concedes it — "Nothing reads decisions.preview yet" — and a grep for decisions\.preview across src/kiro_crew counts 0 readers, so today the switch, the config path and the arm rows remove no harm: every gateway answers without a decisions section and the card's only reachable state is disabled, with an "update it first" note that is false until the backend releases. The consent-before-egress ordering the copy implies is had more strongly by shipping the card inside the backend PR than by merging it ahead.
    Subtraction: defer — move DecisionsPreviewCard, decisionsPreview.ts, the seven i18n keys × 13 catalogs, both registry entries and the capture harness into feat/decisions-oracle.
    Clears when: a backend reading decisions.preview is merged on main (a counted consumer), or the maintainers accept the staged split as-is.

What this change ships

Inventory (10 items) — 7 justified

Intent: give the upcoming Jev shadow-decisions feature a visible, gateway-side off switch before it exists — an ADDITION.

  1. A fifth Feature Previews card, "Decisions (Jev)", whose switch is gateway config — zero consumers (see above)
  2. A new config path decisions.preview written via the shared PATCH route — zero consumers (see above)
  3. Read-only rows showing each decision point's arm — zero consumers (see above)
  4. The switch refuses input with distinct notes: gateway too old, read failed, write refused — justified
  5. The toggle reachable through Settings search via both regenerated registries — justified
  6. An egress sentence stating message text leaves the machine, shown in every state — justified
  7. Section caveat now reads "per-device unless a card says otherwise", in 13 catalogs — justified
  8. Feature-map developer row stops calling every preview a client flag — justified
  9. A screenshot harness that asserts framing and state rather than only capturing — justified
  10. The anchor guard's switch count raised to five — justified

[FIRST-PRINCIPLES-REVIEWED] bfcf16a

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed bfcf16abf904377d86e36e655eb045dcb515218c — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] bfcf16a

Verdict parsed from the review's SHA-scoped output markers for commit bfcf16abf904377d86e36e655eb045dcb515218c.

False positive or not applicable? A repository writer can comment:
/ai-review override fable bfcf16abf904377d86e36e655eb045dcb515218c: <one-sentence reason>

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

UX-level review of bfcf16abf904377d86e36e655eb045dcb515218c — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

UX-Verdict: CONCERNS

The consent moment stalls: the egress copy names no destination, and both repair paths ("Reload", "Update it first") are prose with no action.

Watch

  • The egress sentence says "sent over the internet to the decision service this Kiro Crew is set up to use" — no name, no link. The reader understood it ("sure about that") yet refused the flip: "I'd want to understand that first." Every user hits this at the exact opt-in moment, every time; the card offers no way to learn what the service is. Fix: name the configured service (or link where it's set) in decisions_egress.
  • decisions_config_unavailable ends "Reload to try again." as plain text; the reader: "I could not tell if anything there is actually clickable besides 'Ask the agent'." Siblings (KnowledgePicker, TaskDetailPanel) ship onRetry; recovery here demands a full page refresh. Fix: an inline retry that refetches ['kirocrewConfig'].
  • decisions_backend_required ends "Update it first." with no path — reader: "I don't see where 'update it' happens." Fix: link the sentence to Settings › Releases, where updating lives.

Suggestions

  • The arm value shadow is bare config vocabulary ("I don't know what 'shadow' means here"); append a plain-word gloss to decisions_points, e.g. "shadow = logged only, never acted on."

[UX-REVIEWED] bfcf16a

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 17, 2026
@iamwhatever
iamwhatever force-pushed the feat/decisions-preview-card branch from d8c8b7d to a3d1c9d Compare September 17, 2026 10:11
@iamwhatever

Copy link
Copy Markdown
Collaborator Author

self-added: no
mechanism: card copy rewritten across all 14 catalogs; no new code path

span=a54c634103a4

  • Primary control fails the dare test — fixed in a3d1c9d.

Legitimate on all three counts, and the reader's own words name each one.
Egress: the line pointed at a dotted config key. Nobody can consent to
decisions.provider. It now says the text and the skill descriptions leave this
machine and go over the internet to the decision service this Kiro Crew is set
up to use, and that nothing is sent while it is off.
Codename: expanded, not dropped. The description now opens "Jev is a small,
cheap decision model", so the label's word means something by the time the
reader reaches the switch.
Persistence: "this instance's configuration" is gone. It reads "saved on the
machine that runs Kiro Crew, not in this browser, so it applies on every device
you open it from" — the user-visible consequence rather than the storage layer.
The section-wide caveat dropped the same "instance" wording, since the card now
explains its own scope.

@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 17, 2026
@iamwhatever

Copy link
Copy Markdown
Collaborator Author

self-added: no
mechanism: none — copy only

span=36d0697758c4

  • decisions_points wording — fixed in a3d1c9d, taking the suggested line.

Adopted as written: "Each check below shows the mode it runs in." The mono
config values stay, as asked.
arm and point were my vocabulary, not the reader's, so they left the prose
everywhere rather than only in this one string — the card, the tests and the
feature-map row now say "check" and "mode" too. The catalog KEYS keep their
decisions_points spelling on purpose: renaming a key across fourteen catalogs
buys no user-visible change.

@iamwhatever

Copy link
Copy Markdown
Collaborator Author

self-added: no
mechanism: tracking issue #11510 + a code comment naming it

span=a4ad832dba77

  • Nothing schedules the configKey's return — fixed in a3d1c9d by scheduling it.

The item is right that the omission was justified and unscheduled, which are two
different things. Issue #11510 now carries it: open, labelled
deferred-finding, assigned, Due: 2026-10-01, and it names the four steps
including re-running gen:settings so the fixture guard is proven ENGAGED rather
than the prop merely present.
The Watch item's own clause about a renamed section is covered by step 4 of that
issue: DECISION_POINTS is checked against the merged backend's spellings, not
assumed.
The code says it too, not just this thread — the card's header now names #11510,
because the next reader of the file does not read the PR.

@iamwhatever

Copy link
Copy Markdown
Collaborator Author

self-added: no
mechanism: none in this PR — the ask lands in another PR's tests

span=52fcf3b0c21c

Agreed and worth doing, but not doable here: the ask is about a test in a PR this
one does not contain, so nothing in this diff can carry it.
Folded into #11510 rather than dropped — its step 4 checks DECISION_POINTS
against the merged backend's spellings, which is the same protection from the
other side and does not depend on the other PR's author reading this thread.
Issue #11510 is open, labelled deferred-finding, assigned, Due: 2026-10-01.

@iamwhatever

Copy link
Copy Markdown
Collaborator Author

self-added: no
mechanism: none added; a false code comment removed

span=042c75028efa

  • Item 1, zero consumers of decisions.preview — the count is right and the comment that hid it is fixed in a3d1c9d.

The measurement stands and I am not arguing with it: zero readers on base,
_EDITABLE_CONFIG names no decisions path, and decisions/gate.py does not
exist. That last one was a defect in MY comment, not a gap in the lane's reading
— the header named a module in the present tense that is not on main, which is
the same error class as citing a substitute nobody confirmed exists. Both
headers now say plainly that nothing reads this value yet and that the gate,
the section and the schema entry all arrive in a separate PR.
On ordering: the linked record is the goal this PR was dispatched from, which
splits the work into one frontend PR and one backend PR built in parallel, with
the frontend required to compile and test green on main ALONE. That is why the
switch ships inert and self-describing rather than waiting.
What the PR does not claim is that it is useful yet. The card's own copy, the
feature-map row and the third screenshot all say the switch is off and why.

@iamwhatever

Copy link
Copy Markdown
Collaborator Author

self-added: no
mechanism: tracking issue #11510 + a code comment naming it

span=f43af3ea00df

Correct that this is a rule bent to survive an ordering, and correct that it
cannot clear here: the clause names a base whose SCHEMA_REGISTRY has the key,
and adding it against today's main fails test_settingref_schema_fixture.py on
every commit. That failure is the rule working, so I am not weakening it.
What I had wrong was calling the omission merely absent. It is a GUARD SWITCHED
OFF for one control, and until this round nothing switched it back on. #11510 now
does: open, labelled deferred-finding, assigned, Due: 2026-10-01, and its
deliverable is the guard covering the key, not the prop being present.
The card's header names that issue, so the next reader of the file meets the debt
where the code is rather than in this thread.

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 17, 2026
@iamwhatever

Copy link
Copy Markdown
Collaborator Author

Readiness is red on a main-side failure, not on anything in this diff.

Backend Tests (3.12, 3) fails one node id:

test/test_pod_windows_run.py::test_public_up_prepares_home_only_after_start_reservation[False]
kiro_crew.pod.windows.WindowsTaskError: the Task Scheduler pod backend is win32-only; this host is linux.

Reproduced on a pristine origin/main worktree at 65b08d3f96, with no part of this branch applied: same node id, same error, same [False]-fails / [True]-passes split. This PR changes no Python at all — its only file under src/ is the generated settings-registry.generated.json.

The other three reds are consequences of that one:

Red Why
Backend Tests (3.12, 3) 🟥 the main-side test above
Backend Tests (3.12, 1) 🟨 cancelled as a fail-fast sibling of shard 3
Coverage Gate 🟨 requires the upstream coverage jobs to have succeeded
PR Readiness 🟨 aggregate of the above

🟥 real · 🟨 cascade

The fix is already open as #11518 (test(pod): stub require_backend in the Windows-run fixture, two lines in the fixture). So this branch is not patching it: a main-side failure fixed inside the PR that merely inherited it puts an unrelated file in this diff and leaves the next PR to hit it again. This branch will rebase once #11518 lands.

Everything else on this head is green, including all three whole-design lanes and both line-level reviewer lanes.

…calStorage

Settings > Developer > Feature Previews gains a fifth card, "Decisions (Jev)".
It is the first card in that section whose switch is a `config.json` value
(`decisions.preview`) rather than a per-device `previewFlags.ts` key, because the
gate that acts on it runs in the gateway and cannot read this browser's
localStorage.

The switch writes through the existing `PATCH /api/config/kirocrew` route and
reads the shared `['kirocrewConfig']` cache, so it cannot disagree with the file
about its own state. A gateway whose config has no `decisions` section — every
gateway until the backend PR lands — renders the switch disabled with the reason
beside it instead of offering a write that comes back 400.

The card carries the egress fact in body weight, three read-only rows for the
point arms when the config exposes them, and no `configKey`: that prop feeds
`settingsRegistry.gen.ts`, whose every key is asserted to exist in the backend
`SCHEMA_REGISTRY`.

The section's once-per-section caveat no longer claims every switch here is
per-device, which stopped being true with this card.
@iamwhatever
iamwhatever force-pushed the feat/decisions-preview-card branch from a3d1c9d to bfcf16a Compare September 17, 2026 15:28
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 17, 2026
@github-actions github-actions Bot added the readiness: passed Eligible automated validation passed for the current revision label Sep 17, 2026
@bolichen97
bolichen97 merged commit e767248 into main Sep 18, 2026
82 checks passed
@bolichen97
bolichen97 deleted the feat/decisions-preview-card branch September 18, 2026 00:28
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants