ecoportal-api-graphql 1.3.10 → 1.3.12

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/.ai-assistance/code/diff_pairing_engine.md +243 -0
  3. data/.ai-assistance/code/filter_contract_matrix.md +177 -0
  4. data/.ai-assistance/code/graphql_domain_knowledge.md +20 -10
  5. data/.ai-assistance/code/template_diff_pairing_domain.md +175 -0
  6. data/.ai-assistance/code/workflow-command-guide.md +28 -0
  7. data/.ai-assistance/projects/ooze-graphql-native-migration/INVENTORY.md +136 -0
  8. data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +6 -1
  9. data/.ai-assistance/projects/qa-services-delivery/DECISIONS.md +93 -0
  10. data/.ai-assistance/projects/qa-services-delivery/INTENT.md +76 -0
  11. data/.ai-assistance/projects/qa-services-delivery/PHASE3-SCOPE.md +115 -0
  12. data/.ai-assistance/projects/qa-services-delivery/ROADMAP.md +99 -0
  13. data/.ai-assistance/projects/qa-services-delivery/TODO.md +81 -0
  14. data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +87 -0
  15. data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +108 -0
  16. data/.ai-assistance/projects/template-diff-deploy/INTENT.md +12 -0
  17. data/.ai-assistance/projects/template-diff-deploy/TODO.md +9 -0
  18. data/.ai-assistance/projects/template-maintenance/PHASE0-FINDINGS.md +93 -0
  19. data/.ai-assistance/projects/template-maintenance/README.md +14 -0
  20. data/.ai-assistance/skills/procedural-memory/SKILL.md +319 -0
  21. data/.ai-assistance/standards-version.json +21 -20
  22. data/CHANGELOG.md +119 -0
  23. data/CLAUDE.md +10 -0
  24. data/docs/self-docs/ARCHITECTURE.md +88 -0
  25. data/docs/self-docs/CHANGES.jsonl +7 -0
  26. data/docs/self-docs/CONVENTIONS.md +74 -0
  27. data/docs/self-docs/INTEGRATIONS.md +63 -0
  28. data/docs/self-docs/OVERVIEW.md +51 -0
  29. data/docs/self-docs/STATUS.md +69 -0
  30. data/docs/self-docs/self-docs-index.json +39 -0
  31. data/docs/worklog.md +256 -0
  32. data/ecoportal-api-graphql.gemspec +1 -1
  33. data/lib/ecoportal/api/common/graphql/model/diffable.rb +54 -54
  34. data/lib/ecoportal/api/graphql/base/action.rb +43 -43
  35. data/lib/ecoportal/api/graphql/base/contractor_entity/member_changes.rb +67 -67
  36. data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +7 -0
  37. data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -28
  38. data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -25
  39. data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -24
  40. data/lib/ecoportal/api/graphql/base/page/data_field.rb +1 -1
  41. data/lib/ecoportal/api/graphql/base/page/section_collection.rb +85 -79
  42. data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -17
  43. data/lib/ecoportal/api/graphql/base/register.rb +18 -18
  44. data/lib/ecoportal/api/graphql/builder/template_builder.rb +174 -0
  45. data/lib/ecoportal/api/graphql/builder.rb +17 -16
  46. data/lib/ecoportal/api/graphql/compat/filter_translator.rb +63 -28
  47. data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -51
  48. data/lib/ecoportal/api/graphql/concerns.rb +14 -14
  49. data/lib/ecoportal/api/graphql/diff/change.rb +59 -0
  50. data/lib/ecoportal/api/graphql/diff/command_synthesizer.rb +329 -0
  51. data/lib/ecoportal/api/graphql/diff/cross_object_diff.rb +165 -0
  52. data/lib/ecoportal/api/graphql/diff/deploy.rb +121 -0
  53. data/lib/ecoportal/api/graphql/diff/id_resolver.rb +64 -0
  54. data/lib/ecoportal/api/graphql/diff/pairing/candidate.rb +32 -0
  55. data/lib/ecoportal/api/graphql/diff/pairing/engine.rb +173 -0
  56. data/lib/ecoportal/api/graphql/diff/pairing/ledger.rb +119 -0
  57. data/lib/ecoportal/api/graphql/diff/pairing/signals.rb +104 -0
  58. data/lib/ecoportal/api/graphql/diff/strategy.rb +113 -0
  59. data/lib/ecoportal/api/graphql/diff/version_diff.rb +332 -0
  60. data/lib/ecoportal/api/graphql/diff.rb +34 -0
  61. data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -181
  62. data/lib/ecoportal/api/graphql/fragment/page.rb +85 -85
  63. data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +1 -0
  64. data/lib/ecoportal/api/graphql/input/action/update.rb +14 -14
  65. data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +41 -41
  66. data/lib/ecoportal/api/graphql/input/location_structure/apply_commands.rb +47 -47
  67. data/lib/ecoportal/api/graphql/input/location_structure/draft/add_commands.rb +49 -49
  68. data/lib/ecoportal/api/graphql/input/location_structure/update_command.rb +27 -27
  69. data/lib/ecoportal/api/graphql/input/search_conf.rb +436 -367
  70. data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +27 -18
  71. data/lib/ecoportal/api/graphql/interface/location_structure/command.rb +30 -30
  72. data/lib/ecoportal/api/graphql/logic/input.rb +26 -26
  73. data/lib/ecoportal/api/graphql/mutation/action/archive.rb +1 -1
  74. data/lib/ecoportal/api/graphql/mutation/action/create.rb +1 -1
  75. data/lib/ecoportal/api/graphql/mutation/action/update.rb +1 -1
  76. data/lib/ecoportal/api/graphql/mutation/contractor_entity/create.rb +1 -1
  77. data/lib/ecoportal/api/graphql/mutation/contractor_entity/destroy.rb +1 -1
  78. data/lib/ecoportal/api/graphql/mutation/contractor_entity/update.rb +1 -1
  79. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_workflow.rb +1 -1
  80. data/lib/ecoportal/api/graphql/mutation/kickstand/start_workflow.rb +1 -1
  81. data/lib/ecoportal/api/graphql/mutation/kickstand/stop_workflow.rb +1 -1
  82. data/lib/ecoportal/api/graphql.rb +1 -0
  83. data/lib/ecoportal/api/graphql_version.rb +1 -1
  84. data/tests/dump_template_model.rb +90 -0
  85. data/tests/validate_queries.rb +31 -9
  86. metadata +40 -3
@@ -0,0 +1,99 @@
1
+ # ROADMAP — where ecoportal-qa is going, and how the pieces get us there
2
+
3
+ A narrative companion to INTENT/DECISIONS/TODO. Read this when you want the *why* and the *shape of
4
+ the journey*, not the task list.
5
+
6
+ ---
7
+
8
+ ## The destination (the "north star")
9
+
10
+ > QA in Services Delivery verifies org config & template changes **faster, consistently, and
11
+ > auditably** — and over time those checks run **as a human-gated pipeline on the Jira ticket**, so
12
+ > QA spends their attention on *judgement* (does this design meet the customer's intent?) instead of
13
+ > *mechanical checking* (is field X present, of type Y, in section Z?).
14
+
15
+ Two things stay true at every step: **the tool is read-only against orgs**, and **humans make the
16
+ decisions**. Agents/automation assist; they never approve on their own.
17
+
18
+ ---
19
+
20
+ ## Why this shape — one Result, many channels
21
+
22
+ The whole design rests on a single idea: **every run produces one canonical `Result`** (ticket ·
23
+ target · verdict · per-check outcome). Everything else is a *consumer* of that Result.
24
+
25
+ ```
26
+ what QA means turns intent verifies, the single
27
+ (declarative) into checks read-only source of truth
28
+ ┌───────────────────┐ ┌──────────────┐ ┌───────────────┐ ┌────────────────────┐
29
+ │ QA template DSL │ ► │ Compiler │ ► │ Runner │ ► │ RESULT │
30
+ │ (.qa.yml) │ │ (front→IR) │ │ (live/fixture)│ │ (canonical JSON) │
31
+ └───────────────────┘ └──────────────┘ └───────────────┘ └─────────┬──────────┘
32
+
33
+ ┌────────────────────────┬───────────────────┬──────────┴───────────┐
34
+ ▼ ▼ ▼ ▼
35
+ Reporters Jira CI Monitoring
36
+ (text/json/csv/html) (comment + gate) (ticket-as-pipeline) (dashboards)
37
+ [P1–P2 ✅] [P3 ▶] [P4] [P4]
38
+ ```
39
+
40
+ **Why this matters for "how we keep going":** adding Jira (P3), CI (P4), or auto-approve (P5) never
41
+ touches the DSL, compiler, or runner. Each new capability is *another consumer of the Result*. That's
42
+ what lets us extend without rework — and why the QA-facing language (the `.qa.yml`) can stay stable
43
+ even as the WorkflowCommand/GraphQL API underneath churns.
44
+
45
+ ---
46
+
47
+ ## The capability ladder — each rung is independently useful
48
+
49
+ Each phase delivers value **on its own**, even if we stopped there. That's deliberate: we're never
50
+ "halfway to something that works" — we're "fully at rung N, climbing to N+1."
51
+
52
+ | Rung | Capability | Value delivered on its own | Status |
53
+ |---|---|---|---|
54
+ | **P1** | **Author & run checks** | QA writes YAML, gets green/red on a real template — repeatable, no Ruby | ✅ done |
55
+ | **P2** | **Report & operate** | CSV/HTML/JSON reports, a CLI, PII redaction, rich checks, CI-ready exit codes | ✅ done |
56
+ | **P3** | **Connect to Jira** | Results land on the ticket QA already works in; human gates transitions | ▶ scoped |
57
+ | **P4** | **Automate the pipeline** | "Ticket as CI pipeline" — checks run on ticket state, results archived, monitored | planned |
58
+ | **P5** | **Delegate the routine** | Opt-in, governed auto-approve when checks pass — humans touch only exceptions | planned |
59
+
60
+ **Where we are:** top of **P2**. Right now, today, a QA person can author a `.qa.yml`, run
61
+ `ecoportal-qa`, and read a green/red report of a real template. That is already the core value; P3+
62
+ is about *meeting QA where they work* and *removing manual steps*, not about making it "finally work".
63
+
64
+ ---
65
+
66
+ ## How each rung changes the human's day
67
+
68
+ - **P1–P2 (now):** QA runs a command, reads a report. Faster than eyeballing a template by hand, and
69
+ the same checks run identically every time.
70
+ - **P3 (next):** QA doesn't leave Jira. The result appears on the ticket; they read it and click the
71
+ transition themselves. Traceability (which checks, which verdict) is on the ticket forever.
72
+ - **P4:** The run happens *for* them when the ticket moves — no manual step. Same result, same human
73
+ gate, just no "remember to run it".
74
+ - **P5:** For the routine, all-green cases a team opts into, the ticket can advance automatically —
75
+ freeing the human to spend time only where a check failed or judgement is genuinely needed.
76
+
77
+ The trajectory is **less toil, same authority**: at every rung the human still decides; we just remove
78
+ the mechanical work leading up to the decision.
79
+
80
+ ---
81
+
82
+ ## Guardrails that never move (so "going faster" never means "going riskier")
83
+
84
+ 1. **Read-only against orgs** — the check path never writes to a customer org.
85
+ 2. **Humans decide** — no auto-transition until P5, and even then opt-in + governed + audited.
86
+ 3. **Auditable** — the canonical Result is the record; Jira comments are idempotent and redacted.
87
+ 4. **Governed by ep-ai-standards** — the tool declares conformance; standards live there, code lives here.
88
+ 5. **QA owns the language** — the `.qa.yml` DSL is the delegation surface; the team writes their own checks.
89
+
90
+ ---
91
+
92
+ ## What could change the plan (honest unknowns)
93
+ - **Sandbox access** — P2's live capture of a real template (`6a3fa5b8…622b`) and P3's live Jira posting
94
+ both need credentials we don't have yet. Until then we build against fixtures + dry-run clients.
95
+ - **Jira specifics** — project, workflow statuses, MCP-vs-REST (see PHASE3-SCOPE.md). These shape P3/P4
96
+ but not the core.
97
+ - **Where check-sets live** — repo vs per-customer vs shared config; affects the ticket→check-set index.
98
+
99
+ None of these block the *core* — they only decide *which channel we wire next and how*.
@@ -0,0 +1,81 @@
1
+ # TODOs — QA-as-CI/CD (ecoportal-qa)
2
+
3
+ Legend: `[ ]` todo · `[~]` in progress · `[x]` done · `[!]` blocked
4
+
5
+ Code lives in `ecoportal-qa`; this file tracks the plan.
6
+
7
+ ---
8
+
9
+ ## Phase 1 — DSL + compiler + runner + canonical result ✅ DONE 2026-07-02
10
+
11
+ - [x] `DSL::CheckSet` — parse + validate a declarative `.qa.yml` (meta.ticket/target + expect tree).
12
+ - [x] `Compiler` — DSL → stateless `Check` objects (stage/section/field exists, field type/options/required).
13
+ - [x] `TemplateModel` — normalised read-only view over a page/template doc (stages→sections→fields).
14
+ - [x] `Source::FixtureSource` (offline JSON) + `Source::GraphqlSource` (lazy live read, read-only).
15
+ - [x] `Runner` + canonical `Result` (verdict passed / passed_with_gaps / changes_requested) with honest `:skip`.
16
+ - [x] `Reporters::{Json,Text}`.
17
+ - [x] Proven end-to-end against the real `toocs_coding_page.json` snapshot (7 pass / 2 fail / 3 skip).
18
+ - [x] 11 specs green; rubocop clean (team `.rubocop.yml`); ep-ai-standards conformance stub.
19
+
20
+ **Gate 1:** QA can author YAML → get a green/red result on a real template with zero Ruby. ✅
21
+
22
+ ---
23
+
24
+ ## Phase 2 — Reporters + more check primitives + CLI ✅ MOSTLY DONE 2026-07-03 (branch `feature/phase-2`, commit `22cc865`)
25
+
26
+ - [x] `Reporters::Csv` + `Reporters::Html` (derive from the canonical Result). JSON/Text refactored
27
+ to render from the canonical hash so redaction applies uniformly.
28
+ - [x] More checks: field ordering + section ordering, force/binding presence, select-option weights,
29
+ field-count bounds (exact or `{min,max}`), "must NOT exist" (stage/section/field absent),
30
+ regex on labels (`label_pattern`, optional `type`). Force/option/required checks `skip` honestly
31
+ when the snapshot didn't capture them.
32
+ - [!] Capture the reference template `6a3fa5b8…622b` via the gem's `dump_template_model.rb`; pin as
33
+ a fixture + author a real check-set. **BLOCKED on a sandbox org + creds** (Open Q #2). Substituted
34
+ a synthetic `sample_template_page.json` (real doc shape) to exercise the new checks offline.
35
+ - [x] PII-redaction pass on results (`Redactor` — emails + custom patterns; preserves verdict/counts).
36
+ - [x] `exe/ecoportal-qa run <check-set> --source fixture:PATH|graphql:ID [--format ...] [--redact]`
37
+ CLI; exit code encodes verdict for CI (0/1/2/3).
38
+
39
+ 31 specs green, rubocop clean. Only the live-capture item remains (creds-blocked).
40
+
41
+ ## Phase 2b (carried) — monitoring hookup
42
+ - [ ] Feed the canonical Result JSON into a dashboard/monitoring sink (design alongside Phase 4 CI).
43
+
44
+ ## Phase 3 — Jira adapter (human-gated) — SCOPED 2026-07-03 (see `PHASE3-SCOPE.md`)
45
+
46
+ **Blocked on build** until Oscar answers the 4 open questions in PHASE3-SCOPE.md (Jira project +
47
+ workflow statuses; REST token vs MCP-only; verdict field vs comment-only; where check-sets live).
48
+
49
+ - [~] Design complete — `Jira::{Client,RestClient,McpClient,DryRunClient,Publisher}`; result funnels
50
+ in as just another consumer of the canonical Result (no runner/DSL changes).
51
+ - [x] **3a** — `Jira::Client` interface + `DryRunClient` + `Publisher` (render→redact→idempotent
52
+ comment). Offline-specable (DryRunClient records calls). *DONE 2026-07-03, branch
53
+ `feature/qa-jira-3a-dryrun-publisher` (ecoportal-qa, UNMERGED). 21 new specs (52 total), rubocop
54
+ clean. Report-only default (no transition), redact-before-post, per-ticket idempotency marker,
55
+ audit trail. 3b/3c still blocked on the 4 open Qs (Jira project/statuses, REST token vs MCP,
56
+ verdict field, where check-sets live).*
57
+ - [ ] **3b** — `Jira::RestClient` (base URL + service-account token; env-gated integration spec).
58
+ - [ ] **3c** — CLI `report --jira [--jira-client rest|mcp|dry-run] [--transition NAME] [--attach ...]`
59
+ + audit log. Default = report-only (no transition); `--transition` is explicit + human-invoked.
60
+ - [ ] **3d** — (optional) `Jira::McpClient`.
61
+ - [x] Decision recorded: interface + REST-first, MCP later; Phase 3 never auto-transitions (DECISIONS).
62
+
63
+ ## Phase 4 — CI-as-code backend ("ticket as pipeline")
64
+
65
+ - [ ] Compiler backend: emit a CI job (GitLab CI / GH Actions) from a check-set.
66
+ - [ ] Pipeline-per-ticket: on ticket state, run the check-set, archive the Result artifact.
67
+ - [ ] Monitoring/dashboards fed by the canonical Result JSON.
68
+
69
+ ## Phase 5 — Opt-in auto-approve hooks (governed)
70
+
71
+ - [ ] QA-owned rule: `verdict == passed` + explicit opt-in → offer/perform an auto-transition.
72
+ - [ ] Off by default; audit every auto-approval; RBAC on who can enable it.
73
+
74
+ ---
75
+
76
+ ## Open questions (Oscar)
77
+ 1. Which Jira project(s)/workflow are we targeting, and MCP vs REST for the adapter?
78
+ 2. Sandbox org + creds to run `GraphqlSource` / capture `6a3fa5b8…622b` live.
79
+ 3. Do QA prefer authoring in YAML or JSON (both parse the same) — and where do check-sets live
80
+ (in `ecoportal-qa/qa/checks/`, per-customer repos, or a shared config repo)?
81
+ 4. Publish `ecoportal-qa` to the internal gem registry, or keep it path/git-sourced for now?
@@ -0,0 +1,87 @@
1
+ # Project: Template Automatic Build & Maintenance ★ #1 PRIORITY
2
+
3
+ **Status:** ACTIVE — the #1-priority project. **Created:** 2026-07-04 (merges the former
4
+ `template-diff-deploy` + `template-maintenance` projects into one plan).
5
+ **Domain reference (read first):** `.ai-assistance/code/template_diff_pairing_domain.md`
6
+ **Code spec:** `.ai-assistance/code/diff_pairing_engine.md`
7
+ **Governing security standard (read before wiring live creds):** `security/ai-service-account-scope`
8
+ in ep-ai-standards. This automation MUST authenticate as a dedicated per-org SERVICE ACCOUNT whose
9
+ grant covers templates/org-config only -- page records (customer data + PII) are structurally out of
10
+ scope, enforced by the credential grant, not by instruction. This is the practitioner discovery point
11
+ for that standard (the template automation is its concrete consumer). The "sandbox creds" in success
12
+ criterion #4 below must be provisioned to that scope. Platform dependency RESOLVED: templates ARE pages
13
+ (`PageUnion`), but `stage.tags` (wrongly deprecated ~21 months ago) has been restored via ecoPortal MR
14
+ !6649, so a rule scoped to template stages can structurally exclude record stages. Defining and
15
+ provisioning the concrete grant is now OUR work (this gem + agent), not a platform blocker -- see the
16
+ service-account grant task in `TODO.md`.
17
+ **Superseded folders (pointers only now):** `template-diff-deploy/`, `template-maintenance/`.
18
+ **Sibling projects:** `qa-services-delivery` (verify), `ooze-graphql-native-migration`,
19
+ `template-csv-pipeline` (memory).
20
+
21
+ ---
22
+
23
+ ## Why this is one project
24
+
25
+ Both former projects are two halves of the SAME lifecycle and share ONE emission layer (an ordered
26
+ `WorkflowCommand` batch with `placeholderId` threading applied to `executeWorkflowCommands`):
27
+
28
+ - **BUILD** (was `template-maintenance`) — build/maintain 300+ templates programmatically
29
+ (CSV→templates pipeline, deadline ~Sept 2026): declare a desired template → emit an ordered command
30
+ sequence → apply → read back to verify.
31
+ - **MAINTAIN / DIFF-DEPLOY** (was `template-diff-deploy`) — make UAT→PROD change replication smooth:
32
+ detect (diff) → review → deploy (delta commands) → verify → monitor, eliminating the two manual
33
+ regressions (lost PROD-only changes; premature release of unapproved UAT changes).
34
+
35
+ BUILD is "add everything"; DIFF-DEPLOY is "add/remove/edit only the delta". Same command vocabulary,
36
+ same id-threading, same apply path. Keeping them as one project keeps the emission layer unified.
37
+
38
+ ## The core hard problem (unchanged)
39
+
40
+ No stable identity across independent Mongo objects (template vs child page, UAT vs PROD). Self-version
41
+ diff (same ids) is trivial and done; cross-object diff is an **equivalence-matching** problem — pair
42
+ fields on multiple weak signals (genome + type + label + options), human-assisted for the ambiguous
43
+ ones, with a persisted **learning ledger**. See the domain reference for the full failure-mode map.
44
+
45
+ ## Approach (confirmed)
46
+
47
+ - **Self-version diff first** (done) — the portable "commit".
48
+ - **Pairing = equivalence matching**, not identity — multi-signal, confidence-scored, human-in-loop,
49
+ ledger-backed (`Diff::Pairing::Engine` + `Ledger`; also feeds Product's Field-ID / template-entity-id).
50
+ - **Diff MODALITIES** — a composable family, NOT one diff: `Diff::Strategy` over four axes
51
+ (pairing `:id|:genome|:type_label|:assisted` × scope `:structural|:config_only|:data_migration` ×
52
+ move-sensitivity × intent `:changelog|:deploy|:sync_readiness`). `VersionDiff` is the `:id`
53
+ front-end; `CrossObjectDiff` is the cross-object front-end (pairs fields, emits the SAME `Change`
54
+ output against the pairing map, consumed by the existing synthesizer/deploy unchanged).
55
+ - **Unified BUILD emitter in the gem** — `Builder::TemplateBuilder` turns a declarative spec into the
56
+ ordered `WorkflowCommand` batch (stages→sections→fields→options→config→gauge-stops), threading
57
+ `placeholderId`s exactly as `Diff::CommandSynthesizer` does. BUILD and DIFF-DEPLOY share one layer.
58
+ - **Layering** — mechanics (read model, command vocab, build emitter, diff, apply, pairing engine +
59
+ ledger) live in the gem so admin/troubleshooting/integration/QA all reuse them; orchestration
60
+ (interactive assisted-resolution UX, session flows, `TypedFieldsPairing` as an extra signal) is in
61
+ eco-helpers.
62
+
63
+ ## The pipeline
64
+
65
+ ```
66
+ BUILD: desired spec ─► TemplateBuilder ─► ordered WorkflowCommands ─► Builder::Template#create/update ─► verify
67
+ MAINTAIN: pair (id|genome|type_label|assisted+ledger) ─► diff (modality) ─► delta WorkflowCommands
68
+ ─► Deploy#execute! (gated on `unsupported`) ─► verify (ecoportal-qa) ─► monitor (sync-ready)
69
+ ```
70
+
71
+ ## Scope
72
+
73
+ - In: self-version diff (done); genomeSignature exposure (done); pairing engine + ledger (done); diff
74
+ modalities/Strategy (done); cross-object diff path (done); Change→WorkflowCommands synthesis (done);
75
+ gem-level BUILD emitter (done); UAT→PROD replay; sync-readiness monitoring; CSV→templates pipeline
76
+ (later); tighten `ecoportal-qa` to consume the gem's page-model.
77
+ - Out: waiting on Product's template-entity-id (we bridge + feed it); auto-applying without human
78
+ review of unresolved pairings; workflow-config-per-stage diff until `PagesWorkflow.stages` read model
79
+ is completed (see `template-maintenance/PHASE0-FINDINGS.md` §2).
80
+
81
+ ## Success criteria
82
+
83
+ 1. A ticket's UAT changes are captured exactly and rendered as a review checklist (self-version). ✅
84
+ 2. Two templates diff correctly with pairing; unresolved pairings are escalated to a human + remembered. ✅ engine + cross-object path ready; interactive UX pending (eco-helpers).
85
+ 3. A reviewed delta deploys to PROD as WorkflowCommands, then is verified (ecoportal-qa) — no regressions.
86
+ 4. A template can be BUILT from a declarative spec via one ordered command batch. ✅ emitter ready; live characterization pending (sandbox creds).
87
+ 5. Monitoring shows which registers/subsets are sync-ready to their active template.
@@ -0,0 +1,108 @@
1
+ # TODOs — Template Automatic Build & Maintenance ★ #1 PRIORITY
2
+
3
+ Legend: `[ ]` todo · `[~]` in progress · `[x]` done · `[!]` blocked
4
+ Domain reference: `.ai-assistance/code/template_diff_pairing_domain.md`
5
+ Code spec: `.ai-assistance/code/diff_pairing_engine.md`
6
+
7
+ This merges the former `template-diff-deploy/TODO.md` (MAINTAIN track) and
8
+ `template-maintenance/DESIGN.md`+`PHASE0-FINDINGS.md` (BUILD track). BUILD and DIFF-DEPLOY share ONE
9
+ emission layer: an ordered `WorkflowCommand` batch with `placeholderId` threading.
10
+
11
+ ---
12
+
13
+ ## MAINTAIN track (diff → deploy → verify → monitor)
14
+
15
+ ### Phase 1 — Self-version diff (same object, exact) ✅ DONE
16
+ - [x] `Diff::VersionDiff` — two same-id snapshots → structured changelog (stages/sections/fields/options).
17
+ - [x] `Diff::Change` — command-ready (op/kind/id/before/after) + human `#description`.
18
+
19
+ ### Phase 2 — Expose genome + Change→commands ✅ DONE
20
+ - [x] `genomeSignature` on the data-field fragment + `Base::Page::DataField` (`passthrough`).
21
+ - [x] `Diff::CommandSynthesizer` — `[Change]` → ordered built `WorkflowCommand` batch; edit-mode moves
22
+ gated on `Diff::IdResolver`; type change / resolver-less move → UNSUPPORTED (never guessed).
23
+ - [x] `Diff::Deploy` — `from_versions(before, after, target_doc:)`; `#execute!` gates on `unsupported`.
24
+
25
+ ### Phase 3 — Pairing engine (equivalence) ✅ DONE
26
+ - [x] `Diff::Pairing::Engine` + `Signals` (genome 0.5 / type 0.2 / label 0.2 / options 0.1), greedy 1:1,
27
+ auto-accept ≥0.85 unless near-tie, ambiguous 0.5–0.85, unmatched <0.5; genome fallible (0 not veto).
28
+ - [x] `Diff::Pairing::Ledger` — persisted confirmed pairs (consulted first; supersede-on-correct).
29
+ - [x] `Engine#confirm!(candidate, matched_by: :human)` assisted-resolution hook.
30
+ - [ ] TODO (eco-helpers): interactive assisted-resolution UX; `TypedFieldsPairing` as an extra signal;
31
+ live A/B parity harness.
32
+
33
+ ### Phase 3b — Gauge-stop + typed byType config emission + placeholderId threading ✅ DONE
34
+ - [x] `VersionDiff` emits `:gauge_stop` + `:field_config` (byType, data-driven `BYTYPE_CONFIG`,
35
+ conservative — only confirmed read↔byType-key matches; unmappable props NOT fabricated).
36
+ - [x] `CommandSynthesizer` maps them; `thread_placeholders:` id-threading primitive (Deploy → on).
37
+
38
+ ### Phase 4 — Diff modalities (composable strategies) ✅ DONE 2026-07-04 (branch `feature/template-phase4-modalities-builder-docs`)
39
+ - [x] `Diff::Strategy` value object over the four axes: pairing `:id|:genome|:type_label|:assisted` ×
40
+ scope `:structural|:config_only|:data_migration` × move-sensitivity × intent
41
+ `:changelog|:deploy|:sync_readiness`. Validates axes; `#filter(changes)` applies scope +
42
+ move-sensitivity; `.default` reproduces the pre-Phase-4 self-version behaviour EXACTLY (BC).
43
+ - [x] `VersionDiff.new(before, after, strategy:)` — the `:id` front-end now filters its computed
44
+ change-set through the strategy (default = unchanged; specs prove BC + config_only + move-insensitive).
45
+ - [x] `Diff::CrossObjectDiff` — the CROSS-OBJECT front-end: pairs FIELDS via `Pairing::Engine` (+ optional
46
+ `Ledger`), builds the id-correspondence map from the ACCEPTED pairs, then emits the SAME `Change`
47
+ output (relabel/retype on paired fields; target-only → :added; confidently-unpaired source → :removed).
48
+ Ambiguous/unmatched (incl. same-genome relabels + near-ties) are held in `#unresolved` for a human —
49
+ NEVER auto-paired, auto-added, or auto-removed. Feeds `CommandSynthesizer`/`Deploy` unchanged.
50
+ - [x] `Deploy.from_cross_object(source, target, engine:, strategy:)` + `Deploy#pairing` (exposes
51
+ accepted/ambiguous/unmatched for adjudication before apply).
52
+ - [x] Specs: `strategy_spec` (16), `cross_object_diff_spec`, `version_diff_modalities_spec`. Full diff
53
+ suite 120 examples (was 93; +27). Rubocop clean.
54
+
55
+ ### Phase 5 — Deploy + verify + monitor (NEXT)
56
+ - [ ] UAT→PROD replay end-to-end: pair → diff → delta commands → apply → verify via `ecoportal-qa`.
57
+ - [ ] Pre/post self-version diff on PROD to confirm the recreation matches the intended delta.
58
+ - [ ] Sync-readiness monitoring: which registers/subsets can sync to their active template.
59
+
60
+ ---
61
+
62
+ ## BUILD track (build-from-scratch → verify)
63
+
64
+ ### Phase B0 — Study & capture ✅ DONE (see `template-maintenance/PHASE0-FINDINGS.md`)
65
+ - [x] `Builder::Template#create/update(commands:)` + `Input::WorkflowCommand` are the apply facade.
66
+ - [x] `placeholderId` is the id-threading primitive (client-chosen, resolved intra-batch).
67
+ - [x] `tests/dump_template_model.rb` captures a template's structural page doc; reference target
68
+ `6a3fa5b8f89e07c758df622b` (needs sandbox creds to run live).
69
+
70
+ ### Phase B1 — Gem-level BUILD emitter (unified with diff) ✅ DONE 2026-07-04 (branch `feature/template-phase4-modalities-builder-docs`)
71
+ - [x] `Builder::TemplateBuilder` — declarative spec (stages→sections→fields→options→config→gauge-stops)
72
+ → ordered `WorkflowCommand` batch, `placeholderId`-threaded. Order: addStage → addSection →
73
+ addStageSection → editSectionHeader → addField → addSelectFieldOption / addGaugeFieldStop /
74
+ editFieldConfiguration(byType:). Verified against `Builder::Template#create/update(commands:)` + the
75
+ real input classes' VALID_KEYS (only valid keys emitted; nothing fabricated — config passed through
76
+ under its byType sub-hash exactly as supplied). Shares the SAME `placeholderId` threading concept as
77
+ `Diff::CommandSynthesizer`. 13 specs. Rubocop clean.
78
+ - [ ] UNSUPPORTED/deferred here (honest): forces/strategies/callbacks/tasks/recipients in a build spec
79
+ (the command inputs exist but the build spec shape for them was not confirmed — add per confirmed
80
+ shape); `field_type` enum values not validated against the schema (passed through as given).
81
+
82
+ ### Phase B2 — Live characterization (NEXT, needs sandbox)
83
+ - [ ] Replay `TemplateBuilder` output to reconstruct sample `6a3fa5b8…622b`; assert structure-equivalence
84
+ vs the pinned `dump_template_model.rb` fixture. `executeWorkflowCommands` mutates → sandbox, not CI.
85
+
86
+ ### Phase B3 — CSV→templates pipeline (later, deadline ~Sept 2026)
87
+ - [ ] CSV extract → `TemplateBuilder` spec → build batch, for the 300+ template delivery. Section/field
88
+ identity via hidden-field + description (see memory: project-template-csv-pipeline).
89
+
90
+ ---
91
+
92
+ ## Cross-cutting
93
+ - [ ] **Service-account grant (gates live use; governed by ep-ai-standards `security/ai-service-account-scope`).**
94
+ This automation must run as a dedicated per-org SERVICE ACCOUNT scoped to templates/org-config only --
95
+ page records out of scope, enforced by the grant (structural PII/compliance boundary, not instruction).
96
+ Platform enabler is in place: `stage.tags` (wrongly deprecated ~21 months ago) restored via ecoPortal
97
+ MR !6649, so a rule scoped to template stages can exclude record stages. OUR remaining work:
98
+ (a) enumerate in-scope object types (templates; and which of registers / stages / tag defs / workflow +
99
+ automation config / custom-field defs -- explicit in/out per type, each a potential path back to records);
100
+ (b) write the concrete `stage.tags` rule and confirm no record stage shares those tags;
101
+ (c) fix the read/write surface per type (edit templates? read-only org-config?);
102
+ (d) provision the per-org account and confirm its `canXXX` set carries NO page-record capability.
103
+ Feeds the "sandbox creds" needed by Phase B2 / Phase 5. Reference: INTENT header + the standard.
104
+ - [ ] Tighten `ecoportal-qa` to consume the gem's page-model instead of its own `TemplateModel`.
105
+ - [ ] Emit `stageId`/`sectionId` back-refs on diff-side `addField`/`addSection` (BUILD already does;
106
+ the diff synthesizer's structural adds still emit only label/placeholder — then threading covers them).
107
+ - [ ] Extend `BYTYPE_CONFIG` per field type as read↔byType-key shapes are confirmed; gauge-stop reorder.
108
+ - [ ] Complete the `PagesWorkflow.stages` read model to unblock workflow-config-per-stage diff.
@@ -0,0 +1,12 @@
1
+ # MOVED → `template-automatic-build-maintenance` ★ #1 PRIORITY
2
+
3
+ This project has been **merged** into the unified **Template Automatic Build & Maintenance** project.
4
+
5
+ - Single INTENT: `../template-automatic-build-maintenance/INTENT.md`
6
+ - Single TODO: `../template-automatic-build-maintenance/TODO.md`
7
+
8
+ The MAINTAIN track (diff → deploy → verify → monitor) that lived here is now the "MAINTAIN track"
9
+ section of the merged TODO. Nothing here is authoritative any more — do not update this folder.
10
+
11
+ Domain reference (unchanged): `.ai-assistance/code/template_diff_pairing_domain.md`.
12
+ Code spec (unchanged): `.ai-assistance/code/diff_pairing_engine.md`.
@@ -0,0 +1,9 @@
1
+ # MOVED → `template-automatic-build-maintenance/TODO.md`
2
+
3
+ The diff/deploy TODOs (Phases 1–5) are now the **MAINTAIN track** of the merged
4
+ **Template Automatic Build & Maintenance** project (★ #1 priority):
5
+
6
+ → `../template-automatic-build-maintenance/TODO.md`
7
+
8
+ Phases 1–3b + Phase 4 (diff modalities: `Diff::Strategy` + `CrossObjectDiff`) are DONE there.
9
+ Do not add TODOs here.
@@ -0,0 +1,93 @@
1
+ # Template-Maintenance — Phase 0 Findings (2026-07-02)
2
+
3
+ Ground-truth survey of the gem + eco-helpers surface, verified against actual code (not the
4
+ DESIGN.md second-hand survey). Corrections to DESIGN.md assumptions are flagged **⚠**.
5
+
6
+ ---
7
+
8
+ ## 1. The gem mutation surface is MORE complete than DESIGN.md implied
9
+
10
+ - **⚠ `Builder::Template` already exists and is wired** (`lib/ecoportal/api/graphql/builder/template.rb`,
11
+ exposed as `GraphQL#template`, `graphql.rb:98`). It offers:
12
+ `create(commands:)`, `update(model, commands:, patch_ver:)`, `publish(id:)`, `unpublish(id:)`,
13
+ `update_information(id:, ...)`, `create_related_page(...)`, `destroy_related_page(...)`.
14
+ → The template create/update **apply** step is done. We do **not** need to build a new facade
15
+ for `executeWorkflowCommands` on the template side — `Builder::Template#create/update` already
16
+ wrap `Mutation::Template::{Create,Update}` with a `commands:` array.
17
+ - `Builder::Page#execute_workflow_commands` also exists (per `builder/CLAUDE.md`) for the
18
+ page-level workflow command path.
19
+ - `Input::WorkflowCommand` (`input/workflow_command.rb`) is the command registry:
20
+ **108 commands** in `COMMAND_MAP`, `SCHEMA_VERSION = '20260605'`, with:
21
+ - `.build(command_key, **kwargs)` → `{ key => sliced_kwargs }` (validates key, slices to VALID_KEYS, compacts nils, keeps false)
22
+ - `.build_commands(specs)` → maps an ordered array of single-key specs into the batch
23
+ - `.valid_key?(key)`
24
+
25
+ ## 2. A template IS a page (read path)
26
+
27
+ - **⚠ DESIGN.md Open Question #2 ("is PagesWorkflow read complete enough to diff a template?")
28
+ is partly moot for STRUCTURE.** `Query::Templates` returns `PageUnion` nodes with `PageFields`
29
+ — a template is read as an ordinary page. So the structural skeleton
30
+ (**stages → sections → components(fields)**) comes from the page model, exactly like a live page.
31
+ `dump_template_model.rb` therefore reuses `api.pages.get(id)` for structure.
32
+ - The `PagesWorkflow` read model (`base/pages_workflow.rb`) is the **workflow-command config**
33
+ layer (callbacks, operations, triggers, strategies, recipients). **⚠ It is partial:** `stages`
34
+ is a bare `passarray` (no per-stage strategy/section tree). So:
35
+ - **Build-from-scratch (Phase 2): UNBLOCKED.** Emit commands, execute, read the page back to verify.
36
+ - **Structural diff (Phase 4): mostly UNBLOCKED** via the page model (stages/sections/fields).
37
+ - **Workflow-config diff (callbacks/strategies per stage): BLOCKED** on completing the
38
+ `PagesWorkflow.stages` read model. Track as a Phase-4 dependency, not a Phase-2 blocker.
39
+ - Forces read is on the page model but `Query::PageWithForces` is WIP (see forces notes) — a
40
+ forces-aware diff is a later dependency.
41
+
42
+ ## 3. `placeholderId` is the id-threading primitive (the key design fact)
43
+
44
+ The location-structure engine threads server-assigned ids **post-hoc** (zip input↔result, extract
45
+ `newId`, remap table). **The workflow command bus does NOT need that** — it uses **client-chosen
46
+ `placeholderId`s resolved intra-batch**:
47
+
48
+ | Command | Placeholder it defines | References (by placeholder or real id) |
49
+ |---|---|---|
50
+ | `addStage` | `placeholderId` | — |
51
+ | `addSection` | `placeholderId` | — |
52
+ | `addStageSection` | — | `stageId`, `sectionId` |
53
+ | `addField` | `placeholderId` | `stageId`, `sectionId`, `column` |
54
+ | `addSelectFieldOption` | `placeholderId` | `dataFieldId` |
55
+ | `addForce` | `placeholderId` | — |
56
+ | `addBinding` | `placeholderId` | `forceId` |
57
+
58
+ → The emitter assigns a stable `placeholderId` to every new node and references it downstream in
59
+ the **same batch**. This is simpler than the location `track_changed_ids` remap. **⚠ Correction to
60
+ DESIGN.md:** we mirror the location engine's *shape* (DSL → ordered commands → apply → read-back),
61
+ but the id-threading is placeholder-based, not `newId`-remap-based.
62
+
63
+ **Open question to confirm live:** does `executeWorkflowCommands` resolve placeholderIds across the
64
+ *whole* batch, or only within ordering constraints (e.g. must addStage precede addStageSection)?
65
+ The emitter already emits in dependency order (stage → section → stageSection → field → option),
66
+ so this is safe either way; confirm during the Phase-3 sandbox replay.
67
+
68
+ ## 4. What Phase 0 delivered (this commit)
69
+
70
+ - `tests/dump_template_model.rb` — captures a template's structural page doc
71
+ (`spec/fixtures/templates/<id>.json`) + best-effort workflow command log
72
+ (`<id>.commands.json`). Mirrors `dump_page_model.rb`; run `--label before/after` around a batch
73
+ to diff. **Reference target:** sample `6a3fa5b8f89e07c758df622b` (needs a live/sandbox org to run).
74
+ - `spec/.../input/workflow_command_serialization_spec.rb` — offline serialization contract for the
75
+ structural command families + the `build_commands` ordered-emitter shape + placeholderId threading.
76
+ 10 examples, green. This is the net the eco-helpers emitter is written against.
77
+
78
+ ## 5. Revised phase view
79
+
80
+ - **Phase 1 (gem):** DONE-ENOUGH. `Builder::Template` + `Input::WorkflowCommand` are the facade;
81
+ offline command specs exist and grow as new command types are exercised.
82
+ - **Phase 2 (eco-helpers):** `samples/pages/template/{base,dsl}.rb` + a command-emitter service
83
+ (DSL → ordered commands w/ placeholderId threading → `Builder::Template#create/update` → read-back).
84
+ Offline emitter specs are fully achievable without live calls.
85
+ - **Phase 3 (sandbox):** replay to reconstruct `6a3fa5b8…622b`; assert structure-equivalence vs the
86
+ `dump_template_model.rb` fixture. Needs a sandbox org (Open Question #1 — creds TBD).
87
+ - **Phase 4 (diff):** structural diff via page model now; workflow-config diff waits on the
88
+ `PagesWorkflow.stages` read model.
89
+
90
+ ## Open questions still owned by Oscar
91
+ 1. Sandbox org + creds for the Phase-3 live characterization harness.
92
+ 2. Priority of completing the `PagesWorkflow.stages` read model (gates workflow-config diff).
93
+ 3. Does `6a3fa5b8…622b` exercise forces? (gates the forces read/write split for its replay)
@@ -0,0 +1,14 @@
1
+ # MOVED → `template-automatic-build-maintenance` ★ #1 PRIORITY
2
+
3
+ This project has been **merged** into the unified **Template Automatic Build & Maintenance** project.
4
+
5
+ - Single INTENT: `../template-automatic-build-maintenance/INTENT.md`
6
+ - Single TODO: `../template-automatic-build-maintenance/TODO.md`
7
+
8
+ The BUILD track (build-from-scratch → verify → CSV pipeline) that lived here is now the "BUILD track"
9
+ section of the merged TODO. The gem-level BUILD emitter is DONE
10
+ (`Builder::TemplateBuilder`, shares the diff's `placeholderId` emission layer).
11
+
12
+ `DESIGN.md` and `PHASE0-FINDINGS.md` are RETAINED here as background — the merged INTENT/TODO reference
13
+ them for the survey + the `placeholderId` id-threading facts. They are historical context, not the
14
+ active task list. Do not add new TODOs here — use the merged TODO.