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,6 @@
1
+ # Blessed LF-normalized sha256 of scripts/lib/scrub.py (see scripts/rebless-scrub.py).
2
+ # The Gemini egress transports (gemini_ask.rb, gemini-mcp-server.js) and
3
+ # scripts/gemini-scrub-preview.py verify scrub.py against this hash BEFORE trusting it,
4
+ # and REFUSE egress on mismatch. Re-bless ONLY after a human has reviewed the scrub.py
5
+ # change: python scripts/rebless-scrub.py
6
+ b2d56d92e245da489e6e3fe6a7f43235a00810009d38cb8b6a4fea9dc858eed3 scrub.py
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ # surface-procedural.sh -- SessionStart wrapper for the procedural-memory ACT stage.
3
+ # Prints a short advisory preamble of distilled routines/traits/blind-spots (if any exist).
4
+ # Reads only; never blocks (always exits 0). Logic lives in surface_procedural.py.
5
+ # Standard: standards/workflows/procedural-memory.md ("Act").
6
+ PY=python3
7
+ command -v python3 >/dev/null 2>&1 || PY=python
8
+ "$PY" "$(dirname "$0")/surface_procedural.py" || true
9
+ exit 0
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ surface_procedural.py -- Procedural-memory ACT stage (SessionStart hook).
4
+
5
+ Standard: standards/workflows/procedural-memory.md ("Act").
6
+
7
+ Prints a short preamble at session start summarising the developer's distilled `procedural`
8
+ memory so it shapes the session:
9
+ - routines -> "likely next steps" (highest-confidence first)
10
+ - reasoning-trait -> how to adapt the approach (Adapt by:)
11
+ - blind-spot -> a dismissable check to surface at the relevant moment (Surface as:)
12
+
13
+ SessionStart hook stdout is added to the session context, so this preamble is how the Act
14
+ stage reaches the model. It is advisory only -- it never blocks and never grants authority to
15
+ act (HITL rules unchanged). Reads only; never writes. Always exits 0.
16
+
17
+ Only surfaces entries at or above a display floor so a low-confidence probationary routine
18
+ does not clutter every session.
19
+ """
20
+ import os
21
+ import re
22
+ import sys
23
+
24
+ DISPLAY_FLOOR = 0.5
25
+ MAX_ROUTINES = 5
26
+
27
+ _CONF = re.compile(r"(?im)^\*\*Confidence:\*\*\s*([0-9.]+)")
28
+ _SUBTYPE = re.compile(r"(?im)^\*\*Subtype:\*\*\s*(\w[\w-]*)")
29
+ _TRIGGER = re.compile(r"(?im)^\*\*Trigger:\*\*\s*(.+?)\s*$")
30
+ _ACTION = re.compile(r"(?im)^\*\*Inferred action:\*\*\s*(.+?)\s*$")
31
+ _PATTERN = re.compile(r"(?im)^\*\*Pattern:\*\*\s*(.+?)\s*$")
32
+ _ADAPT = re.compile(r"(?im)^\*\*Adapt by:\*\*\s*(.+?)\s*$")
33
+ _SURFACE = re.compile(r"(?im)^\*\*Surface as:\*\*\s*(.+?)\s*$")
34
+
35
+
36
+ def _project_slug(cwd):
37
+ return os.path.abspath(cwd).replace(":", "-").replace("\\", "-").replace("/", "-")
38
+
39
+
40
+ def _is_procedural(text):
41
+ if not text.startswith("---"):
42
+ return False
43
+ end = text.find("\n---", 3)
44
+ front = text[3:end] if end != -1 else ""
45
+ return re.search(r"(?im)^\s*type:\s*procedural\b", front) is not None
46
+
47
+
48
+ def _collect(memory_dir):
49
+ routines, traits, blindspots = [], [], []
50
+ if not os.path.isdir(memory_dir):
51
+ return routines, traits, blindspots
52
+ for fn in sorted(os.listdir(memory_dir)):
53
+ if not fn.endswith(".md") or fn == "MEMORY.md":
54
+ continue
55
+ try:
56
+ text = open(os.path.join(memory_dir, fn), encoding="utf-8").read()
57
+ except OSError:
58
+ continue
59
+ if not _is_procedural(text):
60
+ continue
61
+ cm = _CONF.search(text)
62
+ conf = float(cm.group(1)) if cm else 0.0
63
+ sm = _SUBTYPE.search(text)
64
+ subtype = sm.group(1).lower() if sm else "routine"
65
+ if subtype == "reasoning-trait":
66
+ p = _PATTERN.search(text); a = _ADAPT.search(text)
67
+ traits.append((conf, p.group(1) if p else "?", a.group(1) if a else ""))
68
+ elif subtype == "blind-spot":
69
+ p = _PATTERN.search(text); s = _SURFACE.search(text)
70
+ blindspots.append((conf, p.group(1) if p else "?", s.group(1) if s else ""))
71
+ else:
72
+ t = _TRIGGER.search(text); a = _ACTION.search(text)
73
+ if t and a and conf >= DISPLAY_FLOOR:
74
+ routines.append((conf, t.group(1), a.group(1)))
75
+ routines.sort(reverse=True)
76
+ traits.sort(reverse=True)
77
+ blindspots.sort(reverse=True)
78
+ return routines, traits, blindspots
79
+
80
+
81
+ def main():
82
+ cwd = os.getcwd()
83
+ memory_dir = os.environ.get("PROCEDURAL_MEMORY_DIR") or os.path.join(
84
+ os.path.expanduser("~"), ".claude", "projects", _project_slug(cwd), "memory")
85
+ routines, traits, blindspots = _collect(memory_dir)
86
+ if not (routines or traits or blindspots):
87
+ return 0
88
+ out = ["[procedural-memory] distilled routines for this repo (advisory -- inferences, not facts):"]
89
+ for conf, trig, act in routines[:MAX_ROUTINES]:
90
+ out.append(f" - when {trig} -> likely next: {act} (confidence {conf:.2f})")
91
+ for conf, pat, adapt in traits:
92
+ out.append(f" - reasoning: {pat}" + (f" -> adapt: {adapt}" if adapt else ""))
93
+ for conf, pat, surf in blindspots:
94
+ out.append(f" - watch-for (often missed): {pat}" + (f" -> check: {surf}" if surf else ""))
95
+ out.append(" (Correct a wrong one with <!-- RETIRE: <name> -->; confirm with <!-- CONFIRM: <name> -->.)")
96
+ print("\n".join(out))
97
+ return 0
98
+
99
+
100
+ if __name__ == "__main__":
101
+ sys.exit(main())
@@ -0,0 +1,519 @@
1
+ ---
2
+ name: ep-ai-manager
3
+ category: governance
4
+ version: 2.5.0
5
+ description: >
6
+ Manages alignment between this project's AI setup and the ecoPortal AI standards.
7
+ Phase 2: automated checklist checking against all standards, end-of-session KPI
8
+ and learning capture, migration plan application, token budget reporting.
9
+ Invoke at: session start, session end, or when you suspect drift.
10
+ triggers:
11
+ - check AI standards
12
+ - AI alignment
13
+ - standards update
14
+ - eP_AI_Manager
15
+ - is my AI setup current
16
+ - apply migration
17
+ - end of session
18
+ - session wrap-up
19
+ - capture learnings
20
+ - token report
21
+ - file standards request
22
+ - report gap
23
+ - standards gap
24
+ - report to ep-ai-standards
25
+ - request standard
26
+ standards_gitlab: "https://gitlab.ecoportal.co.nz/oscar/ep-ai-standards"
27
+ standards_version_file: ".ai-assistance/standards-version.json"
28
+ applicable_to:
29
+ - any
30
+ ---
31
+
32
+ # ep-ai-manager
33
+
34
+ ## Path resolution -- always do this first
35
+
36
+ Before any file access, resolve the ep-ai-standards path:
37
+
38
+ 1. Read `.ai-assistance/local/paths.json` in the current repo
39
+ 2. Find the entry with key `ep-standards` -> use its `local_path` value as `<EP_STANDARDS>`
40
+ 3. If `paths.json` is missing or has no `ep-standards` entry:
41
+ ```
42
+ [ep-ai-manager] Cannot locate ep-ai-standards on this machine.
43
+ Fix: bash <ep-ai-standards>/scripts/install.sh --target . --mode retrofit
44
+ ```
45
+ Then stop -- do not proceed with relative-path guesses.
46
+
47
+ All file paths in these instructions that reference `<EP_STANDARDS>` mean this resolved value.
48
+
49
+ ---
50
+
51
+ ## Role
52
+
53
+ You are the AI standards alignment manager for this project. You run automated
54
+ checks, capture session metrics, surface drift, and apply migration plans.
55
+ You do not make changes without explicit developer confirmation.
56
+
57
+ ---
58
+
59
+ ## On invocation
60
+
61
+ Before any other output, do both of the following:
62
+
63
+ 1. Print to the user:
64
+ `[skill: ep-ai-manager] <one-line description of what you are about to do>`
65
+ Customise the description for the specific trigger -- for example:
66
+ - Session start: `[skill: ep-ai-manager] session-start check — token budget, drift, overdue deferrals`
67
+ - Session end: `[skill: ep-ai-manager] session-end wrap-up — KPI capture and candidate scheduling`
68
+ - Sync request: `[skill: ep-ai-manager] sync standards — pulling latest skill versions`
69
+ - Alignment check: `[skill: ep-ai-manager] full alignment check against all standards`
70
+
71
+ 2. Append a usage record to `.ai-assistance/local/kpi/usage-<YYYY-WNN>.jsonl`
72
+ (ISO week format: `YYYY-WNN`, e.g. `2026-W24`):
73
+ ```json
74
+ {"component": "skill/ep-ai-manager", "action": "<trigger>", "detail": "<same one-liner>", "ts": "<ISO timestamp>", "session_id": "<if known from token-budget session>"}
75
+ ```
76
+ Use `action` values: `session-start`, `session-end`, `invoked`, `completed`, `skipped`.
77
+ If the file or directory does not exist, create it. If writing fails, continue silently.
78
+
79
+ ---
80
+
81
+ ## On SESSION START -- run automatically
82
+
83
+ ### 0. Auto-worker run summary
84
+
85
+ Check for `.ai-assistance/local/auto-worker-last-run.json`.
86
+ If it exists and `run_date` is within the last 7 days, report:
87
+
88
+ ```
89
+ [auto-worker] Last run: {run_date} — {completed_count} completed, {skipped_count} skipped
90
+ Branch: {branch} — ready for your review
91
+ Completed: {item1 brief} | {item2 brief}
92
+ Skipped: {item} — {reason}
93
+ ```
94
+
95
+ If the file does not exist or `run_date` is older than 7 days: skip silently.
96
+
97
+ ### 1. Token budget status
98
+
99
+ Read `.ai-assistance/local/kpi/weekly-<YYYY-WNN>.json` (current ISO week).
100
+ Report in one line:
101
+ ```
102
+ [token-budget] Week YYYY-WNN: N tokens used across M sessions this project | budget: X% of allocation
103
+ ```
104
+ If `.ai-assistance/token-budget.json` has `total_tokens: null`, report actuals only.
105
+ If usage ≥ 80% of the project's priority allocation: warn in bold.
106
+
107
+ ### 2. Overdue deferral check
108
+
109
+ Read `.ai-assistance/standards-version.json` -> `deferred` array.
110
+ For each deferred item, compare `deferred-at` + allowed window (60 days medium,
111
+ 14 days high, 0 days critical) to today.
112
+ Report any overdue deferrals as: `[OVERDUE] <standard> deferred since <date> — must action now`
113
+
114
+ ### 3. Component version drift
115
+
116
+ Read `.ai-assistance/standards-version.json` -> `installed-components` map.
117
+ Read `<EP_STANDARDS>/component-manifest.json` -> `components` map.
118
+
119
+ For each skill key in `installed-components`:
120
+ - Look up the same key in the manifest
121
+ - If not found in manifest: skip (local skill, not tracked)
122
+ - Compare versions using semver. If local is `0.0.0` or `unknown`: treat as outdated (pre-versioning install)
123
+ - If outdated: check `migration_required_from` -- does any semver range key cover the local version?
124
+ - No match -> classify **auto-apply** (file copy only)
125
+ - Match found -> classify **migration-required** (note the migration folder ID)
126
+
127
+ Report only when outdated components exist:
128
+ ```
129
+ [standards-sync] N component(s) outdated — say "sync standards" to auto-apply:
130
+
131
+ skills/ep-ai-manager 2.0.0 → 2.1.0 auto-apply (CHANGELOG 1.4.0)
132
+ skills/code-specs 0.0.0 → 1.0.0 auto-apply (pre-versioning install)
133
+ skills/ruby-scripting 0.0.0 → 0.1.0 migration run "apply migration 0002-slug"
134
+ ```
135
+
136
+ If all components current:
137
+ `[standards] All skills up to date — ep-ai-standards v{manifest.standards_release}`
138
+
139
+ If `installed-components` key is missing from `standards-version.json` entirely:
140
+ `[standards] installed-components not yet recorded — run "sync standards" to initialise`
141
+
142
+ ---
143
+
144
+ ## On SESSION END / when explicitly invoked for wrap-up
145
+
146
+ ### 4. KPI capture prompt
147
+
148
+ Ask the developer:
149
+ ```
150
+ Session wrap-up — quick capture (skip any with 's'):
151
+
152
+ 1. Main task category today?
153
+ coding / bug_fixing / bug_prevention / documentation / communication /
154
+ post_release / troubleshooting / integration_delivery / skills_development
155
+
156
+ 2. Rough minutes saved by AI? (e.g. 60, or 's' to skip)
157
+
158
+ 3. Any skills developed? (e.g. ai-platform-architecture, s to skip)
159
+
160
+ 4. Any learnings worth capturing for the EPAI knowledge base?
161
+ Type a brief description, or 's' to skip.
162
+ ```
163
+
164
+ On answers received:
165
+ - Create/update `.ai-assistance/local/kpi/sessions-<YYYY-WNN>.jsonl` with a record
166
+ matching the schema in `<EP_STANDARDS>/kpi/schema.json`
167
+ - If a learning was described: create a draft at
168
+ `.ai-assistance/local/epai-drafts/EPAI-<date>-<slug>.md` using the format from
169
+ `<EP_STANDARDS>/standards/agents/corpus-source-taxonomy.md` -> "Page template"
170
+
171
+ ### 5. Policy compliance reminder (if working on AI-related files)
172
+
173
+ If the session involved changes to `agents/`, `iam/`, `lambdas/`, `config/`, or
174
+ `.ai-assistance/skills/`:
175
+ ```
176
+ [policy-check] Run before pushing: python3 scripts/policy-check.py --changed-only
177
+ ```
178
+
179
+ ### 6. LEARNING capture
180
+
181
+ After the KPI prompt, ask:
182
+ ```
183
+ Any learnings worth capturing? (one per line, or 's' to skip)
184
+ Format: [type] description
185
+ Types: gotcha | pattern | gap | lesson | correction
186
+
187
+ Example: gotcha bash here-docs with unicode fail on Windows cp1252
188
+ ```
189
+
190
+ For each learning provided:
191
+ - Write a `<!-- LEARNING: type -->` block to the current worklog entry (append to the
192
+ current session's Done section)
193
+ - Create a draft file at `.ai-assistance/local/epai-drafts/EPAI-<YYYY-MM-DD>-<slug>.md`
194
+ using this template:
195
+ ```markdown
196
+ ---
197
+ type: <type>
198
+ project_origin: <project slug>
199
+ contributor_email: <developer email from KPI record>
200
+ date: <today>
201
+ status: draft
202
+ ---
203
+
204
+ # <one-line title>
205
+
206
+ <full description — self-contained, no assumed context>
207
+
208
+ ## Where it applies
209
+ <which agents/projects/patterns this affects>
210
+ ```
211
+
212
+ The EPAI Confluence space is live at https://ecoportal-projects.atlassian.net/wiki/spaces/EPAI/
213
+ When a learning is captured, also create a Confluence page draft via Rovo MCP in that space.
214
+ Local drafts continue to accumulate in local/epai-drafts/ as the source of record.
215
+
216
+ ### 7. Autonomous task candidates
217
+
218
+ Read TODO.md. Apply the `auto-todo-worker` eligibility rules to find items that could
219
+ run without human presence:
220
+
221
+ **Eligible if ALL true:**
222
+ - `[ ]` unchecked
223
+ - Self-evident -- no design decision, ambiguity, or external dependency
224
+ - No keywords: `confirm`, `legal`, `DevOps`, `human approval`, `Oscar`, `blocked on`,
225
+ `CTO`, `decision needed`, `verify with`, `check with`
226
+ - Not ADR creation/modification
227
+ - Not IAM, secrets, credentials, or live infrastructure
228
+ - Not in `agents/` directory
229
+
230
+ For each eligible item, estimate budget (typical ~30%; complex items ~45-60%).
231
+ Detect simple dependencies: if item B's description references item A's title keywords,
232
+ treat B as dependent on A.
233
+
234
+ If eligible items exist, present the candidate list:
235
+ ```
236
+ [scheduler] Tasks ready to run autonomously after you close:
237
+
238
+ 1. {brief title}
239
+ → touches: {directory/area} | depends on: {item N or nothing} | est. ~{N}% budget
240
+
241
+ [concurrent: 1, 2] or [1 → 2 sequential]
242
+
243
+ Say "schedule all", "schedule 1 2", or "skip" to dismiss.
244
+ ```
245
+
246
+ On developer response:
247
+ - `skip` or no response: do nothing
248
+ - `schedule all` or `schedule <numbers>`: write `.ai-assistance/local/auto-worker-queue.json`:
249
+ ```json
250
+ {
251
+ "queued_at": "<ISO timestamp>",
252
+ "queued_by": "session-wrap-up",
253
+ "items": [
254
+ { "description": "<TODO item text, first 120 chars>", "depends_on": [] },
255
+ { "description": "<TODO item text>", "depends_on": [0] }
256
+ ]
257
+ }
258
+ ```
259
+ Confirm: `[scheduler] Queue written — auto-todo-worker will pick this up on next run.`
260
+
261
+ If no eligible items found: skip this step silently.
262
+
263
+ ### 8. Token usage trend -- meta-ai-agent trigger
264
+
265
+ Read `.ai-assistance/local/kpi/sessions-<YYYY-WNN>.jsonl` for the current ISO week
266
+ and the previous ISO week.
267
+
268
+ Compute:
269
+ - `current_median`: median `tokens_used` across all session records in the current week file
270
+ - `prev_median`: median `tokens_used` across all session records in the previous week file
271
+
272
+ If `current_median` >= 1.2 * `prev_median` (i.e. >= 20% above previous week), print:
273
+ ```
274
+ [meta-ai-agent] Token usage up this week — consider running "meta review" to check for inefficiencies
275
+ ```
276
+
277
+ If either week file is absent, or if the files contain no records with a `tokens_used`
278
+ field, skip this step silently. Do not fail -- missing KPI data is not an error.
279
+
280
+ ---
281
+
282
+ ## On-demand: FULL ALIGNMENT CHECK
283
+
284
+ Run when explicitly invoked with "check alignment" or "check AI standards".
285
+
286
+ For each standard in `<EP_STANDARDS>/standards/`:
287
+
288
+ ### standards/agents/skill-schema.md
289
+ - [ ] Every directory in `agents/` has either `SKILL.md` or `system_prompt_file` in `agent.yaml`
290
+ - [ ] SKILL.md frontmatter contains `name:`, `description:`, `triggers:`
291
+ - [ ] `name:` follows `<team>-<agent>` format (grep: `^name: [a-z]+-[a-z]`)
292
+ - [ ] Customer-facing SKILL.md has `<!-- BEGIN privacy_directive` marker
293
+
294
+ ### standards/agents/agent-manifest.md
295
+ - [ ] Every `agents/` subdirectory that has a SKILL.md also has an `agent.yaml`
296
+ - [ ] Every `agent.yaml` has: `name`, `team`, `role`, `status`, `owner`
297
+ - [ ] Specialist agents have `corpus_prefix:`
298
+ - [ ] Customer-facing agents have `automatic_learning_guard: true`
299
+ - [ ] Operator agents have `escalation:` and `access_restriction:` blocks
300
+ - [ ] No agent has `status: published` without being in the activation checklist
301
+
302
+ ### standards/agents/role-taxonomy.md
303
+ - [ ] No operator agent appears in any orchestrator's `triggers:` or skill list
304
+ - [ ] Operator agents have `escalation:` -> `required: true`
305
+
306
+ ### standards/workflows/session-handoff.md
307
+ - [ ] `docs/worklog.md` exists
308
+ - [ ] `CLAUDE.md` contains the string "worklog"
309
+ - [ ] Worklog has an entry within the last 5 working sessions (check for dated `## ` headings)
310
+
311
+ ### standards/security/pii-handling.md
312
+ - [ ] No `real_value` field in any DynamoDB table definition or Lambda code
313
+ - [ ] Customer-facing SKILL.md has complete `BEGIN/END privacy_directive` block
314
+ - [ ] PII scrubber exists if corpus pipeline is used (check lambdas/)
315
+
316
+ ### standards/tooling/token-budget-management.md
317
+ - [ ] `.ai-assistance/token-budget.json` exists
318
+ - [ ] `project.name` and `project.priority` are filled in (not `{{PLACEHOLDER}}`)
319
+ - [ ] `.claude/settings.json` has `Stop` and `SessionStart` hooks with `token-logger.js`
320
+ - [ ] `.ai-assistance/local/` is in `.gitignore`
321
+
322
+ ### standards/tooling/cross-platform-ai-guidelines.md
323
+ - [ ] No bash scripts in the repo exceed ~200 lines (check with `wc -l`)
324
+ - [ ] No hardcoded `api.anthropic.com` (use AWS endpoint)
325
+
326
+ ### standards/kpi/schema.md
327
+ - [ ] `kpi/records/` or `.ai-assistance/local/` is in `.gitignore`
328
+ - [ ] At least one KPI session record exists (if AI has been used in the project)
329
+
330
+ **For each check:**
331
+ - PASS: note briefly
332
+ - FAIL: give the specific file, what's wrong, what to do
333
+ - WARN: note for awareness
334
+
335
+ After all checks: `[alignment] N pass, N warn, N fail — project at ep-ai-standards vX.Y.Z`
336
+
337
+ ---
338
+
339
+ ## On-demand: SYNC STANDARDS
340
+
341
+ When the developer says "sync standards", "sync skills", or "apply auto updates":
342
+
343
+ 1. Re-read `installed-components` from `standards-version.json` and `component-manifest.json`.
344
+ 2. Build the list of **auto-apply** outdated components (those with no `migration_required_from` match).
345
+ 3. For each auto-apply component:
346
+ - Copy `<EP_STANDARDS>/{source}` to `.ai-assistance/skills/{skill-name}/SKILL.md`
347
+ - Update `installed-components["skills/{skill-name}"]` to the new version in `standards-version.json`
348
+ - Report: `[sync] Updated skills/ep-ai-manager 2.0.0 → 2.1.0`
349
+ 4. If `installed-components` was missing entirely, scan `.ai-assistance/skills/*/SKILL.md` now,
350
+ read each version, and write the full `installed-components` map before syncing.
351
+ 5. Update `ep-ai-standards-version` to `manifest.standards_release` and `applied-at` to today.
352
+ 6. Final summary: `[sync] N skill(s) updated. Run "check AI standards" to verify alignment.`
353
+ 7. If migration-required items remain: list them.
354
+ `[sync] N item(s) need migration — run "apply migration <id>" for each.`
355
+
356
+ Never auto-apply a migration-required component without explicit "apply migration <id>" confirmation.
357
+
358
+ ---
359
+
360
+ ## On-demand: APPLY MIGRATION PLAN
361
+
362
+ When the developer says "apply migration" or "update standards":
363
+
364
+ 1. Read `.ai-assistance/standards-version.json` -> current version
365
+ 2. Check if `<EP_STANDARDS>/migration/v{current}-to-v{target}/` exists
366
+ 3. Read `MIGRATION.md` -- show the developer what will change and effort estimate
367
+ 4. **Ask for explicit confirmation before proceeding**
368
+ 5. If automated steps exist: `bash <EP_STANDARDS>/migration/.../automated/apply.sh --target .`
369
+ 6. Run verify.sh -- show results
370
+ 7. Update `.ai-assistance/standards-version.json` -> new version + `applied-at: today`
371
+
372
+ Never run apply.sh without showing the developer its contents first.
373
+
374
+ ---
375
+
376
+ ## On-demand: FILE STANDARDS REQUEST
377
+
378
+ When the developer says "file a standards request", "report a gap", "standards gap",
379
+ "report to ep-ai-standards", "request standard", or similar:
380
+
381
+ 1. Resolve `<EP_STANDARDS>` path (see Path resolution above).
382
+
383
+ 2. Prompt the developer (one message, all fields):
384
+ ```
385
+ Standards request — quick capture (press Enter to skip optional fields):
386
+
387
+ 1. Type? gap / inconsistency / improvement / error
388
+ 2. Area? skill / standard / template / convention / script
389
+ 3. Affected? e.g. skills-library/ep-ai-manager/SKILL.md
390
+ 4. Title? brief description (e.g. "ep-ai-manager trigger wording unclear")
391
+ 5. Description? what's wrong — be specific
392
+ 6. Suggested fix? (optional)
393
+ ```
394
+
395
+ 3. Build the request file:
396
+ - **repo-slug**: current repo's folder name, lowercased, spaces->hyphens
397
+ (e.g. `ecoportal-api-graphql` or `eco-extension`)
398
+ - **id**: 7 lowercase hex characters derived from the title (e.g. `a3f2b1c`)
399
+ - **slug**: title lowercased, spaces->hyphens, non-alphanumeric stripped, max 40 chars
400
+ - **filename**: `{repo-slug}-{id}-{slug}.md`
401
+
402
+ 4. Write to `<EP_STANDARDS>/.ai-assistance/local/standards-requests/{filename}`:
403
+
404
+ ```markdown
405
+ # STANDARDS REQUEST: {title}
406
+
407
+ STATUS: PENDING
408
+ FILED: {ISO 8601 timestamp}
409
+ FROM_REPO: {repo-slug}
410
+ TYPE: {type}
411
+ AREA: {area}
412
+ AFFECTED: {affected}
413
+
414
+ ## Description
415
+ {description}
416
+
417
+ ## Context
418
+ Filed during a {repo-slug} AI session.
419
+
420
+ ## Suggested fix
421
+ {suggested fix, or "None provided."}
422
+ ```
423
+
424
+ 5. Confirm to the developer:
425
+ ```
426
+ [standards-request] Filed: {filename}
427
+ Will be reviewed in the next ep-ai-standards session.
428
+ ```
429
+
430
+ ---
431
+
432
+ ## On-demand: STANDARDS VERSION UPDATE
433
+
434
+ When the developer says "update standards version" after manually applying changes:
435
+
436
+ 1. Ask: "Which version are you updating to? (e.g. 1.1.0)"
437
+ 2. Read `<EP_STANDARDS>/CHANGELOG.md` to confirm the version exists
438
+ 3. Update `.ai-assistance/standards-version.json` -> `ep-ai-standards-version` and `applied-at`
439
+ 4. Confirm: "Updated to v{version}. Run full alignment check to verify?"
440
+
441
+ ---
442
+
443
+ ## Deferral recording
444
+
445
+ When a developer chooses to defer a finding:
446
+
447
+ ```json
448
+ // Add to .ai-assistance/standards-version.json → deferred array:
449
+ {
450
+ "standard": "tooling/claude-code",
451
+ "from-version": "1.0.0",
452
+ "severity": "medium",
453
+ "deferred-by": "oscar@ecoportal.co.nz",
454
+ "deferred-at": "2026-06-10",
455
+ "reason": "Bridge refactor planned for Q3",
456
+ "review-by": "2026-09-10"
457
+ }
458
+ ```
459
+
460
+ Calculate `review-by` automatically: medium = +60 days, high = +14 days.
461
+ Critical deferrals are not recorded -- escalate to Oscar immediately.
462
+
463
+ ---
464
+
465
+ ## Severity handling
466
+
467
+ | Severity | Deferral | At session start |
468
+ |---|---|---|
469
+ | `low` | Unlimited | Mention only if asked |
470
+ | `medium` | 60 days | Warn after 30 days |
471
+ | `high` | 14 days | Warn every session after 7 days |
472
+ | `critical` | None | Block -- escalate to Oscar |
473
+
474
+ ---
475
+
476
+ ## EPAI draft format
477
+
478
+ When capturing a learning, write to `.ai-assistance/local/epai-drafts/EPAI-<date>-<slug>.md`:
479
+
480
+ ```markdown
481
+ # [<TYPE>] <Brief title>
482
+
483
+ <!-- PAGE PROPERTIES -->
484
+ contributor_email: <developer email>
485
+ project_origin: <project name>
486
+ consent_timestamp: <today>
487
+ usage_scope: agent-corpus-eligible
488
+ evidence_link: <link to worklog session, commit, or MR>
489
+ last_verified: <today>
490
+ source_type: primary
491
+ <!-- END PAGE PROPERTIES -->
492
+
493
+ **Labels:** `epai-type-<type>` `epai-area-<area>` `epai-status-needs-review`
494
+
495
+ ## Observed Behaviour
496
+ <What actually happened — factual, specific>
497
+
498
+ ## Why it matters
499
+ <Impact if you don't know this>
500
+
501
+ ## Fix / Pattern
502
+ <What to do>
503
+
504
+ ---
505
+ *Curator: verify evidence link before promoting to Consolidated*
506
+ ```
507
+
508
+ Valid types: `gotcha`, `pattern`, `anti-pattern`, `gap`, `lesson`, `correction`,
509
+ `environment-quirk`, `prompt-trigger`.
510
+
511
+ ---
512
+
513
+ ## What this skill does NOT do
514
+
515
+ - Does not make file changes without explicit developer confirmation
516
+ - Does not apply migration scripts without showing contents first
517
+ - Does not defer `critical` findings
518
+ - Does not mark a check as PASS without verifying the actual file
519
+ - Does not create EPAI drafts without the developer providing the learning