# Mission 001 — Read-only analysis report

## Executive summary
- **What is failing?** Playlist indexing stops after the first page (~100 items). Continuation fetches return HTTP 200 but parsing yields 0 items because the current parser does not handle modern YouTube browse response shapes correctly.
- **What the server returns:** The debug reports indicate the presence of `onResponseReceivedActions`, but `allPlaylistVideoRendererCount` is consistently 0, meaning the items are nested differently or use different renderer names.
- **Most likely root cause:** [extractItemsAndContinuationFromBrowseResponse](file:///Users/bertm/_00_GIT_MASTER/AG-PlaylistButler_MVP/PlaylistButler_MVP/_VERSIONS/AntiGravity_Versions/playlist-butler-v0.3.9_v5/src/content/content_bridge.js#480-530) fails to traverse the `onResponseReceivedActions` array effectively and lacks support for `updateSectionListContinuation` or entity-based responses (`frameworkUpdates`).

## Observations from Debug report
- **Initial Step:** Continuation token extracted from first page (OK).
- **Fetch Step:** POST to `/youtubei/v1/browse` returns status 200 (OK).
- **Parse Step:** `items.length === 0`.
- **Response Shape:** Contains `trackingParams` and `onResponseReceivedActions`.
- **Parser Mismatch:** [deepCollect](file:///Users/bertm/_00_GIT_MASTER/AG-PlaylistButler_MVP/PlaylistButler_MVP/_VERSIONS/AntiGravity_Versions/playlist-butler-v0.3.9_v5/src/content/content_bridge.js#246-269) with `playlistVideoRenderer` finds 0 matches in the response payload.

## Hypotheses (ranked)
1. **Hypothesis 1: OnResponseReceivedActions Nesting.** The items are inside `onResponseReceivedActions[*].appendContinuationItemsAction.continuationItems` but the current [deepCollect](file:///Users/bertm/_00_GIT_MASTER/AG-PlaylistButler_MVP/PlaylistButler_MVP/_VERSIONS/AntiGravity_Versions/playlist-butler-v0.3.9_v5/src/content/content_bridge.js#246-269) predicate for containers is too narrow or misses the array elements.
2. **Hypothesis 2: Alternative Renderer Names.** Items use `richItemRenderer` or `playlistPanelVideoRenderer` in a way that falls outside the current [extractPlaylistVideoRenderers](file:///Users/bertm/_00_GIT_MASTER/AG-PlaylistButler_MVP/PlaylistButler_MVP/_VERSIONS/AntiGravity_Versions/playlist-butler-v0.3.9_v5/src/content/content_bridge.js#361-393) recursion logic.
3. **Hypothesis 3: Entity-Driven Responses.** The response uses `frameworkUpdates.entityBatchUpdate.mutations`. items are stored as `YtEntities` and must be parsed from the batch update rather than renderers.

## Response-shape inventory
Current code must be expanded to support:
- `onResponseReceivedActions` (array of actions)
- `appendContinuationItemsAction.continuationItems` (standard)
- `reloadContinuationItemsCommand.continuationItems` (reload)
- `updateSectionListContinuation` (section-level update)
- `frameworkUpdates.entityBatchUpdate.mutations` (entity based)

## Proposed extraction algorithm (pseudocode)
```javascript
function unifiedExtract(respJson) {
  let items = [];
  let token = null;

  // 1. Gather all action nodes from all plausible paths
  const actionContainers = [];
  if (Array.isArray(respJson.onResponseReceivedActions)) {
    for (const a of respJson.onResponseReceivedActions) {
      const node = a.appendContinuationItemsAction || a.reloadContinuationItemsCommand || a.updateSectionListContinuation;
      if (node) actionContainers.push(node);
    }
  }
  // Support legacy top-level containers
  if (respJson.appendContinuationItemsAction) actionContainers.push(respJson.appendContinuationItemsAction);
  if (respJson.continuationContents?.playlistVideoListContinuation) actionContainers.push(respJson.continuationContents.playlistVideoListContinuation);

  // 2. Extract items and tokens from discovered actions
  for (const action of actionContainers) {
    const continuationItems = action.continuationItems || action.contents || [];
    items.push(...extractPlaylistVideoRenderers(continuationItems));
    
    if (!token) {
      const picked = pickContinuationFromListContents(continuationItems);
      token = picked.token;
    }
  }

  // 3. Fallback to Framework Entities if still empty
  if (items.length === 0 && respJson.frameworkUpdates?.entityBatchUpdate?.mutations) {
    items = extractFromEntityMutations(respJson.frameworkUpdates.entityBatchUpdate.mutations);
  }

  return { items, continuation: token };
}
```

## Minimal instrumentation plan
Add these keys-only logs to [src/content/content_bridge.js](file:///Users/bertm/_00_GIT_MASTER/AG-PlaylistButler_MVP/PlaylistButler_MVP/_VERSIONS/AntiGravity_Versions/playlist-butler-v0.3.9_v5/src/content/content_bridge.js) to identify the precise shape:
```javascript
// Inside extractItemsAndContinuationFromBrowseResponse
dbg("continuation payload inventory", {
  rootKeys: Object.keys(respJson),
  actionCount: respJson.onResponseReceivedActions?.length,
  firstActionType: respJson.onResponseReceivedActions?.[0] ? Object.keys(respJson.onResponseReceivedActions[0])[0] : null,
  hasFramework: !!respJson.frameworkUpdates,
  hasContinuationContents: !!respJson.continuationContents
});
```

## Patch plan (no code yet)
- **[src/content/content_bridge.js](file:///Users/bertm/_00_GIT_MASTER/AG-PlaylistButler_MVP/PlaylistButler_MVP/_VERSIONS/AntiGravity_Versions/playlist-butler-v0.3.9_v5/src/content/content_bridge.js)**
  - Refactor [extractItemsAndContinuationFromBrowseResponse](file:///Users/bertm/_00_GIT_MASTER/AG-PlaylistButler_MVP/PlaylistButler_MVP/_VERSIONS/AntiGravity_Versions/playlist-butler-v0.3.9_v5/src/content/content_bridge.js#480-530) to use the unified scanner above.
  - Update [extractPlaylistVideoRenderers](file:///Users/bertm/_00_GIT_MASTER/AG-PlaylistButler_MVP/PlaylistButler_MVP/_VERSIONS/AntiGravity_Versions/playlist-butler-v0.3.9_v5/src/content/content_bridge.js#361-393) to handle deeper nesting levels (e.g., `richItemRenderer.item.playlistVideoRenderer`).
  - Add `extractFromEntityMutations` helper to handle `frameworkUpdates`.
- **`src/content/page_context.js`**
  - Verify if `ytcfg` or `initialData` shapes need similar broadening (likely OK for first 100).

## Risks / unknowns
- **Parser-only fixes:** Some playlists (like "Watch Later") may have different restrictive shapes.
- **Detection:** If `allPlaylistVideoRendererCount` remains 0 after the patch, it confirms we need entity-based parsing. 
- **Mitigation:** The unified extraction approach handles multiple paths simultaneously to ensure robustness across different A/B tests.

## Acceptance criteria mapping
By implementing a multi-path parser, we ensure that no matter which Innertube shape YouTube serves (classic, modern action-based, or entity-based), the extension successfully extracts the next batch of videos, allowing the 100-item barrier to be broken.
