ecoportal-api-graphql 1.3.13 → 1.3.15
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/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +29 -29
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +42 -42
- data/.ai-assistance/projects/TODO.md +170 -118
- data/.ai-assistance/skills/ai-instructions/SKILL.md +48 -48
- data/.ai-assistance/skills/code-specs/SKILL.md +69 -69
- data/.ai-assistance/skills/gemini-assist/SKILL.md +63 -63
- data/.ai-assistance/skills/project-cycle/SKILL.md +177 -177
- data/.ai-assistance/skills/refactor/SKILL.md +62 -62
- data/.ai-assistance/skills/spec-generation/SKILL.md +72 -72
- data/.gitignore +37 -37
- data/.gitlab-ci.yml +64 -12
- data/CHANGELOG.md +77 -0
- data/changelog.d/fixed-mr51-payload-item-mismatches.md +17 -0
- data/docs/worklog.md +0 -18
- data/lib/ecoportal/api/common/graphql/client.rb +0 -2
- data/lib/ecoportal/api/common/graphql/http_client.rb +6 -0
- data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -17
- data/lib/ecoportal/api/graphql/base/location_classification_type.rb +15 -15
- data/lib/ecoportal/api/graphql/base/location_structure.rb +27 -27
- data/lib/ecoportal/api/graphql/base/organization.rb +15 -15
- data/lib/ecoportal/api/graphql/base/page/task.rb +24 -0
- data/lib/ecoportal/api/graphql/base/page.rb +17 -16
- data/lib/ecoportal/api/graphql/base/template.rb +35 -0
- data/lib/ecoportal/api/graphql/base.rb +37 -35
- data/lib/ecoportal/api/graphql/builder/contractor_entity.rb +41 -41
- data/lib/ecoportal/api/graphql/compat/response.rb +35 -35
- data/lib/ecoportal/api/graphql/connection/action.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/action_category.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/contractor_entity.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/page.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/person_member.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -11
- data/lib/ecoportal/api/graphql/connection.rb +17 -17
- data/lib/ecoportal/api/graphql/file_upload/client.rb +21 -10
- data/lib/ecoportal/api/graphql/fragment/field_configuration.rb +38 -0
- data/lib/ecoportal/api/graphql/fragment/force.rb +14 -10
- data/lib/ecoportal/api/graphql/fragment/location_node.rb +26 -26
- data/lib/ecoportal/api/graphql/fragment/page_task.rb +39 -0
- data/lib/ecoportal/api/graphql/fragment/template.rb +51 -0
- data/lib/ecoportal/api/graphql/fragment.rb +3 -0
- data/lib/ecoportal/api/graphql/input/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -21
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -28
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -23
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -251
- data/lib/ecoportal/api/graphql/input.rb +24 -24
- data/lib/ecoportal/api/graphql/logic/connection.rb +34 -34
- data/lib/ecoportal/api/graphql/model/page/task.rb +16 -0
- data/lib/ecoportal/api/graphql/model/page.rb +16 -15
- data/lib/ecoportal/api/graphql/model/person_member.rb +15 -15
- data/lib/ecoportal/api/graphql/model/template.rb +15 -0
- data/lib/ecoportal/api/graphql/model.rb +32 -30
- data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -45
- data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +7 -7
- data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +8 -7
- data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +34 -35
- data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -35
- data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -36
- data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -13
- data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -46
- data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +1 -1
- data/lib/ecoportal/api/graphql/mutation.rb +20 -20
- data/lib/ecoportal/api/graphql/payload/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/payload/ok_payload.rb +36 -21
- data/lib/ecoportal/api/graphql/payload/page/draft.rb +26 -13
- data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -13
- data/lib/ecoportal/api/graphql/payload/page/review_task_batch.rb +23 -0
- data/lib/ecoportal/api/graphql/payload/page.rb +20 -19
- data/lib/ecoportal/api/graphql/payload/preset_view.rb +15 -11
- data/lib/ecoportal/api/graphql/payload/register.rb +15 -11
- data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/payload/template/update_information.rb +1 -1
- data/lib/ecoportal/api/graphql/query/location_structures.rb +57 -57
- data/lib/ecoportal/api/graphql/query/page_with_forces.rb +9 -3
- data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +9 -3
- data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -78
- data/lib/ecoportal/api/graphql/query.rb +27 -27
- data/lib/ecoportal/api/graphql.rb +9 -5
- data/lib/ecoportal/api/graphql_version.rb +1 -1
- data/tests/dump_schema.rb +88 -0
- data/tests/validate_queries.rb +34 -12
- metadata +11 -1
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
# SKILL: Refactor
|
|
2
|
-
|
|
3
|
-
**Purpose:** Identify, log, and (with developer confirmation) apply refactoring opportunities. Never apply changes without explicit confirmation.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## When to Use
|
|
8
|
-
|
|
9
|
-
- You notice a structural issue while working on something else — log it, don't fix it silently.
|
|
10
|
-
- The developer asks for a refactor audit of an area.
|
|
11
|
-
- A cycle-end review surfaces cleanup opportunities.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Criteria for a Refactor Candidate
|
|
16
|
-
|
|
17
|
-
Flag something when it fails one or more of:
|
|
18
|
-
|
|
19
|
-
- **Understandability:** Is it clear what this does and why?
|
|
20
|
-
- **Dependency tracking:** Are dependencies implicit or tangled?
|
|
21
|
-
- **Separation of concerns:** Is this class/method doing more than one thing?
|
|
22
|
-
- **Simplicity:** Could this be simpler without losing correctness?
|
|
23
|
-
- **Alignment:** Does this match the conventions of the rest of the codebase?
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Log Format
|
|
28
|
-
|
|
29
|
-
Refactoring opportunities are logged in `.ai-assistance/code/refactoring/<area>.md`.
|
|
30
|
-
|
|
31
|
-
Each entry:
|
|
32
|
-
|
|
33
|
-
```markdown
|
|
34
|
-
## [SHORT-ID] <Title>
|
|
35
|
-
|
|
36
|
-
- **File(s):** `lib/ecoportal/...`
|
|
37
|
-
- **Type:** [extract_class | extract_method | rename | simplify | reorganise | other]
|
|
38
|
-
- **Priority:** [high | medium | low]
|
|
39
|
-
- **Status:** [open | confirmed | applied | dismissed]
|
|
40
|
-
- **Identified:** YYYY-MM-DD
|
|
41
|
-
|
|
42
|
-
**Observation:**
|
|
43
|
-
What the issue is, concisely.
|
|
44
|
-
|
|
45
|
-
**Proposed change:**
|
|
46
|
-
What you'd do to fix it. High-level — not code unless it's very short.
|
|
47
|
-
|
|
48
|
-
**Risk:**
|
|
49
|
-
Any backwards-compat or downstream risk if applied.
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## Workflow
|
|
55
|
-
|
|
56
|
-
1. Identify opportunity → log it in the appropriate refactoring file.
|
|
57
|
-
2. Present a summary to the developer: "I've logged N refactoring opportunities in `.ai-assistance/code/refactoring/`. Want to review them?"
|
|
58
|
-
3. Developer confirms which ones to apply → mark as `confirmed`.
|
|
59
|
-
4. Apply one at a time, update specs if needed, mark as `applied`.
|
|
60
|
-
5. If dismissed by developer → mark as `dismissed` with a brief reason.
|
|
61
|
-
|
|
62
|
-
**Never apply a refactor without a `confirmed` status.**
|
|
1
|
+
# SKILL: Refactor
|
|
2
|
+
|
|
3
|
+
**Purpose:** Identify, log, and (with developer confirmation) apply refactoring opportunities. Never apply changes without explicit confirmation.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- You notice a structural issue while working on something else — log it, don't fix it silently.
|
|
10
|
+
- The developer asks for a refactor audit of an area.
|
|
11
|
+
- A cycle-end review surfaces cleanup opportunities.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Criteria for a Refactor Candidate
|
|
16
|
+
|
|
17
|
+
Flag something when it fails one or more of:
|
|
18
|
+
|
|
19
|
+
- **Understandability:** Is it clear what this does and why?
|
|
20
|
+
- **Dependency tracking:** Are dependencies implicit or tangled?
|
|
21
|
+
- **Separation of concerns:** Is this class/method doing more than one thing?
|
|
22
|
+
- **Simplicity:** Could this be simpler without losing correctness?
|
|
23
|
+
- **Alignment:** Does this match the conventions of the rest of the codebase?
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Log Format
|
|
28
|
+
|
|
29
|
+
Refactoring opportunities are logged in `.ai-assistance/code/refactoring/<area>.md`.
|
|
30
|
+
|
|
31
|
+
Each entry:
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
## [SHORT-ID] <Title>
|
|
35
|
+
|
|
36
|
+
- **File(s):** `lib/ecoportal/...`
|
|
37
|
+
- **Type:** [extract_class | extract_method | rename | simplify | reorganise | other]
|
|
38
|
+
- **Priority:** [high | medium | low]
|
|
39
|
+
- **Status:** [open | confirmed | applied | dismissed]
|
|
40
|
+
- **Identified:** YYYY-MM-DD
|
|
41
|
+
|
|
42
|
+
**Observation:**
|
|
43
|
+
What the issue is, concisely.
|
|
44
|
+
|
|
45
|
+
**Proposed change:**
|
|
46
|
+
What you'd do to fix it. High-level — not code unless it's very short.
|
|
47
|
+
|
|
48
|
+
**Risk:**
|
|
49
|
+
Any backwards-compat or downstream risk if applied.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Workflow
|
|
55
|
+
|
|
56
|
+
1. Identify opportunity → log it in the appropriate refactoring file.
|
|
57
|
+
2. Present a summary to the developer: "I've logged N refactoring opportunities in `.ai-assistance/code/refactoring/`. Want to review them?"
|
|
58
|
+
3. Developer confirms which ones to apply → mark as `confirmed`.
|
|
59
|
+
4. Apply one at a time, update specs if needed, mark as `applied`.
|
|
60
|
+
5. If dismissed by developer → mark as `dismissed` with a brief reason.
|
|
61
|
+
|
|
62
|
+
**Never apply a refactor without a `confirmed` status.**
|
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
# SKILL: Spec Generation
|
|
2
|
-
|
|
3
|
-
**Purpose:** Add RSpec specs that guard backwards compatibility and document expected behaviour, placed in the correct spec folder mirroring the lib/ structure.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## When to Use
|
|
8
|
-
|
|
9
|
-
- A change could break downstream consumers (primarily `eco-helpers`).
|
|
10
|
-
- A public method or interface is being added, changed, or removed.
|
|
11
|
-
- A bug is fixed — add a regression spec.
|
|
12
|
-
- A new class is added that other code will depend on.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## Placement Rules
|
|
17
|
-
|
|
18
|
-
Spec files **mirror `lib/` structure** under `spec/`:
|
|
19
|
-
|
|
20
|
-
| Code file | Spec file |
|
|
21
|
-
|-----------|----------|
|
|
22
|
-
| `lib/ecoportal/api/graphql/model/action.rb` | `spec/ecoportal/api/graphql/model/action_spec.rb` |
|
|
23
|
-
| `lib/ecoportal/api/graphql/input/location_structure/draft.rb` | `spec/ecoportal/api/graphql/input/location_structure/draft_spec.rb` |
|
|
24
|
-
|
|
25
|
-
Create the folder path if it doesn't exist.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Spec Requirements
|
|
30
|
-
|
|
31
|
-
Each spec file for a changed area must have:
|
|
32
|
-
|
|
33
|
-
1. A description of the class/module under test.
|
|
34
|
-
2. At least one spec covering the public interface being changed.
|
|
35
|
-
3. For backwards-compat specs: a comment `# backwards-compat` so they're easily searched.
|
|
36
|
-
|
|
37
|
-
**Minimum spec template:**
|
|
38
|
-
|
|
39
|
-
```ruby
|
|
40
|
-
# frozen_string_literal: true
|
|
41
|
-
|
|
42
|
-
RSpec.describe Ecoportal::API::Graphql::<ClassName> do
|
|
43
|
-
# backwards-compat
|
|
44
|
-
describe '#<method>' do
|
|
45
|
-
subject { described_class.new(<args>) }
|
|
46
|
-
|
|
47
|
-
it '<expected behaviour>' do
|
|
48
|
-
expect(subject.<method>).to <matcher>
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
## Linked to Code Specs for AI
|
|
57
|
-
|
|
58
|
-
Whenever a spec file is created or updated for an area, check the corresponding `.ai-assistance/code/<area>.md` doc:
|
|
59
|
-
- Does it mention this class/method?
|
|
60
|
-
- Does it reflect the current expected behaviour?
|
|
61
|
-
|
|
62
|
-
If not — update it. This redundancy is intentional: it saves AI agents from having to parse spec files to understand expected behaviour.
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Gemini Assist Integration
|
|
67
|
-
|
|
68
|
-
When generating specs for a large area, you may use `gemini-assist` to:
|
|
69
|
-
- Get a first-pass draft of spec cases given the source file.
|
|
70
|
-
- Check for edge cases you may have missed.
|
|
71
|
-
|
|
72
|
-
Always verify the generated specs run and match actual behaviour before committing.
|
|
1
|
+
# SKILL: Spec Generation
|
|
2
|
+
|
|
3
|
+
**Purpose:** Add RSpec specs that guard backwards compatibility and document expected behaviour, placed in the correct spec folder mirroring the lib/ structure.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- A change could break downstream consumers (primarily `eco-helpers`).
|
|
10
|
+
- A public method or interface is being added, changed, or removed.
|
|
11
|
+
- A bug is fixed — add a regression spec.
|
|
12
|
+
- A new class is added that other code will depend on.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Placement Rules
|
|
17
|
+
|
|
18
|
+
Spec files **mirror `lib/` structure** under `spec/`:
|
|
19
|
+
|
|
20
|
+
| Code file | Spec file |
|
|
21
|
+
|-----------|----------|
|
|
22
|
+
| `lib/ecoportal/api/graphql/model/action.rb` | `spec/ecoportal/api/graphql/model/action_spec.rb` |
|
|
23
|
+
| `lib/ecoportal/api/graphql/input/location_structure/draft.rb` | `spec/ecoportal/api/graphql/input/location_structure/draft_spec.rb` |
|
|
24
|
+
|
|
25
|
+
Create the folder path if it doesn't exist.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Spec Requirements
|
|
30
|
+
|
|
31
|
+
Each spec file for a changed area must have:
|
|
32
|
+
|
|
33
|
+
1. A description of the class/module under test.
|
|
34
|
+
2. At least one spec covering the public interface being changed.
|
|
35
|
+
3. For backwards-compat specs: a comment `# backwards-compat` so they're easily searched.
|
|
36
|
+
|
|
37
|
+
**Minimum spec template:**
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
# frozen_string_literal: true
|
|
41
|
+
|
|
42
|
+
RSpec.describe Ecoportal::API::Graphql::<ClassName> do
|
|
43
|
+
# backwards-compat
|
|
44
|
+
describe '#<method>' do
|
|
45
|
+
subject { described_class.new(<args>) }
|
|
46
|
+
|
|
47
|
+
it '<expected behaviour>' do
|
|
48
|
+
expect(subject.<method>).to <matcher>
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Linked to Code Specs for AI
|
|
57
|
+
|
|
58
|
+
Whenever a spec file is created or updated for an area, check the corresponding `.ai-assistance/code/<area>.md` doc:
|
|
59
|
+
- Does it mention this class/method?
|
|
60
|
+
- Does it reflect the current expected behaviour?
|
|
61
|
+
|
|
62
|
+
If not — update it. This redundancy is intentional: it saves AI agents from having to parse spec files to understand expected behaviour.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Gemini Assist Integration
|
|
67
|
+
|
|
68
|
+
When generating specs for a large area, you may use `gemini-assist` to:
|
|
69
|
+
- Get a first-pass draft of spec cases given the source file.
|
|
70
|
+
- Check for edge cases you may have missed.
|
|
71
|
+
|
|
72
|
+
Always verify the generated specs run and match actual behaviour before committing.
|
data/.gitignore
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
# it's a gem, ignore the lockfile
|
|
2
|
-
Gemfile.lock
|
|
3
|
-
|
|
4
|
-
.env
|
|
5
|
-
.ai-assistance/local_paths.md
|
|
6
|
-
# build artifacts
|
|
7
|
-
*.gem
|
|
8
|
-
/.bundle/
|
|
9
|
-
/vendor/bundle
|
|
10
|
-
/spec/reports/
|
|
11
|
-
/tmp/
|
|
12
|
-
/pkg/
|
|
13
|
-
|
|
14
|
-
# docs
|
|
15
|
-
/.yardoc
|
|
16
|
-
/_yardoc/
|
|
17
|
-
/coverage/
|
|
18
|
-
/doc/
|
|
19
|
-
|
|
20
|
-
# rspec failure tracking
|
|
21
|
-
.rspec_status
|
|
22
|
-
scratch.rb
|
|
23
|
-
tests/.byebug_history
|
|
24
|
-
|
|
25
|
-
.solargraph.yml
|
|
26
|
-
|
|
27
|
-
# AI assistance — local-only files
|
|
28
|
-
.ai-assistance/code/dep-graph.json
|
|
29
|
-
.ai-assistance/tmp/
|
|
30
|
-
.ai-assistance/local/
|
|
31
|
-
.ai-assistance/bridge/inbox/*.draft.md
|
|
32
|
-
.ai-assistance/bridge/inbox/*.local.md
|
|
33
|
-
.ai-assistance/bridge/LOCK
|
|
34
|
-
.ai-assistance/bridge/STATUS
|
|
35
|
-
.ai-assistance/capabilities/.env
|
|
36
|
-
.ai-assistance/scripts/*.env
|
|
37
|
-
.ai-assistance/scripts/node_modules/
|
|
1
|
+
# it's a gem, ignore the lockfile
|
|
2
|
+
Gemfile.lock
|
|
3
|
+
|
|
4
|
+
.env
|
|
5
|
+
.ai-assistance/local_paths.md
|
|
6
|
+
# build artifacts
|
|
7
|
+
*.gem
|
|
8
|
+
/.bundle/
|
|
9
|
+
/vendor/bundle
|
|
10
|
+
/spec/reports/
|
|
11
|
+
/tmp/
|
|
12
|
+
/pkg/
|
|
13
|
+
|
|
14
|
+
# docs
|
|
15
|
+
/.yardoc
|
|
16
|
+
/_yardoc/
|
|
17
|
+
/coverage/
|
|
18
|
+
/doc/
|
|
19
|
+
|
|
20
|
+
# rspec failure tracking
|
|
21
|
+
.rspec_status
|
|
22
|
+
scratch.rb
|
|
23
|
+
tests/.byebug_history
|
|
24
|
+
|
|
25
|
+
.solargraph.yml
|
|
26
|
+
|
|
27
|
+
# AI assistance — local-only files
|
|
28
|
+
.ai-assistance/code/dep-graph.json
|
|
29
|
+
.ai-assistance/tmp/
|
|
30
|
+
.ai-assistance/local/
|
|
31
|
+
.ai-assistance/bridge/inbox/*.draft.md
|
|
32
|
+
.ai-assistance/bridge/inbox/*.local.md
|
|
33
|
+
.ai-assistance/bridge/LOCK
|
|
34
|
+
.ai-assistance/bridge/STATUS
|
|
35
|
+
.ai-assistance/capabilities/.env
|
|
36
|
+
.ai-assistance/scripts/*.env
|
|
37
|
+
.ai-assistance/scripts/node_modules/
|
data/.gitlab-ci.yml
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
# CI pipeline for ecoportal-api-graphql
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
# (allow_failure: true) — it reports pass/fail status without gating merges.
|
|
5
|
-
# Rationale (2026-07-10): add visibility now; tighten to a blocking gate later
|
|
6
|
-
# once the suite is reliably green on a clean runner.
|
|
3
|
+
# Two jobs, deliberately different in strictness:
|
|
7
4
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
5
|
+
# rspec — NON-BLOCKING (allow_failure: true): reports pass/fail without gating merges.
|
|
6
|
+
# Rationale (2026-07-10): visibility now; tighten to a gate later once the
|
|
7
|
+
# suite is reliably green on a clean runner.
|
|
8
|
+
# validate-queries — BLOCKING: renders EVERY query + mutation in the gem against a FRESHLY
|
|
9
|
+
# introspected schema and fails only on deterministic, non-negotiable
|
|
10
|
+
# breakage (see below). This is the gate the 2026-07-03 v1.3.10 prod hotfix
|
|
11
|
+
# (LocationStructure `updatedAt`) called for — those queries had no spec and
|
|
12
|
+
# first broke in a client runner.
|
|
13
|
+
#
|
|
14
|
+
# Live/AI specs (tagged :live) are excluded from rspec: they need ANTHROPIC_API_KEY + network.
|
|
15
|
+
# They already self-skip when the key is absent, but ~live keeps the runner off that path entirely.
|
|
11
16
|
#
|
|
12
17
|
# NOTE: graphlient resolves from a git fork (see Gemfile); ecoportal-api / -v2 come
|
|
13
18
|
# from rubygems. If a required floor (e.g. ecoportal-api-v2 >= 3.3.3) is not yet
|
|
14
|
-
# published, `bundle install` may fail on the runner
|
|
15
|
-
#
|
|
19
|
+
# published, `bundle install` may fail on the runner. For `rspec` that surfaces as a
|
|
20
|
+
# warning; for `validate-queries` it fails the pipeline — the fix is to publish the
|
|
21
|
+
# floor gem, not to relax the gate.
|
|
16
22
|
|
|
17
23
|
workflow:
|
|
18
24
|
rules:
|
|
@@ -27,10 +33,8 @@ workflow:
|
|
|
27
33
|
stages:
|
|
28
34
|
- test
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
stage: test
|
|
36
|
+
.ruby:
|
|
32
37
|
image: ruby:3.2.2
|
|
33
|
-
allow_failure: true # non-blocking: surface failures, do not gate merges
|
|
34
38
|
variables:
|
|
35
39
|
BUNDLE_PATH: vendor/bundle # cache installed gems between pipelines
|
|
36
40
|
cache:
|
|
@@ -41,5 +45,53 @@ rspec:
|
|
|
41
45
|
- ruby -v
|
|
42
46
|
- gem install bundler
|
|
43
47
|
- bundle install --jobs "$(nproc)" --retry 3
|
|
48
|
+
|
|
49
|
+
rspec:
|
|
50
|
+
extends: .ruby
|
|
51
|
+
stage: test
|
|
52
|
+
allow_failure: true # non-blocking: surface failures, do not gate merges
|
|
44
53
|
script:
|
|
45
54
|
- bundle exec rspec --tag ~live --format progress
|
|
55
|
+
|
|
56
|
+
# Offline validation of every gem query + mutation against the LIVE schema.
|
|
57
|
+
#
|
|
58
|
+
# Fails the pipeline on exactly two deterministic classes (see tests/validate_queries.rb):
|
|
59
|
+
# 1. FRAGMENT / RENDER FAILURES — the operation cannot even be rendered (dead fragment
|
|
60
|
+
# reference / unresolved constant). This is what broke the live LocationDraft, Action and
|
|
61
|
+
# ContractorEntity paths.
|
|
62
|
+
# 2. STRUCTURAL BUGS — "field must have selections" (object field selected bare) and
|
|
63
|
+
# "selections can't be made on scalars" (scalar over-selected).
|
|
64
|
+
# Everything else (unknown fields etc.) is reported but does NOT fail: it is usually schema
|
|
65
|
+
# staleness or union handling, and needs a human read.
|
|
66
|
+
#
|
|
67
|
+
# The schema is introspected AT PIPELINE TIME on purpose — a committed 6.3MB dump goes stale,
|
|
68
|
+
# and a stale schema both invents noise and hides real findings.
|
|
69
|
+
#
|
|
70
|
+
# Required CI/CD variables (Settings → CI/CD → Variables; mask the password):
|
|
71
|
+
# EP_CI_SERVER host of a NON-PRODUCTION environment (e.g. an internal/UAT host)
|
|
72
|
+
# EP_CI_ORG_ID organisation id on that host
|
|
73
|
+
# EP_CI_USER_EMAIL service account with READ access only — introspection needs no write scope
|
|
74
|
+
# EP_CI_USER_PASS its password (masked)
|
|
75
|
+
# Until all four are set the job does not run, so the gate is inert rather than red. Use a
|
|
76
|
+
# service account on a non-production org: the job only ever issues an introspection query,
|
|
77
|
+
# never touches records.
|
|
78
|
+
validate-queries:
|
|
79
|
+
extends: .ruby
|
|
80
|
+
stage: test
|
|
81
|
+
rules:
|
|
82
|
+
- if: '$EP_CI_SERVER && $EP_CI_ORG_ID && $EP_CI_USER_EMAIL && $EP_CI_USER_PASS'
|
|
83
|
+
variables:
|
|
84
|
+
BUNDLE_PATH: vendor/bundle
|
|
85
|
+
# The gem reads its connection from these names (Common::GraphQL::AuthService).
|
|
86
|
+
SERVER: $EP_CI_SERVER
|
|
87
|
+
ORGANIZATION_ID: $EP_CI_ORG_ID
|
|
88
|
+
USER_EMAIL: $EP_CI_USER_EMAIL
|
|
89
|
+
USER_PASS: $EP_CI_USER_PASS
|
|
90
|
+
script:
|
|
91
|
+
- bundle exec ruby tests/dump_schema.rb ci_schema.json
|
|
92
|
+
- bundle exec ruby tests/validate_queries.rb ci_schema.json
|
|
93
|
+
artifacts:
|
|
94
|
+
when: on_failure # keep the exact schema the failure was diagnosed against
|
|
95
|
+
expire_in: 1 week
|
|
96
|
+
paths:
|
|
97
|
+
- ci_schema.json
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,83 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.3.15] - 2026-07-30
|
|
6
|
+
|
|
7
|
+
**Backwards-compatible.** Every change here fixes a query or mutation document that the live
|
|
8
|
+
schema rejected — i.e. code paths that could not run at all — plus the CI gate that found them.
|
|
9
|
+
Cherry-picked onto the 1.3.x line from `main`, where they landed alongside **breaking** changes
|
|
10
|
+
that are deliberately NOT in this release (see "Deferred to 1.4.0" below).
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`FileUpload::Client` authenticated the v2 REST endpoints wrongly.** The ported S3
|
|
15
|
+
register/poll steps sent `Authorization: Bearer <oauth_token>`; those endpoints authenticate via
|
|
16
|
+
`X-ECOPORTAL-API-KEY`, so step 3 404'd and `#upload` never completed. `Ecoportal::API::GraphQL.new`
|
|
17
|
+
accepts an additive `api_key:` (independent of the GraphQL endpoint's own email/pass OAuth),
|
|
18
|
+
`HttpClient#api_key` is public, and the client raises a clear error when it is missing.
|
|
19
|
+
⚠ **Not yet live-verified** — verify against a sandbox org before relying on it.
|
|
20
|
+
- **Three invalid union selections.** `Query::RegisterPresetViews` (`fieldConfigurations`),
|
|
21
|
+
`Query::PageWithForces` (`forces`) and `Query::PagesWorkflowCommands` (`workflow`) selected
|
|
22
|
+
fields directly on a union, which is invalid GraphQL ("Selections can't be made directly on
|
|
23
|
+
unions") — all three queries failed outright. They now select through the members' shared
|
|
24
|
+
interface (`FieldConfigurationInterface` / `BasePageInterface`). Response shapes are unchanged.
|
|
25
|
+
- **`fragment ForceFields` asked for a non-existent `id` on bindings.** `Force.bindings` is
|
|
26
|
+
`[BindingInterface]`, whose only fields are `name` / `referenceId` (+ per-member `reference`),
|
|
27
|
+
so every query spreading the fragment was invalid. Now selects on the interface directly.
|
|
28
|
+
NOTE the read/write asymmetry this exposes: `WorkflowEditBindingInput` / `removeBinding` require
|
|
29
|
+
a binding `id` that no read exposes, so `BindingCollection#delete!(binding)` has no source for it.
|
|
30
|
+
- **7 invalid `PageFields` spreads.** The four review-task mutations return `item: TaskInterface`
|
|
31
|
+
(not a page — the page is reachable via `subject`), `Template::{Create,Destroy}RelatedPage`
|
|
32
|
+
return `RelatedPages`, and `Template::UpdateInformation` returns the narrow `Template` type. New
|
|
33
|
+
`:PageTaskFields` / `:TemplateRelatedPages` / `:TemplateInformation` fragments and read-only
|
|
34
|
+
`Base/Model::Page::Task` + `Base/Model::Template::{RelatedPages,Information}` models; the payload
|
|
35
|
+
`item_class`es pointed at `Model::PageUnion`, which never matched the schema. The template
|
|
36
|
+
mutations that genuinely return a page (`Create`/`Publish`/`Unpublish`/`Update`) are unchanged.
|
|
37
|
+
- **6 payloads selected an `item` their type does not have.** `Page::CreateDraft` → `{ id }`,
|
|
38
|
+
`Page::{DeleteDraft,PublishDraft}` → `{ ok }`, `Page::BatchUpdateReviewTask` → `{ count }` (and
|
|
39
|
+
its own `Payload::Page::ReviewTaskBatch`), `Register::Destroy` → `{ ok }`, `PresetView::Destroy`
|
|
40
|
+
→ `{ clientMutationId errors }`. `Payload::Page::Draft` gained `id`/`ok`, `Payload::Register`
|
|
41
|
+
gained `ok`.
|
|
42
|
+
- **`executeWorkflowCommands` was rendered with arguments it never accepted** — the schema takes a
|
|
43
|
+
single `input: ExecuteWorkflowCommandsInput!`, so the **page-level command bus could not run**.
|
|
44
|
+
Both classes' `#query` already built the right `input` hash; only the documents were wrong.
|
|
45
|
+
- **`Payload::OkPayload` now models `errors`** — it selected them but computed `success?` purely
|
|
46
|
+
from `ok`, silently ignoring a populated errors object.
|
|
47
|
+
|
|
48
|
+
### Added (tooling — no runtime effect)
|
|
49
|
+
|
|
50
|
+
- **`validate-queries` CI job** (`.gitlab-ci.yml`): renders every query + mutation against a
|
|
51
|
+
freshly introspected schema and fails on render failures plus the always-invalid selection
|
|
52
|
+
classes ("must have selections", "can't be made on scalars", "can't be made directly on unions",
|
|
53
|
+
"can't be spread inside"). This gate is what found everything above.
|
|
54
|
+
- **`tests/dump_schema.rb`**: fresh introspection through the gem's own `HttpClient`. Requires
|
|
55
|
+
`SERVER` explicitly (the gem's default host is production). Schema dumps are never committed.
|
|
56
|
+
|
|
57
|
+
### Deferred to 1.4.0 (breaking — intentionally absent here)
|
|
58
|
+
|
|
59
|
+
- The AI-summary rewire (`generateAiSummary` → `generatePageSummary`,
|
|
60
|
+
`submitAiSummaryFeedback` → `submitPageAiSummaryFeedback`, changed `#query` keywords,
|
|
61
|
+
`Base::AiSummaryVersion` field removals) and the removal of the `SmartFill` mutations
|
|
62
|
+
(`smartFillGenerate` / `smartFillSubmitFeedback` do not exist in the schema).
|
|
63
|
+
- The 2026-07-13 `WorkflowCommandInput` schema sync and the polymorphic read-model layer.
|
|
64
|
+
|
|
65
|
+
## [1.3.14] - 2026-07-16
|
|
66
|
+
|
|
67
|
+
Re-issue of the **1.3.13 HttpClient resilience fix** under a fresh version. The `1.3.13`
|
|
68
|
+
artifact published to the gem server (2026-07-11) could not be relied upon to contain the
|
|
69
|
+
fix below — RubyGems versions are immutable and cannot be re-pushed, so the fix ships here
|
|
70
|
+
as `1.3.14`. Functionally identical to the *intended* `1.3.13` (this is `1.3.12` + the
|
|
71
|
+
resilience fix, nothing else). **Backwards-compatible.**
|
|
72
|
+
|
|
73
|
+
Consumers that need the 429/1015 resilience (e.g. bulk `cans-upsert` live runs) must floor
|
|
74
|
+
`ecoportal-api-graphql >= 1.3.14` — a `>= 1.3.13` floor is not sufficient.
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- Same fix as documented under `[1.3.13]` below: `Common::GraphQL::HttpClient#execute` routes
|
|
79
|
+
through the inherited `instrument → with_retry { rate_throttling }` pipeline, so a single
|
|
80
|
+
HTTP 429 (Cloudflare edge rate-limit `1015`) no longer aborts the run.
|
|
81
|
+
|
|
5
82
|
## [1.3.13] - 2026-07-12
|
|
6
83
|
|
|
7
84
|
Reliability: the GraphQL `HttpClient` now inherits the **same request-resilience
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
- **Six mutations selected an `item` their payload type does not have** — invalid documents, so
|
|
2
|
+
none could run. Found by the first live run of the `validate-queries` CI gate (2026-07-30);
|
|
3
|
+
every replacement selection below is the schema's own shape, verified against a fresh prod
|
|
4
|
+
introspection:
|
|
5
|
+
- `Page::CreateDraft` → `CreatePageDraftPayload { id }` (the new draft's page id)
|
|
6
|
+
- `Page::DeleteDraft` / `Page::PublishDraft` → `{ ok }`
|
|
7
|
+
- `Page::BatchUpdateReviewTask` → `{ count }` (tasks updated). It also shared
|
|
8
|
+
`Payload::Page::ReviewTask`, whose type has an `item`; it now has its own
|
|
9
|
+
`Payload::Page::ReviewTaskBatch`.
|
|
10
|
+
- `Register::Destroy` → `DestroyRegisterPayload { ok }`
|
|
11
|
+
- `PresetView::Destroy` → `DestroyPresetViewPayload { clientMutationId errors }` — no payload
|
|
12
|
+
data at all, not even `ok`.
|
|
13
|
+
`Payload::Page::Draft` gained `id`/`ok` and `Payload::Register` gained `ok`; the create/update
|
|
14
|
+
variants of Register/PresetView still return `item` and are untouched. ★ Note `Logic::Payload`'s
|
|
15
|
+
inherited `embeds_one :item` materialises a HOLLOW item (all fields nil, and it writes
|
|
16
|
+
`'item' => {}` into the doc) when the response has no `item` — it does not return nil, so read
|
|
17
|
+
the scalars. 21 specs.
|
data/docs/worklog.md
CHANGED
|
@@ -8,24 +8,6 @@ This file tracks cross-project session state and the overall repo status.
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## ▶ SESSION 2026-07-10 — OD-001 decision-support delivered to ep-ai-standards (breadcrumb)
|
|
12
|
-
|
|
13
|
-
Produced a decision-support doc mapping this repo's REAL GraphQL mutation surface onto
|
|
14
|
-
ep-ai-standards' operation-class ladder for OD-001 (the GraphQL gateway PROD "low-risk"
|
|
15
|
-
auto-approval threshold / provisional Dial A). Evidence-based from live introspection
|
|
16
|
-
(`.ai-assistance/tmp/20260605T101224_live_ep_graphql_schema.graphql.json` -- 190 mutations)
|
|
17
|
-
+ code-specs + `lib/ecoportal/api/graphql/mutation/**`. Key findings: register-type axis is
|
|
18
|
-
real + in-schema (`Register.registerStatus: TESTING|LIVE`, `base/register.rb:7`); org-type
|
|
19
|
-
(sandpit/uat/demo/PROD) is NOT a schema field (infra/credential-level, `base/organization.rb`
|
|
20
|
-
exposes only id/name/companyName/logoUrl); several mutations do not map cleanly (payload-hidden
|
|
21
|
-
deletions in `updatePage`, bulk/cascade, cross-env UAT->PROD deploy, egress/email/credential
|
|
22
|
-
mints). Delivered to ep-ai-standards' bridge inbox as
|
|
23
|
-
`ecoportal-api-graphql-od001-mutation-mapping.md` (STATUS: PENDING) -- a PROPOSAL for the graphql
|
|
24
|
-
team + Oscar to validate via an ep-ai-standards MR. NO code changed in this repo. Does not change
|
|
25
|
-
OD-001's ruling. (OD-007 breadcrumb convention.)
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
11
|
## ▶ SESSION 2026-07-05 — 3 live act-gov crashes = ONE root cause (stale released gems); fixed-stack floors
|
|
30
12
|
|
|
31
13
|
Three live crashes on act-gov (`-toocs-coding` + org-structure sync), all the **same class**: the
|
|
@@ -21,6 +21,12 @@ module Ecoportal
|
|
|
21
21
|
|
|
22
22
|
attr_reader :host, :version
|
|
23
23
|
|
|
24
|
+
# Publicly exposes the configured api_key (inherited as `private` from
|
|
25
|
+
# `Common::Client`) — `FileUpload::Client` needs it directly to authenticate
|
|
26
|
+
# against the v2 REST endpoints, independent of this client's own `@version`
|
|
27
|
+
# (which stays `'graphql'` and therefore always builds a Bearer `key_token`).
|
|
28
|
+
public :api_key
|
|
29
|
+
|
|
24
30
|
include Ecoportal::API::Common::GraphQL::AuthService
|
|
25
31
|
|
|
26
32
|
def initialize(
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
module Ecoportal
|
|
2
|
-
module API
|
|
3
|
-
class GraphQL
|
|
4
|
-
module Base
|
|
5
|
-
# Represents one generation version of an AiSummary field.
|
|
6
|
-
# Returned by generateAiSummary — tracks the prompt, output, token usage, and status.
|
|
7
|
-
class AiSummaryVersion < Logic::BaseModel
|
|
8
|
-
passkey :id
|
|
9
|
-
passthrough :aiSummary, :status, :prompt
|
|
10
|
-
passthrough :inputTokens, :outputTokens, :totalTokens
|
|
11
|
-
passthrough :resourceType # "field" | "stage" | "page"
|
|
12
|
-
passarray :ratings # [[user_id, score], ...]
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
1
|
+
module Ecoportal
|
|
2
|
+
module API
|
|
3
|
+
class GraphQL
|
|
4
|
+
module Base
|
|
5
|
+
# Represents one generation version of an AiSummary field.
|
|
6
|
+
# Returned by generateAiSummary — tracks the prompt, output, token usage, and status.
|
|
7
|
+
class AiSummaryVersion < Logic::BaseModel
|
|
8
|
+
passkey :id
|
|
9
|
+
passthrough :aiSummary, :status, :prompt
|
|
10
|
+
passthrough :inputTokens, :outputTokens, :totalTokens
|
|
11
|
+
passthrough :resourceType # "field" | "stage" | "page"
|
|
12
|
+
passarray :ratings # [[user_id, score], ...]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
module Ecoportal
|
|
2
|
-
module API
|
|
3
|
-
class GraphQL
|
|
4
|
-
module Base
|
|
5
|
-
class LocationClassificationType < Logic::BaseModel
|
|
6
|
-
root!
|
|
7
|
-
|
|
8
|
-
passkey :id
|
|
9
|
-
passthrough :name
|
|
10
|
-
passboolean :active
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
1
|
+
module Ecoportal
|
|
2
|
+
module API
|
|
3
|
+
class GraphQL
|
|
4
|
+
module Base
|
|
5
|
+
class LocationClassificationType < Logic::BaseModel
|
|
6
|
+
root!
|
|
7
|
+
|
|
8
|
+
passkey :id
|
|
9
|
+
passthrough :name
|
|
10
|
+
passboolean :active
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|