Performance and correctness findings keep turning out to be shape-dependent —
they hold on the foundation they were measured against and say nothing certain
about a differently-proportioned one. #5701 is the current example: the
proposed fix is verified exactly correct on a space-heavy foundation, and its
central trade-off inverts on an app-heavy one, which we currently cannot test.
This issue is the detail report: everything measurable about a foundation,
with accurate resource names throughout, because that is what an operator acts
on — "org_1 has 19 apps" is noise, "payments-prod has 19 apps and no quota" is
a finding. The anonymous, shareable projection of this data is #5703.
Access model
The detail report is not hidden — it is protected by the viewer's CF
access. Collection runs through the user's own endpoint connection, so a cf
admin sees the whole foundation and a non-admin sees exactly what their roles
grant. CF authorization is the gate; no masking layer of our own on top.
Whether a collection saw the whole foundation or a role-limited slice cannot
be reliably determined from the data itself, so every report and export
carries a standing generic note: the data reflects the collecting user's
visibility and makes no completeness claim (see #5703).
Dimensions that matter
Entity counts. Cheap to capture — GET /v3/<resource>?per_page=1 and read
pagination.total_results:
orgs, spaces, apps, routes, service instances (managed and user-provided
separately), service credential bindings, service brokers, service offerings,
service plans, domains, organization quotas, space quotas, security groups,
isolation segments, stacks, buildpacks, users.
Ratios, which are what actually select code paths. The absolute counts
matter less than:
- apps per space, and spaces per org
- how much of the space population is empty (spaces with 0 apps)
- how much of the org population is empty (orgs with 0 spaces, orgs with 0 apps)
- routes per app, and whether any route has multiple destinations
- bindings per app, and per service instance
Distribution, not just totals. An evenly spread 2500 spaces and a
foundation where one org holds 2400 of them exercise paging, scoping and
enrichment very differently. Record histograms and percentiles, not
min/mean/max (see #5703 for why three-number summaries mislead).
Environment facts. CF API version, whether CAPI is co-located or remote,
and rough round-trip latency to it — a slow upstream changes which
optimisations are even visible.
Composition, not just topology
Counts say how many entities exist and how they nest; composition says what
each one looks like. Same read-only collection, one extra drain
(/v3/processes):
App sizing. Memory, disk and instance count per app, as distributions with
the same percentile/zero treatment. A foundation of 64M single-instance apps
and one of 4G clustered apps exercise quota and placement code differently at
identical app counts.
App lifecycle. Buildpack vs docker split; which buildpacks are actually
used and how often, pinned vs detected; stacks in use vs stacks defined;
started vs stopped ratio — a mostly-stopped foundation exercises different
paths than a running one.
Service composition. Which offerings and plans are actually consumed, not
just how many brokers are registered; bindings per app and per service
instance as distributions.
Route and domain composition. Routes per domain, shared vs private
domains, apps with 0/1/N routes.
Org and space sizing. Which quotas are assigned and how much of each is
actually consumed — memory allocated vs in use.
Users and roles. Total users is in the counts list already; per-org role
listings make orgs with no users visible. Users are the consumers of the
console, and a raw /v3/users count mixes kinds — classify via the UAA data:
individual accounts, client credentials, system/service identities — so a
count always says which kind it counts.
Dynamic measurement
The static shape is half the report; the other half is what operations
actually cost on this foundation, measured the way the initial-load work
measured page readiness:
Backend cost baseline. For any list the UI drains, record raw CAPI time
against jetstream native-handler time for the same data, taken on a quiet page
with nothing else in flight (Resource Timing separates queueing from server
TTFB). #5701 shows why: raw /v3/organizations at 884ms vs the native handler
at 13.2s is the whole finding, and neither number means anything without the
other.
Operation timings. The same treatment for other Stratos operations — the
org/space/app drains, page-level data readiness, service catalog loads — so a
regression or an optimisation can be stated as numbers on a named foundation
rather than an impression.
Configuration cost experiments. With operation timing in place, the cost
of a configuration becomes an A/B on the same metrics. First candidate: CSP
nonces — the plumbing exists but is inert, so "what does enabling the nonce
cost per request and at initial load" is measurable before it is ever turned
on by default.
Operator visuals
The detailed data renders the views an operator acts on. Identity is the
payload here — the statistical projection buckets populations, the operator
view ranks named resources, because the next step is always per-item. The
forms below are the starting set and the examples are illustrative, never
limiting: the report is not bounded by CAPI-derived data. How a view
behaves — snapshot, periodically refreshed, or live — follows from the
meaning of the data being collected, not from its source: foundation shape
moves slowly and reads honestly as a snapshot; Diego cell state (for example
cfdot output) means something that changes second to second, so its
natural form is a live graph. Both are in scope wherever they answer an
operator question.
Ranked, named leaderboards — orgs by apps, by spaces, by assigned memory;
spaces by apps; apps by memory×instances. Depth is user-controlled (top-X or
all, default 10), with a possible deployment-time configuration parameter so
the CF operator installing the console can set the default.
Orgs by apps (foundation A, top 5 of 63)
org_1 ██████████████████ 19
system ██████████████ 15
org_2 █████████ 10
e2e ████ 5
opensource ██ 3
Dead weight as tables, not charts — orgs with no users / no spaces / no
apps, apps with no routes, spaces with services but no apps, unused
buildpacks, quota assigned but untouched. Short actionable lists; the reader's
next step is per-item. First live collection immediately proved the point —
foundation A's "orgs with no users" turned out to be two-week-old orphaned
e2e-test orgs (delete-org-20260719T…, zz-deltest-cascade) nobody knew
were still there.
Share bars for part-to-whole stories — stack migration readiness (what
fraction of apps still sit on the old stack, with the named stragglers as the
table underneath), started vs stopped, occupied vs empty:
Stack migration (foundation A): cflinuxfs4 ████████████████▏50 · cflinuxfs3 ██▏6 (56 apps)
Concentration tiles — "14.3% of all apps sit in one space" as a headline
number with the named holder a click away.
Treemap stays on the table: the org → space → app nesting is exactly the
structure the console's data layer already holds, so an area-by-size
drill-down may fall out naturally from how the registry data interconnects —
not committed for v1, not ruled out.
Dynamics forms (for the measurement leg above): raw-CAPI vs
native-handler as paired bars per resource; configuration A/Bs — nonce off →
on first — as before/after dumbbells per metric; every number median-of-N
with N stated.
Cross-foundation comparison is gated by roles like everything else: a
user compares exactly the foundations their connections can see.
Data points so far
Foundation A is the one all current live verification has run against.
Foundation B exists but is only partly populated.
| Entity |
A |
B |
| orgs |
63 |
62 |
| spaces |
2511 |
603 |
| apps |
56 |
0 |
| routes |
93 |
0 |
| service instances (managed) |
7 |
0 |
| service instances (user-provided) |
2 |
0 |
| service credential bindings |
14 |
0 |
| service brokers |
4 |
1 |
| service offerings |
6 |
1 |
| service plans |
12 |
2 |
| domains |
4 |
3 |
| organization quotas |
11 |
4 |
| space quotas |
3 |
0 |
| security groups |
3 |
3 |
| isolation segments |
1 |
1 |
| stacks |
2 |
1 |
| buildpacks |
24 |
11 |
| users |
14 |
305 |
A is space-heavy and app-light: ~40 spaces per org, and most spaces hold no
apps at all. That single profile is the basis for every performance claim made
so far.
What's missing
An app-heavy, space-light foundation — the inverse regime. Without it,
findings like #5701 can only be stated as "verified on one shape", and any fix
that trades a spaces drain against an apps drain is unfalsifiable in the
direction that matters.
Filling in B's zeros is tracked separately. What this issue asks for is that
whenever a foundation is built or changed, the detail report gets recorded
with it, so the next performance finding can say which regimes it holds in.
Performance and correctness findings keep turning out to be shape-dependent —
they hold on the foundation they were measured against and say nothing certain
about a differently-proportioned one. #5701 is the current example: the
proposed fix is verified exactly correct on a space-heavy foundation, and its
central trade-off inverts on an app-heavy one, which we currently cannot test.
This issue is the detail report: everything measurable about a foundation,
with accurate resource names throughout, because that is what an operator acts
on — "org_1 has 19 apps" is noise, "payments-prod has 19 apps and no quota" is
a finding. The anonymous, shareable projection of this data is #5703.
Access model
The detail report is not hidden — it is protected by the viewer's CF
access. Collection runs through the user's own endpoint connection, so a cf
admin sees the whole foundation and a non-admin sees exactly what their roles
grant. CF authorization is the gate; no masking layer of our own on top.
Whether a collection saw the whole foundation or a role-limited slice cannot
be reliably determined from the data itself, so every report and export
carries a standing generic note: the data reflects the collecting user's
visibility and makes no completeness claim (see #5703).
Dimensions that matter
Entity counts. Cheap to capture —
GET /v3/<resource>?per_page=1and readpagination.total_results:orgs, spaces, apps, routes, service instances (managed and user-provided
separately), service credential bindings, service brokers, service offerings,
service plans, domains, organization quotas, space quotas, security groups,
isolation segments, stacks, buildpacks, users.
Ratios, which are what actually select code paths. The absolute counts
matter less than:
Distribution, not just totals. An evenly spread 2500 spaces and a
foundation where one org holds 2400 of them exercise paging, scoping and
enrichment very differently. Record histograms and percentiles, not
min/mean/max (see #5703 for why three-number summaries mislead).
Environment facts. CF API version, whether CAPI is co-located or remote,
and rough round-trip latency to it — a slow upstream changes which
optimisations are even visible.
Composition, not just topology
Counts say how many entities exist and how they nest; composition says what
each one looks like. Same read-only collection, one extra drain
(
/v3/processes):App sizing. Memory, disk and instance count per app, as distributions with
the same percentile/zero treatment. A foundation of 64M single-instance apps
and one of 4G clustered apps exercise quota and placement code differently at
identical app counts.
App lifecycle. Buildpack vs docker split; which buildpacks are actually
used and how often, pinned vs detected; stacks in use vs stacks defined;
started vs stopped ratio — a mostly-stopped foundation exercises different
paths than a running one.
Service composition. Which offerings and plans are actually consumed, not
just how many brokers are registered; bindings per app and per service
instance as distributions.
Route and domain composition. Routes per domain, shared vs private
domains, apps with 0/1/N routes.
Org and space sizing. Which quotas are assigned and how much of each is
actually consumed — memory allocated vs in use.
Users and roles. Total users is in the counts list already; per-org role
listings make orgs with no users visible. Users are the consumers of the
console, and a raw
/v3/userscount mixes kinds — classify via the UAA data:individual accounts, client credentials, system/service identities — so a
count always says which kind it counts.
Dynamic measurement
The static shape is half the report; the other half is what operations
actually cost on this foundation, measured the way the initial-load work
measured page readiness:
Backend cost baseline. For any list the UI drains, record raw CAPI time
against jetstream native-handler time for the same data, taken on a quiet page
with nothing else in flight (Resource Timing separates queueing from server
TTFB). #5701 shows why: raw
/v3/organizationsat 884ms vs the native handlerat 13.2s is the whole finding, and neither number means anything without the
other.
Operation timings. The same treatment for other Stratos operations — the
org/space/app drains, page-level data readiness, service catalog loads — so a
regression or an optimisation can be stated as numbers on a named foundation
rather than an impression.
Configuration cost experiments. With operation timing in place, the cost
of a configuration becomes an A/B on the same metrics. First candidate: CSP
nonces — the plumbing exists but is inert, so "what does enabling the nonce
cost per request and at initial load" is measurable before it is ever turned
on by default.
Operator visuals
The detailed data renders the views an operator acts on. Identity is the
payload here — the statistical projection buckets populations, the operator
view ranks named resources, because the next step is always per-item. The
forms below are the starting set and the examples are illustrative, never
limiting: the report is not bounded by CAPI-derived data. How a view
behaves — snapshot, periodically refreshed, or live — follows from the
meaning of the data being collected, not from its source: foundation shape
moves slowly and reads honestly as a snapshot; Diego cell state (for example
cfdotoutput) means something that changes second to second, so itsnatural form is a live graph. Both are in scope wherever they answer an
operator question.
Ranked, named leaderboards — orgs by apps, by spaces, by assigned memory;
spaces by apps; apps by memory×instances. Depth is user-controlled (top-X or
all, default 10), with a possible deployment-time configuration parameter so
the CF operator installing the console can set the default.
Dead weight as tables, not charts — orgs with no users / no spaces / no
apps, apps with no routes, spaces with services but no apps, unused
buildpacks, quota assigned but untouched. Short actionable lists; the reader's
next step is per-item. First live collection immediately proved the point —
foundation A's "orgs with no users" turned out to be two-week-old orphaned
e2e-test orgs (
delete-org-20260719T…,zz-deltest-cascade) nobody knewwere still there.
Share bars for part-to-whole stories — stack migration readiness (what
fraction of apps still sit on the old stack, with the named stragglers as the
table underneath), started vs stopped, occupied vs empty:
Concentration tiles — "14.3% of all apps sit in one space" as a headline
number with the named holder a click away.
Treemap stays on the table: the org → space → app nesting is exactly the
structure the console's data layer already holds, so an area-by-size
drill-down may fall out naturally from how the registry data interconnects —
not committed for v1, not ruled out.
Dynamics forms (for the measurement leg above): raw-CAPI vs
native-handler as paired bars per resource; configuration A/Bs — nonce off →
on first — as before/after dumbbells per metric; every number median-of-N
with N stated.
Cross-foundation comparison is gated by roles like everything else: a
user compares exactly the foundations their connections can see.
Data points so far
Foundation A is the one all current live verification has run against.
Foundation B exists but is only partly populated.
A is space-heavy and app-light: ~40 spaces per org, and most spaces hold no
apps at all. That single profile is the basis for every performance claim made
so far.
What's missing
An app-heavy, space-light foundation — the inverse regime. Without it,
findings like #5701 can only be stated as "verified on one shape", and any fix
that trades a spaces drain against an apps drain is unfalsifiable in the
direction that matters.
Filling in B's zeros is tracked separately. What this issue asks for is that
whenever a foundation is built or changed, the detail report gets recorded
with it, so the next performance finding can say which regimes it holds in.