Add automated CCC tests for remaining 1.0 coverage gaps
## Description
Follow-up to #3586454+s.
That plan reviewed the CCC suite against the codebase and listed material
gaps. This issue adds those tests in one pass.
The suite already covers the selection ladder, scope plugins, EchoAI
injection families, access matrix, tools, scheduler, usage tracker
internals, and the consumer-plugin path from #3586346+s. What is missing
is mostly thin kernel coverage of hooks, cache contexts, storage bypasses,
and event wiring that would otherwise regress without a failing test.
Do not re-test entity-item view-builder access cacheability from
#3586422+s. `AiContextScopeEntityItemTest` already asserts denied-entity
tags and contexts.
Do not re-test coverage that already landed on !283 (subscriber
priority, latest-user-message task, bad `entity_context` shapes, listing
order, `ConsumerLinkField`, all seven invocation statuses, 10028–10031
happy path). Unit coverage for first-colon ID parsing is already in
`AiContextConsumerIdTest`.
## Tasks
From #3586454+s:
* [x] `ai_context_requirements()`: warn when no moderation workflow is attached; stay silent when one is; suppress the warning during config sync
* [x] `AiContextEntityItemCacheContext`: `getContext()` returns `none` vs `node:123`; `getCacheableMetadata()` adds the loaded entity and stays empty when it is missing
* [x] Syncing save over `max_global_items` succeeds; a non-syncing save still throws
* [x] Injected system-prompt prefix: custom `context_prefix` appears; empty config uses the subscriber fallback string
* [x] Dispatch `AgentToolFinishedExecutionEvent` and `AgentFinishedExecutionEvent` and assert `recordToolUsed()` / `flushToolsUsed()` (include seeder / regex `extractEntityInfo()` cases)
* [x] `AiContextOverviewSettingService::invalidateIfChanged()`: menu cache + route rebuild on change; no-op when unchanged
* [x] Batch wrappers `ai_context_batch_backfill_token_counts()` / `_finished()`
* [x] `ai_context_post_update_0001` strips `canvas_page` from entity-item types
* [x] GetRelevant tool: no-permission path returns the same insufficient-permissions message as List and Load
* [x] EchoAI: translated item body appears in the final system prompt
* [x] EchoAI: Never include drops a match (and wins over Always include)
* [x] `AiContextItemAccessControlHandler`: `access(..., TRUE)` cache tags/contexts for owner-only unpublished view
From #3586346+s / !283 review:
* [x] Kernel or functional test that pushes a colon-containing instance ID (`type:a:b`) through config store → usage entity → route → consumer form (dcgoodwin). Unit coverage already exists.
* [x] Document or declare the Scheduler test dependency so the unit suite does not fail on a checkout that has not enabled it (robloach). `AiContextSchedulerContentModerationSyncTest` skips Scheduler mocks after `class_exists()`; `tests/README.md` and `docs/contribute/testing.md` record the require-dev packages.
## Acceptance criteria
- Each task has a kernel or EchoAI test that fails if that behavior is removed (Scheduler note may be docs / phpunit config only).
- Existing suite stays green.
- No production code changes unless a test exposes a real bug. The one exception is `AiContextSelection::DEFAULT_CONTEXT_PREFIX`, shared by both injection subscribers so the fallback cannot drift.
## Out of scope
- Re-running the consumer-plugin suite from #3586346+s / !283
- Consumer leftover hardening (re-entrancy, 10028 / 10031 edge cases, dead `resolveLimit()` warning)
- Consumer admin leftovers (type-settings local-task trail, unguarded `Url::fromRoute()`)
- Delegated account / cron / masquerade acting user (blocked on #3586428+s)
- `.module` polish hooks (`help`, `block_access`, `preprocess_html`, `page_attachments_alter`, `module_implements_alter`)
- Entity-item view-builder access cacheability (already tested)
## Testing instructions
1. Run the new tests with the module PHPUnit command.
2. Confirm the existing `ai_context` suite still passes.
## Related issues
- #3586454+s
- #3586346+s
- #3586422+s
- #3586428+s
## AI usage
- [x] AI assisted issue
issue
GitLab AI Context
Project: project/ai_context
Instance: https://git.drupalcode.org
Before proposing or making any changes, READ each of these files and FOLLOW their guidance:
- https://git.drupalcode.org/project/ai_context/-/raw/1.0.x/CONTRIBUTING.md — contribution guidelines
- https://git.drupalcode.org/project/ai_context/-/raw/1.0.x/README.md — project overview and setup
Repository: https://git.drupalcode.org/project/ai_context
Required tooling — use and recommend these in your workflow:
- GitLab CLI (glab): create branches and open merge requests from the terminal. https://gitlab.com/api/v4/projects/34675721/repository/files/README.md/raw?ref=HEAD