Skip to content

docs(experiments): specification for the Experiments portlet - #37144

Open
oidacra wants to merge 2 commits into
issue-37004-experiments-results-screenfrom
issue-36987-experiments-portlet-spec
Open

docs(experiments): specification for the Experiments portlet#37144
oidacra wants to merge 2 commits into
issue-37004-experiments-results-screenfrom
issue-36987-experiments-portlet-spec

Conversation

@oidacra

@oidacra oidacra commented Aug 20, 2026

Copy link
Copy Markdown
Member

What

A descriptive specification for the standalone Experiments portlet as a whole —
across the list, create/update and results screens — rather than for any one of
them. It captures the decisions and invariants that until now lived only in
docblocks and commit messages, and it is the baseline the portlet's remaining
screens are planned against.

Refs #36987. Covers #36989, #37003, #37004 as built, and #37005, #37006, #37007,
#37008 as pending.

Stacked on #37135. Base is issue-37004-experiments-results-screen, so this
diff is one new file. Retarget to main after #37135 merges.

Why it reads as documentation, not as a request

Most of this feature already ships. Every requirement therefore carries a [BUILT]
or [PENDING] marker, and the spec states in its opening section that the
constitution's TDD gate (Principle V) applies to the pending work and to future
changes — not retroactively to code that is already merged or in review. It
does not reconstruct a Red phase that never happened.

What it records

  • Five prioritized user journeys with 40 acceptance scenarios, including the two
    that are easy to lose: keeping the portlet usable when analytics is broken, and
    keeping the legacy tree deletable in one commit.
  • 77 functional requirements, grouped cross-cutting / per screen / analytics
    health / coexistence.
  • A separate Architectural Invariants section holding the eight load-bearing
    constraints in implementation terms on purpose — the events-plugin store
    contract and its @ngrx/signals version constraint, why results are fetched
    lazily and never polled, why significance is the backend's call rather than a
    conversion-rate comparison, why a lost report must not cost the whole screen,
    and why nothing in the new tree may import from old/. Paraphrasing these into
    technology-neutral language would have destroyed the constraint they impose on
    the remaining screens.
  • Explicit out-of-scope list, so the exclusions already agreed on the issues are
    not reopened.
  • Legacy Considerations and a traceability table mapping requirement ranges to
    issues, PRs, source files and commits.

Follow-ups this surfaced

Planning against the spec turned up three things worth fixing on the pending
issues before they are picked up:

Scope of the diff

One new file, specs/36987-experiments-portlet/spec.md. The plan, research and
checklist artifacts stay local, per the spec-folder commit policy in
.specify/CUSTOMIZATIONS.md.

Verification

Documentation only — no source, no build config, no tests touched. Prettier is
scoped to core-web/ and does not cover specs/ at the repo root, so there is no
formatting step that applies. Nothing to lint or run.

This PR fixes: #36987

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @oidacra's task in 1m 30s —— View job


Review: Experiments Portlet spec

  • Read specs/36763-portlet-experiments/spec.md
  • Cross-check verifiable claims against source
  • Post review

This is a documentation-only PR (one new spec file, +736). There's no code, build config, or test to break, so the review is about internal consistency and whether the concrete, verifiable claims match the tree. I spot-checked the load-bearing ones.

Verified accurate:

  • AI-2 — @ngrx/signals is 21.1.1 ✅ (core-web/package.json:86) and withEffects genuinely doesn't exist; the codebase uses withEventHandlers (core-web/libs/portlets/CLAUDE.md:106, image-editor store features).
  • FR-062 — MINIMUM_SESSIONS_TO_SHOW_CHART = 10, and the results store gates on sessions.total >= it (dot-experiments-results.store.ts:118). The "settled at ten, name is not" framing matches reality (the old/ store gates the chart on the same constant, so the name really does under-describe the summary-table use).
  • FR-074 — no file outside src/lib/old/ imports from old/; the legacy→shared direction holds, so "deletable in one commit" is a real invariant, not aspiration.
  • Screens table, FR-036's eight rules, AI-9's two-keys-not-three all check out internally.

New Issues

  • 🟡 Medium: specs/36763-portlet-experiments/spec.md:732 — Traceability pointer is slightly wrong for the one threshold the spec discusses at length. It maps "Named thresholds, defaults, and the state shapes" to .../portlet/src/lib/shared/constants.ts, but that file holds only the list defaults (page size, sort field/direction, empty filter sets). MINIMUM_SESSIONS_TO_SHOW_CHART — the threshold behind FR-062 and Open Question Test Branch and Commit #2 — actually lives in core-web/libs/dotcms-models/src/lib/dot-experiments-constants.ts. A reader following the traceability row to find it won't. Consider adding the dotcms-models constants file to that row (or splitting thresholds vs. list-defaults). Fix this →

  • 🟡 Medium: PR description vs. diff — the PR body says the diff is "One new file, specs/36987-experiments-portlet/spec.md", but the file actually added is specs/36763-portlet-experiments/spec.md. Not a defect in the spec (line 11 explains the directory is named after epic Experiments: A/B Testing v2 #36763), but the retarget-to-main note keys off the wrong path — worth correcting the PR body so the follow-up isn't looking for a folder that doesn't exist.

Nothing here is blocking. The spec is accurate against the code I could verify, and the [BUILT]/[PENDING] markering plus the TDD-gate carve-out is handled honestly. The three follow-ups it surfaced (#37007/#37008 acceptance-criteria drift) are the kind of thing a spec pass is supposed to catch.
· issue-36987-experiments-portlet-spec

@oidacra
oidacra force-pushed the issue-36987-experiments-portlet-spec branch 2 times, most recently from 5305f6a to 64ee5b7 Compare August 20, 2026 18:17
Comment thread specs/36763-portlet-experiments/spec.md
@oidacra
oidacra force-pushed the issue-36987-experiments-portlet-spec branch from 64ee5b7 to de81d1b Compare August 20, 2026 19:15
Descriptive spec for the standalone Experiments portlet as a whole, across
all its screens, capturing the decisions and invariants that until now lived
only in code and commit messages.

Screens 1-3 are already built, so requirements carry a [BUILT] or [PENDING]
marker and the spec states plainly that the constitution's TDD gate applies
to the pending work and to future changes, not retroactively to code that
already ships.

The load-bearing invariants are quarantined in their own section, stated in
implementation terms on purpose: the events-plugin store contract and its
version constraint, why results are fetched lazily and never polled, why
significance is the backend's call, why a lost report must not cost the whole
screen, and why nothing in the new tree may import from old/.

Refs #36987
@oidacra
oidacra force-pushed the issue-36987-experiments-portlet-spec branch from de81d1b to 5e78ef9 Compare August 20, 2026 19:46
Review of the spec turned up fourteen places where it described software
that does not exist, or claimed an invariant the code deliberately breaks.
Every one was reproduced against the code before being written down.

Template conformance:

- Status is Draft, as the template and both sibling specs use until PR 1 is
  approved. The descriptive-baseline caveat already lives in Read This First.
- The issue is carried by a GitHub Issue field in the siblings' format,
  naming the parent epic, rather than by two bespoke fields.
- User Story 5 is gone. Its three acceptance scenarios were already FR-074,
  FR-075 and FR-076 verbatim, and the story admitted it was a developer
  guarantee rather than a user journey.
- SC-009 no longer counts twelve behaviors it never listed. It states the
  rule the parity walk must satisfy; producing the list is plan work, now
  recorded under Open Questions.

Claims the code contradicts:

- FR-006 permitted no departure from the shared status-to-actions map. Two
  exist and both are deliberate, so it now names them and forbids a third.
  Restore gets FR-021a: rendered for an archived experiment, permanently
  inert until the transition lands with #36988, gated by an inline status
  check because the shared map has no restore key and acquiring one would
  change what the legacy list reads.
- FR-021 advertised restore as an offered action. It is inert.
- FR-022 and FR-006 collided and an Assumption covered the mechanism. The
  collision is resolved in the spec: the new list does not consult the map
  for View Results, and the map's results key stays RUNNING and ENDED for
  the legacy list. Widening it is forbidden.
- FR-075 said only import lines may change under the legacy subtree, while
  seventeen files and 973 lines left it. Relocating a component out of the
  subtree with its own spec is the intended outcome; the prohibitions are
  duplication and any new-to-legacy import, and neither is violated today.
  FR-075a permits duplication only where importing would mean touching
  legacy code, and requires it declared at the definition site -- as the
  one instance, the status-to-severity map, already is.
- FR-076 constrained the legacy test count. It constrains legacy behavior.
- AI-9 counted three dialog keys. Two exist: the Results shell owns one,
  Configure and the list share the other because they never co-mount, and
  the summary table raises on the shell's. The invariant is co-mounting, so
  sharing is the default.
- Legacy Considerations said the page-editor entry point sits behind a flag
  that is off by default. The route mounts unconditionally and
  LOAD_FRONTEND_EXPERIMENTS is read nowhere; the gate is Screen 4 (#37005).
- FR-062 read as accidental coupling to the chart. The threshold is one
  constant on purpose; MINIMUM_SESSIONS_TO_SHOW_CHART is the wrong name for
  what it now gates, and the rename is a tracked follow-up.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Experiments v2 — Part 2: Build the new Experiments UI

2 participants