Driven by a parity comparison between BCAppsBCQuality PR #27 and BCAppsCampAIRHack PR #162 on byte-identical content: | | BCQuality | AIRHack | |--|--|--| | Total findings | 6 | 10 | | Performance | 0 | 4 | | Security | 0 | 5 | Standalone runs of al-security-review and al-performance-review against the SAME diff produced the expected matches (rimd-on-read-only via inherent-permissions-minimal-grant; redundant-Get via avoid-redundant-get-when-record-already-loaded). The miss in the live run is therefore not a knowledge-coverage gap and not a worklist filtering issue. It is attention dilution inside the al-code-review super-skill, which the model collapses into one rolled-up generation pass on real-size PRs. Changes: microsoft/skills/review/al-code-review.md - New 'Execution discipline (mandatory)' subsection in the Action step that explicitly forbids collapsing leaves into one shared reasoning pass and requires each sub-skill to walk its Source -> Relevance -> Worklist -> Action steps as its own iteration before the next leaf starts. - Self-review pass is now described as the final, mandatory iteration with a concrete candidate-category checklist (architecture-level smells, error-handling gaps, magic constants, privacy/telemetry, resource lifecycle). Returning zero agent findings on a real-size diff is explicitly defined as a defect. microsoft/skills/review/al-{security,performance,privacy,style, upgrade,ui}-review.md - Each leaf skill now states that when an unambiguous .good.al companion exists, findings[].suggested-code should carry the literal replacement for the source lines. Closes the one-click-suggestion gap created when BCQ#19 only updated al-code-review. microsoft/knowledge/security/case-must-handle-unknown-enum-values.{md, bad.al, good.al} - New article: case over a security-sensitive enum (Authentication Type, Authorization Mode, Identity Provider, Permission Scope, Encryption Algorithm) MUST have an else arm. Without it, an unknown enum value silently falls through and the security context never initialises. The bad sample is lifted from the SharePoint Graph helper that triggered the parity finding. microsoft/knowledge/performance/instream-length-unreliable-for-bc- streams.{md,bad,good} - New article: InStream.Length returns 0 / partial for HTTP-response streams and some file-API streams, breaking size-threshold branching in upload code. Bad sample is the simple-vs-chunked Graph upload pattern; good sample materialises into a Temp Blob first. Companion change: microsoft/BCAppsBCQuality#28 extends the orchestrator's bootstrap prompt with the same per-iteration execution discipline and adds a CI warning when a >5-file PR returns zero agent findings (regression signal). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
3.4 KiB
| bc-version | domain | keywords | technologies | countries | application-area | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
performance |
|
|
|
|
InStream.Length is unreliable for branching on payload size
Description
InStream exposes a Length property that returns the total byte count of the underlying buffer when the runtime can determine it. The catch is that "when the runtime can determine it" depends on how the stream was obtained:
- Streams produced from a
Blobor aTemp Blobfield byCreateInStream—Lengthis reliable; the blob is fully materialised. - Streams produced from a Media or MediaSet field — same: backed by a known-size payload.
- Streams produced from
HttpResponseMessage.Content.ReadAsand from manyFile.*APIs —Lengthmay return0or a partial value, because the underlying transport is consumed incrementally and the total length is not known until the stream is exhausted. - Streams from
Streamparameters supplied by callers — depends entirely on what the caller passed in.
Branching upload behaviour on Stream.Length is the common failure mode. The pattern is:
if Stream.Length <= MaxSimpleUploadSize then
UploadSimple(Stream)
else
UploadChunked(Stream);
For a Microsoft Graph drive upload, MaxSimpleUploadSize is 4 MB. If Stream.Length returns 0 (because the stream came from an HTTP response or a freshly written outstream that the runtime cannot size cheaply), the code takes the simple-upload path with a 10 MB file behind it, the API returns 413 Payload Too Large, and the upload fails. The error surfaces to the user as a generic HTTP failure with no obvious connection to the buggy size check.
The same trap applies to any code that "skips the work if the stream is empty": if Stream.Length = 0 then exit; silently drops payloads when the stream came from a source that does not pre-compute length.
Best Practice
Decide which behaviour you actually need.
- Always-chunked is the safe default when the stream's origin is not under your control. Chunked uploads work for any payload size; the per-chunk overhead is small for small payloads.
- When a size threshold is genuinely required (for example, choosing between two endpoints with different cost profiles), copy the stream into a known-size buffer first — typically a
Temp Blob— and read length from the blob, which IS reliable. The cost of one round-trip through a blob is acceptable for the upload-routing decision. - When the threshold is informational (logging, telemetry), guard against
0:if (Stream.Length > 0) and (Stream.Length <= Threshold)so an unknown size routes to the safe path, not the optimistic one.
See sample: instream-length-unreliable-for-bc-streams.good.al.
Anti Pattern
Branching upload size, validation, or buffer allocation directly on Stream.Length when the stream's origin is anything other than a freshly-materialised blob. Detection signal: Stream.Length (or .Length on a variable typed InStream or OutStream) appearing as the left or right side of <=, <, >=, >, or = against a size-like constant or Label, with no prior copy through a Blob. The narrower signal — branching simple-vs-chunked upload on Length for Graph or REST endpoints with a documented size cap — is the high-confidence case.
See sample: instream-length-unreliable-for-bc-streams.bad.al.