# Project scoping audit

Every feature that is project-scoped must read/write using the **current project** (`getResolvedCurrentProjectId()` / `setCurrentProjectId(id)`) and must **refresh when the project changes** (via `loadProject(projectId)` in `js/app-init.js`).

## Summary

| Feature | Data location | Reads current project? | Refreshes on project switch? | Notes |
|--------|----------------|------------------------|------------------------------|--------|
| **Frameworks** | `getStoredFrameworks()` (filtered by `project_id`) | Yes | Yes | Dropdown and grid filtered by current project (fix: populateFrameworkSelect filters by project). |
| **Guides** | `getStoredGuides()` (filtered by `project_id`) | Yes | Yes | Dropdown filtered by project; loadProject loads project's primary guide or newGuide (fix: populateGuideSelect filters, no project = no guides). |
| **Checklist** | `project.checklist`, storage via `getStoredChecks()` | Yes (storage uses getResolvedCurrentProjectId) | Yes | loadProject calls setChecklistState(p.checklist), renderChecklistSectionsNow(). |
| **Checklist sections** | `project.implementationChecklistSections` or default | Yes (getChecklistSections uses getResolvedCurrentProjectId) | Yes | renderChecklistSectionsNow() runs after switch; getChecklistSections() uses current project. |
| **GA audit checklist** | `project.ga_audit_checklist` | Yes | Yes | loadProject sets setGaAuditChecklistState(p.ga_audit_checklist), invalidates caches, clears displayed result, ensureGaAuditHistoryLoaded(true). |
| **GA audit history** | Fetched by project | Yes | Yes | invalidateGaAuditHistoryCache(); ensureGaAuditHistoryLoaded(true). |
| **Project fields** | Project object (name, notes, domains, measurementId, etc.) | N/A (form is for current project) | Yes | loadProject populates all project form fields from `p`. |
| **Server-side / saved events** | `project.serverEvents` | Yes (getProjectServerEvents uses currentProjectId) | Yes | refreshServerSideUI() → renderSavedEvents() uses getProjectServerEvents(). |
| **Stakeholders** | `project.stakeholders` | Yes (getProjectStakeholders) | Yes | refreshInterviewsUI() re-renders stakeholder list from current project. |
| **Interview question bank** | `project.interviewQuestionBank` or user_settings | Yes (getInterviewQuestionBank uses getResolvedCurrentProjectId) | Yes | loadProject now calls renderInterviewsQuestionBank() so Interviews section shows new project's content. |
| **Interview templates** | `project.interviewTemplates` or user_settings | Yes (getInterviewTemplates uses getResolvedCurrentProjectId) | Yes | loadProject now calls renderInterviewsTemplates(). |
| **Share links** | Supabase `project_share_links` by project_id | Yes (loadShareLinks(projectId)) | Yes | When share links section is visible, loadProject now reloads list and "Share links for: X" heading. |
| **Pixels** | Project fields (metaPixelId, etc.) | Yes | Yes | loadProject calls projectPixels.setPixelIdsInDom(p) and clearPixelIdsInDom when no project. |
| **Get started step** | `project.getStartedStep` | Yes (renderGetStartedCarousel uses current project) | Yes | loadProject calls renderGetStartedCarousel(). |
| **Workflow (11 steps)** | **Project-specific.** No separate storage; computed from current project via getWorkflowState() (project name/creds, stakeholders, GA audit sections, brief questions, framework rows, GTM import, server events, domains, checklist, guide). | Yes | Yes | loadProject calls refreshWorkflowUI(). Opening the Workflow section calls refreshWorkflowUI() so the page always shows the current project's progress. |
| **Overview / quick start** | Current project for dropdown | Yes | Yes | loadProject calls renderProjectQuickStart() when overview active. |
| **GTM comparison / import** | `gtmImportedData` + localStorage per project (`getGtmImportStorageKey(projectId)`) | Yes | Yes | loadProject now calls loadGtmImportForProject(projectId) before renderGtmComparison/renderGtmImportSummary so the new project's GTM import is shown (or cleared when no project). |
| **Cross-domain / config code blocks** | Domains, measurement ID from project | Yes (getProjectDomains, getProjectMeasurementId) | Yes | loadProject calls updateDomainCodeBlocks(), updateServerSideCodeBlock(), updateTestSuiteBlock(), updateTestSuiteUrl(); config snippet uses getConfigCodeWithDomains. |
| **Test suite** | Domains from project for URL hint | Yes (getProjectDomains) | Yes | updateTestSuiteBlock() and updateTestSuiteUrl() called in loadProject. |
| **Brief questions** | `project.briefQuestions` | Yes (getProjectBriefQuestions uses getResolvedCurrentProjectId) | Yes | Used in workflow step 4 and Interviews question bank; renderInterviewsQuestionBank() refreshes on switch. |
| **Project notes modal** | project-name + project-notes form values | N/A | Yes | Form fields are repopulated by loadProject; modal reads from them when opened. |
| **Export readiness UI** | getExportReadiness() (project, framework, guide, checklist, etc.) | Yes | Yes | loadProject calls updateExportReadinessUI(). |
| **Deliverable export** | Uses current framework rows, guide, checks | Yes (implicit via current project) | N/A | Modal reads current state at export time. |
| **Snippets** | User-level (Supabase), not per-project | N/A | N/A | No change on project switch. |
| **AI extract (last)** | Per-project (localStorage + user_settings keyed by project_id; Framework section cleared on switch) (one “last” result per user; apply target = current project) | Yes | Yes | **100% project-specific.** Input: current project name hint. Apply: current project. Last extract stored per project: `blueprintLastAiExtract_<projectId>` and `user_settings.last_ai_extract_<projectId>`. On project switch: Framework section preview cleared; Overview section loads new project's last extract (or clears). app-ui.js framework source URL uses current project's extract. |
| **Datalayer config** | User-level (localStorage / user_settings) | N/A | N/A | Not per-project. |

## Project-specific vs derived

- **Workflow** is project-specific: the 11-step progress (What's next, step list, step label bar) is **derived** from the current project's data each time. There is no separate "workflow state" store; `getWorkflowState()` uses `getResolvedCurrentProjectId()` and that project's stakeholders, checklist, framework, guide, etc. So switching projects automatically changes which steps are "done" and what's next.

## Rules

1. **Read** current project only via `getResolvedCurrentProjectId()` or `Blueprint.currentProject.get()`.
2. **Write** current project only via `setCurrentProjectId(id)` or `Blueprint.currentProject.set(id)`.
3. **On project switch**: `loadProject(projectId)` in `js/app-init.js` must update all project-scoped UI and state. When adding a new project-scoped feature, add the appropriate refresh call to `loadProject` (and, for dropdowns/lists, filter by current project in the populate function).

## Files to touch when adding project-scoped data

- **app.js**: getters (e.g. getProjectServerEvents), populateFrameworkSelect, populateGuideSelect, setChecklistState.
- **js/app-init.js**: `loadProject()` — add any new refresh (e.g. renderX, refreshX) so the UI shows the new project's data.
- **js/storage.js**: getChecks/setChecks use getCurrentProjectIdForStorage() (per-project).
- **js/app-data.js**: getChecklistSections, getInterviewQuestionBank, getInterviewTemplates, saveProject*, getStoredFrameworks/Guides (frameworks/guides filtered in app.js populate, not in getStored*).
- **js/app-ui.js**: renderInterviewsStakeholderList, renderChecklistSectionsImpl use current project; renderInterviewsQuestionBank, renderInterviewsTemplates use getInterviewQuestionBank/Templates (per-project).
- **js/app-share-links.js**: loadShareLinks(projectId), getCurrentProjectId() for create/modal.

## Verification checklist (manual test)

After switching project (dropdown or quick-start):

- [ ] **Project panel**: Name, notes, domains, measurement ID, API secret, GA4 property, container ID, client name/company, web URL, tags, event prefix, Adobe/mobile fields match the new project.
- [ ] **Measurement Framework**: Dropdown shows only "Template" + frameworks for the new project; grid shows that project's framework (or template).
- [ ] **Implementation Guide**: Dropdown shows only guides for the new project; content is the new project's primary/first guide or "— New guide —".
- [ ] **Implementation Checklist**: Sections and checked state match the new project.
- [ ] **GA Audit**: History list and any displayed result are for the new project (previous result cleared).
- [ ] **Server-side**: Saved events list and code block use the new project's events and measurement ID.
- [ ] **Stakeholders (Interviews)**: List is the new project's stakeholders.
- [ ] **Interviews**: Question bank and templates (and brief keys) are the new project's.
- [ ] **Share links**: If panel share section is open, list and "Share links for: X" show the new project.
- [ ] **GTM Import**: Summary and comparison show the new project's GTM import (or empty if none).
- [ ] **Cross-domain / Config**: Code blocks use the new project's domains and measurement ID.
- [ ] **Test suite**: URL hint uses the new project's first domain.
- [ ] **Overview**: Get started carousel step and "What's next" reflect the new project.
- [ ] **Workflow section**: Open the Workflow page; step list, progress, and "What's next" match the new project (reset on project switch and when opening the section).
- [ ] **Pixels**: If present, pixel IDs in DOM are the new project's (or cleared when no project).
- [ ] **AI extract**: Overview section shows the new project's last extract preview (or none); Framework section extract preview is cleared. Apply always targets current project.

## Changes made (fix/project-switch-frameworks-guides)

1. **app.js – populateFrameworkSelect()**: Filter frameworks to current project only (`project_id` / `projectId` match). No project ⇒ only "Template" in dropdown.
2. **app.js – populateGuideSelect()**: When no project, show no guides (`raw = []`). Set dropdown value only if currentGuideId is in the filtered list.
3. **js/app-init.js – loadProject()**: Call `renderInterviewsQuestionBank()` and `renderInterviewsTemplates()` so the Interviews section shows the new project's question bank and templates. When the share links section is visible, reload share links for the new project and update the "Share links for: X" heading.
4. **GTM import (per-project)**: GTM import is stored in localStorage by project key. **app.js**: Expose `loadGtmImportForProject` in init deps. **js/app-init.js**: In `loadProject()`, call `loadGtmImportForProject(projectId)` before `renderGtmComparison()` and `renderGtmImportSummary()` so the GTM comparison and import summary show the new project's data (or clear when no project).
5. **Workflow page reset**: **app.js**: When the user opens the Workflow section (`showSection('workflow')`), call `refreshWorkflowUI()` instead of only `renderWorkflowSection()` so the full workflow (What's next, steps list, step label bar) is refreshed and always reflects the current project. **js/app-init.js**: Comments added next to `refreshWorkflowUI()` in `loadProject()` to document that the workflow is reset on project switch.

## Changes made (AI extract 100% project-specific)

6. **js/core.js**: Added `Blueprint.getAiExtractStorageKey(projectId)` — returns `blueprintLastAiExtract_<projectId>` when projectId is set, else null.
7. **app.js**: AI extract save/load is project-scoped: `saveLastAiExtract(result)` only saves when current project is set (localStorage + user_settings key `last_ai_extract_<projectId>`). `loadLastAiExtract(projectId)` loads that project's last extract. Added `refreshAiExtractPreviewForProject(projectId)` to show or clear Overview preview; exposed via deps and `window.refreshAiExtractPreviewForProject`. Init restores preview only for current project.
8. **js/app-init.js**: Added `clearFrameworkAiExtractPreview()` (clears in-memory result and hides Framework section preview); exposed on window. In `loadProject()`, call `clearFrameworkAiExtractPreview()` and `refreshAiExtractPreviewForProject(projectId)` so on project switch the Framework preview is cleared and the Overview section shows the new project's last extract (or clears).
9. **js/app-ui.js**: Framework source URL fallback uses current project's last extract key (`Blueprint.getAiExtractStorageKey(projectId)`) instead of single global key.

10. **AI extract single entry point**: Framework section no longer has its own "AI: Import from Brief" / extract UI. Replaced with a hint card: "To pull framework rows from a document … open the **Project** panel and use **AI: Extract requirements**." All extract flow lives in the Project panel (index.html: project-ai-extract); Framework handlers removed from js/app-init.js; loadProject no longer calls clearFrameworkAiExtractPreview.
