---
id: CAP-PFM-MTD-010
title: Make Custom Records Discoverable
feature: FEAT-PFM-MTD-04
product_area: platform
module: metadata
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes:
  - CAP-CO-DEF-011
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 D9 (placement), D15 (custom apps), B2,
    B3, B4
  - 'services-platform:docs/milestones/plans/PLAT-M5-plan.md#2. Andrew''s placement direction; PLAT-M5-S01, S13'
  - 'services-platform:docs/proposals/custom-objects/09-decisions.md#D-14 (the administrator chooses an area)'
purpose: >-
  Staff need recognizable entry points for the organization own record types. Placement and custom apps group
  those entry points within the console.
outcomes:
  - Staff need recognizable entry points for the organization own record types.
  - A custom app cannot be archived while live object placements depend on it.
actors:
  - ACT-PFM-007
  - ACT-PFM-008
  - ACT-PFM-009
surfaces:
  - console
  - api
preconditions: []
postconditions: []
permissions:
  - key: platform.custom-objects.read
    where_checked: api
    scope: tenant
  - key: platform.custom-objects.create
    where_checked: api
    scope: tenant
  - key: platform.custom-objects.update
    where_checked: api
    scope: tenant
  - key: platform.custom-objects.delete
    where_checked: api
    scope: tenant
  - key: platform.custom-records.read
    where_checked: api
    scope: record
rules:
  - id: BR-CAP-PFM-MTD-010-01
    proved_by: [ATS-PFM-MTD-010-01, TST-PFM-MTD-067, TST-PFM-MTD-074]
    statement: >-
      Placement accepts the seventeen console navigation apps or a workspace unarchived custom app and an
      optional trimmed group of one to one hundred characters, rejecting unknown apps.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-objects.integration.spec.js#refuses
      an app that is neither an application nor a custom app, naming it, and writes nothing
    origin: 'services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 D9 (Andrew, 2026-10-19)'
  - id: BR-CAP-PFM-MTD-010-02
    proved_by: [ATS-PFM-MTD-010-02, TST-PFM-MTD-013, TST-PFM-MTD-067]
    statement: >-
      An object can be created, activated and used without placement or ownership, remaining accessible
      through links and queries.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-objects.integration.spec.js#activates
      an object that has neither a placement nor an owner association type (B4: no activation rule for either)
    origin: >-
      services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 B4 (Andrew, 2026-10-19: "Allow
      neither")
  - id: BR-CAP-PFM-MTD-010-03
    proved_by: [ATS-PFM-MTD-010-03, TST-PFM-MTD-049, TST-PFM-MTD-060]
    statement: >-
      A custom app has a permanent nonreserved key, bounded label, icon and order from zero to ten thousand,
      supports archival and restoration instead of deletion and uses versioned replacement.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
      the key of a platform application, which a placement could not tell from it
    origin: >-
      services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 D15 (Andrew, 2026-10-19: "Build in
      PLAT-M5")
  - id: BR-CAP-PFM-MTD-010-04
    proved_by: [ATS-PFM-MTD-010-04, ATS-PFM-MTD-010-07, TST-PFM-MTD-049, TST-PFM-MTD-060]
    statement: >-
      App archival is refused while draft or active objects are placed there, naming those objects; archived
      objects may retain the placement.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
      to archive an app while a draft and an active object are placed in it, naming both, and leaves the app
      unchanged
    origin: >-
      services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 B3 (Andrew, 2026-10-19: "Refuse,
      name objects")
  - id: BR-CAP-PFM-MTD-010-05
    proved_by: [ATS-PFM-MTD-010-05, TST-PFM-MTD-060, TST-PFM-MTD-067]
    statement: An archived app rejects new placements and replacements until it is restored.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-objects.integration.spec.js#refuses
      a new placement in an archived custom app, and writes nothing
    origin: none
  - id: BR-CAP-PFM-MTD-010-06
    proved_by: []
    statement: Custom-app counts remain unrestricted pending the product-wide limits mechanism.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#accepts
      the tenant’s twenty-first app and lists all twenty-one (Andrew’s B2: no limit until the app-wide limits
      mechanism)
    origin: 'services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 B2 (Andrew, 2026-10-19: deferred)'
  - id: BR-CAP-PFM-MTD-010-07
    proved_by: []
    statement: >-
      App reads include placed active objects with their groups and staff access and omit draft or archived
      objects.
    kind: calculation
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/e2e/custom-apps.e2e.spec.js#leaves a draft object placed
      in the app out of its objects
    origin: none
  - id: BR-CAP-PFM-MTD-010-08
    proved_by: [ATS-PFM-MTD-009-13, ATS-PFM-MTD-010-06, ATS-PFM-MTD-010-07, TST-PFM-MTD-051]
    statement: >-
      Navigation includes only readable active objects and apps containing them, respects object-scoped roles
      and refuses callers with no record-read reach.
    kind: permission
    enforced_at:
      - api
      - service
      - console
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/e2e/custom-object-records.e2e.spec.js#answers a role
      scoped to custom_object:boat the boats alone
    origin: 'services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 D10, D15'
  - id: BR-CAP-PFM-MTD-010-09
    proved_by: []
    statement: 'Reset preserves custom apps and object placements, whereas workspace deletion removes them.'
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: documented
    source: 'services-platform:docs/custom-objects.md#Tenant lifecycle'
    origin: >-
      services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 D7 (Andrew, 2026-10-19: "Keep
      types")
scenarios:
  - id: SC-CAP-PFM-MTD-010-01
    given: Boat belongs to the Marina custom app.
    when: Open the console as a boat reader.
    then: The app menu offers Marina with Boats beneath it.
    provenance: test
    source: >-
      react-console:acceptance/custom-objects/playwright/journeys/PLAT-M5-S01.spec.ts#PLAT-M5-S01: an
      administrator creates Boat in a new app, owned by Contact, and Marina > Boats appears
edge_cases:
  - id: SC-CAP-PFM-MTD-010-02
    given: Contact ownership is configured for Boat but no app placement exists.
    when: Find the boats through the owner record.
    then: The contact page provides access without a Boats menu entry.
    provenance: test
    source: >-
      react-console:acceptance/custom-objects/playwright/journeys/PLAT-M5-S13.spec.ts#PLAT-M5-S13: a boat
      placed only on contacts has no menu entry and is reached from the contact's page
constraints: []
depends_on:
  - CAP-PFM-MTD-009
related:
  - CAP-PFM-MTD-011
journeys:
  - JNY-PFM-MTD-001
  - JNY-PFM-MTD-003
open_questions: []
concepts:
  - CON-PFM-MTD-004
evidence:
  - >-
    services-platform at ad3f6a6582843008ed0f5a17b131878d15829af1:
    modules/custom-objects/aws/api/custom-apps.routes.ts , custom-objects.routes.ts , placement-apps.ts,
    services/persistence/custom-apps.ts , db/custom-apps.table.ts; tests e2e/custom-apps.e2e.spec.js,
    e2e/custom-object-records.e2e.spec.js , integration/services/persistence/custom-apps*.integration.spec.js,
    custom-objects.integration.spec.js , unit/placement-apps.spec.ts; docs/custom-objects.md §25.
  - >-
    react-console at baf6dae382d6f41dce976c7f848667ccc37e076f:
    components/configuration/custom-objects/placement-section.tsx,
    components/configuration/resources/custom-apps.tsx, lib/configuration/areas.ts ; Playwright PLAT-M5-S01,
    S13.
controls: [TC-AI-001, TC-AI-002, TC-AI-003, TC-AI-004, TC-AI-006, TC-AI-007, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-DAT-001, TC-DAT-003, TC-DAT-004, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-MTD-04-01, FAS-PFM-MTD-04-02, FAS-PFM-MTD-04-03, FAS-PFM-MTD-04-04, FAS-PFM-MTD-04-05]
suites: [TST-PFM-MTD-013, TST-PFM-MTD-049, TST-PFM-MTD-051, TST-PFM-MTD-060, TST-PFM-MTD-067, TST-PFM-MTD-074]
---

# Make Custom Records Discoverable

## Purpose

Staff need recognizable entry points for the organization own record types. Placement and custom apps group those entry points within the console.

## How It Works

An administrator selects an existing app or defines a custom app. An object placement may include a navigation group. The object can also remain without app placement. The console requests navigation for the current staff member. Platform includes the active objects that person can read and the apps containing them. A custom app cannot be archived while live object placements depend on it. Placement provides a location to work; the corresponding permissions still determine access.

## Relationships

Object definitions hold placement. Record access filters navigation, and relationship cards provide another route to unplaced records.
