myxce/.kiro/specs/scheduler-sim-diagnostics/research.md
Rongsong Shen eb23c158ae
feat(scheduler-sim-diagnostics): promote byte-unit formatting to shared lib (task 7)
Adds Requirement 6 (Human-Readable Units for Byte-Valued Limit Amounts)
to the spec: requirements, design, research, and tasks 7-10.

Task 7 (Foundation): promotes the REPL's byte-unit scaling algorithm
and byte-valued-resource classifier into src/lib/utils.c/utils.h
(xce_format_bytes_human, xce_is_bytes_valued_resource), alongside a
new display-mode-aware composer (xce_format_units_value,
xce_units_mode_t) -- following the fmt_jobid()/fmt_jobid_ex()
precedent. repl_fmt.c's scheduler-only functions become one-line
delegating wrappers. repl_info.c's repl_format_numeric_resource_value()
(shared by info worker/info resource, not just the upcoming info
limit) is retyped to delegate to the new shared composer via a small
translate_units_mode() bridge, so every consumer benefits from one
implementation going forward.

No existing symbol's signature changed; only new symbols were added.
Confirmed behavior-preserving: full scheduler suite 85/85 unchanged,
including info worker/info resource's own regression tests. One
narrow, untested, out-of-requirement-scope corner case found and
documented (swp's RAW/BOTH mode rendering under the new single-value
composer interface) -- logged in tasks.md's Implementation Notes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
2026-09-20 04:40:44 +00:00

132 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Research & Design Decisions
## Summary
- **Feature**: `scheduler-sim-diagnostics` — Requirement set 1 (`info limit` REPL command)
- **Discovery Scope**: Extension (extends `scheduler-core`'s existing REPL `info` command family and reuses the limit plugin's existing library-level formatters)
- **Key Findings**:
- The REPL `info` command family is a single dispatch function (`repl_cmd_info()`, `repl_info.c`) with one `else if` branch per subcommand — adding `limit` is a mechanical extension of an existing, well-established pattern.
- The limit plugin's private state (`limit_plugin_data_t.limits`/`.instances`) is genuinely inaccessible outside `src/scheduler/src/plugins/limit.c` today — no header declares the type — confirming the brief's stated gap. Two new one-line accessor functions close it.
- Two library-level formatters already exist and already do most of Requirement 1's work: `fmt_limit()` and `fmt_consumer()`/`fmt_group()` (`src/lib/xcelimit.c`, `src/lib/group.c`), including correct inclusion/exclusion membership rendering (`fmt_group()` already prefixes excluded members with `~`). These are adopted directly rather than rebuilt.
- The live-usage read path (GLOBAL/SHARED single value vs. per-host breakout) is architecturally identical to the pattern `repl_info_resource_one()` already implements for ordinary resources (bug-1593) — the limit's synthetic capacity resource (`vres->vres`) is a real, already-registered `xce_resource_t` carrying the same `XCE_RESF_GLOBAL`/`XCE_RESF_SHARED` flags, so the same shared-vs-per-host read logic applies unmodified.
## Research Log
### Limit plugin data model and matching semantics
- **Context**: Requirement 1 AC1/AC5/AC6 need to know exactly what "scope dimensions" and "restricted membership" mean at the code level, since an earlier requirements-review pass surfaced a scope-vs-runtime ambiguity.
- **Sources Consulted**: `src/include/xce/types.h` (`xce_limit_t`, `xce_limit_config_t`, `xce_consumer_t`, `xce_vres_t`), `src/lib/xcelimit.c` (`match_limit`, `build_vres_name`, `is_host_based_limit`, `instantiate_limit`), `src/scheduler/src/plugins/limit.c` (`match_job_host_limit_`, `match_job_global_limit_`, `update_limit_res_value`, `limit_prepare_allocation`), `.wolf/buglog.json` bug-1784 through bug-1790 (the gitea#46 limit-matching bug chain, 2026-09-07).
- **Findings**:
- `xce_consumer_type_t` has exactly four values (`CONSUMER_USER`, `CONSUMER_QUEUE`, `CONSUMER_PROJECT`, `CONSUMER_HOST`) — the four scope dimensions in Requirement 1 AC1 are the complete set, no fifth dimension exists.
- `is_host_based_limit()` forces per-host live-instance tracking whenever the limit's value is a percentage of a resource that is not `XCE_RESF_GLOBAL`/`XCE_RESF_SHARED` — independent of whether `CONSUMER_HOST` was configured. This is real, current behavior (not hypothetical), confirmed via source and via bug-1789/1790's live-verified fix history for the equivalent allocation-side computation.
- `xce_group_t` supports both `members` and `ex_members` (exclusion). Confirmed as real production configuration via bug-1789's own note describing a live-configured limit ("`limit1`... all users except rshen").
- `fmt_group()` (`src/lib/group.c:388`) already renders both lists correctly (`~name` prefix for exclusions, `all`/`[]` shortcuts for the builtin groups) and is already `extern`-declared in `group.h`. `fmt_consumer()`/`fmt_limit()` (`src/lib/xcelimit.c`) already compose `fmt_group()` into a full per-limit configuration dump.
- The bug-1789/1790 chain (global limits under-counting usage against per-host resources) is fully closed as of 2026-09-07 — the underlying accounting this feature displays is trustworthy across all resource/scope combinations by the time this spec is implemented.
- **Implications**: Requirement 1's "configured setting" display can adopt `fmt_limit()` directly instead of re-deriving formatting logic; only the effective-host-scope annotation (Req 1.6) and the live-instance/usage rendering (Req 2, Req 3) are genuinely new code.
### REPL command dispatch and existing `info resource` precedent
- **Context**: Need the concrete integration point for a new `info limit` subcommand and a working precedent for the GLOBAL/SHARED-vs-per-host live-value read split Requirement 2/3 require.
- **Sources Consulted**: `src/scheduler/src/repl.c` (`repl_dispatch`), `src/scheduler/src/repl_info.c` (`repl_cmd_info`, `repl_info_resource`, `repl_info_resource_one`), `src/scheduler/src/plugin.c` (`plugin_register`, `plugin_register_core`), `src/scheduler/include/plugin.h`, bug-1593 (`repl_info_resource_one`'s original GLOBAL/SHARED-vs-host-based display bug and fix).
- **Findings**:
- `repl_cmd_info()` is a flat `strcmp` chain on `tokens[1]`; every existing subcommand (`job`, `queue`, `worker`, `resource`, ...) follows the identical `<name>|all` argument shape Requirement 4 asks for.
- `repl_info_resource_one()` (post bug-1593) reads a GLOBAL/SHARED resource's value from `sim->core.sharedInst`/`sharedInUse` at `r->idx`, and explicitly declines to fabricate a value for a host-based resource, deferring to `info worker <name>` instead. This is the exact pattern Requirement 2/3's per-instance read path reuses.
- Plugins are registered into `core->plugins`, a name-keyed handle (`key_add(core->plugins, plugin->name, plugin)`); the limit plugin registers under the fixed name `"xce_sched_limit"` (`limit.c:557`). No plugin is ever renamed or re-registered under a different name at runtime.
- Synthetic limit-capacity resources (`vres->vres`, created by `add_vres()`) are inserted into the *same* `core.resmgr` table `info resource`/`info worker` already iterate, flagged `XCE_RESF_LIMIT` (already a named flag in `repl_resource_flags_str`'s table). A GLOBAL-scoped limit's synthetic resource can therefore already appear in `info resource all`/`info resource <name>` today, labeled with the `LIMIT` flag — this is pre-existing behavior, unrelated to and unmodified by this spec.
- **Implications**: New command wiring is a single new branch in `repl_cmd_info()` plus one new static function `repl_info_limit()` in `repl_info.c`, following `repl_info_resource()`'s two-level (`_all` iterates, `_one` prints) shape.
## Architecture Pattern Evaluation
| Option | Description | Strengths | Risks / Limitations | Notes |
|--------|-------------|-----------|----------------------|-------|
| Direct struct exposure | Move `limit_plugin_data_t` into a shared header, let `repl_info.c` include it directly | Fewer functions | Breaks the plugin's existing data encapsulation; every other plugin's private state stays file-local — this would be the only exception | Rejected |
| Opaque query API (iterator_t out-params, per-limit filtered iteration) | New multi-function query interface returning filtered iterators | Nice call-site ergonomics | New abstraction with only one consumer today; no existing accessor in this codebase does filtered iteration — the rest (`sched_get_job`, `core->plugins`, `core->jobs`) hand back a raw kvmgr/xlist handle and let the caller `iter_make`/`iter_next` | Rejected — over-built for current scope |
| **Selected: two raw-handle accessors** | `limit_get_configured_limits(core)` / `limit_get_usage_instances(core)` return the plugin's existing `var` (kvmgr/xlist) handles, or `NULL` if the plugin isn't registered; caller iterates with the same `iter_make`/`iter_next`/`iter_destroy` idiom used everywhere else in this codebase | Matches every existing accessor pattern (`core->jobs`, `core->queues`, `core->plugins`); minimal new surface (2 functions); no new types | Caller must filter usage instances by `vres->limit == target_limit` itself (pointer comparison) since there is no per-limit index — acceptable, this table is small (configured-limit count × scope-instance count, not job count) | Selected |
## Design Decisions
### Decision: Plugin accessor shape
- **Context**: `limit_plugin_data_t` (`limits`, `instances`) is a file-local type in `limit.c`; Requirement 1/2/3 need read access to both from `repl_info.c`.
- **Alternatives Considered**:
1. Expose the struct itself in a shared header.
2. Build a filtered query API (per-name lookup, per-limit instance iteration) as new plugin-owned functions.
3. Expose the two raw `var` handles as accessor functions, encapsulating the plugin-name lookup (`"xce_sched_limit"`) inside the accessor itself so `repl_info.c` never needs to know it.
- **Selected Approach**: (3). `limit_get_configured_limits(xce_sched_core_t* core)` and `limit_get_usage_instances(xce_sched_core_t* core)`, both declared in `src/scheduler/include/plugin.h` next to `register_limit`, defined in `limit.c`. Each does its own `key_find(core->plugins, "xce_sched_limit", &plugin)` and returns `plugin->data`'s relevant field (or `NULL` if the plugin is not registered or not yet initialized).
- **Rationale**: Matches the codebase's existing accessor idiom exactly (raw handle + shared iterator trio), keeps the plugin-name string fully inside `limit.c` (the one place that already owns it via `register_limit()`), and adds the minimum surface needed — no speculative per-limit-name lookup function, since both AC1's "specific name" and AC2's "all" cases are satisfiable by iterating the same handle and filtering in the caller.
- **Trade-offs**: `repl_info_limit()` finds a limit by name via a direct `key_find()` (`privdata->limits` is a name-keyed kvmgr — confirmed by bug-1685's duplicate-name `-EEXIST` handling, so this is O(1)-ish, not a scan) and a linear scan + pointer-equality filter to find that limit's usage instances in `privdata->instances` (keyed by vresname, not by owning limit — no index avoids this scan; accepted, same scale argument as below).
- **Follow-up**: None — this is a stable, minimal boundary; a future capability under this same open-ended spec (e.g. decision-reasoning trace) does not need to touch this accessor.
### Decision: Reuse `fmt_limit()`/`fmt_group()` for configured-setting display
- **Context**: Requirement 1 AC1/AC5 need to print a limit's name, per-dimension scope, and inclusion/exclusion membership.
- **Alternatives Considered**:
1. Write new formatting code in `repl_info.c` mirroring the other `repl_info_*_one()` functions' inline `fprintf` style.
2. Call the existing `fmt_limit()` (which internally calls `fmt_consumer()`/`fmt_group()`) into a stack buffer and print it, then append the REPL-specific effective-scope note and live-usage lines the library formatter cannot know about.
- **Selected Approach**: (2).
- **Rationale**: `fmt_limit()` already produces correct, exclusion-aware output and is already used/tested at the library level (`src/lib/testsuite/test_limit.c`); duplicating that logic in `repl_info.c` would both waste effort and risk drifting out of sync with the library's own definition of "configured setting." Build-vs-adopt: adopt.
- **Trade-offs**: `fmt_limit()`'s output format (multi-line, `PER_USER = [...]` style) is fixed by the library; the REPL wraps it with a `[sim]` prefix per line rather than reformatting it, consistent with how other multi-line `info` output is already prefixed in this codebase (e.g. `info jobs`).
- **Follow-up**: None.
### Decision: Effective-scope annotation is REPL-layer, not library-layer
- **Context**: Requirement 1 AC6 (implicit host-scoping for a percent-based limit on a non-shared resource) is not something `fmt_limit()` knows about — it only reflects the *configured* `consumers[]` array.
- **Alternatives Considered**:
1. Change `fmt_limit()` itself to call `is_host_based_limit()` and inject a synthetic `PER_HOST` line.
2. Keep `fmt_limit()` unchanged (it is a general-purpose library formatter used elsewhere, e.g. by any future non-REPL consumer that wants the raw configured shape) and have `repl_info_limit()` call `is_host_based_limit()` itself, appending an explanatory note only when `consumers[CONSUMER_HOST] == NULL` and the check returns true.
- **Selected Approach**: (2).
- **Rationale**: `is_host_based_limit()` is already `extern`-declared in `xcelimit.h` and used exactly this way (a yes/no runtime check) elsewhere; the "effective scope" concept is specific to what an *operator inspecting the REPL* needs to know, not a general property of the limit's configured shape — keeping it at the REPL layer avoids changing a shared library formatter's contract for one caller's needs.
- **Trade-offs**: None material.
- **Follow-up**: None.
## Risks & Mitigations
- **Risk**: Linear scan over `privdata->instances` to filter by `vres->limit == target_limit` (this table is keyed by vresname, not by owning limit, so no index avoids the scan) could be surprising if a deployment ever configures an unusually large number of distinct scope instances (e.g. thousands of users × per-user limits). — **Mitigation**: Accepted for this spec; the existing `match_job_host_limit_`/`match_job_global_limit_` matching path already does a `key_find` per job per limit against the same table, so this table's size is already load-bearing elsewhere in the scheduler's hot path — a read-only REPL scan over it is not a new scaling concern this spec introduces.
- **Risk**: Because `is_host_based_limit()` takes the plugin's `resmgr` handle (a `var`), calling it from `repl_info.c` requires reaching `sim->core.resmgr` — already public (used directly by `repl_info_resource()` today), so no new exposure needed. — **Mitigation**: N/A, confirmed no new risk.
- **Risk**: `build_vres_name()` never encodes host in the vresname it builds — an implementer could reasonably assume "one `xce_vres_t` per (user, queue, project, host)" and design storage/iteration around a cardinality that does not exist. — **Mitigation**: `design.md`'s Data Models section states the correct cardinality explicitly (one `xce_vres_t` per (user, queue, project); host is produced by a per-worker fan-out over that single `xce_vres_t`'s shared resource index, not by additional `xce_vres_t` instances) and cites this finding.
- **Risk**: Build wiring for the new white-box test — `test_repl_resource_table.c`'s existing `#include` set omits `plugins/limit.c` and `resource.c`; naively adding a limit-info test there, or to a careless new file, reproduces the exact undefined-reference class bug-1762 already hit once for a sibling REPL feature. — **Mitigation**: `design.md`'s Testing Strategy calls this out explicitly and directs a new sibling test file (`test_repl_limit_table.c`) with its own white-box include set, real (not stubbed) `resource.c` since the test needs non-trivial accounted-usage values.
## Additional Findings (design review pass)
- `xce_sched_worker_t` carries two separate zarrays per resource idx: `.resvals` (capacity) and `.resInUse` (accounted usage) — confirmed via `sched_check_host_satisfy_()` (`resource.c`), which reads both the identical way this design's per-host instance display needs to.
- `instantiate_limit()` and `update_limit_res_value()` run back-to-back inside the same `match_job_host_limit_`/`match_job_global_limit_` call (`plugins/limit.c`) — a matched `xce_vres_t`'s *capacity* entry is therefore always populated by the time the `xce_vres_t` itself is observable via the new accessors; only its *in-use* entry can legitimately be absent (genuine zero usage, not "no instance").
- `resource-accounting.md`'s reservation-vs-actual-usage reconciliation is explicitly scoped to resources flagged both `XCE_RESF_DYNAMIC` and `XCE_RESF_CONSUMABLE`. A limit's synthetic capacity resource (`add_vres()`) is flagged `XCE_RESF_LIMIT | XCE_RESF_CONSUMABLE | XCE_RESF_RELEASE` plus `GLOBAL|SHARED` or `DEFAULT_HOST` — never `XCE_RESF_DYNAMIC` — so that steering document does not apply here; the capacity/in-use pair this design reads is an exact scheduler-computed accounting number, not an OS-reconciled estimate.
- `src/scheduler/testsuite/test_no_bare_printf.c` regression-tests every file under `src/scheduler/src/` for bare `printf()` calls (must use `fprintf(stdout, ...)`); bug-1599 documents a known scanner false-positive on *comment prose*, not an exemption for real code.
## Requirement 6 Research (added 2026-09-19 — Human-Readable Units for Byte-Valued Limit Amounts)
### Two independent root causes behind one operator-reported symptom
- **Context**: A live REPL transcript showed a limit's configured amount (`mem = 5000000000.000000`) and its usage amount (`accounted=9011200.00 capacity=4635344384.00`) both as raw byte counts, despite the REPL already having a general byte-formatting convention used by `info worker`/`info resource`.
- **Sources Consulted**: `src/scheduler/src/repl_info.c` (`repl_print_limit_usage()`, `repl_print_limit_usage_per_host()`, `repl_format_consumable_value_and_used()`, `repl_format_numeric_resource_value()`, `repl_current_units_mode()`), `src/scheduler/src/repl_fmt.c` (`repl_is_bytes_valued_resource()`, `repl_format_bytes_human()`), `src/lib/xcelimit.c` (`fmt_limit()`), `src/lib/testsuite/test_limit.c`.
- **Findings**:
- The usage lines (`accounted=`/`capacity=`) already call `repl_format_consumable_value_and_used()`, which already checks `repl_is_bytes_valued_resource()` — but the argument passed is `vres->vres->name`, a synthesized compound name (`build_vres_name()`, e.g. `limits2/u=rshen/q=xce/p=`), never the literal resource name (`"mem"`). The classifier silently always returns false for a limit's usage instance. The correct string (`limit->val.resname`, or `vres->limit->val.resname` in the per-host helper) is already reachable in scope — this is a genuine, isolated argument bug, not a missing feature.
- The configured-value line is printed entirely by `fmt_limit()` (`src/lib/xcelimit.c`), a shared-library function with a fixed `"%s = %f%s\n"` format and its own direct unit tests (`test_limit.c`, 5 call sites) — it has no REPL units-mode concept at all, and `repl_info.c`'s own existing comment explicitly documents a prior decision NOT to reformat its output.
- Full-tree grep confirms `fmt_limit()` has exactly one production caller (`repl_info.c`, this spec's own command) and one direct-test file — no other component (controller, worker, apiserver) calls it, despite `libxce` being linked by all of them.
- **Implications**: the usage-line issue is a one-line-per-call-site bug fix. The config-line issue requires a real design decision (discovery phase surfaced 3 approaches; user selected "reformat at the source").
### Reformat-at-the-source approach: how to add a units-mode parameter without breaking `fmt_limit()`'s contract
- **Context**: the user selected reformatting `fmt_limit()`'s output at its source over a REPL-layer annotation, but `fmt_limit()`'s existing signature has no way to receive a display-mode preference, and its 5 existing `test_limit.c` assertions pin its exact current output.
- **Alternatives Considered**:
1. Add a units-mode parameter directly to `fmt_limit()`'s existing signature — breaks its contract; all 5 `test_limit.c` call sites and both `repl_info.c` call sites need updating, and the meaning of "default" behavior for `fmt_limit()`'s own unit tests becomes ambiguous.
2. Add a new sibling function `fmt_limit_units()` taking the extra parameter, and retype `fmt_limit()` itself into a one-line wrapper calling it with a fixed RAW mode.
3. Duplicate `fmt_limit()`'s whole body into a new function rather than sharing it via delegation.
- **Selected Approach**: (2).
- **Rationale**: verified algebraically that `xce_format_units_value(XCE_UNITS_RAW, v, ...)` produces the bare `"%f"` rendering of `v`, identical to what `fmt_limit()` prints today in both its percent and non-percent branches — so `fmt_limit(buf, len, limit) == fmt_limit_units(buf, len, limit, XCE_UNITS_RAW)` for every input, provably byte-identical. This means `test_limit.c`'s 5 existing assertions require **zero changes**, contrary to this spec's own discovery-phase estimate that they would need updating. Delegation (not duplication) avoids the two functions' consumer-dump loops drifting apart later.
- **Trade-offs**: two function names to maintain instead of one, but this matches the codebase's own precedent (`fmt_jobid()`/`fmt_jobid_ex()`, `limit_seed_config()`/`limit_seed_config_impl()`) for "add an extended sibling rather than break an existing signature."
- **Follow-up**: none.
### Where the byte-formatter should live once it needs a non-scheduler caller
- **Context**: `fmt_limit_units()` lives in `src/lib/xcelimit.c` (shared library) but needs the same byte-scaling algorithm `repl_format_bytes_human()` (`src/scheduler/src/repl_fmt.c`, scheduler-only) already implements.
- **Alternatives Considered**:
1. Duplicate the algorithm into `xcelimit.c` directly.
2. Promote the algorithm (and its companion classifier/composer) into an existing shared-lib file, with the scheduler's existing functions becoming thin wrappers.
3. Create a brand-new shared-lib file solely for this.
- **Selected Approach**: (2), landing in `src/lib/utils.c`/`src/include/utils.h` — the existing home of `fmt_jobid()`/`fmt_jobid_ex()`, the closest existing precedent for a small, generic, cross-component display-formatting helper with no domain coupling (confirmed no similar helper already exists elsewhere in `src/lib`; `src/controller/testsuite/test.c`'s own `format_bytes_human()` is test-only and not a production dependency, so not consolidated by this spec).
- **Rationale**: avoids duplicating the same divide-by-1024-while->=1024 algorithm a third time in the codebase (it already exists twice: `repl_format_bytes_human()` production, `test.c`'s `format_bytes_human()` test-only); `utils.c`/`utils.h` is already linked into every component that would ever need this, with zero new build-system surface.
- **Trade-offs**: `xce_repl_units_mode_t` (scheduler-private, `repl_internal.h`) and the new `xce_units_mode_t` (shared, `utils.h`) are two independently-defined enums with the same three semantic values, rather than one shared type — deliberate, since promoting the scheduler's own enum into shared-lib scope would be a layering violation in the other direction (a `src/lib` type must not be defined in terms of a scheduler-internal concept, nor should the scheduler's already-tested, already-working `xce_repl_units_mode_t` plumbing need to change for this). A small `translate_units_mode()` switch at the one new call site (`repl_info.c`) bridges the two; a Revalidation Trigger in `design.md` flags this mapping for a future maintainer.
- **Follow-up**: none.
### Existing test fixtures were asserting on the bug, not a locked contract
- **Context**: `test_repl_limit_table.c` (built under Requirement Set 1) has multiple fixtures using `"mem"` as the limit's constrained resource, with assertions like `strstr(captured, "accounted=4.00")`.
- **Finding**: these assertions currently pass BECAUSE of the resource-name argument bug (the classifier is never actually asked about `"mem"`), not because `"4.00"` is a deliberately-chosen display contract. Fixing the bug will legitimately change this output to a human-formatted form.
- **Implication**: this is expected, not a regression to avoid — `design.md`'s Testing Strategy requires the implementer to prove this via a temporary revert (old assertion fails for the right reason once the bug is fixed) before finalizing the updated assertion, the same RED→GREEN discipline as any other bug fix in this codebase.
## References
- `.wolf/buglog.json` bug-1784–bug-1790 (gitea#46 limit-matching chain, 2026-09-07) — internal, not a URL.
- `.wolf/buglog.json` bug-1593 (`info resource` GLOBAL/SHARED-vs-host-based display fix, 2026-08-19) — internal, not a URL.
- `.wolf/buglog.json` bug-1685 (limit plugin double-free; also the evidence that `privdata->limits` is name-keyed) — internal, not a URL.
- `.wolf/buglog.json` bug-1762 (build-wiring precedent for white-box REPL test files) — internal, not a URL.
- `.wolf/buglog.json` bug-1599 (bare-printf regression scanner false-positive) — internal, not a URL.
- `.kiro/steering/resource-accounting.md` — reservation-vs-actual-usage policy, scoped to `XCE_RESF_DYNAMIC`; confirmed not applicable to this design.