# API Watchdog Needs-Data Checklist

## Purpose

This document is for parallel data-prep only.

It covers the API scenes currently marked `needs-data`:

1. `quote-approval-order-contract`
2. `security-and-visibility`

Excluded here:

1. `blocked` scenes
2. already `ready` scenes

## Status Legend

| Status | Meaning |
| --- | --- |
| `shared-gap` | should become shared baseline if stable |
| `scenario-gap` | keep scenario-local for now |
| `env-gap` | should be provided by bootstrap/deploy path |

## Work Rules

1. every row must be idempotent
2. every row must have a stable logical key
3. relationship rows are mandatory, not optional polish
4. if a row is required by multiple stories, prefer `shared-gap`
5. if a row mutates heavily during tests, keep `scenario-gap`

## Need-Data Summary

| Scene | Case | Priority | Missing Layer | Current Gap |
| --- | --- | --- | --- | --- |
| `quote-approval-order-contract` | `quote-create-api` | P0 | shared | source opportunity/product graph already defined in shared baseline; import closure still required |
| `quote-approval-order-contract` | `approval-submit-api` | P0 | env + scenario | approval probe quote can be seeded; approval config and pending graph remain absent |
| `security-and-visibility` | `security-effective-access-api` | P0 | env + shared | contrasted actors/perm/business seed scaffolded; bootstrap bindings and explicit share contract still pending |

## Scene: Quote / Approval / Order / Contract

### Target Cases

1. `quote-create-api`
2. `approval-submit-api`

### Shared-Gap Rows

These are already covered by the shared baseline catalog and should not be
duplicated in a second quote-only graph.

| status | bucket | record_key | object | match_key | required_fields | relations | unlocks |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `shared-gap` | `opportunity-core` | `opp_product_ready_01` | `Opportunity` | `Name=Watchdog Seed Opportunity` | `Name`, `AccountId`, `OwnerId`, `StageName`, `CloseDate`, `Pricebook2Id` | `account_customer_01`, `pricebook_standard_watchdog` | `quote-create-api`, `approval-submit-api`, `order-from-quote-api` |
| `shared-gap` | `opportunity-core` | `ocr_primary_01` | `OpportunityContactRole` | `OpportunityId=opp_product_ready_01 + ContactId=contact_primary_01` | `OpportunityId`, `ContactId`, `IsPrimary` | `opp_product_ready_01`, `contact_primary_01` | `quote-create-api`, `approval-submit-api` |
| `shared-gap` | `opportunity-line-items` | `oli_tidb_01` | `OpportunityLineItem` | `OpportunityId=opp_product_ready_01 + PricebookEntryId=pbe_tidb_01` | `OpportunityId`, `PricebookEntryId`, `Quantity`, `UnitPrice` | `opp_product_ready_01`, `pbe_tidb_01` | `quote-create-api`, `approval-submit-api`, `order-from-quote-api` |
| `shared-gap` | `opportunity-line-items` | `oli_support_01` | `OpportunityLineItem` | `OpportunityId=opp_product_ready_01 + PricebookEntryId=pbe_support_01` | `OpportunityId`, `PricebookEntryId`, `Quantity`, `UnitPrice` | `opp_product_ready_01`, `pbe_support_01` | `quote-create-api`, `approval-submit-api`, `order-from-quote-api` |

### Scenario-Gap Rows

These should stay local until mutation behavior stabilizes.

| status | bucket | record_key | object | match_key | required_fields | relations | unlocks |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `scenario-gap` | `quote-core` | `quote_draft_source_01` | `Quote` | `Name=Watchdog Draft Quote Source` | `Name`, `OpportunityId`, `AccountId`, `ContactId`, `Pricebook2Id`, `How_will_the_customer_place_an_order__c`, `Payment_Term__c`, `Payment_Type__c`, `Status`, `ExpirationDate` | `opp_product_ready_01`, `contact_primary_01` | `order-from-quote-api`, `quote-line-items-api` |
| `scenario-gap` | `quote-core` | `qli_draft_source_tidb_01` | `QuoteLineItem` | `QuoteId=quote_draft_source_01 + Product2Id=product_tidb_01` | `QuoteId`, `Product2Id`, `Quantity`, `UnitPrice` | `quote_draft_source_01`, `product_tidb_01` | `order-from-quote-api`, `quote-line-items-api` |
| `scenario-gap` | `quote-core` | `qli_draft_source_support_01` | `QuoteLineItem` | `QuoteId=quote_draft_source_01 + Product2Id=product_support_01` | `QuoteId`, `Product2Id`, `Quantity`, `UnitPrice` | `quote_draft_source_01`, `product_support_01` | `order-from-quote-api`, `quote-line-items-api` |
| `scenario-gap` | `approval-core` | `approval_probe_quote_01` | `Quote` | `Name=Watchdog Approval Probe Quote` | `Name`, `OpportunityId`, `AccountId`, `ContactId`, `Pricebook2Id`, `How_will_the_customer_place_an_order__c`, `Payment_Term__c`, `Payment_Type__c`, `Status`, `ExpirationDate` | `opp_product_ready_01`, `contact_primary_01` | `approval-submit-api` |
| `scenario-gap` | `approval-core` | `approval_probe_quote_line_tidb_01` | `QuoteLineItem` | `QuoteId=approval_probe_quote_01 + Product2Id=product_tidb_01` | `QuoteId`, `Product2Id`, `Quantity`, `UnitPrice` | `approval_probe_quote_01`, `product_tidb_01` | `approval-submit-api` |
| `scenario-gap` | `approval-core` | `approval_probe_quote_line_support_01` | `QuoteLineItem` | `QuoteId=approval_probe_quote_01 + Product2Id=product_support_01` | `QuoteId`, `Product2Id`, `Quantity`, `UnitPrice` | `approval_probe_quote_01`, `product_support_01` | `approval-submit-api` |

### Env-Gap Rows

These should come from deploy/bootstrap config, not ad-hoc manual inserts.

| status | bucket | record_key | object | required_fields | relations | unlocks |
| --- | --- | --- | --- | --- | --- | --- |
| `env-gap` | `approval-core` | `approval_process_quote_default` | `ProcessDefinition` plus first `ProcessNode` and lifecycle `ProcessAction` records | object target, entry criteria, submitter scope, active state, recall enabled, pending work-item step, `Quote.Status` updates for submit/final approval/final rejection/recall | quote object + approver actor | `approval-submit-api` |
| `env-gap` | `approval-core` | `approval_actor_sales_manager_01` | `User` / assignment | active approver identity with permission to approve | role hierarchy + permission set | `approval-submit-api` |
| `env-gap` | `approval-core` | `quote_submit_permission_sales_01` | `PermissionSet` / assignment | submit permission exposed to sales-user | `actor_sales` | `approval-submit-api` |

### Relationship Constraints

These are mandatory:

1. `opp_product_ready_01.Pricebook2Id -> pricebook_standard_watchdog`
2. every source `OpportunityLineItem.PricebookEntryId` must point to the same pricebook as the opportunity
3. `quote_draft_source_01.OpportunityId -> opp_product_ready_01`
4. `approval_probe_quote_01.OpportunityId -> opp_product_ready_01`
5. all `QuoteLineItem` rows must clone from the same product graph used by source OLI, and on current runtime they reference `Product2Id`
6. the quote submit actor must own or be allowed to submit the seeded quote

### Done Definition

`quote-create-api` is data-ready when:

1. there is one source opportunity with `2` line items
2. the opportunity has one primary OCR
3. the opportunity and line items are visible to `sales-user`
4. a new quote can be created without inventing missing references at runtime
5. shared baseline import includes `opp_product_ready_01`, `ocr_primary_01`, `oli_tidb_01`, and `oli_support_01`

`approval-submit-api` is unblocked when:

1. one active approval definition targets Quote
2. sales-user can see submit action
3. one approver actor can receive pending work items
4. `approval_probe_quote_01` and its line items are present before submission
5. a submitted quote yields observable pending state

## Scene: Security And Visibility

### Target Case

1. `security-effective-access-api`

### Scaffold Status

Repo-tracked seed scaffolds now exist in:

1. `watchdog/seeds/catalog/auth-identities.yaml`
2. `watchdog/seeds/catalog/security-visibility.yaml`
3. `watchdog/seeds/catalog/account-contact-core.yaml`
4. `watchdog/seeds/catalog/lead-core.yaml`
5. `watchdog/seeds/catalog/opportunity-core.yaml`

### Env-Gap Rows

These should be provisioned by bootstrap/deploy or env seed.

| status | bucket | record_key | object | required_fields | relations | unlocks |
| --- | --- | --- | --- | --- | --- | --- |
| `env-gap` | `auth-identities` | `actor_sales_restricted_01` | `User` | active username/email/profile/role | `role_sales_rep_restricted_01` | `security-effective-access-api` |
| `env-gap` | `auth-identities` | `actor_sales_extended_01` | `User` | active username/email/profile/role | `role_sales_rep_extended_01` | `security-effective-access-api` |
| `env-gap` | `security-visibility` | `role_sales_rep_restricted_01` | `UserRole` | `Name`, `ParentRoleId` | `role_sales_root` | `security-effective-access-api` |
| `env-gap` | `security-visibility` | `role_sales_rep_extended_01` | `UserRole` | `Name`, `ParentRoleId` | `role_sales_root` | `security-effective-access-api` |
| `env-gap` | `security-visibility` | `permset_sales_restricted_01` | `PermissionSet` | object CRUD + field permissions reduced | assigned to restricted actor | `security-effective-access-api` |
| `env-gap` | `security-visibility` | `permset_sales_extended_01` | `PermissionSet` | broader object CRUD + field permissions | assigned to extended actor | `security-effective-access-api` |
| `env-gap` | `security-visibility` | `psa_sales_restricted_01` | `PermissionSetAssignment` | `AssigneeId`, `PermissionSetId` | restricted actor + restricted permset | `security-effective-access-api` |
| `env-gap` | `security-visibility` | `psa_sales_extended_01` | `PermissionSetAssignment` | `AssigneeId`, `PermissionSetId` | extended actor + extended permset | `security-effective-access-api` |

### Shared-Gap Rows

These are business records used to prove visible vs hidden behavior.

| status | bucket | record_key | object | match_key | required_fields | relations | unlocks |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `shared-gap` | `account-contact-core` | `account_visible_restricted_01` | `Account` | `Name=Watchdog Visible Restricted Account` | `Name`, `OwnerId` | owner = restricted actor | `security-effective-access-api` |
| `shared-gap` | `account-contact-core` | `account_hidden_restricted_01` | `Account` | `Name=Watchdog Hidden Restricted Account` | `Name`, `OwnerId` | owner = extended actor | `security-effective-access-api` |
| `shared-gap` | `lead-core` | `lead_visible_restricted_01` | `Lead` | `Email=watchdog+rls-visible@example.invalid` | `FirstName`, `LastName`, `Company`, `Email`, `OwnerId`, `Status` | owner = restricted actor | `security-effective-access-api` |
| `shared-gap` | `lead-core` | `lead_hidden_restricted_01` | `Lead` | `Email=watchdog+rls-hidden@example.invalid` | `FirstName`, `LastName`, `Company`, `Email`, `OwnerId`, `Status` | owner = extended actor | `security-effective-access-api` |
| `shared-gap` | `opportunity-core` | `opp_visible_restricted_01` | `Opportunity` | `Name=Watchdog Visible Restricted Opportunity` | `Name`, `AccountId`, `OwnerId`, `StageName`, `CloseDate` | `account_visible_restricted_01` | `security-effective-access-api` |
| `shared-gap` | `opportunity-core` | `opp_hidden_restricted_01` | `Opportunity` | `Name=Watchdog Hidden Restricted Opportunity` | `Name`, `AccountId`, `OwnerId`, `StageName`, `CloseDate` | `account_hidden_restricted_01` | `security-effective-access-api` |

### Shared-Gap Permission Proof Rows

Use at least one field-level contrast.

| status | bucket | record_key | object | required_fields | relations | unlocks |
| --- | --- | --- | --- | --- | --- | --- |
| `shared-gap` | `security-visibility` | `fieldperm_opp_amount_hidden_restricted_01` | `FieldPermissions` | deny read/write on `Opportunity.Amount` for restricted permset | `permset_sales_restricted_01` | `security-effective-access-api` |
| `shared-gap` | `security-visibility` | `fieldperm_opp_amount_visible_extended_01` | `FieldPermissions` | allow read/write on `Opportunity.Amount` for extended permset | `permset_sales_extended_01` | `security-effective-access-api` |
| `shared-gap` | `security-visibility` | `objperm_opp_read_restricted_01` | `ObjectPermissions` | read allowed, delete/edit intentionally contrasted | `permset_sales_restricted_01` | `security-effective-access-api` |
| `shared-gap` | `security-visibility` | `objperm_opp_full_extended_01` | `ObjectPermissions` | broader CRUD for control actor | `permset_sales_extended_01` | `security-effective-access-api` |

### Optional But Useful Share Rows

Add only if the platform contract supports them cleanly.

| status | bucket | record_key | object | required_fields | relations | purpose |
| --- | --- | --- | --- | --- | --- | --- |
| `shared-gap` | `security-visibility` | `acct_share_visible_to_restricted_01` | `AccountShare` or equivalent | parent id, user/group target, access level | hidden/visible account + restricted actor | prove explicit share path |
| `shared-gap` | `security-visibility` | `opp_share_visible_to_restricted_01` | `OpportunityShare` or equivalent | parent id, user/group target, access level | hidden/visible opp + restricted actor | prove explicit share path |

### Relationship Constraints

These are mandatory:

1. restricted and extended actors must not collapse to the same effective role/perm view
2. visible rows and hidden rows must be owned by different actors
3. at least one field must be readable by extended actor and hidden from restricted actor
4. at least one object action must differ between the two actors
5. lookup/reference rows must not accidentally grant access through ownership shortcuts

### Done Definition

`security-effective-access-api` is unblocked when:

1. two contrasted actors can both authenticate
2. one actor can query `visible_*` records
3. the same actor cannot query `hidden_*` control records
4. one contrasted field returns different readable shape between actors
5. effective permission endpoint can distinguish the two actors

Current remaining blockers after scaffold:

1. bootstrap must provision valid `ProfileId` + password/login path for `actor_sales_restricted_01` and `actor_sales_extended_01`
2. seed/apply contract for `UserRole`, `PermissionSet`, `PermissionSetAssignment`, `ObjectPermissions`, and `FieldPermissions` must be confirmed runnable in downstream env
3. explicit share rows are still deferred until object/API contract is confirmed

### Minimal Validation Reads

Use the same read set for both contrasted actors:

1. account visibility
   - `SELECT Id, Name, OwnerId FROM Account WHERE Name IN ('Watchdog Visible Restricted Account', 'Watchdog Hidden Restricted Account')`
2. lead visibility
   - `SELECT Id, Email, OwnerId FROM Lead WHERE Email IN ('watchdog+rls-visible@example.invalid', 'watchdog+rls-hidden@example.invalid')`
3. opportunity visibility + field contrast
   - `SELECT Id, Name, Amount, OwnerId FROM Opportunity WHERE Name IN ('Watchdog Visible Restricted Opportunity', 'Watchdog Hidden Restricted Opportunity')`
4. effective permission contrast
   - call the effective-permission API for `Opportunity` and compare create/edit/delete + field read/edit on `Amount`

Expected proof:

1. restricted actor sees only `visible_*` rows
2. extended actor sees both control rows or at minimum its own `hidden_*` rows
3. restricted actor does not receive readable `Opportunity.Amount`
4. extended actor does receive readable `Opportunity.Amount`

## Assignment Suggestion

Parallelize by bucket, not by story:

1. owner A: `quote-core`
2. owner B: `approval-core`
3. owner C: `security-visibility`
4. owner D: `auth-identities` contrasted actors

## Handoff Output Required

For each delivered gap, the data-prep owner should report:

1. bucket name
2. record keys added
3. owner/actor used
4. relationship chain created
5. whether it belongs in shared or scenario-local seed
6. one example query or API read proving the row is visible as intended
