Coding-agent skill
Namespace & Isolation Preflight
Does everything belong to the right person, place, and scope?
Use when: Changing shared state, resource identifiers, access boundaries, or where data and task results are delivered.
Namespace & Isolation Preflight
Does everything belong to the right person, place, and scope, including after the request ends?
Review the relevant code and contracts. A namespace is an identity boundary, not automatically a permission boundary. A unique ID, directory prefix, or hidden interface control does not prove isolation.
Establish the intended boundaries
Identify the actors, resources, and supported sharing behavior. Distinguish an organization, account, room, agent, installation, and machine when the system actually gives them different authority. Do not invent a tenant layer for a product that does not have one.
For each affected resource, record its canonical identity, owner, authorized readers and writers, and allowed destinations. Separate the actor making a request from the owner of the data and the audience receiving the result. An agent's owner need not be the only person allowed to interact with it. Preserve intentional collaboration; question accidental access or disclosure.
Use repository code and tests to distinguish enforced boundaries from labels and intentions. Review-only requests authorize investigation, not migrations, permission changes, or live access experiments.
Follow identity from entry to effect
Trace a representative operation through its real producer and consumers: request or tool call, authorization, lookup, mutation, queued work, and delivery. Then examine alternate entry points that could bypass that path.
Check the mechanisms touched by the change:
- Keys and queries. Is an identifier globally unique or only unique within
a parent? Do lookups, joins, mutations, uniqueness constraints, and bulk operations use the same scope? Resolve client-supplied IDs under server-verified authority. Never treat an unguessable ID as permission.
- Names and paths. Are display names being mistaken for identity? Check
rename, case folding, Unicode normalization, prefix matching, traversal, symlinks, and extension or plugin name collisions where applicable. Follow the storage platform's actual semantics rather than assuming normalization.
- Derived state. Do cache keys, search filters, indexes, previews, and
deduplication keys preserve the relevant scope? A filtered source query is not enough if a shared cache serves its result to someone else.
- Subscriptions and delivery. Check event channels, WebSockets, notifications,
exports, generated files, and logs. Permission to read a source does not automatically authorize publishing it to a different audience.
- Background work. Does a queued or delegated task retain trustworthy actor,
source scope, purpose, and destination? Retries and resumed tasks must not inherit whichever room or account happens to be active later.
Name the authoritative enforcement point. Reuse it where possible instead of adding inconsistent filters to every caller. Preserve distinct read, write, and disclosure decisions where the product requires them.
Test changes over time
Trace the relevant transitions: rename, membership change, revoked delegation, deletion, reconnect, retry, import, backup restore, and movement between servers. Ask what happens to cached results, running tasks, signed links, and subscriptions.
State the intended revocation contract. Check authority again at the effect or delivery boundary when a stale grant could violate that contract. A durable, explicitly scoped grant may be valid; reconstructing authority from a mutable display name or current UI selection is not.
Imports and restores must preserve or deliberately remap ownership. Do not silently attach colliding IDs to unrelated local accounts. Identify how partial migration, stale indexes, and old queued work are recovered or held safely.
Prove separation and collaboration
Use synthetic principals and resources in an isolated test environment. Exercise the real storage, cache, or delivery path where that mechanism determines the result. Useful cases include two parents with the same local resource ID, a guessed foreign ID, a warmed cache followed by another user, a revoked member with a queued result, and an interrupted import retried after restart.
For each relevant boundary, prove both sides: the unauthorized operation fails without leaking data, and the intended shared operation still works. Include metadata and existence leaks when they matter to the product's threat model. A correct denial should not be indistinguishable from a broken authorized path.
Applying this to Nautilo
Use these checks when reviewing Nautilo, not as an architecture to impose on other products. Verify the symbols in the checkout being reviewed; source contracts do not establish which version a running server has adopted.
Room authority is not agent ownership. Start at packages/trust/src/types.ts, the policy resolver, and findReadableNamespacesForSubset in packages/trust/src/queries.ts. The namespace envelope separates readable, mutable, and writable namespaces. Writable means attachment destination, not permission to mutate every existing memory. Do not replace these sets with one owner check. Scope-mode memory uses its bound scope and agent instead. Tool arguments and model-generated text must not replace envelope identities.
Check the audience rule in the correct direction. For ordinary namespace-envelope reads, the candidate room's human audience must contain the current room's human audience. In a room with two people, one person's private namespace does not become readable merely because their Genie is present. Conversely, do not block intended reads from a room shared with both people. Follow packages/db/src/queries/namespace-access.ts for public boundaries: matching the stored human list alone is insufficient for an open room. Subthreads can share the top-level room's namespace. Do not manufacture a new identity or access boundary from a thread label.
Attachments are not single ownership. Inspect the memory/artifact namespace junctions in packages/db/src/schema/ and the access consumers. A memory can have multiple attachments; scope-origin memory also retains its writable origin. resolveRequiredMemoryNamespaceIds in packages/lattice-bridge/src/memory/required-namespace-set.ts combines those coordinates and rejects missing or stale origin authority. Do not simplify that set to the active room or the first attachment.
A cryptographic Domain is not a Namespace. In the Lattice implementation, separate stable Namespaces can share a Domain while retaining separate object histories and keys. Follow the Namespace binding and keyring code under packages/lattice-crypto/src/namespace/ and the authority consumers in packages/lattice-bridge/. Check namespace identity, Domain, Human/AI key class, access revision, generation, and authenticated head together. A matching Domain or cached key alone is not proof of current namespace authority. Audience changes must follow the existing rebind protocol, not rewrite the namespace identity or invent a parallel key store. Revocation cannot erase plaintext or old keys already obtained by a former participant.
Private work and return delivery are distinct. Trace buildWideEnvelopeForSpeaker and the private-task dispatch and completion paths. An authorized excursion can read privately and bring selected work back to its calling room. Check the persisted caller, return destination, attachment target order, and result-text audience. A flag controlling artifact attachment does not by itself make the completion message private. Preserve authorized sharing without treating a wider read envelope as blanket disclosure.
Exercise the applicable cases, using existing tests as starting points:
packages/agent/tests/unit-isolated/no-namespace-traversal-via-tool-args.test.ts:
hostile tool content cannot substitute the bound speaker, agent, or scope.
packages/lattice-bridge/tests/scenarios/shared-domain-two-namespaces.test.ts:
both legitimate decryptions work and cross-namespace decryption fails.
packages/lattice-bridge/tests/unit/memory-required-namespace-set.test.ts:
retain the complete attachment/origin set; reject missing authority.
- For changed delivery code, test a private excursion returning to its original
room after the user switches rooms, and separately test attachment and text disclosure. A passing crypto test does not cover that runtime path.
Return the review
Lead with Ready, Ready with named decisions, or Blocked for the reviewed change. Keep the report proportional to the findings:
- The affected boundary and the behavior that is actually enforced.
- Any blocker: trigger, affected path, consequence, and code evidence.
- The smallest coherent fix and a regression test for it.
- Unverified paths or a product-policy decision that code cannot settle.
Separate confirmed failures from plausible risks. Do not dump a checklist of unrelated subsystems or call the entire application isolated because one path passed. Security review can explore other attacks without repeating this map.