#2 — Rubric Gap Analysis — monitoring-ru-consumption enhancement: Add Node.js requestCharge example
| Field |
Value |
| Type |
Rule Enhancement |
| Target Rule |
monitoring-ru-consumption |
| Severity |
HIGH |
| Source |
SCOPE Node.js AI Food Delivery — Monitoring & Diagnostics, Criterion q_9_metrics, Phase 3 static + Phase 6a validation (2026-04-17) |
| Labels |
SCOPE, enhancement, rule:monitoring |
| Related |
Closed #56 (C#-only examples added) |
Summary
The monitoring-ru-consumption rule has solid C# examples (operation-level response.RequestCharge, per-page query tracking, custom RequestHandler, Azure Monitor KQL) but no Node.js / TypeScript example. 64 of 65 SCOPE Node.js runs ship Cosmos DB code with zero RU instrumentation, including 24/25 AK runs. This is the Node.js analogue of #56, which closed the .NET path.
Rubric Gap Analysis
Hit rate by cohort (criterion q_9_metrics — HIT = requestCharge captured in at least one code path). Measured 2026-04-17 by scanning all .js/.ts/.mjs/.cjs files under each of the 65 run directories (node_modules excluded) for the literal string requestCharge:
| Cohort |
Hits |
Misses |
Hit rate |
| AK (25 runs) |
1 |
24 |
4% |
| Control (40 runs) |
0 |
40 |
0% |
| All |
1 |
64 |
1.5% |
The single AK hit was P04/R04 (Claude Sonnet 4.6 + CosmosDB Agent Kit), which emits 12 requestCharge references across cosmosService.js (create / read / query / patch / fetchNext) and 7 across orders.test.js. This run demonstrates what adoption looks like when the rule lands — every operation type wraps into a logRu(op, ru) helper.
The skill does not land in the other 24 AK runs because the rule never shows the Node.js API surface. Agents treat requestCharge as a concern of the .NET runtime.
Evidence
Existing Rule Coverage
The current rule (skills/cosmosdb-best-practices/rules/monitoring-ru-consumption.md, verified against AzureCosmosDB/cosmosdb-agent-kit@main on 2026-04-17) contains 4 C# code blocks (operation-level tracking, per-page query tracking, RequestHandler middleware, Azure Monitor KQL) and 0 blocks in any other language. Full-category gap — all monitoring-* and sdk-diagnostics rules are C#-only:
| Rule |
C# blocks |
Node.js blocks |
Java blocks |
Python blocks |
monitoring-ru-consumption |
4 |
0 |
0 |
0 |
monitoring-azure-monitor |
4 |
0 |
0 |
0 |
monitoring-diagnostic-logs |
4 |
0 |
0 |
0 |
sdk-diagnostics |
3 |
0 |
0 |
0 |
Existing C# headline:
// existing C# example from the rule
var response = await _container.ReadItemAsync<Order>(orderId, new PartitionKey(customerId));
_logger.LogDebug("Read order {OrderId}: {RU} RU, {Latency}ms",
orderId, response.RequestCharge,
response.Diagnostics.GetClientElapsedTime().TotalMilliseconds);
Missing Anti-Pattern (Node.js — representative of 64/65 runs)
// instance 1 — control run
const { resources } = await ordersContainer.items
.query(ordersQuery, { partitionKey: userId })
.fetchAll();
return resources; // requestCharge not logged, not tracked, not alerted
// instance 2 — agent-kit run
const response = await this.container.items.query<OrderDocument>(spec, opts).fetchAll();
return response.resources; // response.requestCharge available but unused
// instance 3 — agent-kit run, point read
const { resource } = await container.item(orderId, userId).read();
return resource; // no RU, no latency, no telemetry
Correct Pattern (proposed Node.js block to add to the rule)
import { Container, FeedResponse } from '@azure/cosmos';
import { logger, metrics } from './telemetry';
// ❌ Anti-pattern: discard requestCharge and diagnostics
export async function getOrderBad(container: Container, id: string, userId: string) {
const { resource } = await container.item(id, userId).read();
return resource;
}
// ✅ Preferred: capture requestCharge on every call
export async function getOrder(container: Container, id: string, userId: string) {
const response = await container.item(id, userId).read();
logger.debug({
op: 'ReadItem',
container: container.id,
ru: response.requestCharge,
statusCode: response.statusCode,
activityId: response.activityId,
}, 'cosmos.readItem');
metrics.histogram('cosmos.readItem.ru').record(response.requestCharge, { container: container.id });
return response.resource;
}
// ✅ Queries: accumulate RU across pages (single-page tracking undercounts paged results)
export async function getCustomerOrders(container: Container, userId: string) {
const iterator = container.items.query<OrderSummary>({
query: 'SELECT c.id, c.userId, c.status, c.total, c.createdAt FROM c WHERE c.userId = @u ORDER BY c.createdAt DESC',
parameters: [{ name: '@u', value: userId }],
}, { partitionKey: userId });
const results: OrderSummary[] = [];
let totalRU = 0;
while (iterator.hasMoreResults()) {
const page: FeedResponse<OrderSummary> = await iterator.fetchNext();
results.push(...page.resources);
totalRU += page.requestCharge;
}
logger.info({ op: 'Query', container: container.id, count: results.length, totalRU }, 'cosmos.query.total');
if (totalRU > 100) {
logger.warn({ totalRU, count: results.length }, 'cosmos.query.expensive');
}
metrics.histogram('cosmos.query.ru').record(totalRU, { container: container.id });
return results;
}
// ✅ Writes: create/upsert/replace/patch/delete all expose requestCharge
export async function createOrder(container: Container, order: Order) {
const response = await container.items.create(order);
logger.debug({ op: 'CreateItem', ru: response.requestCharge }, 'cosmos.createItem');
return response.resource;
}
requestCharge surface in @azure/cosmos v4
| Operation |
Result type |
RU field |
container.item(id, pk).read() |
ItemResponse<T> |
response.requestCharge |
container.items.create(doc) |
ItemResponse<T> |
response.requestCharge |
container.items.upsert(doc) |
ItemResponse<T> |
response.requestCharge |
container.item(id, pk).replace(doc) |
ItemResponse<T> |
response.requestCharge |
container.item(id, pk).patch(ops) |
ItemResponse<T> |
response.requestCharge |
container.item(id, pk).delete() |
ItemResponse<T> |
response.requestCharge |
container.items.query(...).fetchAll() |
FeedResponse<T> |
response.requestCharge |
container.items.query(...).fetchNext() |
FeedResponse<T> per page |
sum across pages |
container.items.bulk(ops) |
OperationResponse[] |
op.requestCharge per operation |
Runtime Validation
Executed _agent_files/phase-6a/scope-node-002-validation.js against the Cosmos DB emulator (@azure/cosmos v4.5.1, emulator @ https://localhost:8081, 2026-04-17). Confirms every operation in the surface table returns a numeric requestCharge:
| Operation |
RU returned |
typeof requestCharge === 'number' |
container.items.create(doc) |
6.67 |
✓ |
container.items.upsert(doc) |
10.67 |
✓ |
container.item(id, pk).read() |
1.00 |
✓ |
container.item(id, pk).replace(doc) |
10.67 |
✓ |
container.item(id, pk).patch(ops) |
10.79 |
✓ |
container.items.query(...).fetchAll() |
2.82 |
✓ |
container.items.query(...).fetchNext() — page 1 |
2.82 |
✓ |
container.items.query(...).fetchNext() — summed across pages |
2.82 |
✓ |
container.items.bulk([op1, op2]) |
[6.29, 6.29] per op |
✓ |
container.item(id, pk).delete() |
6.67 |
✓ |
All 10 API shapes in the proposed surface table return requestCharge as a top-level numeric property. No operation requires a separate diagnostics call to obtain the RU charge, which keeps the correct-pattern block concise.
Runner + raw output: _agent_files/phase-6a/scope-node-002-validation.js and scope-node-002-validation.json.
Rule Contradiction Scan
| Rule |
Interaction |
sdk-diagnostics |
Complementary — Node.js analogue is response.diagnostics; cross-link |
query-use-projections (#1 draft) |
Reinforcing — requestCharge is how you verify a projection helped on YOUR data |
query-point-reads (#3 draft) |
Reinforcing — point-read RU savings visible only via requestCharge |
sdk-retry-429 |
Complementary |
No contradictions.
Documentation Cross-reference
Recommended cosmosdb-agent-kit Fix
- Add a Node.js / TypeScript example block alongside the existing C# examples.
- Add the
requestCharge surface table above to the rule.
- Cross-link to
sdk-diagnostics for the Node.js response.diagnostics pattern.
#2 — Rubric Gap Analysis —
monitoring-ru-consumptionenhancement: Add Node.jsrequestChargeexamplemonitoring-ru-consumptionSCOPE,enhancement,rule:monitoringSummary
The
monitoring-ru-consumptionrule has solid C# examples (operation-levelresponse.RequestCharge, per-page query tracking, customRequestHandler, Azure Monitor KQL) but no Node.js / TypeScript example. 64 of 65 SCOPE Node.js runs ship Cosmos DB code with zero RU instrumentation, including 24/25 AK runs. This is the Node.js analogue of #56, which closed the .NET path.Rubric Gap Analysis
Hit rate by cohort (criterion
q_9_metrics— HIT =requestChargecaptured in at least one code path). Measured 2026-04-17 by scanning all.js/.ts/.mjs/.cjsfiles under each of the 65 run directories (node_modulesexcluded) for the literal stringrequestCharge:The single AK hit was P04/R04 (Claude Sonnet 4.6 + CosmosDB Agent Kit), which emits 12
requestChargereferences acrosscosmosService.js(create / read / query / patch / fetchNext) and 7 acrossorders.test.js. This run demonstrates what adoption looks like when the rule lands — every operation type wraps into alogRu(op, ru)helper.The skill does not land in the other 24 AK runs because the rule never shows the Node.js API surface. Agents treat
requestChargeas a concern of the .NET runtime.Evidence
Existing Rule Coverage
The current rule (
skills/cosmosdb-best-practices/rules/monitoring-ru-consumption.md, verified againstAzureCosmosDB/cosmosdb-agent-kit@mainon 2026-04-17) contains 4 C# code blocks (operation-level tracking, per-page query tracking,RequestHandlermiddleware, Azure Monitor KQL) and 0 blocks in any other language. Full-category gap — all monitoring-* and sdk-diagnostics rules are C#-only:monitoring-ru-consumptionmonitoring-azure-monitormonitoring-diagnostic-logssdk-diagnosticsExisting C# headline:
Missing Anti-Pattern (Node.js — representative of 64/65 runs)
Correct Pattern (proposed Node.js block to add to the rule)
requestChargesurface in@azure/cosmosv4container.item(id, pk).read()ItemResponse<T>response.requestChargecontainer.items.create(doc)ItemResponse<T>response.requestChargecontainer.items.upsert(doc)ItemResponse<T>response.requestChargecontainer.item(id, pk).replace(doc)ItemResponse<T>response.requestChargecontainer.item(id, pk).patch(ops)ItemResponse<T>response.requestChargecontainer.item(id, pk).delete()ItemResponse<T>response.requestChargecontainer.items.query(...).fetchAll()FeedResponse<T>response.requestChargecontainer.items.query(...).fetchNext()FeedResponse<T>per pagecontainer.items.bulk(ops)OperationResponse[]op.requestChargeper operationRuntime Validation
Executed
_agent_files/phase-6a/scope-node-002-validation.jsagainst the Cosmos DB emulator (@azure/cosmosv4.5.1, emulator @https://localhost:8081, 2026-04-17). Confirms every operation in the surface table returns a numericrequestCharge:typeof requestCharge === 'number'container.items.create(doc)container.items.upsert(doc)container.item(id, pk).read()container.item(id, pk).replace(doc)container.item(id, pk).patch(ops)container.items.query(...).fetchAll()container.items.query(...).fetchNext()— page 1container.items.query(...).fetchNext()— summed across pagescontainer.items.bulk([op1, op2])[6.29, 6.29]per opcontainer.item(id, pk).delete()All 10 API shapes in the proposed surface table return
requestChargeas a top-level numeric property. No operation requires a separate diagnostics call to obtain the RU charge, which keeps the correct-pattern block concise.Runner + raw output:
_agent_files/phase-6a/scope-node-002-validation.jsandscope-node-002-validation.json.Rule Contradiction Scan
sdk-diagnosticsresponse.diagnostics; cross-linkquery-use-projections(#1 draft)requestChargeis how you verify a projection helped on YOUR dataquery-point-reads(#3 draft)requestChargesdk-retry-429No contradictions.
Documentation Cross-reference
requestCharge.FeedResponse(JavaScript) — exposesrequestChargebut the landing-page guidance does not pair it with telemetry.ItemResponse(JavaScript).Recommended cosmosdb-agent-kit Fix
requestChargesurface table above to the rule.sdk-diagnosticsfor the Node.jsresponse.diagnosticspattern.