Skip to content

Session Log: AV7's invisible-content gap found and fixed, AV6 shipped, AV10 scoped

Date: 2026-09-14 (afternoon, continuing the same day's AV8 session) Driver: Yuji Shinozaki (with Claude Code) Outcome: Found and fixed a real gap that made every grouped AV video/audio node invisible in Drupal to everyone, including a site administrator. Shipped AV6 (KMaps display wiring). Scoped AV10 (Kaltura configuration layer) from a direct inspection of the existing contrib module, not from the sprint doc's design note alone. Three new deferred notes filed for genuinely open findings.

PRs merged: #201 (AV7 permission fix), #202 (docs), #203 (AV6), #204 (AV10 scope note).


1. "We can see the content, but only 63 of 7,395 video nodes"

Checking /admin/content?type=video&status=All as the site's administrator account showed only 63 nodes, not the 7,395 published video nodes AV4 migrated. Chased this precisely rather than guessing:

  • Ruled out an INNER JOIN to users_field_data dropping rows for unmigrated authors — all 7,396 video nodes' uids resolve to real users (the shared D7 user base migrated cleanly; this wasn't an authorship gap).
  • Ruled out langcode filtering, Views caching, and the classic node_access grant table (which had exactly one row — Drupal's own "no access-restricting module" sentinel, which should grant universal access, not restrict it).
  • Found the real mechanism by direct, escalating checks: a group_relationship->access('view') check on a real grouped video node returned FORBIDDEN — even for the administrator account, which tested positive for bypass node access, bypass group access, and administer group (Group module's own true super-permission). None of the three overrode it.
  • Root cause: Group module generates a permission string (view group_node:video entity) the moment a content plugin is installed on a group type, but grants it to nobody. shanti_image had this wired since Sprint 1; audio/video never got the equivalent grants when AV4 installed those plugins on collection/subcollection. Group's own per-plugin access check doesn't defer to the generic bypass permissions the way core's node access system does — a real, documented-now design trap (see [[project-kmassets-sync-writer-bugs]] sibling memory project-mandala-state for the full technical trace).

Fixed (PR #201): mirrored shanti_image's exact permission shape onto audio/video across all 10 collection/subcollection role configs.

2. Why a one-time fix wasn't enough

Discussed with Yuji whether this is a design risk (it is — Group's per-plugin permission model is fail-closed by design, which is right for security but means every future bundle added to a Group-enabled content type repeats this exact gap unless something catches it) and whether a shared base implementation could provide "universal permissions by default" without losing Group's per-content-type granularity.

Landed on: the permission string is already auto-generated by a shared base (no code needed per plugin); only the grant needs automating. Built drush mandala:group-permission-audit [--fix] in mandala_group_inheritance — derives the canonical permission shape per (group type, role) from whatever plugins already have grants, with no bundle name hardcoded, so it stays correct as Sources/Texts add their own bundles later. Verified: it correctly re-derives the exact real gap from the pre-fix state, and reports clean post-fix.

3. A deploy near-miss, caught and corrected

Merging PR #201 and then manually triggering a CodePipeline execution created a real duplicate — the merge's GitHub webhook had already auto-started a deploy for the identical commit. Caught mid-flight; stopped the redundant execution with a graceful stop-pipeline-execution (no --abandon) specifically so the currently-running Terraform/Ansible deploy action would finish naturally rather than being killed mid-apply — confirmed by polling the underlying CodeBuild job directly, since CodePipeline's own stage status stayed showing "Stopping" well after the real job had actually succeeded. Filed as [[codepipeline-webhook-plus-manual-trigger-duplicates-deploy]] so the next person (or session) doesn't re-trigger this.

Verified post-deploy: mandala:group-permission-audit reports 0 drift, the admin content listing shows all 7,395 video nodes, and the specific group_relationship->access('view') check that was FORBIDDEN is now ALLOWED.

4. AV6: KMaps field display wiring (done)

Scoped and built in one pass. All 6 KMaps fields already had data fully migrated by AV4; the only gap was that no core.entity_form_display/ core.entity_view_display config existed at all for audio/video, so viewing fell back to the field type's plain default formatter instead of the rich popover UX Images has.

Built both displays by taking Drupal's own computed default (via entity_display.repository, so every non-KMaps field keeps exactly the behavior it already had) and overriding only the 6 KMaps fields. Every other custom AV field (field_video, field_workflow, the PBCore set, field_transcript) lands in hidden — not a regression, the honest reflection that those fields never had a display default to begin with (created via migration, not Field UI) and their real presentation is AV9's job.

Hit, and worked around, a real config:export hazard along the way: a bare drush config:export after building the two new displays touched 24 files, not 2 — it re-serializes the entire active config, and in the process silently deleted the AV8 rationale comment block from mandala_kmassets_sync.settings.yml (added the same morning). Reverted the 20 unintended files, kept only the 4 real ones. This is the third documented recurrence of config:export's comment-stripping behavior and had never been formalized — filed as [[config-export-not-scoped-strips-comments]].

Verified against real migrated content, both locally (DDEV, which turned out to already have the full AV dataset) and again on dev-0 post-deploy: rendered a real video node through the new display and confirmed real KMaps taxonomy data renders correctly via the popover formatter.

5. AV10: Kaltura configuration layer, scoped

Rather than re-deriving the design from the sprint doc's existing notes alone, inspected the actual drupal/kaltura_media contrib module directly: it already provides a working-but-minimal baseline (a kaltura field type + a kWidget.embed()-based formatter with no dimension/delivery/ rotate-stretch support), and confirmed AV4 already migrated real per-node values — frozen from a single hardcoded migration-time constant, which is exactly what AV10 needs to stop being the source of truth for.

Wrote up a full scope note (docs/planning/av10-kaltura-configuration-layer.md) proposing a kaltura_player_config config entity (modeled on image_style) plus a site-level settings object, and traced the exact secret-delivery pattern already proven for SimpleSAML/solr-proxy in terraform-infrastructure so AV11's eventual credential wiring is a two-line addition to an established mechanism, not a new design.

What's left

  • AV9 (Kaltura player formatter + gallery UI) — now has a real design to build against once AV10 exists, at least minimally.
  • AV10 itself — scoped, not built.
  • AV11/AV12/AV13 — unchanged, depend on AV10.
  • AV7's two AV-specific realms (group_access_uva_member, mb_collection_admin) — independent of everything above, can run in parallel whenever there's capacity.
  • Three new deferred notes filed this session, none blocking: [[config-export-not-scoped-strips-comments]], [[codepipeline-webhook-plus-manual-trigger-duplicates-deploy]], [[images-title-sort-s-possible-silent-truncation]] (carried over from the morning's AV8 work, formalized now).