jpie 2.0.2 → 2.1.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.
- checksums.yaml +4 -4
- data/.claude/skills/nasa-power-of-ten-ruby/SKILL.md +71 -0
- data/.claude/skills/root-cause-analysis/SKILL.md +62 -0
- data/.claude/skills/skills-and-subagents/SKILL.md +54 -0
- data/.cursor/agents/bias-reviewer.md +58 -0
- data/.cursor/agents/lint-format.md +58 -0
- data/.cursor/agents/nasa-power-of-ten-reviewer.md +38 -0
- data/.cursor/agents/systematic-debugging.md +52 -0
- data/Gemfile.lock +1 -1
- data/README.md +114 -0
- data/lib/json_api/controllers/concerns/controller_helpers/error_rendering.rb +18 -0
- data/lib/json_api/controllers/concerns/resource_actions/crud_helpers.rb +17 -0
- data/lib/json_api/controllers/concerns/resource_actions/resource_loading.rb +3 -2
- data/lib/json_api/controllers/concerns/resource_actions/serialization.rb +24 -0
- data/lib/json_api/controllers/concerns/resource_actions/sideposting.rb +97 -0
- data/lib/json_api/controllers/concerns/resource_actions/sideposting_primary_first.rb +63 -0
- data/lib/json_api/controllers/concerns/resource_actions.rb +18 -4
- data/lib/json_api/railtie.rb +2 -0
- data/lib/json_api/resources/resource.rb +4 -0
- data/lib/json_api/resources/resource_loader.rb +10 -21
- data/lib/json_api/routing.rb +2 -2
- data/lib/json_api/serialization/concerns/deserialization_helpers.rb +11 -2
- data/lib/json_api/serialization/concerns/relationship_processing.rb +1 -1
- data/lib/json_api/serialization/concerns/relationships_deserialization.rb +12 -0
- data/lib/json_api/sideposting/lid_resolver.rb +72 -0
- data/lib/json_api/sideposting/order.rb +11 -0
- data/lib/json_api/sideposting/processor.rb +118 -0
- data/lib/json_api/support/relationship_helpers.rb +4 -0
- data/lib/json_api/support/resource_identifier.rb +4 -0
- data/lib/json_api/version.rb +1 -1
- data/lib/json_api.rb +6 -1
- metadata +13 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b40ba16f3f137fa6f71b5f03a5aae566168c602ecab473c8804991a5b37ed70f
|
|
4
|
+
data.tar.gz: ea9ba44be05ef07e7306ff0772377277f8f25eab9c199fbe2d3f7be7fc2477ac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 216f53ac57192799de8c9864b72d19da9c9284600c4e6b5807ef3c1aca501986411a5a110cfd5ae862066ab033c9b8d85002cb1a1c5a9232129221d2228356d0
|
|
7
|
+
data.tar.gz: d59596b504c0c69d9783ab590fdf70053ee7728def4ecc38902cb32f70d6313d81bfda425307bb8e0673e47daa808829df37e163cfe97c12767307ab29515ee9
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: NASA Power of Ten (Ruby/Rails)
|
|
3
|
+
description: NASA Power of Ten rules that are highly or partially relevant in modern Ruby/Rails. Use when reviewing or writing Ruby/Rails code for method length, scope of data, return values, assertions, control flow, loop bounds, and deep chains. Rules 3 and 8 (heap allocation, preprocessor) are not applicable.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# NASA Power of Ten (Ruby/Rails)
|
|
7
|
+
|
|
8
|
+
Use this skill when reviewing or writing Ruby/Rails code for the NASA Power of Ten rules that are **highly** or **partially** relevant. Ignore rules 3 and 8 (heap allocation, preprocessor); treat the rest as below.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Highly relevant
|
|
13
|
+
|
|
14
|
+
### 4. Limit function length (~60 lines)
|
|
15
|
+
|
|
16
|
+
- Keep methods short enough to fit on one "page" (~60 lines).
|
|
17
|
+
- Extract steps into private helpers or small service methods.
|
|
18
|
+
- Long methods are a common source of bugs; RuboCop and Rails style favour short methods.
|
|
19
|
+
|
|
20
|
+
### 5. Assertions / validate assumptions (intent)
|
|
21
|
+
|
|
22
|
+
- Validate preconditions at entry (e.g. required args, presence).
|
|
23
|
+
- Check invariants after critical steps where it matters.
|
|
24
|
+
- In Rails: use validations, `bang` methods, and explicit checks; avoid silent continuation after failure.
|
|
25
|
+
|
|
26
|
+
### 6. Minimize scope of data
|
|
27
|
+
|
|
28
|
+
- Declare variables in the innermost block where they are needed.
|
|
29
|
+
- Prefer method-local and block-local variables over instance variables.
|
|
30
|
+
- Avoid global or broad shared state; keep state as local as possible.
|
|
31
|
+
|
|
32
|
+
### 7. Check return values
|
|
33
|
+
|
|
34
|
+
- Use the return value of non-void methods (e.g. `save`, `update`, service calls) or explicitly ignore it.
|
|
35
|
+
- Do not call methods that return success/failure and then ignore the result without a comment or explicit discard (e.g. `_ = ...`).
|
|
36
|
+
- Let failures surface; do not hide them by ignoring return values.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Partially relevant
|
|
41
|
+
|
|
42
|
+
### 1. Simple control flow
|
|
43
|
+
|
|
44
|
+
- Prefer iteration over recursion in hot or complex paths.
|
|
45
|
+
- Avoid exotic control flow (e.g. continuations, complex throw/catch) in normal app code.
|
|
46
|
+
- Recursion is acceptable when depth is naturally bounded (e.g. tree walk with limited depth).
|
|
47
|
+
|
|
48
|
+
### 2. Fixed loop bounds
|
|
49
|
+
|
|
50
|
+
- For loops that could run over unbounded or very large data (e.g. background jobs, batch processing), use a maximum iteration count or limit.
|
|
51
|
+
- Prefer iterating over bounded collections or ranges; if using `while`/`loop`, add an explicit cap and break.
|
|
52
|
+
|
|
53
|
+
### 9. Limit deep dereference / long chains
|
|
54
|
+
|
|
55
|
+
- Avoid long method chains (e.g. `a.b.c.d.e`) that are brittle (nil, N+1, unclear ownership).
|
|
56
|
+
- Prefer one or two steps and named intermediate variables.
|
|
57
|
+
- Avoid dynamic dispatch (e.g. `send`) in critical paths unless simple and well-defined.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Summary
|
|
62
|
+
|
|
63
|
+
| Rule | Relevance | Focus |
|
|
64
|
+
|------|-------------|--------|
|
|
65
|
+
| 4 | High | Short methods; extract helpers. |
|
|
66
|
+
| 5 | High (intent) | Validations; explicit checks. |
|
|
67
|
+
| 6 | High | Smallest scope; locals over instance/global. |
|
|
68
|
+
| 7 | High | Use or explicitly ignore return values. |
|
|
69
|
+
| 1 | Partial | Iteration over recursion; no exotic control flow. |
|
|
70
|
+
| 2 | Partial | Bounded loops in jobs/large data. |
|
|
71
|
+
| 9 | Partial | Short chains; named intermediates. |
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Root Cause Analysis
|
|
3
|
+
description: Systematic investigation to find the true underlying cause of a problem before applying fixes. Use when debugging test or gem behaviour failures, investigating serialization or request/response errors, or when a fix might only address symptoms. Covers problem definition (observable symptom only), evidence gathering, 5 Whys / Fishbone, verification (would fixing this prevent it?), and red flags that indicate skipping investigation. Do NOT use for routine implementation steps or when the cause is already known and verified.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Root Cause Analysis
|
|
7
|
+
|
|
8
|
+
**Do not treat symptoms—find and fix the root cause.** Before applying a fix, verify you understand why the failure occurs. Symptom fixes mask underlying issues and waste time.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- Debugging RSpec or gem integration failures, or serialization/request errors
|
|
13
|
+
- Investigating "why did X break?" or "why does this spec fail?"
|
|
14
|
+
- Before applying a fix when the cause is unclear or multiple hypotheses exist
|
|
15
|
+
- When the user asks for root cause analysis or systematic debugging
|
|
16
|
+
|
|
17
|
+
Do NOT use when the cause is already known and verified, or for routine implementation with no failure under investigation.
|
|
18
|
+
|
|
19
|
+
## Core principle
|
|
20
|
+
|
|
21
|
+
**Find root cause before attempting fixes.** Quick patches mask underlying issues. If you are trying fixes without understanding why they might work, you are guessing—stop and investigate.
|
|
22
|
+
|
|
23
|
+
## Problem definition (observable symptom only)
|
|
24
|
+
|
|
25
|
+
Define the problem in terms of **what is observed**, not assumed cause:
|
|
26
|
+
|
|
27
|
+
- **What:** Observable symptom (e.g. "RSpec fails with NoMethodError", "JSON:API response missing attribute")
|
|
28
|
+
- **Where:** Class, file, or endpoint affected
|
|
29
|
+
- **When:** Timeline, frequency, or trigger (e.g. "after adding filter X", "only with include Y")
|
|
30
|
+
- **Impact:** What is affected (e.g. "all resource specs", "serialization for type Z")
|
|
31
|
+
|
|
32
|
+
## Evidence gathering
|
|
33
|
+
|
|
34
|
+
Gather facts before concluding. Prefer:
|
|
35
|
+
|
|
36
|
+
- Full error message and stack trace
|
|
37
|
+
- Reproduction steps (exact test or request that fails)
|
|
38
|
+
- Recent changes (git log, new options, caller expectations)
|
|
39
|
+
- Comparison with working cases (what is different: params, include, serializer?)
|
|
40
|
+
|
|
41
|
+
Do not rely on assumptions. Evidence supports or refutes hypotheses.
|
|
42
|
+
|
|
43
|
+
## Methodology and verification
|
|
44
|
+
|
|
45
|
+
- **5 Whys:** State the problem; ask "why?" repeatedly until you reach a cause you can fix. **Verify:** Would fixing this prevent the issue? If not, keep investigating.
|
|
46
|
+
- **One hypothesis at a time:** Form a testable hypothesis, check it with minimal change, then iterate or proceed.
|
|
47
|
+
- **Phased protocol:** Investigation → pattern analysis → hypothesis and test → implementation only after root cause is identified and verified.
|
|
48
|
+
|
|
49
|
+
## Red flags: you are skipping investigation
|
|
50
|
+
|
|
51
|
+
| Thought or action | Reality |
|
|
52
|
+
|-------------------|---------|
|
|
53
|
+
| "Let me try this quick fix" | You do not understand the cause |
|
|
54
|
+
| "Maybe if I add a guard here" | Guessing, not debugging |
|
|
55
|
+
| "Let me try a few things" | Random changes waste time |
|
|
56
|
+
|
|
57
|
+
When you notice these, stop and run through problem definition, evidence, and one hypothesis at a time.
|
|
58
|
+
|
|
59
|
+
## Integration
|
|
60
|
+
|
|
61
|
+
- **bias-reviewer** checks for error-masking (fix root cause, not symptom). Use this skill to *do* the investigation before the bias-reviewer runs.
|
|
62
|
+
- For complex failures: consider invoking the **systematic-debugging** sub-agent, which enforces this protocol and returns a diagnosis before any fix.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Skills and Sub-agents
|
|
3
|
+
description: How to use and design skills and sub-agents for separation of concerns. Use when creating or updating a skill or sub-agent, when deciding whether to use a rule vs skill vs command, when delegating to a sub-agent, or when structuring a task across multiple capabilities. Covers rules vs skills vs commands, skill description (what/when/do-not), when to use sub-agents vs skills, single responsibility, and description design for discovery. Do NOT use for routine implementation that does not touch skill/agent authoring or task delegation strategy.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skills and Sub-agents
|
|
7
|
+
|
|
8
|
+
Guidance for separation of concerns: when to use rules vs skills vs commands, how to scope skills and sub-agents, and when to delegate.
|
|
9
|
+
|
|
10
|
+
## Rules vs skills vs commands
|
|
11
|
+
|
|
12
|
+
| | Rules | Skills | Commands |
|
|
13
|
+
|--|--------|--------|----------|
|
|
14
|
+
| **Role** | Guide behaviour | Give a capability (workflows, procedures) | Trigger a saved prompt |
|
|
15
|
+
| **Activation** | Always on, or agent decides, or file match, or manual | Agent decides or user invokes `/skill` | User invokes only (agent never auto-runs) |
|
|
16
|
+
| **Content** | Flat instructions | SKILL.md + optional resources | Single Markdown prompt |
|
|
17
|
+
| **Use for** | Standards, conventions, guardrails | Multi-step workflows, domain knowledge | Quick, repeatable one-shot prompts |
|
|
18
|
+
|
|
19
|
+
- **Rules** — "How the agent should behave." No procedures.
|
|
20
|
+
- **Skills** — "How to do something." Loaded on demand.
|
|
21
|
+
- **Commands** — "A prompt you retype often." User-triggered only.
|
|
22
|
+
|
|
23
|
+
Do not put multi-step procedures in rules; use a skill. Do not put always-on behaviour in a skill; use a rule.
|
|
24
|
+
|
|
25
|
+
## Skill scope and description
|
|
26
|
+
|
|
27
|
+
Each skill should have **one clear responsibility**. The description is used for discovery (name + description first; full content when relevant).
|
|
28
|
+
|
|
29
|
+
### Description pattern: what + when + do-not
|
|
30
|
+
|
|
31
|
+
- **What** — What the skill does and what it produces.
|
|
32
|
+
- **When** — Trigger conditions and terms users actually use.
|
|
33
|
+
- **Do NOT** — Explicit out-of-scope to avoid mis-triggering.
|
|
34
|
+
|
|
35
|
+
Write in **third person**. Be concrete; vague descriptions reduce correct triggering.
|
|
36
|
+
|
|
37
|
+
## When to use sub-agents vs skills
|
|
38
|
+
|
|
39
|
+
| Use **sub-agents** when… | Use **skills** when… |
|
|
40
|
+
|--------------------------|------------------------|
|
|
41
|
+
| Context isolation (long or noisy work) | Single-purpose task |
|
|
42
|
+
| Parallel workstreams | Quick, repeatable action |
|
|
43
|
+
| Specialized expertise over many steps | Task completes in one shot |
|
|
44
|
+
| Independent verification | No separate context needed |
|
|
45
|
+
|
|
46
|
+
Sub-agents run in their own context and return a result to the parent. Skills are procedures the main agent follows.
|
|
47
|
+
|
|
48
|
+
## Sub-agent responsibility
|
|
49
|
+
|
|
50
|
+
- **One clear responsibility per sub-agent.** Avoid generic "helper" agents.
|
|
51
|
+
- **Description drives delegation.** Include "when to use" (and "use proactively when…" if appropriate).
|
|
52
|
+
- **Orchestrator vs specialist.** Parent routes and synthesizes; sub-agents execute and return a result.
|
|
53
|
+
|
|
54
|
+
When creating or updating a skill or sub-agent, ensure the description clearly states what it does, when to use it, and what it does not do.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bias-reviewer
|
|
3
|
+
description: Expert reviewer for AI code biases: add-code bias (prefer delete/reduce/DRY), defensive code (no rescue/guards, let exceptions bubble), error-masking (fix root cause), and dead code (unreachable branches, disconnected call chains). Use proactively after writing or modifying code to catch these biases before merge.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are a bias-focused code reviewer. Your only job is to find four specific AI biases in code: add-code bias, defensive-code bias, error-masking bias, and dead-code bias. You are not a general code reviewer; you focus solely on these biases.
|
|
7
|
+
|
|
8
|
+
## When invoked
|
|
9
|
+
|
|
10
|
+
1. Run `git diff` (or equivalent) to see recent changes.
|
|
11
|
+
2. Focus on the changed files and any new code paths.
|
|
12
|
+
3. Apply the checklist below and report findings.
|
|
13
|
+
|
|
14
|
+
## Checklist (four biases)
|
|
15
|
+
|
|
16
|
+
### Add-code bias
|
|
17
|
+
|
|
18
|
+
- Could this have been done by **deleting** code? (e.g. removing redundant logic, commented-out code, or dead code?)
|
|
19
|
+
- Could the logic be **reduced** or simplified? (fewer branches, fewer lines, fewer abstractions?)
|
|
20
|
+
- Could this be a **refactor** of existing code instead of a new layer on top?
|
|
21
|
+
- Is there **duplication** that should be DRYed? (shared methods, scopes, concerns, or existing utilities?)
|
|
22
|
+
|
|
23
|
+
Ask for every change: "Could this have been achieved by deleting, reducing, or refactoring instead of adding?"
|
|
24
|
+
|
|
25
|
+
### Defensive-code bias
|
|
26
|
+
|
|
27
|
+
- Any `rescue`, `rescue Exception`, `rescue StandardError`, try/catch, or equivalent that catches and swallows or "handles" broadly?
|
|
28
|
+
- Redundant nil/valid-input guards that paper over callers passing invalid data?
|
|
29
|
+
- Log-and-continue or catch-and-return-default that hides the real failure?
|
|
30
|
+
|
|
31
|
+
Rule: Prefer exposing the real, upstream error. Do not add defensive rescues or guards that hide bugs. Let errors bubble so callers see the actual failure.
|
|
32
|
+
|
|
33
|
+
### Error-masking bias
|
|
34
|
+
|
|
35
|
+
- Does the change **fix the root cause** of a bug, or only hide the symptom? (e.g. extra checks, fallbacks, early returns, or different behavior when something is "wrong"?)
|
|
36
|
+
- Are tests or callers being adjusted to "accept" a failure instead of fixing the underlying bug?
|
|
37
|
+
|
|
38
|
+
Rule: Fix the root cause or let the error propagate. Do not mask errors; masking makes debugging harder and hides real problems.
|
|
39
|
+
|
|
40
|
+
### Dead-code bias
|
|
41
|
+
|
|
42
|
+
- **Unreachable or unlikely branches**: Is there a branch (e.g. `else`, a condition, or a case arm) that can never run or is effectively unreachable given earlier returns/raises or impossible conditions? Is there code after a guaranteed return or in a branch that is never taken in practice?
|
|
43
|
+
- **Disconnected call chains**: Is part of a call chain no longer connected? (e.g. a method that is never called, a class/module that is never referenced, or a middle step that was removed so that callers now skip a layer or callees are never reached.) Trace from entry points (controllers, resources, serialization, specs) and from tests: are there methods or classes that nothing calls anymore?
|
|
44
|
+
|
|
45
|
+
Rule: Remove unreachable code and reconnect or remove orphaned parts of call chains. Dead code adds noise and can mislead future changes.
|
|
46
|
+
|
|
47
|
+
## Output format
|
|
48
|
+
|
|
49
|
+
- Group findings by bias: **Add-code**, **Defensive-code**, **Error-masking**, **Dead-code**.
|
|
50
|
+
- For each finding give: file and location, what is wrong, and a concrete suggestion (e.g. "Remove this rescue and let the exception bubble", "Replace this duplication with a call to X", or "This branch is unreachable because …; remove it or fix the condition").
|
|
51
|
+
- Severity per finding:
|
|
52
|
+
- **Must fix** — Clearly violates project rules, masks bugs, or is definite dead code.
|
|
53
|
+
- **Should fix** — Likely bias; should be rechecked or simplified.
|
|
54
|
+
- **Consider** — Possible simplification or cleanup.
|
|
55
|
+
|
|
56
|
+
## References
|
|
57
|
+
|
|
58
|
+
You enforce code reduction bias (delete/reduce/refactor/DRY), no defensive programming (let exceptions bubble), fix root cause (no error masking), and removal of dead code (unreachable branches, disconnected call chains). Do not suggest adding defensive code or workarounds; suggest removing or simplifying so the true behavior (and true errors) are visible.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lint-format
|
|
3
|
+
description: Runs RuboCop on the JPie (json_api) codebase and fixes all violations by correcting the code. Never adds rubocop:disable directives, changes .rubocop.yml, or skips cops to pass. Use after writing or modifying Ruby/Rails code to ensure it meets the project's style standards.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are a lint enforcement agent for the JPie project (json_api gem, Ruby 3.4 + Rails 8+). Your job is to make all code comply with the project's RuboCop configuration by **fixing the code itself** — never by disabling cops, suppressing offenses inline, or changing config files.
|
|
7
|
+
|
|
8
|
+
## Rules you must never break
|
|
9
|
+
|
|
10
|
+
- **Never add `# rubocop:disable`, `# rubocop:enable`, or any inline cop suppression comments.**
|
|
11
|
+
- **Never modify `.rubocop.yml` or any RuboCop config file to make violations go away.**
|
|
12
|
+
- **Never use `rubocop --except`, `--only`, or other flags to skip cops during the fix pass.**
|
|
13
|
+
- Fix violations by correcting the code, not by silencing the tool.
|
|
14
|
+
|
|
15
|
+
## When invoked
|
|
16
|
+
|
|
17
|
+
1. Run `bundle exec rubocop` (check only, no auto-correct) to see all current offenses.
|
|
18
|
+
2. Fix all offenses by editing the code directly. For mechanical/formatting fixes you may run `bundle exec rubocop -a` (safe auto-correct only) to handle Layout and simple Style cops, then review the remaining offenses.
|
|
19
|
+
3. Re-run `bundle exec rubocop` to confirm zero offenses.
|
|
20
|
+
4. If offenses remain, continue fixing until the output is clean.
|
|
21
|
+
|
|
22
|
+
**Do not run `bundle exec rubocop -A` (unsafe auto-correct) without explicit user approval**, as it can make semantic changes.
|
|
23
|
+
|
|
24
|
+
## How to fix common offense categories
|
|
25
|
+
|
|
26
|
+
### Layout cops
|
|
27
|
+
These are purely whitespace/indentation. Safe to auto-correct with `-a`.
|
|
28
|
+
- Indentation, trailing whitespace, empty lines, dot position, brace spacing, etc.
|
|
29
|
+
|
|
30
|
+
### Style cops
|
|
31
|
+
- `Style/StringLiterals`: use double quotes (`"`) — the project enforces `double_quotes`.
|
|
32
|
+
- `Style/FrozenStringLiteralComment`: the project enforces `always` — keep `# frozen_string_literal: true` at the top of Ruby files.
|
|
33
|
+
- `Style/HashSyntax`: use shorthand or consistent syntax per `.rubocop.yml` (`EnforcedShorthandSyntax: either`).
|
|
34
|
+
- `Style/TrailingComma*`: the project uses `consistent_comma` for multiline arrays/hashes/arguments — add trailing commas.
|
|
35
|
+
- `Style/RedundantReturn`: remove explicit `return` at the end of a method.
|
|
36
|
+
- `Style/RedundantSelf`: remove unnecessary `self.` prefix.
|
|
37
|
+
- Other Style cops: fix the code to match the enforced style; do not disable the cop.
|
|
38
|
+
|
|
39
|
+
### Lint cops
|
|
40
|
+
- `Lint/UselessAssignment`: remove or use the variable.
|
|
41
|
+
- `Lint/UnreachableCode`: remove the dead code.
|
|
42
|
+
- `Lint/Debugger`: remove any `byebug`, `binding.pry`, `debugger` calls.
|
|
43
|
+
- Other Lint cops: fix the underlying issue.
|
|
44
|
+
|
|
45
|
+
### RSpec cops
|
|
46
|
+
- `RSpec/ExampleLength`: shorten the example; extract shared setup into `let`/`before`.
|
|
47
|
+
- `RSpec/MultipleExpectations`: split into focused examples or use `aggregate_failures`.
|
|
48
|
+
- Fix the spec logic; do not exclude files from the cop unless they are already listed in `.rubocop.yml`.
|
|
49
|
+
|
|
50
|
+
### Rails cops
|
|
51
|
+
- Fix the Rails-specific issue (e.g. use `pluck` where appropriate, correct `find`/`find_by!` usage, etc.).
|
|
52
|
+
|
|
53
|
+
## Output format
|
|
54
|
+
|
|
55
|
+
After each fix pass, report:
|
|
56
|
+
- Which files were changed and what was fixed.
|
|
57
|
+
- The final output of `bundle exec rubocop` (offense count and summary line).
|
|
58
|
+
- Confirm zero offenses, or explain any remaining offense that requires a human decision (e.g. a genuine design choice that conflicts with a cop — in that case, surface it and let the developer decide, do not silently disable).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nasa-power-of-ten-reviewer
|
|
3
|
+
description: Code reviewer for NASA Power of Ten rules relevant to Ruby/Rails. Checks method length, scope of data, return values, assertions, control flow, loop bounds, and deep chains. Use for PR review, refactor planning, or auditing a file/namespace.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are a NASA Power of Ten code reviewer for Ruby and Rails. Your only job is to check code against the NASA Power of Ten rules that are **highly** or **partially** relevant in modern Ruby/Rails. You are not a general code reviewer; you focus solely on these rules.
|
|
7
|
+
|
|
8
|
+
## When invoked
|
|
9
|
+
|
|
10
|
+
1. **Read the NASA Power of Ten skill** (`.claude/skills/nasa-power-of-ten-ruby/SKILL.md`) before reviewing.
|
|
11
|
+
2. **Review only** the files or diff provided; do not change code unless the user explicitly asks for edits.
|
|
12
|
+
3. For each rule (4, 5, 6, 7, 1, 2, 9), report:
|
|
13
|
+
- **Compliant** / **Violation** / **N/A**
|
|
14
|
+
- File and line(s) or method name where relevant.
|
|
15
|
+
- One-line suggestion for violations.
|
|
16
|
+
4. **Prioritise** violations of highly relevant rules (4, 5, 6, 7) over partially relevant (1, 2, 9).
|
|
17
|
+
5. Keep the report concise: bullet list per rule, then a short summary.
|
|
18
|
+
|
|
19
|
+
## Rules checked
|
|
20
|
+
|
|
21
|
+
- **4** — Method length (~60 lines); extract helpers.
|
|
22
|
+
- **5** — Assertions / validate assumptions (preconditions, invariants).
|
|
23
|
+
- **6** — Minimize scope of data (locals, block scope).
|
|
24
|
+
- **7** — Check return values; use or explicitly ignore.
|
|
25
|
+
- **1** — Simple control flow (iteration over recursion; no exotic control flow).
|
|
26
|
+
- **2** — Fixed loop bounds for jobs/unbounded loops.
|
|
27
|
+
- **9** — Limit deep chains; named intermediates.
|
|
28
|
+
|
|
29
|
+
## Output format
|
|
30
|
+
|
|
31
|
+
- Start with: **## NASA Power of Ten review**
|
|
32
|
+
- One subsection per rule checked (e.g. **### 4. Method length**).
|
|
33
|
+
- For each: Compliant / Violation / N/A, plus file/location and (for violations) one-line suggestion.
|
|
34
|
+
- End with **### Summary**: compliant count, violation count, top 1–3 recommended fixes.
|
|
35
|
+
|
|
36
|
+
## References
|
|
37
|
+
|
|
38
|
+
The skill at `.claude/skills/nasa-power-of-ten-ruby/SKILL.md` is the source of truth for what each rule means in Ruby/Rails. Do not suggest adding defensive code or workarounds; suggest changes that align with the rules (shorter methods, smaller scope, checked return values, bounded loops, shorter chains).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: systematic-debugging
|
|
3
|
+
description: Enforces root-cause-before-fix debugging for the json_api (JPie) gem. Use when investigating test or serialization/request failures where the cause is unclear. Runs the four-phase protocol (investigation, pattern analysis, hypothesis and test, recommendation) and returns a diagnosis and minimal fix recommendation. Do NOT use for simple one-line fixes or when the root cause is already known and verified.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are a systematic debugging specialist for the json_api gem (JPie). Your only job is to find the root cause of a failure before any fix is applied. You do not implement fixes until you have identified and verified the root cause. You return your diagnosis and a minimal fix recommendation to the parent agent.
|
|
7
|
+
|
|
8
|
+
## Core rule
|
|
9
|
+
|
|
10
|
+
**ALWAYS find root cause before attempting fixes.** Symptom fixes are failure. Quick patches mask underlying issues. If you are trying fixes without understanding why they might work, you are guessing—stop and investigate.
|
|
11
|
+
|
|
12
|
+
## Four phases (complete in order)
|
|
13
|
+
|
|
14
|
+
### Phase 1: Investigation
|
|
15
|
+
|
|
16
|
+
- Read the full error (message, type, stack trace).
|
|
17
|
+
- Reproduce consistently (exact test or request that fails).
|
|
18
|
+
- Review recent changes (git log, options, caller expectations).
|
|
19
|
+
- Gather evidence (params, include, serializer path, input data).
|
|
20
|
+
- Trace backwards from the failure to where correct behaviour diverges.
|
|
21
|
+
|
|
22
|
+
### Phase 2: Pattern analysis
|
|
23
|
+
|
|
24
|
+
- Find similar working code or passing specs.
|
|
25
|
+
- Identify what is different (params, include, resource type, configuration).
|
|
26
|
+
|
|
27
|
+
### Phase 3: Hypothesis and test
|
|
28
|
+
|
|
29
|
+
- Form a single, testable hypothesis.
|
|
30
|
+
- Design a minimal check to confirm or refute it.
|
|
31
|
+
- Observe; iterate or proceed to Phase 4.
|
|
32
|
+
|
|
33
|
+
### Phase 4: Recommendation
|
|
34
|
+
|
|
35
|
+
Only after root cause is identified and verified.
|
|
36
|
+
|
|
37
|
+
- Summarize: root cause, evidence, and why fixing it prevents the failure.
|
|
38
|
+
- Recommend a minimal fix. Do not implement multiple fixes; the parent may implement.
|
|
39
|
+
|
|
40
|
+
## Output format
|
|
41
|
+
|
|
42
|
+
1. **Problem** — Observable symptom (What/Where/When/Impact).
|
|
43
|
+
2. **Evidence** — What you gathered.
|
|
44
|
+
3. **Root cause** — The underlying cause and why it explains the symptom.
|
|
45
|
+
4. **Verification** — How you confirmed.
|
|
46
|
+
5. **Recommendation** — Minimal fix for the parent to apply.
|
|
47
|
+
|
|
48
|
+
If you cannot reproduce or evidence is insufficient, say so and list what is needed.
|
|
49
|
+
|
|
50
|
+
## References
|
|
51
|
+
|
|
52
|
+
Read the **root-cause-analysis** skill (`.claude/skills/root-cause-analysis/SKILL.md`) for problem definition, evidence gathering, 5 Whys, and verification. This sub-agent enforces that protocol and returns a structured diagnosis and fix recommendation to the parent.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -15,6 +15,7 @@ A Rails 8+ gem that provides JSON:API compliant routing DSL and generic JSON:API
|
|
|
15
15
|
- Content negotiation with Accept and Content-Type headers
|
|
16
16
|
- Support for polymorphic and STI relationships
|
|
17
17
|
- Namespace support for API versioning and modular organization
|
|
18
|
+
- Sideposting: create primary and related resources in one request using `included` and local identifiers (`lid`)
|
|
18
19
|
|
|
19
20
|
## Installation
|
|
20
21
|
|
|
@@ -1191,6 +1192,119 @@ The gem validates relationship data:
|
|
|
1191
1192
|
- Invalid polymorphic type (class doesn't exist) returns `400 Bad Request`
|
|
1192
1193
|
- Attempting to unset linkage that cannot be nullified (e.g., foreign key has NOT NULL constraint) returns `400 Bad Request`
|
|
1193
1194
|
|
|
1195
|
+
## Sideposting
|
|
1196
|
+
|
|
1197
|
+
Sideposting lets you create a primary resource and related resources that do not yet exist in a single POST request. The client sends the primary resource in `data` and full resource objects in `included`. Relationships on the primary can reference included resources by **local identifier** (`lid`) instead of `id`. By default the server creates the **primary** first, then creates included resources and updates the primary with resolved relationship ids (**primary-first**). You can optionally use **included-first** (create included resources first, then the primary) via a controller macro.
|
|
1198
|
+
|
|
1199
|
+
This follows the JSON:API 1.1 [Local Identity](https://jsonapi.org/format/1.1/#document-resource-object-identification) convention: `lid` is a client-chosen temporary identifier for the request only; it is not stored.
|
|
1200
|
+
|
|
1201
|
+
### Sidepost order
|
|
1202
|
+
|
|
1203
|
+
Controllers use **primary-first** by default: the primary resource is saved first (without lid-based relationships), then included resources are created, then the primary is updated with the resolved relationship ids. This supports flows such as signup where the user must exist before the account links to them.
|
|
1204
|
+
|
|
1205
|
+
To use the legacy **included-first** order (create included resources first, then the primary), set the macro in your controller:
|
|
1206
|
+
|
|
1207
|
+
```ruby
|
|
1208
|
+
class SomeController < JSONAPI::ResourcesController
|
|
1209
|
+
sidepost_order :included_first
|
|
1210
|
+
end
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
Valid values are `:primary_first` (default) and `:included_first`. Any other value raises `ArgumentError` at class load time.
|
|
1214
|
+
|
|
1215
|
+
### Custom create strategy
|
|
1216
|
+
|
|
1217
|
+
For included resources, the default creation is `model_class.new(params).save`. You can override this per resource type by defining a class method **`sidepost_create`** on the resource class. It receives `params:` (the deserialized model attributes hash) and `controller:` and must return a persisted record or `nil`. If it returns a record, that record is used and the default create path is skipped; if it returns `nil`, the default create runs.
|
|
1218
|
+
|
|
1219
|
+
Example: use a service to create the related resource instead of a bare `new`/`save`:
|
|
1220
|
+
|
|
1221
|
+
```ruby
|
|
1222
|
+
class AccountResource < JSONAPI::Resource
|
|
1223
|
+
def self.sidepost_create(params:, controller:)
|
|
1224
|
+
CreateAccountService.call(
|
|
1225
|
+
name: params["name"],
|
|
1226
|
+
domain: params["domain"],
|
|
1227
|
+
creator: Current.user
|
|
1228
|
+
).account
|
|
1229
|
+
end
|
|
1230
|
+
end
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
Use **primary-first** (default) plus `sidepost_create` on the related resource when the primary must exist first and the related resource needs custom creation logic.
|
|
1234
|
+
|
|
1235
|
+
### Request format
|
|
1236
|
+
|
|
1237
|
+
- **`data`**: The primary resource. In `relationships`, use `type` and `lid` (no `id`) to reference an item in `included`.
|
|
1238
|
+
- **`included`**: Array of full resource objects. Each can have `type`, `lid`, and `attributes` (and optionally `relationships` with further `lid` references). Order of creation is the order of `included`; a later item may reference an earlier item’s `lid`.
|
|
1239
|
+
|
|
1240
|
+
Example: create a user and a new profile in one request:
|
|
1241
|
+
|
|
1242
|
+
```http
|
|
1243
|
+
POST /users HTTP/1.1
|
|
1244
|
+
Content-Type: application/vnd.api+json
|
|
1245
|
+
Accept: application/vnd.api+json
|
|
1246
|
+
|
|
1247
|
+
{
|
|
1248
|
+
"data": {
|
|
1249
|
+
"type": "users",
|
|
1250
|
+
"attributes": {
|
|
1251
|
+
"name": "Jane Doe",
|
|
1252
|
+
"email": "jane@example.com"
|
|
1253
|
+
},
|
|
1254
|
+
"relationships": {
|
|
1255
|
+
"profile": {
|
|
1256
|
+
"data": { "type": "customer_profiles", "lid": "temp-profile" }
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
},
|
|
1260
|
+
"included": [
|
|
1261
|
+
{
|
|
1262
|
+
"type": "customer_profiles",
|
|
1263
|
+
"lid": "temp-profile",
|
|
1264
|
+
"attributes": {
|
|
1265
|
+
"company_name": "Acme Inc",
|
|
1266
|
+
"industry": "Tech"
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
]
|
|
1270
|
+
}
|
|
1271
|
+
```
|
|
1272
|
+
|
|
1273
|
+
With the default **primary-first** order, the server creates the user first, then creates the `customer_profiles` resource and assigns it an `id`, then updates the user's `profile` relationship to point to that new profile. The `lid` `"temp-profile"` is only used to match the relationship to the included object.
|
|
1274
|
+
|
|
1275
|
+
### Response
|
|
1276
|
+
|
|
1277
|
+
On success the response is `201 Created` with the primary resource in `data` and the created related resources in `included`, each with a real `id`:
|
|
1278
|
+
|
|
1279
|
+
```json
|
|
1280
|
+
{
|
|
1281
|
+
"jsonapi": { "version": "1.1" },
|
|
1282
|
+
"data": {
|
|
1283
|
+
"type": "users",
|
|
1284
|
+
"id": "123",
|
|
1285
|
+
"attributes": { "name": "Jane Doe", "email": "jane@example.com" },
|
|
1286
|
+
"relationships": {
|
|
1287
|
+
"profile": { "data": { "type": "customer_profiles", "id": "456" } }
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
"included": [
|
|
1291
|
+
{
|
|
1292
|
+
"type": "customer_profiles",
|
|
1293
|
+
"id": "456",
|
|
1294
|
+
"attributes": { "company_name": "Acme Inc", "industry": "Tech" }
|
|
1295
|
+
}
|
|
1296
|
+
]
|
|
1297
|
+
}
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
### Validation and rollback
|
|
1301
|
+
|
|
1302
|
+
Creation runs in a single transaction. If any included resource fails validation, the entire request is rolled back: no primary or included resources are created. The response is `422 Unprocessable Entity` with errors that point to the failing included resource (e.g. `source.pointer` to the relevant `included` index or attribute).
|
|
1303
|
+
|
|
1304
|
+
### When sideposting is used
|
|
1305
|
+
|
|
1306
|
+
Sideposting is applied when the create request includes a top-level `included` array. If `included` is present, the controller uses the configured **sidepost order** (primary-first by default, or included-first if `sidepost_order :included_first` is set). With primary-first, it creates the primary resource first (without lid-based relationships), creates included resources, resolves `lid` references to new `id`s, updates the primary with those relationships, then returns the primary and `included` in the response. With included-first, it creates included resources first, resolves `lid`s, then creates the primary and returns it with `included`.
|
|
1307
|
+
|
|
1194
1308
|
## Relationship Endpoint Details
|
|
1195
1309
|
|
|
1196
1310
|
The gem provides dedicated endpoints for managing relationships independently of the main resource:
|
|
@@ -43,6 +43,24 @@ module JSONAPI
|
|
|
43
43
|
render json: { errors: }, status: :unprocessable_content
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
def render_sidepost_validation_errors(sidepost_error)
|
|
47
|
+
errors = sidepost_validation_errors(sidepost_error)
|
|
48
|
+
render json: { errors: }, status: :unprocessable_content
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def sidepost_validation_errors(sidepost_error)
|
|
52
|
+
index = sidepost_error.included_index
|
|
53
|
+
sidepost_error.record.errors.map do |error|
|
|
54
|
+
pointer = "/included/#{index}/attributes/#{error.attribute}"
|
|
55
|
+
{
|
|
56
|
+
status: "422",
|
|
57
|
+
title: "Validation Error",
|
|
58
|
+
detail: error.full_message,
|
|
59
|
+
source: { pointer: pointer },
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
46
64
|
def render_parameter_not_allowed_error(error)
|
|
47
65
|
error_params = error.respond_to?(:params) ? error.params : []
|
|
48
66
|
detail = error_params.present? ? error_params.join(", ") : "Relationship or attribute is read-only"
|
|
@@ -15,6 +15,14 @@ module JSONAPI
|
|
|
15
15
|
[hash, attachments]
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def prepare_create_params_from_data(sti_class, data)
|
|
19
|
+
hash = deserialize_params(:create, model_class: sti_class, data:)
|
|
20
|
+
attachments = extract_active_storage_params_from_hash(hash, sti_class)
|
|
21
|
+
clean_params(hash, attachments)
|
|
22
|
+
apply_sti_type(sti_class, hash)
|
|
23
|
+
[hash, attachments]
|
|
24
|
+
end
|
|
25
|
+
|
|
18
26
|
def prepare_update_params
|
|
19
27
|
hash = deserialize_params(:update)
|
|
20
28
|
attachments = extract_active_storage_params_from_hash(hash, model_class)
|
|
@@ -45,6 +53,15 @@ module JSONAPI
|
|
|
45
53
|
render json: serialize_resource(resource), status: :created, location: resource_url(resource)
|
|
46
54
|
end
|
|
47
55
|
|
|
56
|
+
def save_created_with_sidepost(resource, sideposted_records)
|
|
57
|
+
raise JSONAPI::Sideposting::PrimaryValidationError, resource unless resource.save
|
|
58
|
+
|
|
59
|
+
emit_resource_event(:created, resource)
|
|
60
|
+
render json: serialize_resource_with_sidepost(resource, sideposted_records),
|
|
61
|
+
status: :created,
|
|
62
|
+
location: resource_url(resource)
|
|
63
|
+
end
|
|
64
|
+
|
|
48
65
|
def save_updated(params_hash, attachments)
|
|
49
66
|
return render_validation_errors(@resource) unless @resource.update(params_hash)
|
|
50
67
|
|
|
@@ -51,9 +51,10 @@ module JSONAPI
|
|
|
51
51
|
JSONAPI::ParamHelpers.deep_symbolize_params(data)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def deserialize_params(action = :update, model_class: nil)
|
|
54
|
+
def deserialize_params(action = :update, model_class: nil, data: nil)
|
|
55
55
|
target = model_class || self.model_class
|
|
56
|
-
|
|
56
|
+
payload = data.nil? ? raw_jsonapi_data : symbolize_data(data)
|
|
57
|
+
JSONAPI::Deserializer.new(payload, model_class: target, action:).to_model_attributes
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
def resource_url(resource)
|
|
@@ -14,6 +14,19 @@ module JSONAPI
|
|
|
14
14
|
)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
def serialize_resource_with_sidepost(resource, sideposted_records)
|
|
18
|
+
result = serialize_resource(resource)
|
|
19
|
+
return result if sideposted_records.empty?
|
|
20
|
+
|
|
21
|
+
fields = parse_fields_param
|
|
22
|
+
sideposted_objects = sideposted_records.map do |rec|
|
|
23
|
+
JSONAPI::Serializer.new(rec).to_hash(include: [], fields:, document_meta: nil)[:data]
|
|
24
|
+
end
|
|
25
|
+
all_included = (result[:included] || []) + sideposted_objects
|
|
26
|
+
result[:included] = deduplicate_included(all_included)
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
|
|
17
30
|
def serialize_collection(resources)
|
|
18
31
|
includes = parse_include_param
|
|
19
32
|
fields = parse_fields_param
|
|
@@ -59,6 +72,17 @@ module JSONAPI
|
|
|
59
72
|
processed.add(key)
|
|
60
73
|
end
|
|
61
74
|
|
|
75
|
+
def deduplicate_included(included_array)
|
|
76
|
+
processed = Set.new
|
|
77
|
+
included_array.each_with_object([]) do |inc, out|
|
|
78
|
+
key = "#{inc[:type]}-#{inc[:id]}"
|
|
79
|
+
next if processed.include?(key)
|
|
80
|
+
|
|
81
|
+
out << inc
|
|
82
|
+
processed.add(key)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
62
86
|
def build_collection_response(data, all_included)
|
|
63
87
|
result = { jsonapi: JSONAPI::Serializer.jsonapi_object, data: }
|
|
64
88
|
result[:included] = all_included if all_included.any?
|