Boundary Correctness

Parsing, normalization, adapters, and policy where representations or systems meet.

64 items in this guidance cluster.

Tagged guidance

Guide item

Agent Workflow rules

Boundary rules

Avoid Global Mutable StateBOUNDARY-AVOID-GLOBAL-MUTABLE-STATEKeep shared process state behind explicit owners, handles, synchronization, and reset policy. This preserves test isolation and lifecycle reasoning while still allowing deliberate globals such as caches or registries when their contract is visible.Choose Resource Identity ModelBOUNDARY-CHOOSE-RESOURCE-IDENTITY-MODELDecide whether the boundary mutates records, sets, files, sessions, handles, or whole documents before designing reconciliation. The chosen unit controls idempotency, matching, conflict handling, and the cost of later migration.Define Compaction InvariantsBOUNDARY-DEFINE-COMPACTION-INVARIANTSState the budget and cut-point rules before deleting, summarizing, or moving context. Explicit invariants make compaction reviewable and reduce the risk that later work treats lossy or nondeterministic summaries as authoritative.Define Hook Failure PolicyBOUNDARY-DEFINE-HOOK-FAILURE-POLICYSpecify whether each hook class blocks, retries, logs and continues, rolls back, or leaves partial state. This gives extension points predictable failure behavior without forcing one global answer onto every hook.Distinguish Input ClassesBOUNDARY-DISTINGUISH-INPUT-CLASSESKeep unknown, unsupported, denied, and preserved inputs in separate result or error paths. The distinction protects compatibility, authorization messaging, and recovery behavior while allowing small internal parsers to stay simpler.Expose Partial Stream OutputBOUNDARY-EXPOSE-PARTIAL-STREAM-OUTPUTSurface streaming tokens, chunks, or events as provisional output until completion promotes a final result. Callers get progress and diagnostics without corrupting authoritative state with partial provider data.Give Tools Identity Policy And LimitsBOUNDARY-GIVE-TOOLS-IDENTITY-POLICY-AND-LIMITSGive effectful callable units typed identity, authorization policy, cancellation, and bounded output. The added ceremony is reserved for real tool boundaries where auditability, recovery, and blast-radius control matter.Ground Integrations In Primary SourcesBOUNDARY-GROUND-INTEGRATIONS-IN-PRIMARY-SOURCESBase adapter behavior on provider docs, specs, or captured API responses before encoding local assumptions. When primary sources are incomplete, label observations and inferences so guesses do not become fake guarantees.Identify Anemic State MachinesBOUNDARY-IDENTIFY-ANEMIC-STATE-MACHINESReplace scattered booleans and conditionals with named states and transitions when lifecycle behavior is already complex. The move exposes illegal transitions and missing recovery paths without over-formalizing simple linear code.Keep Backend Adapters At EdgeBOUNDARY-KEEP-BACKEND-ADAPTERS-AT-EDGEKeep provider-specific terminal, storage, network, and runtime APIs in adapter layers at the boundary. Core logic stays stable and testable while real backend differences remain modeled instead of hidden behind a false common API.Keep One Authoritative Owner Per FactBOUNDARY-KEEP-ONE-AUTHORITATIVE-OWNER-PER-FACTGive each state fact one definite authority and derive other views from it where practical. Cached, persisted, or adapter-specific projections may duplicate a fact only when their source and invalidation or reconciliation contract are explicit.Make Ambient Inputs ExplicitBOUNDARY-MAKE-AMBIENT-INPUTS-EXPLICITPass time, randomness, environment, locale, terminal size, network clients, and process state through visible inputs when they affect behavior. Injecting only the relevant ambient values improves tests and portability without spreading oversized context objects.Make Dynamic Conflicts DeterministicBOUNDARY-MAKE-DYNAMIC-CONFLICTS-DETERMINISTICDefine stable ordering, duplicate handling, priority, or override policy for dynamic registrations. Deterministic conflict behavior prevents hash order or load timing from changing which plugin, guest, generated item, or handler wins.Make Exec Tools NoninteractiveBOUNDARY-MAKE-EXEC-TOOLS-NONINTERACTIVEDefault agent, CI, and background exec paths away from prompts, editors, pagers, and credential UI. Commands then fail or complete predictably, while human interactive modes remain explicit opt-ins.Make Policy Boundaries ExplicitBOUNDARY-MAKE-POLICY-BOUNDARIES-EXPLICITRoute writes, network calls, shell execution, publication, telemetry, redaction, and credential use through a visible policy decision before effects run. Callers can then understand allowed, denied, redacted, fallback, preserved, and unsupported outcomes.Model Real Upstream SurfaceBOUNDARY-MODEL-REAL-UPSTREAM-SURFACEShape local integration APIs around the providers actual records, pages, permissions, rate limits, and consistency behavior. Wrappers may simplify common paths, but they should not promise capabilities the upstream cannot provide.Name Lifecycle TransitionsBOUNDARY-NAME-LIFECYCLE-TRANSITIONSModel creation, activation, cancellation, teardown, reload, and promotion as named operations when they carry different invariants. This keeps ordering, cleanup, retry, and recovery rules visible without adding ceremony to simple constructed values.Parse Uncertainty At EdgeBOUNDARY-PARSE-UNCERTAINTY-AT-EDGEParse and validate raw strings, JSON, CLI args, provider responses, and user input at the boundary before passing values inward. Core logic receives typed invariants, while domain-specific checks that require later context remain explicit policy decisions.Read Normalize Compare MutateBOUNDARY-READ-NORMALIZE-COMPARE-MUTATEReconcile external state by reading the current provider view, normalizing it, comparing intent, and mutating only the real difference. The loop avoids destructive or noisy writes when formatting, defaults, ordering, or outside actors create drift.Reject Unsupported ShapesBOUNDARY-REJECT-UNSUPPORTED-SHAPESFail unsupported names, values, TTLs, targets, record families, protocols, or modes at the boundary with clear errors. Preserve unknown data only when compatibility requires round-tripping and the system can do so safely.Report Provider DiagnosticsBOUNDARY-REPORT-PROVIDER-DIAGNOSTICSReturn freshness, permission, budget, load, cache, partial-result, and degradation signals with provider-backed data. These diagnostics help callers decide trust, retry, display, and support behavior without exposing unactionable internals.Separate Pure Core From EffectsBOUNDARY-SEPARATE-PURE-CORE-FROM-EFFECTSMove domain computation away from rendering, I/O, mutation, and global state when effects obscure the decision being tested. The split gives tests a stable surface, but should be skipped when it adds indirection without a useful boundary.Separate UI And App StateBOUNDARY-SEPARATE-UI-AND-APP-STATEKeep selection, focus, scroll, expansion, and transient input mode separate from application-owned data when they change under different rules. The separation prevents rendering concerns from mutating domain state while allowing tiny tools to stay simple until friction appears.Stage Generated BehaviorBOUNDARY-STAGE-GENERATED-BEHAVIORValidate generated, reloadable, or plugin-provided behavior in a staging path before promoting it over known-good behavior. Staging adds recovery cost only where runtime-loaded or generated artifacts can fail after deployment.Track Dynamic Registration ProvenanceBOUNDARY-TRACK-DYNAMIC-REGISTRATION-PROVENANCEStore stable source names, versions, paths, owners, or generated identifiers for extension, guest, generated-code, and config registrations. Provenance makes conflicts and failures diagnosable while avoiding sensitive source details.Treat Terminal UI As Product SurfaceBOUNDARY-TREAT-TERMINAL-UI-AS-PRODUCT-SURFACETreat terminal layout, input, scroll behavior, color, viewport size, and platform differences as a user-facing contract when people rely on the interface. This makes regressions reviewable without requiring full visual testing for every tiny internal tool.Use Conservative Terminal DefaultsBOUNDARY-USE-CONSERVATIVE-TERMINAL-DEFAULTSChoose first-run terminal behavior that works with limited color, small viewports, ordinary keyboard input, and varied fonts or accessibility settings. Advanced styling can remain opt-in or capability-detected after the baseline is readable and usable.

Observability rules

Refactoring rules

Review rules

Rust rules

Avoid Broad Context and CallbacksRUST-AVOID-BROAD-CONTEXT-AND-CALLBACKSPass explicit inputs and keep control flow local instead of hiding it in context bags or callbacks. This makes ownership, effects, and ordering easier to audit.Contain UnsafeRUST-CONTAIN-UNSAFEKeep unsafe blocks small, wrapped by safe APIs, documented, and tested through safe behavior. Localized obligations make the safety argument auditable.Do Not Default pub(crate)RUST-DO-NOT-DEFAULT-PUB-CRATEStart items private and widen to pub(crate) only for deliberate shared internals. This keeps modules independent and makes crate-local contracts visible.Hide Test-Only HelpersRUST-HIDE-TEST-ONLY-HELPERSKeep fixtures and shortcuts behind test-only modules, features, or support crates unless they are deliberate API. This prevents scaffolding from leaking into production contracts.Inject Host Interactions at BoundariesRUST-INJECT-HOST-INTERACTIONS-AT-BOUNDARIESPass filesystem, network, time, randomness, and process behavior through boundaries when tests or alternate environments need control. This keeps the core deterministic and effects explicit.Make Feature Flags Additive Where PossibleRUST-MAKE-FEATURE-FLAGS-ADDITIVE-WHERE-POSSIBLEDesign feature flags as additive capabilities whenever possible so Cargo feature unification does not surprise downstream builds. Make incompatible combinations explicit when addition cannot model the real choice.Make Side Effects ExplicitRUST-MAKE-SIDE-EFFECTS-EXPLICITPut mutation, I/O, registration, cleanup, and background work in names, call sites, or docs when callers must account for them. Keep tiny private helpers plain when the surrounding code already makes the effect obvious.Prefer Constructors And Conversion TraitsRUST-PREFER-CONSTRUCTORS-AND-CONVERSION-TRAITSUse inherent constructors and standard conversion traits to show whether construction builds, validates, converts, borrows, allocates, or can fail. Prefer public fields only when direct construction is truly part of the contract.Preserve Valid State On FailureRUST-PRESERVE-VALID-STATE-ON-FAILUREKeep values valid when fallible operations return errors so callers can retry, inspect, or drop them predictably. Use transactional updates or staging when partial mutation would expose a broken state.Use Send Static Across TasksRUST-USE-SEND-STATIC-ACROSS-TASKSRequire owned Send + static values, futures, errors, and handles when they cross spawn or thread boundaries. Avoid imposing those bounds on local synchronous APIs where they would reject valid use.Validate Unsafe Through Safe APIRUST-VALIDATE-UNSAFE-THROUGH-SAFE-APITest unsafe internals through the safe API wrapper that callers rely on. Internal unsafe tests and tools such as Miri should support, not replace, proof that safe calls uphold the contract.

Testing rules

Pattern items

Avoid Boolean Flag Parametersavoid-boolean-flag-parametersBehavioral boolean flags make call sites depend on hidden ordering and branch semantics. Distinguish them from boolean domain data, then prefer named operations, enums, options types, or builders when the caller selects behavior.Give Agents Objectives With Boundariesgive-agents-objectives-with-boundariesAgents perform better when they know both the desired outcome and the limits around scope, authority, and risk. State objectives with concrete boundaries so the agent can act autonomously without expanding the task accidentally.Inject Time And Randomnessinject-time-and-randomnessDirect reads of clocks and random sources make behavior hard to reproduce, test, and explain. Pass time and randomness through explicit seams at the boundary where nondeterminism is chosen.Keep Async Boundaries Explicitkeep-async-boundaries-explicitHidden async work obscures cancellation, ordering, resource ownership, and failure behavior. Make task spawning, awaiting, retries, and background ownership visible at the boundary where callers need to reason about them.Log At Owned Boundarieslog-at-owned-boundariesLogs become noisy and inconsistent when every helper records the same failure from a partial viewpoint. Log where the code owns the operation and can attach useful, safe diagnostic context.Make Invalid States Hard To Expressmake-invalid-states-hard-to-expressRepeated validation spreads invariants across callers and lets unchecked values travel too far. Move the rule into a construction boundary or precise type when that reduces downstream reasoning without adding local ceremony.Make Parameters Explicitmake-parameters-explicitHidden configuration, time, randomness, globals, or environment reads make behavior hard to reason about and test. Gather ambient data at the outer boundary, then pass typed inputs or a cohesive context to the inner operation.Make Side Effects Visiblemake-side-effects-visibleCallers need to know when an operation touches persistence, networks, time, global state, caches, metrics, processes, or other external systems. Make caller-relevant effects visible in names, docs, or examples without documenting implementation noise.Make State Transitions Explicitmake-state-transitions-explicitLifecycle rules become fragile when status fields, timestamps, events, and validation checks are coordinated by hand. Give meaningful transitions a named owner that holds the preconditions, state update, event, and error behavior together.Make Validation Policy Explicitmake-validation-policy-explicitValidation rules often differ by workflow even when they touch the same field. Name the policy at the boundary with distinct constructors, modes, policy types, or parsed values once real competing rules exist.Parse Dont Validateparse-dont-validateValidation that returns raw values forces callers to remember which invariants already hold. Parse uncertain input into types that carry those invariants, using constructors or standard parsing traits that honestly match the boundary.Return Structured Errorsreturn-structured-errorsString-only errors hide the stable facts callers need for branching, retrying, logging, and support. Return structured error values with machine-readable kinds and context, then render audience-specific messages at the boundary.Use Guard Clauseuse-guard-clauseThe normal path is harder to see when the rest of a routine is indented under a precondition or failure case. Exit early for boring gates so the ordinary behavior stays flat, visible, and ordered by what readers must rule out.

Principle items