geneseed 4.9.3 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +14 -1
  3. package/SHIPPED.md +1 -1
  4. package/adapters/claude-code/README.md +1 -1
  5. package/adapters/opencode/plugins/geneseed-guard.js +20 -1
  6. package/docs/README.md +1 -1
  7. package/docs/concepts/collaboration.md +1 -1
  8. package/docs/concepts/enforcement-table.md +2 -2
  9. package/docs/concepts/foreman-mode.md +1 -1
  10. package/docs/concepts/hooks.md +3 -2
  11. package/docs/concepts/kits.md +79 -0
  12. package/docs/concepts/loop-branch.md +1 -1
  13. package/docs/concepts/loop-bricks.md +1 -1
  14. package/docs/concepts/loop-contracts.md +1 -1
  15. package/docs/concepts/loop-gates.md +1 -1
  16. package/docs/concepts/loop-notes.md +1 -1
  17. package/docs/concepts/loop-risk.md +1 -1
  18. package/docs/concepts/loop-templates.md +1 -1
  19. package/docs/concepts/loops.md +1 -1
  20. package/docs/concepts/lsp.md +1 -1
  21. package/docs/extending.md +108 -4
  22. package/docs/guides/install-scripted.md +1 -1
  23. package/docs/reference/glossary.md +1 -0
  24. package/docs/reference/plugin-guard.md +1 -1
  25. package/docs/understand/enforced-vs-asked.md +1 -1
  26. package/docs/understand/machine-claude.md +1 -1
  27. package/docs/web-ui.md +4 -0
  28. package/harness.config.json +1 -1
  29. package/js/build/args.mjs +27 -6
  30. package/js/build/driver.mjs +29 -8
  31. package/js/build/emit-claude.mjs +3 -1
  32. package/js/build/generate.mjs +10 -5
  33. package/js/build/render.mjs +94 -11
  34. package/js/build/source.mjs +117 -4
  35. package/js/cli-table.json +15 -0
  36. package/js/hosts/hooks.mjs +2 -1
  37. package/js/hosts/installs.mjs +35 -2
  38. package/js/hosts/loss.mjs +23 -2
  39. package/js/hosts/native.mjs +14 -4
  40. package/js/inspect/checks-authoring.mjs +6 -3
  41. package/js/inspect/checks-build.mjs +7 -1
  42. package/js/inspect/checks-counts.mjs +153 -6
  43. package/js/inspect/checks-repo.mjs +95 -20
  44. package/js/inspect/cost.mjs +1 -1
  45. package/js/inspect/diff.mjs +3 -1
  46. package/js/inspect/inventory.mjs +1 -0
  47. package/js/inspect/status.mjs +44 -3
  48. package/js/inspect/validate.mjs +3 -3
  49. package/js/maintain/setup.mjs +30 -6
  50. package/js/maintain/update.mjs +1 -1
  51. package/js/web/actions.mjs +30 -6
  52. package/js/web/api.mjs +40 -3
  53. package/js/web/routes.mjs +2 -1
  54. package/package.json +1 -1
  55. package/registry.json +63 -0
  56. package/src/AGENT.md.tmpl +2 -2
  57. package/src/agents/devops-engineer.md +45 -0
  58. package/src/agents/docker-expert.md +45 -0
  59. package/src/agents/kubernetes-specialist.md +45 -0
  60. package/src/agents/reviewer.md +2 -0
  61. package/src/agents/spring-boot-engineer.md +48 -0
  62. package/src/agents/spring-reviewer.md +49 -0
  63. package/src/agents/spring-security.md +50 -0
  64. package/src/kits/spring-boot/rules.md +10 -0
  65. package/src/kits/spring-boot.json +23 -0
  66. package/src/laws/universal.md +13 -13
  67. package/src/licenses/mattpocock-skills.txt +21 -0
  68. package/src/licenses/piomin-spring-boot.txt +190 -0
  69. package/src/licenses/superpowers.txt +21 -0
  70. package/src/licenses/vibe-wise.txt +21 -0
  71. package/src/skills/brainstorm/builder-check.md +2 -0
  72. package/src/skills/brainstorm.md +4 -0
  73. package/src/skills/codebase-design.md +4 -0
  74. package/src/skills/daydream/VENDOR.md +7 -7
  75. package/src/skills/debug.md +5 -1
  76. package/src/skills/develop.md +2 -0
  77. package/src/skills/geneseed-code-review.md +6 -2
  78. package/src/skills/git-history.md +2 -0
  79. package/src/skills/handoff.md +2 -0
  80. package/src/skills/learn-mode.md +3 -2
  81. package/src/skills/parallel-agents.md +2 -0
  82. package/src/skills/pipeline.md +2 -0
  83. package/src/skills/plan.md +4 -0
  84. package/src/skills/prototype.md +2 -0
  85. package/src/skills/react-view-transitions/AGENTS.md +646 -221
  86. package/src/skills/react-view-transitions/SKILL.md +28 -16
  87. package/src/skills/react-view-transitions/VENDOR.md +10 -8
  88. package/src/skills/react-view-transitions/references/css-recipes.md +72 -2
  89. package/src/skills/react-view-transitions/references/implementation.md +14 -26
  90. package/src/skills/react-view-transitions/references/nextjs.md +94 -37
  91. package/src/skills/react-view-transitions/references/patterns.md +60 -48
  92. package/src/skills/react-view-transitions/references/troubleshooting.md +47 -0
  93. package/src/skills/research.md +2 -0
  94. package/src/skills/review-response.md +2 -0
  95. package/src/skills/rule.md +2 -0
  96. package/src/skills/ship.md +2 -0
  97. package/src/skills/skill-forge.md +1 -0
  98. package/src/skills/spring-boot/cloud.md +103 -0
  99. package/src/skills/spring-boot/java-modern.md +71 -0
  100. package/src/skills/spring-boot/logging.md +89 -0
  101. package/src/skills/spring-boot/security.md +112 -0
  102. package/src/skills/spring-boot/web.md +112 -0
  103. package/src/skills/spring-boot.md +57 -0
  104. package/src/skills/spring-data-jpa/patterns.md +68 -0
  105. package/src/skills/spring-data-jpa.md +81 -0
  106. package/src/skills/spring-testing.md +93 -0
  107. package/src/skills/wayfinder.md +2 -0
  108. package/src/upstreams.json +59 -0
  109. package/themes/_TEMPLATE.json +9 -0
  110. package/themes/biker.json +9 -0
  111. package/themes/commentator.json +9 -0
  112. package/themes/cyberpunk.json +9 -0
  113. package/themes/gamer.json +9 -0
  114. package/themes/imperial.json +9 -0
  115. package/themes/joker.json +9 -0
  116. package/themes/marvin.json +9 -0
  117. package/themes/mean.json +9 -0
  118. package/themes/military.json +9 -0
  119. package/themes/neutral.json +9 -0
  120. package/themes/pirate.json +9 -0
  121. package/themes/sports.json +9 -0
  122. package/themes/verstappen.json +9 -0
  123. package/themes/wizard.json +9 -0
  124. package/web/dist/assets/{Activity-C8sZVIUU.js → Activity-BjyJaf8e.js} +1 -1
  125. package/web/dist/assets/{ActivityDetail-Cc6vWUS4.js → ActivityDetail-Bo9nRDNV.js} +1 -1
  126. package/web/dist/assets/{Dashboard-C5ATa3ow.js → Dashboard-BQJGU7vE.js} +2 -2
  127. package/web/dist/assets/{Docs-tDW7i7mo.js → Docs-DLVckkew.js} +1 -1
  128. package/web/dist/assets/{GreenhouseView-zk2Uzc3A.js → GreenhouseView--DML7CW3.js} +1 -1
  129. package/web/dist/assets/{Installs-CB_rvbBP.js → Installs-CxCiln1F.js} +2 -2
  130. package/web/dist/assets/{JournalView-Bfj27XHC.js → JournalView-ymgZleHi.js} +1 -1
  131. package/web/dist/assets/{Laws-BA3CzUf5.js → Laws-BkKnZOBI.js} +1 -1
  132. package/web/dist/assets/Library-C8ercIZv.js +1 -0
  133. package/web/dist/assets/{Loops-DaPk-ckL.js → Loops-C95zhbFl.js} +1 -1
  134. package/web/dist/assets/{OperatorHudView-CyiGhiHk.js → OperatorHudView-B9qwc3w3.js} +1 -1
  135. package/web/dist/assets/{Personal-Bwd27CmT.js → Personal-DyR72AF-.js} +1 -1
  136. package/web/dist/assets/{Tabs-BOD7Kaz1.js → Tabs-ChCr_HqV.js} +1 -1
  137. package/web/dist/assets/api-BfpZmUMI.js +1 -0
  138. package/web/dist/assets/{drawable-CyLe1gtW.js → drawable-CgYi0Dtg.js} +1 -1
  139. package/web/dist/assets/index-B32EvfDT.js +2 -0
  140. package/web/dist/index.html +2 -2
  141. package/web/dist/assets/Library-BWwiy9Vb.js +0 -1
  142. package/web/dist/assets/api-CiXlkbe0.js +0 -1
  143. package/web/dist/assets/index-DQXG_mqo.js +0 -2
@@ -1,5 +1,7 @@
1
1
  # Patterns and Guidelines
2
2
 
3
+ Use the official [React `<ViewTransition>` reference](https://react.dev/reference/react/ViewTransition) for API mechanics. This file collects reusable implementation patterns and failure modes from production apps.
4
+
3
5
  ## Searchable Grid with `useDeferredValue`
4
6
 
5
7
  `useDeferredValue` makes filter updates a transition, activating `<ViewTransition>`:
@@ -114,38 +116,74 @@ Use `key` when content identity changes (state resets). Omit for cross-fades (ta
114
116
 
115
117
  ## Isolate Elements from Parent Animations
116
118
 
117
- ### Persistent Layout Elements
119
+ Pull an element out of the animated `root` snapshot by giving it its own `view-transition-name`. **`view-transition-name: none` is a no-op** — it's the CSS default, so the element stays in `root` (a common flicker bug). Use a real, unique name, then neutralize with `<ViewTransition default="none">` (no CSS) or CSS (needed for `z-index`/`display` control — see [css-recipes.md](css-recipes.md#persistent-element-isolation)).
120
+
121
+ - **Persistent chrome** (nav, sidebar, player bar): `<nav style={{ viewTransitionName: 'persistent-nav' }}>` + isolation CSS. `<ViewTransition default="none">` works too, but its auto-name can't take `z-index`/backdrop `display:none` — hand-name when you need those.
122
+ - **Floating elements** (popovers, menus): left open, they're captured in `root` and flicker on settle. Real name + isolation ([Floating Element Isolation](css-recipes.md#floating-element-isolation-popovers-menus-tooltips-control-clusters)). A static name is fine if only one is mounted (`unmountOnHide`); native top-layer (`popover`/`<dialog>`) settle-flicker is a browser limit.
123
+ - **Naming an interactive element has a cost:** named participants are skipped by hit-testing while a transition runs ([csswg#10930](https://github.com/w3c/csswg-drafts/issues/10930)) — clicks and hover fall through to whatever is beneath. Portal named popovers/menus; rendered inline in a clickable row, mid-transition clicks activate the row and read as outside-clicks that close the popover.
124
+ - **Third-party floating components** (toast libraries, portals you don't render): put the name on an always-mounted wrapper you own — `<div style={{ viewTransitionName: 'toaster' }} className="pointer-events-none fixed inset-0">`. Library containers often unmount when empty, so naming them directly leaves the group unpinned exactly when a toast appears mid-transition. Name a dialog's backdrop separately from its panel so each pins independently.
118
125
 
119
- Persistent elements (headers, navbars, sidebars) get captured in the page's transition snapshot. Fix with `viewTransitionName`:
126
+ ## Suspense reveal flicker
127
+
128
+ An element rendered in **both** the fallback and the content flickers (opacity dip) on reveal — it fades against itself. Not a morph. **Fix: render it outside the `<Suspense>` boundary** (mount once, above it), or pin it with a `view-transition-name`.
120
129
 
121
130
  ```jsx
122
- <nav style={{ viewTransitionName: "persistent-nav" }}>{/* ... */}</nav>
131
+ <h1>{title}</h1>
132
+ <Suspense fallback={<BodySkeleton />}><Body /></Suspense>
123
133
  ```
124
134
 
125
- Then add the persistent element isolation CSS from `css-recipes.md`. For `backdrop-blur`/`backdrop-filter`, use the backdrop-blur workaround from `css-recipes.md`.
135
+ Don't put a manual `viewTransitionName` on the root DOM node inside `<ViewTransition>` — React's auto-name overrides it.
126
136
 
127
- ### Floating Elements
137
+ ## Sliding Indicator (tabs)
128
138
 
129
- Give popovers/tooltips their own `viewTransitionName`:
139
+ One shared-name indicator rendered under the **active** tab morphs between positions on change (slide the group, disable old/new — see [Sliding Indicator](css-recipes.md#sliding-indicator-tab-underline--segmented-pill)). Render it only under the active tab so exactly one element holds `indicatorName`; use a distinct `indicatorName` per tab strip. Trigger the state change inside `startTransition` so the move animates. Whatever owns `active` drives it — local state here, routing in Next (see [Routing-Driven Tabs](nextjs.md#routing-driven-tabs)).
130
140
 
131
- ```jsx
132
- <SelectPopover style={{ viewTransitionName: 'popover' }}>{options}</SelectPopover>
141
+ ```tsx
142
+ import { useState, useTransition, ViewTransition } from 'react';
143
+
144
+ export function Tabs({ tabs, indicatorName = 'tab-indicator' }) {
145
+ const [active, setActive] = useState(tabs[0].value);
146
+ const [, startTransition] = useTransition();
147
+ return (
148
+ <nav>
149
+ {tabs.map(t => (
150
+ <button key={t.value} type="button"
151
+ aria-current={active === t.value ? 'page' : undefined}
152
+ onClick={() => startTransition(() => setActive(t.value))}>
153
+ <span>{t.label}</span>
154
+ {active === t.value && (
155
+ <ViewTransition name={indicatorName} share="tab-underline">
156
+ <span className="active-underline" aria-hidden />
157
+ </ViewTransition>
158
+ )}
159
+ </button>
160
+ ))}
161
+ </nav>
162
+ );
163
+ }
133
164
  ```
134
165
 
135
- Global fix: see persistent element isolation in `css-recipes.md`.
166
+ Because the state change is a transition, if the newly-active tab renders suspending content the whole update — indicator **and** `aria-current` — waits for it to commit, and the strip feels dead on click. Give the controls an immediate value with `useOptimistic` (drive `aria-current` from it) so feedback is instant while the content streams. The routing variant ([Routing-Driven Tabs](nextjs.md#routing-driven-tabs)) does exactly this: optimistic `aria-current`, committed `active` for the bar.
136
167
 
137
- ## Shared Controls Between Skeleton and Content
168
+ ## Layout Displacement Morph
138
169
 
139
- Give matching controls in fallback and content the same `viewTransitionName`:
170
+ Only content inside an activated boundary animates position — everything else teleports to its new layout spot. When a list grows or shrinks, wrap the sibling content below it so it glides instead of jumping:
140
171
 
141
172
  ```jsx
142
- // Fallback
143
- <input disabled placeholder="Search..." style={{ viewTransitionName: 'search-input' }} />
144
- // Content
145
- <input placeholder="Search..." style={{ viewTransitionName: 'search-input' }} />
173
+ <FavoritesList /> {/* rows enter/exit */}
174
+ <ViewTransition> {/* bare: update enabled */}
175
+ <section>
176
+ <h2>You Might Also Like</h2>
177
+ <Recommendations />
178
+ </section>
179
+ </ViewTransition>
146
180
  ```
147
181
 
148
- Don't put manual `viewTransitionName` on the root DOM node inside `<ViewTransition>` — React's auto-generated name overrides it.
182
+ The section — heading included — morphs as one group when rows above are added or removed. Nothing inside the section changed; the *displacement* is the update.
183
+
184
+ - React only measures boundaries that are direct children of nodes along the changed path — a VT buried under an extra wrapper element won't activate. Place the boundary as a direct sibling of the changing content.
185
+ - Sometimes the better fix is no morph at all: pad fixed-size lists to a constant slot count with invisible fillers so the grid never changes height and nothing below it moves.
186
+ - `default="none"` disables exactly this morph — it turns off `update`. Named/shared elements get `default="none"`; displaced siblings and keyed list items stay bare or set `update="auto"`.
149
187
 
150
188
  ## Reusable Animated Collapse
151
189
 
@@ -164,7 +202,9 @@ function AnimatedCollapse({ open, children }) {
164
202
  <AnimatedCollapse open={open}><SectionContent /></AnimatedCollapse>
165
203
  ```
166
204
 
167
- ## Preserve State with Activity
205
+ ## Composing with Activity
206
+
207
+ `Activity` is orthogonal to view transitions: it preserves the state of a hidden subtree, `ViewTransition` animates it. Compose them for an in-page show/hide (drawer, panel, tab body) that keeps its scroll/form state while it animates in and out:
168
208
 
169
209
  ```jsx
170
210
  <Activity mode={isVisible ? 'visible' : 'hidden'}>
@@ -174,6 +214,8 @@ function AnimatedCollapse({ open, children }) {
174
214
  </Activity>
175
215
  ```
176
216
 
217
+ Only reach for Activity when there's state worth preserving — a stateless element (e.g. the sliding indicator above) gains nothing from it. In Next.js, layout-hosted chrome already persists across navigations without Activity (see [nextjs.md](nextjs.md#layout-level-viewtransition)).
218
+
177
219
  ## Exclude Elements with `useOptimistic`
178
220
 
179
221
  `useOptimistic` values update before the transition snapshot, excluding them from animation. Use for controls (labels); use committed state for animated content:
@@ -200,7 +242,7 @@ function cycleSort() {
200
242
 
201
243
  ## View Transition Events
202
244
 
203
- Imperative control via `onEnter`, `onExit`, `onUpdate`, `onShare`. Always return a cleanup function. `onShare` takes precedence over `onEnter`/`onExit`.
245
+ Imperative control via `onEnter`, `onExit`, `onUpdate`, `onShare`. Return a cleanup function to cancel your animation when the transition finishes. `onShare` takes precedence over `onEnter`/`onExit`.
204
246
 
205
247
  ```jsx
206
248
  <ViewTransition
@@ -230,33 +272,3 @@ The `types` array (second argument) lets you vary animation based on transition
230
272
  | Route transition (slide) | 150–250ms |
231
273
  | Suspense reveal (skeleton → content) | 200–400ms |
232
274
  | Shared element morph | 300–500ms |
233
-
234
- ---
235
-
236
- ## Troubleshooting
237
-
238
- **VT not activating:** Ensure `<ViewTransition>` comes before any DOM node. Ensure state update is inside `startTransition`.
239
-
240
- **"Two ViewTransition components with the same name":** Names must be globally unique. Use IDs: `name={`hero-${item.id}`}`.
241
-
242
- **`router.back()` and browser back/forward skip animation:** Use `router.push()` with an explicit URL instead. See SKILL.md "router.back() and Browser Back Button."
243
-
244
- **`flushSync` skips animations:** Use `startTransition` instead.
245
-
246
- **Only updates animate (no enter/exit):** Without `<Suspense>`, React treats swaps as updates. Conditionally render the VT itself, or wrap in `<Suspense>`.
247
-
248
- **Layout VT prevents page VTs from animating:** Nested VTs never fire enter/exit inside a parent VT. If your layout has a VT wrapping `{children}`, page-level enter/exit will silently not work. Remove the layout VT.
249
-
250
- **List reorder not animating with `useOptimistic`:** Optimistic values resolve before snapshot. Use committed state for list order.
251
-
252
- **TS error "Property 'default' is missing":** Type-keyed objects require a `default` key.
253
-
254
- **Hash fragments cause scroll jumps:** Navigate without hash; scroll programmatically after navigation.
255
-
256
- **Backdrop-blur flickers:** Use the backdrop-blur workaround from `css-recipes.md`.
257
-
258
- **`border-radius` lost during transitions:** Apply `border-radius` directly to the captured element.
259
-
260
- **Skeleton controls slide away:** Give matching controls the same `viewTransitionName`.
261
-
262
- **Batching:** Multiple updates during animation are batched. A→B→C→D becomes B→D.
@@ -0,0 +1,47 @@
1
+ # Troubleshooting
2
+
3
+ **VT not activating:** Ensure `<ViewTransition>` comes before any DOM node and the state update is inside `startTransition`. A raw `viewTransitionName` only isolates an element; it does not activate a React view transition.
4
+
5
+ **"Two ViewTransition components with the same name":** Names must be globally unique. Use IDs such as `name={`hero-${item.id}`}`. If a reusable component appears in multiple surfaces at once, move the name to the specific consumer or make it conditional.
6
+
7
+ **Unrelated content crossfades:** A bare VT uses the browser crossfade for every trigger. Set `default="none"` and opt in only to the triggers that boundary should handle.
8
+
9
+ **Scrolling hangs while a transition animates:** the `::view-transition` overlay is `position: fixed` and its snapshots don't scroll — a browser limitation, not fixable in React (skipping snaps to the end). Keep reveal durations short; for scroll-driven UI use gesture transitions (experimental `useSwipeTransition`, if available).
10
+
11
+ **Open popover flickers when a background transition settles:** it's captured in `root`. Give it a real `view-transition-name` + isolation (not `none`) — see [Isolate Elements from Parent Animations](patterns.md#isolate-elements-from-parent-animations).
12
+
13
+ **Popover closes or goes dead when clicked mid-transition:** named participants are skipped by hit-testing while a transition runs; clicks land on what's beneath and read as outside-clicks. Portal the popover (see [Isolate Elements from Parent Animations](patterns.md#isolate-elements-from-parent-animations)); brief dead clicks during the transition remain — that's the price of the name.
14
+
15
+ **Shared morph silently not firing:** `share` resolved to `none`. Either the VT has `default="none"` with no explicit `share` prop, or `share` is type-keyed and the navigation never adds the type — the link needs `transitionTypes` (or `addTransitionType` in the transition).
16
+
17
+ **Shared morph competes with a page fade:** A fading page exit dissolves the source while it is morphing. Remove that exit or use motion that preserves the shared element's continuity.
18
+
19
+ **An enter animation runs even though Suspense never showed its fallback:** The content-side VT became the topmost entering subtree during navigation. At that call site, add a host DOM element immediately outside `Suspense`. The host suppresses the nested enter during warm navigation but remains mounted so the VT can enter on a later fallback-to-content reveal. Keep the host outside `Suspense`, not inside the reusable crossfade; do nothing when a direct host already exists.
20
+
21
+ **Section below a list teleports instead of gliding:** it's outside any activated boundary, its VT has `default="none"` (which disables `update`), or it isn't an immediate sibling of the changing content. See [Layout Displacement Morph](patterns.md#layout-displacement-morph).
22
+
23
+ **`router.back()` and browser back/forward skip the directional slide:** traversals carry no transition types, so type-keyed maps resolve to `default` — untyped shared-element morphs still apply. Use `router.push()` for typed animations.
24
+
25
+ **`flushSync` skips animations:** Use `startTransition` instead.
26
+
27
+ **Only updates animate (no enter/exit):** Without `<Suspense>`, React treats swaps as updates. Conditionally render the VT itself, or wrap in `<Suspense>`.
28
+
29
+ **Suspense reveal does not animate:** Suspense resolves in a separate transition without navigation types. Use string `enter`/`exit` props rather than a type map.
30
+
31
+ **Layout VT prevents page VTs from animating:** Nested VTs skip their own enter/exit when they mount or unmount as one unit with a parent VT. Keep route boundaries in pages, not a layout wrapping `{children}`. React has experimental upstream `parentEnter`/`parentExit` work ([PR #36690](https://github.com/facebook/react/pull/36690)), but those props are not currently available in the Next.js client runtime; do not recommend them in a Next.js app unless the installed runtime and docs explicitly include them.
32
+
33
+ **Same-route content does not animate with `update`:** Nested VTs can own the mutation before an outer boundary sees it. For a real identity change, use `key` with a stable `name` and `share` instead.
34
+
35
+ **List reorder not animating with `useOptimistic`:** Optimistic values resolve before snapshot. Use committed state for list order.
36
+
37
+ **TS error "Property 'default' is missing":** Type-keyed objects require a `default` key.
38
+
39
+ **Hash fragments cause scroll jumps:** Navigate without hash; scroll programmatically after navigation.
40
+
41
+ **Backdrop-blur flickers:** Use the [Backdrop-Blur Workaround](css-recipes.md#backdrop-blur-workaround).
42
+
43
+ **`border-radius` lost during transitions:** Apply `border-radius` directly to the captured element.
44
+
45
+ **Skeleton controls slide away:** Give matching controls the same `viewTransitionName`.
46
+
47
+ **Batching:** Multiple updates during animation are batched. A→B→C→D becomes B→D.
@@ -2,6 +2,8 @@
2
2
 
3
3
  > {{DESC_RESEARCH}}
4
4
 
5
+ {{CREDIT:mattpocock-skills:skills/engineering/research/SKILL.md}}
6
+
5
7
  **Trigger:** a question needs current, external, or wide-ranging facts that are neither
6
8
  in this repository nor in the model's own knowledge. They are gathered and verified from
7
9
  the open web.
@@ -2,6 +2,8 @@
2
2
 
3
3
  > {{DESC_REVIEW_RESPONSE}}
4
4
 
5
+ {{CREDIT:superpowers:skills/receiving-code-review/SKILL.md}}
6
+
5
7
  **Trigger:** review feedback has arrived — from a human or another agent — and is about
6
8
  to be acted on; or the user wants a review criterion recorded ("add a review rule…",
7
9
  "reviews should always check…").
@@ -3,6 +3,8 @@
3
3
  > {{DESC_RULE}}
4
4
  <!-- aliases: consolidate-memory -->
5
5
 
6
+ {{CREDIT:mattpocock-skills-2509:skills/engineering/retro/SKILL.md}}
7
+
6
8
  **Trigger:** the user says "always…", "never…", "from now on…" or "remember that…". Anything the user wants to outlive this session counts: a standing rule ("make that a
7
9
  rule") or a durable fact ("note that…", "keep in mind…"). Also fires when a
8
10
  `feedback` {{MEMORY}} lesson keeps recurring and deserves promotion, or when
@@ -2,6 +2,8 @@
2
2
 
3
3
  > {{DESC_SHIP}}
4
4
 
5
+ {{CREDIT:mattpocock-skills-2509:skills/engineering/pr/SKILL.md}}
6
+
5
7
  **Trigger:** the change is committed and verified, and it is time to open a pull
6
8
  request or merge the branch.
7
9
 
@@ -40,6 +40,7 @@ apparatus, so every step ends in the user's assent ({{LAW:one-intent-one-act}},
40
40
  every theme, `SKILL_CLASS`, a `registry.json` row, the README enumeration, then
41
41
  `doctor --all` and the suites. A skill that bundles a script or several files is a
42
42
  folder skill (`<name>/SKILL.md` + `VENDOR.md`), listed in `VENDORED_SKILL_DIRS`.
43
+ Text adapted from outside gets a `src/upstreams.json` entry and a `CREDIT` token (docs/extending.md §3, in the Geneseed source repo).
43
44
  5. **Merge or retire honestly.** When two skills fold into one: keep the best steps of
44
45
  each, rewrite the exit so the survivor routes to what the retired one led to, and
45
46
  remove the retired file and *every* reference to it (table row, sibling links, theme
@@ -0,0 +1,103 @@
1
+ # {{SKILL}}: spring-boot — cloud-native
2
+
3
+ {{CREDIT:piomin-spring-boot:skills/spring-boot/references/cloud.md}}
4
+
5
+ Configuration, outbound clients, resilience, observability and packaging for a service that runs on Kubernetes or similar. Spring Cloud components (Config, Eureka, Gateway, LoadBalancer) ship in a release train tied to a Boot line: pick the train from the Spring Cloud supported-versions table, never mix trains. On Kubernetes, native service discovery and ConfigMaps often replace Eureka and Config Server; reach for those only when the platform lacks them.
6
+
7
+ ## Config and secrets
8
+ ```yaml
9
+ spring:
10
+ config:
11
+ import: "optional:configtree:/run/secrets/"
12
+ ```
13
+ A Config Tree reads each mounted file as one property, so secrets stay out of environment variables and `/proc`. Mount the Kubernetes Secret read-only at that path.
14
+
15
+ ## Declarative HTTP clients
16
+ Replace OpenFeign with an `@HttpExchange` interface. Boot registers groups; the URL and timeouts come from properties.
17
+
18
+ ```java
19
+ @HttpExchange("/users")
20
+ public interface UserClient {
21
+ @GetExchange("/{id}")
22
+ UserResponse get(@PathVariable Long id);
23
+
24
+ @PostExchange
25
+ UserResponse create(@RequestBody UserCreateRequest request);
26
+ }
27
+
28
+ @SpringBootApplication
29
+ @ImportHttpServices(group = "users", types = UserClient.class)
30
+ public class App { }
31
+ ```
32
+
33
+ ```yaml
34
+ spring:
35
+ http:
36
+ serviceclient:
37
+ users:
38
+ base-url: "http://user-service"
39
+ read-timeout: 2s
40
+ ```
41
+
42
+ Without Boot's group support, build one by hand with `HttpServiceProxyFactory.builderFor(RestClientAdapter.create(restClient)).build().createClient(UserClient.class)`.
43
+
44
+ ## Resilience
45
+ Framework 7 ships retry and concurrency limits in core. Enable them once, then annotate:
46
+
47
+ ```java
48
+ @Configuration
49
+ @EnableResilientMethods
50
+ class ResilienceConfig { }
51
+
52
+ @Retryable(includes = IOException.class, maxRetries = 3, delay = 200, multiplier = 2, maxDelay = 2000)
53
+ public Quote fetch(String id) { ... }
54
+
55
+ @ConcurrencyLimit(10)
56
+ public void sendNotification() { ... }
57
+ ```
58
+
59
+ Retry only idempotent calls. Circuit breaking: at the edge, use the Gateway `CircuitBreaker` filter; in-service, Resilience4j is still the common choice but its annotation and property names were not re-verified for Boot 4 here, so check its current docs before copying old config.
60
+
61
+ ## Gateway
62
+ Spring Cloud Gateway's reactive server uses the `spring.cloud.gateway.server.webflux.*` prefix in current releases (older guides show `spring.cloud.gateway.*`). Prefer YAML routes over the Java DSL for simple cases.
63
+
64
+ ```yaml
65
+ spring:
66
+ cloud:
67
+ gateway:
68
+ server:
69
+ webflux:
70
+ routes:
71
+ - id: users
72
+ uri: lb://user-service
73
+ predicates: [Path=/api/users/**]
74
+ filters:
75
+ - CircuitBreaker=usersBreaker
76
+ ```
77
+
78
+ The `CircuitBreaker` filter needs a Spring Cloud CircuitBreaker starter (for example the reactor-Resilience4j one) on the classpath. Gateway is WebFlux; do not put it in the same module as a Spring MVC service.
79
+
80
+ ## Observability
81
+ - Health: `/actuator/health/liveness` and `/actuator/health/readiness` are on by default (`management.endpoint.health.probes.enabled=false` disables them); point the Kubernetes probes at those. Expose only what you need (`management.endpoints.web.exposure.include`), and protect the rest.
82
+ - Metrics: add `micrometer-registry-prometheus` and expose `prometheus`.
83
+ - Traces over OTLP: add `org.springframework.boot:spring-boot-starter-opentelemetry` and configure `management.opentelemetry.tracing.export.otlp.endpoint` (the old `management.otlp.tracing.*` keys are not the Boot 4 names). Boot samples 10% by default; set `management.tracing.sampling.probability: 1.0` only where the volume allows.
84
+ - Custom spans: `Observation.createNotStarted("order.process", registry).lowCardinalityKeyValue(...).observe(() -> ...)` (Micrometer Observation, unchanged by Boot 4 as far as the docs show).
85
+
86
+ ## Lifecycle
87
+ ```yaml
88
+ server:
89
+ shutdown: graceful
90
+ spring:
91
+ lifecycle:
92
+ timeout-per-shutdown-phase: 30s
93
+ ```
94
+ Add a short Kubernetes `preStop` sleep so the endpoint leaves the load balancer before the JVM stops accepting work. Keep the pod's termination grace period longer than the shutdown phase timeout.
95
+
96
+ ## Images
97
+ - No Dockerfile needed: `./mvnw spring-boot:build-image` builds an OCI image with Cloud Native Buildpacks (Paketo). With the native profile: `./mvnw -Pnative spring-boot:build-image`. Bind `build-image-no-fork` to `package` to build in CI.
98
+ - If you do write a Dockerfile: multi-stage, JRE-only runtime image, non-root user, `-XX:MaxRAMPercentage` instead of a fixed heap.
99
+ - GraalVM native: fast start and low memory, at the cost of build time and reflection hints; adopt it deliberately, not by default.
100
+ - Use whatever CI the project already has; do not introduce a new one.
101
+
102
+ ## Kubernetes manifest essentials
103
+ Liveness and readiness probes on the actuator paths, a startup probe for slow starts, resource requests and limits, `runAsNonRoot`, config from ConfigMap and Secret. For TLS, let the platform terminate it (Ingress plus cert-manager) or mount the certificate and use an SSL bundle ([spring-boot/security.md](security.md)).
@@ -0,0 +1,71 @@
1
+ # {{SKILL}}: spring-boot — modern Java in Spring code
2
+
3
+ {{CREDIT:piomin-spring-boot:skills/java-modern/SKILL.md}}
4
+
5
+ {{CREDIT:piomin-spring-boot:skills/code-quality/SKILL.md}}
6
+
7
+ Java 21+ features and where they pay off in a Spring Boot codebase, plus the null-safety and Optional habits that prevent most runtime NPEs. This is a language reference for writing Spring code, not a review procedure.
8
+
9
+ ## Where each feature fits
10
+ | Feature | Use for | Avoid when |
11
+ |---------|---------|------------|
12
+ | Records | DTOs, `@ConfigurationProperties`, value objects, events | JPA entities (Hibernate needs a no-arg constructor and mutable state), types needing inheritance |
13
+ | Sealed interfaces | Closed result and event hierarchies | Types that third parties must extend |
14
+ | Pattern-matching `switch` | Handling sealed types exhaustively, replacing `instanceof` chains | Plain equality checks |
15
+ | Virtual threads | Blocking I/O at high concurrency (`spring.threads.virtual.enabled: true`) | CPU-bound work |
16
+ | Text blocks | JPQL, SQL, JSON fixtures in tests | One-line strings |
17
+ | Sequenced collections | `getFirst()`, `getLast()`, `reversed()` on ordered collections | Unordered sets and maps |
18
+
19
+ ## Records
20
+ ```java
21
+ @ConfigurationProperties(prefix = "app.notification")
22
+ public record MailProperties(boolean enabled, String sender, Duration connectTimeout, Backoff backoff) {
23
+ public record Backoff(int attempts, Duration pause) {}
24
+ }
25
+ ```
26
+ Register with `@ConfigurationPropertiesScan` or `@EnableConfigurationProperties`. Constraint annotations on record components work with `@Validated` on the properties class.
27
+
28
+ ## Sealed results and exhaustive switch
29
+ ```java
30
+ public sealed interface PaymentResult permits Paid, Declined, Pending {}
31
+ public record Paid(String transactionId, BigDecimal amount) implements PaymentResult {}
32
+ public record Declined(String code, String message) implements PaymentResult {}
33
+ public record Pending(String reference) implements PaymentResult {}
34
+
35
+ String describe(PaymentResult result) {
36
+ return switch (result) {
37
+ case Paid p -> "Paid: " + p.transactionId();
38
+ case Declined d -> "Declined: " + d.message();
39
+ case Pending p -> "Pending: " + p.reference();
40
+ };
41
+ }
42
+ ```
43
+ No `default` branch on a sealed switch: adding a subtype then fails the build at every switch that must handle it. Use guards (`case Order o when o.total().signum() > 0 ->`) sparingly; a guard-heavy switch is usually a rules table in disguise.
44
+
45
+ Prefer a sealed result over an exception for expected outcomes (declined, not found); keep exceptions for the unexpected.
46
+
47
+ ## Virtual threads
48
+ One property moves request handling and `@Async` work to virtual threads, so blocking JDBC and `RestClient` calls scale without reactive code. Pitfalls: a `synchronized` block that holds a monitor across blocking I/O pins the carrier thread on older JDKs (use `ReentrantLock`; Java 24+ lessens this), and thread-locals (MDC, security context) do not follow work onto another thread. Bound pools that guard scarce resources (the DB connection pool still limits real concurrency). Keep WebFlux for streaming, SSE or backpressure.
49
+
50
+ ## Null safety and Optional
51
+ Spring Framework 7 and Boot 4 annotate their APIs with JSpecify (`@NullMarked`, `@Nullable`); use the same in your own packages so the compiler or IDE can check callers.
52
+
53
+ ```java
54
+ // repository lookups: return Optional, decide at the edge
55
+ User user = repository.findById(id).orElseThrow(() -> new ResourceNotFoundException("User " + id));
56
+
57
+ // derive without unwrapping
58
+ String label = Optional.ofNullable(user.getNickname()).map(String::toUpperCase).orElse("");
59
+ ```
60
+
61
+ Habits:
62
+ - `Optional` is a return type, not a field, parameter or collection element. Never `Optional.get()` without a prior check; use `orElseThrow`.
63
+ - Return an empty collection, never `null`.
64
+ - Do not catch `Exception` broadly or swallow it; wrap with the cause (`new ProcessingException("...", e)`) and log once, where it is handled.
65
+ - Open resources with try-with-resources.
66
+ - Chained calls on values that can be null (`a.getB().getC()`) need a guard or `Optional` mapping.
67
+
68
+ ## Quick rules
69
+ - DTO or config -> record. Entity -> normal class.
70
+ - Closed set of outcomes -> sealed interface plus exhaustive switch.
71
+ - Blocking service -> virtual threads, not WebFlux.
@@ -0,0 +1,89 @@
1
+ # {{SKILL}}: spring-boot — logging
2
+
3
+ {{CREDIT:piomin-spring-boot:skills/logging-patterns/SKILL.md}}
4
+
5
+ SLF4J through Spring Boot's logging, with structured (JSON) output where logs are machine-read. Structured output is a good default for deployed environments and CI because fields parse directly (`jq`, log platforms, an AI assistant reading logs); keep readable text for local development.
6
+
7
+ ## Structured logging (built in)
8
+ No extra dependency. Pick a format per target:
9
+
10
+ ```yaml
11
+ logging:
12
+ structured:
13
+ format:
14
+ console: logstash # also: ecs, gelf
15
+ ```
16
+
17
+ `logging.structured.format.file` does the same for file output. A custom Logback or Log4j2 config must use Boot's structured encoder or layout (`StructuredLogEncoder` / `StructuredLogLayout` fed by `CONSOLE_LOG_STRUCTURED_FORMAT`), or the property has no effect.
18
+
19
+ Profile split so developers keep readable logs:
20
+
21
+ ```yaml
22
+ ---
23
+ spring:
24
+ config:
25
+ activate:
26
+ on-profile: "!local"
27
+ logging:
28
+ structured:
29
+ format:
30
+ console: logstash
31
+ ```
32
+
33
+ Run locally with the `local` profile for plain text. With Micrometer Tracing present, Boot adds `[traceId-spanId]` to every log line by default; change the format with `logging.pattern.correlation`. Whether they appear as JSON fields depends on the format; confirm in a real log line.
34
+
35
+ ## Writing log statements
36
+ ```java
37
+ private static final Logger log = LoggerFactory.getLogger(OrderService.class);
38
+
39
+ log.info("Order created id={} customer={}", order.id(), order.customerId()); // parameterized
40
+ log.error("Payment failed for order {}", order.id(), e); // exception last, stack trace kept
41
+ ```
42
+ - Placeholders, never string concatenation. Guard only genuinely expensive arguments with `isDebugEnabled()`.
43
+ - Pass the exception as the final argument; do not log `e.getMessage()` alone.
44
+ - Log an exception once, where it is handled, not at every layer it passes through.
45
+ - Never log secrets, tokens, passwords or personal data; log identifiers.
46
+ - Lombok's `@Slf4j` is fine where the project uses Lombok; otherwise declare the logger as above.
47
+
48
+ | Level | Meaning |
49
+ |-------|---------|
50
+ | ERROR | Something failed and needs attention |
51
+ | WARN | Unexpected but handled (fallback used, retry succeeded) |
52
+ | INFO | Business events (order created, job finished) |
53
+ | DEBUG | Technical detail (queries, outbound calls) |
54
+ | TRACE | Verbose internals |
55
+
56
+ ## MDC for correlation
57
+ MDC attaches key-value context to every log line written by the current thread. Set it at the edge in a filter and always clear it in `finally`.
58
+
59
+ ```java
60
+ @Component
61
+ @Order(Ordered.HIGHEST_PRECEDENCE)
62
+ class MdcFilter extends OncePerRequestFilter {
63
+ @Override
64
+ protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
65
+ throws ServletException, IOException {
66
+ String header = req.getHeader("X-Request-Id");
67
+ // client-supplied: accept only a short, safe value
68
+ String id = header != null && header.matches("[A-Za-z0-9-]{1,64}") ? header : UUID.randomUUID().toString();
69
+ MDC.put("requestId", id);
70
+ try {
71
+ chain.doFilter(req, res);
72
+ } finally {
73
+ MDC.remove("requestId");
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ MDC is thread-local, so it does not follow work handed to another thread (virtual or platform). For executor tasks, copy the context with a `TaskDecorator` (`MDC.getCopyOfContextMap()` before, `MDC.setContextMap(...)` inside the task, `MDC.clear()` in `finally`) and register it on the executor you use; check that Boot applies it to your executor in the version you run.
80
+
81
+ Useful fields for incident analysis: `requestId`, user or tenant id, and a duration on slow-path logs. Keep them to identifiers, not payloads.
82
+
83
+ ## Quick reference
84
+ | Need | Use |
85
+ |------|-----|
86
+ | JSON console | `logging.structured.format.console: logstash` |
87
+ | Per-package level | `logging.level.com.example.orders: DEBUG` |
88
+ | Correlate a request | MDC `requestId` set in a filter |
89
+ | Follow one request in JSON logs | `jq 'select(.requestId == "abc")' app.log` (field name depends on MDC key and format) |
@@ -0,0 +1,112 @@
1
+ # {{SKILL}}: spring-boot — security
2
+
3
+ {{CREDIT:piomin-spring-boot:skills/spring-boot/references/security.md}}
4
+
5
+ Spring Security 7. Default posture for an API: a stateless OAuth2 resource server that validates JWTs issued by an identity provider. Do not write your own token issuer, JWT filter or password store unless the task is explicitly to build an authorization server; that code is where the security bugs live.
6
+
7
+ ## Resource server
8
+ Properties first (Boot builds the decoder from them):
9
+
10
+ ```yaml
11
+ spring:
12
+ security:
13
+ oauth2:
14
+ resourceserver:
15
+ jwt:
16
+ issuer-uri: https://auth.example.com
17
+ audiences:
18
+ - orders-api
19
+ ```
20
+
21
+ ```java
22
+ @Configuration
23
+ @EnableWebSecurity
24
+ @EnableMethodSecurity
25
+ class SecurityConfig {
26
+
27
+ @Bean
28
+ SecurityFilterChain api(HttpSecurity http) throws Exception {
29
+ http
30
+ .cors(Customizer.withDefaults())
31
+ .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
32
+ .authorizeHttpRequests(auth -> auth
33
+ .requestMatchers("/actuator/health/**").permitAll()
34
+ .requestMatchers("/api/admin/**").hasRole("ADMIN")
35
+ .anyRequest().authenticated())
36
+ .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
37
+ return http.build();
38
+ }
39
+ }
40
+ ```
41
+
42
+ - Lambda DSL only; `.and()` chaining and `authorizeRequests` are gone in Security 7.
43
+ - Disabling CSRF is acceptable only for a stateless, bearer-token API with no cookie-based session. If any endpoint authenticates with a cookie, keep CSRF on.
44
+ - `cors(Customizer.withDefaults())` picks up a `CorsConfigurationSource` bean; define that bean with explicit origins.
45
+ - Default JWT algorithm trust is RS256 only; widen it deliberately if the provider signs otherwise.
46
+
47
+ ## Mapping claims to authorities
48
+ The default converter maps the `scope` claim to `SCOPE_` authorities. For roles in another claim:
49
+
50
+ ```java
51
+ @Bean
52
+ JwtAuthenticationConverter jwtAuthenticationConverter() {
53
+ var authorities = new JwtGrantedAuthoritiesConverter();
54
+ authorities.setAuthoritiesClaimName("roles");
55
+ authorities.setAuthorityPrefix("ROLE_");
56
+ var converter = new JwtAuthenticationConverter();
57
+ converter.setJwtGrantedAuthoritiesConverter(authorities);
58
+ return converter;
59
+ }
60
+ ```
61
+
62
+ Wire it with `oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))`.
63
+
64
+ ## Validate audience
65
+ An issuer check alone accepts any token that issuer minted for any API. The `audiences` property in the YAML above makes Boot require the `aud` claim. Define your own `JwtDecoder` bean only when you need custom validators; it replaces the property-driven decoder, audiences included.
66
+
67
+ ## Method security
68
+ `@EnableMethodSecurity` turns on `@PreAuthorize` and `@PostAuthorize`. Authorize in the service layer, next to the data, not only on URL patterns. The legacy Access API (`AccessDecisionManager`, voters) moved to the separate `spring-security-access` module; `@Secured` still works via `@EnableMethodSecurity(securedEnabled = true)`, but prefer `@PreAuthorize`.
69
+
70
+ ```java
71
+ @PreAuthorize("hasRole('ADMIN') or #ownerId == authentication.name")
72
+ public Order find(String ownerId, Long id) { ... }
73
+ ```
74
+
75
+ ## Testing
76
+ Use the `jwt()` request post processor with MockMvc (`SecurityMockMvcRequestPostProcessors.jwt()`; default token has `scope=read`), or `@WithMockUser` for method security. Always test the 401 (no token) and 403 (wrong authority) paths, not only success.
77
+
78
+ ## TLS with SSL bundles
79
+ Configure server and client TLS with SSL bundles (named key material). Reloading works only when the consuming component supports it (Tomcat and Netty servers) and only through the file watcher, so the material must be on the file system with a `file:` location; a `classpath:` bundle cannot reload.
80
+
81
+ ```yaml
82
+ spring:
83
+ ssl:
84
+ bundle:
85
+ pem:
86
+ server:
87
+ reload-on-update: true
88
+ keystore:
89
+ certificate: "file:/etc/ssl/app/tls.crt"
90
+ private-key: "file:/etc/ssl/app/tls.key"
91
+ server:
92
+ ssl:
93
+ bundle: server
94
+ ```
95
+
96
+ Tune detection with `spring.ssl.bundle.watch.file.quiet-period` (default 10s). Apply a bundle to an outbound `RestClient` with the `RestClientSsl` helper (it replaces any request factory you set earlier; use a separate `client` bundle with a `truststore` for the upstream, not the server's keystore bundle), or build a factory when you need more than SSL:
97
+
98
+ ```java
99
+ RestClient client(RestClient.Builder builder, RestClientSsl ssl) {
100
+ return builder.baseUrl("https://internal-service").apply(ssl.fromBundle("client")).build();
101
+ }
102
+ // more than SSL:
103
+ // var settings = HttpClientSettings.ofSslBundle(bundles.getBundle("client")).withReadTimeout(Duration.ofSeconds(5));
104
+ // builder.requestFactory(ClientHttpRequestFactoryBuilder.detect().build(settings))
105
+ ```
106
+
107
+ ## Checklist
108
+ - HTTPS everywhere outside local dev; secrets from env or mounted files ([spring-boot/cloud.md](cloud.md) covers config trees).
109
+ - Validate issuer and audience; keep token lifetimes short; never log tokens.
110
+ - Passwords (if you must store any): `PasswordEncoder` from `PasswordEncoderFactories`/BCrypt, never a plain hash.
111
+ - Rate-limit authentication endpoints at the gateway or proxy.
112
+ - Ask the `spring-security` agent for review (it hands the generic audit to `security`) when the change touches authentication or authorization.