ecoportal-api 0.10.16 → 0.10.17

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.

Potentially problematic release.


This version of ecoportal-api might be problematic. Click here for more details.

Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.ai-assistance/.gitignore +2 -0
  3. data/.ai-assistance/bridge/.gitignore +10 -0
  4. data/.ai-assistance/bridge/CLAUDE.md +96 -0
  5. data/.ai-assistance/bridge/archive/.gitkeep +0 -0
  6. data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
  7. data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
  8. data/.ai-assistance/capabilities/assumptions-log.md +23 -0
  9. data/.ai-assistance/scripts/bridge-inbox-check.sh +119 -0
  10. data/.ai-assistance/scripts/bridge-init.sh +86 -0
  11. data/.ai-assistance/scripts/confine-to-subtree.sh +58 -0
  12. data/.ai-assistance/scripts/dirty-tree-guard.sh +96 -0
  13. data/.ai-assistance/scripts/distill_procedural.py +602 -0
  14. data/.ai-assistance/scripts/log-mcp-access.sh +24 -0
  15. data/.ai-assistance/scripts/log-skill-usage.sh +79 -0
  16. data/.ai-assistance/scripts/log_mcp_access.py +158 -0
  17. data/.ai-assistance/scripts/observe-session.sh +13 -0
  18. data/.ai-assistance/scripts/observe_session.py +287 -0
  19. data/.ai-assistance/scripts/protect-host-paths.sh +135 -0
  20. data/.ai-assistance/scripts/scrub.py +1149 -0
  21. data/.ai-assistance/scripts/scrub.py.sha256 +6 -0
  22. data/.ai-assistance/scripts/surface-procedural.sh +9 -0
  23. data/.ai-assistance/scripts/surface_procedural.py +101 -0
  24. data/.ai-assistance/skills/ep-ai-manager/SKILL.md +519 -0
  25. data/.ai-assistance/skills/project-self-docs/SKILL.md +259 -0
  26. data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +378 -0
  27. data/.ai-assistance/standards-version.json +12 -0
  28. data/.ai-assistance/version.json +8 -0
  29. data/.claude/.gitignore +2 -0
  30. data/.claude/settings.json +128 -0
  31. data/CHANGELOG.md +8 -5
  32. data/CLAUDE.md +95 -71
  33. data/docs/self-docs/ARCHITECTURE.md +145 -0
  34. data/docs/self-docs/CHANGES.jsonl +7 -0
  35. data/docs/self-docs/COMPLIANCE.md +66 -0
  36. data/docs/self-docs/CONVENTIONS.md +74 -0
  37. data/docs/self-docs/INTEGRATIONS.md +62 -0
  38. data/docs/self-docs/OPERATIONS.md +64 -0
  39. data/docs/self-docs/OVERVIEW.md +61 -0
  40. data/docs/self-docs/STATUS.md +71 -0
  41. data/docs/self-docs/self-docs-index.json +51 -0
  42. data/docs/worklog.md +48 -0
  43. data/lib/ecoportal/api/common/client/with_retry.rb +6 -0
  44. data/lib/ecoportal/api/version.rb +1 -1
  45. metadata +40 -1
@@ -0,0 +1,128 @@
1
+ {
2
+ "permissions": {
3
+ "defaultMode": "acceptEdits",
4
+ "allow": [
5
+ "Bash(bundle exec rspec *)",
6
+ "Bash(bundle exec rspec)",
7
+ "Bash(bundle exec rubocop *)",
8
+ "Bash(bundle exec rubocop)",
9
+ "Bash(git status)",
10
+ "Bash(git log *)",
11
+ "Bash(git diff *)",
12
+ "Bash(git diff)",
13
+ "Bash(git add *)",
14
+ "Bash(git commit *)",
15
+ "Bash(git branch *)",
16
+ "Bash(git checkout *)",
17
+ "Bash(git push -o merge_request.create*)",
18
+ "Bash(git push -u origin *)",
19
+ "Bash(git push origin HEAD*)",
20
+ "Bash(git push *)",
21
+ "Bash(bundle install)",
22
+ "Bash(bundle exec *)",
23
+ "Edit(.ai-assistance/**)",
24
+ "Read(.git/**)",
25
+ "WebFetch(domain:anthropic.com)",
26
+ "WebFetch(domain:docs.anthropic.com)"
27
+ ],
28
+ "deny": [
29
+ "Bash(git push --force*)",
30
+ "Bash(git push * --force*)",
31
+ "Bash(git push --force-with-lease*)",
32
+ "Bash(git push * --force-with-lease*)",
33
+ "Bash(git push origin main*)",
34
+ "Bash(git push origin master*)",
35
+ "Bash(git push * origin main*)",
36
+ "Bash(git push * origin master*)",
37
+ "Bash(rm -rf *)",
38
+ "Bash(cat .env*)",
39
+ "Read(.env*)",
40
+ "Read(./secrets/**)",
41
+ "Edit(.env*)",
42
+ "Edit(//System/**)",
43
+ "Edit(//Library/**)",
44
+ "Edit(~/Library/**)",
45
+ "Edit(//usr/**)",
46
+ "Edit(//etc/**)",
47
+ "Edit(//bin/**)",
48
+ "Edit(//sbin/**)",
49
+ "Edit(//boot/**)",
50
+ "Edit(//c/Windows/**)",
51
+ "Edit(//c/Program Files/**)",
52
+ "Edit(//c/Program Files (x86)/**)",
53
+ "Read(~/.ssh/**)",
54
+ "Read(~/.aws/**)"
55
+ ]
56
+ },
57
+ "hooks": {
58
+ "SessionStart": [
59
+ {
60
+ "matcher": "",
61
+ "hooks": [
62
+ {
63
+ "type": "command",
64
+ "command": "bash .ai-assistance/scripts/bridge-init.sh 2>/dev/null || true"
65
+ },
66
+ {
67
+ "type": "command",
68
+ "command": "bash .ai-assistance/scripts/bridge-inbox-check.sh 2>/dev/null || true"
69
+ },
70
+ {
71
+ "type": "command",
72
+ "command": "bash .ai-assistance/scripts/surface-procedural.sh"
73
+ },
74
+ {
75
+ "type": "command",
76
+ "command": "bash .ai-assistance/scripts/dirty-tree-guard.sh 2>/dev/null || true"
77
+ }
78
+ ]
79
+ }
80
+ ],
81
+ "Stop": [
82
+ {
83
+ "matcher": "",
84
+ "hooks": [
85
+ {
86
+ "type": "command",
87
+ "command": "bash .ai-assistance/scripts/observe-session.sh"
88
+ },
89
+ {
90
+ "type": "command",
91
+ "command": "bash .ai-assistance/scripts/dirty-tree-guard.sh 2>/dev/null || true"
92
+ }
93
+ ]
94
+ }
95
+ ],
96
+ "PreToolUse": [
97
+ {
98
+ "matcher": "Bash",
99
+ "hooks": [
100
+ {
101
+ "type": "command",
102
+ "command": "bash .ai-assistance/scripts/protect-host-paths.sh"
103
+ }
104
+ ]
105
+ },
106
+ {
107
+ "matcher": "Skill",
108
+ "hooks": [
109
+ {
110
+ "type": "command",
111
+ "command": "bash .ai-assistance/scripts/log-skill-usage.sh 2>/dev/null || true"
112
+ }
113
+ ]
114
+ }
115
+ ],
116
+ "PostToolUse": [
117
+ {
118
+ "matcher": "mcp__.*",
119
+ "hooks": [
120
+ {
121
+ "type": "command",
122
+ "command": "bash .ai-assistance/scripts/log-mcp-access.sh 2>/dev/null || true"
123
+ }
124
+ ]
125
+ }
126
+ ]
127
+ }
128
+ }
data/CHANGELOG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [0.10.17] - 2026-06-xx
5
+ ## [0.10.17] - 2026-08-08
6
6
 
7
7
  ### Added
8
8
 
@@ -10,11 +10,14 @@ All notable changes to this project will be documented in this file.
10
10
 
11
11
  ### Fixed
12
12
 
13
- ## [0.10.16] - 2026-06-05
14
-
15
- ### Added
13
+ - `WithRetry` never retried timeouts: `HTTP::TimeoutError` (and so `HTTP::ConnectTimeoutError`)
14
+ is a sibling of `HTTP::ConnectionError` in the `http` gem hierarchy, not a subclass — a
15
+ transient connect/read/write timeout propagated immediately and killed whole batches mid-loop
16
+ (live-confirmed on the Farmers native card, 2026-07-30). Added `HTTP::TimeoutError` to
17
+ `HANDLED_CONNECTION_ERRORS`; applies to all downstream gems that inherit this retry layer
18
+ (`ecoportal-api-v2`, `ecoportal-api-graphql`, `eco-helpers`).
16
19
 
17
- ### Changed
20
+ ## [0.10.16] - 2026-06-05
18
21
 
19
22
  ### Fixed
20
23
 
data/CLAUDE.md CHANGED
@@ -1,71 +1,95 @@
1
- # CLAUDE.md — ecoportal-api
2
-
3
- AI agent instructions for this repository.
4
-
5
- **Cross-cutting architecture context lives in `ecoportal-api-graphql` — see its `CLAUDE.md` and `.claude/` folder for the full dependency map, project history, and shared skills.**
6
-
7
- ---
8
-
9
- ## Repository Role
10
-
11
- `ecoportal-api` is the **foundational REST API gem** in the EcoPortal Ruby stack. It provides authentication, HTTP client infrastructure, org context, and the `Ecoportal::API::Common` namespace that all downstream gems build on.
12
-
13
- **Position in chain:**
14
- ```
15
- ecoportal-api ← THIS REPO
16
- ↓
17
- ecoportal-api-v2
18
- ↓
19
- ecoportal-api-graphql
20
- ↓
21
- eco-helpers
22
- ```
23
-
24
- **Remote:** https://gitlab.ecoportal.co.nz/ecoportal/ecoportal-api.git
25
-
26
- ---
27
-
28
- ## Key Folder Layout
29
-
30
- ```
31
- lib/ecoportal/api/
32
- common/ Shared infrastructure (client, logging, response, batch, hash_diff)
33
- client/ HTTP client — rate throttling, retries, timeouts, APM, error handling
34
- errors/ Shared error classes
35
- internal/ Internal API resources (people, permissions, schema, login providers)
36
- v1/ REST API v1 resources (people, schema, jobs)
37
- logger.rb Shared logger
38
- version.rb Gem version
39
- ```
40
-
41
- ---
42
-
43
- ## Namespace
44
-
45
- `Ecoportal::API` — specifically:
46
- - `Ecoportal::API::Common` — shared base classes (consumed by `ecoportal-api-v2` and `ecoportal-api-graphql`)
47
- - `Ecoportal::API::Internal` — internal API endpoints
48
- - `Ecoportal::API::V1` — v1 REST endpoints
49
-
50
- ---
51
-
52
- ## Key Concerns
53
-
54
- - `Common::Client` — base HTTP client with rate throttling, retry logic, timeouts, and APM integration. All downstream clients inherit or wrap this.
55
- - `Common::BaseModel` / `Common::BaseClass` — base model classes. `ecoportal-api-v2` extends these heavily.
56
- - Changes to `Common::` affect **all downstream gems** — treat as a public API.
57
-
58
- ---
59
-
60
- ## Backwards Compatibility
61
-
62
- Changes to `Ecoportal::API::Common::*` propagate to `ecoportal-api-v2`, `ecoportal-api-graphql`, and `eco-helpers`. Any breaking change must be coordinated across the chain.
63
-
64
- ---
65
-
66
- ## Running Tests
67
-
68
- ```bash
69
- bundle install
70
- bundle exec rspec
71
- ```
1
+ # CLAUDE.md — ecoportal-api
2
+
3
+ AI agent instructions for this repository.
4
+
5
+ **Cross-cutting architecture context lives in `ecoportal-api-graphql` — see its `CLAUDE.md` and `.claude/` folder for the full dependency map, project history, and shared skills.**
6
+
7
+ ---
8
+
9
+ ## Read these first — every session
10
+
11
+ ```
12
+ docs/worklog.md <- current state, blockers, open questions
13
+ .ai-assistance/local/paths.json <- local paths to ep-ai-standards + sibling repos (gitignored)
14
+ ```
15
+
16
+ ---
17
+
18
+ ## Repository Role
19
+
20
+ `ecoportal-api` is the **foundational REST API gem** in the EcoPortal Ruby stack. It provides authentication, HTTP client infrastructure, org context, and the `Ecoportal::API::Common` namespace that all downstream gems build on.
21
+
22
+ **Position in chain:**
23
+ ```
24
+ ecoportal-api ← THIS REPO
25
+ ↓
26
+ ecoportal-api-v2
27
+ ↓
28
+ ecoportal-api-graphql
29
+ ↓
30
+ eco-helpers
31
+ ```
32
+
33
+ **Remote:** https://gitlab.ecoportal.co.nz/ecoportal/ecoportal-api.git
34
+
35
+ ---
36
+
37
+ ## Key Folder Layout
38
+
39
+ ```
40
+ lib/ecoportal/api/
41
+ common/ Shared infrastructure (client, logging, response, batch, hash_diff)
42
+ client/ HTTP client — rate throttling, retries, timeouts, APM, error handling
43
+ errors/ Shared error classes
44
+ internal/ Internal API resources (people, permissions, schema, login providers)
45
+ v1/ REST API v1 resources (people, schema, jobs)
46
+ logger.rb Shared logger
47
+ version.rb Gem version
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Namespace
53
+
54
+ `Ecoportal::API` — specifically:
55
+ - `Ecoportal::API::Common` — shared base classes (consumed by `ecoportal-api-v2` and `ecoportal-api-graphql`)
56
+ - `Ecoportal::API::Internal` — internal API endpoints
57
+ - `Ecoportal::API::V1` — v1 REST endpoints
58
+
59
+ ---
60
+
61
+ ## Key Concerns
62
+
63
+ - `Common::Client` — base HTTP client with rate throttling, retry logic, timeouts, and APM integration. All downstream clients inherit or wrap this.
64
+ - `Common::BaseModel` / `Common::BaseClass` — base model classes. `ecoportal-api-v2` extends these heavily.
65
+ - Changes to `Common::` affect **all downstream gems** — treat as a public API.
66
+
67
+ ---
68
+
69
+ ## Backwards Compatibility
70
+
71
+ Changes to `Ecoportal::API::Common::*` propagate to `ecoportal-api-v2`, `ecoportal-api-graphql`, and `eco-helpers`. Any breaking change must be coordinated across the chain.
72
+
73
+ ---
74
+
75
+ ## Running Tests
76
+
77
+ ```bash
78
+ bundle install
79
+ bundle exec rspec
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Token economy -- ENFORCED
85
+
86
+ This gem shares one account-wide weekly token ceiling with every other AI-enabled
87
+ repo. Keep sessions short and scoped -- end or reset one when a work item completes.
88
+ Any background/subagent work MUST start from a fresh context (never a fork) with a
89
+ self-contained task card naming an explicit model tier; never let a launched agent
90
+ silently inherit this session's model. Delegated agents COMMIT but MUST NOT push or
91
+ open MRs -- this session verifies independently, then pushes. Before briefing a task
92
+ that depends on an external API, probe its real limits (endpoint, window/page size,
93
+ pagination, scope) first -- do not let the agent discover them mid-task. Batch shell
94
+ commands; avoid per-command round-trips or polling loops.
95
+ Full standard: `standards/workflows/token-economy.md` in ep-ai-standards.
@@ -0,0 +1,145 @@
1
+ ---
2
+ schema_version: "1.1"
3
+ repo: "ecoportal-api"
4
+ doc: architecture
5
+ last_generated: "2026-07-23"
6
+ source_head: "69bf8e3"
7
+ review_status: draft
8
+ ---
9
+
10
+ # Architecture -- ecoportal-api
11
+
12
+ **Scope:** High-level structure only. No `.ai-assistance/code/` code-specs exist yet in this repo
13
+ (scanner: `existing_context.code_specs` is empty) -- this section is derived directly from the
14
+ `lib/` tree, not from a code-specs layer.
15
+
16
+ ## Top-level structure
17
+
18
+ (From the scanner's `top_level_dirs`.)
19
+
20
+ | Directory | Role |
21
+ |---|---|
22
+ | `lib/` | The gem source (`Ecoportal::API` namespace: `common/`, `internal/`, `v1/`, `errors/`) |
23
+ | `spec/` | RSpec test suite, mirrors the `lib/` namespace layout |
24
+ | `bin/` | `bin/setup`, `bin/console` -- local dev helpers (from `README.md`) |
25
+ | `docs/` | This self-docs set + `docs/worklog.md` (session handoff log) |
26
+ | `.ai-assistance/` | AI tooling scaffold (skills, scripts, bridge, capabilities) -- most-touched
27
+ directory after `lib/` in the last 50 commits (scanner `activity_clusters`: `.ai-assistance` 27
28
+ touches vs `lib` 28) |
29
+ | `.claude/` | Committed Claude Code settings (`settings.json`: permissions + hooks) |
30
+ | `.ruby-lsp` | Ruby LSP cache/config directory |
31
+
32
+ ## Key components
33
+
34
+ - `Ecoportal::API::Common` (`lib/ecoportal/api/common.rb` + `common/`) -- shared base classes and
35
+ infrastructure consumed by every downstream gem:
36
+ - `Common::Client` (`common/client.rb`) -- the HTTP client (wraps the `http` gem), with
37
+ sub-modules for rate throttling (`client/rate_throttling.rb`, `client/throughput*.rb`),
38
+ retries (`client/with_retry.rb`), timeouts (`client/time_out.rb`), error handling
39
+ (`client/error*.rb`), and optional Elastic APM reporting (`client/elastic_apm_integration.rb`).
40
+ - `Common::BaseModel` / `Common::BaseClass` (`common/base_model.rb`, `common/base_class.rb`) --
41
+ base model classes; `ecoportal-api-v2` extends these heavily (per `CLAUDE.md`).
42
+ - `Common::Response` / `Common::WrappedResponse`, `Common::BatchOperation` /
43
+ `Common::BatchResponse`, `Common::HashDiff`, `Common::Logging`, `Common::DocHelpers`.
44
+ - `Ecoportal::API::Internal` (`lib/ecoportal/api/internal/`) -- internal API resources: people,
45
+ person/person_details/person_schema(s), permissions, policy groups, login providers, preferences,
46
+ schema fields, account.
47
+ - `Ecoportal::API::V1` (`lib/ecoportal/api/v1/`) -- v1 REST resources: people, person(s),
48
+ person_schema(s), schema_field(s), and async jobs (`v1/job.rb`, `job/awaiter*`, `job/status.rb`).
49
+ - `Ecoportal::API::Errors` (`lib/ecoportal/api/errors/`) -- shared error base + timeout error
50
+ classes.
51
+
52
+ (Evidence: `CLAUDE.md` "Key Folder Layout"/"Key Concerns", cross-checked against the actual
53
+ `lib/` file listing.)
54
+
55
+ ## How they fit
56
+
57
+ A consumer requires `ecoportal/api`, constructs a `Common::Client` with an API key/host/version,
58
+ and calls into `Internal::*` or `V1::*` resource classes, which use `Common::BaseModel`/
59
+ `Common::BaseClass` for their data shape and go through `Common::Client` for all HTTP traffic
60
+ (retry/throttle/timeout/APM are cross-cutting concerns applied at the client layer, not per
61
+ resource). `Common::Response`/`WrappedResponse` normalise API responses back into Ruby objects for
62
+ the resource classes.
63
+
64
+ ## Entry points
65
+
66
+ - `lib/ecoportal/api.rb` -- the gem root; `require 'dotenv/load'` runs on require, so a `.env` file
67
+ in the consuming process is picked up automatically.
68
+ - `bin/console` -- interactive `pry`/IRB console for local experimentation (`README.md`).
69
+ - `bin/setup` -- installs dependencies for local development.
70
+ - There is no server/CLI/Lambda entry point -- this is a library, not a standalone service
71
+ (no `cdk.json`, `Dockerfile`, `manifest.json`, or `config/application.rb` found at the repo
72
+ root).
73
+
74
+ ## Diagrams
75
+
76
+ Only one diagram category is evidenced in this repo. The CI/CD pipeline graph, the privacy
77
+ data-flow diagram, and the deployment-topology diagram are each OMITTED rather than fabricated,
78
+ per the rule "2 real diagrams beat 4 where two are invented" (here: 1 real diagram beats 4 where 3
79
+ would be invented):
80
+
81
+ - **CI/CD pipeline graph:** omitted -- no `.gitlab-ci.yml`, `Jenkinsfile`, or `.github/workflows/`
82
+ found in this repo (scanner `structure.ci_files` is empty). unknown -- needs owner input on
83
+ whether CI runs from an external/inherited GitLab CI template not present as a file in this
84
+ working tree.
85
+ - **Data-flow (privacy-relevant paths):** omitted -- no scrub/PII-transform module exists in
86
+ `lib/`; the gem's own `.ai-assistance/scripts/scrub.py` is AI-tooling-session scrubbing, not a
87
+ data path this gem's runtime code implements. See `COMPLIANCE.md`.
88
+ - **Deployment topology:** omitted -- no CDK/Terraform/Dockerfile/docker-compose file exists in
89
+ this repo (it is a library gem, not a deployed service).
90
+
91
+ ### Component / layer diagram
92
+
93
+ The client (`Common::Client`) is the single HTTP boundary; everything above it is resource/model
94
+ code that never talks HTTP directly. APM reporting is an optional side-channel off the client,
95
+ gated by `ElasticAPM.running?` and a rescued `StandardError` (fails open to "APM disabled", not to
96
+ a crash) -- see `client/elastic_apm_integration.rb`.
97
+
98
+ ```mermaid
99
+ flowchart TD
100
+ subgraph Consumers["Downstream gems (ecoportal-api-v2, ecoportal-api-graphql, eco-helpers)"]
101
+ C[Consumer code]
102
+ end
103
+ subgraph Resources["Ecoportal::API::Internal / V1 resources"]
104
+ R[Internal::* and V1::* resource classes]
105
+ end
106
+ subgraph CommonLayer["Ecoportal::API::Common"]
107
+ BM[BaseModel / BaseClass]
108
+ CL[Client]
109
+ RT[RateThrottling / Throughput]
110
+ WR[WithRetry]
111
+ TO[TimeOut]
112
+ RESP[Response / WrappedResponse]
113
+ end
114
+ EXT[["ecoPortal API (live.ecoportal.com)"]]
115
+ APM[["Elastic APM (Elastic Cloud, optional)"]]
116
+
117
+ C --> R
118
+ R --> BM
119
+ R --> CL
120
+ CL --> RT
121
+ CL --> WR
122
+ CL --> TO
123
+ CL --> RESP
124
+ CL -->|HTTPS, api_key| EXT
125
+ CL -.->|error telemetry, opt-in| APM
126
+ ```
127
+
128
+ Sources: `lib/ecoportal/api/common/client.rb`, `lib/ecoportal/api/common/client/*.rb`,
129
+ `lib/ecoportal/api/common/base_model.rb`, `lib/ecoportal/api/internal/`, `lib/ecoportal/api/v1/`,
130
+ `CLAUDE.md`.
131
+
132
+ ## Build / test tooling
133
+
134
+ - Build: Bundler/RubyGems (`Rakefile` -- `bundler/gem_tasks`; `bundle exec rake install` /
135
+ `rake release` per `README.md`).
136
+ - Tests: RSpec (`Rakefile` -- `RSpec::Core::RakeTask`; run via `bundle exec rspec` per `CLAUDE.md`
137
+ and `README.md`'s `rake spec`). Lint: RuboCop (`RuboCop::RakeTask`, `.rubocop.yml`). Docs: YARD
138
+ (`YARD::Rake::YardocTask`, `.yardopts`). `rake` (no args) runs RuboCop then RSpec.
139
+ - CI: none found in this repo (scanner `structure.ci_files` is empty; see "Diagrams" above).
140
+
141
+ ## Links to detailed docs
142
+
143
+ None yet -- `.ai-assistance/code/` has no code-specs for this repo (scanner:
144
+ `existing_context.code_specs` is empty). Running the `code-specs` skill would give this section
145
+ real per-area links instead of this note.
@@ -0,0 +1,7 @@
1
+ {"ts": "2026-07-23T10:50:08Z", "file": "docs/self-docs/OVERVIEW.md", "change": "created", "from_hash": null, "to_hash": "a819fa9d200c7ab7"}
2
+ {"ts": "2026-07-23T10:50:08Z", "file": "docs/self-docs/ARCHITECTURE.md", "change": "created", "from_hash": null, "to_hash": "49a7df216c5bdc07"}
3
+ {"ts": "2026-07-23T10:50:08Z", "file": "docs/self-docs/CONVENTIONS.md", "change": "created", "from_hash": null, "to_hash": "f45075d3f3ca043c"}
4
+ {"ts": "2026-07-23T10:50:08Z", "file": "docs/self-docs/INTEGRATIONS.md", "change": "created", "from_hash": null, "to_hash": "b5cb184b9e829040"}
5
+ {"ts": "2026-07-23T10:50:08Z", "file": "docs/self-docs/STATUS.md", "change": "created", "from_hash": null, "to_hash": "147657c19c13c34e"}
6
+ {"ts": "2026-07-23T10:50:08Z", "file": "docs/self-docs/COMPLIANCE.md", "change": "created", "from_hash": null, "to_hash": "802e24afbd20f486"}
7
+ {"ts": "2026-07-23T10:50:08Z", "file": "docs/self-docs/OPERATIONS.md", "change": "created", "from_hash": null, "to_hash": "475c09e0348a9187"}
@@ -0,0 +1,66 @@
1
+ ---
2
+ schema_version: "1.1"
3
+ repo: "ecoportal-api"
4
+ doc: compliance
5
+ last_generated: "2026-07-23"
6
+ source_head: "69bf8e3"
7
+ review_status: draft # audit answers MUST come from 'reviewed' docs only -- a human sets this
8
+ ---
9
+
10
+ # Compliance -- ecoportal-api
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
+ ## ISO 27001
19
+
20
+ | Control area | Status (evidence / planned) | Evidence link |
21
+ |---|---|---|
22
+ | Access control (credential handling) | EVIDENCE (partial) | `api_key` passed at `Client.new` call site, sent as `X-ApiKey` header (`lib/ecoportal/api/common/client.rb`); the key's storage/rotation is the CONSUMER's responsibility, not this gem's -- this gem does not persist credentials |
23
+ | Secure engineering (lint/test gate) | EVIDENCE (partial) | `.rubocop.yml`, RSpec suite in `spec/`, `rake` default task runs RuboCop then RSpec (`Rakefile`) -- but no CI file enforces this automatically pre-merge (see `ARCHITECTURE.md` Diagrams section); PLANNED/unknown whether merge is gated on these passing |
24
+ | Dependency management | EVIDENCE | `ecoportal-api.gemspec` pins runtime deps with version constraints; `Gemfile.lock` exists but is gitignored (`.gitignore`: "it's a gem, ignore the lockfile") |
25
+ | Third-party telemetry vendor review | unknown -- needs owner input | Elastic APM is used (see below); whether it has been through a formal vendor/DPA review is not evidenced in this repo |
26
+ | Incident/error monitoring | EVIDENCE (partial) | `ElasticApmIntegration` reports `UnexpectedServerError`s only, fails open/silent on APM-unavailable (`client/elastic_apm_integration.rb`) |
27
+
28
+ ## Data handled
29
+
30
+ | Data class (none/team/customer/PII) | What / where | Treatment + retention | Exposed? (how) |
31
+ |---|---|---|---|
32
+ | Customer/employee data (potentially PII) | Person records (`Internal::Person`, `V1::Person`, schema fields) -- this gem transports them between the consuming application and the ecoPortal API; it does not persist them itself (no database/store in this repo) | In-memory only within this gem's process lifetime; retention is entirely the CONSUMER application's responsibility, not this gem's | Not exposed by this gem directly -- exposure depends on what the consuming application does with the returned model objects. unknown -- needs owner input on which consumers log/persist full response bodies |
33
+ | APM telemetry | Error class/status metadata sent to Elastic Cloud on unexpected server errors (`transaction_sample_rate: 0.1`) | Elastic Cloud-side retention -- unknown -- needs owner input (governed by ecoPortal's Elastic Cloud account settings, not this repo) | Sent to Elastic Cloud (`https://<account>.apm.<region>.aws.cloud.es.io`), see Vendors below |
34
+
35
+ ## Third-party vendors in the data path
36
+
37
+ | Vendor | What data | Disclosed to end users? | Notes |
38
+ |---|---|---|---|
39
+ | Elastic (Elastic Cloud / Elastic APM) | Error/transaction telemetry, `transaction_sample_rate: 0.1`, only for unexpected server error responses; scoped by `ELASTIC_APM_*` env vars, defaults to `ap-southeast-2` region | unknown -- needs owner input | Optional: disables itself silently (`rescue StandardError`) if unavailable or unconfigured -- does not fail the host application (`elastic_apm_integration.rb`) |
40
+ | RubyGems.org | Gem package (source code, no runtime customer data) at release time | N/A -- public package registry | `rubygems_mfa_required: true` set in gemspec |
41
+
42
+ No other outbound third-party vendor call was found in `lib/` (only `http`, `dotenv`,
43
+ `elastic-apm`, `rate_throttle_client` are runtime dependencies per the gemspec).
44
+
45
+ ## PII / data-leak controls
46
+
47
+ - No PII-scrubbing module exists in this gem's runtime code (`lib/`) -- this gem is a thin HTTP
48
+ client/model layer; it does not implement scrubbing because it is not itself an ingestion/storage
49
+ system. Responsibility for PII handling sits with the consuming application. This is an
50
+ observation of current reality, not a judgement that it should change -- unknown -- needs owner
51
+ input on whether that responsibility split is the intended design or a gap.
52
+ - `.ai-assistance/scripts/scrub.py` exists in this repo, but it scrubs AI-tooling session
53
+ artefacts (per the ecoPortal AI-standards convention), not this gem's runtime data path -- do not
54
+ conflate the two.
55
+ - Credential handling: the `api_key` is passed in by the caller at construction time and is not
56
+ logged by default (`Common::Logging` was not audited line-by-line in this run for redaction
57
+ behaviour) -- unknown -- needs owner input on whether `api_key` can ever land in a log line.
58
+
59
+ ## AI-generated content
60
+
61
+ - Externally-exposed AI content: no -- this is a code library with no output surface that presents
62
+ AI-generated content to end users.
63
+ - Marked as AI-generated: N/A.
64
+ - Review process: N/A. This self-docs set ITSELF is AI-generated (by the `project-self-docs`
65
+ skill) and carries `review_status: draft` until a human reviews it -- see this file's own
66
+ frontmatter.
@@ -0,0 +1,74 @@
1
+ ---
2
+ schema_version: "1.1"
3
+ repo: "ecoportal-api"
4
+ doc: conventions
5
+ last_generated: "2026-07-23"
6
+ source_head: "69bf8e3"
7
+ review_status: draft
8
+ ---
9
+
10
+ # Conventions -- ecoportal-api
11
+
12
+ **Source of truth:** `.rubocop.yml` (root, plus `spec/.rubocop.yml` for the spec directory),
13
+ `.editorconfig` not found, `.solargraph.yml`/`.ruby-lsp/` (editor tooling, not style rules). No
14
+ `ai-discovery` `conventions.md` exists yet in `.ai-assistance/local/` (scanner:
15
+ `existing_context.conventions` is `null`) -- this doc is derived directly from `.rubocop.yml` and
16
+ observed git history instead.
17
+
18
+ ## Code style
19
+
20
+ (From `.rubocop.yml`, root.)
21
+
22
+ - Target Ruby version: 3.2 (`AllCops.TargetRubyVersion: 3.2`; gemspec requires `>= 3.2.2`)
23
+ - `NewCops: enable` -- new RuboCop cops are opted in by default
24
+ - Notable size limits: `Metrics/ClassLength` max 500, `Metrics/ModuleLength` max 300,
25
+ `Metrics/MethodLength` max 50, `Metrics/BlockLength` max 50 (heredoc/array/method_call each
26
+ count as one), `Metrics/AbcSize` max 30, `Metrics/ParameterLists` max 5,
27
+ `Metrics/CyclomaticComplexity`/`Metrics/PerceivedComplexity` max 30
28
+ - `Style/HashSyntax`: `no_mixed_keys`, shorthand allowed either way
29
+ - `Style/ClassAndModuleChildren`: disabled (compact nested-namespace style, e.g.
30
+ `class Ecoportal::API::Foo`, is allowed alongside nested `module`/`class` blocks)
31
+ - Indentation/quotes/line-length: unknown -- needs owner input; not asserted by `.rubocop.yml`'s
32
+ cop list read so far and no `.editorconfig`/`.rubocop.yml` `Layout/*` overrides were inspected
33
+ beyond what is quoted above -- do not infer from a single file without checking further.
34
+
35
+ ## Branch naming
36
+
37
+ Observed from the local + remote branch list (`git branch -a`): `feat/<slug>`, `fix/<slug>`,
38
+ `chore/<slug>`, `auto/<slug>` prefixes (e.g. `feat/deploy-project-self-docs`,
39
+ `fix/migration-0013-scrub-settings-hygiene`, `chore/ai-standards-alignment-1.9.1`,
40
+ `auto/claude-md-startup-reads`). This matches the `type/slug` convention used across the
41
+ ecoPortal AI-tooling fleet (see `ep-ai-standards` branching model). Numeric-only branches also
42
+ exist from pre-AI-tooling history (e.g. `remotes/origin/19-ecoportal-api-common-basemodel-...`) --
43
+ treat those as legacy, not the current convention.
44
+
45
+ ## Commit message style
46
+
47
+ Observed from `git log --oneline -20`: `type: message` or `type(scope): message`, e.g.
48
+ `fix: migration 0013 -- canonical scrub.py + kit refresh + settings hygiene`,
49
+ `chore(ai): confirm alignment with ep-ai-standards v1.9.1`,
50
+ `feat(ai): deploy project-self-docs skill (self-documentation, run in-repo)`. Feature-branch work
51
+ merges via GitLab merge commits (`Merge branch '<branch>' into 'master'`).
52
+
53
+ ## Test conventions
54
+
55
+ - Runner: RSpec (`bundle exec rspec`, `.rspec` config file, `.rspec_status` present for
56
+ failure-tracking -- gitignored per `.gitignore`).
57
+ - Placement: `spec/` mirrors the `lib/` namespace (e.g. `lib/ecoportal/api/internal/person.rb` ->
58
+ `spec/ecoportal/api/internal/person_*_spec.rb`); fixture JSON files sit alongside their specs
59
+ (e.g. `spec/ecoportal/api/internal/person_employee.json`).
60
+ - Coverage expectations: unknown -- needs owner input; no coverage tool/threshold config
61
+ (e.g. SimpleCov) was found in the files inspected.
62
+ - `.rubocop.yml` in `spec/` scopes lint rules separately for specs (file present but not read in
63
+ detail here -- unknown -- needs owner input on spec-specific cop overrides).
64
+
65
+ ## Project-specific rules
66
+
67
+ From `CLAUDE.md`:
68
+
69
+ - Changes to `Ecoportal::API::Common::*` are treated as a public API -- they propagate to
70
+ `ecoportal-api-v2`, `ecoportal-api-graphql`, and `eco-helpers`; any breaking change must be
71
+ coordinated across that chain, not made unilaterally in this repo.
72
+ - Cross-cutting architecture/dependency context for the wider gem chain lives in
73
+ `ecoportal-api-graphql`'s `CLAUDE.md` and `.claude/` folder, not duplicated here.
74
+ - `docs/worklog.md` is the required first read every AI session (session handoff log).