---
id: ATS-PFM-DJE-005
scope: CAP-PFM-DJE-005
product_area: platform
module: data-job-engine
revision: 2
status: proposed
approved_by: null
approved_at: null
source_sha:
  macroservice-libraries: 54536a441935c6e09fdb7f96f0bec71c52ba4e03
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-PFM-DJE-005-01
    title: Job visibility follows the caller’s authority
    level: api
    proves:
      - BR-CAP-PFM-DJE-005-01
    preconditions: 'Create jobs from two submitters, only one with workspace-wide visibility.'
    steps:
      - do: List jobs and attempt to read the other submitter’s job.
        expect: >-
          The privileged caller sees both; the other sees only their own and receives not found for
          the other job.
        provenance: test
        source: >-
          services-crm:modules/data-jobs/tests/integration/services/jobs/list-contact-data-jobs.integration.spec.js#answers
          a holder of the bulk key both jobs, whoever submitted them (premise)
    expected: >-
      The privileged caller sees both; the other sees only their own and receives not found for the
      other job.
    negative: true
    data_needs: Distinct caller personas.
    bound_to:
      - TST-PFM-DJE-123
      - TST-PFM-DJE-170
    provenance: test
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/list-contact-data-jobs.integration.spec.js#answers
      a holder of the bulk key both jobs, whoever submitted them (premise)
    category: permission
    binding_evidence:
      - >-
        services-crm:modules/data-jobs/tests/integration/services/jobs/list-contact-data-jobs.integration.spec.js#answers
        a holder of the bulk key both jobs, whoever submitted them (premise)
      - >-
        services-crm:modules/data-jobs/tests/integration/services/jobs/list-contact-data-jobs.integration.spec.js#answers
        a caller without the bulk key only the jobs they submitted
      - >-
        services-platform:modules/data-jobs/tests/integration/services/jobs/data-job-visibility.integration.spec.js#answers
        the clerk 404 for the registrar’s job
  - id: ATS-PFM-DJE-005-02
    title: List filters reject unsupported input
    level: api
    proves:
      - BR-CAP-PFM-DJE-005-02
    preconditions: Prepare jobs in different states.
    steps:
      - do: 'Filter by a valid state, an impossible state and an unknown parameter.'
        expect: The valid filter narrows results; invalid filter input receives 400.
        provenance: test
        source: >-
          services-crm:modules/data-jobs/tests/e2e/data-jobs.e2e.spec.js#refuses a state no job can
          be in with 400, rather than a filter it quietly ignores
    expected: The valid filter narrows results; invalid filter input receives 400.
    negative: true
    data_needs: No additional fixture is needed.
    bound_to:
      - TST-PFM-DJE-109
      - TST-PFM-DJE-064
    provenance: test
    source: >-
      services-crm:modules/data-jobs/tests/e2e/data-jobs.e2e.spec.js#refuses a state no job can be
      in with 400, rather than a filter it quietly ignores
    category: validation
    binding_evidence:
      - >-
        services-crm:modules/data-jobs/tests/e2e/data-jobs.e2e.spec.js#refuses a state no job can be
        in with 400, rather than a filter it quietly ignores
      - >-
        services-crm:modules/data-jobs/tests/e2e/data-jobs.e2e.spec.js#refuses a query parameter the
        list does not offer with 400
      - >-
        react-console:acceptance/crm/playwright/journeys/data-operations/CRM-ACC-0665.spec.ts#CRM-ACC-0665
  - id: ATS-PFM-DJE-005-03
    title: Outcome paging preserves input sequence
    level: api
    proves:
      - BR-CAP-PFM-DJE-005-03
    preconditions: Complete a job containing 100 input records.
    steps:
      - do: Read successive result and refusal pages and apply an outcome filter.
        expect: Rows remain in sequence without repetition and match the requested outcome.
        provenance: test
        source: >-
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
          a page of results in item order, each with its outcome
    expected: Rows remain in sequence without repetition and match the requested outcome.
    negative: false
    data_needs: No additional fixture is needed.
    bound_to:
      - TST-PFM-DJE-117
    provenance: test
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
      a page of results in item order, each with its outcome
    category: boundary
    binding_evidence:
      - >-
        services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
        a page of results in item order, each with its outcome
      - >-
        services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
        the next page from the cursor the last one gave, so no item is read twice
      - >-
        services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
        only the items of the outcome asked for, which is how a client lists what was applied
  - id: ATS-PFM-DJE-005-04
    title: History attributes a job’s changes
    level: browser
    proves:
      - BR-CAP-PFM-DJE-005-05
    preconditions: Complete a contact-changing job.
    steps:
      - do: Inspect the changed contacts’ history.
        expect: 'Revisions identify job source, submitter and batch.'
        provenance: test
        source: >-
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#records
          a job as the source of that revision, not a console
    expected: 'Revisions identify job source, submitter and batch.'
    negative: false
    data_needs: No additional fixture is needed.
    bound_to:
      - TST-PFM-DJE-117
      - TST-PFM-DJE-079
    provenance: test
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#records
      a job as the source of that revision, not a console
    category: invariant
    binding_evidence:
      - >-
        services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#records
        a job as the source of that revision, not a console
      - >-
        services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#stamps
        the job on the revision, so every record one job changed can be listed
      - >-
        services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#carries
        the job on the change event, so a handler knows the change was a job’s before it reacts
      - >-
        react-console:acceptance/data-jobs/playwright/journeys/cross-application/DJE-ACC-0050.spec.ts#DJE-ACC-0050
  - id: ATS-PFM-DJE-005-05
    title: Proposed retention removes aged outcome evidence
    level: api
    proves:
      - BR-CAP-PFM-DJE-005-07
    preconditions: Prepare a job completed 91 days earlier.
    steps:
      - do: 'Run the planned retention task, inspect the job and request rollback.'
        expect: Expired evidence is removed and rollback is refused outside its window.
        provenance: proposed
        source: 'services-platform:docs/milestones/PLAT-M19-data-job-engine-foundations.md#D6'
    expected: Expired evidence is removed and rollback is refused outside its window.
    negative: false
    data_needs: A fixture-controlled clock.
    bound_to: []
    provenance: proposed
    source: 'services-platform:docs/milestones/PLAT-M19-data-job-engine-foundations.md#D6'
    category: rule
    binding_evidence: []
  - id: ATS-PFM-DJE-005-06
    title: Daily purge respects age and workspace
    level: api
    proves:
      - BR-CAP-PFM-DJE-005-08
    preconditions: Prepare idempotency entries of different ages in two workspaces.
    steps:
      - do: Purge one workspace twice.
        expect: Only its entries older than 30 days disappear; the second purge removes nothing.
        provenance: test
        source: >-
          macroservice-libraries:packages/data-jobs/src/tests/integration/submission-purge.integration.spec.ts#deletes
          the tenant's rows older than 30 days and keeps the rest, the row exactly 30 days old
          included
    expected: Only its entries older than 30 days disappear; the second purge removes nothing.
    negative: false
    data_needs: No additional fixture is needed.
    bound_to:
      - TST-PFM-DJE-034
    provenance: test
    source: >-
      macroservice-libraries:packages/data-jobs/src/tests/integration/submission-purge.integration.spec.ts#deletes
      the tenant's rows older than 30 days and keeps the rest, the row exactly 30 days old included
    category: boundary
    binding_evidence:
      - >-
        macroservice-libraries:packages/data-jobs/src/tests/integration/submission-purge.integration.spec.ts#deletes
        the tenant's rows older than 30 days and keeps the rest, the row exactly 30 days old
        included
      - >-
        macroservice-libraries:packages/data-jobs/src/tests/integration/submission-purge.integration.spec.ts#leaves
        another tenant's rows alone, however old
      - >-
        macroservice-libraries:packages/data-jobs/src/tests/integration/submission-purge.integration.spec.ts#is
        idempotent: a second run finds nothing to delete
  - id: ATS-PFM-DJE-005-07
    title: Planned large-result tools support investigation
    level: browser
    proves:
      - BR-CAP-PFM-DJE-005-09
    preconditions: 'Complete a 5,000-record job.'
    steps:
      - do: 'Search for a producer key, inspect refusal-code counts and request refusal export.'
        expect: 'The matching results, grouped totals and generated data export are available.'
        provenance: proposed
        source: >-
          services-platform:docs/milestones/plans/PLAT-M21-plan.md#Added 2026-10-22 (from PLAT-M19
          review R3): results at scale, server side
    expected: 'The matching results, grouped totals and generated data export are available.'
    negative: false
    data_needs: 'An input file with 5,000 rows.'
    bound_to: []
    provenance: proposed
    source: >-
      services-platform:docs/milestones/plans/PLAT-M21-plan.md#Added 2026-10-22 (from PLAT-M19
      review R3): results at scale, server side
    category: rule
    binding_evidence: []
  - id: ATS-PFM-DJE-005-08
    title: Keyed submission remains identifiable in inspection
    category: interaction
    level: api
    exercises:
      - CAP-PFM-DJE-001
      - CAP-PFM-DJE-005
    proves:
      - BR-CAP-PFM-DJE-001-05
      - BR-CAP-PFM-DJE-005-03
    preconditions: Create an authorized caller and two selected contacts.
    steps:
      - do: 'Submit a keyed update, repeat it, process it, and read its job and result pages.'
        expect: Both submissions identify the same job and inspection contains each input position once.
        provenance: proposed
        source: >-
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
          the second submission with the first job, having written nothing;
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
          the next page from the cursor the last one gave, so no item is read twice
    expected: Both submissions identify the same job and inspection contains each input position once.
    negative: false
    data_needs: Create an authorized caller and two selected contacts.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
      the second submission with the first job, having written nothing;
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
      the next page from the cursor the last one gave, so no item is read twice
  - id: ATS-PFM-DJE-005-09
    title: Inspection reconciles partial processing
    category: interaction
    level: api
    exercises:
      - CAP-PFM-DJE-003
      - CAP-PFM-DJE-005
    proves:
      - BR-CAP-PFM-DJE-003-02
      - BR-CAP-PFM-DJE-003-04
      - BR-CAP-PFM-DJE-005-03
      - BR-CAP-PFM-DJE-005-05
    preconditions: 'Prepare valid, invalid and inaccessible contacts in one job.'
    steps:
      - do: >-
          Process the job and read all result and refusal pages alongside the changed records'
          history.
        expect: >-
          The pages account for each position; eligible changes have attribution and refusals
          disclose no record values.
        provenance: proposed
        source: >-
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#refuses
          the other five, counting each as what refused it;
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#names
          the rule a refused value broke, and quotes no value of the record;
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
          the next page from the cursor the last one gave, so no item is read twice;
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#stamps
          the job on the revision, so every record one job changed can be listed
    expected: >-
      The pages account for each position; eligible changes have attribution and refusals disclose
      no record values.
    negative: true
    data_needs: 'Prepare valid, invalid and inaccessible contacts in one job.'
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#refuses
      the other five, counting each as what refused it;
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#names
      the rule a refused value broke, and quotes no value of the record;
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
      the next page from the cursor the last one gave, so no item is read twice;
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#stamps
      the job on the revision, so every record one job changed can be listed
  - id: ATS-PFM-DJE-005-10
    title: Restoration retains an inspectable link and exclusions
    category: interaction
    level: api
    exercises:
      - CAP-PFM-DJE-005
      - CAP-PFM-DJE-008
    proves:
      - BR-CAP-PFM-DJE-005-05
      - BR-CAP-PFM-DJE-008-01
      - BR-CAP-PFM-DJE-008-02
    preconditions: Complete a contact title update and edit one affected title afterward.
    steps:
      - do: 'Plan and apply restoration, then inspect both jobs and the excluded contact.'
        expect: >-
          The restoration is linked to the original; reversible contacts are restored and the later
          edit remains with an exclusion.
        provenance: proposed
        source: >-
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#stamps
          the job on the revision, so every record one job changed can be listed;
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-update-rollback.integration.spec.js#plans
          it already validated, because planning a rollback IS validating it;
          services-crm:modules/data-jobs/tests/integration/services/jobs/contact-update-rollback.integration.spec.js#says
          which field changed since the job and when, without saying what it was changed to
    expected: >-
      The restoration is linked to the original; reversible contacts are restored and the later edit
      remains with an exclusion.
    negative: false
    data_needs: Complete a contact title update and edit one affected title afterward.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#stamps
      the job on the revision, so every record one job changed can be listed;
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-update-rollback.integration.spec.js#plans
      it already validated, because planning a rollback IS validating it;
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-update-rollback.integration.spec.js#says
      which field changed since the job and when, without saying what it was changed to
evidence:
  - >-
    Reviewed evidence: macroservice-libraries (54536a44): src/data-job-reads.ts,
    submission-purge.ts, data-job-config.ts; tests submission-purge.integration.spec.ts.
  - >-
    Reviewed evidence: services-crm (a8d1364f): docs/data-jobs.md (Listing a tenant's jobs;
    Attribution); services/persistence/data-jobs.ts; tests
    list-contact-data-jobs.integration.spec.js, contact-data-jobs.integration.spec.js,
    data-job-instants.integration.spec.js, data-jobs.e2e.spec.js.
  - >-
    Reviewed evidence: services-platform (ad3f6a65): docs/data-jobs/platform-data-operations.md (The
    surfaces); tests data-job-visibility.integration.spec.js; docs/milestones/PLAT-M19 (D3, D4, D6,
    R3), plans/PLAT-M21-plan.md.
  - >-
    Reviewed evidence: react-console (baf6dae3): dataManagement/dataJobs pages;
    components/data-management/data-jobs/data-jobs-screen.tsx, job-detail-screen.tsx,
    job-results-tab.tsx, job-rejections-tab.tsx; lib/api/data-jobs/crm.ts (CRM_SERVES_ITEM_TOOLS).
  - >-
    Testing back-fill: related capability rules, domain invariants, state transitions and crossing
    workflows at the pinned source revisions.
coverage:
  rule: covered
  transition: 'gap: FND-PFM-DJE-065'
  validation: covered
  permission: covered
  edge: 'gap: FND-PFM-DJE-065'
  failure: 'gap: FND-PFM-DJE-065'
  boundary: covered
  concurrency: 'gap: FND-PFM-DJE-065'
  invariant: covered
  interaction: covered
coverage_notes:
  transition: >-
    Selected lifecycle examples do not cover every applicable object and batch transition. The
    linked finding retains the remaining matrix.
  execution: >-
    Covered means a specification scenario exists. Proposed scenarios and registered suites with no
    run result do not establish executed conformance.
related:
  - CAP-PFM-DJE-003
  - CAP-PFM-DJE-008
workflows:
  - WF-PFM-DJE-002
  - WF-PFM-DJE-018
coverage_evidence:
  - rule: BR-CAP-PFM-DJE-005-01
    scenarios:
      - ATS-PFM-DJE-005-01
    provenance: proposed
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/list-contact-data-jobs.integration.spec.js#answers
      a caller without the bulk key only the jobs they submitted
  - rule: BR-CAP-PFM-DJE-005-02
    scenarios:
      - ATS-PFM-DJE-005-02
    provenance: proposed
    source: >-
      services-crm:modules/data-jobs/tests/e2e/data-jobs.e2e.spec.js#refuses a state no job can be
      in with 400, rather than a filter it quietly ignores
  - rule: BR-CAP-PFM-DJE-005-03
    scenarios:
      - ATS-PFM-DJE-005-03
      - ATS-PFM-DJE-005-08
      - ATS-PFM-DJE-005-09
    provenance: proposed
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#answers
      the next page from the cursor the last one gave, so no item is read twice
  - rule: BR-CAP-PFM-DJE-005-05
    scenarios:
      - ATS-PFM-DJE-005-04
      - ATS-PFM-DJE-005-09
      - ATS-PFM-DJE-005-10
    provenance: proposed
    source: >-
      services-crm:modules/data-jobs/tests/integration/services/jobs/contact-data-jobs.integration.spec.js#stamps
      the job on the revision, so every record one job changed can be listed
  - rule: BR-CAP-PFM-DJE-005-07
    scenarios:
      - ATS-PFM-DJE-005-05
    provenance: proposed
    source: 'services-platform:docs/milestones/PLAT-M19-data-job-engine-foundations.md#D6'
  - rule: BR-CAP-PFM-DJE-005-08
    scenarios:
      - ATS-PFM-DJE-005-06
    provenance: proposed
    source: >-
      macroservice-libraries:packages/data-jobs/src/tests/integration/submission-purge.integration.spec.ts#deletes
      the tenant's rows older than 30 days and keeps the rest, the row exactly 30 days old included
  - rule: BR-CAP-PFM-DJE-005-09
    scenarios:
      - ATS-PFM-DJE-005-07
    provenance: proposed
    source: >-
      services-platform:docs/milestones/plans/PLAT-M21-plan.md#Added 2026-10-22 (from PLAT-M19
      review R3): results at scale, server side
---

# Inspect job progress and account for its changes: Capability Test Specification

## Coverage

The structured coverage rows link required rules to their scenarios. A covered category has a defined check; executable coverage and successful execution require separate evidence. The category map keeps incomplete lifecycle matrices and other missing checks visible as findings.

## Interactions With Related Capabilities

Keyed submission remains identifiable in inspection. Inspection reconciles partial processing. Restoration retains an inspectable link and exclusions. The interaction scenarios use the related capabilities' rule references and identify the shared fixture and observable outcome.
