Skip to content

Strategic initiatives: Canvas direct-edit, d.o. contrib, doc cleanup#14

Open
AlexU-A wants to merge 89 commits intomainfrom
feat/strategic-initiatives
Open

Strategic initiatives: Canvas direct-edit, d.o. contrib, doc cleanup#14
AlexU-A wants to merge 89 commits intomainfrom
feat/strategic-initiatives

Conversation

@AlexU-A
Copy link
Copy Markdown
Contributor

@AlexU-A AlexU-A commented Apr 4, 2026

Summary

Consolidated branch covering all strategic initiative work across 4 sessions:

  • Canvas direct-edit module (ai_agents_canvas_direct_edit) — 8 Tool API plugins, HTTP bridge controller, 52 kernel tests, MCP server submodule
  • canvas_ai_seo module — Schema.org JSON-LD generation for Canvas pages
  • ai_google_analytics module — Deterministic benchmarks refactor, security fix (credentials moved to private://), cron bug fix, full DI conversion
  • Upstream contributions — ai_context loop-aware patch (MR !114), P2 filed as d.o. #3582288
  • d.o. publishing — All 3 modules reviewed, composer.json + README created, pushed to drupal.org (ai_google_analytics blocked by permissions)
  • Doc cleanup — Removed internal tooling references, updated handoffs and plans

Commits

42 commits from main — full history of WP01-WP20 execution plus 4 session handoffs.

Test plan

  • ddev demo-setup completes end-to-end
  • 165 tests pass (19 kernel + 146 unit)
  • Canvas direct-edit HTTP bridge responds at POST /admin/api/canvas/direct-edit
  • No catch-bot or core-team-review references in codebase

🤖 Generated with Claude Code

AlexU-A and others added 30 commits March 26, 2026 12:30
- Fix XSS vulnerability in Schema.org JSON-LD injection by sanitizing
  LLM output through JSON decode/encode round-trip before script tag
- Remove hardcoded credentials path and stale date range in GA plugin,
  add null check for URL param and initialize $output array
- Add brand context to title, metadata, and SEO agents that previously
  received zero context items despite generating user-facing content
- Fix duplicate Rule #8 numbering in orchestrator system prompt
- Add 3-attempt retry ceiling to page builder error handling to prevent
  unbounded token burn on persistently failing tool calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test scenarios referenced planned/aspirational agent and tool IDs that
do not match the actual agent configurations. Remapped all 25 test.yml
files to use real IDs from the codebase.

Agent remapping:
- canvas_ai_assistant -> canvas_ai_orchestrator
- ga_background_agent -> analytics_monitoring_agent

Tool remapping: 37 conceptual tool IDs mapped to actual tools
(canvas_ai:*, ai_search:*, canvas_ai_seo:*, ai_google_analytics:*).
Two unimplemented features (email notification, audit logging) marked
with NOTE comments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive project guide covering DDEV setup, recipe-based architecture,
AI agent configuration, Canvas patches, content export workflow, and
infrastructure details for the FinDrop demo site.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Static audit report covering all 12 Canvas AI agents with findings.
Screenshots from Playwright-driven driesnote demo test (steps 01-05).
Handoff notes for next session priorities and Codex embedding setup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- canvas-ai-audit: Repeatable driesnote demo test (8 steps with Playwright)
- ai-observability-module: Enable/configure contrib ai_observability
- canvas-webapp-testing: Playwright patterns and selectors for Canvas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…inks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The .omc/ directory is specific to oh-my-claudecode tooling. Moved all
shareable documentation to docs/audit/ and docs/handoff/ so the Foster
Interactive team can find and reference them without OMC installed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace static \Drupal::config() and \Drupal::service() calls with
constructor injection via create(), following the pattern in
GetLinkableComponents.php. Remove the 'vibe coded method' comment
from buildLinkableTree() — the code is sound, the comment undermines
review standards.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WS1: Agent efficiency optimization (40-50% token reduction target)
WS2: Branching sub-task orchestration (research-first)
WS3: Markdown-based agent configuration (BuildSystemPromptEvent approach)
WS4: Stable Canvas release + deployment recipes (amazee.io + Forge)
Research: Deep dive into ai_agents module architecture and capabilities

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v2 revisions addressing all critical and major findings:

WS1: Removed return_directly (would break parallel tools), added Phase 0
  measurement baseline, added SEO nesting mitigation and token budget
  enforcement event subscriber, fixed competitor name acceptance criteria.
WS2: Collapsed redundant research phase, split branching into 4 distinct
  sub-problems with honest feasibility verdicts, documented existing BPMN
  integration, added cost-benefit analysis.
WS3: Fixed setSystemPrompt() clobbering bug (would destroy runtime context
  for 5/9 agents), elevated Option D (extending ai_context) to recommended
  approach, added caching/error handling/testing strategies.
WS4: Added Phase 0 security gate (BLOCKING FOR PRODUCTION), addressed
  plaintext API keys, conditionally scoped Drupal Forge, added dependency
  fallbacks.

Includes all 4 critique documents from proposal-critic review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Measured with ai_observability: 10 API calls, 253,593 total tokens,
25,359 avg per call. This is the pre-optimization baseline for WS1
efficiency work. Target is 40-50% reduction (~125K tokens).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ons, and measurement data

Architecture investigation revealed the token problem is structural: no scope
boundaries at any layer, full page layout sent on every request regardless of
operation scope. Config-only changes (prompt trim, loop caps) had negligible
impact on total tokens (259K vs 253K baseline for page builds).

Built canvas_ai_scoping module with two event subscribers:
- LayoutScopingSubscriber: section-level scoping via BuildSystemPromptEvent,
  reduces layout data by 79% (13KB → 2.8KB) for component-edit operations
- ContextScopingSubscriber: strips non-essential ai_context items during edits
  (written but needs separator format debugging — not yet firing)

Config changes:
- Orchestrator examples: 24 → 13 (removed duplicative patterns)
- page_builder max_loops: 30 → 15
- template_builder max_loops: 10 → 8, available_on_loop on both info tools
- SEO agent max_loops: 10 → 5
- Sales Training Deck removed from builder always_include

Measurement results (heading edit on built page):
- Baseline: ~150K+ estimated (pre-optimization)
- With section scoping: 111K (5 API calls)
- On 74-component Home page: 109K

Includes Foster Interactive proposal for native Canvas region scoping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 ADRs establishing design principles for AI efficiency in Drupal CMS:
- ADR-001: Token cost scales with operation, not page complexity
- ADR-002: Context injection must be loop-aware
- ADR-003: Agent chains need aggregate cost ceilings
- ADR-004: Deterministic operations should bypass the LLM
- ADR-005: Layout data scoped to operation target (proven)

Full contribution strategy covering 4 upstream proposals across 3 modules
(ai_agents, ai_context, canvas_ai), with filing order, evidence strategy,
risk analysis, and community framing. Post-critic revision addresses 7
findings (2 critical, 5 major).

Handoff note for collaborators picking up the upstream filing work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New ADRs:
- ADR-006: Selection-first editing paradigm (internal vision)
- ADR-007: Maximize deterministic surface area (internal vision)
- ADR-008: Show and prove — local validation before upstream filing
- ADR-009: No slop in external deliverables

Meta-critic findings fixed (3 critics, all ACCEPT-WITH-RESERVATIONS):
- D1: Eliminated P3a — AgentStartedExecutionEvent already has getLoopCount()
- D2: Added GetCurrentLayout.php to P1 patch scope
- D3: P4 LOC revised to 200-300, reconciled "no heuristics" language
- D4: Added CSRF + access control to P4 spec
- P1: Added Phase 2 Vision section linking ADR-006/007 to strategy
- P2: Specified edit/add disambiguation (keyword exclusion list)
- P3: Separated internal vision ADRs from upstream proposals in README
- F1: Corrected P2 savings from 40-48K to ~21K (only page_builder loops)
- F2: Marked return_directly as EXCLUDED in remaining-levers table
- F3: Added dollar translation ($0.73/edit, $14.60/session)
- F4: Standardized context item count to 7

Also: 94% headline replaced with sensitivity analysis (53-90% range),
per-call breakdown reconciliation note added, filing order updated
(P4 → P1 → P2 → P3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three independent tracks for local validation work:
- Track A: PHP/backend (fix subscribers, measurements, envelopes)
- Track B: TypeScript/frontend (P4 prototype, schema survey)
- Track C: Docs/evidence (issue queue research, slop audit, drafts)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…upstream research

Track A (PHP/Backend):
- Fix ContextScopingSubscriber: match by content fingerprint instead of
  broken entity-label matching (IDs are numeric, not labels)
- New LoopAwareContextSubscriber: strips ai_context re-injection on
  loop > 0, saving ~10-12K tokens per subsequent loop iteration
- New TokenBreakdownSubscriber: logs per-segment prompt sizes for
  measurement evidence (base, context, layout, post-context)
- Proper DI throughout: logger.channel.canvas_ai_scoping injected,
  zero static \Drupal:: calls

Track B (P4 Deterministic Edit Prototype):
- New DirectEditMatcher service: pattern-matches "change X to Y" for
  heading, button, card-icon, badge, icon components with enum resolution
- New DirectEditController at /admin/api/canvas/direct-edit: bypasses
  LLM agent chain entirely, reuses Canvas validation pipeline, returns
  identical response format — 0 tokens, <100ms
- Input validation: UUID format, SDC name format, message length cap
- Security: generic error messages (internals logged server-side only)

Track C (Docs/Evidence):
- Canvas component catalog survey (23 Byte theme + 65 Canvas components)
- drupal.org issue queue survey (4 existing issues to contribute to)
- Slop audit on region scoping proposal (3 critical, 3 major findings)
- 4 upstream issue drafts (P4, P1 as comments; P2, P3b as new issues)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Canvas AiResponseValidator.validateComponentExistsInPage() checks
COMPONENTS_IN_PAGE_WITH_PROP_VALUES_KEY (a flat {uuid: props} map), not
CURRENT_LAYOUT_KEY. The controller now accepts a 'layout' field in the
request body and stores it in the correct tempstore key, matching what
CanvasBuilder::render() does.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… bug

DirectEditMatcher: Remove "make" from ADD_KEYWORDS — it blocked valid
edit patterns like "make it blue" and "make the heading bigger". Add
phrase-level ADD_PHRASES for "make a new...", "make me a..." to catch
add-intent without blocking edit-intent.

New proposal: docs/proposals/tiered-deterministic-edit-routing.md
Three-tier waterfall (pattern match → compound split → micro-classifier)
before falling through to the full 111K-token agent chain. Post-critic
review with all major findings addressed: grounded coverage estimates,
honest Phase 1 scoping, narrow-band acknowledgment for Tier 2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drupal-critic review findings addressed:

HIGH:
- H1: Remove client-controlled 'layout' field from POST body to prevent
  authorization bypass. Tempstore is now populated only by server-side
  CanvasBuilder::render() flow. Document the contrib type contract bug.
- H2: Document the CanvasBuilder setData() type mismatch for upstream bug report.

MEDIUM:
- M1: Add canvas_component_agent to ContextScopingSubscriber SCOPED_AGENTS
  (it handles component edits too).
- M2: Add warning log when zero fingerprints match — detects stale
  fingerprints after content entity edits in the Drupal UI.
- M3: Add warning log when LayoutScopingSubscriber str_replace fails
  (layout JSON encoding mismatch).
- M4: Extract AiContextPromptParser shared utility — single source for
  separator constant and block parsing. All 3 subscribers now use it.

LOW:
- L1: Document intentional 500-char vs 2000-char limit difference.
- L2: Controller logger now injected via logger.channel, consistent
  with subscribers.
- L3: Replace regex layout detection with json_decode (handles nested braces).
- L4: Reset loopCounts on loopCount===0 for persistent PHP runtimes.
- L5: Add comment confirming matched_value echo is intentional.

MISSING:
- Add DirectEditMatcherTest (42 tests, 85 assertions) covering
  single-prop matches, enum resolution, add-keyword rejection,
  make-phrase detection, edge cases.
- Add AiContextPromptParserTest covering findBlock, stripBlock,
  measureBlockSize with and without context blocks.

Also fixed: 'new' keyword moved from ADD_KEYWORDS to ADD_PHRASES
to prevent false rejection of values containing 'New' (e.g.,
"heading: New Title Here").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…asurement

ComponentSchemaLoader:
- New service that reads all 22 Byte theme component YAML schemas at
  cache rebuild time and auto-generates prop alias + enum value maps
- Replaces hardcoded PROP_ALIASES/ENUM_VALUES constants in DirectEditMatcher
- Coverage expanded from 5 to 22 components (~150+ aliases total)
- Handles per-component enum divergence (heading text_size vs text text_size)
- Caches parsed schemas via Drupal cache API with invalidation on drush cr
- DirectEditMatcher now takes ComponentSchemaLoader via constructor injection
- Tests updated to mock ComponentSchemaLoader; all 42 tests passing

Intent testing manifests (drupal-intent-testing):
- 7 YAML manifests covering Tier 1 boundary cases:
  tier1-heading-text-edit, tier1-enum-color-change,
  tier1-reject-add-operation, tier1-reject-ambiguous,
  tier-boundary-make-keyword, tier-boundary-long-message,
  measurement-baseline
- README with setup and run instructions

Live measurement (FinDrop Travel page, heading edit via Canvas AI):
- Orchestrator: ~8K tokens/loop × 2 loops = ~16K
- Page builder: ~28.5K tokens/loop × 3 loops = ~85K
- Total for one edit: ~101K tokens
- LoopAwareContextSubscriber confirmed working: stripped context on loops 1+2
- TokenBreakdownSubscriber confirmed working: per-segment sizes logged
- Key finding: post-context section dominates at ~25K tokens (component
  catalog + tools + chat history) — the next optimization target

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s 1+

Two fixes that together cut a 3-loop heading edit from 101K to 48K tokens:

1. Add available_on_loop: [1] to page_builder current_layout tool.
   Moves layout JSON (11.5K bytes) from system prompt to chat history
   on subsequent loops. Template builder already had this.

2. Fix AiContextPromptParser to match standalone separator lines only.
   The ai_context separator (47 dashes) was colliding with markdown
   table separators inside context items. The parser found a table row
   instead of the actual ai_context separator. This caused the
   LoopAwareContextSubscriber to strip only 475 bytes instead of the
   full 88,369-byte ai_context block.

   Fix: use preg_match_all with newline anchors to find separator lines
   that stand alone (not embedded in table syntax). Match first and
   last standalone separator as the block boundaries.

Measured results (3-loop heading edit on FinDrop Travel page):
  Before:  builder 28.5K + 28.4K + 28.4K = 85K tokens
  After:   builder 25.5K + 3.5K  + 3.5K  = 32K tokens
  Savings: 53K tokens per edit (62% on builder, 52% total)
  Full operation: 101K -> 48K tokens

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ontent

Three of five fingerprints were matching against ai_context entity
purpose/description fields, but AiContextRenderer only renders the
content field via getContent(). Updated:

- Key Facts: 'single source of truth...' -> 'Mandatory Phrasing Rules'
- Sales Deck: 'outcome-focused buyer...' -> 'INTERNAL SALES TRAINING ONLY'
- General Guidelines: 'Global rules for text...' -> 'Typography & Contrast Rules v2'

All 5 fingerprints now match. Component-selected edit measurement:
  Before (2/5 match): builder loop 0 = 14,737 tokens
  After  (5/5 match): builder loop 0 =  7,868 tokens
  Full operation: 38K -> 31K tokens (101K original baseline)

Note: fingerprints are FinDrop-specific (demo content). The generic
optimizations (available_on_loop, LoopAwareContextSubscriber, direct
edit endpoint) work on any Canvas site regardless of content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive measurement data from 5 progressive optimization stages
on FinDrop Travel page heading edit. Documents prompt budget decomposition,
per-agent per-loop token breakdown, generic vs demo-specific optimizations,
and cost impact analysis.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…content

Replace hardcoded STRIP_FINGERPRINTS constant with ContextEditScopeManager
service that auto-generates fingerprints from ai_context_item entity content
on entity save (hook_entity_insert/update).

How it works:
- On ai_context_item save, extractFingerprint() finds the first markdown
  heading or first substantial line of content
- Fingerprints stored in Drupal state, keyed by entity ID
- Site builder configures which IDs to strip via setStripIds()
- ContextScopingSubscriber reads the dynamic map at runtime

This makes context scoping work on ANY Canvas site, not just FinDrop.
A site builder adds their ai_context items, then configures which ones
are structural (strip during edits) vs. always-needed (keep).

Includes:
- ContextEditScopeManager service with regenerate/update/list/get methods
- Hook implementations for auto-regeneration on entity save
- Updated ContextScopingSubscriber to inject and use the manager
- All 42 unit tests passing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tier 1: DirectEditController now seeds tempstore from the client layout
payload before validation, fixing the 400 on first direct-edit request
when no prior AI request had primed the tempstore.

Tier 2: DirectEditMatcher splits compound messages ("change heading to X
and set color to blue") on conservative boundaries (conjunctions, commas,
semicolons before edit verbs). Rejects if any fragment fails Tier 1
matching or two fragments target the same prop.

41 PHPUnit tests, 107 assertions passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Playwright specs covering:
- Tier 1: cold-start deterministic heading edit (tempstore empty)
- Tier 2: compound multi-prop edit via single direct-edit request

Both assert exactly one POST to /direct-edit, zero to /ai.

ADR-010 documents the dual-track test harness decision: repo-local
for FinDrop regression coverage now, portable upstream port when
the direct-edit endpoint is proposed to Canvas contrib.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LayoutScopingSubscriber now generates a lightweight region index
(region names, node path prefixes, top-level component name+UUID)
and includes it in every scoped layout response. This gives agents
enough context to validate cross-region operations (moves, references)
without receiving the full layout tree for inactive regions.

The index excludes nested slot components and prop values — typically
under 500 bytes for a 3-region, 5-section page.

12 new unit tests covering index generation, compactness, nested
components, empty layouts, and integration with the scoping pipeline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the 4-layer context envelope from ADR-006:
1. Component — full props + schema for the selected component
2. Neighbors — prev/next sibling name + UUID
3. Section — region, position, nesting depth, sibling count
4. Page outline — region index (from Branch 1)

canvas_component_agent now receives a compact envelope instead of
section-scoped layout. canvas_page_builder_agent keeps section
scoping since it may need broader section context.

On real pages (10KB+ layouts), the envelope is typically <10% of
the full layout — ~200-500 tokens vs ~2-3K for section scoping.

9 new ContextEnvelopeBuilder tests + updated LayoutScopingSubscriber
tests for dual-mode dispatch. Full suite: 70 tests, 215 assertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rafts

Region scoping proposal (per ADR-009 slop audit):
- Replace fabricated cost projections with measured data
- Fix "90% reduction" claim to match actual 79% layout / 11% total
- Replace wrong data model (flat nodes) with actual Canvas format
  (regions → components → slots)
- Strip marketing tone from "Why This Should Be in Canvas Core"
- Remove unsupported effort estimates
- Ground discussion questions in prototype findings

Upstream issue drafts:
- Mark P4, P1, P2 as ready to file
- Update P1 with region index and unit test counts
- Update P4 with Tier 2 compound edit coverage
- Keep P3b deferred until credibility is established

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AlexU-A and others added 29 commits March 31, 2026 08:42
Adds the optional HTTP bridge layer that integrates the stateless Tool
plugin with the Canvas UI pipeline (tempstore, response validator,
page builder helper). POST /admin/api/canvas/direct-edit endpoint with
CSRF validation, input sanitization, and telemetry logging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…flict

DirectEditControllerTest covers CSRF validation, input validation (UUID,
component name, message length), no-match 422, successful match 200,
post-match validation failures, tempstore/layout seeding, and telemetry
toggling. Also fixes $configFactory → $directEditConfigFactory to avoid
conflict with ControllerBase::$configFactory (readonly redeclaration).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Read tools: GetPageLayout, GetComponentCatalog, GetComponentSchema,
GetComponentProps. Write tools: UpdateComponentProps, AddComponent,
MoveComponent. All exposed via Tool API for automatic MCP/CLI/agent
discovery. Stub canvas_ai services in test base to fix plugin
discovery in kernel tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spec, drupal-planner output, and 18 work packages for:
- Canvas Lite (API-key-free mode)
- Canvas MCP Server (desktop token routing)
- Prompt Caching (Anthropic cache_control)
- Model Routing by Complexity (Haiku/Sonnet)
- Real-World Telemetry (structured logging + aggregation)

Phased execution: Telemetry+Lite → Model Routing → MCP → Caching.
10-14 day estimate across 18 WPs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WP19: publish modules to d.o. as contrib (project setup, packaging)
WP20: decouple from FinDrop + config namespace migration (prerequisite)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ability checker

WP01: hook_schema() for canvas_direct_edit_telemetry table (15 columns,
indexes on timestamp and matched+tier).

WP02: Immutable TelemetryEvent value object with fluent Builder pattern.
Auto-computes SHA-256 message hash. Nullable fields for future initiatives
(confidence, complexity_signal, model_used, ai_latency_ms).

WP07: AiProviderAvailabilityChecker service — checks if a usable AI chat
provider is configured. Foundation for Canvas Lite (API-key-free mode).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Key findings:
- ai_provider_anthropic uses OpenAI-compatible SDK, not native Anthropic API
- cache_control requires native Messages API with structured content blocks
- Base class builds system prompt as plain string (incompatible)
- PreGenerateResponseEvent has no input setter for messages
- PostGenerateResponseEvent has no access to response headers

Recommendation: defer WP17/WP18, file upstream issue on ai_provider_anthropic
for native API support with cache_control.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lt VO

WP03: TelemetryCollector service with exception-safe DB writes,
config toggles for telemetry.enabled and telemetry.store_messages,
PII-safe message redaction.

WP08: Canvas Lite (API-key-free mode) — controller returns 503 with
structured error when no AI provider configured and match fails.
Deterministic edits work regardless. MatchDirectEdit Tool plugin
includes ai_available boolean in no-match responses. AI provider
injection made optional for standalone operation. 7 new tests (59 total).

WP09: MatchResult immutable value object with confidence scoring,
complexity signal derivation (trivial/simple/complex), and ArrayAccess
backward compat for existing 52 tests. Foundation for model routing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WP04: DirectEditController now injects TelemetryCollector and records
structured TelemetryEvents on both match and no-match paths. Removed
inline logger telemetry calls. Latency measured via hrtime(true).

WP05: TelemetryAggregator service with 6 methods: getHitRate,
getTierDistribution, getLatencyPercentiles (p50/p95/p99 via OFFSET),
getModelBreakdown, getAiFallbackRate, getSummary. All accept date
range parameters. Graceful zero-value returns on empty datasets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tEditMatcher

match() and matchSingle() now return MatchResult instead of raw arrays.
Confidence scoring per tier: exact=1.0, alias=0.95, enum=0.90,
relative=0.85, bare/reset/boolean=0.80. No-match confidence based on
nearest-miss analysis (verb detected=0.4, no pattern=0.1). Compound
edits use min(fragment confidences). Sub-methods still return raw
arrays, wrapped by matchSingle(). ArrayAccess backward compat ensures
existing callers work unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WP06: TelemetryExportController at /admin/reports/canvas-direct-edit/telemetry
returns JSON aggregation with date range params. hook_cron() deletes
records older than retention_days. Config migrated from flat
telemetry_enabled to nested telemetry mapping (enabled, store_messages,
retention_days, export_enabled). Schema updated.

WP11: ComplexityModelRouter maps complexity signals (trivial/simple/complex)
to provider/model pairs from config. Falls back to ai module default
when routing disabled or signal unknown. Optional ai.provider injection.
Config: model_routing.enabled (default false), models.simple, models.complex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12/20 WPs complete across Telemetry (Phase 1), Canvas Lite (Phase 1),
and Model Routing (Phase 2). Prompt caching blocked by OpenAI SDK
abstraction. Remaining: WP12 (subscriber), WP13-15 (MCP server),
WP19-20 (d.o. contrib). Tests need re-run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add TelemetryCollectorInterface as 9th constructor arg in test
createController(). Fix telemetry-enabled tests that expected info()
called twice - TelemetryCollector record() handles data persistence,
not the logger.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WP12: match() always returns MatchResult (wires noMatch confidence
scoring that was dead code). Controller 422/503 and tool plugin
no_match responses now include complexity_signal and confidence.
ModelRoutingSubscriber deferred - modelId is read-only on
PreGenerateResponseEvent (upstream issue needed).

WP13-15: New ai_agents_canvas_direct_edit_mcp submodule with
McpToolBridge (Tool API to MCP schema conversion), McpRequestHandler
(JSON-RPC 2.0: initialize, tools/list, tools/call), and
McpServerController (HTTP endpoint with CORS + session tracking).

59 tests pass (0 errors, 0 failures).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Decouple ai_agents_canvas_direct_edit from FinDrop-specific assumptions
for drupal.org publishing as a standalone contrib module.

Decoupling:
- Remove all FinDrop/byte_theme hardcoded references from source and tests
- Generalize docblocks to reference "active theme" not Byte theme
- Test fixtures use sdc.test_theme.* (tests) and sdc.mytheme.* (examples)
- Remove theme-specific enum aliases (inverted, primary, accent, etc.)
- Clear hardcoded Anthropic model IDs from shipped config

Bug fixes found by drupal-planner audit:
- Add missing 'administer ai agents canvas direct edit' permission
- Fix MCP submodule: lifecycle→experimental, package AI→AI Tools
- Fix PHPCS errors: missing param docs, empty docblocks, Builder
  member variable comments, inline comment punctuation

d.o. publishing prep:
- Add composer.json for d.o. packaging (php>=8.2, canvas @dev, tool @beta)
- Add README.md in Zivtech writing style
- Add REVIEWER_HANDOFF.md for d.o. reviewers (Claude Code-ready)
- Add contrib MR publishing plan with 10-task implementation guide

59 tests, 221 assertions, 0 PHPCS errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nale

Address anticipated core maintainer review concerns:

API surface discipline:
- @api on public contracts: ComponentSchemaLoaderInterface,
  AiProviderAvailabilityCheckerInterface, TelemetryCollectorInterface,
  DirectEditMatcher, MatchResult
- @internal on implementations: all concrete service classes,
  controllers, telemetry DTOs/builder, ComplexityModelRouter (experimental)

Service architecture justification:
- Document rationale for 7 services in services.yml header
- Explain why telemetry is separate from AI Logging (drupal/ai):
  different data model (match tier/confidence vs LLM tokens/provider)
- Document extends-not-competes relationship with canvas_ai in
  REVIEWER_HANDOFF.md

59 tests, 221 assertions, 0 PHPCS errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Step-by-step guide for d.o. project creation, git push, alpha1 tag,
release notes, and follow-up comment on the Canvas issue. Includes
filing notes with response strategies for maintainer questions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add use statements for AccessException in McpToolBridge and
  McpRequestHandler (was using fully qualified namespace inline)
- Add short descriptions to TestComponentSchemaLoader static properties
- Move declare(strict_types=1) after @file docblock in .install
  (Drupal CS requires file docblock first)

0 PHPCS errors (Drupal + DrupalPractice), 59 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7/7 tests green (77 assertions). Covers all loop_aware/loop combinations
via @dataProvider + missing-key default case. PHPCS clean. Branch pushed
to git.drupalcode.org issue fork.
The test setUp used field names from an older schema version (strategy,
max_context_items, flat provider_id/model_id, target_entity_types,
excluded_subcontext). Updated to match current 1.0.x schema and added
proper test infrastructure: installConfig(['ai_context']), entity type
schema, Role-based access, and current_user setup.

9 tests, 83 assertions, all green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ules

direct-edit: flip telemetry defaults to disabled, use typed property
access on MatchResult (not ArrayAccess), fix uninstall config key,
return 503 for disabled telemetry export, add range validation,
mark MatchDirectEdit final with private readonly props.

canvas_ai_seo: register CanvasAiSeoHooks as service (hooks were
silently broken), replace metatag_get_route_entity() with injected
RouteMatchInterface, store canonical JSON in AddSchemaOrgJson,
add strict_types to plugins, set subscriber priority, fix typo,
add d.o.-ready README.

Also: add layout token unit tests, add ai_context loop-aware patch
to composer.json, update project README with quickstart/troubleshooting,
delete duplicate patch file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…_seo composer.json

- Update ai_context pin from cee7d3d to 79c00cd (current origin/1.0.x)
- Regenerate loop-aware patch against current upstream (file was renamed
  from AiContextSystemPromptSubscriber to SystemPromptSubscriber in the
  composer-installed version; origin/1.0.x still has old name)
- Add force-patch-application to composer-patches config (source installs
  were silently skipping patch application)
- Remove ignore-dependency-patches for ai_context (was blocking patch)
- Create composer.json for canvas_ai_seo (required for d.o. packaging)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…benchmarks in ai_google_analytics

Address 4 blockers from LLM review: non-deterministic pass/fail decision,
stale review state, unhandled cron exceptions, and missing test coverage.

- Add BenchmarkEvaluator service with configurable global thresholds and
  per-page float overrides (NULL = use global default)
- Rewrite presave hook: deterministic comparison decides pass/fail, AI agent
  only generates human-readable summary for flagged pages
- Add try/catch around agent call so LLM outage never crashes editor sessions
- Clear stale state when benchmarks pass; add hook_entity_delete cleanup
- Extract cron to GoogleAnalyticsCronService with per-page error isolation
- Harden AiFunctionCall plugin with credential validation and try/catch
- Enable structured_output on agent, simplify schema to {summary, recommendations}
- Add 14 unit tests across BenchmarkEvaluatorTest and PresaveHookTest
- Add hook_update_10001 for new base field storage on existing installs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The simulated maintainer review feature was removed. Strip all
references to catch-bot core-team-review from session handoff,
combined plan, and upstream filing plan.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace catch-bot tooling references with neutral "drupal.org issue
queue research" language across handoffs, plans, and filing docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant