Client Visibility Model
Status: decided
Updated: 2026-07-30
Scope: org product behavior, portal projections, and all client-facing publication/file-sharing flows
Purpose
This document defines how client exposure follows the purpose of each record.
The product should stop mixing multiple mental models such as:
client visibleversusinternal onlysharedversusinternal- feature-specific visibility rules that feel unrelated
The product should answer the exposure question through structure wherever it can. Operators should not have to repeatedly classify every record as private or client-facing.
Core model
There are two outcomes:
- Client visible
- Internal
They are not a universal field that every record must expose. A record either:
- inherits exposure from its client-facing parent;
- is internal by its record type and purpose; or
- offers an explicit exposure choice because both outcomes are legitimate.
This makes the safe behavior predictable without adding a switch to every form.
Client-visible records can appear in a client-facing surface such as:
- the portal;
- a client-facing project timeline;
- a client-facing contract view;
- a shared file workspace; or
- a notification/email flow that exists to inform the client.
Internal means the record is for agency operators only.
Internal records must never be casually repurposed into client-facing records by a buried toggle or ambiguous copy. They should be represented by an internal record type such as a note or task context item.
Feature Rules
Projects
Projects are client-facing delivery containers.
- a project is not an internal task bucket;
- a project exists to represent delivery state in a way that can be communicated outward;
- new projects should default to client-visible;
- publishing a project update is a client-facing act; and
- agencies that want private work tracking should use notes, time, internal files, or external PM tools instead of turning projects into hidden internal containers.
Product consequence:
Projects are client-visible whenever the client has portal access. Projects and tasks do not expose a visibility control in normal product UI.
Project tasks and blockers inherit the project's exposure. They do not ask the operator to repeat the visibility decision for each child record. Notes, time entries, and internal files keep their feature-level rules because they are agency working material rather than project delivery content.
Project ownership is also the default work-assignment context:
- new projects default to their creator, with owner and initial status chosen in the project form when a different setup is needed;
- new tasks default to the project owner while still allowing an explicit unassigned state; and
- new blockers default to the project owner.
Project updates
Project updates are never a separate visibility system.
- drafts are internal because they are drafts;
- published updates are client-visible because they are published project communication;
- update publication should inherit the project's client-facing nature rather than introduce another parallel visibility toggle.
Files
File items can be either client-visible or internal.
- client-visible drive items are part of the shared client workspace;
- internal drive items are agency-only support material;
- file requests, upload links, and portal file browsing should operate only on the client-visible side unless a clearly internal agency workflow is involved.
Important:
shared is an implementation/storage term. In product language, it means client visible.
Contracts
Contracts can be client-visible or internal.
- published/shared contract records can appear in the client relationship surface;
- internal contract drafts, prep copies, or support material remain agency-only.
Important:
Contract visibility should use the same user-facing terms as Files, even if both currently reuse the same underlying validator.
Notes
Notes are internal.
- notes are working memory;
- notes are not a client-facing publishing tool;
- if note content should become client-facing, create a project update or client-facing document from it.
Time
Time entries are internal.
- the record itself is internal;
- summaries derived from time may influence billing or client reporting later;
- raw time logs are not a client-facing visibility surface by default.
Billing and commercial records
Invoices, subscriptions/retainers, and quotes are inherently client-related records, but they are not governed by the same binary visibility toggle used by file collaboration.
They follow their own commercial lifecycle and portal exposure rules.
The portal presents client-facing commercial documents through one
Agreements destination. Quotes retain their billing ownership and contracts
retain their contract ownership; the combined destination is a projection, not
a new parallel record type. Delivery-scoped portal grants may see published
contracts but not quotes. Billing and full grants may see both.
Client decisions and feedback
Acknowledgement, approval, change-request, and feedback records do not change an artifact's visibility. They are client relationship signals attached to an already client-visible project update, file, or contract. The agency and portal must read the same response record.
UI Rules
Do not show an exposure label or control when the behavior follows directly from the record type or parent. In particular, project lists, project forms, task forms, and task detail do not repeat a visibility decision.
Where an explicit choice is materially necessary, the UI uses:
Client visibleInternal
Do not mix the following in equivalent places:
Sharedwhen the real meaning is client-visibleClient workspacein one area andClient visiblein another for the same underlying ruleInternal onlyon one feature andInternalon another unless the distinction is materially important
Preferred guidance for those explicit choices:
- use
Client visiblefor labels, badges, filters, and fields; - use
Internalfor agency-only records; - reserve descriptive helper text for cases where the consequence is not obvious.
Current Implementation Mapping
The current codebase still stores multiple values:
- projects and project tasks: legacy
client_visibleandinternal_onlyvalues are temporarily retained for compatibility, but normal application writes and migration normalize them toclient_visible - drive items:
sharedandinternal - contracts:
sharedandinternal
Until storage is unified, the app should map them like this:
| Storage value | Product meaning |
|---|---|
client_visible | Client visible |
shared | Client visible |
internal_only | Internal |
internal | Internal |
Migration Direction
The product direction is not to add more visibility variants.
The direction is:
- derive exposure from record purpose before asking the user;
- make projects and their tasks inherit client delivery exposure;
- keep internal agency work in notes, task context, time, and internal artifacts;
- retain explicit exposure only for artifacts such as Files and contracts where both states are meaningful; and
- remove redundant badges, columns, and switches so operators can predict what a record does without rereading the screen.