@pikku/skills 0.12.57 → 0.12.59

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 (72) hide show
  1. package/dist/skills.gen.js +2 -2
  2. package/package.json +10 -7
  3. package/skills/pikku-a11y/SKILL.md +5 -3
  4. package/skills/pikku-addon/SKILL.md +8 -9
  5. package/skills/pikku-agent/SKILL.md +16 -13
  6. package/skills/pikku-agent/references/runner-vercel.md +1 -1
  7. package/skills/pikku-architect/SKILL.md +7 -10
  8. package/skills/pikku-auth/SKILL.md +16 -20
  9. package/skills/pikku-auth/references/better-auth.md +74 -37
  10. package/skills/pikku-auth/references/sessions.md +8 -0
  11. package/skills/pikku-build/SKILL.md +36 -68
  12. package/skills/pikku-build/references/app.md +27 -828
  13. package/skills/pikku-build/references/feature.md +3 -5
  14. package/skills/pikku-build/references/phase-1-what-it-is.md +167 -0
  15. package/skills/pikku-build/references/phase-2-people-and-apps.md +133 -0
  16. package/skills/pikku-build/references/phase-3-plan.md +62 -0
  17. package/skills/pikku-build/references/phase-4-build.md +249 -0
  18. package/skills/pikku-build/references/phase-5-prove-and-ship.md +148 -0
  19. package/skills/pikku-build/references/platform.md +5 -6
  20. package/skills/pikku-build/references/quick.md +2 -2
  21. package/skills/pikku-build/references/ship.md +4 -5
  22. package/skills/pikku-changes/SKILL.md +13 -7
  23. package/skills/pikku-concepts/SKILL.md +19 -258
  24. package/skills/pikku-concepts/references/cli.md +63 -0
  25. package/skills/pikku-concepts/references/functions.md +117 -0
  26. package/skills/pikku-concepts/references/language.md +5 -5
  27. package/skills/pikku-concepts/references/packages.md +6 -4
  28. package/skills/pikku-concepts/references/testing.md +14 -0
  29. package/skills/pikku-convert-screen/SKILL.md +7 -1
  30. package/skills/pikku-deploy/SKILL.md +29 -30
  31. package/skills/pikku-deploy/references/ws.md +1 -1
  32. package/skills/pikku-emails/SKILL.md +7 -3
  33. package/skills/pikku-guide/SKILL.md +7 -10
  34. package/skills/pikku-i18n/SKILL.md +18 -19
  35. package/skills/pikku-i18n/references/messages.md +6 -8
  36. package/skills/pikku-knowledge/SKILL.md +42 -45
  37. package/skills/pikku-kysely/SKILL.md +50 -13
  38. package/skills/pikku-list-query/SKILL.md +27 -14
  39. package/skills/pikku-meta/SKILL.md +13 -16
  40. package/skills/pikku-meta/references/audit.md +1 -45
  41. package/skills/pikku-meta/references/versioning.md +1 -1
  42. package/skills/pikku-middleware/SKILL.md +23 -13
  43. package/skills/pikku-middleware/references/middleware-patterns.md +2 -2
  44. package/skills/pikku-n8n-import/SKILL.md +7 -8
  45. package/skills/pikku-permissions/SKILL.md +14 -8
  46. package/skills/pikku-react/SKILL.md +7 -6
  47. package/skills/pikku-realtime/SKILL.md +16 -11
  48. package/skills/pikku-report/SKILL.md +8 -10
  49. package/skills/pikku-scenario/SKILL.md +15 -18
  50. package/skills/pikku-scenario/references/steps.md +3 -4
  51. package/skills/pikku-scenario/references/virtual-users.md +1 -1
  52. package/skills/pikku-seo/SKILL.md +16 -8
  53. package/skills/pikku-service-backends/SKILL.md +26 -28
  54. package/skills/pikku-services/SKILL.md +8 -9
  55. package/skills/pikku-services/references/services.md +1 -1
  56. package/skills/pikku-software-archaeology/README.md +12 -1
  57. package/skills/pikku-software-archaeology/SKILL.md +9 -2
  58. package/skills/pikku-software-archaeology/references/pikku-mapping.md +1 -1
  59. package/skills/pikku-software-archaeology/references/sources/express.md +75 -0
  60. package/skills/pikku-software-archaeology/references/sources/laravel.md +77 -0
  61. package/skills/pikku-software-archaeology/references/sources/nestjs.md +73 -0
  62. package/skills/pikku-software-archaeology/references/sources/rails.md +72 -0
  63. package/skills/pikku-software-archaeology/references/tests-to-scenarios.md +72 -0
  64. package/skills/pikku-tailwind/SKILL.md +15 -16
  65. package/skills/pikku-theme/SKILL.md +15 -17
  66. package/skills/pikku-ui/SKILL.md +4 -3
  67. package/skills/pikku-webhook/SKILL.md +10 -11
  68. package/skills/pikku-wiring/SKILL.md +45 -47
  69. package/skills/pikku-wiring/references/channel.md +1 -1
  70. package/skills/pikku-wiring/references/scheduler.md +1 -1
  71. package/skills/pikku-wiring/references/trigger.md +24 -1
  72. package/skills/pikku-workflow/SKILL.md +7 -8
@@ -1,18 +1,14 @@
1
1
  ---
2
2
  name: pikku-scenario
3
3
  description: >-
4
- Use when writing or running Pikku scenarios, running a virtual user against a stage, or when
5
- asked to test Pikku functions or improve coverage. A scenario (pikkuScenario) drives the app
6
- the way users do — steps run as virtual users over the real transport against a running server — so a
7
- flow doubles as an e2e test and a staged/production health check. Covers scenario.do /
8
- expectEventually / expectError / expectService / expectScore, declared steps via
9
- pikkuScenarioStep (browser steps driven by @pikku/playwright) written as intent rather than
10
- clicks, virtual users / virtual users / environments in pikku.config.json, VIRTUAL_USER_SIGNING_KEY, the
11
- `pikku scenario` and `pikku virtual-user` commands, and live coverage
12
- via `pikku dev --coverage`. TRIGGER when: user asks about scenarios, testing a Pikku function,
13
- coverage, e2e flows, browser/UI e2e, health checks, virtual users, or adversarial
14
- runs against a stage. DO NOT TRIGGER when: user asks about running an existing suite (use
15
- Bash) or CI config.
4
+ Writing and running Pikku scenarios (pikkuScenario): virtual users drive the app over the real
5
+ transport, so a flow doubles as an e2e test and a stage/production health check. Covers
6
+ scenario.do/expectEventually/expectError/expectService/expectScore, pikkuScenarioStep (Playwright
7
+ browser steps), virtual users and environments in pikku.config.json, VIRTUAL_USER_SIGNING_KEY,
8
+ `pikku scenario`/`pikku virtual-user`, coverage via `pikku dev --coverage`. TRIGGER when: asked
9
+ about scenarios, testing a Pikku function, coverage, e2e or browser flows, health checks, virtual
10
+ users or adversarial stage runs. DO NOT TRIGGER for running an existing suite (use Bash) or CI
11
+ config.
16
12
  installGroups: [core]
17
13
  ---
18
14
 
@@ -31,13 +27,13 @@ installGroups: [core]
31
27
  This skill covers writing and running scenarios end to end. Five topics are one level down, and
32
28
  each says when to open it:
33
29
 
34
- | Read | For |
35
- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
36
- | `references/steps.md` | Authoring a `pikkuScenarioStep` — intent, witnesses, what a step is given |
37
- | `references/browser.md` | Browser bindings, and locating by message key in a translated app |
30
+ | Read | For |
31
+ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
32
+ | `references/steps.md` | Authoring a `pikkuScenarioStep` — intent, witnesses, what a step is given |
33
+ | `references/browser.md` | Browser bindings, and locating by message key in a translated app |
38
34
  | `references/virtual-users.md` | Virtual users vs virtual users, `defineVirtualUsers`, upstream credentials for virtual users, and human sign-in as a virtual user |
39
- | `references/coverage.md` | Live coverage, filling it, and unit tests for pure logic |
40
- | `references/virtual-user-run.md` | Running a model-driven virtual user against a stage |
35
+ | `references/coverage.md` | Live coverage, filling it, and unit tests for pure logic |
36
+ | `references/virtual-user-run.md` | Running a model-driven virtual user against a stage |
41
37
 
42
38
  ## What a scenario is
43
39
 
@@ -321,6 +317,7 @@ pikku scenario run local --spawn --exclude-tags ai-live
321
317
  | `--api-url` / `--app-url` | Override the environment's URLs — for a target that only exists at run time |
322
318
  | `--trace` | Keep every stack frame on failure (default shows only the project's own) |
323
319
  | `--coverage` | Reset/snapshot server coverage per scenario |
320
+ | `--run-id <id>` | Record the run under this id rather than a fresh one, so a caller can follow it |
324
321
 
325
322
  Output is `PASS <name> (<ms>) → <output>` / `FAIL <name> (<ms>): <error>`, then `N/M scenarios passed against '<env>'`. A scenario inside a feature is named `<Feature> › <scenario> <data>`.
326
323
 
@@ -358,10 +358,9 @@ is the app's language, not the team's. Ask, or leave it `en`.
358
358
 
359
359
  **Where a non-`en` `metaLocale` still shows English, today.** The reporter composes a
360
360
  sentence as `<Keyword> <virtual user> <template>` (`composeStepProse`), and the keyword is
361
- an English literal. The Console translates the Given/When/Then keywords into its own
362
- UI language; the CLI reporter does not, so `metaLocale: "de"` gives you German step
361
+ an English literal. The CLI reporter does not translate the Given/When/Then keywords, so `metaLocale: "de"` gives you German step
363
362
  prose inside an English frame — `Given shopper kauft 1 Äpfel`. Write templates that read
364
363
  acceptably in that frame rather than trying to defeat it. A second gap: where a
365
- function or scenario declares no `title`, the Console falls back to splitting the
366
- **identifier** into an English-looking label (`toEnglishName`), so under a
364
+ function or scenario declares no `title`, the label falls back to splitting the
365
+ **identifier** into English words (`toEnglishName`), so under a
367
366
  non-`en` `metaLocale` meta is worth authoring rather than leaving to the fallback.
@@ -47,7 +47,7 @@ harmless but worth knowing before you align it to the surrounding code.
47
47
  One more thing worth knowing before writing a rich virtual user: **`virtualUser.converse`
48
48
  builds its prompt from `name`, `jobTitle`, `personality` and the scenario's
49
49
  `task` only.** Fields like `disposition`, `goals` and `roles` are read and
50
- stored, and the console shows them, but they do not reach the conversing
50
+ stored, but they do not reach the conversing
51
51
  virtual user's instructions. Anything that must shape how someone talks belongs in
52
52
  `personality` or in the task.
53
53
 
@@ -1,9 +1,12 @@
1
1
  ---
2
2
  name: pikku-seo
3
3
  description: >-
4
- On-page SEO rules for the app's PUBLIC pages: per-route head() titles and meta descriptions, Open Graph tags, crawlable links and URLs, JSON-LD on the landing page, and noindex for the logged-in area.
5
- TRIGGER when: building or reworking any public page (landing, pricing, about, blog/content pages), writing page titles or meta tags, or the user asks about SEO / Google / discoverability / social sharing previews.
6
- DO NOT TRIGGER when: the task is accessibility, including heading order, landmarks or alt text (use pikku-a11y), or working on logged-in /app screens (they are noindexed — only the one robots rule below applies), backend functions, database, or deployment.
4
+ On-page SEO for the app's PUBLIC pages: per-route head() titles and meta descriptions, Open Graph,
5
+ crawlable links/URLs, landing-page JSON-LD, noindex for the logged-in area. TRIGGER when: building
6
+ or reworking a public page (landing, pricing, about, blog), writing page titles or meta tags, or
7
+ asked about SEO, Google, discoverability or social share previews. DO NOT TRIGGER for
8
+ accessibility incl. heading order, landmarks, alt text (use pikku-a11y), logged-in /app screens
9
+ (noindexed; only the one robots rule applies), backend, database or deployment.
7
10
  installGroups: [client]
8
11
  ---
9
12
 
@@ -31,7 +34,10 @@ export const Route = createFileRoute('/pricing')({
31
34
  'Simple per-seat pricing for Acme Scheduling. Start free, upgrade when your team grows — no setup fees, cancel anytime.',
32
35
  },
33
36
  { property: 'og:title', content: 'Pricing — Acme Scheduling' },
34
- { property: 'og:description', content: 'Simple per-seat pricing. Start free.' },
37
+ {
38
+ property: 'og:description',
39
+ content: 'Simple per-seat pricing. Start free.',
40
+ },
35
41
  ],
36
42
  }),
37
43
  component: PricingPage,
@@ -90,16 +96,18 @@ or offers (fake schema is a Google penalty, not a boost):
90
96
 
91
97
  ```tsx
92
98
  head: () => ({
93
- meta: [
94
- /* title + description as above */
95
- ],
99
+ meta: [/* title + description as above */],
96
100
  scripts: [
97
101
  {
98
102
  type: 'application/ld+json',
99
103
  children: JSON.stringify({
100
104
  '@context': 'https://schema.org',
101
105
  '@graph': [
102
- { '@type': 'Organization', name: 'Acme Scheduling', description: '…' },
106
+ {
107
+ '@type': 'Organization',
108
+ name: 'Acme Scheduling',
109
+ description: '…',
110
+ },
103
111
  { '@type': 'WebSite', name: 'Acme Scheduling' },
104
112
  ],
105
113
  }),
@@ -1,16 +1,14 @@
1
1
  ---
2
2
  name: pikku-service-backends
3
3
  description: >-
4
- Use when picking or wiring a backend for one of Pikku's core service interfaces — ContentService
5
- (S3, Backblaze B2), QueueService (SQS), SecretService (AWS Secrets Manager, Redis, MongoDB),
6
- SchemaService (AJV, cfworker), ChannelStore, EventHubStore, WorkflowService, SessionStore or
7
- AgentRunService (Redis, MongoDB). Covers which backend to choose, what each one silently does
8
- differently, and the failures they swallow. TRIGGER when: code uses S3Content, B2Content,
9
- SQSQueueService, AWSSecrets, RedisChannelStore, RedisSecretService, MongoDBChannelStore,
10
- PikkuMongoDB, AjvSchemaService or CFWorkerSchemaService, or the user asks how to store files,
11
- secrets, channel state or sessions. DO NOT TRIGGER when: defining service factories themselves
12
- (use pikku-services), SQL via Kysely (use pikku-kysely), or the Lambda/Cloudflare runtimes
13
- themselves (use pikku-deploy).
4
+ Picking or wiring a backend for Pikku's core service interfaces: ContentService (S3, B2),
5
+ QueueService (SQS), SecretService (AWS Secrets Manager, Redis, MongoDB), SchemaService (AJV,
6
+ cfworker), ChannelStore, EventHubStore, WorkflowService, SessionStore, AgentRunService (Redis,
7
+ MongoDB). Covers which to choose, silent differences, swallowed failures. TRIGGER when: code uses
8
+ S3Content, B2Content, SQSQueueService, AWSSecrets, Redis*/MongoDB* stores, PikkuMongoDB,
9
+ AjvSchemaService or CFWorkerSchemaService, or asked how to store files, secrets, channel state or
10
+ sessions. DO NOT TRIGGER for defining service factories (use pikku-services), Kysely SQL (use
11
+ pikku-kysely) or the Lambda/Cloudflare runtimes (use pikku-deploy).
14
12
  installGroups: [core]
15
13
  ---
16
14
 
@@ -26,17 +24,17 @@ behind the interface.
26
24
 
27
25
  ## Pick a backend
28
26
 
29
- | Interface | Backends | Package |
30
- | --- | --- | --- |
31
- | `ContentService` | `S3Content`, `B2Content` | `@pikku/aws-services`, `@pikku/backblaze` |
32
- | `QueueService` | `SQSQueueService` | `@pikku/aws-services` |
33
- | `SecretService` | `AWSSecrets`, `RedisSecretService`, `MongoDBSecretService` | `@pikku/aws-services`, `@pikku/redis`, `@pikku/mongodb` |
34
- | `SchemaService` | `AjvSchemaService`, `CFWorkerSchemaService` | `@pikku/schema-ajv`, `@pikku/schema-cfworker` |
35
- | `ChannelStore`, `EventHubStore` | Redis, MongoDB | `@pikku/redis`, `@pikku/mongodb` |
36
- | `PikkuWorkflowService`, `WorkflowRunService` | Redis, MongoDB | `@pikku/redis`, `@pikku/mongodb` |
37
- | `SessionStore`, `AgentRunService`, `DeploymentService` | Redis, MongoDB | `@pikku/redis`, `@pikku/mongodb` |
38
- | `AgentStorageService`, `AgentRunStateService` | MongoDB **only** | `@pikku/mongodb` |
39
- | `LeaseService` | `RedisLeaseService` | `@pikku/redis` |
27
+ | Interface | Backends | Package |
28
+ | ------------------------------------------------------ | ---------------------------------------------------------- | ------------------------------------------------------- |
29
+ | `ContentService` | `S3Content`, `B2Content` | `@pikku/aws-services`, `@pikku/backblaze` |
30
+ | `QueueService` | `SQSQueueService` | `@pikku/aws-services` |
31
+ | `SecretService` | `AWSSecrets`, `RedisSecretService`, `MongoDBSecretService` | `@pikku/aws-services`, `@pikku/redis`, `@pikku/mongodb` |
32
+ | `SchemaService` | `AjvSchemaService`, `CFWorkerSchemaService` | `@pikku/schema-ajv`, `@pikku/schema-cfworker` |
33
+ | `ChannelStore`, `EventHubStore` | Redis, MongoDB | `@pikku/redis`, `@pikku/mongodb` |
34
+ | `PikkuWorkflowService`, `WorkflowRunService` | Redis, MongoDB | `@pikku/redis`, `@pikku/mongodb` |
35
+ | `SessionStore`, `AgentRunService`, `DeploymentService` | Redis, MongoDB | `@pikku/redis`, `@pikku/mongodb` |
36
+ | `AgentStorageService`, `AgentRunStateService` | MongoDB **only** | `@pikku/mongodb` |
37
+ | `LeaseService` | `RedisLeaseService` | `@pikku/redis` |
40
38
 
41
39
  SQL is the third option for every store interface in that table —
42
40
  `KyselyChannelStore`, `KyselyWorkflowService`, `KyselySecretService` and friends
@@ -79,12 +77,12 @@ wrapper has to be constructed and initialised before any of them.
79
77
 
80
78
  Where they diverge:
81
79
 
82
- | | `S3Content` | `B2Content` |
83
- | --- | --- | --- |
84
- | Signing failure | **Fails open** — logs and returns the *unsigned* URL | Throws |
85
- | `writeFile` memory | Streams | **Buffers the whole stream** to compute a SHA-1 |
86
- | Client-side upload integrity | Presigned, expires at a fixed 3600s | `X-Bz-Content-Sha1: do_not_verify` — unverified |
87
- | Credential rotation | Picked up by the SDK provider chain | Auth is cached for the instance's lifetime — construct a new `B2Content` |
80
+ | | `S3Content` | `B2Content` |
81
+ | ---------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------ |
82
+ | Signing failure | **Fails open** — logs and returns the _unsigned_ URL | Throws |
83
+ | `writeFile` memory | Streams | **Buffers the whole stream** to compute a SHA-1 |
84
+ | Client-side upload integrity | Presigned, expires at a fixed 3600s | `X-Bz-Content-Sha1: do_not_verify` — unverified |
85
+ | Credential rotation | Picked up by the SDK provider chain | Auth is cached for the instance's lifetime — construct a new `B2Content` |
88
86
 
89
87
  The S3 fail-open is the one to design around: on a private CloudFront
90
88
  distribution the client gets a 403, and on a public one you have just handed out
@@ -119,7 +117,7 @@ Swapping them changes behaviour without changing types:
119
117
  becomes `1` in the wiring layer, not here.
120
118
 
121
119
  Both throw `UnprocessableContentError` (422) on a failed validation, and both
122
- throw a **bare string** — `Missing validator for <name>` — for a *missing*
120
+ throw a **bare string** — `Missing validator for <name>` — for a _missing_
123
121
  schema. It is not an `Error`, so `catch (e) { e.message }` reads `undefined`.
124
122
  That almost always means codegen did not run.
125
123
 
@@ -1,15 +1,14 @@
1
1
  ---
2
2
  name: pikku-services
3
3
  description: >-
4
- Use for the service layer of a Pikku app — dependency injection with pikkuServices and
5
- pikkuWireServices, startup/shutdown work with pikkuServerLifecycle, configuration through the
6
- secrets, variables and credentials services, the audit sink and buffer, and structured logging.
7
- TRIGGER when: code uses pikkuServices/pikkuWireServices/pikkuServerLifecycle, defineSecret,
8
- defineVariable or defineCredential, user asks about services.ts, lifecycle.ts, dependency
9
- injection, env vars, secrets, API credentials, audit logging, AuditService, PinoLogger, or a
10
- built-in service. DO NOT TRIGGER when: user asks about middleware (use pikku-middleware),
11
- authentication or permissions (use pikku-auth), or a third-party backend such as Redis, S3 or
12
- MongoDB (use pikku-service-backends).
4
+ The Pikku service layer: dependency injection with pikkuServices/pikkuWireServices,
5
+ startup/shutdown via pikkuServerLifecycle, config through the secrets, variables and credentials
6
+ services, audit sink and buffer, structured logging. TRIGGER when: code uses
7
+ pikkuServices/pikkuWireServices/pikkuServerLifecycle/defineSecret/defineVariable/defineCredential,
8
+ or asked about services.ts, lifecycle.ts, DI, env vars, secrets, API credentials, audit logging,
9
+ PinoLogger or a built-in service. DO NOT TRIGGER for middleware (use pikku-middleware),
10
+ authentication or permissions (use pikku-auth), or third-party backends such as Redis, S3, MongoDB
11
+ (use pikku-service-backends).
13
12
  installGroups: [core]
14
13
  ---
15
14
 
@@ -63,7 +63,7 @@ A service factory should **construct** services, not run startup side effects. S
63
63
 
64
64
  ```typescript
65
65
  // src/lifecycle.ts
66
- import { pikkuServerLifecycle } from '@pikku/core'
66
+ import { pikkuServerLifecycle } from '@pikku/core/lifecycle'
67
67
  import type { SingletonServices } from '../types/application-types.js'
68
68
 
69
69
  export const lifecycle = pikkuServerLifecycle<SingletonServices>({
@@ -38,7 +38,7 @@ Concept names are the stable IDs. On re-run after code changes, re-extract only
38
38
 
39
39
  ## How Pikku consumes the blueprint
40
40
 
41
- Full mapping table in `references/pikku-mapping.md`. Summary: entities → Kysely migrations + Zod schemas; commands/queries → `pikkuFunc`s; api surfaces → `wireHTTP`; policies → shared permission functions (collapsing duplicated legacy checks); system workflows → `wireScheduler`/`wireQueueWorker`/`pikkuWorkflowFunc`; integrations → injected services with `defineSecret`/`defineCredential`; test-derived scenarios → `pikkuUserFlow` stories / e2e tests. Humans resolve `migration.json.decisionsNeeded` before any generation starts.
41
+ Full mapping table in `references/pikku-mapping.md`. Summary: entities → Kysely migrations + Zod schemas; commands/queries → `pikkuFunc`s; api surfaces → `wireHTTP`; policies → shared permission functions (collapsing duplicated legacy checks); system workflows → `wireScheduler`/`wireQueueWorker`/`pikkuWorkflowFunc`; integrations → injected services with `defineSecret`/`defineCredential`; test-derived scenarios → `pikkuScenario` / `pikkuFeature` (see `references/tests-to-scenarios.md`). Humans resolve `migration.json.decisionsNeeded` before any generation starts.
42
42
 
43
43
  ## How uncertainty is represented
44
44
 
@@ -75,6 +75,8 @@ pikku-software-archaeology/
75
75
  ├── references/
76
76
  │ ├── blueprint.schema.json # the output contract (JSON Schema)
77
77
  │ ├── pikku-mapping.md # blueprint → Pikku primitives
78
+ │ ├── tests-to-scenarios.md # legacy test suite → blueprint scenarios → pikkuScenario
79
+ │ ├── sources/ # per-framework guides: rails, laravel, nestjs, express
78
80
  │ ├── second-opinion.md # blueprint → owner-facing report: method, voice, red flags
79
81
  │ └── second-opinion-report-template.md # the layered structure to fill in
80
82
  ├── example/
@@ -82,3 +84,12 @@ pikku-software-archaeology/
82
84
  └── scripts/
83
85
  └── validate.mjs # schema + cross-file validation (node, no deps)
84
86
  ```
87
+
88
+ ## Backlog: real-app test runs
89
+
90
+ The framework guides in `references/sources/` are written from general knowledge of each framework and have not been run against a real codebase yet. Run the skill on these, scoped to one slice each (not the whole repo), clone into a scratch directory rather than a pikku repo, and fix whatever the guides miss:
91
+
92
+ - **Redmine** (Rails, issue tracker, ~2006): the hardest test of `sources/rails.md` — callbacks, workflow states, plugins. Scope to issues + workflow transitions.
93
+ - **Immich** (NestJS, photo backend): exercises `sources/nestjs.md` — guards, cron/queues, event handlers. Scope to one domain (e.g. albums or sharing).
94
+
95
+ Things to check on each run: implicit events found, validator passes, which grep patterns in the guide come up empty, and how `tests-to-scenarios.md` handles the repo's own suite.
@@ -75,7 +75,14 @@ Build an inventory before interpreting anything:
75
75
  - TODO / FIXME / HACK / XXX / deprecated markers — each is a gaps.json candidate.
76
76
  - **The test suite** — locate it now, excavate it in Phase 2.
77
77
 
78
- Where intent hides, per ecosystem (read these first):
78
+ **Framework guides.** Once you know the framework, read its guide before extracting. Each lists where intent hides, how its idioms map to blueprint concepts, the traps, and a grep checklist. Read only the ones that apply:
79
+
80
+ - Rails: `references/sources/rails.md`
81
+ - Laravel: `references/sources/laravel.md`
82
+ - NestJS: `references/sources/nestjs.md`
83
+ - Express (also the approach for Koa/Fastify/Hono): `references/sources/express.md`
84
+
85
+ Other ecosystems fall back to this table. Where intent hides, per ecosystem (read these first):
79
86
 
80
87
  | Ecosystem | Highest-yield locations |
81
88
  | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -91,7 +98,7 @@ Where intent hides, per ecosystem (read these first):
91
98
  Tests are the closest thing to an executable product spec. For every test file:
92
99
 
93
100
  - `describe`/`context`/`it` names → **scenarios** (attach to the matching workflow in `workflows.json` under `scenarios[]`, with `fromTest` set).
94
- - User-flow/journey harnesses (`pikkuUserFlow` stories, `.feature` files, Playwright journeys) are the highest-grade scenario source — they already ARE given/when/outcome sequences; extract them verbatim.
101
+ - Journey harnesses (`pikkuScenario` flows, `.feature` files, Playwright/Capybara journeys, request/feature/e2e specs) are the highest-grade scenario source — they already ARE given/when/outcome sequences; extract them verbatim. Triage every test by kind first: see `references/tests-to-scenarios.md`.
95
102
  - Assertions → confirmations of policies and invariants (upgrade their `confidence` to `high`, add the test as evidence).
96
103
  - Fixtures/factories → entity attribute shapes and realistic example data.
97
104
  - Edge-case tests → business rules that exist **nowhere else in the code** (e.g. "replayed webhook events are idempotent" may only be stated in a test).
@@ -14,7 +14,7 @@ The `.knowledge/` blueprint is designed so each concept maps onto exactly one Pi
14
14
  | `workflows.json` kind=user | frontend flows + the commands they chain |
15
15
  | `workflows.json` kind=system, with `schedule` | `wireScheduler` entries |
16
16
  | `workflows.json` multi-step / checkpointing | `pikkuWorkflowFunc` with one `workflow.do(...)` step per blueprint step |
17
- | `workflows.json` `scenarios[]` | **`pikkuUserFlow` stories — this is the canonical target.** Each scenario's given/when/outcome maps 1:1 onto a user-flow step sequence; group scenarios by their workflow into one flow per journey. Only scenarios with no user-facing surface (pure system workflows: cron sweeps, webhook ingest) fall back to API/e2e tests |
17
+ | `workflows.json` `scenarios[]` | **`pikkuScenario` + `pikkuFeature` — this is the canonical target** (`pikkuUserFlow` was renamed to scenario in #862). Each blueprint scenario's given/when/outcome maps onto `scenario.given/when/then` steps; group by workflow into one scenario per journey and by domain into a `pikkuFeature`. Pure system workflows (cron sweeps, webhook ingest) are still scenarios: fire them with `scenario.runScheduledTask` or the webhook route, then `expectEventually`. See `tests-to-scenarios.md` |
18
18
  | `api.json` | `wireHTTP` routes: keep `path`+`method` for compatibility, point at the mapped command/query func; `auth: none`/capability-URL surfaces get `auth: false` |
19
19
  | `api.json` kind=webhook-in | `wireHTTP` with `auth: false` + signature-verification middleware from the integration |
20
20
  | `integrations.json` | services in `services.ts` (constructor-injected classes); `configVia` env vars become `defineSecret` / config; per-user credentials become `defineCredential` |
@@ -0,0 +1,75 @@
1
+ # Source guide: Express (Node.js)
2
+
3
+ Read this when the survey finds `express` in `package.json`. Express has no conventions, so every Express app is its own framework. The same product can be one 3,000-line `server.js` or a layered app that imitates Nest. Your job is to find which, then trace the request path by hand. The same approach works for Koa, Fastify and Hono; adjust the registration syntax.
4
+
5
+ ## Detect
6
+
7
+ `express` in dependencies, `app.listen`, `express.Router()`. Note the module system (CJS vs ESM), TypeScript or not, the ORM or query layer (Prisma, Sequelize, Knex, Mongoose, raw `pg`/`mysql2`), the session/auth approach (Passport, JWT, express-session) and the entry point named in `package.json` `main`/`scripts.start`.
8
+
9
+ ## Where intent hides
10
+
11
+ Start at the entry file and follow `app.use`. **Order is behavior**: middleware registered before a route applies to it, middleware registered after does not.
12
+
13
+ | Location | Blueprint concept |
14
+ | ------------------------------------------------------------------------- | ----------------------------------------------------------------- |
15
+ | entry file (`app.js`, `server.js`, `index.js`, `src/app.ts`): every `app.use` in order | global middleware chain, raw-body routes, CORS, mounts, architecture constraints |
16
+ | `routes/`, `routers/`, `api/`: `router.get/post/...`, `app.route()` | api surfaces; track the mount prefix (`app.use('/v1/invoices', r)`) |
17
+ | auth middleware (`requireAuth`, `isAdmin`, `passport.authenticate`, `jwt.verify`) | authorization policies; note which routes use it and which do not |
18
+ | handlers/controllers: inline `if (...) return res.status(4xx)` | preconditions and policies (every guard is one) |
19
+ | validation: `joi`, `zod`, `yup`, `express-validator`, `celebrate`, hand-rolled `if (!req.body.x)` | command `input[]` + validation policies |
20
+ | models: Mongoose schemas, Sequelize models, Prisma schema, SQL files, migrations (`knex`, `umzug`), or raw SQL strings | entities, constraints |
21
+ | `services/`, `lib/`, `utils/`, `helpers/` | business logic; the real commands usually live here |
22
+ | `jobs/`, `workers/`, `cron/`, `node-cron`, `agenda`, `bull`/`bullmq`, `setInterval` at module scope | system workflows |
23
+ | webhook routes (`/webhooks/stripe`, signature checks, `express.raw`) | integration ingress; command + signature policy |
24
+ | `socket.io` / `ws` setup | realtime interface |
25
+ | `process.env.*`, `config/`, `dotenv` | integrations, toggles, deployment constraints |
26
+ | `test/`, `__tests__/`, `*.test.js` (supertest/jest/mocha) | scenarios; edge cases not in the code |
27
+
28
+ ## Idiom -> blueprint -> Pikku
29
+
30
+ | Express idiom | Blueprint concept | Pikku shortcut |
31
+ | ----------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------ |
32
+ | `router.post('/:id/send', auth, handler)` | command (`SendInvoice`), not "POST /:id/send" | `pikkuFunc` + `wireHTTP` keeping path for compatibility |
33
+ | `router.use(requireAuth)` before a set of routes | authorization policy `enforcedAt` each route in scope | auth by default; list public routes explicitly |
34
+ | `if (resource.userId !== req.user.id) return 403` | ownership policy, repeated per handler | one named permission function, wired everywhere |
35
+ | `req.body` hand-checks / Joi schema | `input[]` + validation policy | Zod schema |
36
+ | `res.status(409)` on duplicate / state conflict | precondition on entity state | guard clause inside the func |
37
+ | `await sendEmail(...)` at the end of a handler | **implicit event consumer** | emit event, consumer in `wireQueueWorker` |
38
+ | `setTimeout`/`setInterval`/`node-cron` in module scope | system workflow (often undocumented) | `wireScheduler` |
39
+ | `emitter.emit/on` (`EventEmitter`) | in-process event (not durable) | EventHub / queue; note the lost-on-crash gap |
40
+ | Mongoose/Sequelize hooks (`pre('save')`, `beforeCreate`) | implicit events / validation | explicit step in command |
41
+ | `express.raw()` / `bodyParser.raw` for one route | deployment constraint (signature verification) | raw-body config on that `wireHTTP` route |
42
+ | `app.use(express.static)` + SPA fallback | frontend hosting | separate deploy / TanStack Start |
43
+ | `res.render` (EJS/Pug/Handlebars) | server-rendered frontend (see SKILL.md note) | TanStack route + `usePikkuQuery` |
44
+ | `req.session` / `req.user` set by Passport | session model | Pikku session |
45
+
46
+ ## Traps
47
+
48
+ - **Public by omission.** The unguarded route is the default. Build a table of every route vs which middleware applies and look for ones that have none; the legacy apps that "have auth" often have three unguarded admin endpoints. Each is a gaps.json security entry.
49
+ - **Middleware order bugs.** A route registered before `app.use(auth)` is public even though it sits in the same file. Read order top to bottom, with mounts.
50
+ - **`next(err)` and the error handler** define the real error contract (status codes, shapes). Read the final `app.use((err, req, res, next) => ...)` and record the mapping.
51
+ - **Business logic in handlers.** If no service layer exists, the handler IS the command: its guards, DB writes, side effects and response form one concept. Name it from the effect.
52
+ - **Duplicated handlers.** Copy-pasted routes (`/v1` and `/v2`, or admin and user versions) that drifted. List both, flag the difference as a gap.
53
+ - **Hidden writers.** Several routes and a cron job may all write one status column. Grep the column to find every transition, as with Rails.
54
+ - **Callbacks and fire-and-forget.** `doThing().catch(console.error)` without `await` means a side effect that can fail silently: an invariant with `enforcedBy: nothing`.
55
+ - **Tenancy by hand.** Whether `WHERE tenant_id = req.user.tenantId` appears on every query is the question. Missing on one is a gap.
56
+ - **Mongo and loose schemas.** With schemaless data, the entity shape is spread across writers. Reconstruct attributes from schemas, fixtures and serializers, and say which are optional in practice.
57
+ - **Env-driven branches** (`if (process.env.NODE_ENV === 'production')`, feature flags): record the flag and the behavior difference.
58
+ - **Monolith with many entry points**: `server.js`, `worker.js`, `cron.js`, `scripts/`. Each runnable is a component in `architecture.json`.
59
+
60
+ ## Search checklist
61
+
62
+ ```
63
+ grep -rnE "app\.use\(|router\.use\(" . # middleware + mounts, in file order
64
+ grep -rnE "(app|router)\.(get|post|put|patch|delete|all|route)\(" --include=*.{js,ts} .
65
+ grep -rnE "status\((401|403|404|409|422)\)" --include=*.{js,ts} . # guards = policies
66
+ grep -rnE "req\.user|req\.session|req\.auth" --include=*.{js,ts} .
67
+ grep -rnE "setInterval|setTimeout\(.*[0-9]{4,}|cron\.schedule|new CronJob|agenda\.define|\.process\(" .
68
+ grep -rnE "express\.raw|bodyParser\.raw|stripe\.webhooks|constructEvent|createHmac" .
69
+ grep -rnE "emit\(|\.on\('" --include=*.{js,ts} .
70
+ grep -rnE "process\.env\.[A-Z_]+" -o --include=*.{js,ts} . | sort | uniq -c | sort -rn
71
+ ```
72
+
73
+ ## Confidence notes
74
+
75
+ Behavior in a handler plus a supertest scenario is `high`. A rule that only appears in one route of several similar ones is `medium` (decide whether the others should have it, and record the decision needed). Because there is no declarative structure, expect more `medium` than in Nest, and prefer a short `decisionsNeeded` list over guessing.
@@ -0,0 +1,77 @@
1
+ # Source guide: Laravel (PHP)
2
+
3
+ Read this when the survey finds `composer.json` with `laravel/framework` and an `artisan` file. Laravel is conventional like Rails, but a lot of behavior is wired by service providers and the event system, so read those before the controllers.
4
+
5
+ ## Detect
6
+
7
+ `artisan`, `bootstrap/app.php`, `routes/`, `app/Http`, `database/migrations`. Check the Laravel version in `composer.lock`: before 11, middleware lives in `app/Http/Kernel.php`; from 11, in `bootstrap/app.php`. Also check for Livewire, Inertia, Filament or Nova, which change what the frontend and admin are.
8
+
9
+ ## Where intent hides
10
+
11
+ | Location | Blueprint concept |
12
+ | ------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
13
+ | `database/migrations/*` (replay in order), `database/schema/*.sql` | entities, attributes, DB-enforced invariants |
14
+ | `app/Models/*`: `$fillable`/`$guarded`, `$casts`, relations, scopes, `booted()` | entities, relationships, tenancy scopes |
15
+ | `app/Observers/*`, model `$dispatchesEvents`, `boot()` hooks | **implicit events** (`created`, `updated`, `deleted`) |
16
+ | `app/Events` + `app/Listeners` + `EventServiceProvider` | explicit events and their consumers (`explicit: true`) |
17
+ | `routes/web.php`, `api.php`, `channels.php`, `console.php` | api surfaces, realtime, CLI |
18
+ | `app/Http/Controllers/**` | commands/queries; invokable controllers are one command each |
19
+ | `app/Http/Requests/*` (FormRequest): `rules()`, `authorize()` | input shape + validation and authorization policies |
20
+ | `app/Policies/*`, `Gate::define` in `AuthServiceProvider` | policies (authorization) |
21
+ | `app/Http/Middleware/*` + middleware groups | cross-cutting policies (auth, tenancy, throttling, locale) |
22
+ | `app/Jobs/*`, `config/queue.php`, `app/Console/Kernel.php` / `routes/console.php` `schedule()` | system workflows, async effects |
23
+ | `app/Console/Commands/*` | operator workflows and CLI interface |
24
+ | `app/Notifications/*`, `app/Mail/*` | event consumers + integrations (channels: mail, Slack, SMS) |
25
+ | `app/Providers/*` | bindings, global scopes, gates, macros: read for hidden wiring |
26
+ | `config/*.php` + `.env.example` | integrations, feature toggles, tenancy mode |
27
+ | `tests/Feature`, `tests/Unit` (Pest/PHPUnit) | scenarios; rules that exist only in tests |
28
+ | `app/Filament/**`, `app/Nova/**`, Livewire `app/Livewire/**` | admin workflows; Livewire components are UI **and** commands |
29
+
30
+ ## Idiom -> blueprint -> Pikku
31
+
32
+ | Laravel idiom | Blueprint concept | Pikku shortcut |
33
+ | ------------------------------------------------------ | -------------------------------------------------- | -------------------------------------------------------- |
34
+ | `Model::created` observer / `$dispatchesEvents` | implicit event, consumers = observer body | emit event; consumer = `wireQueueWorker` |
35
+ | `event(new X)` + `Listener` (`ShouldQueue`) | explicit event + async consumer | EventHub topic + queue worker |
36
+ | `FormRequest::rules()` | validation policy + command `input[]` | Zod schema on `input` |
37
+ | `FormRequest::authorize()`, `$this->authorize()`, `Gate::allows`, `->can()` | authorization policy | one named permission function |
38
+ | `Route::middleware('auth:sanctum', 'can:...')` | auth policy `enforcedAt` the route group | auth middleware + permission |
39
+ | `Policy` class methods (`view`, `update`, `delete`) | per-entity authorization policies | permission functions per action |
40
+ | `dispatch(new Job)` / `Bus::chain` / `Bus::batch` | command effect: async step / multi-step workflow | `wireQueueWorker` / `pikkuWorkflowFunc` |
41
+ | `$schedule->command(...)->dailyAt(...)` | system workflow with `schedule` | `wireScheduler` |
42
+ | `Notification::send` (mail/database/slack) | event consumer + integration | service call in consumer |
43
+ | `Gate::before`, super-admin bypass | invariant exception: **record it** | explicit permission, never a global bypass |
44
+ | global scope (`addGlobalScope`, `SoftDeletes`, tenant trait) | tenancy scoping / soft-delete state | session filter on reads; `archived` state |
45
+ | Eloquent accessors/mutators, `$casts` | derived/normalized attributes | Zod transforms or query mapping |
46
+ | Sanctum / Passport / Fortify / Breeze | `LogInUser` etc. + token issuance | auth middleware / `better-auth` |
47
+ | Livewire component (`wire:click`, public props) | UI route + commands in one class: split them | TanStack route + funcs |
48
+ | Blade + Alpine/Inertia pages | frontend (see SKILL.md server-rendered note) | TanStack route; Alpine behaviors = `custom-logic` candidates |
49
+
50
+ ## Traps
51
+
52
+ - **Observers and model `boot()` are invisible from controllers.** A controller that only calls `$order->save()` may trigger emails, stock updates and audit logs. Read the model's observers before naming the command's effects.
53
+ - **Mass assignment shapes the API.** `$fillable` is the real allowed-input list; `$guarded = []` means any column is writable (a gap if exposed to users).
54
+ - **Service providers run before everything.** Macros, global scopes, gates and route model bindings registered there change behavior across the app.
55
+ - **Route model binding** (`Route::get('/invoices/{invoice}')`) silently adds a lookup + 404 + possibly a scope. Count it as a precondition, and check `Route::bind` customisations.
56
+ - **Queued vs sync.** `ShouldQueue` on a listener/notification changes consistency semantics (eventual vs immediate). Record which.
57
+ - **Duplicated rules between FormRequest, controller and model** are common; list every location in `enforcedAt`, and flag drift as a gap.
58
+ - **Config in `.env` drives behavior** (`FEATURE_X`, `TENANCY_MODE`). Collect every `env(` / `config(` that gates a branch.
59
+ - **Third-party packages carry product behavior** (Spatie `laravel-permission` = roles/permissions, `laravel-medialibrary`, Cashier = billing, `laravel-activitylog` = audit events). Read the package config, not the vendor code, and map to policies/integrations.
60
+ - **Raw `DB::` queries and `DB::statement`** often hold reporting logic and tenancy bypasses. Grep and review each.
61
+
62
+ ## Search checklist
63
+
64
+ ```
65
+ grep -rnE "Observer|\$dispatchesEvents|static::(created|updated|deleted|saving)" app
66
+ grep -rnE "event\(|Event::dispatch|::dispatch\(|dispatch\(new" app
67
+ grep -rnE "Gate::|->authorize\(|->can\(|@can|middleware\('can" app routes
68
+ grep -rnE "addGlobalScope|withoutGlobalScope|SoftDeletes|withTrashed" app
69
+ grep -rnE "DB::(raw|select|statement|table)" app
70
+ grep -rnE "env\(|config\('.*feature" app config
71
+ grep -rn "schedule->" app routes
72
+ cat composer.json | grep -E "spatie|laravel/(cashier|sanctum|passport|fortify|nova)|livewire|filament"
73
+ ```
74
+
75
+ ## Confidence notes
76
+
77
+ A validation in a FormRequest backed by a Feature test is `high`. Behavior reconstructed only from an observer with no test is `medium`. Anything resting on a Livewire component's public properties alone is `low`; confirm through the Blade template and its tests.
@@ -0,0 +1,73 @@
1
+ # Source guide: NestJS
2
+
3
+ Read this when the survey finds `@nestjs/core` in `package.json` and `nest-cli.json`. NestJS is the closest source to Pikku: modules, controllers, guards and providers map almost one-to-one. The risk is not finding things, it is over-mirroring the structure instead of recovering the product.
4
+
5
+ ## Detect
6
+
7
+ `nest-cli.json`, `src/main.ts` (`NestFactory.create`), `*.module.ts`. Check the platform adapter (Express or Fastify), transports (`@nestjs/microservices`, GraphQL, WebSockets) and monorepo mode (`apps/`, `libs/`): each app is a component, each lib may be a shared domain.
8
+
9
+ ## Where intent hides
10
+
11
+ | Location | Blueprint concept |
12
+ | --------------------------------------------------------------------------- | -------------------------------------------------------------------- |
13
+ | `*.module.ts` (imports/providers/exports) | domain candidates; the dependency graph shows ownership |
14
+ | `*.controller.ts`: `@Get/@Post/...`, `@UseGuards`, `@Roles`, `@HttpCode` | api surfaces, command/query split, authorization |
15
+ | `*.service.ts` | the actual command/query logic (controllers are usually thin) |
16
+ | `dto/*.ts` with `class-validator` (`@IsString`, `@Min`), `class-transformer` | command `input[]` + validation policies |
17
+ | `*.entity.ts` (TypeORM), `schema.prisma`, `*.schema.ts` (Mongoose), `*.model.ts` (Sequelize/MikroORM) | entities, relations, constraints |
18
+ | `*.guard.ts`, `@Roles()`/`@Public()` decorators, `APP_GUARD` providers | policies (authorization), including global defaults |
19
+ | `*.pipe.ts`, global `ValidationPipe` options | validation policies (check `whitelist`, `forbidNonWhitelisted`) |
20
+ | `*.interceptor.ts`, `*.filter.ts`, `*.middleware.ts` | cross-cutting behavior: audit, caching, error mapping, tenancy |
21
+ | `@nestjs/schedule` (`@Cron`, `@Interval`), `@nestjs/bull`/`bullmq` (`@Processor`, `@Process`) | system workflows, queue consumers |
22
+ | `@nestjs/event-emitter` (`@OnEvent`, `emitter.emit`), `@nestjs/cqrs` | explicit events and consumers; CQRS gives commands/queries for free |
23
+ | `@nestjs/microservices` (`@MessagePattern`, `@EventPattern`) | event/command ingress from other services |
24
+ | `*.gateway.ts` (`@WebSocketGateway`, `@SubscribeMessage`) | realtime interface |
25
+ | `*.resolver.ts` (GraphQL) | queries/commands via `@Query`/`@Mutation` |
26
+ | `@nestjs/config` + `.env.example`, `config/*.ts` | integrations, toggles |
27
+ | `*.spec.ts` (unit), `test/*.e2e-spec.ts` (supertest) | scenarios; e2e specs are the best source |
28
+
29
+ ## Idiom -> blueprint -> Pikku
30
+
31
+ | NestJS idiom | Blueprint concept | Pikku shortcut |
32
+ | ---------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------- |
33
+ | `@Controller` method + service method | one command or query | one `pikkuFunc`, wired with `wireHTTP` |
34
+ | DTO class + `class-validator` | `input[]` + validation policy | Zod schema |
35
+ | `@UseGuards(AuthGuard, RolesGuard)` / `@Roles` | authorization policy | permission functions / middleware |
36
+ | `APP_GUARD` global guard + `@Public()` opt-out | default-deny policy + exceptions list | auth by default, `auth: false` for the listed routes |
37
+ | `@Injectable` provider | service or domain logic | class in `services.ts` |
38
+ | `@Cron`, `@Interval`, `@Timeout` | system workflow with `schedule` | `wireScheduler` |
39
+ | `@Processor` / `@Process` | queue consumer | `wireQueueWorker` |
40
+ | `emitter.emit('x')` + `@OnEvent('x')` | explicit event + consumer | EventHub topic / queue |
41
+ | CQRS `CommandHandler`, `QueryHandler`, `EventsHandler`, sagas | commands, queries, events, workflow | direct 1:1; sagas become `pikkuWorkflowFunc` |
42
+ | `@Interceptor` (logging/caching/transform) | cross-cutting concern | middleware, only if behavior matters to the product |
43
+ | Exception filter mapping errors to status codes | error contract of the API | keep status/code mapping explicit in the func |
44
+ | Request-scoped provider (`Scope.REQUEST`), `AsyncLocalStorage`, `nestjs-cls` | session / tenant context | Pikku session / services |
45
+ | `@nestjs/swagger` decorators | documented contract (check for drift) | generated from `wireHTTP` + schemas |
46
+
47
+ ## Traps
48
+
49
+ - **Structure is not intent.** Nest makes it easy to produce a service-per-table with CRUD controllers. Do not turn `UsersController.update` into `UpdateUser` unless it is a real business action; look at what the service does (status flips, notifications) and name the command from that.
50
+ - **Services calling services.** Cross-module calls (`this.billing.charge`) are the real dependency graph. Derive domains from entity ownership, not from module folders.
51
+ - **Guards can be bypassed by omission.** Check every controller for a missing `@UseGuards` when the rest have one, and read `APP_GUARD` registrations to see whether the default is secure. Missing coverage is a gaps.json security entry.
52
+ - **Global setup in `main.ts`**: `app.useGlobalPipes`, `useGlobalGuards`, `enableCors`, `setGlobalPrefix`, `enableVersioning`, raw-body config for webhooks. These change every route's behavior and belong in `architecture.json`.
53
+ - **Decorators carry semantics.** `@Transform`, `@Exclude`, `@Expose`, `@SerializeOptions` shape responses; `@Exclude` on a password field is an invariant about never exposing it.
54
+ - **Dynamic modules** (`forRoot`, `forRootAsync`) hide which features are on. Read the module's config call and the env it reads.
55
+ - **ORM hooks** (`@BeforeInsert`, `@AfterUpdate`, TypeORM subscribers, Prisma middleware/extensions) are implicit events like Rails callbacks.
56
+ - **Event emitter is in-process.** `@OnEvent` handlers run in the same process and are lost on crash, unlike real queues. Record that delivery guarantee as an invariant (`enforcedBy: nothing`).
57
+ - **Hybrid apps** (HTTP + microservice + WebSocket in one `main.ts`): each transport is an `interfaces.json` entry.
58
+
59
+ ## Search checklist
60
+
61
+ ```
62
+ grep -rnE "@(Get|Post|Put|Patch|Delete|All)\(" src apps libs
63
+ grep -rnE "@UseGuards|@Roles|@Public|APP_GUARD|@SetMetadata" src apps libs
64
+ grep -rnE "useGlobal(Pipes|Guards|Interceptors|Filters)|enableCors|setGlobalPrefix|rawBody" src apps
65
+ grep -rnE "@(Cron|Interval|Timeout|Process|Processor|OnEvent|EventPattern|MessagePattern|SubscribeMessage)" src apps libs
66
+ grep -rnE "@(BeforeInsert|AfterInsert|BeforeUpdate|AfterUpdate|EventSubscriber)|\$use\(" src apps libs
67
+ grep -rnE "forRootAsync?\(|ConfigService|process\.env" src apps libs
68
+ ls test *.e2e-spec.ts 2>/dev/null
69
+ ```
70
+
71
+ ## Confidence notes
72
+
73
+ A DTO validation exercised by an e2e spec is `high`. A guard-based rule with no test is `medium`. Because Nest's structure is declarative, most extraction is `high`; keep `medium`/`low` for logic buried in service methods that mix concerns and for anything decided by dynamic module config.