This section applies to every agent (Claude, Codex, Cursor, opencode, …) working in this repo.
- Every PR runs the central Security Scan required gate:
osv-scan+dependency-review(diff-scoped) andtrivy-fs(repo-wide, CRITICAL/HIGH, fixable only). It runs on every PR base branch, including stacked PRs. - A failing
trivy-fsis a REAL finding, not a flake. Read the job log — it prints each finding's rule id / severity / file — or open the run's SARIF results. Then remediate: bump the vulnerable dependency (this repo pins Python deps inrequirements-*.txt), fix theDockerfile/docker-compose.*.yml/k8s/*.yamlmisconfig, or add a narrow, documented.trivyignore/.trivyignore.yamlentry for a genuine false positive. Never weaken,continue-on-error, or disable the gate. - Worked example (currently blocking PRs here): KSV-0118 (
runAsNonRootunset) and KSV-0014 (readOnlyRootFilesystemunset), both HIGH k8s misconfigs, fire onk8s/*.yamlbecause the Deployments/StatefulSet have nosecurityContext. Fix by adding the containersecurityContext(runAsNonRoot: true,readOnlyRootFilesystem: true, plus writable volume mounts as needed) — do not ignore it. - A local
trivyscan with a stale DB misses findings: runtrivy --download-db-onlyfirst, and scan the merge ref, not just the PR head. - The org
code_scanningruleset is intentionally CodeQL-only (multiple code-scanning tools can't converge on one PR ref). Gating is enforced by the Security Scan job result, not by that rule — do not add Trivy, Scorecard, or other tools to thecode_scanningrule.
- This repo has no
.codegraph/index, so use normal search (grep/find/ripgrep). If a.codegraph/index is later added at the repo root, prefer CodeGraph (codegraph explore "<query>", or the code-review-graph MCP tools) before grep/find when understanding or locating code — it surfaces callers, callees, and impact that text search misses.
- Org rule: do not read config/secrets via
os.getenv()/ raw environment variables at runtime. Read them from a KV / credential registry. Org Actions secrets (e.g.OPENAI_API_KEY) flow into the KV via a bootstrap/CI step; runtime reads from the KV — env is only transport into the KV, never the runtime source. The reference implementation is xtrmLLMBatchPython's pgcrypto-encrypted Postgres credential registry (get_credential(name)); reuse that pattern (a DB-backed KV is fine) unless a dedicated KV is adopted. - Applies here: this service authenticates to external systems (OpenAI, plus
SMTP/IMAP/POP3 mail) and holds signing/encryption secrets, so new config and
credentials belong in the registry, not scattered
os.getenvreads. - Already aligned — keep it this way: per-tenant provider and mail
credentials (OpenAI keys, mailbox passwords) are stored in the DB-backed,
Fernet-encrypted tenant credential store and read from there at runtime (see
backend/services/llm_provider_selection.py,backend/api/tenant_config.py), not from environment variables. Add any new tenant/app secret to that store. - Known deviation to migrate: the bootstrap keys
ENCRYPTION_KEYandAUTH_SESSION_HMAC_SECRETare still read from the environment at runtime via pydantic-settings (backend/core/config.py). Treat these as the root-of-trust that unlocks the registry and keep env strictly as bootstrap transport; do not add furtheros.getenvsecret reads, and migrate toward the KV pattern as it is adopted.
- This repo (naruon) is the ECOSYSTEM HUB: email/PIM that DOM-decomposes emails and files (PDF via the newsdom-api sidecar) into a persisted content-graph + project-graph knowledge graph in Postgres, and composes the ecosystem services below.
- The org is an ecosystem around naruon (the hub: email/PIM that DOM-decomposes emails/files into a persisted knowledge graph). Its components are each a standalone program that must ALSO work as a git submodule, grown separately and together: wardnet = WAF/IDS/AI SOC/LB/APIM; clearfolio = document viewer; pg-erd-cloud = ERD tool; contextual-orchestrator = LLM cost/perf/upstream-LB gateway (beyond LiteLLM); codec-carver = STT/omni-modal speech-video codec; fast-mlsirm = LLM-as-a-Judge calibration + evaluation-item quality (uses aFIPC FIPC + kaefa item-fit); keyverse = passwordless SSO (OIDC/SCIM/ADFS/LDAP/FIDO2/OAuth2.1, eliminate passwords); newsdom-api = PDF→DOM sidecar; semantic-data-portal = upper ontology/catalog/governance plane with its own graph engine.
- Org rule: substantive feature or process PRs should find the relevant
academic papers and commit their PDFs into the PR (e.g. a
docs/papers/orreferences/directory) with full citations, respecting copyright — attach the PDF only when redistribution is permissible; otherwise cite, link, and summarize instead. - Example for this repo's domain: a PR touching the content/project knowledge-graph pipeline (DOM decomposition, entity/relation extraction, grounded graph retrieval) should ground itself in the relevant layout-analysis and knowledge-graph / grounded-retrieval literature.
- GitHub Actions used by governed workflows must be pinned to full commit SHAs
with a trailing version comment, for example
# v6; major-only refs such as@v6are not allowed in release or security workflows. - Security scanners are required gates. Do not use
continue-on-error: trueto hide Bandit, Strix, CodeQL, or dependency findings; preserve artifacts with explicitif: ${{ always() }}upload steps when needed. - Repository rulesets that require code-scanning tools such as Scorecard or Trivy must have matching PR and default-branch workflows that upload those tools' SARIF results. If merge is blocked with "Code scanning is waiting for results from Scorecard" or another ruleset-required tool, restore the missing SARIF workflow and rerun it instead of bypassing or weakening the ruleset.
- PR-scoped Strix scans must include trusted import context for changed backend
Python entrypoints; do not scan
backend/main.pyor routers as isolated single files if that makes real repo modules look missing. - PR-scoped Strix scans should include changed scanner/workflow/gate code but
exclude large CI self-test harnesses such as
scripts/ci/test_*.shfrom the scanner target. Those harnesses remain covered by Strix self-tests; scanning them as source can exhaust model context before security evidence finalizes. - Prefer upgrading or removing vulnerable dependencies over downgrading patched packages unless compatibility evidence is recorded in the PR.
- OpenCode Review, Strix Security Scan, and PR Review Merge Scheduler are
provided by ContextualWisdomLab central required workflows in
ContextualWisdomLab/.github; do not reintroduce repo-local copies of.github/workflows/opencode-review.yml,.github/workflows/strix.yml,.github/workflows/strix-selftest.yml, or.github/workflows/pr-review-merge-scheduler.yml. - The central Strix Security Scan uses GitHub Models by default through
STRIX_GITHUB_MODELS_TOKEN,STRIX_LLM=openai/gpt-5, andLLM_API_BASE_FILEpointing at a trusted file containinghttps://models.github.ai/inference; GitHub Models scans must try the configured GPT-5-or-newer model first and may fall back to the explicit workflow fallback list, currentlygithub_models/deepseek/deepseek-r1-0528andgithub_models/deepseek/deepseek-v3-0324, when GitHub Models provider capacity or model availability blocks the primary run. The Strix gate must route these fallback names through the GitHub Models endpoint with OpenAI-compatible child model names such asopenai/deepseek/deepseek-r1-0528, not the public DeepSeek API. Do not use GPT-4.1 or weaker GitHub Models fallbacks for Strix or OpenCode PR review evidence. Keep the GitHub Models endpoint in a trusted input file and pass the token only through the provider-scoped Strix child-process key path. LegacySTRIX_LLMsecrets must not override PR, push, or scheduled Strix defaults. Vertex remains available only for manualworkflow_dispatchevidence when thestrix_llminput explicitly selectsvertex_ai/gemini-3.1-pro-preview-customtoolsorvertex_ai/gemini-2.5-flashwithGCP_SA_KEY; expose Google/Vertex credentials only for Vertex provider mode. Direct OpenAI GPT-5.4-or-newer scans remain supported only for manualstrix_llmselections withSTRIX_OPENAI_API_KEY. Do not silently fall back between providers, and do not treat timeout-class provider infrastructure failures as clean PR evidence even when Strix printed zero vulnerabilities before failing. Disable silent Vertex fallback models in the workflow unless a future PR proves a new exact fallback contract with no Timeout/Fatal/Warn/Denied output. Record provider evidence in the PR. Known third-party Strix/Pydantic serializer warnings must be filtered narrowly inside the Strix gate child process, not as a visible workflow env entry, so Warn-class logs are not accepted as clean evidence and warning-filter variable names do not pollute GitHub logs. Strix workflow runtime budget keys should be exported inside the execution shell, not listed as visible stepenv:timeout names, so clean runs do not carry stale timeout-signal strings. Keep PR-scope process budgets large enough for Strix to finalize reports after the scanner emits completion events; a wrapper timeout aftervulnerability_count: 0is still failed evidence, not a pass. PR evidence must present the full scannable changed-file set from the PR head, plus allowlisted trusted context files, to Strix in one scanner invocation; do not split changed files into separate scanner runs or copy the entire PR-head repository tree by default because either breaks Strix's required whole-context and bounded-input contract. Keep architecture docs and reusable Strix gate tests aligned with this rule so stale Vertex-default, OpenAI-only, unavailable-model, blanket-warning, or generic-key examples cannot re-enter copied workflow guidance. - HMAC fallback sessions are local/control-plane compatibility credentials, not
authoritative workspace-membership evidence. Sensitive tenant security posture
surfaces must require OIDC/JWKS-backed membership or an explicit dependency
override in tests; do not allow a signed HMAC
workspaceclaim alone to open cross-workspace security data.
- Follow
docs/development/merge-gate-policy.mdfor PR gate interpretation. - PR Governance must stay metadata-only: no PR-head checkout, no admin merge, no review dismissal, and no security-check suppression.
- Pending/queued checks, pending CodeRabbit evidence, and a missing structured
OpenCode fallback approval are wait states, not hard failures. Hard blockers
should be reported through the idempotent
<!-- pr-governance:metadata-gate -->comment path. - CodeRabbit gating is evidence-preferred. When the current head has CodeRabbit
check-run evidence, pending evidence is a wait state and blocking evidence is
a blocker. When no CodeRabbit check-run exists, require an exact-current-head
APPROVEDreview from theopencode-agentGitHub App whose body names the head SHA and contains structured adversarial validation withstatus=passedand at least two falsified probes. Reject stale-head,github-actions, and insufficient-probe approvals. - The GitHub check-runs API silently refuses to move a completed check-run back to a non-completed status (the PATCH returns 200 but the run stays completed), which pins a stale failure to the head. Gate publishers must create a fresh check-run for completed→non-completed transitions instead of patching the completed one.
- OpenCode Agent approvals must be gated on current-head GitHub Checks. If a completed check run or status context failed, or the check rollup cannot be verified, the OpenCode review must request changes or explain the verification failure instead of approving.
- When OpenCode requests changes because Strix or another GitHub Check failed,
it must access the failed check logs and annotations, cite the exact failure
phrase, map each actionable failure to a concrete repository
path:line, and provide root cause, fix direction, regression-test direction, and a source-backed suggested diff. A review that only cites a workflow URL, check name, or generic failure summary is not sufficient. If Strix output contains multiple model vulnerability reports, include every model-reported vulnerability separately with the model name, title, severity, endpoint, and Code Locations/path:line evidence when present. - Strix logs may print the report's
Model ...line after the title, endpoint, and Code Locations block. Failed-check evidence parsers and OpenCode review validators must attribute each vulnerability to that in-report model line, not to a previous retry attempt such as a failed primaryopenai/gpt-5run. - OpenCode Agent PR reviews must be general-purpose and meticulous rather than narrowly scenario-specific. Configure the review prompt to use all relevant MCP sources: CodeGraph for structural source evidence, DeepWiki for repo docs, Context7 for current library/API behavior, and web search only for bounded external lookups. The agent may directly read changed files and focused hunks in read-only mode when MCP evidence is insufficient, but must not edit files or execute project code during the review.
- OpenCode Agent findings should be concrete and directly usable: each blocking
finding should name the observable impact, the trigger condition or affected
workflow, the smallest source-backed fix, and an exact verification command or
test target when the repository already has one. Avoid generic architecture
advice unless it maps to the cited
path:line. When GitHub reports a merge conflict, include a concrete conflict-resolution direction instead of only saying the PR is blocked: name the base/head branch, tell the author to merge or rebase the latest base branch into the PR branch, resolve conflict markers in the changed files, rerun the focused checks, and push the same branch. For Greptile-style specificity, carry a P1/P2/P3 priority in each finding, cite the evidence type that justifies it (nearby implementation, matching existing example, cross-file counterpart, current official docs, or failed check/log evidence), flag unrelated PR scope drift as scope risk instead of burying it in style notes, and makesuggested_diffa GitHub suggestion-ready minimal diff when possible. Include one compact Mermaid graph in the human-readable review body that maps the changed surface to the main risk, fix, and verification path. - OpenCode
Review Overviewcomments are durable gate evidence. Publish them through an idempotent marker such as<!-- opencode-review-overview -->and update the existing comment instead of deleting it after approval, failed checks, or check-rollup lookup failures. - Keep CodeRabbit
request_changes_workflowenabled for robot approval, but keep CodeRabbit GitHub Checks integration disabled. GitHub Actions are already evaluated by required checks and PR Governance; letting CodeRabbit also gate on Actions can strand a stale GitHubCHANGES_REQUESTEDreview when an external scanner such as direct-OpenAI Strix is quota-blocked after comments are fixed. STARTUP_FAILUREin required PR governance/check metadata is a hard blocker and should use the same idempotent metadata-gate comment path.- Required-check state handling fails closed: only explicit pass states (success, pass, skipped, neutral) satisfy the gate and only explicit pending states wait; any unrecognized state is a blocker. Gate blocker comments publish sanitized names and generic error text — raw CLI diagnostics belong in the workflow run log, never in PR comments.
- Trusted-base governance materialization must tolerate transient GitHub API
truncation such as
unexpected end of JSON inputwith bounded retries and archive validation; do not convert that infrastructure flake into a CodeRabbit or review blocker.
- First-run frontend sessions should open the Today execution dashboard while preserving explicit Dashboard, Email, and Calendar startup choices.
- Workspace navigation changes must keep the desktop primary nav and the tablet/mobile drawer in sync for Mail, Calendar, Tasks, Projects, Context Search, AI Hub, Data, Security, and Settings; add route and responsive E2E coverage instead of documenting unavailable destinations as implemented.
- Workspace destination pages must show actionable detail surfaces, not inert marketing placeholders. Calendar needs month/week/detail/coordination and CalDAV writeback states; Tasks needs source-linked ticket boards/details; Search needs result/detail graph/timeline states; Projects needs decision logs; Data needs repository/ingestion/embedding/quality/WebDAV queues; Security and Settings need governance and operational control surfaces. Keep provider writes labeled as future work until source-backed integrations exist.
- Browser frontend writes to signed backend routes must use the HttpOnly
naruon_sessioncookie through the same-origin Next.js/api/*proxy, which translates the server-readable cookie into backendAuthorization: Bearer. Browser code must not store bearer/session tokens inlocalStorageorsessionStorage, and must not emit or forward public identity headers such asX-User-Id,X-Organization-Id,X-Group-Id,X-Group-Ids,X-User-Role, orX-Dev-Auth-Token; tests/mocks must exercise the signed-session cookie path. - Frontend Docker, Compose, and publish workflows must not compile or inject a
public
NEXT_PUBLIC_API_URLinto browser bundles. Browser API calls stay on same-origin/api/*; only server-side Next.js route handlers should read runtimeBACKEND_INTERNAL_URL, with the exact Docker-network opt-in when running local Compose. - JWT/session verification must reject unsupported critical headers (
crit) before trusting payload claims; do not rely only on library defaults for this boundary. - Session authority is a server-side verification result, not a token payload
claim. HMAC and OIDC paths must pass
session_verifierfrom the code path that validated the signature/key; never derive it from_session_verifieror a similar user-controlled JWT claim. - Private backend
/api/*routers must be registered with the defaultget_auth_contextsigned-session dependency; only explicitly documented public endpoints such as/may omit it. Keep runtime feature/configuration endpoints signed-session protected; if the browser needs unauthenticated bootstrap data, add a narrowly scoped non-/apiendpoint that cannot reveal operational feature flags or provider state. Prometheus/metricsmust stay disabled by default and, when enabled, sit behind a trusted scrape path or reverse proxy access policy. Admin/provider registry endpoints must enforce role checks in addition to authentication. LLM providerbase_urlvalues must fail closed unless they are HTTPS, exact-host allowlisted byALLOWED_LLM_BASE_URL_HOSTS, and resolve only to global addresses. Runtime LLM calls that use a custom providerbase_urlmust build theirhttpxclient throughbuild_llm_provider_http_clientso TCP connects only to prevalidated global IP addresses while TLS/SNI still uses the allowlisted hostname; do not hand a freshly validated URL to a generic client that can resolve DNS again at connect time. - OIDC issuer and JWKS URLs are outbound identity-provider fetch surfaces. They
must use HTTPS, must not include userinfo or fragments, must reject localhost
and non-global IP literals, and must be exact-host allowlisted by
ALLOWED_OIDC_HOSTSbefore any JWKS preload or token verification path can use them. Allowlisted OIDC hostnames must also resolve only to global addresses, and JWKS retrieval must connect to the already validated pinned address while preserving TLS/SNI for the allowlisted hostname. - Email-derived tasks must stay source-linked to the email/thread and tenant owner scope. Do not expose new sequential database ids through task APIs; use opaque public ids for user-visible ticket tasks. Task titles are plain text: reject HTML-like execution item markup at the backend boundary rather than storing user-supplied tags for later UI rendering. Parsed email display fields must not persist active HTML/script markup, and email API list/detail/thread responses must sanitize stored subject/body/snippet/address display fields before returning them. Preserve message/thread identifiers separately from UI-safe subject/body, address, and attachment display text.
- Email file import must keep frontend file pickers,
/api/emails/import-files, andservices.email_import_servicein the same source-backed contract: supported uploads are.eml,.zip, and.mbox; imported email and attachment vectors must use the active organization LLM provider'sembedding_modelandbase_urlwhen configured, fit provider vector dimensions to storage, and fall back to zero vectors only when provider embedding generation is unavailable. Tests must cover the localembeddinggemmapath so Data workspace imports do not silently bypass the selected embedding model. - Home/Today dashboard reply-wait surfaces must read signed
/api/emails/pending-repliesdata instead of inferring pending replies from generic inbox fixtures or static copy. Tests and E2E mocks must verify the HttpOnlynaruon_sessioncookie proxy path and must not add public identity headers. - TenantConfig/provider account settings must be scoped by signed-session
user_idandorganization_id; do not query provider credentials or API keys byuser_idonly. Frontend Settings onboarding must use bearer-session API calls for account config, CalDAV/WebDAV source readiness, and runner token rotation, and mocks must not reintroduce public identity headers. - Self-service mailbox configuration routes must enforce owner-required
RBAC/ABAC through
services.access_policy; system/platform admins may not use user-facing/api/configroutes to read or mutate another user's mailbox credentials. Cross-user administration needs a dedicated audited admin route. - Workspace-scoped resources must carry
workspace_idthrough bothAccessRequestandResourcePolicy, and SQL scopes for WebDAV/Data/Security surfaces must filter the current workspace in addition to owner and organization. Do not expose same-organization cross-workspace records. - User-owned mailbox/provider account endpoints must not treat
system_adminorplatform_adminJWT roles as an owner session. Elevated operators need separate audited support flows;/api/accounts/configmust reject forged or orgless privileged sessions before credential lookup, and tests must exercise the real signed bearer path rather than only dev public-header overrides. - HMAC fallback sessions must not authorize
system_adminorplatform_adminroles. Platform-wide operators require the OIDC/JWKS path or a separately audited support flow so compromise of an HMAC session secret cannot mint platform administrator claims. AUTH_SESSION_HMAC_SECRETvalidation must enforce byte length, distinct character count, character-class diversity, placeholder/public-fixture rejection, and an explicit estimated entropy floor; keep runtime-secret tests aligned so long low-entropy strings cannot pass by length alone.- Reply-wait task escalation must reuse the server-authoritative pending reply
path, create or update source-linked
reply_slaticket tasks by opaque task id, and sanitize generated task titles from email subjects before persistence. Do not create duplicate reminder tasks for the same pending sent-mail message. - Mail connection updates and workers must validate server-side SMTP, POP3, IMAP, and relay destinations before persistence or network connection. POP3 credentials are required for POP3 sync; missing credentials must fail the sync path instead of logging a successful no-op. Do not place sensitive credential values, secret-derived values, or password-shaped field names in logs or raised exception text; use generic operation phrases such as "account configuration incomplete" instead of credential-type labels.
- SMTP, IMAP, and POP3 host validation must reject legacy numeric IP literal
forms such as decimal integers, hexadecimal integers, and octal dotted forms
before DNS or socket connection;
socket.getaddrinfomay resolve those forms to loopback/private addresses even whenipaddress.ip_addressrejects them. - GitHub Actions
run:blocks must not directly interpolate${{ github.* }},${{ inputs.* }}, or other expression data into shell conditions or commands. Pass expression values through stepenv:keys first, then quote shell variables such as"$IS_PR_EVIDENCE_RUN"inside the script. PR base/head SHA values from manual workflow inputs must be regex-validated as git SHAs before any fetch, diff, or artifact metadata use. - Privileged
pull_request_targetscanner jobs must treat PR-head blobs as non-executable input data. When copying PR-head files into temporary scan scopes, strip executable bits instead of preserving100755modes. PR-scoped Strix workflow runs should use the explicitSTRIX_TARGET_PATH=__PR_SCOPE__sentinel so the trusted base checkout is never presented as the PR scan target. Strix child processes that inspect untrusted PR scope data must set package-manager lifecycle script guards such asNPM_CONFIG_IGNORE_SCRIPTS,PNPM_CONFIG_IGNORE_SCRIPTS, andYARN_ENABLE_SCRIPTS=false; do not allow a scanner dependency install to execute PR-providedpackage.jsonscripts. - Test harness HTTP smoke helpers must not use broad URL opener APIs such as
urllib.request.urlopen; keep URL scheme validation and use explicit HTTP or HTTPS clients so Bandit/Strix do not normalize test-only SSRF patterns into production examples. - Screenshot and browser-capture helper scripts must build navigation targets
from a fixed localhost origin and an explicit route allowlist before calling
Playwright
page.goto; do not concatenate raw route or URL strings, and log capture failures with fixed message templates plus sanitized fields. - Alembic migrations must use structured Alembic/SQLAlchemy operation APIs such
as
op.create_indexandop.drop_indexfor schema objects. Do not build DDL withsa.text(f"...")or interpolated identifier strings, even when the current identifiers are static. - Infrastructure Docker Compose services must inherit the repo hardening
contract:
no-new-privileges:true,read_only: true, read-only config mounts, and explicittmpfsentries for the few runtime paths that must be writable. - Settings account screens must be source-backed by signed-session APIs rather than static provider examples. Display only masked secret presence flags, keep blank secret fields out of save payloads so stored values are preserved, and do not reintroduce public identity headers in frontend account mocks.
- New database tables and columns must use at least two-word
snake_casenames; avoid single-token columns such asid,title,status, orpriorityon newly introduced objects. - Public audit/event identifiers that may use human-readable prefixes must not
be stored in artificially short
varchar(n)columns; use opaque source UIDs that fit seeded smoke data and provider evidence without truncation. - When reviews find public/private identifier leaks, stale API fixture shapes, or recurring bug patterns, update tests, frontend mocks, E2E mocks, README examples, architecture docs, and explicitly record the anti-pattern in
AGENTS.mdso the same bug pattern does not reappear in copied examples. - When reviews find missing browser security headers or tabnabbing hardening,
update both backend header tests and frontend link tests. Global backend
responses must include
Referrer-Policy, andtarget="_blank"links must use explicitrel="noopener noreferrer". - When robot review cites an obsolete Strix provider policy, update the docs and
tests to the current GitHub Models default contract before accepting a
rollback suggestion; do not reintroduce generic
LLM_API_KEYor cross-provider credential forwarding while trying to satisfy old comments. - When reviews find inert navigation/dead-space controls, either wire them to an
implemented workspace route/API or remove the control; do not leave
high-traffic drawer/sidebar entries as permanent
준비 중copy. - AI Hub tabs must be backed by signed source evidence from
/api/ai-hub/surfaceor a narrower signed API. Do not reintroduce static model-score fixtures, fake workflow logs, or provider names that are not derived from prompt, provider, or audit data. - Data document repository assets must be backed by signed
/api/data/quality-surfaceevidence from scoped email and attachment rows. Do not reintroduce static file lists, sequential attachment/email ids, raw message ids, raw thread ids, message bodies, provider URLs, usernames, credentials, or claims that Naruon itself stores customer file capacity. - Icon-only workspace controls must carry localized
aria-labeltext matching the visible app language; do not rely on the SVG icon alone for Calendar, Tasks, drawer, modal, or toolbar actions. - Execution steps resulting in
Timeout,Fatal,Warn, orDeniedoutputs are considered hard failures. Tests must run without these warnings to be considered passing. - Strix success artifacts must also be scanned for
Timeout,Fatal,Warn, orDeniedoutput before accepting clean evidence. Filter only narrowly known third-party Strix internal warnings, such as thestrix.core.executionnon-lifecycle continuation line, before artifact upload; fail closed on any remaining warning-class report log output. - DB-affecting API slices need both mocked fast tests and a real PostgreSQL bootstrap/smoke path before PR merge evidence is considered complete.
- When a backend container reports missing
DATABASE_URLorAUTH_SESSION_HMAC_SECRET, verify the runtime path injects the operator env throughscripts/naruon_compose.sh, Kubernetes secrets, or an explicit orchestrator secret. Do not add code defaults, and do not mount or declare the full~/.envas a Composeenv_filebecause unrelated local secrets may leak into the backend container. - Backend container entrypoints must pass through
python scripts/start_backend.pybeforeuvicornimportsmain:app. Do not reintroduce Dockerfile, Compose, live-E2E, or gateway commands that calluvicorn main:appdirectly and expose Pydantic import tracebacks for missing runtime settings. - DAV/WebDAV/CalDAV routes are private integration surfaces unless explicitly documented otherwise. Register them with the default signed-session dependency, require the signed-session dependency in handler signatures, enforce route owner scope before capability/discovery/read/writeback responses, reject ownerless DAV paths instead of treating them as shared roots, escape XML response fields before interpolation, and keep path values separate from log-safe display values.
- Self-hosted runner WebSocket routes must validate both a signed bearer session and a server-side WorkspaceRunnerConfig registration token before accepting the socket. Do not use the raw path token as identity, a log value, or the sole active-connection key.
- Self-hosted connector command handlers must never return placeholder or mock
success for IMAP/SMTP execution. If no local customer-network adapter is
configured, fail closed with
adapter_not_configuredandprovider_write_executed=false; if an adapter is configured, wrap only the adapter's actual result in the standard runner response envelope. - Calendar UI actions must request
/api/calendar/writeback-intentwith server-authoritative source selection and provenance. Do not wire browser actions back to legacy/api/calendar/syncunless a trusted backend credential dependency and source-owner contract are explicitly in scope. - Calendar writeback UI must fail closed while the signed source registry is
loading or errored; do not emit intent POSTs without a confirmed opaque
target_source_id, and keep tests covering the loading/error boundary. - Calendar and WebDAV workspaces must expose the current opaque writeback source
as a deliberate user selection with capability and ETag/If-Match state.
Automatic first-source fallback may initialize the control, but intent POSTs
must use the selected opaque source and
409responses must render as conflicts, not generic errors or completed writes. - Calendar and WebDAV writeback source selection must resolve through opaque
source_uidvalues, signed-session organization scope, and persisted writeback eligibility, not sequential CalDAV or WebDAV account ids. Missing writeback eligibility must fail closed. Browser-visible source ids must not reveal or be deterministically derived from account primary keys. WebDAV account readiness may expose only source-safe labels and ETag/If-Match evidence, never provider URLs, usernames, credentials, or sequential account ids. Provider mutations remain future work until connector execution can enforce capability, consent, and ETag/If-Match checks. - DAV/WebDAV folder and write paths must not expose sequential folder primary
keys or claim provider write success from skeleton endpoints. Browser-visible
project folders use opaque
folder_uidvalues, and/davmutation methods must fail closed until source, capability, credential, and ETag/If-Match execution exists. - WebDAV folder listings must stay tenant-scoped by both
user_idand signed sessionorganization_id; do not reintroduce user-only folder queries because the same principal can exist in multiple B2B tenants. - Mobile workspace drawers must lock background body scroll while open and keep the drawer itself scrollable; responsive E2E screenshots should cover the open hamburger state after scrolling the drawer.
- Self-sent knowledge extraction must first prove true self-to-self addressing, stay idempotent per source email, preserve email/thread provenance, and store only plain-text task titles. Do not create unlinked knowledge tasks from raw dict payloads when the source email row is unavailable.
- Self-sent knowledge WebDAV/Notes materialization requests must start from an
opaque task uid, re-check owner and organization scope server-side, reject
non-
self_sent_knowledgetasks, and return intent metadata only until the connector/provider write path has source-backed ETag conflict handling. - Private API services should return deterministic
error_codevalues for expected failures; route layers must not derive HTTP status from human-readable message substrings. - UI async state for repeated task/action rows must be keyed by the row's opaque public id so one row's loading, error, or result state cannot overwrite another row.
- Sender ontology and relationship DAG APIs must stay scoped to the signed
session owner and organization. Source-backed UI panels must request
source_message_idandsource_thread_idfilters instead of presenting a global relationship graph as if it were current-thread evidence. - Sender DAG capture must start from a signed source email lookup, not browser-submitted relationship classifications. Route layers should derive the thread provenance server-side, persist only scoped ontology metadata, and keep provider writes out of relationship capture.
- Unique email and forwarded-import dedupe must use strong scoped signals:
normalized Message-ID, References/In-Reply-To, persisted duplicate provenance,
or exact body/attachment fingerprints. Do not merge threads from subject-only
Fwd:orRe:similarity, and keep duplicate cleanup intent-only until provenance persistence and source-backed import rewiring are implemented. - Operational dashboards must distinguish live server-observed state from planned instrumentation. Show connector registration and active outbound runner socket state when available, but label sync lag, provider throttling, queue depth, and writeback conflict dashboards as pending until source-backed connector events exist.
- Security governance screens must be source-backed by signed
/api/security/access-surfacedata. Do not ship static RBAC/ABAC rows, fake blocked-login logs, unsupported TLS/TDE claims, or permanent Security tab placeholders as implemented features. The browser-facing Security API and UI must not expose source ids, event ids, decision ids, review ids, workspace/org/user/group claims, provider execution flags, raw hosts, or resource UIDs; render scoped governance labels instead. New Security mocks and E2E fixtures must preserve bearer-session calls and omit public identity headers. - Data workspace repository, ingestion, embedding, and quality surfaces must be
source-backed by signed
/api/data/quality-surfacedata or explicitly labeled pending. Do not ship static ingestion logs, fake progress percentages, fake vector counts, unsupported embedding model names, static quality totals, or provider-write success claims; Data mocks and E2E fixtures must preserve the bearer-session call and omit public identity headers. - Project workspace lists, milestones, task links, and decision logs must be
source-backed by signed
/api/webdav/foldersand/api/tasksdata or explicitly labeled pending. Do not reintroduce static project names, inert report/filter buttons, provider write success claims, or sequential database ids in project UI/tests. - Project workspace folder rendering must prove owner scope before display:
/api/webdav/foldersincludes server-scopedowner_user_idandorganization_id, and the browser filters those folders against decoded signed-session claims before building project cards. Keepfolder_uidas an internal opaque key only; do not render it as visible UI text. - Self-hosted connector APM history must be persisted as scoped control-plane signal events before the UI claims durable heartbeat evidence. Do not expose runner registration tokens, path tokens, or raw provider credentials in event ids, details, logs, Settings mocks, or E2E fixtures.
- A
ForeignKeycolumn alone does NOT guarantee SQLAlchemy flush ordering. The unit of work orders cross-mapper INSERTs only throughrelationship()-derived dependencies, so a parent+child pair added in onesession.add_all([...]); commit()without a relationship can emit the child INSERT first and raiseForeignKeyViolationErroron real PostgreSQL (SQLite and mocked sessions hide it). Every FK table pair must have arelationship()in at least one direction;tests/test_model_relationship_integrity.pyguards this. Do not "fix" the symptom by flushing parents manually in one seeding helper while leaving the model relationship missing. @pytest.mark.postgressmoke tests exercise the real schema, so raw-SQL seeding must match the live models exactly: use the current table names (email_records, notemails), the correctRETURNINGcolumn per table, and include everyNOT NULLcolumn the ORM default would have filled (created_at,observed_at,parse_content_type,parser_key). Under asyncpg,INSERT ... SELECT/UNIONparameters default totext, so cast integer FK params explicitly (CAST(:email_id AS INTEGER)).- Postgres smoke seeding of
EncryptedStringcolumns (credentials_encrypted, providerapi_key, runner tokens) must set a FernetENCRYPTION_KEYfor the test (monkeypatchsettings.ENCRYPTION_KEY) and storeget_fernet().encrypt(...)output, never plaintext placeholders — the API read path decrypts and fails closed without a key. - Org-scoped listing endpoints accumulate rows across smoke runs on a shared
database. Smoke tests that assert an exact count must use a unique
organization_idper run (e.g.f"org-...-{uuid.uuid4().hex[:12]}"), not a hardcoded"org-acme", or the count drifts as history builds up. - Every
create_async_enginein a test must beawait engine.dispose()d on all paths (including skips). A leaked pooled connection surfaces later as a GCResourceWarning, which fails an unrelated downstream test under CI'sPYTHONWARNINGS=error. - Model column defaults must be timezone-aware: use
default=lambda: datetime.datetime.now(datetime.timezone.utc), never the deprecateddatetime.datetime.utcnow, whoseDeprecationWarningis fatal underPYTHONWARNINGS=error. - Provider-backed AI Hub agent cards and the security access surface are
admin/authoritative-verifier gated; HMAC bearer sessions cannot carry
tenant-admin roles or satisfy
_require_authoritative_workspace_scope. Smoke tests must assert the deny-first boundary (member persona sees no agent cards) or authenticate through an authoritative-verifierAuthContextoverride — do not weaken the endpoint to make the test pass.
- If CodeGraph is not initialized for this repository, agents may run
codegraph init -iautonomously without asking first; keep generated.codegraph/and.cursor/rules/codegraph.mdcartifacts local unless a future repository policy explicitly says to commit them. OpenCode PR review uses the projectopencode.jsoncMCP servers for CodeGraph, DeepWiki, Context7, and web search. It must initialize CodeGraph before review so structural findings cite graph-backed evidence instead of relying only on grep or raw file reads; use Context7 for current library docs, DeepWiki for repository documentation, and web search only for bounded external lookups. - StepSecurity
harden-runnerwill trigger false-positivesuspicious_file_accesslockouts on Next.js build and dev server executions (e.g.,router_init.jschecksum matches). Configuredisable-file-monitoring: truein theharden-runnerstep rather than disabling the workflow or usingcontinue-on-error. - Next.js 15+ Turbopack resolves workspace roots by scanning upward for
package-lock.json. Do not create or leave apackage-lock.jsonin the user's home directory (~/), as it will cause Turbopack to spawn infinite background worker node processes attempting to compile the entire home directory. pydantic-settingsstrictly rejects unexpected environment variables by default. When sharing a common.envfile between frontend and backend services, you must explicitly setextra="ignore"in theSettingsConfigDictto prevent fatal startup crashes.- Backend startup must not add code defaults for
DATABASE_URLorAUTH_SESSION_HMAC_SECRET. Support explicit env files from the operator, repository root, and backend working directory, and require Compose/Kubernetes to inject the mandatory values so missing runtime configuration fails before deployment rather than duringuvicorn main:appimport. - Python standard library
reflags (re.IGNORECASE) must be passed via theflags=keyword argument. Do not use inline(?i)at the start of the expression, as it will triggerDeprecationWarningregressions in Python 3.11+ test suites. - Next.js builds in memory-constrained CI environments (e.g., GitHub Actions) can fail with OOM errors due to PostCSS worker explosion. Set
POSTCSS_WORKERS: "1"andDISABLE_POSTCSS_WORKERS: "true"in the build environment to limit memory usage. - Release version bumps must keep
VERSION,CHANGELOG.md,frontend/package.json, FastAPI app metadata, runtime-config responses, and Docker runtime packaging synchronized. The backend should read the release version fromVERSION; do not add a new hardcoded API version string. - Before opening a PR, new committers should run the focused tests that cover
the changed contract and include exact commands in the PR body. For release
and Docker changes, at minimum verify
python -m pytest backend/tests/test_release_governance.py backend/tests/test_runtime_config_api.py -q,corepack pnpm@11.5.3 --dir frontend test --runInBandwhen frontend behavior changes, and a Docker build of the affected image. - GHCR publishing evidence for the combined
naruonimage must include the exact image name, tag, local image ID, push result, and registry verification from GitHub Packages or an equivalent manifest/API query. Publish the package with public visibility unless a repository policy explicitly says otherwise. Do not treat a local tag as published evidence. GitHub's REST Packages API and GraphQL package mutations currently do not expose a supported package visibility change operation for GHCR container packages; when API checks showvisibility: private, complete the public conversion through the logged-in GitHub package settings UI (Package settings->Danger Zone->Change visibility) and then verify anonymous pull/token access before declaring the image public. - Docker image security inspection is part of release evidence. Use a current container scanner such as Trivy or Grype against the exact pushed image tag and treat high/critical actionable findings as blockers until fixed or documented with precise non-applicability evidence.
- Docker Compose and Podman live-E2E work must clean up after itself. Stop
stale
naruon*containers, remove unused volumes/layers withpodman system prune --all --volumes --forcewhen safe, and verifypodman pshas no stale Naruon services. If Podman reports broken storage metadata such as missing overlay layers orreadlink ... overlay: invalid argument, runpodman system check --repair --forcebefore relying onpodman system dfor additional image scans. - Keep contributor setup friction low: document any new required environment variables, model tags, package-manager version pins, or live-E2E ports in the same PR that introduces them, and avoid hidden local-only defaults that make another committer's PR fail after checkout.
-
Stepwise execution: Each phase requires an atomic PR, GitHub PR Tracking, Push, and Robot Review. A phase only ends when merged. Do not proceed without merge.
-
TDD + DDD: Practice TDD, micro TDD, nano TDD, Domain Driven Development, and Context Driven Development.
-
API Wiring: Always work with API wiring completed.
-
Collaboration: Respect other agents' concurrent work; do not overwrite or dismiss unfamiliar changes.
-
Subagent Delegation: Actively delegate tasks to Subagents.
-
UI/Browser Testing: Use a real browser for testing (do not rely on assumptions).
-
Strict Errors: Treat
Timeout,Fatal,Warn, andDeniedoutputs as hard failures. -
Goal: Actively manage tasks to ensure open PR counts converge to 0.
-
When the gate exhausts fallbacks after the primary model produces a finding at or above threshold and then fails with a retryable error (like
NOT_FOUND), ensure the final output explicitly reportsStrix quick scan failed with a non-recoverable error.to prevent downgrading the finding to pass or misleadingly reporting an unavailability error.
As of 2026-08-04, code-owner review requirements (require_code_owner_reviews in branch
protection, require_code_owner_review in rulesets) are disabled across the ContextualWisdomLab
org: there is a single maintainer (solo developer), so a code-owner approval gate can never be
satisfied. This is ON HOLD until the org has multiple maintainers — do NOT re-enable these
settings or add CODEOWNERS-based merge gates before then.