<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ahmad Ra'fat</title>
    <description>The latest articles on DEV Community by Ahmad Ra'fat (@luffy_14).</description>
    <link>https://dev.to/luffy_14</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F90767%2F7f705147-5559-4d90-b659-319369a6e4c7.jpg</url>
      <title>DEV Community: Ahmad Ra'fat</title>
      <link>https://dev.to/luffy_14</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/luffy_14"/>
    <language>en</language>
    <item>
      <title>Multi-Tenancy Is the Real Agent Platform Problem</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Tue, 23 Jun 2026 12:33:46 +0000</pubDate>
      <link>https://dev.to/luffy_14/multi-tenancy-is-the-real-agent-platform-problem-1dh2</link>
      <guid>https://dev.to/luffy_14/multi-tenancy-is-the-real-agent-platform-problem-1dh2</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Read on &lt;a href="https://built-properly.hashnode.dev/multi-tenancy-is-the-real-agent-platform-problem" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What I have seen when building agent platforms is that most agent demos &lt;em&gt;work&lt;/em&gt; because there is only one tenant. One user, one memory store, one tool set, one trace, one notebook, and one happy path. &lt;strong&gt;&lt;em&gt;Nothing to keep apart.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then teams turn the demo into a platform, and prompts stop being the hard part. The hard question becomes a boring one: &lt;strong&gt;can every database query, cache key, stream, tool call, trace, and memory lookup prove which tenant it belongs to?&lt;/strong&gt; If even one of them cannot, you have a leak waiting to happen.&lt;/p&gt;

&lt;p&gt;I keep seeing agent-platform conversations start in the wrong place. They open on &lt;em&gt;model choice, orchestration style, or memory quality&lt;/em&gt;, and only later get around to the question of whether one tenant's data, tools, costs, and traces stay away from another's.&lt;/p&gt;

&lt;p&gt;That boundary is not a hardening task you bolt on at the end. It is the shape of the platform, and you pay for it later if you pretend otherwise.&lt;/p&gt;

&lt;p&gt;So here is the mechanism I would look for in a real agent platform: a &lt;strong&gt;typed request context&lt;/strong&gt;, &lt;strong&gt;carried into the graph&lt;/strong&gt;, &lt;strong&gt;scoped access at every boundary&lt;/strong&gt;, and &lt;strong&gt;tests that make a tenant leak&lt;/strong&gt; boring to catch before it becomes an incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Demo Version Hides the Problem
&lt;/h2&gt;

&lt;p&gt;A single-user agent can hold state in memory and still look impressive. It calls a search tool with no tenant filter. It stores chat history under a bare &lt;code&gt;thread_id&lt;/code&gt;. It writes traces with raw prompts in them, because nobody else is looking.&lt;/p&gt;

&lt;p&gt;But this seems fine, right?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Yes, but only for a demo. Not for a platform.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What changes is the unit of correctness. The agent is no longer just answering "&lt;em&gt;what should I do next?&lt;/em&gt;" It is also carrying a boundary through every step it takes. Drop that boundary, and the model can still hand back a perfectly good answer, and you have still failed, because the answer went to the wrong tenant.&lt;/p&gt;

&lt;p&gt;If you have worked on multi-tenant platforms before, that means: &lt;strong&gt;every operation that touches data, tools, memory, cost, traces, or events must be scoped by tenant before the model gets a chance to improvise.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Tenant Design Rules for Agent Platforms
&lt;/h2&gt;

&lt;p&gt;These are not agent-specific ideas. This is normal backend security applied to an agent runtime. We translate the multi-tenant and authorization guidelines we already know into the places an agent platform leaks: memory, tools, retrieval, streams, and traces.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concept&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;In a multi-tenant platform&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;In an agent platform&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Tenant isolation&lt;/strong&gt; &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Tenant context, cross-tenant access, cache/session isolation, database isolation, file isolation, logging, and audit all need explicit controls.&lt;/td&gt;
&lt;td&gt;Agent memory, tools, streams, and traces are tenant-isolation surfaces too.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Retrieval authorization&lt;/strong&gt; &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/RAG_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;RAG systems need access-control metadata, retrieval-time authorization, deletion propagation, and tests for cross-tenant retrieval.&lt;/td&gt;
&lt;td&gt;Vector filtering is not optional platform glue. It is part of authorization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Least-privilege tools&lt;/strong&gt; &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/AI_Agent_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Agents need least-privilege tools, explicit authorization for sensitive actions, memory isolation, and safe tool design.&lt;/td&gt;
&lt;td&gt;Tool catalogs should be filtered before the model can call them.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Object-level authorization&lt;/strong&gt; &lt;a href="https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Every endpoint that acts on an object ID needs an authorization check for that object.&lt;/td&gt;
&lt;td&gt;Every tool call, memory lookup, vector search, file read, and background job needs the same object-level check.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Function-level authorization&lt;/strong&gt; &lt;a href="https://owasp.org/API-Security/editions/2023/en/0xa5-broken-function-level-authorization/" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Function access should be denied by default and granted explicitly by role or policy.&lt;/td&gt;
&lt;td&gt;Do not expose every tool to every agent. Scope tools by tenant, role, domain, and action.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;No implicit trust&lt;/strong&gt; &lt;a href="https://csrc.nist.gov/pubs/sp/800/207/final" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Access decisions should be explicit, per request, and policy-driven.&lt;/td&gt;
&lt;td&gt;The graph runtime should not inherit trust from "inside the service." Nodes and tools still need context-aware checks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Application-boundary enforcement&lt;/strong&gt; &lt;a href="https://csrc.nist.gov/pubs/sp/800/207/a/final" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Cloud-native applications need granular application access controls, service identity, and policy enforcement near application boundaries.&lt;/td&gt;
&lt;td&gt;Agent gateways, graph runtimes, and tool services should each enforce policy at their own boundary.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Attribute-based policy&lt;/strong&gt; &lt;a href="https://csrc.nist.gov/pubs/sp/800/162/upd2/final" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;ABAC decisions use subject, object, action, and environment attributes.&lt;/td&gt;
&lt;td&gt;Tenant, user, role, tool, target resource, and runtime context belong in the policy decision.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Structural guardrails&lt;/strong&gt; &lt;a href="https://www.anthropic.com/engineering/building-effective-agents" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Use the simplest agentic shape that works, design tools carefully, and add guardrails where the agent can compound errors.&lt;/td&gt;
&lt;td&gt;Applied to tenancy, this means enforcement belongs around tools and data access, not inside a prompt asking the model to behave.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Trace correlation&lt;/strong&gt; &lt;a href="https://opentelemetry.io/docs/concepts/context-propagation/" rel="noopener noreferrer"&gt;(source)&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Context travels across service and process boundaries so telemetry can be correlated.&lt;/td&gt;
&lt;td&gt;Trace IDs are not enough. Tenant-safe attributes must be attached deliberately and kept out of untrusted downstream calls.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The agent view says the same thing from another angle, though let me be precise about it: Anthropic is not publishing a multi-tenancy standard. I am taking its tool design and guardrail guidance and pointing it at a tenancy problem.&lt;/p&gt;

&lt;p&gt;The principle that carries over is to push enforcement to the most structural layer you have. A prompt is the softest layer there is. A scoped repository, a policy check, or a tenant-aware tool wrapper is harder to bypass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Tenant Context Has to Travel
&lt;/h2&gt;

&lt;p&gt;The first and most common mistake I have seen is treating &lt;code&gt;tenant_id&lt;/code&gt; as an HTTP-layer concern. It starts there, but it cannot stay there. In an agent platform, the agent graph is a second execution environment, the tools are a third, and the context has to survive every hop between them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0neo0x05vqjlsgpbqnwb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0neo0x05vqjlsgpbqnwb.png" alt="Tenant context propagation path across an agent platform, from HTTP request to graph execution, tools, storage, streams, and traces." width="799" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read this diagram as &lt;strong&gt;&lt;em&gt;three&lt;/em&gt;&lt;/strong&gt; things moving together, none of them moves on its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt;: identity, tenant, role, locale, and trace metadata travel from the API layer down into the graph and the tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boundary&lt;/strong&gt;: every hop is a chance to drop the scope, infer it from the wrong place, or let a caller quietly override it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invariant&lt;/strong&gt;: nothing touches storage, retrieval, cache, a stream, or a trace write until the tenant context is already resolved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Look at the diagram for a moment. The important path is &lt;code&gt;RequestContext&lt;/code&gt; through &lt;code&gt;Agent service&lt;/code&gt;, &lt;code&gt;Graph state&lt;/code&gt;, &lt;code&gt;Node execution&lt;/code&gt;, and &lt;code&gt;Tool wrapper&lt;/code&gt;. That is the tenant boundary. Storage, vector search, cache, stream events, and trace attributes should only see a request after that boundary is already present. I would design that path first, before I write a single prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mechanism I Would Build
&lt;/h2&gt;

&lt;p&gt;I would start with &lt;em&gt;what not to do&lt;/em&gt;: I would not thread &lt;code&gt;tenant_id&lt;/code&gt;, &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;locale&lt;/code&gt;, and &lt;code&gt;trace_id&lt;/code&gt; through every function as five loose parameters. That turns isolation into a copy-paste exercise, and copy-paste is exactly where we forget one.&lt;/p&gt;

&lt;p&gt;Instead, I would use a single explicit request context object, and then make every boundary either accept it or fail closed. &lt;strong&gt;&lt;em&gt;No third option.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Start With a Typed Context
&lt;/h3&gt;

&lt;p&gt;This code snippet is illustrative. It shows the shape I mean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;

&lt;span class="n"&gt;Role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;owner&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;member&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;viewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# These values should come from trusted auth and membership lookup,
&lt;/span&gt;    &lt;span class="c1"&gt;# not from user-supplied request fields.
&lt;/span&gt;    &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Role&lt;/span&gt;
    &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;require_tenant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Missing tenant context is a platform bug. Fail before any model,
&lt;/span&gt;        &lt;span class="c1"&gt;# storage, or tool boundary can run unscoped.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;PermissionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;missing tenant context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know it is boring, but that boring little &lt;code&gt;require_tenant&lt;/code&gt; function is the whole point. If a background job, a tool, or a graph node tries to run without tenant context, the platform stops right there. &lt;strong&gt;Missing scope&lt;/strong&gt; is not something the model &lt;strong&gt;can recover&lt;/strong&gt; from by trying harder. It is a &lt;strong&gt;bug in the platform&lt;/strong&gt;, and it should fail like one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Put Context Into the Graph Boundary
&lt;/h3&gt;

&lt;p&gt;If you are building an agent platform, you will have flows or graphs. A useful rule: the graph should pull context from &lt;strong&gt;runtime metadata or state&lt;/strong&gt;, not from the user prompt.&lt;/p&gt;

&lt;p&gt;You should never ask the model to remember the tenant for you. The model can reason over data it is allowed to see, but it should never be the decision maker on who owns that data.&lt;/p&gt;

&lt;p&gt;Check this illustrative snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;AgentResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Keep tenant context outside the user prompt. The model can use allowed
&lt;/span&gt;    &lt;span class="c1"&gt;# data, but it should not enforce ownership.
&lt;/span&gt;    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ainvoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;configurable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;# Scope the thread so memory/checkpoint lookup cannot
&lt;/span&gt;                &lt;span class="c1"&gt;# collide across tenants.
&lt;/span&gt;                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thread_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;# Metadata is for observability, not authorization.
&lt;/span&gt;                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tenant_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trace_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful when it comes to observability context.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;OpenTelemetry&lt;/strong&gt; baggage can carry account or user identifiers downstream, but its own docs warn that baggage can cross service boundaries and should not contain credentials, API keys, or PII. I would keep tenant IDs opaque and scrub propagation before calling external services.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So you should not let baggage, trace attributes, user headers, or model-visible state be the source of authorization. Treat any incoming propagation header as untrusted until your own auth and membership lookup rebuild the request context from scratch. W3C Baggage has no integrity protection either, making it a carrier of correlation data rather than proof of anything.&lt;/p&gt;

&lt;p&gt;The split I keep coming back to is simple. &lt;strong&gt;Authorization context&lt;/strong&gt; comes from trusted auth claims and a membership lookup. &lt;strong&gt;Observability context&lt;/strong&gt; is there to help you debug a request after the decision has already been made. Attaching a safe, opaque tenant tag to a trace so an operator can find a broken flow is fine. Letting that same trace tag decide which documents a tool can read is not, and the gap between those two is where engineers get burned.&lt;/p&gt;

&lt;p&gt;Watch the propagation scope too. Internal service-to-service calls may genuinely need tenant-safe correlation metadata. A third-party model call rarely needs your tenant identifier riding along in baggage. If you do need correlation out there, use a request ID that cannot be traced back to a customer or a workspace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make Storage APIs Tenant-Scoped by Construction
&lt;/h3&gt;

&lt;p&gt;This is where I want the structural layer to earn its keep.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;The query API should make the safe path the short one and the unsafe path the awkward one.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;People usually follow the path of least resistance, so make the path of least resistance correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoryStore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_facts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# Force callers through the tenant-aware API before building a query.
&lt;/span&gt;        &lt;span class="n"&gt;tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require_tenant&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select id, subject_id, fact, source
            from memory_facts
            where tenant_id = :tenant_id
              and subject_id = :subject_id
            order by created_at desc
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tenant_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subject_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do not like APIs like &lt;code&gt;list_facts(subject_id)&lt;/code&gt; in a multi-tenant platform. They ask every caller to remember the boundary. That is what fails once the same rule has to be repeated across too many call sites.&lt;/p&gt;

&lt;p&gt;The better move is to make &lt;strong&gt;the scoped path&lt;/strong&gt; the &lt;strong&gt;only&lt;/strong&gt; normal path. Pass a &lt;code&gt;RequestContext&lt;/code&gt;, a tenant-scoped repository, or a tenant-scoped DB session into the storage layer, and hide the raw unscoped query behind a small internal API that only migrations, repair jobs, and reviewed admin tooling ever touch.&lt;/p&gt;

&lt;p&gt;Check this illustrative snippet showing the API I am pointing at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TenantMemoryStore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# The tenant is captured once, when the scoped repository is created.
&lt;/span&gt;        &lt;span class="c1"&gt;# Callers cannot forget it on each method call.
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;from_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TenantMemoryStore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require_tenant&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_facts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# No tenant argument here. The repository is already scoped.
&lt;/span&gt;        &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select id, subject_id, fact, source
            from memory_facts
            where tenant_id = :tenant_id
              and subject_id = :subject_id
            order by created_at desc
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tenant_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subject_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check this flow showing what I mean:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5pozlvgst555dpmc8z3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5pozlvgst555dpmc8z3.png" alt="Tenant-scoped repository flow from RequestContext to TenantMemoryStore, scoped repository, tenant-filtered query, and memory facts." width="798" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may say the payoff here is small. I would agree. &lt;strong&gt;But&lt;/strong&gt; it matters: this shape makes sure the graph node gets handed a repository that is already scoped. It can ask for facts by subject all day long, and there is no spelling of that request that turns into a cross-tenant read.&lt;/p&gt;

&lt;p&gt;This is not just a security win. It makes tests cleaner too. A cross-tenant test can call the same public repository method the graph calls, instead of poking around trying to confirm that every caller remembered to tack on &lt;code&gt;tenant_id&lt;/code&gt; by hand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap Tools With Policy Before the Model Sees Them
&lt;/h3&gt;

&lt;p&gt;Another layer we should treat with care is the tools given to the model.&lt;/p&gt;

&lt;p&gt;Tools are the place where an agent platform quietly turns into an authorization system, whether you planned for that or not. The model asks for an action. The platform decides whether that action even exists for this tenant and this role.&lt;/p&gt;

&lt;p&gt;I would model that as an &lt;strong&gt;ABAC&lt;/strong&gt; decision rather than a role check sprinkled through the codebase wherever someone happened to think of it. The policy input wants the subject, the tenant, the tool or action, the target resource, and the environment.&lt;/p&gt;

&lt;p&gt;Implement it with &lt;strong&gt;OPA&lt;/strong&gt;, &lt;strong&gt;Cedar behind Amazon Verified Permissions (AVP)&lt;/strong&gt;, &lt;strong&gt;OpenFGA&lt;/strong&gt;, or a &lt;strong&gt;plain in-process policy module&lt;/strong&gt;, whatever fits your stack. None of those choices makes the decision point itself optional.&lt;/p&gt;

&lt;p&gt;Check this illustrative example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToolGate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PolicyEngine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolRegistry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ToolResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Decide before invocation, so the model cannot bypass tenancy by
&lt;/span&gt;        &lt;span class="c1"&gt;# selecting a more powerful tool.
&lt;/span&gt;        &lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_call_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Return a recoverable denial instead of leaking tool internals.
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ToolResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permission_denied&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the cleanest match to the point of the whole article. The prompt is allowed to describe the behavior you want. The tool gate is what enforces it. And when the two disagree, which they eventually will, code wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Engineers Forget
&lt;/h2&gt;

&lt;p&gt;Some bugs are obvious, like a DB query missing &lt;code&gt;where tenant_id = ...&lt;/code&gt;. That one is at least visible in code review.&lt;/p&gt;

&lt;p&gt;The bugs that actually leak in production are &lt;strong&gt;the quiet ones&lt;/strong&gt;, hiding in the side channels around the agent that nobody thinks of as "&lt;em&gt;the data layer&lt;/em&gt;."&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Surface&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Common mistake&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Safer invariant&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Risk shape&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redis keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chat:{thread_id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tenant:{tenant_id}:chat:{thread_id}&lt;/code&gt;, with opaque IDs where logs may escape.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Easy to miss&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vector search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Embedding search across one shared collection with no metadata filter.&lt;/td&gt;
&lt;td&gt;Tenant and permission metadata travel with every chunk, and retrieval-time checks run before context enters the prompt. If the store cannot enforce this reliably, use separate collections or indexes.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;High impact&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAG deletion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deleting the source document but leaving chunks, embeddings, cached answers, or summaries behind.&lt;/td&gt;
&lt;td&gt;Deletion and retention rules cover derived artifacts, not only the original blob.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stale access&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;File/blob storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Object keys or signed URLs that are not bound to the tenant and authorization checks.&lt;/td&gt;
&lt;td&gt;File reads go through a tenant-aware access layer, even when the blob store is shared.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Direct leak&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Streams&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stream names are built from conversation IDs only.&lt;/td&gt;
&lt;td&gt;Stream names include tenant scope, and reconnect tokens cannot subscribe across tenants.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Streaming trap&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Background jobs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A queued job carries an object ID but not the tenant and actor context that authorized it.&lt;/td&gt;
&lt;td&gt;Jobs carry scoped context and recheck permissions before side effects.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Async drift&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rate limits &amp;amp; quotas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Global limits hide noisy-neighbor behavior and per-tenant abuse.&lt;/td&gt;
&lt;td&gt;Budget, quota, and rate-limit keys are tenant-scoped before they are user-scoped.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Shared resource&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tool credentials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One service credential is reused for every tenant-specific integration.&lt;/td&gt;
&lt;td&gt;Credential lookup is tenant-scoped and action-scoped before tool invocation.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Policy boundary&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Evaluation datasets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Regression cases mixed with tenant-specific prompts or documents.&lt;/td&gt;
&lt;td&gt;Use synthetic or approved datasets. Never let tenant data leak into shared eval fixtures.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Data hygiene&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Traces &amp;amp; logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Raw prompts, tool payloads, or document snippets in shared observability sinks.&lt;/td&gt;
&lt;td&gt;Attach safe tenant tags for observability and incident response only, never as the source of authorization. Redact prompt and tool payloads before shared sinks.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Exposure risk&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Tenant Isolation Checklist
&lt;/h2&gt;

&lt;p&gt;If you hand me an agent platform to review, this is the checklist I would open with. It is deliberately practical. No architecture poetry, just the questions that tend to surface a leak.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Layer&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Question to ask&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What good looks like&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP/API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Where does tenant context come from?&lt;/td&gt;
&lt;td&gt;Derived from trusted auth claims or membership lookup, not from a user-controlled parameter alone.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent graph&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can a node run without context?&lt;/td&gt;
&lt;td&gt;No. Missing context fails before model or tool execution.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can every agent call every tool?&lt;/td&gt;
&lt;td&gt;No. Tool availability is filtered by tenant, role, domain, and action.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can a repository method run unscoped?&lt;/td&gt;
&lt;td&gt;Repository methods accept &lt;code&gt;RequestContext&lt;/code&gt; or a tenant-scoped session.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vector/RAG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Is tenant filtering required by the API, and do chunks carry access-control attributes?&lt;/td&gt;
&lt;td&gt;The retrieval function cannot be called without a tenant filter, and permissions are rechecked at retrieval time.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache/Streams&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can one tenant guess another tenant's key?&lt;/td&gt;
&lt;td&gt;Keys include tenant scope and use opaque IDs where key names might be logged.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What crosses into logs, traces, and metrics?&lt;/td&gt;
&lt;td&gt;Safe identifiers only. PII, prompt payloads, tool payloads, secrets, and sensitive business data are removed before shared sinks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Do tests try cross-tenant reads and writes?&lt;/td&gt;
&lt;td&gt;Yes. Cover cross-tenant retrieval, cache leakage, stale permissions, background jobs, and unauthorized tool invocation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Policy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What attributes feed authorization?&lt;/td&gt;
&lt;td&gt;Subject, tenant, action, resource, and environment come from trusted auth and membership data, not telemetry or model-visible state.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  A Test That Should Exist
&lt;/h3&gt;

&lt;p&gt;A checklist is useful, but I would still want one boring regression test sitting in CI.&lt;/p&gt;

&lt;p&gt;This test has tenant &lt;code&gt;A&lt;/code&gt; write a fact. Tenant &lt;code&gt;B&lt;/code&gt; reads through the normal scoped API and &lt;strong&gt;&lt;em&gt;must&lt;/em&gt;&lt;/strong&gt; get nothing back.&lt;/p&gt;

&lt;p&gt;Here is the tell: &lt;strong&gt;&lt;em&gt;if you can only make that test pass by reaching for a special bypass, your isolation boundary is living in the wrong layer.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check this illustrative snippet showing the test shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_memory_store_does_not_cross_tenants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_store&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Tenant A and tenant B share the same subject_id on purpose.
&lt;/span&gt;    &lt;span class="c1"&gt;# The tenant boundary, not the subject ID, must isolate the data.
&lt;/span&gt;    &lt;span class="n"&gt;tenant_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tenant-a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;member&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trace-a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tenant_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tenant-b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;member&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trace-b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Write through the normal tenant-scoped API.
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;memory_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;tenant_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;case-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User approved the draft contract.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# A second tenant asks for the same subject. The expected result is empty,
&lt;/span&gt;    &lt;span class="c1"&gt;# or a permission error if your API chooses to fail closed more loudly.
&lt;/span&gt;    &lt;span class="n"&gt;visible_to_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;memory_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_facts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;tenant_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;case-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;visible_to_b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would push for the same test shape for vector retrieval, cache keys, stream subscriptions, background jobs, and tool calls. One small test per surface is a lot cheaper than writing the incident report for a cross-tenant leak after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Honest Tradeoff for an MVP
&lt;/h2&gt;

&lt;p&gt;There is one shortcut I can live with early on: &lt;code&gt;org_id = user_id&lt;/code&gt;. For a personal-workspace MVP, that can be enough, as long as the interface already treats "&lt;em&gt;organization&lt;/em&gt;" as a real concept and membership still comes from &lt;strong&gt;&lt;em&gt;trusted auth data&lt;/em&gt;&lt;/strong&gt; rather than a &lt;strong&gt;&lt;em&gt;field the user can set&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The shortcut that actually hurts is pretending the concept does not exist at all. Build every table, cache key, graph thread, and tool policy around a &lt;em&gt;user-only model&lt;/em&gt;, and the day your first real organization shows up, moving to tenants is a rewrite across the entire platform instead of a swap behind one abstraction.&lt;/p&gt;

&lt;p&gt;Here is my take on both shortcuts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Acceptable early shortcut:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;org_id = user_id&lt;/code&gt; behind an &lt;code&gt;OrgContext&lt;/code&gt; or &lt;code&gt;RequestContext&lt;/code&gt; abstraction, with a trusted membership lookup ready to replace it.&lt;/li&gt;
&lt;li&gt;Tables already include &lt;code&gt;tenant_id&lt;/code&gt; or &lt;code&gt;org_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tests use two tenants, even if each tenant has one user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Shortcut that ages badly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User IDs are threaded everywhere by hand, tools read global configuration, and memory keys are plain &lt;code&gt;thread_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The first real organization forces a schema and API redesign at the same time.&lt;/li&gt;
&lt;li&gt;There is no clean place to add membership.&lt;/li&gt;
&lt;li&gt;There is no reliable way to run cross-tenant leak tests, because the platform is fully centered on a single-user model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I would &lt;strong&gt;recommend here:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are going to defer real organizations, keep the tenant-shaped interface anyway. You can always swap the implementation underneath later. What you cannot do cheaply is add the boundary back after every call site has spent six months learning to ignore it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Take Away
&lt;/h2&gt;

&lt;p&gt;Multi-tenancy is not a feature you add on top of your orchestration. It is the boundary your orchestration runs inside, and the sooner you treat it that way, the less it costs you later.&lt;/p&gt;

&lt;p&gt;If you have to do one thing after reading this article, &lt;strong&gt;&lt;em&gt;do this&lt;/em&gt;&lt;/strong&gt;: pick a single agent flow and trace the tenant context from the HTTP request all the way to the final tool call. Write down every spot where the context gets copied, inferred, dropped, logged, or turned into a key. That little map is usually where the real work, and the real risk, has been hiding.&lt;/p&gt;

&lt;p&gt;Next, I will probably go a level deeper into request-context propagation for agents: &lt;em&gt;how to carry tenant, locale, role, and trace metadata&lt;/em&gt; through the graph without polluting every tool signature to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/" rel="noopener noreferrer"&gt;OWASP API Security Top 10, API1:2023 Broken Object Level Authorization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://owasp.org/API-Security/editions/2023/en/0xa5-broken-function-level-authorization/" rel="noopener noreferrer"&gt;OWASP API Security Top 10, API5:2023 Broken Function Level Authorization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP Multi-Tenant Application Security Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/RAG_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP RAG Security Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/AI_Agent_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP AI Agent Security Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP Logging Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://csrc.nist.gov/pubs/sp/800/207/final" rel="noopener noreferrer"&gt;NIST SP 800-207, Zero Trust Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://csrc.nist.gov/pubs/sp/800/207/a/final" rel="noopener noreferrer"&gt;NIST SP 800-207A, A Zero Trust Architecture Model for Access Control in Cloud-Native Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://csrc.nist.gov/pubs/sp/800/162/upd2/final" rel="noopener noreferrer"&gt;NIST SP 800-162, Attribute-Based Access Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/engineering/building-effective-agents" rel="noopener noreferrer"&gt;Anthropic, Building Effective Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opentelemetry.io/docs/concepts/context-propagation/" rel="noopener noreferrer"&gt;OpenTelemetry, Context Propagation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opentelemetry.io/docs/concepts/signals/baggage/" rel="noopener noreferrer"&gt;OpenTelemetry, Baggage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/trace-context/" rel="noopener noreferrer"&gt;W3C Trace Context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/baggage/" rel="noopener noreferrer"&gt;W3C Baggage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>multiplatform</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Make GitHub Contribution Chart fancier</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Tue, 06 Feb 2024 09:23:55 +0000</pubDate>
      <link>https://dev.to/luffy_14/make-github-contribution-chart-fancier-1mgi</link>
      <guid>https://dev.to/luffy_14/make-github-contribution-chart-fancier-1mgi</guid>
      <description>&lt;p&gt;Hello, fellow developers and the GoLang community!&lt;/p&gt;

&lt;p&gt;It always bothered me when I opened my GitHub profile and saw my contribution charts empty, while I did commit every day to work on GitLab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3smofa4tp0ydmx4kftgt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3smofa4tp0ydmx4kftgt.png" alt="GitHub Contribution Chart" width="800" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, I thought why not bring my GitLab commits to Github and make my contribution chart look a bit fancy? ✨✨&lt;/p&gt;

&lt;p&gt;As you know you master a programming language by building things with it, so I thought of building a very basic/simple tool using GoLang that does exactly that, fetch my commits from GitLab, specifically the dates of the commits, then push those to a private GitHub repo as an empty commits reserving their dates for history records.&lt;/p&gt;

&lt;p&gt;The tool doesn't get any information from GitLab except the dates and pushes them to GitHub as empty commits with a standard commit message.&lt;/p&gt;

&lt;p&gt;And my chart transformed like this! 🌟🌟&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9d6o6i93v0rh1lkges5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9d6o6i93v0rh1lkges5.png" alt="Improved GitHub Contribution Chart" width="800" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tool is designed in a way to support multiple different repository management tools such as BitBucket, but if you need that your contribution is more than welcome!&lt;br&gt;
Currently, only GitLab &amp;amp; GitHub.&lt;/p&gt;

&lt;p&gt;Find it here (Make sure to read the ReadMe file): &lt;a href="https://github.com/AhmedRaafat14/universal-commits-migrator"&gt;https://github.com/AhmedRaafat14/universal-commits-migrator&lt;/a&gt;&lt;br&gt;
Very basic/intuitive docs: &lt;a href="https://ahmedraafat14.github.io/universal-commits-migrator/"&gt;https://ahmedraafat14.github.io/universal-commits-migrator/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks&lt;/p&gt;

</description>
      <category>go</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Unveiling PHP Library Development 101: from Basics to Advanced</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Sun, 04 Feb 2024 22:27:10 +0000</pubDate>
      <link>https://dev.to/luffy_14/unveiling-php-library-development-101-from-basics-to-advanced-47c3</link>
      <guid>https://dev.to/luffy_14/unveiling-php-library-development-101-from-basics-to-advanced-47c3</guid>
      <description>&lt;p&gt;🚀 PHP Library Development 101 book is out for &lt;strong&gt;FREE&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;Hello, DEV Community!&lt;/p&gt;

&lt;p&gt;I'm excited to offer my book, &lt;strong&gt;PHP Library Development 101: From Basics to Advanced&lt;/strong&gt; for &lt;strong&gt;FREE&lt;/strong&gt;. With nearly a decade of working experience in PHP, I've distilled my learning and discoveries into this book to help others embark on their journey in building PHP libraries.&lt;/p&gt;

&lt;p&gt;This book was reviewed by expert PHP developers to ensure the accuracy of the content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Book?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This book is not just about understanding PHP libraries; it's a comprehensive guide aimed at shaping you into a proficient PHP developer. Whether you're just starting or are an experienced developer, this book is tailored to enhance your skills deepen your understanding of PHP library development, and put you on the right path in the PHP library development world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What You'll Learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The foundational concepts of PHP libraries.&lt;/li&gt;
&lt;li&gt;Advanced topics like Namespaces, Autoloading, Exceptions, and more.&lt;/li&gt;
&lt;li&gt;Practical approaches to write reusable code and contribute to the open-source community.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My Learning Philosophy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I believe in learning by sharing and writing. This book embodies that philosophy, serving as a practical starting point for those looking to master PHP libraries and advanced PHP subjects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be Part of This Journey&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As we approach the release of the book, I invite you to subscribe for exclusive early insights and chapter previews. Your feedback and participation would be invaluable to this journey.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Get The book&lt;/strong&gt;&lt;br&gt;
You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two PDF files one in Dark and the other in Light themes.&lt;/li&gt;
&lt;li&gt;Two directories contain the code of the library built through the book.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find it here: &lt;a href="https://ahmadraafat.gumroad.com/l/php-library-development-book"&gt;https://ahmadraafat.gumroad.com/l/php-library-development-book&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And subscribe to my newsletter directly: &lt;a href="https://ahmadraafat.gumroad.com/subscribe"&gt;https://ahmadraafat.gumroad.com/subscribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Unveiling PHP Library Development 101: from Basics to Advanced</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Thu, 04 Jan 2024 11:22:04 +0000</pubDate>
      <link>https://dev.to/luffy_14/unveiling-php-library-development-101-from-basics-to-advanced-4pf4</link>
      <guid>https://dev.to/luffy_14/unveiling-php-library-development-101-from-basics-to-advanced-4pf4</guid>
      <description>&lt;p&gt;🌟 &lt;strong&gt;Early Insights Alert: PHP Library Development 101&lt;/strong&gt; 🌟&lt;/p&gt;

&lt;p&gt;Hello, DEV Community!&lt;/p&gt;

&lt;p&gt;I'm excited to offer an exclusive sneak peek into my upcoming book, &lt;strong&gt;PHP Library Development 101: From Basics to Advanced&lt;/strong&gt;. With nearly a decade of working experience in PHP, I've distilled my learning and discoveries into this book to help others embark on their journey in building PHP libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Book?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This book is not just about understanding PHP libraries; it's a comprehensive guide aimed at shaping you into a proficient PHP developer. Whether you're just starting or are an experienced developer, this book is tailored to enhance your skills deepen your understanding of PHP library development, and put you on the right path in the PHP library development world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What You'll Learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The foundational concepts of PHP libraries.&lt;/li&gt;
&lt;li&gt;Advanced topics like Namespaces, Autoloading, Exceptions, and more.&lt;/li&gt;
&lt;li&gt;Practical approaches to write reusable code and contribute to the open-source community.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My Learning Philosophy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I believe in learning by sharing and writing. This book embodies that philosophy, serving as a practical starting point for those looking to master PHP libraries and advanced PHP subjects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be Part of This Journey&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As we approach the release of the book, I invite you to subscribe for exclusive early insights and chapter previews. Your feedback and participation would be invaluable to this journey.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Sneak Peek and Subscription&lt;/strong&gt;&lt;br&gt;
Get a glimpse of the content and be among the first to access the book by subscribing: &lt;a href="https://ahmadraafat.gumroad.com/l/php-library-development-101-book"&gt;https://ahmadraafat.gumroad.com/l/php-library-development-101-book&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or subscribe to my newsletter directly: &lt;a href="https://ahmadraafat.gumroad.com/subscribe"&gt;https://ahmadraafat.gumroad.com/subscribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
      <category>symfony</category>
      <category>laravel</category>
    </item>
    <item>
      <title>30-Day LeetCoding Challenge: Product of Array Except Self</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Thu, 16 Apr 2020 11:24:19 +0000</pubDate>
      <link>https://dev.to/luffy_14/30-day-leetcoding-challenge-product-of-array-except-self-36ic</link>
      <guid>https://dev.to/luffy_14/30-day-leetcoding-challenge-product-of-array-except-self-36ic</guid>
      <description>&lt;h3&gt;
  
  
  &lt;a href="https://leetcode.com/explore/challenge/card/30-day-leetcoding-challenge/530/week-3/3300/"&gt;The problem&lt;/a&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  Solution:
&lt;/h2&gt;

&lt;p&gt;The problem asks us to replace each element in an array with the product of the other array elements and also asks us to do it in &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, for example if we have this array &lt;code&gt;arr = [1, 2, 3]&lt;/code&gt;, the result should be &lt;code&gt;ans = [6, 3, 2]&lt;/code&gt; do we replaced the &lt;code&gt;1&lt;/code&gt; with &lt;code&gt;2 * 3 = 6&lt;/code&gt; and so on.&lt;/p&gt;

&lt;p&gt;To do this in &lt;strong&gt;O(n)&lt;/strong&gt; should make you think a little bit about how we can do that! I can think of that we have two arrays one we put for each element the product of the elements before it (left) and another one do the same but from the end (right) some kind of DP. Then we do another loop to multiple for each element the &lt;code&gt;left[i] * right[i]&lt;/code&gt; and that will be our answer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;try to do this solution yourself :).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then I saw that they asking for a follow-up to do it in constant space without considering our output array.&lt;/p&gt;

&lt;p&gt;So, I thought of simulating the two array approach but instead of using two arrays, I will just use two pointers one of them will call it &lt;code&gt;left_product&lt;/code&gt; and this one will go from the left to right and another pointer called &lt;code&gt;right_product&lt;/code&gt; which will go from the right to the left.&lt;/p&gt;

&lt;p&gt;The only thing when I do from the left I use index &lt;code&gt;i = 0, 1,..n&lt;/code&gt; but from the right we need the invert (~) of this number so we can get it from the right &lt;code&gt;i = -1,-2,...-n&lt;/code&gt;, and we keep updating the two pointers with the latest product we find until we finish with the whole array.&lt;/p&gt;

&lt;p&gt;Let's do an example to make it more clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define &lt;code&gt;arr = [1, 2, 3]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We will be using &lt;code&gt;left_product = 1&lt;/code&gt;, &lt;code&gt;right_product = 1&lt;/code&gt; we initialise them with &lt;code&gt;1&lt;/code&gt; as we do product.&lt;/li&gt;
&lt;li&gt;We will initialise as well our answer array by the same size of our &lt;code&gt;arr&lt;/code&gt; but will be filled with ones &lt;code&gt;ans = [1, 1, 1]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start with &lt;code&gt;idx = 0&lt;/code&gt; so our &lt;code&gt;num = arr[idx] = 1&lt;/code&gt; now will find our &lt;code&gt;ans[idx]&lt;/code&gt; by multiple it by the latest &lt;code&gt;left_product&lt;/code&gt; we have so: &lt;code&gt;ans[0] = ans[0] * left_product = 1 * 1 = 1&lt;/code&gt;, our new answer is &lt;code&gt;ans = [1, 1, 1]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, we calculate the new &lt;code&gt;left_product&lt;/code&gt; by multiple it with the current &lt;code&gt;arr[idx]&lt;/code&gt; so it will be &lt;code&gt;left_product = left_product * arr[0] = 1 * 1 = 1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do the right as we said we need to use the invert so &lt;code&gt;idx = - (idx + 1)&lt;/code&gt; or we can just use the invert operator &lt;code&gt;idx = ~idx&lt;/code&gt;, and find our new &lt;code&gt;ans[~idx] = ans[-1]&lt;/code&gt; will multiple it by the right_product so &lt;code&gt;ans[-1] = ans[-1] * right_product = 1 * 1 = 1&lt;/code&gt;, and our new answer is &lt;code&gt;ans = [1, 1, 1]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calculate the &lt;code&gt;right_product&lt;/code&gt; by multiple it's value with the &lt;code&gt;arr[~idx]&lt;/code&gt; so it will be &lt;code&gt;right_product = right_product * arr[-1] = 1 * 3 = 3&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Well, keep doing the above procedure over and over until you will reach the final answer array which will be &lt;code&gt;ans = [6, 3, 2]&lt;/code&gt;. Do it on paper so you can get the idea very clear.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pseudocode:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. define ans = [1] * len(arr)
2. define our pointers left_product,right_product = 1, 1
3. loop over the arr from i = 0 to len(arr)
4.    calculate ans[i] = ans[i] * left_product
5.    calculate left_product = left_product * arr[i]
6.    calculate ans[~i] = ans[~i] * right_product
7.    calculate right_product = right_product * arr[~i]
8. return our ans
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Time Complexity: it is so clear we do &lt;strong&gt;O(n)&lt;/strong&gt; one loop over the array.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Space Complexity: taking away the answer array it will be constant space &lt;strong&gt;O(1)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/AhmedRaafat14/Leetcode-problems/blob/master/30-Day-LeetCoding-Challenge/product-of-array-except-self/Solution.py"&gt;Solution in python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>leetcode</category>
      <category>coding</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>How many requests can the 4-cores server that run PHP app take at the same time?</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Wed, 15 Apr 2020 09:57:46 +0000</pubDate>
      <link>https://dev.to/luffy_14/how-many-requests-can-the-4-cores-server-take-at-the-same-time-16ef</link>
      <guid>https://dev.to/luffy_14/how-many-requests-can-the-4-cores-server-take-at-the-same-time-16ef</guid>
      <description>&lt;p&gt;If you have a server with 4-cores and 8GB ram, how many requests can this server take at the same time and how many child processes you can run on it?&lt;br&gt;
I'm taking about PHP and if you are running a PHP application there.&lt;/p&gt;

</description>
      <category>php</category>
      <category>discuss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>30-Day LeetCoding Challenge: Perform String Shifts</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Wed, 15 Apr 2020 07:56:19 +0000</pubDate>
      <link>https://dev.to/luffy_14/30-day-leetcoding-challenge-perform-string-shifts-4eih</link>
      <guid>https://dev.to/luffy_14/30-day-leetcoding-challenge-perform-string-shifts-4eih</guid>
      <description>&lt;h3&gt;
  
  
  &lt;a href="https://leetcode.com/explore/challenge/card/30-day-leetcoding-challenge/529/week-2/3299/"&gt;The problem&lt;/a&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  Solution:
&lt;/h2&gt;

&lt;p&gt;The problem is asking us to do some shifts on a string but doing that with some rules in considerations, it says you will have a string &lt;code&gt;s&lt;/code&gt; and an array called &lt;code&gt;shifts&lt;/code&gt; that will hold a list of lists (array of arrays) each item will have two elements the first element is the direction of the shift and the second one is the amount to move.&lt;/p&gt;

&lt;p&gt;The directions are &lt;code&gt;0&lt;/code&gt; for left shift and &lt;code&gt;1&lt;/code&gt; for the right shift.&lt;/p&gt;

&lt;p&gt;So, if the shifts array like this &lt;code&gt;shifts = [[0, 1], [1, 2]]&lt;/code&gt;, so we will shift the string twice the first shift with &lt;code&gt;direction = 0&lt;/code&gt; and &lt;code&gt;amount = 1&lt;/code&gt; means do left shift by the &lt;code&gt;amount = 1&lt;/code&gt; chars so remove the first letter and append it to the end, and the right shift &lt;code&gt;1&lt;/code&gt; is the opposite remove from the end and put it first.&lt;/p&gt;

&lt;p&gt;let's do an example to make it more clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;define &lt;code&gt;s = 'abc'&lt;/code&gt; and &lt;code&gt;shifts = [[0, 1], [1, 2]]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So with the first &lt;code&gt;shift = [0, 1]&lt;/code&gt; we see our direction &lt;code&gt;direction = 0&lt;/code&gt; and our amount &lt;code&gt;amount = 1&lt;/code&gt;, so we remove from the beginning and append to the end, so remove &lt;code&gt;a&lt;/code&gt; and add to the end &lt;code&gt;s = 'bca'&lt;/code&gt; and decrease our amount by 1 &lt;code&gt;amount = 1 - 1 = 0&lt;/code&gt; so we are done with this shift.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, the second &lt;code&gt;shift = [1, 2]&lt;/code&gt; the &lt;code&gt;direction = 1&lt;/code&gt; says do right shift which means we will move the last &lt;code&gt;amount = 2&lt;/code&gt; to the beginning of the strings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First we move the &lt;code&gt;a&lt;/code&gt; from &lt;code&gt;s = 'bca'&lt;/code&gt; to beginning it will be &lt;code&gt;s = 'abc'&lt;/code&gt;, and our &lt;code&gt;amount = 2 - 1 = 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Move the &lt;code&gt;c&lt;/code&gt; from &lt;code&gt;s = 'abc'&lt;/code&gt; to the beginning it will be &lt;code&gt;s = 'cab'&lt;/code&gt; and the &lt;code&gt;amount = 1 - 1 = 0&lt;/code&gt; so we are done.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We don't have any other shifts so we stop here with the final answer as &lt;code&gt;s = 'cab'&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you look more closely to the approach and how we reached our answer you will find a pattern for example to do the left shift by &lt;code&gt;one&lt;/code&gt; letter we moved &lt;code&gt;s[0]&lt;/code&gt; to the end we can simulate it to be &lt;code&gt;s = s[0+1:] + s[0] = s[1:] + s[0] = s[1:] + s[:1]&lt;/code&gt; right from the 1st index to the end plus our one single letter if we look very well that the one in &lt;code&gt;s[1:] &amp;amp; s[:1]&lt;/code&gt; is exactly our amount so we can say I need to bring every char after the &lt;code&gt;amount&lt;/code&gt; to the beginning and put every char from beginning to the &lt;code&gt;amount&lt;/code&gt; at the end so it will be: &lt;code&gt;s = s[amount:] + s[:amount]&lt;/code&gt; that's how we do the left shift.&lt;/p&gt;

&lt;p&gt;For the right shift we do from the end so in Python to call the string from the end we use the &lt;code&gt;minus (-)&lt;/code&gt; sign, for example, indexes from beginning for &lt;code&gt;s = 'abc'&lt;/code&gt; is &lt;code&gt;0, 1, 2&lt;/code&gt; but from the tail (end) is &lt;code&gt;-1, -2, -3&lt;/code&gt; so we can use the same equation with just adding the &lt;code&gt;-&lt;/code&gt; to our amount so move from the end to beginning using &lt;code&gt;s = s[-amount:] + s[:-amount]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pseudocode:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. loop over the shifts for each shift
2.    define direction = shift[0] and amount = shift[1]
3.    check if direction is 0 so do left shift using s[amount:] + s[:amount]
4.    otherwise direction is 1 do the right shift using s[-amount:] + s[:-amount]
5. return our final s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Time Complexity: So we have a loop over the shifts which will be &lt;strong&gt;O(n)&lt;/strong&gt; where &lt;code&gt;n&lt;/code&gt; is the number of shifts we do, in each shift we do modification on the string and that will cost us &lt;strong&gt;O(c)&lt;/strong&gt; where &lt;code&gt;c&lt;/code&gt; is the number of chars in the string so our overall time will be &lt;strong&gt;O(n . c)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Space Complexity: While modifying the string we keep the old &amp;amp; the new string in memory so that will cost us the length of the string which will be &lt;strong&gt;O(c)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/AhmedRaafat14/Leetcode-problems/blob/master/30-Day-LeetCoding-Challenge/perform-string-shifts/Solution.py"&gt;Solution in python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>leetcode</category>
      <category>coding</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>30-Day LeetCoding Challenge: Contiguous Array</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Tue, 14 Apr 2020 13:51:27 +0000</pubDate>
      <link>https://dev.to/luffy_14/30-day-leetcoding-challenge-contiguous-array-488b</link>
      <guid>https://dev.to/luffy_14/30-day-leetcoding-challenge-contiguous-array-488b</guid>
      <description>&lt;h3&gt;
  
  
  &lt;a href="https://leetcode.com/explore/challenge/card/30-day-leetcoding-challenge/529/week-2/3298/"&gt;The problem&lt;/a&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  Solution:
&lt;/h2&gt;

&lt;p&gt;The problem asking us to find the maximum length of the subarray that contains equal numbers of 0 and 1.&lt;/p&gt;

&lt;p&gt;We can say if we have zero counter and one counter our contiguous subarray with an equal number of 0 &amp;amp; 1 will happen when the counters are equal right!&lt;/p&gt;

&lt;p&gt;So let's define one counter called &lt;code&gt;count = 0&lt;/code&gt; and when we see &lt;code&gt;0&lt;/code&gt; we decrease the count by &lt;code&gt;1&lt;/code&gt; and when we see &lt;code&gt;1&lt;/code&gt; we increase it with &lt;code&gt;1&lt;/code&gt; and when the &lt;code&gt;count == 0&lt;/code&gt; that means we have our subarray so calculate the length.&lt;/p&gt;

&lt;p&gt;To be able to find our answer which is the &lt;code&gt;maximum length&lt;/code&gt; we need to store this counts somewhere associated with their indexes and when we encounter the same count again then we subtract the new index with the stored index and find our answer.&lt;/p&gt;

&lt;p&gt;I can't think of any data structure to help us do that besides the &lt;strong&gt;hash-map&lt;/strong&gt; so we will be using it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;define &lt;code&gt;arr = [0, 0, 1, 0, 0, 0, 1, 1]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;define &lt;code&gt;start with map = {0: -1}&lt;/code&gt; to hold our counts associated with indexes&lt;/li&gt;
&lt;li&gt;define &lt;code&gt;count = 0&lt;/code&gt; to count the number of zeros and ones&lt;/li&gt;
&lt;li&gt;&lt;p&gt;define &lt;code&gt;max_length = 0&lt;/code&gt; to hold our answer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When we encounter first &lt;code&gt;arr[0] = 0&lt;/code&gt; so we decrease the count by &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;count -= 1 = -1&lt;/code&gt; do we have count &lt;code&gt;-1&lt;/code&gt; in our &lt;code&gt;map&lt;/code&gt; so so store it with the index: &lt;code&gt;map[count] = arr[0]&lt;/code&gt; =&amp;gt; &lt;code&gt;map = {0: -1, -1: 0}&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When &lt;code&gt;arr[1] = 0&lt;/code&gt; so our new count will be &lt;code&gt;count = -2&lt;/code&gt; not in the map so add it: &lt;code&gt;map = {0: -1, -1: 0, -2: 1}&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When &lt;code&gt;arr[2] = 1&lt;/code&gt; now increase our count by one so it will be &lt;code&gt;count = -2 + 1 = -1&lt;/code&gt; do we have this in our &lt;code&gt;map&lt;/code&gt; yes we have it so we subtract the current index &lt;code&gt;2&lt;/code&gt; from the one stored in the &lt;code&gt;map&lt;/code&gt; ==&amp;gt; &lt;code&gt;0&lt;/code&gt;, so it will be &lt;code&gt;tmp = 2 - 0 = 2&lt;/code&gt; compare it with our &lt;code&gt;max_length&lt;/code&gt; and choose the max: &lt;code&gt;max_length = max(max_length, tmp) = max(0, 2) = 2&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then we continue doing this until the end we will get our final answer which will be &lt;code&gt;max_length = 6&lt;/code&gt;, do it on paper to get the idea.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pseudocode:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. define our count_idx_map = {0: -1}
2. define our max_length = 0
3. define count = 0
4. loop over the arr from i = 0 to len(arr)
5.    add 1 to count if arr[i] = 1 and decrease 1 if it is = 0
6.    if count in count_idx_map find our max_length = max(max_length, i - count_idx_map[count])
7.    otherwise add the count to the map count_idx_map[count] = i
8. at end return out max_length
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Time Complexity: We do one loop over our array items &lt;strong&gt;(n)&lt;/strong&gt; and inside the loop, we do check if the &lt;code&gt;count&lt;/code&gt; in the map or not but this takes only &lt;strong&gt;O(1)&lt;/strong&gt; as it is hash-map, so overall will be &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Space Complexity: Our hash-map in the worst case will be filled with counts for all array items if there is no 0 or 1 so we can say it is &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/AhmedRaafat14/Leetcode-problems/blob/master/30-Day-LeetCoding-Challenge/contiguous-array/Solution.py"&gt;Solution in python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>leetcode</category>
      <category>coding</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>30-Day LeetCoding Challenge: Last Stone Weight</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Tue, 14 Apr 2020 12:49:19 +0000</pubDate>
      <link>https://dev.to/luffy_14/30-day-leetcoding-challenge-last-stone-weight-3iej</link>
      <guid>https://dev.to/luffy_14/30-day-leetcoding-challenge-last-stone-weight-3iej</guid>
      <description>&lt;h3&gt;
  
  
  &lt;a href="https://leetcode.com/explore/challenge/card/30-day-leetcoding-challenge/529/week-2/3297/"&gt;The problem&lt;/a&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  Solution:
&lt;/h2&gt;

&lt;p&gt;The problem says that we have an array of stones weights and we need to keep smashing them until we only have one weight remains and return it.&lt;/p&gt;

&lt;p&gt;With some conditions which are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;we choose the two &lt;strong&gt;heaviest&lt;/strong&gt; weights &lt;code&gt;x &amp;amp; y&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;if the two weights are equal we smash both of them entirely &lt;code&gt;smash x &amp;amp; y&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;if the two weights are not equal we destroy &lt;code&gt;x&lt;/code&gt; and update the &lt;code&gt;y&lt;/code&gt; weight to be &lt;code&gt;y = y - x&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, we will have only one stone left and that will be our answer.&lt;/p&gt;

&lt;p&gt;If we think about the first step we need to pick the &lt;strong&gt;heaviest&lt;/strong&gt; two stones with an un-sorted array that is not easy it will cost us a lot of time to find it each time we need to pick a new two stones, so this a perfect use case of &lt;strong&gt;max-heap&lt;/strong&gt; whenever you call it returns the maximum element it has it use tree underlaying to do it or an array or whatever doesn't matter. We need to focus only on the use of it.&lt;/p&gt;

&lt;p&gt;So, we use max-heap and call it each time to get the heaviest stones.&lt;/p&gt;

&lt;p&gt;Let's do an example so it can be more clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have list of stones: &lt;code&gt;stones = [2,7,4,1,8,1]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We will create &lt;code&gt;max-heap&lt;/code&gt; with the &lt;code&gt;stones&lt;/code&gt; array so let's assume it will look like this underlaying: &lt;code&gt;stones_heap = [1,1,2,4,7,8]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need to pick the heaviest two stones so will pop from the heap: &lt;code&gt;stone_1 = stones_heap.pop() = 8&lt;/code&gt; =&amp;gt; &lt;code&gt;stones_heap = [1,1,2,4,7]&lt;/code&gt; and &lt;code&gt;stone_2 = stones_heap.pop() = 7&lt;/code&gt; ==&amp;gt; &lt;code&gt;stones_heap = [1,1,2,4]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, we check is &lt;code&gt;stone_1 == stone_2&lt;/code&gt; no they are not. So, we destroy &lt;code&gt;stone_1&lt;/code&gt; and update our &lt;code&gt;stone_2 = stone_2 - stone_1 = 8 - 7 = 1&lt;/code&gt;, then we add it to our heap &lt;code&gt;stones_heap.push(stone_2) = stones_heap.push(1)&lt;/code&gt; ==&amp;gt; &lt;code&gt;stones_heap = [1,1,1,2,4]&lt;/code&gt; of course the heap takes care of putting the new weight in the correct place.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We keep doing this process again and again until we will have only one item in our heap &lt;code&gt;stones_heap = [1]&lt;/code&gt; and this will be our answer to return. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do it to the end on paper to get the full idea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pseudocode:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We need to keep in mind in some languages like python it only implements &lt;strong&gt;min-heap&lt;/strong&gt; so to be able to use a &lt;strong&gt;max-heap&lt;/strong&gt; we need to transform all the numbers to be negative and transform it back to it is original when we return our answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I am solving in &lt;code&gt;Python&lt;/code&gt; I will write the following in the same steps as it is implemented in python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Loop over the stones array from i = 0 to len(stones)
2.    stones[i] *= -1 so we can use the max-heap
3. put the stones in heap: heapq.heapify(stones)
4. loop until the len(stones) is equal to 1
5.    stone_1 = stones.heappop()
6.    stone_2 = stones.heappop()
7.    if stone_1 != stone_2
8.        we need to add the new stone weight to the heap: heapq.heappush(stones, stone_1 - stone_2)
9. if the heap is empty return 0
10. otherwise, return -heapq.heappop(stones)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using language that has &lt;code&gt;max-heap&lt;/code&gt; discard the steps &lt;code&gt;1 &amp;amp; 2&lt;/code&gt; and in the step &lt;code&gt;#10&lt;/code&gt; remove the &lt;code&gt;minus (-)&lt;/code&gt; from the beginning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Time Complexity: To transform the list to heap we will take &lt;strong&gt;O(n)&lt;/strong&gt; where &lt;code&gt;n&lt;/code&gt; is the number of stones we have, and the loop will take only &lt;strong&gt;O(n - 1) = O(n)&lt;/strong&gt; and the operations to push &amp;amp; pop will take &lt;strong&gt;O(log(n))&lt;/strong&gt; so overall will take: &lt;strong&gt;O(n log(n))&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Space Complexity: In our python solution you should now that it modifies the list in-place and we don't use any extra space so our overall space will be &lt;strong&gt;O(1)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/AhmedRaafat14/Leetcode-problems/blob/master/30-Day-LeetCoding-Challenge/last-stone-weight/Solution.py"&gt;Solution in python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>leetcode</category>
      <category>coding</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>30-Day LeetCoding Challenge: Diameter of Binary Tree</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Tue, 14 Apr 2020 11:53:28 +0000</pubDate>
      <link>https://dev.to/luffy_14/30-day-leetcoding-challenge-diameter-of-binary-tree-3124</link>
      <guid>https://dev.to/luffy_14/30-day-leetcoding-challenge-diameter-of-binary-tree-3124</guid>
      <description>&lt;h3&gt;
  
  
  &lt;a href="https://leetcode.com/explore/challenge/card/30-day-leetcoding-challenge/529/week-2/3293/"&gt;The problem&lt;/a&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  Solution:
&lt;/h2&gt;

&lt;p&gt;It asks us to find a binary tree diameter, it also explains to us that is the diameter of a binary tree is &lt;strong&gt;the length of the longest path between any two nodes in a tree.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is also indicated that doesn't matter if the path passes by the root or not.&lt;/p&gt;

&lt;p&gt;So, we need to calculate the depth of each node in the tree and then find the longest path between the nodes.&lt;/p&gt;

&lt;p&gt;First the depth of node can be found by &lt;code&gt;node.depth = max(node.left.depth, node.right.depth) + 1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, the path always will be the depth of the left subtree + the depth of the right subtree, so on each node, we need to choose the maximum value that takes us to the final answer so our answer will be: &lt;code&gt;diameter = max(diameter, left_depth + right_depth)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, how we can calculate that if you look to the illustration above we see that we need to go deep to the tree to get one node depth which means we need to use &lt;code&gt;depth-first search DFS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We use DFS to find the left subtree depth then find the right subtree depth and later choose between them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pseudocode:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;diameterOfBinaryTree(root)&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. define our answer
2. call find_depth_helper(root) with the root to find the longest path.
3. return our answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;find_depth_helper(node)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. if the node is None return 0
2. find_depth_helper(node.left) --&amp;gt; left_depth
3. find_depth_helper(node.right) --&amp;gt; right_depth
4. answer = max(answer, left_depth + right_depth)
5. return the current node depth max(left_depth, right_depth) + 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time Complexity: it is so clear that we go over all our nodes so our time will be &lt;strong&gt;O(n)&lt;/strong&gt; where &lt;strong&gt;n&lt;/strong&gt; is the number of nodes in the tree.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Space Complexity: we use recursion stack as we do recursion calls and this stack will be equal to the number of nodes we have so our overall space will be &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/AhmedRaafat14/Leetcode-problems/blob/master/30-Day-LeetCoding-Challenge/diameter-of-binary-tree/Solution.py"&gt;Solution in python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>leetcode</category>
      <category>coding</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>30-Day LeetCoding Challenge: Min Stack</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Tue, 14 Apr 2020 10:08:23 +0000</pubDate>
      <link>https://dev.to/luffy_14/30-day-leetcoding-challenge-min-stack-20l7</link>
      <guid>https://dev.to/luffy_14/30-day-leetcoding-challenge-min-stack-20l7</guid>
      <description>&lt;h3&gt;
  
  
  &lt;a href="https://leetcode.com/explore/challenge/card/30-day-leetcoding-challenge/529/week-2/3292/"&gt;The problem&lt;/a&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  Solution:
&lt;/h2&gt;

&lt;p&gt;The question is asking us to build &lt;code&gt;Stack&lt;/code&gt; but a special one that returns the minimum value in it in constant time &lt;strong&gt;O(1)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We all now very clear that Stack is working with the &lt;strong&gt;LIFO&lt;/strong&gt; principle which means &lt;strong&gt;Last In First Out&lt;/strong&gt;, and why this is important to think about because we know pushing item to stack takes &lt;strong&gt;O(1)&lt;/strong&gt; and also removing an item from stack takes &lt;strong&gt;O(1)&lt;/strong&gt;, and getting the top item of the stack is &lt;strong&gt;O(1)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We all know this very clear so it is easy to build stack class using list (array) to store the items.&lt;/p&gt;

&lt;p&gt;But here we need a special function called &lt;code&gt;getMin()&lt;/code&gt; which should return to us the minimum item in the stack in &lt;strong&gt;O(1)&lt;/strong&gt; if we think about it ooh that is not possible I have to sort the stack first or I should do a loop over the list to find that minimum item and of course that is not &lt;strong&gt;O(1)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So how we gonna fix that okay let's see we no whenever we put an element to the stack we will not be able to remove it until we remove the one above it, imagine a stack of plates you can not remove the plate in the middle without moving the top one's first right! So we can apply the same principle by adjusting our stack a little bit to not just store our item no we will store with each one the minimum value we found so far.&lt;/p&gt;

&lt;p&gt;So, for example, let's say we are creating our stack and the first item we push is &lt;code&gt;3&lt;/code&gt; will ask is the stack empty yes it is so that means that &lt;code&gt;3&lt;/code&gt; is our minimum value we know so far!&lt;/p&gt;

&lt;p&gt;Our stack will look like this &lt;code&gt;[(item, min_item)] = [(3, 3)]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So if we add now a new item let's say &lt;code&gt;2&lt;/code&gt;, so we check again the stack now it is not empty so we check is our new item (&lt;code&gt;2&lt;/code&gt;) smaller than the minimum one stored with our top item in the stack which is &lt;code&gt;3&lt;/code&gt;, so yes &lt;code&gt;2 &amp;lt; 3&lt;/code&gt; so we push to our stack our new item associated with the new minimum value.&lt;/p&gt;

&lt;p&gt;Our new stack looks &lt;code&gt;[(3, 3), (2, 2)]&lt;/code&gt;, now if we want to call &lt;code&gt;getMin()&lt;/code&gt; it will just return the second element in the top pair in the stack :).&lt;/p&gt;

&lt;p&gt;Now, the &lt;code&gt;getMin()&lt;/code&gt; will cost &lt;strong&gt;O(1)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Try to simulate it on paper with more items.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pseudocode:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;push(number)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    1. if stack is empty
    2.    stack.append((number, number))
    3. otherwise:
    4.    define smallest_num = min(number, stack[-1][-1]) compare with the top
    5.    stack.append((number, smallest_num))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pop()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    1. always get &amp;amp; remove the first number in the top pair: stack.pop()[0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;top()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    1. always get the first number in the top pair: stack[-1][0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getMin()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  1. always get the second number in the top pair: stack [-1][1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time Complexity: for all functions will be &lt;strong&gt;O(1)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Space Complexity: we use a list to store the elements as a stack which will be &lt;strong&gt;O(n)&lt;/strong&gt; where n is the number of items, we also have a modified stack that stores the numbers in pairs so it will be &lt;strong&gt;2.O(n) == O(n)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/AhmedRaafat14/Leetcode-problems/blob/master/30-Day-LeetCoding-Challenge/min-stack/Solution.py"&gt;Solution in python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>leetcode</category>
      <category>coding</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>30-Day LeetCoding Challenge: Backspace String Compare</title>
      <dc:creator>Ahmad Ra'fat</dc:creator>
      <pubDate>Fri, 10 Apr 2020 12:49:03 +0000</pubDate>
      <link>https://dev.to/luffy_14/30-day-leetcoding-challenge-backspace-string-compare-2k0j</link>
      <guid>https://dev.to/luffy_14/30-day-leetcoding-challenge-backspace-string-compare-2k0j</guid>
      <description>&lt;h3&gt;
  
  
  &lt;a href="https://leetcode.com/explore/challenge/card/30-day-leetcoding-challenge/529/week-2/3291/"&gt;The problem&lt;/a&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  Solution:
&lt;/h2&gt;

&lt;p&gt;The problem is so easy if you read it carefully it asks if string &lt;strong&gt;S&lt;/strong&gt; and string &lt;strong&gt;T&lt;/strong&gt; are equal consider you typing them in a text editor (notepad), but it is not that easy you will have a special character called &lt;strong&gt;#&lt;/strong&gt; it means &lt;strong&gt;backspace&lt;/strong&gt; so whenever you see it assume you clicked on the backspace button in your keyboard.&lt;/p&gt;

&lt;p&gt;So, if you have a string like this &lt;code&gt;ab#c&lt;/code&gt; if we follow the rule we mentioned above, you first type &lt;code&gt;a&lt;/code&gt; then you find &lt;code&gt;b&lt;/code&gt; not a &lt;code&gt;#&lt;/code&gt; so type it and it will be &lt;code&gt;ab&lt;/code&gt; now we see &lt;code&gt;#&lt;/code&gt; and it means backspace so click on the backspace button will remove the &lt;code&gt;b&lt;/code&gt; and leave us with &lt;code&gt;a&lt;/code&gt; then the last one is &lt;code&gt;c&lt;/code&gt; type it &lt;code&gt;ac&lt;/code&gt; and this our final result.&lt;/p&gt;

&lt;p&gt;It is so obvious now whenever we see the backspace &lt;code&gt;#&lt;/code&gt; char we remove the last typed char and this takes to a data structure called &lt;strong&gt;&lt;a href="https://www.tutorialspoint.com/data_structures_algorithms/stack_algorithm.htm"&gt;Stack&lt;/a&gt;&lt;/strong&gt; as the stack works in &lt;strong&gt;LIFO&lt;/strong&gt; manner which means &lt;code&gt;last-in-first-out&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, we will go through the two strings to build a stack from their characters and in the end, we compare both of them if they are equal or not.&lt;/p&gt;

&lt;p&gt;Let us do an example.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;S = "ab#c"&lt;/code&gt;&lt;br&gt;
&lt;code&gt;T = "ad#c"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We will do each string separately and then combine all elements in the stack into one string and then compare them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We will use stack called &lt;code&gt;typed_chars = []&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Start with &lt;code&gt;S = "ab#c"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Starting with &lt;code&gt;ch = 'a'&lt;/code&gt; is our &lt;code&gt;ch == '#' # backspace&lt;/code&gt;  no it is not so add it to the stack &lt;code&gt;typed_chars = ['a']&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;With &lt;code&gt;ch = 'b'&lt;/code&gt; is &lt;code&gt;ch == '#'&lt;/code&gt; no so add it to the stack &lt;code&gt;typed_chars = ['a', 'b']&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;With &lt;code&gt;ch = '#'&lt;/code&gt; is &lt;code&gt;ch == '#'&lt;/code&gt; yes it is so we simulate the click on the &lt;code&gt;backspace #&lt;/code&gt; button by removing the last typed character &lt;code&gt;typed_chars.pop() ==&amp;gt; 'b'&lt;/code&gt; so our new stack is &lt;code&gt;typed_chars = ['a']&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;With our last &lt;code&gt;S&lt;/code&gt; char &lt;code&gt;ch = 'c'&lt;/code&gt; is &lt;code&gt;ch == '#'&lt;/code&gt; no it is not so add to the stack &lt;code&gt;typed_chars = ['a', 'c']&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build final string from the &lt;code&gt;typed_chars&lt;/code&gt; to be &lt;code&gt;new_s = 'ac'&lt;/code&gt; remember it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now will do the same with the &lt;code&gt;T = "ad#c"&lt;/code&gt; it will be exactly the same process and will end-up with &lt;code&gt;new_t = 'ac'&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If we compare both &lt;code&gt;new_s == new_t = 'ac' == 'ac' = True&lt;/code&gt;, and this is our answer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pseudocode:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. define helper function called helper that takes one string: helper(s)
2.      define typed_chars = []
3.      loop for ch in s:
4.          check if ch != '#':
5.              typed_chars.append(ch)
5.          if ch is equal to # and typed_chars not empty:
6.              typed_chars.pop()
7.      return "".join(typed_chars) to return one string
8. check if helper(S) is equal to helper(T) or not and return the result.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Time Complexity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can see that we loop two times to build our stacks the first one is for &lt;strong&gt;O(n)&lt;/strong&gt; where &lt;strong&gt;n&lt;/strong&gt; is our first-string length, and the second time is for &lt;strong&gt;O(m)&lt;/strong&gt; where &lt;strong&gt;m&lt;/strong&gt; is our second string length, so in total it will be &lt;strong&gt;O(n + m)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Space Complexity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We store our chars in stack two times of course for our first string and our second one, so in total it will be &lt;strong&gt;O(n + m)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/AhmedRaafat14/Leetcode-problems/blob/master/30-Day-LeetCoding-Challenge/backspace-string-compare/Solution.py"&gt;Solution in python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>leetcode</category>
      <category>coding</category>
      <category>algorithms</category>
    </item>
  </channel>
</rss>
