ecoportal-api-graphql 1.3.11 → 1.3.13

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.ai-assistance/code/filter_contract_matrix.md +177 -0
  3. data/.ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md +244 -0
  4. data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +10 -0
  5. data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +11 -0
  6. data/.ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md +236 -0
  7. data/.ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md +243 -0
  8. data/.ai-assistance/projects/template-model-logic/DESIGN_NOTE.md +154 -0
  9. data/.ai-assistance/scripts/bridge-inbox-check.sh +75 -0
  10. data/.ai-assistance/skills/procedural-memory/SKILL.md +319 -0
  11. data/.ai-assistance/skills/project-self-docs/SKILL.md +181 -0
  12. data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +378 -0
  13. data/.ai-assistance/standards-version.json +22 -21
  14. data/.claude/settings.json +150 -146
  15. data/.gitlab-ci.yml +45 -0
  16. data/CHANGELOG.md +58 -0
  17. data/CLAUDE.md +11 -0
  18. data/docs/self-docs/ARCHITECTURE.md +88 -0
  19. data/docs/self-docs/CHANGES.jsonl +12 -0
  20. data/docs/self-docs/COMPLIANCE.md +79 -0
  21. data/docs/self-docs/CONVENTIONS.md +74 -0
  22. data/docs/self-docs/INTEGRATIONS.md +65 -0
  23. data/docs/self-docs/OPERATIONS.md +76 -0
  24. data/docs/self-docs/OVERVIEW.md +64 -0
  25. data/docs/self-docs/STATUS.md +73 -0
  26. data/docs/self-docs/self-docs-index.json +51 -0
  27. data/docs/worklog.md +15 -20
  28. data/lib/ecoportal/api/common/graphql/client.rb +2 -0
  29. data/lib/ecoportal/api/common/graphql/http_client.rb +189 -177
  30. data/lib/ecoportal/api/common/graphql/model/diffable.rb +54 -54
  31. data/lib/ecoportal/api/graphql/base/action.rb +43 -43
  32. data/lib/ecoportal/api/graphql/base/contractor_entity/member_changes.rb +67 -67
  33. data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +7 -0
  34. data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -28
  35. data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -25
  36. data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -24
  37. data/lib/ecoportal/api/graphql/base/page/section_collection.rb +85 -79
  38. data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -17
  39. data/lib/ecoportal/api/graphql/base/register.rb +18 -18
  40. data/lib/ecoportal/api/graphql/compat/filter_translator.rb +63 -28
  41. data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -51
  42. data/lib/ecoportal/api/graphql/concerns.rb +14 -14
  43. data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -181
  44. data/lib/ecoportal/api/graphql/fragment/page.rb +85 -85
  45. data/lib/ecoportal/api/graphql/input/action/update.rb +14 -14
  46. data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +41 -41
  47. data/lib/ecoportal/api/graphql/input/location_structure/apply_commands.rb +47 -47
  48. data/lib/ecoportal/api/graphql/input/location_structure/draft/add_commands.rb +49 -49
  49. data/lib/ecoportal/api/graphql/input/location_structure/update_command.rb +27 -27
  50. data/lib/ecoportal/api/graphql/input/search_conf.rb +436 -367
  51. data/lib/ecoportal/api/graphql/interface/location_structure/command.rb +30 -30
  52. data/lib/ecoportal/api/graphql/logic/input.rb +26 -26
  53. data/lib/ecoportal/api/graphql_version.rb +1 -1
  54. metadata +20 -1
@@ -1,146 +1,150 @@
1
- {
2
- "permissions": {
3
- "defaultMode": "auto",
4
- "allowedTools": [
5
- "Read"
6
- ],
7
- "permissionRules": [
8
- {
9
- "tool": "Write",
10
- "pattern": "/**",
11
- "action": "allow"
12
- },
13
- {
14
- "tool": "StrReplace",
15
- "pattern": "/**",
16
- "action": "allow"
17
- }
18
- ],
19
- "allow": [
20
- "WebFetch(domain:anthropic.com)",
21
- "WebFetch(domain:npmjs.com)",
22
- "Write(.glaudeignore)",
23
- "Update(.glaudeignore)",
24
- "Bash(rm .ai-assistance/bridge/LOCK)",
25
- "Read(.git/**)",
26
- "Read(**/rubygems/**)",
27
- "Write(*.md)",
28
- "Edit(.ai-assistance/**)",
29
- "Update(.ai-assistance/**)",
30
- "Write(.ai-assistance/**)",
31
- "Write(lib/**)",
32
- "Write(spec/**)",
33
- "Bash(git status)",
34
- "Bash(git diff *)",
35
- "Bash(git log *)",
36
- "Bash(git add *)",
37
- "Bash(git commit *)",
38
- "PowerShell(git status)",
39
- "PowerShell(git diff *)",
40
- "PowerShell(git log *)",
41
- "PowerShell(git add *)",
42
- "PowerShell(git commit *)",
43
- "Bash(npm test)",
44
- "Bash(npm run lint)",
45
- "Bash(npm run build)",
46
- "Bash(vitest *)",
47
- "Bash(jest *)",
48
- "PowerShell(npm test)",
49
- "PowerShell(npm run lint)",
50
- "PowerShell(npm run build)",
51
- "Bash(black .)",
52
- "Bash(pytest)",
53
- "Bash(python -m unittest)",
54
- "Bash(ruff check *)",
55
- "Bash(bundle exec rspec *)",
56
- "Bash(bundle exec rubocop *)",
57
- "PowerShell(pytest)",
58
- "PowerShell(python -m unittest)",
59
- "PowerShell(ruff check *)",
60
- "PowerShell(bundle exec rspec *)",
61
- "PowerShell(bundle exec rubocop *)",
62
- "Bash(node .ai-assistance/scripts/token-logger.js)",
63
- "Bash(node .ai-assistance/scripts/token-session-start.js)",
64
- "Bash(cd C:/ruby_scripts/git/graphlient && git status*)",
65
- "Bash(cd C:/ruby_scripts/git/graphlient && git log*)",
66
- "Bash(cd C:/ruby_scripts/git/graphlient && git diff*)",
67
- "Bash(cd C:/ruby_scripts/git/graphlient && git show*)",
68
- "Bash(cd C:/ruby_scripts/git/graphlient && git branch*)",
69
- "Bash(cd C:/ruby_scripts/git/graphlient && git fetch*)",
70
- "Bash(cd C:/ruby_scripts/git/graphlient && git checkout*)",
71
- "Bash(cd C:/ruby_scripts/git/graphlient && git switch*)",
72
- "Bash(cd C:/ruby_scripts/git/graphlient && git add*)",
73
- "Bash(cd C:/ruby_scripts/git/graphlient && git commit*)",
74
- "Bash(cd C:/ruby_scripts/git/graphlient && git rev-parse*)",
75
- "Bash(cd C:/ruby_scripts/git/graphlient && git merge-base*)",
76
- "Bash(cd C:/ruby_scripts/git/graphlient && git ls-remote*)",
77
- "Bash(cd C:/ruby_scripts/git/graphlient && git stash*)",
78
- "Bash(cd C:/ruby_scripts/git/graphlient && bundle exec rspec*)",
79
- "Bash(cd C:/ruby_scripts/git/graphlient && bundle exec rake test*)",
80
- "Bash(cd C:/ruby_scripts/git/graphlient && bundle exec rubocop*)",
81
- "Bash(cd C:/ruby_scripts/git/graphlient && bundle install*)",
82
- "Bash(cd C:/ruby_scripts/git/graphql-client && git status*)",
83
- "Bash(cd C:/ruby_scripts/git/graphql-client && git log*)",
84
- "Bash(cd C:/ruby_scripts/git/graphql-client && git diff*)",
85
- "Bash(cd C:/ruby_scripts/git/graphql-client && git show*)",
86
- "Bash(cd C:/ruby_scripts/git/graphql-client && git branch*)",
87
- "Bash(cd C:/ruby_scripts/git/graphql-client && git fetch*)",
88
- "Bash(cd C:/ruby_scripts/git/graphql-client && git checkout*)",
89
- "Bash(cd C:/ruby_scripts/git/graphql-client && git switch*)",
90
- "Bash(cd C:/ruby_scripts/git/graphql-client && git add*)",
91
- "Bash(cd C:/ruby_scripts/git/graphql-client && git commit*)",
92
- "Bash(cd C:/ruby_scripts/git/graphql-client && git rev-parse*)",
93
- "Bash(cd C:/ruby_scripts/git/graphql-client && git merge-base*)",
94
- "Bash(cd C:/ruby_scripts/git/graphql-client && git ls-remote*)",
95
- "Bash(cd C:/ruby_scripts/git/graphql-client && git stash*)",
96
- "Bash(cd C:/ruby_scripts/git/graphql-client && bundle exec rake test*)",
97
- "Bash(cd C:/ruby_scripts/git/graphql-client && bundle exec rubocop*)",
98
- "Bash(cd C:/ruby_scripts/git/graphql-client && bundle install*)"
99
- ],
100
- "deny": [
101
- "Read(*.env)",
102
- "Read(./.env*)",
103
- "Read(./secrets/**)",
104
- "Bash(*cat *.env*)",
105
- "Bash(*grep *.env*)",
106
- "Bash(printenv*)",
107
- "Bash(env)",
108
- "Write(.git/*)",
109
- "Edit(.git/*)",
110
- "Bash(git push *)",
111
- "Bash(rm -rf *)"
112
- ]
113
- },
114
- "hooks": {
115
- "SessionStart": [
116
- {
117
- "matcher": "",
118
- "hooks": [
119
- {
120
- "type": "command",
121
- "command": "bash .ai-assistance/scripts/bridge-init.sh 2>/dev/null || true"
122
- },
123
- {
124
- "type": "command",
125
- "command": "node .ai-assistance/scripts/token-session-start.js 2>/dev/null || true"
126
- }
127
- ]
128
- }
129
- ],
130
- "Stop": [
131
- {
132
- "matcher": "",
133
- "hooks": [
134
- {
135
- "type": "command",
136
- "command": "node .ai-assistance/scripts/token-logger.js 2>/dev/null || true"
137
- },
138
- {
139
- "type": "command",
140
- "command": "bash scripts/auto-worker-scheduler.sh 2>/dev/null || true"
141
- }
142
- ]
143
- }
144
- ]
145
- }
146
- }
1
+ {
2
+ "permissions": {
3
+ "defaultMode": "auto",
4
+ "allowedTools": [
5
+ "Read"
6
+ ],
7
+ "permissionRules": [
8
+ {
9
+ "tool": "Write",
10
+ "pattern": "/**",
11
+ "action": "allow"
12
+ },
13
+ {
14
+ "tool": "StrReplace",
15
+ "pattern": "/**",
16
+ "action": "allow"
17
+ }
18
+ ],
19
+ "allow": [
20
+ "WebFetch(domain:anthropic.com)",
21
+ "WebFetch(domain:npmjs.com)",
22
+ "Write(.glaudeignore)",
23
+ "Update(.glaudeignore)",
24
+ "Bash(rm .ai-assistance/bridge/LOCK)",
25
+ "Read(.git/**)",
26
+ "Read(**/rubygems/**)",
27
+ "Write(*.md)",
28
+ "Edit(.ai-assistance/**)",
29
+ "Update(.ai-assistance/**)",
30
+ "Write(.ai-assistance/**)",
31
+ "Write(lib/**)",
32
+ "Write(spec/**)",
33
+ "Bash(git status)",
34
+ "Bash(git diff *)",
35
+ "Bash(git log *)",
36
+ "Bash(git add *)",
37
+ "Bash(git commit *)",
38
+ "PowerShell(git status)",
39
+ "PowerShell(git diff *)",
40
+ "PowerShell(git log *)",
41
+ "PowerShell(git add *)",
42
+ "PowerShell(git commit *)",
43
+ "Bash(npm test)",
44
+ "Bash(npm run lint)",
45
+ "Bash(npm run build)",
46
+ "Bash(vitest *)",
47
+ "Bash(jest *)",
48
+ "PowerShell(npm test)",
49
+ "PowerShell(npm run lint)",
50
+ "PowerShell(npm run build)",
51
+ "Bash(black .)",
52
+ "Bash(pytest)",
53
+ "Bash(python -m unittest)",
54
+ "Bash(ruff check *)",
55
+ "Bash(bundle exec rspec *)",
56
+ "Bash(bundle exec rubocop *)",
57
+ "PowerShell(pytest)",
58
+ "PowerShell(python -m unittest)",
59
+ "PowerShell(ruff check *)",
60
+ "PowerShell(bundle exec rspec *)",
61
+ "PowerShell(bundle exec rubocop *)",
62
+ "Bash(node .ai-assistance/scripts/token-logger.js)",
63
+ "Bash(node .ai-assistance/scripts/token-session-start.js)",
64
+ "Bash(cd C:/ruby_scripts/git/graphlient && git status*)",
65
+ "Bash(cd C:/ruby_scripts/git/graphlient && git log*)",
66
+ "Bash(cd C:/ruby_scripts/git/graphlient && git diff*)",
67
+ "Bash(cd C:/ruby_scripts/git/graphlient && git show*)",
68
+ "Bash(cd C:/ruby_scripts/git/graphlient && git branch*)",
69
+ "Bash(cd C:/ruby_scripts/git/graphlient && git fetch*)",
70
+ "Bash(cd C:/ruby_scripts/git/graphlient && git checkout*)",
71
+ "Bash(cd C:/ruby_scripts/git/graphlient && git switch*)",
72
+ "Bash(cd C:/ruby_scripts/git/graphlient && git add*)",
73
+ "Bash(cd C:/ruby_scripts/git/graphlient && git commit*)",
74
+ "Bash(cd C:/ruby_scripts/git/graphlient && git rev-parse*)",
75
+ "Bash(cd C:/ruby_scripts/git/graphlient && git merge-base*)",
76
+ "Bash(cd C:/ruby_scripts/git/graphlient && git ls-remote*)",
77
+ "Bash(cd C:/ruby_scripts/git/graphlient && git stash*)",
78
+ "Bash(cd C:/ruby_scripts/git/graphlient && bundle exec rspec*)",
79
+ "Bash(cd C:/ruby_scripts/git/graphlient && bundle exec rake test*)",
80
+ "Bash(cd C:/ruby_scripts/git/graphlient && bundle exec rubocop*)",
81
+ "Bash(cd C:/ruby_scripts/git/graphlient && bundle install*)",
82
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git status*)",
83
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git log*)",
84
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git diff*)",
85
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git show*)",
86
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git branch*)",
87
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git fetch*)",
88
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git checkout*)",
89
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git switch*)",
90
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git add*)",
91
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git commit*)",
92
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git rev-parse*)",
93
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git merge-base*)",
94
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git ls-remote*)",
95
+ "Bash(cd C:/ruby_scripts/git/graphql-client && git stash*)",
96
+ "Bash(cd C:/ruby_scripts/git/graphql-client && bundle exec rake test*)",
97
+ "Bash(cd C:/ruby_scripts/git/graphql-client && bundle exec rubocop*)",
98
+ "Bash(cd C:/ruby_scripts/git/graphql-client && bundle install*)"
99
+ ],
100
+ "deny": [
101
+ "Read(*.env)",
102
+ "Read(./.env*)",
103
+ "Read(./secrets/**)",
104
+ "Bash(*cat *.env*)",
105
+ "Bash(*grep *.env*)",
106
+ "Bash(printenv*)",
107
+ "Bash(env)",
108
+ "Write(.git/*)",
109
+ "Edit(.git/*)",
110
+ "Bash(git push *)",
111
+ "Bash(rm -rf *)"
112
+ ]
113
+ },
114
+ "hooks": {
115
+ "SessionStart": [
116
+ {
117
+ "matcher": "",
118
+ "hooks": [
119
+ {
120
+ "type": "command",
121
+ "command": "bash .ai-assistance/scripts/bridge-init.sh 2>/dev/null || true"
122
+ },
123
+ {
124
+ "type": "command",
125
+ "command": "node .ai-assistance/scripts/token-session-start.js 2>/dev/null || true"
126
+ },
127
+ {
128
+ "type": "command",
129
+ "command": "bash .ai-assistance/scripts/bridge-inbox-check.sh 2>/dev/null || true"
130
+ }
131
+ ]
132
+ }
133
+ ],
134
+ "Stop": [
135
+ {
136
+ "matcher": "",
137
+ "hooks": [
138
+ {
139
+ "type": "command",
140
+ "command": "node .ai-assistance/scripts/token-logger.js 2>/dev/null || true"
141
+ },
142
+ {
143
+ "type": "command",
144
+ "command": "bash scripts/auto-worker-scheduler.sh 2>/dev/null || true"
145
+ }
146
+ ]
147
+ }
148
+ ]
149
+ }
150
+ }
data/.gitlab-ci.yml ADDED
@@ -0,0 +1,45 @@
1
+ # CI pipeline for ecoportal-api-graphql
2
+ #
3
+ # First automated check on merge requests. Runs the RSpec suite NON-BLOCKING
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.
7
+ #
8
+ # Live/AI specs (tagged :live) are excluded: they need ANTHROPIC_API_KEY + network.
9
+ # They already self-skip when the key is absent, but ~live keeps the runner off
10
+ # that path entirely.
11
+ #
12
+ # NOTE: graphlient resolves from a git fork (see Gemfile); ecoportal-api / -v2 come
13
+ # 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 — because this job is
15
+ # allow_failure, that surfaces as a warning rather than blocking the MR.
16
+
17
+ workflow:
18
+ rules:
19
+ # Merge-request pipelines.
20
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
21
+ # Avoid a duplicate branch pipeline when an MR is already open for the branch.
22
+ - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
23
+ when: never
24
+ # Branch pipelines (incl. main/master pushes and feature branches without an MR).
25
+ - if: '$CI_COMMIT_BRANCH'
26
+
27
+ stages:
28
+ - test
29
+
30
+ rspec:
31
+ stage: test
32
+ image: ruby:3.2.2
33
+ allow_failure: true # non-blocking: surface failures, do not gate merges
34
+ variables:
35
+ BUNDLE_PATH: vendor/bundle # cache installed gems between pipelines
36
+ cache:
37
+ key: "ruby-322-bundle"
38
+ paths:
39
+ - vendor/bundle
40
+ before_script:
41
+ - ruby -v
42
+ - gem install bundler
43
+ - bundle install --jobs "$(nproc)" --retry 3
44
+ script:
45
+ - bundle exec rspec --tag ~live --format progress
data/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.3.13] - 2026-07-12
6
+
7
+ Reliability: the GraphQL `HttpClient` now inherits the **same request-resilience
8
+ contingencies as the REST clients** instead of a bespoke, divergent request path.
9
+ **Backwards-compatible** — `execute` returns the same parsed body and raises the same
10
+ error classes; only the transport path changed.
11
+
12
+ ### Fixed
13
+
14
+ - **`Common::GraphQL::HttpClient#execute` no longer bypasses the inherited
15
+ `Common::Client` pipeline.** It previously issued the POST raw
16
+ (`base_request.post`) and handled the response itself, so GraphQL requests got
17
+ **none** of the resilience baked into `ecoportal-api`'s client over the years —
18
+ a single **HTTP 429** (Cloudflare edge rate-limit, error `1015`) aborted the whole
19
+ run. `execute` now routes through `instrument` → `with_retry { rate_throttling }`,
20
+ gaining for free:
21
+ - **rate-limit backoff** via `rate_throttle_client` (default
22
+ `ExponentialIncreaseProportionalDecrease`) — 429s are throttled + retried, not fatal.
23
+ - **retry on 5xx / connection errors** (`WithRetry`, 5 attempts) + empty-body guard.
24
+ - timeouts, and (deep-)logging / APM instrumentation.
25
+ - `handle_http_response` now consumes the wrapped `Common::Response` (single body
26
+ read — also removes a latent double-read hazard). The GraphQL-errors-in-a-200-body
27
+ path (`ResponseError`) is unchanged.
28
+ This is convergence on the shared base, so any future contingency added to
29
+ `Common::Client` now applies to GraphQL automatically.
30
+
31
+ ## [1.3.12] - 2026-07-09
32
+
33
+ Compat-layer correctness for register **field filters** + the native `SearchConf` field-filter DSL.
34
+ **Backwards-compatible** — bug fixes (broken field filters returned 0 results) + additive methods;
35
+ no public return type changed. (The `get_by_name`→collection contract change is intentionally **not**
36
+ in this release — it is a breaking change parked on `feature/model-input-fixes` for a deliberate,
37
+ signed-off future decision.)
38
+
39
+ ### Fixed
40
+
41
+ - **`Compat::FilterTranslator`** now routes register **FIELD** filters to the backend-correct ES
42
+ operation (previously emitted the system-key operation → 0 results):
43
+ - Date FIELD (key `date.zXXXX`) → `range_date_filter` + `path: membranes.date` (system date keeps
44
+ `date_filter`).
45
+ - Select FIELD (key `select_str.zXXXX`) → `has_any_filter` / `doesnt_have_any_filter` +
46
+ `path: membranes.select_str` + `field_key: 'selected'` (system option key keeps
47
+ `one_of_filter`/`none_of_filter`).
48
+ - `exact_filter` no longer derives a `membranes.<type>` path for **system** keys
49
+ (`external_id`/`id`/`state`) — a spurious path produced term `membranes.external_id.external_id`
50
+ → 0 results (silent miss → dup-on-upsert). Register FIELD keys still get the nested path.
51
+ - Verified against the backend filter definitions; catalogued in
52
+ `.ai-assistance/code/filter_contract_matrix.md`.
53
+
54
+ ### Added
55
+
56
+ - **`SearchConf` field-filter DSL:** `sc[:'date.zXXXX'].between/since/before` now emits
57
+ `range_date_filter` + `membranes.date` for register Date FIELDs; new `FieldRef#any_of` / `#none_of`
58
+ → `has_any_filter` / `doesnt_have_any_filter` (path + `field_key`) for register field value
59
+ membership (distinct from `one_of`, which ORs `exact_filter` for system keys).
60
+ - **`get_by_id`** on `DataField::Collection` and `SectionCollection` (v2-compat find-by-id) — fixes
61
+ `NoMethodError` in consumers that resolve a field/section by id.
62
+
5
63
  ## [1.3.11] - 2026-07-04
6
64
 
7
65
  Template automatic **build & maintenance** engine (backwards-compatible — a self-contained new
data/CLAUDE.md CHANGED
@@ -11,8 +11,18 @@ docs/worklog.md ← current state, blockers, open quest
11
11
  .ai-assistance/projects/<slug>/TODO.md ← active project task list
12
12
  .ai-assistance/local/paths.json ← local paths to sibling repos (gitignored — machine-specific)
13
13
  .ai-assistance/bridge/CLAUDE.md ← bridge protocol: format, lifecycle, cross-repo requests
14
+ .ai-assistance/bridge/inbox/ <- unread cross-project bridge messages (PENDING = unread); surfaced at session start by .ai-assistance/scripts/bridge-inbox-check.sh
15
+ .ai-assistance/code/ecoPortal_architecture/ ← VERIFIED PLATFORM KNOWLEDGE — how the platform behaves
16
+ (data model, API layers, page/stage workflows, fields, search).
17
+ Read 00_overview_and_index first, then the area file for the
18
+ task at hand, BEFORE implementing platform logic. See ADR-012.
14
19
  ```
15
20
 
21
+ > Platform behaviour is documented in `ecoPortal_architecture/` for a reason: do not infer a
22
+ > stage/workflow/field rule from code alone when a verified note exists. If you confirm a new
23
+ > platform fact, add it to the relevant doc with its evidence (`file:line` or "confirmed by
24
+ > &lt;user&gt; on &lt;date&gt;") in the same session -- that tree is the durable, shared home.
25
+
16
26
  ---
17
27
 
18
28
  ## Repository Role
@@ -95,6 +105,7 @@ the developer explicitly requests it for a specific commit.
95
105
  | `spec-generation` | `.ai-assistance/skills/spec-generation/SKILL.md` | Generate RSpec specs for backwards-compat |
96
106
  | `refactor` | `.ai-assistance/skills/refactor/SKILL.md` | Identify and log refactoring opportunities |
97
107
  | `project-cycle` | `.ai-assistance/skills/project-cycle/SKILL.md` | Project planning, TODOs, decisions |
108
+ | `procedural-memory` | `.ai-assistance/skills/procedural-memory/SKILL.md` | Distill recurring work routines and surface likely next steps |
98
109
 
99
110
  ---
100
111
 
@@ -0,0 +1,88 @@
1
+ ---
2
+ schema_version: "1.1"
3
+ repo: "ecoportal-api-graphql"
4
+ doc: architecture
5
+ last_generated: "2026-07-09"
6
+ source_head: "3f7b877"
7
+ review_status: draft
8
+ ---
9
+
10
+ # Architecture -- ecoportal-api-graphql
11
+
12
+ **Scope:** High-level structure only. Deep per-area detail lives in `.ai-assistance/code/`
13
+ (`code-specs`); link to it rather than duplicating.
14
+
15
+ ## Top-level structure
16
+
17
+ | Directory | Role |
18
+ |---|---|
19
+ | `lib/` | The gem source. `lib/ecoportal/api/graphql/` holds the layered client; `lib/ecoportal/api/common/graphql/` holds shared utilities (HTTP client, base model, helpers, patches). |
20
+ | `spec/` | RSpec specs, mirroring the `lib/` structure. |
21
+ | `tests/` | Manual integration test scripts (live-API characterization, not the RSpec suite). |
22
+ | `bin/` | Development console / helper executables. |
23
+ | `scripts/` | Token-offload and tooling scripts (see `scripts/CLAUDE.md`). |
24
+ | `docs/` | Human docs, worklog, and this self-docs set. |
25
+ | `.ai-assistance/` | AI agent resources: code-specs, project intent/TODOs, skills, bridge, conventions. |
26
+ | `.claude/` | Claude Code settings and hooks for this repo. |
27
+
28
+ ## Key components
29
+
30
+ Reused from `.ai-assistance/code/OVERVIEW.md` and `.ai-assistance/code/ecoPortal_architecture/`;
31
+ not re-analysed here.
32
+
33
+ - **Main class `Ecoportal::API::GraphQL`** (`lib/ecoportal/api/graphql.rb`) -- the public entry
34
+ point, instantiated with credentials (email, pass, org_id, host). Exposes builders and
35
+ convenience shortcuts per domain.
36
+ - **`Common::GraphQL::Client`** (`lib/ecoportal/api/common/graphql/client.rb`) -- the HTTP layer,
37
+ extending `Graphlient::Client`. Handles auth (`AuthService`), org context, and timeouts;
38
+ `no_schema: true` is always passed, so schema introspection is disabled and queries are not
39
+ validated against a schema at build time.
40
+ - **Layered namespace** under `Ecoportal::API::GraphQL::` (folder mirrors module exactly):
41
+ `Builder` (developer-facing facade per domain) -> `Query` / `Mutation` (operation definitions)
42
+ -> `Logic` (execution: base_query, base_model, payload, input, connection) -> `Fragment`
43
+ (fragment strings) -> `Model` (read-side response models), `Input` / `Payload` (write side) ->
44
+ `Base` (shared base classes), `Interface` (GraphQL interface types) -> `Common::GraphQL::Client`.
45
+ - **`Compat::` layer** -- v2 compatibility shims (`Pages`, `Registers`, `StageView`,
46
+ `StageCollection`, etc.) that let `eco-helpers` treat `graphql` as a drop-in for the older `apiv2`
47
+ interface.
48
+ - **`Diff::` and `Builder::TemplateBuilder`** -- the template build-and-maintenance engine
49
+ (self-version and cross-object diff, pairing engine + ledger, `Change`-to-`WorkflowCommand`
50
+ synthesis, unified BUILD emitter). This is where current development is concentrated.
51
+
52
+ ## How they fit
53
+
54
+ A consumer instantiates `Ecoportal::API::GraphQL` with credentials, then calls a `Builder` for the
55
+ domain it wants (page, template, action, contractor entity, location structure). The builder
56
+ composes `Query`/`Mutation` operations, which the `Logic` layer executes through
57
+ `Common::GraphQL::Client` over HTTP. Fragments are assembled at query build time
58
+ (`BaseQuery#assemble_fragments` scans query strings for `...FragmentName` spreads). Responses parse
59
+ into `Model` objects (read side); mutations take `Input` objects and return `Payload` objects
60
+ (write side). The `Compat` layer re-exposes this so downstream `eco-helpers` scripts keep working
61
+ unchanged.
62
+
63
+ ## Entry points
64
+
65
+ - `require 'ecoportal/api-graphql'` loads `ecoportal-api-v2` (and transitively `ecoportal-api`),
66
+ the version file, `ecoportal/api/common.graphql`, then `ecoportal/api/graphql`.
67
+ - Runtime entry is `Ecoportal::API::GraphQL.new(email:, pass:, org_id:, host:)`.
68
+
69
+ ## Build / test tooling
70
+
71
+ - Build: RubyGems / Bundler. Gem spec: `ecoportal-api-graphql.gemspec`. Version constant:
72
+ `Ecoportal::API::GRAPQL_VERSION`.
73
+ - Tests: RSpec (`bundle exec rspec`). Live AI tests are `--tag live` and require `ANTHROPIC_API_KEY`
74
+ loaded from `.env` via `dotenv`.
75
+ - CI: no CI config file was detected at the repo root by the scanner (`ci_files: []`).
76
+ <!-- TODO(human): confirm whether CI runs elsewhere (e.g. GitLab CI defined outside root) or is absent. -->
77
+
78
+ ## Links to detailed docs
79
+
80
+ - `.ai-assistance/code/OVERVIEW.md` -- gem overview, entry points, namespace convention.
81
+ - `.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md` -- verified platform
82
+ knowledge index (data model, API layers, page/stage workflows, fields, search).
83
+ - `.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md` -- the integration gem stack.
84
+ - `.ai-assistance/code/dependencies.md` -- full dependency map (remotes, fork relationships).
85
+ - `.ai-assistance/code/diff_pairing_engine.md`, `.ai-assistance/code/diff_service_deep_dive.md`,
86
+ `.ai-assistance/code/template_diff_pairing_domain.md` -- the template diff/build engine.
87
+ - `.ai-assistance/code/data_fields.md`, `.ai-assistance/code/graphql_domain_knowledge.md`,
88
+ `.ai-assistance/code/schema_analysis.md` -- data-field and schema detail.
@@ -0,0 +1,12 @@
1
+ {"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/OVERVIEW.md", "change": "created", "from_hash": null, "to_hash": "a43c976a85ebfad6"}
2
+ {"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/ARCHITECTURE.md", "change": "created", "from_hash": null, "to_hash": "b0989977a912aab1"}
3
+ {"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/CONVENTIONS.md", "change": "created", "from_hash": null, "to_hash": "1649159eb799a7b8"}
4
+ {"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/INTEGRATIONS.md", "change": "created", "from_hash": null, "to_hash": "bae9f91748e33af1"}
5
+ {"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/STATUS.md", "change": "created", "from_hash": null, "to_hash": "08841d1d45eea2d6"}
6
+ {"ts": "2026-07-06T11:15:00Z", "file": "docs/self-docs/OVERVIEW.md", "change": "updated", "from_hash": "a43c976a85ebfad6", "to_hash": "391055c9a6a687b9"}
7
+ {"ts": "2026-07-06T11:15:00Z", "file": "docs/self-docs/ARCHITECTURE.md", "change": "updated", "from_hash": "b0989977a912aab1", "to_hash": "ad05f25b95b49c00"}
8
+ {"ts": "2026-07-09T07:08:00Z", "file": "docs/self-docs/OVERVIEW.md", "change": "updated", "from_hash": "391055c9a6a687b9", "to_hash": "c403012498ab9247"}
9
+ {"ts": "2026-07-09T07:08:00Z", "file": "docs/self-docs/INTEGRATIONS.md", "change": "updated", "from_hash": "bae9f91748e33af1", "to_hash": "d271ac76361e4638"}
10
+ {"ts": "2026-07-09T07:08:00Z", "file": "docs/self-docs/STATUS.md", "change": "updated", "from_hash": "08841d1d45eea2d6", "to_hash": "1acf51a3e7822dfb"}
11
+ {"ts": "2026-07-09T07:08:00Z", "file": "docs/self-docs/COMPLIANCE.md", "change": "created", "from_hash": null, "to_hash": "1e72c2356b4e3579"}
12
+ {"ts": "2026-07-09T07:08:00Z", "file": "docs/self-docs/OPERATIONS.md", "change": "created", "from_hash": null, "to_hash": "878b50d02339acb7"}
@@ -0,0 +1,79 @@
1
+ ---
2
+ schema_version: "1.1"
3
+ repo: "ecoportal-api-graphql"
4
+ doc: compliance
5
+ last_generated: "2026-07-09"
6
+ source_head: "3f7b877"
7
+ review_status: draft # audit answers MUST come from 'reviewed' docs only -- a human sets this
8
+ ---
9
+
10
+ # Compliance -- ecoportal-api-graphql
11
+
12
+ **Purpose:** the audit / privacy / ISO-27001 surface for this project. It lets ROVO (over the EP Projects
13
+ Register) answer compliance questions across the fleet. **Trust rule:** everything here separates
14
+ EVIDENCE (a fact or link) from INTENT (planned); a reviewer must set `review_status: reviewed` before an
15
+ answer built on this doc is used for audit or re-certification -- a confident answer on unreviewed content
16
+ would mislead an auditor.
17
+
18
+ > This document is DRAFT and was composed from the repo's own context (CLAUDE.md, the
19
+ > `.ai-assistance/code/` specs, and the active project INTENT). Fields marked PLANNED or "human to
20
+ > confirm" MUST be verified by the repo owner before any audit use.
21
+
22
+ ## ISO 27001
23
+
24
+ This is a Ruby client library (a gem), not a hosted service; it holds no infrastructure of its own and
25
+ introduces no independent ISO-27001 control surface. Its compliance posture is inherited from (a) the
26
+ ecoPortal platform it authenticates against and (b) the consuming project's runtime environment. The
27
+ control this repo DOES own is access-scoping of the automation that uses it.
28
+
29
+ | Control area | Status (evidence / planned) | Evidence link |
30
+ |---|---|---|
31
+ | Access control / least privilege (A.9) | EVIDENCE: the template-automation work is required to run under a per-org SERVICE ACCOUNT scoped to templates/org-config only, with page records structurally out of scope by credential grant (ep-ai-standards `security/ai-service-account-scope`). | `.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md`; `security/ai-service-account-scope` standard |
32
+ | Change control (A.12.1) | EVIDENCE: no silent breaking changes -- public-interface changes are recorded in the active project's DECISIONS log and confirmed with the developer; semver + CHANGELOG. | `CLAUDE.md`; `CHANGELOG.md` |
33
+ | Input validation (A.14.2) | EVIDENCE: `no_schema: true` disables build-time schema validation; the compensating control is a query validator now covering mutations. | `.ai-assistance/code/` (query validator); `ARCHITECTURE.md` |
34
+ | Auditability of automated changes | PLANNED / human to confirm: template BUILD/diff emits an ordered `WorkflowCommand` batch and a pairing ledger; whether that stream is retained as an audit trail for re-certification needs owner confirmation. | `.ai-assistance/code/diff_pairing_engine.md` |
35
+
36
+ ## Data handled
37
+
38
+ The gem can read and write live ecoPortal domain objects through the API. Data classification is by
39
+ CAPABILITY (what the client can touch), then narrowed by the credential scope in use at runtime.
40
+
41
+ | Data class (none/team/customer/PII) | What / where | Treatment + retention | Exposed? (how) |
42
+ |---|---|---|---|
43
+ | Customer data + PII | Page records read/written via the GraphQL API (can contain personal data). | In-memory only during a client session; the gem persists nothing itself. Retention lives in the ecoPortal platform, not here. | Not exposed by the gem beyond the authenticated API call. Read/write capability is gated by the runtime credential; template-automation deliberately excludes page records by service-account scope. |
44
+ | Customer/org config (non-PII) | Templates, location structures, org configuration. | In-memory during a session; changes applied via `WorkflowCommand` batches. Retention in the platform. | Only via the authenticated API; this is the scope the template-automation service account is limited to. |
45
+ | Team-only | `.env` credentials for live tests (`ANTHROPIC_API_KEY`, ecoPortal creds via dotenv). | Local `.env`, gitignored; loaded only for `--tag live` specs. | Not committed; not exposed. |
46
+
47
+ ## Third-party vendors in the data path
48
+
49
+ | Vendor | What data | Disclosed to end users? | Notes |
50
+ |---|---|---|---|
51
+ | Anthropic (Claude) | Only for `--tag live` AI tests, gated by `ANTHROPIC_API_KEY`. No customer page data is sent as part of normal gem operation. | N/A -- development-time only, not a production data path. | Live AI tests are opt-in; the gem's runtime API client does not call any LLM. Human to confirm no PII appears in live-test fixtures. |
52
+
53
+ No customer/PII data leaves the ecoPortal boundary through this gem in normal operation: it is a client
54
+ for ecoPortal's own API, not a bridge to an external SaaS.
55
+
56
+ ## PII / data-leak controls
57
+
58
+ - **Credential scope (primary control):** the template-automation service account is scoped to
59
+ templates/org-config only, so page records (customer data + PII) are out of reach by GRANT, not by
60
+ instruction (ep-ai-standards `security/ai-service-account-scope`). This is the load-bearing leak
61
+ control.
62
+ - **`org_id` scoping:** every client session is bound to a single org context; cross-org reads require
63
+ distinct credentials.
64
+ - **No local persistence:** the gem holds data in memory for the session and writes nothing to disk;
65
+ there is no cache or log of customer records to leak.
66
+ - **`.env` gitignored:** credentials for live tests never enter version control.
67
+ - **Query validator:** compensates for `no_schema: true` by catching malformed queries/mutations before
68
+ they hit the live API.
69
+
70
+ ## AI-generated content
71
+
72
+ - Externally-exposed AI content: **no.** The gem produces no customer-facing or publicly-exposed AI
73
+ content. AI assistance is confined to development (code generation, template-automation planning) and
74
+ `--tag live` AI tests; its output is reviewed code and internal artefacts, not exposed to end users.
75
+ - Marked as AI-generated: **N/A** for external content. Internally, AI-assisted commits follow the
76
+ developer-only authorship convention (no `Co-Authored-By` unless requested), and Gemini responses must
77
+ be verified against actual code before applying.
78
+ - Review process: standard MR review; public-interface changes additionally logged in the active
79
+ project's DECISIONS log and confirmed with the developer.