Skip to content
Draft
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
cf472cf
docs(agents): add CWL-ENTRY read-first block
seonghobae Sep 2, 2026
94f7c51
docs(agents): codify evidence-based delivery
seonghobae Sep 4, 2026
6992172
docs(agents): preserve exact-head delivery safeguards
seonghobae Sep 4, 2026
2721136
docs(agents): retain predecessor recovery guidance
seonghobae Sep 4, 2026
01a2872
docs(agents): bound external MCP disclosures
seonghobae Sep 4, 2026
b5080b6
chore(stack): adopt canonical LLM owner foundation
seonghobae Sep 4, 2026
c603716
docs(agents): define orchestrator ownership boundary
seonghobae Sep 4, 2026
808abc8
test(agents): enforce remote MCP confidentiality
seonghobae Sep 4, 2026
03277a9
fix(agents): remove duplicate timeout authority
seonghobae Sep 4, 2026
c0331e1
fix(agents): restore owner release contract
seonghobae Sep 4, 2026
785b187
fix(agents): keep LLM policy in foundation lane
seonghobae Sep 4, 2026
a302174
fix(agents): retain foundation owner boundary
seonghobae Sep 4, 2026
57254cb
docs(agents): make repository entry verifiable
seonghobae Sep 4, 2026
4c31028
merge: stack verifiable agent entry
seonghobae Sep 4, 2026
73e7d7c
merge: inherit canonical agent entry
seonghobae Sep 4, 2026
f038377
test(docs): pin architecture LLM owner boundary
seonghobae Sep 5, 2026
93e99fa
docs(agents): restack operating guidance
seonghobae Sep 5, 2026
d665f92
docs(agents): codify minimal repair discipline
seonghobae Sep 5, 2026
104ecf3
merge: inherit current LLM owner guidance
seonghobae Sep 5, 2026
3b20e63
merge: inherit current agent guidance prerequisite
seonghobae Sep 5, 2026
cd55625
docs(agents): make operating know-how reproducible and safe
seonghobae Sep 5, 2026
5dade0a
docs(agents): distinguish clean-lock and migration evidence
seonghobae Sep 5, 2026
16fd89f
docs(agents): require migrated-schema and rollback data evidence
seonghobae Sep 5, 2026
63a0fb3
docs(agents): preserve confidence and tool mutation contracts
seonghobae Sep 5, 2026
847ef38
test(agents): pin product recurrence guidance
seonghobae Sep 5, 2026
93b7925
docs(agents): scope strict confidence checks to frontend consumers
seonghobae Sep 5, 2026
10ee05c
docs(agents): land product recurrence contracts
seonghobae Sep 5, 2026
aab070a
docs(agents): integrate concurrent recurrence guards without rewritin…
seonghobae Sep 5, 2026
498cf0c
docs(agents): consolidate concurrent recurrence guidance
seonghobae Sep 5, 2026
162c0df
docs(agents): record lease ownership and cancellation checks
seonghobae Sep 5, 2026
54e79d0
docs(agents): capture conflict and interleaving verification
seonghobae Sep 5, 2026
e30e3ab
docs(agents): distinguish runtime evidence from authorization
seonghobae Sep 6, 2026
5b5a49c
docs(agents): distinguish schema and resource ownership evidence
seonghobae Sep 6, 2026
0c94ffe
fix: document safe CI failure and cancellation evidence
seonghobae Sep 6, 2026
7beb0fa
fix: correct Trivy database refresh command
seonghobae Sep 6, 2026
8ec7381
docs(agents): separate review admission from merge authority
seonghobae Sep 6, 2026
29a5615
docs(agents): preserve publisher and verification evidence boundaries
seonghobae Sep 6, 2026
a813e6e
docs(agents): require manifest coverage and warning-strict evidence
seonghobae Sep 6, 2026
d33f1d7
docs(agents): verify actual trees before delta reconciliation
seonghobae Sep 6, 2026
d26d868
docs(agents): preserve dashboard response validation guidance
seonghobae Sep 6, 2026
8a6afec
docs(agents): distinguish scope jobs from security verdicts
seonghobae Sep 6, 2026
b3c4afd
docs(agents): 검증 중 소스 변경 방지 절차를 명시
seonghobae Sep 7, 2026
645d200
docs(agents): 자동 배포 조건과 지속 학습 규칙 기록
seonghobae Sep 7, 2026
943b29b
docs(agents): record exact-head and visual evidence rules
seonghobae Sep 8, 2026
749aae1
Merge remote-tracking branch 'origin/codex/agents-operating-playbook'…
seonghobae Sep 8, 2026
af551f9
Merge remote-tracking branch 'origin/codex/agents-operating-playbook'…
seonghobae Sep 8, 2026
d2ea21c
docs(agents): record gateway failure ownership boundary
seonghobae Sep 8, 2026
ac45cea
docs(agents): record CodeQL evidence lineage
seonghobae Sep 8, 2026
e6913e6
docs(agents): bound headless visual evidence
seonghobae Sep 8, 2026
32b3da9
docs: record actions run evidence boundary
seonghobae Sep 8, 2026
c8e4c03
docs: distinguish clean branches from protected evidence
seonghobae Sep 8, 2026
782a403
docs: verify pr source before repair push
seonghobae Sep 8, 2026
671f46d
docs: classify skipped stacked reviews
seonghobae Sep 8, 2026
d82d0c2
docs: record archive import redaction boundary
seonghobae Sep 8, 2026
cc04122
docs: require independent importer log probes
seonghobae Sep 8, 2026
1aa5033
test(governance): port OpenCode redirect boundary
seonghobae Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .agents/skills/github-robot-review-gate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,16 @@ context successfully on the protected branch.

## Safe temporary handling

- Require explicit maintainer authorization for the exact ruleset change and
substitute evidence; a delivery request alone is not authorization.
- Prefer rerunning or updating the branch before touching rulesets.
- If temporary removal is unavoidable, capture before/after ruleset JSON, owner,
expiry, current head SHA, equivalent temporary evidence, and a dated rollback
note in the PR.
- Restore required contexts and confirm `gh pr checks --required` shows the
hardened context before declaring the gate resolved.
- Restore the captured configuration on success, failure, cancellation, or
expiry. Block further landing until restoration is verified.

## Common mistakes

Expand Down
454 changes: 413 additions & 41 deletions AGENTS.md

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
flowchart LR
UI[Next.js frontend] --> API[FastAPI backend / Naruon control plane]
API --> DB[(Postgres + pgvector)]
API --> LLM[OpenAI APIs when configured]
API --> CO[contextual-orchestrator released gateway]
API --> CONN[Outbound-only self-hosted connector]
CONN --> MAIL[Customer IMAP/POP3/SMTP]
CONN --> DAV[Customer CalDAV/CardDAV/WebDAV]
Expand Down Expand Up @@ -227,6 +227,10 @@ URL must be HTTPS and exact-host allowlisted by `ALLOWED_LLM_BASE_URL_HOSTS`;
an unset or rejected endpoint fails closed to the deterministic extractor. Design
and grounding: [`docs/architecture/kg-extractor-seam.md`](docs/architecture/kg-extractor-seam.md).

Provider discovery, capability routing, and fallback belong to
`contextual-orchestrator`. Production adoption requires an immutable released
owner API/client/schema; an unavailable or incompatible release fails closed.

## CI security boundary

OpenCode Review, Strix Security Scan, and PR Review Merge Scheduler are supplied
Expand Down
22 changes: 19 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,23 @@ indexes, and auditable writeback intent. `ARCHITECTURE.md` and
```
Next.js frontend ──> FastAPI backend (control plane) ──> Postgres + pgvector
├──> OpenAI-compatible LLM providers (Ollama locally)
├──> contextual-orchestrator consumer boundary
│ └──> provider/model routing owned by contextual-orchestrator
└──> outbound-only self-hosted connector (connector/)
└──> customer IMAP/POP3/SMTP + CalDAV/CardDAV/WebDAV
```

Naruon owns product behavior and model-call authorization, but not provider/model
routing. Production model work requires an immutable released
`contextual-orchestrator` API/client/schema; a missing or incompatible owner
contract fails closed instead of falling back to a direct provider. The
Ollama/MLX paths in local Compose are explicit non-production development
fixtures. Central model-backed GitHub Actions are owned by
`ContextualWisdomLab/.github` and use `orchestrator/free`; do not copy that pool
selection into Naruon runtime. Verify protected release evidence before claiming
the owner contract is available; an open PR or unreleased branch is only proposed
evidence.

- `backend/` — FastAPI app (`main.py`, routers in `api/`, domain logic in
`services/`, SQLAlchemy models in `db/`, Alembic in `alembic/`). Owns
persistence, canonical email threading
Expand All @@ -140,8 +152,12 @@ Next.js frontend ──> FastAPI backend (control plane) ──> Postgres + pgve
ETag/If-Match); intents are the default response.
- Auth: signed HS256 bearer sessions (HMAC via `AUTH_SESSION_HMAC_SECRET`) or
enterprise OIDC/JWKS; private `/api/*` routers register the default
`get_auth_context` dependency. LLM `base_url` and OIDC/SMTP/IMAP/POP3 hosts
are strict egress allowlists that resolve only to pinned global addresses.
`get_auth_context` dependency. Naruon owns domain tools, authorization, and
context assembly; provider/model routing belongs to `contextual-orchestrator`.
Existing direct-provider `base_url` surfaces are legacy migration debt and
must not be extended as production routing authority; keep their SSRF controls
until they are removed. OIDC/SMTP/IMAP/POP3 hosts remain strict egress
allowlists that resolve only to pinned global addresses.
- CI (`.github/workflows/`): `app-ci.yml` (backend ruff+pytest, frontend
test/lint/build), plus `bandit`, `codeql`, `trivy`, `scorecard`,
`pr-governance`, `docker-publish` (GHCR on `v*` tags matching `VERSION`), and
Expand Down
171 changes: 171 additions & 0 deletions backend/tests/test_agent_llm_authority_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
"""Regression contracts for current LLM-routing authority guidance."""

import json
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[2]


def _read(path: str) -> str:
"""Read repository guidance as UTF-8 text."""
return (REPO_ROOT / path).read_text(encoding="utf-8")


def test_agent_guidance_does_not_reintroduce_direct_model_routing_authority() -> None:
"""Current agent and architecture guidance must not prescribe direct routing."""
agents = _read("AGENTS.md")
claude = _read("CLAUDE.md")
architecture = _read("ARCHITECTURE.md")

forbidden_current_guidance = (
"STRIX_GITHUB_MODELS_TOKEN",
"https://models.github.ai/inference",
"Direct OpenAI GPT-5.4-or-newer",
"OpenAI-compatible LLM providers",
"API --> LLM[OpenAI APIs when configured]",
)
combined = f"{agents}\n{claude}\n{architecture}"
for phrase in forbidden_current_guidance:
assert phrase not in combined


def test_agent_guidance_names_canonical_llm_owner_and_fail_closed_boundary() -> None:
"""Guidance must preserve product ownership while delegating LLM routing."""
agents = _read("AGENTS.md")
claude = _read("CLAUDE.md")
architecture = _read("ARCHITECTURE.md")
normalized_architecture = " ".join(architecture.lower().split())

assert "ContextualWisdomLab/.github" in agents
assert "contextual-orchestrator" in agents
assert "orchestrator/free" in agents
assert "fail closed" in agents.lower()
assert "contextual-orchestrator" in claude
assert "Naruon owns" in claude
assert "provider/model routing" in claude
assert "gateway token" in agents
assert "provider names, model" in agents
assert "immutable released owner API/client/schema" in agents
assert "open pr or unreleased branch" in " ".join(agents.lower().split())
assert "shared application/agent/gateway wall-clock timeout" in agents
assert "contextual-orchestrator" in normalized_architecture
assert "provider discovery" in normalized_architecture
assert "immutable released" in normalized_architecture
assert "fails closed" in normalized_architecture


def test_agent_guidance_preserves_product_contract_recurrence_rules() -> None:
"""Canonical guidance must retain product contracts repaired in owner lanes."""
agents = _read("AGENTS.md")
normalized_agents = " ".join(agents.lower().split())

assert "/api/llm/summarize" in agents
assert "integer percentage in `0..100`" in normalized_agents
assert "frontend consumers must reject fractional" in normalized_agents
assert "without rounding, coercion, or unit inference" in normalized_agents
assert "dynamic tool registration (`post /api/tools`)" in normalized_agents
assert "update (`patch /api/tools/{code}`)" in normalized_agents
assert "deletion (`delete /api/tools/{code}`)" in normalized_agents
assert "built-in immutability" in normalized_agents
assert "real provider/adapter execution target" in normalized_agents
assert "do not substitute a process-global registry or placeholder success" in normalized_agents


def test_agent_guidance_requires_physical_lease_ownership_and_interruption_checks() -> None:
"""Keep the concurrency repair procedure discoverable without claiming runtime proof."""
normalized_agents = " ".join(_read("AGENTS.md").lower().split())
for required_phrase in (
"session-level advisory lock",
"held physical connection",
"one-slot pool",
"invalidate before session-close rollback",
"reconnecting without a lease",
"last completed item",
"independent replica",
"actual task cancellation",
"source-only checks do not prove these runtime outcomes",
):
assert required_phrase in normalized_agents


def test_agent_guidance_separates_runtime_evidence_from_authorization() -> None:
"""Retain the operating procedure without treating prose as a live gate test."""
normalized_agents = " ".join(_read("AGENTS.md").lower().split())
for required_phrase in (
"protected source sha, actual consumer pin",
"configuration scope and revision",
"api readback",
"schema or parser support is not authorization",
"explicit authorization for the exact principal and resource",
"test fixtures or a known bot sender",
"compare the current value and revision",
"preserve the restoration receipt",
"original `github.actor` privileges",
"`github.triggering_actor` can differ",
"do not blindly rerun",
):
assert required_phrase in normalized_agents


def test_agent_guidance_separates_schema_reproduction_and_resource_ownership() -> None:
"""Keep diagnostic boundaries discoverable; prose checks are not runtime evidence."""
normalized_agents = " ".join(_read("AGENTS.md").lower().split())
for required_phrase in (
"general json schema validation",
"provider's supported subset",
"original model response",
"counterexample, not the proven cause",
"resource-owning transport boundary",
"borrowed streams",
"ownership transfer",
"garbage collection",
"resourcewarning",
):
assert required_phrase in normalized_agents


def test_agent_guidance_requires_safe_negative_probes_and_real_ci_outcomes() -> None:
"""Text conformance preserves the procedure, not executed CI evidence."""
normalized_agents = " ".join(_read("AGENTS.md").lower().split())
for required_phrase in (
"collection skips and expected failures",
"actual process exit status",
"task-owned decoy files",
"key-only assertions",
"inherited provider and replica settings",
"sanitize reports before potentially blocking teardown",
"task-owned process groups",
"trivy image --download-db-only",
):
assert required_phrase in normalized_agents


def test_opencode_config_uses_only_contextual_orchestrator_free() -> None:
"""Repository OpenCode model work must use only the canonical logical pool."""
raw_config = _read("opencode.jsonc")
config = json.loads(raw_config)

assert config["model"] == "contextual-orchestrator/orchestrator/free"
assert config["small_model"] == "contextual-orchestrator/orchestrator/free"
assert config["enabled_providers"] == ["contextual-orchestrator"]
assert set(config["provider"]) == {"contextual-orchestrator"}

provider = config["provider"]["contextual-orchestrator"]
assert provider["options"] == {
"baseURL": "http://127.0.0.1:8100/v1",
"headers": {"Authorization": "Bearer {env:CONTEXTUAL_ORCHESTRATOR_TOKEN}"},
"timeout": False,
}
assert set(provider["models"]) == {"orchestrator/free"}

forbidden_direct_routing = (
"github-models",
"STRIX_GITHUB_MODELS_TOKEN",
"https://models.github.ai/inference",
'"openai/gpt-5"',
'"deepseek/deepseek-r1-0528"',
'"deepseek/deepseek-v3-0324"',
)
for phrase in forbidden_direct_routing:
assert phrase not in raw_config
41 changes: 41 additions & 0 deletions backend/tests/test_release_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,53 @@ def test_agent_lifecycle_governance_artifacts_stay_aligned() -> None:
assert "owner-issued" in readme
assert "docs/development/merge-gate-policy.md" in guidance
assert "prove complete-delta succession" in guidance
assert "Superpowers systematic debugging" in guidance
assert "Ponytail ladder" in guidance
assert "Do not use administrative merge bypass" in guidance
assert "stale-context procedure" in guidance
assert "successor's exact tree, effective diff, tests, and lineage record" in guidance
assert research_artifact.read_bytes().startswith(b"%PDF-")


@pytest.mark.parametrize(
"relative_path",
[
"AGENTS.md",
"docs/development/merge-gate-policy.md",
".agents/skills/github-robot-review-gate/SKILL.md",
],
)
def test_gate_guidance_requires_authorized_recoverable_changes(relative_path: str) -> None:
"""Every linked gate procedure must retain authorization and failure rollback."""
guidance_text = " ".join(read_repo_text(relative_path).split())
assert "explicit maintainer authorization" in guidance_text
assert "success, failure, cancellation, or expiry" in guidance_text


def test_agent_playbook_keeps_skill_links_and_safe_execution_boundaries() -> None:
"""Keep local skill references runnable and prevent known unsafe copy patterns."""
guidance_text = read_repo_text("AGENTS.md")
skill_paths = re.findall(r"\]\((\.agents/skills/[^)]+/SKILL\.md)\)", guidance_text)
assert {
".agents/skills/fix-development-mistakes/SKILL.md",
".agents/skills/github-actions-privileged-pr-scan/SKILL.md",
".agents/skills/github-robot-review-gate/SKILL.md",
} <= set(skill_paths)
for skill_path in skill_paths:
assert read_repo_text(skill_path).startswith("---\nname:")
assert "git config --get-all remote.origin.fetch" in guidance_text
assert ".headRefOid as $head" in guidance_text
assert "select(.commit.oid == $head)" in guidance_text
assert "podman system prune --all --volumes --force" not in guidance_text
assert "podman system check --repair --force" not in guidance_text
assert "--dir frontend test --runInBand" not in guidance_text
assert "documentation-only work" in guidance_text
assert "closingIssuesReferences" in guidance_text
assert "기존 canonical release workflow" in guidance_text
assert "레지스트리 게시 성공과 실제 서비스 배포를 구분" in guidance_text
assert "`AGENTS.md`에 지속 반영" in guidance_text


def assert_dockerfile_stage_from(dockerfile: str, image: str, stage_alias: str) -> None:
pattern = (
rf"^FROM {re.escape(image)}@sha256:[0-9a-f]{{64}} AS {re.escape(stage_alias)}$"
Expand Down
11 changes: 8 additions & 3 deletions docs/development/merge-gate-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,18 @@ context before the hardened Strix workflow can emit a valid result.

Handling policy:

This procedure requires explicit maintainer authorization for the exact ruleset
change and substitute evidence. A delivery request alone is not authorization
to weaken a required gate; without it, repair or rerun the canonical owner.

1. Prefer branch update or rerun first.
2. If the required context cannot be emitted until the PR lands, document the
stale context and use only a temporary, reversible ruleset adjustment.
Capture equivalent temporary evidence before merge, such as a trusted-base
rerun, scanner artifact, SARIF output, or manual security review evidence
tied to the current head SHA.
3. Restore the `strix` required context after the hardened workflow emits it
successfully on the protected branch.
3. Restore the captured configuration on success, failure, cancellation, or
expiry. If repair fails, restore before pursuing a different approach.
4. Re-run required-check evidence after restore.

## PR #108/#109 evidence summary
Expand All @@ -124,7 +128,8 @@ Handling policy:
- Any temporary ruleset change must have captured before/after JSON, owner,
expiry, head SHA, equivalent temporary evidence, and a named restore
condition.
- Restore required contexts immediately after the repaired workflow emits them.
- Restore required contexts on success, failure, cancellation, or expiry;
verify restoration before any further landing.
- If the platform still rejects merge after policy-aligned settings and passing
checks, record the rejection as an external blocker with the exact command
output and head SHA.
Expand Down